@eldrforge/kodrdriv 0.0.51 → 1.2.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.
Files changed (85) hide show
  1. package/README.md +3 -1
  2. package/dist/application.js +29 -5
  3. package/dist/application.js.map +1 -1
  4. package/dist/arguments.js +187 -75
  5. package/dist/arguments.js.map +1 -1
  6. package/dist/commands/audio-commit.js +35 -14
  7. package/dist/commands/audio-commit.js.map +1 -1
  8. package/dist/commands/audio-review.js +41 -20
  9. package/dist/commands/audio-review.js.map +1 -1
  10. package/dist/commands/clean.js +2 -2
  11. package/dist/commands/clean.js.map +1 -1
  12. package/dist/commands/commit.js +369 -47
  13. package/dist/commands/commit.js.map +1 -1
  14. package/dist/commands/development.js +264 -0
  15. package/dist/commands/development.js.map +1 -0
  16. package/dist/commands/link.js +357 -182
  17. package/dist/commands/link.js.map +1 -1
  18. package/dist/commands/publish.js +419 -306
  19. package/dist/commands/publish.js.map +1 -1
  20. package/dist/commands/release.js +240 -18
  21. package/dist/commands/release.js.map +1 -1
  22. package/dist/commands/review.js +56 -40
  23. package/dist/commands/review.js.map +1 -1
  24. package/dist/commands/select-audio.js +4 -4
  25. package/dist/commands/select-audio.js.map +1 -1
  26. package/dist/commands/tree.js +779 -40
  27. package/dist/commands/tree.js.map +1 -1
  28. package/dist/commands/unlink.js +267 -372
  29. package/dist/commands/unlink.js.map +1 -1
  30. package/dist/commands/versions.js +224 -0
  31. package/dist/commands/versions.js.map +1 -0
  32. package/dist/constants.js +50 -12
  33. package/dist/constants.js.map +1 -1
  34. package/dist/content/diff.js +122 -1
  35. package/dist/content/diff.js.map +1 -1
  36. package/dist/content/files.js +192 -0
  37. package/dist/content/files.js.map +1 -0
  38. package/dist/content/issues.js +17 -46
  39. package/dist/content/issues.js.map +1 -1
  40. package/dist/content/log.js +16 -0
  41. package/dist/content/log.js.map +1 -1
  42. package/dist/logging.js +3 -3
  43. package/dist/logging.js.map +1 -1
  44. package/dist/main.js +0 -0
  45. package/dist/prompt/commit.js +11 -4
  46. package/dist/prompt/commit.js.map +1 -1
  47. package/dist/prompt/instructions/commit.md +20 -2
  48. package/dist/prompt/instructions/release.md +27 -10
  49. package/dist/prompt/instructions/review.md +75 -8
  50. package/dist/prompt/release.js +15 -7
  51. package/dist/prompt/release.js.map +1 -1
  52. package/dist/prompt/review.js +2 -2
  53. package/dist/prompt/review.js.map +1 -1
  54. package/dist/types.js +36 -7
  55. package/dist/types.js.map +1 -1
  56. package/dist/util/child.js +146 -4
  57. package/dist/util/child.js.map +1 -1
  58. package/dist/util/countdown.js +215 -0
  59. package/dist/util/countdown.js.map +1 -0
  60. package/dist/util/general.js +157 -13
  61. package/dist/util/general.js.map +1 -1
  62. package/dist/util/git.js +587 -0
  63. package/dist/util/git.js.map +1 -0
  64. package/dist/util/github.js +531 -11
  65. package/dist/util/github.js.map +1 -1
  66. package/dist/util/interactive.js +463 -0
  67. package/dist/util/interactive.js.map +1 -0
  68. package/dist/util/openai.js +152 -25
  69. package/dist/util/openai.js.map +1 -1
  70. package/dist/util/performance.js +5 -73
  71. package/dist/util/performance.js.map +1 -1
  72. package/dist/util/safety.js +4 -4
  73. package/dist/util/safety.js.map +1 -1
  74. package/dist/util/storage.js +30 -3
  75. package/dist/util/storage.js.map +1 -1
  76. package/dist/util/validation.js +1 -25
  77. package/dist/util/validation.js.map +1 -1
  78. package/package.json +12 -10
  79. package/test-increment.js +0 -0
  80. package/test-multiline/cli/package.json +8 -0
  81. package/test-multiline/core/package.json +5 -0
  82. package/test-multiline/mobile/package.json +8 -0
  83. package/test-multiline/web/package.json +8 -0
  84. package/dist/util/npmOptimizations.js +0 -174
  85. package/dist/util/npmOptimizations.js.map +0 -1
@@ -1,20 +1,19 @@
1
- import path from 'path';
2
- import { execute as execute$2 } from './commit.js';
1
+ import path__default from 'path';
2
+ import { execute as execute$1 } from './commit.js';
3
3
  import { hasStagedChanges } from '../content/diff.js';
4
- import { execute as execute$3 } from './release.js';
5
- import { execute as execute$4 } from './link.js';
6
- import { execute as execute$1 } from './unlink.js';
4
+ import { execute as execute$2 } from './release.js';
7
5
  import { getDryRunLogger, getLogger } from '../logging.js';
8
- import { runWithDryRunSupport, run } from '../util/child.js';
9
- import { getCurrentBranchName, findOpenPullRequestByHeadRef, createPullRequest, waitForPullRequestChecks, mergePullRequest, createRelease, getWorkflowsTriggeredByRelease, waitForReleaseWorkflows } from '../util/github.js';
6
+ import { runWithDryRunSupport, run, validateGitRef, runSecure } from '../util/child.js';
7
+ import { getCurrentBranchName, findOpenPullRequestByHeadRef, createPullRequest, waitForPullRequestChecks, mergePullRequest, createRelease, closeMilestoneForVersion, getWorkflowsTriggeredByRelease, waitForReleaseWorkflows } from '../util/github.js';
10
8
  import { create } from '../util/storage.js';
11
- import { incrementPatchVersion, getOutputPath } from '../util/general.js';
9
+ import { calculateTargetVersion, checkIfTagExists, confirmVersionInteractively, getOutputPath } from '../util/general.js';
12
10
  import { DEFAULT_OUTPUT_DIRECTORY } from '../constants.js';
13
11
  import { safeJsonParse, validatePackageJson } from '../util/validation.js';
12
+ import { safeSyncBranchWithRemote, localBranchExists, isBranchInSyncWithRemote } from '../util/git.js';
14
13
 
