@argos-ci/core 5.2.1 → 5.3.0
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.
- package/dist/index.cjs +10 -0
- package/dist/index.d.mts +46 -1
- package/dist/index.mjs +126 -48
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -12,3 +12,13 @@ exports.readConfig = async (...args) => {
|
|
|
12
12
|
const { readConfig } = await import("./index.mjs");
|
|
13
13
|
return readConfig(...args);
|
|
14
14
|
};
|
|
15
|
+
|
|
16
|
+
exports.deploy = async (...args) => {
|
|
17
|
+
const { deploy } = await import("./index.mjs");
|
|
18
|
+
return deploy(...args);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
exports.skip = async (...args) => {
|
|
22
|
+
const { skip } = await import("./index.mjs");
|
|
23
|
+
return skip(...args);
|
|
24
|
+
};
|
package/dist/index.d.mts
CHANGED
|
@@ -139,6 +139,51 @@ declare function getConfigFromOptions({
|
|
|
139
139
|
} | false | undefined;
|
|
140
140
|
}): Promise<Config>;
|
|
141
141
|
//#endregion
|
|
142
|
+
//#region src/deploy.d.ts
|
|
143
|
+
interface DeployParameters {
|
|
144
|
+
/**
|
|
145
|
+
* Directory containing the static files to deploy.
|
|
146
|
+
*/
|
|
147
|
+
root: string;
|
|
148
|
+
/**
|
|
149
|
+
* Argos repository access token.
|
|
150
|
+
*/
|
|
151
|
+
token?: string;
|
|
152
|
+
/**
|
|
153
|
+
* Base URL of the Argos API.
|
|
154
|
+
* @default "https://api.argos-ci.com/v2/"
|
|
155
|
+
*/
|
|
156
|
+
apiBaseUrl?: string;
|
|
157
|
+
/**
|
|
158
|
+
* Git commit SHA of the deployment.
|
|
159
|
+
*/
|
|
160
|
+
commit?: string;
|
|
161
|
+
/**
|
|
162
|
+
* Git branch name of the deployment.
|
|
163
|
+
*/
|
|
164
|
+
branch?: string;
|
|
165
|
+
/**
|
|
166
|
+
* Pull request number associated with the deployment.
|
|
167
|
+
*/
|
|
168
|
+
prNumber?: number;
|
|
169
|
+
/**
|
|
170
|
+
* Deployment environment.
|
|
171
|
+
*/
|
|
172
|
+
environment?: "preview" | "production";
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Deploy a static site (e.g. Storybook) to Argos.
|
|
176
|
+
*/
|
|
177
|
+
declare function deploy(params: DeployParameters): Promise<{
|
|
178
|
+
id: string;
|
|
179
|
+
status: "pending" | "ready" | "error";
|
|
180
|
+
environment: "preview" | "production";
|
|
181
|
+
branch: string;
|
|
182
|
+
commitSha: string;
|
|
183
|
+
url: string;
|
|
184
|
+
createdAt: string;
|
|
185
|
+
}>;
|
|
186
|
+
//#endregion
|
|
142
187
|
//#region src/finalize.d.ts
|
|
143
188
|
type FinalizeParameters = {
|
|
144
189
|
parallel?: {
|
|
@@ -322,4 +367,4 @@ declare function skip(params: SkipParameters): Promise<{
|
|
|
322
367
|
build: ArgosAPISchema.components["schemas"]["Build"];
|
|
323
368
|
}>;
|
|
324
369
|
//#endregion
|
|
325
|
-
export { Config, FinalizeParameters, UploadParameters, finalize, getConfigFromOptions, readConfig, skip, upload };
|
|
370
|
+
export { Config, DeployParameters, FinalizeParameters, UploadParameters, deploy, finalize, getConfigFromOptions, readConfig, skip, upload };
|
package/dist/index.mjs
CHANGED
|
@@ -3,16 +3,16 @@ import convict from "convict";
|
|
|
3
3
|
import { execSync } from "node:child_process";
|
|
4
4
|
import createDebug from "debug";
|
|
5
5
|
import { createReadStream, existsSync, readFileSync } from "node:fs";
|
|
6
|
-
import {
|
|
7
|
-
import { basename, extname, resolve } from "node:path";
|
|
6
|
+
import { readFile, stat } from "node:fs/promises";
|
|
7
|
+
import { basename, extname, join, resolve } from "node:path";
|
|
8
8
|
import glob from "fast-glob";
|
|
9
|
+
import mime from "mime-types";
|
|
10
|
+
import { createClient, throwAPIError } from "@argos-ci/api-client";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
9
12
|
import { promisify } from "node:util";
|
|
10
13
|
import sharp from "sharp";
|
|
11
14
|
import tmp from "tmp";
|
|
12
|
-
import { createHash } from "node:crypto";
|
|
13
|
-
import { readFile } from "node:fs/promises";
|
|
14
15
|
import { getPlaywrightTracePath, readMetadata, readVersionFromPackage } from "@argos-ci/util";
|
|
15
|
-
import mime from "mime-types";
|
|
16
16
|
//#region src/debug.ts
|
|
17
17
|
const KEY = "@argos-ci/core";
|
|
18
18
|
const debug = createDebug(KEY);
|
|
@@ -978,6 +978,48 @@ async function getConfigFromOptions({ parallel, ...options }) {
|
|
|
978
978
|
});
|
|
979
979
|
}
|
|
980
980
|
//#endregion
|
|
981
|
+
//#region src/hashing.ts
|
|
982
|
+
const hashFile = async (filepath) => {
|
|
983
|
+
const fileStream = createReadStream(filepath);
|
|
984
|
+
const hash = createHash("sha256");
|
|
985
|
+
await new Promise((resolve, reject) => {
|
|
986
|
+
fileStream.on("error", reject);
|
|
987
|
+
hash.on("error", reject);
|
|
988
|
+
hash.on("finish", resolve);
|
|
989
|
+
fileStream.pipe(hash);
|
|
990
|
+
});
|
|
991
|
+
return hash.digest("hex");
|
|
992
|
+
};
|
|
993
|
+
//#endregion
|
|
994
|
+
//#region src/s3.ts
|
|
995
|
+
async function uploadFile(input) {
|
|
996
|
+
const file = await readFile(input.path);
|
|
997
|
+
const response = await fetch(input.url, {
|
|
998
|
+
method: "PUT",
|
|
999
|
+
headers: {
|
|
1000
|
+
"Content-Type": input.contentType,
|
|
1001
|
+
"Content-Length": file.length.toString()
|
|
1002
|
+
},
|
|
1003
|
+
signal: AbortSignal.timeout(3e4),
|
|
1004
|
+
body: new Uint8Array(file)
|
|
1005
|
+
});
|
|
1006
|
+
if (!response.ok) throw new Error(`Failed to upload file to ${input.url}: ${response.status} ${response.statusText}`);
|
|
1007
|
+
}
|
|
1008
|
+
//#endregion
|
|
1009
|
+
//#region src/util/chunk.ts
|
|
1010
|
+
/**
|
|
1011
|
+
* Split an array into chunks of a given size.
|
|
1012
|
+
*/
|
|
1013
|
+
const chunk = (collection, size) => {
|
|
1014
|
+
const result = [];
|
|
1015
|
+
for (let x = 0; x < Math.ceil(collection.length / size); x++) {
|
|
1016
|
+
const start = x * size;
|
|
1017
|
+
const end = start + size;
|
|
1018
|
+
result.push(collection.slice(start, end));
|
|
1019
|
+
}
|
|
1020
|
+
return result;
|
|
1021
|
+
};
|
|
1022
|
+
//#endregion
|
|
981
1023
|
//#region src/auth.ts
|
|
982
1024
|
const base64Encode = (obj) => Buffer.from(JSON.stringify(obj), "utf8").toString("base64");
|
|
983
1025
|
/**
|
|
@@ -1002,6 +1044,84 @@ function getAuthToken(args) {
|
|
|
1002
1044
|
}
|
|
1003
1045
|
}
|
|
1004
1046
|
//#endregion
|
|
1047
|
+
//#region src/deploy.ts
|
|
1048
|
+
const CHUNK_SIZE$1 = 10;
|
|
1049
|
+
function getContentType(filePath) {
|
|
1050
|
+
return mime.lookup(filePath) || "application/octet-stream";
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Deploy a static site (e.g. Storybook) to Argos.
|
|
1054
|
+
*/
|
|
1055
|
+
async function deploy(params) {
|
|
1056
|
+
const { token: _token, ...debugParams } = params;
|
|
1057
|
+
debug("Starting deploy with params", debugParams);
|
|
1058
|
+
const config = await getConfigFromOptions(params);
|
|
1059
|
+
const authToken = getAuthToken(config);
|
|
1060
|
+
const apiClient = createClient({
|
|
1061
|
+
baseUrl: config.apiBaseUrl,
|
|
1062
|
+
authToken
|
|
1063
|
+
});
|
|
1064
|
+
debug("Listing files in", params.root);
|
|
1065
|
+
const relativePaths = await glob("**/*", {
|
|
1066
|
+
cwd: params.root,
|
|
1067
|
+
onlyFiles: true,
|
|
1068
|
+
dot: true
|
|
1069
|
+
});
|
|
1070
|
+
if (relativePaths.length === 0) throw new Error(`No files found in directory: ${params.root}`);
|
|
1071
|
+
debug(`Found ${relativePaths.length} files`);
|
|
1072
|
+
const files = await Promise.all(relativePaths.map(async (relativePath) => {
|
|
1073
|
+
const absolutePath = join(params.root, relativePath);
|
|
1074
|
+
const [hash, stats] = await Promise.all([hashFile(absolutePath), stat(absolutePath)]);
|
|
1075
|
+
return {
|
|
1076
|
+
absolutePath,
|
|
1077
|
+
path: relativePath,
|
|
1078
|
+
hash,
|
|
1079
|
+
size: stats.size,
|
|
1080
|
+
contentType: getContentType(absolutePath)
|
|
1081
|
+
};
|
|
1082
|
+
}));
|
|
1083
|
+
const filesByPath = new Map(files.map((file) => [file.path, file]));
|
|
1084
|
+
debug("Creating deployment");
|
|
1085
|
+
const createResponse = await apiClient.POST("/deployments", { body: {
|
|
1086
|
+
commit: config.commit ?? null,
|
|
1087
|
+
branch: config.branch ?? null,
|
|
1088
|
+
prNumber: config.prNumber ?? null,
|
|
1089
|
+
environment: params.environment,
|
|
1090
|
+
files: files.map(({ path, hash, size, contentType }) => ({
|
|
1091
|
+
path,
|
|
1092
|
+
hash,
|
|
1093
|
+
size,
|
|
1094
|
+
contentType
|
|
1095
|
+
}))
|
|
1096
|
+
} });
|
|
1097
|
+
if (createResponse.error) throwAPIError(createResponse.error);
|
|
1098
|
+
const { deploymentId, uploadFiles: filesToUpload } = createResponse.data;
|
|
1099
|
+
debug(`Deployment created: ${deploymentId}, files to upload: ${filesToUpload.length}`);
|
|
1100
|
+
const uploadChunks = chunk(filesToUpload.map(({ path, uploadUrl }) => {
|
|
1101
|
+
const file = filesByPath.get(path);
|
|
1102
|
+
if (!file) throw new Error(`Invariant: file not found for path: ${path}`);
|
|
1103
|
+
return {
|
|
1104
|
+
url: uploadUrl,
|
|
1105
|
+
path: file.absolutePath,
|
|
1106
|
+
contentType: file.contentType
|
|
1107
|
+
};
|
|
1108
|
+
}), CHUNK_SIZE$1);
|
|
1109
|
+
for (let i = 0; i < uploadChunks.length; i++) {
|
|
1110
|
+
const uploadChunk = uploadChunks[i];
|
|
1111
|
+
if (!uploadChunk) continue;
|
|
1112
|
+
debug(`Uploading chunk ${i + 1}/${uploadChunks.length}`);
|
|
1113
|
+
await Promise.all(uploadChunk.map(({ url, path, contentType }) => uploadFile({
|
|
1114
|
+
url,
|
|
1115
|
+
path,
|
|
1116
|
+
contentType
|
|
1117
|
+
})));
|
|
1118
|
+
}
|
|
1119
|
+
debug("Finalizing deployment");
|
|
1120
|
+
const finalizeResponse = await apiClient.POST("/deployments/{deploymentId}/finalize", { params: { path: { deploymentId } } });
|
|
1121
|
+
if (finalizeResponse.error) throwAPIError(finalizeResponse.error);
|
|
1122
|
+
return finalizeResponse.data;
|
|
1123
|
+
}
|
|
1124
|
+
//#endregion
|
|
1005
1125
|
//#region src/finalize.ts
|
|
1006
1126
|
/**
|
|
1007
1127
|
* Finalize pending builds.
|
|
@@ -1100,48 +1220,6 @@ async function optimizeScreenshot(filepath) {
|
|
|
1100
1220
|
}
|
|
1101
1221
|
}
|
|
1102
1222
|
//#endregion
|
|
1103
|
-
//#region src/hashing.ts
|
|
1104
|
-
const hashFile = async (filepath) => {
|
|
1105
|
-
const fileStream = createReadStream(filepath);
|
|
1106
|
-
const hash = createHash("sha256");
|
|
1107
|
-
await new Promise((resolve, reject) => {
|
|
1108
|
-
fileStream.on("error", reject);
|
|
1109
|
-
hash.on("error", reject);
|
|
1110
|
-
hash.on("finish", resolve);
|
|
1111
|
-
fileStream.pipe(hash);
|
|
1112
|
-
});
|
|
1113
|
-
return hash.digest("hex");
|
|
1114
|
-
};
|
|
1115
|
-
//#endregion
|
|
1116
|
-
//#region src/s3.ts
|
|
1117
|
-
async function uploadFile(input) {
|
|
1118
|
-
const file = await readFile(input.path);
|
|
1119
|
-
const response = await fetch(input.url, {
|
|
1120
|
-
method: "PUT",
|
|
1121
|
-
headers: {
|
|
1122
|
-
"Content-Type": input.contentType,
|
|
1123
|
-
"Content-Length": file.length.toString()
|
|
1124
|
-
},
|
|
1125
|
-
signal: AbortSignal.timeout(3e4),
|
|
1126
|
-
body: new Uint8Array(file)
|
|
1127
|
-
});
|
|
1128
|
-
if (!response.ok) throw new Error(`Failed to upload file to ${input.url}: ${response.status} ${response.statusText}`);
|
|
1129
|
-
}
|
|
1130
|
-
//#endregion
|
|
1131
|
-
//#region src/util/chunk.ts
|
|
1132
|
-
/**
|
|
1133
|
-
* Split an array into chunks of a given size.
|
|
1134
|
-
*/
|
|
1135
|
-
const chunk = (collection, size) => {
|
|
1136
|
-
const result = [];
|
|
1137
|
-
for (let x = 0; x < Math.ceil(collection.length / size); x++) {
|
|
1138
|
-
const start = x * size;
|
|
1139
|
-
const end = start + size;
|
|
1140
|
-
result.push(collection.slice(start, end));
|
|
1141
|
-
}
|
|
1142
|
-
return result;
|
|
1143
|
-
};
|
|
1144
|
-
//#endregion
|
|
1145
1223
|
//#region src/version.ts
|
|
1146
1224
|
const require = createRequire(import.meta.url);
|
|
1147
1225
|
/**
|
|
@@ -1386,4 +1464,4 @@ function formatPreviewUrl(url, formatter) {
|
|
|
1386
1464
|
return new URL(urlObj.pathname + urlObj.search + urlObj.hash, formatter.baseUrl).href;
|
|
1387
1465
|
}
|
|
1388
1466
|
//#endregion
|
|
1389
|
-
export { finalize, getConfigFromOptions, readConfig, skip, upload };
|
|
1467
|
+
export { deploy, finalize, getConfigFromOptions, readConfig, skip, upload };
|
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.
|
|
4
|
+
"version": "5.3.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@argos-ci/api-client": "0.
|
|
42
|
+
"@argos-ci/api-client": "0.19.0",
|
|
43
43
|
"@argos-ci/util": "3.4.0",
|
|
44
44
|
"convict": "^6.2.5",
|
|
45
45
|
"debug": "^4.4.3",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"lint": "eslint .",
|
|
68
68
|
"test": "vitest"
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "9ff7241b23dce530910d0fffd113db23e2fbe949"
|
|
71
71
|
}
|