-
Notifications
You must be signed in to change notification settings - Fork 17
242 lines (218 loc) · 9.47 KB
/
Copy pathrelease.yml
File metadata and controls
242 lines (218 loc) · 9.47 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
name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
permissions:
contents: write
jobs:
release:
strategy:
fail-fast: false
matrix:
include:
- platform: windows-latest
args: ''
label: Windows x64
- platform: macos-latest
args: '--target aarch64-apple-darwin'
label: macOS Apple Silicon
- platform: ubuntu-22.04
args: ''
label: Linux x64
- platform: ubuntu-22.04-arm
args: ''
label: Linux arm64
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Resolve release tag
id: tag
shell: bash
run: |
# Tag-pushes give refs/tags/v1.2.3; workflow_dispatch falls back to
# the latest tag in the working tree. Bare version (no leading "v")
# is what tauri stamps into bundle filenames; both forms are
# exposed for downstream steps.
TAG="${GITHUB_REF#refs/tags/}"
if [ "$TAG" = "$GITHUB_REF" ]; then
TAG=$(git describe --tags --abbrev=0)
fi
VERSION="${TAG#v}"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Install system dependencies (Linux)
if: startsWith(matrix.platform, 'ubuntu-')
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf rpm
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
cache-dependency-path: src-ui/package-lock.json
- name: Install & build frontend
run: |
cd src-ui
npm ci
npm run build
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ contains(matrix.args, 'aarch64-apple-darwin') && 'aarch64-apple-darwin' || '' }}
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
key: ${{ matrix.label }}
- name: Install Tauri CLI
run: cargo install tauri-cli --version "^2.0" --locked
# ── macOS: build .app, re-sign without hardened runtime, then bundle DMG ──
# tauri-bundler's default codesign for adhoc identities adds
# --options runtime, which without Apple notarization causes Gatekeeper
# to silently reject the GUI launch (click icon, nothing happens). We
# build the .app first, codesign --deep WITHOUT runtime, then bundle
# the DMG from the re-signed .app. Gatekeeper accepts adhoc-signed
# apps that aren't claiming hardened runtime.
- name: Build .app only (macOS)
if: matrix.platform == 'macos-latest'
run: cargo tauri build --bundles app ${{ matrix.args }}
- name: Re-sign without hardened runtime (macOS)
if: matrix.platform == 'macos-latest'
run: |
APP=$(find target/aarch64-apple-darwin/release/bundle/macos -maxdepth 2 -name "*.app" | head -1)
if [ -z "$APP" ]; then
echo "::error::No .app found"
find target -name "*.app" 2>/dev/null
exit 1
fi
echo "Re-signing: $APP"
codesign --force --deep --sign - "$APP"
codesign -dv --verbose=4 "$APP" 2>&1
# Sanity assertion: no 'runtime' flag, sealed resources present.
if codesign -dv --verbose=4 "$APP" 2>&1 | grep -qE "flags=.*runtime"; then
echo "::error::runtime flag still present after re-sign"
exit 1
fi
if codesign -dv --verbose=4 "$APP" 2>&1 | grep -q "Sealed Resources=none"; then
echo "::error::Sealed Resources=none after re-sign"
exit 1
fi
- name: Build DMG from re-signed .app (macOS)
if: matrix.platform == 'macos-latest'
run: cargo tauri bundle --bundles dmg ${{ matrix.args }}
# ── Windows / Linux: standard one-shot bundle build ──
- name: Build all bundles (Windows / Linux)
if: matrix.platform != 'macos-latest'
run: cargo tauri build ${{ matrix.args }}
# ── Stage + rename artifacts to platform-labelled format ──
# Tauri's bundler emits names like `Coffee CLI_1.9.2_amd64.deb` which
# don't tell the end user which OS they're for and use inconsistent
# arch slugs (amd64/x86_64/x64 across formats, plus rpm's `-1` release
# suffix). We rename to the EchoBird-style convention:
# Coffee.CLI_<version>_<OS>_<arch>.<ext>
# so a glance at the GitHub Releases page makes the OS/arch obvious
# and the install scripts have a single uniform pattern to grep.
- name: Stage and rename artifacts
id: stage
shell: bash
env:
VERSION: ${{ steps.tag.outputs.version }}
run: |
set -e
STAGING="$RUNNER_TEMP/coffee-release"
mkdir -p "$STAGING"
rename_one() {
local src="$1"
[ -f "$src" ] || return 0
local base
base=$(basename "$src")
local out=""
case "$base" in
# Windows
*_x64-setup.exe) out="Coffee.CLI_${VERSION}_Windows_x64-setup.exe" ;;
*_x64_*.msi|*_x64.msi) out="Coffee.CLI_${VERSION}_Windows_x64.msi" ;;
# macOS (Coffee CLI ships arm64 only; Intel runs via Rosetta)
*_aarch64.dmg) out="Coffee.CLI_${VERSION}_macOS_arm64.dmg" ;;
# Linux .deb (Tauri uses Debian "amd64"/"arm64")
*_amd64.deb) out="Coffee.CLI_${VERSION}_Linux_x64.deb" ;;
*_arm64.deb) out="Coffee.CLI_${VERSION}_Linux_arm64.deb" ;;
# Linux .AppImage (upstream AppImage uses "amd64"/"aarch64")
*_amd64.AppImage) out="Coffee.CLI_${VERSION}_Linux_x64.AppImage" ;;
*_aarch64.AppImage) out="Coffee.CLI_${VERSION}_Linux_arm64.AppImage" ;;
# Linux .rpm (uses dot-separated `<name>-<ver>-<release>.<arch>.rpm`)
*.x86_64.rpm) out="Coffee.CLI_${VERSION}_Linux_x64.rpm" ;;
*.aarch64.rpm) out="Coffee.CLI_${VERSION}_Linux_arm64.rpm" ;;
*) echo " skip: $base"; return 0 ;;
esac
cp "$src" "$STAGING/$out"
echo " staged: $base → $out"
}
echo "Bundle scan roots:"
for root in \
target/release/bundle \
target/aarch64-apple-darwin/release/bundle \
; do
[ -d "$root" ] || continue
echo " $root"
while IFS= read -r f; do
rename_one "$f"
done < <(find "$root" -type f \
\( -name "*.exe" -o -name "*.msi" \
-o -name "*.dmg" \
-o -name "*.deb" -o -name "*.rpm" -o -name "*.AppImage" \) )
done
echo ""
echo "Final staged assets:"
ls -la "$STAGING"
echo "staging=$STAGING" >> "$GITHUB_OUTPUT"
# ── Create release if missing, then upload all staged artifacts ──
# Each matrix job races to create the release; `gh release create`
# fails idempotently if another job already created it (caught and
# ignored), then `gh release upload --clobber` overwrites any
# earlier-job placeholder of the same filename.
- name: Upload to GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -e
TAG="${{ steps.tag.outputs.tag }}"
STAGING="${{ steps.stage.outputs.staging }}"
# Wait briefly in case another matrix job is mid-create.
for i in 1 2 3 4 5 6; do
if gh release view "$TAG" --repo "${{ github.repository }}" >/dev/null 2>&1; then
break
fi
echo "Release $TAG not yet present (attempt $i/6); waiting 10s..."
sleep 10
done
if ! gh release view "$TAG" --repo "${{ github.repository }}" >/dev/null 2>&1; then
echo "Creating release $TAG"
# Notes body lives in install/release-notes-template.md so the
# YAML block doesn't have to fight bash heredoc indentation
# rules. `gh release create --notes-file` accepts a path; we
# don't need to interpolate the tag because the body is the
# generic install instructions, not version-specific changelog.
NOTES_FILE="$GITHUB_WORKSPACE/install/release-notes-template.md"
if [ -f "$NOTES_FILE" ]; then
gh release create "$TAG" --repo "${{ github.repository }}" \
--title "Coffee CLI $TAG" \
--notes-file "$NOTES_FILE" \
|| echo "Release already exists (created by sibling matrix job)"
else
gh release create "$TAG" --repo "${{ github.repository }}" \
--title "Coffee CLI $TAG" \
--notes "Coffee CLI $TAG. See README for install instructions." \
|| echo "Release already exists (created by sibling matrix job)"
fi
fi
# Upload everything in staging. --clobber overwrites if a previous
# job already pushed an asset with the same name.
shopt -s nullglob
for f in "$STAGING"/*; do
echo "uploading: $(basename "$f")"
gh release upload "$TAG" "$f" --repo "${{ github.repository }}" --clobber
done