diff --git a/desktop/packaging/arch/README.md b/desktop/packaging/arch/README.md index 633986e..52fd071 100644 --- a/desktop/packaging/arch/README.md +++ b/desktop/packaging/arch/README.md @@ -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 diff --git a/desktop/packaging/install.sh b/desktop/packaging/install.sh index 4cfacaa..be5247e 100755 --- a/desktop/packaging/install.sh +++ b/desktop/packaging/install.sh @@ -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 | 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 | 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 diff --git a/desktop/packaging/publish-release.sh b/desktop/packaging/publish-release.sh index a8fb643..3aee7f6 100755 --- a/desktop/packaging/publish-release.sh +++ b/desktop/packaging/publish-release.sh @@ -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 </dev/null fi [ -n "${RELEASE_ID:-}" ] || { echo "ERROR: could not resolve release id" >&2; exit 1; } echo " release id = $RELEASE_ID"