Skip to content

Commit eaf3281

Browse files
committed
ci: assemble complete latest.json across all platforms
Since v3.60.0 (commit cca1578) only the macOS x86_64 "coordinator" job uploaded latest.json, and a single tauri-action job only knows its own build, so latest.json ended up with just darwin-x86_64. Updater clients on every other arch stopped seeing an update. Add a compose_latest_json job (needs: release / release_beta) that runs after all builds, reads each platform's updater artifact + .sig already on the release, and assembles one complete latest.json (darwin x64/arm, windows x64/arm, linux x64/arm), overwriting the partial one. Applied to both release.yml and release_beta.yml.
1 parent 1fd5c22 commit eaf3281

2 files changed

Lines changed: 202 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,104 @@ jobs:
153153
token: ${{ secrets.MY_TOKEN }}
154154
files: |
155155
*.proxy.json
156+
157+
compose_latest_json:
158+
name: "Compose complete latest.json"
159+
needs: release
160+
runs-on: ubuntu-latest
161+
steps:
162+
- name: Assemble latest.json from every platform's updater assets
163+
uses: actions/github-script@v7
164+
with:
165+
github-token: ${{ secrets.MY_TOKEN }}
166+
script: |
167+
const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/");
168+
const tag = "${{ github.ref_name }}";
169+
170+
const rel = await github.rest.repos.getReleaseByTag({ owner, repo, tag });
171+
const assets = await github.paginate(github.rest.repos.listReleaseAssets, {
172+
owner, repo, release_id: rel.data.id, per_page: 100,
173+
});
174+
const byName = new Map(assets.map((a) => [a.name, a]));
175+
176+
const downloadUrl = (name) =>
177+
`https://github.com/${owner}/${repo}/releases/download/${tag}/${encodeURIComponent(name)}`;
178+
179+
async function readSig(name) {
180+
const a = byName.get(name + ".sig");
181+
if (!a) return null;
182+
const res = await github.request(
183+
"GET /repos/{owner}/{repo}/releases/assets/{asset_id}",
184+
{ owner, repo, asset_id: a.id, headers: { accept: "application/octet-stream" } }
185+
);
186+
return Buffer.from(res.data).toString("utf8").trim();
187+
}
188+
189+
// First non-.sig asset whose name matches the regex.
190+
const pick = (re) =>
191+
(assets.find((a) => re.test(a.name) && !a.name.endsWith(".sig")) || {}).name;
192+
193+
const macX = pick(/^alist-desktop_x64\.app\.tar\.gz$/);
194+
const macA = pick(/^alist-desktop_aarch64\.app\.tar\.gz$/);
195+
const winXmsi = pick(/_x64_en-US\.msi\.zip$/);
196+
const winXnsis = pick(/_x64-setup\.nsis\.zip$/);
197+
const winA = pick(/_arm64-setup\.nsis\.zip$/);
198+
const linX = pick(/_amd64\.AppImage\.tar\.gz$/);
199+
const linA = pick(/_aarch64\.AppImage\.tar\.gz$/);
200+
201+
const platforms = {};
202+
async function add(key, name) {
203+
if (!name) return;
204+
const signature = await readSig(name);
205+
if (signature == null) {
206+
core.warning(`no .sig for ${name}, skipping ${key}`);
207+
return;
208+
}
209+
platforms[key] = { signature, url: downloadUrl(name) };
210+
}
211+
212+
await add("darwin-x86_64", macX);
213+
await add("darwin-x86_64-app", macX);
214+
await add("darwin-aarch64", macA);
215+
await add("darwin-aarch64-app", macA);
216+
await add("windows-x86_64", winXmsi || winXnsis);
217+
if (winXmsi) await add("windows-x86_64-msi", winXmsi);
218+
if (winXnsis) await add("windows-x86_64-nsis", winXnsis);
219+
await add("windows-aarch64", winA);
220+
await add("windows-aarch64-nsis", winA);
221+
await add("linux-x86_64", linX);
222+
await add("linux-aarch64", linA);
223+
224+
if (Object.keys(platforms).length === 0) {
225+
core.setFailed("No updater artifacts found; latest.json not composed");
226+
return;
227+
}
228+
229+
let version = tag.replace(/^v/, "");
230+
const versioned = winXnsis || winXmsi || winA || linX || linA || "";
231+
const vm = versioned.match(/alist-desktop_([0-9][^_]*)_/);
232+
if (vm) version = vm[1];
233+
234+
const latest = {
235+
version,
236+
notes: "See the assets to download and install this version.",
237+
pub_date: new Date().toISOString(),
238+
platforms,
239+
};
240+
const body = JSON.stringify(latest, null, 2);
241+
core.info("Composed latest.json:\n" + body);
242+
243+
const existing = byName.get("latest.json");
244+
if (existing) {
245+
await github.rest.repos.deleteReleaseAsset({ owner, repo, asset_id: existing.id });
246+
}
247+
await github.rest.repos.uploadReleaseAsset({
248+
owner, repo, release_id: rel.data.id,
249+
name: "latest.json",
250+
data: body,
251+
headers: {
252+
"content-type": "application/json",
253+
"content-length": Buffer.byteLength(body),
254+
},
255+
});
256+
core.info(`Uploaded latest.json with platforms: ${Object.keys(platforms).join(", ")}`);

