Skip to content

Commit 4a943db

Browse files
RoyLinRoyLin
authored andcommitted
feat(safeclaw): onboarding, slash commands, CI release workflow
- Add first-launch onboarding page with avatar and nickname setup - User avatar: click to edit, no white border, horizontally centered - Hide workflow/agent-marketplace nav items and upload-file button - Remove 视频分析专家 and 文档审核专家 built-in personas - Integrate a3s-code v1.3.4 per-session CronScheduler for /loop, /cron-list, /cron-cancel - Add cron drainer background task to inject scheduled prompts into sessions - Add .github/workflows/safeclaw-release.yml: universal macOS DMG build, GitHub Release, Homebrew Cask update - Add homebrew-tap/Casks/safeclaw.rb initial Cask
1 parent 935dcd4 commit 4a943db

16 files changed

Lines changed: 667 additions & 126 deletions

File tree

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
name: SafeClaw Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "safeclaw-v*"
7+
workflow_dispatch:
8+
inputs:
9+
tag:
10+
description: "Tag to release (e.g. safeclaw-v0.1.0)"
11+
required: true
12+
13+
permissions:
14+
contents: write
15+
16+
jobs:
17+
build-dmg:
18+
name: Build Universal macOS DMG
19+
runs-on: macos-latest
20+
21+
steps:
22+
- uses: actions/checkout@v4
23+
with:
24+
submodules: recursive
25+
26+
- name: Resolve version
27+
id: version
28+
run: |
29+
TAG="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
30+
VERSION="${TAG#safeclaw-v}"
31+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
32+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
33+
34+
- name: Setup pnpm
35+
uses: pnpm/action-setup@v4
36+
with:
37+
version: latest
38+
39+
- name: Setup Node.js
40+
uses: actions/setup-node@v4
41+
with:
42+
node-version: 22
43+
cache: pnpm
44+
cache-dependency-path: apps/safeclaw/pnpm-lock.yaml
45+
46+
- name: Install Rust (stable)
47+
uses: dtolnay/rust-toolchain@stable
48+
with:
49+
targets: aarch64-apple-darwin,x86_64-apple-darwin
50+
51+
- uses: Swatinem/rust-cache@v2
52+
with:
53+
workspaces: apps/safeclaw/src-tauri -> target
54+
cache-on-failure: true
55+
56+
- name: Install system dependencies
57+
run: brew install cmake protobuf
58+
59+
- name: Install frontend dependencies
60+
working-directory: apps/safeclaw
61+
run: pnpm install --frozen-lockfile
62+
63+
- name: Import Apple certificate
64+
if: ${{ secrets.APPLE_CERTIFICATE != '' }}
65+
env:
66+
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
67+
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
68+
run: |
69+
KEYCHAIN_PASSWORD="$(openssl rand -hex 16)"
70+
KEYCHAIN_PATH="$RUNNER_TEMP/signing.keychain-db"
71+
echo "$APPLE_CERTIFICATE" | base64 --decode > "$RUNNER_TEMP/cert.p12"
72+
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
73+
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
74+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
75+
security import "$RUNNER_TEMP/cert.p12" \
76+
-k "$KEYCHAIN_PATH" \
77+
-P "$APPLE_CERTIFICATE_PASSWORD" \
78+
-T /usr/bin/codesign \
79+
-T /usr/bin/productsign
80+
security list-keychain -d user -s "$KEYCHAIN_PATH"
81+
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
82+
83+
- name: Build universal DMG
84+
working-directory: apps/safeclaw
85+
env:
86+
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
87+
ORT_STRATEGY: download
88+
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
89+
APPLE_ID: ${{ secrets.APPLE_ID }}
90+
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
91+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
92+
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
93+
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
94+
run: pnpm tauri build --target universal-apple-darwin
95+
96+
- name: Locate DMG
97+
id: dmg
98+
working-directory: apps/safeclaw
99+
run: |
100+
DMG_PATH="$(find src-tauri/target/universal-apple-darwin/release/bundle/dmg -name '*.dmg' | head -1)"
101+
echo "path=$DMG_PATH" >> "$GITHUB_OUTPUT"
102+
echo "name=$(basename "$DMG_PATH")" >> "$GITHUB_OUTPUT"
103+
104+
- name: Upload DMG artifact
105+
uses: actions/upload-artifact@v4
106+
with:
107+
name: safeclaw-dmg
108+
path: apps/safeclaw/${{ steps.dmg.outputs.path }}
109+
if-no-files-found: error
110+
111+
github-release:
112+
name: Create GitHub Release
113+
runs-on: ubuntu-latest
114+
needs: build-dmg
115+
outputs:
116+
version: ${{ steps.version.outputs.version }}
117+
tag: ${{ steps.version.outputs.tag }}
118+
sha256: ${{ steps.sha256.outputs.sha256 }}
119+
dmg_name: ${{ steps.artifact.outputs.dmg_name }}
120+
121+
steps:
122+
- uses: actions/checkout@v4
123+
with:
124+
fetch-depth: 0
125+
126+
- name: Resolve version
127+
id: version
128+
run: |
129+
TAG="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
130+
VERSION="${TAG#safeclaw-v}"
131+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
132+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
133+
134+
- name: Download DMG artifact
135+
uses: actions/download-artifact@v4
136+
with:
137+
name: safeclaw-dmg
138+
path: dist
139+
140+
- name: Get DMG name and SHA256
141+
id: sha256
142+
run: |
143+
DMG_PATH="$(find dist -name '*.dmg' | head -1)"
144+
SHA256="$(shasum -a 256 "$DMG_PATH" | awk '{print $1}')"
145+
echo "sha256=$SHA256" >> "$GITHUB_OUTPUT"
146+
echo "dmg_path=$DMG_PATH" >> "$GITHUB_OUTPUT"
147+
148+
- name: Get DMG artifact name
149+
id: artifact
150+
run: |
151+
DMG_PATH="$(find dist -name '*.dmg' | head -1)"
152+
echo "dmg_name=$(basename "$DMG_PATH")" >> "$GITHUB_OUTPUT"
153+
154+
- name: Generate release notes
155+
run: |
156+
PREV_TAG=$(git tag --sort=-v:refname | grep '^safeclaw-v' | head -2 | tail -1)
157+
if [ -z "$PREV_TAG" ]; then
158+
NOTES="Initial release of SafeClaw."
159+
else
160+
NOTES=$(git log "${PREV_TAG}..HEAD" --oneline --no-merges --pretty=format:"- %s" | head -50)
161+
fi
162+
echo "$NOTES" > /tmp/release-notes.md
163+
164+
- name: Create GitHub Release
165+
env:
166+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
167+
TAG: ${{ steps.version.outputs.tag }}
168+
run: |
169+
DMG_PATH="$(find dist -name '*.dmg' | head -1)"
170+
if gh release view "$TAG" &>/dev/null; then
171+
gh release upload "$TAG" "$DMG_PATH" --clobber
172+
else
173+
gh release create "$TAG" "$DMG_PATH" \
174+
--title "SafeClaw $TAG" \
175+
--notes-file /tmp/release-notes.md
176+
fi
177+
178+
update-homebrew-cask:
179+
name: Update Homebrew Cask
180+
runs-on: ubuntu-latest
181+
needs: github-release
182+
183+
steps:
184+
- name: Checkout homebrew-tap
185+
uses: actions/checkout@v4
186+
with:
187+
repository: A3S-Lab/homebrew-tap
188+
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
189+
path: homebrew-tap
190+
191+
- name: Write Cask
192+
env:
193+
VERSION: ${{ needs.github-release.outputs.version }}
194+
TAG: ${{ needs.github-release.outputs.tag }}
195+
SHA256: ${{ needs.github-release.outputs.sha256 }}
196+
DMG_NAME: ${{ needs.github-release.outputs.dmg_name }}
197+
run: |
198+
mkdir -p homebrew-tap/Casks
199+
cat > homebrew-tap/Casks/safeclaw.rb <<CASK
200+
# typed: false
201+
# frozen_string_literal: true
202+
203+
cask "safeclaw" do
204+
version "${VERSION}"
205+
sha256 "${SHA256}"
206+
207+
url "https://github.com/A3S-Lab/a3s/releases/download/${TAG}/${DMG_NAME}"
208+
name "SafeClaw"
209+
desc "Secure personal AI assistant with TEE support"
210+
homepage "https://github.com/A3S-Lab/a3s"
211+
212+
depends_on macos: ">= :ventura"
213+
214+
app "SafeClaw.app"
215+
216+
zap trash: [
217+
"~/Library/Application Support/com.a3s.safeclaw",
218+
"~/Library/Caches/com.a3s.safeclaw",
219+
"~/Library/Preferences/com.a3s.safeclaw.plist",
220+
"~/Library/Logs/com.a3s.safeclaw",
221+
]
222+
end
223+
CASK
224+
# Strip leading whitespace from heredoc indentation
225+
sed -i 's/^ //' homebrew-tap/Casks/safeclaw.rb
226+
227+
- name: Commit and push
228+
working-directory: homebrew-tap
229+
run: |
230+
git config user.name "github-actions[bot]"
231+
git config user.email "github-actions[bot]@users.noreply.github.com"
232+
git add Casks/safeclaw.rb
233+
git diff --cached --quiet || git commit -m "chore: update SafeClaw cask to ${{ needs.github-release.outputs.tag }}"
234+
git push

0 commit comments

Comments
 (0)