MegaLinter: All-in-One Linting and Code Quality in CI

MegaLinter: All-in-One Linting and Code Quality in CI

MegaLinter is a ready-to-use linting orchestrator that runs 100+ linters across all major languages and file types in a single Docker container. Instead of configuring Flake8, ESLint, hadolint, ShellCheck, markdownlint, yamllint, and a dozen others separately, MegaLinter runs all of them with one GitHub Actions workflow. It produces a unified report and only reports errors — not the "I ran successfully" noise.

Why MegaLinter Instead of Individual Linters

Setting up individual linters for each language in a polyglot repository is painful:

  • Each linter has its own config format
  • Each needs its own CI step
  • Report formats differ — some produce JSON, some text, some SARIF
  • Version management for each tool adds maintenance overhead

MegaLinter provides:

  • One Docker image with all linters pre-installed
  • Unified configuration in one .mega-linter.yml file
  • Consistent SARIF output for all findings
  • GitHub PR comments with a summary table
  • Auto-fix mode that commits fixes directly

Quick Start: GitHub Actions

The fastest way to enable MegaLinter:

# .github/workflows/mega-linter.yml
name: MegaLinter
on:
  push:
    branches: [main, develop]
  pull_request:

jobs:
  megalinter:
    name: MegaLinter
    runs-on: ubuntu-latest
    permissions:
      contents: write
      issues: write
      pull-requests: write
    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - name: MegaLinter
        uses: oxsecurity/megalinter@v8
        env:
          VALIDATE_ALL_CODEBASE: false  # Only lint changed files on PRs
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

That's it. MegaLinter detects all languages in your repository and runs the appropriate linters.

Configuration File

Create .mega-linter.yml in your repository root to customize behavior:

# .mega-linter.yml

# Linters to enable (empty = all applicable linters)
ENABLE_LINTERS:
  - PYTHON_RUFF
  - PYTHON_MYPY
  - JAVASCRIPT_ES
  - TYPESCRIPT_ES
  - DOCKERFILE_HADOLINT
  - BASH_SHELLCHECK
  - YAML_YAMLLINT
  - JSON_JSONLINT
  - MARKDOWN_MARKDOWNLINT
  - TERRAFORM_TFLINT
  - GO_GOLANGCI_LINT
  - DOCKERFILE_HADOLINT

# Linters to disable selectively
DISABLE_LINTERS:
  - SPELL_CSPELL  # Spell checking produces too many false positives

# File patterns to exclude
FILTER_REGEX_EXCLUDE: "(vendor/|node_modules/|.terraform/|dist/)"

# Only lint changed files in PRs (faster)
VALIDATE_ALL_CODEBASE: false

# Report format
REPORTERS:
  - GitHub
  - SARIF

# Apply auto-fixes and commit them
APPLY_FIXES: all
APPLY_FIXES_EVENT: push
APPLY_FIXES_COMMIT_MSG: "style: apply MegaLinter auto-fixes"

# Severity threshold
FAIL_IF_ERRORS: true

Language-Specific Configuration

MegaLinter picks up existing linter config files (.eslintrc, pyproject.toml, etc.). Override per-linter settings in .mega-linter.yml:

# Python — pass args to Ruff
PYTHON_RUFF_ARGUMENTS: "--config pyproject.toml"

# JavaScript — use existing eslint config
JAVASCRIPT_ES_CONFIG_FILE: ".eslintrc.json"

# Markdown — allow long lines in code blocks
MARKDOWN_MARKDOWNLINT_RULES_PATH: .markdownlint.json

# Terraform — additional args
TERRAFORM_TFLINT_ARGUMENTS: "--config .tflint.hcl"

# ShellCheck severity
BASH_SHELLCHECK_ARGUMENTS: "--severity warning"

Flavor Profiles: Smaller Images

MegaLinter's full image is large. Flavor images include only linters for a specific stack:

Flavor Languages
python Python, JSON, YAML, Markdown, Shell
javascript JS, TS, JSON, YAML, Markdown
go Go, JSON, YAML, Shell
java Java, JSON, YAML, XML
terraform Terraform, JSON, YAML, Shell
dotnet C#, JSON, YAML

Use a flavor for faster CI:

- name: MegaLinter
  uses: oxsecurity/megalinter/flavors/python@v8
  env:
    VALIDATE_ALL_CODEBASE: false
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Auto-Fix Configuration

MegaLinter can commit auto-fixes directly to the PR branch:

