quorum-cli 0.3.0

Quorum CLI: the quorum binary.
//! Embedded shell templates for `quorum install --hook=<type>`.
//!
//! Both templates are POSIX sh (Bourne-compatible). The first 5 lines
//! contain the literal substring `# quorum-managed-hook` so the
//! idempotency scanner in [`super`] recognizes them on re-install.
//!
//! The templates handle `QUORUM_SKIP`, `command -v quorum` fail-open,
//! and `QUORUM_HOOK_POLICY` (`fail-open` default, `fail-closed`, `warn`)
//! per v1.0 §4.5.2 / §4.5.3. The actual review pipeline is invoked via
//! `quorum review --hook-mode=<type>`.

/// Marker scanned by [`super::is_managed`] over the first 5 lines.
pub const HOOK_MARKER: &str = "# quorum-managed-hook";

/// Pre-commit template. Reviews the staged diff (Phase 1A path) via
/// `DiffSource::StagedIndex`. Bypass message references `git commit`.
pub const PRE_COMMIT_TEMPLATE: &str = r##"#!/bin/sh
# quorum-managed-hook v1 (pre-commit)
# Generated by `quorum install --hook=pre-commit`. Modifying this file
# manually means subsequent `quorum install` invocations refuse to overwrite.
# Bypass for one commit: env QUORUM_SKIP=1 git commit ...

if [ "${QUORUM_SKIP:-0}" != "0" ]; then
    echo "quorum: skipped (QUORUM_SKIP set)" >&2
    exit 0
fi

if ! command -v quorum >/dev/null 2>&1; then
    echo "quorum: binary not on PATH; skipping" >&2
    exit 0
fi

quorum review --hook-mode=pre-commit
HOOK_EXIT=$?

# Default fail-open: only exit 1 (high-severity findings) blocks.
# QUORUM_HOOK_POLICY=fail-closed blocks on auth (exit 3) and tooling (exit 2).
# QUORUM_HOOK_POLICY=warn never blocks regardless of exit code.
if [ "${QUORUM_HOOK_POLICY:-fail-open}" = "warn" ]; then
    exit 0
fi

if [ "$HOOK_EXIT" -eq 1 ]; then
    echo "" >&2
    echo "quorum: high-severity findings present; commit blocked." >&2
    echo "       run \`quorum review --tui\` to dismiss or \`quorum review\` for full output." >&2
    echo "       to bypass once: QUORUM_SKIP=1 git commit" >&2
    exit 1
fi

if [ "${QUORUM_HOOK_POLICY:-fail-open}" = "fail-closed" ]; then
    if [ "$HOOK_EXIT" -eq 2 ] || [ "$HOOK_EXIT" -eq 3 ]; then
        echo "quorum: tooling or auth failure (exit $HOOK_EXIT); commit blocked (fail-closed)." >&2
        exit 1
    fi
fi

exit 0
"##;

/// Pre-push template. Forwards Git's stdin ref tuples to
/// `quorum review --hook-mode=pre-push`; the binary parses each tuple
/// and computes a commit-range diff. Bypass message references
/// `git push`.
pub const PRE_PUSH_TEMPLATE: &str = r##"#!/bin/sh
# quorum-managed-hook v1 (pre-push)
# Generated by `quorum install --hook=pre-push`. Modifying this file
# manually means subsequent `quorum install` invocations refuse to overwrite.
# Bypass for one push: env QUORUM_SKIP=1 git push ...
#
# Git provides each ref being pushed on stdin as:
#     <local-ref> <local-sha> <remote-ref> <remote-sha>
# This hook forwards stdin to `quorum review --hook-mode=pre-push`, which
# computes the commit range per ref and diffs accordingly.

if [ "${QUORUM_SKIP:-0}" != "0" ]; then
    echo "quorum: skipped (QUORUM_SKIP set)" >&2
    exit 0
fi

if ! command -v quorum >/dev/null 2>&1; then
    echo "quorum: binary not on PATH; skipping" >&2
    exit 0
fi

quorum review --hook-mode=pre-push
HOOK_EXIT=$?

if [ "${QUORUM_HOOK_POLICY:-fail-open}" = "warn" ]; then
    exit 0
fi

if [ "$HOOK_EXIT" -eq 1 ]; then
    echo "" >&2
    echo "quorum: high-severity findings present; push blocked." >&2
    echo "       run \`quorum review --tui\` to dismiss or \`quorum review\` for full output." >&2
    echo "       to bypass once: QUORUM_SKIP=1 git push" >&2
    exit 1
fi

if [ "${QUORUM_HOOK_POLICY:-fail-open}" = "fail-closed" ]; then
    if [ "$HOOK_EXIT" -eq 2 ] || [ "$HOOK_EXIT" -eq 3 ]; then
        echo "quorum: tooling or auth failure (exit $HOOK_EXIT); push blocked (fail-closed)." >&2
        exit 1
    fi
fi

exit 0
"##;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn marker_present_in_first_five_lines_of_both_templates() {
        for (name, body) in [
            ("pre-commit", PRE_COMMIT_TEMPLATE),
            ("pre-push", PRE_PUSH_TEMPLATE),
        ] {
            let first_five = body.lines().take(5).collect::<Vec<_>>().join("\n");
            assert!(
                first_five.contains(HOOK_MARKER),
                "{name} template must contain {HOOK_MARKER:?} in its first 5 lines"
            );
        }
    }

    #[test]
    fn templates_start_with_posix_shebang() {
        for (name, body) in [
            ("pre-commit", PRE_COMMIT_TEMPLATE),
            ("pre-push", PRE_PUSH_TEMPLATE),
        ] {
            assert!(
                body.starts_with("#!/bin/sh\n"),
                "{name} template must start with #!/bin/sh"
            );
        }
    }

    #[test]
    fn pre_commit_template_references_git_commit_bypass() {
        assert!(PRE_COMMIT_TEMPLATE.contains("QUORUM_SKIP=1 git commit"));
        assert!(PRE_COMMIT_TEMPLATE.contains("commit blocked"));
    }

    #[test]
    fn pre_push_template_references_git_push_bypass() {
        assert!(PRE_PUSH_TEMPLATE.contains("QUORUM_SKIP=1 git push"));
        assert!(PRE_PUSH_TEMPLATE.contains("push blocked"));
    }

    #[test]
    fn templates_invoke_correct_hook_mode() {
        assert!(PRE_COMMIT_TEMPLATE.contains("quorum review --hook-mode=pre-commit"));
        assert!(PRE_PUSH_TEMPLATE.contains("quorum review --hook-mode=pre-push"));
    }

    #[test]
    fn templates_honor_quorum_hook_policy() {
        for body in [PRE_COMMIT_TEMPLATE, PRE_PUSH_TEMPLATE] {
            assert!(body.contains("QUORUM_HOOK_POLICY"));
            assert!(body.contains("warn"));
            assert!(body.contains("fail-closed"));
            assert!(body.contains("fail-open"));
        }
    }

    #[test]
    fn templates_handle_quorum_skip_and_missing_binary() {
        for body in [PRE_COMMIT_TEMPLATE, PRE_PUSH_TEMPLATE] {
            assert!(body.contains("QUORUM_SKIP"));
            assert!(body.contains("command -v quorum"));
            assert!(body.contains("binary not on PATH"));
        }
    }
}