Adds scripts/** to the push path filter -- it was missing entirely, so
that directory never triggered CI regardless of what changed there.
pull_request no longer has a paths: filter (moved that check inside each
job, in shell, since a label-only PR with no relevant diff still needs
to trigger the workflow for the job-level label check to ever run).
Added labeled/unlabeled to pull_request types for the same reason.
Splits the old build-docs job into a docs job with two sections: doc
tests/build (generate concat, verify-manual.py, compile man page) run
whenever relevant on any event; publish (site build, Cloudflare deploy,
commit-back) is step-gated to push/dispatch only, as before. The docs
job now also runs on a PR when it's labeled Kind/Documentation or
Area/Docs, or its diff touches docs/manual/**, docs/build-manual.py,
docs/manualtools.py, docs/verify-manual.py, or docs/site/**.
test gains the same shape: also runs on a PR labeled Kind/Testing,
Area/Tests, Area/CI, or Area/Scripts, independent of what it touches.
docs no longer needs: test. main's branch protection already requires
test to pass before a PR merges, so by the time a push-to-main reaches
this job, test has necessarily already passed; re-checking it here
would be redundant. Leaves the same gap as
block_admin_merge_override=false on that rule: a direct admin push
bypasses it, an accepted trust boundary, not a new one.
build-docs's auto-commit step was seen rejected as non-fast-forward
(run 976, sha 3bbda31): npm ci + astro build + the Cloudflare Pages
deploy ahead of it can take several minutes, long enough for another
PR to merge into main first. A bare `git push` has no way to recover
from that -- the whole job just fails, even though every real step
(tests, manual verification, man page, site build, deploy) already
succeeded.
This commit only ever touches three generated files
(fish-config.md/.1, the component registry), so a rebase onto
whatever landed is always mechanical -- retry push up to 3 times,
rebasing onto origin/main between attempts. Ends on an explicit
`test "$pushed" -eq 1` rather than trailing off the for loop, so a
run that exhausts all three retries still fails loudly instead of
reporting success.
Adds a pull_request trigger (same path filters, YAML anchor to share
them with push) so branches get CI feedback before merge instead of
only after. build-docs is excluded on pull_request: it auto-commits
generated docs straight to the checked-out ref and deploys the
Cloudflare Pages production site with --branch=main, neither of which
should run against PR content that is not main yet.
Verified live, twice: bundling apt-utils with another package (first
attempt) and installing it fully alone, first (second attempt) both
still print debconf's "delaying package configuration" notice exactly
once per job. It fires during apt-utils' own first-ever install, before
debconf considers it "installed" -- no install ordering this workflow
controls can pre-seed that. Reverted to the simpler bundled form (the
separate-step version added a step for zero measured benefit) and
documented it as accepted/unfixable, same class as the runs-on/
checkout-hint noise already left alone.
The previous fix bundled apt-utils into the same apt-get install as
software-properties-common. Verified against a live CI run: debconf
still printed "delaying package configuration" once per job, because
apt-utils configures in the same transaction as everything else and
still lands after at least one other package -- bundling doesn't help.
Installing it alone, first, actually finishes it before anything else
runs.
An audit of a full green run's test + build-docs logs (agy scan, spot-
checked) turned up stray output beyond the mkrep git-init hint already
fixed. Two real bugs, plus CI-config cosmetics:
- agents-vault.fish/agents-init.fish: `set -l x (some_fish_function ...)`
command substitutions do not inherit a caller-scoped stderr redirect in
fish (proven with a two-line repro: `outer 2>/dev/null` where outer
does `set -l x (inner)` still leaks inner's stderr to the real
terminal). This let _agents_repo_ensure_symlink's and
_agents_repo_sync's raw internal error messages leak past `--silent`
for real users too, always duplicating the clean summary message each
caller already echoes on failure. Fixed at all 4 call sites by adding
an explicit `2>/dev/null` directly on each command substitution, since
a redirect on the outer call cannot reach it.
- ci.yml: apt-get install missing `apt-utils`, so debconf printed
"delaying package configuration" on every install in both jobs --
installing it first fixes the chicken-and-egg.
- ci.yml: added `NODE_OPTIONS: --no-deprecation` at the build-docs job
level to silence Node's internal punycode-module deprecation notice
(astro's toolchain still pulls it in transitively).
- ci.yml: `npm ci --no-fund` drops the funding nag.
- ci.yml: `gpg --batch --quiet --import` drops gpg's normal-case import
status lines during the bot commit-signing setup.
Deliberately NOT silenced: npm's deprecated-glob warning, its audit
vulnerability summary, and its allow-scripts notice about esbuild's
postinstall -- these are genuine dependency-hygiene signal, not noise,
and no workflow-level flag exists to hide them without also hiding real
future findings. Also not fixable here: a Gitea Actions/act runner
warning ('runs-on' key not defined in CI/test) that traces to neither
workflow YAML in this repo -- both already set runs-on on every job,
confirmed twice; it's runner-internal, like actions/checkout's own
git-init hint in its Checkout-step preamble.
Verified: full suite 730/730 passing ($status 0); the exact mkdir-
collision repro that surfaced the command-substitution bug re-run
clean (rc=1, empty stderr); test-agents-vault.fish standalone,
320/320, zero occurrences of the previously-leaked messages.
Raw multi-line armored key piped through 'echo | gpg --import' came out
CRC-corrupted (Invalid keyring) on the first real run -- something in the
secret/env round-trip mangles embedded newlines. Regenerated the bot key
(old one is unrecoverable, secrets are write-only) and store it
base64-encoded, decoded with base64 -d before import.
actions@gitea was never a verified email on any Gitea account, so the
CI docs-regen commit (git commit && git push, done client-side in the
runner) could never show as verified regardless of server-side
[repository.signing] config -- Gitea only signs commits it generates
itself (merge button, web editor, wiki), never ones a client pushes.
Import a dedicated passphrase-less key for a new fishconfig-bot
account (verified email, no login) from the CI_GPG_PRIVATE_KEY repo
secret and sign with it instead.
Labels don't travel with a mirror push -- mirroring copies files, not
repository settings -- but they matter on the GitHub side anyway, because
GitHub reads the same .github/ISSUE_TEMPLATE/ files and silently drops a
labels: entry naming a label it doesn't have. Until now the only thing
keeping the two sets aligned was remembering to do it by hand, which is
exactly the kind of thing that gets forgotten and then fails invisibly.
Add scripts/sync-labels.py and a workflow that runs it daily, on any change
to the script itself, and on manual dispatch. Gitea stays the source of
truth: labels are managed there and GitHub is made to match.
- **Creates and updates** anything missing or drifted. Colors and
descriptions are normalized before comparison -- Gitea returns colors
bare, GitHub sometimes with a leading '#', and a description may be null
on one side and "" on the other -- so a steady state is a true no-op
rather than a rewrite of all 33 labels every run.
- **Deletes only unused extras.** An extra label on the mirror is removed
only when no issue or PR there carries it; one in use is reported with
its count and left alone. An unattended scheduled job must not be able to
strip a label off somebody's issue.
- **Refuses to run on an empty source**, since treating that as truth would
propose deleting every label on the mirror.
- **--dry-run** prints the plan and changes nothing; **--self-test** checks
the diff logic offline against fixtures, and gates the sync step in CI so
a broken diff can't mutate anything.
Stdlib only, so the CI step installs nothing beyond python3. The job is
gated with the same github.server_url check ci.yml uses -- without it the
mirror would queue this forever against a runner that only exists on Gitea.
Labels are matched by name, so a rename reads as delete-plus-create; the
new name is created and the old is pruned only if unused. The two forges
share no stable label ID, so a rename can't be tracked across them.
The test/build-docs jobs target a self-hosted runner (racknerd-mini)
that only exists on the Gitea instance. When GitHub re-runs this same
workflow on the mirror, those jobs sit queued forever with no matching
runner, so the commit never gets a completed status.
Gate both jobs to skip when github.server_url is github.com, and add a
trivial github-mirror job (runs-on: ubuntu-latest, which GitHub does
provide) that only runs on the mirror, so the check completes instead
of hanging.
The only other cd left in the workflow mixed two working directories
in one step: docs/build-manual.py --site runs from the repo root, then
npm ci/astro build need docs/site. Split into two steps so each can
use working-directory instead, keeping the whole file cd-free and
consistent with the Cloudflare deploy step's fix.
Gitea/GitHub Actions auto-groups a multi-line run: script under
"Run <first line>". With `cd docs/site` as the first line, the
collapsed log tree showed that instead of the actual wrangler deploy
command. Moving the directory change to the step's working-directory
key drops cd from the script entirely, so the group label now reflects
the command that's actually running.
workflow_dispatch already ran the whole pipeline manually, but there
was no way to fire just one job (e.g. re-run docs generation without
re-running the fish test suite) the way the old standalone
build-docs.yml let you. Add a job choice input (all/test/build-docs,
defaulting to all) and gate each job on it via `if:`, while leaving
the push-triggered path's needs: test gating untouched.
Two more sources of CI noise/fragility alongside the debconf frontend
fix: fish's install pulls in man-db/groff-base/xsel as recommends,
triggering a slow mandb rebuild for tooling nothing here uses;
--no-install-recommends skips that. apt-get update had no retry
policy, so a transient blip against the PPA mirror failed the whole
job; -o Acquire::Retries=3 gives it a few chances first.
apt-get install was probing for a Dialog then Readline debconf frontend
before falling back to Teletype on the non-interactive CI runner,
adding noise and failed-negotiation log lines to every run. Passing
DEBIAN_FRONTEND=noninteractive directly on the sudo command line (env
vars set via step-level `env:` don't survive sudo's env_reset) skips
the negotiation and goes straight to the frontend that actually works
here.
Adds tests/run-tests.fish (syntax lint over every .fish file, plus a
sandboxed interactive load) and tests/functional.fish (10 checks
covering XDG/PATH/CDPATH setup, key bindings, abbreviations, core
functions, exit rewiring, and the opinionated-component registry).
The sandbox copies config-relevant files into a scratch HOME/XDG tree
rather than symlinking the checkout, since this repo also serves as a
live ~/.config/fish and a symlink would let universal-variable writes
leak into the real fish_variables file.
Wires the suite into build-docs.yml as a `test` job that `build-docs`
now depends on, so a broken config can no longer get published to the
docs site. Documents the workflow in the README's new Testing section.
Ubuntu 24.04's default repo only carries fish 3.7, but
docs/manual/06-dependency-catalog.md states fish >= 4.0 as a
project dependency. ppa:fish-shell/release-4 is the fish-shell
maintainers' own PPA and explicitly supports 24.04.
The registry round-trip test spawns a real fish subprocess to verify
conf.d/__fish_config_op_registry.fish sources correctly, but the
runner image has no fish binary, so the workflow failed with
FileNotFoundError.
- Quote both keys and values in the generated __fish_config_op_registry.fish
keys array so a future identity/tag containing a glob-special character
(*, ?, [) can't be silently dropped by fish's set, desyncing the
positionally-paired keys/values arrays.
- Deduplicate the tag list per site in build_registry() so an identity
collision across sources (e.g. auto-pull tagged autoexec/sync in both
functions/auto-pull.fish and conf.d/auto-pull.fish) no longer produces a
duplicated tag in the committed registry.
- Add a comment to the generated file's header noting it must sort first
among conf.d/*.fish guard-callers, since that ordering is currently
implicit in the filename rather than stated anywhere.
- CI's auto-commit step now also stages conf.d/__fish_config_op_registry.fish
alongside the docs it already regenerates, so a # COMPONENT header change
pushed without a manual __fish_config_op_registry_rebuild self-heals
instead of silently drifting from what CI just regenerated.
Bundled fix wave for six findings from the sub-category-granularity
whole-branch review:
- config-settings: reset in_subcat on Tab/Shift-Tab so up/down keep
routing to cur_row instead of freezing on the Sponge/Paths pages
after a drill-down
- verify-manual: _parsed_components() now delegates to
generate_component_registry.collect_components() instead of a
lossy dict.update() merge, closing a taxonomy-check blind spot on
identity collisions
- verify-manual: add test_committed_registry_matches_headers to
catch the committed registry drifting from current # COMPONENT
headers
- build-docs workflow: trigger on conf.d/** and config.fish edits,
not just functions/**
- __config_settings_draw_subcat: show (Universal)/(Session) in the
drill-down title so the persistence scope is visible before toggling
- __config_settings_draw / config-settings: mention the Enter
sub-category drill-down in the on-screen hint and --help text
- generate_component_registry: send the "wrote ..." progress line to
stderr so it no longer corrupts --concat's stdout output