Checklists in the body, colour from tags, and commit-derived CalVer #4
@@ -199,19 +199,29 @@ jobs:
|
||||
JSON
|
||||
cat dist/thoughtsync-android.json
|
||||
|
||||
# The rolling dev channel, same fixed-tag release the desktop bundles use.
|
||||
# CI artifacts are per-run and auth-gated, so they are no use as a fetch
|
||||
# target; a release asset has a permanent URL. Only ever a SIGNED build —
|
||||
# publishing an unsigned APK would offer people something they cannot
|
||||
# install over what they already have.
|
||||
- name: Publish to the dev channel
|
||||
if: github.ref == 'refs/heads/dev' && steps.build.outputs.keystore != ''
|
||||
# The rolling channel for this branch, the same fixed-tag releases the desktop
|
||||
# bundles use. CI artifacts are per-run and auth-gated, so they are no use as a
|
||||
# fetch target; a release asset has a permanent URL. Only ever a SIGNED build —
|
||||
# publishing an unsigned APK would offer people something they cannot install
|
||||
# over what they already have.
|
||||
#
|
||||
# `stable` from main is new in M314 step 3, and it is what lets the server image
|
||||
# bake in a client that matches its own channel: a :latest image fetches the APK
|
||||
# from `stable`, a :dev image from `dev`. Before this, main published no APK at
|
||||
# all and every image — stable included — baked in the dev one.
|
||||
- name: Publish to the channel for this branch
|
||||
if: (github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main') && steps.build.outputs.keystore != ''
|
||||
working-directory: .
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
RELEASE_TAG: dev
|
||||
RELEASE_PRERELEASE: "true"
|
||||
run: bash desktop/packaging/publish-release.sh
|
||||
run: |
|
||||
case "$GITHUB_REF_NAME" in
|
||||
main) RELEASE_TAG=stable; RELEASE_PRERELEASE=false ;;
|
||||
*) RELEASE_TAG=dev; RELEASE_PRERELEASE=true ;;
|
||||
esac
|
||||
export RELEASE_TAG RELEASE_PRERELEASE
|
||||
echo "Publishing the APK to the $RELEASE_TAG channel."
|
||||
bash desktop/packaging/publish-release.sh
|
||||
|
||||
- name: Upload the APK
|
||||
# Mirrored action, never actions/upload-artifact. @v4+ throws
|
||||
|
||||
@@ -330,7 +330,18 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
mkdir -p client
|
||||
base="${{ github.server_url }}/${{ github.repository }}/releases/download/dev"
|
||||
# THE CHANNEL IS A PROPERTY OF THE IMAGE. A :dev image serves the dev
|
||||
# client; :latest serves the stable one. This read `download/dev`
|
||||
# unconditionally until M314 step 3, on every branch — so every stable
|
||||
# server shipped a dev-channel APK to anyone who downloaded the client
|
||||
# from it. Not a versioning gap; a plain defect, fixed here because this
|
||||
# is the step that gave `stable` an APK to point at.
|
||||
case "${{ github.ref_name }}" in
|
||||
main|v*) channel=stable ;;
|
||||
*) channel=dev ;;
|
||||
esac
|
||||
echo "Baking in the $channel client."
|
||||
base="${{ github.server_url }}/${{ github.repository }}/releases/download/$channel"
|
||||
ok=1
|
||||
for f in thoughtsync.apk thoughtsync-android.json; do
|
||||
curl -fsSL -H "Authorization: token $GITHUB_TOKEN" -o "client/$f" "$base/$f" || ok=0
|
||||
|
||||
@@ -216,26 +216,40 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: bash desktop/packaging/publish-release.sh
|
||||
|
||||
# The rolling DEVELOPMENT channel (M10.9): a release whose tag never moves, so
|
||||
# the updater has a permanent URL to read — Forgejo has no
|
||||
# /releases/latest/download/<asset> route, so "newest" can't be named in a URL.
|
||||
# The rolling channel for this branch: `dev` from dev, `stable` from main. Both
|
||||
# are releases whose tag never moves, so the updater has a permanent URL to
|
||||
# read — Forgejo has no /releases/latest/download/<asset> route, so "newest"
|
||||
# cannot be named in a URL.
|
||||
#
|
||||
# MAIN PUBLISHING HERE is what makes a `v*` tag optional (note 3127 §0). Until
|
||||
# M314 step 3 this job built on main and published nothing, so the stable
|
||||
# channel moved only when somebody cut a tag — that section's diagnostic
|
||||
# failing outright: main publishing was not sufficient for a user to receive
|
||||
# the build.
|
||||
#
|
||||
# Gated on the signing key INSIDE the script rather than with an `if:`, because
|
||||
# the secrets context isn't reliably available to step conditions. Publishing
|
||||
# bundles the app would then refuse to verify is worse than publishing nothing:
|
||||
# it looks like a working feed.
|
||||
- name: Publish to the dev channel
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
- name: Publish to the channel for this branch
|
||||
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
RELEASE_TAG: dev
|
||||
RELEASE_PRERELEASE: "true"
|
||||
run: |
|
||||
if [ -z "${TAURI_SIGNING_PRIVATE_KEY:-}" ]; then
|
||||
echo "No TAURI_SIGNING_PRIVATE_KEY — skipping the dev channel publish."
|
||||
echo "No TAURI_SIGNING_PRIVATE_KEY — skipping the channel publish."
|
||||
exit 0
|
||||
fi
|
||||
# POSIX `case`, not bash `[[ ]]` — these run under busybox sh (rule 81).
|
||||
# `prerelease` is true for dev so it does not read as a supported build,
|
||||
# and false for stable, which is the real thing.
|
||||
case "$GITHUB_REF_NAME" in
|
||||
main) RELEASE_TAG=stable; RELEASE_PRERELEASE=false ;;
|
||||
*) RELEASE_TAG=dev; RELEASE_PRERELEASE=true ;;
|
||||
esac
|
||||
export RELEASE_TAG RELEASE_PRERELEASE
|
||||
echo "Publishing to the $RELEASE_TAG channel."
|
||||
bash desktop/packaging/publish-release.sh
|
||||
|
||||
# Windows installer, CROSS-COMPILED from Linux — there is no Windows build host.
|
||||
@@ -326,26 +340,40 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: bash desktop/packaging/publish-release.sh
|
||||
|
||||
# The rolling DEVELOPMENT channel (M10.9): a release whose tag never moves, so
|
||||
# the updater has a permanent URL to read — Forgejo has no
|
||||
# /releases/latest/download/<asset> route, so "newest" can't be named in a URL.
|
||||
# The rolling channel for this branch: `dev` from dev, `stable` from main. Both
|
||||
# are releases whose tag never moves, so the updater has a permanent URL to
|
||||
# read — Forgejo has no /releases/latest/download/<asset> route, so "newest"
|
||||
# cannot be named in a URL.
|
||||
#
|
||||
# MAIN PUBLISHING HERE is what makes a `v*` tag optional (note 3127 §0). Until
|
||||
# M314 step 3 this job built on main and published nothing, so the stable
|
||||
# channel moved only when somebody cut a tag — that section's diagnostic
|
||||
# failing outright: main publishing was not sufficient for a user to receive
|
||||
# the build.
|
||||
#
|
||||
# Gated on the signing key INSIDE the script rather than with an `if:`, because
|
||||
# the secrets context isn't reliably available to step conditions. Publishing
|
||||
# bundles the app would then refuse to verify is worse than publishing nothing:
|
||||
# it looks like a working feed.
|
||||
- name: Publish to the dev channel
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
- name: Publish to the channel for this branch
|
||||
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
RELEASE_TAG: dev
|
||||
RELEASE_PRERELEASE: "true"
|
||||
run: |
|
||||
if [ -z "${TAURI_SIGNING_PRIVATE_KEY:-}" ]; then
|
||||
echo "No TAURI_SIGNING_PRIVATE_KEY — skipping the dev channel publish."
|
||||
echo "No TAURI_SIGNING_PRIVATE_KEY — skipping the channel publish."
|
||||
exit 0
|
||||
fi
|
||||
# POSIX `case`, not bash `[[ ]]` — these run under busybox sh (rule 81).
|
||||
# `prerelease` is true for dev so it does not read as a supported build,
|
||||
# and false for stable, which is the real thing.
|
||||
case "$GITHUB_REF_NAME" in
|
||||
main) RELEASE_TAG=stable; RELEASE_PRERELEASE=false ;;
|
||||
*) RELEASE_TAG=dev; RELEASE_PRERELEASE=true ;;
|
||||
esac
|
||||
export RELEASE_TAG RELEASE_PRERELEASE
|
||||
echo "Publishing to the $RELEASE_TAG channel."
|
||||
bash desktop/packaging/publish-release.sh
|
||||
|
||||
# The updater manifest, written AFTER both bundle jobs — they run in separate
|
||||
@@ -359,7 +387,7 @@ jobs:
|
||||
manifest:
|
||||
name: Update manifest
|
||||
needs: [build, windows]
|
||||
if: github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/v')
|
||||
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: python-ci
|
||||
container:
|
||||
image: git.fabledsword.com/bvandeusen/ci-tauri:1.97
|
||||
@@ -380,19 +408,31 @@ jobs:
|
||||
# could drift, and a manifest whose version doesn't match the binary it
|
||||
# points at is an updater that never settles.
|
||||
version="$(sh desktop/packaging/build-version.sh)"
|
||||
if [ "${GITHUB_REF_NAME}" = "dev" ]; then
|
||||
export RELEASE_TAG=dev
|
||||
export RELEASE_NOTES="Development build from ${GITHUB_SHA}"
|
||||
# Rolling channel: drop the previous build's bundles once the manifest
|
||||
# points at this one. Nothing can reach them, and they're ~100 MB a push.
|
||||
export PRUNE_OLD_ASSETS=true
|
||||
APP_VERSION="$version" bash desktop/packaging/write-manifest.sh
|
||||
else
|
||||
export RELEASE_TAG="${GITHUB_REF_NAME}"
|
||||
export RELEASE_NOTES="ThoughtSync ${GITHUB_REF_NAME}"
|
||||
# Twice: once onto the versioned release itself, and once onto the
|
||||
# permanent `stable` pointer the app actually reads. Same manifest both
|
||||
# times — its URLs point at the versioned assets either way.
|
||||
APP_VERSION="$version" bash desktop/packaging/write-manifest.sh
|
||||
APP_VERSION="$version" MANIFEST_TAG=stable bash desktop/packaging/write-manifest.sh
|
||||
fi
|
||||
# POSIX `case` (rule 81). For a tag, GITHUB_REF_NAME is the tag name, so
|
||||
# the default arm is the tag path.
|
||||
case "${GITHUB_REF_NAME}" in
|
||||
dev|main)
|
||||
# Both are rolling channels: the manifest lands on the same release
|
||||
# that holds the bundles, and the previous build's bundles are dropped
|
||||
# once it points at this one. Nothing can reach them, and they are
|
||||
# ~100 MB a push.
|
||||
case "${GITHUB_REF_NAME}" in
|
||||
main) export RELEASE_TAG=stable
|
||||
export RELEASE_NOTES="Stable build from ${GITHUB_SHA}" ;;
|
||||
*) export RELEASE_TAG=dev
|
||||
export RELEASE_NOTES="Development build from ${GITHUB_SHA}" ;;
|
||||
esac
|
||||
export PRUNE_OLD_ASSETS=true
|
||||
APP_VERSION="$version" bash desktop/packaging/write-manifest.sh
|
||||
;;
|
||||
*)
|
||||
# A versioned release gets its own manifest and NOTHING ELSE. It used
|
||||
# to also write the `stable` pointer — that moved to main above, and
|
||||
# two writers for one channel is a race with no winner worth having.
|
||||
# The tag's build consequence goes entirely in M314 step 7; this only
|
||||
# stops it fighting over `stable`.
|
||||
export RELEASE_TAG="${GITHUB_REF_NAME}"
|
||||
export RELEASE_NOTES="ThoughtSync ${GITHUB_REF_NAME}"
|
||||
APP_VERSION="$version" bash desktop/packaging/write-manifest.sh
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -18,7 +18,7 @@ pacman system:
|
||||
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
|
||||
That installs the newest build from `main`. To follow the rolling development
|
||||
channel instead, pass the flag through the pipe:
|
||||
|
||||
```sh
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
# curl -fsSL https://git.fabledsword.com/bvandeusen/thoughtsync/raw/branch/dev/desktop/packaging/install.sh | sh
|
||||
#
|
||||
# Two channels, the SAME two the app's own updater offers (src-tauri/src/update.rs):
|
||||
# stable (default) — the newest tagged v* release.
|
||||
# stable (default) — the rolling build from every merge to `main`.
|
||||
# dev — the rolling build from every green push to `dev`.
|
||||
# Both are fixed-tag releases: the tag never moves and the assets are pruned to the
|
||||
# current build, so the tag alone names the newest one. `stable` only became one in
|
||||
# M314 step 3, when `main` started publishing — before that it was a manifest-only
|
||||
# pointer at whatever `v*` tag somebody had last cut.
|
||||
# Pick one with `--channel dev` or `TS_CHANNEL=dev`. Through a pipe the options go
|
||||
# after a `--`: curl -fsSL <url> | sh -s -- --channel dev
|
||||
#
|
||||
@@ -41,7 +45,7 @@ ThoughtSync desktop installer.
|
||||
|
||||
install.sh [--channel stable|dev]
|
||||
|
||||
--channel stable newest tagged release (default)
|
||||
--channel stable newest build from main (default)
|
||||
--channel dev rolling build from the latest green push to `dev`
|
||||
-h, --help this text
|
||||
|
||||
@@ -82,20 +86,23 @@ esac
|
||||
# --- resolve the release for this channel -----------------------------------
|
||||
say "Finding the latest ThoughtSync build on the $channel channel…"
|
||||
|
||||
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.
|
||||
# ONE lookup for both channels now. Each is a release whose tag never moves and whose
|
||||
# assets are pruned to the current build, so the tag alone names the newest build on
|
||||
# that channel — which is exactly what an installer wants and what the in-app updater
|
||||
# already reads.
|
||||
json="$(curl -fsSL "$API/releases/tags/$channel" 2>/dev/null)" ||
|
||||
die "the $channel channel has nothing published yet."
|
||||
|
||||
# TRANSITIONAL — delete with the rest of the old scheme (M314 step 7).
|
||||
#
|
||||
# `stable` existed before this as a manifest-ONLY pointer: `latest.json` naming a
|
||||
# version whose bundles lived on a separate `v<version>` release. Between this commit
|
||||
# and the first merge to `main` it still looks like that, and `stable` is the DEFAULT
|
||||
# channel — so without this fallback `curl … | sh` is broken for everyone in that
|
||||
# window. It costs nothing once main has published: the grep finds the bundles and
|
||||
# this branch never runs again.
|
||||
if [ "$channel" = "stable" ] && ! printf '%s' "$json" | grep -q "releases/download/stable/[^\"]*\.\(AppImage\|deb\|pkg\.tar\)"; then
|
||||
say "stable has no bundles of its own yet — falling back to the version its manifest names."
|
||||
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 |
|
||||
@@ -104,11 +111,8 @@ else
|
||||
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."
|
||||
die "no stable build published yet — try --channel dev, or merge to main."
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -118,6 +118,11 @@ first_id() { grep -oE '"id"[[:space:]]*:[[:space:]]*[0-9]+' | head -1 | grep -oE
|
||||
# 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.
|
||||
#
|
||||
# Both CHANNELS are rolling pointer releases (M314 step 3): `dev` republishes on
|
||||
# every green push to dev, `stable` on every merge to main. Each says so, because a
|
||||
# release that prunes its own assets behaves differently from a versioned one and a
|
||||
# reader deserves to know which they are looking at.
|
||||
if [ "$TAG" = "dev" ]; then
|
||||
INSTALL_TAIL='sh -s -- --channel dev'
|
||||
# Backticks BARE, not `\``. The heredoc below is unquoted, so there the backslash
|
||||
@@ -125,6 +130,10 @@ if [ "$TAG" = "dev" ]; then
|
||||
# Here single quotes already do that job, so a backslash would survive into the
|
||||
# body as `\``, which is not a legal JSON escape: Forgejo answers 422.
|
||||
CHANNEL_NOTE='\n\nThis is the rolling **dev** channel: republished on every green push to `dev`, and pruned to the current build.'
|
||||
elif [ "$TAG" = "stable" ]; then
|
||||
# install.sh defaults to stable, so no flag.
|
||||
INSTALL_TAIL='sh'
|
||||
CHANNEL_NOTE='\n\nThis is the rolling **stable** channel: republished on every merge to `main`, and pruned to the current build. No tag is required for a build to arrive here.'
|
||||
else
|
||||
INSTALL_TAIL='sh'
|
||||
CHANNEL_NOTE=''
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
//! In-app updates (M10.9).
|
||||
//!
|
||||
//! Two channels, because two audiences: `stable` follows tagged `v*` releases,
|
||||
//! `dev` follows every green push. Each reads a `latest.json` published as an asset
|
||||
//! on a release whose TAG NEVER CHANGES — verified necessary, because Forgejo has no
|
||||
//! `/releases/latest/download/<asset>` route (it 404s), so "newest" cannot be named
|
||||
//! in a URL. A fixed tag can.
|
||||
//! Two channels, because two audiences: `stable` follows every merge to `main`,
|
||||
//! `dev` follows every green push to `dev`. Each reads a `latest.json` published as
|
||||
//! an asset on a release whose TAG NEVER CHANGES — verified necessary, because
|
||||
//! Forgejo has no `/releases/latest/download/<asset>` route (it 404s), so "newest"
|
||||
//! cannot be named in a URL. A fixed tag can.
|
||||
//!
|
||||
//! `stable` followed tagged `v*` releases until M314 step 3, and its manifest pointed
|
||||
//! at bundles living on a different release. It holds its own bundles now, exactly as
|
||||
//! `dev` always has — so a build reaches stable users with no tag cut anywhere, which
|
||||
//! is the whole point of the change. NOTHING HERE MOVED: this code only ever read
|
||||
//! `<channel>/latest.json`, and that is still where the manifest lands.
|
||||
//!
|
||||
//! The feed lives on Fabled-Git rather than on a ThoughtSync server, deliberately:
|
||||
//! this app is usable having never linked a server, and an install that can't reach
|
||||
|
||||
Reference in New Issue
Block a user