diff --git a/package-lock.json b/package-lock.json index 1b53315..274ada8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,8 @@ "@types/tmp": "^0.2.6", "@types/which": "^3.0.4", "tmp": "^0.2.5", - "which": "^5.0.0" + "which": "^5.0.0", + "yaml": "^2.8.1" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^8.46.2", @@ -4408,6 +4409,18 @@ "node": ">=4.0" } }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 24598c4..d381a47 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,8 @@ "@types/tmp": "^0.2.6", "@types/which": "^3.0.4", "tmp": "^0.2.5", - "which": "^5.0.0" + "which": "^5.0.0", + "yaml": "^2.8.1" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^8.46.2", diff --git a/src/plugins.ts b/src/plugins.ts new file mode 100644 index 0000000..a8b6c67 --- /dev/null +++ b/src/plugins.ts @@ -0,0 +1,90 @@ +import * as core from "@actions/core" +import { exec, ExecOptionsWithStringEncoding } from "child_process" +import * as fs from "fs" +import * as path from "path" +import { promisify } from "util" +import YAML from "yaml" + +const execShellCommand = promisify(exec) + +type ExecRes = { + stdout: string + stderr: string +} + +const printOutput = (res: ExecRes): void => { + if (res.stdout) { + core.info(res.stdout) + } + if (res.stderr) { + core.info(res.stderr) + } +} + +export async function install(binPath: string): Promise { + let rootDir = core.getInput(`working-directory`) + + if (rootDir) { + if (!fs.existsSync(rootDir) || !fs.lstatSync(rootDir).isDirectory()) { + throw new Error(`working-directory (${rootDir}) was not a path`) + } + + rootDir = path.resolve(rootDir) + } else { + rootDir = process.cwd() + } + + const configFile = [".custom-gcl.yml", ".custom-gcl.yaml", ".custom-gcl.json"] + .map((v) => path.join(rootDir, v)) + .find((filePath) => fs.existsSync(filePath)) + + if (!configFile || configFile === "") { + return "" + } + + core.info(`Found configuration for the plugin module system : ${configFile}`) + + core.info(`Building and installing custom golangci-lint binary...`) + + const startedAt = Date.now() + + const config = YAML.parse(fs.readFileSync(configFile, "utf-8")) + + const v: string = core.getInput(`version`) + if (v !== "" && config.version !== v) { + core.warning( + `The golangci-lint version (${config.version}) defined inside in ${configFile} does not match the version defined in the action (${v})` + ) + } + + if (!config.destination) { + config.destination = "." + } + if (!config.name) { + config.name = "custom-gcl" + } + + if (!fs.existsSync(config.destination)) { + core.info(`Creating destination directory: ${config.destination}`) + fs.mkdirSync(config.destination, { recursive: true }) + } + + const cmd = `${binPath} custom` + + core.info(`Running [${cmd}] in [${rootDir}] ...`) + + try { + const options: ExecOptionsWithStringEncoding = { + cwd: rootDir, + } + + const res = await execShellCommand(cmd, options) + printOutput(res) + + core.info(`Built custom golangci-lint binary in ${Date.now() - startedAt}ms`) + + return path.join(rootDir, config.destination, config.name) + } catch (exc) { + throw new Error(`Failed to build custom golangci-lint binary: ${exc.message}`) + } +} diff --git a/src/run.ts b/src/run.ts index 52ea0f0..8687b1e 100644 --- a/src/run.ts +++ b/src/run.ts @@ -8,6 +8,7 @@ import { promisify } from "util" import { restoreCache, saveCache } from "./cache" import { install } from "./install" import { fetchPatch, isOnlyNewIssues } from "./patch" +import * as plugins from "./plugins" const execShellCommand = promisify(exec) @@ -22,7 +23,13 @@ async function prepareEnv(installOnly: boolean): Promise { // Prepare cache, lint and go in parallel. await restoreCache() - const binPath = await install() + let binPath = await install() + + // Build custom golangci-lint if needed. + const customBinPath = await plugins.install(binPath) + if (customBinPath !== "") { + binPath = customBinPath + } if (installOnly) { return { binPath, patchPath: `` } @@ -203,9 +210,7 @@ export async function run(): Promise { try { const installOnly = core.getBooleanInput(`install-only`, { required: true }) - const { binPath, patchPath } = await core.group(`prepare environment`, () => { - return prepareEnv(installOnly) - }) + const { binPath, patchPath } = await core.group(`prepare environment`, () => prepareEnv(installOnly)) core.addPath(path.dirname(binPath))