Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
276 changes: 123 additions & 153 deletions lib/entry-points.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package-lock.json

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

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
"@octokit/plugin-rest-endpoint-methods": "^18.0.0",
"@octokit/plugin-retry": "^8.1.1",
"archiver": "^8.0.0",
"fast-deep-equal": "^3.1.3",
"follow-redirects": "^1.16.0",
"get-folder-size": "^5.0.0",
"https-proxy-agent": "^7.0.6",
Expand Down
4 changes: 2 additions & 2 deletions src/actions-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export function getWorkflowEventName(env: Env = getEnv()) {
* Returns whether the current workflow is executing a local copy of the Action, e.g. we're running
* a workflow on the codeql-action repo itself.
*/
export function isRunningLocalAction(env: Env = getEnv()): boolean {
export function isRunningLocalAction(env: ReadOnlyEnv = getEnv()): boolean {
const relativeScriptPath = getRelativeScriptPath(env);
return (
relativeScriptPath.startsWith("..") || path.isAbsolute(relativeScriptPath)
Expand All @@ -115,7 +115,7 @@ export function isRunningLocalAction(env: Env = getEnv()): boolean {
*
* This can be used to get the Action's name or tell if we're running a local Action.
*/
function getRelativeScriptPath(env: Env): string {
function getRelativeScriptPath(env: ReadOnlyEnv): string {
const runnerTemp = env.getRequired(ActionsEnvVars.RUNNER_TEMP);
const actionsDirectory = path.join(path.dirname(runnerTemp), "_actions");
return path.relative(actionsDirectory, __filename);
Expand Down
17 changes: 17 additions & 0 deletions src/logging.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import test from "ava";

import { joinMessageStrings } from "./logging";
import { setupTests } from "./testing-utils";

setupTests(test);

test("joinMessageStrings", async (t) => {
// For strings and errors, it is the identity function.
t.deepEqual(joinMessageStrings("Hello"), "Hello");

const error = new Error("Some error");
t.deepEqual(joinMessageStrings(error), error);

// For arrays of strings, we get a join-ed string.
t.deepEqual(joinMessageStrings(["foo", "bar"]), "foo bar");
});
43 changes: 35 additions & 8 deletions src/logging.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,50 @@
import * as core from "@actions/core";

/** Types that all of our logging functions accept. */
export type Loggable = string | string[];

/** Some logging functions also accept errors directly. */
export type LoggableError = Loggable | Error;

export interface Logger {
debug: (message: string) => void;
info: (message: string) => void;
warning: (message: string | Error) => void;
error: (message: string | Error) => void;
debug: (message: Loggable) => void;
info: (message: Loggable) => void;
warning: (message: LoggableError) => void;
error: (message: LoggableError) => void;

isDebug: () => boolean;

startGroup: (name: string) => void;
endGroup: () => void;
}

/** If `message` is an array of strings, the array is `join`-ed into one string separated by spaces. */
export const joinMessageStrings = <T>(
message: string[] | string | T,
): string | T => {
if (Array.isArray(message)) {
return message.join(" ");
} else {
return message;
}
};

/**
* Wraps a logging function so that `joinMessageStrings` is applied to the input before
* calling the wrapped logging function.
*/
const autoJoinMessageStrings = <T>(fn: (msg: string | T) => void) => {
return (message: string[] | string | T) => {
return fn(joinMessageStrings(message));
};
};

export function getActionsLogger(): Logger {
return {
debug: core.debug,
info: core.info,
warning: core.warning,
error: core.error,
debug: autoJoinMessageStrings(core.debug),
info: autoJoinMessageStrings(core.info),
warning: autoJoinMessageStrings(core.warning),
error: autoJoinMessageStrings(core.error),
isDebug: core.isDebug,
startGroup: core.startGroup,
endGroup: core.endGroup,
Expand Down
18 changes: 0 additions & 18 deletions src/setup-codeql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,24 +126,6 @@ test.serial("convert to semver", (t) => {
}
});

test.serial("getCodeQLActionRepository", (t) => {
const logger = getRunnerLogger(true);

initializeEnvironment("1.2.3");

// isRunningLocalAction() === true
delete process.env["GITHUB_ACTION_REPOSITORY"];
process.env["RUNNER_TEMP"] = path.dirname(__dirname);
const repoLocalRunner = setupCodeql.getCodeQLActionRepository(logger);
t.deepEqual(repoLocalRunner, "github/codeql-action");

// isRunningLocalAction() === false
sinon.stub(actionsUtil, "isRunningLocalAction").returns(false);
process.env["GITHUB_ACTION_REPOSITORY"] = "xxx/yyy";
const repoEnv = setupCodeql.getCodeQLActionRepository(logger);
t.deepEqual(repoEnv, "xxx/yyy");
});

test.serial(
"getCodeQLSource sets CLI version for a semver tagged bundle",
async (t) => {
Expand Down
82 changes: 6 additions & 76 deletions src/setup-codeql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { performance } from "perf_hooks";

import * as core from "@actions/core";
import * as toolcache from "@actions/tool-cache";
import { default as deepEqual } from "fast-deep-equal";
import * as semver from "semver";
import { v4 as uuidV4 } from "uuid";

Expand All @@ -14,7 +13,6 @@ import {
isAnalyzingPullRequest,
isDynamicWorkflow,
isGitHubHostedRunner,
isRunningLocalAction,
} from "./actions-util";
import * as api from "./api-client";
import {
Expand Down Expand Up @@ -45,6 +43,7 @@ import {
logPerLanguageBundleFallback,
} from "./per-language-bundles";
import { getBundlePlatform } from "./platform";
import { getCodeQLAssetDownloadURL } from "./setup/repository";
import * as tar from "./tar";
import {
deleteToolcacheBundles,
Expand All @@ -66,83 +65,13 @@ export enum ToolsSource {
Download = "DOWNLOAD",
}

const CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action";
const CODEQL_NIGHTLIES_REPOSITORY_OWNER = "dsp-testing";
const CODEQL_NIGHTLIES_REPOSITORY_NAME = "codeql-cli-nightlies";

const CODEQL_BUNDLE_VERSION_ALIAS: string[] = ["linked", "latest"];
const CODEQL_NIGHTLY_TOOLS_INPUTS = ["nightly", "nightly-latest"];
const CODEQL_TOOLCACHE_INPUT = "toolcache";

export function getCodeQLActionRepository(logger: Logger): string {
if (isRunningLocalAction()) {
// This handles the case where the Action does not come from an Action repository,
// e.g. our integration tests which use the Action code from the current checkout.
// In these cases, the GITHUB_ACTION_REPOSITORY environment variable is not set.
logger.info(
"The CodeQL Action is checked out locally. Using the default CodeQL Action repository.",
);
return CODEQL_DEFAULT_ACTION_REPOSITORY;
}

return util.getRequiredEnvParam("GITHUB_ACTION_REPOSITORY");
}

async function getCodeQLBundleDownloadURL(
tagName: string,
apiDetails: api.GitHubApiDetails,
codeQLBundleName: string,
logger: Logger,
): Promise<string> {
const codeQLActionRepository = getCodeQLActionRepository(logger);
const potentialDownloadSources = [
// This GitHub instance, and this Action.
[apiDetails.url, codeQLActionRepository],
// This GitHub instance, and the canonical Action.
[apiDetails.url, CODEQL_DEFAULT_ACTION_REPOSITORY],
// GitHub.com, and the canonical Action.
[util.GITHUB_DOTCOM_URL, CODEQL_DEFAULT_ACTION_REPOSITORY],
];
// We now filter out any duplicates.
// Duplicates will happen either because the GitHub instance is GitHub.com, or because the Action is not a fork.
const uniqueDownloadSources = potentialDownloadSources.filter(
(source, index, self) => {
return !self.slice(0, index).some((other) => deepEqual(source, other));
},
);
for (const downloadSource of uniqueDownloadSources) {
const [apiURL, repository] = downloadSource;
// If we've reached the final case, short-circuit the API check since we know the bundle exists and is public.
if (
apiURL === util.GITHUB_DOTCOM_URL &&
repository === CODEQL_DEFAULT_ACTION_REPOSITORY
) {
break;
}
const [repositoryOwner, repositoryName] = repository.split("/");
try {
const release = await api.getApiClient().rest.repos.getReleaseByTag({
owner: repositoryOwner,
repo: repositoryName,
tag: tagName,
});
for (const asset of release.data.assets) {
if (asset.name === codeQLBundleName) {
logger.info(
`Found CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} with URL ${asset.url}.`,
);
return asset.url;
}
}
} catch (e) {
logger.info(
`Looked for CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} but got error ${e}.`,
);
}
}
return `https://github.com/${CODEQL_DEFAULT_ACTION_REPOSITORY}/releases/download/${tagName}/${codeQLBundleName}`;
}

function tryGetBundleVersionFromTagName(
tagName: string,
logger: Logger,
Expand Down Expand Up @@ -716,9 +645,10 @@ export async function getCodeQLSource(
? "zstd"
: "gzip";

const action = { env: getEnv(), logger };
const platform = getBundlePlatform();
const perLanguageBundleLanguage = await getPerLanguageBundleLanguage(
{ env: getEnv(), features, logger },
{ ...action, features },
{
rawLanguages,
cliVersion,
Expand All @@ -730,11 +660,11 @@ export async function getCodeQLSource(

// Resolves the combined or per-language bundle URL for the requested release.
const resolveBundleURL = (language?: BuiltInLanguage) =>
getCodeQLBundleDownloadURL(
bundleTagName,
getCodeQLAssetDownloadURL(
action,
apiDetails,
bundleTagName,
getCodeQLBundleName(compressionMethod, platform, language),
logger,
);

const combinedBundleURL = await resolveBundleURL();
Expand Down
34 changes: 34 additions & 0 deletions src/setup/repository.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as path from "path";

import test from "ava";
import * as sinon from "sinon";

import * as actionsUtil from "../actions-util";
import { ActionsEnvVars } from "../environment";
import { callee, setupTests } from "../testing-utils";
import { initializeEnvironment } from "../util";

import { getCodeQLActionRepository } from "./repository";

setupTests(test);

test.serial("getCodeQLActionRepository", async (t) => {
initializeEnvironment("1.2.3");

const target = callee(getCodeQLActionRepository)
.withArgs()
.withEnv((env) => {
env.set(ActionsEnvVars.RUNNER_TEMP, path.dirname(__dirname));
});

// isRunningLocalAction() === true
await target.passes(t.deepEqual, "github/codeql-action");

// isRunningLocalAction() === false
sinon.stub(actionsUtil, "isRunningLocalAction").returns(false);
await target
.withEnv((env) => {
env.set(ActionsEnvVars.GITHUB_ACTION_REPOSITORY, "xxx/yyy");
})
.passes(t.deepEqual, "xxx/yyy");
});
Loading
Loading