@kubb/cli 5.6.0 → 5.6.1
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/{Telemetry-bGdFBaGt.cjs → Telemetry-CDyZhMHb.cjs} +2 -2
- package/dist/{Telemetry-bGdFBaGt.cjs.map → Telemetry-CDyZhMHb.cjs.map} +1 -1
- package/dist/{Telemetry-RBcAjNxj.js → Telemetry-WZD1Le7A.js} +2 -2
- package/dist/{Telemetry-RBcAjNxj.js.map → Telemetry-WZD1Le7A.js.map} +1 -1
- package/dist/{background-0PMj56FJ.js → background-CQmrmXMz.js} +3 -7
- package/dist/background-CQmrmXMz.js.map +1 -0
- package/dist/{background-R0T_7Szu.cjs → background-Ch3napsn.cjs} +2 -2
- package/dist/background-Ch3napsn.cjs.map +1 -0
- package/dist/background-D7IE4acj.js +2 -0
- package/dist/{commands-Si-RFAS6.cjs → commands-CCMgXgwc.cjs} +31 -15
- package/dist/commands-CCMgXgwc.cjs.map +1 -0
- package/dist/{commands-BAgGrcvc.js → commands-ECaVqjMu.js} +31 -15
- package/dist/commands-ECaVqjMu.js.map +1 -0
- package/dist/{config-ByfJSj3q.cjs → config-B2BlaYOU.cjs} +2 -2
- package/dist/{config-ByfJSj3q.cjs.map → config-B2BlaYOU.cjs.map} +1 -1
- package/dist/{config-8Xl4yZmw.js → config-CCxOcwWu.js} +3 -3
- package/dist/{config-8Xl4yZmw.js.map → config-CCxOcwWu.js.map} +1 -1
- package/dist/index.cjs +13 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +13 -19
- package/dist/index.js.map +1 -1
- package/dist/{output-B6Ondw0g.js → output-CnSY0tCG.js} +12 -22
- package/dist/output-CnSY0tCG.js.map +1 -0
- package/dist/{output-i3s5c4Xu.cjs → output-gox62nKo.cjs} +11 -21
- package/dist/output-gox62nKo.cjs.map +1 -0
- package/dist/{run-yvuot2N9.cjs → run-3xcH2h63.cjs} +2 -2
- package/dist/{run-yvuot2N9.cjs.map → run-3xcH2h63.cjs.map} +1 -1
- package/dist/{run-D8Wz_17X.cjs → run-BIK3c6u-.cjs} +68 -139
- package/dist/run-BIK3c6u-.cjs.map +1 -0
- package/dist/{run-3I2NPeXG.cjs → run-BKHV1Apt.cjs} +2 -2
- package/dist/{run-3I2NPeXG.cjs.map → run-BKHV1Apt.cjs.map} +1 -1
- package/dist/{run-DQoKTiGk.js → run-BP_Z7A4e.js} +2 -2
- package/dist/{run-DQoKTiGk.js.map → run-BP_Z7A4e.js.map} +1 -1
- package/dist/{run-BkLJ5mXn.cjs → run-BvKVx0qs.cjs} +2 -2
- package/dist/{run-BkLJ5mXn.cjs.map → run-BvKVx0qs.cjs.map} +1 -1
- package/dist/{run-CmNDNEjR.js → run-DAYWA84e.js} +9 -6
- package/dist/run-DAYWA84e.js.map +1 -0
- package/dist/{run-CZlkqkDc.js → run-J7ekQ_kc.js} +2 -2
- package/dist/{run-CZlkqkDc.js.map → run-J7ekQ_kc.js.map} +1 -1
- package/dist/{run-Dgph0WYk.js → run-TsXMXWSj.js} +73 -132
- package/dist/run-TsXMXWSj.js.map +1 -0
- package/dist/{run-t8nm7zcT.cjs → run-UY0fzPon.cjs} +9 -6
- package/dist/run-UY0fzPon.cjs.map +1 -0
- package/dist/{run-D5AeacqK.js → run-sM7YjAc9.js} +2 -2
- package/dist/{run-D5AeacqK.js.map → run-sM7YjAc9.js.map} +1 -1
- package/dist/{snapshot-CkHXR0Nl.js → snapshot-7yPYU6ol.js} +68 -136
- package/dist/snapshot-7yPYU6ol.js.map +1 -0
- package/dist/{snapshot-D4nKbwkw.cjs → snapshot-Bi1Jy0hs.cjs} +66 -134
- package/dist/snapshot-Bi1Jy0hs.cjs.map +1 -0
- package/dist/studioWorker.js +3 -9
- package/dist/studioWorker.js.map +1 -1
- package/package.json +8 -12
- package/dist/background-0PMj56FJ.js.map +0 -1
- package/dist/background-BqS01QDb.js +0 -2
- package/dist/background-R0T_7Szu.cjs.map +0 -1
- package/dist/commands-BAgGrcvc.js.map +0 -1
- package/dist/commands-Si-RFAS6.cjs.map +0 -1
- package/dist/output-B6Ondw0g.js.map +0 -1
- package/dist/output-i3s5c4Xu.cjs.map +0 -1
- package/dist/run-CmNDNEjR.js.map +0 -1
- package/dist/run-D8Wz_17X.cjs.map +0 -1
- package/dist/run-Dgph0WYk.js.map +0 -1
- package/dist/run-t8nm7zcT.cjs.map +0 -1
- package/dist/snapshot-CkHXR0Nl.js.map +0 -1
- package/dist/snapshot-D4nKbwkw.cjs.map +0 -1
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
import "./rolldown-runtime-CRm0XQPb.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { i as loadConfigs, s as run } from "./run-TsXMXWSj.js";
|
|
3
|
+
import { i as runtime } from "./Telemetry-WZD1Le7A.js";
|
|
4
|
+
import { r as setupReporters } from "./config-CCxOcwWu.js";
|
|
5
|
+
import { h as createPlainLogger, r as logBlock } from "./output-CnSY0tCG.js";
|
|
5
6
|
import { styleText } from "node:util";
|
|
6
|
-
import { randomUUID } from "node:crypto";
|
|
7
7
|
import process from "node:process";
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
8
9
|
import { dirname, join } from "node:path";
|
|
9
10
|
import { readFileSync } from "node:fs";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
11
|
+
import { Hookable, cliReporter, logLevel } from "@kubb/core";
|
|
12
|
+
import { createSnapshot, defaultStudioUrl } from "@kubb/studio";
|
|
13
|
+
//#region ../../internals/utils/src/fs.ts
|
|
14
|
+
/**
|
|
15
|
+
* Reads the file at `path` as a UTF-8 string.
|
|
16
|
+
* Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const source = await read('./src/Pet.ts')
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
async function read(path) {
|
|
24
|
+
if (runtime.isBun) return Bun.file(path).text();
|
|
25
|
+
return readFile(path, { encoding: "utf8" });
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
12
28
|
//#region src/runners/studio/ci.ts
|
|
13
29
|
function readGithubPullRequest(eventPath) {
|
|
14
30
|
if (!eventPath) return;
|
|
@@ -81,35 +97,17 @@ function detectCi(env = process.env) {
|
|
|
81
97
|
//#endregion
|
|
82
98
|
//#region src/runners/studio/snapshot.ts
|
|
83
99
|
/**
|
|
84
|
-
*
|
|
85
|
-
* handshake-ack timeout.
|
|
100
|
+
* Walks up from `directory` to the first package.json with a name and a version.
|
|
86
101
|
*/
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
]);
|
|
97
|
-
async function findPackageJson(startDirectory) {
|
|
98
|
-
let directory = startDirectory;
|
|
99
|
-
for (;;) {
|
|
100
|
-
const file = join(directory, "package.json");
|
|
101
|
-
if (await exists(file)) {
|
|
102
|
-
const packageJson = JSON.parse(await read(file));
|
|
103
|
-
if (packageJson.name && packageJson.version) return {
|
|
104
|
-
name: packageJson.name,
|
|
105
|
-
version: packageJson.version
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
const parent = dirname(directory);
|
|
109
|
-
if (parent === directory) break;
|
|
110
|
-
directory = parent;
|
|
111
|
-
}
|
|
112
|
-
throw new Error(`No package.json with a name and a version found above ${startDirectory}`);
|
|
102
|
+
async function findPackageJson(directory) {
|
|
103
|
+
const packageJson = JSON.parse(await read(join(directory, "package.json")).catch(() => "{}"));
|
|
104
|
+
if (packageJson.name && packageJson.version) return {
|
|
105
|
+
name: packageJson.name,
|
|
106
|
+
version: packageJson.version
|
|
107
|
+
};
|
|
108
|
+
const parent = dirname(directory);
|
|
109
|
+
if (parent === directory) throw new Error(`No package.json with a name and a version found above ${process.cwd()}`);
|
|
110
|
+
return findPackageJson(parent);
|
|
113
111
|
}
|
|
114
112
|
/** Reads package.json only for whichever of name/version --name and --version did not supply. */
|
|
115
113
|
async function resolvePackageMetadata(options) {
|
|
@@ -117,10 +115,10 @@ async function resolvePackageMetadata(options) {
|
|
|
117
115
|
name: options.name,
|
|
118
116
|
version: options.packageVersion
|
|
119
117
|
};
|
|
120
|
-
const
|
|
118
|
+
const found = await findPackageJson(process.cwd());
|
|
121
119
|
return {
|
|
122
|
-
name: options.name ??
|
|
123
|
-
version: options.packageVersion ??
|
|
120
|
+
name: options.name ?? found.name,
|
|
121
|
+
version: options.packageVersion ?? found.version
|
|
124
122
|
};
|
|
125
123
|
}
|
|
126
124
|
function resolveToken(options) {
|
|
@@ -145,21 +143,10 @@ function resolveCiIdentity(options) {
|
|
|
145
143
|
base: base?.id === ci.id ? void 0 : base
|
|
146
144
|
};
|
|
147
145
|
}
|
|
148
|
-
/** Rejects a bad `--timeout` up front instead of letting it reach `waitForJob` as NaN or <= 0. */
|
|
149
|
-
function resolveTimeoutMs(options) {
|
|
150
|
-
const seconds = options.timeout ?? 600;
|
|
151
|
-
if (!Number.isFinite(seconds) || seconds <= 0) throw new Error("--timeout must be a positive number of seconds");
|
|
152
|
-
return Math.min(seconds, MAX_TIMEOUT_SECONDS) * 1e3;
|
|
153
|
-
}
|
|
154
|
-
/** The CI key is a credential, so refuse to send it anywhere but HTTPS or a loopback dev host. */
|
|
155
|
-
function assertSecureStudioUrl(studioUrl) {
|
|
156
|
-
const url = new URL(studioUrl);
|
|
157
|
-
if (url.protocol !== "https:" && !LOOPBACK_HOSTS.has(url.hostname)) throw new Error(`Refusing to send the CI API key to ${studioUrl}. Use an https:// Studio URL, or a loopback host for local development.`);
|
|
158
|
-
}
|
|
159
146
|
function absoluteUrl(studioUrl, path) {
|
|
160
147
|
return new URL(path, `${studioUrl}/`).toString();
|
|
161
148
|
}
|
|
162
|
-
function toResult(studioUrl, snapshot,
|
|
149
|
+
function toResult({ studioUrl, snapshot, ci }) {
|
|
163
150
|
return {
|
|
164
151
|
id: snapshot.id,
|
|
165
152
|
name: snapshot.name,
|
|
@@ -168,7 +155,7 @@ function toResult(studioUrl, snapshot, agentSlug, ci) {
|
|
|
168
155
|
url: absoluteUrl(studioUrl, snapshot.url),
|
|
169
156
|
snapshotIdUrl: absoluteUrl(studioUrl, snapshot.snapshotIdUrl),
|
|
170
157
|
expiresAt: snapshot.expiresAt,
|
|
171
|
-
agentUrl: absoluteUrl(studioUrl, `/agents/${
|
|
158
|
+
agentUrl: absoluteUrl(studioUrl, `/agents/${snapshot.agent.slug ?? snapshot.agent.id}`),
|
|
172
159
|
changes: snapshot.changes,
|
|
173
160
|
branchChanges: snapshot.branchChanges && ci.base ? {
|
|
174
161
|
...snapshot.branchChanges,
|
|
@@ -202,126 +189,71 @@ function printSummary(result) {
|
|
|
202
189
|
]);
|
|
203
190
|
}
|
|
204
191
|
/**
|
|
205
|
-
* Generates
|
|
206
|
-
*
|
|
207
|
-
* packs the tarball in one step, so this needs no separate generation run.
|
|
192
|
+
* Generates the project, packs it, and publishes it to Kubb Studio in one request, under the CI
|
|
193
|
+
* agent the run's identity names.
|
|
208
194
|
*/
|
|
209
195
|
async function snapshot(options) {
|
|
210
|
-
const timeoutMs = resolveTimeoutMs(options);
|
|
211
196
|
const token = resolveToken(options);
|
|
212
|
-
|
|
213
|
-
const { configPath } = await loadConfigs(options);
|
|
197
|
+
const { config } = await loadConfigs(options);
|
|
214
198
|
const ci = resolveCiIdentity(options);
|
|
215
199
|
const { name, version: packageVersion } = await resolvePackageMetadata(options);
|
|
216
200
|
const logLevel$1 = logLevel[options.logLevel ?? "info"];
|
|
217
|
-
process.env.KUBB_AGENT_SECRET = ci.id;
|
|
218
201
|
const write = options.json ? (line) => console.error(line) : (line) => console.log(line);
|
|
219
|
-
const
|
|
202
|
+
const hooks = new Hookable();
|
|
203
|
+
setupReporters(hooks, {
|
|
204
|
+
logLevel: logLevel$1,
|
|
205
|
+
reporters: [cliReporter],
|
|
206
|
+
logger: createPlainLogger(write)
|
|
207
|
+
});
|
|
220
208
|
const step = (message) => {
|
|
221
209
|
if (logLevel$1 > logLevel.silent) write(message);
|
|
222
210
|
};
|
|
223
|
-
step("
|
|
224
|
-
const agent = await createAgent({
|
|
225
|
-
studioUrl: options.studioUrl,
|
|
226
|
-
token,
|
|
227
|
-
name: ci.name,
|
|
228
|
-
machineToken: machineTokenFrom(ci.id)
|
|
229
|
-
});
|
|
230
|
-
const instanceId = randomUUID();
|
|
231
|
-
const { promise: ready, resolve: markReady } = Promise.withResolvers();
|
|
232
|
-
const shutdown = new AbortController();
|
|
233
|
-
const connection = runConnection({
|
|
234
|
-
credentials: { token: agent.token },
|
|
235
|
-
signal: shutdown.signal,
|
|
236
|
-
clientOptions: () => ({
|
|
237
|
-
studioUrl: options.studioUrl,
|
|
238
|
-
instanceId,
|
|
239
|
-
configPath,
|
|
240
|
-
root: process.cwd(),
|
|
241
|
-
version: options.version,
|
|
242
|
-
permissions: options.permission,
|
|
243
|
-
loadConfig: async () => (await loadConfigs(options)).config,
|
|
244
|
-
installLogger: async (hooks) => {
|
|
245
|
-
setupReporters(hooks, {
|
|
246
|
-
logLevel: logLevel$1,
|
|
247
|
-
reporters: [cliReporter],
|
|
248
|
-
logger
|
|
249
|
-
});
|
|
250
|
-
hooks.hook("studio:ready", () => markReady());
|
|
251
|
-
}
|
|
252
|
-
}),
|
|
253
|
-
onTokenRejected: ({ error }) => Promise.reject(error)
|
|
254
|
-
});
|
|
255
|
-
const connectionLost = new AbortController();
|
|
256
|
-
const connectionEnded = /* @__PURE__ */ new Error("The Kubb Studio connection ended before the snapshot finished");
|
|
257
|
-
connection.then(() => connectionLost.abort(connectionEnded), (error) => connectionLost.abort(error));
|
|
258
|
-
const lost = connection.then(() => Promise.reject(connectionEnded), (error) => Promise.reject(error));
|
|
259
|
-
lost.catch(() => {});
|
|
211
|
+
step("Generating the snapshot");
|
|
260
212
|
try {
|
|
261
|
-
|
|
262
|
-
try {
|
|
263
|
-
await Promise.race([
|
|
264
|
-
ready,
|
|
265
|
-
lost,
|
|
266
|
-
new Promise((_, reject) => {
|
|
267
|
-
readyTimeout = setTimeout(() => reject(/* @__PURE__ */ new Error("Timed out waiting for Kubb Studio to confirm the agent was ready")), READY_TIMEOUT_MS);
|
|
268
|
-
})
|
|
269
|
-
]);
|
|
270
|
-
} finally {
|
|
271
|
-
clearTimeout(readyTimeout);
|
|
272
|
-
}
|
|
273
|
-
step("Creating snapshot job");
|
|
274
|
-
const snapshotDeadline = Date.now() + timeoutMs;
|
|
275
|
-
const snapshotTimeoutMs = () => Math.max(snapshotDeadline - Date.now(), 1);
|
|
276
|
-
const job = await createJob({
|
|
213
|
+
const published = await createSnapshot({
|
|
277
214
|
studioUrl: options.studioUrl,
|
|
278
|
-
token,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
name,
|
|
215
|
+
apiKey: token,
|
|
216
|
+
config,
|
|
217
|
+
hooks,
|
|
218
|
+
id: ci.id,
|
|
219
|
+
name: ci.name,
|
|
220
|
+
packageName: name,
|
|
283
221
|
version: packageVersion,
|
|
284
222
|
commit: ci.commit,
|
|
285
|
-
baseId: ci.base?.id
|
|
286
|
-
timeoutMs: snapshotTimeoutMs(),
|
|
287
|
-
signal: connectionLost.signal
|
|
223
|
+
baseId: ci.base?.id
|
|
288
224
|
});
|
|
289
|
-
|
|
290
|
-
const finished = await Promise.race([waitForJob({
|
|
225
|
+
const result = toResult({
|
|
291
226
|
studioUrl: options.studioUrl,
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
signal: connectionLost.signal
|
|
296
|
-
}), lost]);
|
|
297
|
-
if (finished.status === "failed") throw new Error(finished.error ?? "Snapshot job failed");
|
|
298
|
-
if (!finished.snapshot) throw new Error("Snapshot job succeeded without a snapshot");
|
|
299
|
-
const result = toResult(options.studioUrl, finished.snapshot, agent.slug, ci);
|
|
227
|
+
snapshot: published,
|
|
228
|
+
ci
|
|
229
|
+
});
|
|
300
230
|
step("Snapshot published");
|
|
301
231
|
if (options.json) console.log(JSON.stringify(result));
|
|
302
232
|
if (!options.json) printSummary(result);
|
|
303
233
|
} catch (error) {
|
|
304
234
|
step("Snapshot failed");
|
|
305
235
|
throw error;
|
|
306
|
-
} finally {
|
|
307
|
-
step("Disconnecting from Kubb Studio");
|
|
308
|
-
shutdown.abort();
|
|
309
236
|
}
|
|
310
237
|
}
|
|
311
238
|
const runner = async ({ values }) => {
|
|
312
239
|
const options = {
|
|
313
|
-
|
|
240
|
+
configPath: values.config,
|
|
241
|
+
studioUrl: values.url ?? defaultStudioUrl,
|
|
242
|
+
logLevel: values.logLevel,
|
|
314
243
|
token: values.token,
|
|
315
244
|
id: values.id,
|
|
316
245
|
baseId: values.baseId,
|
|
317
246
|
name: values.name,
|
|
318
247
|
packageVersion: values.packageVersion,
|
|
319
|
-
timeout: values.timeout,
|
|
320
248
|
json: values.json
|
|
321
249
|
};
|
|
322
|
-
await run(
|
|
250
|
+
await run({
|
|
251
|
+
options,
|
|
252
|
+
action: () => snapshot(options),
|
|
253
|
+
json: options.json
|
|
254
|
+
});
|
|
323
255
|
};
|
|
324
256
|
//#endregion
|
|
325
257
|
export { runner };
|
|
326
258
|
|
|
327
|
-
//# sourceMappingURL=snapshot-
|
|
259
|
+
//# sourceMappingURL=snapshot-7yPYU6ol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-7yPYU6ol.js","names":["logLevel","logLevelMap"],"sources":["../../../internals/utils/src/fs.ts","../src/runners/studio/ci.ts","../src/runners/studio/snapshot.ts"],"sourcesContent":["import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Whether `stored` already holds `source`, comparing on the trimmed text rather than the exact\n * bytes. Surrounding whitespace is what a formatter adds and what editors strip, and neither is a\n * reason to rewrite the file.\n *\n * Both sides are trimmed, so a storage that keeps bytes verbatim settles on the same answer as one\n * that normalizes what it stores. Trimming only `stored` would leave a source with leading\n * whitespace rewritten on every build, since the stored copy keeps the whitespace the comparison\n * has already dropped.\n */\nexport function matchesStored({ stored, source }: { stored: string; source: string }): boolean {\n return stored.trim() === source.trim()\n}\n\n/**\n * Writes `data` to `path`, trimming surrounding whitespace and ending the file with a single newline\n * the way prettier, biome, and oxfmt all do.\n * Skips the write when the trimmed content is empty, or when the file already holds that content.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns the trimmed content plus a newline\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const content = `${trimmed}\\n`\n const resolved = resolve(path)\n let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\n }\n\n // Creating the directory up front costs a syscall per file, and every file after the first in a\n // directory pays it for nothing. Write first and only fall back when the directory is missing,\n // which also stays correct when something removed it mid-run.\n try {\n await writeFile(resolved, content, { encoding: 'utf-8' })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, content, { encoding: 'utf-8' })\n }\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== content) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return content\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","import { readFileSync } from 'node:fs'\nimport process from 'node:process'\n\n/**\n * Stable identity for the CI agent a snapshot run registers or reuses.\n */\nexport type CiContext = {\n /**\n * Fed into the machine token, so the same merge or pull request reuses one agent.\n */\n id: string\n /**\n * Agent display name shown in Studio.\n */\n name: string\n /** The commit this run builds, so the next snapshot can diff against it. */\n commit?: string\n /** The branch a pull request merges into, and the `id` runs on that branch register under. */\n base?: { branch: string; id: string }\n}\n\ntype GithubPullRequest = { number: number; head?: { sha?: string }; base?: { ref?: string } }\n\nfunction readGithubPullRequest(eventPath: string | undefined): GithubPullRequest | undefined {\n if (!eventPath) {\n return undefined\n }\n\n try {\n const event = JSON.parse(readFileSync(eventPath, 'utf8')) as { pull_request?: GithubPullRequest }\n return event.pull_request?.number ? event.pull_request : undefined\n } catch {\n return undefined\n }\n}\n\n/** Reproduces GitLab's `CI_COMMIT_REF_SLUG` for a branch it gives no slug of, like a merge request's target. */\nexport function gitlabRefSlug(ref: string): string {\n return ref\n .toLowerCase()\n .replace(/[^a-z0-9]/gu, '-')\n .slice(0, 63)\n .replace(/^-+|-+$/g, '')\n}\n\n/**\n * Detects the CI provider from its environment variables and derives a stable identity from it.\n * The GitHub row reproduces `gh:<repositoryId>:<prNumber>`, the identity `kubb-labs/action` has\n * always registered agents under, so an existing repository keeps reusing its agent.\n */\nexport function detectCi(env: Record<string, string | undefined> = process.env): CiContext | null {\n if (env.GITHUB_ACTIONS) {\n const pullRequest = readGithubPullRequest(env.GITHUB_EVENT_PATH)\n const repositoryId = env.GITHUB_REPOSITORY_ID ?? env.GITHUB_REPOSITORY ?? ''\n const branch = env.GITHUB_REF?.startsWith('refs/heads/') ? env.GITHUB_REF.slice('refs/heads/'.length) : undefined\n // A branch run is scoped by its ref, which a pull request number never collides with.\n const scope = pullRequest?.number ?? (branch ? `refs/heads/${branch}` : env.GITHUB_RUN_ID) ?? ''\n const baseBranch = pullRequest?.base?.ref\n\n return {\n id: `gh:${repositoryId}:${scope}`,\n name: `${env.GITHUB_REPOSITORY ?? 'github'}#${pullRequest?.number ?? branch ?? scope}`,\n // The head commit, not the merge commit a pull request run checks out.\n commit: pullRequest?.head?.sha ?? env.GITHUB_SHA,\n base: baseBranch ? { branch: baseBranch, id: `gh:${repositoryId}:refs/heads/${baseBranch}` } : undefined,\n }\n }\n\n if (env.GITLAB_CI) {\n const scope = env.CI_MERGE_REQUEST_IID ?? env.CI_COMMIT_REF_SLUG ?? ''\n const baseBranch = env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME\n\n return {\n id: `gl:${env.CI_PROJECT_ID ?? ''}:${scope}`,\n name: `${env.CI_PROJECT_PATH ?? 'gitlab'}#${scope}`,\n commit: env.CI_MERGE_REQUEST_SOURCE_BRANCH_SHA || env.CI_COMMIT_SHA,\n base: baseBranch ? { branch: baseBranch, id: `gl:${env.CI_PROJECT_ID ?? ''}:${gitlabRefSlug(baseBranch)}` } : undefined,\n }\n }\n\n if (env.BITBUCKET_BUILD_NUMBER) {\n const scope = env.BITBUCKET_PR_ID ?? env.BITBUCKET_BRANCH ?? ''\n\n return { id: `bb:${env.BITBUCKET_REPO_UUID ?? ''}:${scope}`, name: `${env.BITBUCKET_REPO_FULL_NAME ?? 'bitbucket'}#${scope}`, commit: env.BITBUCKET_COMMIT }\n }\n\n if (env.CIRCLECI) {\n // Owner-qualified: two projects with the same name under different CircleCI orgs must not\n // collide on one agent, since registering it purges the other project's live sessions.\n const owner = env.CIRCLE_PROJECT_USERNAME ?? ''\n const project = env.CIRCLE_PROJECT_REPONAME ?? 'circleci'\n const scope = env.CIRCLE_PR_NUMBER ?? env.CIRCLE_BRANCH ?? ''\n\n return { id: `circle:${owner}:${project}:${scope}`, name: `${owner ? `${owner}/` : ''}${project}#${scope}`, commit: env.CIRCLE_SHA1 }\n }\n\n return null\n}\n","import { dirname, join } from 'node:path'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { read } from '@internals/utils'\nimport { cliReporter, Hookable, type KubbHooks, logLevel as logLevelMap } from '@kubb/core'\nimport { createSnapshot, defaultStudioUrl } from '@kubb/studio'\nimport type { BranchSnapshotChanges, PublishedSnapshot, SnapshotChanges } from '@kubb/studio/protocol'\nimport { logBlock } from '../../loggers/output.ts'\nimport { createPlainLogger } from '../../loggers/plainLogger.ts'\nimport setupReporters from '../../loggers/reporters.ts'\nimport { type CiContext, detectCi } from './ci.ts'\nimport { loadConfigs, run, type SnapshotOptions } from './run.ts'\nimport type { command } from '../../commands/studio/snapshot.ts'\nimport type { CommandRunner } from 'gunshi'\n\n/**\n * Walks up from `directory` to the first package.json with a name and a version.\n */\nasync function findPackageJson(directory: string): Promise<{ name: string; version: string }> {\n const packageJson = JSON.parse(await read(join(directory, 'package.json')).catch(() => '{}')) as Partial<{ name: string; version: string }>\n if (packageJson.name && packageJson.version) return { name: packageJson.name, version: packageJson.version }\n\n const parent = dirname(directory)\n if (parent === directory) throw new Error(`No package.json with a name and a version found above ${process.cwd()}`)\n\n return findPackageJson(parent)\n}\n\n/** Reads package.json only for whichever of name/version --name and --version did not supply. */\nasync function resolvePackageMetadata(options: SnapshotOptions): Promise<{ name: string; version: string }> {\n if (options.name && options.packageVersion) return { name: options.name, version: options.packageVersion }\n\n const found = await findPackageJson(process.cwd())\n return { name: options.name ?? found.name, version: options.packageVersion ?? found.version }\n}\n\nfunction resolveToken(options: SnapshotOptions): string {\n const token = options.token ?? process.env.KUBB_TOKEN\n\n if (!token) {\n throw new Error('An organization CI API key is required. Pass --token or set KUBB_TOKEN.')\n }\n\n return token\n}\n\nfunction resolveCiIdentity(options: SnapshotOptions): CiContext {\n const detected = detectCi()\n // A custom --id drops the detected base, since runs on that branch may use a custom id too.\n const ci = options.id ? { id: options.id, name: detected?.name ?? options.id, commit: detected?.commit } : detected\n\n if (!ci) {\n throw new Error('Could not detect a supported CI provider (GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI). Pass --id.')\n }\n\n // An explicit --base-id may not name a branch, so it labels the comparison itself.\n const base = options.baseId ? { branch: options.baseId, id: options.baseId } : ci.base\n\n return { ...ci, base: base?.id === ci.id ? undefined : base }\n}\n\nfunction absoluteUrl(studioUrl: string, path: string): string {\n return new URL(path, `${studioUrl}/`).toString()\n}\n\ntype SnapshotResult = {\n id: string\n name: string | null\n version: string | null\n integrity: string | null\n url: string\n snapshotIdUrl: string\n expiresAt: string\n agentUrl: string\n /** What changed since the previous snapshot of this package on this agent. */\n changes?: SnapshotChanges\n /** What differs from the latest snapshot of the base branch, named by `branch`. */\n branchChanges?: BranchSnapshotChanges & { branch: string }\n}\n\nfunction toResult({ studioUrl, snapshot, ci }: { studioUrl: string; snapshot: PublishedSnapshot; ci: CiContext }): SnapshotResult {\n return {\n id: snapshot.id,\n name: snapshot.name,\n version: snapshot.version,\n integrity: snapshot.integrity,\n url: absoluteUrl(studioUrl, snapshot.url),\n snapshotIdUrl: absoluteUrl(studioUrl, snapshot.snapshotIdUrl),\n expiresAt: snapshot.expiresAt,\n agentUrl: absoluteUrl(studioUrl, `/agents/${snapshot.agent.slug ?? snapshot.agent.id}`),\n changes: snapshot.changes,\n branchChanges: snapshot.branchChanges && ci.base ? { ...snapshot.branchChanges, branch: ci.base.branch } : undefined,\n }\n}\n\n/** How many files differ, `against` what. */\nfunction describeChanges(changes: SnapshotChanges, against: string): string {\n const { added, changed, removed } = changes\n\n return added.length + changed.length + removed.length\n ? `${added.length} added, ${changed.length} changed, ${removed.length} removed ${against}`\n : `No changes ${against}`\n}\n\n/** One summary line: how many files changed, and since which run. */\nexport function formatChanges(changes: SnapshotChanges): string {\n if (!changes.base) {\n return 'First snapshot'\n }\n\n return describeChanges(changes, `since ${changes.base.commit?.slice(0, 7) ?? changes.base.createdAt}`)\n}\n\n/** One summary line: how the snapshot differs from the base branch's latest one. */\nexport function formatBranchChanges(changes: BranchSnapshotChanges & { branch: string }): string {\n if (changes.base) return describeChanges(changes, `against ${changes.branch}`)\n\n return changes.baseFound ? `No snapshot of ${changes.branch} for this package yet` : `No snapshot of ${changes.branch} to compare with`\n}\n\nfunction printSummary(result: SnapshotResult): void {\n logBlock([\n `${styleText('dim', 'Package'.padEnd(10))} ${result.name ?? '(unnamed)'}@${result.version ?? '0.0.0'}`,\n `${styleText('dim', 'Tarball'.padEnd(10))} ${styleText('cyan', result.url)}`,\n `${styleText('dim', 'Agent'.padEnd(10))} ${result.agentUrl}`,\n ...(result.branchChanges ? [`${styleText('dim', 'Branch'.padEnd(10))} ${formatBranchChanges(result.branchChanges)}`] : []),\n ...(result.changes ? [`${styleText('dim', 'Changes'.padEnd(10))} ${formatChanges(result.changes)}`] : []),\n `${styleText('dim', 'Expires'.padEnd(10))} ${result.expiresAt}`,\n ])\n}\n\n/**\n * Generates the project, packs it, and publishes it to Kubb Studio in one request, under the CI\n * agent the run's identity names.\n */\nexport async function snapshot(options: SnapshotOptions): Promise<void> {\n const token = resolveToken(options)\n const { config } = await loadConfigs(options)\n const ci = resolveCiIdentity(options)\n const { name, version: packageVersion } = await resolvePackageMetadata(options)\n const logLevel = logLevelMap[options.logLevel ?? 'info']\n\n const write = options.json ? (line: string) => console.error(line) : (line: string) => console.log(line)\n const hooks = new Hookable<KubbHooks>()\n setupReporters(hooks, { logLevel, reporters: [cliReporter], logger: createPlainLogger(write) })\n const step = (message: string) => {\n if (logLevel > logLevelMap.silent) {\n write(message)\n }\n }\n\n step('Generating the snapshot')\n\n try {\n const published = await createSnapshot({\n studioUrl: options.studioUrl,\n apiKey: token,\n config,\n hooks,\n id: ci.id,\n name: ci.name,\n packageName: name,\n version: packageVersion,\n commit: ci.commit,\n baseId: ci.base?.id,\n })\n const result = toResult({ studioUrl: options.studioUrl, snapshot: published, ci })\n\n step('Snapshot published')\n\n if (options.json) {\n console.log(JSON.stringify(result))\n }\n if (!options.json) {\n printSummary(result)\n }\n } catch (error) {\n step('Snapshot failed')\n throw error\n }\n}\n\nexport const runner: CommandRunner<{ args: typeof command.args; extensions: {} }> = async ({ values }) => {\n const options: SnapshotOptions = {\n configPath: values.config,\n studioUrl: values.url ?? defaultStudioUrl,\n logLevel: values.logLevel,\n token: values.token,\n id: values.id,\n baseId: values.baseId,\n name: values.name,\n packageVersion: values.packageVersion,\n json: values.json,\n }\n await run({ options, action: () => snapshot(options), json: options.json })\n}\n"],"mappings":"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"}
|
|
@@ -1,15 +1,31 @@
|
|
|
1
1
|
const require_agent = require("./agent-DElUuYuy.cjs");
|
|
2
|
-
const require_run = require("./run-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
2
|
+
const require_run = require("./run-BIK3c6u-.cjs");
|
|
3
|
+
const require_Telemetry = require("./Telemetry-CDyZhMHb.cjs");
|
|
4
|
+
const require_output = require("./output-gox62nKo.cjs");
|
|
5
|
+
const require_config = require("./config-B2BlaYOU.cjs");
|
|
5
6
|
let node_util = require("node:util");
|
|
6
|
-
let node_crypto = require("node:crypto");
|
|
7
7
|
let node_process = require("node:process");
|
|
8
8
|
node_process = require_agent.__toESM(node_process, 1);
|
|
9
|
+
let node_fs_promises = require("node:fs/promises");
|
|
9
10
|
let node_path = require("node:path");
|
|
10
11
|
let _kubb_core = require("@kubb/core");
|
|
11
12
|
let node_fs = require("node:fs");
|
|
12
13
|
let _kubb_studio = require("@kubb/studio");
|
|
14
|
+
//#region ../../internals/utils/src/fs.ts
|
|
15
|
+
/**
|
|
16
|
+
* Reads the file at `path` as a UTF-8 string.
|
|
17
|
+
* Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const source = await read('./src/Pet.ts')
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
async function read(path) {
|
|
25
|
+
if (require_Telemetry.runtime.isBun) return Bun.file(path).text();
|
|
26
|
+
return (0, node_fs_promises.readFile)(path, { encoding: "utf8" });
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
13
29
|
//#region src/runners/studio/ci.ts
|
|
14
30
|
function readGithubPullRequest(eventPath) {
|
|
15
31
|
if (!eventPath) return;
|
|
@@ -82,35 +98,17 @@ function detectCi(env = node_process.default.env) {
|
|
|
82
98
|
//#endregion
|
|
83
99
|
//#region src/runners/studio/snapshot.ts
|
|
84
100
|
/**
|
|
85
|
-
*
|
|
86
|
-
* handshake-ack timeout.
|
|
101
|
+
* Walks up from `directory` to the first package.json with a name and a version.
|
|
87
102
|
*/
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
]);
|
|
98
|
-
async function findPackageJson(startDirectory) {
|
|
99
|
-
let directory = startDirectory;
|
|
100
|
-
for (;;) {
|
|
101
|
-
const file = (0, node_path.join)(directory, "package.json");
|
|
102
|
-
if (await require_run.exists(file)) {
|
|
103
|
-
const packageJson = JSON.parse(await require_run.read(file));
|
|
104
|
-
if (packageJson.name && packageJson.version) return {
|
|
105
|
-
name: packageJson.name,
|
|
106
|
-
version: packageJson.version
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
const parent = (0, node_path.dirname)(directory);
|
|
110
|
-
if (parent === directory) break;
|
|
111
|
-
directory = parent;
|
|
112
|
-
}
|
|
113
|
-
throw new Error(`No package.json with a name and a version found above ${startDirectory}`);
|
|
103
|
+
async function findPackageJson(directory) {
|
|
104
|
+
const packageJson = JSON.parse(await read((0, node_path.join)(directory, "package.json")).catch(() => "{}"));
|
|
105
|
+
if (packageJson.name && packageJson.version) return {
|
|
106
|
+
name: packageJson.name,
|
|
107
|
+
version: packageJson.version
|
|
108
|
+
};
|
|
109
|
+
const parent = (0, node_path.dirname)(directory);
|
|
110
|
+
if (parent === directory) throw new Error(`No package.json with a name and a version found above ${node_process.default.cwd()}`);
|
|
111
|
+
return findPackageJson(parent);
|
|
114
112
|
}
|
|
115
113
|
/** Reads package.json only for whichever of name/version --name and --version did not supply. */
|
|
116
114
|
async function resolvePackageMetadata(options) {
|
|
@@ -118,10 +116,10 @@ async function resolvePackageMetadata(options) {
|
|
|
118
116
|
name: options.name,
|
|
119
117
|
version: options.packageVersion
|
|
120
118
|
};
|
|
121
|
-
const
|
|
119
|
+
const found = await findPackageJson(node_process.default.cwd());
|
|
122
120
|
return {
|
|
123
|
-
name: options.name ??
|
|
124
|
-
version: options.packageVersion ??
|
|
121
|
+
name: options.name ?? found.name,
|
|
122
|
+
version: options.packageVersion ?? found.version
|
|
125
123
|
};
|
|
126
124
|
}
|
|
127
125
|
function resolveToken(options) {
|
|
@@ -146,21 +144,10 @@ function resolveCiIdentity(options) {
|
|
|
146
144
|
base: base?.id === ci.id ? void 0 : base
|
|
147
145
|
};
|
|
148
146
|
}
|
|
149
|
-
/** Rejects a bad `--timeout` up front instead of letting it reach `waitForJob` as NaN or <= 0. */
|
|
150
|
-
function resolveTimeoutMs(options) {
|
|
151
|
-
const seconds = options.timeout ?? 600;
|
|
152
|
-
if (!Number.isFinite(seconds) || seconds <= 0) throw new Error("--timeout must be a positive number of seconds");
|
|
153
|
-
return Math.min(seconds, MAX_TIMEOUT_SECONDS) * 1e3;
|
|
154
|
-
}
|
|
155
|
-
/** The CI key is a credential, so refuse to send it anywhere but HTTPS or a loopback dev host. */
|
|
156
|
-
function assertSecureStudioUrl(studioUrl) {
|
|
157
|
-
const url = new URL(studioUrl);
|
|
158
|
-
if (url.protocol !== "https:" && !LOOPBACK_HOSTS.has(url.hostname)) throw new Error(`Refusing to send the CI API key to ${studioUrl}. Use an https:// Studio URL, or a loopback host for local development.`);
|
|
159
|
-
}
|
|
160
147
|
function absoluteUrl(studioUrl, path) {
|
|
161
148
|
return new URL(path, `${studioUrl}/`).toString();
|
|
162
149
|
}
|
|
163
|
-
function toResult(studioUrl, snapshot,
|
|
150
|
+
function toResult({ studioUrl, snapshot, ci }) {
|
|
164
151
|
return {
|
|
165
152
|
id: snapshot.id,
|
|
166
153
|
name: snapshot.name,
|
|
@@ -169,7 +156,7 @@ function toResult(studioUrl, snapshot, agentSlug, ci) {
|
|
|
169
156
|
url: absoluteUrl(studioUrl, snapshot.url),
|
|
170
157
|
snapshotIdUrl: absoluteUrl(studioUrl, snapshot.snapshotIdUrl),
|
|
171
158
|
expiresAt: snapshot.expiresAt,
|
|
172
|
-
agentUrl: absoluteUrl(studioUrl, `/agents/${
|
|
159
|
+
agentUrl: absoluteUrl(studioUrl, `/agents/${snapshot.agent.slug ?? snapshot.agent.id}`),
|
|
173
160
|
changes: snapshot.changes,
|
|
174
161
|
branchChanges: snapshot.branchChanges && ci.base ? {
|
|
175
162
|
...snapshot.branchChanges,
|
|
@@ -203,126 +190,71 @@ function printSummary(result) {
|
|
|
203
190
|
]);
|
|
204
191
|
}
|
|
205
192
|
/**
|
|
206
|
-
* Generates
|
|
207
|
-
*
|
|
208
|
-
* packs the tarball in one step, so this needs no separate generation run.
|
|
193
|
+
* Generates the project, packs it, and publishes it to Kubb Studio in one request, under the CI
|
|
194
|
+
* agent the run's identity names.
|
|
209
195
|
*/
|
|
210
196
|
async function snapshot(options) {
|
|
211
|
-
const timeoutMs = resolveTimeoutMs(options);
|
|
212
197
|
const token = resolveToken(options);
|
|
213
|
-
|
|
214
|
-
const { configPath } = await require_run.loadConfigs(options);
|
|
198
|
+
const { config } = await require_run.loadConfigs(options);
|
|
215
199
|
const ci = resolveCiIdentity(options);
|
|
216
200
|
const { name, version: packageVersion } = await resolvePackageMetadata(options);
|
|
217
201
|
const logLevel = _kubb_core.logLevel[options.logLevel ?? "info"];
|
|
218
|
-
node_process.default.env.KUBB_AGENT_SECRET = ci.id;
|
|
219
202
|
const write = options.json ? (line) => console.error(line) : (line) => console.log(line);
|
|
220
|
-
const
|
|
203
|
+
const hooks = new _kubb_core.Hookable();
|
|
204
|
+
require_config.setupReporters(hooks, {
|
|
205
|
+
logLevel,
|
|
206
|
+
reporters: [_kubb_core.cliReporter],
|
|
207
|
+
logger: require_output.createPlainLogger(write)
|
|
208
|
+
});
|
|
221
209
|
const step = (message) => {
|
|
222
210
|
if (logLevel > _kubb_core.logLevel.silent) write(message);
|
|
223
211
|
};
|
|
224
|
-
step("
|
|
225
|
-
const agent = await (0, _kubb_studio.createAgent)({
|
|
226
|
-
studioUrl: options.studioUrl,
|
|
227
|
-
token,
|
|
228
|
-
name: ci.name,
|
|
229
|
-
machineToken: (0, _kubb_studio.machineTokenFrom)(ci.id)
|
|
230
|
-
});
|
|
231
|
-
const instanceId = (0, node_crypto.randomUUID)();
|
|
232
|
-
const { promise: ready, resolve: markReady } = Promise.withResolvers();
|
|
233
|
-
const shutdown = new AbortController();
|
|
234
|
-
const connection = (0, _kubb_studio.runConnection)({
|
|
235
|
-
credentials: { token: agent.token },
|
|
236
|
-
signal: shutdown.signal,
|
|
237
|
-
clientOptions: () => ({
|
|
238
|
-
studioUrl: options.studioUrl,
|
|
239
|
-
instanceId,
|
|
240
|
-
configPath,
|
|
241
|
-
root: node_process.default.cwd(),
|
|
242
|
-
version: options.version,
|
|
243
|
-
permissions: options.permission,
|
|
244
|
-
loadConfig: async () => (await require_run.loadConfigs(options)).config,
|
|
245
|
-
installLogger: async (hooks) => {
|
|
246
|
-
require_config.setupReporters(hooks, {
|
|
247
|
-
logLevel,
|
|
248
|
-
reporters: [_kubb_core.cliReporter],
|
|
249
|
-
logger
|
|
250
|
-
});
|
|
251
|
-
hooks.hook("studio:ready", () => markReady());
|
|
252
|
-
}
|
|
253
|
-
}),
|
|
254
|
-
onTokenRejected: ({ error }) => Promise.reject(error)
|
|
255
|
-
});
|
|
256
|
-
const connectionLost = new AbortController();
|
|
257
|
-
const connectionEnded = /* @__PURE__ */ new Error("The Kubb Studio connection ended before the snapshot finished");
|
|
258
|
-
connection.then(() => connectionLost.abort(connectionEnded), (error) => connectionLost.abort(error));
|
|
259
|
-
const lost = connection.then(() => Promise.reject(connectionEnded), (error) => Promise.reject(error));
|
|
260
|
-
lost.catch(() => {});
|
|
212
|
+
step("Generating the snapshot");
|
|
261
213
|
try {
|
|
262
|
-
|
|
263
|
-
try {
|
|
264
|
-
await Promise.race([
|
|
265
|
-
ready,
|
|
266
|
-
lost,
|
|
267
|
-
new Promise((_, reject) => {
|
|
268
|
-
readyTimeout = setTimeout(() => reject(/* @__PURE__ */ new Error("Timed out waiting for Kubb Studio to confirm the agent was ready")), READY_TIMEOUT_MS);
|
|
269
|
-
})
|
|
270
|
-
]);
|
|
271
|
-
} finally {
|
|
272
|
-
clearTimeout(readyTimeout);
|
|
273
|
-
}
|
|
274
|
-
step("Creating snapshot job");
|
|
275
|
-
const snapshotDeadline = Date.now() + timeoutMs;
|
|
276
|
-
const snapshotTimeoutMs = () => Math.max(snapshotDeadline - Date.now(), 1);
|
|
277
|
-
const job = await (0, _kubb_studio.createJob)({
|
|
214
|
+
const published = await (0, _kubb_studio.createSnapshot)({
|
|
278
215
|
studioUrl: options.studioUrl,
|
|
279
|
-
token,
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
name,
|
|
216
|
+
apiKey: token,
|
|
217
|
+
config,
|
|
218
|
+
hooks,
|
|
219
|
+
id: ci.id,
|
|
220
|
+
name: ci.name,
|
|
221
|
+
packageName: name,
|
|
284
222
|
version: packageVersion,
|
|
285
223
|
commit: ci.commit,
|
|
286
|
-
baseId: ci.base?.id
|
|
287
|
-
timeoutMs: snapshotTimeoutMs(),
|
|
288
|
-
signal: connectionLost.signal
|
|
224
|
+
baseId: ci.base?.id
|
|
289
225
|
});
|
|
290
|
-
|
|
291
|
-
const finished = await Promise.race([(0, _kubb_studio.waitForJob)({
|
|
226
|
+
const result = toResult({
|
|
292
227
|
studioUrl: options.studioUrl,
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
signal: connectionLost.signal
|
|
297
|
-
}), lost]);
|
|
298
|
-
if (finished.status === "failed") throw new Error(finished.error ?? "Snapshot job failed");
|
|
299
|
-
if (!finished.snapshot) throw new Error("Snapshot job succeeded without a snapshot");
|
|
300
|
-
const result = toResult(options.studioUrl, finished.snapshot, agent.slug, ci);
|
|
228
|
+
snapshot: published,
|
|
229
|
+
ci
|
|
230
|
+
});
|
|
301
231
|
step("Snapshot published");
|
|
302
232
|
if (options.json) console.log(JSON.stringify(result));
|
|
303
233
|
if (!options.json) printSummary(result);
|
|
304
234
|
} catch (error) {
|
|
305
235
|
step("Snapshot failed");
|
|
306
236
|
throw error;
|
|
307
|
-
} finally {
|
|
308
|
-
step("Disconnecting from Kubb Studio");
|
|
309
|
-
shutdown.abort();
|
|
310
237
|
}
|
|
311
238
|
}
|
|
312
239
|
const runner = async ({ values }) => {
|
|
313
240
|
const options = {
|
|
314
|
-
|
|
241
|
+
configPath: values.config,
|
|
242
|
+
studioUrl: values.url ?? _kubb_studio.defaultStudioUrl,
|
|
243
|
+
logLevel: values.logLevel,
|
|
315
244
|
token: values.token,
|
|
316
245
|
id: values.id,
|
|
317
246
|
baseId: values.baseId,
|
|
318
247
|
name: values.name,
|
|
319
248
|
packageVersion: values.packageVersion,
|
|
320
|
-
timeout: values.timeout,
|
|
321
249
|
json: values.json
|
|
322
250
|
};
|
|
323
|
-
await require_run.run(
|
|
251
|
+
await require_run.run({
|
|
252
|
+
options,
|
|
253
|
+
action: () => snapshot(options),
|
|
254
|
+
json: options.json
|
|
255
|
+
});
|
|
324
256
|
};
|
|
325
257
|
//#endregion
|
|
326
258
|
exports.runner = runner;
|
|
327
259
|
|
|
328
|
-
//# sourceMappingURL=snapshot-
|
|
260
|
+
//# sourceMappingURL=snapshot-Bi1Jy0hs.cjs.map
|