-
Phandroid v2.0.25 Stable
released this
2026-10-23 06:56:20 +02:00 | 4531 commits to main since this releaseSame fixes as dev 2.0.81 — the upstream-merge model and the release-pipeline fixes of 2026-10-18/19.
Fixed
- The F-Droid "What's New" can no longer be blocked by a changelog entry that mentions
a heading. The converter stripped backticks and bold but left a literal###in the
text, so the release gate's "no Markdown left" assertion failed — and that is exactly
what happened to 2.0.81 on the dev channel: the entry about the duplicate### Changed
heading fed its own heading into the What's New text, the gate refused, and the release
was never signed (dev stayed on 2.0.80). The converter now drops heading hash runs (the
words stay, so the entry still reads correctly), which makes its guarantee hold for any
input. prod was not affected. merge=ourscan no longer silently discard your change. The fork-owned files
(README, CHANGELOG, LICENSE, SECURITY, PRIVACY) must keep our version on an upstream
merge — but git calls that driver for every three-way merge, and on a rebase or
cherry-pick "ours" is the branch being replayed onto while "theirs" is your commit.
The built-intruetherefore threw your change away, silently: it did exactly that
three times on 2026-10-18/19 (both prod cherry-picks and one rebase, each losing a
CHANGELOG entry), with no conflict and nothing in the diffstat to catch the eye.
scripts/phandroid-merge-ours.shreplaces it: it still keeps ours, but writes the
discarded version to.git/phandroid-dropped/<path>and names it on stderr, so the
loss is visible while it happens and recoverable afterwards. It needs the driver once
per clone (git config merge.phandroid-ours.driver …; the workflow declares it for
CI), and a clone without it now gets a visible conflict instead of silence.- The upstream sync no longer re-resolves the lockfile from a stale base — the
outage class that stranded dev at 2.0.79 for two days is closed.
package-lock.jsonis nowmerge=theirs: a merge takes upstream's lock, the
dependency resolution they actually build and test with, and the sync
regenerates it only to re-add the fork's eleven overlay entries. The old model
kept our lock (merge=ours) and re-resolved it from scratch on every merge,
handing each upstream dependency bump to npm's peer resolver as a fresh coin
flip: on 2026-10-16 upstream bumped@linguito ~6.9.1 and npm walked the
unguarded peerOptional chain (@lingui/vite-plugin→
@rolldown/plugin-babel→@babel/plugin-transform-runtime) into Babel 8.x
against the tree's@babel/[email protected]—ERESOLVE, and the sync failed
eleven consecutive runs while dev sat 20 commits behind upstream. Starting from
upstream's lock instead, the identical regeneration touches nothing upstream
owns (measured: 716 entries, 0 changed, 0 removed, 195 added by the overlay;
verified under npm 9.2 and 11.21). A new guard,
scripts/phandroid-lockfile-invariant.mjs, fails the run if the regeneration
changes or drops any upstream-owned entry — the drift can neither crash the
sync nor ship silently again. This branch carries the same model and the same
guard, so the nextupstream/productionmove cannot hit the same wall. - The lockfile guard fails closed. Input it cannot check — a lockfile without a
packagesmap (lockfileVersion 1), an unparsable or missing file — now stops the
run with one line naming the file and the reason (exit 2), instead of comparing
zero entries and reporting a green "0 upstream entries unchanged". A guard that
validates nothing is the failure mode this pipeline keeps hitting: an unmatched
test glob ran zero tests and exited 0, and the awk the changelog bump replaced
exited 0 on a missing[Unreleased]heading. Only exit 0 means the invariant held. - A release section no longer carries two
### Changedheadings. The generated
upstream-merge block was followed by the moved hand-written entries including
their own### Changed, so a release whose entries had one showed that heading
twice — in the release body on both instances (2.0.80 shipped that way) and, for
sections short enough to survive the 500-character limit, twice as "Changed:" in
the F-Droid "What's New" text (measured against the pre-fix script; in 2.0.80 the
truncation dropped the second block, so there only the release body showed it).
Hand-written### Changedbullets now merge into the generated block, which stays
first because the upstream merge is the release's headline; every other
hand-written section keeps its place. A body without a### Changedheading still
produces byte-identical output — verified against the pre-fix script over six
input shapes. A heading spelled with extra whitespace (### Changed) counts as
the same heading, and a body of nothing but headings no longer counts as fork
changes: it used to append a stray empty heading and drop the "no Android-fork
code changes" claim for a release that had none.
Changed
- A failed sync now says why, and for how long it has been failing. The
lockfile step tees its output into the failure report — theERESOLVEthat
killed eleven runs was previously only findable by pulling job logs by hand —
a failed merge lists its conflicted files the same way, and every CI-failure
issue now carries a trend line ("N consecutive failed run(s); last success:
run X on …") computed from the run history. Forgejo notifies nobody about
Actions results, so the issue body is the only alarm channel — and "still
down" reads differently than "another failure". - The sync announces conflict candidates before merging. Files that both
upstream and the fork changed since the last merge — minus the ones the merge
drivers own — are printed up front, so the next real conflict shows up in the
job log and the issue instead of being a surprise mid-merge. - The conflict-candidate list is read from
.gitattributesinstead of a fixed
list of files. Upstream edits its ownCHANGELOG.mdregularly andmerge=ours
resolves that without a human, so the hardcoded version would have announced a
conflict candidate on most runs — the noise that makes a real one invisible.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- The F-Droid "What's New" can no longer be blocked by a changelog entry that mentions