Skip to content

Commit c6b8bb8

Browse files
authored
feat(desktop): build macOS previews from a PR label (pingdotgg#8182)
1 parent 06de9e9 commit c6b8bb8

3 files changed

Lines changed: 220 additions & 10 deletions

File tree

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
name: Desktop macOS Preview
2+
3+
on:
4+
pull_request:
5+
types: [labeled, synchronize, reopened]
6+
7+
permissions:
8+
contents: read
9+
pull-requests: write
10+
11+
concurrency:
12+
group: desktop-macos-preview-${{ github.event.pull_request.number }}
13+
cancel-in-progress: true
14+
15+
jobs:
16+
build:
17+
name: Build macOS Apple Silicon preview
18+
if: >-
19+
github.event.pull_request.head.repo.full_name == github.repository &&
20+
contains(github.event.pull_request.labels.*.name, 'preview:mac') &&
21+
(github.event.action != 'labeled' || github.event.label.name == 'preview:mac')
22+
runs-on: blacksmith-12vcpu-macos-26
23+
timeout-minutes: 30
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@v6
27+
with:
28+
ref: ${{ github.event.pull_request.head.sha }}
29+
sparse-checkout: |
30+
/*
31+
!/.repos/
32+
sparse-checkout-cone-mode: false
33+
34+
- name: Setup Vite+
35+
uses: voidzero-dev/setup-vp@v1
36+
with:
37+
node-version-file: package.json
38+
cache: true
39+
run-install: false
40+
41+
- name: Install desktop dependencies
42+
run: vp install --filter=@t3tools/desktop... --filter=t3... --filter=@t3tools/scripts...
43+
44+
- name: Cache resource monitor
45+
id: resource_monitor_cache
46+
uses: actions/cache@v6
47+
with:
48+
path: native/resource-monitor/target/aarch64-apple-darwin/release/t3-resource-monitor
49+
key: resource-monitor-aarch64-apple-darwin-${{ hashFiles('native/resource-monitor/Cargo.lock', 'native/resource-monitor/Cargo.toml', 'native/resource-monitor/src/**') }}
50+
51+
- name: Setup Rust
52+
if: steps.resource_monitor_cache.outputs.cache-hit != 'true'
53+
uses: dtolnay/rust-toolchain@stable
54+
with:
55+
targets: aarch64-apple-darwin
56+
57+
- id: version
58+
name: Set preview version and public configuration
59+
shell: bash
60+
env:
61+
PR_NUMBER: ${{ github.event.pull_request.number }}
62+
run: |
63+
set -euo pipefail
64+
65+
base_version="$(node -p "require('./apps/desktop/package.json').version")"
66+
preview_version="${base_version}-pr.${PR_NUMBER}.${GITHUB_RUN_NUMBER}"
67+
node scripts/update-release-package-versions.ts "$preview_version"
68+
cp .env.example .env
69+
70+
echo "version=$preview_version" >> "$GITHUB_OUTPUT"
71+
72+
- id: build
73+
name: Build unsigned macOS DMG
74+
shell: bash
75+
env:
76+
T3CODE_DESKTOP_REUSE_RESOURCE_MONITOR: ${{ steps.resource_monitor_cache.outputs.cache-hit == 'true' }}
77+
PREVIEW_VERSION: ${{ steps.version.outputs.version }}
78+
run: |
79+
set -euo pipefail
80+
81+
vp run dist:desktop:artifact \
82+
--platform mac \
83+
--target dmg \
84+
--arch arm64 \
85+
--build-version "$PREVIEW_VERSION" \
86+
--verbose
87+
88+
shopt -s nullglob
89+
dmg_files=(release/*.dmg)
90+
if (( ${#dmg_files[@]} != 1 )); then
91+
printf 'Expected one DMG, found %s.\n' "${#dmg_files[@]}" >&2
92+
exit 1
93+
fi
94+
printf 'dmg_name=%s\n' "$(basename "${dmg_files[0]}")" >> "$GITHUB_OUTPUT"
95+
96+
- id: upload
97+
name: Upload macOS DMG
98+
uses: actions/upload-artifact@v7
99+
with:
100+
path: release/*.dmg
101+
if-no-files-found: error
102+
archive: false
103+
overwrite: true
104+
retention-days: 7
105+
106+
- name: Comment download link
107+
uses: actions/github-script@v8
108+
env:
109+
ARTIFACT_URL: ${{ steps.upload.outputs.artifact-url }}
110+
DMG_NAME: ${{ steps.build.outputs.dmg_name }}
111+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
112+
PREVIEW_VERSION: ${{ steps.version.outputs.version }}
113+
with:
114+
script: |
115+
const { data: pullRequest } = await github.rest.pulls.get({
116+
owner: context.repo.owner,
117+
repo: context.repo.repo,
118+
pull_number: context.payload.pull_request.number,
119+
});
120+
if (pullRequest.head.sha !== process.env.HEAD_SHA) {
121+
core.info("Skipping the outdated macOS preview comment.");
122+
return;
123+
}
124+
125+
const marker = "<!-- desktop-macos-preview -->";
126+
const body = [
127+
marker,
128+
"### macOS preview",
129+
"",
130+
`[Download Apple Silicon DMG](${process.env.ARTIFACT_URL})`,
131+
"",
132+
`Version: ${process.env.PREVIEW_VERSION}`,
133+
`Commit: ${process.env.HEAD_SHA.slice(0, 7)}`,
134+
"",
135+
"Unsigned build. Clear quarantine before opening:",
136+
"```sh",
137+
`xattr -d com.apple.quarantine ~/Downloads/${process.env.DMG_NAME}`,
138+
"```",
139+
"",
140+
"The download requires GitHub access and expires after 7 days.",
141+
].join("\n");
142+
143+
const { data: comments } = await github.rest.issues.listComments({
144+
owner: context.repo.owner,
145+
repo: context.repo.repo,
146+
issue_number: context.payload.pull_request.number,
147+
per_page: 100,
148+
});
149+
const existing = comments.find((comment) => comment.body?.includes(marker));
150+
151+
if (existing) {
152+
await github.rest.issues.updateComment({
153+
owner: context.repo.owner,
154+
repo: context.repo.repo,
155+
comment_id: existing.id,
156+
body,
157+
});
158+
} else {
159+
await github.rest.issues.createComment({
160+
owner: context.repo.owner,
161+
repo: context.repo.repo,
162+
issue_number: context.payload.pull_request.number,
163+
body,
164+
});
165+
}

‎scripts/build-desktop-artifact.test.ts‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,45 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
223223
}),
224224
);
225225

226+
it.effect("omits update feeds for pull request preview builds", () =>
227+
Effect.gen(function* () {
228+
const preview = yield* createBuildConfig(
229+
"mac",
230+
"dmg",
231+
"0.0.33-pr.8182.1",
232+
false,
233+
false,
234+
undefined,
235+
undefined,
236+
);
237+
const release = yield* createBuildConfig(
238+
"mac",
239+
"dmg",
240+
"0.0.33",
241+
false,
242+
false,
243+
undefined,
244+
undefined,
245+
);
246+
247+
assert.notProperty(preview, "publish");
248+
assert.deepStrictEqual(release.publish, [
249+
{
250+
provider: "github",
251+
owner: "pingdotgg",
252+
repo: "t3code",
253+
releaseType: "release",
254+
},
255+
]);
256+
}).pipe(
257+
Effect.provide(
258+
ConfigProvider.layer(
259+
ConfigProvider.fromEnv({ env: { GITHUB_REPOSITORY: "pingdotgg/t3code" } }),
260+
),
261+
),
262+
),
263+
);
264+
226265
it("omits bundled workspace packages from staged desktop dependencies", () => {
227266
assert.deepStrictEqual(
228267
resolveDesktopRuntimeDependencies(

‎scripts/build-desktop-artifact.ts‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2017,6 +2017,10 @@ export function resolveDesktopUpdateChannel(version: string): "latest" | "nightl
20172017
return /-nightly\.\d{8}\.\d+$/.test(version) ? "nightly" : "latest";
20182018
}
20192019

2020+
function isDesktopPreviewVersion(version: string): boolean {
2021+
return /-pr\./.test(version);
2022+
}
2023+
20202024
export function resolveDesktopWebAssetBrand(version: string): WebAssetBrand {
20212025
return resolveWebAssetBrandForChannel(resolveDesktopUpdateChannel(version));
20222026
}
@@ -2093,16 +2097,18 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* (
20932097
],
20942098
};
20952099
const updateChannel = resolveDesktopUpdateChannel(version);
2096-
const publishConfig = yield* resolveGitHubPublishConfig(updateChannel);
2097-
if (publishConfig) {
2098-
buildConfig.publish = [publishConfig];
2099-
} else if (mockUpdates) {
2100-
buildConfig.publish = [
2101-
{
2102-
provider: "generic",
2103-
url: resolveMockUpdateServerUrl(mockUpdateServerPort),
2104-
},
2105-
];
2100+
if (!isDesktopPreviewVersion(version)) {
2101+
const publishConfig = yield* resolveGitHubPublishConfig(updateChannel);
2102+
if (publishConfig) {
2103+
buildConfig.publish = [publishConfig];
2104+
} else if (mockUpdates) {
2105+
buildConfig.publish = [
2106+
{
2107+
provider: "generic",
2108+
url: resolveMockUpdateServerUrl(mockUpdateServerPort),
2109+
},
2110+
];
2111+
}
21062112
}
21072113

21082114
if (platform === "mac") {

0 commit comments

Comments
 (0)