@decaf-ts/utils 0.12.1 → 0.12.2

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.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/dist/utils.cjs +1 -1
  3. package/dist/utils.cjs.map +1 -1
  4. package/dist/utils.js +1 -1
  5. package/dist/utils.js.map +1 -1
  6. package/lib/bin/release-chain-dispatch.cjs +19 -0
  7. package/lib/bin/release-chain-dispatch.d.ts +1 -0
  8. package/lib/bin/release-chain-dispatch.js.map +1 -0
  9. package/lib/bin/release-chain.cjs +19 -0
  10. package/lib/bin/release-chain.d.ts +1 -0
  11. package/lib/bin/release-chain.js.map +1 -0
  12. package/lib/cli/commands/index.cjs +1 -0
  13. package/lib/cli/commands/index.d.ts +1 -0
  14. package/lib/cli/commands/index.js.map +1 -1
  15. package/lib/cli/commands/release-chain.cjs +130 -0
  16. package/lib/cli/commands/release-chain.d.ts +62 -0
  17. package/lib/cli/commands/release-chain.js.map +1 -0
  18. package/lib/esm/bin/release-chain-dispatch.d.ts +1 -0
  19. package/lib/esm/bin/release-chain-dispatch.js +16 -0
  20. package/lib/esm/bin/release-chain-dispatch.js.map +1 -0
  21. package/lib/esm/bin/release-chain.d.ts +1 -0
  22. package/lib/esm/bin/release-chain.js +16 -0
  23. package/lib/esm/bin/release-chain.js.map +1 -0
  24. package/lib/esm/cli/commands/index.d.ts +1 -0
  25. package/lib/esm/cli/commands/index.js +1 -0
  26. package/lib/esm/cli/commands/index.js.map +1 -1
  27. package/lib/esm/cli/commands/release-chain.d.ts +62 -0
  28. package/lib/esm/cli/commands/release-chain.js +125 -0
  29. package/lib/esm/cli/commands/release-chain.js.map +1 -0
  30. package/lib/esm/index.d.ts +2 -1
  31. package/lib/esm/index.js +2 -1
  32. package/lib/esm/index.js.map +1 -1
  33. package/lib/esm/release-chain/index.d.ts +43 -0
  34. package/lib/esm/release-chain/index.js +353 -0
  35. package/lib/esm/release-chain/index.js.map +1 -0
  36. package/lib/index.cjs +2 -1
  37. package/lib/index.d.ts +2 -1
  38. package/lib/index.js.map +1 -1
  39. package/lib/release-chain/index.cjs +362 -0
  40. package/lib/release-chain/index.d.ts +43 -0
  41. package/lib/release-chain/index.js.map +1 -0
  42. package/package.json +4 -2
@@ -0,0 +1,16 @@
1
+ /* istanbul ignore file */
2
+ import { ReleaseChainCommand } from "./../cli/commands/index.js";
3
+ new ReleaseChainCommand()
4
+ .execute()
5
+ .then(() => ReleaseChainCommand.log.info("Release chain executed successfully."))
6
+ .catch((error) => {
7
+ const message = error instanceof Error ? error.message : `${error}`;
8
+ try {
9
+ ReleaseChainCommand.log.error(message);
10
+ }
11
+ catch {
12
+ console.error(message);
13
+ }
14
+ process.exit(1);
15
+ });
16
+ //# sourceMappingURL=release-chain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-chain.js","sourceRoot":"","sources":["../../../src/bin/release-chain.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,OAAO,EAAE,mBAAmB,EAAE,mCAAwB;AAEtD,IAAI,mBAAmB,EAAE;KACtB,OAAO,EAAE;KACT,IAAI,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;KAChF,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IACxB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC;IACpE,IAAI,CAAC;QACH,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export * from "./build-scripts";
2
2
  export * from "./tag-release";
3
+ export * from "./release-chain";
@@ -1,3 +1,4 @@
1
1
  export * from "./build-scripts.js";
2
2
  export * from "./tag-release.js";
