@eldrforge/kodrdriv 0.0.33 → 0.0.37
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 +46 -69
- package/dist/application.js +146 -0
- package/dist/application.js.map +1 -0
- package/dist/arguments.js +22 -21
- package/dist/arguments.js.map +1 -1
- package/dist/commands/audio-commit.js +43 -21
- package/dist/commands/audio-commit.js.map +1 -1
- package/dist/commands/audio-review.js +46 -38
- package/dist/commands/audio-review.js.map +1 -1
- package/dist/commands/clean.js +28 -12
- package/dist/commands/clean.js.map +1 -1
- package/dist/commands/commit.js +132 -39
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/link.js +177 -159
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/publish-tree.js +19 -6
- package/dist/commands/publish-tree.js.map +1 -1
- package/dist/commands/publish.js +152 -82
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/release.js +21 -16
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/review.js +286 -60
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/select-audio.js +25 -8
- package/dist/commands/select-audio.js.map +1 -1
- package/dist/commands/unlink.js +349 -159
- package/dist/commands/unlink.js.map +1 -1
- package/dist/constants.js +14 -5
- package/dist/constants.js.map +1 -1
- package/dist/content/diff.js +7 -5
- package/dist/content/diff.js.map +1 -1
- package/dist/content/log.js +4 -1
- package/dist/content/log.js.map +1 -1
- package/dist/error/CancellationError.js +9 -0
- package/dist/error/CancellationError.js.map +1 -0
- package/dist/error/CommandErrors.js +120 -0
- package/dist/error/CommandErrors.js.map +1 -0
- package/dist/logging.js +55 -12
- package/dist/logging.js.map +1 -1
- package/dist/main.js +6 -131
- package/dist/main.js.map +1 -1
- package/dist/prompt/commit.js +4 -0
- package/dist/prompt/commit.js.map +1 -1
- package/dist/prompt/instructions/commit.md +33 -24
- package/dist/prompt/instructions/release.md +39 -5
- package/dist/prompt/release.js +41 -1
- package/dist/prompt/release.js.map +1 -1
- package/dist/types.js +9 -2
- package/dist/types.js.map +1 -1
- package/dist/util/github.js +71 -4
- package/dist/util/github.js.map +1 -1
- package/dist/util/npmOptimizations.js +174 -0
- package/dist/util/npmOptimizations.js.map +1 -0
- package/dist/util/openai.js +4 -2
- package/dist/util/openai.js.map +1 -1
- package/dist/util/performance.js +202 -0
- package/dist/util/performance.js.map +1 -0
- package/dist/util/safety.js +166 -0
- package/dist/util/safety.js.map +1 -0
- package/dist/util/storage.js +10 -0
- package/dist/util/storage.js.map +1 -1
- package/dist/util/validation.js +81 -0
- package/dist/util/validation.js.map +1 -0
- package/package.json +19 -18
- package/packages/components/package.json +4 -0
- package/packages/tools/package.json +4 -0
- package/packages/utils/package.json +4 -0
- package/scripts/pre-commit-hook.sh +52 -0
- package/test-project/package.json +1 -0
package/dist/commands/publish.js
CHANGED
|
@@ -4,14 +4,16 @@ import { hasStagedChanges } from '../content/diff.js';
|
|
|
4
4
|
import { execute as execute$3 } from './release.js';
|
|
5
5
|
import { execute as execute$4 } from './link.js';
|
|
6
6
|
import { execute as execute$1 } from './unlink.js';
|
|
7
|
-
import { getLogger } from '../logging.js';
|
|
7
|
+
import { getDryRunLogger, getLogger } from '../logging.js';
|
|
8
8
|
import { runWithDryRunSupport, run } from '../util/child.js';
|
|
9
9
|
import { getCurrentBranchName, findOpenPullRequestByHeadRef, createPullRequest, waitForPullRequestChecks, mergePullRequest, createRelease, getWorkflowsTriggeredByRelease, waitForReleaseWorkflows } from '../util/github.js';
|
|
10
10
|
import { create } from '../util/storage.js';
|
|
11
11
|
import { incrementPatchVersion, getOutputPath } from '../util/general.js';
|
|
12
12
|
import { DEFAULT_OUTPUT_DIRECTORY } from '../constants.js';
|
|
13
|
+
import { safeJsonParse, validatePackageJson } from '../util/validation.js';
|
|
13
14
|
|
|
14
15
|
const scanNpmrcForEnvVars = async (storage)=>{
|
|
16
|
+
const logger = getLogger();
|
|
15
17
|
const npmrcPath = path.join(process.cwd(), '.npmrc');
|
|
16
18
|
const envVars = [];
|
|
17
19
|
if (await storage.exists(npmrcPath)) {
|
|
@@ -28,15 +30,17 @@ const scanNpmrcForEnvVars = async (storage)=>{
|
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
32
33
|
} catch (error) {
|
|
33
|
-
|
|
34
|
+
logger.warn(`Failed to read .npmrc file at ${npmrcPath}: ${error.message}`);
|
|
35
|
+
logger.verbose('This may affect environment variable detection for publishing');
|
|
34
36
|
}
|
|
37
|
+
} else {
|
|
38
|
+
logger.debug('.npmrc file not found, skipping environment variable scan');
|
|
35
39
|
}
|
|
36
40
|
return envVars;
|
|
37
41
|
};
|
|
38
42
|
const validateEnvironmentVariables = (requiredEnvVars, isDryRun)=>{
|
|
39
|
-
const logger =
|
|
43
|
+
const logger = getDryRunLogger(isDryRun);
|
|
40
44
|
const missingEnvVars = [];
|
|
41
45
|
for (const envVar of requiredEnvVars){
|
|
42
46
|
if (!process.env[envVar]) {
|
|
@@ -45,7 +49,7 @@ const validateEnvironmentVariables = (requiredEnvVars, isDryRun)=>{
|
|
|
45
49
|
}
|
|
46
50
|
if (missingEnvVars.length > 0) {
|
|
47
51
|
if (isDryRun) {
|
|
48
|
-
logger.warn(`
|
|
52
|
+
logger.warn(`Missing required environment variables: ${missingEnvVars.join(', ')}`);
|
|
49
53
|
} else {
|
|
50
54
|
logger.error(`Missing required environment variables: ${missingEnvVars.join(', ')}`);
|
|
51
55
|
throw new Error(`Missing required environment variables: ${missingEnvVars.join(', ')}. Please set these environment variables before running publish.`);
|
|
@@ -54,46 +58,44 @@ const validateEnvironmentVariables = (requiredEnvVars, isDryRun)=>{
|
|
|
54
58
|
};
|
|
55
59
|
const runPrechecks = async (runConfig)=>{
|
|
56
60
|
var _runConfig_publish;
|
|
57
|
-
const
|
|
61
|
+
const isDryRun = runConfig.dryRun || false;
|
|
62
|
+
const logger = getDryRunLogger(isDryRun);
|
|
58
63
|
const storage = create({
|
|
59
64
|
log: logger.info
|
|
60
65
|
});
|
|
61
|
-
|
|
62
|
-
logger.info(isDryRun ? 'DRY RUN: Running prechecks...' : 'Running prechecks...');
|
|
66
|
+
logger.info('Running prechecks...');
|
|
63
67
|
// Check if we're in a git repository
|
|
64
68
|
try {
|
|
65
69
|
if (isDryRun) {
|
|
66
|
-
logger.info('
|
|
70
|
+
logger.info('Would check git repository with: git rev-parse --git-dir');
|
|
67
71
|
} else {
|
|
68
72
|
await run('git rev-parse --git-dir');
|
|
69
73
|
}
|
|
70
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
71
74
|
} catch (error) {
|
|
72
75
|
if (!isDryRun) {
|
|
73
76
|
throw new Error('Not in a git repository. Please run this command from within a git repository.');
|
|
74
77
|
}
|
|
75
78
|
}
|
|
76
79
|
// Check for uncommitted changes
|
|
77
|
-
logger.info(
|
|
80
|
+
logger.info('Checking for uncommitted changes...');
|
|
78
81
|
try {
|
|
79
82
|
if (isDryRun) {
|
|
80
|
-
logger.info('
|
|
83
|
+
logger.info('Would check git status with: git status --porcelain');
|
|
81
84
|
} else {
|
|
82
85
|
const { stdout } = await run('git status --porcelain');
|
|
83
86
|
if (stdout.trim()) {
|
|
84
87
|
throw new Error('Working directory has uncommitted changes. Please commit or stash your changes before running publish.');
|
|
85
88
|
}
|
|
86
89
|
}
|
|
87
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
88
90
|
} catch (error) {
|
|
89
91
|
if (!isDryRun) {
|
|
90
92
|
throw new Error('Failed to check git status. Please ensure you are in a valid git repository.');
|
|
91
93
|
}
|
|
92
94
|
}
|
|
93
95
|
// Check if we're on a release branch
|
|
94
|
-
logger.info(
|
|
96
|
+
logger.info('Checking current branch...');
|
|
95
97
|
if (isDryRun) {
|
|
96
|
-
logger.info('
|
|
98
|
+
logger.info('Would verify current branch is a release branch (starts with "release/")');
|
|
97
99
|
} else {
|
|
98
100
|
const currentBranch = await getCurrentBranchName();
|
|
99
101
|
if (!currentBranch.startsWith('release/')) {
|
|
@@ -101,38 +103,38 @@ const runPrechecks = async (runConfig)=>{
|
|
|
101
103
|
}
|
|
102
104
|
}
|
|
103
105
|
// Check if prepublishOnly script exists in package.json
|
|
104
|
-
logger.info(
|
|
106
|
+
logger.info('Checking for prepublishOnly script...');
|
|
105
107
|
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
106
108
|
if (!await storage.exists(packageJsonPath)) {
|
|
107
109
|
if (!isDryRun) {
|
|
108
110
|
throw new Error('package.json not found in current directory.');
|
|
109
111
|
} else {
|
|
110
|
-
logger.warn('
|
|
112
|
+
logger.warn('package.json not found in current directory.');
|
|
111
113
|
}
|
|
112
114
|
} else {
|
|
113
115
|
var _packageJson_scripts;
|
|
114
116
|
let packageJson;
|
|
115
117
|
try {
|
|
116
118
|
const packageJsonContents = await storage.readFile(packageJsonPath, 'utf-8');
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
const parsed = safeJsonParse(packageJsonContents, packageJsonPath);
|
|
120
|
+
packageJson = validatePackageJson(parsed, packageJsonPath);
|
|
119
121
|
} catch (error) {
|
|
120
122
|
if (!isDryRun) {
|
|
121
123
|
throw new Error('Failed to parse package.json. Please ensure it contains valid JSON.');
|
|
122
124
|
} else {
|
|
123
|
-
logger.warn('
|
|
125
|
+
logger.warn('Failed to parse package.json. Please ensure it contains valid JSON.');
|
|
124
126
|
}
|
|
125
127
|
}
|
|
126
128
|
if (packageJson && !((_packageJson_scripts = packageJson.scripts) === null || _packageJson_scripts === void 0 ? void 0 : _packageJson_scripts.prepublishOnly)) {
|
|
127
129
|
if (!isDryRun) {
|
|
128
130
|
throw new Error('prepublishOnly script is required in package.json but was not found. Please add a prepublishOnly script that runs your pre-flight checks (e.g., clean, lint, build, test).');
|
|
129
131
|
} else {
|
|
130
|
-
logger.warn('
|
|
132
|
+
logger.warn('prepublishOnly script is required in package.json but was not found.');
|
|
131
133
|
}
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
// Check required environment variables
|
|
135
|
-
logger.verbose(
|
|
137
|
+
logger.verbose('Checking required environment variables...');
|
|
136
138
|
const coreRequiredEnvVars = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.requiredEnvVars) || [];
|
|
137
139
|
const npmrcEnvVars = isDryRun ? [] : await scanNpmrcForEnvVars(storage); // Skip .npmrc scan in dry run
|
|
138
140
|
const allRequiredEnvVars = [
|
|
@@ -142,65 +144,67 @@ const runPrechecks = async (runConfig)=>{
|
|
|
142
144
|
])
|
|
143
145
|
];
|
|
144
146
|
if (allRequiredEnvVars.length > 0) {
|
|
145
|
-
logger.verbose(
|
|
147
|
+
logger.verbose(`Required environment variables: ${allRequiredEnvVars.join(', ')}`);
|
|
146
148
|
validateEnvironmentVariables(allRequiredEnvVars, isDryRun);
|
|
147
149
|
} else {
|
|
148
|
-
logger.verbose(
|
|
150
|
+
logger.verbose('No required environment variables specified.');
|
|
149
151
|
}
|
|
150
|
-
logger.info(
|
|
152
|
+
logger.info('All prechecks passed.');
|
|
151
153
|
};
|
|
152
154
|
const execute = async (runConfig)=>{
|
|
153
|
-
const
|
|
155
|
+
const isDryRun = runConfig.dryRun || false;
|
|
156
|
+
const logger = getDryRunLogger(isDryRun);
|
|
154
157
|
const storage = create({
|
|
155
158
|
log: logger.info
|
|
156
159
|
});
|
|
157
|
-
|
|
160
|
+
// Track whether the publish process completed successfully
|
|
161
|
+
let publishCompleted = false;
|
|
158
162
|
// Run prechecks before starting any work
|
|
159
163
|
await runPrechecks(runConfig);
|
|
160
|
-
logger.info(
|
|
164
|
+
logger.info('Starting release process...');
|
|
161
165
|
try {
|
|
162
166
|
var _runConfig_publish, _runConfig_publish1, _runConfig_publish2;
|
|
163
167
|
// Unlink all workspace packages before starting (if enabled)
|
|
164
168
|
const shouldUnlink = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.unlinkWorkspacePackages) !== false; // default to true
|
|
165
169
|
if (shouldUnlink) {
|
|
166
|
-
logger.verbose(
|
|
170
|
+
logger.verbose('Unlinking workspace packages...');
|
|
167
171
|
await execute$1(runConfig);
|
|
168
172
|
} else {
|
|
169
|
-
logger.verbose(
|
|
173
|
+
logger.verbose('Skipping unlink workspace packages (disabled in config).');
|
|
170
174
|
}
|
|
171
175
|
let pr = null;
|
|
172
176
|
if (isDryRun) {
|
|
173
|
-
logger.info('
|
|
174
|
-
logger.info('
|
|
177
|
+
logger.info('Would check for existing pull request');
|
|
178
|
+
logger.info('Assuming no existing PR found for demo purposes');
|
|
175
179
|
} else {
|
|
176
180
|
const branchName = await getCurrentBranchName();
|
|
177
181
|
pr = await findOpenPullRequestByHeadRef(branchName);
|
|
178
182
|
}
|
|
179
183
|
if (pr) {
|
|
180
|
-
logger.info(
|
|
184
|
+
logger.info(`Found existing pull request for branch: ${pr.html_url}`);
|
|
181
185
|
} else {
|
|
182
186
|
var _runConfig_publish3;
|
|
183
|
-
logger.info(
|
|
187
|
+
logger.info('No open pull request found, starting new release publishing process...');
|
|
184
188
|
// 1. Prepare for release
|
|
185
|
-
logger.verbose(
|
|
186
|
-
logger.verbose(
|
|
189
|
+
logger.verbose('Preparing for release: switching from workspace to remote dependencies.');
|
|
190
|
+
logger.verbose('Updating dependencies to latest versions from registry');
|
|
187
191
|
const updatePatterns = (_runConfig_publish3 = runConfig.publish) === null || _runConfig_publish3 === void 0 ? void 0 : _runConfig_publish3.dependencyUpdatePatterns;
|
|
188
192
|
if (updatePatterns && updatePatterns.length > 0) {
|
|
189
|
-
logger.verbose(
|
|
193
|
+
logger.verbose(`Updating dependencies matching patterns: ${updatePatterns.join(', ')}`);
|
|
190
194
|
const patternsArg = updatePatterns.join(' ');
|
|
191
|
-
await runWithDryRunSupport(`
|
|
195
|
+
await runWithDryRunSupport(`npm update ${patternsArg}`, isDryRun);
|
|
192
196
|
} else {
|
|
193
|
-
logger.verbose(
|
|
194
|
-
await runWithDryRunSupport('
|
|
197
|
+
logger.verbose('No dependency update patterns specified, updating all dependencies');
|
|
198
|
+
await runWithDryRunSupport('npm update', isDryRun);
|
|
195
199
|
}
|
|
196
|
-
logger.verbose(
|
|
197
|
-
await runWithDryRunSupport('git add package.json
|
|
198
|
-
logger.info(
|
|
199
|
-
await runWithDryRunSupport('
|
|
200
|
-
logger.verbose(
|
|
200
|
+
logger.verbose('Staging changes for release commit');
|
|
201
|
+
await runWithDryRunSupport('git add package.json package-lock.json', isDryRun);
|
|
202
|
+
logger.info('Running prepublishOnly script...');
|
|
203
|
+
await runWithDryRunSupport('npm run prepublishOnly', isDryRun);
|
|
204
|
+
logger.verbose('Checking for staged changes...');
|
|
201
205
|
if (isDryRun) {
|
|
202
|
-
logger.verbose('
|
|
203
|
-
logger.verbose('
|
|
206
|
+
logger.verbose('Assuming staged changes exist for demo purposes');
|
|
207
|
+
logger.verbose('Would create commit...');
|
|
204
208
|
await execute$2(runConfig);
|
|
205
209
|
} else {
|
|
206
210
|
if (await hasStagedChanges()) {
|
|
@@ -210,13 +214,14 @@ const execute = async (runConfig)=>{
|
|
|
210
214
|
logger.verbose('No changes to commit, skipping commit.');
|
|
211
215
|
}
|
|
212
216
|
}
|
|
213
|
-
logger.info(
|
|
217
|
+
logger.info('Bumping version...');
|
|
214
218
|
// Manually increment version without creating a tag
|
|
215
219
|
if (isDryRun) {
|
|
216
|
-
logger.info('
|
|
220
|
+
logger.info('Would manually increment patch version in package.json and commit');
|
|
217
221
|
} else {
|
|
218
222
|
const packageJsonContents = await storage.readFile('package.json', 'utf-8');
|
|
219
|
-
const
|
|
223
|
+
const parsed = safeJsonParse(packageJsonContents, 'package.json');
|
|
224
|
+
const packageJson = validatePackageJson(parsed, 'package.json');
|
|
220
225
|
const currentVersion = packageJson.version;
|
|
221
226
|
const newVersion = incrementPatchVersion(currentVersion);
|
|
222
227
|
packageJson.version = newVersion;
|
|
@@ -227,10 +232,10 @@ const execute = async (runConfig)=>{
|
|
|
227
232
|
await run(`git commit -m "chore: bump version to ${newVersion}"`);
|
|
228
233
|
logger.info(`Version change committed: ${newVersion}`);
|
|
229
234
|
}
|
|
230
|
-
logger.info(
|
|
235
|
+
logger.info('Generating release notes...');
|
|
231
236
|
const releaseSummary = await execute$3(runConfig);
|
|
232
237
|
if (isDryRun) {
|
|
233
|
-
logger.info('
|
|
238
|
+
logger.info('Would write release notes to RELEASE_NOTES.md and RELEASE_TITLE.md in output directory');
|
|
234
239
|
} else {
|
|
235
240
|
const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
|
|
236
241
|
await storage.ensureDirectory(outputDirectory);
|
|
@@ -240,11 +245,13 @@ const execute = async (runConfig)=>{
|
|
|
240
245
|
await storage.writeFile(releaseTitlePath, releaseSummary.title, 'utf-8');
|
|
241
246
|
logger.info(`Release notes and title generated and saved to ${releaseNotesPath} and ${releaseTitlePath}.`);
|
|
242
247
|
}
|
|
243
|
-
logger.info(
|
|
244
|
-
|
|
245
|
-
|
|
248
|
+
logger.info('Pushing to origin...');
|
|
249
|
+
// Get current branch name and push explicitly to avoid pushing to wrong remote/branch
|
|
250
|
+
const currentBranch = await getCurrentBranchName();
|
|
251
|
+
await runWithDryRunSupport(`git push origin ${currentBranch}`, isDryRun);
|
|
252
|
+
logger.info('Creating pull request...');
|
|
246
253
|
if (isDryRun) {
|
|
247
|
-
logger.info('
|
|
254
|
+
logger.info('Would get commit title and create PR with GitHub API');
|
|
248
255
|
pr = {
|
|
249
256
|
number: 123,
|
|
250
257
|
html_url: 'https://github.com/mock/repo/pull/123',
|
|
@@ -259,7 +266,7 @@ const execute = async (runConfig)=>{
|
|
|
259
266
|
logger.info(`Pull request created: ${pr.html_url}`);
|
|
260
267
|
}
|
|
261
268
|
}
|
|
262
|
-
logger.info(
|
|
269
|
+
logger.info(`Waiting for PR #${pr.number} checks to complete...`);
|
|
263
270
|
if (!isDryRun) {
|
|
264
271
|
var _runConfig_publish4, _runConfig_publish5, _runConfig_publish6;
|
|
265
272
|
// Configure timeout and user confirmation behavior
|
|
@@ -274,21 +281,23 @@ const execute = async (runConfig)=>{
|
|
|
274
281
|
}
|
|
275
282
|
const mergeMethod = ((_runConfig_publish1 = runConfig.publish) === null || _runConfig_publish1 === void 0 ? void 0 : _runConfig_publish1.mergeMethod) || 'squash';
|
|
276
283
|
if (isDryRun) {
|
|
277
|
-
logger.info(`
|
|
284
|
+
logger.info(`Would merge PR #${pr.number} using ${mergeMethod} method`);
|
|
278
285
|
} else {
|
|
279
286
|
await mergePullRequest(pr.number, mergeMethod);
|
|
280
287
|
}
|
|
281
|
-
logger.info(
|
|
288
|
+
logger.info('Checking out main branch...');
|
|
282
289
|
await runWithDryRunSupport('git checkout main', isDryRun);
|
|
283
290
|
await runWithDryRunSupport('git pull origin main', isDryRun);
|
|
284
291
|
// Now create and push the tag on the main branch
|
|
285
|
-
logger.info(
|
|
292
|
+
logger.info('Creating release tag...');
|
|
293
|
+
let tagName;
|
|
286
294
|
if (isDryRun) {
|
|
287
|
-
logger.info('
|
|
295
|
+
logger.info('Would read package.json version and create git tag');
|
|
296
|
+
tagName = 'v1.0.0'; // Mock version for dry run
|
|
288
297
|
} else {
|
|
289
298
|
const packageJsonContents = await storage.readFile('package.json', 'utf-8');
|
|
290
299
|
const { version } = JSON.parse(packageJsonContents);
|
|
291
|
-
|
|
300
|
+
tagName = `v${version}`;
|
|
292
301
|
// Check if tag already exists locally
|
|
293
302
|
try {
|
|
294
303
|
const { stdout } = await run(`git tag -l ${tagName}`);
|
|
@@ -304,6 +313,7 @@ const execute = async (runConfig)=>{
|
|
|
304
313
|
logger.info(`Created local tag: ${tagName}`);
|
|
305
314
|
}
|
|
306
315
|
// Check if tag exists on remote before pushing
|
|
316
|
+
let tagWasPushed = false;
|
|
307
317
|
try {
|
|
308
318
|
const { stdout } = await run(`git ls-remote origin refs/tags/${tagName}`);
|
|
309
319
|
if (stdout.trim()) {
|
|
@@ -311,12 +321,14 @@ const execute = async (runConfig)=>{
|
|
|
311
321
|
} else {
|
|
312
322
|
await run(`git push origin ${tagName}`);
|
|
313
323
|
logger.info(`Pushed tag to remote: ${tagName}`);
|
|
324
|
+
tagWasPushed = true;
|
|
314
325
|
}
|
|
315
326
|
} catch (error) {
|
|
316
327
|
// If ls-remote fails, try to push anyway (might be a new remote)
|
|
317
328
|
try {
|
|
318
329
|
await run(`git push origin ${tagName}`);
|
|
319
330
|
logger.info(`Pushed tag to remote: ${tagName}`);
|
|
331
|
+
tagWasPushed = true;
|
|
320
332
|
} catch (pushError) {
|
|
321
333
|
if (pushError.message && pushError.message.includes('already exists')) {
|
|
322
334
|
logger.info(`Tag ${tagName} already exists on remote, continuing...`);
|
|
@@ -325,29 +337,51 @@ const execute = async (runConfig)=>{
|
|
|
325
337
|
}
|
|
326
338
|
}
|
|
327
339
|
}
|
|
340
|
+
// If we just pushed a new tag, wait for GitHub to process it
|
|
341
|
+
if (tagWasPushed) {
|
|
342
|
+
logger.verbose('Waiting for GitHub to process the pushed tag...');
|
|
343
|
+
await new Promise((resolve)=>setTimeout(resolve, 5000)); // 5 second delay
|
|
344
|
+
}
|
|
328
345
|
}
|
|
329
|
-
logger.info(
|
|
330
|
-
let tagName;
|
|
346
|
+
logger.info('Creating GitHub release...');
|
|
331
347
|
if (isDryRun) {
|
|
332
|
-
logger.info('
|
|
333
|
-
tagName = 'v1.0.0'; // Mock version for dry run
|
|
348
|
+
logger.info('Would read package.json version and create GitHub release with retry logic');
|
|
334
349
|
} else {
|
|
335
|
-
const packageJsonContents = await storage.readFile('package.json', 'utf-8');
|
|
336
|
-
const { version } = JSON.parse(packageJsonContents);
|
|
337
|
-
tagName = `v${version}`;
|
|
338
350
|
const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
|
|
339
351
|
const releaseNotesPath = getOutputPath(outputDirectory, 'RELEASE_NOTES.md');
|
|
340
352
|
const releaseTitlePath = getOutputPath(outputDirectory, 'RELEASE_TITLE.md');
|
|
341
353
|
const releaseNotesContent = await storage.readFile(releaseNotesPath, 'utf-8');
|
|
342
354
|
const releaseTitle = await storage.readFile(releaseTitlePath, 'utf-8');
|
|
343
|
-
|
|
355
|
+
// Create release with retry logic to handle GitHub tag processing delays
|
|
356
|
+
let retries = 3;
|
|
357
|
+
while(retries > 0){
|
|
358
|
+
try {
|
|
359
|
+
await createRelease(tagName, releaseTitle, releaseNotesContent);
|
|
360
|
+
logger.info(`GitHub release created successfully for tag: ${tagName}`);
|
|
361
|
+
break; // Success - exit retry loop
|
|
362
|
+
} catch (error) {
|
|
363
|
+
// Check if this is a tag-not-found error that we can retry
|
|
364
|
+
const isTagNotFoundError = error.message && (error.message.includes('not found') || error.message.includes('does not exist') || error.message.includes('Reference does not exist'));
|
|
365
|
+
if (isTagNotFoundError && retries > 1) {
|
|
366
|
+
logger.verbose(`Tag ${tagName} not yet available on GitHub, retrying in 3 seconds... (${retries - 1} retries left)`);
|
|
367
|
+
await new Promise((resolve)=>setTimeout(resolve, 3000));
|
|
368
|
+
retries--;
|
|
369
|
+
} else if (isTagNotFoundError) {
|
|
370
|
+
// Tag not found error and we're out of retries
|
|
371
|
+
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.`);
|
|
372
|
+
} else {
|
|
373
|
+
// Not a tag-not-found error - re-throw the original error
|
|
374
|
+
throw error;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
344
378
|
}
|
|
345
379
|
// Wait for release workflows to complete (if enabled)
|
|
346
380
|
const waitForWorkflows = ((_runConfig_publish2 = runConfig.publish) === null || _runConfig_publish2 === void 0 ? void 0 : _runConfig_publish2.waitForReleaseWorkflows) !== false; // default to true
|
|
347
381
|
if (waitForWorkflows) {
|
|
348
|
-
logger.info(
|
|
382
|
+
logger.info('Waiting for release workflows...');
|
|
349
383
|
if (isDryRun) {
|
|
350
|
-
logger.info('
|
|
384
|
+
logger.info('Would monitor GitHub Actions workflows triggered by release');
|
|
351
385
|
} else {
|
|
352
386
|
var _runConfig_publish7, _runConfig_publish8, _runConfig_publish9, _runConfig_publish10;
|
|
353
387
|
const workflowTimeout = ((_runConfig_publish7 = runConfig.publish) === null || _runConfig_publish7 === void 0 ? void 0 : _runConfig_publish7.releaseWorkflowsTimeout) || 600000; // 10 minutes default
|
|
@@ -376,30 +410,66 @@ const execute = async (runConfig)=>{
|
|
|
376
410
|
});
|
|
377
411
|
}
|
|
378
412
|
} else {
|
|
379
|
-
logger.verbose(
|
|
413
|
+
logger.verbose('Skipping waiting for release workflows (disabled in config).');
|
|
380
414
|
}
|
|
381
|
-
logger.info(
|
|
415
|
+
logger.info('Creating new release branch...');
|
|
382
416
|
if (isDryRun) {
|
|
383
|
-
logger.info('
|
|
417
|
+
logger.info('Would create next release branch (e.g., release/1.0.1) and push to origin');
|
|
384
418
|
} else {
|
|
385
419
|
const packageJsonContents = await storage.readFile('package.json', 'utf-8');
|
|
386
420
|
const { version } = JSON.parse(packageJsonContents);
|
|
387
421
|
const nextVersion = incrementPatchVersion(version);
|
|
388
422
|
const newBranchName = `release/${nextVersion}`;
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
423
|
+
// Check if branch already exists locally
|
|
424
|
+
let branchExists = false;
|
|
425
|
+
try {
|
|
426
|
+
await run(`git show-ref --verify --quiet refs/heads/${newBranchName}`);
|
|
427
|
+
branchExists = true;
|
|
428
|
+
} catch {
|
|
429
|
+
// Branch doesn't exist locally
|
|
430
|
+
branchExists = false;
|
|
431
|
+
}
|
|
432
|
+
if (branchExists) {
|
|
433
|
+
// Branch exists, switch to it
|
|
434
|
+
await run(`git checkout ${newBranchName}`);
|
|
435
|
+
logger.info(`Switched to existing branch ${newBranchName}`);
|
|
436
|
+
} else {
|
|
437
|
+
// Branch doesn't exist, create it
|
|
438
|
+
await run(`git checkout -b ${newBranchName}`);
|
|
439
|
+
logger.info(`Created new branch ${newBranchName}`);
|
|
440
|
+
}
|
|
441
|
+
// Check if branch exists on remote before pushing
|
|
442
|
+
let remoteExists = false;
|
|
443
|
+
try {
|
|
444
|
+
const { stdout } = await run(`git ls-remote origin refs/heads/${newBranchName}`);
|
|
445
|
+
remoteExists = stdout.trim() !== '';
|
|
446
|
+
} catch {
|
|
447
|
+
// Assume remote doesn't exist if ls-remote fails
|
|
448
|
+
remoteExists = false;
|
|
449
|
+
}
|
|
450
|
+
if (remoteExists) {
|
|
451
|
+
logger.info(`Branch ${newBranchName} already exists on remote, skipping push`);
|
|
452
|
+
} else {
|
|
453
|
+
await run(`git push -u origin ${newBranchName}`);
|
|
454
|
+
logger.info(`Branch ${newBranchName} pushed to origin.`);
|
|
455
|
+
}
|
|
392
456
|
}
|
|
393
|
-
logger.info(
|
|
457
|
+
logger.info('Preparation complete.');
|
|
458
|
+
publishCompleted = true; // Mark as completed only if we reach this point
|
|
394
459
|
} finally{
|
|
395
460
|
var _runConfig_publish11;
|
|
396
|
-
//
|
|
461
|
+
// Always restore linked packages if enabled, regardless of success/failure
|
|
462
|
+
// This ensures we don't leave the repository with file: dependencies
|
|
397
463
|
const shouldLink = ((_runConfig_publish11 = runConfig.publish) === null || _runConfig_publish11 === void 0 ? void 0 : _runConfig_publish11.linkWorkspacePackages) !== false; // default to true
|
|
398
464
|
if (shouldLink) {
|
|
399
|
-
|
|
465
|
+
if (publishCompleted) {
|
|
466
|
+
logger.verbose('Restoring linked packages after successful publish...');
|
|
467
|
+
} else {
|
|
468
|
+
logger.verbose('Restoring linked packages after failed publish to avoid leaving file: dependencies...');
|
|
469
|
+
}
|
|
400
470
|
await execute$4(runConfig);
|
|
401
471
|
} else {
|
|
402
|
-
logger.verbose(
|
|
472
|
+
logger.verbose('Skipping restore linked packages (disabled in config).');
|
|
403
473
|
}
|
|
404
474
|
}
|
|
405
475
|
};
|