1/**
2 * Shared config for the release scripts. Read from env vars set by the
3 * reusable workflow so the same scripts work for every consumer.
4 *
5 * ALCHEMY_PUBLISHABLE_DIRS JSON array of package dirs (relative to repo root)
6 * ALCHEMY_PUBLISHABLE_NAMES JSON array of npm names (parallel to dirs)
7 * ALCHEMY_CURRENT_VERSION Anchor for prerelease bumps (e.g. "2.0.0")
8 * ALCHEMY_REPO `<owner>/<repo>` for changelog/release-note URLs
9 * GITHUB_OUTPUT GitHub Actions output file (used by output())
10 */
11import { appendFileSync } from "node:fs";
12
13export type Package = {
14 dir: string;
15 name: string;
16 project: string;
17 install: string;
18 runner: string;
19 artifact: string;
20};
21
22export type PrPackagePlan = {
23 packages: Package[];
24 publishable_names: string[];
25 tags: string[];
26 dependency_tag: string;
27 short: string;
28 install_host: string;
29};
30
31export function fail(message: string): never {
32 console.error(message);
33 process.exit(1);
34}
35
36export function required(name: string): string {
37 const v = process.env[name];
38 if (!v || !v.trim()) {
39 fail(`Required env var ${name} is unset or empty`);
40 }
41 return v.trim();
42}
43
44export function output(name: string, value: unknown): void {
45 const encoded = typeof value === "string" ? value : JSON.stringify(value);
46 appendFileSync(required("GITHUB_OUTPUT"), `${name}=${encoded}\n`);
47}
48
49export function jsonArray<T>(name: string, value: string): T[] {
50 let parsed: unknown;
51 try {
52 parsed = JSON.parse(value);
53 } catch (e) {
54 fail(`${name} is not valid JSON: ${(e as Error).message}`);
55 }
56 if (!Array.isArray(parsed)) {
57 fail(`${name} must be a JSON array`);
58 }
59 return parsed as T[];
60}
61
62function parseJsonArray(name: string, value: string): string[] {
63 const parsed = jsonArray<unknown>(name, value);
64 if (!parsed.every((x) => typeof x === "string" && x.trim().length > 0)) {
65 fail(`${name} must be a JSON array of non-empty strings`);
66 }
67 return parsed as string[];
68}
69
70export function publishableDirs(): string[] {
71 return parseJsonArray(
72 "ALCHEMY_PUBLISHABLE_DIRS",
73 required("ALCHEMY_PUBLISHABLE_DIRS"),
74 );
75}
76
77export function publishableNames(): string[] {
78 return parseJsonArray(
79 "ALCHEMY_PUBLISHABLE_NAMES",
80 required("ALCHEMY_PUBLISHABLE_NAMES"),
81 );
82}
83
84export function currentVersion(): string {
85 const v = required("ALCHEMY_CURRENT_VERSION");
86 if (!/^\d+\.\d+\.\d+$/.test(v)) {
87 console.error(`ALCHEMY_CURRENT_VERSION must be x.y.z (got: ${v})`);
88 process.exit(1);
89 }
90 return v;
91}
92
93export function repo(): string {
94 const r = required("ALCHEMY_REPO");
95 if (!/^[\w.-]+\/[\w.-]+$/.test(r)) {
96 console.error(`ALCHEMY_REPO must be <owner>/<repo> (got: ${r})`);
97 process.exit(1);
98 }
99 return r;
100}