Open-source project
wearerequired/lint-action avatar
wearerequired/lint-action

lint-action: one GitHub Action that runs your linters and can fix them

✨ GitHub Action for detecting and auto-fixing lint errors

611 stars141 forksJavaScriptMIT

At a glance

What is it?
The wearerequired/lint-action project wraps dozens of linters and formatters into a single GitHub Action, reports their findings as check runs and commit annotations, and can push automatic fixes back to your branch. The catch is in the permissions: the default GITHUB_TOKEN cannot rewrite workflow files, so the .github directory has to be excluded from your lint config.
Who is it for?
Adopt lint-action if you want one workflow step that covers several languages and want findings to land as inline annotations rather than raw log output. Do not expect it to install your linters, because the README is explicit that you are responsible for installing them in your CI environment, and do not enable auto-fix on workflow files, since the default GITHUB_TOKEN lacks the workflow permission.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the action actually does for you

The lint-action project provides a GitHub Action for detecting and auto-fixing lint errors, and the README lists three capabilities: it shows linting errors on GitHub commits and pull requests, it allows auto-fixing issues, and it supports many linters and formatters.

The first capability is the one that changes how a team reads CI. Instead of a wall of text in a log, the findings arrive as check runs and as commit annotations, which is what puts a marker next to the offending line in the diff view. There are screenshots in the repository for both, one for check runs on pull requests and one for the annotations themselves.

The second is the auto-fix. Since most of these tools can rewrite files, the action can push those changes back, which removes the most tedious part of a lint failure. That capability is also where the sharp edges are, because writing to a branch needs a token with the right permissions, and the default token does not have them for workflow files.

The third is breadth, and the supported list is long enough to be the reason to use this rather than wiring up each tool separately. C and C++ are covered by ClangFormat, and so are Objective-C and Objective-C++. C# and VB.NET use dotnet-format. CSS uses stylelint. Go has gofmt, staticcheck and golint, where golint is marked deprecated with a note that it has been frozen and deprecated by its authors. JavaScript has ESLint, Prettier and XO. PHP has PHP_CodeSniffer. Python has the widest set, Autopep8, Black, Flake8, isort, Mypy, oitnb, Pylint and Ruff. Ruby has ERB Lint, which requires erb_lint v0.7.0 or later, RuboCop and Standard Ruby. Rust has clippy and rustfmt. Swift has swift-format, marked official, plus SwiftFormat and SwiftLint. TypeScript has tsc.

All linters are disabled by default, and you enable one by setting the option with its name to true.

The project is MIT licensed, the last push was on 2026-10-19, and the releases are v3.0.2 from 2026-07-20, v3.0.1 and v3.0.0 both from 2026-07-19. The 3.0 line is recent, so the major version is where a breaking change would live.

The workflow you paste in, and the two permissions it needs

The usage section asks you to create a workflow in your project, for example at .github/workflows/lint.yml, and gives the format:

yml
name: Lint

on:
  # Trigger the workflow on push or pull request,
  # but only for the main branch
  push:
    branches:
      - main
  # Replace pull_request with pull_request_target if you
  # plan to use this action with forks, see the Limitations section
  pull_request:
    branches:
      - main

# Down scope as necessary via https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
permissions:
  checks: write
  contents: write

jobs:
  run-linters:
    name: Run linters
    runs-on: ubuntu-latest

    steps:
      - name: Check out Git repository
        uses: actions/checkout@v7

      # Install your linters here

      - name: Run linters
        uses: wearerequired/lint-action@v3
        with:
          # Enable your linters here

Two things in that file are worth understanding rather than copying blind.

The first is the permissions block. checks: write is what allows the action to create check runs, and contents: write is what allows it to push fixes. The comment links to the GitHub documentation on modifying the permissions for the github_token and says to down scope as necessary, so the guidance is to grant exactly these two and no more.

The second is the pull_request trigger, and the comment above it. It says to replace pull_request with pull_request_target if you plan to use this action with forks, and points at the limitations section. That substitution is a well-known way to give a workflow access to secrets in fork pull requests, and it also changes the security model, because the workflow then runs with the base repository's token on code from an untrusted fork. The comment does not explain the trade-off, so treat it as a pointer to read rather than a recommendation.

The placeholder comments matter too. The step for installing your linters is empty and marked as such, and the with block has no enabled linters. The action does not install the tools for you, and the README says so in a sentence of its own: you are responsible for installing them in your CI environment.

A working JavaScript setup, and the exclusion it demands

The JavaScript example is the shortest complete configuration, and it shows the pattern of install then enable:

yml
name: Lint

on:
  # Trigger the workflow on push or pull request,
  # but only for the main branch
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  run-linters:
    name: Run linters
    runs-on: ubuntu-latest

    steps:
      - name: Check out Git repository
        uses: actions/checkout@v7

      - name: Set up Node.js
        uses: actions/setup-node@v7
        with:
          node-version: 24

      # ESLint and Prettier must be in `package.json`
      - name: Install Node.js dependencies
        run: npm ci

      - name: Run linters
        uses: wearerequired/lint-action@v3
        with:
          eslint: true
          prettier: true

The linters have to be in package.json for this to work, which is stated in a comment above the install step. The action runs the tools that are already there rather than fetching its own copies, and that is the difference between a consistent result and a version that drifts between runs.

The instruction that follows the example is the one that causes the most confusion when you first hit it. It says to make sure to exclude the .github directory in your ESLint and Prettier configs, because the default GITHUB_TOKEN cannot be used to update workflow files due to the missing workflow permission. The link points back at the limitations section.

