@aspruyt/xfg 6.0.3 → 6.2.0
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/dist/cli/lifecycle-report-builder.d.ts +2 -2
- package/dist/cli/lifecycle-report-builder.js +3 -11
- package/dist/cli/program.d.ts +2 -1
- package/dist/cli/program.js +2 -3
- package/dist/cli/repo-sync-runner.d.ts +24 -0
- package/dist/cli/repo-sync-runner.js +156 -0
- package/dist/cli/results-collector.d.ts +1 -1
- package/dist/cli/results-collector.js +2 -2
- package/dist/cli/settings-factories.d.ts +7 -0
- package/dist/cli/settings-factories.js +27 -0
- package/dist/cli/settings-report-builder.d.ts +1 -1
- package/dist/cli/settings-report-builder.js +12 -23
- package/dist/cli/settings-runner.d.ts +2 -0
- package/dist/cli/settings-runner.js +87 -0
- package/dist/cli/sync-command.d.ts +1 -1
- package/dist/cli/sync-command.js +31 -372
- package/dist/cli/sync-report-builder.d.ts +1 -1
- package/dist/cli/sync-utils.d.ts +8 -0
- package/dist/cli/sync-utils.js +36 -0
- package/dist/cli/types.d.ts +5 -7
- package/dist/cli/unified-summary.d.ts +1 -3
- package/dist/cli/unified-summary.js +7 -5
- package/dist/cli.js +2 -1
- package/dist/{shared → config}/env.js +2 -2
- package/dist/config/extends-resolver.js +4 -3
- package/dist/config/file-reference-resolver.js +4 -2
- package/dist/config/formatter.js +18 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/loader.js +30 -6
- package/dist/config/merge.d.ts +11 -1
- package/dist/config/merge.js +78 -6
- package/dist/config/normalizer.js +53 -38
- package/dist/config/validator.d.ts +1 -4
- package/dist/config/validator.js +13 -599
- package/dist/config/validators/file-validator.d.ts +2 -1
- package/dist/config/validators/file-validator.js +9 -1
- package/dist/config/validators/group-validator.d.ts +3 -0
- package/dist/config/validators/group-validator.js +167 -0
- package/dist/config/validators/repo-entry-validator.d.ts +2 -0
- package/dist/config/validators/repo-entry-validator.js +165 -0
- package/dist/config/validators/repo-settings-validator.js +18 -7
- package/dist/config/validators/ruleset-validator.js +2 -5
- package/dist/config/validators/shared.d.ts +11 -0
- package/dist/config/validators/shared.js +242 -0
- package/dist/lifecycle/ado-migration-source.js +2 -4
- package/dist/lifecycle/github-lifecycle-provider.d.ts +7 -11
- package/dist/lifecycle/github-lifecycle-provider.js +125 -136
- package/dist/lifecycle/{lifecycle-helpers.d.ts → helpers.d.ts} +5 -1
- package/dist/lifecycle/{lifecycle-helpers.js → helpers.js} +9 -8
- package/dist/lifecycle/index.d.ts +2 -2
- package/dist/lifecycle/index.js +1 -1
- package/dist/lifecycle/repo-lifecycle-factory.d.ts +2 -2
- package/dist/output/github-summary.js +2 -3
- package/dist/output/index.d.ts +4 -0
- package/dist/output/index.js +4 -0
- package/dist/output/lifecycle-report.d.ts +1 -1
- package/dist/output/lifecycle-report.js +5 -0
- package/dist/output/sync-report.d.ts +25 -3
- package/dist/output/sync-report.js +11 -11
- package/dist/settings/base-processor.d.ts +18 -7
- package/dist/settings/base-processor.js +26 -5
- package/dist/settings/code-scanning/diff.js +2 -2
- package/dist/settings/code-scanning/formatter.d.ts +2 -6
- package/dist/settings/code-scanning/formatter.js +2 -25
- package/dist/settings/code-scanning/github-code-scanning-strategy.d.ts +3 -7
- package/dist/settings/code-scanning/github-code-scanning-strategy.js +2 -2
- package/dist/settings/code-scanning/processor.js +6 -4
- package/dist/settings/code-scanning/types.d.ts +10 -8
- package/dist/settings/labels/github-labels-strategy.d.ts +3 -11
- package/dist/settings/labels/types.d.ts +12 -10
- package/dist/settings/repo-settings/diff.d.ts +1 -1
- package/dist/settings/repo-settings/diff.js +1 -1
- package/dist/settings/repo-settings/formatter.d.ts +2 -6
- package/dist/settings/repo-settings/formatter.js +4 -23
- package/dist/settings/repo-settings/github-repo-settings-strategy.d.ts +2 -2
- package/dist/settings/repo-settings/github-repo-settings-strategy.js +8 -7
- package/dist/settings/repo-settings/processor.js +11 -11
- package/dist/settings/repo-settings/types.d.ts +2 -2
- package/dist/settings/rulesets/diff-algorithm.js +4 -2
- package/dist/settings/rulesets/diff.js +2 -51
- package/dist/settings/rulesets/formatter.js +4 -0
- package/dist/settings/rulesets/github-ruleset-strategy.d.ts +3 -3
- package/dist/settings/rulesets/github-ruleset-strategy.js +4 -6
- package/dist/settings/rulesets/index.d.ts +1 -1
- package/dist/settings/rulesets/index.js +0 -2
- package/dist/settings/rulesets/processor.js +1 -1
- package/dist/settings/rulesets/types.d.ts +6 -2
- package/dist/shared/command-executor.d.ts +4 -4
- package/dist/shared/command-executor.js +9 -7
- package/dist/shared/diff-format.d.ts +1 -0
- package/dist/shared/diff-format.js +10 -0
- package/dist/shared/errors.d.ts +7 -4
- package/dist/shared/errors.js +8 -8
- package/dist/shared/gh-api-utils.d.ts +3 -34
- package/dist/shared/gh-api-utils.js +23 -53
- package/dist/shared/gh-token-utils.d.ts +26 -0
- package/dist/shared/gh-token-utils.js +32 -0
- package/dist/shared/json-utils.js +1 -1
- package/dist/shared/regex-utils.d.ts +1 -0
- package/dist/shared/regex-utils.js +3 -0
- package/dist/shared/retry-utils.d.ts +1 -0
- package/dist/shared/retry-utils.js +13 -7
- package/dist/sync/auth-options-builder.js +1 -1
- package/dist/sync/branch-manager.js +5 -3
- package/dist/sync/commit-push-manager.js +2 -3
- package/dist/sync/diff-utils.d.ts +0 -1
- package/dist/sync/diff-utils.js +5 -10
- package/dist/sync/file-sync-orchestrator.js +0 -2
- package/dist/sync/file-writer.d.ts +3 -0
- package/dist/sync/file-writer.js +84 -81
- package/dist/sync/index.d.ts +0 -1
- package/dist/sync/index.js +0 -1
- package/dist/sync/manifest.js +1 -1
- package/dist/sync/pr-merge-handler.js +6 -6
- package/dist/sync/sync-workflow.js +1 -1
- package/dist/sync/types.d.ts +2 -2
- package/dist/vcs/ado-pr-strategy.d.ts +3 -5
- package/dist/vcs/ado-pr-strategy.js +131 -33
- package/dist/vcs/authenticated-git-ops.js +45 -23
- package/dist/vcs/git-commit-strategy.js +10 -6
- package/dist/vcs/git-ops.js +30 -24
- package/dist/vcs/github-pr-strategy.d.ts +3 -2
- package/dist/vcs/github-pr-strategy.js +80 -30
- package/dist/vcs/gitlab-pr-strategy.d.ts +2 -5
- package/dist/vcs/gitlab-pr-strategy.js +88 -87
- package/dist/vcs/graphql-commit-strategy.d.ts +1 -5
- package/dist/vcs/graphql-commit-strategy.js +21 -37
- package/dist/vcs/pr-creator.js +9 -2
- package/dist/vcs/pr-strategy.d.ts +2 -3
- package/dist/vcs/pr-strategy.js +0 -1
- package/dist/vcs/types.d.ts +9 -5
- package/package.json +5 -5
- package/dist/config/validators/index.d.ts +0 -3
- package/dist/config/validators/index.js +0 -6
- package/dist/output/types.d.ts +0 -20
- package/dist/output/types.js +0 -1
- package/dist/shared/shell-utils.d.ts +0 -6
- package/dist/shared/shell-utils.js +0 -17
- /package/dist/{shared → config}/env.d.ts +0 -0
- /package/dist/lifecycle/{lifecycle-formatter.d.ts → formatter.d.ts} +0 -0
- /package/dist/lifecycle/{lifecycle-formatter.js → formatter.js} +0 -0
- /package/dist/{vcs → shared}/sanitize-utils.d.ts +0 -0
- /package/dist/{vcs → shared}/sanitize-utils.js +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import pRetry, { AbortError } from "p-retry";
|
|
2
|
-
import {
|
|
2
|
+
import { getStderr } from "./command-executor.js";
|
|
3
|
+
import { sanitizeCredentials } from "./sanitize-utils.js";
|
|
3
4
|
import { ValidationError } from "./errors.js";
|
|
4
5
|
/**
|
|
5
6
|
* Core permanent error patterns shared across all strategies (API, GraphQL, CLI).
|
|
@@ -24,6 +25,14 @@ export const CORE_PERMANENT_ERROR_PATTERNS = [
|
|
|
24
25
|
/set\s+the\s+AZURE_DEVOPS_EXT_PAT\s+environment\s+variable/i,
|
|
25
26
|
/GITLAB_TOKEN\s+environment\s+variable/i,
|
|
26
27
|
];
|
|
28
|
+
export const BRANCH_PROTECTION_ERROR_PATTERNS = [
|
|
29
|
+
/rejected/i,
|
|
30
|
+
/protected\s*branch/i,
|
|
31
|
+
/protected/i,
|
|
32
|
+
/denied/i,
|
|
33
|
+
/required\s*status\s*check/i,
|
|
34
|
+
/push\s*rules?\s*prevent/i,
|
|
35
|
+
];
|
|
27
36
|
/**
|
|
28
37
|
* Default patterns indicating permanent errors that should NOT be retried.
|
|
29
38
|
* Extends CORE_PERMANENT_ERROR_PATTERNS with git-CLI-specific patterns.
|
|
@@ -82,8 +91,7 @@ const RATE_LIMIT_PATTERNS = [
|
|
|
82
91
|
*/
|
|
83
92
|
export function isRateLimitError(error) {
|
|
84
93
|
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
85
|
-
const
|
|
86
|
-
const combined = `${message} ${stderr}`;
|
|
94
|
+
const combined = `${message} ${getStderr(error)}`;
|
|
87
95
|
for (const pattern of RATE_LIMIT_PATTERNS) {
|
|
88
96
|
if (pattern.test(combined)) {
|
|
89
97
|
return true;
|
|
@@ -105,8 +113,7 @@ export function isPermanentError(error, patterns = DEFAULT_PERMANENT_ERROR_PATTE
|
|
|
105
113
|
return true;
|
|
106
114
|
}
|
|
107
115
|
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
108
|
-
const
|
|
109
|
-
const combined = `${message} ${stderr}`;
|
|
116
|
+
const combined = `${message} ${getStderr(error)}`;
|
|
110
117
|
// Check permanent patterns first - these always stop retries
|
|
111
118
|
for (const pattern of patterns) {
|
|
112
119
|
if (pattern.test(combined)) {
|
|
@@ -120,8 +127,7 @@ export function isPermanentError(error, patterns = DEFAULT_PERMANENT_ERROR_PATTE
|
|
|
120
127
|
*/
|
|
121
128
|
export function isTransientError(error, patterns = DEFAULT_TRANSIENT_ERROR_PATTERNS) {
|
|
122
129
|
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
123
|
-
const
|
|
124
|
-
const combined = `${message} ${stderr}`;
|
|
130
|
+
const combined = `${message} ${getStderr(error)}`;
|
|
125
131
|
for (const pattern of patterns) {
|
|
126
132
|
if (pattern.test(combined)) {
|
|
127
133
|
return true;
|
|
@@ -15,7 +15,7 @@ export class BranchManager {
|
|
|
15
15
|
if (!dryRun) {
|
|
16
16
|
this.log.debug("Checking for existing PR...");
|
|
17
17
|
const strategy = this.prStrategyFactory(repoInfo, executor, this.log);
|
|
18
|
-
const
|
|
18
|
+
const closeResult = await strategy.closeExistingPR({
|
|
19
19
|
repoInfo,
|
|
20
20
|
branchName,
|
|
21
21
|
baseBranch,
|
|
@@ -23,11 +23,13 @@ export class BranchManager {
|
|
|
23
23
|
retries,
|
|
24
24
|
token,
|
|
25
25
|
});
|
|
26
|
-
if (closed) {
|
|
26
|
+
if (closeResult.status === "closed") {
|
|
27
27
|
this.log.info("Closed existing PR and deleted branch for fresh sync");
|
|
28
|
-
// Prune stale remote tracking refs so --force-with-lease works correctly
|
|
29
28
|
await gitOps.fetch({ prune: true });
|
|
30
29
|
}
|
|
30
|
+
else if (closeResult.status === "close_failed") {
|
|
31
|
+
this.log.warn(`Failed to close existing PR: ${closeResult.message}`);
|
|
32
|
+
}
|
|
31
33
|
}
|
|
32
34
|
this.log.debug(`Creating branch: ${branchName}`);
|
|
33
35
|
await gitOps.createBranch(branchName);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createCommitStrategy } from "../vcs/index.js";
|
|
2
2
|
import { getRepoDisplayName } from "../repo/index.js";
|
|
3
3
|
import { toErrorMessage } from "../shared/type-guards.js";
|
|
4
|
+
import { BRANCH_PROTECTION_ERROR_PATTERNS } from "../shared/retry-utils.js";
|
|
4
5
|
export class CommitPushManager {
|
|
5
6
|
log;
|
|
6
7
|
commitStrategyFactory;
|
|
@@ -56,9 +57,7 @@ export class CommitPushManager {
|
|
|
56
57
|
const repoName = getRepoDisplayName(repoInfo);
|
|
57
58
|
const message = toErrorMessage(error);
|
|
58
59
|
if (isDirectMode &&
|
|
59
|
-
(
|
|
60
|
-
message.includes("protected") ||
|
|
61
|
-
message.includes("denied"))) {
|
|
60
|
+
BRANCH_PROTECTION_ERROR_PATTERNS.some((p) => p.test(message))) {
|
|
62
61
|
return {
|
|
63
62
|
success: false,
|
|
64
63
|
errorResult: {
|
|
@@ -2,7 +2,6 @@ export type { FileStatus } from "../shared/file-status.js";
|
|
|
2
2
|
export { formatStatusBadge } from "../shared/file-status.js";
|
|
3
3
|
import type { FileStatus } from "../shared/file-status.js";
|
|
4
4
|
export declare function getFileStatus(exists: boolean, changed: boolean): FileStatus;
|
|
5
|
-
export declare function formatDiffLine(line: string): string;
|
|
6
5
|
/**
|
|
7
6
|
* Check if a file is likely binary based on its extension.
|
|
8
7
|
*/
|
package/dist/sync/diff-utils.js
CHANGED
|
@@ -1,19 +1,10 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
1
|
export { formatStatusBadge } from "../shared/file-status.js";
|
|
2
|
+
import { formatDiffLine } from "../shared/diff-format.js";
|
|
3
3
|
export function getFileStatus(exists, changed) {
|
|
4
4
|
if (!exists)
|
|
5
5
|
return "NEW";
|
|
6
6
|
return changed ? "MODIFIED" : "UNCHANGED";
|
|
7
7
|
}
|
|
8
|
-
export function formatDiffLine(line) {
|
|
9
|
-
if (line.startsWith("+"))
|
|
10
|
-
return chalk.green(line);
|
|
11
|
-
if (line.startsWith("-"))
|
|
12
|
-
return chalk.red(line);
|
|
13
|
-
if (line.startsWith("@@"))
|
|
14
|
-
return chalk.cyan(line);
|
|
15
|
-
return line;
|
|
16
|
-
}
|
|
17
8
|
const BINARY_EXTENSIONS = new Set([
|
|
18
9
|
".png",
|
|
19
10
|
".jpg",
|
|
@@ -268,5 +259,9 @@ export function incrementDiffStats(stats, status) {
|
|
|
268
259
|
case "DELETED":
|
|
269
260
|
stats.deletedCount++;
|
|
270
261
|
break;
|
|
262
|
+
default: {
|
|
263
|
+
const _ = status;
|
|
264
|
+
throw new Error(`Unknown FileStatus: ${String(_)}`);
|
|
265
|
+
}
|
|
271
266
|
}
|
|
272
267
|
}
|
|
@@ -41,11 +41,9 @@ export class FileSyncOrchestrator {
|
|
|
41
41
|
else if (info.action === "delete")
|
|
42
42
|
incrementDiffStats(diffStats, "DELETED");
|
|
43
43
|
}
|
|
44
|
-
// Show diff summary in dry-run
|
|
45
44
|
if (dryRun) {
|
|
46
45
|
this.log.diffSummary(diffStats.newCount, diffStats.modifiedCount, diffStats.unchangedCount, diffStats.deletedCount);
|
|
47
46
|
}
|
|
48
|
-
// Build changed files list
|
|
49
47
|
const changedFiles = Array.from(fileChanges.entries()).map(([fileName, info]) => ({ fileName, action: info.action }));
|
|
50
48
|
const hasChanges = changedFiles.some((f) => f.action !== "skip");
|
|
51
49
|
return { fileChanges, diffStats, changedFiles, hasChanges };
|
|
@@ -15,5 +15,8 @@ export declare class FileWriter implements IFileWriter {
|
|
|
15
15
|
*/
|
|
16
16
|
static shouldBeExecutable: typeof shouldBeExecutable;
|
|
17
17
|
writeFiles(files: FileContent[], ctx: FileWriteContext, deps: FileWriterDeps): Promise<FileWriteAllResult>;
|
|
18
|
+
private processOneFile;
|
|
19
|
+
private resolveContent;
|
|
20
|
+
private applyExecutablePermissions;
|
|
18
21
|
}
|
|
19
22
|
export {};
|
package/dist/sync/file-writer.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { convertContentToString, } from "../config/index.js";
|
|
4
4
|
import { interpolateXfgContent } from "../shared/xfg-template.js";
|
|
5
|
-
import {
|
|
5
|
+
import { generateDiff, createDiffStats, incrementDiffStats, computeUnifiedDiff, isBinaryFile, } from "./diff-utils.js";
|
|
6
6
|
/**
|
|
7
7
|
* Determines if a file should be marked as executable.
|
|
8
8
|
* .sh files are auto-executable unless explicit executable: false is set.
|
|
@@ -26,96 +26,100 @@ export class FileWriter {
|
|
|
26
26
|
*/
|
|
27
27
|
static shouldBeExecutable = shouldBeExecutable;
|
|
28
28
|
async writeFiles(files, ctx, deps) {
|
|
29
|
-
const { repoInfo, baseBranch, workDir, dryRun } = ctx;
|
|
30
|
-
const { gitOps, log } = deps;
|
|
31
29
|
const fileChanges = new Map();
|
|
32
30
|
const diffStats = createDiffStats();
|
|
33
31
|
const modeCache = new Map();
|
|
34
32
|
for (const file of files) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
log.info(`Writing ${file.fileName}...`);
|
|
51
|
-
// Apply xfg templating if enabled
|
|
52
|
-
let contentToWrite = file.content;
|
|
53
|
-
if (file.template && contentToWrite !== null) {
|
|
54
|
-
contentToWrite = interpolateXfgContent(contentToWrite, {
|
|
55
|
-
repoInfo,
|
|
56
|
-
fileName: file.fileName,
|
|
57
|
-
vars: file.vars,
|
|
58
|
-
}, { strict: true });
|
|
59
|
-
}
|
|
60
|
-
const fileContent = convertContentToString(contentToWrite, file.fileName, {
|
|
61
|
-
header: file.header,
|
|
62
|
-
schemaUrl: file.schemaUrl,
|
|
63
|
-
});
|
|
64
|
-
// Determine action type (create vs update) BEFORE writing
|
|
65
|
-
const action = fileExistsLocal ? "update" : "create";
|
|
66
|
-
const existingContent = gitOps.getFileContent(file.fileName);
|
|
67
|
-
const changed = gitOps.wouldChange(file.fileName, fileContent);
|
|
68
|
-
const desiredMode = shouldBeExecutable(file)
|
|
69
|
-
? "100755"
|
|
70
|
-
: "100644";
|
|
71
|
-
const currentMode = await gitOps.getFileMode(file.fileName);
|
|
72
|
-
modeCache.set(file.fileName, currentMode);
|
|
73
|
-
const modeDiffers = currentMode !== null && currentMode !== desiredMode;
|
|
74
|
-
if (changed) {
|
|
75
|
-
const writeResult = {
|
|
76
|
-
fileName: file.fileName,
|
|
77
|
-
content: fileContent,
|
|
78
|
-
action,
|
|
79
|
-
...(desiredMode === "100755" || modeDiffers
|
|
80
|
-
? { mode: desiredMode }
|
|
81
|
-
: {}),
|
|
82
|
-
};
|
|
83
|
-
if (!isBinaryFile(file.fileName)) {
|
|
84
|
-
writeResult.diffLines = computeUnifiedDiff(existingContent, fileContent);
|
|
85
|
-
}
|
|
86
|
-
fileChanges.set(file.fileName, writeResult);
|
|
87
|
-
}
|
|
88
|
-
else if (modeDiffers) {
|
|
33
|
+
await this.processOneFile(file, ctx, deps, fileChanges, diffStats, modeCache);
|
|
34
|
+
}
|
|
35
|
+
await this.applyExecutablePermissions(files, fileChanges, modeCache, ctx, deps);
|
|
36
|
+
return { fileChanges, diffStats };
|
|
37
|
+
}
|
|
38
|
+
async processOneFile(file, ctx, deps, fileChanges, diffStats, modeCache) {
|
|
39
|
+
const { repoInfo, baseBranch, workDir, dryRun } = ctx;
|
|
40
|
+
const { gitOps, log } = deps;
|
|
41
|
+
if (file.createOnly) {
|
|
42
|
+
const existsOnBase = await gitOps.fileExistsOnBranch(file.fileName, baseBranch);
|
|
43
|
+
if (existsOnBase) {
|
|
44
|
+
log.info(`Skipping ${file.fileName} (createOnly: exists on ${baseBranch})`);
|
|
89
45
|
fileChanges.set(file.fileName, {
|
|
90
46
|
fileName: file.fileName,
|
|
91
47
|
content: null,
|
|
92
|
-
action: "
|
|
93
|
-
mode: desiredMode,
|
|
94
|
-
modeOnly: true,
|
|
48
|
+
action: "skip",
|
|
95
49
|
});
|
|
50
|
+
return;
|
|
96
51
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
52
|
+
}
|
|
53
|
+
log.info(`Writing ${file.fileName}...`);
|
|
54
|
+
const fileContent = this.resolveContent(file, repoInfo);
|
|
55
|
+
const fileExistsLocal = existsSync(join(workDir, file.fileName));
|
|
56
|
+
const action = fileExistsLocal ? "update" : "create";
|
|
57
|
+
const existingContent = gitOps.getFileContent(file.fileName);
|
|
58
|
+
const changed = gitOps.wouldChange(file.fileName, fileContent);
|
|
59
|
+
const desiredMode = shouldBeExecutable(file)
|
|
60
|
+
? "100755"
|
|
61
|
+
: "100644";
|
|
62
|
+
const currentMode = await gitOps.getFileMode(file.fileName);
|
|
63
|
+
modeCache.set(file.fileName, currentMode);
|
|
64
|
+
const modeDiffers = currentMode !== null && currentMode !== desiredMode;
|
|
65
|
+
if (changed) {
|
|
66
|
+
const writeResult = {
|
|
67
|
+
fileName: file.fileName,
|
|
68
|
+
content: fileContent,
|
|
69
|
+
action,
|
|
70
|
+
...(desiredMode === "100755" || modeDiffers
|
|
71
|
+
? { mode: desiredMode }
|
|
72
|
+
: {}),
|
|
73
|
+
};
|
|
74
|
+
if (!isBinaryFile(file.fileName)) {
|
|
75
|
+
writeResult.diffLines = computeUnifiedDiff(existingContent, fileContent);
|
|
108
76
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
77
|
+
fileChanges.set(file.fileName, writeResult);
|
|
78
|
+
}
|
|
79
|
+
else if (modeDiffers) {
|
|
80
|
+
fileChanges.set(file.fileName, {
|
|
81
|
+
fileName: file.fileName,
|
|
82
|
+
content: null,
|
|
83
|
+
action: "update",
|
|
84
|
+
mode: desiredMode,
|
|
85
|
+
modeOnly: true,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (dryRun) {
|
|
89
|
+
if (changed) {
|
|
90
|
+
const status = existingContent !== null ? "MODIFIED" : "NEW";
|
|
91
|
+
incrementDiffStats(diffStats, status);
|
|
92
|
+
const diffLines = generateDiff(existingContent, fileContent);
|
|
93
|
+
log.fileDiff(file.fileName, status, diffLines);
|
|
112
94
|
}
|
|
113
95
|
else if (modeDiffers) {
|
|
114
96
|
incrementDiffStats(diffStats, "MODIFIED");
|
|
97
|
+
log.info(`Would change mode: ${file.fileName} ${currentMode} -> ${desiredMode}`);
|
|
115
98
|
}
|
|
116
99
|
}
|
|
117
|
-
|
|
118
|
-
|
|
100
|
+
else if (changed) {
|
|
101
|
+
incrementDiffStats(diffStats, action === "create" ? "NEW" : "MODIFIED");
|
|
102
|
+
gitOps.writeFile(file.fileName, fileContent);
|
|
103
|
+
}
|
|
104
|
+
else if (modeDiffers) {
|
|
105
|
+
incrementDiffStats(diffStats, "MODIFIED");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
resolveContent(file, repoInfo) {
|
|
109
|
+
let contentToWrite = file.content;
|
|
110
|
+
if (file.template && contentToWrite !== null) {
|
|
111
|
+
contentToWrite = interpolateXfgContent(contentToWrite, {
|
|
112
|
+
repoInfo,
|
|
113
|
+
fileName: file.fileName,
|
|
114
|
+
vars: file.vars,
|
|
115
|
+
}, { strict: true });
|
|
116
|
+
}
|
|
117
|
+
return convertContentToString(contentToWrite, file.fileName, {
|
|
118
|
+
header: file.header,
|
|
119
|
+
schemaUrl: file.schemaUrl,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async applyExecutablePermissions(files, fileChanges, modeCache, ctx, deps) {
|
|
119
123
|
for (const file of files) {
|
|
120
124
|
const tracked = fileChanges.get(file.fileName);
|
|
121
125
|
if (tracked?.action === "skip") {
|
|
@@ -124,18 +128,17 @@ export class FileWriter {
|
|
|
124
128
|
const desired = shouldBeExecutable(file);
|
|
125
129
|
const currentMode = modeCache.get(file.fileName) ?? null;
|
|
126
130
|
if (desired && currentMode !== "100755") {
|
|
127
|
-
log.info(ctx.dryRun
|
|
131
|
+
deps.log.info(ctx.dryRun
|
|
128
132
|
? `Would set executable: ${file.fileName}`
|
|
129
133
|
: `Setting executable: ${file.fileName}`);
|
|
130
|
-
await gitOps.setExecutable(file.fileName);
|
|
134
|
+
await deps.gitOps.setExecutable(file.fileName);
|
|
131
135
|
}
|
|
132
136
|
else if (!desired && currentMode === "100755") {
|
|
133
|
-
log.info(ctx.dryRun
|
|
137
|
+
deps.log.info(ctx.dryRun
|
|
134
138
|
? `Would clear executable: ${file.fileName}`
|
|
135
139
|
: `Clearing executable: ${file.fileName}`);
|
|
136
|
-
await gitOps.clearExecutable(file.fileName);
|
|
140
|
+
await deps.gitOps.clearExecutable(file.fileName);
|
|
137
141
|
}
|
|
138
142
|
}
|
|
139
|
-
return { fileChanges, diffStats };
|
|
140
143
|
}
|
|
141
144
|
}
|
package/dist/sync/index.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
export { formatDiffLine } from "./diff-utils.js";
|
|
2
1
|
export type { FileChangeDetail, GitOpsFactory, IAuthOptionsBuilder, IBranchManager, ICommitPushManager, IFileSyncOrchestrator, IPRMergeHandler, IRepositoryProcessor, IRepositorySession, IWorkStrategy, ProcessorResult, SessionContext, WorkResult, } from "./types.js";
|
|
3
2
|
export { RepositoryProcessor } from "./repository-processor.js";
|
package/dist/sync/index.js
CHANGED
package/dist/sync/manifest.js
CHANGED
|
@@ -132,7 +132,7 @@ export function saveManifest(workDir, manifest) {
|
|
|
132
132
|
writeFileSync(manifestPath, content, "utf-8");
|
|
133
133
|
}
|
|
134
134
|
catch (error) {
|
|
135
|
-
throw new SyncError(`Failed to save manifest ${manifestPath}: ${toErrorMessage(error)}
|
|
135
|
+
throw new SyncError(`Failed to save manifest ${manifestPath}: ${toErrorMessage(error)}`, { cause: error });
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
export function getManagedFiles(manifest, configId) {
|
|
@@ -5,7 +5,7 @@ export class PRMergeHandler {
|
|
|
5
5
|
this.log = log;
|
|
6
6
|
}
|
|
7
7
|
async createAndMerge(input) {
|
|
8
|
-
const { repoInfo,
|
|
8
|
+
const { repoInfo, prOptions, options, changedFiles, repoName, diffStats, fileChanges, } = input;
|
|
9
9
|
this.log.info("Creating pull request...");
|
|
10
10
|
const strategy = options.dryRun
|
|
11
11
|
? undefined
|
|
@@ -21,19 +21,19 @@ export class PRMergeHandler {
|
|
|
21
21
|
prTemplate: options.prTemplate,
|
|
22
22
|
executor: options.executor,
|
|
23
23
|
token: options.token,
|
|
24
|
-
labels:
|
|
24
|
+
labels: prOptions?.labels,
|
|
25
25
|
log: this.log,
|
|
26
26
|
strategy,
|
|
27
27
|
});
|
|
28
|
-
const mergeMode =
|
|
28
|
+
const mergeMode = prOptions?.merge ?? "auto";
|
|
29
29
|
let mergeResult;
|
|
30
30
|
if (prResult.success && prResult.url && mergeMode !== "manual") {
|
|
31
31
|
this.log.info(`Handling merge (mode: ${mergeMode})...`);
|
|
32
32
|
const mergeConfig = {
|
|
33
33
|
mode: mergeMode,
|
|
34
|
-
strategy:
|
|
35
|
-
deleteBranch:
|
|
36
|
-
bypassReason:
|
|
34
|
+
strategy: prOptions?.mergeStrategy ?? "squash",
|
|
35
|
+
deleteBranch: prOptions?.deleteBranch ?? true,
|
|
36
|
+
bypassReason: prOptions?.bypassReason,
|
|
37
37
|
};
|
|
38
38
|
const result = await mergePR({
|
|
39
39
|
repoInfo,
|
package/dist/sync/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FileContent, RepoConfig } from "../config/index.js";
|
|
1
|
+
import type { FileContent, RepoConfig, PRMergeOptions } from "../config/index.js";
|
|
2
2
|
import type { RepoInfo } from "../repo/index.js";
|
|
3
3
|
import type { ActiveAction } from "../settings/index.js";
|
|
4
4
|
import type { ILocalGitOps, IGitOps, GitAuthOptions, GitOpsOptions, FileAction, FileActionKind } from "../vcs/index.js";
|
|
@@ -176,7 +176,7 @@ export interface PRHandlerOptions extends RunContext {
|
|
|
176
176
|
}
|
|
177
177
|
export interface CreateAndMergeInput {
|
|
178
178
|
repoInfo: RepoInfo;
|
|
179
|
-
|
|
179
|
+
prOptions?: PRMergeOptions;
|
|
180
180
|
options: PRHandlerOptions;
|
|
181
181
|
changedFiles: FileAction[];
|
|
182
182
|
repoName: string;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { PRResult } from "./types.js";
|
|
2
2
|
import { BasePRStrategy } from "./pr-strategy.js";
|
|
3
|
-
import type {
|
|
4
|
-
import type { PRStrategyOptions, CloseExistingPROptions, MergeOptions, MergeResult } from "./types.js";
|
|
5
|
-
import type { ICommandExecutor } from "../shared/command-executor.js";
|
|
3
|
+
import type { PRStrategyOptions, CloseExistingPROptions, ClosePRResult, MergeOptions, MergeResult } from "./types.js";
|
|
6
4
|
export declare class AdoPRStrategy extends BasePRStrategy {
|
|
7
|
-
|
|
5
|
+
private readonly bodyFilePath;
|
|
8
6
|
private getOrgUrl;
|
|
9
7
|
private buildPRUrl;
|
|
10
8
|
/**
|
|
@@ -13,7 +11,7 @@ export declare class AdoPRStrategy extends BasePRStrategy {
|
|
|
13
11
|
*/
|
|
14
12
|
private findExistingPRId;
|
|
15
13
|
findExistingPRUrl(options: CloseExistingPROptions): Promise<string | null>;
|
|
16
|
-
closeExistingPR(options: CloseExistingPROptions): Promise<
|
|
14
|
+
closeExistingPR(options: CloseExistingPROptions): Promise<ClosePRResult>;
|
|
17
15
|
create(options: PRStrategyOptions): Promise<PRResult>;
|
|
18
16
|
/**
|
|
19
17
|
* Extract PR ID and repo info from Azure DevOps PR URL.
|