Simpl-Resultat/scripts/build-latest-json.sh
le king fu 230ecd85ad fix(release): drop the generic linux-x86_64 key from latest.json
Max's decision on PR #416, after its review measured who still read the
key. tauri-plugin-updater looks up "{os}-{arch}-{installer}" first, and
every version that ships the updater (v0.2.1 and later, plugin 2.10.0,
tauri-utils 2.8.2 in each tag's Cargo.lock) carries its bundle-type
marker, so .deb and .rpm installs never reach the generic key. Its last
readers were the AppImage installs v0.4.3 to v0.4.5 (v0.4.2 targets a
route that answers 404, earlier versions read GitHub) and binaries run
outside a package; none of them can install the .deb it served, and the
updater wrote that .deb over the running file. They now get
TargetsNotFound when checking: an error on the Updates card, nothing
installed.

The key leaves the script, scripts/check-latest-json.jq and
verify-release.sh together; the test compares the three lists. Docs fix
the readers named by #416 (v0.4.2 is not one) and the CHANGELOG entry
says what changes for those AppImage installs.

Refs #320

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:30:34 -04:00

116 lines
4.3 KiB
Bash
Executable file

#!/usr/bin/env bash
# Build latest.json, the updater manifest of a release, from its assets (#320).
#
# Usage: build-latest-json.sh <assets-dir> <version> <notes-file> <base-url>
#
# assets-dir directory holding the bundles and their .sig files
# version release version without the leading v, e.g. 0.17.1
# notes-file release notes, embedded verbatim as data (jq --rawfile)
# base-url download URL of the release, without a trailing slash, e.g.
# https://<forgejo>/<owner>/<repo>/releases/download/v0.17.1
#
# Writes the JSON on stdout and its log on stderr. release.yml redirects
# stdout to release-assets/latest.json; scripts/build-latest-json.test.mjs
# runs it under bash -e -o pipefail, as the CI runner does.
#
# Fails closed: unless the assets dir holds exactly one .deb, one .rpm and
# one -setup.exe, each with a non-empty "<asset>.sig" next to it, the script
# exits non-zero before writing anything on stdout. The JSON is built by jq
# alone: notes, file names and signatures enter it as jq values, never as
# text spliced into JSON.
#
# Keys. tauri-plugin-updater 2.x looks up "{os}-{arch}-{installer}" first,
# then "{os}-{arch}" (get_urls in its updater.rs). The installer is the
# bundle type that the bundler writes into the binary of each package.
#
# linux-x86_64-deb the .deb, for .deb installs.
# linux-x86_64-rpm the .rpm, for .rpm installs. Without this key they fell
# back on linux-x86_64, downloaded the .deb and refused it.
# windows-x86_64 the NSIS installer. An NSIS install looks up
# windows-x86_64-nsis first, then this key.
#
# No generic linux-x86_64 key since 2026-10-11 (Max's decision on #320). Every
# version that ships the updater (v0.2.1 and later, tauri-plugin-updater
# 2.10.0) carries its bundle-type marker, so .deb and .rpm installs never
# reach that key. Its only readers were the AppImage installs v0.4.3 to
# v0.4.5 and binaries run outside a package, and the .deb it served overwrote
# them: they now get TargetsNotFound when checking, nothing installed. See
# docs/qa-update-cycle.md, "Les clés de latest.json".
#
# Each URL ends with the file name percent-encoded by jq's @uri, as the upload
# step of release.yml encodes it: the bundle names contain a space.
set -euo pipefail
die() {
printf 'build-latest-json: %s\n' "$*" >&2
exit 1
}
[ "$#" -eq 4 ] || die "usage: build-latest-json.sh <assets-dir> <version> <notes-file> <base-url>"
assets_dir=$1
version=$2
notes_file=$3
base_url=$4
[ -d "$assets_dir" ] || die "assets dir not found: $assets_dir"
[ -n "$version" ] || die "empty version"
[ -f "$notes_file" ] || die "notes file not found: $notes_file"
[ -n "$base_url" ] || die "empty base URL"
# read_asset <kind> <matches...>: the single bundle of a kind and its
# signature, in asset_name and asset_sig. Any other count of matches, or a
# missing or blank "<asset>.sig", stops the script.
asset_name=""
asset_sig=""
read_asset() {
local kind=$1
shift
if [ "$#" -ne 1 ]; then
printf 'build-latest-json: expected exactly one %s in %s, found %d\n' "$kind" "$assets_dir" "$#" >&2
[ "$#" -eq 0 ] || printf ' %s\n' "$@" >&2
exit 1
fi
local file=$1
[ -s "$file.sig" ] || die "missing or empty signature: $file.sig"
asset_name=${file##*/}
asset_sig=$(cat "$file.sig")
[ -n "$asset_sig" ] || die "blank signature: $file.sig"
printf 'build-latest-json: %s <- %s\n' "$kind" "$asset_name" >&2
}
shopt -s nullglob
read_asset .deb "$assets_dir"/*.deb
deb_name=$asset_name
deb_sig=$asset_sig
read_asset .rpm "$assets_dir"/*.rpm
rpm_name=$asset_name
rpm_sig=$asset_sig
read_asset -setup.exe "$assets_dir"/*-setup.exe
exe_name=$asset_name
exe_sig=$asset_sig
pub_date=$(date -u +%Y-%m-%dT%H:%M:%SZ)
jq -n \
--arg version "$version" \
--rawfile notes "$notes_file" \
--arg pub_date "$pub_date" \
--arg base "$base_url" \
--arg deb "$deb_name" --arg deb_sig "$deb_sig" \
--arg rpm "$rpm_name" --arg rpm_sig "$rpm_sig" \
--arg exe "$exe_name" --arg exe_sig "$exe_sig" \
'def asset($name; $sig): {signature: $sig, url: ($base + "/" + ($name | @uri))};
{
version: $version,
notes: $notes,
pub_date: $pub_date,
platforms: {
"linux-x86_64-deb": asset($deb; $deb_sig),
"linux-x86_64-rpm": asset($rpm; $rpm_sig),
"windows-x86_64": asset($exe; $exe_sig)
}
}'