Skip to content

GitHub Action

tmatens/compose-lint runs the linter in a job and, by default, uploads its SARIF to GitHub Code Scanning so findings appear as PR annotations and Security-tab alerts. The copy-paste workflow lives in the README, where release automation keeps the uses: line pinned to the current release; this page is the reference behind it.

Inputs

Input Default What it does
files "" Space-separated list of Compose files to lint. Empty means CLI discovery in the checkout root.
pattern "" Glob to find Compose files, e.g. '**/docker-compose*.yml'.
fail-on high Minimum severity that fails the job: low, medium, high, critical.
config "" Path to a .compose-lint.yml.
skip-suppressed false Hide suppressed findings from the output.
quiet false Text output: one line per finding.
verbose false Text output: repeat the fix block and reference on every finding.
sarif-file "" Path to write SARIF to. Setting it enables the Code Scanning upload.
upload-sarif true Upload the written SARIF to Code Scanning. "false" only writes the file.
version "" compose-lint version to install. Empty means the version this action was released with, so a SHA-pinned uses: is a reproducible check; latest tracks PyPI.
allow-no-files false Succeed when no Compose files are found. Off by default because a pattern that matches nothing is a misconfiguration, and the CLI itself exits 2 in that case.

One output, sarif-written, is "true" when the file requested via sarif-file was written and non-empty, and empty otherwise. Use it to gate a later upload-artifact step.

Pin the SHA, or float on v1

The README's uses: line pins a commit SHA with the version in a trailing comment. That is the supply-chain-rigorous form: it is what OpenSSF Scorecard grades for, and Renovate or Dependabot keep the pin fresh. From 1.0 a floating major tag also exists, uses: tmatens/compose-lint@v1, for setups that prefer automatic updates. It is a mutable pointer moved by the release pipeline, deliberately not part of the signed-tag guarantee that release tags carry — the same trade this linter itself prices in CL-0004 and CL-0019. Pick the form that matches your threat model; the SHA pin is the recommended default.

Why the permissions: blocks are part of the recipe

Without them the job inherits the repository default, which on many repositories is still read-write for every scope. A linting job that needs only contents: read and security-events: write would then run holding a token that can push code and edit releases. Denying everything at the workflow level (permissions: {}) and granting the two scopes the job actually uses keeps a compromised dependency in this job from reaching anything else.

Drop security-events: write if you are not uploading SARIF.

SARIF without the Code Scanning upload

To write the SARIF file and skip the upload — for example to attach it as a build artifact, or on a runner without Code Scanning — set upload-sarif: "false" alongside sarif-file, and drop the security-events: write scope:

      - uses: tmatens/compose-lint@<sha> # vX.Y.Z
        id: lint
        with:
          sarif-file: results.sarif
          upload-sarif: "false"
      - uses: actions/upload-artifact@<sha> # vX.Y.Z
        if: steps.lint.outputs.sarif-written == 'true'
        with:
          name: compose-lint-sarif
          path: results.sarif

Without the action

The action is a thin wrapper over the PyPI package. Installing it directly works in any job that has Python:

      - uses: actions/setup-python@v6
        with:
          python-version: "3.13"
      - run: pip install compose-lint
      - run: compose-lint --format sarif docker-compose.yml > results.sarif

Forgejo Actions runs the same action with two practical differences; see the Forgejo guide.