Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ jobs:
*) echo "Tag '$TAG' does not start with v"; exit 1 ;;
esac

- name: Publish the Apple silicon build under the name the updater looks for
run: |
if [ ! -f release-assets/Lantern-mac.zip ]; then
echo "release-assets/Lantern-mac.zip is missing, nothing to copy to Lantern-mac-arm64.zip"
exit 1
fi
cp release-assets/Lantern-mac.zip release-assets/Lantern-mac-arm64.zip

- name: Create the release when it is missing
run: |
if ! gh release view "$TAG" > /dev/null 2>&1; then
Expand Down
2 changes: 1 addition & 1 deletion .lantern/lang/en/.github/workflows.lantern.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"ci.yml": "Runs the install, typecheck, lint and unit tests, the headless Playwright e2e suites and a unit-test coverage run that is uploaded as an artifact, plus builds and packaging on Ubuntu, macOS and Windows runners, on every push and pull request with SHA-pinned actions",
"codeql.yml": "Scans the JavaScript and TypeScript sources with CodeQL on pushes to main, on pull requests and weekly, with read-only contents access, write access to security events only and SHA-pinned actions",
"deploy-docs.yml": "Builds the documentation site on every change to its sources and on pull requests, and publishes it to GitHub Pages from main with SHA-pinned actions, defaulting the site URL to docs.thelantern.dev",
"release.yml": "Builds the macOS and Windows packages when a v* tag is pushed or a tag is chosen by hand, signing and notarizing the macOS app in the protected release environment only from a v* tag, and attaches them to that tag's GitHub Release, which is created with generated notes when missing"
"release.yml": "Builds the macOS and Windows packages when a v* tag is pushed or a tag is chosen by hand, signing and notarizing the macOS app in the protected release environment only from a v* tag, and attaches them, plus a copy of the macOS zip named for Apple silicon so the updater finds it, to that tag's GitHub Release, which is created with generated notes when missing"
}
}
2 changes: 1 addition & 1 deletion .lantern/map/.github/workflows.lantern
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ lantern 1
.github/workflows/deploy-docs.yml . infra 41b0263d580d
> website/package.json

.github/workflows/release.yml . infra c4bd3239b533
.github/workflows/release.yml . infra 41d03418640d
> package.json
> scripts/mac-ci-keychain.sh
> scripts/mac-sign-notarize.sh
2 changes: 1 addition & 1 deletion .lantern/map/test.lantern
Original file line number Diff line number Diff line change
Expand Up @@ -2011,7 +2011,7 @@ test/reason-definition.test.ts . test 550e8f81a437
= src/shared/reason.ts
< skills/lantern-setup/scripts/prs.mjs#REASON_MAX_CHARS

test/release-workflow.test.ts . test 4e110666c136
test/release-workflow.test.ts . test 3d985cb8cdbb
< .github/workflows/release.yml
< scripts/mac-sign-notarize.sh

Expand Down
35 changes: 35 additions & 0 deletions .lantern/prs/30.pr
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
lantern-pr 1
# 30
#title Publish the Apple silicon build as Lantern-mac-arm64.zip for the updater
#url https://github.com/MobileReality/lantern/pull/30
#branch feat/mac-arm64-release-asset
#base main
#opened 2026-11-10
#head 4ebb126a4a308f49801f78634048d69e7d7ffbf3
#describe what Every release gets Lantern-mac-arm64.zip, a byte-identical copy of Lantern-mac.zip, so the updater can find the Apple silicon build.
#describe before Releases had Lantern-mac.zip and Lantern-win-x64.zip; darwin-arm64 answered 404.
#describe after Releases have three assets; Lantern-mac.zip stays the download link.
#describe verified typecheck, lint and pnpm test are green; the release workflow was not run.
~ .github/workflows/release.yml
~ README.md
~ docs/installation.md
~ test/release-workflow.test.ts
#order .github/workflows/release.yml
#order test/release-workflow.test.ts
#order README.md
#order docs/installation.md
#reason .github/workflows/release.yml The updater files a darwin zip as arm64 only when the name has -arm64, so Apple silicon got a 404.
#reason README.md Tells readers the Apple silicon build is also published as Lantern-mac-arm64.zip for the updater.
#reason docs/installation.md Keeps the release description true now that the publish job adds a third file.
#reason test/release-workflow.test.ts Guards that the copy exists, runs before the upload, and fails when the source is missing.
#story Apple silicon Macs find updates
#step actor A maintainer pushes a v* tag
#file .github/workflows/release.yml
#step action The release also publishes the Mac zip as Lantern-mac-arm64.zip
#file .github/workflows/release.yml
#file test/release-workflow.test.ts
#step subject The release page
#file README.md
#file docs/installation.md
#below interaction Apple silicon users are offered the update
#below consequence The update check answered not found => The update check finds the build
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ Sessions are told apart by their working directory: one entry per VS Code projec

