-
Notifications
You must be signed in to change notification settings - Fork 4.4k
357 lines (321 loc) · 13.9 KB
/
Copy pathrelease.yml
File metadata and controls
357 lines (321 loc) · 13.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
name: Release
on:
push:
tags:
- 'v[0-9]*'
workflow_dispatch:
inputs:
version:
description: "Semver version (e.g. 0.4.0) — only for manual runs"
required: true
ref:
description: "Branch, tag, or SHA to build — only for manual runs"
default: main
required: true
jobs:
release:
name: Release
runs-on: macos-latest
timeout-minutes: 60
permissions:
contents: write
id-token: write # required by block/apple-codesign-action for OIDC
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Determine version
id: version
env:
EVENT_NAME: ${{ github.event_name }}
INPUT_VERSION: ${{ inputs.version }}
run: |
if [[ "$EVENT_NAME" == "push" ]]; then
VERSION="${GITHUB_REF_NAME#v}"
else
VERSION="$INPUT_VERSION"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Validate version
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
echo "::error::Invalid version '$VERSION'. Expected semver (e.g. 0.4.0 or 1.0.0-beta.1)"
exit 1
fi
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }}
persist-credentials: false
- uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
- name: Install desktop dependencies
run: just desktop-install-ci
- name: Patch version
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
cd desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd src-tauri && cargo update --workspace
- name: Generate release config
run: cd desktop && node scripts/build-release-config.mjs
env:
SPROUT_UPDATER_PUBLIC_KEY: ${{ secrets.SPROUT_UPDATER_PUBLIC_KEY }}
SPROUT_UPDATER_ENDPOINT: https://github.com/block/sprout/releases/download/sprout-desktop-latest/latest.json
- name: Build sidecars
run: |
cargo build --release -p sprout-acp -p sprout-mcp -p sprout-agent -p sprout-dev-mcp -p git-credential-nostr -p sprout-cli
./scripts/bundle-sidecars.sh
# Mesh rev derived from Cargo.lock (no lockstep edit on dep bump); cache key tracks it.
- name: Resolve mesh-llm rev
id: mesh_rev
run: |
set -euo pipefail
REV=$(grep -oE 'mesh-llm\.git\?rev=[0-9a-f]{40}' Cargo.lock | head -1 | grep -oE '[0-9a-f]{40}')
[[ -n "$REV" ]] || { echo "::error::could not resolve mesh-llm rev from Cargo.lock"; exit 1; }
echo "rev=$REV" >> "$GITHUB_OUTPUT"
echo "short=${REV:0:7}" >> "$GITHUB_OUTPUT"
- name: Restore mesh llama build cache
id: llama_cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ github.workspace }}/.cache/mesh-llama
key: mesh-llama-${{ runner.os }}-metal-${{ steps.mesh_rev.outputs.rev }}
- name: Build mesh llama native libraries
if: steps.llama_cache.outputs.cache-hit != 'true'
env:
MESH_REV_SHORT: ${{ steps.mesh_rev.outputs.short }}
run: |
set -euo pipefail
cargo fetch --manifest-path desktop/src-tauri/Cargo.toml
SHORT="$MESH_REV_SHORT"
MESH_ROOT=$(find "${CARGO_HOME:-$HOME/.cargo}/git/checkouts" -path "*/$SHORT" -type d -name "$SHORT" | head -1)
if [[ -z "$MESH_ROOT" ]]; then
echo "::error::mesh-llm checkout for $SHORT not found after cargo fetch"
exit 1
fi
export LLAMA_STAGE_BACKEND=metal
export LLAMA_STAGE_BUILD_DIR="$GITHUB_WORKSPACE/.cache/mesh-llama/build-stage-abi-metal"
export CMAKE_OSX_DEPLOYMENT_TARGET=10.15
"$MESH_ROOT/scripts/prepare-llama.sh" pinned
"$MESH_ROOT/scripts/build-llama.sh" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15
- name: Save mesh llama build cache
if: steps.llama_cache.outputs.cache-hit != 'true'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ github.workspace }}/.cache/mesh-llama
key: mesh-llama-${{ runner.os }}-metal-${{ steps.mesh_rev.outputs.rev }}
- name: Build unsigned Tauri app
run: cd desktop && pnpm tauri build --verbose --no-sign --config src-tauri/tauri.release.conf.json
env:
SPROUT_UPDATER_PUBLIC_KEY: ${{ secrets.SPROUT_UPDATER_PUBLIC_KEY }}
SPROUT_UPDATER_ENDPOINT: https://github.com/block/sprout/releases/download/sprout-desktop-latest/latest.json
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
MACOSX_DEPLOYMENT_TARGET: "10.15"
CMAKE_OSX_DEPLOYMENT_TARGET: "10.15"
LLAMA_STAGE_BACKEND: metal
LLAMA_STAGE_BUILD_DIR: ${{ github.workspace }}/.cache/mesh-llama/build-stage-abi-metal
SKIPPY_LLAMA_AUTO_BUILD: "0"
- name: Locate unsigned DMG
id: unsigned
run: |
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
DMG=$(find "$BUNDLE_DIR/dmg" -name '*.dmg' -type f | head -1)
if [[ -z "$DMG" ]]; then
echo "::error::No DMG found in $BUNDLE_DIR/dmg"
exit 1
fi
echo "dmg=$DMG" >> "$GITHUB_OUTPUT"
- name: Codesign and Notarize
id: codesign
uses: block/apple-codesign-action@679535d1ab7c5a7c18e6f9afcba3464512cc3dde # v1.1.0
with:
osx-codesign-role: ${{ secrets.OSX_CODESIGN_ROLE }}
codesign-s3-bucket: ${{ secrets.CODESIGN_S3_BUCKET }}
unsigned-artifact-path: ${{ steps.unsigned.outputs.dmg }}
entitlements-plist-path: desktop/src-tauri/Entitlements.plist
artifact-name: sprout-${{ github.sha }}-${{ github.run_id }}-arm64
- name: Replace DMG and rebuild updater archive
env:
SIGNED_DMG: ${{ steps.codesign.outputs.signed-dmg-path }}
SIGNED_APP_ZIP: ${{ steps.codesign.outputs.signed-artifact-path }}
UNSIGNED_DMG: ${{ steps.unsigned.outputs.dmg }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
set -euo pipefail
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
APP_DIR="${BUNDLE_DIR}/macos"
# Replace unsigned DMG with the signed/notarized one.
cp "$SIGNED_DMG" "$UNSIGNED_DMG"
# Swap the unsigned .app for the signed .app extracted from the action's zip.
EXTRACT_DIR="${RUNNER_TEMP}/signed-app-extract"
rm -rf "$EXTRACT_DIR" && mkdir -p "$EXTRACT_DIR"
ditto -x -k "$SIGNED_APP_ZIP" "$EXTRACT_DIR"
rm -rf "${APP_DIR}/Sprout.app"
cp -R "${EXTRACT_DIR}/Sprout.app" "${APP_DIR}/Sprout.app"
# Rebuild the updater archive from the signed .app and re-sign it with the Tauri updater key.
rm -f "${APP_DIR}/Sprout.app.tar.gz" "${APP_DIR}/Sprout.app.tar.gz.sig"
(cd "$APP_DIR" && tar -czf Sprout.app.tar.gz Sprout.app)
TARBALL_ABS="$(pwd)/${APP_DIR}/Sprout.app.tar.gz"
(cd desktop && pnpm tauri signer sign "$TARBALL_ABS")
- name: Verify code signature
run: |
codesign --verify --deep --strict --verbose=2 \
desktop/src-tauri/target/release/bundle/macos/Sprout.app
spctl --assess --type execute --verbose=4 \
desktop/src-tauri/target/release/bundle/macos/Sprout.app
- name: Locate build artifacts
id: artifacts
run: |
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
# Find the DMG (Tauri names it Sprout_<version>_<arch>.dmg)
DMG=$(find "$BUNDLE_DIR/dmg" -name '*.dmg' -type f | head -1)
if [[ -z "$DMG" ]]; then
echo "::error::No DMG found in $BUNDLE_DIR/dmg"
exit 1
fi
echo "dmg=$DMG" >> "$GITHUB_OUTPUT"
# Find the updater .tar.gz and .sig
ARCHIVE=$(find "$BUNDLE_DIR/macos" -name '*.tar.gz' ! -name '*.sig' -type f | head -1)
SIG="${ARCHIVE}.sig"
if [[ -z "$ARCHIVE" || ! -f "$SIG" ]]; then
echo "::error::Updater archive or signature not found in $BUNDLE_DIR/macos"
exit 1
fi
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
echo "archive_name=$(basename "$ARCHIVE")" >> "$GITHUB_OUTPUT"
echo "sig=$SIG" >> "$GITHUB_OUTPUT"
- name: Generate latest.json
run: |
bash desktop/scripts/generate-oss-latest-json.sh \
"$VERSION" \
"$SIG_PATH" \
"https://github.com/block/sprout/releases/download/sprout-desktop-latest/$ARCHIVE_NAME" \
> latest.json
cat latest.json
env:
VERSION: ${{ steps.version.outputs.version }}
SIG_PATH: ${{ steps.artifacts.outputs.sig }}
ARCHIVE_NAME: ${{ steps.artifacts.outputs.archive_name }}
- name: Create versioned GitHub release
env:
VERSION: ${{ steps.version.outputs.version }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DMG_PATH: ${{ steps.artifacts.outputs.dmg }}
run: |
RELEASE_SHA=$(git rev-parse HEAD)
NOTES=""
if [[ -f CHANGELOG.md ]]; then
NOTES=$(awk "/^## v${VERSION}$/,/^## v/" CHANGELOG.md | sed '$d')
fi
if [[ -z "$NOTES" ]]; then
NOTES="Sprout Desktop v${VERSION}"
fi
gh release create "v${VERSION}" \
--target "$RELEASE_SHA" \
--title "Sprout Desktop v${VERSION}" \
--notes "$NOTES" \
"$DMG_PATH"
- name: Update rolling release for auto-updater
run: |
gh release create sprout-desktop-latest \
--prerelease \
--title "Sprout Desktop Auto-Update" \
--notes "Rolling release for the Tauri auto-updater. Do not download manually — use the versioned release instead." \
2>/dev/null || true
gh release upload sprout-desktop-latest \
latest.json \
"$ARCHIVE_PATH" \
"$SIG_PATH" \
--clobber
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ARCHIVE_PATH: ${{ steps.artifacts.outputs.archive }}
SIG_PATH: ${{ steps.artifacts.outputs.sig }}
release-linux:
name: Release Linux
runs-on: ubuntu-latest
needs: release
timeout-minutes: 60
permissions:
contents: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }}
persist-credentials: false
- uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: desktop/src-tauri
lookup-only: true
- name: Install Tauri dependencies (Linux)
env:
DEBIAN_FRONTEND: noninteractive
run: |
sudo apt-get update \
-o Acquire::Retries=3 \
-o Acquire::http::Timeout=30 \
-o Acquire::https::Timeout=30
sudo apt-get install -y --no-install-recommends \
-o Acquire::Retries=3 \
-o Acquire::http::Timeout=30 \
-o Acquire::https::Timeout=30 \
-o DPkg::Lock::Timeout=120 \
build-essential \
curl \
file \
libasound2-dev \
libayatana-appindicator3-dev \
libgtk-3-dev \
librsvg2-dev \
libssl-dev \
libwebkit2gtk-4.1-dev \
libxdo-dev \
patchelf \
wget
- name: Install desktop dependencies
run: just desktop-install-ci
- name: Patch version
env:
VERSION: ${{ needs.release.outputs.version }}
run: |
cd desktop && node scripts/set-version-from-tag.mjs "$VERSION"
cd src-tauri && cargo update --workspace
- name: Build sidecars
run: |
cargo build --release -p sprout-acp -p sprout-mcp -p sprout-agent -p sprout-dev-mcp -p git-credential-nostr -p sprout-cli
./scripts/bundle-sidecars.sh
- name: Build Linux Tauri app
run: cd desktop && pnpm tauri build --verbose --ci --bundles deb,appimage
env:
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
- name: Locate Linux build artifacts
id: linux-artifacts
run: |
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
DEB=$(find "$BUNDLE_DIR/deb" -name '*.deb' -type f | head -1)
if [[ -z "$DEB" ]]; then
echo "::error::No DEB found in $BUNDLE_DIR/deb"
exit 1
fi
echo "deb=$DEB" >> "$GITHUB_OUTPUT"
APPIMAGE=$(find "$BUNDLE_DIR/appimage" -name '*.AppImage' -type f | head -1)
if [[ -z "$APPIMAGE" ]]; then
echo "::error::No AppImage found in $BUNDLE_DIR/appimage"
exit 1
fi
echo "appimage=$APPIMAGE" >> "$GITHUB_OUTPUT"
- name: Upload Linux artifacts to versioned GitHub release
env:
VERSION: ${{ needs.release.outputs.version }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEB_PATH: ${{ steps.linux-artifacts.outputs.deb }}
APPIMAGE_PATH: ${{ steps.linux-artifacts.outputs.appimage }}
run: |
gh release upload "v$VERSION" \
"$DEB_PATH" \
"$APPIMAGE_PATH" \
--clobber