15
14
  const scanNpmrcForEnvVars = async (storage)=>{
16
15
  const logger = getLogger();
17
- const npmrcPath = path.join(process.cwd(), '.npmrc');
16
+ const npmrcPath = path__default.join(process.cwd(), '.npmrc');
18
17
  const envVars = [];
19
18
  if (await storage.exists(npmrcPath)) {
20
19
  try {
@@ -57,7 +56,7 @@ const validateEnvironmentVariables = (requiredEnvVars, isDryRun)=>{
57
56
  }
58
57
  };
59
58
  const runPrechecks = async (runConfig)=>{
60
- var _runConfig_publish;
59
+ var _runConfig_publish, _runConfig_publish1;
61
60
  const isDryRun = runConfig.dryRun || false;
62
61
  const logger = getDryRunLogger(isDryRun);
63
62
  const storage = create({
@@ -96,19 +95,54 @@ const runPrechecks = async (runConfig)=>{
96
95
  throw new Error(`Failed to check git status: ${originalMessage}. Please ensure you are in a valid git repository and try again.`);
97
96
  }
98
97
  }
99
- // Check if we're on a release branch
98
+ // Check that we're not running from the target branch
100
99
  logger.info('Checking current branch...');
100
+ const targetBranch = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.targetBranch) || 'main';
101
101
  if (isDryRun) {
102
- logger.info('Would verify current branch is a release branch (starts with "release/")');
102
+ logger.info(`Would verify current branch is not the target branch (${targetBranch})`);
103
103
  } else {
104
104
  const currentBranch = await getCurrentBranchName();
105
- if (!currentBranch.startsWith('release/')) {
106
- throw new Error(`Current branch '${currentBranch}' is not a release branch. Please switch to a release branch (e.g., release/1.0.0) before running publish.`);
105
+ if (currentBranch === targetBranch) {
106
+ throw new Error(`Cannot run publish from the target branch '${targetBranch}'. Please switch to a different branch before running publish.`);
107
+ }
108
+ }
109
+ // Check target branch sync with remote
110
+ logger.info(`Checking target branch '${targetBranch}' sync with remote...`);
111
+ if (isDryRun) {
112
+ logger.info(`Would verify target branch '${targetBranch}' is in sync with remote origin`);
113
+ } else {
114
+ // Only check if local target branch exists (it's okay if it doesn't exist locally)
115
+ const targetBranchExists = await localBranchExists(targetBranch);
116
+ if (targetBranchExists) {
117
+ const syncStatus = await isBranchInSyncWithRemote(targetBranch);
118
+ if (!syncStatus.inSync) {
119
+ logger.error(`❌ Target branch '${targetBranch}' is not in sync with remote.`);
120
+ logger.error('');
121
+ if (syncStatus.error) {
122
+ logger.error(` Error: ${syncStatus.error}`);
123
+ } else if (syncStatus.localSha && syncStatus.remoteSha) {
124
+ logger.error(` Local: ${syncStatus.localSha.substring(0, 8)}`);
125
+ logger.error(` Remote: ${syncStatus.remoteSha.substring(0, 8)}`);
126
+ }
127
+ logger.error('');
128
+ logger.error('📋 To resolve this issue:');
129
+ logger.error(` 1. Switch to the target branch: git checkout ${targetBranch}`);
130
+ logger.error(` 2. Pull the latest changes: git pull origin ${targetBranch}`);
131
+ logger.error(' 3. Resolve any merge conflicts if they occur');
132
+ logger.error(' 4. Switch back to your feature branch and re-run publish');
133
+ logger.error('');
134
+ logger.error('💡 Alternatively, run "kodrdriv publish --sync-target" to attempt automatic sync.');
135
+ throw new Error(`Target branch '${targetBranch}' is not in sync with remote. Please sync the branch before running publish.`);
136
+ } else {
137
+ logger.info(`✅ Target branch '${targetBranch}' is in sync with remote.`);
138
+ }
139
+ } else {
140
+ logger.info(`ℹ️ Target branch '${targetBranch}' does not exist locally - will be created when needed.`);
107
141
  }
108
142
  }
109
143
  // Check if prepublishOnly script exists in package.json
110
144
  logger.info('Checking for prepublishOnly script...');
111
- const packageJsonPath = path.join(process.cwd(), 'package.json');
145
+ const packageJsonPath = path__default.join(process.cwd(), 'package.json');
112
146
  if (!await storage.exists(packageJsonPath)) {
113
147
  if (!isDryRun) {
114
148
  throw new Error('package.json not found in current directory.');
@@ -139,7 +173,7 @@ const runPrechecks = async (runConfig)=>{
139
173
  }
140
174
  // Check required environment variables
141
175
  logger.verbose('Checking required environment variables...');
142
- const coreRequiredEnvVars = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.requiredEnvVars) || [];
176
+ const coreRequiredEnvVars = ((_runConfig_publish1 = runConfig.publish) === null || _runConfig_publish1 === void 0 ? void 0 : _runConfig_publish1.requiredEnvVars) || [];
143
177
  const npmrcEnvVars = isDryRun ? [] : await scanNpmrcForEnvVars(storage); // Skip .npmrc scan in dry run
144
178
  const allRequiredEnvVars = [
145
179
  ...new Set([
@@ -155,343 +189,422 @@ const runPrechecks = async (runConfig)=>{
155
189
  }
156
190
  logger.info('All prechecks passed.');
157
191
  };
192
+ const handleTargetBranchSyncRecovery = async (runConfig)=>{
193
+ var _runConfig_publish;
194
+ const isDryRun = runConfig.dryRun || false;
195
+ const logger = getDryRunLogger(isDryRun);
196
+ const targetBranch = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.targetBranch) || 'main';
197
+ logger.info(`🔄 Attempting to sync target branch '${targetBranch}' with remote...`);
198
+ if (isDryRun) {
199
+ logger.info(`Would attempt to sync '${targetBranch}' with remote`);
200
+ return;
201
+ }
202
+ const syncResult = await safeSyncBranchWithRemote(targetBranch);
203
+ if (syncResult.success) {
204
+ logger.info(`✅ Successfully synced '${targetBranch}' with remote.`);
205
+ logger.info('You can now re-run the publish command.');
206
+ } else if (syncResult.conflictResolutionRequired) {
207
+ logger.error(`❌ Failed to sync '${targetBranch}': conflicts detected.`);
208
+ logger.error('');
209
+ logger.error('📋 Manual conflict resolution required:');
210
+ logger.error(` 1. Switch to the target branch: git checkout ${targetBranch}`);
211
+ logger.error(` 2. Pull and resolve conflicts: git pull origin ${targetBranch}`);
212
+ logger.error(' 3. Commit the resolved changes');
213
+ logger.error(' 4. Switch back to your feature branch and re-run publish');
214
+ logger.error('');
215
+ throw new Error(`Target branch '${targetBranch}' has conflicts that require manual resolution.`);
216
+ } else {
217
+ logger.error(`❌ Failed to sync '${targetBranch}': ${syncResult.error}`);
218
+ throw new Error(`Failed to sync target branch: ${syncResult.error}`);
219
+ }
220
+ };
158
221
  const execute = async (runConfig)=>{
222
+ var _runConfig_publish, _runConfig_publish1, _runConfig_publish2, _runConfig_publish3;
159
223
  const isDryRun = runConfig.dryRun || false;
160
224
  const logger = getDryRunLogger(isDryRun);
161
225
  const storage = create({
162
226
  log: logger.info
163
227
  });
164
- // Track whether the publish process completed successfully
165
- let publishCompleted = false;
166
- // Track whether we've unlinked packages (and thus need to restore them)
167
- let packagesUnlinked = false;
228
+ const targetBranch = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.targetBranch) || 'main';
229
+ // Handle --sync-target flag
230
+ if ((_runConfig_publish1 = runConfig.publish) === null || _runConfig_publish1 === void 0 ? void 0 : _runConfig_publish1.syncTarget) {
231
+ await handleTargetBranchSyncRecovery(runConfig);
232
+ return; // Exit after sync operation
233
+ }
168
234
  // Run prechecks before starting any work
169
235
  await runPrechecks(runConfig);
170
236
  logger.info('Starting release process...');
171
- try {
172
- var _runConfig_publish, _runConfig_publish1, _runConfig_publish2;
173
- // Unlink all workspace packages before starting (if enabled)
174
- const shouldUnlink = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.unlinkWorkspacePackages) !== false; // default to true
175
- if (shouldUnlink) {
176
- logger.verbose('Unlinking workspace packages...');
177
- await execute$1(runConfig);
178
- packagesUnlinked = true;
237
+ let pr = null;
238
+ if (isDryRun) {
239
+ logger.info('Would check for existing pull request');
240
+ logger.info('Assuming no existing PR found for demo purposes');
241
+ } else {
242
+ const branchName = await getCurrentBranchName();
243
+ pr = await findOpenPullRequestByHeadRef(branchName);
244
+ }
245
+ if (pr) {
246
+ logger.info(`Found existing pull request for branch: ${pr.html_url}`);
247
+ } else {
248
+ var _runConfig_publish4, _runConfig_publish5, _runConfig_publish6;
249
+ logger.info('No open pull request found, starting new release publishing process...');
250
+ // STEP 1: Determine and set target version FIRST (before any commits)
251
+ logger.info('Determining target version...');
252
+ let newVersion;
253
+ if (isDryRun) {
254
+ logger.info('Would determine target version and update package.json');
255
+ newVersion = '1.0.0'; // Mock version for dry run
179
256
  } else {
180
- logger.verbose('Skipping unlink workspace packages (disabled in config).');
257
+ var _runConfig_publish7, _runConfig_publish8;
258
+ const packageJsonContents = await storage.readFile('package.json', 'utf-8');
259
+ const parsed = safeJsonParse(packageJsonContents, 'package.json');
260
+ const packageJson = validatePackageJson(parsed, 'package.json');
261
+ const currentVersion = packageJson.version;
262
+ // Determine target version based on --targetVersion option
263
+ const targetVersionInput = ((_runConfig_publish7 = runConfig.publish) === null || _runConfig_publish7 === void 0 ? void 0 : _runConfig_publish7.targetVersion) || 'patch';
264
+ const proposedVersion = calculateTargetVersion(currentVersion, targetVersionInput);
265
+ // Check if target tag already exists
266
+ const targetTagName = `v${proposedVersion}`;
267
+ const tagExists = await checkIfTagExists(targetTagName);
268
+ if (tagExists) {
269
+ throw new Error(`Tag ${targetTagName} already exists. Please choose a different version or delete the existing tag.`);
270
+ }
271
+ // Interactive confirmation if --interactive flag is set
272
+ if ((_runConfig_publish8 = runConfig.publish) === null || _runConfig_publish8 === void 0 ? void 0 : _runConfig_publish8.interactive) {
273
+ newVersion = await confirmVersionInteractively(currentVersion, proposedVersion, targetVersionInput);
274
+ // Re-check if the confirmed version's tag exists (in case user entered custom version)
275
+ const confirmedTagName = `v${newVersion}`;
276
+ const confirmedTagExists = await checkIfTagExists(confirmedTagName);
277
+ if (confirmedTagExists) {
278
+ throw new Error(`Tag ${confirmedTagName} already exists. Please choose a different version or delete the existing tag.`);
279
+ }
280
+ } else {
281
+ newVersion = proposedVersion;
282
+ }
283
+ logger.info(`Bumping version from ${currentVersion} to ${newVersion}`);
284
+ // Update package.json with the new version BEFORE any other operations
285
+ packageJson.version = newVersion;
286
+ await storage.writeFile('package.json', JSON.stringify(packageJson, null, 2) + '\n', 'utf-8');
287
+ logger.info(`Version updated in package.json: ${newVersion}`);
181
288
  }
182
- let pr = null;
183
- if (isDryRun) {
184
- logger.info('Would check for existing pull request');
185
- logger.info('Assuming no existing PR found for demo purposes');
289
+ // STEP 2: Prepare for release (with correct version now in package.json)
290
+ logger.verbose('Preparing for release: switching from workspace to remote dependencies.');
291
+ logger.verbose('Updating dependencies to latest versions from registry');
292
+ const updatePatterns = (_runConfig_publish4 = runConfig.publish) === null || _runConfig_publish4 === void 0 ? void 0 : _runConfig_publish4.dependencyUpdatePatterns;
293
+ if (updatePatterns && updatePatterns.length > 0) {
294
+ logger.verbose(`Updating dependencies matching patterns: ${updatePatterns.join(', ')}`);
295
+ const patternsArg = updatePatterns.join(' ');
296
+ await runWithDryRunSupport(`npm update ${patternsArg}`, isDryRun);
186
297
  } else {
187
- const branchName = await getCurrentBranchName();
188
- pr = await findOpenPullRequestByHeadRef(branchName);
298
+ logger.verbose('No dependency update patterns specified, updating all dependencies');
299
+ await runWithDryRunSupport('npm update', isDryRun);
189
300
  }
190
- if (pr) {
191
- logger.info(`Found existing pull request for branch: ${pr.html_url}`);
301
+ logger.info('Running prepublishOnly script...');
302
+ await runWithDryRunSupport('npm run prepublishOnly', isDryRun, {}, true); // Use inherited stdio
303
+ // STEP 3: Stage all changes (version bump + dependencies + any build artifacts)
304
+ logger.verbose('Staging all changes for release commit');
305
+ await runWithDryRunSupport('git add package.json package-lock.json', isDryRun);
306
+ logger.verbose('Checking for staged changes...');
307
+ if (isDryRun) {
308
+ logger.verbose('Assuming staged changes exist for demo purposes');
309
+ logger.verbose('Would create commit...');
310
+ await execute$1(runConfig);
192
311
  } else {
193
- var _runConfig_publish3;
194
- logger.info('No open pull request found, starting new release publishing process...');
195
- // 1. Prepare for release
196
- logger.verbose('Preparing for release: switching from workspace to remote dependencies.');
197
- logger.verbose('Updating dependencies to latest versions from registry');
198
- const updatePatterns = (_runConfig_publish3 = runConfig.publish) === null || _runConfig_publish3 === void 0 ? void 0 : _runConfig_publish3.dependencyUpdatePatterns;
199
- if (updatePatterns && updatePatterns.length > 0) {
200
- logger.verbose(`Updating dependencies matching patterns: ${updatePatterns.join(', ')}`);
201
- const patternsArg = updatePatterns.join(' ');
202
- await runWithDryRunSupport(`npm update ${patternsArg}`, isDryRun);
312
+ if (await hasStagedChanges()) {
313
+ logger.verbose('Staged changes found, creating commit...');
314
+ await execute$1(runConfig);
203
315
  } else {
204
- logger.verbose('No dependency update patterns specified, updating all dependencies');
205
- await runWithDryRunSupport('npm update', isDryRun);
316
+ logger.verbose('No changes to commit, skipping commit.');
206
317
  }
207
- logger.verbose('Staging changes for release commit');
208
- await runWithDryRunSupport('git add package.json package-lock.json', isDryRun);
209
- logger.info('Running prepublishOnly script...');
210
- await runWithDryRunSupport('npm run prepublishOnly', isDryRun);
211
- logger.verbose('Checking for staged changes...');
212
- if (isDryRun) {
213
- logger.verbose('Assuming staged changes exist for demo purposes');
214
- logger.verbose('Would create commit...');
215
- await execute$2(runConfig);
216
- } else {
217
- if (await hasStagedChanges()) {
218
- logger.verbose('Staged changes found, creating commit...');
219
- await execute$2(runConfig);
220
- } else {
221
- logger.verbose('No changes to commit, skipping commit.');
318
+ }
319
+ logger.info('Generating release notes...');
320
+ // Create a modified config for release notes generation that includes the publish --from and --interactive options
321
+ const releaseConfig = {
322
+ ...runConfig
323
+ };
324
+ if (((_runConfig_publish5 = runConfig.publish) === null || _runConfig_publish5 === void 0 ? void 0 : _runConfig_publish5.from) || ((_runConfig_publish6 = runConfig.publish) === null || _runConfig_publish6 === void 0 ? void 0 : _runConfig_publish6.interactive)) {
325
+ // Pass the publish --from and --interactive options to the release config
326
+ releaseConfig.release = {
327
+ ...runConfig.release,
328
+ ...runConfig.publish.from && {
329
+ from: runConfig.publish.from
330
+ },
331
+ ...runConfig.publish.interactive && {
332
+ interactive: runConfig.publish.interactive
222
333
  }
334
+ };
335
+ if (runConfig.publish.from) {
336
+ logger.verbose(`Using custom 'from' reference for release notes: ${runConfig.publish.from}`);
223
337
  }
224
- logger.info('Bumping version...');
225
- // Manually increment version without creating a tag
226
- if (isDryRun) {
227
- logger.info('Would manually increment patch version in package.json and commit');
228
- } else {
229
- const packageJsonContents = await storage.readFile('package.json', 'utf-8');
230
- const parsed = safeJsonParse(packageJsonContents, 'package.json');
231
- const packageJson = validatePackageJson(parsed, 'package.json');
232
- const currentVersion = packageJson.version;
233
- const newVersion = incrementPatchVersion(currentVersion);
234
- packageJson.version = newVersion;
235
- await storage.writeFile('package.json', JSON.stringify(packageJson, null, 2) + '\n', 'utf-8');
236
- logger.info(`Version bumped from ${currentVersion} to ${newVersion}`);
237
- // Stage and commit the version change
238
- await run('git add package.json');
239
- await run(`git commit -m "chore: bump version to ${newVersion}"`);
240
- logger.info(`Version change committed: ${newVersion}`);
338
+ if (runConfig.publish.interactive) {
339
+ logger.verbose('Interactive mode enabled for release notes generation');
241
340
  }
242
- logger.info('Generating release notes...');
243
- const releaseSummary = await execute$3(runConfig);
244
- if (isDryRun) {
245
- logger.info('Would write release notes to RELEASE_NOTES.md and RELEASE_TITLE.md in output directory');
246
- } else {
247
- const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
248
- await storage.ensureDirectory(outputDirectory);
249
- const releaseNotesPath = getOutputPath(outputDirectory, 'RELEASE_NOTES.md');
250
- const releaseTitlePath = getOutputPath(outputDirectory, 'RELEASE_TITLE.md');
251
- await storage.writeFile(releaseNotesPath, releaseSummary.body, 'utf-8');
252
- await storage.writeFile(releaseTitlePath, releaseSummary.title, 'utf-8');
253
- logger.info(`Release notes and title generated and saved to ${releaseNotesPath} and ${releaseTitlePath}.`);
341
+ }
342
+ const releaseSummary = await execute$2(releaseConfig);
343
+ if (isDryRun) {
344
+ logger.info('Would write release notes to RELEASE_NOTES.md and RELEASE_TITLE.md in output directory');
345
+ } else {
346
+ const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
347
+ await storage.ensureDirectory(outputDirectory);
348
+ const releaseNotesPath = getOutputPath(outputDirectory, 'RELEASE_NOTES.md');
349
+ const releaseTitlePath = getOutputPath(outputDirectory, 'RELEASE_TITLE.md');
350
+ await storage.writeFile(releaseNotesPath, releaseSummary.body, 'utf-8');
351
+ await storage.writeFile(releaseTitlePath, releaseSummary.title, 'utf-8');
352
+ logger.info(`Release notes and title generated and saved to ${releaseNotesPath} and ${releaseTitlePath}.`);
353
+ }
354
+ logger.info('Pushing to origin...');
355
+ // Get current branch name and push explicitly to avoid pushing to wrong remote/branch
356
+ const currentBranch = await getCurrentBranchName();
357
+ await runWithDryRunSupport(`git push origin ${currentBranch}`, isDryRun);
358
+ logger.info('Creating pull request...');
359
+ if (isDryRun) {
360
+ logger.info('Would get commit title and create PR with GitHub API');
361
+ pr = {
362
+ number: 123,
363
+ html_url: 'https://github.com/mock/repo/pull/123',
364
+ labels: []
365
+ };
366
+ } else {
367
+ const { stdout: commitTitle } = await run('git log -1 --pretty=%B');
368
+ pr = await createPullRequest(commitTitle, 'Automated release PR.', await getCurrentBranchName());
369
+ if (!pr) {
370
+ throw new Error('Failed to create pull request.');
254
371
  }
255
- logger.info('Pushing to origin...');
256
- // Get current branch name and push explicitly to avoid pushing to wrong remote/branch
257
- const currentBranch = await getCurrentBranchName();
258
- await runWithDryRunSupport(`git push origin ${currentBranch}`, isDryRun);
259
- logger.info('Creating pull request...');
260
- if (isDryRun) {
261
- logger.info('Would get commit title and create PR with GitHub API');
262
- pr = {
263
- number: 123,
264
- html_url: 'https://github.com/mock/repo/pull/123',
265
- labels: []
266
- };
372
+ logger.info(`Pull request created: ${pr.html_url}`);
373
+ }
374
+ }
375
+ logger.info(`Waiting for PR #${pr.number} checks to complete...`);
376
+ if (!isDryRun) {
377
+ var _runConfig_publish9, _runConfig_publish10, _runConfig_publish11;
378
+ // Configure timeout and user confirmation behavior
379
+ const timeout = ((_runConfig_publish9 = runConfig.publish) === null || _runConfig_publish9 === void 0 ? void 0 : _runConfig_publish9.checksTimeout) || 300000; // 5 minutes default
380
+ const senditMode = ((_runConfig_publish10 = runConfig.publish) === null || _runConfig_publish10 === void 0 ? void 0 : _runConfig_publish10.sendit) || false;
381
+ // sendit flag overrides skipUserConfirmation - if sendit is true, skip confirmation
382
+ const skipUserConfirmation = senditMode || ((_runConfig_publish11 = runConfig.publish) === null || _runConfig_publish11 === void 0 ? void 0 : _runConfig_publish11.skipUserConfirmation) || false;
383
+ await waitForPullRequestChecks(pr.number, {
384
+ timeout,
385
+ skipUserConfirmation
386
+ });
387
+ }
388
+ const mergeMethod = ((_runConfig_publish2 = runConfig.publish) === null || _runConfig_publish2 === void 0 ? void 0 : _runConfig_publish2.mergeMethod) || 'squash';
389
+ if (isDryRun) {
390
+ logger.info(`Would merge PR #${pr.number} using ${mergeMethod} method`);
391
+ } else {
392
+ try {
393
+ await mergePullRequest(pr.number, mergeMethod, false); // Don't delete branch
394
+ } catch (error) {
395
+ // Check if this is a merge conflict error
396
+ if (error.message && (error.message.includes('not mergeable') || error.message.includes('Pull Request is not mergeable') || error.message.includes('merge conflict'))) {
397
+ logger.error(`❌ Pull Request #${pr.number} has merge conflicts that need to be resolved.`);
398
+ logger.error('');
399
+ logger.error('📋 To resolve this issue:');
400
+ logger.error(` 1. Visit the Pull Request: ${pr.html_url}`);
401
+ logger.error(' 2. Resolve the merge conflicts through GitHub\'s web interface or locally');
402
+ logger.error(' 3. Once conflicts are resolved, re-run the publish command');
403
+ logger.error('');
404
+ logger.error('💡 The command will automatically detect the existing PR and continue from where it left off.');
405
+ throw new Error(`Merge conflicts detected in PR #${pr.number}. Please resolve conflicts and re-run the command.`);
267
406
  } else {
268
- const { stdout: commitTitle } = await run('git log -1 --pretty=%B');
269
- pr = await createPullRequest(commitTitle, 'Automated release PR.', await getCurrentBranchName());
270
- if (!pr) {
271
- throw new Error('Failed to create pull request.');
272
- }
273
- logger.info(`Pull request created: ${pr.html_url}`);
407
+ // Re-throw other merge errors
408
+ throw error;
274
409
  }
275
410
  }
276
- logger.info(`Waiting for PR #${pr.number} checks to complete...`);
277
- if (!isDryRun) {
278
- var _runConfig_publish4, _runConfig_publish5, _runConfig_publish6;
279
- // Configure timeout and user confirmation behavior
280
- const timeout = ((_runConfig_publish4 = runConfig.publish) === null || _runConfig_publish4 === void 0 ? void 0 : _runConfig_publish4.checksTimeout) || 300000; // 5 minutes default
281
- const senditMode = ((_runConfig_publish5 = runConfig.publish) === null || _runConfig_publish5 === void 0 ? void 0 : _runConfig_publish5.sendit) || false;
282
- // sendit flag overrides skipUserConfirmation - if sendit is true, skip confirmation
283
- const skipUserConfirmation = senditMode || ((_runConfig_publish6 = runConfig.publish) === null || _runConfig_publish6 === void 0 ? void 0 : _runConfig_publish6.skipUserConfirmation) || false;
284
- await waitForPullRequestChecks(pr.number, {
285
- timeout,
286
- skipUserConfirmation
287
- });
288
- }
289
- const mergeMethod = ((_runConfig_publish1 = runConfig.publish) === null || _runConfig_publish1 === void 0 ? void 0 : _runConfig_publish1.mergeMethod) || 'squash';
290
- if (isDryRun) {
291
- logger.info(`Would merge PR #${pr.number} using ${mergeMethod} method`);
411
+ }
412
+ // Switch to target branch and pull latest changes
413
+ logger.info(`Checking out target branch: ${targetBranch}...`);
414
+ try {
415
+ await runWithDryRunSupport(`git checkout ${targetBranch}`, isDryRun);
416
+ await runWithDryRunSupport(`git pull origin ${targetBranch}`, isDryRun);
417
+ } catch (error) {
418
+ // Check if this is a merge conflict or sync issue
419
+ if (!isDryRun && (error.message.includes('conflict') || error.message.includes('CONFLICT') || error.message.includes('diverged') || error.message.includes('non-fast-forward'))) {
420
+ logger.error(`❌ Failed to sync target branch '${targetBranch}' with remote.`);
421
+ logger.error('');
422
+ logger.error('📋 Recovery options:');
423
+ logger.error(` 1. Run 'kodrdriv publish --sync-target' to attempt automatic resolution`);
424
+ logger.error(` 2. Manually resolve conflicts:`);
425
+ logger.error(` - git checkout ${targetBranch}`);
426
+ logger.error(` - git pull origin ${targetBranch}`);
427
+ logger.error(` - Resolve any conflicts and commit`);
428
+ logger.error(` - Re-run your original publish command`);
429
+ logger.error('');
430
+ logger.error('💡 The publish process has been stopped to prevent data loss.');
431
+ throw new Error(`Target branch '${targetBranch}' sync failed. Use recovery options above to resolve.`);
292
432
  } else {
433
+ // Re-throw other errors
434
+ throw error;
435
+ }
436
+ }
437
+ // Now create and push the tag on the target branch
438
+ logger.info('Creating release tag...');
439
+ let tagName;
440
+ if (isDryRun) {
441
+ logger.info('Would read package.json version and create git tag');
442
+ tagName = 'v1.0.0'; // Mock version for dry run
443
+ } else {
444
+ const packageJsonContents = await storage.readFile('package.json', 'utf-8');
445
+ const { version } = safeJsonParse(packageJsonContents, 'package.json');
446
+ tagName = `v${version}`;
447
+ // Check if tag already exists locally
448
+ try {
449
+ // Validate tag name to prevent injection
450
+ if (!validateGitRef(tagName)) {
451
+ throw new Error(`Invalid tag name: ${tagName}`);
452
+ }
453
+ const { stdout } = await runSecure('git', [
454
+ 'tag',
455
+ '-l',
456
+ tagName
457
+ ]);
458
+ if (stdout.trim() === tagName) {
459
+ logger.info(`Tag ${tagName} already exists locally, skipping tag creation`);
460
+ } else {
461
+ await runSecure('git', [
462
+ 'tag',
463
+ tagName
464
+ ]);
465
+ logger.info(`Created local tag: ${tagName}`);
466
+ }
467
+ } catch (error) {
468
+ // If git tag -l fails, create the tag anyway
469
+ await runSecure('git', [
470
+ 'tag',
471
+ tagName
472
+ ]);
473
+ logger.info(`Created local tag: ${tagName}`);
474
+ }
475
+ // Check if tag exists on remote before pushing
476
+ let tagWasPushed = false;
477
+ try {
478
+ const { stdout } = await runSecure('git', [
479
+ 'ls-remote',
480
+ 'origin',
481
+ `refs/tags/${tagName}`
482
+ ]);
483
+ if (stdout.trim()) {
484
+ logger.info(`Tag ${tagName} already exists on remote, skipping push`);
485
+ } else {
486
+ await runSecure('git', [
487
+ 'push',
488
+ 'origin',
489
+ tagName
490
+ ]);
491
+ logger.info(`Pushed tag to remote: ${tagName}`);
492
+ tagWasPushed = true;
493
+ }
494
+ } catch (error) {
495
+ // If ls-remote fails, try to push anyway (might be a new remote)
293
496
  try {
294
- await mergePullRequest(pr.number, mergeMethod);
295
- } catch (error) {
296
- // Check if this is a merge conflict error
297
- if (error.message && (error.message.includes('not mergeable') || error.message.includes('Pull Request is not mergeable') || error.message.includes('merge conflict'))) {
298
- logger.error(`❌ Pull Request #${pr.number} has merge conflicts that need to be resolved.`);
299
- logger.error('');
300
- logger.error('📋 To resolve this issue:');
301
- logger.error(` 1. Visit the Pull Request: ${pr.html_url}`);
302
- logger.error(' 2. Resolve the merge conflicts through GitHub\'s web interface or locally');
303
- logger.error(' 3. Once conflicts are resolved, re-run the publish command');
304
- logger.error('');
305
- logger.error('💡 The command will automatically detect the existing PR and continue from where it left off.');
306
- throw new Error(`Merge conflicts detected in PR #${pr.number}. Please resolve conflicts and re-run the command.`);
497
+ await runSecure('git', [
498
+ 'push',
499
+ 'origin',
500
+ tagName
501
+ ]);
502
+ logger.info(`Pushed tag to remote: ${tagName}`);
503
+ tagWasPushed = true;
504
+ } catch (pushError) {
505
+ if (pushError.message && pushError.message.includes('already exists')) {
506
+ logger.info(`Tag ${tagName} already exists on remote, continuing...`);
307
507
  } else {
308
- // Re-throw other merge errors
309
- throw error;
508
+ throw pushError;
310
509
  }
311
510
  }
312
511
  }
313
- logger.info('Checking out main branch...');
314
- await runWithDryRunSupport('git checkout main', isDryRun);
315
- await runWithDryRunSupport('git pull origin main', isDryRun);
316
- // Now create and push the tag on the main branch
317
- logger.info('Creating release tag...');
318
- let tagName;
319
- if (isDryRun) {
320
- logger.info('Would read package.json version and create git tag');
321
- tagName = 'v1.0.0'; // Mock version for dry run
512
+ // If we just pushed a new tag, wait for GitHub to process it
513
+ if (tagWasPushed) {
514
+ logger.verbose('Waiting for GitHub to process the pushed tag...');
515
+ await new Promise((resolve)=>setTimeout(resolve, 5000)); // 5 second delay
516
+ }
517
+ }
518
+ logger.info('Creating GitHub release...');
519
+ if (isDryRun) {
520
+ var _runConfig_publish12;
521
+ logger.info('Would read package.json version and create GitHub release with retry logic');
522
+ const milestonesEnabled = !((_runConfig_publish12 = runConfig.publish) === null || _runConfig_publish12 === void 0 ? void 0 : _runConfig_publish12.noMilestones);
523
+ if (milestonesEnabled) {
524
+ logger.info('Would close milestone for released version');
322
525
  } else {
323
- const packageJsonContents = await storage.readFile('package.json', 'utf-8');
324
- const { version } = JSON.parse(packageJsonContents);
325
- tagName = `v${version}`;
326
- // Check if tag already exists locally
526
+ logger.info('Would skip milestone closure (--no-milestones)');
527
+ }
528
+ } else {
529
+ const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
530
+ const releaseNotesPath = getOutputPath(outputDirectory, 'RELEASE_NOTES.md');
531
+ const releaseTitlePath = getOutputPath(outputDirectory, 'RELEASE_TITLE.md');
532
+ const releaseNotesContent = await storage.readFile(releaseNotesPath, 'utf-8');
533
+ const releaseTitle = await storage.readFile(releaseTitlePath, 'utf-8');
534
+ // Create release with retry logic to handle GitHub tag processing delays
535
+ let retries = 3;
536
+ while(retries > 0){
327
537
  try {
328
- const { stdout } = await run(`git tag -l ${tagName}`);
329
- if (stdout.trim() === tagName) {
330
- logger.info(`Tag ${tagName} already exists locally, skipping tag creation`);
538
+ var _runConfig_publish13;
539
+ await createRelease(tagName, releaseTitle, releaseNotesContent);
540
+ logger.info(`GitHub release created successfully for tag: ${tagName}`);
541
+ // Close milestone for this version if enabled
542
+ const milestonesEnabled = !((_runConfig_publish13 = runConfig.publish) === null || _runConfig_publish13 === void 0 ? void 0 : _runConfig_publish13.noMilestones);
543
+ if (milestonesEnabled) {
544
+ logger.info('🏁 Closing milestone for released version...');
545
+ const version = tagName.replace(/^v/, ''); // Remove 'v' prefix if present
546
+ await closeMilestoneForVersion(version);
331
547
  } else {
332
- await run(`git tag ${tagName}`);
333
- logger.info(`Created local tag: ${tagName}`);
548
+ logger.debug('Milestone integration disabled via --no-milestones');
334
549
  }
550
+ break; // Success - exit retry loop
335
551
  } catch (error) {
336
- // If git tag -l fails, create the tag anyway
337
- await run(`git tag ${tagName}`);
338
- logger.info(`Created local tag: ${tagName}`);
339
- }
340
- // Check if tag exists on remote before pushing
341
- let tagWasPushed = false;
342
- try {
343
- const { stdout } = await run(`git ls-remote origin refs/tags/${tagName}`);
344
- if (stdout.trim()) {
345
- logger.info(`Tag ${tagName} already exists on remote, skipping push`);
552
+ // Check if this is a tag-not-found error that we can retry
553
+ const isTagNotFoundError = error.message && (error.message.includes('not found') || error.message.includes('does not exist') || error.message.includes('Reference does not exist'));
554
+ if (isTagNotFoundError && retries > 1) {
555
+ logger.verbose(`Tag ${tagName} not yet available on GitHub, retrying in 3 seconds... (${retries - 1} retries left)`);
556
+ await new Promise((resolve)=>setTimeout(resolve, 3000));
557
+ retries--;
558
+ } else if (isTagNotFoundError) {
559
+ // Tag not found error and we're out of retries
560
+ throw new Error(`Tag ${tagName} was not found on GitHub after ${3 - retries + 1} attempts. This may indicate a problem with tag creation or GitHub synchronization.`);
346
561
  } else {
347
- await run(`git push origin ${tagName}`);
348
- logger.info(`Pushed tag to remote: ${tagName}`);
349
- tagWasPushed = true;
350
- }
351
- } catch (error) {
352
- // If ls-remote fails, try to push anyway (might be a new remote)
353
- try {
354
- await run(`git push origin ${tagName}`);
355
- logger.info(`Pushed tag to remote: ${tagName}`);
356
- tagWasPushed = true;
357
- } catch (pushError) {
358
- if (pushError.message && pushError.message.includes('already exists')) {
359
- logger.info(`Tag ${tagName} already exists on remote, continuing...`);
360
- } else {
361
- throw pushError;
362
- }
562
+ // Not a tag-not-found error - re-throw the original error
563
+ throw error;
363
564
  }
364
565
  }
365
- // If we just pushed a new tag, wait for GitHub to process it
366
- if (tagWasPushed) {
367
- logger.verbose('Waiting for GitHub to process the pushed tag...');
368
- await new Promise((resolve)=>setTimeout(resolve, 5000)); // 5 second delay
369
- }
370
566
  }
371
- logger.info('Creating GitHub release...');
567
+ }
568
+ // Wait for release workflows to complete (if enabled)
569
+ const waitForWorkflows = ((_runConfig_publish3 = runConfig.publish) === null || _runConfig_publish3 === void 0 ? void 0 : _runConfig_publish3.waitForReleaseWorkflows) !== false; // default to true
570
+ if (waitForWorkflows) {
571
+ logger.info('Waiting for release workflows...');
372
572
  if (isDryRun) {
373
- logger.info('Would read package.json version and create GitHub release with retry logic');
573
+ logger.info('Would monitor GitHub Actions workflows triggered by release');
374
574
  } else {
375
- const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
376
- const releaseNotesPath = getOutputPath(outputDirectory, 'RELEASE_NOTES.md');
377
- const releaseTitlePath = getOutputPath(outputDirectory, 'RELEASE_TITLE.md');
378
- const releaseNotesContent = await storage.readFile(releaseNotesPath, 'utf-8');
379
- const releaseTitle = await storage.readFile(releaseTitlePath, 'utf-8');
380
- // Create release with retry logic to handle GitHub tag processing delays
381
- let retries = 3;
382
- while(retries > 0){
575
+ var _runConfig_publish14, _runConfig_publish15, _runConfig_publish16, _runConfig_publish17;
576
+ const workflowTimeout = ((_runConfig_publish14 = runConfig.publish) === null || _runConfig_publish14 === void 0 ? void 0 : _runConfig_publish14.releaseWorkflowsTimeout) || 600000; // 10 minutes default
577
+ const senditMode = ((_runConfig_publish15 = runConfig.publish) === null || _runConfig_publish15 === void 0 ? void 0 : _runConfig_publish15.sendit) || false;
578
+ const skipUserConfirmation = senditMode || ((_runConfig_publish16 = runConfig.publish) === null || _runConfig_publish16 === void 0 ? void 0 : _runConfig_publish16.skipUserConfirmation) || false;
579
+ // Get workflow names - either from config or auto-detect
580
+ let workflowNames = (_runConfig_publish17 = runConfig.publish) === null || _runConfig_publish17 === void 0 ? void 0 : _runConfig_publish17.releaseWorkflowNames;
581
+ if (!workflowNames || workflowNames.length === 0) {
582
+ logger.info('No specific workflow names configured, auto-detecting workflows triggered by release events...');
383
583
  try {
384
- await createRelease(tagName, releaseTitle, releaseNotesContent);
385
- logger.info(`GitHub release created successfully for tag: ${tagName}`);
386
- break; // Success - exit retry loop
387
- } catch (error) {
388
- // Check if this is a tag-not-found error that we can retry
389
- const isTagNotFoundError = error.message && (error.message.includes('not found') || error.message.includes('does not exist') || error.message.includes('Reference does not exist'));
390
- if (isTagNotFoundError && retries > 1) {
391
- logger.verbose(`Tag ${tagName} not yet available on GitHub, retrying in 3 seconds... (${retries - 1} retries left)`);
392
- await new Promise((resolve)=>setTimeout(resolve, 3000));
393
- retries--;
394
- } else if (isTagNotFoundError) {
395
- // Tag not found error and we're out of retries
396
- throw new Error(`Tag ${tagName} was not found on GitHub after ${3 - retries + 1} attempts. This may indicate a problem with tag creation or GitHub synchronization.`);
584
+ workflowNames = await getWorkflowsTriggeredByRelease();
585
+ if (workflowNames.length === 0) {
586
+ logger.info('No workflows found that are triggered by release events.');
397
587
  } else {
398
- // Not a tag-not-found error - re-throw the original error
399
- throw error;
400
- }
401
- }
402
- }
403
- }
404
- // Wait for release workflows to complete (if enabled)
405
- const waitForWorkflows = ((_runConfig_publish2 = runConfig.publish) === null || _runConfig_publish2 === void 0 ? void 0 : _runConfig_publish2.waitForReleaseWorkflows) !== false; // default to true
406
- if (waitForWorkflows) {
407
- logger.info('Waiting for release workflows...');
408
- if (isDryRun) {
409
- logger.info('Would monitor GitHub Actions workflows triggered by release');
410
- } else {
411
- var _runConfig_publish7, _runConfig_publish8, _runConfig_publish9, _runConfig_publish10;
412
- const workflowTimeout = ((_runConfig_publish7 = runConfig.publish) === null || _runConfig_publish7 === void 0 ? void 0 : _runConfig_publish7.releaseWorkflowsTimeout) || 600000; // 10 minutes default
413
- const senditMode = ((_runConfig_publish8 = runConfig.publish) === null || _runConfig_publish8 === void 0 ? void 0 : _runConfig_publish8.sendit) || false;
414
- const skipUserConfirmation = senditMode || ((_runConfig_publish9 = runConfig.publish) === null || _runConfig_publish9 === void 0 ? void 0 : _runConfig_publish9.skipUserConfirmation) || false;
415
- // Get workflow names - either from config or auto-detect
416
- let workflowNames = (_runConfig_publish10 = runConfig.publish) === null || _runConfig_publish10 === void 0 ? void 0 : _runConfig_publish10.releaseWorkflowNames;
417
- if (!workflowNames || workflowNames.length === 0) {
418
- logger.info('No specific workflow names configured, auto-detecting workflows triggered by release events...');
419
- try {
420
- workflowNames = await getWorkflowsTriggeredByRelease();
421
- if (workflowNames.length === 0) {
422
- logger.info('No workflows found that are triggered by release events.');
423
- } else {
424
- logger.info(`Auto-detected release workflows: ${workflowNames.join(', ')}`);
425
- }
426
- } catch (error) {
427
- logger.warn(`Failed to auto-detect release workflows: ${error.message}`);
428
- workflowNames = undefined; // Fall back to monitoring all workflows
588
+ logger.info(`Auto-detected release workflows: ${workflowNames.join(', ')}`);
429
589
  }
590
+ } catch (error) {
591
+ logger.warn(`Failed to auto-detect release workflows: ${error.message}`);
592
+ workflowNames = undefined; // Fall back to monitoring all workflows
430
593
  }
431
- await waitForReleaseWorkflows(tagName, {
432
- timeout: workflowTimeout,
433
- workflowNames,
434
- skipUserConfirmation
435
- });
436
- }
437
- } else {
438
- logger.verbose('Skipping waiting for release workflows (disabled in config).');
439
- }
440
- logger.info('Creating new release branch...');
441
- if (isDryRun) {
442
- logger.info('Would create next release branch (e.g., release/1.0.1) and push to origin');
443
- } else {
444
- const packageJsonContents = await storage.readFile('package.json', 'utf-8');
445
- const { version } = JSON.parse(packageJsonContents);
446
- const nextVersion = incrementPatchVersion(version);
447
- const newBranchName = `release/${nextVersion}`;
448
- // Check if branch already exists locally
449
- let branchExists = false;
450
- try {
451
- await run(`git show-ref --verify --quiet refs/heads/${newBranchName}`);
452
- branchExists = true;
453
- } catch {
454
- // Branch doesn't exist locally
455
- branchExists = false;
456
- }
457
- if (branchExists) {
458
- // Branch exists, switch to it
459
- await run(`git checkout ${newBranchName}`);
460
- logger.info(`Switched to existing branch ${newBranchName}`);
461
- } else {
462
- // Branch doesn't exist, create it
463
- await run(`git checkout -b ${newBranchName}`);
464
- logger.info(`Created new branch ${newBranchName}`);
465
- }
466
- // Check if branch exists on remote before pushing
467
- let remoteExists = false;
468
- try {
469
- const { stdout } = await run(`git ls-remote origin refs/heads/${newBranchName}`);
470
- remoteExists = stdout.trim() !== '';
471
- } catch {
472
- // Assume remote doesn't exist if ls-remote fails
473
- remoteExists = false;
474
- }
475
- if (remoteExists) {
476
- logger.info(`Branch ${newBranchName} already exists on remote, skipping push`);
477
- } else {
478
- await run(`git push -u origin ${newBranchName}`);
479
- logger.info(`Branch ${newBranchName} pushed to origin.`);
480
594
  }
595
+ await waitForReleaseWorkflows(tagName, {
596
+ timeout: workflowTimeout,
597
+ workflowNames,
598
+ skipUserConfirmation
599
+ });
481
600
  }
482
- logger.info('Preparation complete.');
483
- publishCompleted = true; // Mark as completed only if we reach this point
484
- } finally{
485
- var _runConfig_publish11;
486
- // Link packages if linking is enabled, regardless of whether we unlinked them
487
- const shouldLink = ((_runConfig_publish11 = runConfig.publish) === null || _runConfig_publish11 === void 0 ? void 0 : _runConfig_publish11.linkWorkspacePackages) !== false; // default to true
488
- if (shouldLink) {
489
- logger.verbose('Ensuring linked packages are properly set up...');
490
- await execute$4(runConfig);
491
- } else {
492
- logger.verbose('Skipping link packages (disabled in config).');
493
- }
601
+ } else {
602
+ logger.verbose('Skipping waiting for release workflows (disabled in config).');
494
603
  }
604
+ // Switch to target branch
605
+ logger.info(`Switching to target branch: ${targetBranch}`);
606
+ await runWithDryRunSupport(`git checkout ${targetBranch}`, isDryRun);
607
+ logger.info('Publish process complete.');
495
608
  };
496
609
 
497
610
  export { execute };