.github/workflows/release_beta.yml

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,104 @@ jobs:
197197
fail_on_unmatched_files: true
198198
files: |
199199
*.proxy.json
200+
201+
compose_latest_json:
202+
name: "Compose complete latest.json beta"
203+
needs: release_beta
204+
runs-on: ubuntu-latest
205+
steps:
206+
- name: Assemble latest.json from every platform's updater assets
207+
uses: actions/github-script@v7
208+
with:
209+
github-token: ${{ secrets.MY_TOKEN }}
210+
script: |
211+
const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/");
212+
const tag = "beta";
213+
214+
const rel = await github.rest.repos.getReleaseByTag({ owner, repo, tag });
215+
const assets = await github.paginate(github.rest.repos.listReleaseAssets, {
216+
owner, repo, release_id: rel.data.id, per_page: 100,
217+
});
218+
const byName = new Map(assets.map((a) => [a.name, a]));
219+
220+
const downloadUrl = (name) =>
221+
`https://github.com/${owner}/${repo}/releases/download/${tag}/${encodeURIComponent(name)}`;
222+
223+
async function readSig(name) {
224+
const a = byName.get(name + ".sig");
225+
if (!a) return null;
226+
const res = await github.request(
227+
"GET /repos/{owner}/{repo}/releases/assets/{asset_id}",
228+
{ owner, repo, asset_id: a.id, headers: { accept: "application/octet-stream" } }
229+
);
230+
return Buffer.from(res.data).toString("utf8").trim();
231+
}
232+
233+
// First non-.sig asset whose name matches the regex.
234+
const pick = (re) =>
235+
(assets.find((a) => re.test(a.name) && !a.name.endsWith(".sig")) || {}).name;
236+
237+
const macX = pick(/^alist-desktop_x64\.app\.tar\.gz$/);
238+
const macA = pick(/^alist-desktop_aarch64\.app\.tar\.gz$/);
239+
const winXmsi = pick(/_x64_en-US\.msi\.zip$/);
240+
const winXnsis = pick(/_x64-setup\.nsis\.zip$/);
241+
const winA = pick(/_arm64-setup\.nsis\.zip$/);
242+
const linX = pick(/_amd64\.AppImage\.tar\.gz$/);
243+
const linA = pick(/_aarch64\.AppImage\.tar\.gz$/);
244+
245+
const platforms = {};
246+
async function add(key, name) {
247+
if (!name) return;
248+
const signature = await readSig(name);
249+
if (signature == null) {
250+
core.warning(`no .sig for ${name}, skipping ${key}`);
251+
return;
252+
}
253+
platforms[key] = { signature, url: downloadUrl(name) };
254+
}
255+
256+
await add("darwin-x86_64", macX);
257+
await add("darwin-x86_64-app", macX);
258+
await add("darwin-aarch64", macA);
259+
await add("darwin-aarch64-app", macA);
260+
await add("windows-x86_64", winXmsi || winXnsis);
261+
if (winXmsi) await add("windows-x86_64-msi", winXmsi);
262+
if (winXnsis) await add("windows-x86_64-nsis", winXnsis);
263+
await add("windows-aarch64", winA);
264+
await add("windows-aarch64-nsis", winA);
265+
await add("linux-x86_64", linX);
266+
await add("linux-aarch64", linA);
267+
268+
if (Object.keys(platforms).length === 0) {
269+
core.setFailed("No updater artifacts found; latest.json not composed");
270+
return;
271+
}
272+
273+
let version = tag.replace(/^v/, "");
274+
const versioned = winXnsis || winXmsi || winA || linX || linA || "";
275+
const vm = versioned.match(/alist-desktop_([0-9][^_]*)_/);
276+
if (vm) version = vm[1];
277+
278+
const latest = {
279+
version,
280+
notes: "See the assets to download and install this version.",
281+
pub_date: new Date().toISOString(),
282+
platforms,
283+
};
284+
const body = JSON.stringify(latest, null, 2);
285+
core.info("Composed latest.json:\n" + body);
286+
287+
const existing = byName.get("latest.json");
288+
if (existing) {
289+
await github.rest.repos.deleteReleaseAsset({ owner, repo, asset_id: existing.id });
290+
}
291+
await github.rest.repos.uploadReleaseAsset({
292+
owner, repo, release_id: rel.data.id,
293+
name: "latest.json",
294+
data: body,
295+
headers: {
296+
"content-type": "application/json",
297+
"content-length": Buffer.byteLength(body),
298+
},
299+
});
300+
core.info(`Uploaded latest.json with platforms: ${Object.keys(platforms).join(", ")}`);

0 commit comments

Comments
 (0)