Skip to content

feat(safeclaw): onboarding, slash commands, CI release workflow #2

feat(safeclaw): onboarding, slash commands, CI release workflow

feat(safeclaw): onboarding, slash commands, CI release workflow #2

name: SafeClaw Release

Check failure on line 1 in .github/workflows/safeclaw-release.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/safeclaw-release.yml

Invalid workflow file

(Line: 64, Col: 13): Unrecognized named-value: 'secrets'. Located at position 1 within expression: secrets.APPLE_CERTIFICATE != ''
on:
push:
tags:
- "safeclaw-v*"
workflow_dispatch:
inputs:
tag:
description: "Tag to release (e.g. safeclaw-v0.1.0)"
required: true
permissions:
contents: write
jobs:
build-dmg:
name: Build Universal macOS DMG
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Resolve version
id: version
run: |
TAG="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
VERSION="${TAG#safeclaw-v}"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: latest
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
cache-dependency-path: apps/safeclaw/pnpm-lock.yaml
- name: Install Rust (stable)
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-darwin,x86_64-apple-darwin
- uses: Swatinem/rust-cache@v2
with:
workspaces: apps/safeclaw/src-tauri -> target
cache-on-failure: true
- name: Install system dependencies
run: brew install cmake protobuf
- name: Install frontend dependencies
working-directory: apps/safeclaw
run: pnpm install --frozen-lockfile
- name: Import Apple certificate
if: ${{ secrets.APPLE_CERTIFICATE != '' }}
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
run: |
KEYCHAIN_PASSWORD="$(openssl rand -hex 16)"
KEYCHAIN_PATH="$RUNNER_TEMP/signing.keychain-db"
echo "$APPLE_CERTIFICATE" | base64 --decode > "$RUNNER_TEMP/cert.p12"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security import "$RUNNER_TEMP/cert.p12" \
-k "$KEYCHAIN_PATH" \
-P "$APPLE_CERTIFICATE_PASSWORD" \
-T /usr/bin/codesign \
-T /usr/bin/productsign
security list-keychain -d user -s "$KEYCHAIN_PATH"
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
- name: Build universal DMG
working-directory: apps/safeclaw
env:
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
ORT_STRATEGY: download
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: pnpm tauri build --target universal-apple-darwin
- name: Locate DMG
id: dmg
working-directory: apps/safeclaw
run: |
DMG_PATH="$(find src-tauri/target/universal-apple-darwin/release/bundle/dmg -name '*.dmg' | head -1)"
echo "path=$DMG_PATH" >> "$GITHUB_OUTPUT"
echo "name=$(basename "$DMG_PATH")" >> "$GITHUB_OUTPUT"
- name: Upload DMG artifact
uses: actions/upload-artifact@v4
with:
name: safeclaw-dmg
path: apps/safeclaw/${{ steps.dmg.outputs.path }}
if-no-files-found: error
github-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: build-dmg
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
sha256: ${{ steps.sha256.outputs.sha256 }}
dmg_name: ${{ steps.artifact.outputs.dmg_name }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve version
id: version
run: |
TAG="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
VERSION="${TAG#safeclaw-v}"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Download DMG artifact
uses: actions/download-artifact@v4
with:
name: safeclaw-dmg
path: dist
- name: Get DMG name and SHA256
id: sha256
run: |
DMG_PATH="$(find dist -name '*.dmg' | head -1)"
SHA256="$(shasum -a 256 "$DMG_PATH" | awk '{print $1}')"
echo "sha256=$SHA256" >> "$GITHUB_OUTPUT"
echo "dmg_path=$DMG_PATH" >> "$GITHUB_OUTPUT"
- name: Get DMG artifact name
id: artifact
run: |
DMG_PATH="$(find dist -name '*.dmg' | head -1)"
echo "dmg_name=$(basename "$DMG_PATH")" >> "$GITHUB_OUTPUT"
- name: Generate release notes
run: |
PREV_TAG=$(git tag --sort=-v:refname | grep '^safeclaw-v' | head -2 | tail -1)
if [ -z "$PREV_TAG" ]; then
NOTES="Initial release of SafeClaw."
else
NOTES=$(git log "${PREV_TAG}..HEAD" --oneline --no-merges --pretty=format:"- %s" | head -50)
fi
echo "$NOTES" > /tmp/release-notes.md
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
run: |
DMG_PATH="$(find dist -name '*.dmg' | head -1)"
if gh release view "$TAG" &>/dev/null; then
gh release upload "$TAG" "$DMG_PATH" --clobber
else
gh release create "$TAG" "$DMG_PATH" \
--title "SafeClaw $TAG" \
--notes-file /tmp/release-notes.md
fi
update-homebrew-cask:
name: Update Homebrew Cask
runs-on: ubuntu-latest
needs: github-release
steps:
- name: Checkout homebrew-tap
uses: actions/checkout@v4
with:
repository: A3S-Lab/homebrew-tap
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
path: homebrew-tap
- name: Write Cask
env:
VERSION: ${{ needs.github-release.outputs.version }}
TAG: ${{ needs.github-release.outputs.tag }}
SHA256: ${{ needs.github-release.outputs.sha256 }}
DMG_NAME: ${{ needs.github-release.outputs.dmg_name }}
run: |
mkdir -p homebrew-tap/Casks
cat > homebrew-tap/Casks/safeclaw.rb <<CASK
# typed: false
# frozen_string_literal: true
cask "safeclaw" do
version "${VERSION}"
sha256 "${SHA256}"
url "https://github.com/A3S-Lab/a3s/releases/download/${TAG}/${DMG_NAME}"
name "SafeClaw"
desc "Secure personal AI assistant with TEE support"
homepage "https://github.com/A3S-Lab/a3s"
depends_on macos: ">= :ventura"
app "SafeClaw.app"
zap trash: [
"~/Library/Application Support/com.a3s.safeclaw",
"~/Library/Caches/com.a3s.safeclaw",
"~/Library/Preferences/com.a3s.safeclaw.plist",
"~/Library/Logs/com.a3s.safeclaw",
]
end
CASK
# Strip leading whitespace from heredoc indentation
sed -i 's/^ //' homebrew-tap/Casks/safeclaw.rb
- name: Commit and push
working-directory: homebrew-tap
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Casks/safeclaw.rb
git diff --cached --quiet || git commit -m "chore: update SafeClaw cask to ${{ needs.github-release.outputs.tag }}"
git push