sshd starts bash in a way that sources ~/.bashrc even for a single
remote command, and this file printed its banner unconditionally. Every
command Ansible runs over SSH therefore returned the banner on stdout, and a
task that parses its own output read that instead: the Matomo bootstrap
asserted on stdout='unknown': unknown terminal type., which is [H[2J[3J
failing when there is no terminal to clear.
The deploy never saw it while the roles were installed over the container
connection, where no login shell is involved. Now that the applications are
deployed over the mesh, every such task reads the banner.
Reproduced and fixed against the rendered template: sourcing it
non-interactively with TERM=unknown printed the terminal error and the whole
banner before, and prints nothing now, while an interactive shell is
unchanged.
The narration comment above the colour block goes with it: the guard moved it
off the file header, where the comment lint allows it, into mid-code, where
it is what that lint calls narration.
3529 invalid comments in 528 files, grandfathered in while the linter
only saw the working-tree diff. Removal ran through the linter's own
collectors, so detection and deletion cannot drift: a comment whose
opener line is blank before it and whose closer line is blank after it
loses all its lines, anything else loses only the comment region, and a
line left holding a bare delimiter goes with it.
Five files keep their comments behind a nocheck marker instead. Four are
upstream configs whose inline docs are upstream's own (mautrix, systemd
journald, nextcloud-oidc-login, MediaWiki). In littlejs the
commented-out entries ARE the deactivation mechanism for an example
tile, so deleting them would delete configuration.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Roles, playbooks, templates and scripts MUST consume the central
`NETWORK_PUBLIC_DNS_RESOLVERS` list from `group_vars/all/08_networks.yml`
instead of pasting `1.1.1.1` / `8.8.8.8` / `1.0.0.1` literally, so a
single edit propagates everywhere those resolvers are used.
- tests/lint/repository/test_no_hardcoded_dns_resolvers.py: scans the
repo for the literal IPs declared in NETWORK_PUBLIC_DNS_RESOLVERS,
fails on any occurrence not covered by `# noqa: hardcoded-dns-resolver`.
- roles/sys-svc-container/vars/main.yml: `SYS_SVC_DOCKER_AUTOMTU_TARGETS`
no longer hard-codes `1.1.1.1`; the list is now built from
`['registry-1.docker.io', 'github.com'] + NETWORK_PUBLIC_DNS_RESOLVERS`.
- roles/user/templates/bashrc.j2: `ip route get 8.8.8.8` ->
`ip route get {{ NETWORK_PUBLIC_DNS_RESOLVERS[0] }}`.
- group_vars/all/08_networks.yml: drop literal IP examples from the
resolver-block doc comment.
- compose/coredns/Corefile.tmpl, scripts/system/network/dns/setup/wsl.sh,
roles/sys-ctl-mtn-cert-deploy/SETUP.md: tag the legitimate literal
occurrences with `# noqa: hardcoded-dns-resolver`. These are
substitution points that cannot consume the variable (CoreDNS
forward directive, host-bootstrap shell fallback, Markdown example).
- docs/contributing/actions/testing/suppression.md: catalog the new
`hardcoded-dns-resolver` rule with position semantics and example.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>