Read that carefully as a permissions fact rather than a linting preference. The auto-fix wants to push a commit that changes a file, and if that file is a workflow file, the push is rejected because the token lacks the workflow permission. The symptom is a lint run that reports a formatting problem in your own workflow file and then cannot fix it, which looks like a bug in the action and is not.

The PHP example follows the same shape with a different setup action, using shivammathur/setup-php and the phpcs tool, and it shows the extra arguments option. The optional php_codesniffer_args is set to "-n", with a comment noting it ignores warnings, and the Composer variant of the same example adds the vendor bin directory to GITHUB_PATH so the tool is on the path for the action.

The fork limitation, and what it means for open source projects

The behaviour of actions like this one is currently limited in the context of forks, and the README puts that note near the top with a pointer to the limitations section.

The reason is structural rather than a bug. A pull request from a fork runs in the fork's context, and the token available there has restricted permissions by design. The action needs checks: write to create check runs and contents: write to push fixes, and neither is available in that context.

For a project that receives outside contributions, this has a direct effect on the workflow. The pull_request trigger runs for every pull request including ones from forks, so the lint job will run, but the write operations will fail. Switching the trigger to pull_request_target, as the comment in the workflow template suggests, changes the context so the token belongs to the base repository. That is the mechanism that makes the action work for forks, and it is also why the limitations section exists.

The security consequence of pull_request_target is well known and worth stating plainly, because the workaround in the template is a one-word change that is easy to apply without thinking. A workflow triggered by pull_request_target runs with access to repository secrets while processing code that came from an untrusted fork. The safe pattern is to check out the fork's code separately, or to run only the steps that do not need secrets, but the action itself needs to write to the repository, so the risk is in the configuration around it.

The other limitation named in the README, the missing workflow permission, is a different shape of problem. It is not about forks at all, it applies to every repository, and it is why the .github directory needs excluding from your linter configuration.

Both of these are the kind of detail that makes this action more than a copy-paste convenience. A user who reads only the first workflow example will hit the workflow permission problem on their own repository, probably in their first run.

How the action is built, and where to look for the internals

The package.json describes the implementation, and it is small enough to be worth reading.

The runtime dependencies are five: @actions/core for the GitHub Actions toolkit, command-exists to check whether a linter is on the path, glob, parse-diff to work out which files a pull request touches, and shescape for shell escaping. That set describes the mechanism rather than a general purpose framework. The action finds the linters you enabled, runs them, and interprets their output, and parse-diff is there so it can restrict work to the changed files.

The build is ncc build ./src/index.js, and the main field points at ./dist/index.js. The repository has a dist/ directory checked in, which is the convention for JavaScript GitHub Actions since the action has to run without an install step. The scripts cover test with jest, lint with eslint at --max-warnings 0, format with prettier over a broad extension list, and their fix variants.

The lint and format configuration is unusually strict for a project of this size. The eslint config extends a shared config and the recommended jsdoc rules, with jsdoc checking enabled for indentation, syntax, tag lines and required descriptions, and the no-unused-vars rule configured to ignore variables whose names begin with the character _. The import/no-unresolved rule has an explicit ignore entry for shescape, which is the usual way to handle a package that ships without types.

The other repository files are the usual set for a maintained action: an action.yml, a .github directory with the workflow definitions and the screenshots, CONTRIBUTING.md, CREDITS.md, a .nvmrc pinning the Node version, a .prettierignore and an .editorconfig. The test directory and the src directory hold the code and its tests.

For the reasons the action's behaviour in edge cases, the source under src/ is where the answer lives, and the limitations section referenced from the README is the place to read before you rely on the auto-fix in a repository that takes external contributions.

Editorial conclusion

Adopt lint-action if you want one workflow step that covers several languages and want findings to land as inline annotations rather than raw log output. Do not expect it to install your linters, because the README is explicit that you are responsible for installing them in your CI environment, and do not enable auto-fix on workflow files, since the default GITHUB_TOKEN lacks the workflow permission. Before you switch a repository over, exclude the .github directory from your linter configuration as the documentation instructs, and check whether your fork contribution workflow needs pull_request_target, which the workflow comments point to in the limitations section.

Frequently asked questions

Which linters does wearerequired/lint-action support?

ClangFormat for C, C++, Objective-C and Objective-C++, dotnet-format for C# and VB.NET, stylelint for CSS, gofmt, staticcheck and golint for Go, ESLint, Prettier and XO for JavaScript, PHP_CodeSniffer for PHP, Autopep8, Black, Flake8, isort, Mypy, oitnb, Pylint and Ruff for Python, ERB Lint, RuboCop and Standard Ruby for Ruby, clippy and rustfmt for Rust, swift-format, SwiftFormat and SwiftLint for Swift, and tsc for TypeScript.

How do I enable a linter in the lint-action workflow?

All linters are disabled by default. You set the option with the linter's name to true, for example eslint: true and prettier: true, in the with block of the action step.

Does lint-action install the linters for me?

No. The README states that the action does not install the linters for you and that you are responsible for installing them in your CI environment. The JavaScript example installs them from package.json with npm ci, and the PHP example uses a setup action with the phpcs tool or Composer.

Why do I need to exclude the .github directory from my lint config?

Because the default GITHUB_TOKEN cannot be used to update workflow files due to the missing workflow permission. Without the exclusion, the action reports a problem in a workflow file and then cannot push the fix for it.

What permissions does the lint-action workflow need?

checks: write so the action can create check runs, and contents: write so it can push automatic fixes. The workflow template comments point at the GitHub documentation on down scoping the github_token permissions.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. wearerequired/lint-action on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/wearerequired-lint-action.svg)](https://hysenlabs.com/projects/wearerequired-lint-action)

Was this useful?