Skip to content

Commit b46d9ee

Browse files
authored
Merge branch 'main' into sidebar-provider-status-icons
2 parents c24d41c + 7968f27 commit b46d9ee

File tree

82 files changed

+3903
-475
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+3903
-475
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,8 @@ jobs:
9292
with:
9393
node-version-file: package.json
9494

95+
- name: Install dependencies
96+
run: bun install --frozen-lockfile
97+
9598
- name: Exercise release-only workflow steps
9699
run: node scripts/release-smoke.ts

.github/workflows/release.yml

Lines changed: 119 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1-
name: Release Desktop
1+
name: Release
22

33
on:
44
push:
55
tags:
66
- "v*.*.*"
7+
schedule:
8+
- cron: "0 9 * * *"
79
workflow_dispatch:
810
inputs:
11+
channel:
12+
description: "Release channel"
13+
required: false
14+
default: stable
15+
type: choice
16+
options:
17+
- stable
18+
- nightly
919
version:
1020
description: "Release version (for example 1.2.3 or v1.2.3)"
11-
required: true
21+
required: false
1222
type: string
1323

1424
permissions:
@@ -21,40 +31,21 @@ jobs:
2131
runs-on: ubuntu-24.04
2232
timeout-minutes: 10
2333
outputs:
34+
release_channel: ${{ steps.release_meta.outputs.release_channel }}
2435
version: ${{ steps.release_meta.outputs.version }}
2536
tag: ${{ steps.release_meta.outputs.tag }}
37+
release_name: ${{ steps.release_meta.outputs.name }}
38+
short_sha: ${{ steps.release_meta.outputs.short_sha }}
39+
previous_tag: ${{ steps.previous_tag.outputs.previous_tag }}
40+
cli_dist_tag: ${{ steps.release_meta.outputs.cli_dist_tag }}
2641
is_prerelease: ${{ steps.release_meta.outputs.is_prerelease }}
2742
make_latest: ${{ steps.release_meta.outputs.make_latest }}
2843
ref: ${{ github.sha }}
2944
steps:
3045
- name: Checkout
3146
uses: actions/checkout@v6
32-
33-
- id: release_meta
34-
name: Resolve release version
35-
shell: bash
36-
run: |
37-
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
38-
raw="${{ github.event.inputs.version }}"
39-
else
40-
raw="${GITHUB_REF_NAME}"
41-
fi
42-
43-
version="${raw#v}"
44-
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
45-
echo "Invalid release version: $raw" >&2
46-
exit 1
47-
fi
48-
49-
echo "version=$version" >> "$GITHUB_OUTPUT"
50-
echo "tag=v$version" >> "$GITHUB_OUTPUT"
51-
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
52-
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
53-
echo "make_latest=true" >> "$GITHUB_OUTPUT"
54-
else
55-
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
56-
echo "make_latest=false" >> "$GITHUB_OUTPUT"
57-
fi
47+
with:
48+
fetch-depth: 0
5849

5950
- name: Setup Bun
6051
uses: oven-sh/setup-bun@v2
@@ -69,6 +60,60 @@ jobs:
6960
- name: Install dependencies
7061
run: bun install --frozen-lockfile
7162

63+
- id: release_meta
64+
name: Resolve release version
65+
shell: bash
66+
env:
67+
DISPATCH_CHANNEL: ${{ github.event.inputs.channel }}
68+
DISPATCH_VERSION: ${{ github.event.inputs.version }}
69+
NIGHTLY_DATE: ${{ github.run_started_at }}
70+
NIGHTLY_SHA: ${{ github.sha }}
71+
NIGHTLY_RUN_NUMBER: ${{ github.run_number }}
72+
run: |
73+
if [[ "${GITHUB_EVENT_NAME}" == "schedule" || ( "${GITHUB_EVENT_NAME}" == "workflow_dispatch" && "${DISPATCH_CHANNEL:-stable}" == "nightly" ) ]]; then
74+
nightly_date="$(date -u -d "$NIGHTLY_DATE" +%Y%m%d)"
75+
76+
node scripts/resolve-nightly-release.ts \
77+
--date "$nightly_date" \
78+
--run-number "$NIGHTLY_RUN_NUMBER" \
79+
--sha "$NIGHTLY_SHA" \
80+
--github-output
81+
82+
echo "release_channel=nightly" >> "$GITHUB_OUTPUT"
83+
echo "cli_dist_tag=nightly" >> "$GITHUB_OUTPUT"
84+
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
85+
echo "make_latest=false" >> "$GITHUB_OUTPUT"
86+
else
87+
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
88+
raw="${DISPATCH_VERSION}"
89+
if [[ -z "$raw" ]]; then
90+
echo "workflow_dispatch stable releases require the version input." >&2
91+
exit 1
92+
fi
93+
else
94+
raw="${GITHUB_REF_NAME}"
95+
fi
96+
97+
version="${raw#v}"
98+
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
99+
echo "Invalid release version: $raw" >&2
100+
exit 1
101+
fi
102+
103+
echo "release_channel=stable" >> "$GITHUB_OUTPUT"
104+
echo "version=$version" >> "$GITHUB_OUTPUT"
105+
echo "tag=v$version" >> "$GITHUB_OUTPUT"
106+
echo "name=T3 Code v$version" >> "$GITHUB_OUTPUT"
107+
echo "cli_dist_tag=latest" >> "$GITHUB_OUTPUT"
108+
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
109+
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
110+
echo "make_latest=true" >> "$GITHUB_OUTPUT"
111+
else
112+
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
113+
echo "make_latest=false" >> "$GITHUB_OUTPUT"
114+
fi
115+
fi
116+
72117
- name: Lint
73118
run: bun run lint
74119

