UNPKG

mega-linter-runner

Version:
613 lines (599 loc) • 22.4 kB
import { glob } from "glob"; import { default as fs } from "fs-extra"; import * as path from "path"; import { default as c } from 'chalk'; import prompts from "prompts"; import { OXSecuritySetup } from "./ox-setup.js"; import { asciiArt } from "./ascii.js"; import { DEFAULT_RELEASE, GLOB_IGNORE_PATTERNS } from "./config.js"; export class MegaLinterUpgrader { constructor() { this.replacements = [ // Documentation base URL { regex: /https:\/\/nvuillam\.github\.io\/mega-linter/gm, replacement: "https://megalinter.github.io", test: "https://nvuillam.github.io/mega-linter/configuration", testRes: "https://megalinter.github.io/configuration", }, // Github actions flavors { regex: /nvuillam\/mega-linter\/flavors\/([a-z]*)@latest/gm, replacement: "megalinter/megalinter/flavors/$1@beta", test: "nvuillam/mega-linter/flavors/python@latest", testRes: "megalinter/megalinter/flavors/python@beta", }, { regex: /nvuillam\/mega-linter\/flavors\/([a-z]*)@insiders/gm, replacement: "megalinter/megalinter/flavors/$1@beta", test: "nvuillam/mega-linter/flavors/python@insiders", testRes: "megalinter/megalinter/flavors/python@beta", }, { regex: /nvuillam\/mega-linter\/flavors\/([a-z]*)@v4\.(.*)/gm, replacement: "megalinter/megalinter/flavors/$1@v5", test: "nvuillam/mega-linter/flavors/python@v4.1.2", testRes: "megalinter/megalinter/flavors/python@v5", }, { regex: /nvuillam\/mega-linter\/flavors\/([a-z]*)@v4/gm, replacement: "megalinter/megalinter/flavors/$1@v5", test: "nvuillam/mega-linter/flavors/python@v4", testRes: "megalinter/megalinter/flavors/python@v5", }, { regex: /nvuillam\/mega-linter\/flavors\/([a-z]*)@([a-z]*)/gm, replacement: "megalinter/megalinter/flavors/$1@$2", test: "nvuillam/mega-linter/flavors/python@alpha", testRes: "megalinter/megalinter/flavors/python@alpha", }, { regex: /nvuillam\/mega-linter\/flavors\/([a-z]*)/gm, replacement: "megalinter/megalinter/flavors/$1", test: "nvuillam/mega-linter/flavors/python", testRes: "megalinter/megalinter/flavors/python", }, // Docker image flavors { regex: /nvuillam\/mega-linter-([a-z]*):latest/gm, replacement: "megalinter/megalinter-$1:beta", test: "nvuillam/mega-linter-python:latest", testRes: "megalinter/megalinter-python:beta", }, { regex: /nvuillam\/mega-linter-([a-z]*):insiders/gm, replacement: "megalinter/megalinter-$1:beta", test: "nvuillam/mega-linter-python:insiders", testRes: "megalinter/megalinter-python:beta", }, { regex: /nvuillam\/mega-linter-([a-z]*):v4\.(.*)/gm, replacement: "megalinter/megalinter-$1:v5", test: "nvuillam/mega-linter-python:v4.1.2", testRes: "megalinter/megalinter-python:v5", }, { regex: /nvuillam\/mega-linter-([a-z]*):v4/gm, replacement: "megalinter/megalinter-$1:v5", test: "nvuillam/mega-linter-python:v4", testRes: "megalinter/megalinter-python:v5", }, { regex: /nvuillam\/mega-linter-([a-z]*):([a-z]*)/gm, replacement: "megalinter/megalinter-$1:$2", test: "nvuillam/mega-linter-python:alpha", testRes: "megalinter/megalinter-python:alpha", }, { regex: /nvuillam\/mega-linter-([a-z]*)/gm, replacement: "megalinter/megalinter-$1", test: "nvuillam/mega-linter-python", testRes: "megalinter/megalinter-python", }, // Github actions using main flavor { regex: /nvuillam\/mega-linter@insiders/gm, replacement: "megalinter/megalinter@beta", test: "nvuillam/mega-linter@insiders", testRes: "megalinter/megalinter@beta", }, { regex: /nvuillam\/mega-linter@latest/gm, replacement: "megalinter/megalinter@beta", test: "nvuillam/mega-linter@latest", testRes: "megalinter/megalinter@beta", }, { regex: /nvuillam\/mega-linter@v4\.(.*)/gm, replacement: "megalinter/megalinter@v5", test: "nvuillam/mega-linter@v4.2.4", testRes: "megalinter/megalinter@v5", }, { regex: /nvuillam\/mega-linter@v4/gm, replacement: "megalinter/megalinter@v5", test: "nvuillam/mega-linter@v4", testRes: "megalinter/megalinter@v5", }, { regex: /nvuillam\/mega-linter@([a-z]*)/gm, replacement: "megalinter/megalinter@$1", test: "nvuillam/mega-linter@alpha", testRes: "megalinter/megalinter@alpha", }, // Docker images using main flavor { regex: /nvuillam\/mega-linter:insiders/gm, replacement: "megalinter/megalinter:beta", test: "nvuillam/mega-linter:insiders", testRes: "megalinter/megalinter:beta", }, { regex: /nvuillam\/mega-linter:latest/gm, replacement: "megalinter/megalinter:beta", test: "nvuillam/mega-linter:latest", testRes: "megalinter/megalinter:beta", }, { regex: /nvuillam\/mega-linter:v4\.(.*)/gm, replacement: "megalinter/megalinter:v5", test: "nvuillam/mega-linter:v4.2.4", testRes: "megalinter/megalinter:v5", }, { regex: /nvuillam\/mega-linter:v4/gm, replacement: "megalinter/megalinter:v5", test: "nvuillam/mega-linter:v4", testRes: "megalinter/megalinter:v5", }, { regex: /nvuillam\/mega-linter:([a-z]*)/gm, replacement: "megalinter/megalinter:$1", test: "nvuillam/mega-linter:alpha", testRes: "megalinter/megalinter:alpha", }, // All remaining cases... cross fingers :) { regex: /nvuillam\/mega-linter/gm, replacement: "megalinter/megalinter", test: "wesh nvuillam/mega-linter", testRes: "wesh megalinter/megalinter", }, // Cancellation of duplicate runs // Job "cancel_duplicate" { regex: /(?<prev_jobs>jobs\s*:(?:.|\n)*)\n(?<indent> *)cancel_duplicates\s*:(?:.|\n)*?\n(?<next_job_key>\k<indent>\S*\s*:)/gim, replacement: `concurrency: group: \${{ github.ref }}-\${{ github.workflow }} cancel-in-progress: true $<prev_jobs> $<next_job_key>`, test: ` jobs: preceding_job: # .. # Cancel duplicate jobs: https://github.com/fkirc/skip-duplicate-actions#option-3-cancellation-only cancel_duplicates: name: Cancel duplicate jobs runs-on: ubuntu-latest steps: - uses: fkirc/skip-duplicate-actions@master with: github_token: \${{ secrets.PAT || secrets.GITHUB_TOKEN }} cancel_others: true intermediate_job: # .. build: name: MegaLinter `, testRes: ` concurrency: group: \${{ github.ref }}-\${{ github.workflow }} cancel-in-progress: true jobs: preceding_job: # .. # Cancel duplicate jobs: https://github.com/fkirc/skip-duplicate-actions#option-3-cancellation-only intermediate_job: # .. build: name: MegaLinter `, }, // Comment "# Cancel duplicate jobs: https://github.com/fkirc/..." { regex: /^\s*#\s*cancel\s*duplicate.*\n/gim, replacement: "", test: ` jobs: # Cancel duplicate jobs: https://github.com/fkirc/skip-duplicate-actions#option-3-cancellation-only build: `, testRes: ` jobs: build: `, }, // V5 to V6 migration rules // GitHub actions { regex: /actions\/checkout@v2/gm, replacement: "actions/checkout@v3", test: "uses: actions/checkout@v2", testRes: "uses: actions/checkout@v3", }, { regex: /actions\/checkout@v3/gm, replacement: "actions/checkout@v4", test: "uses: actions/checkout@v3", testRes: "uses: actions/checkout@v4", }, // Documentation base URL { regex: /https:\/\/megalinter\.github\.io/gm, replacement: "https://oxsecurity.github.io/megalinter", test: "https://megalinter.github.io/configuration", testRes: "https://oxsecurity.github.io/megalinter/configuration", }, // Github actions flavors { regex: /megalinter\/megalinter\/flavors\/([a-z]*)@v5\.(.*)/gm, replacement: `oxsecurity/megalinter/flavors/$1@${DEFAULT_RELEASE}`, test: "megalinter/megalinter/flavors/python@v5.1.2", testRes: `oxsecurity/megalinter/flavors/python@${DEFAULT_RELEASE}`, }, { regex: /megalinter\/megalinter\/flavors\/([a-z]*)@v5/gm, replacement: `oxsecurity/megalinter/flavors/$1@${DEFAULT_RELEASE}`, test: "megalinter/megalinter/flavors/python@v5", testRes: `oxsecurity/megalinter/flavors/python@${DEFAULT_RELEASE}`, }, { regex: /megalinter\/megalinter\/flavors\/([a-z]*)@([a-z]*)/gm, replacement: "oxsecurity/megalinter/flavors/$1@$2", test: "megalinter/megalinter/flavors/python@alpha", testRes: "oxsecurity/megalinter/flavors/python@alpha", }, { regex: /megalinter\/megalinter\/flavors\/([a-z]*)/gm, replacement: "oxsecurity/megalinter/flavors/$1", test: "megalinter/megalinter/flavors/python", testRes: "oxsecurity/megalinter/flavors/python", }, // GitHub Action report folder { regex: /Mega-Linter reports(.*)\n(.*)path:(.*)\n(.*)report(?!s)/gm, replacement: "Mega-Linter reports$1\n$2path:$3\n$4megalinter-reports", test: ` name: Mega-Linter reports path: | report`, testRes: ` name: Mega-Linter reports path: | megalinter-reports`, }, // Docker image flavors { regex: /megalinter\/megalinter-([a-z]*):v5\.(.*)/gm, replacement: `oxsecurity/megalinter-$1:${DEFAULT_RELEASE}`, test: "megalinter/megalinter-python:v5.1.2", testRes: `oxsecurity/megalinter-python:${DEFAULT_RELEASE}`, }, { regex: /megalinter\/megalinter-([a-z]*):v5/gm, replacement: `oxsecurity/megalinter-$1:${DEFAULT_RELEASE}`, test: "megalinter/megalinter-python:v5", testRes: `oxsecurity/megalinter-python:${DEFAULT_RELEASE}`, }, { regex: /megalinter\/megalinter-([a-z]*):([a-z]*)/gm, replacement: "oxsecurity/megalinter-$1:$2", test: "megalinter/megalinter-python:alpha", testRes: "oxsecurity/megalinter-python:alpha", }, { regex: /megalinter\/megalinter-([a-z]*)/gm, replacement: "oxsecurity/megalinter-$1", test: "megalinter/megalinter-python", testRes: "oxsecurity/megalinter-python", }, // Github actions using main flavor { regex: /megalinter\/megalinter@v5\.(.*)/gm, replacement: `oxsecurity/megalinter@${DEFAULT_RELEASE}`, test: "megalinter/megalinter@v5.2.4", testRes: `oxsecurity/megalinter@${DEFAULT_RELEASE}`, }, { regex: /megalinter\/megalinter@v5/gm, replacement: `oxsecurity/megalinter@${DEFAULT_RELEASE}`, test: "megalinter/megalinter@v5", testRes: `oxsecurity/megalinter@${DEFAULT_RELEASE}`, }, { regex: /megalinter\/megalinter@([a-z]*)/gm, replacement: "oxsecurity/megalinter@$1", test: "megalinter/megalinter@alpha", testRes: "oxsecurity/megalinter@alpha", }, // Docker images using main flavor { regex: /megalinter\/megalinter:v5\.(.*)/gm, replacement: `oxsecurity/megalinter:${DEFAULT_RELEASE}`, test: "megalinter/megalinter:v5.2.4", testRes: `oxsecurity/megalinter:${DEFAULT_RELEASE}`, }, { regex: /megalinter\/megalinter:v5/gm, replacement: `oxsecurity/megalinter:${DEFAULT_RELEASE}`, test: "megalinter/megalinter:v5", testRes: `oxsecurity/megalinter:${DEFAULT_RELEASE}`, }, { regex: /megalinter\/megalinter:([a-z]*)/gm, replacement: "oxsecurity/megalinter:$1", test: "megalinter/megalinter:alpha", testRes: "oxsecurity/megalinter:alpha", }, // V6 to V7 migration rules // Documentation base URL { regex: /https:\/\/oxsecurity\.github\.io\/megalinter/gm, replacement: "https://megalinter.io", test: "https://oxsecurity.github.io/megalinter/configuration", testRes: "https://megalinter.io/configuration", }, { regex: /https:\/\/megalinter.io\/configuration/gm, replacement: "https://megalinter.io/latest/config-file", test: "https://megalinter.io/configuration/", testRes: "https://megalinter.io/latest/config-file/", }, { regex: /https:\/\/megalinter.io\/config-file/gm, replacement: "https://megalinter.io/latest/config-file", test: "https://megalinter.io/config-file/", testRes: "https://megalinter.io/latest/config-file/", }, { regex: /https:\/\/megalinter.io\/flavors/gm, replacement: "https://megalinter.io/latest/flavors", test: "https://megalinter.io/flavors/", testRes: "https://megalinter.io/latest/flavors/", }, // Github actions flavors { regex: /oxsecurity\/megalinter\/flavors\/([a-z]*)@v6\.(.*)/gm, replacement: `oxsecurity/megalinter/flavors/$1@${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter/flavors/python@v6.1.2", testRes: `oxsecurity/megalinter/flavors/python@${DEFAULT_RELEASE}`, }, { regex: /oxsecurity\/megalinter\/flavors\/([a-z]*)@v6/gm, replacement: `oxsecurity/megalinter/flavors/$1@${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter/flavors/python@v6", testRes: `oxsecurity/megalinter/flavors/python@${DEFAULT_RELEASE}`, }, // Docker image flavors { regex: /oxsecurity\/megalinter-([a-z]*):v6\.(.*)/gm, replacement: `oxsecurity/megalinter-$1:${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter-python:v6.1.2", testRes: `oxsecurity/megalinter-python:${DEFAULT_RELEASE}`, }, { regex: /oxsecurity\/megalinter-([a-z]*):v6/gm, replacement: `oxsecurity/megalinter-$1:${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter-python:v6", testRes: `oxsecurity/megalinter-python:${DEFAULT_RELEASE}`, }, // Github actions using main flavor { regex: /oxsecurity\/megalinter@v6\.(.*)/gm, replacement: `oxsecurity/megalinter@${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter@v6.2.4", testRes: `oxsecurity/megalinter@${DEFAULT_RELEASE}`, }, { regex: /oxsecurity\/megalinter@v6/gm, replacement: `oxsecurity/megalinter@${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter@v6", testRes: `oxsecurity/megalinter@${DEFAULT_RELEASE}`, }, // Docker images using main flavor { regex: /oxsecurity\/megalinter:v6\.(.*)/gm, replacement: `oxsecurity/megalinter:${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter:v6.2.4", testRes: `oxsecurity/megalinter:${DEFAULT_RELEASE}`, }, { regex: /oxsecurity\/megalinter:v6/gm, replacement: `oxsecurity/megalinter:${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter:v6", testRes: `oxsecurity/megalinter:${DEFAULT_RELEASE}`, }, // V7 to V8 migration rules // Github actions flavors { regex: /oxsecurity\/megalinter\/flavors\/([a-z]*)@v7\.(.*)/gm, replacement: `oxsecurity/megalinter/flavors/$1@${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter/flavors/python@v7.1.2", testRes: `oxsecurity/megalinter/flavors/python@${DEFAULT_RELEASE}`, }, { regex: /oxsecurity\/megalinter\/flavors\/([a-z]*)@v7/gm, replacement: `oxsecurity/megalinter/flavors/$1@${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter/flavors/python@v7", testRes: `oxsecurity/megalinter/flavors/python@${DEFAULT_RELEASE}`, }, // Docker image flavors { regex: /oxsecurity\/megalinter-([a-z]*):v7\.(.*)/gm, replacement: `oxsecurity/megalinter-$1:${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter-python:v7.1.2", testRes: `oxsecurity/megalinter-python:${DEFAULT_RELEASE}`, }, { regex: /oxsecurity\/megalinter-([a-z]*):v7/gm, replacement: `oxsecurity/megalinter-$1:${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter-python:v7", testRes: `oxsecurity/megalinter-python:${DEFAULT_RELEASE}`, }, // Github actions using main flavor { regex: /oxsecurity\/megalinter@v7\.(.*)/gm, replacement: `oxsecurity/megalinter@${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter@v7.2.4", testRes: `oxsecurity/megalinter@${DEFAULT_RELEASE}`, }, { regex: /oxsecurity\/megalinter@v7/gm, replacement: `oxsecurity/megalinter@${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter@v7", testRes: `oxsecurity/megalinter@${DEFAULT_RELEASE}`, }, // Docker images using main flavor { regex: /oxsecurity\/megalinter:v7\.(.*)/gm, replacement: `oxsecurity/megalinter:${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter:v7.2.4", testRes: `oxsecurity/megalinter:${DEFAULT_RELEASE}`, }, { regex: /oxsecurity\/megalinter:v7/gm, replacement: `oxsecurity/megalinter:${DEFAULT_RELEASE}`, test: "oxsecurity/megalinter:v7", testRes: `oxsecurity/megalinter:${DEFAULT_RELEASE}`, }, // Create pull request action { regex: /peter-evans\/create-pull-request@v(3|4|5)/gm, replacement: `peter-evans/create-pull-request@v6`, test: "peter-evans/create-pull-request@v5", testRes: `peter-evans/create-pull-request@v6`, }, // Auto-commit action { regex: /stefanzweifel\/git-auto-commit-action@v(2|3|4)/gm, replacement: `stefanzweifel/git-auto-commit-action@v5`, test: "stefanzweifel/git-auto-commit-action@v4", testRes: `stefanzweifel/git-auto-commit-action@v5`, }, // All remaining cases... cross fingers :) { regex: /megalinter\/megalinter/gm, replacement: "oxsecurity/megalinter", test: "wesh megalinter/megalinter", testRes: "wesh oxsecurity/megalinter", }, ]; } async run() { console.log(asciiArt()); const promptsUpgradeRes = await prompts({ name: "upgrade", message: c.blueBright( `This assistant will automatically upgrade your local files so you use MegaLinter ${DEFAULT_RELEASE}\nPlease confirm to proceed :)` ), type: "confirm", initial: true, }); if (promptsUpgradeRes.upgrade === false) { console.log( `You should upgrade to ${DEFAULT_RELEASE} to benefit from latest versions of linters, and more features :)` ); return; } // Update local files await this.applyReplacements(); this.manageGitIgnore(); console.log(""); console.log( c.green("You MegaLinter local configuration files has been updated !") ); console.log( c.grey( "Now stage and commit updated files then push to see latest version of MegaLinter in action !" ) ); console.log(""); // Propose to try ox service const promptsOXRes = await prompts({ name: "ox", message: c.blueBright( `MegaLinter is now part of ${c.green( "OX Security" )}. -> https://www.ox.security/?ref=megalinter\n\nDo you want to try OX Security to secure your software supply chain security ?` ), type: "confirm", initial: true, }); if (promptsOXRes.ox === true) { new OXSecuritySetup().run(); } } async applyReplacements() { // List yaml and shell files const globPattern1 = `**/*.{yaml,yml,sh,bash}`; const files1 = await glob(globPattern1, { cwd: process.cwd(), dot: true, ignore: GLOB_IGNORE_PATTERNS }); // List Jenkinsfile const globPattern2 = `**/Jenkinsfile`; const files2 = await glob(globPattern2, { cwd: process.cwd(), dot: true, ignore: GLOB_IGNORE_PATTERNS }); // Analyze all files and make appropriate replacements const allFiles = files1.concat(files2); let appliedReplacements = 0; let updatedFiles = 0; for (const file of allFiles) { console.log(c.grey("Processing file " + file)); const initialFileContent = await fs.readFile(file, "utf8"); let updatedFileContent = initialFileContent.slice(); for (const replacementItem of this.replacements) { const newFileContent = updatedFileContent.replace( replacementItem.regex, replacementItem.replacement ); if (newFileContent !== updatedFileContent) { console.log( `- Updating ${file} with replacement ${replacementItem.regex} -> ${replacementItem.replacement} ...` ); updatedFileContent = newFileContent; appliedReplacements++; } } if (updatedFileContent !== initialFileContent) { await fs.writeFile(file, updatedFileContent); console.log(c.cyan(`UPDATED: ${file}`)); updatedFiles++; } } console.log( c.bold( `mega-linter-runner applied ${c.green( appliedReplacements )} replacements in ${c.green(updatedFiles)} files.` ) ); } // Create or update .gitignore files manageGitIgnore() { const gitIgnoreFile = path.join(process.cwd(), ".gitignore"); let gitIgnoreTextLines = []; let doWrite = false; if (fs.existsSync(gitIgnoreFile)) { gitIgnoreTextLines = fs .readFileSync(gitIgnoreFile, "utf8") .split(/\r?\n/); } if (!gitIgnoreTextLines.includes("megalinter-reports/")) { gitIgnoreTextLines.push("megalinter-reports/"); doWrite = true; } if (doWrite) { fs.writeFileSync(gitIgnoreFile, gitIgnoreTextLines.join("\n") + "\n"); console.log( "Updated .gitignore file to exclude megalinter-reports from commits" ); } } }