diff options
| -rw-r--r-- | .github/workflows/ci-check-only.yml | 2 | ||||
| -rw-r--r-- | .github/workflows/ci.yml | 2 | ||||
| -rw-r--r-- | .run/src/bin/ci.py | 37 | ||||
| -rw-r--r-- | mingling_ci/src/cmd/cmd_git_unlock.rs | 29 |
4 files changed, 46 insertions, 24 deletions
diff --git a/.github/workflows/ci-check-only.yml b/.github/workflows/ci-check-only.yml index dc97c81..548026d 100644 --- a/.github/workflows/ci-check-only.yml +++ b/.github/workflows/ci-check-only.yml @@ -61,7 +61,7 @@ jobs: - name: Unlock workspace (idempotency check) if: always() - run: cargo ci git-unlock + run: cargo ci git-unlock --show-diff - name: Fail when the check failed if: steps.check.outcome == 'failure' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4892c3..56667d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: - name: Unlock workspace (idempotency check) if: always() - run: cargo ci git-unlock + run: cargo ci git-unlock --show-diff - name: Fail when the check failed if: steps.check.outcome == 'failure' diff --git a/.run/src/bin/ci.py b/.run/src/bin/ci.py index ced6566..6234a19 100644 --- a/.run/src/bin/ci.py +++ b/.run/src/bin/ci.py @@ -14,20 +14,21 @@ import subprocess import sys from pathlib import Path -# The pipeline steps, in execution order. -STEPS = [ - "git-lock", - "report-clean", - "build-check", - "clippy-check", - "test-all", - "example-check", - "docs-check", - "example-refresh", - "docsify-refresh", - "features-refresh", - # Idempotency check: exits non-zero if CI contaminated the workspace. - "git-unlock", +# The pipeline steps, in execution order, as (command, args) pairs. +STEPS: list[tuple[str, list[str]]] = [ + ("git-lock", []), + ("report-clean", []), + ("build-check", []), + ("clippy-check", []), + ("test-all", []), + ("example-check", []), + ("docs-check", []), + ("example-refresh", []), + ("docsify-refresh", []), + ("features-refresh", []), + # Idempotency check: exits non-zero if CI contaminated the workspace, and + # prints the diff of the contamination before restoring. + ("git-unlock", ["--show-diff"]), ] @@ -57,12 +58,12 @@ def main() -> int: except OSError: pass - for step in STEPS: - print(f"==> cargo ci {step}") - result = subprocess.run(["cargo", "ci", step], check=False) + for command, args in STEPS: + print(f"==> cargo ci {' '.join([command, *args])}") + result = subprocess.run(["cargo", "ci", command, *args], check=False) if result.returncode != 0: print( - f"error: step `{step}` failed with exit code {result.returncode}", + f"error: step `{command}` failed with exit code {result.returncode}", file=sys.stderr, ) return result.returncode diff --git a/mingling_ci/src/cmd/cmd_git_unlock.rs b/mingling_ci/src/cmd/cmd_git_unlock.rs index 6d7389d..41efefc 100644 --- a/mingling_ci/src/cmd/cmd_git_unlock.rs +++ b/mingling_ci/src/cmd/cmd_git_unlock.rs @@ -1,12 +1,13 @@ use mingling::{ Grouped, RenderResult, Routable, - macros::{buffer, command, r_println, renderer}, + macros::{arg, buffer, command, r_println, renderer}, + picker::{EntryPicker, value::Flag}, res::ResExitCode, }; -use crate::Next; use crate::git::{LOCK_FILE, TEMP_COMMIT_MARK, head_message, run_git, worktree_clean}; use crate::res::{CargoError, MessagePrinter}; +use crate::{Entry, Next}; /// Undoes a CI temporary commit created by [`crate::cmd::cmd_git_lock`]. /// @@ -21,9 +22,13 @@ use crate::res::{CargoError, MessagePrinter}; /// /// When the working tree is dirty (e.g. CI left tracked changes behind) the /// restore still runs, but the command reports a non-zero exit code so the -/// caller knows the CI phase contaminated the repository. +/// caller knows the CI phase contaminated the repository. With `--show-diff` +/// the diff of those changes is printed before they are discarded. #[command(node = "git-unlock")] -pub fn git_unlock() -> Next { +// `#[command]` rewrites an owned first param into the entry type, so the args +// must be passed by value even though the body only reads them. +#[allow(clippy::needless_pass_by_value)] +pub fn git_unlock(args: Entry) -> Next { let head = head_message().unwrap_or_default(); if !head.contains(TEMP_COMMIT_MARK) { return ErrorGitUnlock(format!("HEAD is not a CI temporary commit: `{head}`")).to_chain(); @@ -37,6 +42,10 @@ pub fn git_unlock() -> Next { let based_on_dirty = std::fs::read_to_string(LOCK_FILE).is_ok_and(|content| content.trim() == "true"); + if dirty && *args.pick(&arg![show_diff: Flag]).unwrap() { + show_diff(); + } + if let Err(e) = undo_ci_phase(based_on_dirty) { return ErrorGitUnlock(e).to_chain(); } @@ -44,6 +53,18 @@ pub fn git_unlock() -> Next { ResultGitUnlock { dirty }.to_chain() } +/// Prints the tracked changes the CI run left behind, before the restore +/// discards them. Untracked files are not shown (they are removed by clean). +fn show_diff() { + let Ok(diff) = run_git(["diff", "HEAD"]) else { + return; + }; + if diff.is_empty() { + return; + } + println!("{diff}"); +} + /// Restores the workspace, keeping the user's pre-lock changes. /// /// With a base `TEMP` commit (`true`) the marker commit is dropped by a hard |
