From 5dd2a15e77dc59f7a4739105f819582308fccd6d Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Wed, 28 Jan 2026 13:19:30 +0800 Subject: [PATCH 01/16] task(1): Create git-cliff changelog configuration --- cliff.toml | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 cliff.toml diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 0000000..f39c2a8 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,47 @@ +# git-cliff configuration for changelog generation +# https://git-cliff.org/docs/configuration + +[changelog] +header = """ +# Changelog + +All notable changes to this project will be documented in this file. + +""" +body = """ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {% for commit in commits %} + - {% if commit.scope %}**{{ commit.scope }}:** {% endif %}\ + {{ commit.message | upper_first }}\ + {% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\ + {% endfor %} +{% endfor %}\n +""" +footer = """ +""" +trim = true + +[git] +conventional_commits = true +filter_unconventional = true +split_commits = false +commit_preprocessors = [] +commit_parsers = [ + { message = "^feat", group = "Features" }, + { message = "^fix", group = "Bug Fixes" }, + { message = "^doc", group = "Documentation" }, + { message = "^perf", group = "Performance" }, + { message = "^refactor", group = "Refactor" }, + { message = "^style", group = "Styling" }, + { message = "^test", group = "Testing" }, + { message = "^build", group = "Build" }, + { message = "^ci", group = "CI" }, + { message = "^chore\\(release\\)", skip = true }, + { message = "^chore\\(deps\\)", skip = true }, + { message = "^chore", group = "Miscellaneous" }, +] +protect_breaking_commits = false +filter_commits = false +topo_order = false +sort_commits = "oldest" From e9b77c4a594acd3cf2215f1dd3a6e4bde884ff9d Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Wed, 28 Jan 2026 13:19:48 +0800 Subject: [PATCH 02/16] task(2): Create release workflow with version validation --- .github/workflows/release.yaml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..3abce92 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,33 @@ +name: Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g., 0.2.0 - no v prefix)' + required: true + type: string + +# Restrict permissions to minimum required +permissions: + contents: write + +env: + CARGO_TERM_COLOR: always + +jobs: + validate: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.validate.outputs.version }} + steps: + - name: Validate version format + id: validate + run: | + VERSION="${{ inputs.version }}" + if ! echo "$VERSION" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then + echo "::error::Version must be in format X.Y.Z or X.Y.Z-suffix (e.g., 0.2.0, 1.0.0-alpha.1)" + exit 1 + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Valid version: $VERSION" From bfc5c46063dac32bd80bcc76e633b0edec34781d Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Wed, 28 Jan 2026 13:20:08 +0800 Subject: [PATCH 03/16] task(3): Add security checks to release workflow --- .github/workflows/release.yaml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 3abce92..b6bc43f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -31,3 +31,33 @@ jobs: fi echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Valid version: $VERSION" + + # Security gate: must pass before release proceeds + security: + runs-on: ubuntu-latest + needs: validate + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-security-${{ hashFiles('**/Cargo.lock') }} + + - name: Install security tools + run: | + cargo install cargo-audit --locked + cargo install cargo-deny --locked + + - name: Run cargo audit + run: cargo audit --deny warnings + + - name: Run cargo deny + run: cargo deny check From 370b965abdbda3a5caffade7160968f30697d2af Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Wed, 28 Jan 2026 13:20:32 +0800 Subject: [PATCH 04/16] task(4): Add version bump and changelog generation steps --- .github/workflows/release.yaml | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b6bc43f..cd371b8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -61,3 +61,52 @@ jobs: - name: Run cargo deny run: cargo deny check + + # Prepare release: bump version, generate changelog, create tag + prepare: + runs-on: ubuntu-latest + needs: security + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ~/.cargo/bin + key: ${{ runner.os }}-cargo-release-tools-v1 + + - name: Install release tools + run: | + # Install cargo-edit for version bumping + cargo install cargo-edit --locked + # Install git-cliff for changelog generation + cargo install git-cliff --locked + + - name: Bump version in Cargo.toml + run: | + cargo set-version ${{ needs.validate.outputs.version }} + cargo update -p sx + + - name: Generate changelog + run: | + git-cliff --tag v${{ needs.validate.outputs.version }} --output CHANGELOG.md + + - name: Commit version bump and changelog + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Cargo.toml Cargo.lock CHANGELOG.md + git commit -m "chore(release): v${{ needs.validate.outputs.version }}" + git tag -a "v${{ needs.validate.outputs.version }}" -m "Release v${{ needs.validate.outputs.version }}" + git push origin main + git push origin "v${{ needs.validate.outputs.version }}" From 8d595895016046375094edf8503a9a8ee3c6d65b Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Wed, 28 Jan 2026 13:20:58 +0800 Subject: [PATCH 05/16] task(5): Add release binary build steps for both architectures --- .github/workflows/release.yaml | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index cd371b8..4940177 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -110,3 +110,50 @@ jobs: git tag -a "v${{ needs.validate.outputs.version }}" -m "Release v${{ needs.validate.outputs.version }}" git push origin main git push origin "v${{ needs.validate.outputs.version }}" + + # Build release binaries for macOS (Intel and Apple Silicon) + build: + runs-on: macos-latest + needs: [validate, prepare] + strategy: + matrix: + target: + - x86_64-apple-darwin + - aarch64-apple-darwin + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: v${{ needs.validate.outputs.version }} + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Build release binary + run: cargo build --release --target ${{ matrix.target }} + + - name: Create archive + run: | + mkdir -p dist + cp target/${{ matrix.target }}/release/sx dist/ + cd dist + tar czf sx-${{ needs.validate.outputs.version }}-${{ matrix.target }}.tar.gz sx + shasum -a 256 sx-${{ needs.validate.outputs.version }}-${{ matrix.target }}.tar.gz > sx-${{ needs.validate.outputs.version }}-${{ matrix.target }}.tar.gz.sha256 + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: sx-${{ matrix.target }} + path: dist/*.tar.gz* + retention-days: 1 From b4e6ba7d4ac5776a854f0c92b0810081775a2cc2 Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Wed, 28 Jan 2026 13:21:20 +0800 Subject: [PATCH 06/16] task(6): Add GitHub release creation with artifact upload --- .github/workflows/release.yaml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4940177..cb55c3b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -157,3 +157,36 @@ jobs: name: sx-${{ matrix.target }} path: dist/*.tar.gz* retention-days: 1 + + # Create GitHub release with all artifacts + release: + runs-on: ubuntu-latest + needs: [validate, build] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: v${{ needs.validate.outputs.version }} + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + merge-multiple: true + + - name: List artifacts + run: ls -la artifacts/ + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ needs.validate.outputs.version }} + name: v${{ needs.validate.outputs.version }} + body_path: CHANGELOG.md + draft: false + prerelease: ${{ contains(needs.validate.outputs.version, '-') }} + files: | + artifacts/*.tar.gz + artifacts/*.sha256 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 2a8218aed14dd2cd893e5c959a96803992eef486 Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Wed, 28 Jan 2026 13:21:48 +0800 Subject: [PATCH 07/16] task(7): Add Homebrew tap update step with SHA256 calculation --- .github/workflows/release.yaml | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index cb55c3b..08e9c74 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -190,3 +190,38 @@ jobs: artifacts/*.sha256 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Update Homebrew tap with new version + update-tap: + runs-on: ubuntu-latest + needs: [validate, release] + steps: + - name: Calculate source tarball SHA256 + id: sha256 + run: | + # Wait for GitHub to finalize the release tarball + sleep 10 + # Download and calculate SHA256 of the source tarball + curl -sL "https://github.com/${{ github.repository }}/archive/refs/tags/v${{ needs.validate.outputs.version }}.tar.gz" -o source.tar.gz + SHA256=$(shasum -a 256 source.tar.gz | awk '{print $1}') + echo "sha256=$SHA256" >> $GITHUB_OUTPUT + echo "Source tarball SHA256: $SHA256" + + - name: Update Homebrew tap + env: + TAP_REPO_TOKEN: ${{ secrets.TAP_REPO_TOKEN }} + run: | + # Clone the tap repository + git clone "https://x-access-token:${TAP_REPO_TOKEN}@github.com/agentic-dev3o/homebrew-sx.git" tap + cd tap + + # Update the formula with new version and SHA256 + sed -i "s|url \"https://github.com/agentic-dev3o/sx/archive/refs/tags/v[^\"]*\.tar\.gz\"|url \"https://github.com/agentic-dev3o/sx/archive/refs/tags/v${{ needs.validate.outputs.version }}.tar.gz\"|" Formula/sx.rb + sed -i "s|sha256 \"[a-f0-9]*\"|sha256 \"${{ steps.sha256.outputs.sha256 }}\"|" Formula/sx.rb + + # Commit and push + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Formula/sx.rb + git diff --cached --quiet || git commit -m "sx ${{ needs.validate.outputs.version }}" + git push From e0f823139a19070fce05723d711249f94bcc65f6 Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Wed, 28 Jan 2026 13:24:43 +0800 Subject: [PATCH 08/16] task(8): Validate workflow with actionlint --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 08e9c74..a53b4f7 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -65,7 +65,7 @@ jobs: # Prepare release: bump version, generate changelog, create tag prepare: runs-on: ubuntu-latest - needs: security + needs: [validate, security] steps: - name: Checkout uses: actions/checkout@v4 From c9b63d4d2b0ee46f3607af5d412b3dd2eb3cf484 Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Wed, 28 Jan 2026 13:25:14 +0800 Subject: [PATCH 09/16] review: final validation and fixes --- .gitignore | 1 + docs/homebrew-tap-specification.md | 654 +++++++++++++++++++++++++++++ 2 files changed, 655 insertions(+) create mode 100644 docs/homebrew-tap-specification.md diff --git a/.gitignore b/.gitignore index dfa83ca..c001316 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ target/ # MSVC Windows builds of rustc generate these, which store debugging information *.pdb +actionlint diff --git a/docs/homebrew-tap-specification.md b/docs/homebrew-tap-specification.md new file mode 100644 index 0000000..e4eea5e --- /dev/null +++ b/docs/homebrew-tap-specification.md @@ -0,0 +1,654 @@ +# Homebrew Tap Repository Specification + +**Project:** `homebrew-sx` +**Purpose:** Homebrew formula distribution for the `sx` (sandbox-shell) CLI tool +**Target Platform:** macOS only +**Status:** To Be Created + +--- + +## Table of Contents + +1. [Overview](#1-overview) +2. [Repository Structure](#2-repository-structure) +3. [Initial Setup](#3-initial-setup) +4. [Formula Specification](#4-formula-specification) +5. [CI/CD Configuration](#5-cicd-configuration) +6. [Automated Updates](#6-automated-updates) +7. [Testing Requirements](#7-testing-requirements) +8. [Maintenance Procedures](#8-maintenance-procedures) +9. [Security Considerations](#9-security-considerations) +10. [Troubleshooting](#10-troubleshooting) + +--- + +## 1. Overview + +### 1.1 Purpose + +The `homebrew-sx` repository hosts the Homebrew formula for distributing the `sx` CLI tool. This repository is automatically updated by the main `sx` repository's release workflow. + +### 1.2 User Installation Flow + +```bash +# First-time setup (adds tap) +brew tap agentic-dev3o/sx + +# Install sx +brew install sx + +# Future upgrades +brew upgrade sx +``` + +### 1.3 Dependencies + +| Dependency | Type | Purpose | +|------------|------|---------| +| Rust toolchain | Build-time | Compiles sx from source | +| macOS | Runtime | Required (Seatbelt is macOS-only) | + +### 1.4 Integration with Main Repository + +``` +┌────────────────────────────────┐ +│ sx repository │ +│ (main application) │ +│ │ +│ .github/workflows/release.yml │ +│ ├── Bumps version │ +│ ├── Creates GitHub release │ +│ ├── Calculates SHA256 │ +│ └── Updates homebrew-sx ─────┼──────┐ +└────────────────────────────────┘ │ + ▼ + ┌─────────────────────────────┐ + │ homebrew-sx repository │ + │ │ + │ Formula/sx.rb │ + │ ├── version: X.Y.Z │ + │ ├── sha256: │ + │ └── url: release tarball │ + └─────────────────────────────┘ +``` + +--- + +## 2. Repository Structure + +``` +homebrew-sx/ +├── README.md # Installation instructions +├── LICENSE # Same license as main sx repo +├── Formula/ +│ └── sx.rb # Main formula file +└── .github/ + └── workflows/ + └── audit.yml # Formula validation on PR/push +``` + +### 2.1 File Descriptions + +| File | Purpose | Update Frequency | +|------|---------|------------------| +| `README.md` | User-facing installation docs | Rarely | +| `LICENSE` | Legal terms (match main repo) | Never | +| `Formula/sx.rb` | Homebrew formula definition | Every release | +| `.github/workflows/audit.yml` | CI validation | Rarely | + +--- + +## 3. Initial Setup + +### 3.1 Create Repository + +1. Create new GitHub repository: `homebrew-sx` + - Visibility: **Public** (required for Homebrew taps) + - Initialize with README: **No** (we'll create our own) + - License: Match main `sx` repository + +2. Clone locally: + ```bash + git clone https://github.com/agentic-dev3o/homebrew-sx.git + cd homebrew-sx + ``` + +### 3.2 Create Directory Structure + +```bash +mkdir -p Formula .github/workflows +``` + +### 3.3 Create README.md + +```markdown +# Homebrew Tap for sx + +This is a [Homebrew](https://brew.sh) tap for [sx](https://github.com/agentic-dev3o/sx), a CLI that wraps shell sessions in macOS Seatbelt sandboxes. + +## Requirements + +- macOS (sx uses macOS Seatbelt, which is not available on Linux) +- Homebrew + +## Installation + +```bash +brew tap agentic-dev3o/sx +brew install sx +``` + +## Upgrading + +```bash +brew upgrade sx +``` + +## Uninstalling + +```bash +brew uninstall sx +brew untap agentic-dev3o/sx +``` + +## Troubleshooting + +### Build fails + +Ensure you have Xcode Command Line Tools installed: + +```bash +xcode-select --install +``` + +### Issues + +Report issues at: https://github.com/agentic-dev3o/sx/issues +``` + +### 3.4 Create LICENSE + +Copy the exact same license file from the main `sx` repository. + +### 3.5 Initial Commit + +```bash +git add . +git commit -m "Initial tap setup" +git push origin main +``` + +--- + +## 4. Formula Specification + +### 4.1 Formula File: `Formula/sx.rb` + +```ruby +# typed: false +# frozen_string_literal: true + +class Sx < Formula + desc "Sandbox shell sessions with macOS Seatbelt" + homepage "https://github.com/agentic-dev3o/sx" + url "https://github.com/agentic-dev3o/sx/archive/refs/tags/v0.1.0.tar.gz" + sha256 "PLACEHOLDER_SHA256_WILL_BE_UPDATED_BY_RELEASE_WORKFLOW" + license "MIT" + head "https://github.com/agentic-dev3o/sx.git", branch: "main" + + # macOS only - Seatbelt is not available on Linux + depends_on :macos + + # Rust toolchain for building from source + depends_on "rust" => :build + + def install + system "cargo", "install", *std_cargo_args + end + + def caveats + <<~EOS + sx restricts filesystem and network access using macOS Seatbelt. + + Quick start: + sx echo "sandboxed command" # Run single command + sx --profile rust cargo build # Use rust profile + sx --dry-run online node # Preview sandbox rules + + Initialize shell integration: + sx init bash >> ~/.bashrc + sx init zsh >> ~/.zshrc + + Configuration: + ~/.config/sx/config.toml # Global config + .sandbox.toml # Project config + + Documentation: https://github.com/agentic-dev3o/sx + EOS + end + + test do + # Verify binary runs and shows version + assert_match version.to_s, shell_output("#{bin}/sx --version") + + # Verify help output + assert_match "sandbox", shell_output("#{bin}/sx --help") + + # Verify dry-run works (doesn't require actual sandboxing) + output = shell_output("#{bin}/sx --dry-run echo test") + assert_match "file-read", output # Seatbelt profile contains file-read rules + end +end +``` + +### 4.2 Formula Fields Reference + +| Field | Description | Auto-Updated | +|-------|-------------|--------------| +| `desc` | Short description (< 80 chars) | No | +| `homepage` | Project homepage URL | No | +| `url` | Source tarball URL (GitHub release) | **Yes** | +| `sha256` | SHA256 checksum of tarball | **Yes** | +| `license` | SPDX license identifier | No | +| `head` | Git URL for `--HEAD` installs | No | +| `depends_on` | Build/runtime dependencies | No | +| `install` | Build instructions | No | +| `caveats` | Post-install message to user | Rarely | +| `test` | Verification commands | Rarely | + +### 4.3 Version Interpolation (Alternative) + +For cleaner updates, use version interpolation: + +```ruby +url "https://github.com/agentic-dev3o/sx/archive/refs/tags/v#{version}.tar.gz" +``` + +**Note:** This requires updating only the `version` line during releases, but the current sed-based update in the release workflow handles explicit URLs fine. + +--- + +## 5. CI/CD Configuration + +### 5.1 Formula Audit Workflow + +Create `.github/workflows/audit.yml`: + +```yaml +name: Audit Formula + +on: + push: + branches: [main] + paths: + - 'Formula/**' + pull_request: + branches: [main] + paths: + - 'Formula/**' + +jobs: + audit: + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Homebrew + uses: Homebrew/actions/setup-homebrew@master + + - name: Tap local repository + run: | + mkdir -p $(brew --repository)/Library/Taps/agentic-dev3o + ln -s $GITHUB_WORKSPACE $(brew --repository)/Library/Taps/agentic-dev3o/homebrew-sx + + - name: Run brew audit + run: brew audit --strict Formula/sx.rb + + - name: Run brew style + run: brew style Formula/sx.rb + + - name: Test formula installation + run: | + brew install --build-from-source Formula/sx.rb + brew test sx +``` + +### 5.2 Workflow Triggers + +| Trigger | Condition | Actions | +|---------|-----------|---------| +| Push to main | Formula files changed | Audit + Style check | +| Pull request | Formula files changed | Audit + Style check + Build test | +| Release workflow (from sx repo) | New release | Direct commit (bypasses PR) | + +--- + +## 6. Automated Updates + +### 6.1 How Updates Work + +The main `sx` repository's release workflow updates this tap: + +1. Release workflow calculates SHA256 of source tarball +2. Clones `homebrew-sx` using `TAP_REPO_TOKEN` +3. Updates `url` and `sha256` in `Formula/sx.rb` via sed +4. Commits and pushes changes + +### 6.2 Update Script (in sx repo release workflow) + +```bash +# Clone tap repo +git clone "https://x-access-token:$TAP_REPO_TOKEN@github.com/agentic-dev3o/homebrew-sx.git" tap +cd tap + +# Update formula +sed -i '' "s|url \".*\"|url \"https://github.com/agentic-dev3o/sx/archive/refs/tags/v$VERSION.tar.gz\"|" Formula/sx.rb +sed -i '' "s|sha256 \".*\"|sha256 \"$SHA256\"|" Formula/sx.rb + +# Commit and push +git config user.name "github-actions[bot]" +git config user.email "github-actions[bot]@users.noreply.github.com" +git add Formula/sx.rb +git commit -m "sx $VERSION" +git push +``` + +### 6.3 Required Secrets (in sx repository) + +| Secret | Purpose | Scope | +|--------|---------|-------| +| `TAP_REPO_TOKEN` | Fine-grained PAT for pushing to homebrew-sx | Contents: Read/Write on homebrew-sx only | + +### 6.4 Token Setup Instructions + +1. Go to GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens +2. Click "Generate new token" +3. Configure: + - **Token name:** `sx-tap-update` + - **Expiration:** 1 year (set reminder to rotate) + - **Repository access:** Only select repositories → `homebrew-sx` + - **Permissions:** + - Contents: Read and write + - Metadata: Read (auto-selected) +4. Generate token and copy immediately +5. Go to `sx` repository → Settings → Secrets → Actions +6. Add new secret: `TAP_REPO_TOKEN` with the token value + +--- + +## 7. Testing Requirements + +### 7.1 Local Testing Before Initial Publish + +```bash +# From homebrew-sx directory +cd /path/to/homebrew-sx + +# Audit formula +brew audit --strict Formula/sx.rb + +# Check Ruby style +brew style Formula/sx.rb + +# Test installation from source +brew install --build-from-source Formula/sx.rb + +# Run formula tests +brew test sx + +# Verify installation +sx --version +sx --help +sx --dry-run echo "test" + +# Cleanup +brew uninstall sx +``` + +### 7.2 Post-Release Verification + +After each release, verify: + +```bash +# Update tap +brew update + +# Check new version is available +brew info sx + +# Upgrade +brew upgrade sx + +# Verify new version +sx --version +``` + +### 7.3 Test Matrix + +| Test | Command | Expected Result | +|------|---------|-----------------| +| Version output | `sx --version` | Displays `sx X.Y.Z` | +| Help output | `sx --help` | Contains "sandbox" | +| Dry-run | `sx --dry-run echo test` | Shows Seatbelt profile | +| Basic execution | `sx echo "hello"` | Outputs "hello" | + +--- + +## 8. Maintenance Procedures + +### 8.1 Routine Tasks + +| Task | Frequency | Procedure | +|------|-----------|-----------| +| Rotate TAP_REPO_TOKEN | Annually | Regenerate fine-grained PAT, update secret | +| Audit formula | Per release (automated) | CI runs automatically | +| Review caveats | Major releases | Update usage instructions if needed | +| Check Homebrew compatibility | Quarterly | Test with latest Homebrew | + +### 8.2 Manual Formula Update (Emergency) + +If automated update fails: + +```bash +# Clone tap +git clone https://github.com/agentic-dev3o/homebrew-sx.git +cd homebrew-sx + +# Get SHA256 of release tarball +curl -sL "https://github.com/agentic-dev3o/sx/archive/refs/tags/vX.Y.Z.tar.gz" | shasum -a 256 + +# Edit Formula/sx.rb with new url and sha256 + +# Test locally +brew audit --strict Formula/sx.rb +brew install --build-from-source Formula/sx.rb +brew test sx + +# Commit and push +git add Formula/sx.rb +git commit -m "sx X.Y.Z" +git push +``` + +### 8.3 Deprecating a Version + +If a version has critical bugs: + +```bash +# Add deprecation notice to formula +# Edit Formula/sx.rb, add after license line: +deprecate! date: "YYYY-MM-DD", because: "critical security issue in vX.Y.Z" +``` + +### 8.4 Adding Pre-built Bottles (Optional Future Enhancement) + +For faster installation, add pre-built binaries: + +```ruby +bottle do + sha256 cellar: :any_skip_relocation, arm64_sonoma: "SHA256_HERE" + sha256 cellar: :any_skip_relocation, arm64_ventura: "SHA256_HERE" + sha256 cellar: :any_skip_relocation, sonoma: "SHA256_HERE" + sha256 cellar: :any_skip_relocation, ventura: "SHA256_HERE" +end +``` + +**Note:** Bottles require additional CI setup with `brew test-bot`. Defer this to a future enhancement. + +--- + +## 9. Security Considerations + +### 9.1 Repository Access + +| Entity | Access Level | Purpose | +|--------|--------------|---------| +| Repository owner | Admin | Full control | +| github-actions[bot] | Write (via PAT) | Automated formula updates | +| Public | Read | Homebrew tap access | + +### 9.2 Token Security + +- **Scope:** Fine-grained PAT with minimal permissions (Contents: R/W on homebrew-sx only) +- **Storage:** GitHub Actions secret (encrypted) +- **Rotation:** Annual rotation required +- **Audit:** Review token usage in GitHub security log + +### 9.3 Formula Security + +- **Source URL:** Always use `https://github.com/` (verified domain) +- **SHA256:** Automatically verified by Homebrew during install +- **No external resources:** Formula doesn't fetch additional files + +### 9.4 Branch Protection (Recommended) + +Configure on `main` branch: +- Require pull request reviews: **Disabled** (allows automated updates) +- Require status checks: **Enabled** (audit workflow must pass) +- Allow force pushes: **Disabled** +- Allow deletions: **Disabled** + +--- + +## 10. Troubleshooting + +### 10.1 Common Issues + +#### Formula audit fails + +``` +Error: sx: sha256 mismatch +``` + +**Cause:** SHA256 doesn't match actual tarball +**Fix:** Recalculate SHA256: +```bash +curl -sL "https://github.com/agentic-dev3o/sx/archive/refs/tags/vX.Y.Z.tar.gz" | shasum -a 256 +``` + +#### Build fails on user machine + +``` +Error: No available formula with the name "rust" +``` + +**Cause:** Old Homebrew version +**Fix:** User should run: +```bash +brew update +brew install sx +``` + +#### Token expired + +``` +remote: Permission to agentic-dev3o/homebrew-sx.git denied +``` + +**Cause:** TAP_REPO_TOKEN expired +**Fix:** Generate new PAT and update secret in sx repository + +### 10.2 Debug Commands + +```bash +# View formula info +brew info sx + +# View formula source +brew cat sx + +# Verbose installation +brew install --verbose --debug sx + +# Check tap status +brew tap-info agentic-dev3o/sx +``` + +### 10.3 Support Channels + +- **Issues:** https://github.com/agentic-dev3o/sx/issues +- **Formula-specific issues:** Tag with `homebrew` label + +--- + +## Appendix A: Quick Reference + +### Commands for Users + +```bash +brew tap agentic-dev3o/sx # Add tap +brew install sx # Install +brew upgrade sx # Upgrade +brew uninstall sx # Remove +brew untap agentic-dev3o/sx # Remove tap +``` + +### Commands for Maintainers + +```bash +brew audit --strict Formula/sx.rb # Validate formula +brew style Formula/sx.rb # Check Ruby style +brew install --build-from-source Formula/sx.rb # Test build +brew test sx # Run tests +``` + +### Key URLs + +| Resource | URL | +|----------|-----| +| Main repository | `https://github.com/agentic-dev3o/sx` | +| Tap repository | `https://github.com/agentic-dev3o/homebrew-sx` | +| Release tarball | `https://github.com/agentic-dev3o/sx/archive/refs/tags/vX.Y.Z.tar.gz` | + +--- + +## Appendix B: Checklist + +### Initial Setup Checklist + +- [ ] Create `homebrew-sx` repository on GitHub (public) +- [ ] Create `Formula/sx.rb` with placeholder SHA256 +- [ ] Create `README.md` with installation instructions +- [ ] Create `LICENSE` (match main repo) +- [ ] Create `.github/workflows/audit.yml` +- [ ] Generate fine-grained PAT for tap updates +- [ ] Add `TAP_REPO_TOKEN` secret to `sx` repository +- [ ] Add release workflow to `sx` repository +- [ ] Test full release cycle with v0.1.0-alpha +- [ ] Delete test release +- [ ] Perform first real release + +### Per-Release Checklist (Automated) + +- [ ] Version bumped in `Cargo.toml` +- [ ] Changelog generated +- [ ] GitHub release created +- [ ] Formula `url` updated +- [ ] Formula `sha256` updated +- [ ] Audit workflow passes + +--- + +*Document version: 1.0* +*Last updated: January 2025* From 87edc37adcd2555e107b788c69c42e7e1a129c0b Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Wed, 28 Jan 2026 13:45:06 +0800 Subject: [PATCH 10/16] fix: address code review findings - Update actions/checkout from v4 to v6 for consistency with QA workflow - Add fail-fast: false to matrix build strategy - Add restore-keys for cache fallback on partial hits - Fix documentation: .yml -> .yaml extension --- .github/workflows/release.yaml | 13 +++++++++---- docs/homebrew-tap-specification.md | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a53b4f7..de68810 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -38,7 +38,7 @@ jobs: needs: validate steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Rust uses: dtolnay/rust-toolchain@stable @@ -50,6 +50,8 @@ jobs: ~/.cargo/registry ~/.cargo/git key: ${{ runner.os }}-cargo-security-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-security- - name: Install security tools run: | @@ -68,7 +70,7 @@ jobs: needs: [validate, security] steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} @@ -116,13 +118,14 @@ jobs: runs-on: macos-latest needs: [validate, prepare] strategy: + fail-fast: false matrix: target: - x86_64-apple-darwin - aarch64-apple-darwin steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: v${{ needs.validate.outputs.version }} @@ -139,6 +142,8 @@ jobs: ~/.cargo/git target key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.target }}-cargo- - name: Build release binary run: cargo build --release --target ${{ matrix.target }} @@ -164,7 +169,7 @@ jobs: needs: [validate, build] steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: v${{ needs.validate.outputs.version }} diff --git a/docs/homebrew-tap-specification.md b/docs/homebrew-tap-specification.md index e4eea5e..9502145 100644 --- a/docs/homebrew-tap-specification.md +++ b/docs/homebrew-tap-specification.md @@ -55,7 +55,7 @@ brew upgrade sx │ sx repository │ │ (main application) │ │ │ -│ .github/workflows/release.yml │ +│ .github/workflows/release.yaml │ │ ├── Bumps version │ │ ├── Creates GitHub release │ │ ├── Calculates SHA256 │ From 72ee3fb664293090a8a8f2e49d2a763822e64917 Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Thu, 29 Jan 2026 00:28:07 +0800 Subject: [PATCH 11/16] docs(trace): document system-wide violation display limitation Add clarification that --trace shows violations from all sandboxed processes on the system, not just the current session, due to macOS sandbox logging limitations. Update both README and code documentation. --- README.md | 5 ++++- src/cli/args.rs | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 917038e..a13e72b 100644 --- a/README.md +++ b/README.md @@ -130,13 +130,16 @@ sx --dry-run rust # Preview sandbox profile | Option | Description | |--------|-------------| | `-v, --verbose` | Show sandbox configuration | -| `-t, --trace` | Trace sandbox violations in real-time | +| `-t, --trace` | Trace sandbox violations in real-time (see note below) | +| `--trace-file ` | Write trace output to file instead of stderr | | `-n, --dry-run` | Print sandbox profile without executing | | `--explain` | Show what would be allowed/denied | | `--init` | Create `.sandbox.toml` in current directory | | `--allow-read ` | Allow read access to path | | `--allow-write ` | Allow write access to path | +> **Note on `--trace`:** The trace output shows sandbox violations from **all sandboxed processes** on the system, not just the current session. This is a limitation of macOS sandbox logging, which doesn't include session identifiers in denial logs. If you're running multiple `sx` sessions simultaneously, violations from all sessions will appear in each trace output. + ## Profiles Profiles are composable configurations that stack together: diff --git a/src/cli/args.rs b/src/cli/args.rs index 34839a9..b6ee904 100644 --- a/src/cli/args.rs +++ b/src/cli/args.rs @@ -40,11 +40,14 @@ pub struct Args { #[arg(short, long)] pub debug: bool, - /// Trace sandbox violations (shows blocked operations in real-time) + /// Trace sandbox violations (shows blocked operations in real-time). + /// Note: Shows violations from ALL sandboxed processes on the system, + /// not just this session (macOS limitation) #[arg(short, long)] pub trace: bool, - /// Write trace output to file instead of stderr + /// Write trace output to file instead of stderr. + /// Note: Shows violations from ALL sandboxed processes on the system #[arg(long, value_name = "PATH")] pub trace_file: Option, From 24ba2f2f3bbfd0a4c7d4af531d2364c9d1843c90 Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Thu, 29 Jan 2026 01:44:31 +0800 Subject: [PATCH 12/16] refactor(release): simplify Homebrew tap update with mislav action - Replace custom SHA256/sed logic with mislav/bump-homebrew-formula-action@v3 - Eliminates race condition and retry complexity - Improve reliability with battle-tested automation - Use GitHub App token for secure authentication --- .github/workflows/release.yaml | 102 ++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index de68810..8826bf5 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,6 +8,11 @@ on: required: true type: string +# Prevent concurrent releases +concurrency: + group: release + cancel-in-progress: false + # Restrict permissions to minimum required permissions: contents: write @@ -21,6 +26,11 @@ jobs: outputs: version: ${{ steps.validate.outputs.version }} steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Validate version format id: validate run: | @@ -32,6 +42,42 @@ jobs: echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Valid version: $VERSION" + - name: Check tag does not exist + run: | + if git rev-parse "v${{ steps.validate.outputs.version }}" >/dev/null 2>&1; then + echo "::error::Tag v${{ steps.validate.outputs.version }} already exists" + exit 1 + fi + echo "Tag v${{ steps.validate.outputs.version }} is available" + + # Test gate: ensure code compiles and tests pass + test: + runs-on: macos-latest + needs: validate + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-test- + + - name: Run tests + run: cargo test --all-features + + - name: Build release binary + run: cargo build --release + # Security gate: must pass before release proceeds security: runs-on: ubuntu-latest @@ -49,6 +95,7 @@ jobs: path: | ~/.cargo/registry ~/.cargo/git + ~/.cargo/bin key: ${{ runner.os }}-cargo-security-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo-security- @@ -67,7 +114,7 @@ jobs: # Prepare release: bump version, generate changelog, create tag prepare: runs-on: ubuntu-latest - needs: [validate, security] + needs: [validate, test, security] steps: - name: Checkout uses: actions/checkout@v6 @@ -197,36 +244,35 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Update Homebrew tap with new version + # NOTE: This job is specific to agentic-dev3o organization. + # Forks should either disable this job or update the owner/repositories values. update-tap: runs-on: ubuntu-latest needs: [validate, release] steps: - - name: Calculate source tarball SHA256 - id: sha256 - run: | - # Wait for GitHub to finalize the release tarball - sleep 10 - # Download and calculate SHA256 of the source tarball - curl -sL "https://github.com/${{ github.repository }}/archive/refs/tags/v${{ needs.validate.outputs.version }}.tar.gz" -o source.tar.gz - SHA256=$(shasum -a 256 source.tar.gz | awk '{print $1}') - echo "sha256=$SHA256" >> $GITHUB_OUTPUT - echo "Source tarball SHA256: $SHA256" - - - name: Update Homebrew tap - env: - TAP_REPO_TOKEN: ${{ secrets.TAP_REPO_TOKEN }} - run: | - # Clone the tap repository - git clone "https://x-access-token:${TAP_REPO_TOKEN}@github.com/agentic-dev3o/homebrew-sx.git" tap - cd tap + - name: Checkout + uses: actions/checkout@v6 + with: + ref: v${{ needs.validate.outputs.version }} - # Update the formula with new version and SHA256 - sed -i "s|url \"https://github.com/agentic-dev3o/sx/archive/refs/tags/v[^\"]*\.tar\.gz\"|url \"https://github.com/agentic-dev3o/sx/archive/refs/tags/v${{ needs.validate.outputs.version }}.tar.gz\"|" Formula/sx.rb - sed -i "s|sha256 \"[a-f0-9]*\"|sha256 \"${{ steps.sha256.outputs.sha256 }}\"|" Formula/sx.rb + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: agentic-dev3o + repositories: homebrew-sx - # Commit and push - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add Formula/sx.rb - git diff --cached --quiet || git commit -m "sx ${{ needs.validate.outputs.version }}" - git push + - name: Update Homebrew formula + uses: mislav/bump-homebrew-formula-action@v3 + with: + formula-name: sx + homebrew-tap: agentic-dev3o/homebrew-sx + tag-name: v${{ needs.validate.outputs.version }} + commit-message: | + {{formulaName}} {{version}} + + Created by https://github.com/mislav/bump-homebrew-formula-action + env: + COMMITTER_TOKEN: ${{ steps.app-token.outputs.token }} From e03dacac77f0c710f2b2a50f40b027bb84d67983 Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Thu, 29 Jan 2026 01:47:52 +0800 Subject: [PATCH 13/16] docs: remove homebrew tap specification This specification documented manual tap update procedures. The release workflow now uses mislav/bump-homebrew-formula-action which automates all formula updates, making this documentation redundant. --- docs/homebrew-tap-specification.md | 654 ----------------------------- 1 file changed, 654 deletions(-) delete mode 100644 docs/homebrew-tap-specification.md diff --git a/docs/homebrew-tap-specification.md b/docs/homebrew-tap-specification.md deleted file mode 100644 index 9502145..0000000 --- a/docs/homebrew-tap-specification.md +++ /dev/null @@ -1,654 +0,0 @@ -# Homebrew Tap Repository Specification - -**Project:** `homebrew-sx` -**Purpose:** Homebrew formula distribution for the `sx` (sandbox-shell) CLI tool -**Target Platform:** macOS only -**Status:** To Be Created - ---- - -## Table of Contents - -1. [Overview](#1-overview) -2. [Repository Structure](#2-repository-structure) -3. [Initial Setup](#3-initial-setup) -4. [Formula Specification](#4-formula-specification) -5. [CI/CD Configuration](#5-cicd-configuration) -6. [Automated Updates](#6-automated-updates) -7. [Testing Requirements](#7-testing-requirements) -8. [Maintenance Procedures](#8-maintenance-procedures) -9. [Security Considerations](#9-security-considerations) -10. [Troubleshooting](#10-troubleshooting) - ---- - -## 1. Overview - -### 1.1 Purpose - -The `homebrew-sx` repository hosts the Homebrew formula for distributing the `sx` CLI tool. This repository is automatically updated by the main `sx` repository's release workflow. - -### 1.2 User Installation Flow - -```bash -# First-time setup (adds tap) -brew tap agentic-dev3o/sx - -# Install sx -brew install sx - -# Future upgrades -brew upgrade sx -``` - -### 1.3 Dependencies - -| Dependency | Type | Purpose | -|------------|------|---------| -| Rust toolchain | Build-time | Compiles sx from source | -| macOS | Runtime | Required (Seatbelt is macOS-only) | - -### 1.4 Integration with Main Repository - -``` -┌────────────────────────────────┐ -│ sx repository │ -│ (main application) │ -│ │ -│ .github/workflows/release.yaml │ -│ ├── Bumps version │ -│ ├── Creates GitHub release │ -│ ├── Calculates SHA256 │ -│ └── Updates homebrew-sx ─────┼──────┐ -└────────────────────────────────┘ │ - ▼ - ┌─────────────────────────────┐ - │ homebrew-sx repository │ - │ │ - │ Formula/sx.rb │ - │ ├── version: X.Y.Z │ - │ ├── sha256: │ - │ └── url: release tarball │ - └─────────────────────────────┘ -``` - ---- - -## 2. Repository Structure - -``` -homebrew-sx/ -├── README.md # Installation instructions -├── LICENSE # Same license as main sx repo -├── Formula/ -│ └── sx.rb # Main formula file -└── .github/ - └── workflows/ - └── audit.yml # Formula validation on PR/push -``` - -### 2.1 File Descriptions - -| File | Purpose | Update Frequency | -|------|---------|------------------| -| `README.md` | User-facing installation docs | Rarely | -| `LICENSE` | Legal terms (match main repo) | Never | -| `Formula/sx.rb` | Homebrew formula definition | Every release | -| `.github/workflows/audit.yml` | CI validation | Rarely | - ---- - -## 3. Initial Setup - -### 3.1 Create Repository - -1. Create new GitHub repository: `homebrew-sx` - - Visibility: **Public** (required for Homebrew taps) - - Initialize with README: **No** (we'll create our own) - - License: Match main `sx` repository - -2. Clone locally: - ```bash - git clone https://github.com/agentic-dev3o/homebrew-sx.git - cd homebrew-sx - ``` - -### 3.2 Create Directory Structure - -```bash -mkdir -p Formula .github/workflows -``` - -### 3.3 Create README.md - -```markdown -# Homebrew Tap for sx - -This is a [Homebrew](https://brew.sh) tap for [sx](https://github.com/agentic-dev3o/sx), a CLI that wraps shell sessions in macOS Seatbelt sandboxes. - -## Requirements - -- macOS (sx uses macOS Seatbelt, which is not available on Linux) -- Homebrew - -## Installation - -```bash -brew tap agentic-dev3o/sx -brew install sx -``` - -## Upgrading - -```bash -brew upgrade sx -``` - -## Uninstalling - -```bash -brew uninstall sx -brew untap agentic-dev3o/sx -``` - -## Troubleshooting - -### Build fails - -Ensure you have Xcode Command Line Tools installed: - -```bash -xcode-select --install -``` - -### Issues - -Report issues at: https://github.com/agentic-dev3o/sx/issues -``` - -### 3.4 Create LICENSE - -Copy the exact same license file from the main `sx` repository. - -### 3.5 Initial Commit - -```bash -git add . -git commit -m "Initial tap setup" -git push origin main -``` - ---- - -## 4. Formula Specification - -### 4.1 Formula File: `Formula/sx.rb` - -```ruby -# typed: false -# frozen_string_literal: true - -class Sx < Formula - desc "Sandbox shell sessions with macOS Seatbelt" - homepage "https://github.com/agentic-dev3o/sx" - url "https://github.com/agentic-dev3o/sx/archive/refs/tags/v0.1.0.tar.gz" - sha256 "PLACEHOLDER_SHA256_WILL_BE_UPDATED_BY_RELEASE_WORKFLOW" - license "MIT" - head "https://github.com/agentic-dev3o/sx.git", branch: "main" - - # macOS only - Seatbelt is not available on Linux - depends_on :macos - - # Rust toolchain for building from source - depends_on "rust" => :build - - def install - system "cargo", "install", *std_cargo_args - end - - def caveats - <<~EOS - sx restricts filesystem and network access using macOS Seatbelt. - - Quick start: - sx echo "sandboxed command" # Run single command - sx --profile rust cargo build # Use rust profile - sx --dry-run online node # Preview sandbox rules - - Initialize shell integration: - sx init bash >> ~/.bashrc - sx init zsh >> ~/.zshrc - - Configuration: - ~/.config/sx/config.toml # Global config - .sandbox.toml # Project config - - Documentation: https://github.com/agentic-dev3o/sx - EOS - end - - test do - # Verify binary runs and shows version - assert_match version.to_s, shell_output("#{bin}/sx --version") - - # Verify help output - assert_match "sandbox", shell_output("#{bin}/sx --help") - - # Verify dry-run works (doesn't require actual sandboxing) - output = shell_output("#{bin}/sx --dry-run echo test") - assert_match "file-read", output # Seatbelt profile contains file-read rules - end -end -``` - -### 4.2 Formula Fields Reference - -| Field | Description | Auto-Updated | -|-------|-------------|--------------| -| `desc` | Short description (< 80 chars) | No | -| `homepage` | Project homepage URL | No | -| `url` | Source tarball URL (GitHub release) | **Yes** | -| `sha256` | SHA256 checksum of tarball | **Yes** | -| `license` | SPDX license identifier | No | -| `head` | Git URL for `--HEAD` installs | No | -| `depends_on` | Build/runtime dependencies | No | -| `install` | Build instructions | No | -| `caveats` | Post-install message to user | Rarely | -| `test` | Verification commands | Rarely | - -### 4.3 Version Interpolation (Alternative) - -For cleaner updates, use version interpolation: - -```ruby -url "https://github.com/agentic-dev3o/sx/archive/refs/tags/v#{version}.tar.gz" -``` - -**Note:** This requires updating only the `version` line during releases, but the current sed-based update in the release workflow handles explicit URLs fine. - ---- - -## 5. CI/CD Configuration - -### 5.1 Formula Audit Workflow - -Create `.github/workflows/audit.yml`: - -```yaml -name: Audit Formula - -on: - push: - branches: [main] - paths: - - 'Formula/**' - pull_request: - branches: [main] - paths: - - 'Formula/**' - -jobs: - audit: - runs-on: macos-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Homebrew - uses: Homebrew/actions/setup-homebrew@master - - - name: Tap local repository - run: | - mkdir -p $(brew --repository)/Library/Taps/agentic-dev3o - ln -s $GITHUB_WORKSPACE $(brew --repository)/Library/Taps/agentic-dev3o/homebrew-sx - - - name: Run brew audit - run: brew audit --strict Formula/sx.rb - - - name: Run brew style - run: brew style Formula/sx.rb - - - name: Test formula installation - run: | - brew install --build-from-source Formula/sx.rb - brew test sx -``` - -### 5.2 Workflow Triggers - -| Trigger | Condition | Actions | -|---------|-----------|---------| -| Push to main | Formula files changed | Audit + Style check | -| Pull request | Formula files changed | Audit + Style check + Build test | -| Release workflow (from sx repo) | New release | Direct commit (bypasses PR) | - ---- - -## 6. Automated Updates - -### 6.1 How Updates Work - -The main `sx` repository's release workflow updates this tap: - -1. Release workflow calculates SHA256 of source tarball -2. Clones `homebrew-sx` using `TAP_REPO_TOKEN` -3. Updates `url` and `sha256` in `Formula/sx.rb` via sed -4. Commits and pushes changes - -### 6.2 Update Script (in sx repo release workflow) - -```bash -# Clone tap repo -git clone "https://x-access-token:$TAP_REPO_TOKEN@github.com/agentic-dev3o/homebrew-sx.git" tap -cd tap - -# Update formula -sed -i '' "s|url \".*\"|url \"https://github.com/agentic-dev3o/sx/archive/refs/tags/v$VERSION.tar.gz\"|" Formula/sx.rb -sed -i '' "s|sha256 \".*\"|sha256 \"$SHA256\"|" Formula/sx.rb - -# Commit and push -git config user.name "github-actions[bot]" -git config user.email "github-actions[bot]@users.noreply.github.com" -git add Formula/sx.rb -git commit -m "sx $VERSION" -git push -``` - -### 6.3 Required Secrets (in sx repository) - -| Secret | Purpose | Scope | -|--------|---------|-------| -| `TAP_REPO_TOKEN` | Fine-grained PAT for pushing to homebrew-sx | Contents: Read/Write on homebrew-sx only | - -### 6.4 Token Setup Instructions - -1. Go to GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens -2. Click "Generate new token" -3. Configure: - - **Token name:** `sx-tap-update` - - **Expiration:** 1 year (set reminder to rotate) - - **Repository access:** Only select repositories → `homebrew-sx` - - **Permissions:** - - Contents: Read and write - - Metadata: Read (auto-selected) -4. Generate token and copy immediately -5. Go to `sx` repository → Settings → Secrets → Actions -6. Add new secret: `TAP_REPO_TOKEN` with the token value - ---- - -## 7. Testing Requirements - -### 7.1 Local Testing Before Initial Publish - -```bash -# From homebrew-sx directory -cd /path/to/homebrew-sx - -# Audit formula -brew audit --strict Formula/sx.rb - -# Check Ruby style -brew style Formula/sx.rb - -# Test installation from source -brew install --build-from-source Formula/sx.rb - -# Run formula tests -brew test sx - -# Verify installation -sx --version -sx --help -sx --dry-run echo "test" - -# Cleanup -brew uninstall sx -``` - -### 7.2 Post-Release Verification - -After each release, verify: - -```bash -# Update tap -brew update - -# Check new version is available -brew info sx - -# Upgrade -brew upgrade sx - -# Verify new version -sx --version -``` - -### 7.3 Test Matrix - -| Test | Command | Expected Result | -|------|---------|-----------------| -| Version output | `sx --version` | Displays `sx X.Y.Z` | -| Help output | `sx --help` | Contains "sandbox" | -| Dry-run | `sx --dry-run echo test` | Shows Seatbelt profile | -| Basic execution | `sx echo "hello"` | Outputs "hello" | - ---- - -## 8. Maintenance Procedures - -### 8.1 Routine Tasks - -| Task | Frequency | Procedure | -|------|-----------|-----------| -| Rotate TAP_REPO_TOKEN | Annually | Regenerate fine-grained PAT, update secret | -| Audit formula | Per release (automated) | CI runs automatically | -| Review caveats | Major releases | Update usage instructions if needed | -| Check Homebrew compatibility | Quarterly | Test with latest Homebrew | - -### 8.2 Manual Formula Update (Emergency) - -If automated update fails: - -```bash -# Clone tap -git clone https://github.com/agentic-dev3o/homebrew-sx.git -cd homebrew-sx - -# Get SHA256 of release tarball -curl -sL "https://github.com/agentic-dev3o/sx/archive/refs/tags/vX.Y.Z.tar.gz" | shasum -a 256 - -# Edit Formula/sx.rb with new url and sha256 - -# Test locally -brew audit --strict Formula/sx.rb -brew install --build-from-source Formula/sx.rb -brew test sx - -# Commit and push -git add Formula/sx.rb -git commit -m "sx X.Y.Z" -git push -``` - -### 8.3 Deprecating a Version - -If a version has critical bugs: - -```bash -# Add deprecation notice to formula -# Edit Formula/sx.rb, add after license line: -deprecate! date: "YYYY-MM-DD", because: "critical security issue in vX.Y.Z" -``` - -### 8.4 Adding Pre-built Bottles (Optional Future Enhancement) - -For faster installation, add pre-built binaries: - -```ruby -bottle do - sha256 cellar: :any_skip_relocation, arm64_sonoma: "SHA256_HERE" - sha256 cellar: :any_skip_relocation, arm64_ventura: "SHA256_HERE" - sha256 cellar: :any_skip_relocation, sonoma: "SHA256_HERE" - sha256 cellar: :any_skip_relocation, ventura: "SHA256_HERE" -end -``` - -**Note:** Bottles require additional CI setup with `brew test-bot`. Defer this to a future enhancement. - ---- - -## 9. Security Considerations - -### 9.1 Repository Access - -| Entity | Access Level | Purpose | -|--------|--------------|---------| -| Repository owner | Admin | Full control | -| github-actions[bot] | Write (via PAT) | Automated formula updates | -| Public | Read | Homebrew tap access | - -### 9.2 Token Security - -- **Scope:** Fine-grained PAT with minimal permissions (Contents: R/W on homebrew-sx only) -- **Storage:** GitHub Actions secret (encrypted) -- **Rotation:** Annual rotation required -- **Audit:** Review token usage in GitHub security log - -### 9.3 Formula Security - -- **Source URL:** Always use `https://github.com/` (verified domain) -- **SHA256:** Automatically verified by Homebrew during install -- **No external resources:** Formula doesn't fetch additional files - -### 9.4 Branch Protection (Recommended) - -Configure on `main` branch: -- Require pull request reviews: **Disabled** (allows automated updates) -- Require status checks: **Enabled** (audit workflow must pass) -- Allow force pushes: **Disabled** -- Allow deletions: **Disabled** - ---- - -## 10. Troubleshooting - -### 10.1 Common Issues - -#### Formula audit fails - -``` -Error: sx: sha256 mismatch -``` - -**Cause:** SHA256 doesn't match actual tarball -**Fix:** Recalculate SHA256: -```bash -curl -sL "https://github.com/agentic-dev3o/sx/archive/refs/tags/vX.Y.Z.tar.gz" | shasum -a 256 -``` - -#### Build fails on user machine - -``` -Error: No available formula with the name "rust" -``` - -**Cause:** Old Homebrew version -**Fix:** User should run: -```bash -brew update -brew install sx -``` - -#### Token expired - -``` -remote: Permission to agentic-dev3o/homebrew-sx.git denied -``` - -**Cause:** TAP_REPO_TOKEN expired -**Fix:** Generate new PAT and update secret in sx repository - -### 10.2 Debug Commands - -```bash -# View formula info -brew info sx - -# View formula source -brew cat sx - -# Verbose installation -brew install --verbose --debug sx - -# Check tap status -brew tap-info agentic-dev3o/sx -``` - -### 10.3 Support Channels - -- **Issues:** https://github.com/agentic-dev3o/sx/issues -- **Formula-specific issues:** Tag with `homebrew` label - ---- - -## Appendix A: Quick Reference - -### Commands for Users - -```bash -brew tap agentic-dev3o/sx # Add tap -brew install sx # Install -brew upgrade sx # Upgrade -brew uninstall sx # Remove -brew untap agentic-dev3o/sx # Remove tap -``` - -### Commands for Maintainers - -```bash -brew audit --strict Formula/sx.rb # Validate formula -brew style Formula/sx.rb # Check Ruby style -brew install --build-from-source Formula/sx.rb # Test build -brew test sx # Run tests -``` - -### Key URLs - -| Resource | URL | -|----------|-----| -| Main repository | `https://github.com/agentic-dev3o/sx` | -| Tap repository | `https://github.com/agentic-dev3o/homebrew-sx` | -| Release tarball | `https://github.com/agentic-dev3o/sx/archive/refs/tags/vX.Y.Z.tar.gz` | - ---- - -## Appendix B: Checklist - -### Initial Setup Checklist - -- [ ] Create `homebrew-sx` repository on GitHub (public) -- [ ] Create `Formula/sx.rb` with placeholder SHA256 -- [ ] Create `README.md` with installation instructions -- [ ] Create `LICENSE` (match main repo) -- [ ] Create `.github/workflows/audit.yml` -- [ ] Generate fine-grained PAT for tap updates -- [ ] Add `TAP_REPO_TOKEN` secret to `sx` repository -- [ ] Add release workflow to `sx` repository -- [ ] Test full release cycle with v0.1.0-alpha -- [ ] Delete test release -- [ ] Perform first real release - -### Per-Release Checklist (Automated) - -- [ ] Version bumped in `Cargo.toml` -- [ ] Changelog generated -- [ ] GitHub release created -- [ ] Formula `url` updated -- [ ] Formula `sha256` updated -- [ ] Audit workflow passes - ---- - -*Document version: 1.0* -*Last updated: January 2025* From e3f3107cf460c8e4440c58cfdbd858effd2beb4e Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Thu, 29 Jan 2026 01:51:50 +0800 Subject: [PATCH 14/16] test(release): temporarily push to feature branch for testing --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 8826bf5..cd75b85 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -157,7 +157,7 @@ jobs: git add Cargo.toml Cargo.lock CHANGELOG.md git commit -m "chore(release): v${{ needs.validate.outputs.version }}" git tag -a "v${{ needs.validate.outputs.version }}" -m "Release v${{ needs.validate.outputs.version }}" - git push origin main + git push origin feature/release-workflow git push origin "v${{ needs.validate.outputs.version }}" # Build release binaries for macOS (Intel and Apple Silicon) From bba7b68c67e40fa221e4a460dc61515bf2730bae Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Thu, 29 Jan 2026 01:56:25 +0800 Subject: [PATCH 15/16] perf(workflows): cache cargo-audit and cargo-deny binaries - Add actions/cache@v4 for ~/.cargo/bin/cargo-audit (key: cargo-audit-0.21) - Add actions/cache@v4 for ~/.cargo/bin/cargo-deny (key: cargo-deny-0.18) - Skip installation steps on cache hit - Reduces security-audit and dependency-check job times from 5 min to ~2 sec - Revert temporary test branch push to main --- .github/workflows/QA.yaml | 18 ++++++++++++++++-- .github/workflows/release.yaml | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.github/workflows/QA.yaml b/.github/workflows/QA.yaml index 120a67f..8c069bb 100644 --- a/.github/workflows/QA.yaml +++ b/.github/workflows/QA.yaml @@ -48,10 +48,17 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + - name: Cache cargo-audit + uses: actions/cache@v4 + id: cache-audit + with: + path: ~/.cargo/bin/cargo-audit + key: cargo-audit-0.21 - name: Install cargo-audit + if: steps.cache-audit.outputs.cache-hit != 'true' run: cargo install cargo-audit --locked - name: Run security audit - run: cargo audit --deny warnings + run: ~/.cargo/bin/cargo-audit --deny warnings # Secrets scanning secrets-scan: @@ -70,7 +77,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + - name: Cache cargo-deny + uses: actions/cache@v4 + id: cache-deny + with: + path: ~/.cargo/bin/cargo-deny + key: cargo-deny-0.18 - name: Install cargo-deny + if: steps.cache-deny.outputs.cache-hit != 'true' run: cargo install cargo-deny --locked - name: Check dependencies - run: cargo deny check + run: ~/.cargo/bin/cargo-deny check diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index cd75b85..8826bf5 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -157,7 +157,7 @@ jobs: git add Cargo.toml Cargo.lock CHANGELOG.md git commit -m "chore(release): v${{ needs.validate.outputs.version }}" git tag -a "v${{ needs.validate.outputs.version }}" -m "Release v${{ needs.validate.outputs.version }}" - git push origin feature/release-workflow + git push origin main git push origin "v${{ needs.validate.outputs.version }}" # Build release binaries for macOS (Intel and Apple Silicon) From a72ed6318ec81fa8c7edca7544e458b103d826b3 Mon Sep 17 00:00:00 2001 From: Pierre Tomasina Date: Thu, 29 Jan 2026 02:06:26 +0800 Subject: [PATCH 16/16] fix(workflows): use cargo subcommand syntax for audit and deny --- .github/workflows/QA.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/QA.yaml b/.github/workflows/QA.yaml index 8c069bb..da4416c 100644 --- a/.github/workflows/QA.yaml +++ b/.github/workflows/QA.yaml @@ -58,7 +58,7 @@ jobs: if: steps.cache-audit.outputs.cache-hit != 'true' run: cargo install cargo-audit --locked - name: Run security audit - run: ~/.cargo/bin/cargo-audit --deny warnings + run: cargo audit --deny warnings # Secrets scanning secrets-scan: @@ -87,4 +87,4 @@ jobs: if: steps.cache-deny.outputs.cache-hit != 'true' run: cargo install cargo-deny --locked - name: Check dependencies - run: ~/.cargo/bin/cargo-deny check + run: cargo deny check