killer 2.0.1

A Rust security platform: static analysis, the .klr test language, a parallel test framework, project intelligence, code review, and a CI gate.
Documentation
//! CI/CD Guardian helpers.
//!
//! `killer ci` runs the full gate (scan + `.klr` tests + review) with a
//! non-zero exit on failure. `killer github enable` writes a ready-to-use
//! GitHub Actions workflow that runs that gate on every push and pull request.

use std::path::{Path, PathBuf};

use anyhow::{Context, Result};

/// The GitHub Actions workflow written by `killer github enable`.
pub const GITHUB_WORKFLOW: &str = r#"# Killer security gate - generated by `killer github enable`.
name: Killer

on:
  push:
    branches: [main, master]
  pull_request:

jobs:
  killer:
    name: Killer security gate
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Install Killer
        run: cargo install --git https://github.com/martin-k-m/killer --locked || cargo install killer --locked

      - name: Run Killer gate
        run: killer ci --base "origin/${{ github.base_ref || 'main' }}"
"#;

/// Path of the workflow file relative to a repo root.
pub const GITHUB_WORKFLOW_PATH: &str = ".github/workflows/killer.yml";

/// Write the GitHub Actions workflow into `root`. Refuses to overwrite unless
/// `force` is set. Returns the path written.
pub fn write_github_workflow(root: &Path, force: bool) -> Result<PathBuf> {
    let path = root.join(GITHUB_WORKFLOW_PATH);
    if path.exists() && !force {
        anyhow::bail!(
            "{} already exists (use --force to overwrite)",
            path.display()
        );
    }
    if let Some(parent) = path.parent() {
        std::fs::create_dir_all(parent)
            .with_context(|| format!("failed to create {}", parent.display()))?;
    }
    std::fs::write(&path, GITHUB_WORKFLOW)
        .with_context(|| format!("failed to write {}", path.display()))?;
    Ok(path)
}

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

    #[test]
    fn workflow_mentions_killer_ci() {
        assert!(GITHUB_WORKFLOW.contains("killer ci"));
        assert!(GITHUB_WORKFLOW.contains("on:"));
    }

    #[test]
    fn writes_and_guards_overwrite() {
        let dir = tempfile::tempdir().unwrap();
        let p = write_github_workflow(dir.path(), false).unwrap();
        assert!(p.exists());
        // Second write without force fails.
        assert!(write_github_workflow(dir.path(), false).is_err());
        // With force succeeds.
        assert!(write_github_workflow(dir.path(), true).is_ok());
    }
}