@aspruyt/xfg 3.9.5 → 3.9.8

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.
@@ -167,6 +167,7 @@ async function processRulesets(repos, config, options, processor, repoProcessor,
167
167
  }
168
168
  else {
169
169
  logger.error(i + 1, repoName, result.message);
170
+ collector.appendError(repoName, result.message);
170
171
  }
171
172
  results.push({
172
173
  repoName,
@@ -240,6 +241,7 @@ async function processRepoSettings(repos, config, options, processorFactory, res
240
241
  }
241
242
  else {
242
243
  logger.error(current, repoName, result.message);
244
+ collector.appendError(repoName, result.message);
243
245
  }
244
246
  if (!result.skipped) {
245
247
  const existing = results.find((r) => r.repoName === repoName);
@@ -1,6 +1,7 @@
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 } from "../../shared/retry-utils.js";
4
5
  /**
5
6
  * Converts camelCase to snake_case.
6
7
  */
@@ -155,9 +156,18 @@ export class GitHubRepoSettingsStrategy {
155
156
  }
156
157
  async getPrivateVulnerabilityReporting(github, options) {
157
158
  const endpoint = `/repos/${github.owner}/${github.repo}/private-vulnerability-reporting`;
158
- const result = await this.ghApi("GET", endpoint, undefined, options);
159
- const data = JSON.parse(result);
160
- return data.enabled === true;
159
+ try {
160
+ const result = await this.ghApi("GET", endpoint, undefined, options);
161
+ const data = JSON.parse(result);
162
+ return data.enabled === true;
163
+ }
164
+ catch (error) {
165
+ const message = error instanceof Error ? error.message : String(error);
166
+ if (message.includes("HTTP 404")) {
167
+ return false; // 404 = not available (e.g. private repos)
168
+ }
169
+ throw error; // Re-throw other errors
170
+ }
161
171
  }
162
172
  validateGitHub(repoInfo) {
163
173
  if (!isGitHubRepo(repoInfo)) {
@@ -181,9 +191,9 @@ export class GitHubRepoSettingsStrategy {
181
191
  (method === "POST" || method === "PUT" || method === "PATCH")) {
182
192
  const payloadJson = JSON.stringify(payload);
183
193
  const command = `echo ${escapeShellArg(payloadJson)} | ${tokenPrefix}${baseCommand} --input -`;
184
- return await this.executor.exec(command, process.cwd());
194
+ return await withRetry(() => this.executor.exec(command, process.cwd()));
185
195
  }
186
196
  const command = `${tokenPrefix}${baseCommand}`;
187
- return await this.executor.exec(command, process.cwd());
197
+ return await withRetry(() => this.executor.exec(command, process.cwd()));
188
198
  }
189
199
  }
@@ -1,6 +1,7 @@
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 } from "../../shared/retry-utils.js";
4
5
  // =============================================================================
5
6
  // Conversion Functions
6
7
  // =============================================================================
@@ -201,10 +202,10 @@ export class GitHubRulesetStrategy {
201
202
  if (payload && (method === "POST" || method === "PUT")) {
202
203
  const payloadJson = JSON.stringify(payload);
203
204
  const command = `echo ${escapeShellArg(payloadJson)} | ${tokenPrefix}${baseCommand} --input -`;
204
- return await this.executor.exec(command, process.cwd());
205
+ return await withRetry(() => this.executor.exec(command, process.cwd()));
205
206
  }
206
207
  // For GET/DELETE, run command directly
207
208
  const command = `${tokenPrefix}${baseCommand}`;
208
- return await this.executor.exec(command, process.cwd());
209
+ return await withRetry(() => this.executor.exec(command, process.cwd()));
209
210
  }
210
211
  }
@@ -1,6 +1,7 @@
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
5
  /**
5
6
  * Maximum payload size for GitHub GraphQL API (50MB).
6
7
  * Base64 encoding adds ~33% overhead, so raw content should be checked.
@@ -29,6 +30,16 @@ export function validateBranchName(branchName) {
29
30
  `alphanumeric characters, hyphens, underscores, dots, and forward slashes.`);
30
31
  }
31
32
  }
33
+ /**
34
+ * OID mismatch error patterns that should NOT be retried by the inner withRetry.
35
+ * The outer retry loop in commit() handles these by fetching a fresh HEAD OID.
36
+ */
37
+ const OID_MISMATCH_PATTERNS = [
38
+ /expected branch to point to/i,
39
+ /expectedheadoid/i,
40
+ /head oid/i,
41
+ /was provided invalid value/i,
42
+ ];
32
43
  /**
33
44
  * GraphQL-based commit strategy using GitHub's createCommitOnBranch mutation.
34
45
  * Used with GitHub App authentication. Commits via this strategy ARE verified
@@ -162,7 +173,12 @@ export class GraphQLCommitStrategy {
162
173
  // GH_TOKEN env var must be set for the gh command (after the pipe), not echo
163
174
  const tokenPrefix = token ? `GH_TOKEN=${token} ` : "";
164
175
  const command = `echo ${escapeShellArg(requestBody)} | ${tokenPrefix}gh api graphql ${hostnameArg} --input -`;
165
- const response = await this.executor.exec(command, workDir);
176
+ const response = await withRetry(() => this.executor.exec(command, workDir), {
177
+ permanentErrorPatterns: [
178
+ ...DEFAULT_PERMANENT_ERROR_PATTERNS,
179
+ ...OID_MISMATCH_PATTERNS,
180
+ ],
181
+ });
166
182
  // Parse the response
167
183
  const parsed = JSON.parse(response);
168
184
  if (parsed.errors) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aspruyt/xfg",
3
- "version": "3.9.5",
3
+ "version": "3.9.8",
4
4
  "description": "Manage files, settings, and repositories across GitHub, Azure DevOps, and GitLab — declaratively, from a single YAML config",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",