@@ -78,6 +123,14 @@ jobs:
78123
- name: Test
79124
run: bun run test
80125

126+
- id: previous_tag
127+
name: Resolve previous release tag
128+
run: |
129+
node scripts/resolve-previous-release-tag.ts \
130+
--channel "${{ steps.release_meta.outputs.release_channel }}" \
131+
--current-tag "${{ steps.release_meta.outputs.tag }}" \
132+
--github-output
133+
81134
build:
82135
name: Build ${{ matrix.label }}
83136
needs: preflight
@@ -206,16 +259,17 @@ jobs:
206259
"release/*.AppImage" \
207260
"release/*.exe" \
208261
"release/*.blockmap" \
209-
"release/latest*.yml"; do
262+
"release/*.yml"; do
210263
for file in $pattern; do
211264
cp "$file" release-publish/
212265
done
213266
done
214267
215268
if [[ "${{ matrix.platform }}" == "mac" && "${{ matrix.arch }}" != "arm64" ]]; then
216-
if [[ -f release-publish/latest-mac.yml ]]; then
217-
mv release-publish/latest-mac.yml "release-publish/latest-mac-${{ matrix.arch }}.yml"
218-
fi
269+
shopt -s nullglob
270+
for manifest in release-publish/*-mac.yml; do
271+
mv "$manifest" "${manifest%.yml}-${{ matrix.arch }}.yml"
272+
done
219273
fi
220274
221275
- name: Upload build artifacts
@@ -257,7 +311,7 @@ jobs:
257311
run: bun run build --filter=@t3tools/web --filter=t3
258312

259313
- name: Publish CLI package
260-
run: node apps/server/scripts/cli.ts publish --tag latest --app-version "${{ needs.preflight.outputs.version }}" --verbose
314+
run: node apps/server/scripts/cli.ts publish --tag "${{ needs.preflight.outputs.cli_dist_tag }}" --app-version "${{ needs.preflight.outputs.version }}" --verbose
261315

262316
release:
263317
name: Publish GitHub Release
@@ -284,17 +338,42 @@ jobs:
284338

285339
- name: Merge macOS updater manifests
286340
run: |
287-
node scripts/merge-mac-update-manifests.ts \
288-
release-assets/latest-mac.yml \
289-
release-assets/latest-mac-x64.yml
290-
rm -f release-assets/latest-mac-x64.yml
341+
shopt -s nullglob
342+
for x64_manifest in release-assets/*-mac-x64.yml; do
343+
arm64_manifest="${x64_manifest%-x64.yml}.yml"
344+
if [[ -f "$arm64_manifest" ]]; then
345+
node scripts/merge-mac-update-manifests.ts "$arm64_manifest" "$x64_manifest"
346+
rm -f "$x64_manifest"
347+
fi
348+
done
291349
292350
- name: Publish release
351+
if: needs.preflight.outputs.previous_tag != ''
352+
uses: softprops/action-gh-release@v2
353+
with:
354+
tag_name: ${{ needs.preflight.outputs.tag }}
355+
target_commitish: ${{ needs.preflight.outputs.ref }}
356+
name: ${{ needs.preflight.outputs.release_name }}
357+
generate_release_notes: true
358+
previous_tag: ${{ needs.preflight.outputs.previous_tag }}
359+
prerelease: ${{ needs.preflight.outputs.is_prerelease }}
360+
make_latest: ${{ needs.preflight.outputs.make_latest }}
361+
files: |
362+
release-assets/*.dmg
363+
release-assets/*.zip
364+
release-assets/*.AppImage
365+
release-assets/*.exe
366+
release-assets/*.blockmap
367+
release-assets/*.yml
368+
fail_on_unmatched_files: true
369+
370+
- name: Publish first release
371+
if: needs.preflight.outputs.previous_tag == ''
293372
uses: softprops/action-gh-release@v2
294373
with:
295374
tag_name: ${{ needs.preflight.outputs.tag }}
296375
target_commitish: ${{ needs.preflight.outputs.ref }}
297-
name: T3 Code v${{ needs.preflight.outputs.version }}
376+
name: ${{ needs.preflight.outputs.release_name }}
298377
generate_release_notes: true
299378
prerelease: ${{ needs.preflight.outputs.is_prerelease }}
300379
make_latest: ${{ needs.preflight.outputs.make_latest }}
@@ -304,11 +383,12 @@ jobs:
304383
release-assets/*.AppImage
305384
release-assets/*.exe
306385
release-assets/*.blockmap
307-
release-assets/latest*.yml
386+
release-assets/*.yml
308387
fail_on_unmatched_files: true
309388

310389
finalize:
311390
name: Finalize release
391+
if: needs.preflight.outputs.release_channel == 'stable'
312392
needs: [preflight, release]
313393
runs-on: ubuntu-24.04
314394
timeout-minutes: 10

.oxfmtrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"*.tsbuildinfo",
1111
"**/routeTree.gen.ts",
1212
"apps/web/public/mockServiceWorker.js",
13-
"apps/web/src/lib/vendor/qrcodegen.ts"
13+
"apps/web/src/lib/vendor/qrcodegen.ts",
14+
"*.icon/**"
1415
],
1516
"sortPackageJson": {}
1617
}

