@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.
- package/README.md +3 -1
- package/dist/application.js +29 -5
- package/dist/application.js.map +1 -1
- package/dist/arguments.js +187 -75
- package/dist/arguments.js.map +1 -1
- package/dist/commands/audio-commit.js +35 -14
- package/dist/commands/audio-commit.js.map +1 -1
- package/dist/commands/audio-review.js +41 -20
- package/dist/commands/audio-review.js.map +1 -1
- package/dist/commands/clean.js +2 -2
- package/dist/commands/clean.js.map +1 -1
- package/dist/commands/commit.js +369 -47
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/development.js +264 -0
- package/dist/commands/development.js.map +1 -0
- package/dist/commands/link.js +357 -182
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/publish.js +419 -306
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/release.js +240 -18
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/review.js +56 -40
- 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 +779 -40
- package/dist/commands/tree.js.map +1 -1
- package/dist/commands/unlink.js +267 -372
- package/dist/commands/unlink.js.map +1 -1
- package/dist/commands/versions.js +224 -0
- package/dist/commands/versions.js.map +1 -0
- package/dist/constants.js +50 -12
- package/dist/constants.js.map +1 -1
- package/dist/content/diff.js +122 -1
- package/dist/content/diff.js.map +1 -1
- package/dist/content/files.js +192 -0
- package/dist/content/files.js.map +1 -0
- package/dist/content/issues.js +17 -46
- package/dist/content/issues.js.map +1 -1
- package/dist/content/log.js +16 -0
- package/dist/content/log.js.map +1 -1
- package/dist/logging.js +3 -3
- package/dist/logging.js.map +1 -1
- package/dist/main.js +0 -0
- package/dist/prompt/commit.js +11 -4
- package/dist/prompt/commit.js.map +1 -1
- package/dist/prompt/instructions/commit.md +20 -2
- package/dist/prompt/instructions/release.md +27 -10
- package/dist/prompt/instructions/review.md +75 -8
- package/dist/prompt/release.js +15 -7
- package/dist/prompt/release.js.map +1 -1
- package/dist/prompt/review.js +2 -2
- package/dist/prompt/review.js.map +1 -1
- package/dist/types.js +36 -7
- package/dist/types.js.map +1 -1
- package/dist/util/child.js +146 -4
- package/dist/util/child.js.map +1 -1
- package/dist/util/countdown.js +215 -0
- package/dist/util/countdown.js.map +1 -0
- package/dist/util/general.js +157 -13
- package/dist/util/general.js.map +1 -1
- package/dist/util/git.js +587 -0
- package/dist/util/git.js.map +1 -0
- package/dist/util/github.js +531 -11
- package/dist/util/github.js.map +1 -1
- package/dist/util/interactive.js +463 -0
- package/dist/util/interactive.js.map +1 -0
- package/dist/util/openai.js +152 -25
- package/dist/util/openai.js.map +1 -1
- package/dist/util/performance.js +5 -73
- 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 +30 -3
- package/dist/util/storage.js.map +1 -1
- package/dist/util/validation.js +1 -25
- package/dist/util/validation.js.map +1 -1
- package/package.json +12 -10
- package/test-increment.js +0 -0
- package/test-multiline/cli/package.json +8 -0
- package/test-multiline/core/package.json +5 -0
- package/test-multiline/mobile/package.json +8 -0
- package/test-multiline/web/package.json +8 -0
- package/dist/util/npmOptimizations.js +0 -174
- package/dist/util/npmOptimizations.js.map +0 -1
package/dist/commands/publish.js
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { execute as execute$
|
|
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$
|
|
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 {
|
|
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 =
|
|
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
|
|
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(
|
|
102
|
+
logger.info(`Would verify current branch is not the target branch (${targetBranch})`);
|
|
103
103
|
} else {
|
|
104
104
|
const currentBranch = await getCurrentBranchName();
|
|
105
|
-
if (
|
|
106
|
-
throw new Error(`
|
|
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 =
|
|
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 = ((
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
188
|
-
|
|
298
|
+
logger.verbose('No dependency update patterns specified, updating all dependencies');
|
|
299
|
+
await runWithDryRunSupport('npm update', isDryRun);
|
|
189
300
|
}
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
|
205
|
-
await runWithDryRunSupport('npm update', isDryRun);
|
|
316
|
+
logger.verbose('No changes to commit, skipping commit.');
|
|
206
317
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
225
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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(
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
269
|
-
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
logger.
|
|
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
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
logger.
|
|
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
|
-
|
|
309
|
-
throw error;
|
|
508
|
+
throw pushError;
|
|
310
509
|
}
|
|
311
510
|
}
|
|
312
511
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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
|
-
|
|
348
|
-
|
|
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
|
-
|
|
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
|
|
573
|
+
logger.info('Would monitor GitHub Actions workflows triggered by release');
|
|
374
574
|
} else {
|
|
375
|
-
|
|
376
|
-
const
|
|
377
|
-
const
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
|
385
|
-
|
|
386
|
-
|
|
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
|
-
|
|
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
|
-
|
|
483
|
-
|
|
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 };
|