Skip to content

Commit

A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so.

syntaqxcommitted Parent5240891Browse files
5 files+129−300/5 viewed
+91−19
1−# Releases the desktop app, g1t (apps/desktop): builds it for Windows and
2−# Linux on a Linux machine, for macOS on a Mac when one is registered, signs
3−# every updater file, publishes the release to g1t.sh/downloads/desktop/
4−# (the g1t-downloads R2 bucket), and points latest.json at it. Apps already
5−# installed update themselves to it; g1t.sh/download offers it.
1+# Releases the desktop app, g1t (apps/desktop): builds it for Linux on
2+# g1t's machines, for Windows and macOS on machines registered for them,
3+# signs every updater file, publishes the release to
4+# g1t.sh/downloads/desktop/ (the g1t-downloads R2 bucket), and points
5+# latest.json at it. Apps already installed update themselves to it;
6+# g1t.sh/download offers it, and says which platforms a release lacks.
67 #
78 # A release is a tag `desktop-v<version>`, where the version is the one in
89 # apps/desktop/src-tauri/Cargo.toml; or run it by hand. scripts/
910 # desktop-release.mjs does the work, and its header says how to make the
1011 # signing key the first time; CONTRIBUTING.md ("Deploying") says how a
11−# release is made.
12+# release is made; the guide is docs.g1t.sh/guides/desktop/.
1213 #
1314 # Needs the repository secret DESKTOP_SIGNING_KEY (the updater's private
14−# key; its public half is in tauri.conf.json), CLOUDFLARE_API_TOKEN and
15−# CLOUDFLARE_ACCOUNT_ID as the deploy has them, and, for macOS, a
16−# self-hosted runner on a Mac with the labels `self-hosted` and `macos`
17−# plus the repository variable DESKTOP_MACOS_RUNNER set to `true`; without
18−# it the macOS job is skipped and the release carries Windows and Linux.
15+# key; its public half is in tauri.conf.json), and CLOUDFLARE_API_TOKEN and
16+# CLOUDFLARE_ACCOUNT_ID as the deploy has them. The platform jobs beyond
17+# Linux each wait on a repository variable, so a release goes out with what
18+# can be built:
19+#
20+# DESKTOP_WINDOWS_RUNNER=true a self-hosted runner on Windows, labels `self-hosted` and `windows`,
21+# with Rust, Node and the Visual Studio build tools (the NSIS installer is fetched)
22+# DESKTOP_WINDOWS_FROM_LINUX=true no Windows machine: cross-build on g1t's Linux machine with cargo-xwin and NSIS.
23+# Needs aka.ms and download.visualstudio.microsoft.com among the project's
24+# workflow-only domains for desktop-release.yml in production (Settings, Guardrails),
25+# where cargo-xwin fetches the MSVC CRT from.
26+# DESKTOP_MACOS_RUNNER=true a self-hosted runner on a Mac, labels `self-hosted` and `macos`.
1927 name: Desktop release
2028
2129 on:
5563 fi
5664
5765 linux:
58− name: Linux and Windows
66+ name: Linux
5967 needs: version
6068 # The larger machine: linking Tauri on the small one dies with a bus error.
6169 runs-on: g1t-4core
6371 timeout-minutes: 90
6472 steps:
6573 - uses: actions/checkout@v5
66− - name: What Tauri needs on Linux, and to build Windows from here
74+ - name: What Tauri needs on Linux
6775 # Tried three times: the mirror has answered with a mismatched package before.
6876 run: |
6977 ok=0
7078 for try in 1 2 3; do
71− if sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev nsis lld llvm; then ok=1; break; fi
79+ if sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev; then ok=1; break; fi
7280 sudo apt-get clean; sleep 15
7381 done
7482 [ "$ok" = 1 ]
75− cargo install --locked cargo-xwin
7683 - uses: actions/cache@v4
7784 with:
7885 path: |
8592 env:
8693 TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.DESKTOP_SIGNING_KEY }}
8794 TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ""
88− run: node scripts/desktop-release.mjs build --windows-from-linux
95+ run: node scripts/desktop-release.mjs build
8996 - name: Collect
9097 run: node scripts/desktop-release.mjs collect
9198 - uses: actions/upload-artifact@v4
93100 name: desktop-linux
94101 path: target/desktop-release/${{ needs.version.outputs.version }}/
95102
103+ windows:
104+ name: Windows
105+ needs: version
106+ if: vars.DESKTOP_WINDOWS_RUNNER == 'true'
107+ runs-on: [self-hosted, windows]
108+ environment: production
109+ timeout-minutes: 90
110+ steps:
111+ - uses: actions/checkout@v5
112+ - name: Install
113+ run: npm ci --no-audit --no-fund
114+ - name: Build and sign
115+ env:
116+ TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.DESKTOP_SIGNING_KEY }}
117+ TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ""
118+ run: node scripts/desktop-release.mjs build
119+ - name: Collect
120+ run: node scripts/desktop-release.mjs collect
121+ - uses: actions/upload-artifact@v4
122+ with:
123+ name: desktop-windows
124+ path: target/desktop-release/${{ needs.version.outputs.version }}/
125+
126+ windows_cross:
127+ name: Windows, built on Linux
128+ needs: version
129+ if: vars.DESKTOP_WINDOWS_FROM_LINUX == 'true' && vars.DESKTOP_WINDOWS_RUNNER != 'true'
130+ runs-on: g1t-4core
131+ environment: production
132+ timeout-minutes: 90
133+ steps:
134+ - uses: actions/checkout@v5
135+ - name: What the cross build needs
136+ run: |
137+ ok=0
138+ for try in 1 2 3; do
139+ if sudo apt-get update && sudo apt-get install -y build-essential curl wget file libssl-dev nsis lld llvm; then ok=1; break; fi
140+ sudo apt-get clean; sleep 15
141+ done
142+ [ "$ok" = 1 ]
143+ cargo install --locked cargo-xwin
144+ - uses: actions/cache@v4
145+ with:
146+ path: |
147+ ~/.cargo/registry
148+ apps/desktop/src-tauri/target
149+ key: desktop-release-windows-${{ hashFiles('apps/desktop/src-tauri/Cargo.lock') }}
150+ - name: Install
151+ run: npm ci --no-audit --no-fund
152+ - name: Build and sign
153+ env:
154+ TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.DESKTOP_SIGNING_KEY }}
155+ TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ""
156+ run: node scripts/desktop-release.mjs build --windows-from-linux --only-windows
157+ - name: Collect
158+ run: node scripts/desktop-release.mjs collect
159+ - uses: actions/upload-artifact@v4
160+ with:
161+ name: desktop-windows
162+ path: target/desktop-release/${{ needs.version.outputs.version }}/
163+
96164 macos:
97165 name: macOS
98166 needs: version
122190
123191 publish:
124192 name: Publish to g1t.sh/downloads/desktop
125− needs: [version, linux, macos]
126− # With no Mac registered the macOS job is skipped, and the release still goes out.
127− if: always() && needs.linux.result == 'success' && (needs.macos.result == 'success' || needs.macos.result == 'skipped')
193+ needs: [version, linux, windows, windows_cross, macos]
194+ # A platform with no machine registered is skipped, and the release still goes out with the rest.
195+ if: >-
196+ always() && needs.linux.result == 'success'
197+ && contains(fromJSON('["success","skipped"]'), needs.windows.result)
198+ && contains(fromJSON('["success","skipped"]'), needs.windows_cross.result)
199+ && contains(fromJSON('["success","skipped"]'), needs.macos.result)
128200 runs-on: ubuntu-latest
129201 environment: production
130202 timeout-minutes: 20
+6−3
103103 - **The desktop app** is released the same way: bump `version` in
104104 `apps/desktop/src-tauri/Cargo.toml`, merge, and push a
105105 `desktop-v<version>` tag. `.g1t/workflows/desktop-release.yml` builds
106− the installers (Windows and Linux on Linux; macOS on a Mac runner when
107− one is registered), signs the updater's files and publishes them, and
108− installed apps update themselves. `scripts/desktop-release.mjs` does the
106+ the installers (Linux on g1t's machines; Windows and macOS on machines
107+ registered for them, or Windows cross-built on Linux once its download
108+ hosts are allowed, each switched on by a repository variable the
109+ workflow's header names; a platform with no machine is left out of that
110+ release), signs the updater's files and publishes them, and installed
111+ apps update themselves. `scripts/desktop-release.mjs` does the
109112 same by hand. The app loads the live site, so a change to the site needs
110113 no release; only a change under `apps/desktop` does. The guide is
111114 [The desktop app](https://docs.g1t.sh/guides/desktop/).
+4−2
129129 | What | Where |
130130 | --- | --- |
131131 | The updater's signing key | `node scripts/desktop-release.mjs keygen`, once. The private key is the repository secret `DESKTOP_SIGNING_KEY`; the public key is `plugins.updater.pubkey` in `tauri.conf.json`. Losing the private key means every installed app stops updating: keep it. |
132−| Windows and Linux builds | A Linux machine builds both: Linux natively, Windows with `cargo-xwin` and NSIS. |
133−| macOS builds | A Mac, registered as a [self-hosted runner](/guides/self-hosted-runners/) with the labels `self-hosted` and `macos`, and the repository variable `DESKTOP_MACOS_RUNNER` set to `true`. Without one, the macOS job is skipped and the release carries Windows and Linux. |
132+| Linux builds | g1t's own Linux machines, in every release. |
133+| Windows builds | Either a Windows machine registered as a [self-hosted runner](/guides/self-hosted-runners/) with the labels `self-hosted` and `windows` (Rust, Node and the Visual Studio build tools on it) and the repository variable `DESKTOP_WINDOWS_RUNNER` set to `true`; or, with no Windows machine, `DESKTOP_WINDOWS_FROM_LINUX` set to `true`, which cross-builds on Linux with `cargo-xwin` and NSIS and needs `aka.ms` and `download.visualstudio.microsoft.com` among the project's [workflow-only domains](/guides/guardrails/#workflow-only-domains) for `desktop-release.yml` in `production`, where the MSVC runtime is fetched from. |
134+| macOS builds | A Mac, registered as a self-hosted runner with the labels `self-hosted` and `macos`, and the repository variable `DESKTOP_MACOS_RUNNER` set to `true`. |
135+| A platform with no machine | Its job is skipped and the release goes out with the rest; the download page says which platforms a release lacks, and the next release fills them in. |
134136 | Where releases are served | `g1t.sh/downloads/desktop/<version>/<file>`, from the same bucket as the runner's and the CLI's releases. |
135137
136138 A self-hosted g1t does not serve the app's releases: the app always
+20−1
194194 </Card>
195195 )}
196196
197+ {release && !featured && platform && (
198+ <Card className="p-5">
199+ <p className="font-medium">Not yet for {PLATFORMS.find((p) => p.id === platform)?.name}.</p>
200+ <p className="mt-1 text-sm text-muted">
201+ Version {release.version} is out for {Array.from(new Set(release.downloads.map((d) => PLATFORMS.find((p) => p.id === d.platform)?.name))).join(" and ")};
202+ yours is built in a later release. Until then, g1t works as an installed web app: in your browser's menu, choose Install or Add to Dock.
203+ </p>
204+ </Card>
205+ )}
206+
197207 <div className="mt-10 grid gap-3 sm:grid-cols-2">
198208 {WHAT.map((item) => (
199209 <div key={item.title} className="flex gap-3 rounded-xl border border-line bg-surface p-5">
219229 <div className="mt-4 space-y-3">
220230 {ordered.map((p) => {
221231 const files = release.downloads.filter((d) => d.platform === p.id);
222− if (files.length === 0) return null;
232+ // A platform this release lacks: it is built on a machine not yet registered.
233+ if (files.length === 0) {
234+ return (
235+ <Card key={p.id} tone="plain" className="flex flex-wrap items-center gap-2 rounded-xl px-4 py-3">
236+ <span className="text-faint">{p.icon}</span>
237+ <span className="font-medium text-muted">{p.name}</span>
238+ <span className="text-sm text-muted">· Coming in a later release. {p.note}</span>
239+ </Card>
240+ );
241+ }
223242 return (
224243 <Card key={p.id} tone="plain" divided className="rounded-xl">
225244 <div className="flex items-center gap-2 px-4 py-3">
+8−5
88 // release (apps/web/app/routes/download.tsx).
99 //
1010 // node scripts/desktop-release.mjs keygen # once: the updater's signing key
11−// node scripts/desktop-release.mjs build [--windows-from-linux]
11+// node scripts/desktop-release.mjs build [--windows-from-linux [--only-windows]]
1212 // node scripts/desktop-release.mjs collect # this machine's bundles, into the release folder
1313 // node scripts/desktop-release.mjs manifest [--notes "…"] # latest.json and SHA256SUMS from what is collected
1414 // node scripts/desktop-release.mjs publish [--dry-run]
155155 run("npx", ["tauri", "signer", "generate", "--ci"], { cwd: APP });
156156 }
157157
158−function build(windowsFromLinux) {
158+/** `windowsFromLinux` adds the Windows cross build; `onlyWindows` skips this machine's own. */
159+function build(windowsFromLinux, onlyWindows = false) {
159160 if (!process.env.TAURI_SIGNING_PRIVATE_KEY) console.error("warning: TAURI_SIGNING_PRIVATE_KEY is not set; these bundles cannot be fetched by the updater");
160− console.error(`building ${hostPlatform()}`);
161− run("npx", ["tauri", "build", "--ci"], { cwd: APP });
161+ if (!onlyWindows) {
162+ console.error(`building ${hostPlatform()}`);
163+ run("npx", ["tauri", "build", "--ci"], { cwd: APP });
164+ }
162165 if (windowsFromLinux) {
163166 // Tauri's cross build: cargo-xwin for the MSVC target, NSIS for the
164167 // installer, both on PATH (the release workflow installs them).
249252 keygen();
250253 break;
251254 case "build":
252− build(rest.includes("--windows-from-linux"));
255+ build(rest.includes("--windows-from-linux"), rest.includes("--only-windows"));
253256 break;
254257 case "collect":
255258 collect();