3
+ export * from "./release-chain.js";
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/cli/commands/index.ts"],"names":[],"mappings":"AAAA,mCAAgC;AAChC,iCAA8B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/cli/commands/index.ts"],"names":[],"mappings":"AAAA,mCAAgC;AAChC,iCAA8B;AAC9B,mCAAgC"}
@@ -0,0 +1,62 @@
1
+ import { Command } from "../command";
2
+ import { CommandOptions } from "../types";
3
+ import { DefaultCommandValues } from "../constants";
4
+ import { LoggingConfig } from "@decaf-ts/logging";
5
+ declare const releaseChainArgs: {
6
+ meta: {
7
+ type: string;
8
+ default: string;
9
+ };
10
+ branch: {
11
+ type: string;
12
+ default: string;
13
+ };
14
+ current: {
15
+ type: string;
16
+ default: string;
17
+ };
18
+ package: {
19
+ type: string;
20
+ default: string;
21
+ };
22
+ token: {
23
+ type: string;
24
+ default: string;
25
+ };
26
+ submoduleFile: {
27
+ type: string;
28
+ default: string;
29
+ };
30
+ submodulePath: {
31
+ type: string;
32
+ default: string;
33
+ };
34
+ workflow: {
35
+ type: string;
36
+ default: string;
37
+ };
38
+ repo: {
39
+ type: string;
40
+ default: string;
41
+ };
42
+ ref: {
43
+ type: string;
44
+ default: string;
45
+ };
46
+ targetBase: {
47
+ type: string;
48
+ default: string;
49
+ };
50
+ };
51
+ type ReleaseChainAnswerMap = {
52
+ [K in keyof typeof releaseChainArgs]: unknown;
53
+ };
54
+ export declare class ReleaseChainCommand extends Command<CommandOptions<typeof releaseChainArgs>, void> {
55
+ constructor();
56
+ protected run(options: LoggingConfig & typeof DefaultCommandValues & ReleaseChainAnswerMap): Promise<void>;
57
+ }
58
+ export declare class ReleaseChainDispatchCommand extends Command<CommandOptions<typeof releaseChainArgs>, void> {
59
+ constructor();
60
+ protected run(options: LoggingConfig & typeof DefaultCommandValues & ReleaseChainAnswerMap): Promise<void>;
61
+ }
62
+ export {};
@@ -0,0 +1,125 @@
1
+ import { Command } from "./../command.js";
2
+ import { DefaultCommandOptions } from "./../constants.js";
3
+ import { dispatchReleaseChainWorkflow, runReleaseChain, } from "./../../release-chain/index.js";
4
+ import { getPackage } from "./../../utils/index.js";
5
+ import { execSync } from "node:child_process";
6
+ const releaseChainArgs = {
7
+ meta: {
8
+ type: "string",
9
+ default: process.env.RELEASE_CHAIN_META_REPO_URL || "",
10
+ },
11
+ branch: {
12
+ type: "string",
13
+ default: process.env.RELEASE_CHAIN_BRANCH ||
14
+ process.env.GITHUB_REF_NAME ||
15
+ "main",
16
+ },
17
+ current: {
18
+ type: "string",
19
+ default: process.env.RELEASE_CHAIN_CURRENT || process.env.GITHUB_REPOSITORY || "",
20
+ },
21
+ package: {
22
+ type: "string",
23
+ default: "",
24
+ },
25
+ token: {
26
+ type: "string",
27
+ default: process.env.RELEASE_CHAIN_TOKEN || "",
28
+ },
29
+ submoduleFile: {
30
+ type: "string",
31
+ default: process.env.RELEASE_CHAIN_FILE || "",
32
+ },
33
+ submodulePath: {
34
+ type: "string",
35
+ default: process.env.RELEASE_CHAIN_FILE_PATH || "",
36
+ },
37
+ workflow: {
38
+ type: "string",
39
+ default: process.env.RELEASE_CHAIN_WORKFLOW || "release-chain.yaml",
40
+ },
41
+ repo: {
42
+ type: "string",
43
+ default: process.env.RELEASE_CHAIN_REPO || "",
44
+ },
45
+ ref: {
46
+ type: "string",
47
+ default: process.env.RELEASE_CHAIN_REF || "",
48
+ },
49
+ targetBase: {
50
+ type: "string",
51
+ default: process.env.RELEASE_CHAIN_TARGET || "",
52
+ },
53
+ };
54
+ export class ReleaseChainCommand extends Command {
55
+ constructor() {
56
+ super("ReleaseChain", Object.assign({}, DefaultCommandOptions, releaseChainArgs));
57
+ }
58
+ async run(options) {
59
+ const answerMap = options;
60
+ const packageName = answerMap.package ||
61
+ getPackage(process.cwd())?.name;
62
+ if (!packageName) {
63
+ throw new Error("Unable to determine package name");
64
+ }
65
+ const metaRepo = answerMap.meta || detectRemoteUrl();
66
+ if (!metaRepo) {
67
+ throw new Error("A meta repository URL is required");
68
+ }
69
+ const branch = answerMap.branch || detectBranch();
70
+ await runReleaseChain({
71
+ metaRepoUrl: metaRepo,
72
+ branch,
73
+ currentRepo: answerMap.current,
74
+ packageName,
75
+ token: answerMap.token,
76
+ submoduleFile: answerMap.submoduleFile || undefined,
77
+ submodulePath: answerMap.submodulePath || undefined,
78
+ targetBaseBranch: answerMap.targetBase || undefined,
79
+ });
80
+ }
81
+ }
82
+ export class ReleaseChainDispatchCommand extends Command {
83
+ constructor() {
84
+ super("ReleaseChainDispatch", Object.assign({}, DefaultCommandOptions, releaseChainArgs));
85
+ }
86
+ async run(options) {
87
+ const answerMap = options;
88
+ const metaRepo = answerMap.meta || detectRemoteUrl();
89
+ if (!metaRepo) {
90
+ throw new Error("A meta repository URL is required");
91
+ }
92
+ const branch = answerMap.branch || detectBranch();
93
+ await dispatchReleaseChainWorkflow({
94
+ metaRepoUrl: metaRepo,
95
+ branch,
96
+ workflowFile: answerMap.workflow || "release-chain.yaml",
97
+ repoSlug: answerMap.repo,
98
+ currentRepo: answerMap.current,
99
+ token: answerMap.token,
100
+ ref: answerMap.ref || branch,
101
+ targetBaseBranch: answerMap.targetBase || undefined,
102
+ });
103
+ }
104
+ }
105
+ function detectRemoteUrl() {
106
+ try {
107
+ return execSync("git config --get remote.origin.url", {
108
+ encoding: "utf8",
109
+ }).trim();
110
+ }
111
+ catch {
112
+ return undefined;
113
+ }
114
+ }
115
+ function detectBranch() {
116
+ try {
117
+ return execSync("git rev-parse --abbrev-ref HEAD", {
118
+ encoding: "utf8",
119
+ }).trim();
120
+ }
121
+ catch {
122
+ return "main";
123
+ }
124
+ }
125
+ //# sourceMappingURL=release-chain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-chain.js","sourceRoot":"","sources":["../../../../src/cli/commands/release-chain.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ export * from "./input";
3
3
  export * from "./output";
