Skip to content
217 linesCodeBlameRaw
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.
7#
8# A release is a tag `desktop-v<version>`, where the version is the one in
9# apps/desktop/src-tauri/Cargo.toml; or run it by hand. scripts/
10# desktop-release.mjs does the work, and its header says how to make the
11# signing key the first time; CONTRIBUTING.md ("Deploying") says how a
12# release is made; the guide is docs.g1t.sh/guides/desktop/.
13#
14# Needs the repository secret DESKTOP_SIGNING_KEY (the updater's private
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`.
27name: Desktop release
28
29on:
30 push:
31 tags: ["desktop-v*"]
32 workflow_dispatch:
33 inputs:
34 notes:
35 description: "A line about the release, shown in the manifest"
36 type: string
37
38permissions:
39 contents: read
40
41concurrency:
42 group: desktop-release
43 cancel-in-progress: false
44
45env:
46 CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
47 WRANGLER_SEND_METRICS: "false"
48
49jobs:
50 version:
51 name: The tag names this version
52 runs-on: ubuntu-latest
53 outputs:
54 version: ${{ steps.read.outputs.version }}
55 steps:
56 - uses: actions/checkout@v5
57 - id: read
58 run: |
59 version="$(sed -n 's/^version = "\(.*\)"/\1/p' apps/desktop/src-tauri/Cargo.toml | head -1)"
60 echo "version=$version" >> "$GITHUB_OUTPUT"
61 if [ "${GITHUB_REF_TYPE}" = tag ] && [ "desktop-v$version" != "${GITHUB_REF_NAME}" ]; then
62 echo "::error::The tag is ${GITHUB_REF_NAME}, but apps/desktop is $version"; exit 1
63 fi
64
65 linux:
66 name: Linux
67 needs: version
68 # The larger machine: linking Tauri on the small one dies with a bus error.
69 runs-on: g1t-4core
70 environment: production
71 timeout-minutes: 90
72 steps:
73 - uses: actions/checkout@v5
74 - name: What Tauri needs on Linux
75 # Tried three times: the mirror has answered with a mismatched package before.
76 run: |
77 ok=0
78 for try in 1 2 3; do
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
80 sudo apt-get clean; sleep 15
81 done
82 [ "$ok" = 1 ]
83 - uses: actions/cache@v4
84 with:
85 path: |
86 ~/.cargo/registry
87 apps/desktop/src-tauri/target
88 key: desktop-release-linux-${{ hashFiles('apps/desktop/src-tauri/Cargo.lock') }}
89 - name: Install
90 run: npm ci --no-audit --no-fund
91 - name: Build and sign
92 env:
93 TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.DESKTOP_SIGNING_KEY }}
94 TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ""
95 run: node scripts/desktop-release.mjs build
96 - name: Collect
97 run: node scripts/desktop-release.mjs collect
98 - uses: actions/upload-artifact@v4
99 with:
100 name: desktop-linux
101 path: target/desktop-release/${{ needs.version.outputs.version }}/
102
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
164 macos:
165 name: macOS
166 needs: version
167 if: vars.DESKTOP_MACOS_RUNNER == 'true'
168 runs-on: [self-hosted, macos]
169 environment: production
170 timeout-minutes: 90
171 steps:
172 - uses: actions/checkout@v5
173 - name: Install
174 run: npm ci --no-audit --no-fund
175 - name: Build and sign, for Apple silicon and Intel
176 env:
177 TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.DESKTOP_SIGNING_KEY }}
178 TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ""
179 run: |
180 rustup target add aarch64-apple-darwin x86_64-apple-darwin
181 npx tauri build --ci --target aarch64-apple-darwin
182 npx tauri build --ci --target x86_64-apple-darwin
183 working-directory: apps/desktop
184 - name: Collect
185 run: node scripts/desktop-release.mjs collect
186 - uses: actions/upload-artifact@v4
187 with:
188 name: desktop-macos
189 path: target/desktop-release/${{ needs.version.outputs.version }}/
190
191 publish:
192 name: Publish to g1t.sh/downloads/desktop
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)
200 runs-on: ubuntu-latest
201 environment: production
202 timeout-minutes: 20
203 steps:
204 - uses: actions/checkout@v5
205 - uses: actions/download-artifact@v4
206 with:
207 pattern: desktop-*
208 merge-multiple: true
209 path: target/desktop-release/${{ needs.version.outputs.version }}/
210 - name: Install Wrangler
211 run: npm ci --workspaces=false --no-audit --no-fund
212 - name: The manifest
213 run: node scripts/desktop-release.mjs manifest --notes "${{ inputs.notes }}"
214 - name: Publish
215 env:
216 CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
217 run: node scripts/desktop-release.mjs publish