@lodestar/spec-test-util 1.43.0-dev.e341cdc614 → 1.43.0-dev.e3c96e7a79

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.
@@ -0,0 +1,7 @@
1
+ export declare function downloadNightlyTests(opts: {
2
+ specTestsRepoUrl: string;
3
+ outputDir: string;
4
+ testsToDownload: string[];
5
+ branch?: string;
6
+ }, log: (msg: string) => void, date?: string): Promise<void>;
7
+ //# sourceMappingURL=downloadNightlyTests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"downloadNightlyTests.d.ts","sourceRoot":"","sources":["../src/downloadNightlyTests.ts"],"names":[],"mappings":"AA0CA,wBAAsB,oBAAoB,CACxC,IAAI,EAAE;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAC,EAC/F,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAC1B,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CA0Cf"}
@@ -0,0 +1,67 @@
1
+ import { fetch } from "@lodestar/utils";
2
+ import { downloadGenericSpecTests } from "./downloadTests.js";
3
+ async function ghApiFetch(endpoint, token) {
4
+ const res = await fetch(`https://api.github.com${endpoint}`, {
5
+ headers: { Authorization: `token ${token}`, Accept: "application/vnd.github+json" },
6
+ signal: AbortSignal.timeout(30_000),
7
+ });
8
+ if (!res.ok) {
9
+ throw new Error(res.status === 401 ? "GITHUB_TOKEN is invalid or expired" : `GitHub API ${res.status} (${endpoint})`);
10
+ }
11
+ return res.json();
12
+ }
13
+ async function resolveNightlyRunId(repo, token, date, branch) {
14
+ const params = new URLSearchParams({ status: "success", per_page: "1" });
15
+ if (branch)
16
+ params.append("branch", branch);
17
+ // If neither branch nor date narrow the query, restrict to scheduled runs so
18
+ // a PR's successful run on consensus-specs can't outrank the latest master
19
+ // nightly. When a date is given, allow manual re-runs on that day too.
20
+ else if (!date)
21
+ params.append("event", "schedule");
22
+ if (date)
23
+ params.append("created", date);
24
+ const { workflow_runs } = await ghApiFetch(`/repos/${repo}/actions/workflows/tests.yml/runs?${params}`, token);
25
+ const runId = workflow_runs[0]?.id;
26
+ if (!runId) {
27
+ throw new Error(`No successful run found${date ? ` on ${date}` : ""} for ${repo}${branch ? ` (${branch})` : ""}`);
28
+ }
29
+ return runId;
30
+ }
31
+ export async function downloadNightlyTests(opts, log, date) {
32
+ const token = process.env.GITHUB_TOKEN;
33
+ if (!token)
34
+ throw new Error("GITHUB_TOKEN is required for nightly downloads");
35
+ const resolvedDate = date === "latest" || !date ? undefined : date;
36
+ if (resolvedDate && !/^\d{4}-\d{2}-\d{2}$/.test(resolvedDate)) {
37
+ throw new Error(`Invalid date: "${date}". Expected "latest" or YYYY-MM-DD`);
38
+ }
39
+ const repo = new URL(opts.specTestsRepoUrl).pathname.slice(1).replace(/\/$/, "");
40
+ const runId = await resolveNightlyRunId(repo, token, resolvedDate, opts.branch);
41
+ log(`Resolved nightly${resolvedDate ? ` ${resolvedDate}` : ""} to run ${runId}`);
42
+ const { artifacts } = await ghApiFetch(`/repos/${repo}/actions/runs/${runId}/artifacts`, token);
43
+ const urlByTest = {};
44
+ const available = [];
45
+ for (const test of opts.testsToDownload) {
46
+ const artifact = artifacts.find((a) => a.name === `${test}.tar.gz` && !a.expired);
47
+ if (artifact) {
48
+ urlByTest[test] = artifact.archive_download_url;
49
+ available.push(test);
50
+ }
51
+ else {
52
+ log(`Skipping ${test} (not found in run ${runId})`);
53
+ }
54
+ }
55
+ if (available.length === 0)
56
+ throw new Error(`No matching artifacts found in run ${runId}`);
57
+ const authInit = { headers: { Authorization: `token ${token}`, Accept: "application/vnd.github+json" } };
58
+ await downloadGenericSpecTests({
59
+ specVersion: `nightly-${runId}`,
60
+ specTestsRepoUrl: opts.specTestsRepoUrl,
61
+ outputDir: opts.outputDir,
62
+ testsToDownload: available,
63
+ testUrls: urlByTest,
64
+ fetchInit: authInit,
65
+ }, log);
66
+ }
67
+ //# sourceMappingURL=downloadNightlyTests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"downloadNightlyTests.js","sourceRoot":"","sources":["../src/downloadNightlyTests.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,8 @@ export interface DownloadGenericTestsOptions<TestNames extends string> {
12
12
  outputDir: string;
13
13
  specTestsRepoUrl: string;
14
14
  testsToDownload: TestNames[];
15
+ testUrls?: Record<string, string>;
16
+ fetchInit?: RequestInit;
15
17
  }
16
18
  /**
17
19
  * Download spec tests
@@ -21,5 +23,5 @@ export declare function downloadTests(opts: DownloadTestsOptions, log?: (msg: st
21
23
  * Generic Github release downloader.
22
24
  * Used by spec tests and SlashingProtectionInterchangeTest
23
25
  */
24
- export declare function downloadGenericSpecTests<TestNames extends string>({ specVersion, specTestsRepoUrl, outputDir, testsToDownload }: DownloadGenericTestsOptions<TestNames>, log?: (msg: string) => void): Promise<void>;
26
+ export declare function downloadGenericSpecTests<TestNames extends string>({ specVersion, specTestsRepoUrl, outputDir, testsToDownload, testUrls, fetchInit }: DownloadGenericTestsOptions<TestNames>, log?: (msg: string) => void): Promise<void>;
25
27
  //# sourceMappingURL=downloadTests.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"downloadTests.d.ts","sourceRoot":"","sources":["../src/downloadTests.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,uBAAuB,gDAAgD,CAAC;AAIrF,MAAM,MAAM,oBAAoB,GAAG;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,CAAC;IACzB,yFAAyF;IACzF,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,WAAW,2BAA2B,CAAC,SAAS,SAAS,MAAM;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,SAAS,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,oBAAoB,EAAE,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpH;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,SAAS,SAAS,MAAM,EACrE,EAAC,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAC,EAAE,2BAA2B,CAAC,SAAS,CAAC,EACnG,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GACpC,OAAO,CAAC,IAAI,CAAC,CAgEf"}
