Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a new Bash script to sync QGIS APT repositories (Debian/Ubuntu + ubuntugis) via apt-sync.py, including a symlink for ubuntu-ltr and repository size summarization.
Changes:
- Introduces
qgis-deb.shto mirror multiple QGIS repo trees with configured codename/arch lists. - Adds a
ubuntu-ltr -> debian-ltrsymlink step. - Generates a repo size summary file and attempts cleanup at the end.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
f6fed2c to
907dcb0
Compare
|
Copilot review addressed:
|
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved failures in sync status, stale distributions, symlink handling, and size reporting can produce incomplete or misleading mirror results.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (2)
Resolved since last review (11)
IfTUNASYNC_WORKING_DIRis unset/empty, the destination paths become\"/debian\",… IfTUNASYNC_WORKING_DIRis unset/empty, the destination paths become\"/debian\",… IfTUNASYNC_WORKING_DIRis unset/empty, the destination paths become\"/debian\",… Withset -e,|| truesuppresses all failures fromsize-sum.sh, which can hide real problems… Creating a predictable-ish filename in/tmpcan allow symlink/hardlink attacks or accidental… Creating a predictable-ish filename in/tmpcan allow symlink/hardlink attacks or accidental…DEB_CODENAMESincludes both Debian (e.g., bullseye/bookworm/trixie) and Ubuntu (e.g.,…DEB_CODENAMESincludes both Debian (e.g., bullseye/bookworm/trixie) and Ubuntu (e.g.,… The script relies on hard-coded absolute paths forapt-sync.pyandsize-sum.sh, reducing… The script relies on hard-coded absolute paths forapt-sync.pyandsize-sum.sh, reducing…_hereis defined but not used anywhere in this script. Please remove it, or use it to locate…
| UBUNTUGIS_CODENAMES="jammy,noble,bionic,focal,xenial" | ||
| UBUNTUGIS_ARCHES="amd64" | ||
|
|
||
| "$apt_sync" --delete "${BASE_URL}/debian" "$DEB_CODENAMES" main "$DEB_ARCHES" "${WORKDIR}/debian" |
There was a problem hiding this comment.
Resolved differently in f07b2c6: apt-sync.py is reverted to master (its exit-code change affected all 23 callers), and the wrapper now detects failures itself by teeing the apt-sync output and failing on the 'Failed APT repos' log line or a nonzero exit.
happyaron
left a comment
There was a problem hiding this comment.
Thanks for the thorough follow-up on the codename question — I re-checked it against the live upstream and the union claim holds: every codename in DEB_CODENAMES returns 200 under all of debian, ubuntu, debian-ltr and ubuntu-ltr. The ubuntu-ltr → debian-ltr symlink is also justified (identical Release for bookworm/trixie/jammy/noble/resolute).
Inline comments below. One non-line note: several earlier thread replies no longer match the final code (e.g. the TUNASYNC_WORKING_DIR replies say the check was not added, and the size-sum reply says the fallback was reverted, but both are now present). Could you update or resolve those threads so they don't mislead later readers?
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| sys.exit(main()) |
There was a problem hiding this comment.
This changes behaviour for every caller of apt-sync.py, not just QGIS. 23 shell scripts in the repo call it under set -e, and adoptium.py runs it with check=True. Today a partial failure exits 0 and those scripts continue; after this change they abort at the first failed repo (e.g. proxmox.sh would skip the ISO/images sync whenever one apt repo fails).
Reporting failures to tunasync is probably the right direction, but since it is a repo-wide behaviour change, could it be split into its own PR so maintainers can evaluate it separately from adding the QGIS mirror?
There was a problem hiding this comment.
Reverted in f07b2c6: apt-sync.py is byte-identical to master again and keeps exiting 0, so none of the 23 callers (or adoptium.py's check=True) change behaviour. qgis-deb.sh now detects failures itself: it tees the apt-sync.py output and fails the run if the 'Failed APT repos' log line appears (or the process exits nonzero).
| "$apt_sync" --delete "${BASE_URL}/debian-ltr" "$DEB_CODENAMES" main "$DEB_ARCHES" "${WORKDIR}/debian-ltr" | ||
| echo "debian-ltr finished" | ||
|
|
||
| "$apt_sync" --delete "${BASE_URL}/ubuntu" "$DEB_CODENAMES" main "$DEB_ARCHES" "${WORKDIR}/ubuntu" |
There was a problem hiding this comment.
debian and ubuntu appear to be identical trees upstream, just like debian-ltr/ubuntu-ltr. I compared https://qgis.org/{debian,ubuntu}/dists/<c>/Release for bookworm, noble, sid, unstable, oracular, mantic and buster — the sha256 matched in every case.
If that holds, ubuntu could be a symlink to debian too (same treatment as ubuntu-ltr below), which would avoid storing a second full copy.
There was a problem hiding this comment.
Done in f07b2c6: ubuntu is now a symlink to debian with the same treatment as ubuntu-ltr (ln -sfnT, and the script refuses to replace a pre-existing real directory). Note this change does not delete the already-mirrored ubuntu tree anywhere: on a host that still has a real ubuntu/ directory the job stops with an actionable error until the operator removes the duplicate tree.
| # and apt-sync.py --delete removes any on-disk .deb not referenced by the | ||
| # codenames synced in this run. Splitting the list per tree would delete | ||
| # still-published content; apt-sync.py skips codenames absent upstream. | ||
| DEB_CODENAMES="bullseye,bookworm,trixie,jammy,noble,resolute,plucky,questing,focal,xenial,bionic" |
There was a problem hiding this comment.
Upstream publishes more codenames than this list covers. sid/unstable are actively updated (Release dated 2026-08-29), and oracular, mantic, lunar, kinetic and buster also return 200 (stale, though). By the same reasoning as the comment above ("splitting would lose coverage"), these are not mirrored.
Could you either add at least sid, or note in the comment that the omissions are deliberate?
There was a problem hiding this comment.
Added sid and unstable in f07b2c6 (both verified live, Release 200 and recently updated). The comment now also records that the remaining codenames upstream still serves but are stale/EOL (buster, kinetic, lunar, mantic, oracular) are intentionally not mirrored.
| UBUNTUGIS_CODENAMES="jammy,noble,bionic,focal,xenial" | ||
| UBUNTUGIS_ARCHES="amd64" | ||
|
|
||
| "$apt_sync" --delete "${BASE_URL}/debian" "$DEB_CODENAMES" main "$DEB_ARCHES" "${WORKDIR}/debian" |
There was a problem hiding this comment.
Combined with the apt-sync.py exit-code change and set -e, one failed repo aborts the remaining ones. This will also become permanent once upstream drops a codename we have already synced: apt_mirror only ignores a missing Release if it never existed locally (apt-sync.py:174-178), otherwise it returns 1 on every run. Several listed codenames are already EOL (xenial, bionic, focal, bullseye, plucky).
Consider running all five syncs and exiting non-zero at the end if any failed, so a single broken repo doesn't stall the rest.
There was a problem hiding this comment.
Fixed in f07b2c6 without touching apt-sync.py: before each repo sync, qgis-deb.sh probes every codename's Release (the S3 backend answers 403/404 for missing keys). Absent codenames are skipped; if one was mirrored before, its local dists tree is removed up front so --delete can garbage-collect its packages and the mirror never serves a half-retired distribution. Probe errors other than 403/404 keep the codename in the list, so a transient failure cannot remove content.
|
|
||
| REPO_SIZE_FILE=$(mktemp -t qgis-deb-reposize.XXXXXX) | ||
| export REPO_SIZE_FILE | ||
| trap 'rm -f "$REPO_SIZE_FILE"' EXIT |
There was a problem hiding this comment.
Nit: size-sum.sh ... --rm already deletes this file on the success path, so the trap and --rm overlap. Harmless — keeping one of them would be enough.
There was a problem hiding this comment.
Fixed in f07b2c6: the EXIT trap is dropped; size-sum.sh --rm is now the single cleanup point (on failure paths the container's /tmp is ephemeral anyway).
| # and apt-sync.py --delete removes any on-disk .deb not referenced by the | ||
| # codenames synced in this run. Splitting the list per tree would delete | ||
| # still-published content; apt-sync.py skips codenames absent upstream. |
There was a problem hiding this comment.
Fixed in f07b2c6: retired distributions are now handled explicitly inside qgis-deb.sh (apt-sync.py is unchanged). Each codename's Release is probed before syncing; a 403/404 codename is skipped, and if it exists locally its dists tree is removed first so --delete garbage-collects its packages consistently. A codename whose probe fails for any other reason stays in the sync list, so transient errors cannot delete content.
QGIS publishes five sibling apt trees under qgis.org (debian, debian-ltr, ubuntu, ubuntu-ltr, ubuntugis, ubuntugis-ltr). Hand-rolled per-repo jobs are fragile when codenames rotate (~yearly), so this single driver loops the trees with shared codename/arch lists on top of the existing apt-sync.py helper. Key design decisions (shaped by review): - apt-sync.py stays byte-identical to master: making it exit nonzero on mirror failures would change behaviour for all 23 shell callers under set -e (and adoptium.py's check=True). The wrapper detects failures itself instead: it tees apt-sync.py's output and fails the run when the "Failed APT repos" log line appears or the process exits nonzero. - DEB_CODENAMES deliberately mixes Debian and Ubuntu codenames: the qgis.org S3 backend serves the union of both families under each tree (verified live 2026-09-25: /debian/dists/jammy and /ubuntu/dists/bookworm both return 200), and apt-sync.py --delete removes any on-disk .deb not referenced by the synced codenames, so splitting the list per tree would delete still-published content. sid/unstable are included; stale/EOL codenames upstream still serves (buster, kinetic, lunar, mantic, oracular) are intentionally not mirrored, as documented in the script comment. - Retired distributions are handled explicitly: before each repo sync, every codename's Release file is probed (the S3 backend answers 403/404 for missing keys). A gone codename is skipped, and if it was mirrored before, its local dists tree is removed first so --delete can garbage-collect its packages and the mirror never serves a half-retired distribution. Probe errors other than 403/404 keep the codename in the list (transient failures cannot remove content), and if no codename is available at all the script refuses to sync an empty tree. - ubuntu and ubuntu-ltr are served as symlinks to debian and debian-ltr (upstream trees are byte-identical; Release sha256 matched for every codename, verified 2026-09-25), avoiding a second ~50G copy. ln -sfnT treats the destination as the link itself, and the script refuses to replace a pre-existing real directory -- note for existing deployments: a host that still has a real ubuntu/ tree stops with an actionable error until the operator removes that duplicate tree; then the symlink is created. Expected one-time migration. - REPO_SIZE_FILE comes from mktemp; size-sum.sh --rm is the single cleanup point (no EXIT trap) and stays non-fatal with a logged WARNING, since a failed size report must not fail the sync. Verified on the host against the live upstream: all 13 debian + 5 ubuntugis production codenames probe 200; the ubuntu/debian Release files are sha256-identical per codename.
f07b2c6 to
2bc1f64
Compare
|
Branch cleanup note: this branch has been squashed to a single commit, Review items → how they were addressed (all included in the current diff)Maintainer review (@happyaron):
Copilot rounds:
The stale thread replies you flagged (the Some earlier in-thread replies described intermediate states of the branch; please rely on the current diff and this summary. |



Summary
Add
qgis-deb.shto mirror the QGIS apt repositories (https://qgis.org/{debian,debian-ltr,ubuntugis,ubuntugis-ltr}, withubuntu/ubuntu-ltrserved as symlinks) using the existingapt-sync.pyhelper — which this PR leaves byte-identical to master.Why
QGIS publishes sibling apt trees under
qgis.org. Hand-rolling one job per repo with bespoke options is fragile when codenames change (Debian/Ubuntu rotates ~yearly). This single shell driver loops over the trees with shared codename/architecture lists.How it works
sync_repo()runsapt-sync.py --deleteper tree. Sinceapt-sync.pyalways exits 0 (it only logs failures), the wrapper detects failures itself: it tees the output and fails the run when theFailed APT reposlog line appears or the process exits nonzero.DEB_CODENAMESdeliberately mixes Debian and Ubuntu codenames: the qgis.org S3 backend serves the union of both families under each tree (verified live 2026-09-25:/debian/dists/jammyand/ubuntu/dists/bookwormboth return 200), andapt-sync.py --deleteremoves any on-disk .deb not referenced by the synced codenames — splitting the list per tree would delete still-published content.sid/unstableare included; stale/EOL codenames upstream still serves (buster, kinetic, lunar, mantic, oracular) are intentionally not mirrored (documented in the script comment).Releasefile is HEAD-probed (the S3 backend answers 403/404 for missing keys). A gone codename is skipped; if it was mirrored before, its localdiststree is removed first so--deletecan garbage-collect its packages and the mirror never serves a half-retired distribution. Probe errors other than 403/404 keep the codename (transient failures cannot remove content); if no codename is available at all, the script refuses to sync an empty tree.ubuntu→debianandubuntu-ltr→debian-ltrare symlinks: the upstream trees are byte-identical (Releasesha256 matched for every codename, verified 2026-09-25), avoiding a second ~50G copy.ln -sfnTtreats the destination as the link itself, and the script refuses to replace a pre-existing real directory.REPO_SIZE_FILEcomes frommktemp;helpers/size-sum.sh --rmis the single cleanup point (no EXIT trap) and stays non-fatal with a logged WARNING — a failed size report must not fail the sync.Testing
Verified on the host against the live upstream: all 13 debian + 5 ubuntugis production codenames probe 200; the ubuntu/debian
Releasefiles are sha256-identical per codename.Deployment note (one-time migration)
On a deployment that already has a real
ubuntu/(orubuntu-ltr/) directory from an older mirror layout, the next sync stops with an actionable error (remove it before creating the symlink) instead of nesting into it. This is expected: remove the duplicateubuntucopy tree manually once, and the symlink is created on the following run. Fresh deployments are unaffected.Notes
DEB_CODENAMES/UBUNTUGIS_CODENAMES, arches alongside) and can be updated in one place.helpers/size-sum.shfor tunasync size reporting.