@tempo-ai/visual-review 0.0.108-staging.1 → 0.0.108-staging.3
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/README.md +11 -0
- package/dist/bin.js +3 -3
- package/dist/{chunk-DJGBIRN5.js → chunk-2EPHXAPX.js} +14 -4
- package/dist/chunk-2EPHXAPX.js.map +1 -0
- package/dist/{chunk-WGNWCWMJ.js → chunk-2EVRJIRQ.js} +38 -63
- package/dist/chunk-2EVRJIRQ.js.map +1 -0
- package/dist/chunk-FFW3DB6S.js +38 -0
- package/dist/chunk-FFW3DB6S.js.map +1 -0
- package/dist/chunk-HVWIRSNY.js +86 -0
- package/dist/chunk-HVWIRSNY.js.map +1 -0
- package/dist/{chunk-4KKBAMNP.js → chunk-RH4A3H6E.js} +13 -114
- package/dist/chunk-RH4A3H6E.js.map +1 -0
- package/dist/{claim-WI2ZVIDS.js → claim-J654JAS6.js} +4 -4
- package/dist/{cli-DPVTULCN.js → cli-ASUZAROR.js} +5 -5
- package/dist/index.js +7 -7
- package/dist/{report-RD2OQ5TP.js → report-LTFCAIRI.js} +4 -3
- package/package.json +1 -1
- package/templates/visual-review.yml +6 -15
- package/dist/chunk-4KKBAMNP.js.map +0 -1
- package/dist/chunk-DJGBIRN5.js.map +0 -1
- package/dist/chunk-QLAJNWA2.js +0 -9
- package/dist/chunk-QLAJNWA2.js.map +0 -1
- package/dist/chunk-W7PBID4V.js +0 -38
- package/dist/chunk-W7PBID4V.js.map +0 -1
- package/dist/chunk-WGNWCWMJ.js.map +0 -1
- /package/dist/{claim-WI2ZVIDS.js.map → claim-J654JAS6.js.map} +0 -0
- /package/dist/{cli-DPVTULCN.js.map → cli-ASUZAROR.js.map} +0 -0
- /package/dist/{report-RD2OQ5TP.js.map → report-LTFCAIRI.js.map} +0 -0
package/README.md
CHANGED
|
@@ -24,6 +24,17 @@ every release is exercised there before customers resolve it.
|
|
|
24
24
|
The current production ingest origin is the default. Set
|
|
25
25
|
`VISUAL_REVIEW_INGEST_URL` only when using a Tempo preview deployment.
|
|
26
26
|
|
|
27
|
+
Every `visual-review` commit status is published by the Tempo Labs AI GitHub
|
|
28
|
+
App, including no-visual-change declarations, bypasses, and workflow failures.
|
|
29
|
+
The CLI authenticates these requests to Tempo with GitHub Actions OIDC;
|
|
30
|
+
app credentials stay in Tempo. The workflow token is used for PR comments.
|
|
31
|
+
If the app cannot publish, the job reports the failure instead of posting
|
|
32
|
+
under a different identity that cannot satisfy an app-bound required check.
|
|
33
|
+
|
|
34
|
+
The backend checks the signed PR identity against GitHub's current head and
|
|
35
|
+
reads skip declarations from the live PR. Declarations cannot be replayed
|
|
36
|
+
after removal, and reporting failures preserve an existing Tempo verdict.
|
|
37
|
+
|
|
27
38
|
## Releases and rollback
|
|
28
39
|
|
|
29
40
|
Every production release publishes this package at the app version under
|
package/dist/bin.js
CHANGED
|
@@ -6,11 +6,11 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
6
6
|
const [command, ...commandArgs] = argv;
|
|
7
7
|
switch (command) {
|
|
8
8
|
case "capture":
|
|
9
|
-
return await (await import("./cli-
|
|
9
|
+
return await (await import("./cli-ASUZAROR.js")).main(commandArgs);
|
|
10
10
|
case "claim":
|
|
11
|
-
return await (await import("./claim-
|
|
11
|
+
return await (await import("./claim-J654JAS6.js")).main(commandArgs);
|
|
12
12
|
case "report":
|
|
13
|
-
return await (await import("./report-
|
|
13
|
+
return await (await import("./report-LTFCAIRI.js")).main(commandArgs);
|
|
14
14
|
case "install-browsers":
|
|
15
15
|
return await (await import("./install-browsers-5RCJN3AK.js")).main(commandArgs);
|
|
16
16
|
default:
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createRequire as __tempoCreateRequire } from 'node:module'; const require = __tempoCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
} from "./chunk-
|
|
3
|
+
publishWorkflowStatus
|
|
4
|
+
} from "./chunk-FFW3DB6S.js";
|
|
5
5
|
import {
|
|
6
|
+
DEFAULT_VISUAL_REVIEW_INGEST_URL,
|
|
6
7
|
githubPullRequestContext
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-HVWIRSNY.js";
|
|
8
9
|
|
|
9
10
|
// src/claim.ts
|
|
10
11
|
import { randomUUID } from "crypto";
|
|
@@ -108,6 +109,15 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
108
109
|
);
|
|
109
110
|
return 0;
|
|
110
111
|
}
|
|
112
|
+
if (!release && process.env.GITHUB_EVENT_NAME === "pull_request" && required.headSha) {
|
|
113
|
+
try {
|
|
114
|
+
await publishWorkflowStatus("capture-started", required.headSha);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.warn(
|
|
117
|
+
`pending status unavailable: ${error instanceof Error ? error.message : error}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
111
121
|
try {
|
|
112
122
|
if (release) {
|
|
113
123
|
const result2 = await releaseCaptureClaim(
|
|
@@ -143,4 +153,4 @@ export {
|
|
|
143
153
|
releaseCaptureClaim,
|
|
144
154
|
main
|
|
145
155
|
};
|
|
146
|
-
//# sourceMappingURL=chunk-
|
|
156
|
+
//# sourceMappingURL=chunk-2EPHXAPX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/claim.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { appendFile } from \"node:fs/promises\";\n\nimport { DEFAULT_VISUAL_REVIEW_INGEST_URL } from \"./constants\";\nimport { githubPullRequestContext } from \"./github-actions\";\nimport { publishWorkflowStatus } from \"./workflow-status\";\n\nconst PENDING_ENVIRONMENT = \"pending\";\nconst OIDC_AUDIENCE = \"tempo-visual-review\";\n\n/** The slice of `fetch` the claim protocol uses. A plain `(url, init)` mock\n * satisfies it without impersonating the overloaded global. */\ntype FetchLike = (input: string, init?: RequestInit) => Promise<Response>;\n\nasync function mintOidcToken(options: {\n oidcRequestUrl: string;\n oidcRequestToken: string;\n fetchImpl: FetchLike;\n}): Promise<string> {\n const separator = options.oidcRequestUrl.includes(\"?\") ? \"&\" : \"?\";\n const tokenResponse = await options.fetchImpl(\n `${options.oidcRequestUrl}${separator}audience=${OIDC_AUDIENCE}`,\n { headers: { Authorization: `Bearer ${options.oidcRequestToken}` } },\n );\n if (!tokenResponse.ok) {\n throw new Error(`OIDC token request failed: ${tokenResponse.status}`);\n }\n const { value } = (await tokenResponse.json()) as { value?: string };\n if (!value) throw new Error(\"OIDC token response had no value\");\n return value;\n}\n\nasync function postClaimRequest(options: {\n ingestUrl: string;\n oidcToken: string;\n path: \"claim\" | \"fail\";\n body: Record<string, string>;\n fetchImpl: FetchLike;\n}): Promise<Record<string, unknown>> {\n const response = await options.fetchImpl(\n `${options.ingestUrl.replace(/\\/$/, \"\")}/visual-review/baseline/${options.path}`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-visual-review-oidc\": options.oidcToken,\n },\n body: JSON.stringify(options.body),\n },\n );\n if (!response.ok) {\n throw new Error(\n `capture ${options.path} failed: ${response.status} ${(await response.text()).slice(0, 200)}`,\n );\n }\n return (await response.json()) as Record<string, unknown>;\n}\n\nexport async function announceCapture(options: {\n ingestUrl: string;\n repository: string;\n headSha: string;\n oidcRequestUrl: string;\n oidcRequestToken: string;\n ownerToken?: string;\n fetchImpl?: FetchLike;\n}): Promise<{ ownerToken?: string; status?: string }> {\n const ownerToken = options.ownerToken ?? randomUUID();\n const fetchImpl = options.fetchImpl ?? fetch;\n const oidcToken = await mintOidcToken({ ...options, fetchImpl });\n const result = await postClaimRequest({\n ingestUrl: options.ingestUrl,\n oidcToken,\n path: \"claim\",\n body: {\n repoKey: `github.com/${options.repository.toLowerCase()}`,\n commitSha: options.headSha,\n envFingerprint: PENDING_ENVIRONMENT,\n ownerToken,\n },\n fetchImpl,\n });\n const status = typeof result.status === \"string\" ? result.status : undefined;\n return {\n ownerToken: status === \"claimed\" ? ownerToken : undefined,\n status,\n };\n}\n\nexport async function releaseCaptureClaim(options: {\n ingestUrl: string;\n repository: string;\n headSha: string;\n ownerToken: string;\n oidcRequestUrl: string;\n oidcRequestToken: string;\n fetchImpl?: FetchLike;\n}): Promise<{ released?: boolean }> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const oidcToken = await mintOidcToken({ ...options, fetchImpl });\n const result = await postClaimRequest({\n ingestUrl: options.ingestUrl,\n oidcToken,\n path: \"fail\",\n body: {\n repoKey: `github.com/${options.repository.toLowerCase()}`,\n commitSha: options.headSha,\n ownerToken: options.ownerToken,\n error: \"workflow ended before the capture claim completed\",\n },\n fetchImpl,\n });\n return { released: result.released === true };\n}\n\nfunction parseArgs(argv: string[]) {\n if (argv.some((arg) => arg !== \"--release\")) {\n throw new Error(\"claim accepts only the optional --release flag\");\n }\n return { release: argv.includes(\"--release\") };\n}\n\nexport async function main(argv = process.argv.slice(2)): Promise<number> {\n const { release } = parseArgs(argv);\n const pullRequest = githubPullRequestContext();\n const required = {\n ingestUrl:\n process.env.VISUAL_REVIEW_INGEST_URL ?? DEFAULT_VISUAL_REVIEW_INGEST_URL,\n repository: process.env.GITHUB_REPOSITORY,\n // GITHUB_SHA is a merge ref on pull_request runs; the event payload is\n // the only standard environment source for the actual PR head SHA.\n headSha:\n process.env.CAPTURE_HEAD_SHA ??\n pullRequest?.headSha ??\n process.env.GITHUB_SHA,\n oidcRequestUrl: process.env.ACTIONS_ID_TOKEN_REQUEST_URL,\n oidcRequestToken: process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN,\n ...(release\n ? { ownerToken: process.env.VISUAL_REVIEW_CAPTURE_OWNER_TOKEN }\n : {}),\n };\n const missing = Object.entries(required)\n .filter(([, value]) => !value)\n .map(([name]) => name);\n if (missing.length > 0) {\n console.warn(\n `capture announcement skipped; missing ${missing.join(\", \")} (capture will claim after setup)`,\n );\n return 0;\n }\n\n if (\n !release &&\n process.env.GITHUB_EVENT_NAME === \"pull_request\" &&\n required.headSha\n ) {\n try {\n await publishWorkflowStatus(\"capture-started\", required.headSha);\n } catch (error) {\n // Capture still gets a chance to complete and publish its real gate.\n console.warn(\n `pending status unavailable: ${error instanceof Error ? error.message : error}`,\n );\n }\n }\n\n try {\n if (release) {\n const result = await releaseCaptureClaim(\n required as Parameters<typeof releaseCaptureClaim>[0],\n );\n console.log(\n `capture claim release: ${result.released ? \"released\" : \"already complete or transferred\"}`,\n );\n return 0;\n }\n const result = await announceCapture(\n required as Parameters<typeof announceCapture>[0],\n );\n console.log(`capture announcement: ${result.status}`);\n if (result.ownerToken && process.env.GITHUB_ENV) {\n await appendFile(\n process.env.GITHUB_ENV,\n `VISUAL_REVIEW_CAPTURE_OWNER_TOKEN=${result.ownerToken}\\n`,\n );\n }\n return 0;\n } catch (error) {\n // The capture command repeats the atomic claim with the real environment\n // fingerprint; an early announcement outage must not suppress coverage.\n console.warn(\n `capture announcement failed; deferring to capture: ${error instanceof Error ? error.message : error}`,\n );\n return 0;\n }\n}\n"],"mappings":"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","names":["result"]}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { createRequire as __tempoCreateRequire } from 'node:module'; const require = __tempoCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
-
DEFAULT_VISUAL_REVIEW_INGEST_URL
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "./chunk-W7PBID4V.js";
|
|
3
|
+
DEFAULT_VISUAL_REVIEW_INGEST_URL,
|
|
4
|
+
githubPullRequestContext,
|
|
5
|
+
resolveIngestCredential
|
|
6
|
+
} from "./chunk-HVWIRSNY.js";
|
|
8
7
|
|
|
9
8
|
// src/cli.ts
|
|
10
9
|
import { execFileSync, spawn as spawn6 } from "child_process";
|
|
@@ -2896,7 +2895,10 @@ var AppDevServer = class _AppDevServer {
|
|
|
2896
2895
|
await this.stopPromise.catch(() => {
|
|
2897
2896
|
});
|
|
2898
2897
|
}
|
|
2899
|
-
this.startPromise =
|
|
2898
|
+
this.startPromise = (async () => {
|
|
2899
|
+
if (this.child) await this.killChild();
|
|
2900
|
+
return await this.runStartCycle(0, signal);
|
|
2901
|
+
})().finally(() => {
|
|
2900
2902
|
this.startPromise = null;
|
|
2901
2903
|
});
|
|
2902
2904
|
return this.startPromise;
|
|
@@ -3098,7 +3100,8 @@ var AppDevServer = class _AppDevServer {
|
|
|
3098
3100
|
startSpawned = true;
|
|
3099
3101
|
this.wireProcessEvents(outputEpoch);
|
|
3100
3102
|
const onAbort = () => {
|
|
3101
|
-
void this.killChild()
|
|
3103
|
+
void this.killChild().catch(() => {
|
|
3104
|
+
});
|
|
3102
3105
|
};
|
|
3103
3106
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3104
3107
|
removeAbortListener = () => signal?.removeEventListener("abort", onAbort);
|
|
@@ -3197,7 +3200,14 @@ Cleanup also failed: ${cleanupError}` : message;
|
|
|
3197
3200
|
});
|
|
3198
3201
|
cleanupError = describeShellCommandFailure(stopResult);
|
|
3199
3202
|
}
|
|
3200
|
-
|
|
3203
|
+
try {
|
|
3204
|
+
await this.killChild();
|
|
3205
|
+
} catch (error) {
|
|
3206
|
+
return [
|
|
3207
|
+
cleanupError,
|
|
3208
|
+
error instanceof Error ? error.message : String(error)
|
|
3209
|
+
].filter(Boolean).join("\n");
|
|
3210
|
+
}
|
|
3201
3211
|
this.launcherFinished = false;
|
|
3202
3212
|
this.commandEnv = null;
|
|
3203
3213
|
return cleanupError;
|
|
@@ -3553,9 +3563,13 @@ Hint: ${hint.hint}` : ""}`;
|
|
|
3553
3563
|
}
|
|
3554
3564
|
} catch {
|
|
3555
3565
|
}
|
|
3556
|
-
await waitForChildExit(child, 2e3)
|
|
3566
|
+
if (!await waitForChildExit(child, 2e3)) {
|
|
3567
|
+
throw new Error(
|
|
3568
|
+
`App dev server process ${pid} survived termination. Retry cleanup before starting a replacement.`
|
|
3569
|
+
);
|
|
3570
|
+
}
|
|
3557
3571
|
}
|
|
3558
|
-
this.child = null;
|
|
3572
|
+
if (this.child === child) this.child = null;
|
|
3559
3573
|
}
|
|
3560
3574
|
async ensureParentDeathWatchdog() {
|
|
3561
3575
|
if (process.env.TEMPO_DEVSERVER_WATCHDOG === "0") return null;
|
|
@@ -12885,8 +12899,14 @@ var VisualReviewIngestClient = class {
|
|
|
12885
12899
|
response.status
|
|
12886
12900
|
);
|
|
12887
12901
|
}
|
|
12902
|
+
if (reason === "visual_review_unconfigured") {
|
|
12903
|
+
throw new VisualReviewHttpError(
|
|
12904
|
+
"visual-review ingest is unconfigured on the target deployment (set VISUAL_REVIEW_UPLOAD_SECRET, or its OIDC producer credential)",
|
|
12905
|
+
response.status
|
|
12906
|
+
);
|
|
12907
|
+
}
|
|
12888
12908
|
throw new VisualReviewHttpError(
|
|
12889
|
-
|
|
12909
|
+
`visual-review ingest unavailable: ${reason ?? "HTTP 503"}`,
|
|
12890
12910
|
response.status
|
|
12891
12911
|
);
|
|
12892
12912
|
}
|
|
@@ -12920,8 +12940,8 @@ var VisualReviewIngestClient = class {
|
|
|
12920
12940
|
`negotiate failed: ${response.status}${routeMissingHint(response.status)}`
|
|
12921
12941
|
);
|
|
12922
12942
|
}
|
|
12923
|
-
const { missing
|
|
12924
|
-
return
|
|
12943
|
+
const { missing } = await response.json();
|
|
12944
|
+
return missing;
|
|
12925
12945
|
}
|
|
12926
12946
|
async uploadBlob(hash, bytes, size) {
|
|
12927
12947
|
const response = await this.request(
|
|
@@ -13736,13 +13756,13 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
13736
13756
|
});
|
|
13737
13757
|
}
|
|
13738
13758
|
}
|
|
13739
|
-
const
|
|
13759
|
+
const missing = await timing.phase(
|
|
13740
13760
|
"negotiate",
|
|
13741
13761
|
() => ingest.negotiate([...blobsByHash.keys()])
|
|
13742
13762
|
);
|
|
13743
|
-
log(`uploading ${
|
|
13763
|
+
log(`uploading ${missing.length} of ${blobsByHash.size} blob(s)`);
|
|
13744
13764
|
await timing.phase("upload", async () => {
|
|
13745
|
-
for (const hash of
|
|
13765
|
+
for (const hash of missing) {
|
|
13746
13766
|
const blob = blobsByHash.get(hash);
|
|
13747
13767
|
if (!blob) throw new Error(`negotiate returned unknown hash ${hash}`);
|
|
13748
13768
|
await ingest.uploadBlob(hash, blob.bytes, blob);
|
|
@@ -13787,50 +13807,6 @@ status_url=${finalizeResult.statusUrl}
|
|
|
13787
13807
|
process.removeListener("SIGTERM", onSigterm);
|
|
13788
13808
|
}
|
|
13789
13809
|
}
|
|
13790
|
-
function missing(what) {
|
|
13791
|
-
throw new Error(`${what} is required for --upload`);
|
|
13792
|
-
}
|
|
13793
|
-
var OIDC_TOKEN_LIFETIME_MS = 5 * 6e4;
|
|
13794
|
-
var OIDC_TOKEN_REFRESH_MARGIN_MS = 6e4;
|
|
13795
|
-
var VISUAL_REVIEW_OIDC_AUDIENCE = "tempo-visual-review";
|
|
13796
|
-
function githubOidcProvider(requestUrl, requestToken) {
|
|
13797
|
-
let cached = null;
|
|
13798
|
-
return async () => {
|
|
13799
|
-
if (cached && Date.now() - cached.mintedAtMs < OIDC_TOKEN_LIFETIME_MS - OIDC_TOKEN_REFRESH_MARGIN_MS) {
|
|
13800
|
-
return { kind: "oidc", value: cached.value };
|
|
13801
|
-
}
|
|
13802
|
-
const separator = requestUrl.includes("?") ? "&" : "?";
|
|
13803
|
-
const response = await fetch(
|
|
13804
|
-
`${requestUrl}${separator}audience=${VISUAL_REVIEW_OIDC_AUDIENCE}`,
|
|
13805
|
-
{ headers: { Authorization: `Bearer ${requestToken}` } }
|
|
13806
|
-
);
|
|
13807
|
-
if (!response.ok) {
|
|
13808
|
-
throw new Error(
|
|
13809
|
-
`minting the GitHub OIDC token failed: ${response.status} ${(await response.text()).slice(0, 200)}`
|
|
13810
|
-
);
|
|
13811
|
-
}
|
|
13812
|
-
const body = await response.json();
|
|
13813
|
-
if (!body.value) {
|
|
13814
|
-
throw new Error("GitHub OIDC token endpoint returned no value");
|
|
13815
|
-
}
|
|
13816
|
-
cached = { value: body.value, mintedAtMs: Date.now() };
|
|
13817
|
-
return { kind: "oidc", value: body.value };
|
|
13818
|
-
};
|
|
13819
|
-
}
|
|
13820
|
-
function resolveIngestCredential() {
|
|
13821
|
-
const requestUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
|
|
13822
|
-
const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
|
|
13823
|
-
if (requestUrl && requestToken) {
|
|
13824
|
-
return githubOidcProvider(requestUrl, requestToken);
|
|
13825
|
-
}
|
|
13826
|
-
const oidc = process.env.VISUAL_REVIEW_OIDC_TOKEN;
|
|
13827
|
-
if (oidc) return async () => ({ kind: "oidc", value: oidc });
|
|
13828
|
-
const secret = process.env.VISUAL_REVIEW_UPLOAD_SECRET;
|
|
13829
|
-
if (secret) return async () => ({ kind: "secret", value: secret });
|
|
13830
|
-
return missing(
|
|
13831
|
-
"GitHub Actions id-token permission, VISUAL_REVIEW_OIDC_TOKEN, or VISUAL_REVIEW_UPLOAD_SECRET"
|
|
13832
|
-
);
|
|
13833
|
-
}
|
|
13834
13810
|
var isDirectRun = process.argv[1] !== void 0 && (process.argv[1].endsWith("cli.ts") || process.argv[1].endsWith("cli.js"));
|
|
13835
13811
|
if (isDirectRun) {
|
|
13836
13812
|
main().then((code) => {
|
|
@@ -13868,7 +13844,6 @@ export {
|
|
|
13868
13844
|
resolveOrClaimBaseline,
|
|
13869
13845
|
explicitTempoDirRelative,
|
|
13870
13846
|
buildBaselineCaptureArgs,
|
|
13871
|
-
main
|
|
13872
|
-
VISUAL_REVIEW_OIDC_AUDIENCE
|
|
13847
|
+
main
|
|
13873
13848
|
};
|
|
13874
|
-
//# sourceMappingURL=chunk-
|
|
13849
|
+
//# sourceMappingURL=chunk-2EVRJIRQ.js.map
|