Ctrl K
ComunidadGuías…

lefthook.yml

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# SPDX-License-Identifier: FSL-1.1-Apache-2.0
#
# Local Git hooks managed by lefthook (https://lefthook.dev).
# Install with `lefthook install` after `nix run nixpkgs#lefthook --`
# or `bun add -D lefthook && bun lefthook install`.
#
# `pre-commit` uses the `jobs` form (lefthook >= 1.10) rather than
# `commands`, because only a job can carry a `group` — and the
# docsgen/docs-check pair needs one. See the comment on that group.
#
# GLOB SHAPE, once, because two globs here got it wrong: in lefthook's
# matcher `**` does NOT match zero directories when a separator
# follows it, so `docs/**/*.md` silently skips `docs/index.md` and
# `{cue.mod/**}/*.cue` silently skips `cue.mod/module.cue`. Any glob
# that must cover a tree AND its top level spells both out —
# `dir/{*.ext,**/*.ext}`. A TRAILING `/**` (no separator after) is
# fine and does match direct children, which is why the `docsgen` and
# `docs/hooks/**` globs need no such treatment.

pre-commit:
  parallel: true
  jobs:
    - name: cue
      glob: "{cue.mod,schemas,examples}/{*.cue,**/*.cue}"
      run: ./scripts/lint-cue.sh
    - name: spdx
      run: ./scripts/check-spdx-headers.sh
    - name: cyrillic
      run: ./scripts/check-no-cyrillic.sh
    - name: crd-check
      # `operator/crdgen/**` is in the glob for the same reason the
      # docsgen job's glob covers `cli/docsgen/**`: the GENERATOR also
      # changes what's committed, not just its CUE/Rust input — a
      # crdgen-only edit (structural.rs, cue.rs, envelope.rs) can
      # change the rendered CRD YAML with no schema or operator-core
      # change at all. `.github/workflows/crd-check.yml` already lists
      # it in `paths:`; this glob was the local half missing it.
      glob: "{schemas/v1alpha1/*.cue,operator/operator-core/src/*.rs,operator/crdgen/**,operator/charts/apprafter-operator/templates/crd-*.yaml}"
      run: ./scripts/crd-check.sh
    # These two are ORDERED, and the rest of the hook is not.
    #
    # `docsgen` WRITES docs/reference/cli/; `docs-check` READS it (the
    # strict mkdocs build, then the byte-compare). A commit touching
    # both `cli/platform-cli/src/**` and a documentation page matches
    # both globs — and that is the prescribed shape for a CLI change, which
    # regenerates the reference in the same commit — so under the
    # hook's `parallel: true` they would race: docs-check could read a
    # half-written tree, or the pre-regeneration one, and fail or pass
    # for the wrong reason.
    #
    # A `piped` group is lefthook's sequential construct: its jobs run
    # in listed order and the group stops at the first failure (so a
    # docsgen failure never reports itself as reference drift). The
    # group as a whole still runs in parallel with cue/spdx/cyrillic/
    # crd-check, which share no files with it. A job whose glob does
    # not match is skipped without failing the group, so a docs-only
    # commit still runs docs-check and a cli-only commit still runs
    # docsgen.
    #
    # `priority` is NOT the mechanism here: it only orders jobs when
    # the hook is sequential (`parallel: false` or `piped: true`), and
    # this hook is parallel by design.
    - name: docs
      group:
        piped: true
        jobs:
          # The CLI reference is generated from the clap tree, so a
          # touched cli.rs (or anything else under platform-cli) is a
          # docs change too. Regenerating and staging it here means a
          # contributor never sees "the docs drifted" for a file they
          # were never asked to edit — the gate in docs-check then
          # only fires for a hand-edited generated page.
          # `cli/docsgen/**` is in the glob for the same reason from
          # the other side: the GENERATOR also changes the committed
          # artefacts, not just its input. Editing `model.rs` alone
          # (no clap change at all) can add or drop a projected node
          # and change `commands.json` — proven by the root-binary
          # node this hook's own glob was missing.
          # `cli/Cargo.toml` is in the glob because commands.json
          # embeds `cli_version` — a release bump is a reference
          # change.
          # The nix guard mirrors scripts/docs-check.sh: `just
          # docsgen-generate` runs under `nix develop` for the pinned
          # toolchain a byte-compare needs, and without nix a raw
          # "command not found" says nothing about what to do next.
          - name: docsgen
            glob: "{cli/platform-cli/src/**,cli/docsgen/**,cli/Cargo.toml}"
            run: |
              command -v nix >/dev/null 2>&1 || {
                echo "ERROR: regenerating the CLI reference needs nix — docsgen runs" >&2
                echo "under 'nix develop' so local and CI render with one pinned" >&2
                echo "toolchain. Install nix (flakes enabled) — see" >&2
                echo "docs/contributing/setup.md — or skip the local hook with" >&2
                echo "'git commit --no-verify'; the docs workflow still runs on the" >&2
                echo "pull request." >&2
                exit 2
              }
              just docsgen-generate && git add docs/reference/cli
          # Wider than the pages themselves, because `docs-check` runs
          # the 2.19c drift gate and the gate resolves what the docs
          # CLAIM against what the tree SHIPS. Its two inputs are the
          # clap tree (`cli/**`, same files as the `docsgen` job above)
          # and the schemas (`schemas/v1alpha1` plus the CRDs generated
          # from them). Renaming a field or a flag is therefore a
          # documentation change even when no page is touched, and a
          # commit that matched neither glob used to leave the finding
          # for the next contributor's unrelated docs commit to hit.
          # Mirrors the `paths:` filter in .github/workflows/docs.yml.
          #
          # The corpus is `docs/**` PLUS the root `README.md`, which is
          # what `scan::in_scope` lists (`git ls-files -- docs
          # README.md`) and one of the pages the census counts. It is
          # named here because no `docs/` glob can reach it: without
          # the entry, deleting or rewriting the README moved the
          # census and fired nothing on that commit, leaving the
          # finding to land on the NEXT contributor's unrelated docs
          # pull request — the failure the wide globs below exist
          # against.
          #
          # `docs/measurements/docs-health.json` is in the glob as a
          # TRIGGER — editing the committed corpus census must re-run
          # the gate that compares against it — and it is spelled out
          # because the `.md` globs beside it do not cover a `.json`.
          #
          # It must NEVER be regenerated here, the way the `docsgen`
          # job above regenerates docs/reference/cli and `git add`s it.
          # That job is safe only because its TRIGGER (`cli/**`) and its
          # ARTEFACT (`docs/reference/cli/**`) live in different trees,
          # so a docs-only commit never rewrites it. This file's trigger
          # IS `docs/**`: a `docsgen metrics && git add` step here would
          # rewrite and stage the census on every documentation commit,
          # so the committed numbers would always equal the current
          # ones, every deletion would be recorded as intended, and the
          # ratchet would report success on exactly the change it exists
          # to catch. Re-recording is a deliberate act — run `docsgen
          # metrics` by hand, in the same commit, and say why in the
          # commit message.
          - name: docs-check
            glob: "{README.md,docs/{*.md,**/*.md},docs/measurements/docs-health.json,mkdocs.yml,docs/hooks/**,scripts/docs-check.sh,scripts/docs-artefacts-check.py,cli/platform-cli/src/**,cli/docsgen/**,cli/Cargo.toml,schemas/v1alpha1/*.cue,operator/charts/apprafter-operator/templates/crd-*.yaml}"
            run: ./scripts/docs-check.sh
    # THE VERSION GATES.
    #
    # These lived ONLY in `just lint`, and no workflow invokes `just lint` —
    # so the one check that catches "operator code changed but appVersion did
    # not" fired only when somebody remembered to type it. It did not fire for
    # the commit that shipped the surge-free rollout fix: that change passed
    # fmt, clippy, the full workspace test suite and the docs gate, and would
    # still never have become an image, because `release-operator`'s two-axis
    # gate (tag exists + images exist) SKIPS the rebuild when appVersion has
    # not moved. Committed, green, and silently not shipped — the class of
    # miss behind the 0.2.40/0.2.41 double-yank.
    #
    # Safe on a pre-commit hook for two MEASURED reasons. Each script is ~2s,
    # and they are listed FLAT rather than inside a `group:` so the hook's own
    # `parallel: true` applies to them — measured: the same three scripts take
    # 6251ms one after another and 1999ms together, and a `group:` carrying
    # `parallel: true` ran them sequentially anyway on lefthook 2.1.5. A group
    # here would buy a nicer heading and cost 4 seconds on every commit that
    # touches platform-stack. Second reason: each asks `git ls-remote` whether
    # its tag is already PUBLISHED, and with no network that call fails. Outside
    # CI the scripts then print a warning that the check did NOT run and exit 0
    # (scripts/lib/published-tag.sh; they used to say "in flight" instead). An
    # offline commit therefore LOSES the check rather than breaking on it — and
    # loses it only until the pull request: since WI-373 the `version-guards`
    # job in .github/workflows/lint.yml runs the same scripts on every pull
    # request and push to master, and there an undecided guard FAILS. That job
    # is also what catches the commits this hook never sees: `--no-verify`, a
    # clone without `lefthook install`, a web edit, a merge commit.
    #
    # `version-coherence` gets no glob narrower than the union of the others:
    # it cross-checks the platform-stack pins against BOTH chart appVersions
    # and the cue-cmp version, so a file from any of those trees can break it.
    - name: operator-version-bump
      glob: "{operator/**,schemas/v1alpha1/*.cue}"
      run: ./scripts/check-operator-version-bump.sh
    - name: cli-version-bump
      glob: "cli/**"
      run: ./scripts/check-cli-version-bump.sh
    - name: backup-runner-pin
      glob: "{cli/apprafter-backup/**,cli/backup-core/**,cli/Cargo.toml,platform-stack/cue/platform.cue}"
      run: ./scripts/check-backup-runner-pin.sh
    - name: cue-cmp-drift
      glob: "argocd-cue-cmp/**"
      run: ./scripts/check-argocd-cue-cmp-drift.sh
    - name: cue-cmp-mirror
      glob: "{argocd-cue-cmp/plugin.yaml,platform-stack/cue/component_argocd.cue}"
      run: ./scripts/check-cue-cmp-mirror.sh
    - name: version-coherence
      glob: "{operator/charts/**,platform-stack/**,argocd-cue-cmp/**,cli/Cargo.toml}"
      run: ./scripts/check-version-coherence.sh
    # The remaining `just lint` script gates. All offline, all a few seconds at
    # most, all glob-scoped, so they cost nothing on a commit that cannot break
    # them.
    #
    # What is deliberately NOT here: `just lint` also runs `cargo fmt`,
    # `cargo clippy` and the docsgen property tests across two workspaces.
    # Those are minutes, not seconds, and a pre-commit hook that costs minutes
    # gets bypassed with `--no-verify`, which loses the cheap checks too. They
    # stay in `just lint` and in the lint/test workflows, where a cold build is
    # expected.
    - name: heredoc-backticks
      glob: "{scripts/**,e2e/**,.github/workflows/**,argocd-cue-cmp/**}"
      run: ./scripts/check-heredoc-backticks.sh
    # dump_diagnostics is what a failing walk leaves behind, and nothing runs
    # it until a walk fails. See the header of the script.
    - name: dump-diagnostics
      glob: "{e2e/lib.sh,scripts/check-dump-diagnostics.sh,operator/operator-core/src/deadline.rs}"
      run: ./scripts/check-dump-diagnostics.sh
    # A workflow that does not parse fails as a run with ZERO JOBS, which
    # `gh pr checks` does not list — every check can be green while a
    # release workflow is broken. See the header of the script.
    - name: workflows
      glob: ".github/workflows/**"
      run: ./scripts/check-workflows.sh
    - name: crd-structural
      glob: "operator/charts/apprafter-operator/templates/crd-*.yaml"
      run: ./scripts/check-crd-structural.sh
    - name: argocd-health-lua
      glob: "platform-stack/**"
      run: ./scripts/check-argocd-health-lua.sh
    - name: component-enablement
      glob: "platform-stack/**"
      run: ./scripts/check-component-enablement.sh
    - name: plan-checkboxes
      glob: "plan.md"
      run: ./scripts/check-plan-checkboxes.sh
    # There is deliberately NO job here for `landing/cms/payload-types.ts`,
    # the generated file the `payload-types drift` step in
    # .github/workflows/lint.yml checks. Both shapes were built and their
    # failures reproduced before being rejected:
    #
    #   - Regenerate-and-stage, mirroring the `docsgen` job above, breaks under
    #     `git commit -- <paths>`, `--only` and `--amend -- <paths>`. Those run
    #     hooks against a THROWAWAY index (`.git/next-index-<pid>.lock`) that
    #     git discards afterwards, so the `git add` reaches the commit but never
    #     the real index: the artefact stays staged at its pre-hook bytes and
    #     the next plain commit silently reverts it, between two clean commits.
    #     `docsgen` shares this exposure; it is not a reason to add a second.
    #   - Either shape renders from the WORKING TREE while the commit is the
    #     INDEX. lefthook hides unstaged hunks only for PARTIALLY staged files,
    #     so a sibling global that is modified but wholly unstaged is baked into
    #     the artefact anyway — staged into a commit whose sources do not
    #     contain it, or reported as drift the commit does not have.
    #
    # CI is where index, tree and commit are one thing, so that is where the
    # byte-compare runs. `scripts/check-payload-types.sh` carries the long
    # version of this reasoning.

commit-msg:
  commands:
    conventional:
      run: ./scripts/check-commit-msg.sh "{1}"