apps/desktop/scripts/electron-launcher.mjs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
cpSync,
77
existsSync,
88
mkdirSync,
9+
mkdtempSync,
910
readFileSync,
1011
readdirSync,
1112
rmSync,
@@ -23,6 +24,9 @@ const LAUNCHER_VERSION = 1;
2324

2425
const __dirname = dirname(fileURLToPath(import.meta.url));
2526
export const desktopDir = resolve(__dirname, "..");
27+
const repoRoot = resolve(desktopDir, "..", "..");
28+
const defaultIconPath = join(desktopDir, "resources", "icon.icns");
29+
const developmentMacIconPngPath = join(repoRoot, "assets", "dev", "blueprint-macos-1024.png");
2630

2731
function setPlistString(plistPath, key, value) {
2832
const replaceResult = spawnSync("plutil", ["-replace", key, "-string", value, plistPath], {
@@ -43,6 +47,68 @@ function setPlistString(plistPath, key, value) {
4347
throw new Error(`Failed to update plist key "${key}" at ${plistPath}: ${details}`.trim());
4448
}
4549

50+
function runChecked(command, args) {
51+
const result = spawnSync(command, args, { encoding: "utf8" });
52+
if (result.status === 0) {
53+
return;
54+
}
55+
56+
const details = [result.stdout, result.stderr].filter(Boolean).join("\n");
57+
throw new Error(`Failed to run ${command} ${args.join(" ")}: ${details}`.trim());
58+
}
59+
60+
function ensureDevelopmentIconIcns(runtimeDir) {
61+
const generatedIconPath = join(runtimeDir, "icon-dev.icns");
62+
mkdirSync(runtimeDir, { recursive: true });
63+
64+
if (!existsSync(developmentMacIconPngPath)) {
65+
return defaultIconPath;
66+
}
67+
68+
const sourceMtimeMs = statSync(developmentMacIconPngPath).mtimeMs;
69+
if (existsSync(generatedIconPath) && statSync(generatedIconPath).mtimeMs >= sourceMtimeMs) {
70+
return generatedIconPath;
71+
}
72+
73+
const iconsetRoot = mkdtempSync(join(runtimeDir, "dev-iconset-"));
74+
const iconsetDir = join(iconsetRoot, "icon.iconset");
75+
mkdirSync(iconsetDir, { recursive: true });
76+
77+
try {
78+
for (const size of [16, 32, 128, 256, 512]) {
79+
runChecked("sips", [
80+
"-z",
81+
String(size),
82+
String(size),
83+
developmentMacIconPngPath,
84+
"--out",
85+
join(iconsetDir, `icon_${size}x${size}.png`),
86+
]);
87+
88+
const retinaSize = size * 2;
89+
runChecked("sips", [
90+
"-z",
91+
String(retinaSize),
92+
String(retinaSize),
93+
developmentMacIconPngPath,
94+
"--out",
95+
join(iconsetDir, `icon_${size}x${size}@2x.png`),
96+
]);
97+
}
98+
99+
runChecked("iconutil", ["-c", "icns", iconsetDir, "-o", generatedIconPath]);
100+
return generatedIconPath;
101+
} catch (error) {
102+
console.warn(
103+
"[desktop-launcher] Failed to generate dev macOS icon, falling back to default icon.",
104+
error,
105+
);
106+
return defaultIconPath;
107+
} finally {
108+
rmSync(iconsetRoot, { recursive: true, force: true });
109+
}
110+
}
111+
46112
function patchMainBundleInfoPlist(appBundlePath, iconPath) {
47113
const infoPlistPath = join(appBundlePath, "Contents", "Info.plist");
48114
setPlistString(infoPlistPath, "CFBundleDisplayName", APP_DISPLAY_NAME);
@@ -102,7 +168,7 @@ function buildMacLauncher(electronBinaryPath) {
102168
const runtimeDir = join(desktopDir, ".electron-runtime");
103169
const targetAppBundlePath = join(runtimeDir, `${APP_DISPLAY_NAME}.app`);
104170
const targetBinaryPath = join(targetAppBundlePath, "Contents", "MacOS", "Electron");
105-
const iconPath = join(desktopDir, "resources", "icon.icns");
171+
const iconPath = isDevelopment ? ensureDevelopmentIconIcns(runtimeDir) : defaultIconPath;
106172
const metadataPath = join(runtimeDir, "metadata.json");
107173

108174
mkdirSync(runtimeDir, { recursive: true });

0 commit comments

Comments
 (0)