diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 0dd5127023..5fa489cd3f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -146,3 +146,30 @@ jobs:
version: ${{ matrix.version }}
working-directory: sample-plugins
args: --timeout=5m --issues-exit-code=0 ./...
+
+ test-monorepo:
+ needs: [ build ]
+ strategy:
+ matrix:
+ os:
+ - ubuntu-latest
+ - ubuntu-22.04-arm
+ - macos-latest
+ - windows-latest
+ runs-on: ${{ matrix.os }}
+ permissions:
+ contents: read
+ pull-requests: read
+ steps:
+ - uses: actions/checkout@v5
+ - uses: actions/setup-node@v6
+ with:
+ node-version: 24.x
+ - uses: actions/setup-go@v6
+ with:
+ go-version: oldstable
+ - uses: ./
+ with:
+ working-directory: sample-monorepo
+ experimental: "automatic-module-directories"
+ args: --timeout=5m --issues-exit-code=0 ./...
diff --git a/README.md b/README.md
index 9551bf4998..4d6e8b30d9 100644
--- a/README.md
+++ b/README.md
@@ -275,6 +275,7 @@ You will also likely need to add the following `.gitattributes` file to ensure t
| [`skip-save-cache`](#skip-save-cache) | Don't save cache. |
| [`cache-invalidation-interval`](#cache-invalidation-interval) | Number of days before cache invalidation. |
| [`problem-matchers`](#problem-matchers) | Forces the usage of the embedded problem matchers. |
+| [Experimental](#experimental) | Experimental options |
### Installation
@@ -552,6 +553,47 @@ with:
+### Experimental
+
+The following options are experimental: those may or may not be supported in the future, and so they will be either converted into a dedicated option or removed.
+
+List of comma-separated options.
+
+
+Example
+
+```yaml
+uses: golangci/golangci-lint-action@v9
+with:
+ experimental: "foo,bar"
+```
+
+
+
+#### `automatic-module-directories`
+
+(optional)
+
+This option will run golangci-lint in each module directory, useful for monorepos.
+
+The automatic detection of modules uses the `working-directory` as the base directory if defined, otherwise the root directory.
+
+> [!IMPORTANT]
+> - The cache key will refer to the `working-directory` (if defined) because all the golangci-lint runs must use the same cache directory/key.
+> - The version detection will only work if the project has a single module.
+> - If the project has multiple modules, the custom build file must be located in the repository root ( or `working-directory`).
+
+
+Example
+
+```yaml
+uses: golangci/golangci-lint-action@v9
+with:
+ experimental: "automatic-module-directories"
+```
+
+
+
## Annotations
Currently, GitHub parses the action's output and creates [annotations](https://github.blog/2018-12-14-introducing-check-runs-and-annotations/).
diff --git a/action.yml b/action.yml
index cb3120c094..4fedce7ceb 100644
--- a/action.yml
+++ b/action.yml
@@ -65,6 +65,12 @@ inputs:
example: "cache,clean"
default: ""
required: false
+ experimental:
+ description: |
+ Experimental options for the action.
+ List of comma separated options.
+ default: ""
+ required: false
runs:
using: "node24"
main: "dist/run/index.js"
diff --git a/dist/post_run/index.js b/dist/post_run/index.js
index 957503db6f..f86b54ad45 100644
--- a/dist/post_run/index.js
+++ b/dist/post_run/index.js
@@ -96844,8 +96844,10 @@ async function buildCacheKeys() {
return keys;
}
async function restoreCache() {
- if (core.getBooleanInput(`skip-cache`, { required: true }))
+ if (core.getBooleanInput(`skip-cache`, { required: true })) {
+ core.info(`Skipping cache restoration`);
return;
+ }
if (!utils.isValidEvent()) {
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
return;
@@ -96881,10 +96883,14 @@ async function restoreCache() {
}
}
async function saveCache() {
- if (core.getBooleanInput(`skip-cache`, { required: true }))
+ if (core.getBooleanInput(`skip-cache`, { required: true })) {
+ core.info(`Skipping cache saving`);
return;
- if (core.getBooleanInput(`skip-save-cache`, { required: true }))
+ }
+ if (core.getBooleanInput(`skip-save-cache`, { required: true })) {
+ core.info(`Skipping cache saving`);
return;
+ }
// Validate inputs, this can cause task failure
if (!utils.isValidEvent()) {
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
@@ -96999,12 +97005,13 @@ exports.installBinary = installBinary;
const core = __importStar(__nccwpck_require__(37484));
const tc = __importStar(__nccwpck_require__(33472));
const child_process_1 = __nccwpck_require__(35317);
+const fs_1 = __importDefault(__nccwpck_require__(79896));
const os_1 = __importDefault(__nccwpck_require__(70857));
const path_1 = __importDefault(__nccwpck_require__(16928));
const util_1 = __nccwpck_require__(39023);
const which_1 = __importDefault(__nccwpck_require__(11189));
const version_1 = __nccwpck_require__(311);
-const execShellCommand = (0, util_1.promisify)(child_process_1.exec);
+const execCommand = (0, util_1.promisify)(child_process_1.exec);
var InstallMode;
(function (InstallMode) {
InstallMode["Binary"] = "binary";
@@ -97018,6 +97025,7 @@ const printOutput = (res) => {
if (res.stderr) {
core.info(res.stderr);
}
+ return res;
};
/**
* Install golangci-lint.
@@ -97025,6 +97033,15 @@ const printOutput = (res) => {
* @returns path to installed binary of golangci-lint.
*/
async function install() {
+ const problemMatchers = core.getBooleanInput(`problem-matchers`);
+ if (problemMatchers) {
+ const matchersPath = path_1.default.join(__dirname, "../..", "problem-matchers.json");
+ if (fs_1.default.existsSync(matchersPath)) {
+ // Adds problem matchers.
+ // https://github.com/actions/setup-go/blob/cdcb36043654635271a94b9a6d1392de5bb323a7/src/main.ts#L81-L83
+ core.info(`##[add-matcher]${matchersPath}`);
+ }
+ }
const mode = core.getInput("install-mode").toLowerCase();
if (mode === InstallMode.None) {
const binPath = await (0, which_1.default)("golangci-lint", { nothrow: true });
@@ -97064,10 +97081,8 @@ async function goInstall(versionInfo) {
core.info(`Installing golangci-lint ${versionInfo.TargetVersion}...`);
const startedAt = Date.now();
const options = { env: { ...process.env, CGO_ENABLED: "1" } };
- const exres = await execShellCommand(`go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${versionInfo.TargetVersion}`, options);
- printOutput(exres);
- const res = await execShellCommand(`go install -n github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${versionInfo.TargetVersion}`, options);
- printOutput(res);
+ await execCommand(`go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${versionInfo.TargetVersion}`, options).then(printOutput);
+ const res = await execCommand(`go install -n github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${versionInfo.TargetVersion}`, options).then(printOutput);
// The output of `go install -n` when the binary is already installed is `touch `.
const binPath = res.stderr
.split(/\r?\n/)
@@ -97196,9 +97211,6 @@ function isOnlyNewIssues() {
return core.getBooleanInput(`only-new-issues`, { required: true });
}
async function fetchPatch() {
- if (!isOnlyNewIssues()) {
- return ``;
- }
const ctx = github.context;
switch (ctx.eventName) {
case `pull_request`:
@@ -97242,8 +97254,7 @@ async function fetchPullRequestPatch(ctx) {
return ``; // don't fail the action, but analyze without patch
}
try {
- const tempDir = await createTempDir();
- const patchPath = path_1.default.join(tempDir, "pull.patch");
+ const patchPath = await createTempDir().then((tempDir) => path_1.default.join(tempDir, "pull.patch"));
core.info(`Writing patch to ${patchPath}`);
await writeFile(patchPath, (0, diffUtils_1.alterDiffPatch)(patch));
return patchPath;
@@ -97277,8 +97288,7 @@ async function fetchPushPatch(ctx) {
return ``; // don't fail the action, but analyze without patch
}
try {
- const tempDir = await createTempDir();
- const patchPath = path_1.default.join(tempDir, "push.patch");
+ const patchPath = await createTempDir().then((tempDir) => path_1.default.join(tempDir, "push.patch"));
core.info(`Writing patch to ${patchPath}`);
await writeFile(patchPath, (0, diffUtils_1.alterDiffPatch)(patch));
return patchPath;
@@ -97341,7 +97351,7 @@ const fs = __importStar(__nccwpck_require__(79896));
const path = __importStar(__nccwpck_require__(16928));
const util_1 = __nccwpck_require__(39023);
const yaml_1 = __importDefault(__nccwpck_require__(38815));
-const execShellCommand = (0, util_1.promisify)(child_process_1.exec);
+const execCommand = (0, util_1.promisify)(child_process_1.exec);
const printOutput = (res) => {
if (res.stdout) {
core.info(res.stdout);
@@ -97366,7 +97376,7 @@ async function install(binPath) {
.map((filename) => path.join(rootDir, filename))
.find((filePath) => fs.existsSync(filePath));
if (!configFile || configFile === "") {
- return "";
+ return binPath;
}
core.info(`Found configuration for the plugin module system : ${configFile}`);
core.info(`Building and installing custom golangci-lint binary...`);
@@ -97388,18 +97398,16 @@ async function install(binPath) {
}
const cmd = `${binPath} custom`;
core.info(`Running [${cmd}] in [${rootDir}] ...`);
- try {
- const options = {
- 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) {
+ const options = {
+ cwd: rootDir,
+ };
+ return execCommand(cmd, options)
+ .then(printOutput)
+ .then(() => core.info(`Built custom golangci-lint binary in ${Date.now() - startedAt}ms`))
+ .then(() => path.join(rootDir, config.destination, config.name))
+ .catch((exc) => {
throw new Error(`Failed to build custom golangci-lint binary: ${exc.message}`);
- }
+ });
}
@@ -97456,24 +97464,7 @@ const cache_1 = __nccwpck_require__(97377);
const install_1 = __nccwpck_require__(90232);
const patch_1 = __nccwpck_require__(47161);
const plugins = __importStar(__nccwpck_require__(96067));
-const execShellCommand = (0, util_1.promisify)(child_process_1.exec);
-async function prepareEnv(installOnly) {
- const startedAt = Date.now();
- // Prepare cache, lint and go in parallel.
- await (0, cache_1.restoreCache)();
- let binPath = await (0, install_1.install)();
- // Build custom golangci-lint if needed.
- const customBinPath = await plugins.install(binPath);
- if (customBinPath !== "") {
- binPath = customBinPath;
- }
- if (installOnly) {
- return { binPath, patchPath: `` };
- }
- const patchPath = await (0, patch_1.fetchPatch)();
- core.info(`Prepared env in ${Date.now() - startedAt}ms`);
- return { binPath, patchPath };
-}
+const execCommand = (0, util_1.promisify)(child_process_1.exec);
const printOutput = (res) => {
if (res.stdout) {
core.info(res.stdout);
@@ -97482,12 +97473,7 @@ const printOutput = (res) => {
core.info(res.stderr);
}
};
-async function runLint(binPath, patchPath) {
- const debug = core.getInput(`debug`);
- if (debug.split(`,`).includes(`cache`)) {
- const res = await execShellCommand(`${binPath} cache status`);
- printOutput(res);
- }
+async function runGolangciLint(binPath, rootDir) {
const userArgs = core.getInput(`args`);
const addedArgs = [];
const userArgsList = userArgs
@@ -97499,15 +97485,6 @@ async function runLint(binPath, patchPath) {
.map(([key, value]) => [key.toLowerCase(), value ?? ""]);
const userArgsMap = new Map(userArgsList);
const userArgNames = new Set(userArgsList.map(([key]) => key));
- const problemMatchers = core.getBooleanInput(`problem-matchers`);
- if (problemMatchers) {
- const matchersPath = path.join(__dirname, "../..", "problem-matchers.json");
- if (fs.existsSync(matchersPath)) {
- // Adds problem matchers.
- // https://github.com/actions/setup-go/blob/cdcb36043654635271a94b9a6d1392de5bb323a7/src/main.ts#L81-L83
- core.info(`##[add-matcher]${matchersPath}`);
- }
- }
if ((0, patch_1.isOnlyNewIssues)()) {
if (userArgNames.has(`new`) ||
userArgNames.has(`new-from-rev`) ||
@@ -97516,6 +97493,7 @@ async function runLint(binPath, patchPath) {
throw new Error(`please, don't specify manually --new* args when requesting only new issues`);
}
const ctx = github.context;
+ const patchPath = await (0, patch_1.fetchPatch)();
core.info(`only new issues on ${ctx.eventName}: ${patchPath}`);
switch (ctx.eventName) {
case `pull_request`:
@@ -97541,27 +97519,21 @@ async function runLint(binPath, patchPath) {
}
}
const cmdArgs = {};
- const workingDirectory = core.getInput(`working-directory`);
- if (workingDirectory) {
- if (!fs.existsSync(workingDirectory) || !fs.lstatSync(workingDirectory).isDirectory()) {
- throw new Error(`working-directory (${workingDirectory}) was not a path`);
- }
+ if (rootDir) {
if (!userArgNames.has(`path-prefix`) && !userArgNames.has(`path-mode`)) {
addedArgs.push(`--path-mode=abs`);
}
- cmdArgs.cwd = path.resolve(workingDirectory);
+ cmdArgs.cwd = path.resolve(rootDir);
}
await runVerify(binPath, userArgsMap, cmdArgs);
const cmd = `${binPath} run ${addedArgs.join(` `)} ${userArgs}`.trimEnd();
core.info(`Running [${cmd}] in [${cmdArgs.cwd || process.cwd()}] ...`);
const startedAt = Date.now();
- try {
- const res = await execShellCommand(cmd, cmdArgs);
- printOutput(res);
- core.info(`golangci-lint found no issues`);
- }
- catch (exc) {
- // This logging passes issues to GitHub annotations but comments can be more convenient for some users.
+ return execCommand(cmd, cmdArgs)
+ .then(printOutput)
+ .then(() => core.info(`golangci-lint found no issues`))
+ .catch((exc) => {
+ // This logging passes issues to GitHub annotations.
printOutput(exc);
if (exc.code === 1) {
core.setFailed(`issues found`);
@@ -97569,8 +97541,8 @@ async function runLint(binPath, patchPath) {
else {
core.setFailed(`golangci-lint exit with code ${exc.code}`);
}
- }
- core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`);
+ })
+ .finally(() => core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`));
}
async function runVerify(binPath, userArgsMap, cmdArgs) {
const verify = core.getBooleanInput(`verify`, { required: true });
@@ -97586,8 +97558,7 @@ async function runVerify(binPath, userArgsMap, cmdArgs) {
cmdVerify += ` --config=${userArgsMap.get("config")}`;
}
core.info(`Running [${cmdVerify}] in [${cmdArgs.cwd || process.cwd()}] ...`);
- const res = await execShellCommand(cmdVerify, cmdArgs);
- printOutput(res);
+ await execCommand(cmdVerify, cmdArgs).then(printOutput);
}
async function getConfigPath(binPath, userArgsMap, cmdArgs) {
let cmdConfigPath = `${binPath} config path`;
@@ -97596,22 +97567,73 @@ async function getConfigPath(binPath, userArgsMap, cmdArgs) {
}
core.info(`Running [${cmdConfigPath}] in [${cmdArgs.cwd || process.cwd()}] ...`);
try {
- const resPath = await execShellCommand(cmdConfigPath, cmdArgs);
+ const resPath = await execCommand(cmdConfigPath, cmdArgs);
return resPath.stderr.trim();
}
catch {
return ``;
}
}
+async function debugAction(binPath) {
+ const flags = core.getInput(`debug`).split(`,`);
+ if (flags.includes(`clean`)) {
+ const cmd = `${binPath} cache clean`;
+ core.info(`Running [${cmd}] ...`);
+ await execCommand(cmd).then(printOutput);
+ }
+ if (flags.includes(`cache`)) {
+ const cmd = `${binPath} cache status`;
+ core.info(`Running [${cmd}] ...`);
+ await execCommand(cmd).then(printOutput);
+ }
+}
+function getWorkingDirectory() {
+ const workingDirectory = core.getInput(`working-directory`);
+ if (workingDirectory) {
+ if (!fs.existsSync(workingDirectory) || !fs.lstatSync(workingDirectory).isDirectory()) {
+ throw new Error(`working-directory (${workingDirectory}) was not a path`);
+ }
+ }
+ return workingDirectory;
+}
+function modulesAutoDetection(rootDir) {
+ const o = {
+ cwd: rootDir,
+ exclude: ["**/vendor/**", "**/node_modules/**", "**/.git/**", "**/dist/**"],
+ };
+ const matches = fs.globSync("**/go.mod", o);
+ const dirs = matches
+ .filter((m) => typeof m === "string")
+ .map((m) => path.resolve(rootDir, path.dirname(m)))
+ .sort();
+ return [...new Set(dirs)];
+}
+async function runLint(binPath) {
+ const workingDirectory = getWorkingDirectory();
+ const experimental = core.getInput(`experimental`).split(`,`);
+ if (experimental.includes(`automatic-module-directories`)) {
+ const wds = modulesAutoDetection(workingDirectory);
+ const cwd = process.cwd();
+ for (const wd of wds) {
+ await core.group(`run golangci-lint in ${path.relative(cwd, wd)}`, () => runGolangciLint(binPath, wd));
+ }
+ return;
+ }
+ await core.group(`run golangci-lint`, () => runGolangciLint(binPath, workingDirectory));
+}
async function run() {
try {
- const installOnly = core.getBooleanInput(`install-only`, { required: true });
- const { binPath, patchPath } = await core.group(`prepare environment`, () => prepareEnv(installOnly));
+ await core.group(`Restore cache`, cache_1.restoreCache);
+ const binPath = await core.group(`Install`, () => (0, install_1.install)().then(plugins.install));
core.addPath(path.dirname(binPath));
+ if (core.getInput(`debug`)) {
+ await core.group(`Debug`, () => debugAction(binPath));
+ }
+ const installOnly = core.getBooleanInput(`install-only`, { required: true });
if (installOnly) {
return;
}
- await core.group(`run golangci-lint`, () => runLint(binPath, patchPath));
+ await runLint(binPath);
}
catch (error) {
core.error(`Failed to run: ${error}, ${error.stack}`);
diff --git a/dist/run/index.js b/dist/run/index.js
index 073f718316..98236e9dcf 100644
--- a/dist/run/index.js
+++ b/dist/run/index.js
@@ -96844,8 +96844,10 @@ async function buildCacheKeys() {
return keys;
}
async function restoreCache() {
- if (core.getBooleanInput(`skip-cache`, { required: true }))
+ if (core.getBooleanInput(`skip-cache`, { required: true })) {
+ core.info(`Skipping cache restoration`);
return;
+ }
if (!utils.isValidEvent()) {
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
return;
@@ -96881,10 +96883,14 @@ async function restoreCache() {
}
}
async function saveCache() {
- if (core.getBooleanInput(`skip-cache`, { required: true }))
+ if (core.getBooleanInput(`skip-cache`, { required: true })) {
+ core.info(`Skipping cache saving`);
return;
- if (core.getBooleanInput(`skip-save-cache`, { required: true }))
+ }
+ if (core.getBooleanInput(`skip-save-cache`, { required: true })) {
+ core.info(`Skipping cache saving`);
return;
+ }
// Validate inputs, this can cause task failure
if (!utils.isValidEvent()) {
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
@@ -96999,12 +97005,13 @@ exports.installBinary = installBinary;
const core = __importStar(__nccwpck_require__(37484));
const tc = __importStar(__nccwpck_require__(33472));
const child_process_1 = __nccwpck_require__(35317);
+const fs_1 = __importDefault(__nccwpck_require__(79896));
const os_1 = __importDefault(__nccwpck_require__(70857));
const path_1 = __importDefault(__nccwpck_require__(16928));
const util_1 = __nccwpck_require__(39023);
const which_1 = __importDefault(__nccwpck_require__(11189));
const version_1 = __nccwpck_require__(311);
-const execShellCommand = (0, util_1.promisify)(child_process_1.exec);
+const execCommand = (0, util_1.promisify)(child_process_1.exec);
var InstallMode;
(function (InstallMode) {
InstallMode["Binary"] = "binary";
@@ -97018,6 +97025,7 @@ const printOutput = (res) => {
if (res.stderr) {
core.info(res.stderr);
}
+ return res;
};
/**
* Install golangci-lint.
@@ -97025,6 +97033,15 @@ const printOutput = (res) => {
* @returns path to installed binary of golangci-lint.
*/
async function install() {
+ const problemMatchers = core.getBooleanInput(`problem-matchers`);
+ if (problemMatchers) {
+ const matchersPath = path_1.default.join(__dirname, "../..", "problem-matchers.json");
+ if (fs_1.default.existsSync(matchersPath)) {
+ // Adds problem matchers.
+ // https://github.com/actions/setup-go/blob/cdcb36043654635271a94b9a6d1392de5bb323a7/src/main.ts#L81-L83
+ core.info(`##[add-matcher]${matchersPath}`);
+ }
+ }
const mode = core.getInput("install-mode").toLowerCase();
if (mode === InstallMode.None) {
const binPath = await (0, which_1.default)("golangci-lint", { nothrow: true });
@@ -97064,10 +97081,8 @@ async function goInstall(versionInfo) {
core.info(`Installing golangci-lint ${versionInfo.TargetVersion}...`);
const startedAt = Date.now();
const options = { env: { ...process.env, CGO_ENABLED: "1" } };
- const exres = await execShellCommand(`go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${versionInfo.TargetVersion}`, options);
- printOutput(exres);
- const res = await execShellCommand(`go install -n github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${versionInfo.TargetVersion}`, options);
- printOutput(res);
+ await execCommand(`go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${versionInfo.TargetVersion}`, options).then(printOutput);
+ const res = await execCommand(`go install -n github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${versionInfo.TargetVersion}`, options).then(printOutput);
// The output of `go install -n` when the binary is already installed is `touch `.
const binPath = res.stderr
.split(/\r?\n/)
@@ -97196,9 +97211,6 @@ function isOnlyNewIssues() {
return core.getBooleanInput(`only-new-issues`, { required: true });
}
async function fetchPatch() {
- if (!isOnlyNewIssues()) {
- return ``;
- }
const ctx = github.context;
switch (ctx.eventName) {
case `pull_request`:
@@ -97242,8 +97254,7 @@ async function fetchPullRequestPatch(ctx) {
return ``; // don't fail the action, but analyze without patch
}
try {
- const tempDir = await createTempDir();
- const patchPath = path_1.default.join(tempDir, "pull.patch");
+ const patchPath = await createTempDir().then((tempDir) => path_1.default.join(tempDir, "pull.patch"));
core.info(`Writing patch to ${patchPath}`);
await writeFile(patchPath, (0, diffUtils_1.alterDiffPatch)(patch));
return patchPath;
@@ -97277,8 +97288,7 @@ async function fetchPushPatch(ctx) {
return ``; // don't fail the action, but analyze without patch
}
try {
- const tempDir = await createTempDir();
- const patchPath = path_1.default.join(tempDir, "push.patch");
+ const patchPath = await createTempDir().then((tempDir) => path_1.default.join(tempDir, "push.patch"));
core.info(`Writing patch to ${patchPath}`);
await writeFile(patchPath, (0, diffUtils_1.alterDiffPatch)(patch));
return patchPath;
@@ -97341,7 +97351,7 @@ const fs = __importStar(__nccwpck_require__(79896));
const path = __importStar(__nccwpck_require__(16928));
const util_1 = __nccwpck_require__(39023);
const yaml_1 = __importDefault(__nccwpck_require__(38815));
-const execShellCommand = (0, util_1.promisify)(child_process_1.exec);
+const execCommand = (0, util_1.promisify)(child_process_1.exec);
const printOutput = (res) => {
if (res.stdout) {
core.info(res.stdout);
@@ -97366,7 +97376,7 @@ async function install(binPath) {
.map((filename) => path.join(rootDir, filename))
.find((filePath) => fs.existsSync(filePath));
if (!configFile || configFile === "") {
- return "";
+ return binPath;
}
core.info(`Found configuration for the plugin module system : ${configFile}`);
core.info(`Building and installing custom golangci-lint binary...`);
@@ -97388,18 +97398,16 @@ async function install(binPath) {
}
const cmd = `${binPath} custom`;
core.info(`Running [${cmd}] in [${rootDir}] ...`);
- try {
- const options = {
- 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) {
+ const options = {
+ cwd: rootDir,
+ };
+ return execCommand(cmd, options)
+ .then(printOutput)
+ .then(() => core.info(`Built custom golangci-lint binary in ${Date.now() - startedAt}ms`))
+ .then(() => path.join(rootDir, config.destination, config.name))
+ .catch((exc) => {
throw new Error(`Failed to build custom golangci-lint binary: ${exc.message}`);
- }
+ });
}
@@ -97456,24 +97464,7 @@ const cache_1 = __nccwpck_require__(97377);
const install_1 = __nccwpck_require__(90232);
const patch_1 = __nccwpck_require__(47161);
const plugins = __importStar(__nccwpck_require__(96067));
-const execShellCommand = (0, util_1.promisify)(child_process_1.exec);
-async function prepareEnv(installOnly) {
- const startedAt = Date.now();
- // Prepare cache, lint and go in parallel.
- await (0, cache_1.restoreCache)();
- let binPath = await (0, install_1.install)();
- // Build custom golangci-lint if needed.
- const customBinPath = await plugins.install(binPath);
- if (customBinPath !== "") {
- binPath = customBinPath;
- }
- if (installOnly) {
- return { binPath, patchPath: `` };
- }
- const patchPath = await (0, patch_1.fetchPatch)();
- core.info(`Prepared env in ${Date.now() - startedAt}ms`);
- return { binPath, patchPath };
-}
+const execCommand = (0, util_1.promisify)(child_process_1.exec);
const printOutput = (res) => {
if (res.stdout) {
core.info(res.stdout);
@@ -97482,12 +97473,7 @@ const printOutput = (res) => {
core.info(res.stderr);
}
};
-async function runLint(binPath, patchPath) {
- const debug = core.getInput(`debug`);
- if (debug.split(`,`).includes(`cache`)) {
- const res = await execShellCommand(`${binPath} cache status`);
- printOutput(res);
- }
+async function runGolangciLint(binPath, rootDir) {
const userArgs = core.getInput(`args`);
const addedArgs = [];
const userArgsList = userArgs
@@ -97499,15 +97485,6 @@ async function runLint(binPath, patchPath) {
.map(([key, value]) => [key.toLowerCase(), value ?? ""]);
const userArgsMap = new Map(userArgsList);
const userArgNames = new Set(userArgsList.map(([key]) => key));
- const problemMatchers = core.getBooleanInput(`problem-matchers`);
- if (problemMatchers) {
- const matchersPath = path.join(__dirname, "../..", "problem-matchers.json");
- if (fs.existsSync(matchersPath)) {
- // Adds problem matchers.
- // https://github.com/actions/setup-go/blob/cdcb36043654635271a94b9a6d1392de5bb323a7/src/main.ts#L81-L83
- core.info(`##[add-matcher]${matchersPath}`);
- }
- }
if ((0, patch_1.isOnlyNewIssues)()) {
if (userArgNames.has(`new`) ||
userArgNames.has(`new-from-rev`) ||
@@ -97516,6 +97493,7 @@ async function runLint(binPath, patchPath) {
throw new Error(`please, don't specify manually --new* args when requesting only new issues`);
}
const ctx = github.context;
+ const patchPath = await (0, patch_1.fetchPatch)();
core.info(`only new issues on ${ctx.eventName}: ${patchPath}`);
switch (ctx.eventName) {
case `pull_request`:
@@ -97541,27 +97519,21 @@ async function runLint(binPath, patchPath) {
}
}
const cmdArgs = {};
- const workingDirectory = core.getInput(`working-directory`);
- if (workingDirectory) {
- if (!fs.existsSync(workingDirectory) || !fs.lstatSync(workingDirectory).isDirectory()) {
- throw new Error(`working-directory (${workingDirectory}) was not a path`);
- }
+ if (rootDir) {
if (!userArgNames.has(`path-prefix`) && !userArgNames.has(`path-mode`)) {
addedArgs.push(`--path-mode=abs`);
}
- cmdArgs.cwd = path.resolve(workingDirectory);
+ cmdArgs.cwd = path.resolve(rootDir);
}
await runVerify(binPath, userArgsMap, cmdArgs);
const cmd = `${binPath} run ${addedArgs.join(` `)} ${userArgs}`.trimEnd();
core.info(`Running [${cmd}] in [${cmdArgs.cwd || process.cwd()}] ...`);
const startedAt = Date.now();
- try {
- const res = await execShellCommand(cmd, cmdArgs);
- printOutput(res);
- core.info(`golangci-lint found no issues`);
- }
- catch (exc) {
- // This logging passes issues to GitHub annotations but comments can be more convenient for some users.
+ return execCommand(cmd, cmdArgs)
+ .then(printOutput)
+ .then(() => core.info(`golangci-lint found no issues`))
+ .catch((exc) => {
+ // This logging passes issues to GitHub annotations.
printOutput(exc);
if (exc.code === 1) {
core.setFailed(`issues found`);
@@ -97569,8 +97541,8 @@ async function runLint(binPath, patchPath) {
else {
core.setFailed(`golangci-lint exit with code ${exc.code}`);
}
- }
- core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`);
+ })
+ .finally(() => core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`));
}
async function runVerify(binPath, userArgsMap, cmdArgs) {
const verify = core.getBooleanInput(`verify`, { required: true });
@@ -97586,8 +97558,7 @@ async function runVerify(binPath, userArgsMap, cmdArgs) {
cmdVerify += ` --config=${userArgsMap.get("config")}`;
}
core.info(`Running [${cmdVerify}] in [${cmdArgs.cwd || process.cwd()}] ...`);
- const res = await execShellCommand(cmdVerify, cmdArgs);
- printOutput(res);
+ await execCommand(cmdVerify, cmdArgs).then(printOutput);
}
async function getConfigPath(binPath, userArgsMap, cmdArgs) {
let cmdConfigPath = `${binPath} config path`;
@@ -97596,22 +97567,73 @@ async function getConfigPath(binPath, userArgsMap, cmdArgs) {
}
core.info(`Running [${cmdConfigPath}] in [${cmdArgs.cwd || process.cwd()}] ...`);
try {
- const resPath = await execShellCommand(cmdConfigPath, cmdArgs);
+ const resPath = await execCommand(cmdConfigPath, cmdArgs);
return resPath.stderr.trim();
}
catch {
return ``;
}
}
+async function debugAction(binPath) {
+ const flags = core.getInput(`debug`).split(`,`);
+ if (flags.includes(`clean`)) {
+ const cmd = `${binPath} cache clean`;
+ core.info(`Running [${cmd}] ...`);
+ await execCommand(cmd).then(printOutput);
+ }
+ if (flags.includes(`cache`)) {
+ const cmd = `${binPath} cache status`;
+ core.info(`Running [${cmd}] ...`);
+ await execCommand(cmd).then(printOutput);
+ }
+}
+function getWorkingDirectory() {
+ const workingDirectory = core.getInput(`working-directory`);
+ if (workingDirectory) {
+ if (!fs.existsSync(workingDirectory) || !fs.lstatSync(workingDirectory).isDirectory()) {
+ throw new Error(`working-directory (${workingDirectory}) was not a path`);
+ }
+ }
+ return workingDirectory;
+}
+function modulesAutoDetection(rootDir) {
+ const o = {
+ cwd: rootDir,
+ exclude: ["**/vendor/**", "**/node_modules/**", "**/.git/**", "**/dist/**"],
+ };
+ const matches = fs.globSync("**/go.mod", o);
+ const dirs = matches
+ .filter((m) => typeof m === "string")
+ .map((m) => path.resolve(rootDir, path.dirname(m)))
+ .sort();
+ return [...new Set(dirs)];
+}
+async function runLint(binPath) {
+ const workingDirectory = getWorkingDirectory();
+ const experimental = core.getInput(`experimental`).split(`,`);
+ if (experimental.includes(`automatic-module-directories`)) {
+ const wds = modulesAutoDetection(workingDirectory);
+ const cwd = process.cwd();
+ for (const wd of wds) {
+ await core.group(`run golangci-lint in ${path.relative(cwd, wd)}`, () => runGolangciLint(binPath, wd));
+ }
+ return;
+ }
+ await core.group(`run golangci-lint`, () => runGolangciLint(binPath, workingDirectory));
+}
async function run() {
try {
- const installOnly = core.getBooleanInput(`install-only`, { required: true });
- const { binPath, patchPath } = await core.group(`prepare environment`, () => prepareEnv(installOnly));
+ await core.group(`Restore cache`, cache_1.restoreCache);
+ const binPath = await core.group(`Install`, () => (0, install_1.install)().then(plugins.install));
core.addPath(path.dirname(binPath));
+ if (core.getInput(`debug`)) {
+ await core.group(`Debug`, () => debugAction(binPath));
+ }
+ const installOnly = core.getBooleanInput(`install-only`, { required: true });
if (installOnly) {
return;
}
- await core.group(`run golangci-lint`, () => runLint(binPath, patchPath));
+ await runLint(binPath);
}
catch (error) {
core.error(`Failed to run: ${error}, ${error.stack}`);
diff --git a/sample-monorepo/a/go.mod b/sample-monorepo/a/go.mod
new file mode 100644
index 0000000000..fa18c999c0
--- /dev/null
+++ b/sample-monorepo/a/go.mod
@@ -0,0 +1,3 @@
+module github.com/golangci/actiona
+
+go 1.24.0
diff --git a/sample-monorepo/a/sample.go b/sample-monorepo/a/sample.go
new file mode 100644
index 0000000000..80504b0df1
--- /dev/null
+++ b/sample-monorepo/a/sample.go
@@ -0,0 +1,26 @@
+// Package sample is used as test input for golangci action.
+package sample
+
+import (
+ "crypto/md5"
+ "encoding/hex"
+ "errors"
+)
+
+// Hash~
+func Hash(data string) string {
+ retError()
+ retError2()
+
+ h := md5.New()
+ h.Write([]byte(data))
+ return hex.EncodeToString(h.Sum(nil))
+}
+
+func retError() error {
+ return errors.New("err")
+}
+
+func retError2() error {
+ return errors.New("err2")
+}
diff --git a/sample-monorepo/a/suba/go.mod b/sample-monorepo/a/suba/go.mod
new file mode 100644
index 0000000000..2f75be60cd
--- /dev/null
+++ b/sample-monorepo/a/suba/go.mod
@@ -0,0 +1,3 @@
+module github.com/golangci/actiona/suba
+
+go 1.24.0
diff --git a/sample-monorepo/a/suba/sample.go b/sample-monorepo/a/suba/sample.go
new file mode 100644
index 0000000000..80504b0df1
--- /dev/null
+++ b/sample-monorepo/a/suba/sample.go
@@ -0,0 +1,26 @@
+// Package sample is used as test input for golangci action.
+package sample
+
+import (
+ "crypto/md5"
+ "encoding/hex"
+ "errors"
+)
+
+// Hash~
+func Hash(data string) string {
+ retError()
+ retError2()
+
+ h := md5.New()
+ h.Write([]byte(data))
+ return hex.EncodeToString(h.Sum(nil))
+}
+
+func retError() error {
+ return errors.New("err")
+}
+
+func retError2() error {
+ return errors.New("err2")
+}
diff --git a/sample-monorepo/b/go.mod b/sample-monorepo/b/go.mod
new file mode 100644
index 0000000000..7be68bcf73
--- /dev/null
+++ b/sample-monorepo/b/go.mod
@@ -0,0 +1,3 @@
+module github.com/golangci/actionb
+
+go 1.24.0
diff --git a/sample-monorepo/b/sample.go b/sample-monorepo/b/sample.go
new file mode 100644
index 0000000000..80504b0df1
--- /dev/null
+++ b/sample-monorepo/b/sample.go
@@ -0,0 +1,26 @@
+// Package sample is used as test input for golangci action.
+package sample
+
+import (
+ "crypto/md5"
+ "encoding/hex"
+ "errors"
+)
+
+// Hash~
+func Hash(data string) string {
+ retError()
+ retError2()
+
+ h := md5.New()
+ h.Write([]byte(data))
+ return hex.EncodeToString(h.Sum(nil))
+}
+
+func retError() error {
+ return errors.New("err")
+}
+
+func retError2() error {
+ return errors.New("err2")
+}
diff --git a/sample-monorepo/c/go.mod b/sample-monorepo/c/go.mod
new file mode 100644
index 0000000000..bfcbddc1b3
--- /dev/null
+++ b/sample-monorepo/c/go.mod
@@ -0,0 +1,3 @@
+module github.com/golangci/actionc
+
+go 1.24.0
diff --git a/sample-monorepo/c/sample.go b/sample-monorepo/c/sample.go
new file mode 100644
index 0000000000..80504b0df1
--- /dev/null
+++ b/sample-monorepo/c/sample.go
@@ -0,0 +1,26 @@
+// Package sample is used as test input for golangci action.
+package sample
+
+import (
+ "crypto/md5"
+ "encoding/hex"
+ "errors"
+)
+
+// Hash~
+func Hash(data string) string {
+ retError()
+ retError2()
+
+ h := md5.New()
+ h.Write([]byte(data))
+ return hex.EncodeToString(h.Sum(nil))
+}
+
+func retError() error {
+ return errors.New("err")
+}
+
+func retError2() error {
+ return errors.New("err2")
+}
diff --git a/src/cache.ts b/src/cache.ts
index 57b817e9f6..2e51782bfa 100644
--- a/src/cache.ts
+++ b/src/cache.ts
@@ -74,7 +74,10 @@ async function buildCacheKeys(): Promise {
}
export async function restoreCache(): Promise {
- if (core.getBooleanInput(`skip-cache`, { required: true })) return
+ if (core.getBooleanInput(`skip-cache`, { required: true })) {
+ core.info(`Skipping cache restoration`)
+ return
+ }
if (!utils.isValidEvent()) {
utils.logWarning(
@@ -116,8 +119,15 @@ export async function restoreCache(): Promise {
}
export async function saveCache(): Promise {
- if (core.getBooleanInput(`skip-cache`, { required: true })) return
- if (core.getBooleanInput(`skip-save-cache`, { required: true })) return
+ if (core.getBooleanInput(`skip-cache`, { required: true })) {
+ core.info(`Skipping cache saving`)
+ return
+ }
+
+ if (core.getBooleanInput(`skip-save-cache`, { required: true })) {
+ core.info(`Skipping cache saving`)
+ return
+ }
// Validate inputs, this can cause task failure
if (!utils.isValidEvent()) {
diff --git a/src/install.ts b/src/install.ts
index 8d71ec79e5..f8b2b18b4f 100644
--- a/src/install.ts
+++ b/src/install.ts
@@ -1,6 +1,7 @@
import * as core from "@actions/core"
import * as tc from "@actions/tool-cache"
import { exec, ExecOptionsWithStringEncoding } from "child_process"
+import fs from "fs"
import os from "os"
import path from "path"
import { promisify } from "util"
@@ -8,7 +9,7 @@ import which from "which"
import { getVersion, VersionInfo } from "./version"
-const execShellCommand = promisify(exec)
+const execCommand = promisify(exec)
export enum InstallMode {
Binary = "binary",
@@ -21,13 +22,15 @@ type ExecRes = {
stderr: string
}
-const printOutput = (res: ExecRes): void => {
+const printOutput = (res: ExecRes): ExecRes => {
if (res.stdout) {
core.info(res.stdout)
}
if (res.stderr) {
core.info(res.stderr)
}
+
+ return res
}
/**
@@ -36,6 +39,17 @@ const printOutput = (res: ExecRes): void => {
* @returns path to installed binary of golangci-lint.
*/
export async function install(): Promise {
+ const problemMatchers = core.getBooleanInput(`problem-matchers`)
+
+ if (problemMatchers) {
+ const matchersPath = path.join(__dirname, "../..", "problem-matchers.json")
+ if (fs.existsSync(matchersPath)) {
+ // Adds problem matchers.
+ // https://github.com/actions/setup-go/blob/cdcb36043654635271a94b9a6d1392de5bb323a7/src/main.ts#L81-L83
+ core.info(`##[add-matcher]${matchersPath}`)
+ }
+ }
+
const mode = core.getInput("install-mode").toLowerCase()
if (mode === InstallMode.None) {
@@ -84,17 +98,14 @@ async function goInstall(versionInfo: VersionInfo): Promise {
const options: ExecOptionsWithStringEncoding = { env: { ...process.env, CGO_ENABLED: "1" } }
- const exres = await execShellCommand(
- `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${versionInfo.TargetVersion}`,
- options
+ await execCommand(`go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${versionInfo.TargetVersion}`, options).then(
+ printOutput
)
- printOutput(exres)
- const res = await execShellCommand(
+ const res = await execCommand(
`go install -n github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${versionInfo.TargetVersion}`,
options
- )
- printOutput(res)
+ ).then(printOutput)
// The output of `go install -n` when the binary is already installed is `touch `.
const binPath = res.stderr
diff --git a/src/patch.ts b/src/patch.ts
index f2bb41266e..bbe89c5fa7 100644
--- a/src/patch.ts
+++ b/src/patch.ts
@@ -17,10 +17,6 @@ export function isOnlyNewIssues(): boolean {
}
export async function fetchPatch(): Promise {
- if (!isOnlyNewIssues()) {
- return ``
- }
-
const ctx = github.context
switch (ctx.eventName) {
@@ -70,8 +66,7 @@ async function fetchPullRequestPatch(ctx: Context): Promise {
}
try {
- const tempDir = await createTempDir()
- const patchPath = path.join(tempDir, "pull.patch")
+ const patchPath = await createTempDir().then((tempDir) => path.join(tempDir, "pull.patch"))
core.info(`Writing patch to ${patchPath}`)
await writeFile(patchPath, alterDiffPatch(patch))
return patchPath
@@ -108,8 +103,7 @@ async function fetchPushPatch(ctx: Context): Promise {
}
try {
- const tempDir = await createTempDir()
- const patchPath = path.join(tempDir, "push.patch")
+ const patchPath = await createTempDir().then((tempDir) => path.join(tempDir, "push.patch"))
core.info(`Writing patch to ${patchPath}`)
await writeFile(patchPath, alterDiffPatch(patch))
return patchPath
diff --git a/src/plugins.ts b/src/plugins.ts
index 97a624f3c1..6d11d6de55 100644
--- a/src/plugins.ts
+++ b/src/plugins.ts
@@ -5,7 +5,7 @@ import * as path from "path"
import { promisify } from "util"
import YAML from "yaml"
-const execShellCommand = promisify(exec)
+const execCommand = promisify(exec)
type ExecRes = {
stdout: string
@@ -40,7 +40,7 @@ export async function install(binPath: string): Promise {
.find((filePath) => fs.existsSync(filePath))
if (!configFile || configFile === "") {
- return ""
+ return binPath
}
core.info(`Found configuration for the plugin module system : ${configFile}`)
@@ -74,18 +74,15 @@ export async function install(binPath: string): Promise {
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}`)
+ const options: ExecOptionsWithStringEncoding = {
+ cwd: rootDir,
}
+
+ return execCommand(cmd, options)
+ .then(printOutput)
+ .then(() => core.info(`Built custom golangci-lint binary in ${Date.now() - startedAt}ms`))
+ .then(() => 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 8687b1ee0e..7faeafb5d1 100644
--- a/src/run.ts
+++ b/src/run.ts
@@ -10,37 +10,7 @@ import { install } from "./install"
import { fetchPatch, isOnlyNewIssues } from "./patch"
import * as plugins from "./plugins"
-const execShellCommand = promisify(exec)
-
-type Env = {
- binPath: string
- patchPath: string
-}
-
-async function prepareEnv(installOnly: boolean): Promise {
- const startedAt = Date.now()
-
- // Prepare cache, lint and go in parallel.
- await restoreCache()
-
- 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: `` }
- }
-
- const patchPath = await fetchPatch()
-
- core.info(`Prepared env in ${Date.now() - startedAt}ms`)
-
- return { binPath, patchPath }
-}
+const execCommand = promisify(exec)
type ExecRes = {
stdout: string
@@ -56,13 +26,7 @@ const printOutput = (res: ExecRes): void => {
}
}
-async function runLint(binPath: string, patchPath: string): Promise {
- const debug = core.getInput(`debug`)
- if (debug.split(`,`).includes(`cache`)) {
- const res = await execShellCommand(`${binPath} cache status`)
- printOutput(res)
- }
-
+async function runGolangciLint(binPath: string, rootDir: string): Promise {
const userArgs = core.getInput(`args`)
const addedArgs: string[] = []
@@ -77,17 +41,6 @@ async function runLint(binPath: string, patchPath: string): Promise {
const userArgsMap = new Map(userArgsList)
const userArgNames = new Set(userArgsList.map(([key]) => key))
- const problemMatchers = core.getBooleanInput(`problem-matchers`)
-
- if (problemMatchers) {
- const matchersPath = path.join(__dirname, "../..", "problem-matchers.json")
- if (fs.existsSync(matchersPath)) {
- // Adds problem matchers.
- // https://github.com/actions/setup-go/blob/cdcb36043654635271a94b9a6d1392de5bb323a7/src/main.ts#L81-L83
- core.info(`##[add-matcher]${matchersPath}`)
- }
- }
-
if (isOnlyNewIssues()) {
if (
userArgNames.has(`new`) ||
@@ -99,6 +52,7 @@ async function runLint(binPath: string, patchPath: string): Promise {
}
const ctx = github.context
+ const patchPath = await fetchPatch()
core.info(`only new issues on ${ctx.eventName}: ${patchPath}`)
@@ -130,17 +84,12 @@ async function runLint(binPath: string, patchPath: string): Promise {
const cmdArgs: ExecOptionsWithStringEncoding = {}
- const workingDirectory = core.getInput(`working-directory`)
- if (workingDirectory) {
- if (!fs.existsSync(workingDirectory) || !fs.lstatSync(workingDirectory).isDirectory()) {
- throw new Error(`working-directory (${workingDirectory}) was not a path`)
- }
-
+ if (rootDir) {
if (!userArgNames.has(`path-prefix`) && !userArgNames.has(`path-mode`)) {
addedArgs.push(`--path-mode=abs`)
}
- cmdArgs.cwd = path.resolve(workingDirectory)
+ cmdArgs.cwd = path.resolve(rootDir)
}
await runVerify(binPath, userArgsMap, cmdArgs)
@@ -150,22 +99,21 @@ async function runLint(binPath: string, patchPath: string): Promise {
core.info(`Running [${cmd}] in [${cmdArgs.cwd || process.cwd()}] ...`)
const startedAt = Date.now()
- try {
- const res = await execShellCommand(cmd, cmdArgs)
- printOutput(res)
- core.info(`golangci-lint found no issues`)
- } catch (exc) {
- // This logging passes issues to GitHub annotations but comments can be more convenient for some users.
- printOutput(exc)
-
- if (exc.code === 1) {
- core.setFailed(`issues found`)
- } else {
- core.setFailed(`golangci-lint exit with code ${exc.code}`)
- }
- }
- core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`)
+ return execCommand(cmd, cmdArgs)
+ .then(printOutput)
+ .then(() => core.info(`golangci-lint found no issues`))
+ .catch((exc) => {
+ // This logging passes issues to GitHub annotations.
+ printOutput(exc)
+
+ if (exc.code === 1) {
+ core.setFailed(`issues found`)
+ } else {
+ core.setFailed(`golangci-lint exit with code ${exc.code}`)
+ }
+ })
+ .finally(() => core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`))
}
async function runVerify(binPath: string, userArgsMap: Map, cmdArgs: ExecOptionsWithStringEncoding): Promise {
@@ -186,8 +134,7 @@ async function runVerify(binPath: string, userArgsMap: Map, cmdA
core.info(`Running [${cmdVerify}] in [${cmdArgs.cwd || process.cwd()}] ...`)
- const res = await execShellCommand(cmdVerify, cmdArgs)
- printOutput(res)
+ await execCommand(cmdVerify, cmdArgs).then(printOutput)
}
async function getConfigPath(binPath: string, userArgsMap: Map, cmdArgs: ExecOptionsWithStringEncoding): Promise {
@@ -199,26 +146,98 @@ async function getConfigPath(binPath: string, userArgsMap: Map,
core.info(`Running [${cmdConfigPath}] in [${cmdArgs.cwd || process.cwd()}] ...`)
try {
- const resPath = await execShellCommand(cmdConfigPath, cmdArgs)
+ const resPath = await execCommand(cmdConfigPath, cmdArgs)
return resPath.stderr.trim()
} catch {
return ``
}
}
+async function debugAction(binPath: string) {
+ const flags = core.getInput(`debug`).split(`,`)
+
+ if (flags.includes(`clean`)) {
+ const cmd = `${binPath} cache clean`
+
+ core.info(`Running [${cmd}] ...`)
+
+ await execCommand(cmd).then(printOutput)
+ }
+
+ if (flags.includes(`cache`)) {
+ const cmd = `${binPath} cache status`
+
+ core.info(`Running [${cmd}] ...`)
+
+ await execCommand(cmd).then(printOutput)
+ }
+}
+
+function getWorkingDirectory(): string {
+ const workingDirectory = core.getInput(`working-directory`)
+ if (workingDirectory) {
+ if (!fs.existsSync(workingDirectory) || !fs.lstatSync(workingDirectory).isDirectory()) {
+ throw new Error(`working-directory (${workingDirectory}) was not a path`)
+ }
+ }
+
+ return workingDirectory
+}
+
+function modulesAutoDetection(rootDir: string): string[] {
+ const o: fs.GlobOptions = {
+ cwd: rootDir,
+ exclude: ["**/vendor/**", "**/node_modules/**", "**/.git/**", "**/dist/**"],
+ }
+
+ const matches = fs.globSync("**/go.mod", o)
+
+ const dirs = matches
+ .filter((m) => typeof m === "string")
+ .map((m) => path.resolve(rootDir, path.dirname(m)))
+ .sort()
+
+ return [...new Set(dirs)]
+}
+
+async function runLint(binPath: string): Promise {
+ const workingDirectory = getWorkingDirectory()
+
+ const experimental = core.getInput(`experimental`).split(`,`)
+
+ if (experimental.includes(`automatic-module-directories`)) {
+ const wds = modulesAutoDetection(workingDirectory)
+
+ const cwd = process.cwd()
+
+ for (const wd of wds) {
+ await core.group(`run golangci-lint in ${path.relative(cwd, wd)}`, () => runGolangciLint(binPath, wd))
+ }
+
+ return
+ }
+
+ await core.group(`run golangci-lint`, () => runGolangciLint(binPath, workingDirectory))
+}
+
export async function run(): Promise {
try {
- const installOnly = core.getBooleanInput(`install-only`, { required: true })
+ await core.group(`Restore cache`, restoreCache)
- const { binPath, patchPath } = await core.group(`prepare environment`, () => prepareEnv(installOnly))
+ const binPath = await core.group(`Install`, () => install().then(plugins.install))
core.addPath(path.dirname(binPath))
+ if (core.getInput(`debug`)) {
+ await core.group(`Debug`, () => debugAction(binPath))
+ }
+
+ const installOnly = core.getBooleanInput(`install-only`, { required: true })
if (installOnly) {
return
}
- await core.group(`run golangci-lint`, () => runLint(binPath, patchPath))
+ await runLint(binPath)
} catch (error) {
core.error(`Failed to run: ${error}, ${error.stack}`)
core.setFailed(error.message)