Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .automation/generated/linters_matrix.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"clojure_clj_kondo",
"clojure_cljstyle",
"cloudformation_cfn_lint",
"coffee_coffeelint",
"copypaste_jscpd",
"cpp_cppcheck",
"cpp_cpplint",
Expand Down Expand Up @@ -145,7 +144,6 @@
"c_cpplint",
"c_clang_format",
"cloudformation_cfn_lint",
"coffee_coffeelint",
"cpp_cppcheck",
"cpp_cpplint",
"cpp_clang_format",
Expand Down
3 changes: 0 additions & 3 deletions .automation/test/sql/good/.wireit/poison.sql

This file was deleted.

1 change: 0 additions & 1 deletion .claude/agents/descriptor-expert.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ Fill in as many as applicable when creating or reviewing a descriptor:
| `cli_lint_mode_project_exclude_seed_values` | Defaults re-included when the flag REPLACES the tool's built-in defaults (bandit `-x`, devskim `-g`) |
| `cli_lint_mode_project_exclude_config_key` | Dotted key of the resolved config list the flag REPLACES (checkov `skip-path`, trivy `scan.skip-dirs`) — entries re-emitted first |
| `cli_lint_mode_project_exclude_ignore_file_arg_name` | Flag receiving a generated ignore file (prettier/markdownlint `--ignore-path`); with `_seed_files` (workspace files merged in, first existing wins), `_pass_existing` (files re-passed when the flag replaces their discovery) |
| `cli_lint_mode_project_exclude_workspace_file_name` | Generated ignore file written at workspace root only if absent, removed after run (sqlfluff, coffeelint, secretlint) — for tools that only discover ignore files inside the repo |
| `ignore_file_name` | Ignore file (e.g., `.eslintignore`) |
| `cli_lint_ignore_arg_name` | Ignore file argument |
| `cli_version_arg_name` | Version arg if not `--version` |
Expand Down
7 changes: 3 additions & 4 deletions .claude/rules/descriptors.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ cli_lint_mode_project_exclude_seed_values: ["**/.git/**"] # defaults to re-in
cli_lint_mode_project_exclude_ignore_file_arg_name: "--ignore-path" # argument receiving the generated ignore file
cli_lint_mode_project_exclude_ignore_file_seed_files: [".toolignore"] # workspace files merged into it (first existing wins)
cli_lint_mode_project_exclude_ignore_file_pass_existing: [".gitignore"] # files re-passed via the same arg when it replaces their discovery
# When the tool only discovers ignore files inside the analyzed repository:
cli_lint_mode_project_exclude_workspace_file_name: ".toolignore" # written at workspace root only if absent, removed after the run
```

One more property preserves config-file lists that the CLI flag would replace:
Expand All @@ -69,12 +67,13 @@ One more property preserves config-file lists that the CLI flag would replace:
cli_lint_mode_project_exclude_config_key: "scan.skip-dirs" # dotted key path of the resolved config list the flag REPLACES: its entries are re-emitted first
```

**Never write inside the analyzed sources.** Every file MegaLinter generates goes to `REPORT_OUTPUT_FOLDER` (`write_report_generated_file`). A file created then deleted at the workspace root during the run crashes the project-mode linters walking the tree at the same moment (trivy aborts with `walk dir error … no such file or directory`), and the failure is timing-dependent, so it surfaces as a random red build. A tool that can only read exclusions from a file it discovers itself inside the repository gets **no** exclusion forwarding (coffeelint, and sqlfluff whose `ignore_paths` is read only from a config file located between the working directory and the analyzed path) — say so in `disabled_reason` or in `linter_text`, do not write the file.

**Choosing the mechanism** (exactly ONE per linter — the base class applies all declared mechanisms, so declaring two forwards twice):

1. Native CLI exclusion flag → `..._exclude_arg_name` (+ value template / separator / seed values / config key).
2. Flag taking an ignore FILE → `..._exclude_ignore_file_*` (generated in the report folder, merged with seeds).
3. Tool only discovers ignore files inside the repo → `..._exclude_workspace_file_name` (temp file at workspace root, only if absent, auto-removed).
4. Anything needing a generated/merged CONFIG (yamllint extends, rubocop inherit_from, phpstan includes, TOML/PHP configs…) → override `manage_excluded_directories_config(cmd)` in the linter class. It is called only in project mode when forwarding is active (single gate: `is_project_exclude_forwarding_active`, overridable via `FORWARD_EXCLUDED_DIRECTORIES` / `<LINTER_KEY>_FORWARD_EXCLUDED_DIRECTORIES`). Use the base helpers `find_cli_argument_value_index`, `replace_or_append_cli_argument`, `write_report_generated_file`, `write_workspace_generated_file`, `read_workspace_file_lines`, and call `log_project_exclude_forwarding` so the action shows in the console log.
3. Anything needing a generated/merged CONFIG (yamllint extends, rubocop inherit_from, phpstan includes, TOML/PHP configs…) → override `manage_excluded_directories_config(cmd)` in the linter class. It is called only in project mode when forwarding is active (single gate: `is_project_exclude_forwarding_active`, overridable via `FORWARD_EXCLUDED_DIRECTORIES` / `<LINTER_KEY>_FORWARD_EXCLUDED_DIRECTORIES`). Use the base helpers `find_cli_argument_value_index`, `replace_or_append_cli_argument`, `write_report_generated_file`, `read_workspace_file_lines`, and call `log_project_exclude_forwarding` so the action shows in the console log.

