@olivervea/pipelines 2.0.0-beta.6 → 2.0.0-beta.8

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/src/util/tar.ts CHANGED
@@ -147,6 +147,19 @@ export async function writeTarGz(out: string, entries: readonly TarEntry[], leve
147
147
  }
148
148
  }
149
149
 
150
+ /** Writes a deterministic zstd-compressed tar (level 3: fast to write, and unpacks far faster than gzip). */
151
+ export async function writeTarZst(out: string, entries: readonly TarEntry[], level = 3): Promise<void> {
152
+ const tar = `${out}.tar-tmp`;
153
+ const tmp = `${out}.partial`;
154
+ try {
155
+ writeTar(tar, entries);
156
+ await Bun.write(tmp, await Bun.zstdCompress(await Bun.file(tar).bytes(), { level }));
157
+ renameSync(tmp, out);
158
+ } finally {
159
+ rmSync(tar, { force: true });
160
+ }
161
+ }
162
+
150
163
  /** Extracts a tar or tar.gz file into `dir`. */
151
164
  export async function extractTar(archive: string, dir: string): Promise<void> {
152
165
  const a = new Bun.Archive(await Bun.file(archive).bytes());
@@ -1,33 +0,0 @@
1
- // npm registry calls for release steps (no npm CLI needed in the image).
2
- import type { Ctx } from "../ctx/types.ts";
3
-
4
- export const NPM_REGISTRY = "https://registry.npmjs.org";
5
-
6
- const packagePath = (name: string) => name.replace("/", "%2f");
7
-
8
- /** Whether `name@version` is on the registry. */
9
- export async function npmHasVersion(ctx: Pick<Ctx, "http">, name: string, version: string, registry = NPM_REGISTRY): Promise<boolean> {
10
- const res = await ctx.http.fetch(`${registry}/${packagePath(name)}`, { headers: { "cache-control": "no-cache" } });
11
- if (res.status === 404) {
12
- await res.body?.cancel().catch(() => {});
13
- return false;
14
- }
15
- if (!res.ok) throw new Error(`npm registry lookup of ${name} failed: HTTP ${res.status}`);
16
- return Boolean(((await res.json()) as { versions?: Record<string, unknown> }).versions?.[version]);
17
- }
18
-
19
- /** `npm dist-tag add <name>@<version> <tag>`: points the tag at an already published version. */
20
- export async function npmDistTag(
21
- ctx: Pick<Ctx, "http" | "log">,
22
- o: { name: string; version: string; tag: string; token: string; registry?: string },
23
- ): Promise<void> {
24
- const url = `${o.registry ?? NPM_REGISTRY}/-/package/${packagePath(o.name)}/dist-tags/${encodeURIComponent(o.tag)}`;
25
- const res = await ctx.http.fetch(url, {
26
- method: "PUT",
27
- headers: { authorization: `Bearer ${o.token}`, "content-type": "application/json" },
28
- body: JSON.stringify(o.version),
29
- });
30
- await res.body?.cancel().catch(() => {});
31
- if (!res.ok) throw new Error(`npm dist-tag ${o.name}@${o.version} ${o.tag} failed: HTTP ${res.status}`);
32
- ctx.log("npm dist-tag", { package: `${o.name}@${o.version}`, tag: o.tag });
33
- }
@@ -1,85 +0,0 @@
1
- import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import type { Ctx, RunInfo } from "../ctx/types.ts";
4
- import type { AnyModel } from "../core/models.ts";
5
- import type { Secret } from "../core/secrets.ts";
6
- import { makeStep, type InputSpec, type LibraryStep, type Step, type StepDef, type StepInput } from "../core/steps.ts";
7
- import { sha256Hex } from "../util/hash.ts";
8
-
9
- /** A config value that may depend on the run: a constant or a function of the step's inputs and run info. */
10
- export type RunValue<I, T = string> = T | ((input: StepInput<I>, run: RunInfo) => T);
11
-
12
- export interface ScriptDef<N extends string, I extends InputSpec, O extends AnyModel | null, Sec extends readonly Secret[]>
13
- extends StepDef<N, I, O, Sec> {
14
- /** An embedded script: `import s from "./scripts/x.sh" with { type: "file" }`. */
15
- script: string;
16
- /** Extra environment; values may be functions of the inputs (evaluated in the step, not at describe). */
17
- env?: Record<string, RunValue<I>>;
18
- }
19
-
20
- /** `deployTo` → `INPUT_DEPLOY_TO`. */
21
- export function inputEnvName(key: string): string {
22
- return "INPUT_" + key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
23
- }
24
-
25
- /**
26
- * Runs a shell script (the zero-TypeScript path). Contract for the script:
27
- * - `$OLVE_INPUT`: path of the validated input as JSON; each input key is also `INPUT_<KEY>`
28
- * (strings raw, everything else JSON).
29
- * - `$OLVE_OUTPUT`: if the step has an output model, write the output JSON there. Objects of the
30
- * form `{"$file": "<path>"}` / `{"$dir": "<path>"}` are turned into file references.
31
- * - Declared secrets are in the environment under their names.
32
- */
33
- export function script<
34
- const N extends string,
35
- const I extends InputSpec = {},
36
- O extends AnyModel | null = null,
37
- const Sec extends readonly Secret[] = [],
38
- >(def: ScriptDef<N, I, O, Sec>): Step<N, I, O, Sec[number]["name"]> & LibraryStep {
39
- const env = def.env ?? {};
40
- return makeStep("script", def as never, {
41
- manifestConfig: () => ({
42
- scriptSha256: "sha256:" + sha256Hex(readFileSync(def.script)),
43
- env: Object.fromEntries(Object.entries(env).filter(([, v]) => typeof v === "string")),
44
- computedEnv: Object.keys(env).filter((k) => typeof env[k] === "function").sort(),
45
- }),
46
- async run(input: Record<string, unknown>, ctx: Ctx) {
47
- mkdirSync(ctx.tmpDir, { recursive: true });
48
- const dir = mkdtempSync(join(ctx.tmpDir, "script-"));
49
- const inputPath = join(dir, "input.json");
50
- const outputPath = join(dir, "output.json");
51
- writeFileSync(inputPath, JSON.stringify(input));
52
- const vars: Record<string, string> = { OLVE_INPUT: inputPath };
53
- for (const [k, v] of Object.entries(input)) {
54
- if (v !== undefined) vars[inputEnvName(k)] = typeof v === "string" ? v : JSON.stringify(v);
55
- }
56
- for (const [k, v] of Object.entries(env)) {
57
- vars[k] = typeof v === "function" ? (v as (i: unknown, r: RunInfo) => string)(input, ctx.run) : v;
58
- }
59
- if (def.output) vars.OLVE_OUTPUT = outputPath;
60
- await ctx.sh({ script: def.script }, { env: vars });
61
- if (!def.output) return undefined;
62
- let raw: unknown;
63
- try {
64
- raw = JSON.parse(readFileSync(outputPath, "utf8"));
65
- } catch (e) {
66
- throw new Error(`script did not write valid JSON to $OLVE_OUTPUT: ${(e as Error).message}`);
67
- }
68
- return resolveFilePlaceholders(raw, ctx);
69
- },
70
- }) as never;
71
- }
72
-
73
- async function resolveFilePlaceholders(v: unknown, ctx: Ctx): Promise<unknown> {
74
- if (Array.isArray(v)) return Promise.all(v.map((x) => resolveFilePlaceholders(x, ctx)));
75
- if (typeof v === "object" && v !== null) {
76
- const o = v as Record<string, unknown>;
77
- const keys = Object.keys(o);
78
- if (keys.length === 1 && typeof o.$file === "string") return ctx.output.file(o.$file);
79
- if (keys.length === 1 && typeof o.$dir === "string") return ctx.output.dir(o.$dir);
80
- const out: Record<string, unknown> = {};
81
- for (const k of keys) out[k] = await resolveFilePlaceholders(o[k], ctx);
82
- return out;
83
- }
84
- return v;
85
- }