Some checks failed
repo hygiene / hygiene (push) Has been cancelled
repo-hygiene.yml is the one workflow with no path filter. Its whole
purpose is to run on every commit, because the other six are scoped
with `paths:` and a commit touching only unfiltered files otherwise
gets no checks at all. The file's own header comment explains this,
citing commit 8c9ccb9, which pushed 30 conflict-marker lines into two
workflow files and silently disabled the CAD gates.
It has never run. Not once. Of the first 47 task records after a runner
was registered, every other workflow appears and this one does not,
across pushes that touched .forgejo/, tools/, crates/ and Cargo.lock.
The cause is the mapping-with-null-values form:
on:
push:
pull_request:
Valid YAML, both keys parse as None, and it is the spelling GitHub
documents for "all branches". This instance does not schedule it. The
list form does.
So the workflow that exists to catch silently-disabled checks was
itself a silently-disabled check.
138 lines
5.7 KiB
YAML
138 lines
5.7 KiB
YAML
name: repo hygiene
|
|
|
|
# The one workflow with NO path filter.
|
|
#
|
|
# Every other workflow in this directory gates a specific crate and is
|
|
# scoped with `paths:`. That is correct for build and test jobs -- there
|
|
# is no reason to compile the CAD module because a payment file moved.
|
|
# But it leaves a structural hole: a commit that touches only unfiltered
|
|
# paths runs no checks whatsoever.
|
|
#
|
|
# That hole is not hypothetical. Commit 8c9ccb9 pushed 30 unresolved
|
|
# conflict-marker lines across five files:
|
|
#
|
|
# .forgejo/workflows/nigig-build.yml <- invalid YAML: the CAD gates
|
|
# could not even be parsed,
|
|
# so they silently stopped running
|
|
# .forgejo/workflows/pdf.yml
|
|
# crates/apps/.../cad/workspace.rs <- ~3,000 lines also lost
|
|
# crates/apps/.../spreadsheet-engine/src/formula2.rs
|
|
# crates/apps/.../cad/ARCHITECTURE.md
|
|
#
|
|
# `git diff --check` already existed as a step inside nigig-build.yml and
|
|
# would have caught this -- but it was in the file the same commit broke,
|
|
# and it only ran for paths under that workflow's filter.
|
|
#
|
|
# So the checks here must satisfy three properties:
|
|
# 1. no path filter,
|
|
# 2. no dependency on the Rust toolchain or the network, so they cannot
|
|
# be knocked out by an unrelated build break, and
|
|
# 3. they must validate the CI configuration itself.
|
|
|
|
# NOTE the list form. This was previously written as
|
|
#
|
|
# on:
|
|
# push:
|
|
# pull_request:
|
|
#
|
|
# which is valid YAML -- both keys parse as null -- and is how GitHub
|
|
# spells "every branch". This instance does not schedule it. Across the
|
|
# first 47 task records after a runner was registered, every other
|
|
# workflow ran and this one never did, not once, despite having no path
|
|
# filter and despite pushes touching every filtered path in the repo.
|
|
#
|
|
# That is the worst possible failure for this particular file, because
|
|
# its entire reason to exist is to be the workflow that always runs.
|
|
on: [push, pull_request]
|
|
|
|
jobs:
|
|
hygiene:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
# An unresolved merge is the highest-severity thing that can reach
|
|
# main: the file does not parse, and if it is a workflow the checks
|
|
# that would have caught it stop running.
|
|
- name: No conflict markers anywhere in the tree
|
|
run: |
|
|
set -euo pipefail
|
|
# Anchored to line start, and the closing marker requires the
|
|
# trailing space git writes, so ordinary text (a Markdown rule,
|
|
# a Rust shift operator) does not trip it.
|
|
if git grep -nE '^(<{7} |={7}$|>{7} )' -- \
|
|
':(exclude).forgejo/workflows/repo-hygiene.yml'; then
|
|
echo
|
|
echo "ERROR: unresolved conflict markers are committed above."
|
|
echo "Resolve the merge before pushing; these files do not parse."
|
|
exit 1
|
|
fi
|
|
echo "OK"
|
|
|
|
# A workflow that is not valid YAML does not fail -- it does not run.
|
|
# That is strictly worse than a red build, because it is invisible.
|
|
- name: Every workflow file must be valid YAML
|
|
run: |
|
|
set -euo pipefail
|
|
python3 - <<'PY'
|
|
import sys, pathlib, yaml
|
|
bad = []
|
|
for f in sorted(pathlib.Path('.forgejo/workflows').glob('*.yml')):
|
|
try:
|
|
doc = yaml.safe_load(f.read_text())
|
|
except Exception as exc:
|
|
bad.append((f, exc))
|
|
continue
|
|
if not isinstance(doc, dict) or 'jobs' not in doc:
|
|
bad.append((f, 'no top-level `jobs:` mapping'))
|
|
for f, why in bad:
|
|
print(f"{f}: {why}")
|
|
if bad:
|
|
print()
|
|
print("ERROR: the workflow file(s) above do not parse, so their")
|
|
print("checks are silently not running.")
|
|
sys.exit(1)
|
|
print("OK")
|
|
PY
|
|
|
|
# A script a workflow invokes as `./tools/x.sh` must be executable
|
|
# in the index, not just in whoever's working copy. The mode is
|
|
# what gets committed; a local `chmod +x` that is never staged is
|
|
# invisible until CI runs.
|
|
#
|
|
# This was live on main. The first real CI run after a runner was
|
|
# registered failed two pay-domain jobs at their first real step:
|
|
#
|
|
# ./tools/makepad-native-libs.sh: Permission denied
|
|
# ./tools/test-mpesa-store-clean.sh: Permission denied
|
|
#
|
|
# Five of the six scripts under tools/ were mode 100644. Every one
|
|
# of them is invoked with a leading `./` from pay-domain.yml or
|
|
# pdf.yml, so those jobs could never have passed. Nobody noticed
|
|
# because nothing had ever executed them.
|
|
- name: Scripts invoked by a workflow must be executable
|
|
run: |
|
|
set -euo pipefail
|
|
fail=0
|
|
while IFS= read -r script; do
|
|
mode=$(git ls-files -s -- "$script" | cut -d' ' -f1)
|
|
if [ "$mode" != "100755" ]; then
|
|
echo "$script is mode $mode, expected 100755"
|
|
fail=1
|
|
fi
|
|
done < <(git ls-files 'tools/*.sh')
|
|
if [ "$fail" -ne 0 ]; then
|
|
echo
|
|
echo "ERROR: the script(s) above are committed non-executable."
|
|
echo "A workflow that runs them as ./tools/<name>.sh dies with"
|
|
echo "'Permission denied' before doing any work."
|
|
echo "Fix with: git update-index --chmod=+x <script>"
|
|
exit 1
|
|
fi
|
|
echo "OK"
|
|
|
|
# Whitespace errors: trailing space, spaces-before-tab, and the
|
|
# marker-adjacent damage a bad merge leaves behind.
|
|
- name: Reject whitespace errors
|
|
run: git diff --check "$(git rev-list --max-parents=0 HEAD | tail -1)"..HEAD || git diff --check
|