Rules and known traps (each was hit for real — verify against official docs/source before filling anything):

Expand Down
1 change: 1 addition & 0 deletions .claude/rules/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ globs: ["megalinter/tests/**/*.py", ".automation/test/**"]
- `.wireit` is a default excluded directory that almost no tool skips natively, so `test_success_project_lint_mode` passes only if the forwarding actually excludes it — the fixture is a regression test for the forwarding, not for the linter rules
- When adding forwarding to a linter, add its poison fixture; when a project success test fails on a file under `.wireit/`, the forwarding is broken, not the fixture
- Constraints: only in folders with a `good/` subfolder, and only when every project-capable linter sharing the test folder has forwarding (otherwise the poison legitimately fails the non-forwarding tenant)
- No poison fixture for a linter that can not receive exclusions at all (coffeelint, sqlfluff): its `good/` folder must stay clean, and the limitation is documented in the descriptor `linter_text` instead
- Poisons may be vacuous for tools that natively skip the directory (dot-folder-skipping globs, verified-only secret scanners): they never false-fail, so that is acceptable

## Running Tests
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/add-linter/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ Add the linter entry with **as many properties as possible**. Even though the JS
- **Excluded directories forwarding** — when `project` is a supported lint mode, MegaLinter must forward `EXCLUDED_DIRECTORIES` to the tool or it will scan `node_modules`/build caches raw. Pick exactly ONE mechanism (see `.claude/rules/descriptors.md` → "Project Lint Mode: Forwarding Excluded Directories" for full semantics and known traps):
- native CLI flag → `cli_lint_mode_project_exclude_arg_name` (+ `_arg_value` `{{DIR}}`/`{{WORKSPACE}}` template, `_separator` if a repeated flag overrides, `_seed_values` if the flag replaces the tool's built-in defaults, `_config_key` if it replaces a list in the tool's config file)
- flag taking an ignore file → `cli_lint_mode_project_exclude_ignore_file_arg_name` (+ `_seed_files`, `_pass_existing`)
- ignore file only discovered inside the repo → `cli_lint_mode_project_exclude_workspace_file_name`
- generated/merged config needed → `manage_excluded_directories_config()` override in the linter class
- tool that can only read an ignore file it discovers itself inside the repository → **no forwarding**: MegaLinter never writes in the analyzed sources, document the limitation in `linter_text`

**Research the official docs first**: exact flag, value syntax (path/glob/regex, anchoring), repeatability, and whether it replaces config/built-in defaults — a wrong choice silently drops exclusions or clobbers user configuration.
- **Poison fixture** — after declaring forwarding, add a deliberately failing file in `.automation/test/<test_folder>/good/.wireit/` so `test_success_project_lint_mode` guards the forwarding against regressions (only if all project-capable linters sharing the folder have forwarding)
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Note: Can be used with `oxsecurity/megalinter@beta` in your GitHub Action mega-l
- `EXCLUDED_DIRECTORIES` are forwarded in project lint mode through a generated configuration extending the workspace one

- Disabled linters
- **COFFEE_COFFEELINT** is disabled: CoffeeScript tooling is discontinued, and coffeelint can not receive `EXCLUDED_DIRECTORIES` in project lint mode (it has no exclusion option and reads `.coffeelintignore` only from its working directory). The linter will be removed in a future version

