-
Notifications
You must be signed in to change notification settings - Fork 88
159 lines (138 loc) · 6.21 KB
/
nightly-release.yml
File metadata and controls
159 lines (138 loc) · 6.21 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
name: Nightly Release
# Fires on every push to main and on manual re-trigger.
# Produces a single rolling "nightly" prerelease DMG: one tag, one release,
# always pointing to the latest successful build on main.
on:
push:
branches:
- main
# workflow_dispatch lets you force a rebuild without a no-op commit,
# useful when a previous run failed due to a transient CI flake.
workflow_dispatch:
permissions:
contents: write
jobs:
guard:
name: Check if build needed
# Runs on a cheap ubuntu runner to avoid spinning up a macOS runner
# when the nightly tag already points to HEAD (duplicate push, bot commit, etc.).
runs-on: ubuntu-latest
outputs:
should_build: ${{ steps.check.outputs.should_build }}
# head_sha pins the exact commit this guard evaluated. The nightly job
# checks out this SHA explicitly so a concurrent push between jobs cannot
# cause the build and the tag to diverge.
head_sha: ${{ steps.check.outputs.head_sha }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Check nightly tag vs HEAD
id: check
run: |
NIGHTLY_SHA=$(git rev-parse nightly 2>/dev/null || echo "none")
HEAD_SHA=$(git rev-parse HEAD)
echo "head_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT"
# Manual triggers always build, regardless of tag state.
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "manual trigger, forcing build"
echo "should_build=true" >> "$GITHUB_OUTPUT"
elif [ "$NIGHTLY_SHA" = "$HEAD_SHA" ]; then
echo "nightly tag already at HEAD ($HEAD_SHA), skipping build"
echo "should_build=false" >> "$GITHUB_OUTPUT"
else
echo "should_build=true" >> "$GITHUB_OUTPUT"
fi
nightly:
name: Build and publish nightly DMG
needs: guard
if: needs.guard.outputs.should_build == 'true'
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
# Pin to the SHA guard evaluated, not the live HEAD. Without this, a
# second push landing between guard and this checkout would build a
# different commit than the one guard approved.
ref: ${{ needs.guard.outputs.head_sha }}
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.11
- name: Install stable Rust toolchain
run: rustup toolchain install stable --no-self-update
# The nightly toolchain with llvm-tools is required by cargo-llvm-cov
# to instrument binaries and enforce the 100% coverage gate.
- name: Install nightly Rust toolchain
run: rustup toolchain install nightly-2026-03-30 --component llvm-tools --no-self-update
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@213ccc1a076163c093f914550b94feb90fab916d # v2.79.2
with:
tool: cargo-llvm-cov
- name: Install frontend dependencies
run: bun install --frozen-lockfile
- name: Lint and format check
run: bun run lint && bun run format:check
- name: Typecheck
run: bun run typecheck
- name: Run all tests with coverage enforcement
run: bun run test:all:coverage
- name: Build Tauri app
# VITE_GIT_COMMIT_SHA is set here, not on a separate frontend step, because
# tauri build runs beforeBuildCommand (bun run build:frontend) internally.
# A separate frontend build step would be overwritten by that second pass.
# AboutTab slices the SHA to 7 chars: v0.6.1+nightly.abc1234
env:
VITE_GIT_COMMIT_SHA: ${{ needs.guard.outputs.head_sha }}
run: bun run build:backend
# Ad-hoc signing with "-" satisfies Gatekeeper for drag-install without
# requiring an Apple Developer certificate in CI secrets. The app is not
# notarized, which is acceptable for a nightly/dev artifact.
- name: Ad-hoc sign the app
run: |
codesign --deep --force --sign - src-tauri/target/release/bundle/macos/Thuki.app
codesign --verify --verbose src-tauri/target/release/bundle/macos/Thuki.app
- name: Install create-dmg
run: brew install create-dmg
- name: Create DMG installer
run: |
mkdir -p /tmp/thuki-dmg-src
cp -r src-tauri/target/release/bundle/macos/Thuki.app /tmp/thuki-dmg-src/
mkdir -p src-tauri/target/release/bundle/dmg
create-dmg \
--volname "Thuki" \
--background "src-tauri/assets/dmg-background.png" \
--window-pos 200 120 \
--window-size 600 380 \
--icon-size 128 \
--icon "Thuki.app" 170 170 \
--hide-extension "Thuki.app" \
--app-drop-link 430 170 \
"src-tauri/target/release/bundle/dmg/Thuki.dmg" \
"/tmp/thuki-dmg-src"
rm -rf /tmp/thuki-dmg-src
# refs/tags/ is explicit to prevent ambiguity if a "nightly" branch
# is ever created alongside the tag.
- name: Force-push nightly tag
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -f nightly
git push origin refs/tags/nightly --force
# Floating release: delete the previous nightly release (if it exists)
# then recreate it. There is always exactly one nightly prerelease,
# always pointing to the latest build.
- name: Publish nightly prerelease
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# "|| true" tolerates the first run when no nightly release exists yet.
gh release delete nightly --yes || true
gh release create nightly \
--title "Thuki Nightly" \
--notes "Automated build from main ($(git rev-parse --short HEAD)). Not for production use." \
--prerelease \
src-tauri/target/release/bundle/dmg/Thuki.dmg