@aspruyt/xfg 3.1.1 → 3.1.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.
@@ -52,6 +52,9 @@ export declare class GraphQLCommitStrategy implements CommitStrategy {
52
52
  * This is critical for GitHub App authentication where the global git config
53
53
  * may have a PAT token embedded, but we need to use the GitHub App installation token.
54
54
  *
55
+ * Uses a repo-specific URL pattern (including owner/repo) so it has a LONGER
56
+ * prefix match than the global config and takes precedence.
57
+ *
55
58
  * Applies to all remote operations: push, fetch, ls-remote, etc.
56
59
  */
57
60
  private buildAuthenticatedGitCommand;
@@ -72,7 +72,7 @@ export class GraphQLCommitStrategy {
72
72
  // Ensure the branch exists on remote and is up-to-date with local HEAD
73
73
  // createCommitOnBranch requires the branch to already exist
74
74
  // For PR branches (force=true), we force-update to ensure fresh start from main
75
- await this.ensureBranchExistsOnRemote(branchName, workDir, options.force, token, githubInfo.host);
75
+ await this.ensureBranchExistsOnRemote(branchName, workDir, options.force, token, githubInfo.host, githubInfo.owner, githubInfo.repo);
76
76
  // Retry loop for expectedHeadOid mismatch
77
77
  let lastError = null;
78
78
  for (let attempt = 0; attempt <= retries; attempt++) {
@@ -80,7 +80,7 @@ export class GraphQLCommitStrategy {
80
80
  // Fetch from remote to ensure we have the latest HEAD
81
81
  // This is critical for expectedHeadOid to match
82
82
  const safeBranch = escapeShellArg(branchName);
83
- await this.executor.exec(this.buildAuthenticatedGitCommand(`fetch origin ${safeBranch}:refs/remotes/origin/${safeBranch}`, token, githubInfo.host), workDir);
83
+ await this.executor.exec(this.buildAuthenticatedGitCommand(`fetch origin ${safeBranch}:refs/remotes/origin/${safeBranch}`, token, githubInfo.host, githubInfo.owner, githubInfo.repo), workDir);
84
84
  // Get the remote HEAD SHA for this branch (not local HEAD)
85
85
  const headSha = await this.executor.exec(`git rev-parse origin/${safeBranch}`, workDir);
86
86
  // Build and execute the GraphQL mutation
@@ -179,14 +179,21 @@ export class GraphQLCommitStrategy {
179
179
  * This is critical for GitHub App authentication where the global git config
180
180
  * may have a PAT token embedded, but we need to use the GitHub App installation token.
181
181
  *
182
+ * Uses a repo-specific URL pattern (including owner/repo) so it has a LONGER
183
+ * prefix match than the global config and takes precedence.
184
+ *
182
185
  * Applies to all remote operations: push, fetch, ls-remote, etc.
183
186
  */
184
- buildAuthenticatedGitCommand(gitArgs, token, host = "github.com") {
187
+ buildAuthenticatedGitCommand(gitArgs, token, host = "github.com", owner, repo) {
185
188
  if (!token) {
186
189
  return `git ${gitArgs}`;
187
190
  }
188
- // Override URL rewrite to use the provided token
189
- const urlOverride = `url."https://x-access-token:${token}@${host}/".insteadOf="https://${host}/"`;
191
+ // Use repo-specific URL pattern for LONGER prefix match to override global config
192
+ // Global config: url."https://x-access-token:PAT@github.com/".insteadOf = "https://github.com/"
193
+ // Our config: url."https://x-access-token:APP@github.com/owner/repo".insteadOf = "https://github.com/owner/repo"
194
+ // The longer prefix (owner/repo) takes precedence in git's URL matching
195
+ const repoPath = owner && repo ? `${owner}/${repo}` : "";
196
+ const urlOverride = `url."https://x-access-token:${token}@${host}/${repoPath}".insteadOf="https://${host}/${repoPath}"`;
190
197
  return `git -c ${escapeShellArg(urlOverride)} ${gitArgs}`;
191
198
  }
192
199
  /**
@@ -198,23 +205,23 @@ export class GraphQLCommitStrategy {
198
205
  *
199
206
  * For direct mode (force=false): just ensure branch exists.
200
207
  */
201
- async ensureBranchExistsOnRemote(branchName, workDir, force, token, host) {
208
+ async ensureBranchExistsOnRemote(branchName, workDir, force, token, host, owner, repo) {
202
209
  // Branch name was validated in commit(), safe for shell use
203
210
  try {
204
211
  // Check if the branch exists on remote
205
- await this.executor.exec(this.buildAuthenticatedGitCommand(`ls-remote --exit-code --heads origin ${escapeShellArg(branchName)}`, token, host), workDir);
212
+ await this.executor.exec(this.buildAuthenticatedGitCommand(`ls-remote --exit-code --heads origin ${escapeShellArg(branchName)}`, token, host, owner, repo), workDir);
206
213
  // Branch exists - for PR branches, delete and recreate to ensure fresh from main
207
214
  if (force) {
208
- await this.executor.exec(this.buildAuthenticatedGitCommand(`push origin --delete ${escapeShellArg(branchName)}`, token, host), workDir);
215
+ await this.executor.exec(this.buildAuthenticatedGitCommand(`push origin --delete ${escapeShellArg(branchName)}`, token, host, owner, repo), workDir);
209
216
  // Now push fresh branch from local HEAD
210
- await this.executor.exec(this.buildAuthenticatedGitCommand(`push -u origin HEAD:${escapeShellArg(branchName)}`, token, host), workDir);
217
+ await this.executor.exec(this.buildAuthenticatedGitCommand(`push -u origin HEAD:${escapeShellArg(branchName)}`, token, host, owner, repo), workDir);
211
218
  }
212
219
  // For direct mode (force=false), leave existing branch as-is
213
220
  }
214
221
  catch {
215
222
  // Branch doesn't exist on remote, push it
216
223
  // This pushes the current local branch to create it on remote
217
- await this.executor.exec(this.buildAuthenticatedGitCommand(`push -u origin HEAD:${escapeShellArg(branchName)}`, token, host), workDir);
224
+ await this.executor.exec(this.buildAuthenticatedGitCommand(`push -u origin HEAD:${escapeShellArg(branchName)}`, token, host, owner, repo), workDir);
218
225
  }
219
226
  }
220
227
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aspruyt/xfg",
3
- "version": "3.1.1",
3
+ "version": "3.1.2",
4
4
  "description": "CLI tool for repository-as-code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",