1
+ {"version":3,"file":"downloadTests.d.ts","sourceRoot":"","sources":["../src/downloadTests.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,uBAAuB,gDAAgD,CAAC;AAIrF,MAAM,MAAM,oBAAoB,GAAG;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,CAAC;IACzB,yFAAyF;IACzF,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,WAAW,2BAA2B,CAAC,SAAS,SAAS,MAAM;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,SAAS,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,SAAS,CAAC,EAAE,WAAW,CAAC;CACzB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,oBAAoB,EAAE,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpH;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,SAAS,SAAS,MAAM,EACrE,EACE,WAAW,EACX,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,QAAQ,EACR,SAAS,EACV,EAAE,2BAA2B,CAAC,SAAS,CAAC,EACzC,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GACpC,OAAO,CAAC,IAAI,CAAC,CAiEf"}
@@ -17,7 +17,7 @@ export async function downloadTests(opts, log = logEmpty) {
17
17
  * Generic Github release downloader.
18
18
  * Used by spec tests and SlashingProtectionInterchangeTest
19
19
  */
20
- export async function downloadGenericSpecTests({ specVersion, specTestsRepoUrl, outputDir, testsToDownload }, log = logEmpty) {
20
+ export async function downloadGenericSpecTests({ specVersion, specTestsRepoUrl, outputDir, testsToDownload, testUrls, fetchInit, }, log = logEmpty) {
21
21
  log(`outputDir = ${outputDir}`);
22
22
  // Use version.txt as a flag to prevent re-downloading the tests
23
23
  const versionFile = path.join(outputDir, "version.txt");
@@ -32,10 +32,11 @@ export async function downloadGenericSpecTests({ specVersion, specTestsRepoUrl,
32
32
  }
33
33
  fs.mkdirSync(outputDir, { recursive: true });
34
34
  await Promise.all(testsToDownload.map(async (test) => {
35
- const url = `${specTestsRepoUrl ?? defaultSpecTestsRepoUrl}/releases/download/${specVersion}/${test}.tar.gz`;
35
+ const defaultUrl = `${specTestsRepoUrl ?? defaultSpecTestsRepoUrl}/releases/download/${specVersion}/${test}.tar.gz`;
36
36
  const tarball = path.join(outputDir, `${test}.tar.gz`);
37
37
  await retry(async () => {
38
- const res = await fetch(url, { signal: AbortSignal.timeout(30 * 60 * 1000) });
38
+ const url = testUrls?.[test] ?? defaultUrl;
39
+ const res = await fetch(url, { signal: AbortSignal.timeout(30 * 60 * 1000), ...fetchInit });
39
40
  if (!res.ok) {
40
41
  throw new Error(`Failed to download file from ${url}: ${res.status} ${res.statusText}`);
41
42
  }
@@ -56,7 +57,7 @@ export async function downloadGenericSpecTests({ specVersion, specTestsRepoUrl,
56
57
  }, {
57
58
  retries: 3,
58
59
  onRetry: (e, attempt) => {
59
- log(`Download attempt ${attempt} for ${url} failed: ${e.message}`);
60
+ log(`Download attempt ${attempt} for ${test} failed: ${e.message}`);
60
61
  },
61
62
  });
62
63
  // download tar
@@ -1 +1 @@
1
- {"version":3,"file":"downloadTests.js","sourceRoot":"","sources":["../src/downloadTests.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"downloadTests.js","sourceRoot":"","sources":["../src/downloadTests.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lodestar/spec-test-util",
3
- "version": "1.43.0-dev.e341cdc614",
3
+ "version": "1.43.0-dev.e3c96e7a79",
4
4
  "description": "Spec test suite generator from yaml test files",
5
5
  "author": "ChainSafe Systems",
6
6
  "license": "Apache-2.0",
@@ -19,6 +19,11 @@
19
19
  "bun": "./src/downloadTests.ts",
20
20
  "types": "./lib/downloadTests.d.ts",
21
21
  "import": "./lib/downloadTests.js"
22
+ },
23
+ "./downloadNightlyTests": {
24
+ "bun": "./src/downloadNightlyTests.ts",
25
+ "types": "./lib/downloadNightlyTests.d.ts",
26
+ "import": "./lib/downloadNightlyTests.js"
22
27
  }
23
28
  },
24
29
  "files": [
@@ -54,15 +59,15 @@
54
59
  "blockchain"
55
60
  ],
56
61
  "dependencies": {
57
- "@lodestar/utils": "^1.43.0-dev.e341cdc614",
62
+ "@lodestar/utils": "^1.43.0-dev.e3c96e7a79",
58
63
  "rimraf": "^4.4.1",
59
64
  "snappyjs": "^0.7.0",
60
65
  "vitest": "catalog:"
61
66
  },
62
67
  "peerDependencies": {
63
- "@chainsafe/ssz": "^1.2.2",
64
- "@lodestar/types": "^1.43.0-dev.e341cdc614",
68
+ "@chainsafe/ssz": "^1.4.0",
69
+ "@lodestar/types": "^1.43.0-dev.e3c96e7a79",
65
70
  "vitest": "catalog:"
66
71
  },
67
- "gitHead": "befc1d1fc43563f7092cc69715eb472fdc5701b6"
72
+ "gitHead": "5d4e250663c0eed1aca56cf5f4c63c1712e189aa"
68
73
  }
@@ -0,0 +1,89 @@
1
+ import {fetch} from "@lodestar/utils";
2
+ import {downloadGenericSpecTests} from "./downloadTests.js";
3
+
4
+ type WorkflowRunsResponse = {workflow_runs: {id: number}[]};
5
+ type ArtifactsListResponse = {artifacts: {archive_download_url: string; expired: boolean; name: string}[]};
6
+
7
+ async function ghApiFetch<T>(endpoint: string, token: string): Promise<T> {
8
+ const res = await fetch(`https://api.github.com${endpoint}`, {
9
+ headers: {Authorization: `token ${token}`, Accept: "application/vnd.github+json"},
10
+ signal: AbortSignal.timeout(30_000),
11
+ });
12
+
13
+ if (!res.ok) {
14
+ throw new Error(
15
+ res.status === 401 ? "GITHUB_TOKEN is invalid or expired" : `GitHub API ${res.status} (${endpoint})`
16
+ );
17
+ }
18
+
19
+ return res.json() as Promise<T>;
20
+ }
21
+
22
+ async function resolveNightlyRunId(repo: string, token: string, date?: string, branch?: string): Promise<number> {
23
+ const params = new URLSearchParams({status: "success", per_page: "1"});
24
+ if (branch) params.append("branch", branch);
25
+ // If neither branch nor date narrow the query, restrict to scheduled runs so
26
+ // a PR's successful run on consensus-specs can't outrank the latest master
27
+ // nightly. When a date is given, allow manual re-runs on that day too.
28
+ else if (!date) params.append("event", "schedule");
29
+ if (date) params.append("created", date);
30
+
31
+ const {workflow_runs} = await ghApiFetch<WorkflowRunsResponse>(
32
+ `/repos/${repo}/actions/workflows/tests.yml/runs?${params}`,
33
+ token
34
+ );
35
+
36
+ const runId = workflow_runs[0]?.id;
37
+ if (!runId) {
38
+ throw new Error(`No successful run found${date ? ` on ${date}` : ""} for ${repo}${branch ? ` (${branch})` : ""}`);
39
+ }
40
+ return runId;
41
+ }
42
+
43
+ export async function downloadNightlyTests(
44
+ opts: {specTestsRepoUrl: string; outputDir: string; testsToDownload: string[]; branch?: string},
45
+ log: (msg: string) => void,
46
+ date?: string
47
+ ): Promise<void> {
48
+ const token = process.env.GITHUB_TOKEN;
49
+ if (!token) throw new Error("GITHUB_TOKEN is required for nightly downloads");
50
+
51
+ const resolvedDate = date === "latest" || !date ? undefined : date;
52
+ if (resolvedDate && !/^\d{4}-\d{2}-\d{2}$/.test(resolvedDate)) {
53
+ throw new Error(`Invalid date: "${date}". Expected "latest" or YYYY-MM-DD`);
54
+ }
55
+
56
+ const repo = new URL(opts.specTestsRepoUrl).pathname.slice(1).replace(/\/$/, "");
57
+ const runId = await resolveNightlyRunId(repo, token, resolvedDate, opts.branch);
58
+ log(`Resolved nightly${resolvedDate ? ` ${resolvedDate}` : ""} to run ${runId}`);
59
+
60
+ const {artifacts} = await ghApiFetch<ArtifactsListResponse>(`/repos/${repo}/actions/runs/${runId}/artifacts`, token);
61
+
62
+ const urlByTest: Record<string, string> = {};
63
+ const available: string[] = [];
64
+ for (const test of opts.testsToDownload) {
65
+ const artifact = artifacts.find((a) => a.name === `${test}.tar.gz` && !a.expired);
66
+ if (artifact) {
67
+ urlByTest[test] = artifact.archive_download_url;
68
+ available.push(test);
69
+ } else {
70
+ log(`Skipping ${test} (not found in run ${runId})`);
71
+ }
72
+ }
73
+
74
+ if (available.length === 0) throw new Error(`No matching artifacts found in run ${runId}`);
75
+
76
+ const authInit: RequestInit = {headers: {Authorization: `token ${token}`, Accept: "application/vnd.github+json"}};
77
+
78
+ await downloadGenericSpecTests(
79
+ {
80
+ specVersion: `nightly-${runId}`,
81
+ specTestsRepoUrl: opts.specTestsRepoUrl,
82
+ outputDir: opts.outputDir,
83
+ testsToDownload: available,
84
+ testUrls: urlByTest,
85
+ fetchInit: authInit,
86
+ },
87
+ log
88
+ );
89
+ }
@@ -25,6 +25,8 @@ export interface DownloadGenericTestsOptions<TestNames extends string> {
25
25
  outputDir: string;
26
26
  specTestsRepoUrl: string;
27
27
  testsToDownload: TestNames[];
28
+ testUrls?: Record<string, string>;
29
+ fetchInit?: RequestInit;
28
30
  }
29
31
 
30
32
  /**
@@ -39,7 +41,14 @@ export async function downloadTests(opts: DownloadTestsOptions, log: (msg: strin
39
41
  * Used by spec tests and SlashingProtectionInterchangeTest
40
42
  */
41
43
  export async function downloadGenericSpecTests<TestNames extends string>(
42
- {specVersion, specTestsRepoUrl, outputDir, testsToDownload}: DownloadGenericTestsOptions<TestNames>,
44
+ {
45
+ specVersion,
46
+ specTestsRepoUrl,
47
+ outputDir,
48
+ testsToDownload,
49
+ testUrls,
50
+ fetchInit,
51
+ }: DownloadGenericTestsOptions<TestNames>,
43
52
  log: (msg: string) => void = logEmpty
44
53
  ): Promise<void> {
45
54
  log(`outputDir = ${outputDir}`);
@@ -62,12 +71,13 @@ export async function downloadGenericSpecTests<TestNames extends string>(
62
71
 
63
72
  await Promise.all(
64
73
  testsToDownload.map(async (test) => {
65
- const url = `${specTestsRepoUrl ?? defaultSpecTestsRepoUrl}/releases/download/${specVersion}/${test}.tar.gz`;
74
+ const defaultUrl = `${specTestsRepoUrl ?? defaultSpecTestsRepoUrl}/releases/download/${specVersion}/${test}.tar.gz`;
66
75
  const tarball = path.join(outputDir, `${test}.tar.gz`);
67
76
 
68
77
  await retry(
69
78
  async () => {
70
- const res = await fetch(url, {signal: AbortSignal.timeout(30 * 60 * 1000)});
79
+ const url = testUrls?.[test] ?? defaultUrl;
80
+ const res = await fetch(url, {signal: AbortSignal.timeout(30 * 60 * 1000), ...fetchInit});
71
81
 
72
82
  if (!res.ok) {
73
83
  throw new Error(`Failed to download file from ${url}: ${res.status} ${res.statusText}`);
@@ -95,7 +105,7 @@ export async function downloadGenericSpecTests<TestNames extends string>(
95
105
  {
96
106
  retries: 3,
97
107
  onRetry: (e, attempt) => {
98
- log(`Download attempt ${attempt} for ${url} failed: ${e.message}`);
108
+ log(`Download attempt ${attempt} for ${test} failed: ${e.message}`);
99
109
  },
100
110
  }
101
111
  );