packaging: the installer learns the same two channels the app updates on
install.sh asked /releases/latest and installed whatever came back. That is v0.1.0 today, which predates the updater, and it was about to get worse: the `stable` pointer release write-manifest.sh creates is non-prerelease and holds only latest.json, so from the next v* tag onward it would have WON /releases/latest and the installer would have found nothing to install. So resolve a channel instead of a "latest". `--channel stable|dev` (or TS_CHANNEL), default stable, named to match update.rs's Channel exactly. dev reads /releases/tags/dev. stable reads the pointer's own latest.json, takes its version, and installs that v* release — the same file the app reads, so the installer and the updater cannot disagree about what stable means. Two things found on the way. The dev release's description still told people to run the stable command, and always would have: publish-release.sh writes a body only when it CREATES a release, and a fixed-tag release is only created once, so the text froze at the first build. The 409 path now PATCHes it. And the asset greps were unanchored, so a .AppImage.sig URL could match as the bundle URL — harmless by coincidence, not by construction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MKsUY9Z45KQd34V956hZ9Q
This commit is contained in:
@@ -15,11 +15,18 @@ Easiest — the one-command installer picks this package automatically on any
|
||||
pacman system:
|
||||
|
||||
```sh
|
||||
curl -fsSL https://git.fabledsword.com/bvandeusen/thoughtsync/raw/branch/main/desktop/packaging/install.sh | sh
|
||||
curl -fsSL https://git.fabledsword.com/bvandeusen/thoughtsync/raw/branch/dev/desktop/packaging/install.sh | sh
|
||||
```
|
||||
|
||||
That installs the newest tagged release. To follow the rolling development
|
||||
channel instead, pass the flag through the pipe:
|
||||
|
||||
```sh
|
||||
curl -fsSL https://git.fabledsword.com/bvandeusen/thoughtsync/raw/branch/dev/desktop/packaging/install.sh | sh -s -- --channel dev
|
||||
```
|
||||
|
||||
Or grab the `.pkg.tar.*` from the
|
||||
[latest release](https://git.fabledsword.com/bvandeusen/thoughtsync/releases/latest)
|
||||
[releases page](https://git.fabledsword.com/bvandeusen/thoughtsync/releases)
|
||||
and install it directly:
|
||||
|
||||
```sh
|
||||
|
||||
+106
-17
@@ -4,12 +4,18 @@
|
||||
#
|
||||
# curl -fsSL https://git.fabledsword.com/bvandeusen/thoughtsync/raw/branch/dev/desktop/packaging/install.sh | sh
|
||||
#
|
||||
# Points at `dev` because that is currently the repo's only branch — `main` does
|
||||
# not exist yet, so a main URL 404s. Move this to `main` once that branch is
|
||||
# created, so the public install command stops tracking day-to-day work.
|
||||
# Two channels, the SAME two the app's own updater offers (src-tauri/src/update.rs):
|
||||
# stable (default) — the newest tagged v* release.
|
||||
# dev — the rolling build from every green push to `dev`.
|
||||
# Pick one with `--channel dev` or `TS_CHANNEL=dev`. Through a pipe the options go
|
||||
# after a `--`: curl -fsSL <url> | sh -s -- --channel dev
|
||||
#
|
||||
# Fetches the LATEST published release for this machine's architecture and
|
||||
# installs it, ending with a working app + menu entry. Native-first:
|
||||
# Served from `dev` rather than `main`: `main` exists but trails day-to-day work by
|
||||
# a long way, so the copy there would install an older script. Move the documented
|
||||
# URL to `main` after a dev→main merge lands, not before.
|
||||
#
|
||||
# Fetches the LATEST published release on the chosen channel for this machine's
|
||||
# architecture and installs it, ending with a working app + menu entry. Native-first:
|
||||
# * Arch/CachyOS (pacman) -> the native .pkg.tar.zst (system libs; needs sudo).
|
||||
# * Debian/Ubuntu (dpkg+apt) -> the native .deb (system libs; needs sudo).
|
||||
# * everything else (Fedora/openSUSE/…) -> the de-bundled AppImage,
|
||||
@@ -29,6 +35,38 @@ say() { printf '==> %s\n' "$1"; }
|
||||
die() { printf 'error: %s\n' "$1" >&2; exit 1; }
|
||||
have() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
ThoughtSync desktop installer.
|
||||
|
||||
install.sh [--channel stable|dev]
|
||||
|
||||
--channel stable newest tagged release (default)
|
||||
--channel dev rolling build from the latest green push to `dev`
|
||||
-h, --help this text
|
||||
|
||||
The channel can also come from TS_CHANNEL. Through a pipe, pass options after
|
||||
`--`: curl -fsSL <url> | sh -s -- --channel dev
|
||||
USAGE
|
||||
}
|
||||
|
||||
# --- channel ----------------------------------------------------------------
|
||||
channel="${TS_CHANNEL:-stable}"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--channel)
|
||||
[ $# -ge 2 ] || die "--channel needs a value (stable or dev)."
|
||||
channel="$2"; shift 2 ;;
|
||||
--channel=*) channel="${1#*=}"; shift ;;
|
||||
-h | --help) usage; exit 0 ;;
|
||||
*) die "unknown option: $1 (try --help)" ;;
|
||||
esac
|
||||
done
|
||||
case "$channel" in
|
||||
stable | dev) : ;;
|
||||
*) die "unknown channel '$channel' — expected stable or dev." ;;
|
||||
esac
|
||||
|
||||
have curl || die "curl is required."
|
||||
|
||||
# --- architecture gate ------------------------------------------------------
|
||||
@@ -41,20 +79,51 @@ case "$arch" in
|
||||
*) die "ThoughtSync ships x86_64 Linux builds only right now (this machine: $arch)." ;;
|
||||
esac
|
||||
|
||||
# --- resolve the latest release ---------------------------------------------
|
||||
say "Finding the latest ThoughtSync release…"
|
||||
json="$(curl -fsSL "$API/releases/latest" 2>/dev/null)" || die \
|
||||
"no published release found at $INSTANCE/$REPO/releases — the maintainer publishes one by pushing a v* tag."
|
||||
# --- resolve the release for this channel -----------------------------------
|
||||
say "Finding the latest ThoughtSync build on the $channel channel…"
|
||||
|
||||
# Pull asset URLs straight out of the release JSON (no jq): the only
|
||||
# .AppImage/.deb URLs present are the asset download links.
|
||||
appimage_url="$(printf '%s' "$json" | grep -oE 'https?://[^"]+\.AppImage' | head -1 || true)"
|
||||
deb_url="$(printf '%s' "$json" | grep -oE 'https?://[^"]+\.deb' | head -1 || true)"
|
||||
pkg_url="$(printf '%s' "$json" | grep -oE 'https?://[^"]+\.pkg\.tar\.[a-z]+' | head -1 || true)"
|
||||
if [ "$channel" = "dev" ]; then
|
||||
# A release whose tag never moves and whose assets are pruned to the current
|
||||
# build — so the tag alone always names the newest dev build.
|
||||
json="$(curl -fsSL "$API/releases/tags/dev" 2>/dev/null)" ||
|
||||
die "the dev channel has nothing published yet."
|
||||
else
|
||||
# Ask the stable channel's own manifest which version is current, then install
|
||||
# THAT release. This is the same file the in-app updater reads, so the installer
|
||||
# and the updater can never disagree about what `stable` means.
|
||||
#
|
||||
# Not `/releases/latest`: that returns the newest non-prerelease release by date,
|
||||
# and the `stable` pointer release (manifest only, no bundles — see
|
||||
# write-manifest.sh) is itself a non-prerelease created moments after the
|
||||
# versioned one. It would win, and it carries nothing installable.
|
||||
manifest="$(curl -fsSL "$INSTANCE/$REPO/releases/download/stable/latest.json" 2>/dev/null || true)"
|
||||
stable_version="$(printf '%s' "$manifest" |
|
||||
grep -oE '"version"[[:space:]]*:[[:space:]]*"[^"]+"' | head -1 |
|
||||
sed -E 's/.*"([^"]+)"$/\1/')"
|
||||
if [ -n "$stable_version" ]; then
|
||||
json="$(curl -fsSL "$API/releases/tags/v$stable_version" 2>/dev/null)" ||
|
||||
die "the stable channel names $stable_version, but there is no v$stable_version release to install."
|
||||
else
|
||||
# No stable pointer yet — the channel predates the updater. Fall back to the
|
||||
# newest non-prerelease release, which is what stable meant before there was
|
||||
# a manifest to ask.
|
||||
json="$(curl -fsSL "$API/releases/latest" 2>/dev/null)" ||
|
||||
die "no stable release published yet — try --channel dev, or ask the maintainer to tag one."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Pull asset URLs straight out of the release JSON (no jq). Anchored on the closing
|
||||
# quote so a `…AppImage.sig` URL can't be truncated into a match of its own.
|
||||
asset_url() {
|
||||
printf '%s' "$json" | grep -oE "https?://[^\"]+$1\"" | head -1 | tr -d '"'
|
||||
}
|
||||
appimage_url="$(asset_url '\.AppImage')"
|
||||
deb_url="$(asset_url '\.deb')"
|
||||
pkg_url="$(asset_url '\.pkg\.tar\.[a-z]+')"
|
||||
version="$(printf '%s' "$json" | grep -oE '"tag_name":"[^"]+"' | head -1 | sed -E 's/.*:"([^"]+)".*/\1/')"
|
||||
[ -n "$appimage_url" ] || [ -n "$deb_url" ] || [ -n "$pkg_url" ] ||
|
||||
die "the latest release has no installable Linux asset."
|
||||
say "Latest release: ${version:-unknown}"
|
||||
die "the $channel release (${version:-unknown}) has no installable Linux asset."
|
||||
say "Installing ${version:-unknown} from the $channel channel"
|
||||
|
||||
tmp="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp"' EXIT INT TERM
|
||||
@@ -69,6 +138,18 @@ need_root() {
|
||||
say "Installing (you may be prompted for your password)…"
|
||||
}
|
||||
|
||||
# Both native paths are package-manager-owned, so the app cannot replace itself
|
||||
# in place (update.rs refuses, by design). Say so at the end of those paths rather
|
||||
# than letting someone discover it from a greyed-out button.
|
||||
native_update_note() {
|
||||
printf ' A package-manager install can'\''t update itself in-app.\n'
|
||||
if [ "$channel" = "dev" ]; then
|
||||
printf ' Re-run this script with --channel dev to move to a newer dev build.\n'
|
||||
else
|
||||
printf ' Re-run this script to move to a newer release.\n'
|
||||
fi
|
||||
}
|
||||
|
||||
# --- native pacman path (Arch/CachyOS/Manjaro) ------------------------------
|
||||
# Preferred over the AppImage on Arch: pacman pulls webkit2gtk-4.1 itself and the
|
||||
# app then runs against the host graphics stack, which is what keeps the
|
||||
@@ -83,6 +164,7 @@ if have pacman && [ -n "$pkg_url" ]; then
|
||||
need_root
|
||||
$sudo pacman -U --noconfirm "$pkg_file"
|
||||
say "Done. Launch ThoughtSync from your application menu, or run thoughtsync."
|
||||
native_update_note
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -97,6 +179,7 @@ if have dpkg && have apt-get && [ -n "$deb_url" ]; then
|
||||
$sudo apt-get install -y "$tmp/thoughtsync.deb" ||
|
||||
{ $sudo dpkg -i "$tmp/thoughtsync.deb" || true; $sudo apt-get -f install -y; }
|
||||
say "Done. Launch ThoughtSync from your application menu."
|
||||
native_update_note
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -105,7 +188,7 @@ fi
|
||||
# own self-integration uses (src/integration.rs) — so the running app sees
|
||||
# itself already installed and never makes a second copy or menu entry.
|
||||
say "Installing the de-bundled AppImage (user-local, no sudo)"
|
||||
[ -n "$appimage_url" ] || die "no AppImage asset on the latest release."
|
||||
[ -n "$appimage_url" ] || die "the $channel release (${version:-unknown}) has no AppImage asset."
|
||||
|
||||
apps_dir="$HOME/Applications"
|
||||
dest="$apps_dir/ThoughtSync.AppImage"
|
||||
@@ -153,3 +236,9 @@ ln -sf "$dest" "$HOME/.local/bin/thoughtsync"
|
||||
say "Installed to $dest"
|
||||
printf ' Launch it from your application menu, or run \033[1mthoughtsync\033[0m'
|
||||
printf ' (if ~/.local/bin is on your PATH).\n'
|
||||
# The AppImage CAN update itself, but the app's own channel is a separate,
|
||||
# stable-by-default setting — installing from dev here does not move it.
|
||||
if [ "$channel" = "dev" ]; then
|
||||
printf ' You installed from the \033[1mdev\033[0m channel — set the app to match in\n'
|
||||
printf ' Sync → App updates → Development, or in-app updates will follow stable.\n'
|
||||
fi
|
||||
|
||||
@@ -94,10 +94,22 @@ api() {
|
||||
first_id() { grep -oE '"id"[[:space:]]*:[[:space:]]*[0-9]+' | head -1 | grep -oE '[0-9]+'; }
|
||||
|
||||
# --- create (or reuse) the release for the tag ------------------------------
|
||||
# The install command printed on a release has to install THAT release's channel.
|
||||
# install.sh defaults to stable, so the rolling dev release must opt in explicitly
|
||||
# — otherwise someone following the instructions here lands on a tagged build and
|
||||
# wonders why the version they were sent isn't what they got.
|
||||
if [ "$TAG" = "dev" ]; then
|
||||
INSTALL_TAIL='sh -s -- --channel dev'
|
||||
CHANNEL_NOTE='\n\nThis is the rolling **dev** channel: republished on every green push to \`dev\`, and pruned to the current build.'
|
||||
else
|
||||
INSTALL_TAIL='sh'
|
||||
CHANNEL_NOTE=''
|
||||
fi
|
||||
|
||||
echo "==> Creating release for $TAG"
|
||||
BODY=$(cat <<JSON
|
||||
{"tag_name":"$TAG","name":"ThoughtSync $TAG","draft":false,"prerelease":$RELEASE_PRERELEASE,
|
||||
"body":"ThoughtSync desktop $TAG.\n\n- **Debian / Ubuntu** — native \`.deb\`\n- **Arch / CachyOS** — native \`.pkg.tar.*\`\n- **everything else** — \`.AppImage\` (de-bundled graphics: renders on any GPU/Wayland setup)\n\nInstall / update — picks the right one for your system:\n\`\`\`\ncurl -fsSL $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/raw/branch/dev/desktop/packaging/install.sh | sh\n\`\`\`"}
|
||||
"body":"ThoughtSync desktop $TAG.\n\n- **Debian / Ubuntu** — native \`.deb\`\n- **Arch / CachyOS** — native \`.pkg.tar.*\`\n- **everything else** — \`.AppImage\` (de-bundled graphics: renders on any GPU/Wayland setup)\n\nInstall / update — picks the right one for your system:\n\`\`\`\ncurl -fsSL $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/raw/branch/dev/desktop/packaging/install.sh | $INSTALL_TAIL\n\`\`\`$CHANNEL_NOTE"}
|
||||
JSON
|
||||
)
|
||||
# 409 = a release for this tag already exists (re-run) — fall through to lookup.
|
||||
@@ -108,6 +120,11 @@ if [ -z "${RELEASE_ID:-}" ]; then
|
||||
echo " release exists; fetching it by tag"
|
||||
release="$(api GET "$API/releases/tags/$TAG")"
|
||||
RELEASE_ID="$(printf '%s' "$release" | first_id)"
|
||||
# And refresh its description. A fixed-tag release (dev, and any re-run) keeps
|
||||
# whatever text the FIRST build wrote — including the install command. A stale
|
||||
# one sends people to the wrong channel, silently.
|
||||
echo " refreshing its description"
|
||||
api PATCH "$API/releases/$RELEASE_ID" -H "Content-Type: application/json" -d "$BODY" >/dev/null
|
||||
fi
|
||||
[ -n "${RELEASE_ID:-}" ] || { echo "ERROR: could not resolve release id" >&2; exit 1; }
|
||||
echo " release id = $RELEASE_ID"
|
||||
|
||||
Reference in New Issue
Block a user