- Re-enabled linters
- **[spectral](https://megalinter.io/latest/descriptors/api_spectral/)** is back as **API_SPECTRAL**, together with the **API** descriptor, to lint your **OpenAPI**, **AsyncAPI** and **Arazzo** specifications ([#8717](https://github.com/oxsecurity/megalinter/issues/8717))
Expand All @@ -32,12 +33,16 @@ Note: Can be used with `oxsecurity/megalinter@beta` in your GitHub Action mega-l
- Media

- Linters enhancements
- **CLOJURE_CLJSTYLE** now forwards `EXCLUDED_DIRECTORIES` through its native repeatable `--ignore` argument, instead of a temporary `.cljstyle` written in your repository. Exclusions are now also applied when your repository already has a `.cljstyle` config, whose own ignore patterns are preserved
- **SQL_SQLFLUFF** does not receive `EXCLUDED_DIRECTORIES` in `project` lint mode anymore: sqlfluff reads path exclusions only from a `.sqlfluffignore`, `.sqlfluff` or `pyproject.toml` located inside the analyzed sources, where MegaLinter used to write a temporary file. List the directories to skip in your own `.sqlfluffignore`, or keep the default `list_of_files` lint mode where MegaLinter filters the files itself
- **SARIF output** is now available for 13 more linters: **zizmor**, **bicep_linter**, **cppcheck**, **clj-kondo**, **roslynator**, **htmlhint**, **protolint**, **sqlfluff**, **swiftlint**, **osv-scanner**, **trufflehog**, **jscpd** and **lintr**. Enable it the same way as any other SARIF-capable linter, with `SARIF_REPORTER: true` (optionally scoped with `SARIF_REPORTER_LINTERS`)
- The 4 **Salesforce Code Analyzer** engines (`SALESFORCE_CODE_ANALYZER_APEX`, `_AURA`, `_LWC`, `_FLOW`) also gained SARIF output: their report switches from CSV to SARIF automatically when SARIF reporting is requested
- `csharp_roslynator` is bumped from 0.12.0 to **0.13.0**, the first release including its SARIF output support
- `clj-kondo`'s upstream SARIF output currently nests the `region` property one level too deep, which may affect line/column display in strict SARIF consumers (clj-kondo/clj-kondo#2345)

- Fixes
- Fixed **random crashes of project-mode linters** (`REPOSITORY_TRIVY`, `REPOSITORY_GRYPE`, `REPOSITORY_SYFT`…) caused by MegaLinter writing temporary ignore files inside the analyzed sources: a file appearing then disappearing while another linter walked the repository aborted its scan (`walk dir error: ... no such file or directory`). **MegaLinter now writes only in REPORT_OUTPUT_FOLDER**, never in your sources
- **REPORT_OUTPUT_FOLDER** is now always excluded from what linters analyze, even when you override `EXCLUDED_DIRECTORIES`, and even when the folder does not exist yet when a linter starts
- The **API reporter** variables (`API_REPORTER`, `API_REPORTER_URL`…) are not flagged as **deprecated** anymore in the configuration JSON schema: they were collateral damage of the removal of the `API` descriptor in v10.0.0, and IDEs displayed them as obsolete

- Reporters
Expand All @@ -64,6 +69,7 @@ Note: Can be used with `oxsecurity/megalinter@beta` in your GitHub Action mega-l
- **megalinter-setup** in upgrade mode now also updates the **installed skills and sub-agents** (`npx skills update`), so the guidance you run matches the MegaLinter version you just upgraded to

- Dev
- Retired the `cli_lint_mode_project_exclude_workspace_file_name` descriptor property and the `write_workspace_generated_file()` helper, and removed the property from the descriptor JSON schema so a future descriptor can not silently reintroduce a write inside the analyzed sources. Exclusion forwarding now offers three mechanisms only: native CLI flag, generated ignore file in the report folder, generated config via `manage_excluded_directories_config()`
- **Deprecation flags of removed linters are now reversible** in the configuration JSON schema: `build.py` clears the `deprecated` flag and the `(deprecated)` title prefix of variables whose linter or descriptor is back, instead of only ever adding them
- **spectral is installed in its own `node_modules` tree** (`/node-deps-spectral`) instead of the shared `/node-deps` one, which is what made it crash: `@prantlf/jsonlint` pins `ajv` to exactly `8.17.1` and so owns the hoisted root copy, while `@stoplight/spectral-core` requires `ajv >= 8.18.0` and gets a nested one, so its hoisted `ajv-errors` bound to the other `ajv` instance and ajv generated invalid JavaScript (`SyntaxError: Unexpected token ':'` at `new Function`). Any npm linter sharing the tree with an exact-pinned transitive dependency can hit the same trap
- **6 Python dependencies removed** from the MegaLinter runtime, replaced by standard library equivalents: `commentjson`, `terminaltables` and `multiprocessing_logging` (unmaintained), plus `termcolor`, `regex` and the obsolete `importlib-metadata` backport
Expand Down
4 changes: 0 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,6 @@ ARG CLJ_KONDO_VERSION=2025.01.16
ARG CLJ_STYLE_VERSION=0.17.642
# renovate: datasource=pypi depName=cfn-lint
ARG PIP_CFN_LINT_VERSION=1.54.0
# renovate: datasource=npm depName=@coffeelint/cli
ARG NPM_COFFEELINT_CLI_VERSION=5.2.11
# renovate: datasource=npm depName=jscpd
ARG NPM_JSCPD_VERSION=5.0.14
# renovate: datasource=nuget depName=csharpier
Expand Down Expand Up @@ -731,7 +729,6 @@ RUN npm config set prefix /usr/local \
&& npm --no-cache install --ignore-scripts --omit=dev \
@salesforce/cli@${NPM_SALESFORCE_CLI_VERSION} \
typescript@${NPM_TYPESCRIPT_VERSION} \
@coffeelint/cli@${NPM_COFFEELINT_CLI_VERSION} \
jscpd@${NPM_JSCPD_VERSION} \
stylelint@${NPM_STYLELINT_VERSION} \
stylelint-config-standard@${NPM_STYLELINT_CONFIG_STANDARD_VERSION} \
Expand Down Expand Up @@ -946,7 +943,6 @@ esac \
&& chmod +x install-cljstyle \
&& ./install-cljstyle --static --version "$CLJ_STYLE_VERSION" \
# cfn-lint installation
# coffeelint installation
# jscpd installation
# cpplint installation
# csharpier installation
Expand Down
4 changes: 0 additions & 4 deletions flavors/dotnetweb/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,6 @@ ARG BICEP_EXE='bicep'
ARG BICEP_DIR='/usr/local/bin'
# renovate: datasource=pypi depName=cpplint
ARG PIP_CPPLINT_VERSION=2.0.2
# renovate: datasource=npm depName=@coffeelint/cli
ARG NPM_COFFEELINT_CLI_VERSION=5.2.11
# renovate: datasource=npm depName=jscpd
ARG NPM_JSCPD_VERSION=5.0.14
# renovate: datasource=nuget depName=csharpier
Expand Down Expand Up @@ -462,7 +460,6 @@ WORKDIR /node-deps
RUN npm config set prefix /usr/local \
&& npm --no-cache install --ignore-scripts --omit=dev \
typescript@${NPM_TYPESCRIPT_VERSION} \
@coffeelint/cli@${NPM_COFFEELINT_CLI_VERSION} \
jscpd@${NPM_JSCPD_VERSION} \
stylelint@${NPM_STYLELINT_VERSION} \
stylelint-config-standard@${NPM_STYLELINT_CONFIG_STANDARD_VERSION} \
Expand Down Expand Up @@ -596,7 +593,6 @@ esac \
&& chmod +x "${BICEP_EXE}" \
&& mv "${BICEP_EXE}" "${BICEP_DIR}" \
# cpplint installation
# coffeelint installation
# jscpd installation
# cpplint installation
# csharpier installation
Expand Down
1 change: 0 additions & 1 deletion flavors/dotnetweb/flavor.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
"BICEP_BICEP_LINTER",
"C_CPPCHECK",
"C_CPPLINT",
"COFFEE_COFFEELINT",
"COPYPASTE_JSCPD",
"CPP_CPPCHECK",
"CPP_CPPLINT",
Expand Down
4 changes: 0 additions & 4 deletions flavors/javascript/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,6 @@ ARG NPM_TYPESCRIPT_VERSION=6.0.3
ARG PIP_ANSIBLE_LINT_VERSION=26.6.0
# renovate: datasource=npm depName=@stoplight/spectral-cli
ARG NPM_SPECTRAL_CLI_VERSION=6.16.3
# renovate: datasource=npm depName=@coffeelint/cli
ARG NPM_COFFEELINT_CLI_VERSION=5.2.11
# renovate: datasource=npm depName=jscpd
ARG NPM_JSCPD_VERSION=5.0.14
# renovate: datasource=npm depName=stylelint
Expand Down Expand Up @@ -432,7 +430,6 @@ WORKDIR /node-deps
RUN npm config set prefix /usr/local \
&& npm --no-cache install --ignore-scripts --omit=dev \
typescript@${NPM_TYPESCRIPT_VERSION} \
@coffeelint/cli@${NPM_COFFEELINT_CLI_VERSION} \
jscpd@${NPM_JSCPD_VERSION} \
stylelint@${NPM_STYLELINT_VERSION} \
stylelint-config-standard@${NPM_STYLELINT_CONFIG_STANDARD_VERSION} \
Expand Down Expand Up @@ -520,7 +517,6 @@ RUN npm --no-cache install --ignore-scripts --omit=dev --prefix /node-deps-spect
# # COPY --link --from=shellcheck /bin/shellcheck /usr/bin/shellcheck
# shfmt installation
# Managed with COPY --link --from=shfmt /bin/shfmt /usr/bin/
# coffeelint installation
# jscpd installation
# stylelint installation
# biome installation
Expand Down
1 change: 0 additions & 1 deletion flavors/javascript/flavor.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"BASH_EXEC",
"BASH_SHELLCHECK",
"BASH_SHFMT",
"COFFEE_COFFEELINT",
"COPYPASTE_JSCPD",
"CSS_STYLELINT",
"CSS_BIOME",
Expand Down
Loading
Loading