diff --git a/crates/prek/src/cli/auto_update/mod.rs b/crates/prek/src/cli/auto_update/mod.rs index b76eb1b5b..00bcf5357 100644 --- a/crates/prek/src/cli/auto_update/mod.rs +++ b/crates/prek/src/cli/auto_update/mod.rs @@ -324,7 +324,7 @@ pub(crate) async fn auto_update( freeze: bool, jobs: usize, dry_run: bool, - check: bool, + exit_code: bool, cooldown_days: u8, printer: Printer, ) -> Result { @@ -380,7 +380,7 @@ pub(crate) async fn auto_update( } } - if apply_result.failure || (check && apply_result.has_updates) { + if apply_result.failure || (exit_code && apply_result.has_updates) { return Ok(ExitStatus::Failure); } Ok(ExitStatus::Success) diff --git a/crates/prek/src/cli/mod.rs b/crates/prek/src/cli/mod.rs index a628eab0a..a5789043d 100644 --- a/crates/prek/src/cli/mod.rs +++ b/crates/prek/src/cli/mod.rs @@ -745,7 +745,10 @@ pub(crate) struct AutoUpdateArgs { /// Do not write changes to the config file, only display what would be changed. #[arg(long)] pub(crate) dry_run: bool, - /// Alias of `--dry-run` that exits with status 1 if updates would be made. + /// Exit with status 1 if updates are available. + #[arg(long)] + pub(crate) exit_code: bool, + /// Alias of `--dry-run --exit-code`. #[arg(long)] pub(crate) check: bool, /// Number of threads to use. diff --git a/crates/prek/src/main.rs b/crates/prek/src/main.rs index f7aefee11..692ed8ee2 100644 --- a/crates/prek/src/main.rs +++ b/crates/prek/src/main.rs @@ -386,7 +386,7 @@ async fn run(cli: Cli) -> Result { args.freeze, args.jobs, args.dry_run || args.check, - args.check, + args.exit_code || args.check, args.cooldown_days, printer, ) diff --git a/crates/prek/tests/auto_update.rs b/crates/prek/tests/auto_update.rs index f11599c3a..f1f514e36 100644 --- a/crates/prek/tests/auto_update.rs +++ b/crates/prek/tests/auto_update.rs @@ -2074,6 +2074,148 @@ fn auto_update_check() -> Result<()> { Ok(()) } +#[test] +fn auto_update_dry_run_exit_code() -> Result<()> { + let context = TestContext::new(); + context.init_project(); + + let repo_path = create_local_git_repo( + &context, + "dry-run-exit-code-test-repo", + &["v1.0.0", "v1.1.0", "v2.0.0"], + )?; + + context.write_pre_commit_config(&indoc::formatdoc! {r" + repos: + - repo: {} + rev: v1.0.0 + hooks: + - id: test-hook + ", repo_path}); + context.git_add("."); + + let filters = context.filters(); + + cmd_snapshot!(filters.clone(), context.auto_update().arg("--dry-run").arg("--exit-code").arg("--cooldown-days").arg("0"), @" + success: false + exit_code: 1 + ----- stdout ----- + [HOME]/test-repos/dry-run-exit-code-test-repo + would update rev `v1.0.0` -> `v2.0.0` + + ----- stderr ----- + "); + + insta::with_settings!( + { filters => filters.clone() }, + { + assert_snapshot!(context.read(PRE_COMMIT_CONFIG_YAML), @" + repos: + - repo: [HOME]/test-repos/dry-run-exit-code-test-repo + rev: v1.0.0 + hooks: + - id: test-hook + "); + } + ); + + Ok(()) +} + +#[test] +fn auto_update_exit_code_updates_config() -> Result<()> { + let context = TestContext::new(); + context.init_project(); + + let repo_path = create_local_git_repo( + &context, + "exit-code-test-repo", + &["v1.0.0", "v1.1.0", "v2.0.0"], + )?; + + context.write_pre_commit_config(&indoc::formatdoc! {r" + repos: + - repo: {} + rev: v1.0.0 + hooks: + - id: test-hook + ", repo_path}); + context.git_add("."); + + let filters = context.filters(); + + cmd_snapshot!(filters.clone(), context.auto_update().arg("--exit-code").arg("--cooldown-days").arg("0"), @" + success: false + exit_code: 1 + ----- stdout ----- + [HOME]/test-repos/exit-code-test-repo + updating rev `v1.0.0` -> `v2.0.0` + + ----- stderr ----- + "); + + insta::with_settings!( + { filters => filters.clone() }, + { + assert_snapshot!(context.read(PRE_COMMIT_CONFIG_YAML), @" + repos: + - repo: [HOME]/test-repos/exit-code-test-repo + rev: v2.0.0 + hooks: + - id: test-hook + "); + } + ); + + Ok(()) +} + +#[test] +fn auto_update_exit_code_succeeds_when_up_to_date() -> Result<()> { + let context = TestContext::new(); + context.init_project(); + + let repo_path = create_local_git_repo( + &context, + "exit-code-up-to-date-test-repo", + &["v1.0.0", "v2.0.0"], + )?; + + context.write_pre_commit_config(&indoc::formatdoc! {r" + repos: + - repo: {} + rev: v2.0.0 + hooks: + - id: test-hook + ", repo_path}); + context.git_add("."); + + let filters = context.filters(); + + cmd_snapshot!(filters.clone(), context.auto_update().arg("--exit-code").arg("--cooldown-days").arg("0"), @" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + "); + + insta::with_settings!( + { filters => filters.clone() }, + { + assert_snapshot!(context.read(PRE_COMMIT_CONFIG_YAML), @" + repos: + - repo: [HOME]/test-repos/exit-code-up-to-date-test-repo + rev: v2.0.0 + hooks: + - id: test-hook + "); + } + ); + + Ok(()) +} + #[test] fn quoting_float_like_version_number() -> Result<()> { let context = TestContext::new(); diff --git a/docs/cli.md b/docs/cli.md index a9ede2750..2fbd6b776 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -552,7 +552,7 @@ prek auto-update [OPTIONS]
--bleeding-edge

Update to the bleeding edge of the default branch instead of the latest tagged version

--cd, -C dir

Change to directory before running

-
--check

Alias of --dry-run that exits with status 1 if updates would be made

+
--check

Alias of --dry-run --exit-code

--color color

Whether to use color in output

May also be set with the PREK_COLOR environment variable.

[default: auto]

Possible values:

    @@ -566,6 +566,7 @@ prek auto-update [OPTIONS]
--exclude-repo repo

Do not update this repository. This option may be specified multiple times

--exclude-tag pattern

Ignore tags matching this glob pattern. This option may be specified multiple times.

For example, use --exclude-tag nightly to skip a moving tag, or --exclude-tag '*-{alpha,beta,rc}*' to skip common prerelease tags.

+
--exit-code

Exit with status 1 if updates are available

--freeze

Store "frozen" hashes in rev instead of tag names

--help, -h

Display the concise help for this command

--include-tag pattern

Only consider tags matching this glob pattern. This option may be specified multiple times.

diff --git a/docs/diff.md b/docs/diff.md index 4cc001772..5fa433e75 100644 --- a/docs/diff.md +++ b/docs/diff.md @@ -54,7 +54,7 @@ For a compatibility-focused command mapping, see [Compatibility with pre-commit] - `prek auto-update` updates all projects in the workspace to their latest revisions. - `prek auto-update` checks updates for the same repository only once, speeding up the process in workspace mode. - `prek auto-update` supports `--dry-run` to preview the updates without applying them. -- `prek auto-update` supports `--check` to exit non-zero when updates are available or frozen-reference mismatches are found, without rewriting the config. +- `prek auto-update` supports `--exit-code` to exit non-zero when updates are available, and `--check` as an alias for `--dry-run --exit-code`. - `prek auto-update` validates pinned SHA revisions against fetched upstream refs, including impostor-commit detection, and keeps stale `# frozen:` comments in sync when it can. - `prek auto-update` supports the `--cooldown-days` option to skip releases newer than the specified number of days (based on the tag creation timestamp for annotated tags, or the tagged commit timestamp for lightweight tags). - `prek auto-update` supports `--exclude-repo` to skip selected repositories while updating everything else.