Packages are attached to each release on the
[releases page](https://github.com/MobileReality/lantern/releases): `Lantern-mac.zip` for
Apple-silicon Macs and `Lantern-win-x64.zip` for Windows. Until a release is published there,
Apple-silicon Macs and `Lantern-win-x64.zip` for Windows. The Apple-silicon build is also published
as `Lantern-mac-arm64.zip` for the updater, and the `Lantern-mac.zip` link stays the one to use. Until a release is published there,
build Lantern from source ([installation](docs/installation.md#install-and-run-from-source)).

The macOS app of a release built by the current workflow is signed with a Developer ID certificate
Expand Down
6 changes: 4 additions & 2 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ the app itself, see [getting-started.md](getting-started.md).

Packages are attached to each release on the
[releases page](https://github.com/MobileReality/lantern/releases): `Lantern-mac.zip` for
Apple-silicon Macs and `Lantern-win-x64.zip` for Windows. Until a release is published there,
Apple-silicon Macs and `Lantern-win-x64.zip` for Windows. The Apple-silicon build is also published
as `Lantern-mac-arm64.zip` for the updater, and the `Lantern-mac.zip` link stays the one to use. Until a release is published there,
build Lantern from source as described below.

The macOS app of a release built by the current workflow is signed with a Developer ID certificate
Expand Down Expand Up @@ -61,7 +62,8 @@ runs the same package scripts as a local build. On a macOS runner, `pnpm run bui
`pnpm run fuses` build the app, `scripts/mac-sign-notarize.sh` signs and notarizes it, and
`pnpm run pack` gives `Lantern-mac.zip` (Apple silicon). `pnpm run build:win` on a Windows runner
gives `Lantern-win-x64.zip`. A last job creates the release for the tag when it is missing, with
generated release notes, and uploads both files; running it again replaces them.
generated release notes, copies `Lantern-mac.zip` to `Lantern-mac-arm64.zip` (the name the updater
needs to find the Apple-silicon build) and uploads the three files; running it again replaces them.

- **Release a new version**: push a tag that starts with `v`, for example
`git tag v0.2.1 && git push origin v0.2.1`. The workflow starts on the push, and the macOS job
Expand Down
30 changes: 30 additions & 0 deletions test/release-workflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,36 @@ describe('release workflow, the signing job', () => {
});
});

describe('release workflow, the publish job', () => {
const PUBLISH = WORKFLOW.jobs.publish.steps;
const copyIndex = PUBLISH.findIndex((step) => step.run?.includes('Lantern-mac-arm64.zip') && step.run.includes('cp '));
const uploadIndex = PUBLISH.findIndex((step) => step.run?.includes('gh release upload'));

it('copies Lantern-mac.zip to Lantern-mac-arm64.zip before the upload, and fails when the source is missing', () => {
expect(copyIndex).toBeGreaterThanOrEqual(0);
expect(copyIndex).toBeLessThan(uploadIndex);
const copy = PUBLISH[copyIndex].run ?? '';
expect(copy).toContain('cp release-assets/Lantern-mac.zip release-assets/Lantern-mac-arm64.zip');
expect(copy).toMatch(/\[ ! -f release-assets\/Lantern-mac\.zip \]/);
expect(copy).toContain('exit 1');
});

it('uploads everything in the assets folder with --clobber, so the stable names and the arm64 copy all go up', () => {
expect(uploadIndex).toBeGreaterThanOrEqual(0);
expect(PUBLISH[uploadIndex].run).toContain('release-assets/* --clobber');
const uploadedByBuilds = Object.values(WORKFLOW.jobs)
.flatMap((job) => job.steps)
.filter((step) => step.uses?.startsWith('actions/upload-artifact@'))
.map((step) => step.with?.path);
expect(uploadedByBuilds.sort()).toEqual(['dist/Lantern-mac.zip', 'dist/Lantern-win-x64.zip']);
});

it('builds and signs nothing in the publish job', () => {
expect(PUBLISH.map((step) => step.run ?? '').join('\n')).not.toMatch(/pnpm|mac-sign-notarize|codesign|notarytool/);
expect(PUBLISH.some(readsSecret)).toBe(false);
});
});

describe('mac entitlements', () => {
const keysOf = (file: string) => [...read(file).matchAll(/<key>([^<]+)<\/key>/g)].map((match) => match[1]).sort();

Expand Down
Loading