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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
use anyhow::Result;
use std::process;
use crate::cli::{cache, commands, dispatch, pipeline};
use super::args::*;
use super::dispatch::DispatchContext;
use super::helpers::*;
pub(crate) fn handle(command: Commands, context: &DispatchContext) -> Result<Option<Commands>> {
let config_path = &context.config_path;
match command {
Commands::Cache { action } => match action {
CacheAction::Clear => {
cache::clear_cache()?;
tracing::info!("Cache cleared.");
Ok(None)
}
CacheAction::Status => {
cache::show_status();
Ok(None)
}
},
Commands::Validate { action } => match action {
ValidateAction::Versions { json, exit_code } => {
let (_workspace, resolved) = load_config(config_path)?;
let crates_to_process = dispatch::select_crates(&resolved, &context.crate_filter)?;
let workspace_root = std::env::current_dir()?;
let mut has_mismatches = false;
for resolved_cfg in &crates_to_process {
let checks = commands::validate_versions::run(resolved_cfg, &workspace_root, json)?;
// ~keep Ask `checks_pass` rather than re-deriving the verdict. The local
// predicate this replaces tested only whether some check failed to match,
// which disagreed with `checks_pass` in both directions: it passed a crate
// whose check set was EMPTY, the vacuous pass `checks_pass` exists to
// refuse, and it failed a `blocked_on_publish` row, which `checks_pass`
// deliberately tolerates because such a row cannot resolve until the
// release being gated is published. `--json` already reported
// `checks_pass`, so one command could answer `ok: true` and exit 1.
if !commands::validate_versions::checks_pass(&checks) {
has_mismatches = true;
}
// ~keep alef #1528: `checks_pass` above only compares version STRINGS across
// manifests; it has no notion of whether a committed `Cargo.lock` can actually
// resolve a requirement reachable from a manifest alef generated. That drift
// (the `tower-http` shape: a hand-written dependency alef never watches moves,
// and nothing about alef's own output changes) never prompts a regen, so
// `check_generated_lock_freshness` -- correct, and already proven against real
// incidents -- never gets a chance to run before the release is cut. This is
// the same check, reachable from the actual release gate instead, and it
// shares `checks_pass`'s own tolerance for a lock genuinely waiting on this
// release's not-yet-published version rather than re-litigating it.
if let Some(version) = resolved_cfg.resolved_version()
&& let Some(error) = pipeline::check_release_lock_freshness(&workspace_root, &version)
{
tracing::error!("[{}] {error:#}", resolved_cfg.name);
has_mismatches = true;
}
}
if has_mismatches && exit_code {
process::exit(1);
}
Ok(None)
}
},
Commands::ReleaseMetadata {
tag,
targets,
git_ref,
event,
dry_run,
force_republish,
json: _,
} => {
let effective_event = if event.is_empty() {
std::env::var("GITHUB_EVENT_NAME").unwrap_or_default()
} else {
event.clone()
};
let resolved_opt = load_config(config_path).ok().map(|(_ws, r)| r);
let resolved_cfg_opt: Option<&crate::core::config::ResolvedCrateConfig> =
resolved_opt.as_ref().and_then(|r| {
dispatch::select_crates(r, &context.crate_filter)
.ok()
.and_then(|v| v.into_iter().next())
});
let meta = commands::release_metadata::compute(
&tag,
&targets,
git_ref.as_deref(),
&effective_event,
dry_run,
force_republish,
resolved_cfg_opt,
)?;
crate::bin_cli::output::payload(meta.to_json()?);
Ok(None)
}
Commands::CheckRegistry {
registry,
package,
version,
tap_repo,
repo,
source,
asset_prefix,
required_assets,
json,
} => {
let extra = commands::check_registry::ExtraParams {
nuget_source: source,
tap_repo,
repo,
asset_prefix,
required_assets,
};
commands::check_registry::check(registry, &package, &version, &extra, json)?;
Ok(None)
}
Commands::GoTag {
version,
remote,
dry_run,
json,
} => {
let (_workspace, resolved) = load_config(config_path)?;
let crates_to_process = dispatch::select_crates(&resolved, &context.crate_filter)?;
let workspace_root = std::env::current_dir()?;
for resolved_cfg in &crates_to_process {
let params = commands::go_tag::GoTagParams {
version: &version,
remote: &remote,
dry_run,
output_json: json,
config: resolved_cfg,
workspace_root: &workspace_root,
};
commands::go_tag::run(¶ms)?;
}
Ok(None)
}
Commands::Snippets { action } => {
let exit_code = commands::snippets::run(action);
if exit_code != std::process::ExitCode::SUCCESS {
process::exit(1);
}
Ok(None)
}
other => Ok(Some(other)),
}
}
#[cfg(test)]
mod tests {
/// This module's own source, read at compile time so the guard cannot drift from the file it
/// guards.
///
/// ~keep Only the half ABOVE the test module is scanned. A self-referential source scan that
/// searched the whole file would always match: the needle appears in this module's own
/// assertion and prose, so the guard could never pass no matter what the production code did.
/// That is the same vacuity-in-reverse this test exists to prevent.
fn production_source() -> &'static str {
include_str!("release_commands.rs")
.split("#[cfg(test)]")
.next()
.expect("split always yields a first element")
}
/// `validate_versions::checks_pass` is the single definition of "these version checks pass".
/// This call site used to re-derive that verdict locally, and the copy disagreed in BOTH
/// directions: vacuously false for an empty check set (the pass `checks_pass` refuses), and
/// true for a `blocked_on_publish` row that `checks_pass` deliberately tolerates. Since
/// `--json` already reported `checks_pass`, one invocation could print `"ok": true` and exit 1.
///
/// The semantics live in `checks_pass`'s own tests. What is untestable through the CLI arm —
/// and what actually regressed — is the re-derivation, so that is what this pins.
#[test]
fn the_version_gate_asks_checks_pass_instead_of_re_deriving_it() {
let source = production_source();
assert!(
source.contains("validate_versions::checks_pass(&checks)"),
"the `validate versions --exit-code` arm must ask `checks_pass` for the verdict"
);
assert!(
!source.contains(".matches)"),
"re-deriving the verdict from `matches` reintroduces the empty-set and \
blocked_on_publish disagreements with `checks_pass`; call it instead"
);
}
}