mirror of
https://github.com/lahfir/agent-desktop.git
synced 2026-07-26 17:12:15 +00:00
Add locked Rust verification, binary command tests, npm package surface checks, dependency policy scanning, workflow security auditing, and Dependabot updates so PR and release gates cover the package surfaces that are shipped.
340 lines
12 KiB
YAML
340 lines
12 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
|
|
# One release run per commit — never cancel, just queue.
|
|
concurrency:
|
|
group: release-${{ github.sha }}
|
|
|
|
# Deny all permissions at workflow level; each job declares only what it needs.
|
|
permissions: {}
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
|
|
jobs:
|
|
release-please:
|
|
name: Release Please
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
permissions:
|
|
contents: write
|
|
pull-requests: write
|
|
outputs:
|
|
release_created: ${{ steps.release.outputs.release_created }}
|
|
tag_name: ${{ steps.release.outputs.tag_name }}
|
|
version: ${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}.${{ steps.release.outputs.patch }}
|
|
steps:
|
|
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
|
|
id: release
|
|
with:
|
|
config-file: release-please-config.json
|
|
manifest-file: .release-please-manifest.json
|
|
|
|
build:
|
|
name: Build (${{ matrix.target }})
|
|
needs: release-please
|
|
if: needs.release-please.outputs.release_created == 'true'
|
|
runs-on: macos-latest
|
|
timeout-minutes: 30
|
|
permissions:
|
|
contents: read
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
target:
|
|
- aarch64-apple-darwin
|
|
- x86_64-apple-darwin
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
ref: ${{ needs.release-please.outputs.tag_name }}
|
|
|
|
- name: Install Rust toolchain
|
|
run: |
|
|
rustup show
|
|
rustup target add ${{ matrix.target }}
|
|
|
|
- name: Cache cargo registry
|
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
|
with:
|
|
path: |
|
|
~/.cargo/registry/index/
|
|
~/.cargo/registry/cache/
|
|
~/.cargo/git/db/
|
|
key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.toml', 'Cargo.lock') }}
|
|
restore-keys: ${{ runner.os }}-${{ matrix.target }}-cargo-
|
|
|
|
- name: Build release binary
|
|
run: cargo build --locked --release --target ${{ matrix.target }}
|
|
|
|
- name: Check binary size
|
|
run: |
|
|
SIZE=$(stat -f%z target/${{ matrix.target }}/release/agent-desktop)
|
|
LIMIT=$((15 * 1024 * 1024))
|
|
echo "Binary size: $(du -sh target/${{ matrix.target }}/release/agent-desktop | cut -f1)"
|
|
if [ "$SIZE" -gt "$LIMIT" ]; then
|
|
echo "FAIL: binary exceeds 15MB limit (${SIZE} bytes)"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Create tarball
|
|
run: |
|
|
VERSION=${{ needs.release-please.outputs.version }}
|
|
TARBALL="agent-desktop-v${VERSION}-${{ matrix.target }}.tar.gz"
|
|
tar -czf "${TARBALL}" -C target/${{ matrix.target }}/release agent-desktop
|
|
shasum -a 256 "${TARBALL}" > "${TARBALL}.sha256"
|
|
|
|
- name: Upload artifact
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: binary-${{ matrix.target }}
|
|
path: |
|
|
agent-desktop-v*.tar.gz
|
|
agent-desktop-v*.tar.gz.sha256
|
|
retention-days: 1
|
|
|
|
# FFI cdylib for language-binding consumers (Python/Swift/Go/Ruby/Node/C).
|
|
# Ships alongside CLI tarballs on the same release tag; npm stays CLI-only.
|
|
build-ffi:
|
|
name: Build FFI (${{ matrix.target }})
|
|
needs: release-please
|
|
if: needs.release-please.outputs.release_created == 'true'
|
|
runs-on: ${{ matrix.runner }}
|
|
timeout-minutes: 30
|
|
permissions:
|
|
contents: read
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- target: aarch64-apple-darwin
|
|
runner: macos-latest
|
|
archive: tar.gz
|
|
lib_name: libagent_desktop_ffi.dylib
|
|
- target: x86_64-apple-darwin
|
|
runner: macos-latest
|
|
archive: tar.gz
|
|
lib_name: libagent_desktop_ffi.dylib
|
|
- target: x86_64-unknown-linux-gnu
|
|
runner: ubuntu-22.04
|
|
archive: tar.gz
|
|
lib_name: libagent_desktop_ffi.so
|
|
- target: aarch64-unknown-linux-gnu
|
|
runner: ubuntu-22.04-arm
|
|
archive: tar.gz
|
|
lib_name: libagent_desktop_ffi.so
|
|
- target: x86_64-pc-windows-msvc
|
|
runner: windows-latest
|
|
archive: zip
|
|
lib_name: agent_desktop_ffi.dll
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
ref: ${{ needs.release-please.outputs.tag_name }}
|
|
|
|
- name: Install Rust toolchain
|
|
run: |
|
|
rustup show
|
|
rustup target add ${{ matrix.target }}
|
|
|
|
- name: Cache cargo registry
|
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
|
with:
|
|
path: |
|
|
~/.cargo/registry/index/
|
|
~/.cargo/registry/cache/
|
|
~/.cargo/git/db/
|
|
key: ${{ runner.os }}-${{ matrix.target }}-ffi-cargo-${{ hashFiles('**/Cargo.toml', 'Cargo.lock') }}
|
|
restore-keys: ${{ runner.os }}-${{ matrix.target }}-ffi-cargo-
|
|
|
|
- name: Build FFI cdylib (release-ffi profile)
|
|
run: cargo build --locked --profile release-ffi -p agent-desktop-ffi --target ${{ matrix.target }}
|
|
|
|
# build.rs bakes install_name=@rpath/... ; a regression here silently
|
|
# breaks Swift/SPM consumers, so verify before shipping.
|
|
- name: Verify macOS install_name
|
|
if: runner.os == 'macOS'
|
|
shell: bash
|
|
run: |
|
|
ACTUAL=$(otool -D target/${{ matrix.target }}/release-ffi/${{ matrix.lib_name }} | tail -1)
|
|
EXPECTED="@rpath/libagent_desktop_ffi.dylib"
|
|
if [ "$ACTUAL" != "$EXPECTED" ]; then
|
|
echo "FAIL: install_name is '$ACTUAL', expected '$EXPECTED'"
|
|
exit 1
|
|
fi
|
|
echo "OK: install_name = $ACTUAL"
|
|
|
|
- name: Stage tarball contents
|
|
shell: bash
|
|
run: |
|
|
VERSION=${{ needs.release-please.outputs.version }}
|
|
STAGE="agent-desktop-ffi-v${VERSION}-${{ matrix.target }}"
|
|
mkdir -p "$STAGE/lib" "$STAGE/include"
|
|
cp "target/${{ matrix.target }}/release-ffi/${{ matrix.lib_name }}" "$STAGE/lib/"
|
|
cp crates/ffi/include/agent_desktop.h "$STAGE/include/"
|
|
cp LICENSE "$STAGE/"
|
|
cat > "$STAGE/README.md" <<EOF
|
|
# agent-desktop FFI cdylib — v${VERSION} (${{ matrix.target }})
|
|
|
|
- \`lib/${{ matrix.lib_name }}\` — shared library
|
|
- \`include/agent_desktop.h\` — cbindgen header
|
|
- \`LICENSE\` — Apache-2.0
|
|
|
|
Docs: https://github.com/lahfir/agent-desktop/tree/main/skills/agent-desktop-ffi
|
|
|
|
macOS \`install_name\` is \`@rpath/libagent_desktop_ffi.dylib\` — linking
|
|
consumers need \`-rpath\` pointing at the dylib's directory; \`dlopen\` /
|
|
ctypes callers resolve by path and need nothing.
|
|
EOF
|
|
echo "STAGE=$STAGE" >> "$GITHUB_ENV"
|
|
|
|
- name: Create archive (Unix)
|
|
if: runner.os != 'Windows'
|
|
shell: bash
|
|
run: |
|
|
tar -czf "${STAGE}.tar.gz" "${STAGE}"
|
|
shasum -a 256 "${STAGE}.tar.gz" > "${STAGE}.tar.gz.sha256"
|
|
|
|
- name: Create archive (Windows)
|
|
if: runner.os == 'Windows'
|
|
shell: pwsh
|
|
run: |
|
|
Compress-Archive -Path "${env:STAGE}" -DestinationPath "${env:STAGE}.zip"
|
|
$hash = (Get-FileHash "${env:STAGE}.zip" -Algorithm SHA256).Hash.ToLower()
|
|
"$hash ${env:STAGE}.zip" | Out-File -Encoding ascii "${env:STAGE}.zip.sha256"
|
|
|
|
- name: Upload artifact
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: ffi-${{ matrix.target }}
|
|
path: |
|
|
agent-desktop-ffi-v*.tar.gz
|
|
agent-desktop-ffi-v*.tar.gz.sha256
|
|
agent-desktop-ffi-v*.zip
|
|
agent-desktop-ffi-v*.zip.sha256
|
|
retention-days: 1
|
|
|
|
publish-github:
|
|
name: Publish to GitHub Release
|
|
needs: [release-please, build, build-ffi]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
permissions:
|
|
contents: write
|
|
id-token: write # Sigstore OIDC exchange
|
|
attestations: write # write provenance bundle back to GitHub
|
|
steps:
|
|
- name: Download all artifacts
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
pattern: "{binary,ffi}-*"
|
|
merge-multiple: true
|
|
|
|
- name: Create checksums file
|
|
run: |
|
|
cat ./*.sha256 > checksums.txt
|
|
cat checksums.txt
|
|
|
|
- name: Upload assets to GitHub Release
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
GH_REPO: ${{ github.repository }}
|
|
run: |
|
|
gh release upload ${{ needs.release-please.outputs.tag_name }} \
|
|
./*.tar.gz ./*.zip checksums.txt \
|
|
--clobber
|
|
|
|
# Keyless Sigstore provenance. Verify with:
|
|
# gh attestation verify <file> --repo lahfir/agent-desktop
|
|
- name: Attest build provenance
|
|
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
|
|
with:
|
|
subject-path: |
|
|
*.tar.gz
|
|
*.zip
|
|
checksums.txt
|
|
|
|
publish-npm:
|
|
name: Publish to npm
|
|
needs: [release-please, publish-github]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
permissions:
|
|
contents: read
|
|
id-token: write
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
ref: ${{ needs.release-please.outputs.tag_name }}
|
|
|
|
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
|
with:
|
|
node-version: '24'
|
|
registry-url: https://registry.npmjs.org
|
|
|
|
- name: Verify GitHub Release assets
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
# Expected: 2 CLI tarballs + 4 FFI tarballs + 1 FFI zip + checksums = 8.
|
|
ASSETS=$(gh release view ${{ needs.release-please.outputs.tag_name }} --json assets --jq '.assets | length')
|
|
if [ "$ASSETS" -lt 8 ]; then
|
|
echo "FAIL: expected at least 8 assets, found ${ASSETS}"
|
|
exit 1
|
|
fi
|
|
echo "OK: ${ASSETS} assets found on release"
|
|
|
|
- name: Prepare npm package
|
|
run: |
|
|
VERSION=${{ needs.release-please.outputs.version }}
|
|
cd npm
|
|
node -e "
|
|
const pkg = require('./package.json');
|
|
pkg.version = '${VERSION}';
|
|
require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
|
|
"
|
|
echo "npm package version set to ${VERSION}"
|
|
cp ../README.md README.md
|
|
cp ../CHANGELOG.md CHANGELOG.md 2>/dev/null || true
|
|
|
|
- name: Publish to npm
|
|
run: npm publish --provenance --access public
|
|
working-directory: npm
|
|
env:
|
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
|
|
publish-skills:
|
|
name: Publish Skills to ClawHub
|
|
needs: [release-please]
|
|
if: needs.release-please.outputs.release_created == 'true'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
ref: ${{ needs.release-please.outputs.tag_name }}
|
|
|
|
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
|
with:
|
|
node-version: '24'
|
|
|
|
- name: Install ClawHub CLI
|
|
run: npm i -g clawhub
|
|
|
|
- name: Authenticate with ClawHub
|
|
run: clawhub login --token "$CLAWHUB_TOKEN" --no-browser
|
|
env:
|
|
CLAWHUB_TOKEN: ${{ secrets.CLAWHUB_TOKEN }}
|
|
|
|
- name: Publish all skills to ClawHub
|
|
run: |
|
|
clawhub sync \
|
|
--root skills/ \
|
|
--all \
|
|
--bump patch \
|
|
--changelog "Release ${{ needs.release-please.outputs.tag_name }}"
|