Skip to content

ci: run bootstrap for real, and fix the two bugs it found - #1

Open
Chikimuras wants to merge 4 commits into
mainfrom
ci/real-bootstrap-job
Open

Chikimuras wants to merge 4 commits into
mainfrom
ci/real-bootstrap-job

Conversation

@Chikimuras

Copy link
Copy Markdown
Owner

Pourquoi

Le job smoke ne lançait que bootstrap.sh --dry-run, qui n'installe rien. Il ne pouvait donc valider que le parsing des scripts, jamais l'installation. C'est ce qui avait fait échouer la CI deux fois de suite sur 20-shell.sh (require_cmd zsh, puis command -v zsh sous set -e).

Ce que fait la PR

  1. smoke pré-installe zsh — le dry-run traverse enfin les vraies branches de 20-shell.sh ([dry-run] sudo chsh -s /usr/bin/zsh tester) au lieu de s'arrêter avant.
  2. Nouveau job bootstrap-real — lance le bootstrap pour de vrai dans ubuntu:24.04, vérifie les post-conditions (ci/assert-bootstrap.sh), puis relance le bootstrap et re-vérifie : le repo se documente comme ré-exécutable, la CI le contrôle.

ci/assert-bootstrap.sh vérifie : les binaires installés sans condition sont sur le PATH, ~/.oh-my-zsh + les deux plugins + tpm sont clonés, les dotfiles résolvent bien dans le repo, et zsh -i démarre. Les outils optionnels (aerc, tealdeer, jira-cli) ne sont pas assertés : leur absence est tolérée par conception.

Les deux bugs trouvés par le nouveau job, dès son premier run

glab : 404 sur toute machine amd64 (fix: use Go arch names…)
10-packages.sh traduisait amd64 → x86_64, alors que glab nomme ses assets avec les noms d'arch Go (glab_1.50.0_linux_amd64.tar.gz). L'install n'a jamais fonctionné sur Intel 64 bits ; seul arm64 tombait juste par coïncidence. curl sortait en 22 et emportait tout le bootstrap avec lui.

50-stow.sh renommait les dotfiles dans le repo à chaque ré-exécution (fix: never back up a file reached through a folded stow symlink)
stow plie les arbres : après un premier passage, ~/.zsh est un symlink vers dotfiles/zsh/.zsh. backup_conflicts() testait ensuite ~/.zsh/rc.d/10-aliases.zsh, qui résout à travers ce symlink vers le fichier source du repo — présent, et pas lui-même un symlink — donc backup_path() le déplaçait. Reproduit :

--- après le 1er stow          --- après le 2e stow
00-os.zsh                      00-os.zsh.forge-backup.20260903-115805
10-aliases.zsh                 10-aliases.zsh.forge-backup.20260903-115805
20-fzf-widgets.zsh             20-fzf-widgets.zsh.forge-backup.20260903-115805
eod-tracker.zsh                eod-tracker.zsh.forge-backup.20260903-115805

