@argos-ci/core 4.3.1 → 4.5.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.d.ts CHANGED
@@ -145,6 +145,7 @@ interface components {
145
145
  key: string;
146
146
  name: string;
147
147
  baseName?: string | null;
148
+ parentName?: string | null;
148
149
  metadata?: {
149
150
  /**
150
151
  * @description Ignored. Can be set to get completions, validations and documentation in some editors.
@@ -227,6 +228,8 @@ interface components {
227
228
  } | null;
228
229
  pwTraceKey?: string | null;
229
230
  threshold?: number | null;
231
+ /** @default image/png */
232
+ contentType: string;
230
233
  };
231
234
  /** @description Build metadata */
232
235
  BuildMetadata: {
package/dist/index.js CHANGED
@@ -66,7 +66,7 @@ function gitMergeBase(input) {
66
66
  }
67
67
  function gitFetch(input) {
68
68
  execSync(
69
- `git fetch --update-head-ok --depth ${input.depth} origin ${input.ref}:${input.ref}`
69
+ `git fetch --force --update-head-ok --depth ${input.depth} origin ${input.ref}:${input.target}`
70
70
  );
71
71
  }
72
72
  function checkIsExecError(error) {
@@ -74,18 +74,23 @@ function checkIsExecError(error) {
74
74
  }
75
75
  function getMergeBaseCommitSha(input) {
76
76
  let depth = 200;
77
+ const argosBaseRef = `argos/${input.base}`;
78
+ const argosHeadRef = `argos/${input.head}`;
77
79
  while (depth < 1e3) {
78
- gitFetch({ ref: input.head, depth });
79
- gitFetch({ ref: input.base, depth });
80
- const mergeBase = gitMergeBase(input);
80
+ gitFetch({ ref: input.head, depth, target: argosHeadRef });
81
+ gitFetch({ ref: input.base, depth, target: argosBaseRef });
82
+ const mergeBase = gitMergeBase({
83
+ base: argosBaseRef,
84
+ head: argosHeadRef
85
+ });
81
86
  if (mergeBase) {
82
87
  return mergeBase;
83
88
  }
84
89
  depth += 200;
85
90
  }
86
91
  if (isDebugEnabled) {
87
- const headShas = listShas(input.head);
88
- const baseShas = listShas(input.base);
92
+ const headShas = listShas(argosHeadRef);
93
+ const baseShas = listShas(argosBaseRef);
89
94
  debug(
90
95
  `No merge base found for ${input.head} and ${input.base} with depth ${depth}`
91
96
  );
@@ -106,7 +111,13 @@ function listShas(path, maxCount) {
106
111
  }
107
112
  function listParentCommits(input) {
108
113
  const limit = 200;
109
- execSync(`git fetch --depth=${limit} origin ${input.sha}`);
114
+ try {
115
+ execSync(`git fetch --depth=${limit} origin ${input.sha}`);
116
+ } catch (error) {
117
+ if (error instanceof Error && error.message.includes("not our ref")) {
118
+ return [];
119
+ }
120
+ }
110
121
  return listShas(input.sha, limit);
111
122
  }
112
123
 
@@ -929,11 +940,11 @@ async function finalize(params) {
929
940
  import { createClient as createClient2, throwAPIError as throwAPIError2 } from "@argos-ci/api-client";
930
941
 
931
942
  // src/discovery.ts
932
- import { resolve } from "path";
943
+ import { extname, resolve } from "path";
933
944
  import glob from "fast-glob";
934
- var discoverScreenshots = async (patterns, { root = process.cwd(), ignore } = {}) => {
945
+ async function discoverSnapshots(patterns, { root = process.cwd(), ignore } = {}) {
935
946
  debug(
936
- `Discovering screenshots with patterns: ${Array.isArray(patterns) ? patterns.join(", ") : patterns} in ${root}`
947
+ `Discovering snapshots with patterns: ${Array.isArray(patterns) ? patterns.join(", ") : patterns} in ${root}`
937
948
  );
938
949
  const matches = await glob(patterns, { onlyFiles: true, ignore, cwd: root });
939
950
  return matches.map((match) => {
@@ -944,7 +955,11 @@ var discoverScreenshots = async (patterns, { root = process.cwd(), ignore } = {}
944
955
  path
945
956
  };
946
957
  });
947
- };
958
+ }
959
+ function checkIsValidImageFile(filename) {
960
+ const lowerFilename = extname(filename).toLowerCase();
961
+ return lowerFilename === ".png" || lowerFilename === ".jpg" || lowerFilename === ".jpeg";
962
+ }
948
963
 
949
964
  // src/optimize.ts
950
965
  import { promisify } from "util";
@@ -954,7 +969,10 @@ import tmp from "tmp";
954
969
  var tmpFile = promisify(tmp.file);
955
970
  var MAX_PIXELS = 8e7;
956
971
  var DEFAULT_MAX_WIDTH = 2048;
957
- var optimizeScreenshot = async (filepath) => {
972
+ async function optimizeScreenshot(filepath) {
973
+ if (!checkIsValidImageFile(filepath)) {
974
+ return filepath;
975
+ }
958
976
  try {
959
977
  const [resultFilePath, metadata] = await Promise.all([
960
978
  tmpFile(),
@@ -1010,7 +1028,7 @@ var optimizeScreenshot = async (filepath) => {
1010
1028
  cause: error
1011
1029
  });
1012
1030
  }
1013
- };
1031
+ }
1014
1032
 
1015
1033
  // src/hashing.ts
1016
1034
  import { createReadStream } from "fs";
@@ -1074,6 +1092,16 @@ async function getArgosCoreSDKIdentifier() {
1074
1092
  return `@argos-ci/core@${version}`;
1075
1093
  }
1076
1094
 
1095
+ // src/mime-type.ts
1096
+ import mime from "mime-types";
1097
+ function getSnapshotMimeType(filepath) {
1098
+ const type = mime.lookup(filepath);
1099
+ if (!type) {
1100
+ throw new Error(`Unable to determine snapshot file type for: ${filepath}`);
1101
+ }
1102
+ return type;
1103
+ }
1104
+
1077
1105
  // src/upload.ts
1078
1106
  var CHUNK_SIZE = 10;
1079
1107
  async function upload(params) {
@@ -1116,19 +1144,20 @@ async function upload(params) {
1116
1144
  return { build: createBuildResponse2.data.build, screenshots: [] };
1117
1145
  }
1118
1146
  const previewUrlFormatter = params.previewUrl ?? (config.previewBaseUrl ? { baseUrl: config.previewBaseUrl } : void 0);
1119
- const files = params.files ?? ["**/*.{png,jpg,jpeg}"];
1120
- debug("Using config and files", config, files);
1121
- const foundScreenshots = await discoverScreenshots(files, {
1147
+ const globs = params.files ?? ["**/*.{png,jpg,jpeg}"];
1148
+ debug("Using config and files", config, globs);
1149
+ const files = await discoverSnapshots(globs, {
1122
1150
  root: params.root,
1123
1151
  ignore: params.ignore
1124
1152
  });
1125
- debug("Found screenshots", foundScreenshots);
1126
- const screenshots = await Promise.all(
1127
- foundScreenshots.map(async (screenshot) => {
1153
+ debug("Found snapshots", files);
1154
+ const snapshots = await Promise.all(
1155
+ files.map(async (snapshot) => {
1156
+ const contentType = getSnapshotMimeType(snapshot.path);
1128
1157
  const [metadata, pwTracePath, optimizedPath] = await Promise.all([
1129
- readMetadata(screenshot.path),
1130
- getPlaywrightTracePath(screenshot.path),
1131
- optimizeScreenshot(screenshot.path)
1158
+ readMetadata(snapshot.path),
1159
+ getPlaywrightTracePath(snapshot.path),
1160
+ contentType.startsWith("image/") ? optimizeScreenshot(snapshot.path) : snapshot.path
1132
1161
  ]);
1133
1162
  const [hash, pwTraceHash] = await Promise.all([
1134
1163
  hashFile(optimizedPath),
@@ -1136,6 +1165,7 @@ async function upload(params) {
1136
1165
  ]);
1137
1166
  const threshold = metadata?.transient?.threshold ?? null;
1138
1167
  const baseName = metadata?.transient?.baseName ?? null;
1168
+ const parentName = metadata?.transient?.parentName ?? null;
1139
1169
  if (metadata) {
1140
1170
  delete metadata.transient;
1141
1171
  if (metadata.url && previewUrlFormatter) {
@@ -1146,13 +1176,15 @@ async function upload(params) {
1146
1176
  }
1147
1177
  }
1148
1178
  return {
1149
- ...screenshot,
1179
+ ...snapshot,
1150
1180
  hash,
1151
1181
  optimizedPath,
1152
1182
  metadata,
1153
1183
  threshold,
1154
1184
  baseName,
1155
- pwTrace: pwTracePath && pwTraceHash ? { path: pwTracePath, hash: pwTraceHash } : null
1185
+ parentName,
1186
+ pwTrace: pwTracePath && pwTraceHash ? { path: pwTracePath, hash: pwTraceHash } : null,
1187
+ contentType
1156
1188
  };
1157
1189
  })
1158
1190
  );
@@ -1196,15 +1228,15 @@ async function upload(params) {
1196
1228
  return null;
1197
1229
  })();
1198
1230
  debug("Creating build");
1199
- const [pwTraceKeys, screenshotKeys] = screenshots.reduce(
1200
- ([pwTraceKeys2, screenshotKeys2], screenshot) => {
1201
- if (screenshot.pwTrace && !pwTraceKeys2.includes(screenshot.pwTrace.hash)) {
1202
- pwTraceKeys2.push(screenshot.pwTrace.hash);
1231
+ const [pwTraceKeys, snapshotKeys] = snapshots.reduce(
1232
+ ([pwTraceKeys2, snapshotKeys2], snapshot) => {
1233
+ if (snapshot.pwTrace && !pwTraceKeys2.includes(snapshot.pwTrace.hash)) {
1234
+ pwTraceKeys2.push(snapshot.pwTrace.hash);
1203
1235
  }
1204
- if (!screenshotKeys2.includes(screenshot.hash)) {
1205
- screenshotKeys2.push(screenshot.hash);
1236
+ if (!snapshotKeys2.includes(snapshot.hash)) {
1237
+ snapshotKeys2.push(snapshot.hash);
1206
1238
  }
1207
- return [pwTraceKeys2, screenshotKeys2];
1239
+ return [pwTraceKeys2, snapshotKeys2];
1208
1240
  },
1209
1241
  [[], []]
1210
1242
  );
@@ -1216,7 +1248,7 @@ async function upload(params) {
1216
1248
  mode: config.mode,
1217
1249
  parallel: config.parallel,
1218
1250
  parallelNonce: config.parallelNonce,
1219
- screenshotKeys,
1251
+ screenshotKeys: snapshotKeys,
1220
1252
  pwTraceKeys,
1221
1253
  prNumber: config.prNumber,
1222
1254
  prHeadCommit: config.prHeadCommit,
@@ -1236,26 +1268,26 @@ async function upload(params) {
1236
1268
  debug("Got uploads url", result);
1237
1269
  const uploadFiles = [
1238
1270
  ...result.screenshots.map(({ key, putUrl }) => {
1239
- const screenshot = screenshots.find((s) => s.hash === key);
1240
- if (!screenshot) {
1241
- throw new Error(`Invariant: screenshot with hash ${key} not found`);
1271
+ const snapshot = snapshots.find((s) => s.hash === key);
1272
+ if (!snapshot) {
1273
+ throw new Error(`Invariant: snapshot with hash ${key} not found`);
1242
1274
  }
1243
1275
  return {
1244
1276
  url: putUrl,
1245
- path: screenshot.optimizedPath,
1246
- contentType: "image/png"
1277
+ path: snapshot.optimizedPath,
1278
+ contentType: snapshot.contentType
1247
1279
  };
1248
1280
  }),
1249
1281
  ...result.pwTraces?.map(({ key, putUrl }) => {
1250
- const screenshot = screenshots.find(
1282
+ const snapshot = snapshots.find(
1251
1283
  (s) => s.pwTrace && s.pwTrace.hash === key
1252
1284
  );
1253
- if (!screenshot || !screenshot.pwTrace) {
1285
+ if (!snapshot || !snapshot.pwTrace) {
1254
1286
  throw new Error(`Invariant: trace with ${key} not found`);
1255
1287
  }
1256
1288
  return {
1257
1289
  url: putUrl,
1258
- path: screenshot.pwTrace.path,
1290
+ path: snapshot.pwTrace.path,
1259
1291
  contentType: "application/json"
1260
1292
  };
1261
1293
  }) ?? []
@@ -1269,13 +1301,15 @@ async function upload(params) {
1269
1301
  }
1270
1302
  },
1271
1303
  body: {
1272
- screenshots: screenshots.map((screenshot) => ({
1273
- key: screenshot.hash,
1274
- name: screenshot.name,
1275
- metadata: screenshot.metadata,
1276
- pwTraceKey: screenshot.pwTrace?.hash ?? null,
1277
- threshold: screenshot.threshold ?? config?.threshold ?? null,
1278
- baseName: screenshot.baseName
1304
+ screenshots: snapshots.map((snapshot) => ({
1305
+ key: snapshot.hash,
1306
+ name: snapshot.name,
1307
+ metadata: snapshot.metadata,
1308
+ pwTraceKey: snapshot.pwTrace?.hash ?? null,
1309
+ threshold: snapshot.threshold ?? config?.threshold ?? null,
1310
+ baseName: snapshot.baseName,
1311
+ parentName: snapshot.parentName,
1312
+ contentType: snapshot.contentType
1279
1313
  })),
1280
1314
  parallel: config.parallel,
1281
1315
  parallelTotal: config.parallelTotal,
@@ -1286,7 +1320,7 @@ async function upload(params) {
1286
1320
  if (uploadBuildResponse.error) {
1287
1321
  throwAPIError2(uploadBuildResponse.error);
1288
1322
  }
1289
- return { build: uploadBuildResponse.data.build, screenshots };
1323
+ return { build: uploadBuildResponse.data.build, screenshots: snapshots };
1290
1324
  }
1291
1325
  async function uploadFilesToS3(files) {
1292
1326
  debug(`Split files in chunks of ${CHUNK_SIZE}`);
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": "4.3.1",
4
+ "version": "4.5.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "exports": {
@@ -40,11 +40,12 @@
40
40
  "access": "public"
41
41
  },
42
42
  "dependencies": {
43
- "@argos-ci/api-client": "0.12.0",
44
- "@argos-ci/util": "3.1.1",
43
+ "@argos-ci/api-client": "0.14.0",
44
+ "@argos-ci/util": "3.2.0",
45
45
  "convict": "^6.2.4",
46
46
  "debug": "^4.4.3",
47
47
  "fast-glob": "^3.3.3",
48
+ "mime-types": "^3.0.1",
48
49
  "sharp": "^0.34.4",
49
50
  "tmp": "^0.2.5"
50
51
  },
@@ -52,9 +53,10 @@
52
53
  "@octokit/webhooks": "^14.1.3",
53
54
  "@types/convict": "^6.1.6",
54
55
  "@types/debug": "^4.1.12",
56
+ "@types/mime-types": "^3.0.1",
55
57
  "@types/tmp": "^0.2.6",
56
58
  "@vercel/repository-dispatch": "^0.1.0",
57
- "msw": "^2.11.3",
59
+ "msw": "^2.11.6",
58
60
  "vitest": "catalog:"
59
61
  },
60
62
  "scripts": {
@@ -65,5 +67,5 @@
65
67
  "lint": "eslint .",
66
68
  "test": "vitest"
67
69
  },
68
- "gitHead": "52596285b0c837a17b86503c5dfc0f1dfb9c295d"
70
+ "gitHead": "fb54cf311b705f214041f18443352577be01e822"
69
71
  }