From 4ca1d0eeb013f9b2445d9b68feb9418d45bfb321 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 2 Sep 2026 04:45:48 +0000 Subject: [PATCH 1/2] Heal Dependabot hygiene by committing pin and format fixes. Copy maplibre-compose's Dependabot hygiene flow: fix-action-pins rewrites consumers from the catalog, and the hygiene job commits mise run fix on same-repo Dependabot pull requests. Co-authored-by: Sargun Vohra --- .github/workflows/action-pins.yml | 3 +- .github/workflows/ci.yml | 18 ++ .mise/tasks/ci/check-action-pins | 5 +- .mise/tasks/ci/commit-hygiene-fixes | 34 +++ .mise/tasks/ci/fix-action-pins | 23 ++ .mise/tasks/ci/generate-workflow | 43 +++- ci/action_pins.py | 37 ++- ci/tests/test_action_pins.py | 223 ++++++++++++++++++ docs/src/content/docs/development/overview.md | 6 + hk.pkl | 1 + 10 files changed, 382 insertions(+), 11 deletions(-) create mode 100755 .mise/tasks/ci/commit-hygiene-fixes create mode 100755 .mise/tasks/ci/fix-action-pins create mode 100644 ci/tests/test_action_pins.py diff --git a/.github/workflows/action-pins.yml b/.github/workflows/action-pins.yml index e7f58d294..8186b42ca 100644 --- a/.github/workflows/action-pins.yml +++ b/.github/workflows/action-pins.yml @@ -1,7 +1,8 @@ # Single source of truth for third-party action pins. Dependabot only scans # `.github/workflows` and a root `action.yml`, so pins used elsewhere — the # composite actions, `ci:generate-workflow` — are listed here to keep them -# updated. `mise run ci:check-action-pins` verifies every reference agrees. +# updated. `mise run ci:check-action-pins` verifies every reference agrees, and +# `mise run ci:fix-action-pins` copies catalog pins onto consumers. name: Action pins # Never runs. The trigger is a branch that is never created, and `!always()` is diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cfc5f9c69..4257eba5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,7 @@ on: - synchronize - reopened - ready_for_review + workflow_dispatch: permissions: contents: read @@ -24,15 +25,25 @@ jobs: name: hygiene runs-on: ubuntu-latest timeout-minutes: 30 + permissions: + contents: write + actions: write environment: ${{ github.event_name == 'push' && 'sccache' || '' }} if: >- github.event_name != 'pull_request' || github.base_ref == 'main' || github.event.pull_request.draft == false + env: + DEPENDABOT_PR: ${{ github.event_name == 'pull_request' && github.actor == 'dependabot[bot]' && github.event.pull_request.head.repo.full_name == github.repository }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + if: env.DEPENDABOT_PR != 'true' with: persist-credentials: false + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + if: env.DEPENDABOT_PR == 'true' + with: + ref: ${{ github.head_ref }} - uses: ./.github/actions/setup-ci-deps with: gradle: false @@ -46,7 +57,14 @@ jobs: - run: mise run --force //bindings/kotlin:generate - run: dprint output-resolved-config > /dev/null - run: mise run fix + - id: apply-fix + if: env.DEPENDABOT_PR == 'true' + env: + GH_TOKEN: ${{ github.token }} + HEAD_REF: ${{ github.head_ref }} + run: mise run ci:commit-hygiene-fixes - name: Check generated and formatted files + if: steps.apply-fix.outputs.committed != 'true' run: | git update-index -q --refresh git diff --exit-code -- . ':(exclude)mise*.lock' diff --git a/.mise/tasks/ci/check-action-pins b/.mise/tasks/ci/check-action-pins index 84ef8e913..e7470e94c 100755 --- a/.mise/tasks/ci/check-action-pins +++ b/.mise/tasks/ci/check-action-pins @@ -19,8 +19,9 @@ def main() -> int: for problem in problems: print(f"error: {problem}", file=sys.stderr) print( - f"\nUpdate {CATALOG.as_posix()} and the references above so every action " - "resolves to one commit, then run `mise run ci:generate-workflow`.", + f"\nRun `mise run ci:fix-action-pins` to copy catalog pins onto consumers. " + f"Unpinned uses and unused catalog entries still require an edit of " + f"{CATALOG.as_posix()}.", file=sys.stderr, ) return 1 diff --git a/.mise/tasks/ci/commit-hygiene-fixes b/.mise/tasks/ci/commit-hygiene-fixes new file mode 100755 index 000000000..c6a63dbf4 --- /dev/null +++ b/.mise/tasks/ci/commit-hygiene-fixes @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# [MISE] description="Commit tracked files left dirty by mise run fix." +set -euo pipefail +cd "${MISE_PROJECT_ROOT:-$(cd "$(dirname "$0")/../../.." && pwd)}" + +committed=false +git update-index -q --refresh +if ! git diff --quiet || ! git diff --cached --quiet; then + git add --update + git -c user.name="github-actions[bot]" \ + -c user.email="41898282+github-actions[bot]@users.noreply.github.com" \ + commit --message "chore: apply hygiene fixes" + committed=true +fi + +if [[ -n ${GITHUB_OUTPUT:-} ]]; then + echo "committed=${committed}" >> "${GITHUB_OUTPUT}" +fi +if [[ ${committed} == true ]]; then + echo "committed=true" +else + echo "Tree already clean." +fi + +if [[ -z ${GH_TOKEN:-} || -z ${HEAD_REF:-} ]]; then + exit 0 +fi +if [[ ${committed} != true && $(git log -1 --pretty=%s) != "chore: apply hygiene fixes" ]]; then + exit 0 +fi +git push +# GITHUB_TOKEN pushes do not start new workflow runs, so dispatch CI on the +# updated branch after the push. +gh workflow run CI --ref "${HEAD_REF}" diff --git a/.mise/tasks/ci/fix-action-pins b/.mise/tasks/ci/fix-action-pins new file mode 100755 index 000000000..a02038175 --- /dev/null +++ b/.mise/tasks/ci/fix-action-pins @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# [MISE] description="Copy catalog action pins onto every consumer." +# [MISE] shell="python" + +import pathlib +import sys + + +ROOT = pathlib.Path(__file__).resolve().parents[3] +sys.path.insert(0, str(ROOT)) + +from ci.action_pins import fix_pins # noqa: E402 + + +def main() -> int: + changed = fix_pins(ROOT) + for path in changed: + print(f"updated {path.relative_to(ROOT).as_posix()}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.mise/tasks/ci/generate-workflow b/.mise/tasks/ci/generate-workflow index 05b0a59de..b4a1201b0 100755 --- a/.mise/tasks/ci/generate-workflow +++ b/.mise/tasks/ci/generate-workflow @@ -68,13 +68,18 @@ def setup( zig: bool = False, gradle: bool = True, save_toolchains: bool = False, + checkout: bool = True, ) -> list[str]: - lines = [ - f" - uses: {CHECKOUT}", - " with:", - " persist-credentials: false", - " - uses: ./.github/actions/setup-ci-deps", - ] + lines: list[str] = [] + if checkout: + lines.extend( + [ + f" - uses: {CHECKOUT}", + " with:", + " persist-credentials: false", + ] + ) + lines.append(" - uses: ./.github/actions/setup-ci-deps") if zig: lines.append(" id: setup") lines.append(" with:") @@ -227,6 +232,10 @@ def render(source: dict[str, object], presets: dict[str, object]) -> str: " - synchronize", " - reopened", " - ready_for_review", + # GITHUB_TOKEN pushes from Dependabot hygiene healing do not start + # pull_request workflows, so that job dispatches CI on the updated + # branch after it commits. + " workflow_dispatch:", "", "permissions:", " contents: read", @@ -240,10 +249,23 @@ def render(source: dict[str, object], presets: dict[str, object]) -> str: " name: hygiene", " runs-on: ubuntu-latest", " timeout-minutes: 30", + " permissions:", + " contents: write", + " actions: write", SCCACHE_ENVIRONMENT, *gate(), + " env:", + " DEPENDABOT_PR: ${{ github.event_name == 'pull_request' && github.actor == 'dependabot[bot]' && github.event.pull_request.head.repo.full_name == github.repository }}", " steps:", - *setup(gradle=False), + f" - uses: {CHECKOUT}", + " if: env.DEPENDABOT_PR != 'true'", + " with:", + " persist-credentials: false", + f" - uses: {CHECKOUT}", + " if: env.DEPENDABOT_PR == 'true'", + " with:", + " ref: ${{ github.head_ref }}", + *setup(gradle=False, checkout=False), " - run: mise run ci:generate-workflow --check", " - run: mise run ci:generate-devcontainer-tools --check", " - run: mise run ci:test-release-tools", @@ -254,7 +276,14 @@ def render(source: dict[str, object], presets: dict[str, object]) -> str: # `mise run fix` provides the project-scoped formatter tools that the # dprint wrappers expect; calling hk directly leaves them uninstalled. " - run: mise run fix", + " - id: apply-fix", + " if: env.DEPENDABOT_PR == 'true'", + " env:", + " GH_TOKEN: ${{ github.token }}", + " HEAD_REF: ${{ github.head_ref }}", + " run: mise run ci:commit-hygiene-fixes", " - name: Check generated and formatted files", + " if: steps.apply-fix.outputs.committed != 'true'", " run: |", " git update-index -q --refresh", " git diff --exit-code -- . ':(exclude)mise*.lock'", diff --git a/ci/action_pins.py b/ci/action_pins.py index 9584dc1c9..277d60225 100644 --- a/ci/action_pins.py +++ b/ci/action_pins.py @@ -1,4 +1,4 @@ -"""Read and verify the third-party GitHub Actions pins catalog.""" +"""Read, verify, and rewrite the third-party GitHub Actions pins catalog.""" from __future__ import annotations @@ -106,3 +106,38 @@ def check_pins(root: pathlib.Path) -> list[str]: f"{CATALOG.as_posix()}: {action} is no longer used; remove the pin" ) return problems + + +# The SHA and version comment; indent, `- `, and `uses:` stay as they were. +_PIN_REF = re.compile(r"@[0-9a-f]{40}\s+#\s*\S+") + + +def _apply_pin(line: str, pin: Pin) -> str: + return _PIN_REF.sub(f"@{pin.sha} # {pin.version}", line, count=1) + + +def fix_pins(root: pathlib.Path) -> list[pathlib.Path]: + """Rewrite consumer pins that disagree with the catalog. + + Unpinned uses, actions missing from the catalog, and unused catalog + entries stay as they are. Those still fail ``check_pins``. + """ + pins = catalog(root) + changed: list[pathlib.Path] = [] + for path in consumers(root): + original = path.read_text() + rewritten: list[str] = [] + dirty = False + for line in original.splitlines(keepends=True): + pinned = PINNED.match(line) + if pinned: + current = Pin(**pinned.groupdict()) + expected = pins.get(current.action) + if expected is not None and current != expected: + line = _apply_pin(line, expected) + dirty = True + rewritten.append(line) + if dirty: + path.write_text("".join(rewritten), newline="\n") + changed.append(path) + return changed diff --git a/ci/tests/test_action_pins.py b/ci/tests/test_action_pins.py new file mode 100644 index 000000000..8ab114779 --- /dev/null +++ b/ci/tests/test_action_pins.py @@ -0,0 +1,223 @@ +"""Tests for the GitHub Actions pins catalog.""" + +from __future__ import annotations + +import pathlib +import tempfile +import unittest + +from action_pins import CATALOG, Pin, check_pins, fix_pins + +OLD = Pin( + action="jdx/mise-action", + sha="3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518", + version="v4.2.5", +) +NEW = Pin( + action="jdx/mise-action", + sha="c2a87611a18de5b3828c5652fe268e992400cb5c", + version="v4.3.0", +) +CACHE = Pin( + action="actions/cache", + sha="55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + version="v6.1.0", +) + + +def _write(root: pathlib.Path, relative: str, text: str) -> pathlib.Path: + path = root / relative + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(text) + return path + + +def _catalog(*pins: Pin) -> str: + steps = "\n".join(f" - uses: {pin.reference}" for pin in pins) + return ( + "name: Action pins\n" + "on:\n" + " push:\n" + " branches:\n" + " - action-pins/never-runs\n" + "jobs:\n" + " pins:\n" + " runs-on: ubuntu-latest\n" + " steps:\n" + f"{steps}\n" + ) + + +def _consumer(pin: Pin, *, list_item: bool = False) -> str: + prefix = " - uses: " if list_item else " uses: " + return ( + "name: Setup\n" + "runs:\n" + " using: composite\n" + " steps:\n" + " - name: Install\n" + f"{prefix}{pin.reference}\n" + ) + + +def _repo(tmp: pathlib.Path, *, catalog_pins: list[Pin], consumer: str) -> pathlib.Path: + _write(tmp, CATALOG.as_posix(), _catalog(*catalog_pins)) + _write(tmp, ".github/actions/setup-ci-deps/action.yml", consumer) + return tmp + + +class CheckPinsTest(unittest.TestCase): + def test_ok_when_consumer_matches_catalog(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + root = _repo( + pathlib.Path(tmp), + catalog_pins=[NEW], + consumer=_consumer(NEW), + ) + self.assertEqual(check_pins(root), []) + + def test_reports_sha_mismatch(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + root = _repo( + pathlib.Path(tmp), + catalog_pins=[NEW], + consumer=_consumer(OLD), + ) + problems = check_pins(root) + self.assertEqual(len(problems), 1) + self.assertIn(OLD.sha, problems[0]) + self.assertIn(NEW.sha, problems[0]) + self.assertIn("setup-ci-deps/action.yml:6", problems[0]) + + def test_reports_unpinned_use(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + root = _repo( + pathlib.Path(tmp), + catalog_pins=[NEW], + consumer=_consumer(NEW).replace(NEW.reference, "jdx/mise-action@v4"), + ) + problems = check_pins(root) + self.assertTrue(any("not pinned" in problem for problem in problems)) + + def test_reports_unused_catalog_pin(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + root = _repo( + pathlib.Path(tmp), + catalog_pins=[NEW, CACHE], + consumer=_consumer(NEW), + ) + problems = check_pins(root) + self.assertEqual( + problems, + [ + f"{CATALOG.as_posix()}: {CACHE.action} is no longer used; remove the pin" + ], + ) + + +class FixPinsTest(unittest.TestCase): + def test_rewrites_mismatched_consumer(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + root = _repo( + pathlib.Path(tmp), + catalog_pins=[NEW], + consumer=_consumer(OLD), + ) + changed = fix_pins(root) + consumer = root / ".github/actions/setup-ci-deps/action.yml" + self.assertEqual(changed, [consumer]) + self.assertEqual(consumer.read_text(), _consumer(NEW)) + self.assertEqual(check_pins(root), []) + + def test_preserves_list_item_uses(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + root = _repo( + pathlib.Path(tmp), + catalog_pins=[NEW], + consumer=_consumer(OLD, list_item=True), + ) + fix_pins(root) + consumer = root / ".github/actions/setup-ci-deps/action.yml" + self.assertEqual(consumer.read_text(), _consumer(NEW, list_item=True)) + + def test_rewrites_workflow_consumers(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + root = pathlib.Path(tmp) + _write(root, CATALOG.as_posix(), _catalog(NEW)) + workflow = _write( + root, + ".github/workflows/ci.yml", + "name: CI\non: push\njobs:\n a:\n runs-on: ubuntu-latest\n" + " steps:\n" + f" - uses: {OLD.reference}\n", + ) + fix_pins(root) + self.assertIn(NEW.reference, workflow.read_text()) + self.assertEqual(check_pins(root), []) + + def test_is_noop_when_already_aligned(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + root = _repo( + pathlib.Path(tmp), + catalog_pins=[NEW], + consumer=_consumer(NEW), + ) + consumer = root / ".github/actions/setup-ci-deps/action.yml" + before = consumer.read_text() + self.assertEqual(fix_pins(root), []) + self.assertEqual(consumer.read_text(), before) + + def test_leaves_unpinned_uses(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + consumer_text = _consumer(NEW).replace(NEW.reference, "jdx/mise-action@v4") + root = _repo( + pathlib.Path(tmp), + catalog_pins=[NEW], + consumer=consumer_text, + ) + consumer = root / ".github/actions/setup-ci-deps/action.yml" + self.assertEqual(fix_pins(root), []) + self.assertEqual(consumer.read_text(), consumer_text) + self.assertTrue( + any("not pinned" in problem for problem in check_pins(root)) + ) + + def test_leaves_unused_catalog_pins(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + root = _repo( + pathlib.Path(tmp), + catalog_pins=[NEW, CACHE], + consumer=_consumer(NEW), + ) + catalog = root / CATALOG + before = catalog.read_text() + self.assertEqual(fix_pins(root), []) + self.assertEqual(catalog.read_text(), before) + self.assertTrue( + any("no longer used" in problem for problem in check_pins(root)) + ) + + def test_is_idempotent(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + root = _repo( + pathlib.Path(tmp), + catalog_pins=[NEW], + consumer=_consumer(OLD), + ) + self.assertTrue(fix_pins(root)) + self.assertEqual(fix_pins(root), []) + self.assertEqual(check_pins(root), []) + + def test_writes_lf_newlines(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + root = _repo( + pathlib.Path(tmp), + catalog_pins=[NEW], + consumer=_consumer(OLD), + ) + consumer = root / ".github/actions/setup-ci-deps/action.yml" + consumer.write_bytes(consumer.read_bytes().replace(b"\n", b"\r\n")) + fix_pins(root) + data = consumer.read_bytes() + self.assertNotIn(b"\r\n", data) + self.assertIn(NEW.reference.encode(), data) diff --git a/docs/src/content/docs/development/overview.md b/docs/src/content/docs/development/overview.md index b0afb7865..2ce7a4f4b 100644 --- a/docs/src/content/docs/development/overview.md +++ b/docs/src/content/docs/development/overview.md @@ -208,6 +208,12 @@ snapshot workflow publishes, so a component republishes only when the paths it consumes changed; `mise run ci:check-snapshot-scopes` keeps every tracked path classified. +Third-party GitHub Actions pins live in `.github/workflows/action-pins.yml`. +`mise run ci:check-action-pins` verifies that every workflow and composite +action agrees with that catalog, and `mise run ci:fix-action-pins` copies +catalog pins onto consumers. On a Dependabot pull request from this repository, +the hygiene job runs `mise run fix` and commits the result. + [Astro](https://astro.build/) and [Starlight](https://starlight.astro.build/) build the documentation site. Generated API reference HTML is installed into `docs/public/reference/` before each docs build. diff --git a/hk.pkl b/hk.pkl index b270cd17e..278d6ed7a 100644 --- a/hk.pkl +++ b/hk.pkl @@ -25,6 +25,7 @@ local lintSteps = new Mapping { ".github/actions/**/action.yaml", ) check = "mise run ci:check-action-pins" + fix = "mise run ci:fix-action-pins" } // Whole-repository check: any added or removed file can break scope coverage, // so this runs on every invocation rather than filtering on a glob. From 28ccd6e45cd104b8067efca104cfa876c99e22f2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 2 Sep 2026 14:48:04 +0000 Subject: [PATCH 2/2] Heal generated workflow pins before hygiene checks. Dependabot catalog bumps must rewrite ci.yml after mise run fix, or generate-workflow --check fails first. Keep mise.lock out of the hygiene commit the same way the final diff check does. Co-authored-by: Sargun Vohra --- .github/workflows/ci.yml | 5 +++-- .mise/tasks/ci/commit-hygiene-fixes | 10 ++++++++-- .mise/tasks/ci/generate-workflow | 12 ++++++++---- docs/src/content/docs/development/overview.md | 3 ++- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4257eba5b..df660328b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,14 +49,15 @@ jobs: gradle: false sccache-access-key-id: ${{ secrets.R2_SCCACHE_ACCESS_KEY_ID }} sccache-secret-access-key: ${{ secrets.R2_SCCACHE_SECRET_ACCESS_KEY }} + - run: dprint output-resolved-config > /dev/null + - run: mise run fix + - run: mise run ci:generate-workflow - run: mise run ci:generate-workflow --check - run: mise run ci:generate-devcontainer-tools --check - run: mise run ci:test-release-tools - run: mise run --force //bindings/dart:ffigen - run: mise run --force //bindings/dotnet:generate - run: mise run --force //bindings/kotlin:generate - - run: dprint output-resolved-config > /dev/null - - run: mise run fix - id: apply-fix if: env.DEPENDABOT_PR == 'true' env: diff --git a/.mise/tasks/ci/commit-hygiene-fixes b/.mise/tasks/ci/commit-hygiene-fixes index c6a63dbf4..15d632725 100755 --- a/.mise/tasks/ci/commit-hygiene-fixes +++ b/.mise/tasks/ci/commit-hygiene-fixes @@ -4,9 +4,12 @@ set -euo pipefail cd "${MISE_PROJECT_ROOT:-$(cd "$(dirname "$0")/../../.." && pwd)}" committed=false +# Hygiene ignores runner-generated lockfile churn; keep that out of the commit. +lockfile_exclude=':(exclude)mise*.lock' git update-index -q --refresh -if ! git diff --quiet || ! git diff --cached --quiet; then - git add --update +if ! git diff --quiet -- . "${lockfile_exclude}" \ + || ! git diff --cached --quiet -- . "${lockfile_exclude}"; then + git add --update -- . "${lockfile_exclude}" git -c user.name="github-actions[bot]" \ -c user.email="41898282+github-actions[bot]@users.noreply.github.com" \ commit --message "chore: apply hygiene fixes" @@ -25,6 +28,9 @@ fi if [[ -z ${GH_TOKEN:-} || -z ${HEAD_REF:-} ]]; then exit 0 fi +# A clean tree whose tip is already a hygiene commit still pushes and +# dispatches. That recovers a run that committed and pushed, then failed +# before `gh workflow run` (maplibre-compose#1140). if [[ ${committed} != true && $(git log -1 --pretty=%s) != "chore: apply hygiene fixes" ]]; then exit 0 fi diff --git a/.mise/tasks/ci/generate-workflow b/.mise/tasks/ci/generate-workflow index b4a1201b0..2600ea30d 100755 --- a/.mise/tasks/ci/generate-workflow +++ b/.mise/tasks/ci/generate-workflow @@ -266,16 +266,20 @@ def render(source: dict[str, object], presets: dict[str, object]) -> str: " with:", " ref: ${{ github.head_ref }}", *setup(gradle=False, checkout=False), + " - run: dprint output-resolved-config > /dev/null", + # `mise run fix` rewrites consumer action pins from the catalog, then + # `ci:generate-workflow` applies those pins to this file. Both run + # before --check so a Dependabot catalog bump can heal instead of + # failing the job first. `mise run fix` also provides the + # project-scoped formatter tools that the dprint wrappers expect. + " - run: mise run fix", + " - run: mise run ci:generate-workflow", " - run: mise run ci:generate-workflow --check", " - run: mise run ci:generate-devcontainer-tools --check", " - run: mise run ci:test-release-tools", " - run: mise run --force //bindings/dart:ffigen", " - run: mise run --force //bindings/dotnet:generate", " - run: mise run --force //bindings/kotlin:generate", - " - run: dprint output-resolved-config > /dev/null", - # `mise run fix` provides the project-scoped formatter tools that the - # dprint wrappers expect; calling hk directly leaves them uninstalled. - " - run: mise run fix", " - id: apply-fix", " if: env.DEPENDABOT_PR == 'true'", " env:", diff --git a/docs/src/content/docs/development/overview.md b/docs/src/content/docs/development/overview.md index 2ce7a4f4b..73126035a 100644 --- a/docs/src/content/docs/development/overview.md +++ b/docs/src/content/docs/development/overview.md @@ -212,7 +212,8 @@ Third-party GitHub Actions pins live in `.github/workflows/action-pins.yml`. `mise run ci:check-action-pins` verifies that every workflow and composite action agrees with that catalog, and `mise run ci:fix-action-pins` copies catalog pins onto consumers. On a Dependabot pull request from this repository, -the hygiene job runs `mise run fix` and commits the result. +the hygiene job runs `mise run fix`, regenerates `.github/workflows/ci.yml` from +the catalog, and commits the result. [Astro](https://astro.build/) and [Starlight](https://starlight.astro.build/) build the documentation site. Generated API reference HTML is installed into