@eldrforge/kodrdriv 1.2.23 â 1.2.24
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/PARALLEL-EXECUTION-FIXES.md +132 -0
- package/PARALLEL_EXECUTION_FIX.md +146 -0
- package/RECOVERY-FIXES.md +72 -0
- package/dist/arguments.js +26 -3
- package/dist/arguments.js.map +1 -1
- package/dist/commands/audio-commit.js +3 -3
- package/dist/commands/audio-commit.js.map +1 -1
- package/dist/commands/audio-review.js +13 -13
- package/dist/commands/audio-review.js.map +1 -1
- package/dist/commands/link.js +13 -13
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/publish.js +200 -146
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/review.js +6 -6
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/select-audio.js +4 -4
- package/dist/commands/select-audio.js.map +1 -1
- package/dist/commands/tree.js +242 -318
- package/dist/commands/tree.js.map +1 -1
- package/dist/commands/unlink.js +8 -8
- package/dist/commands/unlink.js.map +1 -1
- package/dist/commands/versions.js +3 -3
- package/dist/commands/versions.js.map +1 -1
- package/dist/constants.js +4 -4
- package/dist/constants.js.map +1 -1
- package/dist/content/diff.js +5 -2
- package/dist/content/diff.js.map +1 -1
- package/dist/content/files.js +4 -4
- package/dist/content/files.js.map +1 -1
- package/dist/execution/CommandValidator.js +160 -0
- package/dist/execution/CommandValidator.js.map +1 -0
- package/dist/execution/DependencyChecker.js +102 -0
- package/dist/execution/DependencyChecker.js.map +1 -0
- package/dist/execution/DynamicTaskPool.js +455 -0
- package/dist/execution/DynamicTaskPool.js.map +1 -0
- package/dist/execution/RecoveryManager.js +502 -0
- package/dist/execution/RecoveryManager.js.map +1 -0
- package/dist/execution/ResourceMonitor.js +125 -0
- package/dist/execution/ResourceMonitor.js.map +1 -0
- package/dist/execution/Scheduler.js +98 -0
- package/dist/execution/Scheduler.js.map +1 -0
- package/dist/execution/TreeExecutionAdapter.js +170 -0
- package/dist/execution/TreeExecutionAdapter.js.map +1 -0
- package/dist/logging.js +3 -3
- package/dist/logging.js.map +1 -1
- package/dist/ui/ProgressFormatter.js +230 -0
- package/dist/ui/ProgressFormatter.js.map +1 -0
- package/dist/util/checkpointManager.js +168 -0
- package/dist/util/checkpointManager.js.map +1 -0
- package/dist/util/dependencyGraph.js +224 -0
- package/dist/util/dependencyGraph.js.map +1 -0
- package/dist/util/fileLock.js +204 -0
- package/dist/util/fileLock.js.map +1 -0
- package/dist/util/general.js +5 -5
- package/dist/util/general.js.map +1 -1
- package/dist/util/gitMutex.js +116 -0
- package/dist/util/gitMutex.js.map +1 -0
- package/dist/util/mutex.js +96 -0
- package/dist/util/mutex.js.map +1 -0
- package/dist/util/performance.js +4 -4
- package/dist/util/performance.js.map +1 -1
- package/dist/util/safety.js +4 -4
- package/dist/util/safety.js.map +1 -1
- package/dist/util/storage.js +2 -2
- package/dist/util/storage.js.map +1 -1
- package/package.json +6 -6
package/dist/commands/publish.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { execute as execute$
|
|
1
|
+
import path__default from 'path';
|
|
2
|
+
import { execute as execute$2 } from './commit.js';
|
|
3
3
|
import { hasStagedChanges } from '../content/diff.js';
|
|
4
|
-
import { execute as execute$
|
|
4
|
+
import { execute as execute$1 } from './release.js';
|
|
5
5
|
import { getDryRunLogger, getLogger } from '../logging.js';
|
|
6
6
|
import { run, localBranchExists, runSecure, runWithDryRunSupport, safeJsonParse, validatePackageJson, validateGitRef, safeSyncBranchWithRemote, isBranchInSyncWithRemote } from '@eldrforge/git-tools';
|
|
7
7
|
import * as GitHub from '@eldrforge/github-tools';
|
|
@@ -9,10 +9,11 @@ import { create } from '../util/storage.js';
|
|
|
9
9
|
import { calculateBranchDependentVersion, calculateTargetVersion, checkIfTagExists, confirmVersionInteractively, getOutputPath } from '../util/general.js';
|
|
10
10
|
import { DEFAULT_OUTPUT_DIRECTORY, KODRDRIV_DEFAULTS } from '../constants.js';
|
|
11
11
|
import fs from 'fs/promises';
|
|
12
|
+
import { runGitWithLock } from '../util/gitMutex.js';
|
|
12
13
|
|
|
13
14
|
const scanNpmrcForEnvVars = async (storage)=>{
|
|
14
15
|
const logger = getLogger();
|
|
15
|
-
const npmrcPath =
|
|
16
|
+
const npmrcPath = path__default.join(process.cwd(), '.npmrc');
|
|
16
17
|
const envVars = [];
|
|
17
18
|
if (await storage.exists(npmrcPath)) {
|
|
18
19
|
try {
|
|
@@ -42,7 +43,7 @@ const scanNpmrcForEnvVars = async (storage)=>{
|
|
|
42
43
|
* and cleans them up if found by removing package-lock.json and regenerating it.
|
|
43
44
|
*/ const cleanupNpmLinkReferences = async (isDryRun)=>{
|
|
44
45
|
const logger = getDryRunLogger(isDryRun);
|
|
45
|
-
const packageLockPath =
|
|
46
|
+
const packageLockPath = path__default.join(process.cwd(), 'package-lock.json');
|
|
46
47
|
try {
|
|
47
48
|
// Check if package-lock.json exists
|
|
48
49
|
try {
|
|
@@ -238,7 +239,7 @@ const runPrechecks = async (runConfig, targetBranch)=>{
|
|
|
238
239
|
}
|
|
239
240
|
// Check if prepublishOnly script exists in package.json
|
|
240
241
|
logger.info('Checking for prepublishOnly script...');
|
|
241
|
-
const packageJsonPath =
|
|
242
|
+
const packageJsonPath = path__default.join(process.cwd(), 'package.json');
|
|
242
243
|
if (!await storage.exists(packageJsonPath)) {
|
|
243
244
|
if (!isDryRun) {
|
|
244
245
|
throw new Error('package.json not found in current directory.');
|
|
@@ -433,7 +434,10 @@ const execute = async (runConfig)=>{
|
|
|
433
434
|
try {
|
|
434
435
|
const remoteExists = await run(`git ls-remote --exit-code --heads origin ${currentBranch}`).then(()=>true).catch(()=>false);
|
|
435
436
|
if (remoteExists) {
|
|
436
|
-
|
|
437
|
+
// Wrap git pull with lock to prevent concurrent pulls in same repo
|
|
438
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
439
|
+
await run(`git pull origin ${currentBranch} --no-edit`);
|
|
440
|
+
}, `pull ${currentBranch}`);
|
|
437
441
|
logger.info(`â
Synced ${currentBranch} with remote`);
|
|
438
442
|
} else {
|
|
439
443
|
logger.info(`âšī¸ No remote ${currentBranch} branch found, will be created on first push`);
|
|
@@ -490,20 +494,23 @@ const execute = async (runConfig)=>{
|
|
|
490
494
|
if (!targetBranchExists) {
|
|
491
495
|
logger.info(`đ Target branch '${targetBranch}' does not exist, creating it from current branch...`);
|
|
492
496
|
try {
|
|
493
|
-
//
|
|
494
|
-
await
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
'
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
497
|
+
// Wrap git branch and push operations with lock
|
|
498
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
499
|
+
// Create the target branch from the current HEAD
|
|
500
|
+
await runSecure('git', [
|
|
501
|
+
'branch',
|
|
502
|
+
targetBranch,
|
|
503
|
+
'HEAD'
|
|
504
|
+
]);
|
|
505
|
+
logger.info(`â
Created target branch: ${targetBranch}`);
|
|
506
|
+
// Push the new branch to origin
|
|
507
|
+
await runSecure('git', [
|
|
508
|
+
'push',
|
|
509
|
+
'origin',
|
|
510
|
+
targetBranch
|
|
511
|
+
]);
|
|
512
|
+
logger.info(`â
Pushed new target branch to origin: ${targetBranch}`);
|
|
513
|
+
}, `create and push target branch ${targetBranch}`);
|
|
507
514
|
} catch (error) {
|
|
508
515
|
throw new Error(`Failed to create target branch '${targetBranch}': ${error.message}`);
|
|
509
516
|
}
|
|
@@ -565,14 +572,20 @@ const execute = async (runConfig)=>{
|
|
|
565
572
|
// Check if package-lock.json exists before trying to stage it
|
|
566
573
|
const packageLockExists = await storage.exists('package-lock.json');
|
|
567
574
|
const filesToStage = packageLockExists ? 'package.json package-lock.json' : 'package.json';
|
|
568
|
-
|
|
575
|
+
// Wrap git operations with repository lock to prevent .git/index.lock conflicts
|
|
576
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
577
|
+
await runWithDryRunSupport(`git add ${filesToStage}`, isDryRun);
|
|
578
|
+
}, 'stage dependency updates');
|
|
569
579
|
logger.verbose('Checking for staged dependency updates...');
|
|
570
580
|
if (isDryRun) {
|
|
571
581
|
logger.verbose('Would create dependency update commit if changes are staged');
|
|
572
582
|
} else {
|
|
573
583
|
if (await hasStagedChanges()) {
|
|
574
584
|
logger.verbose('Staged dependency changes found, creating commit...');
|
|
575
|
-
|
|
585
|
+
// Commit also needs git lock
|
|
586
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
587
|
+
await execute$2(runConfig);
|
|
588
|
+
}, 'commit dependency updates');
|
|
576
589
|
} else {
|
|
577
590
|
logger.verbose('No dependency changes to commit, skipping commit.');
|
|
578
591
|
}
|
|
@@ -586,102 +599,105 @@ const execute = async (runConfig)=>{
|
|
|
586
599
|
if (isDryRun) {
|
|
587
600
|
logger.info(`Would merge ${targetBranch} into current branch`);
|
|
588
601
|
} else {
|
|
589
|
-
//
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
602
|
+
// Wrap entire merge process with git lock (involves fetch, merge, checkout, add, commit)
|
|
603
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
604
|
+
// Fetch the latest target branch
|
|
605
|
+
try {
|
|
606
|
+
await run(`git fetch origin ${targetBranch}:${targetBranch}`);
|
|
607
|
+
logger.info(`â
Fetched latest ${targetBranch}`);
|
|
608
|
+
} catch (fetchError) {
|
|
609
|
+
logger.warn(`â ī¸ Could not fetch ${targetBranch}: ${fetchError.message}`);
|
|
610
|
+
logger.warn('Continuing without merge - PR may have conflicts...');
|
|
611
|
+
}
|
|
612
|
+
// Check if merge is needed (avoid unnecessary merge commits)
|
|
613
|
+
try {
|
|
614
|
+
const { stdout: mergeBase } = await run(`git merge-base HEAD ${targetBranch}`);
|
|
615
|
+
const { stdout: targetCommit } = await run(`git rev-parse ${targetBranch}`);
|
|
616
|
+
if (mergeBase.trim() === targetCommit.trim()) {
|
|
617
|
+
logger.info(`âšī¸ Already up-to-date with ${targetBranch}, no merge needed`);
|
|
618
|
+
} else {
|
|
619
|
+
// Try to merge target branch into current branch
|
|
620
|
+
let mergeSucceeded = false;
|
|
621
|
+
try {
|
|
622
|
+
await run(`git merge ${targetBranch} --no-edit -m "Merge ${targetBranch} to sync before version bump"`);
|
|
623
|
+
logger.info(`â
Merged ${targetBranch} into current branch`);
|
|
624
|
+
mergeSucceeded = true;
|
|
625
|
+
} catch (mergeError) {
|
|
626
|
+
// If merge conflicts occur, check if they're only in version-related files
|
|
627
|
+
const errorText = [
|
|
628
|
+
mergeError.message || '',
|
|
629
|
+
mergeError.stdout || '',
|
|
630
|
+
mergeError.stderr || ''
|
|
631
|
+
].join(' ');
|
|
632
|
+
if (errorText.includes('CONFLICT')) {
|
|
633
|
+
logger.warn(`â ī¸ Merge conflicts detected, attempting automatic resolution...`);
|
|
634
|
+
// Get list of conflicted files
|
|
635
|
+
const { stdout: conflictedFiles } = await run('git diff --name-only --diff-filter=U');
|
|
636
|
+
const conflicts = conflictedFiles.trim().split('\n').filter(Boolean);
|
|
637
|
+
logger.verbose(`Conflicted files: ${conflicts.join(', ')}`);
|
|
638
|
+
// Check if conflicts are only in package.json and package-lock.json
|
|
639
|
+
const versionFiles = [
|
|
640
|
+
'package.json',
|
|
641
|
+
'package-lock.json'
|
|
642
|
+
];
|
|
643
|
+
const nonVersionConflicts = conflicts.filter((f)=>!versionFiles.includes(f));
|
|
644
|
+
if (nonVersionConflicts.length > 0) {
|
|
645
|
+
logger.error(`â Cannot auto-resolve: conflicts in non-version files: ${nonVersionConflicts.join(', ')}`);
|
|
646
|
+
logger.error('');
|
|
647
|
+
logger.error('Please resolve conflicts manually:');
|
|
648
|
+
logger.error(' 1. Resolve conflicts in the files listed above');
|
|
649
|
+
logger.error(' 2. git add <resolved-files>');
|
|
650
|
+
logger.error(' 3. git commit');
|
|
651
|
+
logger.error(' 4. kodrdriv publish (to continue)');
|
|
652
|
+
logger.error('');
|
|
653
|
+
throw new Error(`Merge conflicts in non-version files. Please resolve manually.`);
|
|
654
|
+
}
|
|
655
|
+
// Auto-resolve version conflicts by accepting current branch versions
|
|
656
|
+
// (keep our working branch's version, which is likely already updated)
|
|
657
|
+
logger.info(`Auto-resolving version conflicts by keeping current branch versions...`);
|
|
658
|
+
for (const file of conflicts){
|
|
659
|
+
if (versionFiles.includes(file)) {
|
|
660
|
+
await run(`git checkout --ours ${file}`);
|
|
661
|
+
await run(`git add ${file}`);
|
|
662
|
+
logger.verbose(`Resolved ${file} using current branch version`);
|
|
663
|
+
}
|
|
648
664
|
}
|
|
665
|
+
// Complete the merge
|
|
666
|
+
await run(`git commit --no-edit -m "Merge ${targetBranch} to sync before version bump (auto-resolved version conflicts)"`);
|
|
667
|
+
logger.info(`â
Auto-resolved version conflicts and completed merge`);
|
|
668
|
+
mergeSucceeded = true;
|
|
669
|
+
} else {
|
|
670
|
+
// Not a conflict error, re-throw
|
|
671
|
+
throw mergeError;
|
|
649
672
|
}
|
|
650
|
-
// Complete the merge
|
|
651
|
-
await run(`git commit --no-edit -m "Merge ${targetBranch} to sync before version bump (auto-resolved version conflicts)"`);
|
|
652
|
-
logger.info(`â
Auto-resolved version conflicts and completed merge`);
|
|
653
|
-
mergeSucceeded = true;
|
|
654
|
-
} else {
|
|
655
|
-
// Not a conflict error, re-throw
|
|
656
|
-
throw mergeError;
|
|
657
673
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
674
|
+
// Only run npm install if merge actually happened
|
|
675
|
+
if (mergeSucceeded) {
|
|
676
|
+
// Run npm install to update package-lock.json based on merged package.json
|
|
677
|
+
logger.info('Running npm install after merge...');
|
|
678
|
+
await run('npm install');
|
|
679
|
+
logger.info('â
npm install completed');
|
|
680
|
+
// Commit any changes from npm install (e.g., package-lock.json updates)
|
|
681
|
+
const { stdout: mergeChangesStatus } = await run('git status --porcelain');
|
|
682
|
+
if (mergeChangesStatus.trim()) {
|
|
683
|
+
logger.verbose('Staging post-merge changes for commit');
|
|
684
|
+
// Check if package-lock.json exists before trying to stage it
|
|
685
|
+
const packageLockExistsPostMerge = await storage.exists('package-lock.json');
|
|
686
|
+
const filesToStagePostMerge = packageLockExistsPostMerge ? 'package.json package-lock.json' : 'package.json';
|
|
687
|
+
await run(`git add ${filesToStagePostMerge}`);
|
|
688
|
+
if (await hasStagedChanges()) {
|
|
689
|
+
logger.verbose('Committing post-merge changes...');
|
|
690
|
+
await execute$2(runConfig);
|
|
691
|
+
}
|
|
676
692
|
}
|
|
677
693
|
}
|
|
678
694
|
}
|
|
695
|
+
} catch (error) {
|
|
696
|
+
// Only catch truly unexpected errors here
|
|
697
|
+
logger.error(`â Unexpected error during merge: ${error.message}`);
|
|
698
|
+
throw error;
|
|
679
699
|
}
|
|
680
|
-
}
|
|
681
|
-
// Only catch truly unexpected errors here
|
|
682
|
-
logger.error(`â Unexpected error during merge: ${error.message}`);
|
|
683
|
-
throw error;
|
|
684
|
-
}
|
|
700
|
+
}, `merge ${targetBranch} into current branch`);
|
|
685
701
|
}
|
|
686
702
|
}
|
|
687
703
|
// STEP 4: Determine and set target version AFTER checks, dependency commit, and target branch merge
|
|
@@ -739,13 +755,18 @@ const execute = async (runConfig)=>{
|
|
|
739
755
|
// Check if package-lock.json exists before trying to stage it
|
|
740
756
|
const packageLockExistsVersionBump = await storage.exists('package-lock.json');
|
|
741
757
|
const filesToStageVersionBump = packageLockExistsVersionBump ? 'package.json package-lock.json' : 'package.json';
|
|
742
|
-
|
|
758
|
+
// Wrap git operations with lock
|
|
759
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
760
|
+
await runWithDryRunSupport(`git add ${filesToStageVersionBump}`, isDryRun);
|
|
761
|
+
}, 'stage version bump');
|
|
743
762
|
if (isDryRun) {
|
|
744
763
|
logger.verbose('Would create version bump commit');
|
|
745
764
|
} else {
|
|
746
765
|
if (await hasStagedChanges()) {
|
|
747
766
|
logger.verbose('Creating version bump commit...');
|
|
748
|
-
await
|
|
767
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
768
|
+
await execute$2(runConfig);
|
|
769
|
+
}, 'commit version bump');
|
|
749
770
|
} else {
|
|
750
771
|
logger.verbose('No version changes to commit.');
|
|
751
772
|
}
|
|
@@ -779,7 +800,7 @@ const execute = async (runConfig)=>{
|
|
|
779
800
|
if ((_runConfig_publish11 = runConfig.publish) === null || _runConfig_publish11 === void 0 ? void 0 : _runConfig_publish11.fromMain) {
|
|
780
801
|
logger.verbose('Forcing comparison against main branch for release notes');
|
|
781
802
|
}
|
|
782
|
-
const releaseSummary = await execute$
|
|
803
|
+
const releaseSummary = await execute$1(releaseConfig);
|
|
783
804
|
if (isDryRun) {
|
|
784
805
|
logger.info('Would write release notes to RELEASE_NOTES.md and RELEASE_TITLE.md in output directory');
|
|
785
806
|
} else {
|
|
@@ -794,7 +815,10 @@ const execute = async (runConfig)=>{
|
|
|
794
815
|
logger.info('Pushing to origin...');
|
|
795
816
|
// Get current branch name and push explicitly to avoid pushing to wrong remote/branch
|
|
796
817
|
const branchName = await GitHub.getCurrentBranchName();
|
|
797
|
-
|
|
818
|
+
// Wrap git push with lock
|
|
819
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
820
|
+
await runWithDryRunSupport(`git push origin ${branchName}`, isDryRun);
|
|
821
|
+
}, `push ${branchName}`);
|
|
798
822
|
logger.info('Creating pull request...');
|
|
799
823
|
if (isDryRun) {
|
|
800
824
|
logger.info('Would get commit title and create PR with GitHub API');
|
|
@@ -889,14 +913,19 @@ const execute = async (runConfig)=>{
|
|
|
889
913
|
}
|
|
890
914
|
}
|
|
891
915
|
try {
|
|
892
|
-
|
|
916
|
+
// Wrap git checkout and pull with lock
|
|
917
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
918
|
+
await runWithDryRunSupport(`git checkout ${targetBranch}`, isDryRun);
|
|
919
|
+
}, `checkout ${targetBranch}`);
|
|
893
920
|
// Sync target branch with remote to avoid conflicts during PR creation
|
|
894
921
|
if (!isDryRun) {
|
|
895
922
|
logger.info(`đ Syncing ${targetBranch} with remote to avoid PR conflicts...`);
|
|
896
923
|
try {
|
|
897
924
|
const remoteExists = await run(`git ls-remote --exit-code --heads origin ${targetBranch}`).then(()=>true).catch(()=>false);
|
|
898
925
|
if (remoteExists) {
|
|
899
|
-
await
|
|
926
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
927
|
+
await run(`git pull origin ${targetBranch} --no-edit`);
|
|
928
|
+
}, `pull ${targetBranch}`);
|
|
900
929
|
logger.info(`â
Synced ${targetBranch} with remote`);
|
|
901
930
|
} else {
|
|
902
931
|
logger.info(`âšī¸ No remote ${targetBranch} branch found, will be created on first push`);
|
|
@@ -979,18 +1008,22 @@ const execute = async (runConfig)=>{
|
|
|
979
1008
|
if (stdout.trim() === tagName) {
|
|
980
1009
|
logger.info(`Tag ${tagName} already exists locally, skipping tag creation`);
|
|
981
1010
|
} else {
|
|
982
|
-
await
|
|
983
|
-
'
|
|
984
|
-
|
|
985
|
-
|
|
1011
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
1012
|
+
await runSecure('git', [
|
|
1013
|
+
'tag',
|
|
1014
|
+
tagName
|
|
1015
|
+
]);
|
|
1016
|
+
}, `create tag ${tagName}`);
|
|
986
1017
|
logger.info(`Created local tag: ${tagName}`);
|
|
987
1018
|
}
|
|
988
1019
|
} catch (error) {
|
|
989
1020
|
// If git tag -l fails, create the tag anyway
|
|
990
|
-
await
|
|
991
|
-
'
|
|
992
|
-
|
|
993
|
-
|
|
1021
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
1022
|
+
await runSecure('git', [
|
|
1023
|
+
'tag',
|
|
1024
|
+
tagName
|
|
1025
|
+
]);
|
|
1026
|
+
}, `create tag ${tagName}`);
|
|
994
1027
|
logger.info(`Created local tag: ${tagName}`);
|
|
995
1028
|
}
|
|
996
1029
|
// Check if tag exists on remote before pushing
|
|
@@ -1004,11 +1037,13 @@ const execute = async (runConfig)=>{
|
|
|
1004
1037
|
if (stdout.trim()) {
|
|
1005
1038
|
logger.info(`Tag ${tagName} already exists on remote, skipping push`);
|
|
1006
1039
|
} else {
|
|
1007
|
-
await
|
|
1008
|
-
'
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1040
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
1041
|
+
await runSecure('git', [
|
|
1042
|
+
'push',
|
|
1043
|
+
'origin',
|
|
1044
|
+
tagName
|
|
1045
|
+
]);
|
|
1046
|
+
}, `push tag ${tagName}`);
|
|
1012
1047
|
logger.info(`Pushed tag to remote: ${tagName}`);
|
|
1013
1048
|
tagWasPushed = true;
|
|
1014
1049
|
}
|
|
@@ -1127,22 +1162,35 @@ const execute = async (runConfig)=>{
|
|
|
1127
1162
|
logger.info(`đ Syncing source branch with target after publish...`);
|
|
1128
1163
|
await runWithDryRunSupport(`git checkout ${currentBranch}`, isDryRun);
|
|
1129
1164
|
if (!isDryRun) {
|
|
1130
|
-
//
|
|
1165
|
+
// Sync target into source
|
|
1131
1166
|
// Note: With squash merging, fast-forward will fail because commit histories diverge
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1167
|
+
if (mergeMethod === 'squash') {
|
|
1168
|
+
// For squash merges, reset to target branch to avoid conflicts
|
|
1169
|
+
// The squash merge created a single commit on target that represents all source commits
|
|
1170
|
+
logger.info(`Resetting ${currentBranch} to ${targetBranch} (squash merge)...`);
|
|
1171
|
+
await run(`git reset --hard ${targetBranch}`);
|
|
1172
|
+
logger.info(`â
Reset ${currentBranch} to ${targetBranch}`);
|
|
1137
1173
|
} else {
|
|
1138
|
-
//
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1174
|
+
// For merge/rebase methods, try to merge target back into source
|
|
1175
|
+
logger.info(`Merging ${targetBranch} into ${currentBranch}...`);
|
|
1176
|
+
// Try fast-forward first (works with merge/rebase methods)
|
|
1177
|
+
// Use runSecure to avoid error output for expected failure
|
|
1178
|
+
let fastForwardSucceeded = false;
|
|
1179
|
+
try {
|
|
1180
|
+
await runSecure('git', [
|
|
1181
|
+
'merge',
|
|
1182
|
+
targetBranch,
|
|
1183
|
+
'--ff-only'
|
|
1184
|
+
]);
|
|
1185
|
+
fastForwardSucceeded = true;
|
|
1186
|
+
logger.info(`â
Fast-forward merged ${targetBranch} into ${currentBranch}`);
|
|
1187
|
+
} catch {
|
|
1188
|
+
logger.verbose(`Fast-forward merge not possible, performing regular merge...`);
|
|
1189
|
+
}
|
|
1190
|
+
if (!fastForwardSucceeded) {
|
|
1191
|
+
await run(`git merge ${targetBranch} --no-edit`);
|
|
1192
|
+
logger.info(`â
Merged ${targetBranch} into ${currentBranch}`);
|
|
1143
1193
|
}
|
|
1144
|
-
await run(`git merge ${targetBranch} --no-edit`);
|
|
1145
|
-
logger.info(`â
Merged ${targetBranch} into ${currentBranch}`);
|
|
1146
1194
|
}
|
|
1147
1195
|
// Determine version bump based on branch configuration
|
|
1148
1196
|
let versionCommand = 'prepatch'; // Default
|
|
@@ -1171,14 +1219,20 @@ const execute = async (runConfig)=>{
|
|
|
1171
1219
|
// Push updated source branch
|
|
1172
1220
|
logger.info(`Pushing updated ${currentBranch} branch...`);
|
|
1173
1221
|
try {
|
|
1174
|
-
await
|
|
1222
|
+
await runGitWithLock(process.cwd(), async ()=>{
|
|
1223
|
+
await run(`git push origin ${currentBranch}`);
|
|
1224
|
+
}, `push ${currentBranch}`);
|
|
1175
1225
|
logger.info(`â
Pushed ${currentBranch} to origin`);
|
|
1176
1226
|
} catch (pushError) {
|
|
1177
1227
|
logger.warn(`â ī¸ Failed to push ${currentBranch}: ${pushError.message}`);
|
|
1178
1228
|
logger.warn(` Please push manually: git push origin ${currentBranch}`);
|
|
1179
1229
|
}
|
|
1180
1230
|
} else {
|
|
1181
|
-
|
|
1231
|
+
if (mergeMethod === 'squash') {
|
|
1232
|
+
logger.info(`Would reset ${currentBranch} to ${targetBranch} (squash merge)`);
|
|
1233
|
+
} else {
|
|
1234
|
+
logger.info(`Would merge ${targetBranch} into ${currentBranch} with --ff-only`);
|
|
1235
|
+
}
|
|
1182
1236
|
logger.info(`Would bump version to next development version`);
|
|
1183
1237
|
logger.info(`Would push ${currentBranch} to origin`);
|
|
1184
1238
|
}
|