Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 53 additions & 24 deletions lib/entry-points.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ export enum EnvVar {
HAS_WARNED_ABOUT_DISK_SPACE = "CODEQL_ACTION_HAS_WARNED_ABOUT_DISK_SPACE",

/**
* Whether a step in this job has already obtained the CodeQL tools. Steps that run afterwards may
* be holding a path into the toolcache, so we must not delete anything from it.
* Whether a step in this job has already set up CodeQL. Steps that run afterwards may be holding
* a path into the toolcache, so we must not delete anything from it.
*/
HAS_OBTAINED_CODEQL_TOOLS = "CODEQL_ACTION_HAS_OBTAINED_CODEQL_TOOLS",
HAS_SET_UP_CODEQL = "CODEQL_ACTION_HAS_SET_UP_CODEQL",

/** Whether the `setup-codeql` action has been run. */
SETUP_CODEQL_ACTION_HAS_RUN = "CODEQL_ACTION_SETUP_CODEQL_HAS_RUN",
Expand Down
72 changes: 51 additions & 21 deletions src/setup-codeql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1199,24 +1199,19 @@ test.serial(
);

test.serial(
"downloadCodeQL continues when cleaning up the toolcache throws",
"deleteToolcacheBundles reports a failure when the toolcache location is unknown",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
delete process.env[ActionsEnvVars.RUNNER_TOOL_CACHE];
Comment thread
mbg marked this conversation as resolved.
Resolved

createToolcacheEntry(tmpDir, "CodeQL", CLEANUP_CLI_VERSION);

sinon
.stub(toolsDownload, "deleteToolcacheBundles")
.rejects(new Error("RUNNER_TOOL_CACHE is not set"));

const cleanupDiagnostic = await runDownloadCodeQL(tmpDir, [
Feature.CleanupToolcacheBundles,
]);
const result = await toolsDownload.deleteToolcacheBundles(
getRunnerLogger(true),
);

t.deepEqual(cleanupDiagnostic, { deletedVersions: [], failed: true });
});
t.deepEqual(
result,
{ deletedVersions: [], failed: true },
"Should report a failure rather than throwing, so the download can continue.",
);
},
);

Expand Down Expand Up @@ -1255,7 +1250,7 @@ test.serial(
);

