Skip to content

Commit 0565cbf

Browse files
incognitojamgithub-actions[bot]
authored andcommitted
ci: publish signed fork nightlies (#2)
1 parent ef18ad8 commit 0565cbf

1 file changed

Lines changed: 326 additions & 0 deletions

File tree

.github/workflows/fork-nightly.yml

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
name: Fork Nightly
2+
3+
on:
4+
schedule:
5+
- cron: "23 */6 * * *"
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: write
10+
id-token: none
11+
12+
concurrency:
13+
group: fork-nightly
14+
cancel-in-progress: false
15+
16+
jobs:
17+
prepare:
18+
name: Rebase and verify
19+
if: github.repository == 'incognitojam/t3code'
20+
runs-on: ubuntu-24.04
21+
timeout-minutes: 30
22+
outputs:
23+
has_changes: ${{ steps.candidate.outputs.has_changes }}
24+
ref: ${{ steps.candidate.outputs.ref }}
25+
version: ${{ steps.release_meta.outputs.version }}
26+
tag: ${{ steps.release_meta.outputs.tag }}
27+
release_name: ${{ steps.release_meta.outputs.name }}
28+
short_sha: ${{ steps.release_meta.outputs.short_sha }}
29+
steps:
30+
- name: Checkout fork patch stack
31+
uses: actions/checkout@v6
32+
with:
33+
ref: main
34+
fetch-depth: 0
35+
sparse-checkout: |
36+
/*
37+
!/.repos/
38+
sparse-checkout-cone-mode: false
39+
40+
- id: candidate
41+
name: Rebase candidate onto upstream
42+
shell: bash
43+
run: |
44+
set -euo pipefail
45+
46+
git config user.name "github-actions[bot]"
47+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
48+
git remote add upstream https://github.com/pingdotgg/t3code.git
49+
git fetch upstream main --tags
50+
git fetch origin '+refs/heads/nightly:refs/remotes/origin/nightly' || true
51+
git fetch origin \
52+
'+refs/heads/nightly-candidate:refs/remotes/origin/nightly-candidate' || true
53+
54+
git switch -C nightly-candidate origin/main
55+
git rebase upstream/main
56+
57+
candidate_ref=$(git rev-parse HEAD)
58+
has_changes=true
59+
if git rev-parse --verify refs/remotes/origin/nightly >/dev/null 2>&1 && \
60+
git diff --quiet origin/nightly HEAD --; then
61+
has_changes=false
62+
fi
63+
64+
echo "has_changes=$has_changes" >> "$GITHUB_OUTPUT"
65+
echo "ref=$candidate_ref" >> "$GITHUB_OUTPUT"
66+
67+
- name: Setup Vite+
68+
if: steps.candidate.outputs.has_changes == 'true'
69+
uses: voidzero-dev/setup-vp@v1
70+
with:
71+
node-version-file: package.json
72+
cache: true
73+
run-install: true
74+
75+
- name: Ensure Electron runtime is installed
76+
if: steps.candidate.outputs.has_changes == 'true'
77+
run: vp run --filter @t3tools/desktop ensure:electron
78+
79+
- name: Check
80+
if: steps.candidate.outputs.has_changes == 'true'
81+
run: vp check
82+
83+
- name: Typecheck
84+
if: steps.candidate.outputs.has_changes == 'true'
85+
run: vp run typecheck
86+
87+
- name: Test
88+
if: steps.candidate.outputs.has_changes == 'true'
89+
run: vp run test
90+
91+
- id: release_meta
92+
name: Resolve nightly version
93+
if: steps.candidate.outputs.has_changes == 'true'
94+
shell: bash
95+
env:
96+
CANDIDATE_REF: ${{ steps.candidate.outputs.ref }}
97+
NIGHTLY_RUN_NUMBER: ${{ github.run_number }}
98+
run: |
99+
nightly_date=$(date -u +%Y%m%d)
100+
node scripts/resolve-nightly-release.ts \
101+
--date "$nightly_date" \
102+
--run-number "$NIGHTLY_RUN_NUMBER" \
103+
--sha "$CANDIDATE_REF" \
104+
--github-output
105+
106+
- name: Publish verified candidate
107+
if: steps.candidate.outputs.has_changes == 'true'
108+
run: git push --force-with-lease origin HEAD:refs/heads/nightly-candidate
109+
110+
build_macos:
111+
name: Build macOS arm64
112+
needs: prepare
113+
if: needs.prepare.outputs.has_changes == 'true'
114+
runs-on: macos-15
115+
timeout-minutes: 45
116+
steps:
117+
- name: Checkout verified candidate
118+
uses: actions/checkout@v6
119+
with:
120+
ref: ${{ needs.prepare.outputs.ref }}
121+
fetch-depth: 0
122+
sparse-checkout: |
123+
/*
124+
!/.repos/
125+
sparse-checkout-cone-mode: false
126+
127+
- name: Setup Vite+
128+
uses: voidzero-dev/setup-vp@v1
129+
with:
130+
node-version-file: package.json
131+
cache: true
132+
run-install: |
133+
args:
134+
- --filter=@t3tools/desktop...
135+
- --filter=t3...
136+
- --filter=@t3tools/scripts...
137+
138+
- name: Setup Rust
139+
uses: dtolnay/rust-toolchain@stable
140+
with:
141+
targets: aarch64-apple-darwin
142+
143+
- name: Align package versions
144+
run: node scripts/update-release-package-versions.ts "${{ needs.prepare.outputs.version }}"
145+
146+
- name: Build signed desktop artifact
147+
shell: bash
148+
env:
149+
T3CODE_DESKTOP_UPDATE_REPOSITORY: ${{ github.repository }}
150+
CSC_LINK: ${{ secrets.CSC_LINK }}
151+
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
152+
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
153+
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
154+
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
155+
APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }}
156+
MACOS_PROVISIONING_PROFILE: ${{ secrets.MACOS_PROVISIONING_PROFILE }}
157+
T3CODE_CLERK_PASSKEY_RP_DOMAINS: ${{ vars.CLERK_PASSKEY_RP_DOMAINS }}
158+
run: |
159+
set -euo pipefail
160+
161+
required=(
162+
CSC_LINK
163+
CSC_KEY_PASSWORD
164+
APPLE_API_KEY
165+
APPLE_API_KEY_ID
166+
APPLE_API_ISSUER
167+
APPLE_TEAM_ID
168+
MACOS_PROVISIONING_PROFILE
169+
T3CODE_CLERK_PASSKEY_RP_DOMAINS
170+
)
171+
for name in "${required[@]}"; do
172+
if [[ -z "${!name:-}" ]]; then
173+
echo "Missing required Apple signing configuration: $name" >&2
174+
exit 1
175+
fi
176+
done
177+
178+
key_path="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8"
179+
printf '%s' "$APPLE_API_KEY" > "$key_path"
180+
export APPLE_API_KEY="$key_path"
181+
182+
profile_path="$RUNNER_TEMP/t3code.provisionprofile"
183+
profile_plist="$RUNNER_TEMP/t3code-profile.plist"
184+
printf '%s' "$MACOS_PROVISIONING_PROFILE" | base64 -D > "$profile_path"
185+
security cms -D -i "$profile_path" > "$profile_plist"
186+
187+
expected_app_id="$APPLE_TEAM_ID.dev.incognitojam.t3code"
188+
profile_app_id=$(/usr/libexec/PlistBuddy \
189+
-c 'Print :Entitlements:com.apple.application-identifier' \
190+
"$profile_plist")
191+
if [[ "$profile_app_id" != "$expected_app_id" ]]; then
192+
echo "Provisioning profile app ID is '$profile_app_id'; expected '$expected_app_id'." >&2
193+
exit 1
194+
fi
195+
196+
profile_domains=$(/usr/libexec/PlistBuddy \
197+
-c 'Print :Entitlements:com.apple.developer.associated-domains' \
198+
"$profile_plist")
199+
IFS=',' read -r -a rp_domains <<< "$T3CODE_CLERK_PASSKEY_RP_DOMAINS"
200+
for domain in "${rp_domains[@]}"; do
201+
normalized_domain=$(printf '%s' "$domain" | xargs)
202+
if ! grep -Fq "webcredentials:$normalized_domain" <<< "$profile_domains"; then
203+
echo "Provisioning profile does not include webcredentials:$normalized_domain." >&2
204+
exit 1
205+
fi
206+
done
207+
208+
export T3CODE_APPLE_TEAM_ID="$APPLE_TEAM_ID"
209+
export T3CODE_MACOS_PROVISIONING_PROFILE="$profile_path"
210+
211+
vp run dist:desktop:artifact \
212+
--platform mac \
213+
--target dmg \
214+
--arch arm64 \
215+
--build-version "${{ needs.prepare.outputs.version }}" \
216+
--signed \
217+
--verbose
218+
219+
- name: Collect release assets
220+
shell: bash
221+
run: |
222+
set -euo pipefail
223+
mkdir -p release-publish
224+
225+
shopt -s nullglob
226+
for pattern in \
227+
"release/*.dmg" \
228+
"release/*.zip" \
229+
"release/*.yml"; do
230+
for file in $pattern; do
231+
cp "$file" release-publish/
232+
done
233+
done
234+
235+
test -n "$(find release-publish -maxdepth 1 -type f -print -quit)"
236+
237+
- name: Upload release assets
238+
uses: actions/upload-artifact@v7
239+
with:
240+
name: desktop-mac-arm64
241+
path: release-publish/*
242+
if-no-files-found: error
243+
244+
release:
245+
name: Publish GitHub prerelease
246+
needs: [prepare, build_macos]
247+
if: needs.prepare.outputs.has_changes == 'true' && needs.build_macos.result == 'success'
248+
runs-on: ubuntu-24.04
249+
timeout-minutes: 10
250+
steps:
251+
- name: Checkout released source
252+
uses: actions/checkout@v6
253+
with:
254+
ref: ${{ needs.prepare.outputs.ref }}
255+
fetch-depth: 0
256+
sparse-checkout: |
257+
/*
258+
!/.repos/
259+
sparse-checkout-cone-mode: false
260+
261+
- name: Download release assets
262+
uses: actions/download-artifact@v8
263+
with:
264+
name: desktop-mac-arm64
265+
path: release-assets
266+
267+
- name: Upload draft prerelease
268+
uses: softprops/action-gh-release@v2
269+
with:
270+
tag_name: ${{ needs.prepare.outputs.tag }}
271+
target_commitish: ${{ needs.prepare.outputs.ref }}
272+
name: ${{ needs.prepare.outputs.release_name }}
273+
body: |
274+
Automated build of `incognitojam/t3code` rebased onto `pingdotgg/t3code@main`.
275+
276+
Source: `${{ needs.prepare.outputs.short_sha }}`
277+
macOS Developer ID signed and notarized.
278+
generate_release_notes: true
279+
draft: true
280+
prerelease: true
281+
make_latest: false
282+
files: release-assets/*
283+
fail_on_unmatched_files: true
284+
token: ${{ github.token }}
285+
286+
- name: Publish complete prerelease
287+
uses: softprops/action-gh-release@v2
288+
with:
289+
tag_name: ${{ needs.prepare.outputs.tag }}
290+
prerelease: true
291+
make_latest: false
292+
token: ${{ github.token }}
293+
294+
- name: Promote successful nightly source
295+
shell: bash
296+
run: |
297+
git fetch origin '+refs/heads/nightly:refs/remotes/origin/nightly' || true
298+
git push --force-with-lease origin HEAD:refs/heads/nightly
299+
300+
notify_failure:
301+
name: Notify Discord
302+
needs: [prepare, build_macos, release]
303+
if: >-
304+
${{
305+
always() &&
306+
(
307+
needs.prepare.result == 'failure' ||
308+
needs.build_macos.result == 'failure' ||
309+
needs.release.result == 'failure'
310+
)
311+
}}
312+
runs-on: ubuntu-24.04
313+
permissions: {}
314+
steps:
315+
- uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645 # v7.0.0
316+
with:
317+
webhook-url: ${{ secrets.DISCORD_WEBHOOK_CI }}
318+
embed-title: Fork Nightly failed
319+
embed-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
320+
embed-description: |
321+
`${{ github.sha }}` · `${{ github.actor }}`
322+
323+
Prepare: `${{ needs.prepare.result }}`
324+
macOS build: `${{ needs.build_macos.result }}`
325+
Release: `${{ needs.release.result }}`
326+
embed-color: 15158332

0 commit comments

Comments
 (0)