feat: dist upgrade --latest downloads and verifies the release from the feed, --expect pins the version (#161)
CI / verify (push) Successful in 1m42s
Release / release (push) Successful in 38s

Files changed:
- CHANGES.md
- INSTALL.md
- VERSION
- instructions/upgrade-instance.md
- tools/CONTRACT.md
- tools/chemenu/commands/dist_cmd.py
- tools/chemenu/commands/version_cmd.py
- tools/chemenu/tests/test_cli.py
- tools/chemenu/tests/test_dist_upgrade.py
- tools/chemenu/version.py
This commit is contained in:
torben committed 2026-09-29 22:08:00 +02:00
1 parent dd885db625
commit cf892315e6
10 files changed
+888 -62

No files matched your search

+46 -1
View File
@@ -59,13 +59,14 @@ concern - readable here, never shipped as something to parse.
---
## 7.1.0-beta.31 - 2026-09-26 - new_page/type_resolver comments no longer claim only entities declare a layout:
## 7.1.0-beta.32 - 2026-09-29 - dist upgrade --latest: one-command update from the release feed
**Author:** Torben Nehmer
<!-- wikitool:bumps -->
**High impact**
- wikitool: one data record per command - `-h`, index and CONTRACT.md render from cli_contract (Gitea #121 Phase 1)
- dist upgrade --latest: one-command update from the release feed
**Medium impact**
- CalDAV task-tracker provider (Nextcloud Tasks, iOS Reminders); review reports unknown-value findings instead of skipping them
@@ -102,6 +103,50 @@ concern - readable here, never shipped as something to parse.
- new_page/type_resolver comments no longer claim only entities declare a layout:
<!-- /wikitool:bumps -->
### dist upgrade --latest: one-command update from the release feed (Gitea #161)
Updating a tarball instance took a manual detour: `version notes`, then fetching the `.tar.gz` and
its `.sha256` from the release page by hand, then `dist upgrade <tarball>`. `dist upgrade --latest`
now does the middle part. It asks the release feed (`update_url` from the stamp,
`$WIKITOOL_UPDATE_URL` and `$WIKITOOL_UPDATE_TOKEN` as before) which release is latest, downloads
the release's archive and checksum into a scratch directory, verifies the archive against the
checksum, and hands over to the existing upgrade path unchanged - classification,
`--keep-local`/`--take-release`, migration report and every refusal are the same code. `<source>`
becomes optional; exactly one of it and `--latest` must be given.
The order is what matters. The local preconditions run first, then the feed is asked, and the
version it reports is judged **before any download**: already installed is a success no-op, an
older version is a downgrade refusal, a `-beta.N` version needs `--pre`. Only then are the two
assets looked up - by exact name, `chemenu-stack-<version>.tar.gz` and its `.sha256`, in the
`assets` of the release object the version query already returned, using the feed's own
`browser_download_url` values; no URL is composed. A release missing either asset is refused
before the first download with the assets it does have and its release page named. The checksum
is mandatory on this path (a `<source>` archive without a sibling `.sha256` is still only a
WARN), and the archive's own `VERSION` must equal the feed's version, otherwise nothing is
applied. `--dry-run` downloads and verifies too - classification needs the tree - and removes
everything again; the scratch directory goes away on every exit.
`--expect <version>` (only with `--latest`) closes the gap between reading the notes and applying
the update: the feed only offers its *latest* release, so if a newer one appeared since
`version notes` was read, the run refuses before downloading anything and names both versions.
`instructions/upgrade-instance.md` passes the version from step 2 in the dry run and in the real
run. The comparison is on parsed versions, so a `v` prefix does not matter.
`$WIKITOOL_UPDATE_TOKEN` is sent to an asset download only when the asset URL has the feed's
scheme, host and port, and as an unredirected header, so a redirect cannot carry it elsewhere.
There is no https enforcement: the checksum comes from the same host as the archive and protects
against transfer errors, not against a compromised feed - `INSTALL.md` says so.
The asset names are a Python constant (`version.ARCHIVE_NAME`) that a test ties to
`.gitea/workflows/release.yml`, so renaming them in one place fails the suite instead of the next
upgrade. `dist upgrade` is now `network: yes` in its record and in the pinned set in `test_cli.py`,
the `version check` record's "never reached implicitly" note names the one explicit exception, the
`fetch_latest` docstring says "three commands", and `tools/CONTRACT.md` is regenerated. The tests
run against a local `http.server` that logs every request and its `Authorization` header, so the
token rule and the "no asset request before the checks pass" rule are asserted on the wire. The
`upstream merge` pointers in the `dist upgrade` record and failure message stay as they are; they
belong to the separate work on the clone path.
### new_page/type_resolver comments no longer claim only entities declare a layout:
Two code comments - `new_page.py`'s module docstring and `TypeResolver.get_layout`'s - still said