From 21d7f57a3f3eccef874a3a24d78961388023a618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Scoma=C3=A7=C3=A3o=20do=20Nascimento?= Date: Wed, 2 Sep 2026 15:03:48 -0300 Subject: [PATCH] fix(lint-workflows): ignore actionlint's stale report of the code-quality scope Co-Authored-By: Claude Opus 5 --- .github/renovate.json | 8 ++++++++ README.md | 13 ++++++++----- actions/lint-workflows/README.md | 29 ++++++++++++++++++++++------- actions/lint-workflows/action.yaml | 28 +++++++++++++++++++++++----- 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/.github/renovate.json b/.github/renovate.json index 6a33b16..1a17ef2 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -23,6 +23,14 @@ { "automerge": true, "matchUpdateTypes": ["minor", "patch"] + }, + { + "automerge": false, + "description": "actions/lint-workflows carries an -ignore for actionlint's stale report of the code-quality permission scope. The bump that makes it unnecessary is the only moment anybody will think to remove it, so the reminder rides in that pull request rather than sitting in a file nobody reads. This rule follows the automerge one so it wins: the pin moves rarely and the pull request is there to be read.", + "matchPackageNames": ["rhysd/actionlint"], + "prBodyNotes": [ + "Check whether this release makes `actions/lint-workflows`'s `-ignore` flag redundant: look for `code-quality` in `rule_permissions.go` at this tag, or lint a workflow granting the scope with this release and see it pass. If it does, delete the `flags:` line and its comment from `actions/lint-workflows/action.yaml`, this rule from `.github/renovate.json`, and the built-in-ignore paragraph from `actions/lint-workflows/README.md`." + ] } ], "prConcurrentLimit": 0, diff --git a/README.md b/README.md index 6198722..4a6ddd6 100644 --- a/README.md +++ b/README.md @@ -67,11 +67,14 @@ without stopping anything — so a workflow linter added that way reports proble to no one. A repository silences a rule by committing `.github/actionlint.yaml`, which -actionlint discovers by itself. That file is also the escape hatch for -actionlint being stale: it carries its own copy of the valid permission scopes, -so a scope GitHub has added since the pinned release reads as an error on a -workflow that is perfectly correct. A consumer uploading code coverage needs -exactly that for `code-quality`, which `actions/upload-code-coverage` requires. +actionlint discovers by itself. + +One stale-list case is already handled here and needs nothing from consumers. +actionlint carries its own copy of the valid permission scopes, so a scope +GitHub has added since the pinned release reads as an error on a workflow that +is perfectly correct — `code-quality`, which `actions/upload-code-coverage` +requires. The action passes an `-ignore` for exactly that message, and retires +it when the pin moves. ## `_publish-npm.yaml` diff --git a/actions/lint-workflows/README.md b/actions/lint-workflows/README.md index 9b6ad63..d0e6755 100644 --- a/actions/lint-workflows/README.md +++ b/actions/lint-workflows/README.md @@ -34,19 +34,34 @@ discovers it on its own, which is why no input here exposes it: ```yaml paths: - .github/workflows/test.yaml: + .github/workflows/deploy.yaml: ignore: - - 'unknown permission scope "code-quality"' + - 'shellcheck reported issue in this script: SC2086' ``` Scope the suppression to the file that needs it rather than passing a global `-ignore`, so the rule keeps running everywhere else. -That example is the real one. actionlint carries its own copy of the valid -permission scopes, so a scope GitHub has added since the pinned release — -`code-quality`, which `actions/upload-code-coverage` requires — is reported as -unknown until the bump lands. The workflow is correct and the linter is stale, -which is the case this file is for. +## The built-in ignore + +This action already passes one `-ignore` of its own, for +`unknown permission scope "code-quality"`. Consumers do not need to suppress +that themselves. + +actionlint carries its own copy of the valid permission scopes, and the pinned +release predates `code-quality`, which `actions/upload-code-coverage` requires. +A workflow granting it is correct and the linter is stale. That is a fact about +the pin rather than about any one repository, so the correction lives here, +beside the pin, instead of in a `.github/actionlint.yaml` in every consumer that +uploads coverage. + +It matches the scope name exactly, so a misspelt scope — `code-qualty` — still +fails. + +It retires with the pin. When `version` is bumped to a release whose permission +table includes `code-quality`, the flag goes: check `rule_permissions.go` at +that tag, or lint a workflow granting the scope with that release and watch it +pass. Renovate puts that check in the body of the bump pull request. ## Where it runs diff --git a/actions/lint-workflows/action.yaml b/actions/lint-workflows/action.yaml index 6a479e5..4e784ce 100644 --- a/actions/lint-workflows/action.yaml +++ b/actions/lint-workflows/action.yaml @@ -21,13 +21,31 @@ runs: # actionlint reads .github/workflows from the working directory, so there # has to be one already. # - # A repository silences a rule by committing .github/actionlint.yaml, which - # actionlint discovers on its own — so no input here exposes it. That file - # is also how a stale built-in list gets worked around: actionlint carries - # its own copy of the valid permission scopes, and a scope GitHub has added - # since the pinned release is reported as unknown until the bump lands. + # A repository silences a rule of its own by committing + # .github/actionlint.yaml, which actionlint discovers on its own — so no + # input here exposes it. - name: Run actionlint uses: raven-actions/actionlint@v2.2.0 with: fail-on-error: true + # A correction to a stale table, not a policy choice. actionlint + # carries its own copy of the valid permission scopes, and the pinned + # release predates code-quality, which actions/upload-code-coverage + # requires — so it reports a correct workflow as wrong. That is not + # specific to any one repository, which is why the suppression lives + # beside the pin that causes it rather than in a .github/actionlint.yaml + # in each consumer. The scope name is matched exactly, so a misspelt + # one still fails. + # + # Writing the spaces as \s is load-bearing. The wrapper splits this + # input on whitespace with a regex that keeps whatever quotes it finds, + # so a quoted argument reaches actionlint still carrying its quotes and + # matches nothing. -ignore takes an RE2 regex, which is what makes \s + # available to spell the message without a space in it. + # + # Retire this when version is bumped to a release whose permission + # table includes code-quality: check rule_permissions.go at that tag, + # or lint a workflow granting the scope with that release and watch it + # pass. + flags: '-ignore unknown\spermission\sscope\s"code-quality"' version: ${{ inputs.version }}