@aspruyt/xfg 4.0.0 → 4.0.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.
- package/README.md +1 -2
- package/dist/cli/index.d.ts +1 -2
- package/dist/cli/index.js +0 -1
- package/dist/cli/program.js +7 -2
- package/dist/cli/{settings/results-collector.d.ts → results-collector.d.ts} +1 -1
- package/dist/cli/{settings/results-collector.js → results-collector.js} +2 -1
- package/dist/cli/settings-report-builder.d.ts +1 -3
- package/dist/cli/sync-command.d.ts +2 -24
- package/dist/cli/sync-command.js +295 -301
- package/dist/cli/types.d.ts +60 -40
- package/dist/cli/types.js +1 -12
- package/dist/config/errors.d.ts +9 -0
- package/dist/config/errors.js +11 -0
- package/dist/config/file-reference-resolver.d.ts +2 -1
- package/dist/config/file-reference-resolver.js +10 -8
- package/dist/config/formatter.d.ts +3 -2
- package/dist/config/index.d.ts +4 -6
- package/dist/config/index.js +4 -8
- package/dist/config/loader.js +4 -2
- package/dist/config/merge.d.ts +0 -9
- package/dist/config/merge.js +2 -7
- package/dist/config/normalizer.d.ts +4 -0
- package/dist/config/normalizer.js +61 -110
- package/dist/config/types.d.ts +15 -19
- package/dist/config/types.js +1 -1
- package/dist/config/validator.d.ts +0 -4
- package/dist/config/validator.js +286 -363
- package/dist/config/validators/file-validator.d.ts +2 -8
- package/dist/config/validators/file-validator.js +6 -17
- package/dist/config/validators/index.d.ts +3 -3
- package/dist/config/validators/index.js +3 -3
- package/dist/config/validators/repo-settings-validator.d.ts +0 -6
- package/dist/config/validators/repo-settings-validator.js +9 -9
- package/dist/config/validators/ruleset-validator.d.ts +0 -14
- package/dist/config/validators/ruleset-validator.js +28 -28
- package/dist/lifecycle/ado-migration-source.js +2 -1
- package/dist/lifecycle/github-lifecycle-provider.d.ts +6 -5
- package/dist/lifecycle/github-lifecycle-provider.js +79 -90
- package/dist/lifecycle/index.d.ts +2 -6
- package/dist/lifecycle/index.js +0 -4
- package/dist/lifecycle/lifecycle-formatter.d.ts +2 -1
- package/dist/lifecycle/lifecycle-formatter.js +4 -0
- package/dist/lifecycle/lifecycle-helpers.d.ts +3 -2
- package/dist/lifecycle/repo-lifecycle-manager.js +4 -11
- package/dist/lifecycle/types.d.ts +0 -8
- package/dist/output/github-summary.d.ts +5 -0
- package/dist/output/github-summary.js +9 -2
- package/dist/output/index.d.ts +2 -2
- package/dist/output/index.js +1 -1
- package/dist/output/lifecycle-report.js +5 -23
- package/dist/output/settings-report.d.ts +14 -3
- package/dist/output/settings-report.js +137 -197
- package/dist/output/summary-utils.d.ts +1 -1
- package/dist/output/summary-utils.js +2 -1
- package/dist/output/sync-report.js +5 -8
- package/dist/output/unified-summary.d.ts +2 -1
- package/dist/output/unified-summary.js +71 -133
- package/dist/settings/base-processor.d.ts +67 -0
- package/dist/settings/base-processor.js +91 -0
- package/dist/settings/index.d.ts +4 -3
- package/dist/settings/index.js +3 -3
- package/dist/settings/labels/converter.d.ts +2 -1
- package/dist/settings/labels/github-labels-strategy.d.ts +9 -18
- package/dist/settings/labels/github-labels-strategy.js +17 -73
- package/dist/settings/labels/index.d.ts +2 -6
- package/dist/settings/labels/index.js +1 -9
- package/dist/settings/labels/processor.d.ts +6 -30
- package/dist/settings/labels/processor.js +62 -152
- package/dist/settings/labels/types.d.ts +5 -8
- package/dist/settings/repo-settings/formatter.d.ts +2 -2
- package/dist/settings/repo-settings/formatter.js +6 -6
- package/dist/settings/repo-settings/github-repo-settings-strategy.d.ts +11 -12
- package/dist/settings/repo-settings/github-repo-settings-strategy.js +32 -79
- package/dist/settings/repo-settings/index.d.ts +2 -5
- package/dist/settings/repo-settings/index.js +1 -9
- package/dist/settings/repo-settings/processor.d.ts +6 -27
- package/dist/settings/repo-settings/processor.js +51 -104
- package/dist/settings/repo-settings/types.d.ts +7 -9
- package/dist/settings/rulesets/diff-algorithm.d.ts +0 -4
- package/dist/settings/rulesets/diff-algorithm.js +1 -10
- package/dist/settings/rulesets/diff.d.ts +1 -1
- package/dist/settings/rulesets/diff.js +2 -21
- package/dist/settings/rulesets/formatter.d.ts +1 -3
- package/dist/settings/rulesets/formatter.js +1 -8
- package/dist/settings/rulesets/github-ruleset-strategy.d.ts +11 -51
- package/dist/settings/rulesets/github-ruleset-strategy.js +24 -85
- package/dist/settings/rulesets/index.d.ts +3 -6
- package/dist/settings/rulesets/index.js +5 -9
- package/dist/settings/rulesets/processor.d.ts +8 -33
- package/dist/settings/rulesets/processor.js +58 -151
- package/dist/settings/rulesets/types.d.ts +35 -6
- package/dist/shared/command-executor.d.ts +2 -22
- package/dist/shared/command-executor.js +8 -7
- package/dist/shared/env.d.ts +0 -8
- package/dist/shared/env.js +14 -70
- package/dist/shared/file-status.d.ts +2 -0
- package/dist/shared/file-status.js +13 -0
- package/dist/shared/gh-api-utils.d.ts +46 -0
- package/dist/shared/gh-api-utils.js +107 -0
- package/dist/shared/index.d.ts +5 -5
- package/dist/shared/index.js +3 -3
- package/dist/shared/interpolation-engine.d.ts +31 -0
- package/dist/shared/interpolation-engine.js +50 -0
- package/dist/shared/logger.d.ts +3 -7
- package/dist/shared/logger.js +4 -1
- package/dist/shared/repo-detector.d.ts +17 -2
- package/dist/shared/repo-detector.js +27 -0
- package/dist/shared/retry-utils.d.ts +9 -17
- package/dist/shared/retry-utils.js +22 -28
- package/dist/shared/sanitize-utils.d.ts +0 -7
- package/dist/shared/sanitize-utils.js +0 -7
- package/dist/shared/shell-utils.d.ts +1 -0
- package/dist/shared/shell-utils.js +3 -0
- package/dist/shared/string-utils.d.ts +4 -0
- package/dist/shared/string-utils.js +6 -0
- package/dist/shared/type-guards.d.ts +17 -0
- package/dist/shared/type-guards.js +26 -0
- package/dist/shared/workspace-utils.d.ts +0 -4
- package/dist/shared/workspace-utils.js +0 -4
- package/dist/{sync → shared}/xfg-template.d.ts +3 -2
- package/dist/{sync → shared}/xfg-template.js +13 -54
- package/dist/sync/auth-options-builder.d.ts +4 -5
- package/dist/sync/auth-options-builder.js +15 -26
- package/dist/sync/branch-manager.d.ts +5 -0
- package/dist/sync/branch-manager.js +12 -10
- package/dist/sync/commit-push-manager.d.ts +1 -1
- package/dist/sync/commit-push-manager.js +22 -18
- package/dist/sync/diff-utils.d.ts +4 -9
- package/dist/sync/diff-utils.js +2 -19
- package/dist/sync/file-sync-orchestrator.js +9 -8
- package/dist/sync/file-writer.d.ts +2 -1
- package/dist/sync/file-writer.js +3 -6
- package/dist/sync/index.d.ts +2 -15
- package/dist/sync/index.js +0 -19
- package/dist/sync/manifest-manager.d.ts +4 -0
- package/dist/sync/manifest-manager.js +5 -1
- package/dist/sync/manifest.d.ts +10 -41
- package/dist/sync/manifest.js +11 -56
- package/dist/sync/pr-merge-handler.d.ts +2 -6
- package/dist/sync/pr-merge-handler.js +6 -3
- package/dist/sync/repository-processor.d.ts +1 -2
- package/dist/sync/repository-processor.js +20 -12
- package/dist/sync/repository-session.js +5 -14
- package/dist/sync/sync-workflow.js +31 -38
- package/dist/sync/types.d.ts +43 -178
- package/dist/vcs/authenticated-git-ops.d.ts +27 -70
- package/dist/vcs/authenticated-git-ops.js +70 -96
- package/dist/vcs/azure-pr-strategy.d.ts +6 -4
- package/dist/vcs/azure-pr-strategy.js +34 -82
- package/dist/vcs/branch-utils.d.ts +6 -0
- package/dist/vcs/branch-utils.js +29 -0
- package/dist/vcs/commit-strategy-selector.d.ts +5 -0
- package/dist/vcs/commit-strategy-selector.js +10 -0
- package/dist/vcs/git-commit-strategy.js +1 -2
- package/dist/vcs/git-ops.d.ts +15 -59
- package/dist/vcs/git-ops.js +46 -110
- package/dist/vcs/github-app-token-manager.d.ts +0 -6
- package/dist/vcs/github-app-token-manager.js +5 -12
- package/dist/vcs/github-pr-strategy.d.ts +5 -5
- package/dist/vcs/github-pr-strategy.js +44 -122
- package/dist/vcs/gitlab-pr-strategy.d.ts +6 -4
- package/dist/vcs/gitlab-pr-strategy.js +39 -87
- package/dist/vcs/graphql-commit-strategy.d.ts +3 -4
- package/dist/vcs/graphql-commit-strategy.js +31 -63
- package/dist/vcs/index.d.ts +3 -16
- package/dist/vcs/index.js +2 -33
- package/dist/vcs/pr-creator.d.ts +9 -9
- package/dist/vcs/pr-creator.js +11 -10
- package/dist/vcs/pr-strategy-factory.d.ts +5 -0
- package/dist/vcs/pr-strategy-factory.js +17 -0
- package/dist/vcs/pr-strategy.d.ts +13 -26
- package/dist/vcs/pr-strategy.js +20 -25
- package/dist/vcs/types.d.ts +87 -21
- package/package.json +2 -1
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { existsSync, writeFileSync, unlinkSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { escapeShellArg } from "../shared/shell-utils.js";
|
|
4
|
-
import {
|
|
5
|
-
import { BasePRStrategy
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { assertGitLabRepo } from "../shared/repo-detector.js";
|
|
5
|
+
import { BasePRStrategy } from "./pr-strategy.js";
|
|
6
|
+
import { withRetry } from "../shared/retry-utils.js";
|
|
7
|
+
import { getStderr } from "../shared/command-executor.js";
|
|
8
|
+
import { parseApiJson } from "../shared/gh-api-utils.js";
|
|
8
9
|
import { sanitizeCredentials } from "../shared/sanitize-utils.js";
|
|
10
|
+
import { toErrorMessage, safeCleanup } from "../shared/type-guards.js";
|
|
9
11
|
export class GitLabPRStrategy extends BasePRStrategy {
|
|
10
|
-
constructor(executor) {
|
|
11
|
-
super(executor);
|
|
12
|
+
constructor(executor, log) {
|
|
13
|
+
super(executor, log);
|
|
12
14
|
this.bodyFilePath = ".mr-description.md";
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
@@ -62,9 +64,7 @@ export class GitLabPRStrategy extends BasePRStrategy {
|
|
|
62
64
|
}
|
|
63
65
|
async checkExistingPR(options) {
|
|
64
66
|
const { repoInfo, branchName, workDir, retries = 3 } = options;
|
|
65
|
-
|
|
66
|
-
throw new Error("Expected GitLab repository");
|
|
67
|
-
}
|
|
67
|
+
assertGitLabRepo(repoInfo, "GitLab PR strategy");
|
|
68
68
|
const repoFlag = this.getRepoFlag(repoInfo);
|
|
69
69
|
// Use glab mr list with JSON output for reliable parsing
|
|
70
70
|
// Note: glab mr list returns open MRs by default (use -c for closed, -M for merged)
|
|
@@ -74,33 +74,23 @@ export class GitLabPRStrategy extends BasePRStrategy {
|
|
|
74
74
|
if (!result || result.trim() === "" || result.trim() === "[]") {
|
|
75
75
|
return null;
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
const mrs = JSON.parse(result);
|
|
77
|
+
const mrs = parseApiJson(result, "glab mr list");
|
|
79
78
|
if (Array.isArray(mrs) && mrs.length > 0 && mrs[0].iid) {
|
|
80
79
|
return this.buildMRUrl(repoInfo, String(mrs[0].iid));
|
|
81
80
|
}
|
|
82
81
|
return null;
|
|
83
82
|
}
|
|
84
83
|
catch (error) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
90
|
-
// Log unexpected errors for debugging
|
|
91
|
-
const stderr = error.stderr ?? "";
|
|
92
|
-
if (stderr && !stderr.includes("no merge requests")) {
|
|
93
|
-
logger.info(`Debug: GitLab MR check failed - ${sanitizeCredentials(stderr).trim()}`);
|
|
94
|
-
}
|
|
84
|
+
const stderr = getStderr(error);
|
|
85
|
+
if (stderr && !stderr.includes("no merge requests")) {
|
|
86
|
+
this.log?.debug(`GitLab MR check failed - ${sanitizeCredentials(stderr).trim()}`);
|
|
95
87
|
}
|
|
96
88
|
return null;
|
|
97
89
|
}
|
|
98
90
|
}
|
|
99
91
|
async closeExistingPR(options) {
|
|
100
92
|
const { repoInfo, branchName, baseBranch, workDir, retries = 3 } = options;
|
|
101
|
-
|
|
102
|
-
throw new Error("Expected GitLab repository");
|
|
103
|
-
}
|
|
93
|
+
assertGitLabRepo(repoInfo, "GitLab PR strategy");
|
|
104
94
|
// First check if there's an existing MR
|
|
105
95
|
const existingUrl = await this.checkExistingPR({
|
|
106
96
|
repoInfo,
|
|
@@ -108,8 +98,6 @@ export class GitLabPRStrategy extends BasePRStrategy {
|
|
|
108
98
|
baseBranch,
|
|
109
99
|
workDir,
|
|
110
100
|
retries,
|
|
111
|
-
title: "", // Not used for check
|
|
112
|
-
body: "", // Not used for check
|
|
113
101
|
});
|
|
114
102
|
if (!existingUrl) {
|
|
115
103
|
return false;
|
|
@@ -117,7 +105,8 @@ export class GitLabPRStrategy extends BasePRStrategy {
|
|
|
117
105
|
// Extract MR IID from URL
|
|
118
106
|
const mrInfo = this.parseMRUrl(existingUrl);
|
|
119
107
|
if (!mrInfo) {
|
|
120
|
-
|
|
108
|
+
this.log?.warn(`Could not extract MR IID from URL: ${existingUrl}`);
|
|
109
|
+
return false;
|
|
121
110
|
}
|
|
122
111
|
const repoFlag = this.getRepoFlag(repoInfo);
|
|
123
112
|
// Close the MR
|
|
@@ -128,11 +117,10 @@ export class GitLabPRStrategy extends BasePRStrategy {
|
|
|
128
117
|
});
|
|
129
118
|
}
|
|
130
119
|
catch (error) {
|
|
131
|
-
const message =
|
|
132
|
-
|
|
120
|
+
const message = toErrorMessage(error);
|
|
121
|
+
this.log?.warn(`Failed to close existing MR !${mrInfo.mrIid}: ${message}`);
|
|
133
122
|
return false;
|
|
134
123
|
}
|
|
135
|
-
// Delete the source branch via git
|
|
136
124
|
const deleteBranchCommand = `git push origin --delete ${escapeShellArg(branchName)}`;
|
|
137
125
|
try {
|
|
138
126
|
await withRetry(() => this.executor.exec(deleteBranchCommand, workDir), {
|
|
@@ -141,18 +129,15 @@ export class GitLabPRStrategy extends BasePRStrategy {
|
|
|
141
129
|
}
|
|
142
130
|
catch (error) {
|
|
143
131
|
// Branch deletion failure is not critical
|
|
144
|
-
const message =
|
|
145
|
-
|
|
132
|
+
const message = toErrorMessage(error);
|
|
133
|
+
this.log?.warn(`Failed to delete branch ${branchName}: ${message}`);
|
|
146
134
|
}
|
|
147
135
|
return true;
|
|
148
136
|
}
|
|
149
137
|
async create(options) {
|
|
150
138
|
const { repoInfo, title, body, branchName, baseBranch, workDir, retries = 3, } = options;
|
|
151
|
-
|
|
152
|
-
throw new Error("Expected GitLab repository");
|
|
153
|
-
}
|
|
139
|
+
assertGitLabRepo(repoInfo, "GitLab PR strategy");
|
|
154
140
|
const repoFlag = this.getRepoFlag(repoInfo);
|
|
155
|
-
// Write description to temp file to avoid shell escaping issues
|
|
156
141
|
const descFile = join(workDir, this.bodyFilePath);
|
|
157
142
|
writeFileSync(descFile, body, "utf-8");
|
|
158
143
|
// glab mr create with description from file
|
|
@@ -181,15 +166,10 @@ export class GitLabPRStrategy extends BasePRStrategy {
|
|
|
181
166
|
throw new Error(`Could not parse MR URL from output: ${result}`);
|
|
182
167
|
}
|
|
183
168
|
finally {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (existsSync(descFile)) {
|
|
169
|
+
safeCleanup(() => {
|
|
170
|
+
if (existsSync(descFile))
|
|
187
171
|
unlinkSync(descFile);
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
catch (cleanupError) {
|
|
191
|
-
logger.info(`Warning: Failed to clean up temp file ${descFile}: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`);
|
|
192
|
-
}
|
|
172
|
+
}, `failed to remove ${descFile}`, this.log ?? { debug() { } });
|
|
193
173
|
}
|
|
194
174
|
}
|
|
195
175
|
async merge(options) {
|
|
@@ -218,56 +198,28 @@ export class GitLabPRStrategy extends BasePRStrategy {
|
|
|
218
198
|
: "";
|
|
219
199
|
if (config.mode === "auto") {
|
|
220
200
|
// Enable auto-merge when pipeline succeeds
|
|
221
|
-
|
|
222
|
-
const flagParts = [
|
|
201
|
+
const autoFlagParts = [
|
|
223
202
|
"--when-pipeline-succeeds",
|
|
224
203
|
strategyFlag,
|
|
225
204
|
deleteBranchFlag,
|
|
226
205
|
].filter(Boolean);
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
message: "Auto-merge enabled. MR will merge when pipeline succeeds.",
|
|
235
|
-
merged: false,
|
|
236
|
-
autoMergeEnabled: true,
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
catch (error) {
|
|
240
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
241
|
-
return {
|
|
242
|
-
success: false,
|
|
243
|
-
message: `Failed to enable auto-merge: ${message}`,
|
|
244
|
-
merged: false,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
206
|
+
const autoCommand = `glab mr merge ${escapeShellArg(mrInfo.mrIid)} ${autoFlagParts.join(" ")} -R ${escapeShellArg(repoFlag)} -y`;
|
|
207
|
+
return this.executeMergeCommand(() => this.executor.exec(autoCommand.trim(), workDir), retries, {
|
|
208
|
+
success: true,
|
|
209
|
+
message: "Auto-merge enabled. MR will merge when pipeline succeeds.",
|
|
210
|
+
merged: false,
|
|
211
|
+
autoMergeEnabled: true,
|
|
212
|
+
}, "Failed to enable auto-merge");
|
|
247
213
|
}
|
|
248
214
|
if (config.mode === "force") {
|
|
249
215
|
// Force merge immediately
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
return {
|
|
258
|
-
success: true,
|
|
259
|
-
message: "MR merged successfully.",
|
|
260
|
-
merged: true,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
catch (error) {
|
|
264
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
265
|
-
return {
|
|
266
|
-
success: false,
|
|
267
|
-
message: `Failed to force merge: ${message}`,
|
|
268
|
-
merged: false,
|
|
269
|
-
};
|
|
270
|
-
}
|
|
216
|
+
const forceFlagParts = [strategyFlag, deleteBranchFlag].filter(Boolean);
|
|
217
|
+
const forceCommand = `glab mr merge ${escapeShellArg(mrInfo.mrIid)} ${forceFlagParts.join(" ")} -R ${escapeShellArg(repoFlag)} -y`;
|
|
218
|
+
return this.executeMergeCommand(() => this.executor.exec(forceCommand.trim(), workDir), retries, {
|
|
219
|
+
success: true,
|
|
220
|
+
message: "MR merged successfully.",
|
|
221
|
+
merged: true,
|
|
222
|
+
}, "Failed to force merge");
|
|
271
223
|
}
|
|
272
224
|
return {
|
|
273
225
|
success: false,
|
|
@@ -21,7 +21,7 @@ export declare const SAFE_BRANCH_NAME_PATTERN: RegExp;
|
|
|
21
21
|
* Validates that a branch name is safe for use in shell commands.
|
|
22
22
|
* Throws an error if the branch name contains potentially dangerous characters.
|
|
23
23
|
*/
|
|
24
|
-
export declare function
|
|
24
|
+
export declare function validateSafeBranchName(branchName: string): void;
|
|
25
25
|
/**
|
|
26
26
|
* GraphQL-based commit strategy using GitHub's createCommitOnBranch mutation.
|
|
27
27
|
* Used with GitHub App authentication. Commits via this strategy ARE verified
|
|
@@ -32,9 +32,8 @@ export declare function validateBranchName(branchName: string): void;
|
|
|
32
32
|
export declare class GraphQLCommitStrategy implements ICommitStrategy {
|
|
33
33
|
/**
|
|
34
34
|
* GraphQL permanent error patterns for ref operations.
|
|
35
|
-
*
|
|
36
|
-
* git-CLI
|
|
37
|
-
* apply to GraphQL responses.
|
|
35
|
+
* Extends CORE_PERMANENT_ERROR_PATTERNS with GraphQL-specific patterns
|
|
36
|
+
* (omits git-CLI patterns like /remote\s*rejected/i).
|
|
38
37
|
*/
|
|
39
38
|
private static readonly GRAPHQL_PERMANENT_ERROR_PATTERNS;
|
|
40
39
|
private executor;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { defaultExecutor, } from "../shared/command-executor.js";
|
|
2
2
|
import { isGitHubRepo } from "../shared/repo-detector.js";
|
|
3
3
|
import { escapeShellArg } from "../shared/shell-utils.js";
|
|
4
|
-
import { withRetry, DEFAULT_PERMANENT_ERROR_PATTERNS, } from "../shared/retry-utils.js";
|
|
4
|
+
import { withRetry, CORE_PERMANENT_ERROR_PATTERNS, DEFAULT_PERMANENT_ERROR_PATTERNS, } from "../shared/retry-utils.js";
|
|
5
|
+
import { toErrorMessage } from "../shared/type-guards.js";
|
|
6
|
+
import { parseApiJson } from "../shared/gh-api-utils.js";
|
|
5
7
|
/**
|
|
6
8
|
* Maximum payload size for GitHub GraphQL API (50MB).
|
|
7
9
|
* Base64 encoding adds ~33% overhead, so raw content should be checked.
|
|
@@ -23,7 +25,7 @@ export const SAFE_BRANCH_NAME_PATTERN = /^[a-zA-Z0-9][-a-zA-Z0-9_./]*$/;
|
|
|
23
25
|
* Validates that a branch name is safe for use in shell commands.
|
|
24
26
|
* Throws an error if the branch name contains potentially dangerous characters.
|
|
25
27
|
*/
|
|
26
|
-
export function
|
|
28
|
+
export function validateSafeBranchName(branchName) {
|
|
27
29
|
if (!SAFE_BRANCH_NAME_PATTERN.test(branchName)) {
|
|
28
30
|
throw new Error(`Invalid branch name for GraphQL commit strategy: "${branchName}". ` +
|
|
29
31
|
`Branch names must start with alphanumeric and contain only ` +
|
|
@@ -50,20 +52,11 @@ const OID_MISMATCH_PATTERNS = [
|
|
|
50
52
|
export class GraphQLCommitStrategy {
|
|
51
53
|
/**
|
|
52
54
|
* GraphQL permanent error patterns for ref operations.
|
|
53
|
-
*
|
|
54
|
-
* git-CLI
|
|
55
|
-
* apply to GraphQL responses.
|
|
55
|
+
* Extends CORE_PERMANENT_ERROR_PATTERNS with GraphQL-specific patterns
|
|
56
|
+
* (omits git-CLI patterns like /remote\s*rejected/i).
|
|
56
57
|
*/
|
|
57
58
|
static GRAPHQL_PERMANENT_ERROR_PATTERNS = [
|
|
58
|
-
|
|
59
|
-
/unauthorized/i,
|
|
60
|
-
/permission\s*denied/i,
|
|
61
|
-
/not\s*accessible\s*by\s*integration/i,
|
|
62
|
-
/bad\s*credentials/i,
|
|
63
|
-
/invalid\s*(token|credentials)/i,
|
|
64
|
-
/401\b/,
|
|
65
|
-
/403\b/,
|
|
66
|
-
/does\s*not\s*exist/i,
|
|
59
|
+
...CORE_PERMANENT_ERROR_PATTERNS,
|
|
67
60
|
/could\s*not\s*resolve/i,
|
|
68
61
|
/already\s*exists/i,
|
|
69
62
|
];
|
|
@@ -80,17 +73,14 @@ export class GraphQLCommitStrategy {
|
|
|
80
73
|
*/
|
|
81
74
|
async commit(options) {
|
|
82
75
|
const { repoInfo, branchName, message, fileChanges, workDir, retries = 3, token, } = options;
|
|
83
|
-
// Validate this is a GitHub repo
|
|
84
76
|
if (!isGitHubRepo(repoInfo)) {
|
|
85
77
|
throw new Error(`GraphQL commit strategy requires GitHub repositories. Got: ${repoInfo.type}`);
|
|
86
78
|
}
|
|
87
|
-
|
|
88
|
-
validateBranchName(branchName);
|
|
79
|
+
validateSafeBranchName(branchName);
|
|
89
80
|
const githubInfo = repoInfo;
|
|
90
|
-
// Separate additions from deletions
|
|
91
81
|
const additions = fileChanges.filter((fc) => fc.content !== null);
|
|
92
82
|
const deletions = fileChanges.filter((fc) => fc.content === null);
|
|
93
|
-
//
|
|
83
|
+
// Base64 encoding adds ~33% overhead to raw content size
|
|
94
84
|
const totalSize = additions.reduce((sum, fc) => {
|
|
95
85
|
const base64Size = Math.ceil((fc.content.length * 4) / 3);
|
|
96
86
|
return sum + base64Size;
|
|
@@ -99,18 +89,15 @@ export class GraphQLCommitStrategy {
|
|
|
99
89
|
throw new Error(`GraphQL payload exceeds 50 MB limit (${Math.round(totalSize / (1024 * 1024))} MB). ` +
|
|
100
90
|
`Consider using smaller files or the git commit strategy.`);
|
|
101
91
|
}
|
|
102
|
-
// Get gitOps for authenticated network operations
|
|
103
92
|
const gitOps = options.gitOps;
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
// For PR branches (force=true), we force-update to ensure fresh start from main
|
|
93
|
+
// createCommitOnBranch requires the branch to already exist on remote.
|
|
94
|
+
// For PR branches (force=true), force-update ensures a fresh start from main.
|
|
107
95
|
await this.ensureBranchExistsOnRemote(branchName, workDir, options.force, githubInfo, token);
|
|
108
|
-
//
|
|
96
|
+
// Outer retry loop for expectedHeadOid mismatch — each iteration re-fetches
|
|
97
|
+
// the remote HEAD so the next mutation uses a fresh OID.
|
|
109
98
|
let lastError = null;
|
|
110
99
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
111
100
|
try {
|
|
112
|
-
// Fetch from remote to ensure we have the latest HEAD
|
|
113
|
-
// This is critical for expectedHeadOid to match
|
|
114
101
|
const safeBranch = escapeShellArg(branchName);
|
|
115
102
|
if (gitOps) {
|
|
116
103
|
await gitOps.fetchBranch(branchName);
|
|
@@ -120,22 +107,18 @@ export class GraphQLCommitStrategy {
|
|
|
120
107
|
}
|
|
121
108
|
// Get the remote HEAD SHA for this branch (not local HEAD)
|
|
122
109
|
const headSha = await this.executor.exec(`git rev-parse origin/${safeBranch}`, workDir);
|
|
123
|
-
// Build and execute the GraphQL mutation
|
|
124
110
|
const result = await this.executeGraphQLMutation(githubInfo, branchName, message, headSha.trim(), additions, deletions, workDir, token);
|
|
125
111
|
return result;
|
|
126
112
|
}
|
|
127
113
|
catch (error) {
|
|
128
|
-
lastError =
|
|
129
|
-
|
|
114
|
+
lastError =
|
|
115
|
+
error instanceof Error ? error : new Error(toErrorMessage(error));
|
|
130
116
|
if (this.isHeadOidMismatchError(lastError) && attempt < retries) {
|
|
131
|
-
// Retry - the next iteration will fetch and get fresh HEAD SHA
|
|
132
117
|
continue;
|
|
133
118
|
}
|
|
134
|
-
// For other errors, throw immediately
|
|
135
119
|
throw lastError;
|
|
136
120
|
}
|
|
137
121
|
}
|
|
138
|
-
// Should not reach here, but just in case
|
|
139
122
|
throw lastError ?? new Error("Unexpected error in GraphQL commit");
|
|
140
123
|
}
|
|
141
124
|
/**
|
|
@@ -143,19 +126,14 @@ export class GraphQLCommitStrategy {
|
|
|
143
126
|
*/
|
|
144
127
|
async executeGraphQLMutation(repoInfo, branchName, message, expectedHeadOid, additions, deletions, workDir, token) {
|
|
145
128
|
const repositoryNameWithOwner = `${repoInfo.owner}/${repoInfo.repo}`;
|
|
146
|
-
// Build file additions with base64 encoding
|
|
147
129
|
const fileAdditions = additions.map((fc) => ({
|
|
148
130
|
path: fc.path,
|
|
149
131
|
contents: Buffer.from(fc.content).toString("base64"),
|
|
150
132
|
}));
|
|
151
|
-
// Build file deletions (path only)
|
|
152
133
|
const fileDeletions = deletions.map((fc) => ({
|
|
153
134
|
path: fc.path,
|
|
154
135
|
}));
|
|
155
|
-
// Build the mutation (minified to avoid shell escaping issues with newlines)
|
|
156
136
|
const mutation = "mutation CreateCommit($input: CreateCommitOnBranchInput!) { createCommitOnBranch(input: $input) { commit { oid } } }";
|
|
157
|
-
// Build the input variables
|
|
158
|
-
// Note: GitHub API doesn't accept empty arrays, so only include fields when non-empty
|
|
159
137
|
const fileChanges = {};
|
|
160
138
|
if (fileAdditions.length > 0) {
|
|
161
139
|
fileChanges.additions = fileAdditions;
|
|
@@ -176,25 +154,18 @@ export class GraphQLCommitStrategy {
|
|
|
176
154
|
fileChanges,
|
|
177
155
|
},
|
|
178
156
|
};
|
|
179
|
-
// Build the GraphQL request body
|
|
180
157
|
const requestBody = JSON.stringify({
|
|
181
158
|
query: mutation,
|
|
182
159
|
variables,
|
|
183
160
|
});
|
|
184
|
-
// Build the gh api graphql command
|
|
185
|
-
// Use --input - to pass the JSON body via stdin (more reliable for complex nested JSON)
|
|
186
|
-
// Use --hostname for GitHub Enterprise
|
|
187
161
|
const hostnameArg = repoInfo.host !== "github.com"
|
|
188
162
|
? `--hostname ${escapeShellArg(repoInfo.host)}`
|
|
189
163
|
: "";
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
// GH_TOKEN env var must be set for the gh command (after the pipe), not echo
|
|
193
|
-
const tokenPrefix = token ? `GH_TOKEN=${token} ` : "";
|
|
194
|
-
const command = `echo ${escapeShellArg(requestBody)} | ${tokenPrefix}gh api graphql ${hostnameArg} --input -`;
|
|
164
|
+
const tokenEnv = token ? { GH_TOKEN: token } : undefined;
|
|
165
|
+
const command = `echo ${escapeShellArg(requestBody)} | gh api graphql ${hostnameArg} --input -`;
|
|
195
166
|
let response;
|
|
196
167
|
try {
|
|
197
|
-
response = await withRetry(() => this.executor.exec(command, workDir), {
|
|
168
|
+
response = await withRetry(() => this.executor.exec(command, workDir, { env: tokenEnv }), {
|
|
198
169
|
permanentErrorPatterns: [
|
|
199
170
|
...DEFAULT_PERMANENT_ERROR_PATTERNS,
|
|
200
171
|
...OID_MISMATCH_PATTERNS,
|
|
@@ -204,12 +175,14 @@ export class GraphQLCommitStrategy {
|
|
|
204
175
|
catch (error) {
|
|
205
176
|
throw this.sanitizeCommandError(error, repositoryNameWithOwner);
|
|
206
177
|
}
|
|
207
|
-
|
|
208
|
-
const parsed = JSON.parse(response);
|
|
178
|
+
const parsed = parseApiJson(response, "GraphQL createCommitOnBranch response");
|
|
209
179
|
if (parsed.errors) {
|
|
210
|
-
|
|
180
|
+
const errors = parsed.errors;
|
|
181
|
+
throw new Error(`GraphQL error: ${errors.map((e) => e.message).join(", ")}`);
|
|
211
182
|
}
|
|
212
|
-
const
|
|
183
|
+
const data = parsed.data;
|
|
184
|
+
const commit = data?.createCommitOnBranch?.commit;
|
|
185
|
+
const oid = commit?.oid;
|
|
213
186
|
if (!oid) {
|
|
214
187
|
throw new Error("GraphQL response missing commit OID");
|
|
215
188
|
}
|
|
@@ -252,7 +225,7 @@ export class GraphQLCommitStrategy {
|
|
|
252
225
|
await this.createRemoteRef(repositoryId, branchName, sha, workDir, repoInfo, token);
|
|
253
226
|
}
|
|
254
227
|
catch (error) {
|
|
255
|
-
const msg =
|
|
228
|
+
const msg = toErrorMessage(error);
|
|
256
229
|
if (/already exists/i.test(msg)) {
|
|
257
230
|
// Branch was created between our query and create — that's fine
|
|
258
231
|
return;
|
|
@@ -260,7 +233,6 @@ export class GraphQLCommitStrategy {
|
|
|
260
233
|
throw error;
|
|
261
234
|
}
|
|
262
235
|
}
|
|
263
|
-
// refId exists + !force: no-op (branch already exists)
|
|
264
236
|
}
|
|
265
237
|
/**
|
|
266
238
|
* Sanitize command execution errors to remove the GraphQL payload.
|
|
@@ -269,7 +241,7 @@ export class GraphQLCommitStrategy {
|
|
|
269
241
|
* of base64-encoded file contents). This extracts just the meaningful stderr.
|
|
270
242
|
*/
|
|
271
243
|
sanitizeCommandError(error, repo) {
|
|
272
|
-
const originalMessage =
|
|
244
|
+
const originalMessage = toErrorMessage(error);
|
|
273
245
|
let cleanMessage;
|
|
274
246
|
if (originalMessage.startsWith("Command failed:")) {
|
|
275
247
|
// Extract stderr: everything after the first newline
|
|
@@ -294,11 +266,7 @@ export class GraphQLCommitStrategy {
|
|
|
294
266
|
*/
|
|
295
267
|
isHeadOidMismatchError(error) {
|
|
296
268
|
const message = error.message.toLowerCase();
|
|
297
|
-
return
|
|
298
|
-
message.includes("expectedheadoid") ||
|
|
299
|
-
message.includes("head oid") ||
|
|
300
|
-
// GitHub may return this generic error for OID mismatches
|
|
301
|
-
message.includes("was provided invalid value"));
|
|
269
|
+
return OID_MISMATCH_PATTERNS.some((pattern) => pattern.test(message));
|
|
302
270
|
}
|
|
303
271
|
/**
|
|
304
272
|
* Execute a GraphQL query or mutation for ref operations.
|
|
@@ -310,18 +278,18 @@ export class GraphQLCommitStrategy {
|
|
|
310
278
|
const hostnameArg = repoInfo.host !== "github.com"
|
|
311
279
|
? `--hostname ${escapeShellArg(repoInfo.host)}`
|
|
312
280
|
: "";
|
|
313
|
-
const
|
|
314
|
-
const command = `echo ${escapeShellArg(requestBody)} |
|
|
281
|
+
const tokenEnv = token ? { GH_TOKEN: token } : undefined;
|
|
282
|
+
const command = `echo ${escapeShellArg(requestBody)} | gh api graphql ${hostnameArg} --input -`;
|
|
315
283
|
let response;
|
|
316
284
|
try {
|
|
317
|
-
response = await withRetry(() => this.executor.exec(command, workDir), {
|
|
285
|
+
response = await withRetry(() => this.executor.exec(command, workDir, { env: tokenEnv }), {
|
|
318
286
|
permanentErrorPatterns: GraphQLCommitStrategy.GRAPHQL_PERMANENT_ERROR_PATTERNS,
|
|
319
287
|
});
|
|
320
288
|
}
|
|
321
289
|
catch (error) {
|
|
322
290
|
throw this.sanitizeCommandError(error, `${repoInfo.owner}/${repoInfo.repo}`);
|
|
323
291
|
}
|
|
324
|
-
const parsed =
|
|
292
|
+
const parsed = parseApiJson(response, "GraphQL API response");
|
|
325
293
|
if (parsed.errors) {
|
|
326
294
|
throw new Error(`GraphQL error: ${parsed.errors.map((e) => e.message).join(", ")}`);
|
|
327
295
|
}
|
package/dist/vcs/index.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
export type { PRMergeConfig,
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export { GitHubAppTokenManager } from "./github-app-token-manager.js";
|
|
5
|
-
export { createPR, mergePR, formatPRBody, formatPRTitle, escapeShellArg, type PROptions, type PRResult, type FileAction, type MergePROptions, } from "./pr-creator.js";
|
|
6
|
-
export { BasePRStrategy, PRWorkflowExecutor } from "./pr-strategy.js";
|
|
7
|
-
export { GitHubPRStrategy } from "./github-pr-strategy.js";
|
|
8
|
-
export { AzurePRStrategy } from "./azure-pr-strategy.js";
|
|
9
|
-
export { GitLabPRStrategy } from "./gitlab-pr-strategy.js";
|
|
10
|
-
export { GitCommitStrategy } from "./git-commit-strategy.js";
|
|
11
|
-
export { GraphQLCommitStrategy, MAX_PAYLOAD_SIZE, } from "./graphql-commit-strategy.js";
|
|
12
|
-
export { getCommitStrategy, hasGitHubAppCredentials, } from "./commit-strategy-selector.js";
|
|
13
|
-
import { RepoInfo } from "../shared/repo-detector.js";
|
|
14
|
-
import type { IPRStrategy } from "./types.js";
|
|
15
|
-
import { ICommandExecutor } from "../shared/command-executor.js";
|
|
16
|
-
export declare function getPRStrategy(repoInfo: RepoInfo, executor?: ICommandExecutor): IPRStrategy;
|
|
1
|
+
export type { PRMergeConfig, FileChange } from "./types.js";
|
|
2
|
+
export { getCommitStrategy, createTokenManager, } from "./commit-strategy-selector.js";
|
|
3
|
+
export { getPRStrategy } from "./pr-strategy-factory.js";
|
package/dist/vcs/index.js
CHANGED
|
@@ -1,35 +1,4 @@
|
|
|
1
|
-
// Core git operations
|
|
2
|
-
export { GitOps, sanitizeBranchName, validateBranchName, } from "./git-ops.js";
|
|
3
|
-
// Authenticated git operations (with per-command auth)
|
|
4
|
-
export { AuthenticatedGitOps, } from "./authenticated-git-ops.js";
|
|
5
|
-
// GitHub App token management
|
|
6
|
-
export { GitHubAppTokenManager } from "./github-app-token-manager.js";
|
|
7
|
-
// PR creation utilities
|
|
8
|
-
export { createPR, mergePR, formatPRBody, formatPRTitle, escapeShellArg, } from "./pr-creator.js";
|
|
9
|
-
// PR strategies
|
|
10
|
-
export { BasePRStrategy, PRWorkflowExecutor } from "./pr-strategy.js";
|
|
11
|
-
export { GitHubPRStrategy } from "./github-pr-strategy.js";
|
|
12
|
-
export { AzurePRStrategy } from "./azure-pr-strategy.js";
|
|
13
|
-
export { GitLabPRStrategy } from "./gitlab-pr-strategy.js";
|
|
14
1
|
// Commit strategies
|
|
15
|
-
export {
|
|
16
|
-
export { GraphQLCommitStrategy, MAX_PAYLOAD_SIZE, } from "./graphql-commit-strategy.js";
|
|
17
|
-
export { getCommitStrategy, hasGitHubAppCredentials, } from "./commit-strategy-selector.js";
|
|
2
|
+
export { getCommitStrategy, createTokenManager, } from "./commit-strategy-selector.js";
|
|
18
3
|
// PR strategy factory
|
|
19
|
-
|
|
20
|
-
import { GitHubPRStrategy } from "./github-pr-strategy.js";
|
|
21
|
-
import { AzurePRStrategy } from "./azure-pr-strategy.js";
|
|
22
|
-
import { GitLabPRStrategy } from "./gitlab-pr-strategy.js";
|
|
23
|
-
export function getPRStrategy(repoInfo, executor) {
|
|
24
|
-
if (isGitHubRepo(repoInfo)) {
|
|
25
|
-
return new GitHubPRStrategy(executor);
|
|
26
|
-
}
|
|
27
|
-
if (isAzureDevOpsRepo(repoInfo)) {
|
|
28
|
-
return new AzurePRStrategy(executor);
|
|
29
|
-
}
|
|
30
|
-
if (isGitLabRepo(repoInfo)) {
|
|
31
|
-
return new GitLabPRStrategy(executor);
|
|
32
|
-
}
|
|
33
|
-
const _exhaustive = repoInfo;
|
|
34
|
-
throw new Error(`Unknown repository type: ${JSON.stringify(_exhaustive)}`);
|
|
35
|
-
}
|
|
4
|
+
export { getPRStrategy } from "./pr-strategy-factory.js";
|
package/dist/vcs/pr-creator.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { RepoInfo } from "../shared/repo-detector.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { IPRStrategyLogger } from "./pr-strategy.js";
|
|
3
|
+
import type { MergeResult, PRMergeConfig, PRResult } from "./types.js";
|
|
3
4
|
import { ICommandExecutor } from "../shared/command-executor.js";
|
|
4
|
-
export { escapeShellArg } from "../shared/shell-utils.js";
|
|
5
5
|
export interface FileAction {
|
|
6
6
|
fileName: string;
|
|
7
7
|
action: "create" | "update" | "skip" | "delete";
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
interface PROptions {
|
|
10
10
|
repoInfo: RepoInfo;
|
|
11
11
|
branchName: string;
|
|
12
12
|
baseBranch: string;
|
|
@@ -23,12 +23,10 @@ export interface PROptions {
|
|
|
23
23
|
token?: string;
|
|
24
24
|
/** Labels to apply to the created PR */
|
|
25
25
|
labels?: string[];
|
|
26
|
+
/** Optional logger for PR strategy debug/warn/info messages */
|
|
27
|
+
log?: IPRStrategyLogger;
|
|
26
28
|
}
|
|
27
|
-
export
|
|
28
|
-
url?: string;
|
|
29
|
-
success: boolean;
|
|
30
|
-
message: string;
|
|
31
|
-
}
|
|
29
|
+
export type { PRResult } from "./types.js";
|
|
32
30
|
/**
|
|
33
31
|
* Format PR body using template with ${xfg:...} variables.
|
|
34
32
|
*
|
|
@@ -45,7 +43,7 @@ export declare function formatPRBody(files: FileAction[], repoInfo: RepoInfo, cu
|
|
|
45
43
|
*/
|
|
46
44
|
export declare function formatPRTitle(files: FileAction[]): string;
|
|
47
45
|
export declare function createPR(options: PROptions): Promise<PRResult>;
|
|
48
|
-
|
|
46
|
+
interface MergePROptions {
|
|
49
47
|
repoInfo: RepoInfo;
|
|
50
48
|
prUrl: string;
|
|
51
49
|
mergeConfig: PRMergeConfig;
|
|
@@ -56,5 +54,7 @@ export interface MergePROptions {
|
|
|
56
54
|
executor?: ICommandExecutor;
|
|
57
55
|
/** GitHub App installation token for authentication */
|
|
58
56
|
token?: string;
|
|
57
|
+
/** Optional logger for PR strategy debug/warn/info messages */
|
|
58
|
+
log?: IPRStrategyLogger;
|
|
59
59
|
}
|
|
60
60
|
export declare function mergePR(options: MergePROptions): Promise<MergeResult>;
|
package/dist/vcs/pr-creator.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from "node:fs";
|
|
2
2
|
import { join, dirname } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { getPRStrategy } from "./
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
export { escapeShellArg } from "../shared/shell-utils.js";
|
|
4
|
+
import { getPRStrategy } from "./pr-strategy-factory.js";
|
|
5
|
+
import { PRWorkflowExecutor } from "./pr-strategy.js";
|
|
6
|
+
import { interpolateXfgContent } from "../shared/xfg-template.js";
|
|
8
7
|
function loadDefaultTemplate() {
|
|
9
8
|
// Try to find PR.md in the project root
|
|
10
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -97,7 +96,7 @@ export function formatPRTitle(files) {
|
|
|
97
96
|
return `chore: sync ${changedFiles.length} config files`;
|
|
98
97
|
}
|
|
99
98
|
export async function createPR(options) {
|
|
100
|
-
const { repoInfo, branchName, baseBranch, files, workDir, dryRun, retries, prTemplate, executor, token, labels, } = options;
|
|
99
|
+
const { repoInfo, branchName, baseBranch, files, workDir, dryRun, retries, prTemplate, executor, token, labels, log, } = options;
|
|
101
100
|
const title = formatPRTitle(files);
|
|
102
101
|
const body = formatPRBody(files, repoInfo, prTemplate);
|
|
103
102
|
if (dryRun) {
|
|
@@ -106,9 +105,10 @@ export async function createPR(options) {
|
|
|
106
105
|
message: `[DRY RUN] Would create PR: "${title}"`,
|
|
107
106
|
};
|
|
108
107
|
}
|
|
109
|
-
// Get the appropriate strategy and execute
|
|
110
|
-
const strategy = getPRStrategy(repoInfo, executor);
|
|
111
|
-
|
|
108
|
+
// Get the appropriate strategy and execute via workflow executor
|
|
109
|
+
const strategy = getPRStrategy(repoInfo, executor, log);
|
|
110
|
+
const workflow = new PRWorkflowExecutor(strategy);
|
|
111
|
+
return workflow.execute({
|
|
112
112
|
repoInfo,
|
|
113
113
|
title,
|
|
114
114
|
body,
|
|
@@ -121,7 +121,7 @@ export async function createPR(options) {
|
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
123
|
export async function mergePR(options) {
|
|
124
|
-
const { repoInfo, prUrl, mergeConfig, workDir, dryRun, retries, executor, token, } = options;
|
|
124
|
+
const { repoInfo, prUrl, mergeConfig, workDir, dryRun, retries, executor, token, log, } = options;
|
|
125
125
|
if (dryRun) {
|
|
126
126
|
const modeText = mergeConfig.mode === "force"
|
|
127
127
|
? "force merge"
|
|
@@ -135,9 +135,10 @@ export async function mergePR(options) {
|
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
// Get the appropriate strategy and execute merge
|
|
138
|
-
const strategy = getPRStrategy(repoInfo, executor);
|
|
138
|
+
const strategy = getPRStrategy(repoInfo, executor, log);
|
|
139
139
|
return strategy.merge({
|
|
140
140
|
prUrl,
|
|
141
|
+
repoInfo,
|
|
141
142
|
config: mergeConfig,
|
|
142
143
|
workDir,
|
|
143
144
|
retries,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { RepoInfo } from "../shared/repo-detector.js";
|
|
2
|
+
import type { IPRStrategy } from "./types.js";
|
|
3
|
+
import { ICommandExecutor } from "../shared/command-executor.js";
|
|
4
|
+
import type { IPRStrategyLogger } from "./pr-strategy.js";
|
|
5
|
+
export declare function getPRStrategy(repoInfo: RepoInfo, executor?: ICommandExecutor, log?: IPRStrategyLogger): IPRStrategy;
|