Après quoi ~/.zshrc cassait : no matches found: ~/.zsh/rc.d/*.zsh. Ça touchait aussi git, kitty, aerc, jira et nvim — tous les paquets dont stow avait plié un répertoire.

Vérification locale

Tout a été rejoué en container ubuntu:24.04 amd64 (comme les runners), pas seulement lu :

  • shellcheck -S warning -x sur tous les scripts : OK
  • job smoke : exit 0
  • job bootstrap-real : exit 0 — run réel + assertions + re-run + re-assertions, et aucun *.forge-backup.* dans dotfiles/ après le second passage

Point relevé, non corrigé ici

zsh -i démarre proprement mais affiche fzf_setup_using_debian: no such file or directory: /usr/share/doc/fzf/examples/key-bindings.zsh : le plugin fzf d'oh-my-zsh cherche les key-bindings à l'ancien emplacement Debian. Préexistant, sans effet sur le démarrage — à traiter séparément si tu veux.

🤖 Generated with Claude Code

Chikimuras and others added 3 commits September 3, 2026 16:04
glab publishes its tarballs as glab_<ver>_linux_amd64 / _arm64, but
10-packages.sh translated dpkg's amd64 to x86_64 before building the URL.
The download therefore 404'd on every 64-bit Intel machine — only arm64
happened to line up. curl exited 22 and, under `set -e`, took the whole
bootstrap with it.

lazygit and jira-cli further down do use x86_64, hence the (wrong)
symmetry; a comment now records the difference.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
stow folds trees: after a first run, ~/.zsh is a single symlink to
dotfiles/zsh/.zsh. backup_conflicts() then walked the package files and
tested ~/.zsh/rc.d/10-aliases.zsh, which resolves *through* that symlink
to the repo's own file — it exists, and it is not itself a symlink, so
backup_path() moved it. Every re-run of bootstrap.sh renamed a large part
of dotfiles/ to *.forge-backup.<ts> inside the repo, and the shell config
then failed to load (~/.zshrc: no matches found: ~/.zsh/rc.d/*.zsh).

Skip any target that already resolves into dotfiles/ — it is stow's own
work, not a user file to preserve.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The smoke job only ever ran bootstrap.sh --dry-run, which installs
nothing. Anything that depends on a binary actually being present was
therefore untested, and broke in CI twice (require_cmd zsh, then
command -v zsh under set -e).

Two changes:

- smoke now pre-installs zsh, so 20-shell.sh traverses its real branches
  ([dry-run] sudo chsh -s /usr/bin/zsh) instead of bailing out early.
- bootstrap-real runs the thing for real in ubuntu:24.04, asserts the
  post-conditions (ci/assert-bootstrap.sh: tools on PATH, omz plugins and
  tpm checked out, dotfiles resolving into the repo, `zsh -i` starting),
  then re-runs bootstrap and re-asserts — the repo claims to be safe to
  re-run, so CI checks it.

It is slow and depends on third-party endpoints, but it is the only job
that proves the install works rather than that the scripts merely parse:
it found both bugs fixed in the two preceding commits on its first run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 3, 2026 12:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

ci/assert-bootstrap.sh currently mixes logical $FORGE_DIR paths with readlink -f physical paths, which can make link assertions fail incorrectly on symlinked checkouts.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR strengthens CI coverage for bootstrap.sh by running both an end-to-end dry-run (with prerequisites that make more branches execute) and a new full “real bootstrap” job that asserts post-conditions and idempotency. It also fixes two bootstrap issues uncovered by exercising the real installation path (glab arch download URL and stow re-run behavior).

Changes:

  • Update smoke CI to preinstall zsh so --dry-run traverses relevant branches.
  • Add bootstrap-real CI job plus ci/assert-bootstrap.sh to validate a real bootstrap run and re-run idempotency.
  • Fix glab asset arch selection and prevent stow conflict backup from renaming files inside the repo on re-runs.
File summaries
File Description
install/50-stow.sh Avoids backing up files that resolve into the repo through folded stow symlinks.
install/10-packages.sh Fixes glab download URL to use dpkg/Go arch strings (e.g., amd64).
ci/assert-bootstrap.sh Adds post-install assertions for PATH tools, plugin checkouts, stowed dotfiles, and zsh -i startup.
.github/workflows/ci.yml Adds manual trigger, improves smoke, and introduces bootstrap-real job.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread ci/assert-bootstrap.sh
# run, not just the first failure.
set -uo pipefail

FORGE_DIR="${FORGE_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
The bootstrap-real job reported, on every run:

  fzf_setup_using_debian:source:40: no such file or directory:
  /usr/share/doc/fzf/examples/key-bindings.zsh

Not a dotfiles bug. The ubuntu image ships /etc/dpkg/dpkg.cfg.d/excludes
with path-exclude=/usr/share/doc/*, so dpkg drops fzf's shell integration
at install time while leaving the examples/ directory behind — enough for
oh-my-zsh's fzf plugin to pass its `-d` check and then source a file that
was never unpacked. A real Ubuntu desktop has it. The container was
lying, so drop the exclusion in both container jobs.

While here, make that class of defect fail the build instead of scrolling
past: the assertion now starts the shell under a pty (script -qec, so
fzf's key-bindings.zsh does not warn `can't change option: zle`) and
requires startup to print *nothing*. Checking the exit status alone was
not enough — a failed `source` inside a plugin leaves it at 0, which is
exactly why this went unnoticed.

Verified both ways in a local amd64 container: with the exclusion left in
place the assertion captures the 107-byte fzf warning and fails; with it
removed, startup is silent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Chikimuras

Copy link
Copy Markdown
Owner Author

Suite : le point fzf signalé plus haut est traité dans 0b37606.

Ce n'était pas un bug des dotfiles. L'image ubuntu:24.04 embarque /etc/dpkg/dpkg.cfg.d/excludes avec path-exclude=/usr/share/doc/* : dpkg jette key-bindings.zsh à l'installation de fzf, tout en laissant le répertoire examples/ en place — assez pour que le plugin fzf d'oh-my-zsh passe son test -d puis source un fichier jamais dépaqueté. Sur une vraie Ubuntu le fichier est là. C'est le container qui mentait, donc l'exclusion est retirée dans les deux jobs.

Au passage, cette classe de défaut fait maintenant échouer le build au lieu de défiler : l'assertion démarre le shell sous un pty (script -qec, sinon key-bindings.zsh râle can't change option: zle) et exige que le démarrage n'affiche rien. Tester le seul code de sortie ne suffisait pas — un source qui échoue dans un plugin oh-my-zsh le laisse à 0, ce qui est précisément pour ça que c'était passé inaperçu.

Vérifié dans les deux sens en container amd64 local : exclusion laissée en place → l'assertion capture les 107 octets d'avertissement fzf et échoue ; exclusion retirée → démarrage silencieux. Run complet (bootstrap réel + assertions + re-run + re-assertions) : exit 0, aucun *.forge-backup.* dans dotfiles/.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants