@argos-ci/core 2.12.0 → 3.1.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
@@ -76,6 +76,13 @@ interface UploadParameters {
76
76
  * Build metadata.
77
77
  */
78
78
  metadata?: BuildMetadata;
79
+ /**
80
+ * Preview URL configuration.
81
+ * Accepts a base URL or a function that receives the URL and returns the preview URL.
82
+ */
83
+ previewUrl?: {
84
+ baseUrl: string;
85
+ } | ((url: string) => string);
79
86
  }
80
87
  /**
81
88
  * Upload screenshots to Argos.
@@ -84,7 +91,7 @@ declare function upload(params: UploadParameters): Promise<{
84
91
  build: {
85
92
  id: ArgosAPISchema.components["schemas"]["BuildId"];
86
93
  number: number;
87
- status: ("accepted" | "rejected") | ("stable" | "diffDetected") | ("expired" | "pending" | "progress" | "error" | "aborted");
94
+ status: ("accepted" | "rejected") | ("no-changes" | "changes-detected") | ("expired" | "pending" | "progress" | "error" | "aborted");
88
95
  url: string;
89
96
  notification: {
90
97
  description: string;
@@ -124,7 +131,7 @@ interface components {
124
131
  /** @description The build number */
125
132
  number: number;
126
133
  /** @description The status of the build */
127
- status: ("accepted" | "rejected") | ("stable" | "diffDetected") | ("expired" | "pending" | "progress" | "error" | "aborted");
134
+ status: ("accepted" | "rejected") | ("no-changes" | "changes-detected") | ("expired" | "pending" | "progress" | "error" | "aborted");
128
135
  /**
129
136
  * Format: uri
130
137
  * @description The URL of the build
@@ -168,6 +175,7 @@ interface components {
168
175
  baseName?: string | null;
169
176
  metadata?: {
170
177
  url?: string;
178
+ previewUrl?: string;
171
179
  viewport?: {
172
180
  width: number;
173
181
  height: number;
@@ -274,6 +282,7 @@ interface Config {
274
282
  mode: "ci" | "monitoring" | null;
275
283
  ciProvider: string | null;
276
284
  threshold: number | null;
285
+ previewBaseUrl: string | null;
277
286
  }
278
287
  declare function readConfig(options?: Partial<Config>): Promise<Config>;
279
288
 
package/dist/index.js CHANGED
@@ -1,8 +1,5 @@
1
1
  // src/upload.ts
2
- import {
3
- createClient,
4
- throwAPIError
5
- } from "@argos-ci/api-client";
2
+ import { createClient, throwAPIError } from "@argos-ci/api-client";
6
3
 
7
4
  // src/config.ts
8
5
  import convict from "convict";
@@ -251,12 +248,18 @@ function getBranchFromPayload(payload) {
251
248
  return null;
252
249
  }
253
250
  function getRepositoryFromContext({ env }) {
254
- if (!env.GITHUB_REPOSITORY) return null;
251
+ if (!env.GITHUB_REPOSITORY) {
252
+ return null;
253
+ }
255
254
  return env.GITHUB_REPOSITORY.split("/")[1] || null;
256
255
  }
257
256
  function readEventPayload({ env }) {
258
- if (!env.GITHUB_EVENT_PATH) return null;
259
- if (!existsSync(env.GITHUB_EVENT_PATH)) return null;
257
+ if (!env.GITHUB_EVENT_PATH) {
258
+ return null;
259
+ }
260
+ if (!existsSync(env.GITHUB_EVENT_PATH)) {
261
+ return null;
262
+ }
260
263
  return JSON.parse(readFileSync(env.GITHUB_EVENT_PATH, "utf-8"));
261
264
  }
262
265
  function getPullRequestFromPayload(payload) {
@@ -290,7 +293,7 @@ var service4 = {
290
293
  jobId: env.GITHUB_JOB || null,
291
294
  runId: env.GITHUB_RUN_ID || null,
292
295
  runAttempt: env.GITHUB_RUN_ATTEMPT ? Number(env.GITHUB_RUN_ATTEMPT) : null,
293
- nonce: `${env.GITHUB_RUN_ID}-${env.GITHUB_RUN_ATTEMPT}` || null,
296
+ nonce: `${env.GITHUB_RUN_ID}-${env.GITHUB_RUN_ATTEMPT}`,
294
297
  branch: getBranchFromContext(context) || pullRequest?.head.ref || (payload ? getBranchFromPayload(payload) : null) || null,
295
298
  prNumber: pullRequest?.number || null,
296
299
  prHeadCommit: pullRequest?.head.sha ?? null,
@@ -337,15 +340,21 @@ var circleci_default = service5;
337
340
 
338
341
  // src/ci-environment/services/travis.ts
339
342
  var getOwner = ({ env }) => {
340
- if (!env.TRAVIS_REPO_SLUG) return null;
343
+ if (!env.TRAVIS_REPO_SLUG) {
344
+ return null;
345
+ }
341
346
  return env.TRAVIS_REPO_SLUG.split("/")[0] || null;
342
347
  };
343
348
  var getRepository = ({ env }) => {
344
- if (!env.TRAVIS_REPO_SLUG) return null;
349
+ if (!env.TRAVIS_REPO_SLUG) {
350
+ return null;
351
+ }
345
352
  return env.TRAVIS_REPO_SLUG.split("/")[1] || null;
346
353
  };
347
354
  var getPrNumber3 = ({ env }) => {
348
- if (env.TRAVIS_PULL_REQUEST) return Number(env.TRAVIS_PULL_REQUEST);
355
+ if (env.TRAVIS_PULL_REQUEST) {
356
+ return Number(env.TRAVIS_PULL_REQUEST);
357
+ }
349
358
  return null;
350
359
  };
351
360
  var service6 = {
@@ -476,7 +485,7 @@ async function getCiEnvironment() {
476
485
 
477
486
  // src/config.ts
478
487
  var mustBeApiBaseUrl = (value) => {
479
- const URL_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
488
+ const URL_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
480
489
  if (!URL_REGEX.test(value)) {
481
490
  throw new Error("Invalid Argos API base URL");
482
491
  }
@@ -628,6 +637,12 @@ var schema = {
628
637
  format: "float-percent",
629
638
  default: null,
630
639
  nullable: true
640
+ },
641
+ previewBaseUrl: {
642
+ env: "ARGOS_PREVIEW_BASE_URL",
643
+ format: String,
644
+ default: null,
645
+ nullable: true
631
646
  }
632
647
  };
633
648
  var createConfig = () => {
@@ -659,7 +674,8 @@ async function readConfig(options = {}) {
659
674
  parallelTotal: options.parallelTotal ?? config.get("parallelTotal") ?? null,
660
675
  parallelIndex: options.parallelIndex ?? config.get("parallelIndex") ?? null,
661
676
  mode: options.mode || config.get("mode") || null,
662
- ciProvider: ciEnv?.key || null
677
+ ciProvider: ciEnv?.key || null,
678
+ previewBaseUrl: config.get("previewBaseUrl") || null
663
679
  });
664
680
  config.validate();
665
681
  return config.get();
@@ -685,16 +701,61 @@ var discoverScreenshots = async (patterns, { root = process.cwd(), ignore } = {}
685
701
 
686
702
  // src/optimize.ts
687
703
  import { promisify } from "node:util";
704
+ import { basename } from "node:path";
688
705
  import sharp from "sharp";
689
706
  import tmp from "tmp";
690
707
  var tmpFile = promisify(tmp.file);
708
+ var MAX_PIXELS = 8e7;
709
+ var DEFAULT_MAX_WIDTH = 2048;
691
710
  var optimizeScreenshot = async (filepath) => {
692
711
  try {
693
- const resultFilePath = await tmpFile();
694
- await sharp(filepath).resize(2048, 64e3, {
695
- fit: "inside",
696
- withoutEnlargement: true
697
- }).png({ force: true }).toFile(resultFilePath);
712
+ const [resultFilePath, metadata] = await Promise.all([
713
+ tmpFile(),
714
+ sharp(filepath).metadata()
715
+ ]);
716
+ const { width, height } = metadata;
717
+ const maxDimensions = (() => {
718
+ if (!width || !height) {
719
+ return {
720
+ width: DEFAULT_MAX_WIDTH,
721
+ height: Math.floor(MAX_PIXELS / DEFAULT_MAX_WIDTH)
722
+ };
723
+ }
724
+ const nbPixels = width * height;
725
+ if (nbPixels <= MAX_PIXELS) {
726
+ return null;
727
+ }
728
+ if (width < height) {
729
+ return {
730
+ width: DEFAULT_MAX_WIDTH,
731
+ height: Math.floor(MAX_PIXELS / DEFAULT_MAX_WIDTH)
732
+ };
733
+ }
734
+ const scaleFactor = Math.sqrt(MAX_PIXELS / nbPixels);
735
+ return {
736
+ width: Math.floor(width * scaleFactor),
737
+ height: Math.floor(height * scaleFactor)
738
+ };
739
+ })();
740
+ let operation = sharp(filepath);
741
+ if (maxDimensions) {
742
+ operation = operation.resize(maxDimensions.width, maxDimensions.height, {
743
+ fit: "inside",
744
+ withoutEnlargement: true
745
+ });
746
+ }
747
+ await operation.png({ force: true }).toFile(resultFilePath);
748
+ if (width && height && maxDimensions) {
749
+ const { width: maxWidth, height: maxHeight } = maxDimensions;
750
+ const widthRatio = maxWidth / width;
751
+ const heightRatio = maxHeight / height;
752
+ const scaleFactor = Math.min(widthRatio, heightRatio);
753
+ const newWidth = Math.floor(width * scaleFactor);
754
+ const newHeight = Math.floor(height * scaleFactor);
755
+ console.warn(
756
+ `Image ${basename(filepath)} resized from ${width}x${height} to ${newWidth}x${newHeight}.`
757
+ );
758
+ }
698
759
  return resultFilePath;
699
760
  } catch (error) {
700
761
  const message = error instanceof Error ? error.message : "Unknown Error";
@@ -772,8 +833,8 @@ var upload = async (input) => {
772
833
  var chunk = (collection, size) => {
773
834
  const result = [];
774
835
  for (let x = 0; x < Math.ceil(collection.length / size); x++) {
775
- let start = x * size;
776
- let end = start + size;
836
+ const start = x * size;
837
+ const end = start + size;
777
838
  result.push(collection.slice(start, end));
778
839
  }
779
840
  return result;
@@ -830,12 +891,23 @@ async function uploadFilesToS3(files) {
830
891
  debugTimeEnd(timeLabel);
831
892
  }
832
893
  }
894
+ function formatPreviewUrl(url, formatter) {
895
+ if (typeof formatter === "function") {
896
+ return formatter(url);
897
+ }
898
+ const urlObj = new URL(url);
899
+ return new URL(
900
+ urlObj.pathname + urlObj.search + urlObj.hash,
901
+ formatter.baseUrl
902
+ ).href;
903
+ }
833
904
  async function upload2(params) {
834
905
  debug("Starting upload with params", params);
835
906
  const [config, argosSdk] = await Promise.all([
836
907
  getConfigFromOptions(params),
837
908
  getArgosCoreSDKIdentifier()
838
909
  ]);
910
+ const previewUrlFormatter = params.previewUrl ?? (config.previewBaseUrl ? { baseUrl: config.previewBaseUrl } : void 0);
839
911
  const files = params.files ?? ["**/*.{png,jpg,jpeg}"];
840
912
  debug("Using config and files", config, files);
841
913
  const authToken = getAuthToken(config);
@@ -863,6 +935,12 @@ async function upload2(params) {
863
935
  const baseName = metadata?.transient?.baseName ?? null;
864
936
  if (metadata) {
865
937
  delete metadata.transient;
938
+ if (metadata.url && previewUrlFormatter) {
939
+ metadata.previewUrl = formatPreviewUrl(
940
+ metadata.url,
941
+ previewUrlFormatter
942
+ );
943
+ }
866
944
  }
867
945
  return {
868
946
  ...screenshot,
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": "2.12.0",
4
+ "version": "3.1.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "exports": {
@@ -40,25 +40,25 @@
40
40
  "access": "public"
41
41
  },
42
42
  "dependencies": {
43
- "@argos-ci/api-client": "0.7.1",
44
- "@argos-ci/util": "2.2.1",
45
- "axios": "^1.7.7",
43
+ "@argos-ci/api-client": "0.8.0",
44
+ "@argos-ci/util": "2.3.0",
45
+ "axios": "^1.7.9",
46
46
  "convict": "^6.2.4",
47
- "debug": "^4.3.7",
48
- "fast-glob": "^3.3.2",
47
+ "debug": "^4.4.0",
48
+ "fast-glob": "^3.3.3",
49
49
  "sharp": "^0.33.5",
50
50
  "tmp": "^0.2.3"
51
51
  },
52
52
  "devDependencies": {
53
- "@octokit/webhooks": "^13.4.1",
53
+ "@octokit/webhooks": "^13.4.2",
54
54
  "@types/convict": "^6.1.6",
55
55
  "@types/debug": "^4.1.12",
56
56
  "@types/tmp": "^0.2.6",
57
- "msw": "^2.5.1"
57
+ "msw": "^2.7.0"
58
58
  },
59
59
  "scripts": {
60
60
  "build": "tsup && cp ./src/index.cjs ./dist",
61
61
  "e2e": "node ./e2e/upload.cjs && node ./e2e/upload.mjs"
62
62
  },
63
- "gitHead": "88672d5cd06a792974844d1cb798641cec6e0562"
63
+ "gitHead": "8d46b4d4fc2b59b4fe93bac6fda02fb18440f935"
64
64
  }