@eldrforge/kodrdriv 0.0.51 → 0.1.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 +2 -1
- package/dist/application.js +5 -3
- package/dist/application.js.map +1 -1
- package/dist/arguments.js +97 -70
- package/dist/arguments.js.map +1 -1
- package/dist/commands/audio-commit.js +7 -7
- package/dist/commands/audio-commit.js.map +1 -1
- package/dist/commands/audio-review.js +13 -13
- package/dist/commands/audio-review.js.map +1 -1
- package/dist/commands/clean.js +2 -2
- package/dist/commands/clean.js.map +1 -1
- package/dist/commands/commit.js +301 -36
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/link.js +7 -7
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/publish.js +285 -306
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/release.js +171 -14
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/review.js +52 -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 +347 -34
- package/dist/commands/tree.js.map +1 -1
- package/dist/commands/unlink.js +5 -5
- package/dist/commands/unlink.js.map +1 -1
- package/dist/constants.js +28 -9
- 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/issues.js +17 -46
- package/dist/content/issues.js.map +1 -1
- package/dist/logging.js +3 -3
- package/dist/logging.js.map +1 -1
- package/dist/prompt/commit.js +2 -2
- package/dist/prompt/commit.js.map +1 -1
- package/dist/prompt/release.js +2 -2
- 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 +18 -5
- package/dist/types.js.map +1 -1
- package/dist/util/child.js +60 -4
- package/dist/util/child.js.map +1 -1
- package/dist/util/general.js +149 -13
- package/dist/util/general.js.map +1 -1
- package/dist/util/github.js +12 -8
- package/dist/util/github.js.map +1 -1
- package/dist/util/interactive.js +297 -0
- package/dist/util/interactive.js.map +1 -0
- package/dist/util/openai.js +87 -8
- package/dist/util/openai.js.map +1 -1
- package/dist/util/performance.js +8 -8
- package/dist/util/performance.js.map +1 -1
- package/dist/util/safety.js +4 -4
- package/dist/util/safety.js.map +1 -1
- package/dist/util/storage.js +2 -2
- package/dist/util/storage.js.map +1 -1
- package/package.json +6 -6
- package/test-increment.js +0 -0
package/dist/commands/publish.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
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
6
|
import { runWithDryRunSupport, run } from '../util/child.js';
|
|
9
7
|
import { getCurrentBranchName, findOpenPullRequestByHeadRef, createPullRequest, waitForPullRequestChecks, mergePullRequest, createRelease, 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';
|
|
14
12
|
|
|
15
13
|
const scanNpmrcForEnvVars = async (storage)=>{
|
|
16
14
|
const logger = getLogger();
|
|
17
|
-
const npmrcPath =
|
|
15
|
+
const npmrcPath = path__default.join(process.cwd(), '.npmrc');
|
|
18
16
|
const envVars = [];
|
|
19
17
|
if (await storage.exists(npmrcPath)) {
|
|
20
18
|
try {
|
|
@@ -57,7 +55,7 @@ const validateEnvironmentVariables = (requiredEnvVars, isDryRun)=>{
|
|
|
57
55
|
}
|
|
58
56
|
};
|
|
59
57
|
const runPrechecks = async (runConfig)=>{
|
|
60
|
-
var _runConfig_publish;
|
|
58
|
+
var _runConfig_publish, _runConfig_publish1;
|
|
61
59
|
const isDryRun = runConfig.dryRun || false;
|
|
62
60
|
const logger = getDryRunLogger(isDryRun);
|
|
63
61
|
const storage = create({
|
|
@@ -96,19 +94,20 @@ const runPrechecks = async (runConfig)=>{
|
|
|
96
94
|
throw new Error(`Failed to check git status: ${originalMessage}. Please ensure you are in a valid git repository and try again.`);
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
|
-
// Check
|
|
97
|
+
// Check that we're not running from the target branch
|
|
100
98
|
logger.info('Checking current branch...');
|
|
99
|
+
const targetBranch = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.targetBranch) || 'main';
|
|
101
100
|
if (isDryRun) {
|
|
102
|
-
logger.info(
|
|
101
|
+
logger.info(`Would verify current branch is not the target branch (${targetBranch})`);
|
|
103
102
|
} else {
|
|
104
103
|
const currentBranch = await getCurrentBranchName();
|
|
105
|
-
if (
|
|
106
|
-
throw new Error(`
|
|
104
|
+
if (currentBranch === targetBranch) {
|
|
105
|
+
throw new Error(`Cannot run publish from the target branch '${targetBranch}'. Please switch to a different branch before running publish.`);
|
|
107
106
|
}
|
|
108
107
|
}
|
|
109
108
|
// Check if prepublishOnly script exists in package.json
|
|
110
109
|
logger.info('Checking for prepublishOnly script...');
|
|
111
|
-
const packageJsonPath =
|
|
110
|
+
const packageJsonPath = path__default.join(process.cwd(), 'package.json');
|
|
112
111
|
if (!await storage.exists(packageJsonPath)) {
|
|
113
112
|
if (!isDryRun) {
|
|
114
113
|
throw new Error('package.json not found in current directory.');
|
|
@@ -139,7 +138,7 @@ const runPrechecks = async (runConfig)=>{
|
|
|
139
138
|
}
|
|
140
139
|
// Check required environment variables
|
|
141
140
|
logger.verbose('Checking required environment variables...');
|
|
142
|
-
const coreRequiredEnvVars = ((
|
|
141
|
+
const coreRequiredEnvVars = ((_runConfig_publish1 = runConfig.publish) === null || _runConfig_publish1 === void 0 ? void 0 : _runConfig_publish1.requiredEnvVars) || [];
|
|
143
142
|
const npmrcEnvVars = isDryRun ? [] : await scanNpmrcForEnvVars(storage); // Skip .npmrc scan in dry run
|
|
144
143
|
const allRequiredEnvVars = [
|
|
145
144
|
...new Set([
|
|
@@ -156,342 +155,322 @@ const runPrechecks = async (runConfig)=>{
|
|
|
156
155
|
logger.info('All prechecks passed.');
|
|
157
156
|
};
|
|
158
157
|
const execute = async (runConfig)=>{
|
|
158
|
+
var _runConfig_publish, _runConfig_publish1, _runConfig_publish2;
|
|
159
159
|
const isDryRun = runConfig.dryRun || false;
|
|
160
160
|
const logger = getDryRunLogger(isDryRun);
|
|
161
161
|
const storage = create({
|
|
162
162
|
log: logger.info
|
|
163
163
|
});
|
|
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;
|
|
168
164
|
// Run prechecks before starting any work
|
|
169
165
|
await runPrechecks(runConfig);
|
|
170
166
|
logger.info('Starting release process...');
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
167
|
+
let pr = null;
|
|
168
|
+
if (isDryRun) {
|
|
169
|
+
logger.info('Would check for existing pull request');
|
|
170
|
+
logger.info('Assuming no existing PR found for demo purposes');
|
|
171
|
+
} else {
|
|
172
|
+
const branchName = await getCurrentBranchName();
|
|
173
|
+
pr = await findOpenPullRequestByHeadRef(branchName);
|
|
174
|
+
}
|
|
175
|
+
if (pr) {
|
|
176
|
+
logger.info(`Found existing pull request for branch: ${pr.html_url}`);
|
|
177
|
+
} else {
|
|
178
|
+
var _runConfig_publish3, _runConfig_publish4, _runConfig_publish5;
|
|
179
|
+
logger.info('No open pull request found, starting new release publishing process...');
|
|
180
|
+
// STEP 1: Determine and set target version FIRST (before any commits)
|
|
181
|
+
logger.info('Determining target version...');
|
|
182
|
+
let newVersion;
|
|
183
183
|
if (isDryRun) {
|
|
184
|
-
logger.info('Would
|
|
185
|
-
|
|
186
|
-
} else {
|
|
187
|
-
const branchName = await getCurrentBranchName();
|
|
188
|
-
pr = await findOpenPullRequestByHeadRef(branchName);
|
|
189
|
-
}
|
|
190
|
-
if (pr) {
|
|
191
|
-
logger.info(`Found existing pull request for branch: ${pr.html_url}`);
|
|
184
|
+
logger.info('Would determine target version and update package.json');
|
|
185
|
+
newVersion = '1.0.0'; // Mock version for dry run
|
|
192
186
|
} else {
|
|
193
|
-
var
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
187
|
+
var _runConfig_publish6, _runConfig_publish7;
|
|
188
|
+
const packageJsonContents = await storage.readFile('package.json', 'utf-8');
|
|
189
|
+
const parsed = safeJsonParse(packageJsonContents, 'package.json');
|
|
190
|
+
const packageJson = validatePackageJson(parsed, 'package.json');
|
|
191
|
+
const currentVersion = packageJson.version;
|
|
192
|
+
// Determine target version based on --targetVersion option
|
|
193
|
+
const targetVersionInput = ((_runConfig_publish6 = runConfig.publish) === null || _runConfig_publish6 === void 0 ? void 0 : _runConfig_publish6.targetVersion) || 'patch';
|
|
194
|
+
const proposedVersion = calculateTargetVersion(currentVersion, targetVersionInput);
|
|
195
|
+
// Check if target tag already exists
|
|
196
|
+
const targetTagName = `v${proposedVersion}`;
|
|
197
|
+
const tagExists = await checkIfTagExists(targetTagName);
|
|
198
|
+
if (tagExists) {
|
|
199
|
+
throw new Error(`Tag ${targetTagName} already exists. Please choose a different version or delete the existing tag.`);
|
|
206
200
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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.');
|
|
201
|
+
// Interactive confirmation if --interactive flag is set
|
|
202
|
+
if ((_runConfig_publish7 = runConfig.publish) === null || _runConfig_publish7 === void 0 ? void 0 : _runConfig_publish7.interactive) {
|
|
203
|
+
newVersion = await confirmVersionInteractively(currentVersion, proposedVersion, targetVersionInput);
|
|
204
|
+
// Re-check if the confirmed version's tag exists (in case user entered custom version)
|
|
205
|
+
const confirmedTagName = `v${newVersion}`;
|
|
206
|
+
const confirmedTagExists = await checkIfTagExists(confirmedTagName);
|
|
207
|
+
if (confirmedTagExists) {
|
|
208
|
+
throw new Error(`Tag ${confirmedTagName} already exists. Please choose a different version or delete the existing tag.`);
|
|
222
209
|
}
|
|
223
|
-
}
|
|
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}`);
|
|
241
|
-
}
|
|
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
210
|
} else {
|
|
247
|
-
|
|
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}.`);
|
|
254
|
-
}
|
|
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
|
-
};
|
|
267
|
-
} 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}`);
|
|
211
|
+
newVersion = proposedVersion;
|
|
274
212
|
}
|
|
213
|
+
logger.info(`Bumping version from ${currentVersion} to ${newVersion}`);
|
|
214
|
+
// Update package.json with the new version BEFORE any other operations
|
|
215
|
+
packageJson.version = newVersion;
|
|
216
|
+
await storage.writeFile('package.json', JSON.stringify(packageJson, null, 2) + '\n', 'utf-8');
|
|
217
|
+
logger.info(`Version updated in package.json: ${newVersion}`);
|
|
275
218
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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`);
|
|
219
|
+
// STEP 2: Prepare for release (with correct version now in package.json)
|
|
220
|
+
logger.verbose('Preparing for release: switching from workspace to remote dependencies.');
|
|
221
|
+
logger.verbose('Updating dependencies to latest versions from registry');
|
|
222
|
+
const updatePatterns = (_runConfig_publish3 = runConfig.publish) === null || _runConfig_publish3 === void 0 ? void 0 : _runConfig_publish3.dependencyUpdatePatterns;
|
|
223
|
+
if (updatePatterns && updatePatterns.length > 0) {
|
|
224
|
+
logger.verbose(`Updating dependencies matching patterns: ${updatePatterns.join(', ')}`);
|
|
225
|
+
const patternsArg = updatePatterns.join(' ');
|
|
226
|
+
await runWithDryRunSupport(`npm update ${patternsArg}`, isDryRun);
|
|
292
227
|
} else {
|
|
293
|
-
|
|
294
|
-
|
|
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.`);
|
|
307
|
-
} else {
|
|
308
|
-
// Re-throw other merge errors
|
|
309
|
-
throw error;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
228
|
+
logger.verbose('No dependency update patterns specified, updating all dependencies');
|
|
229
|
+
await runWithDryRunSupport('npm update', isDryRun);
|
|
312
230
|
}
|
|
313
|
-
logger.info('
|
|
314
|
-
await runWithDryRunSupport('
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
231
|
+
logger.info('Running prepublishOnly script...');
|
|
232
|
+
await runWithDryRunSupport('npm run prepublishOnly', isDryRun, {}, true); // Use inherited stdio
|
|
233
|
+
// STEP 3: Stage all changes (version bump + dependencies + any build artifacts)
|
|
234
|
+
logger.verbose('Staging all changes for release commit');
|
|
235
|
+
await runWithDryRunSupport('git add package.json package-lock.json', isDryRun);
|
|
236
|
+
logger.verbose('Checking for staged changes...');
|
|
319
237
|
if (isDryRun) {
|
|
320
|
-
logger.
|
|
321
|
-
|
|
238
|
+
logger.verbose('Assuming staged changes exist for demo purposes');
|
|
239
|
+
logger.verbose('Would create commit...');
|
|
240
|
+
await execute$1(runConfig);
|
|
322
241
|
} else {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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`);
|
|
331
|
-
} else {
|
|
332
|
-
await run(`git tag ${tagName}`);
|
|
333
|
-
logger.info(`Created local tag: ${tagName}`);
|
|
334
|
-
}
|
|
335
|
-
} 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}`);
|
|
242
|
+
if (await hasStagedChanges()) {
|
|
243
|
+
logger.verbose('Staged changes found, creating commit...');
|
|
244
|
+
await execute$1(runConfig);
|
|
245
|
+
} else {
|
|
246
|
+
logger.verbose('No changes to commit, skipping commit.');
|
|
339
247
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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
|
-
}
|
|
248
|
+
}
|
|
249
|
+
logger.info('Generating release notes...');
|
|
250
|
+
// Create a modified config for release notes generation that includes the publish --from and --interactive options
|
|
251
|
+
const releaseConfig = {
|
|
252
|
+
...runConfig
|
|
253
|
+
};
|
|
254
|
+
if (((_runConfig_publish4 = runConfig.publish) === null || _runConfig_publish4 === void 0 ? void 0 : _runConfig_publish4.from) || ((_runConfig_publish5 = runConfig.publish) === null || _runConfig_publish5 === void 0 ? void 0 : _runConfig_publish5.interactive)) {
|
|
255
|
+
// Pass the publish --from and --interactive options to the release config
|
|
256
|
+
releaseConfig.release = {
|
|
257
|
+
...runConfig.release,
|
|
258
|
+
...runConfig.publish.from && {
|
|
259
|
+
from: runConfig.publish.from
|
|
260
|
+
},
|
|
261
|
+
...runConfig.publish.interactive && {
|
|
262
|
+
interactive: runConfig.publish.interactive
|
|
363
263
|
}
|
|
264
|
+
};
|
|
265
|
+
if (runConfig.publish.from) {
|
|
266
|
+
logger.verbose(`Using custom 'from' reference for release notes: ${runConfig.publish.from}`);
|
|
364
267
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
logger.verbose('Waiting for GitHub to process the pushed tag...');
|
|
368
|
-
await new Promise((resolve)=>setTimeout(resolve, 5000)); // 5 second delay
|
|
268
|
+
if (runConfig.publish.interactive) {
|
|
269
|
+
logger.verbose('Interactive mode enabled for release notes generation');
|
|
369
270
|
}
|
|
370
271
|
}
|
|
371
|
-
|
|
272
|
+
const releaseSummary = await execute$2(releaseConfig);
|
|
372
273
|
if (isDryRun) {
|
|
373
|
-
logger.info('Would
|
|
274
|
+
logger.info('Would write release notes to RELEASE_NOTES.md and RELEASE_TITLE.md in output directory');
|
|
374
275
|
} else {
|
|
375
276
|
const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
|
|
277
|
+
await storage.ensureDirectory(outputDirectory);
|
|
376
278
|
const releaseNotesPath = getOutputPath(outputDirectory, 'RELEASE_NOTES.md');
|
|
377
279
|
const releaseTitlePath = getOutputPath(outputDirectory, 'RELEASE_TITLE.md');
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
throw error;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
280
|
+
await storage.writeFile(releaseNotesPath, releaseSummary.body, 'utf-8');
|
|
281
|
+
await storage.writeFile(releaseTitlePath, releaseSummary.title, 'utf-8');
|
|
282
|
+
logger.info(`Release notes and title generated and saved to ${releaseNotesPath} and ${releaseTitlePath}.`);
|
|
283
|
+
}
|
|
284
|
+
logger.info('Pushing to origin...');
|
|
285
|
+
// Get current branch name and push explicitly to avoid pushing to wrong remote/branch
|
|
286
|
+
const currentBranch = await getCurrentBranchName();
|
|
287
|
+
await runWithDryRunSupport(`git push origin ${currentBranch}`, isDryRun);
|
|
288
|
+
logger.info('Creating pull request...');
|
|
289
|
+
if (isDryRun) {
|
|
290
|
+
logger.info('Would get commit title and create PR with GitHub API');
|
|
291
|
+
pr = {
|
|
292
|
+
number: 123,
|
|
293
|
+
html_url: 'https://github.com/mock/repo/pull/123',
|
|
294
|
+
labels: []
|
|
295
|
+
};
|
|
296
|
+
} else {
|
|
297
|
+
const { stdout: commitTitle } = await run('git log -1 --pretty=%B');
|
|
298
|
+
pr = await createPullRequest(commitTitle, 'Automated release PR.', await getCurrentBranchName());
|
|
299
|
+
if (!pr) {
|
|
300
|
+
throw new Error('Failed to create pull request.');
|
|
402
301
|
}
|
|
302
|
+
logger.info(`Pull request created: ${pr.html_url}`);
|
|
403
303
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
304
|
+
}
|
|
305
|
+
logger.info(`Waiting for PR #${pr.number} checks to complete...`);
|
|
306
|
+
if (!isDryRun) {
|
|
307
|
+
var _runConfig_publish8, _runConfig_publish9, _runConfig_publish10;
|
|
308
|
+
// Configure timeout and user confirmation behavior
|
|
309
|
+
const timeout = ((_runConfig_publish8 = runConfig.publish) === null || _runConfig_publish8 === void 0 ? void 0 : _runConfig_publish8.checksTimeout) || 300000; // 5 minutes default
|
|
310
|
+
const senditMode = ((_runConfig_publish9 = runConfig.publish) === null || _runConfig_publish9 === void 0 ? void 0 : _runConfig_publish9.sendit) || false;
|
|
311
|
+
// sendit flag overrides skipUserConfirmation - if sendit is true, skip confirmation
|
|
312
|
+
const skipUserConfirmation = senditMode || ((_runConfig_publish10 = runConfig.publish) === null || _runConfig_publish10 === void 0 ? void 0 : _runConfig_publish10.skipUserConfirmation) || false;
|
|
313
|
+
await waitForPullRequestChecks(pr.number, {
|
|
314
|
+
timeout,
|
|
315
|
+
skipUserConfirmation
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
const mergeMethod = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.mergeMethod) || 'squash';
|
|
319
|
+
if (isDryRun) {
|
|
320
|
+
logger.info(`Would merge PR #${pr.number} using ${mergeMethod} method`);
|
|
321
|
+
} else {
|
|
322
|
+
try {
|
|
323
|
+
await mergePullRequest(pr.number, mergeMethod, false); // Don't delete branch
|
|
324
|
+
} catch (error) {
|
|
325
|
+
// Check if this is a merge conflict error
|
|
326
|
+
if (error.message && (error.message.includes('not mergeable') || error.message.includes('Pull Request is not mergeable') || error.message.includes('merge conflict'))) {
|
|
327
|
+
logger.error(`❌ Pull Request #${pr.number} has merge conflicts that need to be resolved.`);
|
|
328
|
+
logger.error('');
|
|
329
|
+
logger.error('📋 To resolve this issue:');
|
|
330
|
+
logger.error(` 1. Visit the Pull Request: ${pr.html_url}`);
|
|
331
|
+
logger.error(' 2. Resolve the merge conflicts through GitHub\'s web interface or locally');
|
|
332
|
+
logger.error(' 3. Once conflicts are resolved, re-run the publish command');
|
|
333
|
+
logger.error('');
|
|
334
|
+
logger.error('💡 The command will automatically detect the existing PR and continue from where it left off.');
|
|
335
|
+
throw new Error(`Merge conflicts detected in PR #${pr.number}. Please resolve conflicts and re-run the command.`);
|
|
410
336
|
} else {
|
|
411
|
-
|
|
412
|
-
|
|
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
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
await waitForReleaseWorkflows(tagName, {
|
|
432
|
-
timeout: workflowTimeout,
|
|
433
|
-
workflowNames,
|
|
434
|
-
skipUserConfirmation
|
|
435
|
-
});
|
|
337
|
+
// Re-throw other merge errors
|
|
338
|
+
throw error;
|
|
436
339
|
}
|
|
437
|
-
} else {
|
|
438
|
-
logger.verbose('Skipping waiting for release workflows (disabled in config).');
|
|
439
340
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
341
|
+
}
|
|
342
|
+
logger.info('Checking out main branch...');
|
|
343
|
+
await runWithDryRunSupport('git checkout main', isDryRun);
|
|
344
|
+
await runWithDryRunSupport('git pull origin main', isDryRun);
|
|
345
|
+
// Now create and push the tag on the main branch
|
|
346
|
+
logger.info('Creating release tag...');
|
|
347
|
+
let tagName;
|
|
348
|
+
if (isDryRun) {
|
|
349
|
+
logger.info('Would read package.json version and create git tag');
|
|
350
|
+
tagName = 'v1.0.0'; // Mock version for dry run
|
|
351
|
+
} else {
|
|
352
|
+
const packageJsonContents = await storage.readFile('package.json', 'utf-8');
|
|
353
|
+
const { version } = JSON.parse(packageJsonContents);
|
|
354
|
+
tagName = `v${version}`;
|
|
355
|
+
// Check if tag already exists locally
|
|
356
|
+
try {
|
|
357
|
+
const { stdout } = await run(`git tag -l ${tagName}`);
|
|
358
|
+
if (stdout.trim() === tagName) {
|
|
359
|
+
logger.info(`Tag ${tagName} already exists locally, skipping tag creation`);
|
|
360
|
+
} else {
|
|
361
|
+
await run(`git tag ${tagName}`);
|
|
362
|
+
logger.info(`Created local tag: ${tagName}`);
|
|
456
363
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
364
|
+
} catch (error) {
|
|
365
|
+
// If git tag -l fails, create the tag anyway
|
|
366
|
+
await run(`git tag ${tagName}`);
|
|
367
|
+
logger.info(`Created local tag: ${tagName}`);
|
|
368
|
+
}
|
|
369
|
+
// Check if tag exists on remote before pushing
|
|
370
|
+
let tagWasPushed = false;
|
|
371
|
+
try {
|
|
372
|
+
const { stdout } = await run(`git ls-remote origin refs/tags/${tagName}`);
|
|
373
|
+
if (stdout.trim()) {
|
|
374
|
+
logger.info(`Tag ${tagName} already exists on remote, skipping push`);
|
|
461
375
|
} else {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
376
|
+
await run(`git push origin ${tagName}`);
|
|
377
|
+
logger.info(`Pushed tag to remote: ${tagName}`);
|
|
378
|
+
tagWasPushed = true;
|
|
465
379
|
}
|
|
466
|
-
|
|
467
|
-
|
|
380
|
+
} catch (error) {
|
|
381
|
+
// If ls-remote fails, try to push anyway (might be a new remote)
|
|
468
382
|
try {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
383
|
+
await run(`git push origin ${tagName}`);
|
|
384
|
+
logger.info(`Pushed tag to remote: ${tagName}`);
|
|
385
|
+
tagWasPushed = true;
|
|
386
|
+
} catch (pushError) {
|
|
387
|
+
if (pushError.message && pushError.message.includes('already exists')) {
|
|
388
|
+
logger.info(`Tag ${tagName} already exists on remote, continuing...`);
|
|
389
|
+
} else {
|
|
390
|
+
throw pushError;
|
|
391
|
+
}
|
|
474
392
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
393
|
+
}
|
|
394
|
+
// If we just pushed a new tag, wait for GitHub to process it
|
|
395
|
+
if (tagWasPushed) {
|
|
396
|
+
logger.verbose('Waiting for GitHub to process the pushed tag...');
|
|
397
|
+
await new Promise((resolve)=>setTimeout(resolve, 5000)); // 5 second delay
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
logger.info('Creating GitHub release...');
|
|
401
|
+
if (isDryRun) {
|
|
402
|
+
logger.info('Would read package.json version and create GitHub release with retry logic');
|
|
403
|
+
} else {
|
|
404
|
+
const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
|
|
405
|
+
const releaseNotesPath = getOutputPath(outputDirectory, 'RELEASE_NOTES.md');
|
|
406
|
+
const releaseTitlePath = getOutputPath(outputDirectory, 'RELEASE_TITLE.md');
|
|
407
|
+
const releaseNotesContent = await storage.readFile(releaseNotesPath, 'utf-8');
|
|
408
|
+
const releaseTitle = await storage.readFile(releaseTitlePath, 'utf-8');
|
|
409
|
+
// Create release with retry logic to handle GitHub tag processing delays
|
|
410
|
+
let retries = 3;
|
|
411
|
+
while(retries > 0){
|
|
412
|
+
try {
|
|
413
|
+
await createRelease(tagName, releaseTitle, releaseNotesContent);
|
|
414
|
+
logger.info(`GitHub release created successfully for tag: ${tagName}`);
|
|
415
|
+
break; // Success - exit retry loop
|
|
416
|
+
} catch (error) {
|
|
417
|
+
// Check if this is a tag-not-found error that we can retry
|
|
418
|
+
const isTagNotFoundError = error.message && (error.message.includes('not found') || error.message.includes('does not exist') || error.message.includes('Reference does not exist'));
|
|
419
|
+
if (isTagNotFoundError && retries > 1) {
|
|
420
|
+
logger.verbose(`Tag ${tagName} not yet available on GitHub, retrying in 3 seconds... (${retries - 1} retries left)`);
|
|
421
|
+
await new Promise((resolve)=>setTimeout(resolve, 3000));
|
|
422
|
+
retries--;
|
|
423
|
+
} else if (isTagNotFoundError) {
|
|
424
|
+
// Tag not found error and we're out of retries
|
|
425
|
+
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.`);
|
|
426
|
+
} else {
|
|
427
|
+
// Not a tag-not-found error - re-throw the original error
|
|
428
|
+
throw error;
|
|
429
|
+
}
|
|
480
430
|
}
|
|
481
431
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
logger.verbose('Ensuring linked packages are properly set up...');
|
|
490
|
-
await execute$4(runConfig);
|
|
432
|
+
}
|
|
433
|
+
// Wait for release workflows to complete (if enabled)
|
|
434
|
+
const waitForWorkflows = ((_runConfig_publish1 = runConfig.publish) === null || _runConfig_publish1 === void 0 ? void 0 : _runConfig_publish1.waitForReleaseWorkflows) !== false; // default to true
|
|
435
|
+
if (waitForWorkflows) {
|
|
436
|
+
logger.info('Waiting for release workflows...');
|
|
437
|
+
if (isDryRun) {
|
|
438
|
+
logger.info('Would monitor GitHub Actions workflows triggered by release');
|
|
491
439
|
} else {
|
|
492
|
-
|
|
440
|
+
var _runConfig_publish11, _runConfig_publish12, _runConfig_publish13, _runConfig_publish14;
|
|
441
|
+
const workflowTimeout = ((_runConfig_publish11 = runConfig.publish) === null || _runConfig_publish11 === void 0 ? void 0 : _runConfig_publish11.releaseWorkflowsTimeout) || 600000; // 10 minutes default
|
|
442
|
+
const senditMode = ((_runConfig_publish12 = runConfig.publish) === null || _runConfig_publish12 === void 0 ? void 0 : _runConfig_publish12.sendit) || false;
|
|
443
|
+
const skipUserConfirmation = senditMode || ((_runConfig_publish13 = runConfig.publish) === null || _runConfig_publish13 === void 0 ? void 0 : _runConfig_publish13.skipUserConfirmation) || false;
|
|
444
|
+
// Get workflow names - either from config or auto-detect
|
|
445
|
+
let workflowNames = (_runConfig_publish14 = runConfig.publish) === null || _runConfig_publish14 === void 0 ? void 0 : _runConfig_publish14.releaseWorkflowNames;
|
|
446
|
+
if (!workflowNames || workflowNames.length === 0) {
|
|
447
|
+
logger.info('No specific workflow names configured, auto-detecting workflows triggered by release events...');
|
|
448
|
+
try {
|
|
449
|
+
workflowNames = await getWorkflowsTriggeredByRelease();
|
|
450
|
+
if (workflowNames.length === 0) {
|
|
451
|
+
logger.info('No workflows found that are triggered by release events.');
|
|
452
|
+
} else {
|
|
453
|
+
logger.info(`Auto-detected release workflows: ${workflowNames.join(', ')}`);
|
|
454
|
+
}
|
|
455
|
+
} catch (error) {
|
|
456
|
+
logger.warn(`Failed to auto-detect release workflows: ${error.message}`);
|
|
457
|
+
workflowNames = undefined; // Fall back to monitoring all workflows
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
await waitForReleaseWorkflows(tagName, {
|
|
461
|
+
timeout: workflowTimeout,
|
|
462
|
+
workflowNames,
|
|
463
|
+
skipUserConfirmation
|
|
464
|
+
});
|
|
493
465
|
}
|
|
466
|
+
} else {
|
|
467
|
+
logger.verbose('Skipping waiting for release workflows (disabled in config).');
|
|
494
468
|
}
|
|
469
|
+
// Switch to target branch
|
|
470
|
+
const targetBranch = ((_runConfig_publish2 = runConfig.publish) === null || _runConfig_publish2 === void 0 ? void 0 : _runConfig_publish2.targetBranch) || 'main';
|
|
471
|
+
logger.info(`Switching to target branch: ${targetBranch}`);
|
|
472
|
+
await runWithDryRunSupport(`git checkout ${targetBranch}`, isDryRun);
|
|
473
|
+
logger.info('Publish process complete.');
|
|
495
474
|
};
|
|
496
475
|
|
|
497
476
|
export { execute };
|