diff --git a/.mega-linter.yml b/.mega-linter.yml index ede8e0c55c1..34f51120add 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -33,7 +33,6 @@ JSON_REPORTER: true GITHUB_STATUS_REPORTER: false PLUGINS: - https://raw.githubusercontent.com/megalinter/megalinter/main/.automation/test/mega-linter-plugin-test/test.megalinter-descriptor.yml - - file://.automation/test/mega-linter-plugin-test/test.megalinter-descriptor.yml PRE_COMMANDS: - command: echo "This is MegaLinter PRE_COMMAND on own MegaLinter ! :)" cwd: "root" diff --git a/README.md b/README.md index f5f4eef06eb..bf9558cc4ee 100644 --- a/README.md +++ b/README.md @@ -882,25 +882,28 @@ _Note:_ IF you did not use `MegaLinter` as GitHub Action name, please read [GitH -## Plugins +# Plugins For security reasons, we try to embed in MegaLinter only linters that are widely adopted by open-source community. But any linter can be callable within MegaLinter thanks to the plugin mechanism ! -### Use plugins +## Use plugins -Just add plugin URLs in `PLUGINS` property of `.mega-linter.yml` +Add plugin URLs in `PLUGINS` property of `.mega-linter.yml`. URLs must either begin with "https://" or take the form of "file://\", where \ points to a valid plugin descriptor file. -#### Example +> Note: Both \ and the default mount directory (/tmp/lint/\) will be checked for a valid descriptor. + +### Example ```yaml PLUGINS: - https://raw.githubusercontent.com/megalinter/megalinter/main/.automation/test/mega-linter-plugin-test/test.megalinter-descriptor.yml - https://raw.githubusercontent.com/cookiejar/mega-linter-plugin-cookietemple/main/cookietemple.megalinter-descriptor.yml + - file://.automation/test/mega-linter-plugin-test/test.megalinter-descriptor.yml ``` -### Plugins Catalog +## Plugins Catalog * [jupyfmt](https://github.com/kpj/jupyfmt): The uncompromising Jupyter notebook formatter ([usage](https://github.com/kpj/jupyfmt#mega-linter-integration)) * [nitpick](https://github.com/andreoliwa/nitpick): Command-line tool and flake8 plugin to enforce the same settings across multiple language-independent projects. ([usage](https://github.com/andreoliwa/nitpick#run-as-a-megalinter-plugin)) @@ -908,17 +911,19 @@ PLUGINS: Submit a PR if you want your plugin to appear here :) -### Create plugins +## Create plugins You can implement your own descriptors and load them as plugins during MegaLinter runtime -- Plugins descriptor files must be named **\*\*.megalinter-descriptor.yml** and respect [MegaLinter Json Schema](https://github.com/megalinter/megalinter/blob/main/megalinter/descriptors/schemas/megalinter-descriptor.jsonschema.json) - Descriptor format is exactly the same than [MegaLinter embedded ones](https://github.com/megalinter/megalinter/tree/main/megalinter/descriptors) ([see json schema documentation](https://megalinter.github.io/json-schemas/descriptor.html)) +- Plugins descriptor files must be named **\*\*.megalinter-descriptor.yml** and respect [MegaLinter Json Schema](https://github.com/megalinter/megalinter/blob/main/megalinter/descriptors/schemas/megalinter-descriptor.jsonschema.json) - Plugins must be hosted in a url containing **\*\*/mega-linter-plugin-\*\*/** +- File URLs must conform to the same directory and file naming criteria as defined above. -#### Limitations +### Limitations - For now, the only `install` attributes managed are `dockerfile` instructions starting by `RUN` + diff --git a/megalinter/plugin_factory.py b/megalinter/plugin_factory.py index d5acc897f7c..4f2b54d1173 100644 --- a/megalinter/plugin_factory.py +++ b/megalinter/plugin_factory.py @@ -1,9 +1,9 @@ # Class to manage MegaLinter plugins import logging +import os import shutil import subprocess import sys -import os import requests import yaml @@ -48,10 +48,14 @@ def load_plugin(plugin): if not os.path.isfile(plugin_path): plugin_path = "/tmp/lint/" + plugin_path if not os.path.isfile(plugin_path): - raise Exception(f"[Plugins] Local plugin descriptor {plugin} not found") + raise Exception( + f"[Plugins] Local plugin descriptor {plugin} not found" + ) # Make sure plugin file is readable and not empty if not os.access(plugin_path, os.R_OK): - raise Exception(f"[Plugins] Local plugin descriptor {plugin} not readable") + raise Exception( + f"[Plugins] Local plugin descriptor {plugin} not readable" + ) if os.stat(plugin_path).st_size == 0: raise Exception(f"[Plugins] Plugin descriptor {plugin} is empty") r = open(plugin_path, "r").read() @@ -63,12 +67,15 @@ def load_plugin(plugin): f"[Plugins] Loaded plugin descriptor {descriptor_file} from {plugin}" ) except Exception as e: - raise Exception(f"[Plugins] Unable to load remote plugin {plugin}:\n{str(e)}") + raise Exception( + f"[Plugins] Unable to load remote plugin {plugin}:\n{str(e)}" + ) return descriptor_file else: raise Exception( "[Plugins] Plugin descriptors must follow the format" - f" https://**/mega-linter-plugin-**/**.mega-linter-descriptor.yml or file://**/mega-linter-plugin-**/**.mega-linter-descriptor.yml (wrong value {plugin})" + " https://**/mega-linter-plugin-**/**.mega-linter-descriptor.yml or" + f" file://**/mega-linter-plugin-**/**.mega-linter-descriptor.yml (wrong value {plugin})" ) diff --git a/megalinter/tests/test_megalinter/plugins_test.py b/megalinter/tests/test_megalinter/plugins_test.py index 0633706cf2a..c04de6d3e7c 100644 --- a/megalinter/tests/test_megalinter/plugins_test.py +++ b/megalinter/tests/test_megalinter/plugins_test.py @@ -42,6 +42,22 @@ def test_load_plugin_success(self): self.assertIn("[Plugins] Loaded plugin descriptor", output) self.assertIn("[Plugins] Successful initialization of TEST", output) + def test_load_local_plugin_success(self): + mega_linter, output = utilstest.call_mega_linter( + { + "PLUGINS": ".automation/test/mega-linter-plugin-test/test.megalinter-descriptor.yml", + "LOG_LEVEL": "DEBUG", + "MULTI_STATUS": "false", + "GITHUB_COMMENT_REPORTER": "false", + } + ) + self.assertTrue( + len(mega_linter.linters) > 0, "Linters have been created and run" + ) + self.assertIn("### Processed [TEST] files", output) + self.assertIn("[Plugins] Loaded plugin descriptor", output) + self.assertIn("[Plugins] Successful initialization of TEST", output) + def test_load_plugin_http_error(self): try: utilstest.call_mega_linter(