edsger 0.13.2 → 0.13.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.
|
@@ -8,7 +8,9 @@ import { execSync } from 'child_process';
|
|
|
8
8
|
import { fetchCodeRefineContext, } from './context.js';
|
|
9
9
|
import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
|
|
10
10
|
import { createSystemPrompt, createCodeRefinePrompt } from './prompts.js';
|
|
11
|
-
import { preparePhaseGitEnvironment, hasUncommittedChanges, getUncommittedFiles, } from '../../utils/git-branch-manager.js';
|
|
11
|
+
import { preparePhaseGitEnvironment, hasUncommittedChanges, getUncommittedFiles, syncFeatBranchWithMain, } from '../../utils/git-branch-manager.js';
|
|
12
|
+
import { getFeature } from '../../api/features/get-feature.js';
|
|
13
|
+
import { parsePullRequestUrl } from './context.js';
|
|
12
14
|
function userMessage(content) {
|
|
13
15
|
return {
|
|
14
16
|
type: 'user',
|
|
@@ -62,6 +64,23 @@ export const refineCodeFromPRFeedback = async (options, config) => {
|
|
|
62
64
|
if (verbose) {
|
|
63
65
|
logInfo(`Starting code refine for feature ID: ${featureId}`);
|
|
64
66
|
}
|
|
67
|
+
// Sync feat branch with main before preparing git environment
|
|
68
|
+
// This prevents extra commits from appearing in PR when dev branch is rebased
|
|
69
|
+
try {
|
|
70
|
+
const feature = await getFeature(featureId, verbose);
|
|
71
|
+
if (feature.pull_request_url) {
|
|
72
|
+
const prInfo = parsePullRequestUrl(feature.pull_request_url);
|
|
73
|
+
if (prInfo) {
|
|
74
|
+
await syncFeatBranchWithMain(featureId, githubToken, prInfo.owner, prInfo.repo, 'main', verbose);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (verbose) {
|
|
80
|
+
logInfo(`⚠️ Could not sync feat branch: ${error instanceof Error ? error.message : String(error)}`);
|
|
81
|
+
}
|
|
82
|
+
// Continue even if sync fails - it's not critical
|
|
83
|
+
}
|
|
65
84
|
// Prepare git environment: switch to feature branch and rebase with main
|
|
66
85
|
const cleanupGit = preparePhaseGitEnvironment(featureId, 'main', verbose);
|
|
67
86
|
try {
|
|
@@ -71,3 +71,16 @@ export declare function returnToMainBranch(baseBranch?: string, verbose?: boolea
|
|
|
71
71
|
* @returns Cleanup function that will return to main branch
|
|
72
72
|
*/
|
|
73
73
|
export declare function preparePhaseGitEnvironment(featureId: string, baseBranch?: string, verbose?: boolean): () => void;
|
|
74
|
+
/**
|
|
75
|
+
* Sync feat branch with main using GitHub API
|
|
76
|
+
* This ensures the feat branch (PR base) is up to date with main,
|
|
77
|
+
* preventing extra commits from appearing in PRs when dev branch is rebased.
|
|
78
|
+
*
|
|
79
|
+
* @param featureId - The feature ID (will be used to construct branch name "feat/{featureId}")
|
|
80
|
+
* @param githubToken - GitHub personal access token or app token
|
|
81
|
+
* @param owner - Repository owner
|
|
82
|
+
* @param repo - Repository name
|
|
83
|
+
* @param baseBranch - The base branch to sync from (default: "main")
|
|
84
|
+
* @param verbose - Whether to log verbose output
|
|
85
|
+
*/
|
|
86
|
+
export declare function syncFeatBranchWithMain(featureId: string, githubToken: string, owner: string, repo: string, baseBranch?: string, verbose?: boolean): Promise<boolean>;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Shared utilities for consistent git branch management across all phases
|
|
4
4
|
*/
|
|
5
5
|
import { execSync } from 'child_process';
|
|
6
|
+
import { Octokit } from '@octokit/rest';
|
|
6
7
|
import { logInfo, logError } from './logger.js';
|
|
7
8
|
/**
|
|
8
9
|
* Get current Git branch name
|
|
@@ -358,3 +359,100 @@ export function preparePhaseGitEnvironment(featureId, baseBranch = 'main', verbo
|
|
|
358
359
|
// Return cleanup function
|
|
359
360
|
return cleanup;
|
|
360
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Sync feat branch with main using GitHub API
|
|
364
|
+
* This ensures the feat branch (PR base) is up to date with main,
|
|
365
|
+
* preventing extra commits from appearing in PRs when dev branch is rebased.
|
|
366
|
+
*
|
|
367
|
+
* @param featureId - The feature ID (will be used to construct branch name "feat/{featureId}")
|
|
368
|
+
* @param githubToken - GitHub personal access token or app token
|
|
369
|
+
* @param owner - Repository owner
|
|
370
|
+
* @param repo - Repository name
|
|
371
|
+
* @param baseBranch - The base branch to sync from (default: "main")
|
|
372
|
+
* @param verbose - Whether to log verbose output
|
|
373
|
+
*/
|
|
374
|
+
export async function syncFeatBranchWithMain(featureId, githubToken, owner, repo, baseBranch = 'main', verbose) {
|
|
375
|
+
const featBranch = `feat/${featureId}`;
|
|
376
|
+
try {
|
|
377
|
+
const octokit = new Octokit({ auth: githubToken });
|
|
378
|
+
// Check if feat branch exists
|
|
379
|
+
if (verbose) {
|
|
380
|
+
logInfo(`🔍 Checking if ${featBranch} branch exists...`);
|
|
381
|
+
}
|
|
382
|
+
try {
|
|
383
|
+
await octokit.repos.getBranch({
|
|
384
|
+
owner,
|
|
385
|
+
repo,
|
|
386
|
+
branch: featBranch,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
if (error.status === 404) {
|
|
391
|
+
if (verbose) {
|
|
392
|
+
logInfo(`ℹ️ ${featBranch} branch does not exist, skipping sync`);
|
|
393
|
+
}
|
|
394
|
+
return true; // Not an error, just no feat branch yet
|
|
395
|
+
}
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
// Get the latest SHA of the base branch
|
|
399
|
+
const { data: baseBranchData } = await octokit.repos.getBranch({
|
|
400
|
+
owner,
|
|
401
|
+
repo,
|
|
402
|
+
branch: baseBranch,
|
|
403
|
+
});
|
|
404
|
+
const mainSha = baseBranchData.commit.sha;
|
|
405
|
+
// Get the current SHA of the feat branch
|
|
406
|
+
const { data: featBranchData } = await octokit.repos.getBranch({
|
|
407
|
+
owner,
|
|
408
|
+
repo,
|
|
409
|
+
branch: featBranch,
|
|
410
|
+
});
|
|
411
|
+
const featSha = featBranchData.commit.sha;
|
|
412
|
+
// Check if feat branch is already up to date
|
|
413
|
+
if (verbose) {
|
|
414
|
+
logInfo(`📥 Syncing ${featBranch} with ${baseBranch}...`);
|
|
415
|
+
logInfo(` ${baseBranch} SHA: ${mainSha.substring(0, 7)}`);
|
|
416
|
+
logInfo(` ${featBranch} SHA: ${featSha.substring(0, 7)}`);
|
|
417
|
+
}
|
|
418
|
+
// If already at the same SHA, no need to update
|
|
419
|
+
if (mainSha === featSha) {
|
|
420
|
+
if (verbose) {
|
|
421
|
+
logInfo(`ℹ️ ${featBranch} is already up to date with ${baseBranch}`);
|
|
422
|
+
}
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
// Use git.updateRef to fast-forward feat branch to main's SHA
|
|
426
|
+
// This avoids creating a merge commit
|
|
427
|
+
try {
|
|
428
|
+
await octokit.git.updateRef({
|
|
429
|
+
owner,
|
|
430
|
+
repo,
|
|
431
|
+
ref: `heads/${featBranch}`,
|
|
432
|
+
sha: mainSha,
|
|
433
|
+
force: true, // Force update since feat branch may have diverged
|
|
434
|
+
});
|
|
435
|
+
if (verbose) {
|
|
436
|
+
logInfo(`✅ Successfully synced ${featBranch} to ${baseBranch} (${mainSha.substring(0, 7)})`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
catch (updateError) {
|
|
440
|
+
// 422 means the ref doesn't exist or other validation error
|
|
441
|
+
if (updateError.status === 422 || updateError.status === 404) {
|
|
442
|
+
if (verbose) {
|
|
443
|
+
logInfo(`ℹ️ Could not update ${featBranch} ref, skipping sync`);
|
|
444
|
+
}
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
throw updateError;
|
|
448
|
+
}
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
if (verbose) {
|
|
453
|
+
logError(`⚠️ Failed to sync ${featBranch} with ${baseBranch}: ${error instanceof Error ? error.message : String(error)}`);
|
|
454
|
+
}
|
|
455
|
+
// Don't fail the whole process if sync fails, just log warning
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
}
|