Files
pi-devbox/.gitea/workflows/lint.yml
T
pi f20b2a7926
Lint / hadolint (push) Successful in 9s
Lint / actionlint (push) Has been cancelled
ci(lint): don't re-lint on tag pushes
`on: push:` with no filter also fires on refs/tags/v*, which is duplicate work:
the tagged tree was already linted when that same commit was pushed to main
(v1.6.4 sha e86e5df linted as id=529 on main, then again as id=531 on the tag).

Two costs beyond the wasted run. It consumed one of the two self-hosted runners
while the release pipeline wanted both for its parallel multi-arch variant
builds; and it made release-run discovery ambiguous, since the newest-first runs
listing puts the tag-ref lint run above the publish run.

`branches: ['**']` keeps the documented intent exactly — lint fires early on
every branch push and PR, rather than only at tag time — while excluding tag
refs. docker-publish.yml is untouched and still tag-scoped.
2026-08-04 18:06:34 +02:00

108 lines
4.5 KiB
YAML

name: Lint
# Durable guard against CI-workflow bugs — most importantly the recurring
# "bash-only syntax under the default `sh`/dash shell" footgun that broke
# resolve-versions (ed49b8d) and promote-base-latest (b7197e8 → run 418).
# actionlint runs shellcheck against each `run:` step using its *effective*
# shell, so `set -o pipefail` under dash is flagged as SC3040 before any
# expensive build runs. This is cheap (~10s) and independent of the build
# pipeline, so it fires on every branch push/PR — not just on release tags,
# which is where the build workflow (docker-publish.yml) is otherwise only
# triggered.
#
# `branches: ['**']` (rather than a bare `push:`) deliberately EXCLUDES tag
# pushes. A bare `push:` also fires on `refs/tags/v*`, which was pure duplicate
# work — the tagged tree was already linted when the same commit was pushed to
# main (v1.6.4: lint id=529 on refs/heads/main, then id=531 again on
# refs/tags/v1.6.4, same sha e86e5df). Worse, that duplicate consumed one of the
# two self-hosted runners while the release pipeline wanted both for its
# parallel multi-arch variant builds, and it made release-run discovery
# ambiguous: the runs listing is newest-first, so the tag-ref lint run sorts
# ABOVE the publish run and "first run matching refs/tags/<tag>" picks lint —
# which goes green in minutes while the image is still building. See AGENTS.md
# "Gitea API access" for the head_sha-filtered discovery pattern.
on:
push:
branches:
- '**'
pull_request:
workflow_dispatch:
concurrency:
group: lint-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
actionlint:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- uses: actions/checkout@v4
- name: Install shellcheck
run: |
apt-get update
apt-get install -y --no-install-recommends shellcheck python3-yaml
- name: Gitea shell guard (catches the actionlint blind spot)
# actionlint models GitHub Actions, where the default run shell is
# bash, so it does NOT flag bash syntax in a step that merely OMITS
# `shell:` — which is exactly how ed49b8d and b7197e8 manifested on
# Gitea (default sh/dash). This guard enforces that every run: step
# resolves to bash under Gitea's real defaults. Run it BEFORE
# actionlint so the more precise diagnostic surfaces first.
run: bash scripts/check-workflow-shell.sh .gitea/workflows
- name: Install actionlint (pinned)
env:
ACTIONLINT_VERSION: 1.7.7
run: |
curl -fsSL \
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" \
| tar -xz -C /usr/local/bin actionlint
actionlint --version
- name: Run actionlint
# SHELLCHECK_OPTS excludes pure-style codes (quoting/style opinions)
# so the guard stays focused on correctness bugs — crucially the
# SC3xxx "not POSIX / wrong shell" family that catches the pipefail
# footgun. Do NOT exclude SC3040 (set -o pipefail under sh) or any
# other SC3xxx code.
env:
SHELLCHECK_OPTS: "-e SC2086 -e SC2016 -e SC2129 -e SC2001 -e SC2312"
# Pass explicit paths: actionlint's no-arg mode auto-detects a
# project by looking for `.github/workflows`, which doesn't exist in
# this `.gitea/workflows` repo and hard-fails with exit 3
# ("no project was found"). Globbing the workflow files is the
# supported way to lint a non-GitHub layout.
run: actionlint -color .gitea/workflows/*.yml
hadolint:
# Lint the two Dockerfiles that ARE the project (the shell/actions linting
# above never looked at them). Config — ignored rules + failure threshold
# — lives in .hadolint.yaml, which hadolint reads automatically, so a local
# `hadolint Dockerfile.base` reproduces CI exactly.
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- uses: actions/checkout@v4
- name: Install hadolint (pinned)
env:
HADOLINT_VERSION: 2.14.0
run: |
curl -fsSL \
"https://github.com/hadolint/hadolint/releases/download/v${HADOLINT_VERSION}/hadolint-Linux-x86_64" \
-o /usr/local/bin/hadolint
chmod +x /usr/local/bin/hadolint
hadolint --version
- name: Run hadolint
run: hadolint Dockerfile.base Dockerfile.variant