Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable linter commands #4482

Merged
merged 10 commits into from
Jan 19, 2025
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Note: Can be used with `oxsecurity/megalinter@beta` in your GitHub Action mega-l
- Core
- PHP Linters use now the `bartlett/sarif-php-converters` first official release 1.0.0 to generate SARIF reports
- [Upgrade PHP engine from 8.3 to 8.4](https://github.com/oxsecurity/megalinter/issues/4351) and allow Psalm 5.26 to run on this context (by @llaville)
- Linters can specify in the pre/post commands with a `continue_if_failed` parameter whether the command is to be executed before the execution of the linters themselves (by @bdovaz in [#4482](https://github.com/oxsecurity/megalinter/pull/4482))
bdovaz marked this conversation as resolved.
Show resolved Hide resolved

- New linters
- Reactivate clj-style (Clojure formatter) since its bug is fixed
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,7 @@ PRE_COMMANDS:
- command: npm install eslint-plugin-whatever
cwd: root # Will be run at the root of MegaLinter docker image
secured_env: true # True by default, but if defined to false, no global variable will be hidden (for example if you need GITHUB_TOKEN)
run_before_linters: True # Will be run before the execution of the linters themselves, required for npm/pip commands that cannot be run in parallel
bdovaz marked this conversation as resolved.
Show resolved Hide resolved
- command: echo "pre-test command has been called"
cwd: workspace # Will be run at the root of the workspace (usually your repository root)
continue_if_failed: False # Will stop the process if command is failed (return code > 0)
Expand Down
6 changes: 3 additions & 3 deletions megalinter/Linter.py
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@ def update_active_if_file_found(self):
self.active_only_if_file_found.append(self.config_file_name)

# Processes the linter
def run(self):
def run(self, run_before_linters=None):
self.start_perf = perf_counter()

# Initialize linter reports
Expand All @@ -816,7 +816,7 @@ def run(self):
self.before_lint_files()

# Run commands defined in descriptor, or overridden by user in configuration
pre_post_factory.run_linter_pre_commands(self.master, self)
pre_post_factory.run_linter_pre_commands(self.master, self, run_before_linters)

# Lint each file one by one
if self.cli_lint_mode == "file":
Expand Down Expand Up @@ -871,7 +871,7 @@ def run(self):
os.remove(self.remote_ignore_file_to_delete)

# Run commands defined in descriptor, or overridden by user in configuration
pre_post_factory.run_linter_post_commands(self.master, self)
pre_post_factory.run_linter_post_commands(self.master, self, run_before_linters)

# Generate linter reports
self.elapsed_time_s = perf_counter() - self.start_perf
Expand Down
12 changes: 11 additions & 1 deletion megalinter/MegaLinter.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def run_linters(linters, request_id):
global REQUEST_CONFIG
config.set_config(request_id, REQUEST_CONFIG)
for linter in linters:
linter.run()
linter.run(run_before_linters=False)
return linters


Expand Down Expand Up @@ -244,7 +244,17 @@ def run(self):
config.get(self.request_id, "PARALLEL", "true") == "true"
and len(active_linters) > 1
):
for active_linter in active_linters:
pre_post_factory.run_linter_pre_commands(
active_linter.master, active_linter, run_before_linters=True
)

self.process_linters_parallel(active_linters, linters_do_fixes)

bdovaz marked this conversation as resolved.
Show resolved Hide resolved
for active_linter in active_linters:
pre_post_factory.run_linter_post_commands(
active_linter.master, active_linter, run_before_linters=True
)
else:
self.process_linters_serial(active_linters)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@
"title": "Command",
"type": "object"
},
"linter_command_info": {
"description": "Command information",
"$ref": "#/definitions/command_info",
"properties": {
"run_before_linters": {
"default": false,
"title": "Process command before running linters",
"type": "boolean"
}
},
"required": [],
"title": "Command",
"type": "object"
},
"enum_flavors": {
"enum": [
"all_flavors",
Expand Down Expand Up @@ -1232,7 +1246,7 @@
]
],
"items": {
"$ref": "#/definitions/command_info"
"$ref": "#/definitions/linter_command_info"
},
"title": "Linter Pre-run commands",
"type": "array"
Expand All @@ -1255,7 +1269,7 @@
]
],
"items": {
"$ref": "#/definitions/command_info"
"$ref": "#/definitions/linter_command_info"
},
"title": "Linter Pre-run commands",
"type": "array"
Expand Down
26 changes: 22 additions & 4 deletions megalinter/pre_post_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,37 @@ def run_descriptor_post_commands(mega_linter, descriptor_id):


# Commands to run before a linter (defined in descriptors)
def run_linter_pre_commands(mega_linter, linter):
def run_linter_pre_commands(mega_linter, linter, run_before_linters=None):
if linter.pre_commands is not None:
filtered_commands: list = []

if run_before_linters is None:
filtered_commands = linter.pre_commands
else:
for command_info in linter.pre_commands:
if command_info.get("run_before_linters", False) is run_before_linters:
filtered_commands += command_info

return run_commands(
linter.pre_commands, "[Pre][" + linter.name + "]", mega_linter, linter
filtered_commands, "[Pre][" + linter.name + "]", mega_linter, linter
)
return []


# Commands to run before a linter (defined in descriptors)
def run_linter_post_commands(mega_linter, linter):
def run_linter_post_commands(mega_linter, linter, run_before_linters=None):
if linter.post_commands is not None:
filtered_commands: list = []
bdovaz marked this conversation as resolved.
Show resolved Hide resolved

if run_before_linters is None:
filtered_commands = linter.post_commands
else:
for command_info in linter.post_commands:
if command_info.get("run_before_linters", False) is run_before_linters:
filtered_commands += command_info

return run_commands(
linter.post_commands, "[Post][" + linter.name + "]", mega_linter, linter
filtered_commands, "[Post][" + linter.name + "]", mega_linter, linter
)
return []

Expand Down
Loading