test.serial(
"downloadCodeQL does not clean up the toolcache once a step has already obtained the tools",
"downloadCodeQL does not clean up the toolcache once a step has already set up CodeQL",
async (t) => {
// `.github/workflows/codeql.yml` sets up CodeQL twice and then runs both returned paths. If the
// second setup downloads, it must not delete the bundle the first one handed out.
Expand All @@ -1265,7 +1260,7 @@ test.serial(
features: [Feature.CleanupToolcacheBundles],
runnerEnvironment: "github-hosted",
setUp: () => {
process.env[EnvVar.HAS_OBTAINED_CODEQL_TOOLS] = "true";
process.env[EnvVar.HAS_SET_UP_CODEQL] = "true";
},
},
({ cleanupDiagnostic, destinationDirectory, staleDirectory }) => {
Expand All @@ -1278,11 +1273,11 @@ test.serial(
);

test.serial(
"setupCodeQLBundle records that this job has obtained the CodeQL tools",
"setupCodeQLBundle records that this job has set up CodeQL",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
delete process.env[EnvVar.HAS_OBTAINED_CODEQL_TOOLS];
delete process.env[EnvVar.HAS_SET_UP_CODEQL];

sinon.stub(setupCodeql, "downloadCodeQL").resolves({
codeqlFolder: "codeql",
Expand All @@ -1303,7 +1298,7 @@ test.serial(
);

t.is(
process.env[EnvVar.HAS_OBTAINED_CODEQL_TOOLS],
process.env[EnvVar.HAS_SET_UP_CODEQL],
"true",
"A later step must be able to tell that the toolcache is in use.",
);
Expand Down Expand Up @@ -1402,7 +1397,7 @@ test.serial(
);

test.serial(
"isToolcacheOnWorkspaceFilesystem compares the toolcache against the workspace",
"isToolcacheOnWorkspaceFilesystem assumes a different filesystem when it cannot tell",
async (t) => {
await withTmpDir(async (tmpDir) => {
const logger = getRunnerLogger(true);
Expand All @@ -1419,3 +1414,38 @@ test.serial(
});
},
);

test.serial(
"downloadCodeQL does not delete through a symlinked version directory",
async (t) => {
await withTmpDir(async (tmpDir) => {
const toolcacheRoot = path.join(tmpDir, "toolcache");
setupActionsVars(tmpDir, toolcacheRoot);
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";

createToolcacheEntry(toolcacheRoot, "CodeQL", CLEANUP_STALE_VERSION);

// Somewhere outside the toolcache that a version directory points at.
const outsideDirectory = path.join(tmpDir, "outside");
fs.mkdirSync(outsideDirectory, { recursive: true });
fs.writeFileSync(path.join(outsideDirectory, "contents"), "x");
fs.symlinkSync(
outsideDirectory,
path.join(toolcacheRoot, "CodeQL", "9.9.9"),
);

const cleanupDiagnostic = await runDownloadCodeQL(toolcacheRoot, [
Feature.CleanupToolcacheBundles,
]);

t.true(
fs.existsSync(path.join(outsideDirectory, "contents")),
"Should not delete anything through a symlinked version directory.",
);
t.deepEqual(cleanupDiagnostic, {
deletedVersions: [CLEANUP_STALE_VERSION],
failed: false,
});
});
},
);
35 changes: 14 additions & 21 deletions src/setup-codeql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { default as deepEqual } from "fast-deep-equal";
import * as semver from "semver";
import { v4 as uuidV4 } from "uuid";

import { ActionState } from "./action-common";
import {
isAnalyzingPullRequest,
isDynamicWorkflow,
Expand All @@ -21,7 +22,7 @@ import {
makeDiagnostic,
makeTelemetryDiagnostic,
} from "./diagnostics";
import { EnvVar } from "./environment";
import { EnvVar, getEnv } from "./environment";
import {
CODEQL_VERSION_ZSTD_BUNDLE,
CodeQLDefaultVersionInfo,
Expand All @@ -37,7 +38,6 @@ import {
downloadAndExtract,
getToolcacheDirectory,
isToolcacheOnWorkspaceFilesystem,
ToolcacheCleanupResult,
ToolsDownloadStatusReport,
writeToolcacheMarkerFile,
} from "./tools-download";
Expand Down Expand Up @@ -824,7 +824,7 @@ export const downloadCodeQL = async function (
const extractedBundlePath =
toolcacheInfo?.path ?? getTempExtractionDir(tempDir);

await tryDeleteToolcacheBundles(features, logger);
await tryDeleteToolcacheBundles({ env: getEnv(), features, logger });

const statusReport = await downloadAndExtract(
codeqlURL,
Expand Down Expand Up @@ -885,16 +885,17 @@ function getToolcacheDestinationInfo(
* the toolcache take up space that the analysis could use instead. This holds wherever we extract
* the tools we are obtaining, since the toolcache is on that filesystem either way.
*/
async function tryDeleteToolcacheBundles(
features: FeatureEnablement,
logger: Logger,
): Promise<void> {
// A step that has already obtained the CodeQL tools may hand out a path into the toolcache that a
// later step runs, so only the first step to obtain them can know that nothing else relies on it.
if (util.getOptionalEnvVar(EnvVar.HAS_OBTAINED_CODEQL_TOOLS) !== undefined) {
async function tryDeleteToolcacheBundles({
env,
features,
logger,
}: ActionState<["Logger", "ReadOnlyEnv", "FeatureFlags"]>): Promise<void> {
// A step that has already set up CodeQL may hand out a path into the toolcache that a later step
// runs, so only the first step to set it up can know that nothing else relies on the toolcache.
if (env.getOptional(EnvVar.HAS_SET_UP_CODEQL) !== undefined) {
logger.debug(
"Not deleting the CodeQL tools from the toolcache since a previous step in this job has " +
"already obtained them.",
"already set up CodeQL.",
);
return;
}
Expand All @@ -907,15 +908,7 @@ async function tryDeleteToolcacheBundles(
return;
}

let result: ToolcacheCleanupResult = { deletedVersions: [], failed: true };

try {
result = await deleteToolcacheBundles(logger);
} catch (e) {
logger.info(
`Unable to reclaim disk space from the toolcache: ${util.getErrorMessage(e)}`,
);
}
const result = await deleteToolcacheBundles(logger);
Comment thread
mbg marked this conversation as resolved.
Resolved

addNoLanguageDiagnostic(
undefined,
Expand Down Expand Up @@ -1051,7 +1044,7 @@ export async function setupCodeQLBundle(

// Record that this job now has a copy of the CodeQL tools, so that a later step doesn't delete
// the toolcache out from under the path we are about to return.
core.exportVariable(EnvVar.HAS_OBTAINED_CODEQL_TOOLS, "true");
core.exportVariable(EnvVar.HAS_SET_UP_CODEQL, "true");

return {
codeqlFolder,
Expand Down
Loading
Loading