# In .mega-linter.yml
APPLY_FIXES: all         # or list specific linters: "PYTHON_RUFF,MARKDOWN_MARKDOWNLINT"
APPLY_FIXES_EVENT: push  # commit on push
APPLY_FIXES_COMMIT_MSG: "style: [MegaLinter] apply auto-fixes"

# In .github/workflows/mega-linter.yml
permissions:
  contents: write  # Required for auto-fix commits

Auto-fixes available in MegaLinter include:

  • Ruff (Python formatting and fixable lint errors)
  • Black (Python formatting)
  • isort (Python import sorting)
  • ESLint (JavaScript/TypeScript auto-fixable rules)
  • Prettier (JS/TS/JSON/YAML/Markdown formatting)
  • gofmt (Go formatting)
  • terraform fmt (Terraform formatting)

Understanding the MegaLinter Report

MegaLinter produces a report table in PR comments:

| Linter | Files | Fixed | Errors |
|--------|-------|-------|--------|
| PYTHON_RUFF | 45 | 12 | 0 |
| PYTHON_MYPY | 45 | 0 | 3 |
| DOCKERFILE_HADOLINT | 3 | 0 | 1 |
| BASH_SHELLCHECK | 8 | 0 | 0 |

Files with errors block the PR (when FAIL_IF_ERRORS: true). Fixed counts show how many auto-fixes were applied.

Detailed logs are available in the CI artifacts:

  • megalinter-reports/ — per-linter logs and error details
  • megalinter-reports/linters-reports/ — individual linter output
  • megalinter-reports/megalinter-report.sarif — unified SARIF for security integration

SARIF Integration with GitHub Security

Upload SARIF to GitHub's Security tab:

- name: MegaLinter
  id: ml
  uses: oxsecurity/megalinter@v8
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload SARIF
  uses: github/codeql-action/upload-sarif@v3
  if: always()
  with:
    sarif_file: megalinter-reports/megalinter-report.sarif

Local MegaLinter Run

Test MegaLinter locally before pushing:

# Run with Docker
docker run -v <span class="hljs-string">"/path/to/repo:/tmp/lint" \
  --<span class="hljs-built_in">env VALIDATE_ALL_CODEBASE=<span class="hljs-literal">true \
  oxsecurity/megalinter:v8

<span class="hljs-comment"># Use the megalinter-runner script
npx mega-linter-runner --flavor python --fix

<span class="hljs-comment"># Or via the megalinter CLI
npm install -g mega-linter-runner
mega-linter-runner --fix

Customizing Which Files Are Linted

# .mega-linter.yml

# Exclude entire directories
FILTER_REGEX_EXCLUDE: "(^vendor/|^node_modules/|^dist/|^.terraform/|generated/)"

# Include only specific paths for a linter
PYTHON_RUFF_FILTER_REGEX_INCLUDE: "^src/"

# Override file extensions
YAML_YAMLLINT_FILE_EXTENSIONS: [".yaml", ".yml"]

Comparing MegaLinter vs Individual Linter Setup

Aspect MegaLinter Individual Linters
Setup time 10 minutes Hours to days
Maintenance Update one image tag Update each tool separately
Coverage 100+ languages out of box Only configured languages
CI time ~3-5 min (flavor) / ~8 min (full) Similar per linter, accumulated
Flexibility Moderate Full control
Auto-fix Built-in across all linters Per-tool setup

MegaLinter is best for polyglot repos, teams without a dedicated DevEx function, and projects that want immediate broad coverage. Individual linter setup is better when you need precise control over each tool's behavior.

Connecting Linting to Quality Assurance

MegaLinter gates code quality at the static analysis level. It ensures your code is consistently formatted, free from common bugs, and follows language-specific best practices. But it doesn't verify behavior.

HelpMeTest adds the behavioral layer — verifying that your quality-gated code actually works correctly in production. Combine MegaLinter for code quality enforcement with HelpMeTest for behavioral monitoring, and you cover the full quality spectrum from commit to production.

Summary

  • MegaLinter runs 100+ linters in one Docker container — no per-tool configuration needed
  • VALIDATE_ALL_CODEBASE: false lints only changed files in PRs — fast feedback on large repos
  • Flavor images (python, javascript, go, etc.) are significantly smaller than the full image
  • APPLY_FIXES: all + write permissions auto-commits fixes to PR branches
  • SARIF output integrates all linting findings into GitHub Security tab
  • Per-linter config files (.eslintrc, pyproject.toml) are automatically picked up

Read more