mega-linter-runner 8.8.1-beta202507270754.0 → 8.8.1-beta202507271139.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/generators/mega-linter-custom-flavor/index.js +163 -0
- package/generators/mega-linter-custom-flavor/templates/README.md +20 -0
- package/generators/mega-linter-custom-flavor/templates/action.yml +16 -0
- package/generators/mega-linter-custom-flavor/templates/megalinter-custom-flavor-builder.yml +80 -0
- package/generators/mega-linter-custom-flavor/templates/megalinter-custom-flavor.yml +6 -0
- package/lib/options.js +11 -0
- package/lib/runner.js +15 -0
- package/package.json +4 -2
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { asciiArt } from "../../lib/ascii.js";
|
|
2
|
+
import Generator from 'yeoman-generator';
|
|
3
|
+
import { simpleGit } from 'simple-git';
|
|
4
|
+
import c from 'chalk';
|
|
5
|
+
import fs from "fs"
|
|
6
|
+
import yaml from "js-yaml";
|
|
7
|
+
|
|
8
|
+
export default class GeneratorMegaLinter extends Generator {
|
|
9
|
+
async prompting() {
|
|
10
|
+
console.log(asciiArt());
|
|
11
|
+
this.log(c.cyan(
|
|
12
|
+
`Welcome to the MegaLinter Custom Flavor generator !
|
|
13
|
+
When you don't know what option to select, please use default values`
|
|
14
|
+
));
|
|
15
|
+
|
|
16
|
+
// Verify that the repo name contains "megalinter-custom-flavor"
|
|
17
|
+
const git = simpleGit();
|
|
18
|
+
const remote = await git.getRemotes(true);
|
|
19
|
+
if (!remote[0].refs.fetch.includes("megalinter-custom-flavor")) {
|
|
20
|
+
const errorMessage = `
|
|
21
|
+
ERROR: This generator must be run in a repository whose name includes 'megalinter-custom-flavor'
|
|
22
|
+
Example: 'megalinter-custom-flavor-python-light'
|
|
23
|
+
`
|
|
24
|
+
this.log(c.red(c.bold(errorMessage)));
|
|
25
|
+
throw new Error(errorMessage);
|
|
26
|
+
}
|
|
27
|
+
// Fetch https://raw.githubusercontent.com/megalinter/megalinter/main/megalinter/descriptors/schemas/megalinter-configuration.jsonschema.json
|
|
28
|
+
this.log("Fetching MegaLinter configuration schema...");
|
|
29
|
+
const url = 'https://raw.githubusercontent.com/megalinter/megalinter/main/megalinter/descriptors/schemas/megalinter-configuration.jsonschema.json';
|
|
30
|
+
const response = await fetch(url);
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
throw new Error(`Failed to fetch schema from ${url}`);
|
|
33
|
+
}
|
|
34
|
+
const schema = await response.json();
|
|
35
|
+
const linterKeys = schema.definitions.enum_linter_keys.enum || [];
|
|
36
|
+
|
|
37
|
+
let defaultFlavorLabel = "MyCustomFlavor";
|
|
38
|
+
let defaultSelectedLinters = [];
|
|
39
|
+
if (globalThis.customFlavorLinters) {
|
|
40
|
+
// Check if linters are valid , and crash if there is an invalid linter key
|
|
41
|
+
globalThis.customFlavorLinters.forEach((linter) => {
|
|
42
|
+
if (!linterKeys.includes(linter)) {
|
|
43
|
+
throw new Error(`Invalid linter key: ${linter}`);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
defaultSelectedLinters = globalThis.customFlavorLinters;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Initialize data from existing configuration
|
|
50
|
+
const customFlavorConfigPath = this.destinationPath('megalinter-custom-flavor.yml');
|
|
51
|
+
if (fs.existsSync(customFlavorConfigPath)) {
|
|
52
|
+
const customFlavorConfigContent = fs.readFileSync(customFlavorConfigPath, 'utf8');
|
|
53
|
+
const customFlavorConfig = yaml.load(customFlavorConfigContent);
|
|
54
|
+
if (customFlavorConfig.label) {
|
|
55
|
+
defaultFlavorLabel = customFlavorConfig.label;
|
|
56
|
+
}
|
|
57
|
+
if (customFlavorConfig.linters) {
|
|
58
|
+
defaultSelectedLinters = customFlavorConfig.linters.map((linter) => linter.replace(/^\s*-\s*/, '').trim());
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const prompts = [
|
|
64
|
+
{
|
|
65
|
+
type: 'input',
|
|
66
|
+
name: 'customFlavorLabel',
|
|
67
|
+
message: 'What is the label of your custom flavor?',
|
|
68
|
+
default: defaultFlavorLabel
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: 'checkbox',
|
|
72
|
+
name: 'selectedLinters',
|
|
73
|
+
message: 'Please select the linters you want to include in your custom flavor:',
|
|
74
|
+
choices: linterKeys,
|
|
75
|
+
default: defaultSelectedLinters
|
|
76
|
+
}
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
return this.prompt(prompts).then(async (props) => {
|
|
80
|
+
this.props = props;
|
|
81
|
+
await this._computeValues();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
writing() {
|
|
86
|
+
this._generateFlavorConfig();
|
|
87
|
+
this._generateGitHubWorkflow();
|
|
88
|
+
this._generateGitHubAction();
|
|
89
|
+
this._generateReadme();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
end() {
|
|
93
|
+
this.log("You're all set !");
|
|
94
|
+
this.log(c.green(
|
|
95
|
+
"Now commit, push then create a GitHub Release to generate your custom flavor !")
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async _computeValues() {
|
|
100
|
+
// Custom flavor label
|
|
101
|
+
this.customFlavorLabel = this.props.customFlavorLabel;
|
|
102
|
+
// Custom flavor selected linters
|
|
103
|
+
this.selectedLinters = this.props.selectedLinters.map((linter) => ` - ${linter}`).join("\n");
|
|
104
|
+
// Check at least one linter is selected
|
|
105
|
+
if (this.selectedLinters.length === 0) {
|
|
106
|
+
throw new Error("You must select at least one linter for your custom flavor");
|
|
107
|
+
}
|
|
108
|
+
// Custom flavor author is git username
|
|
109
|
+
const git = simpleGit();
|
|
110
|
+
const user = await git.getConfig('user.name');
|
|
111
|
+
this.customFlavorAuthor = user.value;
|
|
112
|
+
// Get remote repo
|
|
113
|
+
const remote = await git.getRemotes(true);
|
|
114
|
+
this.customFlavorRepo = remote[0].refs.fetch.replace('https://github.com/', '').replace('.git', '');
|
|
115
|
+
// Custom flavor docker image version
|
|
116
|
+
this.customFlavorDockerImageVersion = `ghcr.io/${this.customFlavorRepo}/megalinter-custom-flavor:latest`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
_generateFlavorConfig() {
|
|
120
|
+
this.fs.copyTpl(
|
|
121
|
+
this.templatePath("megalinter-custom-flavor.yml"),
|
|
122
|
+
this.destinationPath(`megalinter-custom-flavor.yml`),
|
|
123
|
+
{
|
|
124
|
+
CUSTOM_FLAVOR_LABEL: this.customFlavorLabel,
|
|
125
|
+
CUSTOM_FLAVOR_LINTERS: this.selectedLinters,
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
_generateGitHubWorkflow() {
|
|
131
|
+
this.fs.copyTpl(
|
|
132
|
+
this.templatePath("megalinter-custom-flavor-builder.yml"),
|
|
133
|
+
this.destinationPath("./.github/workflows/megalinter-custom-flavor-builder.yml"),
|
|
134
|
+
{}
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
_generateGitHubAction() {
|
|
139
|
+
this.fs.copyTpl(
|
|
140
|
+
this.templatePath("action.yml"),
|
|
141
|
+
this.destinationPath("action.yml"),
|
|
142
|
+
{
|
|
143
|
+
CUSTOM_FLAVOR_LABEL: this.customFlavorLabel,
|
|
144
|
+
CUSTOM_FLAVOR_AUTHOR: this.customFlavorAuthor,
|
|
145
|
+
DOCKER_IMAGE_VERSION: this.customFlavorDockerImageVersion,
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
_generateReadme() {
|
|
151
|
+
this.fs.copyTpl(
|
|
152
|
+
this.templatePath("README.md"),
|
|
153
|
+
this.destinationPath("README.md"),
|
|
154
|
+
{
|
|
155
|
+
CUSTOM_FLAVOR_LABEL: this.customFlavorLabel,
|
|
156
|
+
CUSTOM_FLAVOR_LINTERS: this.selectedLinters,
|
|
157
|
+
DOCKER_IMAGE_VERSION: this.customFlavorDockerImageVersion,
|
|
158
|
+
CUSTOM_FLAVOR_GITHUB_ACTION: this.customFlavorRepo
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# MegaLinter Custom Flavor
|
|
2
|
+
|
|
3
|
+
<%= CUSTOM_FLAVOR_LABEL %>
|
|
4
|
+
|
|
5
|
+
## Embedded linters
|
|
6
|
+
|
|
7
|
+
<%= CUSTOM_FLAVOR_LINTERS %>
|
|
8
|
+
|
|
9
|
+
## How to generate the flavor
|
|
10
|
+
|
|
11
|
+
Create a GitHub release on your repo, it will generate and publish your custom flavor using the MegaLinter custom Flavor Builder matching the tag name of your release (example: `9.0.0`)
|
|
12
|
+
|
|
13
|
+
You can also generate a custom flavor using MegaLinter Custom Flavor by pushing on any branch or manually run the workflow `megalinter-custom-flavor-builder.yml`.
|
|
14
|
+
|
|
15
|
+
## How to use the custom flavor
|
|
16
|
+
|
|
17
|
+
Follow [MegaLinter installation guide](https://megalinter.io/latest/install-assisted/), and replace related elements in the workflow.
|
|
18
|
+
|
|
19
|
+
- GitHub Action: On MegaLinter step in .github/workflows/mega-linter.yml, define `uses: <%= CUSTOM_FLAVOR_GITHUB_ACTION %>@main`
|
|
20
|
+
- Docker image: Replace official MegaLinter image by `<%= DOCKER_IMAGE_VERSION %>`
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: "MegaLinter Custom Flavor: <%= CUSTOM_FLAVOR_LABEL %>"
|
|
2
|
+
author: "<%= CUSTOM_FLAVOR_AUTHOR %>"
|
|
3
|
+
description: "MegaLinter Custom Flavor <%= CUSTOM_FLAVOR_LABEL %>"
|
|
4
|
+
outputs:
|
|
5
|
+
has_updated_sources:
|
|
6
|
+
description: "0 if no source file has been updated, 1 if source files has been updated"
|
|
7
|
+
runs:
|
|
8
|
+
using: "docker"
|
|
9
|
+
# You can manually update the Docker image version here, or use the latest tag
|
|
10
|
+
image: "docker://<%= DOCKER_IMAGE_VERSION %>"
|
|
11
|
+
args:
|
|
12
|
+
- "-v"
|
|
13
|
+
- "/var/run/docker.sock:/var/run/docker.sock:rw"
|
|
14
|
+
branding:
|
|
15
|
+
icon: "check"
|
|
16
|
+
color: "green"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# =============================================================
|
|
2
|
+
# Build MegaLinter Custom Flavor Workflow
|
|
3
|
+
#
|
|
4
|
+
# This workflow builds and publishes a custom MegaLinter Docker image
|
|
5
|
+
# to GitHub Container Registry (ghcr.io) and optionally Docker Hub.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# - Triggers on push, release, or manual dispatch.
|
|
9
|
+
# - Tags the image with the release tag (on release) or 'alpha' (otherwise).
|
|
10
|
+
# - Pushes to ghcr.io and, if credentials are set, to Docker Hub.
|
|
11
|
+
#
|
|
12
|
+
# Required repository secrets:
|
|
13
|
+
# - GITHUB_TOKEN: Provided by GitHub Actions (for ghcr.io push)
|
|
14
|
+
# - DOCKERHUB_USERNAME: (optional) Docker Hub username for push
|
|
15
|
+
# - DOCKERHUB_PASSWORD: (optional) Docker Hub password/token for push
|
|
16
|
+
#
|
|
17
|
+
# Optional repository variables:
|
|
18
|
+
# - DOCKERHUB_REPO: Docker Hub repository name (e.g. nvuillam)
|
|
19
|
+
#
|
|
20
|
+
# The MegaLinter custom flavor image will be available at:
|
|
21
|
+
# - ghcr.io/<owner>/<repo>:<tag>
|
|
22
|
+
# - docker.io/<dockerhub_repo>/<repo>:<tag> (if Docker Hub credentials are set)
|
|
23
|
+
# =============================================================
|
|
24
|
+
|
|
25
|
+
name: Build & Push MegaLinter Custom Flavor
|
|
26
|
+
|
|
27
|
+
on:
|
|
28
|
+
push:
|
|
29
|
+
branches-ignore: [main]
|
|
30
|
+
release:
|
|
31
|
+
types: [edited, published]
|
|
32
|
+
workflow_dispatch:
|
|
33
|
+
|
|
34
|
+
concurrency:
|
|
35
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
36
|
+
cancel-in-progress: true
|
|
37
|
+
|
|
38
|
+
jobs:
|
|
39
|
+
build-custom-flavor:
|
|
40
|
+
name: Build Custom MegaLinter Flavor
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
permissions:
|
|
43
|
+
contents: read
|
|
44
|
+
packages: write
|
|
45
|
+
|
|
46
|
+
steps:
|
|
47
|
+
- name: Checkout Code
|
|
48
|
+
uses: actions/checkout@v4
|
|
49
|
+
with:
|
|
50
|
+
fetch-depth: 0
|
|
51
|
+
|
|
52
|
+
- name: Log in to GitHub Container Registry
|
|
53
|
+
uses: docker/login-action@v3
|
|
54
|
+
with:
|
|
55
|
+
registry: ghcr.io
|
|
56
|
+
username: ${{ github.actor }}
|
|
57
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
58
|
+
|
|
59
|
+
# Log in to Docker Hub only if credentials are provided
|
|
60
|
+
- name: Log in to Docker Hub
|
|
61
|
+
uses: docker/login-action@v3
|
|
62
|
+
with:
|
|
63
|
+
registry: docker.io
|
|
64
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
65
|
+
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
|
66
|
+
continue-on-error: true
|
|
67
|
+
|
|
68
|
+
- name: Build MegaLinter Custom Flavor
|
|
69
|
+
uses: oxsecurity/megalinter/flavors/custom-builder@main
|
|
70
|
+
with:
|
|
71
|
+
megalinter-custom-flavor-builder-tag: ${{ github.event_name == 'release' && github.event.release.tag_name || 'beta' }}
|
|
72
|
+
is-latest: ${{ github.event_name == 'release' && 'true' || 'false' }}
|
|
73
|
+
upload-to-ghcr: "true"
|
|
74
|
+
upload-to-dockerhub: ${{ secrets.DOCKERHUB_USERNAME != '' && secrets.DOCKERHUB_PASSWORD != '' && 'true' || 'false' }}
|
|
75
|
+
dockerhub-repo: ${{ vars.DOCKERHUB_REPO }}
|
|
76
|
+
env:
|
|
77
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
78
|
+
CUSTOM_FLAVOR_BUILD_REPO: ${{ github.repository }}
|
|
79
|
+
CUSTOM_FLAVOR_BUILD_REPO_URL: ${{ github.repositoryUrl }}
|
|
80
|
+
CUSTOM_FLAVOR_BUILD_USER: ${{ github.actor }}
|
package/lib/options.js
CHANGED
|
@@ -122,6 +122,17 @@ export const optionsDefinition = optionator.default({
|
|
|
122
122
|
type: "Boolean",
|
|
123
123
|
description: "Generate MegaLinter configuration in your project",
|
|
124
124
|
},
|
|
125
|
+
{
|
|
126
|
+
option: "custom-flavor-setup",
|
|
127
|
+
alias: "cfs",
|
|
128
|
+
type: "Boolean",
|
|
129
|
+
description: "Generate files to create a custom flavor",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
option: "custom-flavor-linters",
|
|
133
|
+
type: "String",
|
|
134
|
+
description: "comma-separated list of linters to include in custom flavor",
|
|
135
|
+
},
|
|
125
136
|
{
|
|
126
137
|
option: "upgrade",
|
|
127
138
|
alias: "u",
|
package/lib/runner.js
CHANGED
|
@@ -54,6 +54,21 @@ export class MegaLinterRunner {
|
|
|
54
54
|
return { status: 0 };
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// Run custom flavor generator
|
|
58
|
+
if (options.customFlavorSetup) {
|
|
59
|
+
if (options.customFlavorLinters) {
|
|
60
|
+
globalThis.customFlavorLinters = options.customFlavorLinters.split(",").map((linter) => linter.trim());
|
|
61
|
+
}
|
|
62
|
+
const env = createEnv();
|
|
63
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
64
|
+
const generatorPath = path.resolve(
|
|
65
|
+
path.join(__dirname, "..", "generators", "mega-linter-custom-flavor")
|
|
66
|
+
);
|
|
67
|
+
console.log("Yeoman generator used: " + generatorPath);
|
|
68
|
+
env.run(generatorPath);
|
|
69
|
+
return { status: 0 };
|
|
70
|
+
}
|
|
71
|
+
|
|
57
72
|
// Run upgrader from v4 to v5
|
|
58
73
|
if (options.upgrade) {
|
|
59
74
|
const megaLinterUpgrader = new MegaLinterUpgrader();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mega-linter-runner",
|
|
3
|
-
"version": "8.8.1-
|
|
3
|
+
"version": "8.8.1-beta202507271139.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/oxsecurity/megalinter.git"
|
|
@@ -82,14 +82,16 @@
|
|
|
82
82
|
"author": "Nicolas Vuillamy",
|
|
83
83
|
"license": "MIT",
|
|
84
84
|
"dependencies": {
|
|
85
|
-
"chalk": "^5.
|
|
85
|
+
"chalk": "^5.4.1",
|
|
86
86
|
"find-package-json": "^1.2.0",
|
|
87
87
|
"fs-extra": "^11.1.1",
|
|
88
88
|
"glob": "^11.0.0",
|
|
89
|
+
"js-yaml": "^4.1.0",
|
|
89
90
|
"mem-fs": "^4.0.0",
|
|
90
91
|
"open": "^10.0.2",
|
|
91
92
|
"optionator": "^0.9.3",
|
|
92
93
|
"prompts": "^2.4.2",
|
|
94
|
+
"simple-git": "^3.28.0",
|
|
93
95
|
"uuid": "^11.0.0",
|
|
94
96
|
"which": "^5.0.0",
|
|
95
97
|
"yeoman-environment": "^4.0.0",
|