Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/QA.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,14 @@ 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
Expand All @@ -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
278 changes: 278 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
name: Release

on:
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g., 0.2.0 - no v prefix)'
required: true
type: string

# Prevent concurrent releases
concurrency:
group: release
cancel-in-progress: false

# 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: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

- 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"

- 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
needs: validate
steps:
- name: Checkout
uses: actions/checkout@v6

- 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-security-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-security-

- 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

# Prepare release: bump version, generate changelog, create tag
prepare:
runs-on: ubuntu-latest
needs: [validate, test, security]
steps:
- name: Checkout
uses: actions/checkout@v6
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 }}"

# Build release binaries for macOS (Intel and Apple Silicon)
build:
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@v6
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') }}
restore-keys: |
${{ runner.os }}-${{ matrix.target }}-cargo-

- 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

# Create GitHub release with all artifacts
release:
runs-on: ubuntu-latest
needs: [validate, build]
steps:
- name: Checkout
uses: actions/checkout@v6
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 }}

# 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: Checkout
uses: actions/checkout@v6
with:
ref: v${{ needs.validate.outputs.version }}

- 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

- 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 }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ target/

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
actionlint
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <PATH>` | 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 <PATH>` | Allow read access to path |
| `--allow-write <PATH>` | 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:
Expand Down
47 changes: 47 additions & 0 deletions cliff.toml
Original file line number Diff line number Diff line change
@@ -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"
7 changes: 5 additions & 2 deletions src/cli/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf>,

Expand Down
Loading