Skip to content

Commit 8cb80d6

Browse files
authored
🤖 ci: speed up mac build job (#599)
## Summary - drop the standalone `bun run build` invocation from the macOS workflow so `make dist-mac` is the only target that compiles everything - replace the ImageMagick dependency with `scripts/generate-icons.ts` (Bun + Sharp) and delete the `install-imagemagick` plumbing from setup-mux plus every workflow that used it - setup now restores caches + runs `bun install` only, cutting the macOS job from ~19m to ~9m (setup step alone shrank from 13m30s ➜ 2m03s) | Run | Workflow version | Setup mux duration | Build macOS job | | --- | --- | --- | --- | | [19375324780](https://github.com/coder/mux/actions/runs/19375324780/job/55441174656) | before | 13m31s | 19m05s | | [19376083893](https://github.com/coder/mux/actions/runs/19376083893/job/55443762040) | after | 2m03s | 8m43s | ## Testing - make build-icons - ./scripts/wait_pr_checks.sh 599 _Generated with `mux`_
1 parent 0683424 commit 8cb80d6

File tree

8 files changed

+98
-75
lines changed

8 files changed

+98
-75
lines changed
Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
name: "Setup Mux"
22
description: "Setup Bun and install dependencies with caching"
3-
inputs:
4-
install-imagemagick:
5-
description: "Install ImageMagick (needed for electron-builder icon generation)"
6-
required: false
7-
default: "false"
83
runs:
94
using: "composite"
105
steps:
@@ -38,38 +33,3 @@ runs:
3833
shell: bash
3934
run: bun install --frozen-lockfile
4035

41-
- name: Install ImageMagick (macOS)
42-
if: inputs.install-imagemagick == 'true' && runner.os == 'macOS'
43-
shell: bash
44-
run: |
45-
if command -v magick &>/dev/null; then
46-
echo "✅ ImageMagick already available"
47-
else
48-
echo "📦 Installing ImageMagick..."
49-
HOMEBREW_NO_AUTO_UPDATE=1 brew install imagemagick
50-
fi
51-
magick --version | head -1
52-
53-
- name: Install ImageMagick (Linux)
54-
if: inputs.install-imagemagick == 'true' && runner.os == 'Linux'
55-
shell: bash
56-
run: |
57-
if command -v convert &>/dev/null; then
58-
echo "✅ ImageMagick already available"
59-
else
60-
echo "📦 Installing ImageMagick..."
61-
sudo apt-get update -qq
62-
sudo apt-get install -y --no-install-recommends imagemagick
63-
fi
64-
convert --version | head -1
65-
- name: Install ImageMagick (Windows)
66-
if: inputs.install-imagemagick == 'true' && runner.os == 'Windows'
67-
shell: powershell
68-
run: |
69-
if (Get-Command magick -ErrorAction SilentlyContinue) {
70-
Write-Host "✅ ImageMagick already available"
71-
} else {
72-
Write-Host "📦 Installing ImageMagick..."
73-
choco install -y imagemagick
74-
}
75-
magick --version | Select-Object -First 1

.github/workflows/build.yml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@ jobs:
1717
fetch-depth: 0 # Required for git describe to find tags
1818

1919
- uses: ./.github/actions/setup-mux
20-
with:
21-
install-imagemagick: true
22-
23-
- name: Build application
24-
run: bun run build
2520

2621
- name: Setup code signing
2722
run: ./scripts/setup-macos-signing.sh
@@ -73,8 +68,6 @@ jobs:
7368
fetch-depth: 0 # Required for git describe to find tags
7469

7570
- uses: ./.github/actions/setup-mux
76-
with:
77-
install-imagemagick: true
7871

7972
- name: Build application
8073
run: bun run build

.github/workflows/publish-npm.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ jobs:
2323
fetch-depth: 0 # Required for git describe to find tags
2424

2525
- uses: ./.github/actions/setup-mux
26-
with:
27-
install-imagemagick: "true"
2826

2927
# Sets up .npmrc with the auth token
3028
- uses: actions/setup-node@v4

.github/workflows/release.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ jobs:
1818
fetch-depth: 0 # Required for git describe to find tags
1919

2020
- uses: ./.github/actions/setup-mux
21-
with:
22-
install-imagemagick: true
2321

2422
- name: Build application
2523
run: bun run build
@@ -48,8 +46,6 @@ jobs:
4846
fetch-depth: 0 # Required for git describe to find tags
4947

5048
- uses: ./.github/actions/setup-mux
51-
with:
52-
install-imagemagick: true
5349

5450
- name: Build application
5551
run: bun run build
@@ -98,8 +94,6 @@ jobs:
9894
fetch-depth: 0 # Required for git describe to find tags
9995

10096
- uses: ./.github/actions/setup-cmux
101-
with:
102-
install-imagemagick: true
10397

10498
- name: Install GNU Make (for build)
10599
run: choco install -y make

Makefile

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -188,30 +188,17 @@ src/version.ts: version
188188
# Platform-specific icon targets
189189
ifeq ($(shell uname), Darwin)
190190
build-icons: build/icon.icns build/icon.png ## Generate Electron app icons from logo (macOS builds both)
191+
192+
build/icon.icns: docs/img/logo.webp scripts/generate-icons.ts
193+
@echo "Generating macOS ICNS icon..."
194+
@bun scripts/generate-icons.ts icns
191195
else
192196
build-icons: build/icon.png ## Generate Electron app icons from logo (Linux builds PNG only)
193197
endif
194198

195-
# Detect ImageMagick command (magick on v7+, convert on older versions)
196-
MAGICK_CMD := $(shell command -v magick 2>/dev/null || command -v convert 2>/dev/null || echo "magick")
197-
198-
build/icon.png: docs/img/logo.webp
199-
@echo "Generating Linux icon..."
200-
@mkdir -p build
201-
@"$(MAGICK_CMD)" docs/img/logo.webp -resize 512x512 build/icon.png
202-
203-
build/icon.icns: docs/img/logo.webp
204-
@echo "Generating macOS icon..."
205-
@mkdir -p build/icon.iconset
206-
@for size in 16 32 64 128 256 512; do \
207-
"$(MAGICK_CMD)" docs/img/logo.webp -resize $${size}x$${size} build/icon.iconset/icon_$${size}x$${size}.png; \
208-
if [ $$size -le 256 ]; then \
209-
double=$$((size * 2)); \
210-
"$(MAGICK_CMD)" docs/img/logo.webp -resize $${double}x$${double} build/icon.iconset/icon_$${size}x$${size}@2x.png; \
211-
fi; \
212-
done
213-
@iconutil -c icns build/icon.iconset -o build/icon.icns
214-
@rm -rf build/icon.iconset
199+
build/icon.png: docs/img/logo.webp scripts/generate-icons.ts
200+
@echo "Generating PNG icon..."
201+
@bun scripts/generate-icons.ts png
215202

216203
## Quality checks (can run in parallel)
217204
static-check: lint typecheck fmt-check check-eager-imports ## Run all static checks (includes startup performance checks)

bun.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
"rehype-raw": "^7.0.0",
106106
"remark-gfm": "^4.0.1",
107107
"remark-math": "^6.0.0",
108+
"sharp": "^0.34.5",
108109
"shiki": "^3.13.0",
109110
"storybook": "^10.0.0",
110111
"tailwind-merge": "^3.3.1",

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@
144144
"rehype-raw": "^7.0.0",
145145
"remark-gfm": "^4.0.1",
146146
"remark-math": "^6.0.0",
147+
"sharp": "^0.34.5",
147148
"shiki": "^3.13.0",
148149
"storybook": "^10.0.0",
149150
"tailwind-merge": "^3.3.1",

scripts/generate-icons.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env bun
2+
import { mkdir, rm } from "node:fs/promises";
3+
import path from "node:path";
4+
import { fileURLToPath } from "node:url";
5+
import sharp from "sharp";
6+
7+
const SIZES = [16, 32, 64, 128, 256, 512];
8+
9+
const __filename = fileURLToPath(import.meta.url);
10+
const __dirname = path.dirname(__filename);
11+
const ROOT = path.resolve(__dirname, "..");
12+
const SOURCE = path.join(ROOT, "docs", "img", "logo.webp");
13+
const BUILD_DIR = path.join(ROOT, "build");
14+
const ICONSET_DIR = path.join(BUILD_DIR, "icon.iconset");
15+
const PNG_OUTPUT = path.join(BUILD_DIR, "icon.png");
16+
const ICNS_OUTPUT = path.join(BUILD_DIR, "icon.icns");
17+
18+
const args = new Set(process.argv.slice(2));
19+
if (args.size === 0) {
20+
args.add("png");
21+
args.add("icns");
22+
}
23+
const needsPng = args.has("png") || args.has("icns");
24+
const needsIcns = args.has("icns");
25+
26+
async function generateIconPng() {
27+
await sharp(SOURCE).resize(512, 512).toFile(PNG_OUTPUT);
28+
}
29+
30+
async function generateIconsetPngs() {
31+
await mkdir(ICONSET_DIR, { recursive: true });
32+
33+
const tasks = SIZES.flatMap((size) => {
34+
const outputs = [
35+
{
36+
file: path.join(ICONSET_DIR, `icon_${size}x${size}.png`),
37+
dimension: size,
38+
},
39+
];
40+
41+
if (size <= 256) {
42+
const retina = size * 2;
43+
outputs.push({
44+
file: path.join(ICONSET_DIR, `icon_${size}x${size}@2x.png`),
45+
dimension: retina,
46+
});
47+
}
48+
49+
return outputs.map(({ file, dimension }) =>
50+
sharp(SOURCE)
51+
.resize(dimension, dimension, { fit: "cover" })
52+
.toFile(file),
53+
);
54+
});
55+
56+
await Promise.all(tasks);
57+
}
58+
59+
async function generateIcns() {
60+
if (process.platform !== "darwin") {
61+
throw new Error("ICNS generation requires macOS (iconutil)");
62+
}
63+
64+
const proc = Bun.spawn([
65+
"iconutil",
66+
"-c",
67+
"icns",
68+
ICONSET_DIR,
69+
"-o",
70+
ICNS_OUTPUT,
71+
]);
72+
const status = await proc.exited;
73+
if (status !== 0) {
74+
throw new Error("iconutil failed to generate .icns file");
75+
}
76+
}
77+
78+
await mkdir(BUILD_DIR, { recursive: true });
79+
80+
if (needsPng) {
81+
await generateIconPng();
82+
}
83+
84+
if (needsIcns) {
85+
await generateIconsetPngs();
86+
await generateIcns();
87+
}
88+
89+
await rm(ICONSET_DIR, { recursive: true, force: true });

0 commit comments

Comments
 (0)