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
Prev Previous commit
Next Next commit
Add a helper to delete the CodeQL tools from the toolcache
`deleteToolcacheBundles` removes `$RUNNER_TOOL_CACHE/CodeQL` and reports which versions were there. It refuses to follow a symlinked CodeQL directory so that it can only ever delete paths that are really inside the toolcache, and reports failures rather than throwing. Not called yet.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
  • Loading branch information
henrymercer and Copilot committed Sep 4, 2026
commit 762a5ed7f7424ff89c26820bdf497fe60e53a474
36 changes: 22 additions & 14 deletions lib/entry-points.js

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

85 changes: 82 additions & 3 deletions src/tools-download.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,16 +197,95 @@ async function downloadAndExtractZstdWithStreaming(
await tar.extractTarZst(response, dest, tarVersion, logger);
}

/** Gets the path to the toolcache directory for the specified version of the CodeQL tools. */
export function getToolcacheDirectory(version: string): string {
/** Gets the path to the toolcache directory that holds all versions of the CodeQL tools. */
function getToolcacheToolDirectory(): string {
return path.join(
getRequiredEnvParam("RUNNER_TOOL_CACHE"),
TOOLCACHE_TOOL_NAME,
semver.clean(version) || version,
);
}

/** Gets the name of the toolcache directory that holds the given version of the CodeQL tools. */
function getToolcacheVersionDirectoryName(version: string): string {
return semver.clean(version) || version;
}

/** Gets the path to the toolcache directory for the specified version of the CodeQL tools. */
export function getToolcacheDirectory(version: string): string {
return path.join(
getToolcacheToolDirectory(),
getToolcacheVersionDirectoryName(version),
os.arch() || "",
);
}

/** The outcome of trying to reclaim disk space by deleting the CodeQL tools from the toolcache. */
export interface ToolcacheCleanupResult {
/** The versions of the CodeQL tools that were deleted. */
deletedVersions: string[];
/**
* Whether we hit an error while trying to delete the tools. Distinguishes a toolcache that had
* nothing to reclaim from one we failed to clean up.
*/
failed: boolean;
}

/**
* Deletes the CodeQL tools from the toolcache.
*
* Only safe to call when we are about to download the tools, since that means we did not resolve
* them from the toolcache and so nothing in there is in use by this job.
*
* This only ever touches the CodeQL directory of the toolcache, and is best-effort: any failure is
* logged rather than propagated, since the caller can proceed without the disk space.
*
* @returns the versions that were deleted, and whether we hit an error while trying.
*/
export async function deleteToolcacheBundles(
logger: Logger,
): Promise<ToolcacheCleanupResult> {
const toolDirectory = getToolcacheToolDirectory();

try {
// Refuse to follow a symlinked CodeQL directory, so that we can only ever delete paths that are
// really inside the toolcache.
if ((await fs.promises.lstat(toolDirectory)).isSymbolicLink()) {
logger.info(
`Not deleting the CodeQL tools from the toolcache since ${toolDirectory} is a symlink.`,
);
return { deletedVersions: [], failed: true };
}
} catch {
logger.debug(
`There are no CodeQL tools at ${toolDirectory} to delete from the toolcache.`,
);
return { deletedVersions: [], failed: false };
}
Comment thread
Copilot marked this conversation as resolved.
Outdated

try {
const versions = (
await fs.promises.readdir(toolDirectory, { withFileTypes: true })
)
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.sort();

await fs.promises.rm(toolDirectory, { force: true, recursive: true });

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be a bit extra safe, do you think it would make sense to perform the symlink check for each of the specific version directories as well before deleting them individually to make sure that the specific version directories aren't symlinks?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be odd on a runner that claims to be GitHub-hosted, but can do. In most cases this shouldn't slow things down much as we expect exactly one CodeQL entry in the toolcache.


logger.info(
`Deleted the CodeQL tools at ${toolDirectory} from the toolcache to free up disk space. ` +
`Versions deleted: ${versions.join(", ") || "none"}.`,
);

return { deletedVersions: versions, failed: false };
} catch (e) {
logger.info(
`Failed to delete the CodeQL tools at ${toolDirectory} from the toolcache: ${getErrorMessage(e)}`,
Comment thread
mbg marked this conversation as resolved.
Outdated
);
return { deletedVersions: [], failed: true };
}
}

export function writeToolcacheMarkerFile(
extractedPath: string,
logger: Logger,
Expand Down