@eldrforge/kodrdriv 1.2.7 → 1.2.10-dev.0
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/.kodrdriv-example-branch-targeting.yaml +71 -0
- package/dist/application.js +7 -3
- package/dist/application.js.map +1 -1
- package/dist/arguments.js +47 -7
- package/dist/arguments.js.map +1 -1
- package/dist/commands/development.js +246 -227
- package/dist/commands/development.js.map +1 -1
- package/dist/commands/link.js +64 -4
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/publish.js +300 -49
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/tree.js +149 -7
- package/dist/commands/tree.js.map +1 -1
- package/dist/commands/updates.js +56 -0
- package/dist/commands/updates.js.map +1 -0
- package/dist/constants.js +27 -4
- package/dist/constants.js.map +1 -1
- package/dist/util/general.js +199 -2
- package/dist/util/general.js.map +1 -1
- package/dist/util/github.js +1 -107
- package/dist/util/github.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/publish.js
CHANGED
|
@@ -3,13 +3,13 @@ import { execute as execute$1 } from './commit.js';
|
|
|
3
3
|
import { hasStagedChanges } from '../content/diff.js';
|
|
4
4
|
import { execute as execute$2 } from './release.js';
|
|
5
5
|
import { getDryRunLogger, getLogger } from '../logging.js';
|
|
6
|
-
import {
|
|
6
|
+
import { run, runSecure, runWithDryRunSupport, validateGitRef } from '../util/child.js';
|
|
7
7
|
import { getCurrentBranchName, findOpenPullRequestByHeadRef, createPullRequest, waitForPullRequestChecks, mergePullRequest, createRelease, closeMilestoneForVersion, getWorkflowsTriggeredByRelease, waitForReleaseWorkflows } from '../util/github.js';
|
|
8
8
|
import { create } from '../util/storage.js';
|
|
9
|
-
import { calculateTargetVersion, checkIfTagExists, confirmVersionInteractively, getOutputPath } from '../util/general.js';
|
|
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 { safeJsonParse, validatePackageJson } from '../util/validation.js';
|
|
12
|
-
import {
|
|
12
|
+
import { localBranchExists, safeSyncBranchWithRemote, isBranchInSyncWithRemote } from '../util/git.js';
|
|
13
13
|
|
|
14
14
|
const scanNpmrcForEnvVars = async (storage)=>{
|
|
15
15
|
const logger = getLogger();
|
|
@@ -55,7 +55,7 @@ const validateEnvironmentVariables = (requiredEnvVars, isDryRun)=>{
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
|
-
const runPrechecks = async (runConfig)=>{
|
|
58
|
+
const runPrechecks = async (runConfig, targetBranch)=>{
|
|
59
59
|
var _runConfig_publish, _runConfig_publish1;
|
|
60
60
|
const isDryRun = runConfig.dryRun || false;
|
|
61
61
|
const logger = getDryRunLogger(isDryRun);
|
|
@@ -95,28 +95,29 @@ const runPrechecks = async (runConfig)=>{
|
|
|
95
95
|
throw new Error(`Failed to check git status: ${originalMessage}. Please ensure you are in a valid git repository and try again.`);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
+
// Use the passed target branch or fallback to config/default
|
|
99
|
+
const effectiveTargetBranch = targetBranch || ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.targetBranch) || 'main';
|
|
98
100
|
// Check that we're not running from the target branch
|
|
99
101
|
logger.info('Checking current branch...');
|
|
100
|
-
const targetBranch = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.targetBranch) || 'main';
|
|
101
102
|
if (isDryRun) {
|
|
102
|
-
logger.info(`Would verify current branch is not the target branch (${
|
|
103
|
+
logger.info(`Would verify current branch is not the target branch (${effectiveTargetBranch})`);
|
|
103
104
|
} else {
|
|
104
105
|
const currentBranch = await getCurrentBranchName();
|
|
105
|
-
if (currentBranch ===
|
|
106
|
-
throw new Error(`Cannot run publish from the target branch '${
|
|
106
|
+
if (currentBranch === effectiveTargetBranch) {
|
|
107
|
+
throw new Error(`Cannot run publish from the target branch '${effectiveTargetBranch}'. Please switch to a different branch before running publish.`);
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
// Check target branch sync with remote
|
|
110
|
-
logger.info(`Checking target branch '${
|
|
111
|
+
logger.info(`Checking target branch '${effectiveTargetBranch}' sync with remote...`);
|
|
111
112
|
if (isDryRun) {
|
|
112
|
-
logger.info(`Would verify target branch '${
|
|
113
|
+
logger.info(`Would verify target branch '${effectiveTargetBranch}' is in sync with remote origin`);
|
|
113
114
|
} else {
|
|
114
115
|
// Only check if local target branch exists (it's okay if it doesn't exist locally)
|
|
115
|
-
const targetBranchExists = await localBranchExists(
|
|
116
|
+
const targetBranchExists = await localBranchExists(effectiveTargetBranch);
|
|
116
117
|
if (targetBranchExists) {
|
|
117
|
-
const syncStatus = await isBranchInSyncWithRemote(
|
|
118
|
+
const syncStatus = await isBranchInSyncWithRemote(effectiveTargetBranch);
|
|
118
119
|
if (!syncStatus.inSync) {
|
|
119
|
-
logger.error(`❌ Target branch '${
|
|
120
|
+
logger.error(`❌ Target branch '${effectiveTargetBranch}' is not in sync with remote.`);
|
|
120
121
|
logger.error('');
|
|
121
122
|
if (syncStatus.error) {
|
|
122
123
|
logger.error(` Error: ${syncStatus.error}`);
|
|
@@ -126,18 +127,18 @@ const runPrechecks = async (runConfig)=>{
|
|
|
126
127
|
}
|
|
127
128
|
logger.error('');
|
|
128
129
|
logger.error('📋 To resolve this issue:');
|
|
129
|
-
logger.error(` 1. Switch to the target branch: git checkout ${
|
|
130
|
-
logger.error(` 2. Pull the latest changes: git pull origin ${
|
|
130
|
+
logger.error(` 1. Switch to the target branch: git checkout ${effectiveTargetBranch}`);
|
|
131
|
+
logger.error(` 2. Pull the latest changes: git pull origin ${effectiveTargetBranch}`);
|
|
131
132
|
logger.error(' 3. Resolve any merge conflicts if they occur');
|
|
132
133
|
logger.error(' 4. Switch back to your feature branch and re-run publish');
|
|
133
134
|
logger.error('');
|
|
134
135
|
logger.error('💡 Alternatively, run "kodrdriv publish --sync-target" to attempt automatic sync.');
|
|
135
|
-
throw new Error(`Target branch '${
|
|
136
|
+
throw new Error(`Target branch '${effectiveTargetBranch}' is not in sync with remote. Please sync the branch before running publish.`);
|
|
136
137
|
} else {
|
|
137
|
-
logger.info(`✅ Target branch '${
|
|
138
|
+
logger.info(`✅ Target branch '${effectiveTargetBranch}' is in sync with remote.`);
|
|
138
139
|
}
|
|
139
140
|
} else {
|
|
140
|
-
logger.info(`ℹ️ Target branch '${
|
|
141
|
+
logger.info(`ℹ️ Target branch '${effectiveTargetBranch}' does not exist locally - will be created when needed.`);
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
// Check if prepublishOnly script exists in package.json
|
|
@@ -208,6 +209,22 @@ const isReleaseNecessaryComparedToTarget = async (targetBranch, isDryRun)=>{
|
|
|
208
209
|
const logger = getDryRunLogger(isDryRun);
|
|
209
210
|
// We compare current HEAD branch to the provided target branch
|
|
210
211
|
const currentBranch = await getCurrentBranchName();
|
|
212
|
+
// Check if target branch exists before trying to compare
|
|
213
|
+
try {
|
|
214
|
+
// Validate target branch exists and is accessible
|
|
215
|
+
await runSecure('git', [
|
|
216
|
+
'rev-parse',
|
|
217
|
+
'--verify',
|
|
218
|
+
targetBranch
|
|
219
|
+
]);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
// Target branch doesn't exist or isn't accessible
|
|
222
|
+
logger.verbose(`Target branch '${targetBranch}' does not exist or is not accessible. Proceeding with publish.`);
|
|
223
|
+
return {
|
|
224
|
+
necessary: true,
|
|
225
|
+
reason: `Target branch '${targetBranch}' does not exist; first release to this branch`
|
|
226
|
+
};
|
|
227
|
+
}
|
|
211
228
|
// If branches are identical, nothing to release
|
|
212
229
|
const { stdout: namesStdout } = await runSecure('git', [
|
|
213
230
|
'diff',
|
|
@@ -268,11 +285,9 @@ const isReleaseNecessaryComparedToTarget = async (targetBranch, isDryRun)=>{
|
|
|
268
285
|
};
|
|
269
286
|
}
|
|
270
287
|
};
|
|
271
|
-
const handleTargetBranchSyncRecovery = async (runConfig)=>{
|
|
272
|
-
var _runConfig_publish;
|
|
288
|
+
const handleTargetBranchSyncRecovery = async (runConfig, targetBranch)=>{
|
|
273
289
|
const isDryRun = runConfig.dryRun || false;
|
|
274
290
|
const logger = getDryRunLogger(isDryRun);
|
|
275
|
-
const targetBranch = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.targetBranch) || 'main';
|
|
276
291
|
logger.info(`🔄 Attempting to sync target branch '${targetBranch}' with remote...`);
|
|
277
292
|
if (isDryRun) {
|
|
278
293
|
logger.info(`Would attempt to sync '${targetBranch}' with remote`);
|
|
@@ -304,14 +319,105 @@ const execute = async (runConfig)=>{
|
|
|
304
319
|
const storage = create({
|
|
305
320
|
log: logger.info
|
|
306
321
|
});
|
|
307
|
-
|
|
322
|
+
// Get current branch for branch-dependent targeting
|
|
323
|
+
let currentBranch;
|
|
324
|
+
if (isDryRun) {
|
|
325
|
+
currentBranch = 'mock-branch';
|
|
326
|
+
} else {
|
|
327
|
+
currentBranch = await getCurrentBranchName();
|
|
328
|
+
// Fetch latest remote information to avoid conflicts
|
|
329
|
+
logger.info('📡 Fetching latest remote information to avoid conflicts...');
|
|
330
|
+
try {
|
|
331
|
+
await run('git fetch origin');
|
|
332
|
+
logger.info('✅ Fetched latest remote information');
|
|
333
|
+
} catch (error) {
|
|
334
|
+
logger.warn(`⚠️ Could not fetch from remote: ${error.message}`);
|
|
335
|
+
}
|
|
336
|
+
// Sync current branch with remote to avoid conflicts
|
|
337
|
+
logger.info(`🔄 Syncing ${currentBranch} with remote to avoid conflicts...`);
|
|
338
|
+
try {
|
|
339
|
+
const remoteExists = await run(`git ls-remote --exit-code --heads origin ${currentBranch}`).then(()=>true).catch(()=>false);
|
|
340
|
+
if (remoteExists) {
|
|
341
|
+
await run(`git pull origin ${currentBranch} --no-edit`);
|
|
342
|
+
logger.info(`✅ Synced ${currentBranch} with remote`);
|
|
343
|
+
} else {
|
|
344
|
+
logger.info(`ℹ️ No remote ${currentBranch} branch found, will be created on first push`);
|
|
345
|
+
}
|
|
346
|
+
} catch (error) {
|
|
347
|
+
if (error.message && error.message.includes('CONFLICT')) {
|
|
348
|
+
logger.error(`❌ Merge conflicts detected when syncing ${currentBranch} with remote`);
|
|
349
|
+
logger.error(` Please resolve the conflicts manually and then run:`);
|
|
350
|
+
logger.error(` 1. Resolve conflicts in the files`);
|
|
351
|
+
logger.error(` 2. git add <resolved-files>`);
|
|
352
|
+
logger.error(` 3. git commit`);
|
|
353
|
+
logger.error(` 4. kodrdriv publish (to continue)`);
|
|
354
|
+
throw new Error(`Merge conflicts detected when syncing ${currentBranch} with remote. Please resolve conflicts manually.`);
|
|
355
|
+
} else {
|
|
356
|
+
logger.warn(`⚠️ Could not sync with remote ${currentBranch}: ${error.message}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// Determine target branch and version strategy based on branch configuration
|
|
361
|
+
let targetBranch = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.targetBranch) || 'main';
|
|
362
|
+
let branchDependentVersioning = false;
|
|
363
|
+
// Check for branches configuration
|
|
364
|
+
if (runConfig.branches && runConfig.branches[currentBranch]) {
|
|
365
|
+
branchDependentVersioning = true;
|
|
366
|
+
const branchConfig = runConfig.branches[currentBranch];
|
|
367
|
+
if (branchConfig.targetBranch) {
|
|
368
|
+
targetBranch = branchConfig.targetBranch;
|
|
369
|
+
}
|
|
370
|
+
logger.info(`🎯 Branch-dependent targeting enabled:`);
|
|
371
|
+
logger.info(` Source branch: ${currentBranch}`);
|
|
372
|
+
logger.info(` Target branch: ${targetBranch}`);
|
|
373
|
+
// Look at target branch config to show version strategy
|
|
374
|
+
const targetBranchConfig = runConfig.branches[targetBranch];
|
|
375
|
+
if (targetBranchConfig === null || targetBranchConfig === void 0 ? void 0 : targetBranchConfig.version) {
|
|
376
|
+
const versionType = targetBranchConfig.version.type;
|
|
377
|
+
const versionTag = targetBranchConfig.version.tag;
|
|
378
|
+
const versionIncrement = targetBranchConfig.version.increment;
|
|
379
|
+
logger.info(` Target branch version strategy: ${versionType}${versionTag ? ` (tag: ${versionTag})` : ''}${versionIncrement ? ' with increment' : ''}`);
|
|
380
|
+
}
|
|
381
|
+
} else {
|
|
382
|
+
logger.debug(`No branch-specific targeting configured for '${currentBranch}', using default target: ${targetBranch}`);
|
|
383
|
+
}
|
|
308
384
|
// Handle --sync-target flag
|
|
309
385
|
if ((_runConfig_publish1 = runConfig.publish) === null || _runConfig_publish1 === void 0 ? void 0 : _runConfig_publish1.syncTarget) {
|
|
310
|
-
await handleTargetBranchSyncRecovery(runConfig);
|
|
386
|
+
await handleTargetBranchSyncRecovery(runConfig, targetBranch);
|
|
311
387
|
return; // Exit after sync operation
|
|
312
388
|
}
|
|
389
|
+
// Check if target branch exists and create it if needed
|
|
390
|
+
logger.info(`Checking if target branch '${targetBranch}' exists...`);
|
|
391
|
+
if (isDryRun) {
|
|
392
|
+
logger.info(`Would check if target branch '${targetBranch}' exists and create if needed`);
|
|
393
|
+
} else {
|
|
394
|
+
const targetBranchExists = await localBranchExists(targetBranch);
|
|
395
|
+
if (!targetBranchExists) {
|
|
396
|
+
logger.info(`🌟 Target branch '${targetBranch}' does not exist, creating it from current branch...`);
|
|
397
|
+
try {
|
|
398
|
+
// Create the target branch from the current HEAD
|
|
399
|
+
await runSecure('git', [
|
|
400
|
+
'branch',
|
|
401
|
+
targetBranch,
|
|
402
|
+
'HEAD'
|
|
403
|
+
]);
|
|
404
|
+
logger.info(`✅ Created target branch: ${targetBranch}`);
|
|
405
|
+
// Push the new branch to origin
|
|
406
|
+
await runSecure('git', [
|
|
407
|
+
'push',
|
|
408
|
+
'origin',
|
|
409
|
+
targetBranch
|
|
410
|
+
]);
|
|
411
|
+
logger.info(`✅ Pushed new target branch to origin: ${targetBranch}`);
|
|
412
|
+
} catch (error) {
|
|
413
|
+
throw new Error(`Failed to create target branch '${targetBranch}': ${error.message}`);
|
|
414
|
+
}
|
|
415
|
+
} else {
|
|
416
|
+
logger.info(`✅ Target branch '${targetBranch}' already exists`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
313
419
|
// Run prechecks before starting any work
|
|
314
|
-
await runPrechecks(runConfig);
|
|
420
|
+
await runPrechecks(runConfig, targetBranch);
|
|
315
421
|
// Early check: determine if a release is necessary compared to target branch
|
|
316
422
|
logger.info('Evaluating if a release is necessary compared to target branch...');
|
|
317
423
|
try {
|
|
@@ -370,27 +476,142 @@ const execute = async (runConfig)=>{
|
|
|
370
476
|
logger.verbose('No dependency changes to commit, skipping commit.');
|
|
371
477
|
}
|
|
372
478
|
}
|
|
373
|
-
// STEP 3:
|
|
479
|
+
// STEP 3: Merge target branch into working branch to avoid conflicts
|
|
480
|
+
logger.info(`Merging target branch '${targetBranch}' into current branch to avoid version conflicts...`);
|
|
481
|
+
if (isDryRun) {
|
|
482
|
+
logger.info(`Would merge ${targetBranch} into current branch`);
|
|
483
|
+
} else {
|
|
484
|
+
// Fetch the latest target branch
|
|
485
|
+
try {
|
|
486
|
+
await run(`git fetch origin ${targetBranch}:${targetBranch}`);
|
|
487
|
+
logger.info(`✅ Fetched latest ${targetBranch}`);
|
|
488
|
+
} catch (fetchError) {
|
|
489
|
+
logger.warn(`⚠️ Could not fetch ${targetBranch}: ${fetchError.message}`);
|
|
490
|
+
logger.warn('Continuing without merge - PR may have conflicts...');
|
|
491
|
+
}
|
|
492
|
+
// Check if merge is needed (avoid unnecessary merge commits)
|
|
493
|
+
try {
|
|
494
|
+
const { stdout: mergeBase } = await run(`git merge-base HEAD ${targetBranch}`);
|
|
495
|
+
const { stdout: targetCommit } = await run(`git rev-parse ${targetBranch}`);
|
|
496
|
+
if (mergeBase.trim() === targetCommit.trim()) {
|
|
497
|
+
logger.info(`ℹ️ Already up-to-date with ${targetBranch}, no merge needed`);
|
|
498
|
+
} else {
|
|
499
|
+
// Try to merge target branch into current branch
|
|
500
|
+
let mergeSucceeded = false;
|
|
501
|
+
try {
|
|
502
|
+
await run(`git merge ${targetBranch} --no-edit -m "Merge ${targetBranch} to sync before version bump"`);
|
|
503
|
+
logger.info(`✅ Merged ${targetBranch} into current branch`);
|
|
504
|
+
mergeSucceeded = true;
|
|
505
|
+
} catch (mergeError) {
|
|
506
|
+
// If merge conflicts occur, check if they're only in version-related files
|
|
507
|
+
const errorText = [
|
|
508
|
+
mergeError.message || '',
|
|
509
|
+
mergeError.stdout || '',
|
|
510
|
+
mergeError.stderr || ''
|
|
511
|
+
].join(' ');
|
|
512
|
+
if (errorText.includes('CONFLICT')) {
|
|
513
|
+
logger.warn(`⚠️ Merge conflicts detected, attempting automatic resolution...`);
|
|
514
|
+
// Get list of conflicted files
|
|
515
|
+
const { stdout: conflictedFiles } = await run('git diff --name-only --diff-filter=U');
|
|
516
|
+
const conflicts = conflictedFiles.trim().split('\n').filter(Boolean);
|
|
517
|
+
logger.verbose(`Conflicted files: ${conflicts.join(', ')}`);
|
|
518
|
+
// Check if conflicts are only in package.json and package-lock.json
|
|
519
|
+
const versionFiles = [
|
|
520
|
+
'package.json',
|
|
521
|
+
'package-lock.json'
|
|
522
|
+
];
|
|
523
|
+
const nonVersionConflicts = conflicts.filter((f)=>!versionFiles.includes(f));
|
|
524
|
+
if (nonVersionConflicts.length > 0) {
|
|
525
|
+
logger.error(`❌ Cannot auto-resolve: conflicts in non-version files: ${nonVersionConflicts.join(', ')}`);
|
|
526
|
+
logger.error('');
|
|
527
|
+
logger.error('Please resolve conflicts manually:');
|
|
528
|
+
logger.error(' 1. Resolve conflicts in the files listed above');
|
|
529
|
+
logger.error(' 2. git add <resolved-files>');
|
|
530
|
+
logger.error(' 3. git commit');
|
|
531
|
+
logger.error(' 4. kodrdriv publish (to continue)');
|
|
532
|
+
logger.error('');
|
|
533
|
+
throw new Error(`Merge conflicts in non-version files. Please resolve manually.`);
|
|
534
|
+
}
|
|
535
|
+
// Auto-resolve version conflicts by accepting current branch versions
|
|
536
|
+
// (keep our working branch's version, which is likely already updated)
|
|
537
|
+
logger.info(`Auto-resolving version conflicts by keeping current branch versions...`);
|
|
538
|
+
for (const file of conflicts){
|
|
539
|
+
if (versionFiles.includes(file)) {
|
|
540
|
+
await run(`git checkout --ours ${file}`);
|
|
541
|
+
await run(`git add ${file}`);
|
|
542
|
+
logger.verbose(`Resolved ${file} using current branch version`);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
// Complete the merge
|
|
546
|
+
await run(`git commit --no-edit -m "Merge ${targetBranch} to sync before version bump (auto-resolved version conflicts)"`);
|
|
547
|
+
logger.info(`✅ Auto-resolved version conflicts and completed merge`);
|
|
548
|
+
mergeSucceeded = true;
|
|
549
|
+
} else {
|
|
550
|
+
// Not a conflict error, re-throw
|
|
551
|
+
throw mergeError;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
// Only run npm install if merge actually happened
|
|
555
|
+
if (mergeSucceeded) {
|
|
556
|
+
// Run npm install to update package-lock.json based on merged package.json
|
|
557
|
+
logger.info('Running npm install after merge...');
|
|
558
|
+
await run('npm install');
|
|
559
|
+
logger.info('✅ npm install completed');
|
|
560
|
+
// Commit any changes from npm install (e.g., package-lock.json updates)
|
|
561
|
+
const { stdout: mergeChangesStatus } = await run('git status --porcelain');
|
|
562
|
+
if (mergeChangesStatus.trim()) {
|
|
563
|
+
logger.verbose('Staging post-merge changes for commit');
|
|
564
|
+
await run('git add package.json package-lock.json');
|
|
565
|
+
if (await hasStagedChanges()) {
|
|
566
|
+
logger.verbose('Committing post-merge changes...');
|
|
567
|
+
await execute$1(runConfig);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
} catch (error) {
|
|
573
|
+
// Only catch truly unexpected errors here
|
|
574
|
+
logger.error(`❌ Unexpected error during merge: ${error.message}`);
|
|
575
|
+
throw error;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
// STEP 4: Determine and set target version AFTER checks, dependency commit, and target branch merge
|
|
374
579
|
logger.info('Determining target version...');
|
|
375
580
|
let newVersion;
|
|
376
581
|
if (isDryRun) {
|
|
377
582
|
logger.info('Would determine target version and update package.json');
|
|
378
583
|
newVersion = '1.0.0'; // Mock version for dry run
|
|
379
584
|
} else {
|
|
380
|
-
var _runConfig_publish8
|
|
585
|
+
var _runConfig_publish8;
|
|
381
586
|
const packageJsonContents = await storage.readFile('package.json', 'utf-8');
|
|
382
587
|
const parsed = safeJsonParse(packageJsonContents, 'package.json');
|
|
383
588
|
const packageJson = validatePackageJson(parsed, 'package.json');
|
|
384
589
|
const currentVersion = packageJson.version;
|
|
385
|
-
|
|
386
|
-
|
|
590
|
+
let proposedVersion;
|
|
591
|
+
let finalTargetBranch = targetBranch;
|
|
592
|
+
if (branchDependentVersioning && runConfig.branches) {
|
|
593
|
+
// Use branch-dependent versioning logic
|
|
594
|
+
const branchDependentResult = await calculateBranchDependentVersion(currentVersion, currentBranch, runConfig.branches, targetBranch);
|
|
595
|
+
proposedVersion = branchDependentResult.version;
|
|
596
|
+
finalTargetBranch = branchDependentResult.targetBranch;
|
|
597
|
+
logger.info(`🎯 Branch-dependent version calculated: ${currentVersion} → ${proposedVersion}`);
|
|
598
|
+
logger.info(`🎯 Final target branch: ${finalTargetBranch}`);
|
|
599
|
+
// Update targetBranch for the rest of the function
|
|
600
|
+
targetBranch = finalTargetBranch;
|
|
601
|
+
} else {
|
|
602
|
+
var _runConfig_publish9;
|
|
603
|
+
// Use existing logic for backward compatibility
|
|
604
|
+
const targetVersionInput = ((_runConfig_publish9 = runConfig.publish) === null || _runConfig_publish9 === void 0 ? void 0 : _runConfig_publish9.targetVersion) || 'patch';
|
|
605
|
+
proposedVersion = calculateTargetVersion(currentVersion, targetVersionInput);
|
|
606
|
+
}
|
|
387
607
|
const targetTagName = `v${proposedVersion}`;
|
|
388
608
|
const tagExists = await checkIfTagExists(targetTagName);
|
|
389
609
|
if (tagExists) {
|
|
390
610
|
throw new Error(`Tag ${targetTagName} already exists. Please choose a different version or delete the existing tag.`);
|
|
391
611
|
}
|
|
392
|
-
if ((
|
|
393
|
-
|
|
612
|
+
if ((_runConfig_publish8 = runConfig.publish) === null || _runConfig_publish8 === void 0 ? void 0 : _runConfig_publish8.interactive) {
|
|
613
|
+
var _runConfig_publish10;
|
|
614
|
+
newVersion = await confirmVersionInteractively(currentVersion, proposedVersion, (_runConfig_publish10 = runConfig.publish) === null || _runConfig_publish10 === void 0 ? void 0 : _runConfig_publish10.targetVersion);
|
|
394
615
|
const confirmedTagName = `v${newVersion}`;
|
|
395
616
|
const confirmedTagExists = await checkIfTagExists(confirmedTagName);
|
|
396
617
|
if (confirmedTagExists) {
|
|
@@ -404,7 +625,7 @@ const execute = async (runConfig)=>{
|
|
|
404
625
|
await storage.writeFile('package.json', JSON.stringify(packageJson, null, 2) + '\n', 'utf-8');
|
|
405
626
|
logger.info(`Version updated in package.json: ${newVersion}`);
|
|
406
627
|
}
|
|
407
|
-
// STEP
|
|
628
|
+
// STEP 5: Commit version bump as a separate commit
|
|
408
629
|
logger.verbose('Staging version bump for commit');
|
|
409
630
|
await runWithDryRunSupport('git add package.json package-lock.json', isDryRun);
|
|
410
631
|
if (isDryRun) {
|
|
@@ -460,8 +681,8 @@ const execute = async (runConfig)=>{
|
|
|
460
681
|
}
|
|
461
682
|
logger.info('Pushing to origin...');
|
|
462
683
|
// Get current branch name and push explicitly to avoid pushing to wrong remote/branch
|
|
463
|
-
const
|
|
464
|
-
await runWithDryRunSupport(`git push origin ${
|
|
684
|
+
const branchName = await getCurrentBranchName();
|
|
685
|
+
await runWithDryRunSupport(`git push origin ${branchName}`, isDryRun);
|
|
465
686
|
logger.info('Creating pull request...');
|
|
466
687
|
if (isDryRun) {
|
|
467
688
|
logger.info('Would get commit title and create PR with GitHub API');
|
|
@@ -472,21 +693,21 @@ const execute = async (runConfig)=>{
|
|
|
472
693
|
};
|
|
473
694
|
} else {
|
|
474
695
|
const { stdout: commitTitle } = await run('git log -1 --pretty=%B');
|
|
475
|
-
pr = await createPullRequest(commitTitle, 'Automated release PR.',
|
|
696
|
+
pr = await createPullRequest(commitTitle, 'Automated release PR.', branchName, targetBranch);
|
|
476
697
|
if (!pr) {
|
|
477
698
|
throw new Error('Failed to create pull request.');
|
|
478
699
|
}
|
|
479
|
-
logger.info(`Pull request created: ${pr.html_url}`);
|
|
700
|
+
logger.info(`Pull request created: ${pr.html_url} (${branchName} → ${targetBranch})`);
|
|
480
701
|
}
|
|
481
702
|
}
|
|
482
703
|
logger.info(`Waiting for PR #${pr.number} checks to complete...`);
|
|
483
704
|
if (!isDryRun) {
|
|
484
|
-
var
|
|
705
|
+
var _runConfig_publish11, _runConfig_publish12, _runConfig_publish13;
|
|
485
706
|
// Configure timeout and user confirmation behavior
|
|
486
|
-
const timeout = ((
|
|
487
|
-
const senditMode = ((
|
|
707
|
+
const timeout = ((_runConfig_publish11 = runConfig.publish) === null || _runConfig_publish11 === void 0 ? void 0 : _runConfig_publish11.checksTimeout) || KODRDRIV_DEFAULTS.publish.checksTimeout;
|
|
708
|
+
const senditMode = ((_runConfig_publish12 = runConfig.publish) === null || _runConfig_publish12 === void 0 ? void 0 : _runConfig_publish12.sendit) || false;
|
|
488
709
|
// sendit flag overrides skipUserConfirmation - if sendit is true, skip confirmation
|
|
489
|
-
const skipUserConfirmation = senditMode || ((
|
|
710
|
+
const skipUserConfirmation = senditMode || ((_runConfig_publish13 = runConfig.publish) === null || _runConfig_publish13 === void 0 ? void 0 : _runConfig_publish13.skipUserConfirmation) || false;
|
|
490
711
|
await waitForPullRequestChecks(pr.number, {
|
|
491
712
|
timeout,
|
|
492
713
|
skipUserConfirmation
|
|
@@ -539,7 +760,37 @@ const execute = async (runConfig)=>{
|
|
|
539
760
|
}
|
|
540
761
|
try {
|
|
541
762
|
await runWithDryRunSupport(`git checkout ${targetBranch}`, isDryRun);
|
|
542
|
-
|
|
763
|
+
// Sync target branch with remote to avoid conflicts during PR creation
|
|
764
|
+
if (!isDryRun) {
|
|
765
|
+
logger.info(`🔄 Syncing ${targetBranch} with remote to avoid PR conflicts...`);
|
|
766
|
+
try {
|
|
767
|
+
const remoteExists = await run(`git ls-remote --exit-code --heads origin ${targetBranch}`).then(()=>true).catch(()=>false);
|
|
768
|
+
if (remoteExists) {
|
|
769
|
+
await run(`git pull origin ${targetBranch} --no-edit`);
|
|
770
|
+
logger.info(`✅ Synced ${targetBranch} with remote`);
|
|
771
|
+
} else {
|
|
772
|
+
logger.info(`ℹ️ No remote ${targetBranch} branch found, will be created on first push`);
|
|
773
|
+
}
|
|
774
|
+
} catch (syncError) {
|
|
775
|
+
if (syncError.message && syncError.message.includes('CONFLICT')) {
|
|
776
|
+
logger.error(`❌ Merge conflicts detected when syncing ${targetBranch} with remote`);
|
|
777
|
+
logger.error(` Please resolve the conflicts manually:`);
|
|
778
|
+
logger.error(` 1. git checkout ${targetBranch}`);
|
|
779
|
+
logger.error(` 2. git pull origin ${targetBranch}`);
|
|
780
|
+
logger.error(` 3. Resolve conflicts in the files`);
|
|
781
|
+
logger.error(` 4. git add <resolved-files>`);
|
|
782
|
+
logger.error(` 5. git commit`);
|
|
783
|
+
logger.error(` 6. git checkout ${currentBranch}`);
|
|
784
|
+
logger.error(` 7. kodrdriv publish (to continue)`);
|
|
785
|
+
throw syncError;
|
|
786
|
+
} else {
|
|
787
|
+
logger.warn(`⚠️ Could not sync ${targetBranch} with remote: ${syncError.message}`);
|
|
788
|
+
// Continue with publish process, but log the warning
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
} else {
|
|
792
|
+
logger.info(`Would sync ${targetBranch} with remote to avoid PR conflicts`);
|
|
793
|
+
}
|
|
543
794
|
} catch (error) {
|
|
544
795
|
// Check if this is a merge conflict or sync issue
|
|
545
796
|
if (!isDryRun && (error.message.includes('conflict') || error.message.includes('CONFLICT') || error.message.includes('diverged') || error.message.includes('non-fast-forward'))) {
|
|
@@ -657,9 +908,9 @@ const execute = async (runConfig)=>{
|
|
|
657
908
|
}
|
|
658
909
|
logger.info('Creating GitHub release...');
|
|
659
910
|
if (isDryRun) {
|
|
660
|
-
var
|
|
911
|
+
var _runConfig_publish14;
|
|
661
912
|
logger.info('Would read package.json version and create GitHub release with retry logic');
|
|
662
|
-
const milestonesEnabled = !((
|
|
913
|
+
const milestonesEnabled = !((_runConfig_publish14 = runConfig.publish) === null || _runConfig_publish14 === void 0 ? void 0 : _runConfig_publish14.noMilestones);
|
|
663
914
|
if (milestonesEnabled) {
|
|
664
915
|
logger.info('Would close milestone for released version');
|
|
665
916
|
} else {
|
|
@@ -675,11 +926,11 @@ const execute = async (runConfig)=>{
|
|
|
675
926
|
let retries = 3;
|
|
676
927
|
while(retries > 0){
|
|
677
928
|
try {
|
|
678
|
-
var
|
|
929
|
+
var _runConfig_publish15;
|
|
679
930
|
await createRelease(tagName, releaseTitle, releaseNotesContent);
|
|
680
931
|
logger.info(`GitHub release created successfully for tag: ${tagName}`);
|
|
681
932
|
// Close milestone for this version if enabled
|
|
682
|
-
const milestonesEnabled = !((
|
|
933
|
+
const milestonesEnabled = !((_runConfig_publish15 = runConfig.publish) === null || _runConfig_publish15 === void 0 ? void 0 : _runConfig_publish15.noMilestones);
|
|
683
934
|
if (milestonesEnabled) {
|
|
684
935
|
logger.info('🏁 Closing milestone for released version...');
|
|
685
936
|
const version = tagName.replace(/^v/, ''); // Remove 'v' prefix if present
|
|
@@ -712,12 +963,12 @@ const execute = async (runConfig)=>{
|
|
|
712
963
|
if (isDryRun) {
|
|
713
964
|
logger.info('Would monitor GitHub Actions workflows triggered by release');
|
|
714
965
|
} else {
|
|
715
|
-
var
|
|
716
|
-
const workflowTimeout = ((
|
|
717
|
-
const senditMode = ((
|
|
718
|
-
const skipUserConfirmation = senditMode || ((
|
|
966
|
+
var _runConfig_publish16, _runConfig_publish17, _runConfig_publish18, _runConfig_publish19;
|
|
967
|
+
const workflowTimeout = ((_runConfig_publish16 = runConfig.publish) === null || _runConfig_publish16 === void 0 ? void 0 : _runConfig_publish16.releaseWorkflowsTimeout) || KODRDRIV_DEFAULTS.publish.releaseWorkflowsTimeout;
|
|
968
|
+
const senditMode = ((_runConfig_publish17 = runConfig.publish) === null || _runConfig_publish17 === void 0 ? void 0 : _runConfig_publish17.sendit) || false;
|
|
969
|
+
const skipUserConfirmation = senditMode || ((_runConfig_publish18 = runConfig.publish) === null || _runConfig_publish18 === void 0 ? void 0 : _runConfig_publish18.skipUserConfirmation) || false;
|
|
719
970
|
// Get workflow names - either from config or auto-detect
|
|
720
|
-
let workflowNames = (
|
|
971
|
+
let workflowNames = (_runConfig_publish19 = runConfig.publish) === null || _runConfig_publish19 === void 0 ? void 0 : _runConfig_publish19.releaseWorkflowNames;
|
|
721
972
|
if (!workflowNames || workflowNames.length === 0) {
|
|
722
973
|
logger.info('No specific workflow names configured, auto-detecting workflows triggered by release events...');
|
|
723
974
|
try {
|