4
4
  export * from "./utils";
5
5
  export * from "./writers";
6
+ export * from "./release-chain";
6
7
  /**
7
8
  * @module utils
8
9
  * @description Utilities and building blocks for Decaf-TS CLI and scripting.
@@ -26,7 +27,7 @@ export * from "./writers";
26
27
  * @const VERSION
27
28
  * @memberOf module:utils
28
29
  */
29
- export declare const VERSION = "0.12.0";
30
+ export declare const VERSION = "0.12.1";
30
31
  /**
31
32
  * @description Represents the current version of the module.
32
33
  * @summary Stores the version for the @decaf-ts/utils package. The build replaces
package/lib/esm/index.js CHANGED
@@ -3,6 +3,7 @@ export * from "./input/index.js";
3
3
  export * from "./output/index.js";
4
4
  export * from "./utils/index.js";
5
5
  export * from "./writers/index.js";
6
+ export * from "./release-chain/index.js";
6
7
  /**
7
8
  * @module utils
8
9
  * @description Utilities and building blocks for Decaf-TS CLI and scripting.
@@ -26,7 +27,7 @@ export * from "./writers/index.js";
26
27
  * @const VERSION
27
28
  * @memberOf module:utils
28
29
  */
29
- export const VERSION = "0.12.0";
30
+ export const VERSION = "0.12.1";
30
31
  /**
31
32
  * @description Represents the current version of the module.
32
33
  * @summary Stores the version for the @decaf-ts/utils package. The build replaces
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,+BAAsB;AACtB,iCAAwB;AACxB,kCAAyB;AACzB,iCAAwB;AACxB,mCAA0B;AAE1B;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;AAErC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,+BAAsB;AACtB,iCAAwB;AACxB,kCAAyB;AACzB,iCAAwB;AACxB,mCAA0B;AAC1B,yCAAgC;AAEhC;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;AAErC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC"}
@@ -0,0 +1,43 @@
1
+ export interface ReleaseChainOptions {
2
+ metaRepoUrl: string;
3
+ branch: string;
4
+ currentRepo?: string;
5
+ packageName: string;
6
+ workspace?: string;
7
+ token?: string;
8
+ submoduleFile?: string;
9
+ submodulePath?: string;
10
+ targetBaseBranch?: string;
11
+ }
12
+ export interface ReleaseChainDispatchOptions {
13
+ metaRepoUrl: string;
14
+ branch: string;
15
+ workflowFile?: string;
16
+ repoSlug?: string;
17
+ currentRepo?: string;
18
+ token?: string;
19
+ ref?: string;
20
+ targetBaseBranch?: string;
21
+ }
22
+ export declare class ReleaseChainRunner {
23
+ private readonly options;
24
+ private readonly workspace;
25
+ private readonly clonesRoot;
26
+ private readonly token?;
27
+ private readonly metaRepoSlug;
28
+ private readonly currentRepo?;
29
+ constructor(options: ReleaseChainOptions);
30
+ run(): Promise<void>;
31
+ private evaluateModule;
32
+ private cloneRepository;
33
+ private checkoutTargetBranch;
34
+ private loadSubmodules;
35
+ private runCommand;
36
+ private githubRequest;
37
+ private findExistingPr;
38
+ private acceptPullRequest;
39
+ private createPullRequest;
40
+ private getDefaultBranch;
41
+ }
42
+ export declare function runReleaseChain(options: ReleaseChainOptions): Promise<void>;
43
+ export declare function dispatchReleaseChainWorkflow(options: ReleaseChainDispatchOptions): Promise<void>;
@@ -0,0 +1,353 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { execSync } from "node:child_process";
4
+ import { Logging } from "@decaf-ts/logging";
5
+ const releaseLog = Logging.for("ReleaseChainRunner");
6
+ export class ReleaseChainRunner {
7
+ constructor(options) {
8
+ this.options = options;
9
+ this.workspace = options.workspace ?? process.cwd();
10
+ this.clonesRoot = path.join(this.workspace, ".release-chain");
11
+ this.token =
12
+ options.token ||
13
+ process.env.RELEASE_CHAIN_TOKEN ||
14
+ process.env.GITHUB_TOKEN ||
15
+ process.env.GH_TOKEN;
16
+ this.metaRepoSlug =
17
+ normalizeRepoSlug(options.metaRepoUrl) ||
18
+ throwOnMissing("Unable to normalize META repo URL");
19
+ this.currentRepo =
20
+ normalizeRepoSlug(options.currentRepo) || detectCurrentRepoSlug();
21
+ }
22
+ async run() {
23
+ const modules = await this.loadSubmodules();
24
+ if (!modules.length) {
25
+ releaseLog.warn("No modules detected in provided .gitmodules file");
26
+ return;
27
+ }
28
+ const startIndex = this.currentRepo
29
+ ? modules.findIndex((module) => module.slug === this.currentRepo)
30
+ : -1;
31
+ const queue = startIndex >= 0 ? modules.slice(startIndex + 1) : modules;
32
+ releaseLog.info(`Evaluating ${queue.length} repositories for ${this.options.packageName}`);
33
+ for (const module of queue) {
34
+ await this.evaluateModule(module);
35
+ }
36
+ }
37
+ async evaluateModule(module) {
38
+ if (!module.slug) {
39
+ releaseLog.verbose(`Skipping ${module.name}: unsupported repository URL`);
40
+ return;
41
+ }
42
+ releaseLog.info(`\n[release-chain] ${module.slug}`);
43
+ const repoDir = path.join(this.clonesRoot, module.slug.replace(/\//g, "__"));
44
+ fs.rmSync(repoDir, { recursive: true, force: true });
45
+ fs.mkdirSync(repoDir, { recursive: true });
46
+ this.cloneRepository(module.url, repoDir);
47
+ await this.checkoutTargetBranch(repoDir);
48
+ const pkgPath = path.join(repoDir, "package.json");
49
+ if (!fs.existsSync(pkgPath)) {
50
+ releaseLog.debug(`No package.json detected for ${module.slug}`);
51
+ return;
52
+ }
53
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
54
+ if (!dependsOnPackage(pkg, this.options.packageName)) {
55
+ releaseLog.debug(`${module.slug} does not depend on ${this.options.packageName}`);
56
+ return;
57
+ }
58
+ ensureGitIdentity(repoDir);
59
+ this.runCommand("npm install --ignore-scripts", repoDir);
60
+ this.runCommand(`npm update ${this.options.packageName}`, repoDir);
61
+ this.runCommand("npm run build --if-present", repoDir);
62
+ this.runCommand("npm run test --if-present", repoDir);
63
+ if (!hasChanges(repoDir)) {
64
+ releaseLog.info(`No changes detected for ${module.slug}`);
65
+ return;
66
+ }
67
+ this.runCommand("git add -A", repoDir);
68
+ this.runCommand(`git commit -m "chore: release chain update for ${this.options.packageName}"`, repoDir);
69
+ this.runCommand(`git push origin ${this.options.branch} --force-with-lease`, repoDir);
70
+ const existingPr = await this.findExistingPr(module.slug);
71
+ if (existingPr) {
72
+ releaseLog.info(`Existing PR #${existingPr.number} detected for ${module.slug}, attempting merge`);
73
+ await this.acceptPullRequest(module.slug, existingPr.number, existingPr.head.sha);
74
+ return;
75
+ }
76
+ const baseBranch = this.options.targetBaseBranch ||
77
+ (await this.getDefaultBranch(module.slug));
78
+ await this.createPullRequest(module.slug, baseBranch);
79
+ }
80
+ cloneRepository(url, target) {
81
+ const authUrl = injectAuth(url, this.token);
82
+ this.runCommand(`git clone --no-tags --depth 1 ${authUrl} ${target}`);
83
+ }
84
+ async checkoutTargetBranch(cwd) {
85
+ try {
86
+ this.runCommand(`git fetch origin ${this.options.branch}:${this.options.branch}`, cwd);
87
+ this.runCommand(`git checkout ${this.options.branch}`, cwd);
88
+ this.runCommand(`git reset --hard origin/${this.options.branch}`, cwd);
89
+ return;
90
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
91
+ }
92
+ catch (error) {
93
+ releaseLog.verbose(`Branch ${this.options.branch} not found remotely; creating from default`);
94
+ }
95
+ const defaultBranch = detectDefaultBranch(cwd);
96
+ this.runCommand(`git checkout -b ${this.options.branch} origin/${defaultBranch}`, cwd);
97
+ }
98
+ async loadSubmodules() {
99
+ if (this.options.submodulePath &&
100
+ fs.existsSync(this.options.submodulePath)) {
101
+ const localContent = fs.readFileSync(this.options.submodulePath, "utf8");
102
+ return parseSubmodules(localContent);
103
+ }
104
+ const candidates = [
105
+ this.options.submoduleFile,
106
+ ".gitsubmodule",
107
+ ".gitmodules",
108
+ ].filter(Boolean);
109
+ for (const file of candidates) {
110
+ try {
111
+ const content = await fetchRawFile(this.metaRepoSlug, this.options.branch, file, this.token);
112
+ if (content) {
113
+ return parseSubmodules(content);
114
+ }
115
+ }
116
+ catch (error) {
117
+ if (file === candidates[candidates.length - 1]) {
118
+ throw error;
119
+ }
120
+ }
121
+ }
122
+ return [];
123
+ }
124
+ runCommand(command, cwd = this.workspace) {
125
+ releaseLog.debug(`[${cwd}] ${command}`);
126
+ execSync(command, {
127
+ cwd,
128
+ stdio: "inherit",
129
+ env: Object.assign({}, process.env, {
130
+ RELEASE_CHAIN_TOKEN: this.token,
131
+ }),
132
+ });
133
+ }
134
+ async githubRequest(path, init) {
135
+ const headers = {
136
+ "User-Agent": "decaf-release-chain",
137
+ Accept: "application/vnd.github+json",
138
+ };
139
+ if (this.token) {
140
+ headers.Authorization = `Bearer ${this.token}`;
141
+ }
142
+ const response = await fetch(`https://api.github.com${path}`, {
143
+ method: init?.method || "GET",
144
+ headers: Object.assign({}, headers, init?.headers),
145
+ body: init?.body,
146
+ });
147
+ if (!response.ok) {
148
+ const text = await response.text();
149
+ throw new Error(`GitHub API ${response.status}: ${text}`);
150
+ }
151
+ if (response.status === 204) {
152
+ return undefined;
153
+ }
154
+ return (await response.json());
155
+ }
156
+ async findExistingPr(slug) {
157
+ const [owner] = slug.split("/");
158
+ const list = await this.githubRequest(`/repos/${slug}/pulls?head=${owner}%3A${encodeURIComponent(this.options.branch)}&state=open&per_page=1`);
159
+ return list?.[0];
160
+ }
161
+ async acceptPullRequest(slug, number, sha) {
162
+ await this.githubRequest(`/repos/${slug}/pulls/${number}/merge`, {
163
+ method: "PUT",
164
+ body: JSON.stringify({
165
+ merge_method: "squash",
166
+ commit_title: `chore: accept release chain for ${this.options.packageName}`,
167
+ sha,
168
+ }),
169
+ });
170
+ }
171
+ async createPullRequest(slug, base) {
172
+ await this.githubRequest(`/repos/${slug}/pulls`, {
173
+ method: "POST",
174
+ body: JSON.stringify({
175
+ title: `${this.options.branch}-release-chain`,
176
+ head: this.options.branch,
177
+ base,
178
+ body: `Automated dependency update for ${this.options.packageName}.`,
179
+ }),
180
+ });
181
+ }
182
+ async getDefaultBranch(slug) {
183
+ const repo = await this.githubRequest(`/repos/${slug}`);
184
+ return repo?.default_branch || "main";
185
+ }
186
+ }
187
+ export async function runReleaseChain(options) {
188
+ const runner = new ReleaseChainRunner(options);
189
+ await runner.run();
190
+ }
191
+ export async function dispatchReleaseChainWorkflow(options) {
192
+ const token = options.token ||
193
+ process.env.RELEASE_CHAIN_TRIGGER_TOKEN ||
194
+ process.env.RELEASE_CHAIN_TOKEN ||
195
+ process.env.GITHUB_TOKEN ||
196
+ process.env.GH_TOKEN;
197
+ const repoSlug = options.repoSlug ||
198
+ detectCurrentRepoSlug() ||
199
+ throwOnMissing("Unable to determine repository slug for dispatch");
200
+ const workflow = options.workflowFile || "release-chain.yaml";
201
+ const ref = options.ref || options.branch;
202
+ const currentRepo = normalizeRepoSlug(options.currentRepo) || repoSlug;
203
+ releaseLog.info(`Dispatching ${workflow} on ${repoSlug} for ${options.branch} (current repo ${currentRepo})`);
204
+ const headers = {
205
+ "User-Agent": "decaf-release-chain-dispatch",
206
+ Accept: "application/vnd.github+json",
207
+ "Content-Type": "application/json",
208
+ };
209
+ if (token) {
210
+ headers.Authorization = `Bearer ${token}`;
211
+ }
212
+ const response = await fetch(`https://api.github.com/repos/${repoSlug}/actions/workflows/${workflow}/dispatches`, {
213
+ method: "POST",
214
+ headers,
215
+ body: JSON.stringify({
216
+ ref,
217
+ inputs: {
218
+ meta_repo_url: options.metaRepoUrl,
219
+ branch: options.branch,
220
+ current_repo: currentRepo,
221
+ target_branch: options.targetBaseBranch || "",
222
+ },
223
+ }),
224
+ });
225
+ if (!response.ok) {
226
+ const text = await response.text();
227
+ throw new Error(`Workflow dispatch failed: ${response.status} ${text}`);
228
+ }
229
+ }
230
+ function parseSubmodules(content) {
231
+ return content
232
+ .split("[submodule")
233
+ .slice(1)
234
+ .map((block) => {
235
+ const nameMatch = block.match(/"(.+?)"\]/);
236
+ const pathMatch = block.match(/path\s*=\s*(.+)/);
237
+ const urlMatch = block.match(/url\s*=\s*(.+)/);
238
+ const url = urlMatch?.[1]?.trim() || "";
239
+ return {
240
+ name: nameMatch?.[1]?.trim() || "",
241
+ path: pathMatch?.[1]?.trim() || "",
242
+ url,
243
+ slug: normalizeRepoSlug(url),
244
+ };
245
+ })
246
+ .filter((entry) => entry.url);
247
+ }
248
+ function normalizeRepoSlug(value) {
249
+ if (!value)
250
+ return undefined;
251
+ if (value.includes("github.com")) {
252
+ const normalized = toHttpsUrl(value);
253
+ const parsed = new URL(normalized);
254
+ return parsed.pathname.replace(/^\//, "").replace(/\.git$/, "");
255
+ }
256
+ return value.replace(/^\//, "");
257
+ }
258
+ function toHttpsUrl(url) {
259
+ if (url.startsWith("git@github.com:")) {
260
+ return `https://github.com/${url.replace("git@github.com:", "")}`;
261
+ }
262
+ if (url.startsWith("ssh://git@github.com/")) {
263
+ return `https://github.com/${url.replace("ssh://git@github.com/", "")}`;
264
+ }
265
+ return url.replace(/\.git$/, "");
266
+ }
267
+ function injectAuth(url, token) {
268
+ const httpsUrl = toHttpsUrl(url);
269
+ if (!token) {
270
+ return httpsUrl;
271
+ }
272
+ const parsed = new URL(httpsUrl);
273
+ parsed.username = "x-access-token";
274
+ parsed.password = token;
275
+ return parsed.toString();
276
+ }
277
+ async function fetchRawFile(slug, branch, file, token) {
278
+ const url = `https://raw.githubusercontent.com/${slug}/${branch}/${file}`;
279
+ const headers = {
280
+ "User-Agent": "decaf-release-chain",
281
+ };
282
+ if (token)
283
+ headers.Authorization = `Bearer ${token}`;
284
+ const response = await fetch(url, { headers });
285
+ if (response.status === 404) {
286
+ return null;
287
+ }
288
+ if (!response.ok) {
289
+ const text = await response.text();
290
+ throw new Error(`Failed to download ${file}: ${response.status} ${text}`);
291
+ }
292
+ return response.text();
293
+ }
294
+ function detectCurrentRepoSlug() {
295
+ if (process.env.GITHUB_REPOSITORY) {
296
+ return process.env.GITHUB_REPOSITORY;
297
+ }
298
+ try {
299
+ const remote = execSync("git config --get remote.origin.url", {
300
+ encoding: "utf8",
301
+ })
302
+ .trim()
303
+ .replace(/\s+/g, "");
304
+ return normalizeRepoSlug(remote);
305
+ }
306
+ catch {
307
+ return undefined;
308
+ }
309
+ }
310
+ function detectDefaultBranch(cwd) {
311
+ try {
312
+ const ref = execSync("git symbolic-ref --short refs/remotes/origin/HEAD", {
313
+ cwd,
314
+ encoding: "utf8",
315
+ }).trim();
316
+ return ref.split("/").pop() || "main";
317
+ }
318
+ catch {
319
+ return "main";
320
+ }
321
+ }
322
+ function hasChanges(cwd) {
323
+ const status = execSync("git status --porcelain", {
324
+ cwd,
325
+ encoding: "utf8",
326
+ }).trim();
327
+ return Boolean(status);
328
+ }
329
+ function ensureGitIdentity(cwd) {
330
+ try {
331
+ execSync("git config user.name", { cwd, stdio: "pipe" });
332
+ execSync("git config user.email", { cwd, stdio: "pipe" });
333
+ }
334
+ catch {
335
+ execSync("git config user.name 'release-chain'", { cwd });
336
+ execSync("git config user.email 'release-chain@users.noreply.github.com'", {
337
+ cwd,
338
+ });
339
+ }
340
+ }
341
+ function dependsOnPackage(pkg, packageName) {
342
+ const sections = [
343
+ "dependencies",
344
+ "devDependencies",
345
+ "peerDependencies",
346
+ "optionalDependencies",
347
+ ];
348
+ return sections.some((section) => Boolean(pkg?.[section]?.[packageName]));
349
+ }
350
+ function throwOnMissing(message) {
351
+ throw new Error(message);
352
+ }
353
+ //# sourceMappingURL=index.js.map