@kungfu-tech/buildchain 2.14.3-alpha.0 → 2.14.3-alpha.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.
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { resolveBuildchainChannel } from "./buildchain-channel-router.mjs";
7
+
8
+ const TARGETS = [
9
+ { pattern: /^alpha\/v(\d+)\/v\d+\.\d+$/, publicationChannel: "alpha", shellChannel: "alpha" },
10
+ { pattern: /^release\/v(\d+)\/v\d+\.\d+$/, publicationChannel: "release", shellChannel: "stable" },
11
+ { pattern: /^(?:publish-gate\/major|major-gate)$/, publicationChannel: "major", shellChannel: "stable" },
12
+ ];
13
+
14
+ function normalized(value) {
15
+ return String(value ?? "").trim();
16
+ }
17
+
18
+ function targetIntent(targetRef, requestedPublicationChannel = "") {
19
+ const ref = normalized(targetRef).replace(/^refs\/heads\//, "");
20
+ const target = TARGETS.find((entry) => entry.pattern.test(ref));
21
+ if (!target) throw new Error(`unsupported promotion target ref: ${ref || "<empty>"}`);
22
+ const requested = normalized(requestedPublicationChannel).toLowerCase();
23
+ if (requested && requested !== target.publicationChannel) {
24
+ throw new Error(`promotion channel ${requested} does not match target ref ${ref} (${target.publicationChannel})`);
25
+ }
26
+ const match = ref.match(target.pattern);
27
+ return {
28
+ targetRef: ref,
29
+ publicationChannel: target.publicationChannel,
30
+ shellChannel: target.shellChannel,
31
+ targetMajor: match?.[1] ? Number(match[1]) : undefined,
32
+ };
33
+ }
34
+
35
+ export function resolvePromotionChannel({
36
+ requestedChannel = "auto",
37
+ requestedRef = "",
38
+ publicationChannel = "",
39
+ targetRef = "",
40
+ routerRef = "",
41
+ packageVersion = "",
42
+ } = {}) {
43
+ const intent = targetIntent(targetRef, publicationChannel);
44
+ const selected = resolveBuildchainChannel({
45
+ requestedChannel,
46
+ requestedRef,
47
+ publishChannel: intent.publicationChannel,
48
+ eventName: "workflow_call",
49
+ routerRef,
50
+ packageVersion,
51
+ });
52
+ const overrideUsed = selected.channel === "override";
53
+ if (!overrideUsed && selected.channel !== intent.shellChannel) {
54
+ throw new Error(
55
+ `promotion target ${intent.targetRef} requires ${intent.shellChannel} shell/runtime, got ${selected.channel}`,
56
+ );
57
+ }
58
+ if (Number.isInteger(intent.targetMajor) && selected.major !== intent.targetMajor) {
59
+ throw new Error(
60
+ `promotion target ${intent.targetRef} requires Buildchain v${intent.targetMajor}, got v${selected.major}`,
61
+ );
62
+ }
63
+ const shellRef = intent.shellChannel === "alpha" ? `v${selected.major}-alpha` : `v${selected.major}`;
64
+ return {
65
+ targetRef: intent.targetRef,
66
+ publicationChannel: intent.publicationChannel,
67
+ channel: intent.shellChannel,
68
+ major: selected.major,
69
+ shellRef,
70
+ runtimeRef: selected.buildchainRef,
71
+ overrideUsed,
72
+ selectionSource: selected.selectionSource,
73
+ reason: selected.reason,
74
+ };
75
+ }
76
+
77
+ function parseArgs(argv) {
78
+ const values = {};
79
+ for (let index = 0; index < argv.length; index += 1) {
80
+ const token = argv[index];
81
+ if (!token.startsWith("--")) throw new Error(`unexpected argument: ${token}`);
82
+ const value = argv[index + 1];
83
+ if (value === undefined || value.startsWith("--")) throw new Error(`missing value for ${token}`);
84
+ values[token.slice(2)] = value;
85
+ index += 1;
86
+ }
87
+ return values;
88
+ }
89
+
90
+ function readPackageVersion(cwd) {
91
+ return JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf8")).version;
92
+ }
93
+
94
+ function writeOutputs(file, result) {
95
+ const outputs = {
96
+ "target-ref": result.targetRef,
97
+ "publication-channel": result.publicationChannel,
98
+ channel: result.channel,
99
+ major: String(result.major),
100
+ "shell-ref": result.shellRef,
101
+ "runtime-ref": result.runtimeRef,
102
+ "override-used": String(result.overrideUsed),
103
+ "selection-source": result.selectionSource,
104
+ reason: result.reason,
105
+ };
106
+ fs.appendFileSync(file, Object.entries(outputs).map(([key, value]) => `${key}=${value}\n`).join(""));
107
+ }
108
+
109
+ function main() {
110
+ const args = parseArgs(process.argv.slice(2));
111
+ const cwd = path.resolve(args.cwd || process.cwd());
112
+ const result = resolvePromotionChannel({
113
+ requestedChannel: args.channel,
114
+ requestedRef: args["buildchain-ref"],
115
+ publicationChannel: args["publication-channel"],
116
+ targetRef: args["target-ref"],
117
+ routerRef: args["router-ref"],
118
+ packageVersion: readPackageVersion(cwd),
119
+ });
120
+ if (process.env.GITHUB_OUTPUT) writeOutputs(process.env.GITHUB_OUTPUT, result);
121
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
122
+ }
123
+
124
+ const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
125
+ if (isMain) {
126
+ try {
127
+ main();
128
+ } catch (error) {
129
+ console.error(`promotion-channel-router: ${error.message}`);
130
+ process.exitCode = 1;
131
+ }
132
+ }