-
Notifications
You must be signed in to change notification settings - Fork 0
137 lines (128 loc) · 6.58 KB
/
Copy pathrelease.yml
File metadata and controls
137 lines (128 loc) · 6.58 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
name: Build release apps
# Hybrid release model (see docs/RELEASING.md): CI does the heavy, awkward part — building BOTH
# macOS arches on their NATIVE runners (you can't easily build x64 on an Apple-silicon Mac) — with
# NO signing secrets. It produces unsigned .app bundles and attaches them to a DRAFT release. You
# then sign + notarize + staple LOCALLY (your Developer ID cert never leaves your machine), attach
# the notarized dmgs, delete the UNSIGNED-*.app.zip assets, and publish.
on:
push:
tags: ["v*"] # e.g. git tag v0.1.0 && git push --tags
workflow_dispatch:
inputs:
vscode_tag:
description: "Override the pinned VS Code tag (blank = scripts/bootstrap.sh default)"
required: false
default: ""
permissions:
contents: write # create/update the draft release
jobs:
# Cheap gate: run the extension unit tests before spending ~1h of macOS build minutes.
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "24"
- name: Extension unit tests
# `shopt` is a bash builtin, so pin the shell instead of relying on the runner default (bash on
# Linux/macOS, but pwsh on Windows — where this step would break if the job were ever copied).
# Explicit `shell: bash` also upgrades the default `bash -e` to
# `bash --noprofile --norc -eo pipefail`, so a stray profile file can't perturb the gate either.
shell: bash
# DISCOVERS suites — it used to `cd extensions/levelcode-ai`, so levelcode-updater's tests never
# ran here, including the one guarding the updater's Download button against serving a raw
# .app.zip. Globbing every extension means a new suite is gated the moment it is added, with no
# list here to keep in sync. Requires are file-relative, so running from the repo root is fine.
run: |
shopt -s nullglob
count=0
for t in extensions/*/test/*.test.js; do
echo "── $t"
node "$t" # `-e` (from `shell: bash` above) aborts the job on the first failure
count=$((count + 1))
done
# A zero-match glob would otherwise report success and gate nothing — the exact failure this
# step is fixing. Fail loudly instead.
if [ "$count" -eq 0 ]; then
echo "::error::No suites matched extensions/*/test/*.test.js — the gate would pass vacuously."
exit 1
fi
echo "──────── $count test files passed ────────"
build:
name: Build ${{ matrix.arch }}
needs: test
strategy:
fail-fast: false
matrix:
include:
- { runner: macos-14, arch: arm64 } # Apple Silicon (native)
# macos-15-intel is GitHub's last native x86_64 image (macos-13 was retired 2025-12-04;
# jobs still requesting it hang forever "Waiting for a runner…" until the 24h queue timeout).
# It's a premium/large runner (bills ~2× minutes) and Intel support ends Fall 2027 — after
# that the x64 build must cross-compile on an arm64 runner. See docs/RELEASING.md §7.
- { runner: macos-15-intel, arch: x64 } # Intel (native)
runs-on: ${{ matrix.runner }}
# Authenticate npm postinstall downloads (notably @vscode/ripgrep, which fetches a prebuilt binary
# from GitHub releases). Without a token they use GitHub's 60/hr ANONYMOUS limit and 403 on busy
# release days; GITHUB_TOKEN raises that to 5000/hr. Read-only default token — no extra perms needed.
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "24" # bootstrap.sh checks the .nvmrc major; 24.x satisfies the 1.126 pin
- name: Cache npm downloads
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ matrix.arch }}-${{ hashFiles('scripts/bootstrap.sh') }}
restore-keys: npm-${{ matrix.arch }}-
- name: Bootstrap Code-OSS + branding + deps
run: ./scripts/bootstrap.sh
env:
VSCODE_TAG: ${{ github.event.inputs.vscode_tag }}
- name: Build LevelCode.app (${{ matrix.arch }}, proprietary stripped)
run: ./scripts/build-macos.sh ${{ matrix.arch }}
- name: Zip the unsigned app
run: ditto -c -k --sequesterRsrc --keepParent "VSCode-darwin-${{ matrix.arch }}/LevelCode.app" "UNSIGNED-LevelCode-${{ matrix.arch }}.app.zip"
- name: Upload app artifact
uses: actions/upload-artifact@v4
with:
name: UNSIGNED-LevelCode-${{ matrix.arch }}
path: UNSIGNED-LevelCode-${{ matrix.arch }}.app.zip
if-no-files-found: error
retention-days: 14
draft-release:
name: Draft release
needs: build
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
path: apps
merge-multiple: true
- name: Create / refresh the draft release with the UNSIGNED apps
uses: softprops/action-gh-release@v2
with:
draft: true
name: LevelCode ${{ github.ref_name }}
tag_name: ${{ github.ref_name }}
files: apps/*.zip
fail_on_unmatched_files: true
body: |
**Draft — not for release as-is.** The attached `UNSIGNED-LevelCode-<arch>.app.zip`
files are CI build artifacts with **no Developer ID signature or notarization**.
To finish the release **locally** (your signing cert never touches CI):
1. `gh release download ${{ github.ref_name }} --pattern 'UNSIGNED-*.app.zip'`
2. For each arch — unzip into `VSCode-darwin-<arch>/`, then
`CODESIGN_IDENTITY="Developer ID Application: …" NOTARY_PROFILE=levelcode-notary ./scripts/make-dmg.sh <arch>`
(signs → notarizes → staples → `LevelCode-<arch>.dmg` **and** `LevelCode-<arch>.app.zip`).
3. `gh release upload ${{ github.ref_name }} LevelCode-arm64.dmg LevelCode-x64.dmg LevelCode-arm64.app.zip LevelCode-x64.app.zip`
— the `.dmg`s are for humans, the `.app.zip`s are the auto-update feed assets (`docs/AUTO-UPDATE.md`).
Upload exactly these four; the `.app.zip.sha256` files `make-dmg.sh` writes stay **local**
(the feed reads GitHub's own asset `digest`, never a sidecar).
4. **Delete the `UNSIGNED-*.app.zip` assets**, add real notes, and publish.
Full runbook: `docs/RELEASING.md`.