upd-cli 0.1.9__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {upd_cli-0.1.9 → upd_cli-0.2.0}/CHANGELOG.md +18 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/Cargo.lock +1 -1
- {upd_cli-0.1.9 → upd_cli-0.2.0}/Cargo.toml +1 -1
- {upd_cli-0.1.9 → upd_cli-0.2.0}/PKG-INFO +1 -1
- {upd_cli-0.1.9 → upd_cli-0.2.0}/fixtures/clispec-v0.2.json +26 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/lib.rs +6 -4
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/schema.rs +31 -17
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/exit_codes.rs +5 -5
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/fix_audit.rs +5 -5
- {upd_cli-0.1.9 → upd_cli-0.2.0}/.mise.toml +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/.pre-commit-config.yaml +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/.pre-commit-hooks.yaml +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/.rumdl.toml +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/LICENSE +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/Makefile +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/README.md +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/assets/logo-wide.svg +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/assets/logo.svg +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/pyproject.toml +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/python/upd_cli/__init__.py +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/python/upd_cli/__main__.py +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/python/upd_cli/py.typed +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/rust-toolchain.toml +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/align.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/audit/cache.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/audit/cvss.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/audit/mod.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/bin/upd-cli.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/cache.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/cli.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/config.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/cooldown.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/http.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/interactive.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/lockfile.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/main.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/output.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/registry/crates_io.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/registry/github_releases.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/registry/go_proxy.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/registry/mock.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/registry/mod.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/registry/npm.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/registry/nuget.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/registry/pypi.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/registry/rubygems.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/registry/terraform.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/registry/utils.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/cargo_toml.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/csproj.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/gemfile.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/github_actions.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/go_mod.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/mise.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/mod.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/npm_range.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/package_json.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/pre_commit.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/pyproject.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/requirements.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/updater/terraform.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/version/compare.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/version/mod.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/version/pep440.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/version/semver_util.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/src/version/tag.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/audit_offline.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/audit_sarif.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/audit_severity.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/bump_filter.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/cooldown_e2e.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/discovery_no_ignore.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/format_json.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/help_text.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/interactive_tty.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/invalid_positional.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/no_args_scope.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/output_streams.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/tests/package_filter.rs +0 -0
- {upd_cli-0.1.9 → upd_cli-0.2.0}/vership.toml +0 -0
|
@@ -19,6 +19,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## [0.2.0](https://github.com/rvben/upd/compare/v0.1.10...v0.2.0) - 2026-06-11
|
|
25
|
+
|
|
26
|
+
### Breaking Changes
|
|
27
|
+
|
|
28
|
+
- **audit**: give vulnerabilities_found its own exit code 6 as a declared outcome ([0037dc4](https://github.com/rvben/upd/commit/0037dc4e075f7d3cca7e51096fc11d07e1aa1cdb))
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **audit**: give vulnerabilities_found its own exit code 6 as a declared outcome ([0037dc4](https://github.com/rvben/upd/commit/0037dc4e075f7d3cca7e51096fc11d07e1aa1cdb))
|
|
33
|
+
|
|
34
|
+
## [0.1.10](https://github.com/rvben/upd/compare/v0.1.9...v0.1.10) - 2026-06-11
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- **schema**: declare updates_available as an outcome, not an error kind ([4f55a02](https://github.com/rvben/upd/commit/4f55a02e3b80384f19199691302f96ba4deb9246))
|
|
39
|
+
|
|
22
40
|
## [0.1.9](https://github.com/rvben/upd/compare/v0.1.8...v0.1.9) - 2026-06-11
|
|
23
41
|
|
|
24
42
|
### Added
|
|
@@ -38,6 +38,11 @@
|
|
|
38
38
|
"description": "The finite set of `kind` values the tool emits in structured errors. Consumers can write exhaustive handlers against this set.",
|
|
39
39
|
"type": "array",
|
|
40
40
|
"items": { "$ref": "#/$defs/error" }
|
|
41
|
+
},
|
|
42
|
+
"outcomes": {
|
|
43
|
+
"description": "Documented non-zero exit codes that signal a data state rather than a failure (the diff/grep convention). An outcome exit writes no error envelope; stdout carries the result. Codes must not overlap with the exit codes declared in `errors`.",
|
|
44
|
+
"type": "array",
|
|
45
|
+
"items": { "$ref": "#/$defs/outcome" }
|
|
41
46
|
}
|
|
42
47
|
},
|
|
43
48
|
"$defs": {
|
|
@@ -106,6 +111,27 @@
|
|
|
106
111
|
"description": { "type": "string" }
|
|
107
112
|
}
|
|
108
113
|
},
|
|
114
|
+
"outcome": {
|
|
115
|
+
"type": "object",
|
|
116
|
+
"required": ["code", "name"],
|
|
117
|
+
"properties": {
|
|
118
|
+
"code": {
|
|
119
|
+
"description": "The exit code the tool returns for this outcome. Consumers can branch on it without parsing anything.",
|
|
120
|
+
"type": "integer",
|
|
121
|
+
"minimum": 1,
|
|
122
|
+
"maximum": 255
|
|
123
|
+
},
|
|
124
|
+
"name": {
|
|
125
|
+
"description": "Stable identifier for the outcome. Snake_case by convention.",
|
|
126
|
+
"type": "string",
|
|
127
|
+
"minLength": 1,
|
|
128
|
+
"pattern": "^[a-z][a-z0-9_]*$"
|
|
129
|
+
},
|
|
130
|
+
"description": {
|
|
131
|
+
"type": "string"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
109
135
|
"error": {
|
|
110
136
|
"type": "object",
|
|
111
137
|
"required": ["kind"],
|
|
@@ -53,15 +53,17 @@ pub fn decide_exit_code(non_mutating: bool, has_pending_updates: bool, has_error
|
|
|
53
53
|
///
|
|
54
54
|
/// - `2` — scan errors occurred; errors take precedence over vulnerability
|
|
55
55
|
/// findings so that CI can distinguish a broken scan from a clean one.
|
|
56
|
-
/// - `
|
|
57
|
-
/// the update exit codes (1 = pending updates,
|
|
58
|
-
///
|
|
56
|
+
/// - `6` — vulnerabilities were found and `no_fail` is `false`; a dedicated
|
|
57
|
+
/// code, distinct from the update exit codes (1 = pending updates,
|
|
58
|
+
/// 2 = errors) and from the error exit codes declared in the schema, so
|
|
59
|
+
/// callers can branch on the exit code alone. Declared as the
|
|
60
|
+
/// `vulnerabilities_found` outcome in the schema.
|
|
59
61
|
/// - `0` — no vulnerabilities found, or `no_fail` suppresses the non-zero exit.
|
|
60
62
|
pub fn decide_audit_exit_code(vuln_count: usize, error_count: usize, no_fail: bool) -> i32 {
|
|
61
63
|
if error_count > 0 {
|
|
62
64
|
2
|
|
63
65
|
} else if vuln_count > 0 && !no_fail {
|
|
64
|
-
|
|
66
|
+
6
|
|
65
67
|
} else {
|
|
66
68
|
0
|
|
67
69
|
}
|
|
@@ -251,13 +251,19 @@ fn build_schema() -> Value {
|
|
|
251
251
|
]
|
|
252
252
|
}
|
|
253
253
|
],
|
|
254
|
-
"
|
|
254
|
+
"outcomes": [
|
|
255
255
|
{
|
|
256
|
-
"
|
|
257
|
-
"
|
|
258
|
-
"
|
|
259
|
-
"retryable": false
|
|
256
|
+
"code": 1,
|
|
257
|
+
"name": "updates_available",
|
|
258
|
+
"description": "Updates are available (dry-run mode only); the report is on stdout. Not an error. Run with --apply to write changes"
|
|
260
259
|
},
|
|
260
|
+
{
|
|
261
|
+
"code": 6,
|
|
262
|
+
"name": "vulnerabilities_found",
|
|
263
|
+
"description": "Security vulnerabilities found during audit; the report is on stdout. Not an error. Use --no-fail to exit 0 instead"
|
|
264
|
+
}
|
|
265
|
+
],
|
|
266
|
+
"errors": [
|
|
261
267
|
{
|
|
262
268
|
"kind": "io_error",
|
|
263
269
|
"description": "File read or write failed, or a required path does not exist",
|
|
@@ -281,12 +287,6 @@ fn build_schema() -> Value {
|
|
|
281
287
|
"description": "Version conflict detected between files",
|
|
282
288
|
"exit_code": 5,
|
|
283
289
|
"retryable": false
|
|
284
|
-
},
|
|
285
|
-
{
|
|
286
|
-
"kind": "vulnerabilities_found",
|
|
287
|
-
"description": "Security vulnerabilities found during audit (use --no-fail to suppress non-zero exit)",
|
|
288
|
-
"exit_code": 3,
|
|
289
|
-
"retryable": false
|
|
290
290
|
}
|
|
291
291
|
]
|
|
292
292
|
})
|
|
@@ -358,18 +358,32 @@ mod tests {
|
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
#[test]
|
|
361
|
-
fn
|
|
361
|
+
fn schema_declares_updates_available_outcome_with_code_1() {
|
|
362
362
|
let s = build_schema();
|
|
363
|
-
let
|
|
364
|
-
let updates_available =
|
|
363
|
+
let outcomes = s["outcomes"].as_array().expect("outcomes must be an array");
|
|
364
|
+
let updates_available = outcomes
|
|
365
365
|
.iter()
|
|
366
|
-
.find(|
|
|
367
|
-
.expect("must declare an 'updates_available'
|
|
366
|
+
.find(|o| o["name"].as_str() == Some("updates_available"))
|
|
367
|
+
.expect("must declare an 'updates_available' outcome");
|
|
368
368
|
assert_eq!(
|
|
369
|
-
updates_available["
|
|
369
|
+
updates_available["code"].as_u64(),
|
|
370
370
|
Some(1),
|
|
371
371
|
"updates_available must map to exit code 1 (the dry-run signal)"
|
|
372
372
|
);
|
|
373
|
+
let errors = s["errors"].as_array().expect("errors must be an array");
|
|
374
|
+
assert!(
|
|
375
|
+
!errors
|
|
376
|
+
.iter()
|
|
377
|
+
.any(|e| e["kind"].as_str() == Some("updates_available")),
|
|
378
|
+
"updates_available is an outcome, not an error kind"
|
|
379
|
+
);
|
|
380
|
+
for outcome in outcomes {
|
|
381
|
+
let code = outcome["code"].as_u64().expect("outcome must have a code");
|
|
382
|
+
assert!(
|
|
383
|
+
!errors.iter().any(|e| e["exit_code"].as_u64() == Some(code)),
|
|
384
|
+
"outcome code {code} must not overlap with error exit codes"
|
|
385
|
+
);
|
|
386
|
+
}
|
|
373
387
|
}
|
|
374
388
|
|
|
375
389
|
#[test]
|
|
@@ -344,8 +344,8 @@ fn decide_audit_exit_code_clean() {
|
|
|
344
344
|
#[test]
|
|
345
345
|
fn decide_audit_exit_code_vulns_without_no_fail() {
|
|
346
346
|
use upd::decide_audit_exit_code;
|
|
347
|
-
assert_eq!(decide_audit_exit_code(1, 0, false),
|
|
348
|
-
assert_eq!(decide_audit_exit_code(162, 0, false),
|
|
347
|
+
assert_eq!(decide_audit_exit_code(1, 0, false), 6);
|
|
348
|
+
assert_eq!(decide_audit_exit_code(162, 0, false), 6);
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
/// Unit test: vulns found, --no-fail present → exit 0.
|
|
@@ -388,7 +388,7 @@ fn audit_on_empty_workspace_exits_zero() {
|
|
|
388
388
|
/// A wiremock server stands in for the OSV API and reports one vulnerability
|
|
389
389
|
/// for `requests==1.0.0`.
|
|
390
390
|
#[tokio::test]
|
|
391
|
-
async fn
|
|
391
|
+
async fn audit_with_vulns_exits_six() {
|
|
392
392
|
use wiremock::matchers::{method, path};
|
|
393
393
|
use wiremock::{Mock, MockServer, ResponseTemplate};
|
|
394
394
|
|
|
@@ -422,8 +422,8 @@ async fn audit_with_vulns_exits_three() {
|
|
|
422
422
|
);
|
|
423
423
|
|
|
424
424
|
assert_eq!(
|
|
425
|
-
code,
|
|
426
|
-
"audit with vulns must exit
|
|
425
|
+
code, 6,
|
|
426
|
+
"audit with vulns must exit 6, the vulnerabilities_found outcome (no --no-fail); stderr: {stderr}"
|
|
427
427
|
);
|
|
428
428
|
}
|
|
429
429
|
|
|
@@ -159,9 +159,9 @@ async fn fix_audit_dry_run_exits_1_and_leaves_file_unchanged() {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
/// When a vulnerability has no `fixed_version`, emit a warning and don't touch the file.
|
|
162
|
-
/// Falls through to normal audit exit code (
|
|
162
|
+
/// Falls through to normal audit exit code (6 = vulnerabilities_found outcome, !no_fail).
|
|
163
163
|
#[tokio::test]
|
|
164
|
-
async fn
|
|
164
|
+
async fn fix_audit_no_fixed_version_warns_and_exits_6() {
|
|
165
165
|
use wiremock::matchers::{method, path};
|
|
166
166
|
use wiremock::{Mock, MockServer, ResponseTemplate};
|
|
167
167
|
|
|
@@ -198,10 +198,10 @@ async fn fix_audit_no_fixed_version_warns_and_exits_3() {
|
|
|
198
198
|
);
|
|
199
199
|
|
|
200
200
|
// No fixable packages → falls through to normal audit exit code.
|
|
201
|
-
// Normal audit with vulnerabilities and !no_fail → exit
|
|
201
|
+
// Normal audit with vulnerabilities and !no_fail → exit 6.
|
|
202
202
|
assert_eq!(
|
|
203
|
-
code,
|
|
204
|
-
"should exit
|
|
203
|
+
code, 6,
|
|
204
|
+
"should exit 6 (vulnerabilities_found outcome, no fix available); stdout: {stdout}\nstderr: {stderr}"
|
|
205
205
|
);
|
|
206
206
|
|
|
207
207
|
let content = fs::read_to_string(&req_path).unwrap();
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|