diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-08-18 11:32:47 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-08-18 11:32:47 +0800 |
| commit | 03b70e49bed33885cb51415dcbd657a93b1c9ffc (patch) | |
| tree | 5b37a945bff8025dc5131eaf8876b21b8ba7b82b /mingling_ci/src/cmd | |
| parent | 9113bf0fa1b60eaea4f4c3f9687b1c0e4f124715 (diff) | |
feat(ci-new): add git-lock and git-unlock CI commands
Add temporary commit and restore commands to stabilize the workspace
during CI runs, with safety checks and error handling.
Diffstat (limited to 'mingling_ci/src/cmd')
| -rw-r--r-- | mingling_ci/src/cmd/cmd_git_lock.rs | 60 | ||||
| -rw-r--r-- | mingling_ci/src/cmd/cmd_git_unlock.rs | 63 |
2 files changed, 123 insertions, 0 deletions
diff --git a/mingling_ci/src/cmd/cmd_git_lock.rs b/mingling_ci/src/cmd/cmd_git_lock.rs new file mode 100644 index 0000000..4398593 --- /dev/null +++ b/mingling_ci/src/cmd/cmd_git_lock.rs @@ -0,0 +1,60 @@ +use mingling::{ + Grouped, RenderResult, Routable, + macros::{buffer, command, r_println, renderer}, + res::ResExitCode, +}; + +use crate::Next; +use crate::git::{LOCK_FILE, TEMP_COMMIT_MESSAGE, run_git, worktree_clean}; +use crate::res::{CargoError, MessagePrinter}; + +/// Temporarily commits the workspace so CI can run on a stable tree. +/// +/// First pins the current HEAD to the `mingling/bkup` backup branch (created +/// or force-reset), then commits everything with a `[DO NOT PUSH] CI TEMP` +/// message. When the tree has no tracked changes, a `MINGLING-CI-CHECKING` +/// marker file is created first so the commit is never empty. +#[command(node = "git-lock")] +pub fn git_lock() -> Next { + if let Err(e) = run_git(["branch", "-f", "mingling/bkup", "HEAD"]) { + return ErrorGitLock(e).to_chain(); + } + + if worktree_clean() + && let Err(e) = std::fs::write(LOCK_FILE, "") + { + return ErrorGitLock(format!("failed to create {LOCK_FILE}: {e}")).to_chain(); + } + + if let Err(e) = run_git(["add", "."]) { + return ErrorGitLock(e).to_chain(); + } + if let Err(e) = run_git(["commit", "-m", TEMP_COMMIT_MESSAGE]) { + return ErrorGitLock(e).to_chain(); + } + + ResultGitLock {}.to_chain() +} + +#[derive(Grouped)] +pub struct ResultGitLock; + +#[derive(Grouped, Default)] +pub struct ErrorGitLock(pub String); + +#[renderer(buffer)] +pub fn render_git_lock(_: ResultGitLock) { + r_println!("Locked: CI temp commit created"); +} + +#[renderer] +pub fn render_error_git_lock( + e: ErrorGitLock, + error: &CargoError, + exit_code: &mut ResExitCode, +) -> RenderResult { + let render_result = RenderResult::new(); + error.println(vec![format!("Git-Lock: {}", e.0)]); + exit_code.exit_code = 1; + render_result +} diff --git a/mingling_ci/src/cmd/cmd_git_unlock.rs b/mingling_ci/src/cmd/cmd_git_unlock.rs new file mode 100644 index 0000000..dadc318 --- /dev/null +++ b/mingling_ci/src/cmd/cmd_git_unlock.rs @@ -0,0 +1,63 @@ +use mingling::{ + Grouped, RenderResult, Routable, + macros::{buffer, command, r_println, renderer}, + res::ResExitCode, +}; + +use crate::Next; +use crate::git::{LOCK_FILE, TEMP_COMMIT_MARK, head_message, run_git}; +use crate::res::{CargoError, MessagePrinter}; + +/// Undoes a CI temporary commit created by [`crate::cmd::cmd_git_lock`]. +/// +/// Only acts when the HEAD commit message contains `CI TEMP` (case-sensitive), +/// which together with the `MINGLING-CI-CHECKING` marker means the workspace +/// is in a CI phase and all uncommitted state may be discarded. Restores the +/// tree in five steps: unstage, restore tracked files, delete untracked files, +/// roll back the temporary commit, and remove the marker file. +#[command(node = "git-unlock")] +pub fn git_unlock() -> 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(); + } + + if let Err(e) = undo_ci_phase() { + return ErrorGitUnlock(e).to_chain(); + } + + ResultGitUnlock {}.to_chain() +} + +/// The five-step restoration sequence of `git-unlock`. +fn undo_ci_phase() -> Result<(), String> { + run_git(["reset"])?; + run_git(["restore", "."])?; + run_git(["clean", "-f", "-d"])?; + run_git(["reset", "--hard", "HEAD~1"])?; + std::fs::remove_file(LOCK_FILE).ok(); + Ok(()) +} + +#[derive(Grouped)] +pub struct ResultGitUnlock; + +#[derive(Grouped, Default)] +pub struct ErrorGitUnlock(pub String); + +#[renderer(buffer)] +pub fn render_git_unlock(_: ResultGitUnlock) { + r_println!("Unlocked: workspace restored"); +} + +#[renderer] +pub fn render_error_git_unlock( + e: ErrorGitUnlock, + error: &CargoError, + exit_code: &mut ResExitCode, +) -> RenderResult { + let render_result = RenderResult::new(); + error.println(vec![format!("Git-Unlock: {}", e.0)]); + exit_code.exit_code = 1; + render_result +} |
