Repository navigation
Expand file tree
/
Copy pathmain.rs
More file actions
124 lines (109 loc) · 5.36 KB
/
Copy pathmain.rs
File metadata and controls
124 lines (109 loc) · 5.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use socket_patch_cli::{commands, parse_argv_with_shortcuts, Commands};
use socket_patch_core::utils::env_compat::promote_peer_env_vars;
use socket_patch_core::utils::socket_cli_config;
/// Restore the default SIGPIPE disposition. The Rust runtime starts every
/// process with SIGPIPE ignored, so once a pipeline consumer exits
/// (`socket-patch scan | head -1`) the next `println!` gets `EPIPE` and
/// *panics* — exit 101 and a "failed printing to stdout: Broken pipe"
/// crash report instead of the quiet SIGPIPE death every other Unix CLI
/// has in that position. Network sockets are unaffected: std and socket2
/// write with `MSG_NOSIGNAL` / `SO_NOSIGPIPE`.
#[cfg(unix)]
fn restore_default_sigpipe() {
// SAFETY: SIG_DFL is a valid disposition for SIGPIPE, and this runs
// first thing in `main`, before any other threads exist.
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}
}
#[cfg(not(unix))]
fn restore_default_sigpipe() {}
#[tokio::main]
async fn main() {
// Must precede any output: clap help writes to a possibly-already-closed
// pipe.
restore_default_sigpipe();
// Ctrl-C / SIGTERM / SIGHUP remove a held `.socket/apply.lock` before
// the signal ends the process (see `interrupt`).
socket_patch_cli::interrupt::install();
// Accept the JS socket-cli's SOCKET_CLI_* peer names (silently —
// they are aliases, not deprecations) so `socket login` / socket-cli
// env setups work for socket-patch unchanged. Canonical names win.
promote_peer_env_vars();
// SOCKET_NO_API_TOKEN (or its SOCKET_CLI_ alias, promoted above)
// suppresses ambient tokens: scrub the env var before clap parses so
// only an explicit `--api-token` flag can authenticate. Core applies
// the same veto to its own env/config fallback layers.
if socket_cli_config::no_api_token_veto() {
std::env::remove_var("SOCKET_API_TOKEN");
}
// Then drop exported-but-empty SOCKET_* flag vars — global and
// subcommand-local (`SOCKET_CWD=` means "unset", not "crash the
// parse").
socket_patch_cli::args::scrub_empty_env_vars();
// The parser surface is `String`-typed, but argv is raw bytes on Unix —
// `std::env::args()` would *panic* on a non-Unicode argument. Collect
// `args_os` instead and turn a bad argument into the contract's clap
// usage error (stderr + exit 2) rather than a crash.
let argv: Vec<String> = match std::env::args_os()
.map(std::ffi::OsString::into_string)
.collect::<Result<_, _>>()
{
Ok(argv) => argv,
Err(bad_arg) => {
eprintln!("Error: Invalid UTF-8 was detected in one or more arguments: {bad_arg:?}");
std::process::exit(2);
}
};
let cli = match parse_argv_with_shortcuts(argv) {
Ok(cli) => cli,
Err(err) => err.exit(),
};
// A successful parse with `update == true` means a subcommand was also
// given (`socket-patch --update scan`) — bare `--update` is rewritten
// to the hidden `self-update` subcommand before it can parse Ok. The
// combination is contradictory; refuse with the contract's usage exit.
if cli.update {
eprintln!(
"Error: --update cannot be combined with a subcommand; run `socket-patch --update` on its own"
);
std::process::exit(2);
}
// A `--cwd`, `--global-prefix` or `--manifest-path` that names nothing
// is a usage error, checked once here before any command runs: read as
// an empty project it made every verifier pass vacuously.
if let Err(message) = cli.command.validate_paths() {
eprintln!("Error: {message}");
std::process::exit(2);
}
// Human-output policy (core advisories and prompt notes go quiet under
// --silent/--json) is fixed once, before any command code runs.
socket_patch_cli::ui::init(cli.command.global_args());
// Passive update notifier: guards + (maybe) a background check kicked
// off before dispatch, joined with a short grace budget after it.
// Structurally skipped for `--update` itself — an explicit update IS
// the check, and it refreshes the notifier's cache on its own.
let notifier = if matches!(cli.command, Commands::SelfUpdate(_)) {
None
} else {
socket_patch_cli::update_notifier::spawn_if_due(cli.command.global_args())
};
let exit_code = match cli.command {
Commands::Scan(args) => commands::scan::run(args).await,
Commands::Apply(args) => commands::apply::run(args).await,
Commands::Vex(args) => commands::vex::run(args).await,
Commands::Vendor(args) => commands::vendor::run(args).await,
Commands::Rollback(args) => commands::rollback::run(args).await,
Commands::Get(args) => commands::get::run(args).await,
Commands::List(args) => commands::list::run(args).await,
Commands::Remove(args) => commands::remove::run(args).await,
Commands::Repair(args) => commands::repair::run(args).await,
Commands::SelfUpdate(args) => commands::update::run(args).await,
Commands::HostedBundle(args) => commands::hosted_bundle::run(args).await,
};
// Never delays exit beyond its 500 ms grace budget; never changes the
// exit code; prints (at most) its notice to stderr after all command
// output.
socket_patch_cli::update_notifier::finish(notifier).await;
std::process::exit(exit_code);
}