versioning: refuse to publish a version below what the channel already serves
CI & Build / Build now, or wait for Android? (push) Successful in 2s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 6s
CI & Build / Python tests (push) Successful in 17s
CI & Build / integration (push) Successful in 21s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 3m13s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 6m21s
Desktop (Tauri) / Update manifest (push) Successful in 5s
Android / Kotlin + Rust (APK) (push) Successful in 9m19s
CI & Build / Build now, or wait for Android? (push) Successful in 2s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 6s
CI & Build / Python tests (push) Successful in 17s
CI & Build / integration (push) Successful in 21s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 3m13s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 6m21s
Desktop (Tauri) / Update manifest (push) Successful in 5s
Android / Kotlin + Rust (APK) (push) Successful in 9m19s
Step 5 of M314, note 3127 §6.3. Everything else in this milestone derives a number and trusts it; this compares the derived value against what the channel is actually serving and fails the lane if it went down. Too-low is the unrecoverable direction: every installed client reports "up to date" forever, and no later build fixes it until one climbs back above the bad number. #2183 and #2993 are both that symptom. ## Two hazards, two mechanisms A shallow clone is now tested DIRECTLY, in `version.sh`, via `--is-shallow-repository`. The empty-result guard only caught the case where nothing matched — and run 4796 showed the worse one, where a partial match returned a real six-days-stale answer. Asking the question outright costs no network and covers artifacts with nothing published to compare against. `guard-forward.sh` handles the rest: a squash or rebase merge rewriting the committer date, a rebuild of an older commit, and clock skew between runners. ## The comparison is per artifact, and the operator differs desktop derived >= published commit time, so equality is the ORDINARY no-change case and `<=` would fail every build that changed nothing android derived > published build time, so equality means two builds in one minute — and Android refuses to install an APK whose versionCode does not RISE The server is deliberately unguarded: nothing compares its version, `:latest` moves regardless, and rule 145 removed the version tags that would be the published list. A too-low value there is a wrong date in a footer, not a stranded client. It still gets the shallow-clone check. ## Proved to fire, not assumed Cloned the repo, checked out a commit eight back, ran the guard against the LIVE dev feed: at the tip derived 1.0.3502151, published 1.0.3502151 -> pass eight back derived 1.0.3501535, published 1.0.3502151 -> FAILS android tip derived 3502171, published 3502152 -> pass stable derived 1.0.3502151, published 0.2.0 -> pass That last row is worth keeping: stable still advertises the bare `0.2.0` from the old Cargo.toml scheme, so the transition orders upward on BOTH channels, not just the one being exercised. A channel with nothing published passes rather than failing — otherwise the first publish to a new channel could never happen. The guard runs BEFORE the build in all three lanes, so a bad derivation costs seconds rather than a five-minute compile and a publish to undo. `compare` is exposed as an explicit mode so the ordering is testable without a network and inspectable without a push — 16 cases including `1.0.9 < 1.0.10`, which a string compare gets exactly backwards. #3145 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -126,6 +126,26 @@ jobs:
|
||||
echo "apk=android/app/build/outputs/apk/debug/app-debug.apk" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# THE ONE CHECK THAT LOOKS AT REALITY (note 3127 §6.3). Everything else in this
|
||||
# lane derives a number and trusts it; this compares the derived value against
|
||||
# what the channel is actually serving, and fails the lane if it went DOWN.
|
||||
#
|
||||
# Placed before the build, not after: a bad derivation should cost seconds, not
|
||||
# a five-minute compile and a publish that has to be undone. Too-low is the
|
||||
# unrecoverable direction — every installed client reports "up to date" forever
|
||||
# and no later build fixes it until one climbs back above the bad number
|
||||
# (#2183, #2993).
|
||||
- name: Guard — the version must not go backwards
|
||||
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main'
|
||||
env:
|
||||
GITHUB_TOKEN: ${ github.token }
|
||||
run: |
|
||||
case "$GITHUB_REF_NAME" in
|
||||
main) channel=stable ;;
|
||||
*) channel=dev ;;
|
||||
esac
|
||||
sh ../packaging/guard-forward.sh android "$channel"
|
||||
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
|
||||
@@ -126,6 +126,26 @@ jobs:
|
||||
# so making it conditional is what lets the pipeline stay green before the
|
||||
# operator has added the secret. With the key present, each bundle gets a
|
||||
# `.sig` beside it — the file the updater actually verifies against.
|
||||
# THE ONE CHECK THAT LOOKS AT REALITY (note 3127 §6.3). Everything else in this
|
||||
# lane derives a number and trusts it; this compares the derived value against
|
||||
# what the channel is actually serving, and fails the lane if it went DOWN.
|
||||
#
|
||||
# Placed before the build, not after: a bad derivation should cost seconds, not
|
||||
# a five-minute compile and a publish that has to be undone. Too-low is the
|
||||
# unrecoverable direction — every installed client reports "up to date" forever
|
||||
# and no later build fixes it until one climbs back above the bad number
|
||||
# (#2183, #2993).
|
||||
- name: Guard — the version must not go backwards
|
||||
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main'
|
||||
env:
|
||||
GITHUB_TOKEN: ${ github.token }
|
||||
run: |
|
||||
case "$GITHUB_REF_NAME" in
|
||||
main) channel=stable ;;
|
||||
*) channel=dev ;;
|
||||
esac
|
||||
sh packaging/guard-forward.sh desktop "$channel"
|
||||
|
||||
- name: Tauri build (deb + AppImage)
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
@@ -328,6 +348,26 @@ jobs:
|
||||
# --runner cargo-xwin swaps cargo for the cross-compiling driver (it supplies
|
||||
# the MSVC CRT/SDK, pre-warmed into the image, and links with lld-link).
|
||||
# Frontend already built above; skip the beforeBuildCommand rebuild.
|
||||
# THE ONE CHECK THAT LOOKS AT REALITY (note 3127 §6.3). Everything else in this
|
||||
# lane derives a number and trusts it; this compares the derived value against
|
||||
# what the channel is actually serving, and fails the lane if it went DOWN.
|
||||
#
|
||||
# Placed before the build, not after: a bad derivation should cost seconds, not
|
||||
# a five-minute compile and a publish that has to be undone. Too-low is the
|
||||
# unrecoverable direction — every installed client reports "up to date" forever
|
||||
# and no later build fixes it until one climbs back above the bad number
|
||||
# (#2183, #2993).
|
||||
- name: Guard — the version must not go backwards
|
||||
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main'
|
||||
env:
|
||||
GITHUB_TOKEN: ${ github.token }
|
||||
run: |
|
||||
case "$GITHUB_REF_NAME" in
|
||||
main) channel=stable ;;
|
||||
*) channel=dev ;;
|
||||
esac
|
||||
sh packaging/guard-forward.sh desktop "$channel"
|
||||
|
||||
- name: Tauri build (NSIS installer)
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
|
||||
Reference in New Issue
Block a user