@argos-ci/core 5.3.1 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +110 -22
  2. package/package.json +6 -6
package/dist/index.mjs CHANGED
@@ -1017,28 +1017,116 @@ const chunk = (collection, size) => {
1017
1017
  return result;
1018
1018
  };
1019
1019
  //#endregion
1020
- //#region src/auth.ts
1020
+ //#region src/github-actions-oidc.ts
1021
+ /**
1022
+ * Check if GitHub Actions OIDC is available for auto-detection.
1023
+ */
1024
+ function isGitHubActionsOidcAvailable() {
1025
+ return process.env.GITHUB_ACTIONS === "true" && Boolean(process.env.ACTIONS_ID_TOKEN_REQUEST_URL) && Boolean(process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN) && !process.env.ARGOS_TOKEN;
1026
+ }
1027
+ async function fetchOidcToken(args) {
1028
+ if (!process.env.ACTIONS_ID_TOKEN_REQUEST_URL) throw new Error(`ACTIONS_ID_TOKEN_REQUEST_URL not found`);
1029
+ if (!process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN) throw new Error(`ACTIONS_ID_TOKEN_REQUEST_TOKEN not found`);
1030
+ const url = new URL(process.env.ACTIONS_ID_TOKEN_REQUEST_URL);
1031
+ url.searchParams.set("audience", args.audience);
1032
+ const response = await fetch(url.toString(), { headers: {
1033
+ Authorization: `Bearer ${process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN}`,
1034
+ Accept: "application/json; api-version=2.0",
1035
+ "Content-Type": "application/json"
1036
+ } });
1037
+ if (!response.ok) throw new Error(`Failed to fetch GitHub Actions OIDC token: ${response.status} ${response.statusText}`);
1038
+ const data = await response.json();
1039
+ if (!data.value) throw new Error("Invalid GitHub Actions OIDC token response: missing 'value' field");
1040
+ return data.value;
1041
+ }
1042
+ /**
1043
+ * Exchange a GitHub Actions OIDC token for a short-lived Argos token.
1044
+ */
1045
+ async function exchangeGitHubActionsOidcToken(args) {
1046
+ const { apiBaseUrl, config } = args;
1047
+ const audience = new URL(apiBaseUrl).origin;
1048
+ const oidcToken = await fetchOidcToken({ audience });
1049
+ const result = await createClient({ baseUrl: apiBaseUrl }).POST("/auth/github-actions/oidc/exchange", { body: {
1050
+ oidcToken,
1051
+ repository: config.originalRepository ?? void 0,
1052
+ commit: config.commit,
1053
+ branch: config.branch,
1054
+ pullRequestNumber: config.prNumber ?? void 0
1055
+ } });
1056
+ if (result.error) throwAPIError(result.error);
1057
+ return result.data.token;
1058
+ }
1059
+ //#endregion
1060
+ //#region src/github-actions-tokenless.ts
1021
1061
  const base64Encode = (obj) => Buffer.from(JSON.stringify(obj), "utf8").toString("base64");
1022
1062
  /**
1023
- * Get the authentication token.
1063
+ * Check if GitHub Actions tokenless authentication is available for auto-detection.
1024
1064
  */
