diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..37da42e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +name: ci + +on: + workflow_dispatch: + schedule: + - cron: "0 0 * * 1" + pull_request: + push: + branches: + - main + +jobs: + rust: + name: rust + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - name: Check formatting + run: cargo fmt --check + - name: Run clippy + run: cargo clippy --locked --all-targets -- -D warnings + - name: Run tests + run: cargo test --locked + + executable-size: + name: executable-size-${{ matrix.name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - name: linux + os: ubuntu-latest + exe: target/release/git-zcrypt + max_size: 850000 + - name: macos + os: macos-latest + exe: target/release/git-zcrypt + max_size: 750000 + - name: windows + os: windows-latest + exe: target/release/git-zcrypt.exe + max_size: 550000 + steps: + - uses: actions/checkout@v7 + - name: Build release executable + run: cargo build --release --locked + - name: Check executable size + env: + EXE: ${{ matrix.exe }} + MAX_SIZE: ${{ matrix.max_size }} + run: | + ruby -e 'path = ENV.fetch("EXE"); max = Integer(ENV.fetch("MAX_SIZE")); size = File.size(path); puts "#{path}: #{size} bytes (limit #{max})"; exit(size <= max ? 0 : 1)' diff --git a/codex-notes/auto-check-executable-sizes/feature_list.json b/codex-notes/auto-check-executable-sizes/feature_list.json new file mode 100644 index 0000000..0f1b6e6 --- /dev/null +++ b/codex-notes/auto-check-executable-sizes/feature_list.json @@ -0,0 +1,28 @@ +{ + "features": [ + { + "id": "add-rust-ci", + "description": "Add a GitHub Actions workflow that runs Rust formatting, clippy, and tests.", + "validation": "rg -q 'actions/checkout@v7' .github/workflows/ci.yml && rg -q 'cargo fmt --check' .github/workflows/ci.yml && rg -q 'cargo clippy --locked --all-targets -- -D warnings' .github/workflows/ci.yml && rg -q 'cargo test --locked' .github/workflows/ci.yml", + "passes": true + }, + { + "id": "add-scheduled-and-manual-triggers", + "description": "Configure CI to run weekly and on manual GitHub Actions dispatch.", + "validation": "rg -q 'workflow_dispatch:' .github/workflows/ci.yml && rg -q 'schedule:' .github/workflows/ci.yml && rg -q 'cron: \"0 0 \\* \\* 1\"' .github/workflows/ci.yml", + "passes": true + }, + { + "id": "add-size-gate", + "description": "Add a release executable-size matrix for Linux, macOS, and Windows with platform-specific thresholds.", + "validation": "rg -q 'cargo build --release --locked' .github/workflows/ci.yml && rg -q 'File.size' .github/workflows/ci.yml && rg -q 'max_size: 850000' .github/workflows/ci.yml && rg -q 'max_size: 750000' .github/workflows/ci.yml && rg -q 'max_size: 550000' .github/workflows/ci.yml", + "passes": true + }, + { + "id": "update-progress", + "description": "Replace the placeholder auto-check-executable-sizes progress validation with checks for the CI workflow.", + "validation": "rg -q 'auto-check-executable-sizes' progress.toml && ! rg -q 'validation = \"false\"' progress.toml", + "passes": true + } + ] +} diff --git a/codex-notes/auto-check-executable-sizes/plan.md b/codex-notes/auto-check-executable-sizes/plan.md new file mode 100644 index 0000000..e4c3730 --- /dev/null +++ b/codex-notes/auto-check-executable-sizes/plan.md @@ -0,0 +1,52 @@ +# Plan: auto-check-executable-sizes + +## Overview + +Add a GitHub Actions CI workflow with standard Rust checks and a release executable-size gate for Linux, macOS, and Windows. + +## Files To Change + +- `.github/workflows/ci.yml` +- `progress.toml` +- `codex-notes/auto-check-executable-sizes/research.md` +- `codex-notes/auto-check-executable-sizes/plan.md` +- `codex-notes/auto-check-executable-sizes/feature_list.json` + +## Implementation Steps + +1. Create a workflow triggered by pull requests, pushes to `main`, manual `workflow_dispatch`, and a weekly schedule. +2. Add a `rust` job on `ubuntu-latest` that runs: + - `cargo fmt --check` + - `cargo clippy --locked --all-targets -- -D warnings` + - `cargo test --locked` +3. Add an `executable-size` job with a matrix for `ubuntu-latest`, `macos-latest`, and `windows-latest`. +4. Use `actions/checkout@v7`, the latest verified stable tag, which is allowed by the GitHub Actions guardrail. +5. Build each platform executable with `cargo build --release --locked`. +6. Measure executable byte size with Ruby's `File.size`, available on GitHub-hosted runners and portable across the matrix. +7. Set thresholds from GitHub Actions run `28735183007`, with modest headroom over measured sizes: + - Linux: measured `768480`, threshold `850000` + - macOS: measured `676032`, threshold `750000` + - Windows: measured `476672`, threshold `550000` +8. Update `progress.toml` validation to check for the workflow, general Rust CI commands, platform thresholds, portable byte-size measurement, and locked release build. +9. Validate the workflow includes the manual trigger and weekly Monday 00:00 UTC cron schedule. + +## Alternatives Considered + +- Shell `stat`: rejected because GNU and BSD flags differ. +- Bash `wc -c < file`: plausible, but more fragile on Windows than a Ruby one-liner. +- A separate executable-size workflow: rejected after user clarified that general Rust CI checks should be included too. +- Third-party Rust toolchain actions: unnecessary because GitHub-hosted runners already include Rust here, and avoiding third-party actions keeps workflow pinning simple. +- Older `actions/checkout` tags: rejected because workflow dependencies should use the latest compatible tag unless the user requests otherwise. + +## Risks + +- Hosted runner toolchain updates can change binary size. The selected thresholds include modest headroom above measured GitHub Actions sizes while still catching large regressions. + +## Test Strategy + +- Run `cargo fmt --check`. +- Run `cargo clippy --locked --all-targets -- -D warnings`. +- Run `cargo test --locked`. +- Run `cargo build --release --locked`. +- Compare thresholds to GitHub Actions run `28735183007` executable sizes. +- Run the updated `progress.toml` validation command. diff --git a/codex-notes/auto-check-executable-sizes/research.md b/codex-notes/auto-check-executable-sizes/research.md new file mode 100644 index 0000000..ddc24a8 --- /dev/null +++ b/codex-notes/auto-check-executable-sizes/research.md @@ -0,0 +1,42 @@ +# Research: auto-check-executable-sizes + +## Relevant Files + +- `progress.toml`: tracks task completion. `auto-check-executable-sizes` currently has `validation = "false"` and `passes = false`. +- `Cargo.toml`: defines package name `git-zcrypt` and release profile optimized for size with `opt-level = "z"`, `lto = "fat"`, and `codegen-units = 1`. +- `.github/workflows/`: absent before this task, so no existing CI conventions need to be preserved. +- `README.md`: install instructions already use `cargo build --release`; no size policy is documented there. + +## Execution Flow + +- General Rust CI should check formatting, lints, and tests. +- Release executable size CI should build the binary on each target platform. +- CI should also be available by manual `workflow_dispatch` and should run periodically once per week. +- After `cargo build --release --locked`, the workflow can read the built executable byte size from `target/release/git-zcrypt` or `target/release/git-zcrypt.exe`. +- The job fails when the measured byte size is above a platform-specific threshold. + +## Data And Invariants + +- The package executable name is `git-zcrypt`. +- Windows uses the `.exe` suffix; Linux and macOS do not. +- Thresholds should be plain byte counts and not comma-separated. +- Thresholds should be based on observed GitHub Actions runner sizes once those measurements are available. +- The check should print the measured size for diagnostics. + +## Existing Patterns + +- Existing task validation commands in `progress.toml` use `rg` checks and concrete command-line validation. +- GitHub Actions security rules allow `actions/*` by tag. Use the latest compatible `actions/*` tag after verification. Third-party actions must be pinned, so the workflow should avoid third-party actions. + +## Pitfalls + +- GNU `stat -c%s` is not portable to macOS; BSD `stat -f%z` is not portable to Linux. +- Windows shell differences can make PowerShell/Bash-specific syntax fragile. +- A threshold too close to the current executable size can create CI noise from toolchain variation. + +## Constraints + +- Use GitHub Actions. +- Include general Rust CI checks. +- Decide executable-size thresholds for each platform. +- Keep the PR scoped to this single task. diff --git a/progress.toml b/progress.toml index 1e8b2f9..c0ed7c3 100644 --- a/progress.toml +++ b/progress.toml @@ -37,5 +37,5 @@ passes = true [[items]] id = "auto-check-executable-sizes" description = "executable sizes matter. Use GitHub Actions to auto-check. You must decide the threshold for each platform" -validation = "false" -passes = false +validation = "rg -q '^name: ci$' .github/workflows/ci.yml && rg -q 'workflow_dispatch:' .github/workflows/ci.yml && rg -q 'schedule:' .github/workflows/ci.yml && rg -q 'cron: \"0 0 \\* \\* 1\"' .github/workflows/ci.yml && rg -q 'pull_request:' .github/workflows/ci.yml && rg -q 'actions/checkout@v7' .github/workflows/ci.yml && rg -q 'cargo fmt --check' .github/workflows/ci.yml && rg -q 'cargo clippy --locked --all-targets -- -D warnings' .github/workflows/ci.yml && rg -q 'cargo test --locked' .github/workflows/ci.yml && rg -q 'cargo build --release --locked' .github/workflows/ci.yml && rg -q 'File.size' .github/workflows/ci.yml && rg -q 'max_size: 850000' .github/workflows/ci.yml && rg -q 'max_size: 750000' .github/workflows/ci.yml && rg -q 'max_size: 550000' .github/workflows/ci.yml && rg -q 'codex-notes/auto-check-executable-sizes/plan.md' codex-notes/auto-check-executable-sizes/plan.md" +passes = true