@ykdz/template 0.0.33 → 0.0.36
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/application.d.ts +2 -1
- package/dist/application.js +64 -75
- package/dist/main.js +6 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.d.ts +50 -36
- package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.js +970 -365
- package/node_modules/@ykdz/template-builtin-presets/dist/src/generation-record.d.ts +24 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/generation-record.js +154 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/index.d.ts +2 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/index.js +1 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/rust-bin/definition.d.ts +31 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/rust-bin/definition.js +29 -33
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.d.ts +1 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.js +17 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/typescript.d.ts +4 -3
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/typescript.js +9 -5
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.d.ts +1 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.js +1 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-cli/definition.d.ts +31 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-cli/definition.js +79 -58
- package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-lib/definition.d.ts +31 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-lib/definition.js +31 -23
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.d.ts +22 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.js +61 -35
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.d.ts +26 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.js +19 -8
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.d.ts +22 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.js +31 -21
- package/node_modules/@ykdz/template-builtin-presets/package.json +12 -11
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment-diagnostics.yml +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment.yml +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.pnpmfile.mjs +19 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/gitignore +1 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/tsconfig.packing-hook.json +8 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/devcontainer/github-cli.Dockerfile +9 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/CHANGELOG.md.template +17 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/RELEASING.md +17 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/artifact.ts +1271 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/changelog.ts +283 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/check-artifact.ts +99 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/check-readiness.ts +66 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/cli-command-identity.ts +4 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/handoff.ts +450 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/publish.ts +483 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/readiness.ts +1125 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/release.ts +819 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/release.yml +119 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/root-tsconfig.json +13 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/tsconfig.json +4 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/turbo.json +6 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/README.md +48 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/assets/CHANGELOG.md.template +17 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/assets/LICENSE-APACHE-2.0.txt +202 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/assets/LICENSE-MIT.txt +21 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/assets/README.md.template +5 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/bridge.ts +3336 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/setup.sh +221 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/src/cli-command-identity.ts +133 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/src/cli.ts +4 -3
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/src/main.ts +5 -4
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/test/integration/command.test.ts +14 -13
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/test/unit/cli-command-identity.test.ts +76 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/tsconfig.build.json +2 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/src/readiness.ts +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/test/global-setup.ts +21 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/tsconfig.json +6 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/vitest.config.ts +7 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/drizzle.config.ts +5 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/playwright.config.ts +102 -22
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/test/playwright-teardown.ts +12 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/vite.config.ts +39 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/playwright.config.ts +48 -7
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/playwright.config.ts +59 -8
- package/node_modules/@ykdz/template-core/dist/module-graph.d.ts +15 -53
- package/node_modules/@ykdz/template-core/dist/module-graph.js +134 -101
- package/node_modules/@ykdz/template-core/dist/package-contribution.d.ts +13 -1
- package/node_modules/@ykdz/template-core/dist/package-contribution.js +92 -0
- package/node_modules/@ykdz/template-core/dist/pnpm-workspace.yaml +14 -10
- package/node_modules/@ykdz/template-core/dist/preset-definition.d.ts +71 -13
- package/node_modules/@ykdz/template-core/dist/preset-definition.js +16 -1
- package/node_modules/@ykdz/template-core/dist/project-blueprint.d.ts +56 -0
- package/node_modules/@ykdz/template-core/dist/project-blueprint.js +271 -0
- package/node_modules/@ykdz/template-core/dist/project-linking-v2.d.ts +2 -2
- package/node_modules/@ykdz/template-core/dist/project-linking-v2.js +2 -2
- package/node_modules/@ykdz/template-core/dist/project-projection.d.ts +2 -1
- package/node_modules/@ykdz/template-core/dist/project-projection.js +107 -20
- package/node_modules/@ykdz/template-core/dist/renderer.d.ts +0 -2
- package/node_modules/@ykdz/template-core/dist/renderer.js +17 -38
- package/node_modules/@ykdz/template-core/dist/template-boundary-check.d.ts +2 -2
- package/node_modules/@ykdz/template-core/dist/template-boundary-check.js +6 -0
- package/node_modules/@ykdz/template-core/dist/toolchain-resolution.d.ts +2 -0
- package/node_modules/@ykdz/template-core/dist/toolchain-resolution.js +9 -3
- package/package.json +3 -3
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/scripts/prepare-database.ts +0 -45
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/scripts/run-playwright.ts +0 -151
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/scripts/run-playwright.ts +0 -62
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/scripts/run-playwright.ts +0 -103
- package/node_modules/@ykdz/template-core/dist/project-blueprint-v2.d.ts +0 -33
- package/node_modules/@ykdz/template-core/dist/project-blueprint-v2.js +0 -182
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
/** Direct npm/OIDC adapter. It deliberately imports Node built-ins only. */
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { mkdtemp, lstat, mkdir, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
acceptDownloadedPublicationArtifact,
|
|
11
|
+
fail,
|
|
12
|
+
PublicationFailure,
|
|
13
|
+
} from "./handoff.ts";
|
|
14
|
+
|
|
15
|
+
export { PublicationFailure } from "./handoff.ts";
|
|
16
|
+
|
|
17
|
+
const packagePath = "{{PUBLIC_CLI_PACKAGE_PATH}}";
|
|
18
|
+
const registry = "https://registry.npmjs.org/";
|
|
19
|
+
|
|
20
|
+
export type ProcessResult = {
|
|
21
|
+
readonly exitCode: number;
|
|
22
|
+
readonly stdout: string;
|
|
23
|
+
readonly stderr: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type DirectPublicationOptions = {
|
|
27
|
+
readonly repositoryRoot: string;
|
|
28
|
+
readonly artifactDirectory: string;
|
|
29
|
+
readonly environment: Readonly<Record<string, string | undefined>>;
|
|
30
|
+
readonly run?: (
|
|
31
|
+
command: string,
|
|
32
|
+
arguments_: readonly string[],
|
|
33
|
+
options: { readonly cwd: string; readonly env: NodeJS.ProcessEnv },
|
|
34
|
+
) => Promise<ProcessResult>;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function canonicalEnvironmentKey(key: string): string {
|
|
38
|
+
return key.toLowerCase().replaceAll(/[^a-z0-9]/gu, "");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function assertNoAmbientCredentials(
|
|
42
|
+
environment: Readonly<Record<string, string | undefined>>,
|
|
43
|
+
): void {
|
|
44
|
+
for (const key of Object.keys(environment)) {
|
|
45
|
+
const canonical = canonicalEnvironmentKey(key);
|
|
46
|
+
if (
|
|
47
|
+
canonical === "nodeauthtoken" ||
|
|
48
|
+
((canonical.startsWith("npm") || canonical.startsWith("pnpm")) &&
|
|
49
|
+
(canonical.includes("auth") ||
|
|
50
|
+
canonical.includes("token") ||
|
|
51
|
+
canonical.includes("password")))
|
|
52
|
+
) {
|
|
53
|
+
fail(
|
|
54
|
+
"credential-fallback",
|
|
55
|
+
`ambient credential variable is forbidden: ${key}`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type LockedSemver = {
|
|
62
|
+
readonly valid: (value: string) => string | null;
|
|
63
|
+
readonly prerelease: (value: string) => readonly unknown[] | null;
|
|
64
|
+
readonly gt: (left: string, right: string) => boolean;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
async function loadLockedSemver(): Promise<LockedSemver> {
|
|
68
|
+
return await import("semver");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isStableSemver(semver: LockedSemver, value: string): boolean {
|
|
72
|
+
return semver.valid(value) !== null && semver.prerelease(value) === null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function defaultRun(
|
|
76
|
+
command: string,
|
|
77
|
+
arguments_: readonly string[],
|
|
78
|
+
options: { readonly cwd: string; readonly env: NodeJS.ProcessEnv },
|
|
79
|
+
): Promise<ProcessResult> {
|
|
80
|
+
return await new Promise((resolve, reject) => {
|
|
81
|
+
const child = spawn(command, arguments_, {
|
|
82
|
+
cwd: options.cwd,
|
|
83
|
+
env: options.env,
|
|
84
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
85
|
+
});
|
|
86
|
+
let stdout = "";
|
|
87
|
+
let stderr = "";
|
|
88
|
+
child.stdout.on("data", (value: Buffer) => (stdout += value));
|
|
89
|
+
child.stderr.on("data", (value: Buffer) => (stderr += value));
|
|
90
|
+
child.once("error", reject);
|
|
91
|
+
child.once("close", (exitCode) =>
|
|
92
|
+
resolve({ exitCode: exitCode ?? 1, stdout, stderr }),
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function parseJson(value: string, code: string): unknown {
|
|
98
|
+
try {
|
|
99
|
+
return JSON.parse(value);
|
|
100
|
+
} catch {
|
|
101
|
+
return fail(code, "command returned invalid JSON");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function stringValue(value: unknown, code: string): string {
|
|
106
|
+
if (typeof value !== "string" || value.length === 0)
|
|
107
|
+
fail(code, "expected string");
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function isolatedEnvironment(
|
|
112
|
+
source: Readonly<Record<string, string | undefined>>,
|
|
113
|
+
session: string,
|
|
114
|
+
): NodeJS.ProcessEnv {
|
|
115
|
+
const environment: NodeJS.ProcessEnv = {};
|
|
116
|
+
for (const key of [
|
|
117
|
+
"PATH",
|
|
118
|
+
"GITHUB_REPOSITORY",
|
|
119
|
+
"GITHUB_RUN_ATTEMPT",
|
|
120
|
+
"GITHUB_SHA",
|
|
121
|
+
"GITHUB_REF",
|
|
122
|
+
"GITHUB_EVENT_NAME",
|
|
123
|
+
"GITHUB_ACTIONS",
|
|
124
|
+
"GITHUB_WORKFLOW_REF",
|
|
125
|
+
"GITHUB_SERVER_URL",
|
|
126
|
+
"GITHUB_RUN_ID",
|
|
127
|
+
"RUNNER_ENVIRONMENT",
|
|
128
|
+
"GITHUB_REPOSITORY_ID",
|
|
129
|
+
"GITHUB_REPOSITORY_OWNER_ID",
|
|
130
|
+
"ACTIONS_ID_TOKEN_REQUEST_URL",
|
|
131
|
+
"ACTIONS_ID_TOKEN_REQUEST_TOKEN",
|
|
132
|
+
]) {
|
|
133
|
+
if (source[key] !== undefined) environment[key] = source[key];
|
|
134
|
+
}
|
|
135
|
+
const home = path.join(session, "home");
|
|
136
|
+
return {
|
|
137
|
+
...environment,
|
|
138
|
+
HOME: home,
|
|
139
|
+
USERPROFILE: home,
|
|
140
|
+
COREPACK_HOME: path.join(session, "corepack"),
|
|
141
|
+
COREPACK_NPM_REGISTRY: registry,
|
|
142
|
+
NPM_CONFIG_USERCONFIG: path.join(session, "user-npmrc"),
|
|
143
|
+
NPM_CONFIG_GLOBALCONFIG: path.join(session, "global-npmrc"),
|
|
144
|
+
NPM_CONFIG_CACHE: path.join(session, "npm-cache"),
|
|
145
|
+
PNPM_HOME: path.join(session, "pnpm-home"),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function command(
|
|
150
|
+
run: NonNullable<DirectPublicationOptions["run"]>,
|
|
151
|
+
cwd: string,
|
|
152
|
+
env: NodeJS.ProcessEnv,
|
|
153
|
+
arguments_: readonly string[],
|
|
154
|
+
code: string,
|
|
155
|
+
): Promise<string> {
|
|
156
|
+
const result = await run("corepack", ["pnpm", "exec", "npm", ...arguments_], {
|
|
157
|
+
cwd,
|
|
158
|
+
env,
|
|
159
|
+
});
|
|
160
|
+
if (result.exitCode !== 0) fail(code, "npm command failed");
|
|
161
|
+
return result.stdout.trim();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function npmArguments(
|
|
165
|
+
prefix: string,
|
|
166
|
+
configurationRoot = prefix,
|
|
167
|
+
): readonly string[] {
|
|
168
|
+
return [
|
|
169
|
+
`--prefix=${prefix}`,
|
|
170
|
+
`--registry=${registry}`,
|
|
171
|
+
`--userconfig=${path.join(configurationRoot, "user-npmrc")}`,
|
|
172
|
+
`--globalconfig=${path.join(configurationRoot, "global-npmrc")}`,
|
|
173
|
+
`--cache=${path.join(configurationRoot, "npm-cache")}`,
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function isExplicitNotFound(result: ProcessResult): boolean {
|
|
178
|
+
return /^npm error code E404\s*$/mu.test(result.stderr);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function assertNoCheckoutNpmrc(repositoryRoot: string): Promise<void> {
|
|
182
|
+
try {
|
|
183
|
+
await lstat(path.join(repositoryRoot, ".npmrc"));
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return;
|
|
186
|
+
fail("checkout-npmrc-conflict", "checkout .npmrc cannot be inspected");
|
|
187
|
+
}
|
|
188
|
+
fail("checkout-npmrc-conflict", "checkout root .npmrc is forbidden");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function assertOidcAvailable(
|
|
192
|
+
environment: Readonly<Record<string, string | undefined>>,
|
|
193
|
+
): void {
|
|
194
|
+
if (
|
|
195
|
+
environment.ACTIONS_ID_TOKEN_REQUEST_URL === undefined ||
|
|
196
|
+
environment.ACTIONS_ID_TOKEN_REQUEST_URL.length === 0 ||
|
|
197
|
+
environment.ACTIONS_ID_TOKEN_REQUEST_TOKEN === undefined ||
|
|
198
|
+
environment.ACTIONS_ID_TOKEN_REQUEST_TOKEN.length === 0
|
|
199
|
+
) {
|
|
200
|
+
fail("oidc-unavailable", "GitHub OIDC request variables are required");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export async function runDirectOidcPublication(
|
|
205
|
+
options: DirectPublicationOptions,
|
|
206
|
+
): Promise<void> {
|
|
207
|
+
assertNoAmbientCredentials(options.environment);
|
|
208
|
+
await assertNoCheckoutNpmrc(options.repositoryRoot);
|
|
209
|
+
assertOidcAvailable(options.environment);
|
|
210
|
+
const { receipt, tgz } = await acceptDownloadedPublicationArtifact({
|
|
211
|
+
repositoryRoot: options.repositoryRoot,
|
|
212
|
+
packagePath,
|
|
213
|
+
artifactDirectory: options.artifactDirectory,
|
|
214
|
+
githubRepository: options.environment.GITHUB_REPOSITORY,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const session = await mkdtemp(
|
|
218
|
+
path.join(options.environment.RUNNER_TEMP ?? tmpdir(), "npm-publication-"),
|
|
219
|
+
);
|
|
220
|
+
const run = options.run ?? defaultRun;
|
|
221
|
+
const env = isolatedEnvironment(options.environment, session);
|
|
222
|
+
try {
|
|
223
|
+
await Promise.all([
|
|
224
|
+
mkdir(path.join(session, "home")),
|
|
225
|
+
mkdir(path.join(session, "corepack")),
|
|
226
|
+
mkdir(path.join(session, "npm-cache")),
|
|
227
|
+
mkdir(path.join(session, "pnpm-home")),
|
|
228
|
+
mkdir(path.join(session, "pnpm-store")),
|
|
229
|
+
]);
|
|
230
|
+
await writeFile(
|
|
231
|
+
path.join(session, ".npmrc"),
|
|
232
|
+
`registry=${registry}\nfetch-retries=1\n`,
|
|
233
|
+
);
|
|
234
|
+
await writeFile(path.join(session, "user-npmrc"), "");
|
|
235
|
+
await writeFile(path.join(session, "global-npmrc"), "");
|
|
236
|
+
const install = await run(
|
|
237
|
+
"corepack",
|
|
238
|
+
[
|
|
239
|
+
"pnpm",
|
|
240
|
+
"install",
|
|
241
|
+
"--frozen-lockfile",
|
|
242
|
+
"--ignore-scripts",
|
|
243
|
+
`--registry=${registry}`,
|
|
244
|
+
`--store-dir=${path.join(session, "pnpm-store")}`,
|
|
245
|
+
],
|
|
246
|
+
{ cwd: options.repositoryRoot, env },
|
|
247
|
+
);
|
|
248
|
+
if (install.exitCode !== 0)
|
|
249
|
+
fail("isolated-install-failed", "frozen install failed");
|
|
250
|
+
const common = npmArguments(session);
|
|
251
|
+
const semver = await loadLockedSemver();
|
|
252
|
+
if (
|
|
253
|
+
(await command(
|
|
254
|
+
run,
|
|
255
|
+
options.repositoryRoot,
|
|
256
|
+
env,
|
|
257
|
+
[...common, "config", "get", "fetch-retries"],
|
|
258
|
+
"effective-config-invalid",
|
|
259
|
+
)) !== "1"
|
|
260
|
+
) {
|
|
261
|
+
fail(
|
|
262
|
+
"effective-config-invalid",
|
|
263
|
+
"session fetch-retries marker is not active",
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
if (
|
|
267
|
+
(await command(
|
|
268
|
+
run,
|
|
269
|
+
options.repositoryRoot,
|
|
270
|
+
env,
|
|
271
|
+
[...common, "config", "get", "registry"],
|
|
272
|
+
"effective-config-invalid",
|
|
273
|
+
)) !== registry
|
|
274
|
+
) {
|
|
275
|
+
fail("effective-config-invalid", "session registry is not active");
|
|
276
|
+
}
|
|
277
|
+
const publication = receipt.publication;
|
|
278
|
+
const latestRaw = await command(
|
|
279
|
+
run,
|
|
280
|
+
options.repositoryRoot,
|
|
281
|
+
env,
|
|
282
|
+
[...common, "view", publication.packageName, "dist-tags", "--json"],
|
|
283
|
+
"registry-preflight-failed",
|
|
284
|
+
);
|
|
285
|
+
const latest = (
|
|
286
|
+
parseJson(latestRaw, "registry-preflight-failed") as { latest?: unknown }
|
|
287
|
+
).latest;
|
|
288
|
+
if (typeof latest !== "string" || !isStableSemver(semver, latest))
|
|
289
|
+
fail("latest-invalid", "daily lane needs stable latest");
|
|
290
|
+
const exact = await run(
|
|
291
|
+
"corepack",
|
|
292
|
+
[
|
|
293
|
+
"pnpm",
|
|
294
|
+
"exec",
|
|
295
|
+
"npm",
|
|
296
|
+
...common,
|
|
297
|
+
"view",
|
|
298
|
+
`${publication.packageName}@${publication.version}`,
|
|
299
|
+
"dist.integrity",
|
|
300
|
+
"--json",
|
|
301
|
+
],
|
|
302
|
+
{ cwd: options.repositoryRoot, env },
|
|
303
|
+
);
|
|
304
|
+
const attempt = Number(options.environment.GITHUB_RUN_ATTEMPT);
|
|
305
|
+
if (!Number.isInteger(attempt) || attempt < 1)
|
|
306
|
+
fail("registry-preflight-failed", "invalid run attempt");
|
|
307
|
+
const exactExists = exact.exitCode === 0;
|
|
308
|
+
if (!exactExists && !isExplicitNotFound(exact)) {
|
|
309
|
+
fail(
|
|
310
|
+
"registry-preflight-failed",
|
|
311
|
+
exact.stderr ||
|
|
312
|
+
exact.stdout ||
|
|
313
|
+
"exact version lookup was not a not-found response",
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
let alreadyWritten = false;
|
|
317
|
+
if (attempt === 1 && exactExists)
|
|
318
|
+
fail("fresh-version-exists", "fresh run cannot reuse a version");
|
|
319
|
+
if (exactExists && attempt > 1) {
|
|
320
|
+
const integrity = stringValue(
|
|
321
|
+
parseJson(exact.stdout, "rerun-integrity-conflict"),
|
|
322
|
+
"rerun-integrity-conflict",
|
|
323
|
+
);
|
|
324
|
+
if (integrity !== receipt.artifact.integrity)
|
|
325
|
+
fail("rerun-integrity-conflict", "existing integrity differs");
|
|
326
|
+
if (latest !== publication.version)
|
|
327
|
+
fail("rerun-latest-conflict", "latest differs from candidate");
|
|
328
|
+
alreadyWritten = true;
|
|
329
|
+
}
|
|
330
|
+
if (!exactExists && !semver.gt(publication.version, latest))
|
|
331
|
+
fail("latest-stale", "candidate must be newer than latest");
|
|
332
|
+
if (!alreadyWritten) {
|
|
333
|
+
await command(
|
|
334
|
+
run,
|
|
335
|
+
options.repositoryRoot,
|
|
336
|
+
env,
|
|
337
|
+
[...common, "publish", tgz, "--access=public", "--provenance"],
|
|
338
|
+
"publish-failed",
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
const readIntegrity = stringValue(
|
|
342
|
+
parseJson(
|
|
343
|
+
await command(
|
|
344
|
+
run,
|
|
345
|
+
options.repositoryRoot,
|
|
346
|
+
env,
|
|
347
|
+
[
|
|
348
|
+
...common,
|
|
349
|
+
"view",
|
|
350
|
+
`${publication.packageName}@${publication.version}`,
|
|
351
|
+
"dist.integrity",
|
|
352
|
+
"--json",
|
|
353
|
+
],
|
|
354
|
+
"postwrite-integrity-conflict",
|
|
355
|
+
),
|
|
356
|
+
"postwrite-integrity-conflict",
|
|
357
|
+
),
|
|
358
|
+
"postwrite-integrity-conflict",
|
|
359
|
+
);
|
|
360
|
+
const postTags = parseJson(
|
|
361
|
+
await command(
|
|
362
|
+
run,
|
|
363
|
+
options.repositoryRoot,
|
|
364
|
+
env,
|
|
365
|
+
[...common, "view", publication.packageName, "dist-tags", "--json"],
|
|
366
|
+
"postwrite-integrity-conflict",
|
|
367
|
+
),
|
|
368
|
+
"postwrite-integrity-conflict",
|
|
369
|
+
) as { latest?: unknown };
|
|
370
|
+
if (
|
|
371
|
+
readIntegrity !== receipt.artifact.integrity ||
|
|
372
|
+
postTags.latest !== publication.version
|
|
373
|
+
)
|
|
374
|
+
fail(
|
|
375
|
+
"postwrite-integrity-conflict",
|
|
376
|
+
"registry does not point to receipt artifact",
|
|
377
|
+
);
|
|
378
|
+
const consumer = path.join(session, `consumer-${randomUUID()}`);
|
|
379
|
+
await mkdir(consumer);
|
|
380
|
+
await writeFile(
|
|
381
|
+
path.join(consumer, "package.json"),
|
|
382
|
+
JSON.stringify({
|
|
383
|
+
private: true,
|
|
384
|
+
dependencies: { [publication.packageName]: publication.version },
|
|
385
|
+
}),
|
|
386
|
+
);
|
|
387
|
+
await writeFile(
|
|
388
|
+
path.join(consumer, ".npmrc"),
|
|
389
|
+
`registry=${registry}\nfetch-retries=1\n`,
|
|
390
|
+
);
|
|
391
|
+
const consumerArguments = npmArguments(consumer, session);
|
|
392
|
+
await command(
|
|
393
|
+
run,
|
|
394
|
+
options.repositoryRoot,
|
|
395
|
+
env,
|
|
396
|
+
[...consumerArguments, "install", "--ignore-scripts"],
|
|
397
|
+
"exact-install-failed",
|
|
398
|
+
);
|
|
399
|
+
const audit = parseJson(
|
|
400
|
+
await command(
|
|
401
|
+
run,
|
|
402
|
+
options.repositoryRoot,
|
|
403
|
+
env,
|
|
404
|
+
[
|
|
405
|
+
...consumerArguments,
|
|
406
|
+
"audit",
|
|
407
|
+
"signatures",
|
|
408
|
+
"--json",
|
|
409
|
+
"--include-attestations",
|
|
410
|
+
],
|
|
411
|
+
"signature-audit-invalid",
|
|
412
|
+
),
|
|
413
|
+
"signature-audit-invalid",
|
|
414
|
+
) as { invalid?: unknown; missing?: unknown; verified?: unknown };
|
|
415
|
+
if (
|
|
416
|
+
typeof audit !== "object" ||
|
|
417
|
+
audit === null ||
|
|
418
|
+
!Array.isArray(audit.invalid) ||
|
|
419
|
+
audit.invalid.length !== 0 ||
|
|
420
|
+
!Array.isArray(audit.missing) ||
|
|
421
|
+
audit.missing.length !== 0 ||
|
|
422
|
+
!Array.isArray(audit.verified)
|
|
423
|
+
) {
|
|
424
|
+
fail(
|
|
425
|
+
"signature-audit-invalid",
|
|
426
|
+
"npm audit schema is incomplete or invalid",
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
const matches = audit.verified.filter(
|
|
430
|
+
(item) =>
|
|
431
|
+
typeof item === "object" &&
|
|
432
|
+
item !== null &&
|
|
433
|
+
!Array.isArray(item) &&
|
|
434
|
+
(item as { name?: unknown }).name === publication.packageName &&
|
|
435
|
+
(item as { version?: unknown }).version === publication.version,
|
|
436
|
+
);
|
|
437
|
+
if (matches.length !== 1)
|
|
438
|
+
fail(
|
|
439
|
+
"signature-audit-invalid",
|
|
440
|
+
"exact package attestation is missing or ambiguous",
|
|
441
|
+
);
|
|
442
|
+
const exactVerified = matches[0] as { attestationBundles?: unknown };
|
|
443
|
+
if (
|
|
444
|
+
!Array.isArray(exactVerified.attestationBundles) ||
|
|
445
|
+
exactVerified.attestationBundles.length === 0
|
|
446
|
+
) {
|
|
447
|
+
fail(
|
|
448
|
+
"signature-audit-invalid",
|
|
449
|
+
"exact package attestation bundle is missing",
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
} finally {
|
|
453
|
+
await rm(session, { recursive: true, force: true });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (import.meta.main) {
|
|
458
|
+
const artifactDirectory = process.env.PUBLICATION_ARTIFACT_DIRECTORY;
|
|
459
|
+
if (artifactDirectory === undefined || !path.isAbsolute(artifactDirectory)) {
|
|
460
|
+
throw new PublicationFailure(
|
|
461
|
+
"artifact-unavailable",
|
|
462
|
+
"PUBLICATION_ARTIFACT_DIRECTORY must be absolute",
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
await runDirectOidcPublication({
|
|
467
|
+
repositoryRoot: process.cwd(),
|
|
468
|
+
artifactDirectory,
|
|
469
|
+
environment: process.env,
|
|
470
|
+
});
|
|
471
|
+
} catch (error) {
|
|
472
|
+
if (!(error instanceof PublicationFailure)) throw error;
|
|
473
|
+
console.error(`ERROR ${error.code}`);
|
|
474
|
+
console.error("Observed: publication precondition was not accepted");
|
|
475
|
+
console.error(
|
|
476
|
+
"Expected: a verified artifact and isolated GitHub OIDC session",
|
|
477
|
+
);
|
|
478
|
+
console.error(
|
|
479
|
+
"Next action: correct the reported publication precondition and rerun the failed job",
|
|
480
|
+
);
|
|
481
|
+
process.exitCode = 1;
|
|
482
|
+
}
|
|
483
|
+
}
|