1025
- function getAuthToken(args) {
1026
- const { token, ciProvider, originalRepository: repository, jobId, runId, prNumber } = args;
1027
- if (token) return token;
1028
- switch (ciProvider) {
1029
- case "github-actions": {
1030
- if (!repository || !jobId || !runId) throw new Error(`Automatic GitHub Actions variables detection failed. Please add the 'ARGOS_TOKEN'`);
1031
- const [owner, repo] = repository.split("/");
1032
- return `tokenless-github-${base64Encode({
1033
- owner,
1034
- repository: repo,
1035
- jobId,
1036
- runId,
1037
- prNumber: prNumber ?? void 0
1038
- })}`;
1039
- }
1040
- default: throw new Error("Missing Argos repository token 'ARGOS_TOKEN'");
1065
+ function isGitHubActionsTokenlessAvailable(config) {
1066
+ return Boolean(config.ciProvider === "github-actions" && config.prHeadCommit && !process.env.ARGOS_TOKEN);
1067
+ }
1068
+ /**
1069
+ * Build a tokenless GitHub Actions bearer token from the CI environment.
1070
+ */
1071
+ function getTokenlessBearerToken(config) {
1072
+ const { originalRepository: repository, jobId, runId, prNumber } = config;
1073
+ if (!repository || !jobId || !runId) throw new Error(`Automatic GitHub Actions variables detection failed. Please set ARGOS_TOKEN.`);
1074
+ const [owner, repo] = repository.split("/");
1075
+ return `tokenless-github-${base64Encode({
1076
+ owner,
1077
+ repository: repo,
1078
+ jobId,
1079
+ runId,
1080
+ prNumber: prNumber ?? void 0
1081
+ })}`;
1082
+ }
1083
+ /**
1084
+ * Exchange a tokenless GitHub Actions bearer token for a short-lived Argos token.
1085
+ */
1086
+ async function exchangeGitHubActionsTokenlessToken(args) {
1087
+ const { apiBaseUrl, config } = args;
1088
+ if (!config.prHeadCommit) throw new Error(`GitHub PR head commit is required for tokenless authentication.`);
1089
+ const tokenlessToken = getTokenlessBearerToken(config);
1090
+ const result = await createClient({ baseUrl: apiBaseUrl }).POST("/auth/github-actions/tokenless/exchange", { body: {
1091
+ tokenlessToken,
1092
+ commit: config.prHeadCommit,
1093
+ branch: config.branch
1094
+ } });
1095
+ if (result.error) throwAPIError(result.error);
1096
+ return result.data.token;
1097
+ }
1098
+ //#endregion
1099
+ //#region src/auth.ts
1100
+ /**
1101
+ * Resolve the Argos authentication token.
1102
+ * Priority: ARGOS_TOKEN > GitHub Actions OIDC > GitHub Actions tokenless exchange.
1103
+ */
1104
+ async function resolveArgosToken(config) {
1105
+ if (config.token) {
1106
+ debug("Authenticated with ARGOS_TOKEN.");
1107
+ return config.token;
1108
+ }
1109
+ if (isGitHubActionsOidcAvailable()) {
1110
+ const token = await exchangeGitHubActionsOidcToken({
1111
+ apiBaseUrl: config.apiBaseUrl,
1112
+ config
1113
+ });
1114
+ debug("Authenticated with GitHub Actions OIDC.");
1115
+ debug(`Repository: ${config.originalRepository}`);
1116
+ debug(`Run: ${config.runId}`);
1117
+ return token;
1118
+ }
1119
+ if (isGitHubActionsTokenlessAvailable(config)) {
1120
+ const token = await exchangeGitHubActionsTokenlessToken({
1121
+ apiBaseUrl: config.apiBaseUrl,
1122
+ config
1123
+ });
1124
+ debug("Authenticated with GitHub Actions tokenless exchange.");
1125
+ debug(`Repository: ${config.originalRepository}`);
1126
+ debug(`Run: ${config.runId}`);
1127
+ return token;
1041
1128
  }
1129
+ throw new Error("Missing Argos repository token 'ARGOS_TOKEN'");
1042
1130
  }
1043
1131
  //#endregion
1044
1132
  //#region src/deploy.ts
@@ -1053,7 +1141,7 @@ async function deploy(params) {
1053
1141
  const { token: _token, ...debugParams } = params;
1054
1142
  debug("Starting deploy with params", debugParams);
1055
1143
  const config = await getConfigFromOptions(params);
1056
- const authToken = getAuthToken(config);
1144
+ const authToken = await resolveArgosToken(config);
1057
1145
  const apiClient = createClient({
1058
1146
  baseUrl: config.apiBaseUrl,
1059
1147
  authToken
@@ -1125,7 +1213,7 @@ async function deploy(params) {
1125
1213
  */
1126
1214
  async function finalize(params) {
1127
1215
  const config = await readConfig({ parallelNonce: params.parallel?.nonce });
1128
- const authToken = getAuthToken(config);
1216
+ const authToken = await resolveArgosToken(config);
1129
1217
  const apiClient = createClient({
1130
1218
  baseUrl: config.apiBaseUrl,
1131
1219
  authToken
@@ -1242,7 +1330,7 @@ function getSnapshotMimeType(filepath) {
1242
1330
  */
1243
1331
  async function skip(params) {
1244
1332
  const [config, argosSdk] = await Promise.all([getConfigFromOptions(params), getArgosCoreSDKIdentifier()]);
1245
- const authToken = getAuthToken(config);
1333
+ const authToken = await resolveArgosToken(config);
1246
1334
  const createBuildResponse = await createClient({
1247
1335
  baseUrl: config.apiBaseUrl,
1248
1336
  authToken
@@ -1279,7 +1367,7 @@ const CHUNK_SIZE = 10;
1279
1367
  async function upload(params) {
1280
1368
  debug("Starting upload with params", params);
1281
1369
  const [config, argosSdk] = await Promise.all([getConfigFromOptions(params), getArgosCoreSDKIdentifier()]);
1282
- const authToken = getAuthToken(config);
1370
+ const authToken = await resolveArgosToken(config);
1283
1371
  const apiClient = createClient({
1284
1372
  baseUrl: config.apiBaseUrl,
1285
1373
  authToken
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@argos-ci/core",
3
3
  "description": "Node.js SDK for visual testing with Argos.",
4
- "version": "5.3.1",
4
+ "version": "6.0.1",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {
@@ -23,7 +23,7 @@
23
23
  "url": "https://github.com/argos-ci/argos-javascript/issues"
24
24
  },
25
25
  "engines": {
26
- "node": ">=20.0.0"
26
+ "node": ">=22.0.0"
27
27
  },
28
28
  "license": "MIT",
29
29
  "keywords": [
@@ -39,8 +39,8 @@
39
39
  "access": "public"
40
40
  },
41
41
  "dependencies": {
42
- "@argos-ci/api-client": "0.19.0",
43
- "@argos-ci/util": "3.4.0",
42
+ "@argos-ci/api-client": "0.20.1",
43
+ "@argos-ci/util": "4.0.0",
44
44
  "convict": "^6.2.5",
45
45
  "debug": "^4.4.3",
46
46
  "fast-glob": "^3.3.3",
@@ -56,7 +56,7 @@
56
56
  "@types/node": "catalog:",
57
57
  "@types/tmp": "^0.2.6",
58
58
  "@vercel/repository-dispatch": "^0.1.0",
59
- "msw": "^2.12.14",
59
+ "msw": "^2.14.5",
60
60
  "vitest": "catalog:"
61
61
  },
62
62
  "scripts": {
@@ -67,5 +67,5 @@
67
67
  "lint": "eslint .",
68
68
  "test": "vitest"
69
69
  },
70
- "gitHead": "42a0f0da3b0ebae3e86f01a07cfdf5c320057f00"
70
+ "gitHead": "75892460d881beaf87ae6a6c62c2c82d5df8fba4"
71
71
  }