Skip to content
Merged
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
16 changes: 8 additions & 8 deletions packages/core/mocks/oidc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const MOCK_ARGOS_TOKEN = "mock-argos-token-returned";
export const MOCK_EXPIRES_AT = "2099-01-01T00:00:00.000Z";
export const MOCK_TOKENLESS_ARGOS_TOKEN = "mock-tokenless-argos-token-returned";

export const oidcHandlers = [
export const tokenExchangeHandlers = [
http.get(MOCK_OIDC_URL, ({ request }) => {
const url = new URL(request.url);
if (url.searchParams.get("audience") !== "https://api.argos-ci.com") {
Expand Down Expand Up @@ -53,21 +53,21 @@ export const oidcHandlers = [
),
];

export const oidcServer = setupServer(...oidcHandlers);
export const tokenExchangeServer = setupServer(...tokenExchangeHandlers);

/**
* Registers vitest lifecycle hooks to start/stop the OIDC mock server and
* Registers vitest lifecycle hooks to start/stop the token mock server and
* reset handlers + env stubs between tests. Returns the server so individual
* tests can override handlers via `server.use(...)`.
*/
export function setupOidcServer() {
beforeAll(() => oidcServer.listen());
export function setupTokenExchangeServer() {
beforeAll(() => tokenExchangeServer.listen());
afterEach(() => {
oidcServer.resetHandlers();
tokenExchangeServer.resetHandlers();
vi.unstubAllEnvs();
});
afterAll(() => oidcServer.close());
return oidcServer;
afterAll(() => tokenExchangeServer.close());
return tokenExchangeServer;
}

/** Stubs the env vars required for `isGitHubActionsOidcAvailable()` to return true. */
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
MOCK_EXPIRES_AT,
MOCK_OIDC_URL,
MOCK_TOKENLESS_ARGOS_TOKEN,
setupOidcServer,
setupTokenExchangeServer,
stubOidcEnv,
} from "../mocks/oidc";

Expand Down Expand Up @@ -43,7 +43,7 @@ const baseConfig: Config = {
subset: false,
};

const server = setupOidcServer();
const server = setupTokenExchangeServer();

describe("resolveArgosToken", () => {
beforeEach(() => {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/github-actions-oidc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {
MOCK_OIDC_TOKEN,
MOCK_ARGOS_TOKEN,
MOCK_EXPIRES_AT,
setupOidcServer,
setupTokenExchangeServer,
stubOidcEnv,
} from "../mocks/oidc";

const server = setupOidcServer();
const server = setupTokenExchangeServer();

describe("isGitHubActionsOidcAvailable", () => {
it("returns true when all OIDC env vars are present and ARGOS_TOKEN is absent", () => {
Expand Down
78 changes: 7 additions & 71 deletions packages/core/src/github-actions-tokenless.test.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,25 @@
import { describe, it, expect, vi } from "vitest";
import { describe, it, expect } from "vitest";
import { http, HttpResponse } from "msw";
import {
isGitHubActionsTokenlessAvailable,
exchangeGitHubActionsTokenlessToken,
} from "./github-actions-tokenless";
import { exchangeGitHubActionsTokenlessToken } from "./github-actions-tokenless";
import {
MOCK_TOKENLESS_ARGOS_TOKEN,
MOCK_EXPIRES_AT,
setupOidcServer,
setupTokenExchangeServer,
} from "../mocks/oidc";

const base64Decode = (str: string): unknown =>
JSON.parse(Buffer.from(str, "base64").toString("utf8"));

const server = setupOidcServer();

describe("isGitHubActionsTokenlessAvailable", () => {
const prHeadCommit = "abc123def456abc123def456abc123def456abc1";

it("returns true when ciProvider is github-actions, prHeadCommit is set and ARGOS_TOKEN is absent", () => {
vi.stubEnv("ARGOS_TOKEN", "");
expect(
isGitHubActionsTokenlessAvailable({
ciProvider: "github-actions",
prHeadCommit,
}),
).toBe(true);
});

it("returns false when ARGOS_TOKEN is set", () => {
vi.stubEnv("ARGOS_TOKEN", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
expect(
isGitHubActionsTokenlessAvailable({
ciProvider: "github-actions",
prHeadCommit,
}),
).toBe(false);
});

it("returns false when ciProvider is not github-actions", () => {
vi.stubEnv("ARGOS_TOKEN", "");
expect(
isGitHubActionsTokenlessAvailable({
ciProvider: "gitlab-ci",
prHeadCommit,
}),
).toBe(false);
});

it("returns false when ciProvider is null", () => {
vi.stubEnv("ARGOS_TOKEN", "");
expect(
isGitHubActionsTokenlessAvailable({ ciProvider: null, prHeadCommit }),
).toBe(false);
});

it("returns false when prHeadCommit is missing", () => {
vi.stubEnv("ARGOS_TOKEN", "");
expect(
isGitHubActionsTokenlessAvailable({
ciProvider: "github-actions",
prHeadCommit: null,
}),
).toBe(false);
});
});
const server = setupTokenExchangeServer();

describe("exchangeGitHubActionsTokenlessToken", () => {
const baseConfig = {
originalRepository: "acme/web",
jobId: "job-1",
runId: "run-42",
prNumber: null,
prHeadCommit: "abc123def456abc123def456abc123def456abc1",
prHeadCommit: null,
commit: "abc123def456abc123def456abc123def456abc1",
Comment thread
gregberge marked this conversation as resolved.
branch: "main",
};

Expand Down Expand Up @@ -111,17 +58,6 @@ describe("exchangeGitHubActionsTokenlessToken", () => {
).rejects.toThrow("Automatic GitHub Actions variables detection failed");
});

it("throws when prHeadCommit is missing", async () => {
await expect(
exchangeGitHubActionsTokenlessToken({
apiBaseUrl: "https://api.argos-ci.com/v2/",
config: { ...baseConfig, prHeadCommit: null },
}),
).rejects.toThrow(
"GitHub PR head commit is required for tokenless authentication",
);
});

it("throws when the Argos API exchange returns an error", async () => {
server.use(
http.post(
Expand Down Expand Up @@ -157,7 +93,7 @@ describe("exchangeGitHubActionsTokenlessToken", () => {
config: { ...baseConfig, prNumber: 99 },
});

expect(capturedBody.commit).toBe(baseConfig.prHeadCommit);
expect(capturedBody.commit).toBe(baseConfig.commit);
expect(capturedBody.branch).toBe(baseConfig.branch);
expect(typeof capturedBody.tokenlessToken).toBe("string");
const bearer = capturedBody.tokenlessToken as string;
Expand Down
18 changes: 5 additions & 13 deletions packages/core/src/github-actions-tokenless.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@ const base64Encode = (obj: any) =>
* Check if GitHub Actions tokenless authentication is available for auto-detection.
*/
export function isGitHubActionsTokenlessAvailable(
config: Pick<Config, "ciProvider" | "prHeadCommit">,
config: Pick<Config, "ciProvider" | "prHeadCommit" | "commit">,
): boolean {
return Boolean(
config.ciProvider === "github-actions" &&
config.prHeadCommit &&
!process.env.ARGOS_TOKEN,
);
return config.ciProvider === "github-actions";
}

/**
Expand Down Expand Up @@ -55,15 +51,11 @@ export async function exchangeGitHubActionsTokenlessToken(args: {
| "prNumber"
| "branch"
| "prHeadCommit"
| "commit"
>;
}): Promise<string> {
const { apiBaseUrl, config } = args;

if (!config.prHeadCommit) {
throw new Error(
`GitHub PR head commit is required for tokenless authentication.`,
);
}
const commit = config.prHeadCommit ?? config.commit;
Comment thread
gregberge marked this conversation as resolved.

const tokenlessToken = getTokenlessBearerToken(config);

Expand All @@ -74,7 +66,7 @@ export async function exchangeGitHubActionsTokenlessToken(args: {
{
body: {
tokenlessToken,
commit: config.prHeadCommit,
commit,
branch: config.branch,
},
},
Expand Down
Loading