edsger 0.30.2 ā 0.30.4
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.
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
1
|
+
import { execSync, execFileSync } from 'child_process';
|
|
2
2
|
import { logInfo, logError } from '../../utils/logger.js';
|
|
3
|
+
import { buildCredentialArgs } from '../../utils/git-push.js';
|
|
3
4
|
import { getFeature } from '../../api/features/index.js';
|
|
4
5
|
import { getPullRequests, } from '../../services/pull-requests.js';
|
|
5
6
|
import { getGitHubConfig } from '../../api/github.js';
|
|
@@ -69,33 +70,33 @@ export async function fetchPRExecutionContext(featureId, verbose) {
|
|
|
69
70
|
logInfo(`Fetching PR execution context for feature: ${featureId}`);
|
|
70
71
|
}
|
|
71
72
|
const devBranchName = getDevBranchName(featureId);
|
|
73
|
+
// Fetch GitHub config and data in parallel (need token before remote branch check)
|
|
74
|
+
const [feature, pullRequests, githubConfig] = await Promise.all([
|
|
75
|
+
getFeature(featureId, verbose),
|
|
76
|
+
getPullRequests({ featureId, verbose }),
|
|
77
|
+
getGitHubConfig(featureId, verbose),
|
|
78
|
+
]);
|
|
72
79
|
// Verify dev branch exists
|
|
73
80
|
const localExists = branchExists(devBranchName);
|
|
74
|
-
const remoteExists = !localExists && remoteBranchExists(devBranchName);
|
|
81
|
+
const remoteExists = !localExists && remoteBranchExists(devBranchName, githubConfig.token);
|
|
75
82
|
if (!localExists && !remoteExists) {
|
|
76
83
|
throw new Error(`Development branch '${devBranchName}' does not exist. ` +
|
|
77
84
|
`The feature must have code on the dev branch before PR execution.`);
|
|
78
85
|
}
|
|
79
|
-
// If branch only exists on remote, fetch it
|
|
86
|
+
// If branch only exists on remote, fetch it (using credential helper)
|
|
80
87
|
if (!localExists && remoteExists) {
|
|
81
88
|
if (verbose) {
|
|
82
89
|
logInfo(`Fetching remote branch ${devBranchName}...`);
|
|
83
90
|
}
|
|
84
|
-
|
|
91
|
+
const credArgs = buildCredentialArgs(githubConfig.token);
|
|
92
|
+
execFileSync('git', [...credArgs, 'fetch', 'origin', devBranchName], {
|
|
85
93
|
encoding: 'utf-8',
|
|
86
94
|
stdio: 'pipe',
|
|
87
95
|
});
|
|
88
96
|
}
|
|
89
|
-
// Fetch data in parallel
|
|
90
|
-
const [feature, pullRequests] = await Promise.all([
|
|
91
|
-
getFeature(featureId, verbose),
|
|
92
|
-
getPullRequests({ featureId, verbose }),
|
|
93
|
-
]);
|
|
94
97
|
if (pullRequests.length === 0) {
|
|
95
98
|
throw new Error('No PR plan found. Run the pr-splitting phase first to create a PR plan.');
|
|
96
99
|
}
|
|
97
|
-
// Fetch GitHub config
|
|
98
|
-
const githubConfig = await getGitHubConfig(featureId, verbose);
|
|
99
100
|
if (!githubConfig.configured) {
|
|
100
101
|
throw new Error(`GitHub is not configured. ${githubConfig.message || 'Please configure GitHub integration.'}`);
|
|
101
102
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
1
|
+
import { execSync, execFileSync } from 'child_process';
|
|
2
2
|
import { logInfo, logError } from '../../utils/logger.js';
|
|
3
|
+
import { buildCredentialArgs } from '../../utils/git-push.js';
|
|
3
4
|
import { getFeature } from '../../api/features/index.js';
|
|
4
5
|
import { getProduct } from '../../api/products.js';
|
|
5
6
|
import { getBranches } from '../../services/branches.js';
|
|
@@ -75,32 +76,32 @@ export async function fetchPRSplittingContext(featureId, verbose, replaceExistin
|
|
|
75
76
|
logInfo(`Fetching PR splitting context for feature: ${featureId}`);
|
|
76
77
|
}
|
|
77
78
|
const devBranchName = getDevBranchName(featureId);
|
|
79
|
+
// Fetch database data and GitHub config in parallel (need token before remote branch check)
|
|
80
|
+
const [feature, existing_branches, existing_pull_requests, githubConfig] = await Promise.all([
|
|
81
|
+
getFeature(featureId, verbose),
|
|
82
|
+
getBranches({ featureId, verbose }).catch(() => []),
|
|
83
|
+
getPullRequests({ featureId, verbose }).catch(() => []),
|
|
84
|
+
getGitHubConfig(featureId, verbose),
|
|
85
|
+
]);
|
|
78
86
|
// Verify dev branch exists
|
|
79
87
|
const localExists = branchExists(devBranchName);
|
|
80
|
-
const remoteExists = !localExists && remoteBranchExists(devBranchName);
|
|
88
|
+
const remoteExists = !localExists && remoteBranchExists(devBranchName, githubConfig.token);
|
|
81
89
|
if (!localExists && !remoteExists) {
|
|
82
90
|
throw new Error(`Development branch '${devBranchName}' does not exist. ` +
|
|
83
91
|
`The feature must have code on the dev branch before PR splitting.`);
|
|
84
92
|
}
|
|
85
|
-
// If branch only exists on remote, fetch it
|
|
93
|
+
// If branch only exists on remote, fetch it (using credential helper)
|
|
86
94
|
if (!localExists && remoteExists) {
|
|
87
95
|
if (verbose) {
|
|
88
96
|
logInfo(`Fetching remote branch ${devBranchName}...`);
|
|
89
97
|
}
|
|
90
|
-
|
|
98
|
+
const credArgs = buildCredentialArgs(githubConfig.token);
|
|
99
|
+
execFileSync('git', [...credArgs, 'fetch', 'origin', devBranchName], {
|
|
91
100
|
encoding: 'utf-8',
|
|
92
101
|
stdio: 'pipe',
|
|
93
102
|
});
|
|
94
103
|
}
|
|
95
|
-
// Fetch database data in parallel
|
|
96
|
-
const [feature, existing_branches, existing_pull_requests] = await Promise.all([
|
|
97
|
-
getFeature(featureId, verbose),
|
|
98
|
-
getBranches({ featureId, verbose }).catch(() => []),
|
|
99
|
-
getPullRequests({ featureId, verbose }).catch(() => []),
|
|
100
|
-
]);
|
|
101
104
|
const product = await getProduct(feature.product_id, verbose);
|
|
102
|
-
// Fetch GitHub config
|
|
103
|
-
const githubConfig = await getGitHubConfig(featureId, verbose);
|
|
104
105
|
// Detect fork status
|
|
105
106
|
let forkInfo = { isFork: false };
|
|
106
107
|
if (githubConfig.configured && githubConfig.token && githubConfig.owner && githubConfig.repo) {
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Creates pull requests from feature branches after successful testing
|
|
4
4
|
*/
|
|
5
5
|
import { Octokit } from '@octokit/rest';
|
|
6
|
-
import { execSync } from 'child_process';
|
|
7
|
-
import { gitPush } from '../../utils/git-push.js';
|
|
6
|
+
import { execSync, execFileSync } from 'child_process';
|
|
7
|
+
import { gitPush, buildCredentialArgs } from '../../utils/git-push.js';
|
|
8
8
|
// GitHub PR title best practice: keep under 72 characters
|
|
9
9
|
const MAX_PR_TITLE_LENGTH = 72;
|
|
10
10
|
const PR_TITLE_PREFIX = 'feat: ';
|
|
@@ -67,7 +67,7 @@ const discardUncommittedChanges = (verbose) => {
|
|
|
67
67
|
* @param branch - The branch to switch to
|
|
68
68
|
* @param verbose - Whether to log verbose output
|
|
69
69
|
*/
|
|
70
|
-
const switchToBranch = (branch, verbose) => {
|
|
70
|
+
const switchToBranch = (branch, verbose, githubToken) => {
|
|
71
71
|
try {
|
|
72
72
|
// First check if branch exists locally
|
|
73
73
|
if (!branchExists(branch)) {
|
|
@@ -77,7 +77,8 @@ const switchToBranch = (branch, verbose) => {
|
|
|
77
77
|
}
|
|
78
78
|
try {
|
|
79
79
|
// Fetch to get latest remote refs
|
|
80
|
-
|
|
80
|
+
const credArgs = buildCredentialArgs(githubToken);
|
|
81
|
+
execFileSync('git', [...credArgs, 'fetch', 'origin'], { encoding: 'utf-8', stdio: 'pipe' });
|
|
81
82
|
// Check if remote branch exists
|
|
82
83
|
execSync(`git rev-parse --verify origin/${branch}`, {
|
|
83
84
|
encoding: 'utf-8',
|
|
@@ -230,7 +231,7 @@ export async function createPullRequest(config, feature) {
|
|
|
230
231
|
if (verbose) {
|
|
231
232
|
console.log(`ā ļø Currently on ${baseBranch} branch, switching to ${devBranch}`);
|
|
232
233
|
}
|
|
233
|
-
switchToBranch(devBranch, verbose);
|
|
234
|
+
switchToBranch(devBranch, verbose, githubToken);
|
|
234
235
|
currentBranch = devBranch;
|
|
235
236
|
}
|
|
236
237
|
// Extract feature ID from current branch (dev/feature-id)
|
|
@@ -67,7 +67,7 @@ export declare function branchExists(branch: string): boolean;
|
|
|
67
67
|
/**
|
|
68
68
|
* Check if a remote branch exists
|
|
69
69
|
*/
|
|
70
|
-
export declare function remoteBranchExists(branch: string): boolean;
|
|
70
|
+
export declare function remoteBranchExists(branch: string, githubToken?: string): boolean;
|
|
71
71
|
/**
|
|
72
72
|
* Options for switchToBranch
|
|
73
73
|
*/
|
|
@@ -79,6 +79,8 @@ export interface SwitchToBranchOptions {
|
|
|
79
79
|
* Set this to true to skip the remote check (useful for well-known local branches like 'main').
|
|
80
80
|
*/
|
|
81
81
|
skipRemoteCheck?: boolean;
|
|
82
|
+
/** GitHub App installation token for authenticated remote operations */
|
|
83
|
+
githubToken?: string;
|
|
82
84
|
}
|
|
83
85
|
/**
|
|
84
86
|
* Switch to a specific Git branch, creating it if it doesn't exist
|
|
@@ -99,7 +101,7 @@ export declare function switchToBranch(branch: string, verbose?: boolean, option
|
|
|
99
101
|
/**
|
|
100
102
|
* Pull latest changes from remote for a specific branch
|
|
101
103
|
*/
|
|
102
|
-
export declare function pullLatestFromBranch(branch: string, verbose?: boolean): void;
|
|
104
|
+
export declare function pullLatestFromBranch(branch: string, verbose?: boolean, githubToken?: string): void;
|
|
103
105
|
/**
|
|
104
106
|
* Switch to feature branch and rebase with main
|
|
105
107
|
* This should be called at the START of each phase
|
|
@@ -109,7 +111,7 @@ export declare function pullLatestFromBranch(branch: string, verbose?: boolean):
|
|
|
109
111
|
* @param verbose - Whether to log verbose output
|
|
110
112
|
* @returns Object containing the previous branch name
|
|
111
113
|
*/
|
|
112
|
-
export declare function switchToFeatureBranchAndRebase(featureBranch: string, baseBranch?: string, verbose?: boolean): {
|
|
114
|
+
export declare function switchToFeatureBranchAndRebase(featureBranch: string, baseBranch?: string, verbose?: boolean, githubToken?: string): {
|
|
113
115
|
previousBranch: string;
|
|
114
116
|
};
|
|
115
117
|
/**
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Git Branch Manager
|
|
3
3
|
* Shared utilities for consistent git branch management across all phases
|
|
4
4
|
*/
|
|
5
|
-
import { execSync } from 'child_process';
|
|
5
|
+
import { execSync, execFileSync } from 'child_process';
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import * as path from 'path';
|
|
8
8
|
import { Octokit } from '@octokit/rest';
|
|
9
9
|
import { logInfo, logError } from './logger.js';
|
|
10
|
-
import { gitForcePush } from './git-push.js';
|
|
10
|
+
import { gitForcePush, buildCredentialArgs } from './git-push.js';
|
|
11
11
|
/**
|
|
12
12
|
* Get current Git branch name
|
|
13
13
|
*/
|
|
@@ -225,10 +225,11 @@ export function branchExists(branch) {
|
|
|
225
225
|
/**
|
|
226
226
|
* Check if a remote branch exists
|
|
227
227
|
*/
|
|
228
|
-
export function remoteBranchExists(branch) {
|
|
228
|
+
export function remoteBranchExists(branch, githubToken) {
|
|
229
229
|
try {
|
|
230
230
|
// Fetch remote refs first to ensure we have latest info
|
|
231
|
-
|
|
231
|
+
const credArgs = buildCredentialArgs(githubToken);
|
|
232
|
+
execFileSync('git', [...credArgs, 'fetch', 'origin', '--prune'], { encoding: 'utf-8', stdio: 'pipe' });
|
|
232
233
|
execSync(`git rev-parse --verify origin/${branch}`, {
|
|
233
234
|
encoding: 'utf-8',
|
|
234
235
|
stdio: 'pipe',
|
|
@@ -280,7 +281,8 @@ export function switchToBranch(branch, verbose, options) {
|
|
|
280
281
|
// This handles multi-clone scenarios where branch was pushed from another clone
|
|
281
282
|
try {
|
|
282
283
|
// Fetch to get latest remote refs
|
|
283
|
-
|
|
284
|
+
const switchCredArgs = buildCredentialArgs(options?.githubToken);
|
|
285
|
+
execFileSync('git', [...switchCredArgs, 'fetch', 'origin'], { encoding: 'utf-8', stdio: 'pipe' });
|
|
284
286
|
// Check if remote branch exists
|
|
285
287
|
execSync(`git rev-parse --verify origin/${branch}`, {
|
|
286
288
|
encoding: 'utf-8',
|
|
@@ -317,7 +319,7 @@ export function switchToBranch(branch, verbose, options) {
|
|
|
317
319
|
/**
|
|
318
320
|
* Pull latest changes from remote for a specific branch
|
|
319
321
|
*/
|
|
320
|
-
export function pullLatestFromBranch(branch, verbose) {
|
|
322
|
+
export function pullLatestFromBranch(branch, verbose, githubToken) {
|
|
321
323
|
try {
|
|
322
324
|
// Check for uncommitted changes and reset if found
|
|
323
325
|
if (hasUncommittedChanges()) {
|
|
@@ -329,7 +331,8 @@ export function pullLatestFromBranch(branch, verbose) {
|
|
|
329
331
|
if (verbose) {
|
|
330
332
|
logInfo(`š„ Pulling latest changes from origin/${branch}...`);
|
|
331
333
|
}
|
|
332
|
-
|
|
334
|
+
const credArgs = buildCredentialArgs(githubToken);
|
|
335
|
+
execFileSync('git', [...credArgs, 'pull', 'origin', branch, '--rebase'], {
|
|
333
336
|
encoding: 'utf-8',
|
|
334
337
|
stdio: verbose ? 'inherit' : 'pipe',
|
|
335
338
|
});
|
|
@@ -350,7 +353,7 @@ export function pullLatestFromBranch(branch, verbose) {
|
|
|
350
353
|
* @param verbose - Whether to log verbose output
|
|
351
354
|
* @returns Object containing the previous branch name
|
|
352
355
|
*/
|
|
353
|
-
export function switchToFeatureBranchAndRebase(featureBranch, baseBranch = 'main', verbose) {
|
|
356
|
+
export function switchToFeatureBranchAndRebase(featureBranch, baseBranch = 'main', verbose, githubToken) {
|
|
354
357
|
const previousBranch = getCurrentBranch();
|
|
355
358
|
if (verbose) {
|
|
356
359
|
logInfo(`\nš Preparing feature branch: ${featureBranch}`);
|
|
@@ -373,7 +376,7 @@ export function switchToFeatureBranchAndRebase(featureBranch, baseBranch = 'main
|
|
|
373
376
|
if (verbose) {
|
|
374
377
|
logInfo(` Pulling latest ${baseBranch}...`);
|
|
375
378
|
}
|
|
376
|
-
pullLatestFromBranch(baseBranch, verbose);
|
|
379
|
+
pullLatestFromBranch(baseBranch, verbose, githubToken);
|
|
377
380
|
}
|
|
378
381
|
catch (error) {
|
|
379
382
|
if (verbose) {
|
|
@@ -384,7 +387,7 @@ export function switchToFeatureBranchAndRebase(featureBranch, baseBranch = 'main
|
|
|
384
387
|
// Switch to feature branch (will create if doesn't exist)
|
|
385
388
|
// Default behavior now checks remote first (handles multi-clone scenarios)
|
|
386
389
|
if (getCurrentBranch() !== featureBranch) {
|
|
387
|
-
switchToBranch(featureBranch, verbose);
|
|
390
|
+
switchToBranch(featureBranch, verbose, { githubToken });
|
|
388
391
|
}
|
|
389
392
|
// Sync with remote feature branch if it exists and local branch needs updating
|
|
390
393
|
// This handles the case where local branch exists but is behind remote
|
|
@@ -397,7 +400,8 @@ export function switchToFeatureBranchAndRebase(featureBranch, baseBranch = 'main
|
|
|
397
400
|
resetUncommittedChanges(verbose);
|
|
398
401
|
}
|
|
399
402
|
// Fetch to get latest remote state (may have been fetched in switchToBranch, but ensures fresh data)
|
|
400
|
-
|
|
403
|
+
const credArgs = buildCredentialArgs(githubToken);
|
|
404
|
+
execFileSync('git', [...credArgs, 'fetch', 'origin'], { encoding: 'utf-8', stdio: 'pipe' });
|
|
401
405
|
// Check if remote feature branch exists
|
|
402
406
|
try {
|
|
403
407
|
execSync(`git rev-parse --verify origin/${featureBranch}`, {
|
|
@@ -459,7 +463,8 @@ export function switchToFeatureBranchAndRebase(featureBranch, baseBranch = 'main
|
|
|
459
463
|
}
|
|
460
464
|
resetUncommittedChanges(verbose);
|
|
461
465
|
}
|
|
462
|
-
|
|
466
|
+
const rebaseCredArgs = buildCredentialArgs(githubToken);
|
|
467
|
+
execFileSync('git', [...rebaseCredArgs, 'pull', 'origin', baseBranch, '--rebase'], {
|
|
463
468
|
encoding: 'utf-8',
|
|
464
469
|
stdio: verbose ? 'inherit' : 'pipe',
|
|
465
470
|
});
|
|
@@ -716,7 +721,7 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
|
|
|
716
721
|
if (verbose) {
|
|
717
722
|
logInfo(` Pulling latest ${baseBranch}...`);
|
|
718
723
|
}
|
|
719
|
-
pullLatestFromBranch(baseBranch, verbose);
|
|
724
|
+
pullLatestFromBranch(baseBranch, verbose, githubToken);
|
|
720
725
|
}
|
|
721
726
|
catch (error) {
|
|
722
727
|
if (verbose) {
|
|
@@ -727,7 +732,7 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
|
|
|
727
732
|
// Switch to feature branch (will create if doesn't exist)
|
|
728
733
|
// Default behavior now checks remote first (handles multi-clone scenarios)
|
|
729
734
|
if (getCurrentBranch() !== featureBranch) {
|
|
730
|
-
switchToBranch(featureBranch, verbose);
|
|
735
|
+
switchToBranch(featureBranch, verbose, { githubToken });
|
|
731
736
|
}
|
|
732
737
|
// Sync with remote feature branch if it exists and local branch needs updating
|
|
733
738
|
// This handles the case where local branch exists but is behind remote
|
|
@@ -739,7 +744,8 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
|
|
|
739
744
|
resetUncommittedChanges(verbose);
|
|
740
745
|
}
|
|
741
746
|
// Fetch to get latest remote state (may have been fetched in switchToBranch, but ensures fresh data)
|
|
742
|
-
|
|
747
|
+
const syncCredArgs = buildCredentialArgs(githubToken);
|
|
748
|
+
execFileSync('git', [...syncCredArgs, 'fetch', 'origin'], { encoding: 'utf-8', stdio: 'pipe' });
|
|
743
749
|
try {
|
|
744
750
|
execSync(`git rev-parse --verify origin/${featureBranch}`, {
|
|
745
751
|
encoding: 'utf-8',
|
|
@@ -802,7 +808,8 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
|
|
|
802
808
|
resetUncommittedChanges(verbose);
|
|
803
809
|
}
|
|
804
810
|
// Fetch the target branch first
|
|
805
|
-
|
|
811
|
+
const fetchCredArgs = buildCredentialArgs(githubToken);
|
|
812
|
+
execFileSync('git', [...fetchCredArgs, 'fetch', 'origin', actualRebaseTarget], {
|
|
806
813
|
encoding: 'utf-8',
|
|
807
814
|
stdio: 'pipe',
|
|
808
815
|
});
|
|
@@ -812,7 +819,7 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
|
|
|
812
819
|
// Command: git rebase --onto <new-base> <old-base>
|
|
813
820
|
// This rebases commits from <old-base>..HEAD onto <new-base>
|
|
814
821
|
// Also fetch the originalBaseBranch for the --onto reference
|
|
815
|
-
|
|
822
|
+
execFileSync('git', [...fetchCredArgs, 'fetch', 'origin', originalBaseBranch], {
|
|
816
823
|
encoding: 'utf-8',
|
|
817
824
|
stdio: 'pipe',
|
|
818
825
|
});
|