relizy 0.2.5-beta.11 → 0.2.5-beta.12
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/dist/cli.mjs +5 -5
- package/dist/index.d.mts +14 -7
- package/dist/index.d.ts +14 -7
- package/dist/index.mjs +6 -6
- package/dist/shared/{relizy.DLoE22sp.mjs → relizy.PGChVRhn.mjs} +2214 -2202
- package/package.json +1 -1
|
@@ -1,2452 +1,2479 @@
|
|
|
1
1
|
import { logger, execPromise } from '@maz-ui/node';
|
|
2
|
-
import
|
|
3
|
-
import { execSync } from 'node:child_process';
|
|
4
|
-
import { existsSync, readFileSync, writeFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
5
3
|
import path, { join, relative } from 'node:path';
|
|
6
|
-
import {
|
|
7
|
-
import { setupDotenv, loadConfig } from 'c12';
|
|
8
|
-
import { formatCompareChanges, formatReference, resolveRepoConfig, getRepoConfig, createGithubRelease, getGitDiff, parseCommits } from 'changelogen';
|
|
9
|
-
import { defu } from 'defu';
|
|
4
|
+
import { getGitDiff, parseCommits, formatCompareChanges, formatReference, resolveRepoConfig, getRepoConfig, createGithubRelease } from 'changelogen';
|
|
10
5
|
import fastGlob from 'fast-glob';
|
|
11
6
|
import { confirm, input } from '@inquirer/prompts';
|
|
7
|
+
import { upperFirst, formatJson } from '@maz-ui/utils';
|
|
12
8
|
import * as semver from 'semver';
|
|
9
|
+
import { execSync } from 'node:child_process';
|
|
10
|
+
import process$1, { exit } from 'node:process';
|
|
11
|
+
import { setupDotenv, loadConfig } from 'c12';
|
|
12
|
+
import { defu } from 'defu';
|
|
13
13
|
import { convert } from 'convert-gitmoji';
|
|
14
14
|
import { fetch as fetch$1 } from 'node-fetch-native';
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
const result = await hookInput(config, dryRun, params);
|
|
25
|
-
if (result)
|
|
26
|
-
logger.debug(`Hook ${hook} returned: ${result}`);
|
|
27
|
-
logger.info(`Hook ${hook} executed`);
|
|
28
|
-
return result;
|
|
16
|
+
function getPackageDependencies({
|
|
17
|
+
packagePath,
|
|
18
|
+
allPackageNames,
|
|
19
|
+
dependencyTypes
|
|
20
|
+
}) {
|
|
21
|
+
const packageJsonPath = join(packagePath, "package.json");
|
|
22
|
+
if (!existsSync(packageJsonPath)) {
|
|
23
|
+
return [];
|
|
29
24
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return result;
|
|
25
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
26
|
+
const deps = [];
|
|
27
|
+
const allDeps = {
|
|
28
|
+
...dependencyTypes?.includes("dependencies") ? packageJson.dependencies : {},
|
|
29
|
+
...dependencyTypes?.includes("peerDependencies") ? packageJson.peerDependencies : {},
|
|
30
|
+
...dependencyTypes?.includes("devDependencies") ? packageJson.devDependencies : {}
|
|
31
|
+
};
|
|
32
|
+
for (const depName of Object.keys(allDeps)) {
|
|
33
|
+
if (allPackageNames.has(depName)) {
|
|
34
|
+
deps.push(depName);
|
|
35
|
+
}
|
|
42
36
|
}
|
|
37
|
+
return deps;
|
|
43
38
|
}
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
function getDependentsOf({
|
|
40
|
+
allPackages,
|
|
41
|
+
packageName
|
|
42
|
+
}) {
|
|
43
|
+
return allPackages.filter(
|
|
44
|
+
(pkg) => pkg.dependencies.includes(packageName)
|
|
47
45
|
);
|
|
48
46
|
}
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (process.env.GITLAB_CI === "true")
|
|
53
|
-
return "GitLab CI";
|
|
54
|
-
if (process.env.CIRCLECI === "true")
|
|
55
|
-
return "CircleCI";
|
|
56
|
-
if (process.env.TRAVIS === "true")
|
|
57
|
-
return "Travis CI";
|
|
58
|
-
if (process.env.JENKINS_HOME || process.env.JENKINS_URL)
|
|
59
|
-
return "Jenkins";
|
|
60
|
-
if (process.env.TF_BUILD === "True")
|
|
61
|
-
return "Azure Pipelines";
|
|
62
|
-
if (process.env.TEAMCITY_VERSION)
|
|
63
|
-
return "TeamCity";
|
|
64
|
-
if (process.env.BITBUCKET_BUILD_NUMBER)
|
|
65
|
-
return "Bitbucket Pipelines";
|
|
66
|
-
if (process.env.DRONE === "true")
|
|
67
|
-
return "Drone";
|
|
68
|
-
if (process.env.APPVEYOR)
|
|
69
|
-
return "AppVeyor";
|
|
70
|
-
if (process.env.BUILDKITE === "true")
|
|
71
|
-
return "Buildkite";
|
|
72
|
-
if (process.env.CODEBUILD_BUILD_ID)
|
|
73
|
-
return "AWS CodeBuild";
|
|
74
|
-
if (process.env.NETLIFY === "true")
|
|
75
|
-
return "Netlify";
|
|
76
|
-
if (process.env.VERCEL === "1")
|
|
77
|
-
return "Vercel";
|
|
78
|
-
if (process.env.HEROKU_TEST_RUN_ID)
|
|
79
|
-
return "Heroku CI";
|
|
80
|
-
if (process.env.BUDDY === "true")
|
|
81
|
-
return "Buddy";
|
|
82
|
-
if (process.env.SEMAPHORE === "true")
|
|
83
|
-
return "Semaphore";
|
|
84
|
-
if (process.env.CF_BUILD_ID)
|
|
85
|
-
return "Codefresh";
|
|
86
|
-
if (process.env.bamboo_buildKey)
|
|
87
|
-
return "Bamboo";
|
|
88
|
-
if (process.env.BUILD_ID && process.env.PROJECT_ID)
|
|
89
|
-
return "Google Cloud Build";
|
|
90
|
-
if (process.env.SCREWDRIVER === "true")
|
|
91
|
-
return "Screwdriver";
|
|
92
|
-
if (process.env.STRIDER === "true")
|
|
93
|
-
return "Strider";
|
|
94
|
-
if (process.env.CI === "true")
|
|
95
|
-
return "Unknown CI";
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
async function executeFormatCmd({
|
|
99
|
-
config,
|
|
100
|
-
dryRun
|
|
47
|
+
function expandPackagesToBumpWithDependents({
|
|
48
|
+
allPackages,
|
|
49
|
+
packagesWithCommits
|
|
101
50
|
}) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
51
|
+
const result = /* @__PURE__ */ new Map();
|
|
52
|
+
logger.debug(`Expanding packages to bump: ${packagesWithCommits.length} packages with commits, ${allPackages.length} total packages`);
|
|
53
|
+
for (const pkg of packagesWithCommits) {
|
|
54
|
+
const packageToBump = {
|
|
55
|
+
...pkg,
|
|
56
|
+
reason: "commits"
|
|
57
|
+
};
|
|
58
|
+
result.set(pkg.name, packageToBump);
|
|
59
|
+
}
|
|
60
|
+
const toProcess = [...packagesWithCommits.map((p) => p.name)];
|
|
61
|
+
const processed = /* @__PURE__ */ new Set();
|
|
62
|
+
while (toProcess.length > 0) {
|
|
63
|
+
const currentPkgName = toProcess.shift();
|
|
64
|
+
if (!currentPkgName || processed.has(currentPkgName)) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
processed.add(currentPkgName);
|
|
68
|
+
const dependents = getDependentsOf({
|
|
69
|
+
packageName: currentPkgName,
|
|
70
|
+
allPackages
|
|
71
|
+
});
|
|
72
|
+
for (const dependent of dependents) {
|
|
73
|
+
if (!result.has(dependent.name)) {
|
|
74
|
+
const currentChain = result.get(currentPkgName)?.dependencyChain || [];
|
|
75
|
+
const chain = [...currentChain, currentPkgName];
|
|
76
|
+
const packageBase = allPackages.find((p) => p.name === dependent.name);
|
|
77
|
+
if (packageBase) {
|
|
78
|
+
const packageToBump = {
|
|
79
|
+
...packageBase,
|
|
80
|
+
reason: "dependency",
|
|
81
|
+
dependencyChain: chain
|
|
82
|
+
};
|
|
83
|
+
result.set(dependent.name, packageToBump);
|
|
84
|
+
toProcess.push(dependent.name);
|
|
85
|
+
logger.debug(`${dependent.name} will be bumped (depends on ${chain.join(" \u2192 ")})`);
|
|
86
|
+
}
|
|
116
87
|
}
|
|
117
|
-
} catch (error) {
|
|
118
|
-
throw new Error(`Format command failed: ${error}`);
|
|
119
88
|
}
|
|
120
|
-
} else {
|
|
121
|
-
logger.debug("No format command specified");
|
|
122
89
|
}
|
|
90
|
+
return Array.from(result.values());
|
|
123
91
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
logger.info("Build completed");
|
|
139
|
-
} else {
|
|
140
|
-
logger.log("[dry-run] exec build command: ", config.publish.buildCmd);
|
|
92
|
+
function topologicalSort(packages) {
|
|
93
|
+
const sorted = [];
|
|
94
|
+
const visited = /* @__PURE__ */ new Set();
|
|
95
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
96
|
+
const packageMap = /* @__PURE__ */ new Map();
|
|
97
|
+
for (const pkg of packages) {
|
|
98
|
+
packageMap.set(pkg.name, pkg);
|
|
99
|
+
}
|
|
100
|
+
function visit(pkgName, path = []) {
|
|
101
|
+
logger.debug(`Visiting ${pkgName}, path: ${path.join(" \u2192 ")}, visiting: ${Array.from(visiting).join(", ")}`);
|
|
102
|
+
if (visiting.has(pkgName)) {
|
|
103
|
+
const cycle = [...path, pkgName];
|
|
104
|
+
logger.warn(`Circular dependency detected: ${cycle.join(" \u2192 ")}`);
|
|
105
|
+
return;
|
|
141
106
|
}
|
|
142
|
-
|
|
143
|
-
|
|
107
|
+
if (visited.has(pkgName)) {
|
|
108
|
+
logger.debug(`${pkgName} already visited globally, skipping`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
visiting.add(pkgName);
|
|
112
|
+
logger.debug(`Added ${pkgName} to visiting set`);
|
|
113
|
+
const pkg = packageMap.get(pkgName);
|
|
114
|
+
if (!pkg) {
|
|
115
|
+
logger.debug(`Package ${pkgName} not found in packageMap`);
|
|
116
|
+
visiting.delete(pkgName);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
logger.debug(`${pkgName} has dependencies: ${pkg.dependencies.join(", ")}`);
|
|
120
|
+
for (const depName of pkg.dependencies) {
|
|
121
|
+
visit(depName, [...path, pkgName]);
|
|
122
|
+
}
|
|
123
|
+
visiting.delete(pkgName);
|
|
124
|
+
visited.add(pkgName);
|
|
125
|
+
sorted.push(pkg);
|
|
126
|
+
logger.debug(`Finished visiting ${pkgName}`);
|
|
144
127
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return "oldVersion" in pkg && !!pkg.oldVersion;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function getGitStatus(cwd) {
|
|
151
|
-
return execSync("git status --porcelain", {
|
|
152
|
-
cwd,
|
|
153
|
-
encoding: "utf8"
|
|
154
|
-
}).trim();
|
|
155
|
-
}
|
|
156
|
-
function checkGitStatusIfDirty() {
|
|
157
|
-
logger.debug("Checking git status");
|
|
158
|
-
const dirty = getGitStatus();
|
|
159
|
-
if (dirty) {
|
|
160
|
-
logger.debug("git status:", `
|
|
161
|
-
${dirty.trim().split("\n").map((line) => line.trim()).join("\n")}`);
|
|
162
|
-
const error = `Git status is dirty!
|
|
163
|
-
|
|
164
|
-
Please commit or stash your changes before bumping or use --no-clean flag.
|
|
165
|
-
|
|
166
|
-
Unstaged files:
|
|
167
|
-
|
|
168
|
-
${dirty.trim()}`;
|
|
169
|
-
throw new Error(error);
|
|
128
|
+
for (const pkg of packages) {
|
|
129
|
+
visit(pkg.name);
|
|
170
130
|
}
|
|
131
|
+
return sorted;
|
|
171
132
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
logger.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
133
|
+
|
|
134
|
+
function readPackageJson(packagePath) {
|
|
135
|
+
const packageJsonPath = join(packagePath, "package.json");
|
|
136
|
+
if (!existsSync(packageJsonPath)) {
|
|
137
|
+
logger.fail(`package.json not found at ${packageJsonPath}`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (!statSync(packagePath).isDirectory()) {
|
|
141
|
+
logger.fail(`Not a directory: ${packagePath}`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
145
|
+
if (!packageJson.name || !packageJson.version) {
|
|
146
|
+
throw new Error(`Invalid package.json at ${packagePath} - missing name or version`);
|
|
180
147
|
}
|
|
148
|
+
return {
|
|
149
|
+
name: packageJson.name,
|
|
150
|
+
version: packageJson.version,
|
|
151
|
+
private: packageJson.private || false,
|
|
152
|
+
path: packagePath
|
|
153
|
+
};
|
|
181
154
|
}
|
|
182
|
-
function
|
|
155
|
+
async function getRootPackage({
|
|
156
|
+
config,
|
|
157
|
+
force,
|
|
158
|
+
from,
|
|
159
|
+
to,
|
|
160
|
+
suffix,
|
|
161
|
+
changelog
|
|
162
|
+
}) {
|
|
183
163
|
try {
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}).trim();
|
|
188
|
-
if (remoteUrl.includes("github.com")) {
|
|
189
|
-
return "github";
|
|
164
|
+
const packageJson = readPackageJson(config.cwd);
|
|
165
|
+
if (!packageJson) {
|
|
166
|
+
throw new Error("Failed to read root package.json");
|
|
190
167
|
}
|
|
191
|
-
|
|
192
|
-
|
|
168
|
+
const commits = await getPackageCommits({
|
|
169
|
+
pkg: packageJson,
|
|
170
|
+
from,
|
|
171
|
+
to,
|
|
172
|
+
config,
|
|
173
|
+
changelog
|
|
174
|
+
});
|
|
175
|
+
let newVersion;
|
|
176
|
+
if (config.monorepo?.versionMode !== "independent") {
|
|
177
|
+
const releaseType = determineReleaseType({
|
|
178
|
+
currentVersion: packageJson.version,
|
|
179
|
+
commits,
|
|
180
|
+
releaseType: config.bump.type,
|
|
181
|
+
preid: config.bump.preid,
|
|
182
|
+
types: config.types,
|
|
183
|
+
force
|
|
184
|
+
});
|
|
185
|
+
if (!releaseType) {
|
|
186
|
+
logger.fail("No commits require a version bump");
|
|
187
|
+
process.exit(0);
|
|
188
|
+
}
|
|
189
|
+
newVersion = getPackageNewVersion({
|
|
190
|
+
currentVersion: packageJson.version,
|
|
191
|
+
releaseType,
|
|
192
|
+
preid: config.bump.preid,
|
|
193
|
+
suffix
|
|
194
|
+
});
|
|
193
195
|
}
|
|
194
|
-
return null;
|
|
195
|
-
} catch {
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
function parseGitRemoteUrl(remoteUrl) {
|
|
200
|
-
const sshRegex = /git@[\w.-]+:([\w.-]+)\/([\w.-]+?)(?:\.git)?$/;
|
|
201
|
-
const httpsRegex = /https?:\/\/[\w.-]+\/([\w.-]+)\/([\w.-]+?)(?:\.git)?$/;
|
|
202
|
-
const sshMatch = remoteUrl.match(sshRegex);
|
|
203
|
-
if (sshMatch) {
|
|
204
196
|
return {
|
|
205
|
-
|
|
206
|
-
|
|
197
|
+
...packageJson,
|
|
198
|
+
path: config.cwd,
|
|
199
|
+
fromTag: from,
|
|
200
|
+
commits,
|
|
201
|
+
newVersion
|
|
207
202
|
};
|
|
203
|
+
} catch (error) {
|
|
204
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
205
|
+
throw new Error(errorMessage);
|
|
208
206
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
207
|
+
}
|
|
208
|
+
function readPackages({
|
|
209
|
+
cwd,
|
|
210
|
+
patterns,
|
|
211
|
+
ignorePackageNames
|
|
212
|
+
}) {
|
|
213
|
+
const packages = [];
|
|
214
|
+
const foundPaths = /* @__PURE__ */ new Set();
|
|
215
|
+
const patternsSet = new Set(patterns);
|
|
216
|
+
if (!patterns)
|
|
217
|
+
patternsSet.add(".");
|
|
218
|
+
logger.debug(`Read package.json files from patterns: ${patternsSet.values()}`);
|
|
219
|
+
for (const pattern of patternsSet) {
|
|
220
|
+
try {
|
|
221
|
+
const matches = fastGlob.sync(pattern, {
|
|
222
|
+
cwd,
|
|
223
|
+
onlyDirectories: true,
|
|
224
|
+
absolute: true,
|
|
225
|
+
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"]
|
|
226
|
+
});
|
|
227
|
+
for (const matchPath of matches) {
|
|
228
|
+
if (foundPaths.has(matchPath))
|
|
229
|
+
continue;
|
|
230
|
+
const packageBase = readPackageJson(matchPath);
|
|
231
|
+
if (!packageBase || packageBase.private || ignorePackageNames?.includes(packageBase.name))
|
|
232
|
+
continue;
|
|
233
|
+
foundPaths.add(matchPath);
|
|
234
|
+
packages.push({
|
|
235
|
+
...packageBase,
|
|
236
|
+
path: matchPath
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
logger.error(error);
|
|
241
|
+
}
|
|
215
242
|
}
|
|
216
|
-
return
|
|
243
|
+
return packages;
|
|
217
244
|
}
|
|
218
|
-
|
|
245
|
+
function getPackageReleaseType({
|
|
246
|
+
pkg,
|
|
219
247
|
config,
|
|
220
|
-
|
|
221
|
-
bumpedPackages,
|
|
222
|
-
newVersion,
|
|
223
|
-
dryRun,
|
|
224
|
-
logLevel
|
|
248
|
+
force
|
|
225
249
|
}) {
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
250
|
+
const releaseType = config.bump.type;
|
|
251
|
+
if (force) {
|
|
252
|
+
return determineReleaseType({
|
|
253
|
+
currentVersion: pkg.version,
|
|
254
|
+
commits: pkg.commits,
|
|
255
|
+
releaseType,
|
|
256
|
+
preid: config.bump.preid,
|
|
257
|
+
types: config.types,
|
|
258
|
+
force
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
if (pkg.reason === "dependency") {
|
|
262
|
+
if (isStableReleaseType(releaseType))
|
|
263
|
+
return "patch";
|
|
264
|
+
if (isPrerelease(pkg.version))
|
|
265
|
+
return "prerelease";
|
|
266
|
+
return "prepatch";
|
|
267
|
+
}
|
|
268
|
+
return determineReleaseType({
|
|
269
|
+
currentVersion: pkg.version,
|
|
270
|
+
commits: pkg.commits,
|
|
271
|
+
releaseType,
|
|
272
|
+
preid: config.bump.preid,
|
|
273
|
+
types: config.types,
|
|
274
|
+
force
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
async function getPackages({
|
|
278
|
+
patterns,
|
|
279
|
+
config,
|
|
280
|
+
suffix,
|
|
281
|
+
force
|
|
282
|
+
}) {
|
|
283
|
+
const readedPackages = readPackages({
|
|
284
|
+
cwd: config.cwd,
|
|
285
|
+
patterns,
|
|
286
|
+
ignorePackageNames: config.monorepo?.ignorePackageNames
|
|
287
|
+
});
|
|
288
|
+
const packages = /* @__PURE__ */ new Map();
|
|
289
|
+
const foundPaths = /* @__PURE__ */ new Set();
|
|
290
|
+
const patternsSet = new Set(patterns);
|
|
291
|
+
if (!patterns)
|
|
292
|
+
patternsSet.add(".");
|
|
293
|
+
logger.debug(`Getting packages from patterns: ${patternsSet.values()}`);
|
|
294
|
+
for (const pattern of patternsSet) {
|
|
295
|
+
const matches = fastGlob.sync(pattern, {
|
|
296
|
+
cwd: config.cwd,
|
|
297
|
+
onlyDirectories: true,
|
|
298
|
+
absolute: true,
|
|
299
|
+
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"]
|
|
300
|
+
});
|
|
301
|
+
for (const matchPath of matches) {
|
|
302
|
+
if (foundPaths.has(matchPath))
|
|
303
|
+
continue;
|
|
304
|
+
const packageBase = readPackageJson(matchPath);
|
|
305
|
+
if (!packageBase) {
|
|
306
|
+
logger.debug(`Failed to read package.json at ${matchPath} - ignored`);
|
|
241
307
|
continue;
|
|
242
308
|
}
|
|
243
|
-
if (
|
|
244
|
-
logger.
|
|
309
|
+
if (packageBase.private) {
|
|
310
|
+
logger.debug(`${packageBase.name} is private and will be ignored`);
|
|
245
311
|
continue;
|
|
246
312
|
}
|
|
247
|
-
if (
|
|
248
|
-
logger.
|
|
313
|
+
if (config.monorepo?.ignorePackageNames?.includes(packageBase.name)) {
|
|
314
|
+
logger.debug(`${packageBase.name} ignored by config (monorepo.ignorePackageNames)`);
|
|
249
315
|
continue;
|
|
250
316
|
}
|
|
251
|
-
|
|
252
|
-
logger.
|
|
253
|
-
|
|
254
|
-
} catch {
|
|
317
|
+
if (!packageBase.version) {
|
|
318
|
+
logger.warn(`${packageBase.name} has no version and will be ignored`);
|
|
319
|
+
continue;
|
|
255
320
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
321
|
+
const { from, to } = await resolveTags({
|
|
322
|
+
config,
|
|
323
|
+
step: "bump",
|
|
324
|
+
pkg: packageBase,
|
|
325
|
+
newVersion: void 0
|
|
326
|
+
});
|
|
327
|
+
const commits = await getPackageCommits({
|
|
328
|
+
pkg: packageBase,
|
|
329
|
+
from,
|
|
330
|
+
to,
|
|
331
|
+
config,
|
|
332
|
+
changelog: false
|
|
333
|
+
});
|
|
334
|
+
foundPaths.add(matchPath);
|
|
335
|
+
const dependencies = getPackageDependencies({
|
|
336
|
+
packagePath: matchPath,
|
|
337
|
+
allPackageNames: new Set(readedPackages.map((p) => p.name)),
|
|
338
|
+
dependencyTypes: config.bump?.dependencyTypes
|
|
339
|
+
});
|
|
340
|
+
packages.set(packageBase.name, {
|
|
341
|
+
...packageBase,
|
|
342
|
+
path: matchPath,
|
|
343
|
+
fromTag: from,
|
|
344
|
+
dependencies,
|
|
345
|
+
commits,
|
|
346
|
+
reason: commits.length > 0 ? "commits" : void 0,
|
|
347
|
+
dependencyChain: void 0,
|
|
348
|
+
newVersion: void 0
|
|
275
349
|
});
|
|
276
|
-
logger.success(`Committed: ${commitMessage}${noVerify ? " (--no-verify)" : ""}`);
|
|
277
|
-
}
|
|
278
|
-
const signTags = internalConfig.signTags ? "-s" : "";
|
|
279
|
-
logger.debug(`Sign tags: ${internalConfig.signTags}`);
|
|
280
|
-
const createdTags = [];
|
|
281
|
-
if (internalConfig.monorepo?.versionMode === "independent" && bumpedPackages && bumpedPackages.length > 0 && internalConfig.release.gitTag) {
|
|
282
|
-
logger.debug(`Creating ${bumpedPackages.length} independent package tags`);
|
|
283
|
-
for (const pkg of bumpedPackages) {
|
|
284
|
-
if (!pkg.newVersion) {
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
const tagName = getIndependentTag({ version: pkg.newVersion, name: pkg.name });
|
|
288
|
-
const tagMessage = internalConfig.templates?.tagMessage?.replaceAll("{{newVersion}}", pkg.newVersion) || tagName;
|
|
289
|
-
if (dryRun) {
|
|
290
|
-
logger.info(`[dry-run] git tag ${signTags} -a ${tagName} -m "${tagMessage}"`);
|
|
291
|
-
} else {
|
|
292
|
-
const cmd = `git tag ${signTags} -a ${tagName} -m "${tagMessage}"`;
|
|
293
|
-
logger.debug(`Executing: ${cmd}`);
|
|
294
|
-
try {
|
|
295
|
-
await execPromise(cmd, {
|
|
296
|
-
logLevel,
|
|
297
|
-
noStderr: true,
|
|
298
|
-
noStdout: true,
|
|
299
|
-
cwd: internalConfig.cwd
|
|
300
|
-
});
|
|
301
|
-
logger.debug(`Tag created: ${tagName}`);
|
|
302
|
-
} catch (error) {
|
|
303
|
-
logger.error(`Failed to create tag ${tagName}:`, error);
|
|
304
|
-
throw error;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
createdTags.push(tagName);
|
|
308
|
-
}
|
|
309
|
-
logger.success(`Created ${createdTags.length} tags for independent packages, ${createdTags.join(", ")}`);
|
|
310
|
-
} else if (internalConfig.release.gitTag) {
|
|
311
|
-
const tagName = internalConfig.templates.tagBody?.replaceAll("{{newVersion}}", newVersion);
|
|
312
|
-
const tagMessage = internalConfig.templates?.tagMessage?.replaceAll("{{newVersion}}", newVersion) || tagName;
|
|
313
|
-
if (dryRun) {
|
|
314
|
-
logger.info(`[dry-run] git tag ${signTags} -a ${tagName} -m "${tagMessage}"`);
|
|
315
|
-
} else {
|
|
316
|
-
const cmd = `git tag ${signTags} -a ${tagName} -m "${tagMessage}"`;
|
|
317
|
-
logger.debug(`Executing: ${cmd}`);
|
|
318
|
-
try {
|
|
319
|
-
await execPromise(cmd, {
|
|
320
|
-
logLevel,
|
|
321
|
-
noStderr: true,
|
|
322
|
-
noStdout: true,
|
|
323
|
-
cwd: internalConfig.cwd
|
|
324
|
-
});
|
|
325
|
-
logger.debug(`Tag created: ${tagName}`);
|
|
326
|
-
} catch (error) {
|
|
327
|
-
logger.error(`Failed to create tag ${tagName}:`, error);
|
|
328
|
-
throw error;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
createdTags.push(tagName);
|
|
332
350
|
}
|
|
333
|
-
logger.debug("Created Tags:", createdTags.join(", "));
|
|
334
|
-
logger.success("Commit and tag completed!");
|
|
335
|
-
await executeHook("success:commit-and-tag", internalConfig, dryRun ?? false);
|
|
336
|
-
return createdTags;
|
|
337
|
-
} catch (error) {
|
|
338
|
-
logger.error("Error committing and tagging:", error);
|
|
339
|
-
await executeHook("error:commit-and-tag", internalConfig, dryRun ?? false);
|
|
340
|
-
throw error;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
async function pushCommitAndTags({ config, dryRun, logLevel, cwd }) {
|
|
344
|
-
logger.start("Start push changes and tags");
|
|
345
|
-
const command = config.release.gitTag ? "git push --follow-tags" : "git push";
|
|
346
|
-
if (dryRun) {
|
|
347
|
-
logger.info(`[dry-run] ${command}`);
|
|
348
|
-
} else {
|
|
349
|
-
logger.debug(`Executing: ${command}`);
|
|
350
|
-
await execPromise(command, { noStderr: true, noStdout: true, logLevel, cwd });
|
|
351
351
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
{
|
|
358
|
-
cwd,
|
|
359
|
-
encoding: "utf8"
|
|
360
|
-
}
|
|
361
|
-
);
|
|
362
|
-
return result.trim();
|
|
363
|
-
}
|
|
364
|
-
function getCurrentGitBranch(cwd) {
|
|
365
|
-
const result = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
366
|
-
cwd,
|
|
367
|
-
encoding: "utf8"
|
|
352
|
+
const packagesArray = Array.from(packages.values());
|
|
353
|
+
const packagesWithCommits = packagesArray.filter((p) => p.commits.length > 0);
|
|
354
|
+
const expandedPackages = expandPackagesToBumpWithDependents({
|
|
355
|
+
allPackages: packagesArray,
|
|
356
|
+
packagesWithCommits
|
|
368
357
|
});
|
|
369
|
-
|
|
358
|
+
for (const pkg of expandedPackages) {
|
|
359
|
+
packages.set(pkg.name, pkg);
|
|
360
|
+
}
|
|
361
|
+
for (const pkg of Array.from(packages.values())) {
|
|
362
|
+
const releaseType = getPackageReleaseType({
|
|
363
|
+
pkg,
|
|
364
|
+
config,
|
|
365
|
+
force
|
|
366
|
+
});
|
|
367
|
+
const newVersion = releaseType ? getPackageNewVersion({
|
|
368
|
+
currentVersion: pkg.version,
|
|
369
|
+
releaseType,
|
|
370
|
+
preid: config.bump.preid,
|
|
371
|
+
suffix
|
|
372
|
+
}) : void 0;
|
|
373
|
+
const graduating = releaseType && isGraduating(pkg.version, releaseType) || isChangedPreid(pkg.version, config.bump.preid);
|
|
374
|
+
packages.set(pkg.name, {
|
|
375
|
+
...pkg,
|
|
376
|
+
newVersion,
|
|
377
|
+
reason: pkg.reason || releaseType && graduating && "graduation" || void 0
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
const packagesToBump = Array.from(packages.values()).filter((p) => p.reason || force);
|
|
381
|
+
if (packagesToBump.length === 0) {
|
|
382
|
+
logger.debug("No packages to bump");
|
|
383
|
+
return [];
|
|
384
|
+
}
|
|
385
|
+
return packagesToBump;
|
|
370
386
|
}
|
|
371
|
-
function
|
|
372
|
-
|
|
373
|
-
|
|
387
|
+
function isAllowedCommit({
|
|
388
|
+
commit,
|
|
389
|
+
type,
|
|
390
|
+
changelog
|
|
391
|
+
}) {
|
|
392
|
+
if (commit.type === "chore" && ["deps", "release"].includes(commit.scope) && !commit.isBreaking) {
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
if (typeof type === "object") {
|
|
396
|
+
return !!type.semver || changelog && !!type.title;
|
|
397
|
+
}
|
|
398
|
+
if (typeof type === "boolean") {
|
|
399
|
+
return type;
|
|
400
|
+
}
|
|
401
|
+
return false;
|
|
374
402
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
commits,
|
|
378
|
-
config,
|
|
403
|
+
async function getPackageCommits({
|
|
404
|
+
pkg,
|
|
379
405
|
from,
|
|
380
406
|
to,
|
|
381
|
-
|
|
407
|
+
config,
|
|
408
|
+
changelog
|
|
382
409
|
}) {
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
const breakingChanges = [];
|
|
386
|
-
const updatedConfig = {
|
|
410
|
+
logger.debug(`Analyzing commits for ${pkg.name} since ${from} to ${to}`);
|
|
411
|
+
const changelogConfig = {
|
|
387
412
|
...config,
|
|
388
413
|
from,
|
|
389
414
|
to
|
|
390
415
|
};
|
|
391
|
-
const
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
});
|
|
399
|
-
markdown.push(formattedCompareLink);
|
|
416
|
+
const rawCommits = await getGitDiff(from, to, changelogConfig.cwd);
|
|
417
|
+
const allCommits = parseCommits(rawCommits, changelogConfig);
|
|
418
|
+
const hasBreakingChanges = allCommits.some((commit) => commit.isBreaking);
|
|
419
|
+
logger.debug(`Has breaking changes: ${hasBreakingChanges}`);
|
|
420
|
+
const rootPackage = readPackageJson(changelogConfig.cwd);
|
|
421
|
+
if (!rootPackage) {
|
|
422
|
+
throw new Error("Failed to read root package.json");
|
|
400
423
|
}
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
if (!
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
if (typeof updatedConfig.types[type] === "boolean") {
|
|
407
|
-
continue;
|
|
424
|
+
const commits = allCommits.filter((commit) => {
|
|
425
|
+
const type = changelogConfig?.types[commit.type];
|
|
426
|
+
if (!isAllowedCommit({ commit, type, changelog })) {
|
|
427
|
+
return false;
|
|
408
428
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const line = formatCommit(commit, updatedConfig);
|
|
412
|
-
markdown.push(line);
|
|
413
|
-
if (commit.isBreaking) {
|
|
414
|
-
breakingChanges.push(line);
|
|
415
|
-
}
|
|
429
|
+
if (pkg.path === changelogConfig.cwd || pkg.name === rootPackage.name) {
|
|
430
|
+
return true;
|
|
416
431
|
}
|
|
432
|
+
const packageRelativePath = relative(changelogConfig.cwd, pkg.path);
|
|
433
|
+
const scopeMatches = commit.scope === pkg.name;
|
|
434
|
+
const bodyContainsPath = commit.body.includes(packageRelativePath);
|
|
435
|
+
return scopeMatches || bodyContainsPath;
|
|
436
|
+
});
|
|
437
|
+
logger.debug(`Found ${commits.length} commit(s) for ${pkg.name} from ${from} to ${to}`);
|
|
438
|
+
if (commits.length > 0) {
|
|
439
|
+
logger.debug(`${pkg.name}: ${commits.length} commit(s) found`);
|
|
440
|
+
} else {
|
|
441
|
+
logger.debug(`${pkg.name}: No commits found`);
|
|
417
442
|
}
|
|
418
|
-
|
|
419
|
-
|
|
443
|
+
return commits;
|
|
444
|
+
}
|
|
445
|
+
function hasLernaJson(rootDir) {
|
|
446
|
+
const lernaJsonPath = join(rootDir, "lerna.json");
|
|
447
|
+
return existsSync(lernaJsonPath);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
async function executeHook(hook, config, dryRun, params) {
|
|
451
|
+
const hookInput = config.hooks?.[hook];
|
|
452
|
+
if (!hookInput) {
|
|
453
|
+
logger.debug(`Hook ${hook} not found`);
|
|
454
|
+
return;
|
|
420
455
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
continue;
|
|
429
|
-
}
|
|
430
|
-
if (updatedConfig.excludeAuthors && updatedConfig.excludeAuthors.some(
|
|
431
|
-
(v) => name.includes(v) || commit.author.email?.includes(v)
|
|
432
|
-
)) {
|
|
433
|
-
continue;
|
|
434
|
-
}
|
|
435
|
-
if (_authors.has(name)) {
|
|
436
|
-
const entry = _authors.get(name);
|
|
437
|
-
entry?.email.add(commit.author.email);
|
|
438
|
-
} else {
|
|
439
|
-
_authors.set(name, { email: /* @__PURE__ */ new Set([commit.author.email]), name });
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
if (updatedConfig.repo?.provider === "github") {
|
|
443
|
-
await Promise.all(
|
|
444
|
-
[..._authors.keys()].map(async (authorName) => {
|
|
445
|
-
const meta = _authors.get(authorName);
|
|
446
|
-
if (!meta) {
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
for (const data of [...meta.email, meta.name]) {
|
|
450
|
-
const { user } = await fetch$1(`https://ungh.cc/users/find/${data}`).then((r) => r.json()).catch(() => ({ user: null }));
|
|
451
|
-
if (user) {
|
|
452
|
-
meta.github = user.username;
|
|
453
|
-
break;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
})
|
|
457
|
-
);
|
|
456
|
+
if (typeof hookInput === "function") {
|
|
457
|
+
logger.info(`Executing hook ${hook}`);
|
|
458
|
+
const result = await hookInput(config, dryRun, params);
|
|
459
|
+
if (result)
|
|
460
|
+
logger.debug(`Hook ${hook} returned: ${result}`);
|
|
461
|
+
logger.info(`Hook ${hook} executed`);
|
|
462
|
+
return result;
|
|
458
463
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
);
|
|
472
|
-
const email = updatedConfig.hideAuthorEmail !== true && _email ? ` <${_email}>` : "";
|
|
473
|
-
const github = i.github ? ` ([@${i.github}](https://github.com/${i.github}))` : "";
|
|
474
|
-
return `- ${i.name}${github || email || ""}`;
|
|
475
|
-
})
|
|
476
|
-
);
|
|
464
|
+
if (typeof hookInput === "string") {
|
|
465
|
+
logger.info(`Executing hook ${hook}`);
|
|
466
|
+
const result = await execPromise(hookInput, {
|
|
467
|
+
logLevel: config.logLevel,
|
|
468
|
+
cwd: config.cwd,
|
|
469
|
+
noStderr: true,
|
|
470
|
+
noStdout: true
|
|
471
|
+
});
|
|
472
|
+
if (result)
|
|
473
|
+
logger.debug(`Hook ${hook} returned: ${result}`);
|
|
474
|
+
logger.info(`Hook ${hook} executed`);
|
|
475
|
+
return result;
|
|
477
476
|
}
|
|
478
|
-
const result = convert(markdown.join("\n").trim(), true);
|
|
479
|
-
return result;
|
|
480
477
|
}
|
|
481
|
-
function
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
const lines = commit.body.split("\n");
|
|
486
|
-
const contentLines = lines.filter((line) => {
|
|
487
|
-
const trimmedLine = line.trim();
|
|
488
|
-
if (!trimmedLine) {
|
|
489
|
-
return false;
|
|
490
|
-
}
|
|
491
|
-
const isFileLine = /^[AMDTUXB](?:\d{3})?\s+/.test(trimmedLine) || /^[RCM]\d{3}\s+/.test(trimmedLine);
|
|
492
|
-
return !isFileLine;
|
|
493
|
-
});
|
|
494
|
-
if (contentLines.length === 0) {
|
|
495
|
-
return "";
|
|
496
|
-
}
|
|
497
|
-
const indentedBody = contentLines.map((line) => ` ${line}`).join("\n");
|
|
498
|
-
return `
|
|
499
|
-
|
|
500
|
-
${indentedBody}
|
|
501
|
-
`;
|
|
478
|
+
function isInCI() {
|
|
479
|
+
return Boolean(
|
|
480
|
+
process.env.CI === "true" || process.env.CONTINUOUS_INTEGRATION === "true" || process.env.GITHUB_ACTIONS === "true" || process.env.GITHUB_WORKFLOW || process.env.GITLAB_CI === "true" || process.env.CIRCLECI === "true" || process.env.TRAVIS === "true" || process.env.JENKINS_HOME || process.env.JENKINS_URL || process.env.BUILD_ID || process.env.TF_BUILD === "True" || process.env.AZURE_PIPELINES === "true" || process.env.TEAMCITY_VERSION || process.env.BITBUCKET_BUILD_NUMBER || process.env.DRONE === "true" || process.env.APPVEYOR === "True" || process.env.APPVEYOR === "true" || process.env.BUILDKITE === "true" || process.env.CODEBUILD_BUILD_ID || process.env.NETLIFY === "true" || process.env.VERCEL === "1" || process.env.HEROKU_TEST_RUN_ID || process.env.BUDDY === "true" || process.env.SEMAPHORE === "true" || process.env.CF_BUILD_ID || process.env.bamboo_buildKey || process.env.BUILD_ID && process.env.PROJECT_ID || process.env.SCREWDRIVER === "true" || process.env.STRIDER === "true"
|
|
481
|
+
);
|
|
502
482
|
}
|
|
503
|
-
function
|
|
504
|
-
|
|
505
|
-
|
|
483
|
+
function getCIName() {
|
|
484
|
+
if (process.env.GITHUB_ACTIONS === "true")
|
|
485
|
+
return "GitHub Actions";
|
|
486
|
+
if (process.env.GITLAB_CI === "true")
|
|
487
|
+
return "GitLab CI";
|
|
488
|
+
if (process.env.CIRCLECI === "true")
|
|
489
|
+
return "CircleCI";
|
|
490
|
+
if (process.env.TRAVIS === "true")
|
|
491
|
+
return "Travis CI";
|
|
492
|
+
if (process.env.JENKINS_HOME || process.env.JENKINS_URL)
|
|
493
|
+
return "Jenkins";
|
|
494
|
+
if (process.env.TF_BUILD === "True")
|
|
495
|
+
return "Azure Pipelines";
|
|
496
|
+
if (process.env.TEAMCITY_VERSION)
|
|
497
|
+
return "TeamCity";
|
|
498
|
+
if (process.env.BITBUCKET_BUILD_NUMBER)
|
|
499
|
+
return "Bitbucket Pipelines";
|
|
500
|
+
if (process.env.DRONE === "true")
|
|
501
|
+
return "Drone";
|
|
502
|
+
if (process.env.APPVEYOR)
|
|
503
|
+
return "AppVeyor";
|
|
504
|
+
if (process.env.BUILDKITE === "true")
|
|
505
|
+
return "Buildkite";
|
|
506
|
+
if (process.env.CODEBUILD_BUILD_ID)
|
|
507
|
+
return "AWS CodeBuild";
|
|
508
|
+
if (process.env.NETLIFY === "true")
|
|
509
|
+
return "Netlify";
|
|
510
|
+
if (process.env.VERCEL === "1")
|
|
511
|
+
return "Vercel";
|
|
512
|
+
if (process.env.HEROKU_TEST_RUN_ID)
|
|
513
|
+
return "Heroku CI";
|
|
514
|
+
if (process.env.BUDDY === "true")
|
|
515
|
+
return "Buddy";
|
|
516
|
+
if (process.env.SEMAPHORE === "true")
|
|
517
|
+
return "Semaphore";
|
|
518
|
+
if (process.env.CF_BUILD_ID)
|
|
519
|
+
return "Codefresh";
|
|
520
|
+
if (process.env.bamboo_buildKey)
|
|
521
|
+
return "Bamboo";
|
|
522
|
+
if (process.env.BUILD_ID && process.env.PROJECT_ID)
|
|
523
|
+
return "Google Cloud Build";
|
|
524
|
+
if (process.env.SCREWDRIVER === "true")
|
|
525
|
+
return "Screwdriver";
|
|
526
|
+
if (process.env.STRIDER === "true")
|
|
527
|
+
return "Strider";
|
|
528
|
+
if (process.env.CI === "true")
|
|
529
|
+
return "Unknown CI";
|
|
530
|
+
return null;
|
|
506
531
|
}
|
|
507
|
-
function
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
532
|
+
async function executeFormatCmd({
|
|
533
|
+
config,
|
|
534
|
+
dryRun
|
|
535
|
+
}) {
|
|
536
|
+
if (config.changelog?.formatCmd) {
|
|
537
|
+
logger.info("Running format command");
|
|
538
|
+
logger.debug(`Running format command: ${config.changelog.formatCmd}`);
|
|
539
|
+
try {
|
|
540
|
+
if (!dryRun) {
|
|
541
|
+
await execPromise(config.changelog.formatCmd, {
|
|
542
|
+
noStderr: true,
|
|
543
|
+
noStdout: true,
|
|
544
|
+
logLevel: config.logLevel,
|
|
545
|
+
cwd: config.cwd
|
|
546
|
+
});
|
|
547
|
+
logger.info("Format completed");
|
|
548
|
+
} else {
|
|
549
|
+
logger.log("[dry-run] exec format command: ", config.changelog.formatCmd);
|
|
550
|
+
}
|
|
551
|
+
} catch (error) {
|
|
552
|
+
throw new Error(`Format command failed: ${error}`);
|
|
553
|
+
}
|
|
554
|
+
} else {
|
|
555
|
+
logger.debug("No format command specified");
|
|
515
556
|
}
|
|
516
|
-
return "";
|
|
517
|
-
}
|
|
518
|
-
function formatName(name = "") {
|
|
519
|
-
return name.split(" ").map((p) => upperFirst(p.trim())).join(" ");
|
|
520
557
|
}
|
|
521
|
-
function
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
558
|
+
async function executeBuildCmd({
|
|
559
|
+
config,
|
|
560
|
+
dryRun
|
|
561
|
+
}) {
|
|
562
|
+
if (config.publish?.buildCmd) {
|
|
563
|
+
logger.info("Running build command");
|
|
564
|
+
logger.debug(`Running build command: ${config.publish.buildCmd}`);
|
|
565
|
+
if (!dryRun) {
|
|
566
|
+
await execPromise(config.publish.buildCmd, {
|
|
567
|
+
noStderr: true,
|
|
568
|
+
noStdout: true,
|
|
569
|
+
logLevel: config.logLevel,
|
|
570
|
+
cwd: config.cwd
|
|
571
|
+
});
|
|
572
|
+
logger.info("Build completed");
|
|
573
|
+
} else {
|
|
574
|
+
logger.log("[dry-run] exec build command: ", config.publish.buildCmd);
|
|
575
|
+
}
|
|
576
|
+
} else {
|
|
577
|
+
logger.debug("No build command specified");
|
|
526
578
|
}
|
|
527
|
-
return groups;
|
|
528
579
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
return fromTag === getFirstCommit(cwd);
|
|
580
|
+
function isBumpedPackage(pkg) {
|
|
581
|
+
return "oldVersion" in pkg && !!pkg.oldVersion;
|
|
532
582
|
}
|
|
533
|
-
async function
|
|
534
|
-
pkg,
|
|
583
|
+
async function getPackagesOrBumpedPackages({
|
|
535
584
|
config,
|
|
536
|
-
|
|
537
|
-
|
|
585
|
+
bumpResult,
|
|
586
|
+
suffix,
|
|
587
|
+
force
|
|
538
588
|
}) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
if (isFirstCommit) {
|
|
542
|
-
fromTag = config.monorepo?.versionMode === "independent" ? getIndependentTag({ version: "0.0.0", name: pkg.name }) : config.templates.tagBody.replace("{{newVersion}}", "0.0.0");
|
|
543
|
-
}
|
|
544
|
-
newVersion = newVersion || pkg.newVersion;
|
|
545
|
-
let toTag = config.to;
|
|
546
|
-
if (!toTag && newVersion) {
|
|
547
|
-
toTag = config.monorepo?.versionMode === "independent" ? getIndependentTag({ version: newVersion, name: pkg.name }) : config.templates.tagBody.replace("{{newVersion}}", newVersion);
|
|
589
|
+
if (bumpResult?.bumpedPackages && bumpResult.bumpedPackages.length > 0) {
|
|
590
|
+
return bumpResult.bumpedPackages;
|
|
548
591
|
}
|
|
549
|
-
|
|
550
|
-
|
|
592
|
+
return await getPackages({
|
|
593
|
+
config,
|
|
594
|
+
patterns: config.monorepo?.packages,
|
|
595
|
+
suffix,
|
|
596
|
+
force
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function getGitStatus(cwd) {
|
|
601
|
+
return execSync("git status --porcelain", {
|
|
602
|
+
cwd,
|
|
603
|
+
encoding: "utf8"
|
|
604
|
+
}).trim();
|
|
605
|
+
}
|
|
606
|
+
function checkGitStatusIfDirty() {
|
|
607
|
+
logger.debug("Checking git status");
|
|
608
|
+
const dirty = getGitStatus();
|
|
609
|
+
if (dirty) {
|
|
610
|
+
logger.debug("git status:", `
|
|
611
|
+
${dirty.trim().split("\n").map((line) => line.trim()).join("\n")}`);
|
|
612
|
+
const error = `Git status is dirty!
|
|
613
|
+
|
|
614
|
+
Please commit or stash your changes before bumping or use --no-clean flag.
|
|
615
|
+
|
|
616
|
+
Unstaged files:
|
|
617
|
+
|
|
618
|
+
${dirty.trim()}`;
|
|
619
|
+
throw new Error(error);
|
|
551
620
|
}
|
|
552
|
-
|
|
621
|
+
}
|
|
622
|
+
async function fetchGitTags(cwd) {
|
|
623
|
+
logger.debug("Fetching git tags from remote");
|
|
553
624
|
try {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
from: fromTag,
|
|
557
|
-
to: toTag
|
|
558
|
-
};
|
|
559
|
-
const generatedChangelog = await generateMarkDown({
|
|
560
|
-
commits: pkg.commits,
|
|
561
|
-
config,
|
|
562
|
-
from: fromTag,
|
|
563
|
-
isFirstCommit,
|
|
564
|
-
to: toTag
|
|
565
|
-
});
|
|
566
|
-
let changelog = generatedChangelog;
|
|
567
|
-
if (pkg.commits.length === 0) {
|
|
568
|
-
changelog = `${changelog}
|
|
569
|
-
|
|
570
|
-
${config.templates.emptyChangelogContent}`;
|
|
571
|
-
}
|
|
572
|
-
const changelogResult = await executeHook("generate:changelog", config, dryRun, {
|
|
573
|
-
commits: pkg.commits,
|
|
574
|
-
changelog
|
|
575
|
-
});
|
|
576
|
-
changelog = changelogResult || changelog;
|
|
577
|
-
logger.verbose(`Output changelog for ${pkg.name}:
|
|
578
|
-
${changelog}`);
|
|
579
|
-
logger.debug(`Changelog generated for ${pkg.name} (${pkg.commits.length} commits)`);
|
|
580
|
-
logger.verbose(`Final changelog for ${pkg.name}:
|
|
581
|
-
|
|
582
|
-
${changelog}
|
|
583
|
-
|
|
584
|
-
`);
|
|
585
|
-
if (dryRun) {
|
|
586
|
-
logger.info(`[dry-run] ${pkg.name} - Generate changelog ${fromTag}...${toTag}`);
|
|
587
|
-
}
|
|
588
|
-
return changelog;
|
|
589
|
-
} catch (error) {
|
|
590
|
-
throw new Error(`Error generating changelog for ${pkg.name} (${fromTag}...${toTag}): ${error}`);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
function writeChangelogToFile({
|
|
594
|
-
cwd,
|
|
595
|
-
pkg,
|
|
596
|
-
changelog,
|
|
597
|
-
dryRun = false
|
|
598
|
-
}) {
|
|
599
|
-
const changelogPath = join(pkg.path, "CHANGELOG.md");
|
|
600
|
-
let existingChangelog = "";
|
|
601
|
-
if (existsSync(changelogPath)) {
|
|
602
|
-
existingChangelog = readFileSync(changelogPath, "utf8");
|
|
603
|
-
}
|
|
604
|
-
const lines = existingChangelog.split("\n");
|
|
605
|
-
const titleIndex = lines.findIndex((line) => line.startsWith("# "));
|
|
606
|
-
let updatedChangelog;
|
|
607
|
-
if (titleIndex !== -1) {
|
|
608
|
-
const beforeTitle = lines.slice(0, titleIndex + 1);
|
|
609
|
-
const afterTitle = lines.slice(titleIndex + 1);
|
|
610
|
-
updatedChangelog = [...beforeTitle, "", changelog, "", ...afterTitle].join("\n");
|
|
611
|
-
} else {
|
|
612
|
-
const title = "# Changelog\n";
|
|
613
|
-
updatedChangelog = `${title}
|
|
614
|
-
${changelog}
|
|
615
|
-
${existingChangelog}`;
|
|
616
|
-
}
|
|
617
|
-
if (dryRun) {
|
|
618
|
-
const relativeChangelogPath = relative(cwd, changelogPath);
|
|
619
|
-
logger.info(`[dry-run] ${pkg.name} - Write changelog to ${relativeChangelogPath}`);
|
|
620
|
-
} else {
|
|
621
|
-
logger.debug(`Writing changelog to ${changelogPath}`);
|
|
622
|
-
writeFileSync(changelogPath, updatedChangelog, "utf8");
|
|
623
|
-
logger.info(`Changelog updated for ${pkg.name} (${"newVersion" in pkg && pkg.newVersion || pkg.version})`);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
function getDefaultConfig() {
|
|
628
|
-
return {
|
|
629
|
-
cwd: process$1.cwd(),
|
|
630
|
-
types: {
|
|
631
|
-
feat: { title: "\u{1F680} Enhancements", semver: "minor" },
|
|
632
|
-
perf: { title: "\u{1F525} Performance", semver: "patch" },
|
|
633
|
-
fix: { title: "\u{1FA79} Fixes", semver: "patch" },
|
|
634
|
-
refactor: { title: "\u{1F485} Refactors", semver: "patch" },
|
|
635
|
-
docs: { title: "\u{1F4D6} Documentation", semver: "patch" },
|
|
636
|
-
build: { title: "\u{1F4E6} Build", semver: "patch" },
|
|
637
|
-
types: { title: "\u{1F30A} Types", semver: "patch" },
|
|
638
|
-
chore: { title: "\u{1F3E1} Chore" },
|
|
639
|
-
examples: { title: "\u{1F3C0} Examples" },
|
|
640
|
-
test: { title: "\u2705 Tests" },
|
|
641
|
-
style: { title: "\u{1F3A8} Styles" },
|
|
642
|
-
ci: { title: "\u{1F916} CI" }
|
|
643
|
-
},
|
|
644
|
-
templates: {
|
|
645
|
-
commitMessage: "chore(release): bump version to {{newVersion}}",
|
|
646
|
-
tagMessage: "Bump version to {{newVersion}}",
|
|
647
|
-
tagBody: "v{{newVersion}}",
|
|
648
|
-
emptyChangelogContent: "No relevant changes for this release"
|
|
649
|
-
},
|
|
650
|
-
excludeAuthors: [],
|
|
651
|
-
noAuthors: false,
|
|
652
|
-
bump: {
|
|
653
|
-
type: "release",
|
|
654
|
-
clean: true,
|
|
655
|
-
dependencyTypes: ["dependencies"],
|
|
656
|
-
yes: false
|
|
657
|
-
},
|
|
658
|
-
changelog: {
|
|
659
|
-
rootChangelog: true,
|
|
660
|
-
includeCommitBody: true
|
|
661
|
-
},
|
|
662
|
-
publish: {
|
|
663
|
-
private: false,
|
|
664
|
-
args: []
|
|
665
|
-
},
|
|
666
|
-
tokens: {
|
|
667
|
-
gitlab: process$1.env.RELIZY_GITLAB_TOKEN || process$1.env.GITLAB_TOKEN || process$1.env.GITLAB_API_TOKEN || process$1.env.CI_JOB_TOKEN,
|
|
668
|
-
github: process$1.env.RELIZY_GITHUB_TOKEN || process$1.env.GITHUB_TOKEN || process$1.env.GH_TOKEN
|
|
669
|
-
},
|
|
670
|
-
scopeMap: {},
|
|
671
|
-
release: {
|
|
672
|
-
commit: true,
|
|
673
|
-
publish: true,
|
|
674
|
-
changelog: true,
|
|
675
|
-
push: true,
|
|
676
|
-
clean: true,
|
|
677
|
-
providerRelease: true,
|
|
678
|
-
noVerify: false
|
|
679
|
-
},
|
|
680
|
-
logLevel: "default",
|
|
681
|
-
safetyCheck: true
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
function setupLogger(logLevel) {
|
|
685
|
-
if (logLevel) {
|
|
686
|
-
logger.setLevel(logLevel);
|
|
687
|
-
logger.debug(`Log level set to: ${logLevel}`);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
async function resolveConfig(config, cwd) {
|
|
691
|
-
if (!config.repo) {
|
|
692
|
-
const resolvedRepoConfig = await resolveRepoConfig(cwd);
|
|
693
|
-
config.repo = {
|
|
694
|
-
...resolvedRepoConfig,
|
|
695
|
-
provider: resolvedRepoConfig.provider
|
|
696
|
-
};
|
|
697
|
-
}
|
|
698
|
-
if (typeof config.repo === "string") {
|
|
699
|
-
const resolvedRepoConfig = getRepoConfig(config.repo);
|
|
700
|
-
config.repo = {
|
|
701
|
-
...resolvedRepoConfig,
|
|
702
|
-
provider: resolvedRepoConfig.provider
|
|
703
|
-
};
|
|
704
|
-
}
|
|
705
|
-
return config;
|
|
706
|
-
}
|
|
707
|
-
async function loadRelizyConfig(options) {
|
|
708
|
-
const cwd = options?.overrides?.cwd ?? process$1.cwd();
|
|
709
|
-
const configName = options?.configName ?? "relizy";
|
|
710
|
-
await setupDotenv({ cwd });
|
|
711
|
-
const defaultConfig = getDefaultConfig();
|
|
712
|
-
const overridesConfig = defu(options?.overrides, options?.baseConfig);
|
|
713
|
-
const results = await loadConfig({
|
|
714
|
-
cwd,
|
|
715
|
-
name: configName,
|
|
716
|
-
packageJson: true,
|
|
717
|
-
defaults: defaultConfig,
|
|
718
|
-
overrides: overridesConfig
|
|
719
|
-
});
|
|
720
|
-
if (!results._configFile) {
|
|
721
|
-
logger.debug(`No config file found with name "${configName}" - using standalone mode`);
|
|
722
|
-
if (options?.configName) {
|
|
723
|
-
logger.error(`No config file found with name "${configName}"`);
|
|
724
|
-
process$1.exit(1);
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
setupLogger(options?.overrides?.logLevel || results.config.logLevel);
|
|
728
|
-
logger.verbose("User config:", formatJson(results.config.changelog));
|
|
729
|
-
const resolvedConfig = await resolveConfig(results.config, cwd);
|
|
730
|
-
logger.debug("Resolved config:", formatJson(resolvedConfig));
|
|
731
|
-
return resolvedConfig;
|
|
732
|
-
}
|
|
733
|
-
function defineConfig(config) {
|
|
734
|
-
return config;
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
function getPackageDependencies({
|
|
738
|
-
packagePath,
|
|
739
|
-
allPackageNames,
|
|
740
|
-
dependencyTypes
|
|
741
|
-
}) {
|
|
742
|
-
const packageJsonPath = join(packagePath, "package.json");
|
|
743
|
-
if (!existsSync(packageJsonPath)) {
|
|
744
|
-
return [];
|
|
745
|
-
}
|
|
746
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
747
|
-
const deps = [];
|
|
748
|
-
const allDeps = {
|
|
749
|
-
...dependencyTypes?.includes("dependencies") ? packageJson.dependencies : {},
|
|
750
|
-
...dependencyTypes?.includes("peerDependencies") ? packageJson.peerDependencies : {},
|
|
751
|
-
...dependencyTypes?.includes("devDependencies") ? packageJson.devDependencies : {}
|
|
752
|
-
};
|
|
753
|
-
for (const depName of Object.keys(allDeps)) {
|
|
754
|
-
if (allPackageNames.has(depName)) {
|
|
755
|
-
deps.push(depName);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
return deps;
|
|
759
|
-
}
|
|
760
|
-
function getDependentsOf({
|
|
761
|
-
allPackages,
|
|
762
|
-
packageName
|
|
763
|
-
}) {
|
|
764
|
-
return allPackages.filter(
|
|
765
|
-
(pkg) => pkg.dependencies.includes(packageName)
|
|
766
|
-
);
|
|
767
|
-
}
|
|
768
|
-
function expandPackagesToBumpWithDependents({
|
|
769
|
-
allPackages,
|
|
770
|
-
packagesWithCommits
|
|
771
|
-
}) {
|
|
772
|
-
const result = /* @__PURE__ */ new Map();
|
|
773
|
-
logger.debug(`Expanding packages to bump: ${packagesWithCommits.length} packages with commits, ${allPackages.length} total packages`);
|
|
774
|
-
for (const pkg of packagesWithCommits) {
|
|
775
|
-
const packageToBump = {
|
|
776
|
-
...pkg,
|
|
777
|
-
reason: "commits"
|
|
778
|
-
};
|
|
779
|
-
result.set(pkg.name, packageToBump);
|
|
780
|
-
}
|
|
781
|
-
const toProcess = [...packagesWithCommits.map((p) => p.name)];
|
|
782
|
-
const processed = /* @__PURE__ */ new Set();
|
|
783
|
-
while (toProcess.length > 0) {
|
|
784
|
-
const currentPkgName = toProcess.shift();
|
|
785
|
-
if (!currentPkgName || processed.has(currentPkgName)) {
|
|
786
|
-
continue;
|
|
787
|
-
}
|
|
788
|
-
processed.add(currentPkgName);
|
|
789
|
-
const dependents = getDependentsOf({
|
|
790
|
-
packageName: currentPkgName,
|
|
791
|
-
allPackages
|
|
792
|
-
});
|
|
793
|
-
for (const dependent of dependents) {
|
|
794
|
-
if (!result.has(dependent.name)) {
|
|
795
|
-
const currentChain = result.get(currentPkgName)?.dependencyChain || [];
|
|
796
|
-
const chain = [...currentChain, currentPkgName];
|
|
797
|
-
const packageBase = allPackages.find((p) => p.name === dependent.name);
|
|
798
|
-
if (packageBase) {
|
|
799
|
-
const packageToBump = {
|
|
800
|
-
...packageBase,
|
|
801
|
-
reason: "dependency",
|
|
802
|
-
dependencyChain: chain
|
|
803
|
-
};
|
|
804
|
-
result.set(dependent.name, packageToBump);
|
|
805
|
-
toProcess.push(dependent.name);
|
|
806
|
-
logger.debug(`${dependent.name} will be bumped (depends on ${chain.join(" \u2192 ")})`);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
return Array.from(result.values());
|
|
812
|
-
}
|
|
813
|
-
function topologicalSort(packages) {
|
|
814
|
-
const sorted = [];
|
|
815
|
-
const visited = /* @__PURE__ */ new Set();
|
|
816
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
817
|
-
const packageMap = /* @__PURE__ */ new Map();
|
|
818
|
-
for (const pkg of packages) {
|
|
819
|
-
packageMap.set(pkg.name, pkg);
|
|
820
|
-
}
|
|
821
|
-
function visit(pkgName, path = []) {
|
|
822
|
-
logger.debug(`Visiting ${pkgName}, path: ${path.join(" \u2192 ")}, visiting: ${Array.from(visiting).join(", ")}`);
|
|
823
|
-
if (visiting.has(pkgName)) {
|
|
824
|
-
const cycle = [...path, pkgName];
|
|
825
|
-
logger.warn(`Circular dependency detected: ${cycle.join(" \u2192 ")}`);
|
|
826
|
-
return;
|
|
827
|
-
}
|
|
828
|
-
if (visited.has(pkgName)) {
|
|
829
|
-
logger.debug(`${pkgName} already visited globally, skipping`);
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
visiting.add(pkgName);
|
|
833
|
-
logger.debug(`Added ${pkgName} to visiting set`);
|
|
834
|
-
const pkg = packageMap.get(pkgName);
|
|
835
|
-
if (!pkg) {
|
|
836
|
-
logger.debug(`Package ${pkgName} not found in packageMap`);
|
|
837
|
-
visiting.delete(pkgName);
|
|
838
|
-
return;
|
|
839
|
-
}
|
|
840
|
-
logger.debug(`${pkgName} has dependencies: ${pkg.dependencies.join(", ")}`);
|
|
841
|
-
for (const depName of pkg.dependencies) {
|
|
842
|
-
visit(depName, [...path, pkgName]);
|
|
843
|
-
}
|
|
844
|
-
visiting.delete(pkgName);
|
|
845
|
-
visited.add(pkgName);
|
|
846
|
-
sorted.push(pkg);
|
|
847
|
-
logger.debug(`Finished visiting ${pkgName}`);
|
|
848
|
-
}
|
|
849
|
-
for (const pkg of packages) {
|
|
850
|
-
visit(pkg.name);
|
|
851
|
-
}
|
|
852
|
-
return sorted;
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
async function githubIndependentMode({
|
|
856
|
-
config,
|
|
857
|
-
dryRun,
|
|
858
|
-
bumpResult,
|
|
859
|
-
force,
|
|
860
|
-
suffix
|
|
861
|
-
}) {
|
|
862
|
-
const repoConfig = config.repo;
|
|
863
|
-
if (!repoConfig) {
|
|
864
|
-
throw new Error("No repository configuration found. Please check your changelog config.");
|
|
865
|
-
}
|
|
866
|
-
logger.debug(`GitHub token: ${config.tokens.github || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
|
|
867
|
-
if (!config.tokens.github && !config.repo?.token) {
|
|
868
|
-
throw new Error("No GitHub token specified. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
|
|
869
|
-
}
|
|
870
|
-
const packages = bumpResult?.bumped && bumpResult?.bumpedPackages || await getPackages({
|
|
871
|
-
suffix,
|
|
872
|
-
patterns: config.monorepo?.packages,
|
|
873
|
-
config,
|
|
874
|
-
force
|
|
875
|
-
});
|
|
876
|
-
logger.info(`Creating ${packages.length} GitHub release(s)`);
|
|
877
|
-
const postedReleases = [];
|
|
878
|
-
for (const pkg of packages) {
|
|
879
|
-
const newVersion = isBumpedPackage(pkg) && pkg.newVersion || pkg.version;
|
|
880
|
-
const from = config.from || pkg.fromTag;
|
|
881
|
-
const to = config.to || getIndependentTag({ version: newVersion, name: pkg.name });
|
|
882
|
-
if (!from) {
|
|
883
|
-
logger.warn(`No from tag found for ${pkg.name}, skipping release`);
|
|
884
|
-
continue;
|
|
885
|
-
}
|
|
886
|
-
const toTag = dryRun ? "HEAD" : to;
|
|
887
|
-
logger.debug(`Processing ${pkg.name}: ${from} \u2192 ${toTag}`);
|
|
888
|
-
const changelog = await generateChangelog({
|
|
889
|
-
pkg,
|
|
890
|
-
config,
|
|
891
|
-
dryRun,
|
|
892
|
-
newVersion
|
|
893
|
-
});
|
|
894
|
-
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
895
|
-
const release = {
|
|
896
|
-
tag_name: to,
|
|
897
|
-
name: to,
|
|
898
|
-
body: releaseBody,
|
|
899
|
-
prerelease: isPrerelease(newVersion)
|
|
900
|
-
};
|
|
901
|
-
logger.debug(`Creating release for ${to}${release.prerelease ? " (prerelease)" : ""}`);
|
|
902
|
-
if (dryRun) {
|
|
903
|
-
logger.info(`[dry-run] Publish GitHub release for ${to}`);
|
|
904
|
-
postedReleases.push({
|
|
905
|
-
name: pkg.name,
|
|
906
|
-
tag: release.tag_name,
|
|
907
|
-
version: newVersion,
|
|
908
|
-
prerelease: release.prerelease
|
|
909
|
-
});
|
|
910
|
-
} else {
|
|
911
|
-
logger.debug(`Publishing release ${to} to GitHub...`);
|
|
912
|
-
await createGithubRelease({
|
|
913
|
-
...config,
|
|
914
|
-
from,
|
|
915
|
-
to,
|
|
916
|
-
repo: repoConfig
|
|
917
|
-
}, release);
|
|
918
|
-
postedReleases.push({
|
|
919
|
-
name: pkg.name,
|
|
920
|
-
tag: release.tag_name,
|
|
921
|
-
version: newVersion,
|
|
922
|
-
prerelease: release.prerelease
|
|
923
|
-
});
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
if (postedReleases.length === 0) {
|
|
927
|
-
logger.warn("No releases created");
|
|
928
|
-
} else {
|
|
929
|
-
logger.success(`Releases ${postedReleases.map((r) => r.tag).join(", ")} published to GitHub!`);
|
|
930
|
-
}
|
|
931
|
-
return postedReleases;
|
|
932
|
-
}
|
|
933
|
-
async function githubUnified({
|
|
934
|
-
config,
|
|
935
|
-
dryRun,
|
|
936
|
-
rootPackage,
|
|
937
|
-
bumpResult
|
|
938
|
-
}) {
|
|
939
|
-
const repoConfig = config.repo;
|
|
940
|
-
if (!repoConfig) {
|
|
941
|
-
throw new Error("No repository configuration found. Please check your changelog config.");
|
|
942
|
-
}
|
|
943
|
-
logger.debug(`GitHub token: ${config.tokens.github || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
|
|
944
|
-
if (!config.tokens.github && !config.repo?.token) {
|
|
945
|
-
throw new Error("No GitHub token specified. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
|
|
946
|
-
}
|
|
947
|
-
const to = config.to || config.templates.tagBody.replace("{{newVersion}}", bumpResult?.bumped && bumpResult.newVersion || rootPackage.version);
|
|
948
|
-
const changelog = await generateChangelog({
|
|
949
|
-
pkg: rootPackage,
|
|
950
|
-
config,
|
|
951
|
-
dryRun,
|
|
952
|
-
newVersion: bumpResult?.newVersion || rootPackage.version
|
|
953
|
-
});
|
|
954
|
-
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
955
|
-
const release = {
|
|
956
|
-
tag_name: to,
|
|
957
|
-
name: to,
|
|
958
|
-
body: releaseBody,
|
|
959
|
-
prerelease: isPrerelease(to)
|
|
960
|
-
};
|
|
961
|
-
logger.debug(`Creating release for ${to}${release.prerelease ? " (prerelease)" : ""}`);
|
|
962
|
-
logger.debug("Release details:", formatJson({
|
|
963
|
-
tag_name: release.tag_name,
|
|
964
|
-
name: release.name,
|
|
965
|
-
prerelease: release.prerelease
|
|
966
|
-
}));
|
|
967
|
-
if (dryRun) {
|
|
968
|
-
logger.info("[dry-run] Publish GitHub release for", release.tag_name);
|
|
969
|
-
} else {
|
|
970
|
-
logger.debug("Publishing release to GitHub...");
|
|
971
|
-
await createGithubRelease({
|
|
972
|
-
...config,
|
|
973
|
-
from: bumpResult?.bumped && bumpResult.fromTag || "v0.0.0",
|
|
974
|
-
to,
|
|
975
|
-
repo: repoConfig
|
|
976
|
-
}, release);
|
|
977
|
-
}
|
|
978
|
-
logger.success(`Release ${to} published to GitHub!`);
|
|
979
|
-
return [{
|
|
980
|
-
name: to,
|
|
981
|
-
tag: to,
|
|
982
|
-
version: to,
|
|
983
|
-
prerelease: release.prerelease
|
|
984
|
-
}];
|
|
985
|
-
}
|
|
986
|
-
async function github(options) {
|
|
987
|
-
try {
|
|
988
|
-
const dryRun = options.dryRun ?? false;
|
|
989
|
-
logger.debug(`Dry run: ${dryRun}`);
|
|
990
|
-
const config = await loadRelizyConfig({
|
|
991
|
-
configName: options.configName,
|
|
992
|
-
baseConfig: options.config,
|
|
993
|
-
overrides: {
|
|
994
|
-
from: options.from,
|
|
995
|
-
to: options.to,
|
|
996
|
-
logLevel: options.logLevel,
|
|
997
|
-
tokens: {
|
|
998
|
-
github: options.token
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
});
|
|
1002
|
-
if (config.monorepo?.versionMode === "independent") {
|
|
1003
|
-
return await githubIndependentMode({
|
|
1004
|
-
config,
|
|
1005
|
-
dryRun,
|
|
1006
|
-
bumpResult: options.bumpResult,
|
|
1007
|
-
force: options.force ?? false,
|
|
1008
|
-
suffix: options.suffix
|
|
1009
|
-
});
|
|
1010
|
-
}
|
|
1011
|
-
const rootPackageBase = readPackageJson(config.cwd);
|
|
1012
|
-
if (!rootPackageBase) {
|
|
1013
|
-
throw new Error("Failed to read root package.json");
|
|
1014
|
-
}
|
|
1015
|
-
const { from, to } = await resolveTags({
|
|
1016
|
-
config,
|
|
1017
|
-
step: "provider-release",
|
|
1018
|
-
newVersion: options.bumpResult?.newVersion || rootPackageBase.version,
|
|
1019
|
-
pkg: rootPackageBase
|
|
1020
|
-
});
|
|
1021
|
-
const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
|
|
1022
|
-
config,
|
|
1023
|
-
force: options.force ?? false,
|
|
1024
|
-
suffix: options.suffix,
|
|
1025
|
-
changelog: true,
|
|
1026
|
-
from,
|
|
1027
|
-
to
|
|
1028
|
-
});
|
|
1029
|
-
return await githubUnified({
|
|
1030
|
-
config,
|
|
1031
|
-
dryRun,
|
|
1032
|
-
rootPackage,
|
|
1033
|
-
bumpResult: options.bumpResult
|
|
1034
|
-
});
|
|
625
|
+
await execPromise("git fetch --tags", { cwd, noStderr: true, noStdout: true, noSuccess: true });
|
|
626
|
+
logger.debug("Git tags fetched successfully");
|
|
1035
627
|
} catch (error) {
|
|
1036
|
-
logger.
|
|
1037
|
-
|
|
628
|
+
logger.fail("Failed to fetch some git tags from remote (tags might already exist locally)", error);
|
|
629
|
+
logger.info("Continuing with local tags");
|
|
1038
630
|
}
|
|
1039
631
|
}
|
|
1040
|
-
|
|
1041
|
-
async function createGitlabRelease({
|
|
1042
|
-
config,
|
|
1043
|
-
release,
|
|
1044
|
-
dryRun
|
|
1045
|
-
}) {
|
|
1046
|
-
const token = config.tokens.gitlab || config.repo?.token;
|
|
1047
|
-
if (!token && !dryRun) {
|
|
1048
|
-
throw new Error(
|
|
1049
|
-
"No GitLab token found. Set GITLAB_TOKEN or CI_JOB_TOKEN environment variable or configure tokens.gitlab"
|
|
1050
|
-
);
|
|
1051
|
-
}
|
|
1052
|
-
const repoConfig = config.repo?.repo;
|
|
1053
|
-
if (!repoConfig) {
|
|
1054
|
-
throw new Error("No repository URL found in config");
|
|
1055
|
-
}
|
|
1056
|
-
logger.debug(`Parsed repository URL: ${repoConfig}`);
|
|
1057
|
-
const projectPath = encodeURIComponent(repoConfig);
|
|
1058
|
-
const gitlabDomain = config.repo?.domain || "gitlab.com";
|
|
1059
|
-
const apiUrl = `https://${gitlabDomain}/api/v4/projects/${projectPath}/releases`;
|
|
1060
|
-
logger.info(`Creating GitLab release at: ${apiUrl}`);
|
|
1061
|
-
const payload = {
|
|
1062
|
-
tag_name: release.tag_name,
|
|
1063
|
-
name: release.name || release.tag_name,
|
|
1064
|
-
description: release.description || "",
|
|
1065
|
-
ref: release.ref || "main"
|
|
1066
|
-
};
|
|
632
|
+
function detectGitProvider(cwd = process.cwd()) {
|
|
1067
633
|
try {
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1075
|
-
released_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1076
|
-
_links: {
|
|
1077
|
-
self: `${apiUrl}/${encodeURIComponent(release.tag_name)}`
|
|
1078
|
-
}
|
|
1079
|
-
};
|
|
634
|
+
const remoteUrl = execSync("git remote get-url origin", {
|
|
635
|
+
cwd,
|
|
636
|
+
encoding: "utf8"
|
|
637
|
+
}).trim();
|
|
638
|
+
if (remoteUrl.includes("github.com")) {
|
|
639
|
+
return "github";
|
|
1080
640
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
method: "POST",
|
|
1084
|
-
headers: {
|
|
1085
|
-
"Content-Type": "application/json",
|
|
1086
|
-
"PRIVATE-TOKEN": token || ""
|
|
1087
|
-
},
|
|
1088
|
-
body: JSON.stringify(payload)
|
|
1089
|
-
});
|
|
1090
|
-
if (!response.ok) {
|
|
1091
|
-
const errorText = await response.text();
|
|
1092
|
-
throw new Error(`GitLab API error (${response.status}): ${errorText}`);
|
|
641
|
+
if (remoteUrl.includes("gitlab.com") || remoteUrl.includes("gitlab")) {
|
|
642
|
+
return "gitlab";
|
|
1093
643
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
return
|
|
1097
|
-
} catch (error) {
|
|
1098
|
-
logger.error("Failed to create GitLab release:", error);
|
|
1099
|
-
throw error;
|
|
644
|
+
return null;
|
|
645
|
+
} catch {
|
|
646
|
+
return null;
|
|
1100
647
|
}
|
|
1101
648
|
}
|
|
1102
|
-
|
|
649
|
+
function parseGitRemoteUrl(remoteUrl) {
|
|
650
|
+
const sshRegex = /git@[\w.-]+:([\w.-]+)\/([\w.-]+?)(?:\.git)?$/;
|
|
651
|
+
const httpsRegex = /https?:\/\/[\w.-]+\/([\w.-]+)\/([\w.-]+?)(?:\.git)?$/;
|
|
652
|
+
const sshMatch = remoteUrl.match(sshRegex);
|
|
653
|
+
if (sshMatch) {
|
|
654
|
+
return {
|
|
655
|
+
owner: sshMatch[1],
|
|
656
|
+
repo: sshMatch[2]
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
const httpsMatch = remoteUrl.match(httpsRegex);
|
|
660
|
+
if (httpsMatch) {
|
|
661
|
+
return {
|
|
662
|
+
owner: httpsMatch[1],
|
|
663
|
+
repo: httpsMatch[2]
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
async function createCommitAndTags({
|
|
1103
669
|
config,
|
|
670
|
+
noVerify,
|
|
671
|
+
bumpedPackages,
|
|
672
|
+
newVersion,
|
|
1104
673
|
dryRun,
|
|
1105
|
-
|
|
1106
|
-
suffix,
|
|
1107
|
-
force
|
|
674
|
+
logLevel
|
|
1108
675
|
}) {
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
676
|
+
const internalConfig = config || await loadRelizyConfig();
|
|
677
|
+
try {
|
|
678
|
+
await executeHook("before:commit-and-tag", internalConfig, dryRun ?? false);
|
|
679
|
+
const filePatternsToAdd = [
|
|
680
|
+
"package.json",
|
|
681
|
+
"lerna.json",
|
|
682
|
+
"CHANGELOG.md",
|
|
683
|
+
"**/CHANGELOG.md",
|
|
684
|
+
"**/package.json"
|
|
685
|
+
];
|
|
686
|
+
logger.start("Start commit and tag");
|
|
687
|
+
logger.debug("Adding files to git staging area...");
|
|
688
|
+
for (const pattern of filePatternsToAdd) {
|
|
689
|
+
if (pattern === "lerna.json" && !hasLernaJson(internalConfig.cwd)) {
|
|
690
|
+
logger.verbose(`Skipping lerna.json as it doesn't exist`);
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
if ((pattern === "lerna.json" || pattern === "CHANGELOG.md") && !existsSync(join(internalConfig.cwd, pattern))) {
|
|
694
|
+
logger.verbose(`Skipping ${pattern} as it doesn't exist`);
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
if (dryRun) {
|
|
698
|
+
logger.info(`[dry-run] git add ${pattern}`);
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
try {
|
|
702
|
+
logger.debug(`git add ${pattern}`);
|
|
703
|
+
execSync(`git add ${pattern}`);
|
|
704
|
+
} catch {
|
|
705
|
+
}
|
|
1132
706
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
config,
|
|
1137
|
-
dryRun,
|
|
1138
|
-
newVersion
|
|
1139
|
-
});
|
|
1140
|
-
if (!changelog) {
|
|
1141
|
-
logger.warn(`No changelog found for ${pkg.name}`);
|
|
1142
|
-
continue;
|
|
707
|
+
const rootPackage = readPackageJson(internalConfig.cwd);
|
|
708
|
+
if (!rootPackage) {
|
|
709
|
+
throw new Error("Failed to read root package.json");
|
|
1143
710
|
}
|
|
1144
|
-
|
|
1145
|
-
const
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
ref: currentBranch.trim()
|
|
1150
|
-
};
|
|
1151
|
-
logger.debug(`Creating release for ${to} (ref: ${release.ref})`);
|
|
711
|
+
newVersion = newVersion || rootPackage.version;
|
|
712
|
+
const versionForMessage = internalConfig.monorepo?.versionMode === "independent" ? bumpedPackages?.map((pkg) => getIndependentTag({ name: pkg.name, version: pkg.newVersion || pkg.version })).join(", ") || "unknown" : newVersion || "unknown";
|
|
713
|
+
const commitMessage = internalConfig.templates.commitMessage?.replaceAll("{{newVersion}}", versionForMessage) || `chore(release): bump version to ${versionForMessage}`;
|
|
714
|
+
const noVerifyFlag = noVerify ? "--no-verify " : "";
|
|
715
|
+
logger.debug(`No verify: ${noVerify}`);
|
|
1152
716
|
if (dryRun) {
|
|
1153
|
-
logger.info(`[dry-run]
|
|
717
|
+
logger.info(`[dry-run] git commit ${noVerifyFlag}-m "${commitMessage}"`);
|
|
1154
718
|
} else {
|
|
1155
|
-
logger.debug(`
|
|
1156
|
-
await
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
postedReleases.push({
|
|
1162
|
-
name: pkg.name,
|
|
1163
|
-
tag: release.tag_name,
|
|
1164
|
-
version: newVersion,
|
|
1165
|
-
prerelease: isPrerelease(newVersion)
|
|
719
|
+
logger.debug(`Executing: git commit ${noVerifyFlag}-m "${commitMessage}"`);
|
|
720
|
+
await execPromise(`git commit ${noVerifyFlag}-m "${commitMessage}"`, {
|
|
721
|
+
logLevel,
|
|
722
|
+
noStderr: true,
|
|
723
|
+
noStdout: true,
|
|
724
|
+
cwd: internalConfig.cwd
|
|
1166
725
|
});
|
|
726
|
+
logger.success(`Committed: ${commitMessage}${noVerify ? " (--no-verify)" : ""}`);
|
|
1167
727
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
cwd: config.cwd
|
|
1197
|
-
});
|
|
1198
|
-
const release = {
|
|
1199
|
-
tag_name: to,
|
|
1200
|
-
name: to,
|
|
1201
|
-
description: releaseBody,
|
|
1202
|
-
ref: currentBranch.trim()
|
|
1203
|
-
};
|
|
1204
|
-
logger.info(`Creating release for ${to} (ref: ${release.ref})`);
|
|
1205
|
-
logger.debug("Release details:", formatJson({
|
|
1206
|
-
tag_name: release.tag_name,
|
|
1207
|
-
name: release.name,
|
|
1208
|
-
ref: release.ref
|
|
1209
|
-
}));
|
|
1210
|
-
if (dryRun) {
|
|
1211
|
-
logger.info("[dry-run] Publish GitLab release for", release.tag_name);
|
|
1212
|
-
} else {
|
|
1213
|
-
logger.debug("Publishing release to GitLab...");
|
|
1214
|
-
await createGitlabRelease({
|
|
1215
|
-
config,
|
|
1216
|
-
release,
|
|
1217
|
-
dryRun
|
|
1218
|
-
});
|
|
1219
|
-
}
|
|
1220
|
-
logger.success(`Release ${to} published to GitLab!`);
|
|
1221
|
-
return [{
|
|
1222
|
-
name: to,
|
|
1223
|
-
tag: to,
|
|
1224
|
-
version: to,
|
|
1225
|
-
prerelease: isPrerelease(rootPackage.version)
|
|
1226
|
-
}];
|
|
1227
|
-
}
|
|
1228
|
-
async function gitlab(options = {}) {
|
|
1229
|
-
try {
|
|
1230
|
-
const dryRun = options.dryRun ?? false;
|
|
1231
|
-
logger.debug(`Dry run: ${dryRun}`);
|
|
1232
|
-
const config = await loadRelizyConfig({
|
|
1233
|
-
configName: options.configName,
|
|
1234
|
-
baseConfig: options.config,
|
|
1235
|
-
overrides: {
|
|
1236
|
-
from: options.from,
|
|
1237
|
-
to: options.to,
|
|
1238
|
-
logLevel: options.logLevel,
|
|
1239
|
-
tokens: {
|
|
1240
|
-
gitlab: options.token
|
|
728
|
+
const signTags = internalConfig.signTags ? "-s" : "";
|
|
729
|
+
logger.debug(`Sign tags: ${internalConfig.signTags}`);
|
|
730
|
+
const createdTags = [];
|
|
731
|
+
if (internalConfig.monorepo?.versionMode === "independent" && bumpedPackages && bumpedPackages.length > 0 && internalConfig.release.gitTag) {
|
|
732
|
+
logger.debug(`Creating ${bumpedPackages.length} independent package tags`);
|
|
733
|
+
for (const pkg of bumpedPackages) {
|
|
734
|
+
if (!pkg.newVersion) {
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
const tagName = getIndependentTag({ version: pkg.newVersion, name: pkg.name });
|
|
738
|
+
const tagMessage = internalConfig.templates?.tagMessage?.replaceAll("{{newVersion}}", pkg.newVersion) || tagName;
|
|
739
|
+
if (dryRun) {
|
|
740
|
+
logger.info(`[dry-run] git tag ${signTags} -a ${tagName} -m "${tagMessage}"`);
|
|
741
|
+
} else {
|
|
742
|
+
const cmd = `git tag ${signTags} -a ${tagName} -m "${tagMessage}"`;
|
|
743
|
+
logger.debug(`Executing: ${cmd}`);
|
|
744
|
+
try {
|
|
745
|
+
await execPromise(cmd, {
|
|
746
|
+
logLevel,
|
|
747
|
+
noStderr: true,
|
|
748
|
+
noStdout: true,
|
|
749
|
+
cwd: internalConfig.cwd
|
|
750
|
+
});
|
|
751
|
+
logger.debug(`Tag created: ${tagName}`);
|
|
752
|
+
} catch (error) {
|
|
753
|
+
logger.error(`Failed to create tag ${tagName}:`, error);
|
|
754
|
+
throw error;
|
|
755
|
+
}
|
|
1241
756
|
}
|
|
757
|
+
createdTags.push(tagName);
|
|
1242
758
|
}
|
|
1243
|
-
|
|
1244
|
-
if (
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
759
|
+
logger.success(`Created ${createdTags.length} tags for independent packages, ${createdTags.join(", ")}`);
|
|
760
|
+
} else if (internalConfig.release.gitTag) {
|
|
761
|
+
const tagName = internalConfig.templates.tagBody?.replaceAll("{{newVersion}}", newVersion);
|
|
762
|
+
const tagMessage = internalConfig.templates?.tagMessage?.replaceAll("{{newVersion}}", newVersion) || tagName;
|
|
763
|
+
if (dryRun) {
|
|
764
|
+
logger.info(`[dry-run] git tag ${signTags} -a ${tagName} -m "${tagMessage}"`);
|
|
765
|
+
} else {
|
|
766
|
+
const cmd = `git tag ${signTags} -a ${tagName} -m "${tagMessage}"`;
|
|
767
|
+
logger.debug(`Executing: ${cmd}`);
|
|
768
|
+
try {
|
|
769
|
+
await execPromise(cmd, {
|
|
770
|
+
logLevel,
|
|
771
|
+
noStderr: true,
|
|
772
|
+
noStdout: true,
|
|
773
|
+
cwd: internalConfig.cwd
|
|
774
|
+
});
|
|
775
|
+
logger.debug(`Tag created: ${tagName}`);
|
|
776
|
+
} catch (error) {
|
|
777
|
+
logger.error(`Failed to create tag ${tagName}:`, error);
|
|
778
|
+
throw error;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
createdTags.push(tagName);
|
|
1256
782
|
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
pkg: rootPackageBase
|
|
1262
|
-
});
|
|
1263
|
-
const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
|
|
1264
|
-
config,
|
|
1265
|
-
force: options.force ?? false,
|
|
1266
|
-
suffix: options.suffix,
|
|
1267
|
-
changelog: true,
|
|
1268
|
-
from,
|
|
1269
|
-
to
|
|
1270
|
-
});
|
|
1271
|
-
logger.debug(`Root package: ${getIndependentTag({ name: rootPackage.name, version: rootPackage.newVersion || rootPackage.version })}`);
|
|
1272
|
-
return await gitlabUnified({
|
|
1273
|
-
config,
|
|
1274
|
-
dryRun,
|
|
1275
|
-
rootPackage,
|
|
1276
|
-
bumpResult: options.bumpResult
|
|
1277
|
-
});
|
|
783
|
+
logger.debug("Created Tags:", createdTags.join(", "));
|
|
784
|
+
logger.success("Commit and tag completed!");
|
|
785
|
+
await executeHook("success:commit-and-tag", internalConfig, dryRun ?? false);
|
|
786
|
+
return createdTags;
|
|
1278
787
|
} catch (error) {
|
|
1279
|
-
logger.error("Error
|
|
788
|
+
logger.error("Error committing and tagging:", error);
|
|
789
|
+
await executeHook("error:commit-and-tag", internalConfig, dryRun ?? false);
|
|
1280
790
|
throw error;
|
|
1281
791
|
}
|
|
1282
792
|
}
|
|
793
|
+
async function pushCommitAndTags({ config, dryRun, logLevel, cwd }) {
|
|
794
|
+
logger.start("Start push changes and tags");
|
|
795
|
+
const command = config.release.gitTag ? "git push --follow-tags" : "git push";
|
|
796
|
+
if (dryRun) {
|
|
797
|
+
logger.info(`[dry-run] ${command}`);
|
|
798
|
+
} else {
|
|
799
|
+
logger.debug(`Executing: ${command}`);
|
|
800
|
+
await execPromise(command, { noStderr: true, noStdout: true, logLevel, cwd });
|
|
801
|
+
}
|
|
802
|
+
logger.success("Pushing changes and tags completed!");
|
|
803
|
+
}
|
|
804
|
+
function getFirstCommit(cwd) {
|
|
805
|
+
const result = execSync(
|
|
806
|
+
"git rev-list --max-parents=0 HEAD",
|
|
807
|
+
{
|
|
808
|
+
cwd,
|
|
809
|
+
encoding: "utf8"
|
|
810
|
+
}
|
|
811
|
+
);
|
|
812
|
+
return result.trim();
|
|
813
|
+
}
|
|
814
|
+
function getCurrentGitBranch(cwd) {
|
|
815
|
+
const result = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
816
|
+
cwd,
|
|
817
|
+
encoding: "utf8"
|
|
818
|
+
});
|
|
819
|
+
return result.trim();
|
|
820
|
+
}
|
|
821
|
+
function getCurrentGitRef(cwd) {
|
|
822
|
+
const branch = getCurrentGitBranch(cwd);
|
|
823
|
+
return branch || "HEAD";
|
|
824
|
+
}
|
|
1283
825
|
|
|
1284
|
-
function
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
826
|
+
async function generateMarkDown({
|
|
827
|
+
commits,
|
|
828
|
+
config,
|
|
829
|
+
from,
|
|
830
|
+
to,
|
|
831
|
+
isFirstCommit
|
|
832
|
+
}) {
|
|
833
|
+
const typeGroups = groupBy(commits, "type");
|
|
834
|
+
const markdown = [];
|
|
835
|
+
const breakingChanges = [];
|
|
836
|
+
const updatedConfig = {
|
|
837
|
+
...config,
|
|
838
|
+
from,
|
|
839
|
+
to
|
|
840
|
+
};
|
|
841
|
+
const versionTitle = updatedConfig.to;
|
|
842
|
+
const changelogTitle = `${updatedConfig.from}...${updatedConfig.to}`;
|
|
843
|
+
markdown.push("", `## ${changelogTitle}`, "");
|
|
844
|
+
if (updatedConfig.repo && updatedConfig.from && versionTitle) {
|
|
845
|
+
const formattedCompareLink = formatCompareChanges(versionTitle, {
|
|
846
|
+
...updatedConfig,
|
|
847
|
+
from: isFirstCommit ? getFirstCommit(updatedConfig.cwd) : updatedConfig.from
|
|
848
|
+
});
|
|
849
|
+
markdown.push(formattedCompareLink);
|
|
850
|
+
}
|
|
851
|
+
for (const type in updatedConfig.types) {
|
|
852
|
+
const group = typeGroups[type];
|
|
853
|
+
if (!group || group.length === 0) {
|
|
1289
854
|
continue;
|
|
1290
855
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
856
|
+
if (typeof updatedConfig.types[type] === "boolean") {
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
markdown.push("", `### ${updatedConfig.types[type]?.title}`, "");
|
|
860
|
+
for (const commit of group.reverse()) {
|
|
861
|
+
const line = formatCommit(commit, updatedConfig);
|
|
862
|
+
markdown.push(line);
|
|
863
|
+
if (commit.isBreaking) {
|
|
864
|
+
breakingChanges.push(line);
|
|
865
|
+
}
|
|
1298
866
|
}
|
|
1299
867
|
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
function detectReleaseTypeFromCommits(commits, types) {
|
|
1303
|
-
return determineSemverChange(commits, types);
|
|
1304
|
-
}
|
|
1305
|
-
function validatePrereleaseDowngrade(currentVersion, targetPreid, configuredType) {
|
|
1306
|
-
if (configuredType !== "prerelease" || !targetPreid || !isPrerelease(currentVersion)) {
|
|
1307
|
-
return;
|
|
868
|
+
if (breakingChanges.length > 0) {
|
|
869
|
+
markdown.push("", "#### \u26A0\uFE0F Breaking Changes", "", ...breakingChanges);
|
|
1308
870
|
}
|
|
1309
|
-
const
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
|
|
871
|
+
const _authors = /* @__PURE__ */ new Map();
|
|
872
|
+
for (const commit of commits) {
|
|
873
|
+
if (!commit.author) {
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
const name = formatName(commit.author.name);
|
|
877
|
+
if (!name || name.includes("[bot]")) {
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
if (updatedConfig.excludeAuthors && updatedConfig.excludeAuthors.some(
|
|
881
|
+
(v) => name.includes(v) || commit.author.email?.includes(v)
|
|
882
|
+
)) {
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
if (_authors.has(name)) {
|
|
886
|
+
const entry = _authors.get(name);
|
|
887
|
+
entry?.email.add(commit.author.email);
|
|
888
|
+
} else {
|
|
889
|
+
_authors.set(name, { email: /* @__PURE__ */ new Set([commit.author.email]), name });
|
|
890
|
+
}
|
|
1313
891
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
892
|
+
if (updatedConfig.repo?.provider === "github") {
|
|
893
|
+
await Promise.all(
|
|
894
|
+
[..._authors.keys()].map(async (authorName) => {
|
|
895
|
+
const meta = _authors.get(authorName);
|
|
896
|
+
if (!meta) {
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
for (const data of [...meta.email, meta.name]) {
|
|
900
|
+
const { user } = await fetch$1(`https://ungh.cc/users/find/${data}`).then((r) => r.json()).catch(() => ({ user: null }));
|
|
901
|
+
if (user) {
|
|
902
|
+
meta.github = user.username;
|
|
903
|
+
break;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
})
|
|
907
|
+
);
|
|
1319
908
|
}
|
|
1320
|
-
const
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
909
|
+
const authors = [..._authors.entries()].map((e) => ({
|
|
910
|
+
name: e[0],
|
|
911
|
+
...e[1]
|
|
912
|
+
}));
|
|
913
|
+
if (authors.length > 0 && !updatedConfig.noAuthors) {
|
|
914
|
+
markdown.push(
|
|
915
|
+
"",
|
|
916
|
+
"### \u2764\uFE0F Contributors",
|
|
917
|
+
"",
|
|
918
|
+
...authors.map((i) => {
|
|
919
|
+
const _email = [...i.email].find(
|
|
920
|
+
(e) => !e.includes("noreply.github.com")
|
|
921
|
+
);
|
|
922
|
+
const email = updatedConfig.hideAuthorEmail !== true && _email ? ` <${_email}>` : "";
|
|
923
|
+
const github = i.github ? ` ([@${i.github}](https://github.com/${i.github}))` : "";
|
|
924
|
+
return `- ${i.name}${github || email || ""}`;
|
|
925
|
+
})
|
|
926
|
+
);
|
|
1324
927
|
}
|
|
1325
|
-
|
|
1326
|
-
return
|
|
928
|
+
const result = convert(markdown.join("\n").trim(), true);
|
|
929
|
+
return result;
|
|
1327
930
|
}
|
|
1328
|
-
function
|
|
1329
|
-
if (!
|
|
1330
|
-
|
|
1331
|
-
return void 0;
|
|
931
|
+
function getCommitBody(commit) {
|
|
932
|
+
if (!commit.body) {
|
|
933
|
+
return "";
|
|
1332
934
|
}
|
|
1333
|
-
const
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
935
|
+
const lines = commit.body.split("\n");
|
|
936
|
+
const contentLines = lines.filter((line) => {
|
|
937
|
+
const trimmedLine = line.trim();
|
|
938
|
+
if (!trimmedLine) {
|
|
939
|
+
return false;
|
|
940
|
+
}
|
|
941
|
+
const isFileLine = /^[AMDTUXB](?:\d{3})?\s+/.test(trimmedLine) || /^[RCM]\d{3}\s+/.test(trimmedLine);
|
|
942
|
+
return !isFileLine;
|
|
943
|
+
});
|
|
944
|
+
if (contentLines.length === 0) {
|
|
945
|
+
return "";
|
|
1337
946
|
}
|
|
1338
|
-
const
|
|
1339
|
-
|
|
1340
|
-
|
|
947
|
+
const indentedBody = contentLines.map((line) => ` ${line}`).join("\n");
|
|
948
|
+
return `
|
|
949
|
+
|
|
950
|
+
${indentedBody}
|
|
951
|
+
`;
|
|
1341
952
|
}
|
|
1342
|
-
function
|
|
1343
|
-
|
|
1344
|
-
return "
|
|
953
|
+
function formatCommit(commit, config) {
|
|
954
|
+
const body = config.changelog.includeCommitBody ? getCommitBody(commit) : "";
|
|
955
|
+
return `- ${commit.scope ? `**${commit.scope.trim()}:** ` : ""}${commit.isBreaking ? "\u26A0\uFE0F " : ""}${upperFirst(commit.description)}${formatReferences(commit.references, config)}${body}`;
|
|
1345
956
|
}
|
|
1346
|
-
function
|
|
1347
|
-
const
|
|
1348
|
-
const
|
|
1349
|
-
if (
|
|
1350
|
-
|
|
1351
|
-
if (!testVersion) {
|
|
1352
|
-
throw new Error(`Unable to change preid from ${currentPreid} to ${preid} for version ${currentVersion}`);
|
|
1353
|
-
}
|
|
1354
|
-
const isUpgrade = semver.gt(testVersion, currentVersion);
|
|
1355
|
-
if (!isUpgrade) {
|
|
1356
|
-
throw new Error(`Unable to change preid from ${currentVersion} to ${testVersion}, it's not a valid upgrade (cannot downgrade from ${currentPreid} to ${preid})`);
|
|
1357
|
-
}
|
|
1358
|
-
return "prerelease";
|
|
957
|
+
function formatReferences(references, config) {
|
|
958
|
+
const pr = references.filter((ref) => ref.type === "pull-request");
|
|
959
|
+
const issue = references.filter((ref) => ref.type === "issue");
|
|
960
|
+
if (pr.length > 0 || issue.length > 0) {
|
|
961
|
+
return ` (${[...pr, ...issue].map((ref) => formatReference(ref, config.repo)).join(", ")})`;
|
|
1359
962
|
}
|
|
1360
|
-
if (
|
|
1361
|
-
|
|
1362
|
-
return void 0;
|
|
963
|
+
if (references.length > 0) {
|
|
964
|
+
return ` (${formatReference(references[0], config.repo)})`;
|
|
1363
965
|
}
|
|
1364
|
-
|
|
1365
|
-
return "prerelease";
|
|
966
|
+
return "";
|
|
1366
967
|
}
|
|
1367
|
-
function
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
const
|
|
1372
|
-
const
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
} else {
|
|
1376
|
-
logger.debug(`Using explicit release type: ${releaseType}`);
|
|
968
|
+
function formatName(name = "") {
|
|
969
|
+
return name.split(" ").map((p) => upperFirst(p.trim())).join(" ");
|
|
970
|
+
}
|
|
971
|
+
function groupBy(items, key) {
|
|
972
|
+
const groups = {};
|
|
973
|
+
for (const item of items) {
|
|
974
|
+
groups[item[key]] = groups[item[key]] || [];
|
|
975
|
+
groups[item[key]]?.push(item);
|
|
1377
976
|
}
|
|
1378
|
-
return
|
|
977
|
+
return groups;
|
|
1379
978
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
979
|
+
|
|
980
|
+
function fromTagIsFirstCommit(fromTag, cwd) {
|
|
981
|
+
return fromTag === getFirstCommit(cwd);
|
|
982
|
+
}
|
|
983
|
+
async function generateChangelog({
|
|
984
|
+
pkg,
|
|
985
|
+
config,
|
|
986
|
+
dryRun,
|
|
987
|
+
newVersion
|
|
1387
988
|
}) {
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
if (force) {
|
|
1393
|
-
logger.debug(`Force flag enabled, using configured type: ${releaseType}`);
|
|
1394
|
-
return releaseType;
|
|
1395
|
-
}
|
|
1396
|
-
const isCurrentPrerelease = isPrerelease(currentVersion);
|
|
1397
|
-
if (!isCurrentPrerelease) {
|
|
1398
|
-
if (releaseType === "release") {
|
|
1399
|
-
return handleStableVersionWithReleaseType(commits, types, force);
|
|
1400
|
-
}
|
|
1401
|
-
if (releaseType === "prerelease") {
|
|
1402
|
-
return handleStableVersionWithPrereleaseType(commits, types, force);
|
|
1403
|
-
}
|
|
1404
|
-
return handleExplicitReleaseType({ releaseType, currentVersion });
|
|
989
|
+
let fromTag = config.from || pkg.fromTag || getFirstCommit(config.cwd);
|
|
990
|
+
const isFirstCommit = fromTagIsFirstCommit(fromTag, config.cwd);
|
|
991
|
+
if (isFirstCommit) {
|
|
992
|
+
fromTag = config.monorepo?.versionMode === "independent" ? getIndependentTag({ version: "0.0.0", name: pkg.name }) : config.templates.tagBody.replace("{{newVersion}}", "0.0.0");
|
|
1405
993
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
994
|
+
let toTag = config.to;
|
|
995
|
+
if (!toTag) {
|
|
996
|
+
toTag = config.monorepo?.versionMode === "independent" ? getIndependentTag({ version: newVersion, name: pkg.name }) : config.templates.tagBody.replace("{{newVersion}}", newVersion);
|
|
1408
997
|
}
|
|
1409
|
-
if (
|
|
1410
|
-
|
|
998
|
+
if (!toTag) {
|
|
999
|
+
throw new Error(`No tag found for ${pkg.name}`);
|
|
1411
1000
|
}
|
|
1412
|
-
|
|
1413
|
-
}
|
|
1414
|
-
function writeVersion(pkgPath, version, dryRun = false) {
|
|
1415
|
-
const packageJsonPath = join(pkgPath, "package.json");
|
|
1001
|
+
logger.debug(`Generating changelog for ${pkg.name} - from ${fromTag} to ${toTag}`);
|
|
1416
1002
|
try {
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1003
|
+
config = {
|
|
1004
|
+
...config,
|
|
1005
|
+
from: fromTag,
|
|
1006
|
+
to: toTag
|
|
1007
|
+
};
|
|
1008
|
+
const generatedChangelog = await generateMarkDown({
|
|
1009
|
+
commits: pkg.commits,
|
|
1010
|
+
config,
|
|
1011
|
+
from: fromTag,
|
|
1012
|
+
isFirstCommit,
|
|
1013
|
+
to: toTag
|
|
1014
|
+
});
|
|
1015
|
+
let changelog = generatedChangelog;
|
|
1016
|
+
if (pkg.commits.length === 0) {
|
|
1017
|
+
changelog = `${changelog}
|
|
1018
|
+
|
|
1019
|
+
${config.templates.emptyChangelogContent}`;
|
|
1020
|
+
}
|
|
1021
|
+
const changelogResult = await executeHook("generate:changelog", config, dryRun, {
|
|
1022
|
+
commits: pkg.commits,
|
|
1023
|
+
changelog
|
|
1024
|
+
});
|
|
1025
|
+
changelog = changelogResult || changelog;
|
|
1026
|
+
logger.verbose(`Output changelog for ${pkg.name}:
|
|
1027
|
+
${changelog}`);
|
|
1028
|
+
logger.debug(`Changelog generated for ${pkg.name} (${pkg.commits.length} commits)`);
|
|
1029
|
+
logger.verbose(`Final changelog for ${pkg.name}:
|
|
1030
|
+
|
|
1031
|
+
${changelog}
|
|
1032
|
+
|
|
1033
|
+
`);
|
|
1422
1034
|
if (dryRun) {
|
|
1423
|
-
logger.info(`[dry-run]
|
|
1424
|
-
return;
|
|
1035
|
+
logger.info(`[dry-run] ${pkg.name} - Generate changelog ${fromTag}...${toTag}`);
|
|
1425
1036
|
}
|
|
1426
|
-
|
|
1427
|
-
`, "utf8");
|
|
1428
|
-
logger.info(`Updated ${packageJson.name}: ${oldVersion} \u2192 ${version}`);
|
|
1037
|
+
return changelog;
|
|
1429
1038
|
} catch (error) {
|
|
1430
|
-
throw new Error(`
|
|
1039
|
+
throw new Error(`Error generating changelog for ${pkg.name} (${fromTag}...${toTag}): ${error}`);
|
|
1431
1040
|
}
|
|
1432
1041
|
}
|
|
1433
|
-
function
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1042
|
+
function writeChangelogToFile({
|
|
1043
|
+
cwd,
|
|
1044
|
+
pkg,
|
|
1045
|
+
changelog,
|
|
1046
|
+
dryRun = false
|
|
1438
1047
|
}) {
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
You should use an explicit release type (use flag: --major, --minor, --patch, --premajor, --preminor, --prepatch, --prerelease)`);
|
|
1444
|
-
}
|
|
1445
|
-
if (isPrereleaseReleaseType(releaseType) && suffix) {
|
|
1446
|
-
newVersion = newVersion.replace(/\.(\d+)$/, `.${suffix}`);
|
|
1447
|
-
}
|
|
1448
|
-
const isValidVersion = semver.gt(newVersion, currentVersion);
|
|
1449
|
-
if (!isValidVersion) {
|
|
1450
|
-
throw new Error(`Unable to bump version "${currentVersion}" to "${newVersion}", new version is not greater than current version`);
|
|
1048
|
+
const changelogPath = join(pkg.path, "CHANGELOG.md");
|
|
1049
|
+
let existingChangelog = "";
|
|
1050
|
+
if (existsSync(changelogPath)) {
|
|
1051
|
+
existingChangelog = readFileSync(changelogPath, "utf8");
|
|
1451
1052
|
}
|
|
1452
|
-
|
|
1453
|
-
|
|
1053
|
+
const lines = existingChangelog.split("\n");
|
|
1054
|
+
const titleIndex = lines.findIndex((line) => line.startsWith("# "));
|
|
1055
|
+
let updatedChangelog;
|
|
1056
|
+
if (titleIndex !== -1) {
|
|
1057
|
+
const beforeTitle = lines.slice(0, titleIndex + 1);
|
|
1058
|
+
const afterTitle = lines.slice(titleIndex + 1);
|
|
1059
|
+
updatedChangelog = [...beforeTitle, "", changelog, "", ...afterTitle].join("\n");
|
|
1060
|
+
} else {
|
|
1061
|
+
const title = "# Changelog\n";
|
|
1062
|
+
updatedChangelog = `${title}
|
|
1063
|
+
${changelog}
|
|
1064
|
+
${existingChangelog}`;
|
|
1454
1065
|
}
|
|
1455
|
-
if (
|
|
1456
|
-
|
|
1066
|
+
if (dryRun) {
|
|
1067
|
+
const relativeChangelogPath = relative(cwd, changelogPath);
|
|
1068
|
+
logger.info(`[dry-run] ${pkg.name} - Write changelog to ${relativeChangelogPath}`);
|
|
1069
|
+
} else {
|
|
1070
|
+
logger.debug(`Writing changelog to ${changelogPath}`);
|
|
1071
|
+
writeFileSync(changelogPath, updatedChangelog, "utf8");
|
|
1072
|
+
logger.info(`Changelog updated for ${pkg.name} (${"newVersion" in pkg && pkg.newVersion || pkg.version})`);
|
|
1457
1073
|
}
|
|
1458
|
-
return newVersion;
|
|
1459
1074
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1075
|
+
|
|
1076
|
+
function getDefaultConfig() {
|
|
1077
|
+
return {
|
|
1078
|
+
cwd: process$1.cwd(),
|
|
1079
|
+
types: {
|
|
1080
|
+
feat: { title: "\u{1F680} Enhancements", semver: "minor" },
|
|
1081
|
+
perf: { title: "\u{1F525} Performance", semver: "patch" },
|
|
1082
|
+
fix: { title: "\u{1FA79} Fixes", semver: "patch" },
|
|
1083
|
+
refactor: { title: "\u{1F485} Refactors", semver: "patch" },
|
|
1084
|
+
docs: { title: "\u{1F4D6} Documentation", semver: "patch" },
|
|
1085
|
+
build: { title: "\u{1F4E6} Build", semver: "patch" },
|
|
1086
|
+
types: { title: "\u{1F30A} Types", semver: "patch" },
|
|
1087
|
+
chore: { title: "\u{1F3E1} Chore" },
|
|
1088
|
+
examples: { title: "\u{1F3C0} Examples" },
|
|
1089
|
+
test: { title: "\u2705 Tests" },
|
|
1090
|
+
style: { title: "\u{1F3A8} Styles" },
|
|
1091
|
+
ci: { title: "\u{1F916} CI" }
|
|
1092
|
+
},
|
|
1093
|
+
templates: {
|
|
1094
|
+
commitMessage: "chore(release): bump version to {{newVersion}}",
|
|
1095
|
+
tagMessage: "Bump version to {{newVersion}}",
|
|
1096
|
+
tagBody: "v{{newVersion}}",
|
|
1097
|
+
emptyChangelogContent: "No relevant changes for this release"
|
|
1098
|
+
},
|
|
1099
|
+
excludeAuthors: [],
|
|
1100
|
+
noAuthors: false,
|
|
1101
|
+
bump: {
|
|
1102
|
+
type: "release",
|
|
1103
|
+
clean: true,
|
|
1104
|
+
dependencyTypes: ["dependencies"],
|
|
1105
|
+
yes: false
|
|
1106
|
+
},
|
|
1107
|
+
changelog: {
|
|
1108
|
+
rootChangelog: true,
|
|
1109
|
+
includeCommitBody: true
|
|
1110
|
+
},
|
|
1111
|
+
publish: {
|
|
1112
|
+
private: false,
|
|
1113
|
+
args: []
|
|
1114
|
+
},
|
|
1115
|
+
tokens: {
|
|
1116
|
+
gitlab: process$1.env.RELIZY_GITLAB_TOKEN || process$1.env.GITLAB_TOKEN || process$1.env.GITLAB_API_TOKEN || process$1.env.CI_JOB_TOKEN,
|
|
1117
|
+
github: process$1.env.RELIZY_GITHUB_TOKEN || process$1.env.GITHUB_TOKEN || process$1.env.GH_TOKEN
|
|
1118
|
+
},
|
|
1119
|
+
scopeMap: {},
|
|
1120
|
+
release: {
|
|
1121
|
+
commit: true,
|
|
1122
|
+
publish: true,
|
|
1123
|
+
changelog: true,
|
|
1124
|
+
push: true,
|
|
1125
|
+
clean: true,
|
|
1126
|
+
providerRelease: true,
|
|
1127
|
+
noVerify: false,
|
|
1128
|
+
gitTag: true
|
|
1129
|
+
},
|
|
1130
|
+
logLevel: "default",
|
|
1131
|
+
safetyCheck: true
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
function setupLogger(logLevel) {
|
|
1135
|
+
if (logLevel) {
|
|
1136
|
+
logger.setLevel(logLevel);
|
|
1137
|
+
logger.debug(`Log level set to: ${logLevel}`);
|
|
1469
1138
|
}
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1139
|
+
}
|
|
1140
|
+
async function resolveConfig(config, cwd) {
|
|
1141
|
+
if (!config.repo) {
|
|
1142
|
+
const resolvedRepoConfig = await resolveRepoConfig(cwd);
|
|
1143
|
+
config.repo = {
|
|
1144
|
+
...resolvedRepoConfig,
|
|
1145
|
+
provider: resolvedRepoConfig.provider
|
|
1146
|
+
};
|
|
1473
1147
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
logger.debug("Lerna version is independent or version mode is independent, skipping update");
|
|
1481
|
-
return;
|
|
1482
|
-
}
|
|
1483
|
-
lernaJson.version = version;
|
|
1484
|
-
if (dryRun) {
|
|
1485
|
-
logger.info(`[dry-run] update lerna.json: ${oldVersion} \u2192 ${version}`);
|
|
1486
|
-
return;
|
|
1487
|
-
}
|
|
1488
|
-
writeFileSync(lernaJsonPath, `${formatJson(lernaJson)}
|
|
1489
|
-
`, "utf8");
|
|
1490
|
-
logger.success(`Updated lerna.json: ${oldVersion} \u2192 ${version}`);
|
|
1491
|
-
} catch (error) {
|
|
1492
|
-
logger.fail(`Unable to update lerna.json: ${error}`);
|
|
1148
|
+
if (typeof config.repo === "string") {
|
|
1149
|
+
const resolvedRepoConfig = getRepoConfig(config.repo);
|
|
1150
|
+
config.repo = {
|
|
1151
|
+
...resolvedRepoConfig,
|
|
1152
|
+
provider: resolvedRepoConfig.provider
|
|
1153
|
+
};
|
|
1493
1154
|
}
|
|
1155
|
+
return config;
|
|
1494
1156
|
}
|
|
1495
|
-
function
|
|
1496
|
-
const
|
|
1497
|
-
|
|
1498
|
-
|
|
1157
|
+
async function loadRelizyConfig(options) {
|
|
1158
|
+
const cwd = options?.overrides?.cwd ?? process$1.cwd();
|
|
1159
|
+
const configName = options?.configName ?? "relizy";
|
|
1160
|
+
await setupDotenv({ cwd });
|
|
1161
|
+
const defaultConfig = getDefaultConfig();
|
|
1162
|
+
const overridesConfig = defu(options?.overrides, options?.baseConfig);
|
|
1163
|
+
const results = await loadConfig({
|
|
1164
|
+
cwd,
|
|
1165
|
+
name: configName,
|
|
1166
|
+
packageJson: true,
|
|
1167
|
+
defaults: defaultConfig,
|
|
1168
|
+
overrides: overridesConfig
|
|
1169
|
+
});
|
|
1170
|
+
if (!results._configFile) {
|
|
1171
|
+
logger.debug(`No config file found with name "${configName}" - using standalone mode`);
|
|
1172
|
+
if (options?.configName) {
|
|
1173
|
+
logger.error(`No config file found with name "${configName}"`);
|
|
1174
|
+
process$1.exit(1);
|
|
1175
|
+
}
|
|
1499
1176
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
const prerelease = semver.prerelease(version);
|
|
1506
|
-
return prerelease ? prerelease.length > 0 : false;
|
|
1507
|
-
}
|
|
1508
|
-
function isStableReleaseType(releaseType) {
|
|
1509
|
-
const stableTypes = ["release", "major", "minor", "patch"];
|
|
1510
|
-
return stableTypes.includes(releaseType);
|
|
1511
|
-
}
|
|
1512
|
-
function isPrereleaseReleaseType(releaseType) {
|
|
1513
|
-
const prereleaseTypes = ["prerelease", "premajor", "preminor", "prepatch"];
|
|
1514
|
-
return prereleaseTypes.includes(releaseType);
|
|
1177
|
+
setupLogger(options?.overrides?.logLevel || results.config.logLevel);
|
|
1178
|
+
logger.verbose("User config:", formatJson(results.config.changelog));
|
|
1179
|
+
const resolvedConfig = await resolveConfig(results.config, cwd);
|
|
1180
|
+
logger.debug("Resolved config:", formatJson(resolvedConfig));
|
|
1181
|
+
return resolvedConfig;
|
|
1515
1182
|
}
|
|
1516
|
-
function
|
|
1517
|
-
return
|
|
1183
|
+
function defineConfig(config) {
|
|
1184
|
+
return config;
|
|
1518
1185
|
}
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1186
|
+
|
|
1187
|
+
async function githubIndependentMode({
|
|
1188
|
+
config,
|
|
1189
|
+
dryRun,
|
|
1190
|
+
bumpResult,
|
|
1191
|
+
force,
|
|
1192
|
+
suffix
|
|
1193
|
+
}) {
|
|
1194
|
+
const repoConfig = config.repo;
|
|
1195
|
+
if (!repoConfig) {
|
|
1196
|
+
throw new Error("No repository configuration found. Please check your changelog config.");
|
|
1525
1197
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
if (!targetPreid || !isPrerelease(currentVersion)) {
|
|
1530
|
-
return false;
|
|
1198
|
+
logger.debug(`GitHub token: ${config.tokens.github || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
|
|
1199
|
+
if (!config.tokens.github && !config.repo?.token) {
|
|
1200
|
+
throw new Error("No GitHub token specified. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
|
|
1531
1201
|
}
|
|
1532
|
-
const
|
|
1533
|
-
|
|
1534
|
-
|
|
1202
|
+
const packages = await getPackagesOrBumpedPackages({
|
|
1203
|
+
config,
|
|
1204
|
+
bumpResult,
|
|
1205
|
+
suffix,
|
|
1206
|
+
force
|
|
1207
|
+
});
|
|
1208
|
+
logger.info(`Creating ${packages.length} GitHub release(s)`);
|
|
1209
|
+
const postedReleases = [];
|
|
1210
|
+
for (const pkg of packages) {
|
|
1211
|
+
const newVersion = isBumpedPackage(pkg) && pkg.newVersion || pkg.version;
|
|
1212
|
+
const from = config.from || pkg.fromTag;
|
|
1213
|
+
const to = config.to || getIndependentTag({ version: newVersion, name: pkg.name });
|
|
1214
|
+
if (!from) {
|
|
1215
|
+
logger.warn(`No from tag found for ${pkg.name}, skipping release`);
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1218
|
+
const toTag = dryRun ? "HEAD" : to;
|
|
1219
|
+
logger.debug(`Processing ${pkg.name}: ${from} \u2192 ${toTag}`);
|
|
1220
|
+
const changelog = await generateChangelog({
|
|
1221
|
+
pkg,
|
|
1222
|
+
config,
|
|
1223
|
+
dryRun,
|
|
1224
|
+
newVersion
|
|
1225
|
+
});
|
|
1226
|
+
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
1227
|
+
const release = {
|
|
1228
|
+
tag_name: to,
|
|
1229
|
+
name: to,
|
|
1230
|
+
body: releaseBody,
|
|
1231
|
+
prerelease: isPrerelease(newVersion)
|
|
1232
|
+
};
|
|
1233
|
+
logger.debug(`Creating release for ${to}${release.prerelease ? " (prerelease)" : ""}`);
|
|
1234
|
+
if (dryRun) {
|
|
1235
|
+
logger.info(`[dry-run] Publish GitHub release for ${to}`);
|
|
1236
|
+
postedReleases.push({
|
|
1237
|
+
name: pkg.name,
|
|
1238
|
+
tag: release.tag_name,
|
|
1239
|
+
version: newVersion,
|
|
1240
|
+
prerelease: release.prerelease
|
|
1241
|
+
});
|
|
1242
|
+
} else {
|
|
1243
|
+
logger.debug(`Publishing release ${to} to GitHub...`);
|
|
1244
|
+
await createGithubRelease({
|
|
1245
|
+
...config,
|
|
1246
|
+
from,
|
|
1247
|
+
to,
|
|
1248
|
+
repo: repoConfig
|
|
1249
|
+
}, release);
|
|
1250
|
+
postedReleases.push({
|
|
1251
|
+
name: pkg.name,
|
|
1252
|
+
tag: release.tag_name,
|
|
1253
|
+
version: newVersion,
|
|
1254
|
+
prerelease: release.prerelease
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1535
1257
|
}
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
dryRun
|
|
1541
|
-
}) {
|
|
1542
|
-
logger.debug(`Analyzing ${pkg.name}`);
|
|
1543
|
-
const currentVersion = pkg.version || "0.0.0";
|
|
1544
|
-
const newVersion = pkg.newVersion;
|
|
1545
|
-
if (!newVersion) {
|
|
1546
|
-
return { bumped: false };
|
|
1258
|
+
if (postedReleases.length === 0) {
|
|
1259
|
+
logger.warn("No releases created");
|
|
1260
|
+
} else {
|
|
1261
|
+
logger.success(`Releases ${postedReleases.map((r) => r.tag).join(", ")} published to GitHub!`);
|
|
1547
1262
|
}
|
|
1548
|
-
|
|
1549
|
-
writeVersion(pkg.path, newVersion, dryRun);
|
|
1550
|
-
return { bumped: true, newVersion, oldVersion: currentVersion };
|
|
1263
|
+
return postedReleases;
|
|
1551
1264
|
}
|
|
1552
|
-
function
|
|
1553
|
-
|
|
1554
|
-
currentVersion,
|
|
1555
|
-
newVersion,
|
|
1265
|
+
async function githubUnified({
|
|
1266
|
+
config,
|
|
1556
1267
|
dryRun,
|
|
1557
|
-
|
|
1268
|
+
rootPackage,
|
|
1269
|
+
bumpResult
|
|
1558
1270
|
}) {
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
if (lernaJsonExists) {
|
|
1563
|
-
logger.log(`${dryRun ? "[dry-run] " : ""}lerna.json: ${currentVersion} \u2192 ${newVersion}`);
|
|
1564
|
-
logger.log("");
|
|
1565
|
-
}
|
|
1271
|
+
const repoConfig = config.repo;
|
|
1272
|
+
if (!repoConfig) {
|
|
1273
|
+
throw new Error("No repository configuration found. Please check your changelog config.");
|
|
1566
1274
|
}
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
})
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1275
|
+
logger.debug(`GitHub token: ${config.tokens.github || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
|
|
1276
|
+
if (!config.tokens.github && !config.repo?.token) {
|
|
1277
|
+
throw new Error("No GitHub token specified. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
|
|
1278
|
+
}
|
|
1279
|
+
const newVersion = bumpResult?.newVersion || rootPackage.version;
|
|
1280
|
+
const to = config.to || config.templates.tagBody.replace("{{newVersion}}", newVersion);
|
|
1281
|
+
const changelog = await generateChangelog({
|
|
1282
|
+
pkg: rootPackage,
|
|
1283
|
+
config,
|
|
1284
|
+
dryRun,
|
|
1285
|
+
newVersion
|
|
1576
1286
|
});
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1287
|
+
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
1288
|
+
const release = {
|
|
1289
|
+
tag_name: to,
|
|
1290
|
+
name: to,
|
|
1291
|
+
body: releaseBody,
|
|
1292
|
+
prerelease: isPrerelease(to)
|
|
1293
|
+
};
|
|
1294
|
+
logger.debug(`Creating release for ${to}${release.prerelease ? " (prerelease)" : ""}`);
|
|
1295
|
+
logger.debug("Release details:", formatJson({
|
|
1296
|
+
tag_name: release.tag_name,
|
|
1297
|
+
name: release.name,
|
|
1298
|
+
prerelease: release.prerelease
|
|
1299
|
+
}));
|
|
1300
|
+
if (dryRun) {
|
|
1301
|
+
logger.info("[dry-run] Publish GitHub release for", release.tag_name);
|
|
1590
1302
|
} else {
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
});
|
|
1599
|
-
logger.log("");
|
|
1600
|
-
}
|
|
1601
|
-
if (packagesAsDependents.length > 0) {
|
|
1602
|
-
logger.log(`${packagesAsDependents.length} dependent package(s):`);
|
|
1603
|
-
packagesAsDependents.forEach((pkg) => {
|
|
1604
|
-
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} ${force ? "(force)" : ""}`);
|
|
1605
|
-
});
|
|
1606
|
-
logger.log("");
|
|
1607
|
-
}
|
|
1608
|
-
if (packagesAsGraduation.length > 0) {
|
|
1609
|
-
logger.log(`${packagesAsGraduation.length} graduation package(s):`);
|
|
1610
|
-
packagesAsGraduation.forEach((pkg) => {
|
|
1611
|
-
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} ${force ? "(force)" : ""}`);
|
|
1612
|
-
});
|
|
1613
|
-
logger.log("");
|
|
1614
|
-
}
|
|
1303
|
+
logger.debug("Publishing release to GitHub...");
|
|
1304
|
+
await createGithubRelease({
|
|
1305
|
+
...config,
|
|
1306
|
+
from: bumpResult?.bumped && bumpResult.fromTag || "v0.0.0",
|
|
1307
|
+
to,
|
|
1308
|
+
repo: repoConfig
|
|
1309
|
+
}, release);
|
|
1615
1310
|
}
|
|
1311
|
+
logger.success(`Release ${to} published to GitHub!`);
|
|
1312
|
+
return [{
|
|
1313
|
+
name: to,
|
|
1314
|
+
tag: to,
|
|
1315
|
+
version: to,
|
|
1316
|
+
prerelease: release.prerelease
|
|
1317
|
+
}];
|
|
1616
1318
|
}
|
|
1617
|
-
function
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
})
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1319
|
+
async function github(options) {
|
|
1320
|
+
try {
|
|
1321
|
+
const dryRun = options.dryRun ?? false;
|
|
1322
|
+
logger.debug(`Dry run: ${dryRun}`);
|
|
1323
|
+
const config = await loadRelizyConfig({
|
|
1324
|
+
configName: options.configName,
|
|
1325
|
+
baseConfig: options.config,
|
|
1326
|
+
overrides: {
|
|
1327
|
+
from: options.from,
|
|
1328
|
+
to: options.to,
|
|
1329
|
+
logLevel: options.logLevel,
|
|
1330
|
+
tokens: {
|
|
1331
|
+
github: options.token
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1625
1334
|
});
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
packagesWithCommits.forEach((pkg) => {
|
|
1634
|
-
pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} (${pkg.commits.length} commits) ${force ? "(force)" : ""}`);
|
|
1635
|
-
});
|
|
1636
|
-
logger.log("");
|
|
1637
|
-
}
|
|
1638
|
-
if (packagesAsDependents.length > 0) {
|
|
1639
|
-
logger.log(`${packagesAsDependents.length} dependent package(s):`);
|
|
1640
|
-
packagesAsDependents.forEach((pkg) => {
|
|
1641
|
-
pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} ${force ? "(force)" : ""}`);
|
|
1335
|
+
if (config.monorepo?.versionMode === "independent") {
|
|
1336
|
+
return await githubIndependentMode({
|
|
1337
|
+
config,
|
|
1338
|
+
dryRun,
|
|
1339
|
+
bumpResult: options.bumpResult,
|
|
1340
|
+
force: options.force ?? false,
|
|
1341
|
+
suffix: options.suffix
|
|
1642
1342
|
});
|
|
1643
|
-
logger.log("");
|
|
1644
1343
|
}
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} ${force ? "(force)" : ""}`);
|
|
1649
|
-
});
|
|
1650
|
-
logger.log("");
|
|
1344
|
+
const rootPackageBase = readPackageJson(config.cwd);
|
|
1345
|
+
if (!rootPackageBase) {
|
|
1346
|
+
throw new Error("Failed to read root package.json");
|
|
1651
1347
|
}
|
|
1348
|
+
const newVersion = options.bumpResult?.newVersion || rootPackageBase.version;
|
|
1349
|
+
const { from, to } = await resolveTags({
|
|
1350
|
+
config,
|
|
1351
|
+
step: "provider-release",
|
|
1352
|
+
newVersion,
|
|
1353
|
+
pkg: rootPackageBase
|
|
1354
|
+
});
|
|
1355
|
+
const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
|
|
1356
|
+
config,
|
|
1357
|
+
force: options.force ?? false,
|
|
1358
|
+
suffix: options.suffix,
|
|
1359
|
+
changelog: true,
|
|
1360
|
+
from,
|
|
1361
|
+
to
|
|
1362
|
+
});
|
|
1363
|
+
return await githubUnified({
|
|
1364
|
+
config,
|
|
1365
|
+
dryRun,
|
|
1366
|
+
rootPackage,
|
|
1367
|
+
bumpResult: options.bumpResult
|
|
1368
|
+
});
|
|
1369
|
+
} catch (error) {
|
|
1370
|
+
logger.error("Error publishing GitHub release:", error);
|
|
1371
|
+
throw error;
|
|
1652
1372
|
}
|
|
1653
1373
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1374
|
+
|
|
1375
|
+
async function createGitlabRelease({
|
|
1656
1376
|
config,
|
|
1657
|
-
|
|
1658
|
-
force,
|
|
1659
|
-
currentVersion,
|
|
1660
|
-
newVersion,
|
|
1377
|
+
release,
|
|
1661
1378
|
dryRun
|
|
1662
1379
|
}) {
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1380
|
+
const token = config.tokens.gitlab || config.repo?.token;
|
|
1381
|
+
if (!token && !dryRun) {
|
|
1382
|
+
throw new Error(
|
|
1383
|
+
"No GitLab token found. Set GITLAB_TOKEN or CI_JOB_TOKEN environment variable or configure tokens.gitlab"
|
|
1384
|
+
);
|
|
1666
1385
|
}
|
|
1667
|
-
const
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1386
|
+
const repoConfig = config.repo?.repo;
|
|
1387
|
+
if (!repoConfig) {
|
|
1388
|
+
throw new Error("No repository URL found in config");
|
|
1389
|
+
}
|
|
1390
|
+
logger.debug(`Parsed repository URL: ${repoConfig}`);
|
|
1391
|
+
const projectPath = encodeURIComponent(repoConfig);
|
|
1392
|
+
const gitlabDomain = config.repo?.domain || "gitlab.com";
|
|
1393
|
+
const apiUrl = `https://${gitlabDomain}/api/v4/projects/${projectPath}/releases`;
|
|
1394
|
+
logger.info(`Creating GitLab release at: ${apiUrl}`);
|
|
1395
|
+
const payload = {
|
|
1396
|
+
tag_name: release.tag_name,
|
|
1397
|
+
name: release.name || release.tag_name,
|
|
1398
|
+
description: release.description || "",
|
|
1399
|
+
ref: release.ref || "main"
|
|
1400
|
+
};
|
|
1401
|
+
try {
|
|
1402
|
+
if (dryRun) {
|
|
1403
|
+
logger.info("[dry-run] GitLab release:", formatJson(payload));
|
|
1404
|
+
return {
|
|
1405
|
+
tag_name: release.tag_name,
|
|
1406
|
+
name: release.name || release.tag_name,
|
|
1407
|
+
description: release.description || "",
|
|
1408
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1409
|
+
released_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1410
|
+
_links: {
|
|
1411
|
+
self: `${apiUrl}/${encodeURIComponent(release.tag_name)}`
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1681
1414
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1415
|
+
logger.debug(`POST GitLab release to ${apiUrl} with payload: ${formatJson(payload)}`);
|
|
1416
|
+
const response = await fetch(apiUrl, {
|
|
1417
|
+
method: "POST",
|
|
1418
|
+
headers: {
|
|
1419
|
+
"Content-Type": "application/json",
|
|
1420
|
+
"PRIVATE-TOKEN": token || ""
|
|
1421
|
+
},
|
|
1422
|
+
body: JSON.stringify(payload)
|
|
1423
|
+
});
|
|
1424
|
+
if (!response.ok) {
|
|
1425
|
+
const errorText = await response.text();
|
|
1426
|
+
throw new Error(`GitLab API error (${response.status}): ${errorText}`);
|
|
1686
1427
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1428
|
+
const result = await response.json();
|
|
1429
|
+
logger.debug(`Created GitLab release: ${result._links.self}`);
|
|
1430
|
+
return result;
|
|
1431
|
+
} catch (error) {
|
|
1432
|
+
logger.error("Failed to create GitLab release:", error);
|
|
1433
|
+
throw error;
|
|
1690
1434
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1435
|
+
}
|
|
1436
|
+
async function gitlabIndependentMode({
|
|
1437
|
+
config,
|
|
1438
|
+
dryRun,
|
|
1439
|
+
bumpResult,
|
|
1440
|
+
suffix,
|
|
1441
|
+
force
|
|
1442
|
+
}) {
|
|
1443
|
+
logger.debug(`GitLab token: ${config.tokens.gitlab || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
|
|
1444
|
+
const packages = await getPackagesOrBumpedPackages({
|
|
1445
|
+
config,
|
|
1446
|
+
bumpResult,
|
|
1447
|
+
suffix,
|
|
1448
|
+
force
|
|
1449
|
+
});
|
|
1450
|
+
logger.info(`Creating ${packages.length} GitLab release(s) for independent packages`);
|
|
1451
|
+
logger.debug("Getting current branch...");
|
|
1452
|
+
const { stdout: currentBranch } = await execPromise("git rev-parse --abbrev-ref HEAD", {
|
|
1453
|
+
noSuccess: true,
|
|
1454
|
+
noStdout: true,
|
|
1455
|
+
logLevel: config.logLevel,
|
|
1456
|
+
cwd: config.cwd
|
|
1457
|
+
});
|
|
1458
|
+
const postedReleases = [];
|
|
1459
|
+
for (const pkg of packages) {
|
|
1460
|
+
const newVersion = isBumpedPackage(pkg) && pkg.newVersion || pkg.version;
|
|
1461
|
+
const from = config.from || pkg.fromTag;
|
|
1462
|
+
const to = getIndependentTag({ version: newVersion, name: pkg.name });
|
|
1463
|
+
if (!from) {
|
|
1464
|
+
logger.warn(`No from tag found for ${pkg.name}, skipping release`);
|
|
1465
|
+
continue;
|
|
1466
|
+
}
|
|
1467
|
+
logger.debug(`Processing ${pkg.name}: ${from} \u2192 ${to}`);
|
|
1468
|
+
const changelog = await generateChangelog({
|
|
1469
|
+
pkg,
|
|
1470
|
+
config,
|
|
1471
|
+
dryRun,
|
|
1472
|
+
newVersion
|
|
1695
1473
|
});
|
|
1696
|
-
if (!
|
|
1697
|
-
logger.
|
|
1698
|
-
|
|
1699
|
-
process.exit(0);
|
|
1474
|
+
if (!changelog) {
|
|
1475
|
+
logger.warn(`No changelog found for ${pkg.name}`);
|
|
1476
|
+
continue;
|
|
1700
1477
|
}
|
|
1701
|
-
|
|
1702
|
-
const
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1478
|
+
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
1479
|
+
const release = {
|
|
1480
|
+
tag_name: to,
|
|
1481
|
+
name: to,
|
|
1482
|
+
description: releaseBody,
|
|
1483
|
+
ref: currentBranch.trim()
|
|
1484
|
+
};
|
|
1485
|
+
logger.debug(`Creating release for ${to} (ref: ${release.ref})`);
|
|
1486
|
+
if (dryRun) {
|
|
1487
|
+
logger.info(`[dry-run] Publish GitLab release for ${to}`);
|
|
1488
|
+
} else {
|
|
1489
|
+
logger.debug(`Publishing release ${to} to GitLab...`);
|
|
1490
|
+
await createGitlabRelease({
|
|
1491
|
+
config,
|
|
1492
|
+
release,
|
|
1493
|
+
dryRun
|
|
1494
|
+
});
|
|
1495
|
+
postedReleases.push({
|
|
1496
|
+
name: pkg.name,
|
|
1497
|
+
tag: release.tag_name,
|
|
1498
|
+
version: newVersion,
|
|
1499
|
+
prerelease: isPrerelease(newVersion)
|
|
1500
|
+
});
|
|
1707
1501
|
}
|
|
1708
|
-
logger.fail("Error while confirming bump");
|
|
1709
|
-
process.exit(1);
|
|
1710
1502
|
}
|
|
1711
|
-
|
|
1503
|
+
if (postedReleases.length === 0) {
|
|
1504
|
+
logger.warn("No releases created");
|
|
1505
|
+
} else {
|
|
1506
|
+
logger.success(`Releases ${postedReleases.map((r) => r.tag).join(", ")} published to GitLab!`);
|
|
1507
|
+
}
|
|
1508
|
+
return postedReleases;
|
|
1712
1509
|
}
|
|
1713
|
-
function
|
|
1714
|
-
|
|
1715
|
-
dryRun
|
|
1510
|
+
async function gitlabUnified({
|
|
1511
|
+
config,
|
|
1512
|
+
dryRun,
|
|
1513
|
+
rootPackage,
|
|
1514
|
+
bumpResult
|
|
1716
1515
|
}) {
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1516
|
+
logger.debug(`GitLab token: ${config.tokens.gitlab || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
|
|
1517
|
+
const newVersion = bumpResult?.newVersion || rootPackage.newVersion || rootPackage.version;
|
|
1518
|
+
const to = config.templates.tagBody.replace("{{newVersion}}", newVersion);
|
|
1519
|
+
const changelog = await generateChangelog({
|
|
1520
|
+
pkg: rootPackage,
|
|
1521
|
+
config,
|
|
1522
|
+
dryRun,
|
|
1523
|
+
newVersion
|
|
1524
|
+
});
|
|
1525
|
+
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
1526
|
+
logger.debug("Getting current branch...");
|
|
1527
|
+
const { stdout: currentBranch } = await execPromise("git rev-parse --abbrev-ref HEAD", {
|
|
1528
|
+
noSuccess: true,
|
|
1529
|
+
noStdout: true,
|
|
1530
|
+
logLevel: config.logLevel,
|
|
1531
|
+
cwd: config.cwd
|
|
1532
|
+
});
|
|
1533
|
+
const release = {
|
|
1534
|
+
tag_name: to,
|
|
1535
|
+
name: to,
|
|
1536
|
+
description: releaseBody,
|
|
1537
|
+
ref: currentBranch.trim()
|
|
1538
|
+
};
|
|
1539
|
+
logger.info(`Creating release for ${to} (ref: ${release.ref})`);
|
|
1540
|
+
logger.debug("Release details:", formatJson({
|
|
1541
|
+
tag_name: release.tag_name,
|
|
1542
|
+
name: release.name,
|
|
1543
|
+
ref: release.ref
|
|
1544
|
+
}));
|
|
1545
|
+
if (dryRun) {
|
|
1546
|
+
logger.info("[dry-run] Publish GitLab release for", release.tag_name);
|
|
1547
|
+
} else {
|
|
1548
|
+
logger.debug("Publishing release to GitLab...");
|
|
1549
|
+
await createGitlabRelease({
|
|
1550
|
+
config,
|
|
1551
|
+
release,
|
|
1722
1552
|
dryRun
|
|
1723
1553
|
});
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1554
|
+
}
|
|
1555
|
+
logger.success(`Release ${to} published to GitLab!`);
|
|
1556
|
+
return [{
|
|
1557
|
+
name: to,
|
|
1558
|
+
tag: to,
|
|
1559
|
+
version: to,
|
|
1560
|
+
prerelease: isPrerelease(newVersion)
|
|
1561
|
+
}];
|
|
1562
|
+
}
|
|
1563
|
+
async function gitlab(options = {}) {
|
|
1564
|
+
try {
|
|
1565
|
+
const dryRun = options.dryRun ?? false;
|
|
1566
|
+
logger.debug(`Dry run: ${dryRun}`);
|
|
1567
|
+
const config = await loadRelizyConfig({
|
|
1568
|
+
configName: options.configName,
|
|
1569
|
+
baseConfig: options.config,
|
|
1570
|
+
overrides: {
|
|
1571
|
+
from: options.from,
|
|
1572
|
+
to: options.to,
|
|
1573
|
+
logLevel: options.logLevel,
|
|
1574
|
+
tokens: {
|
|
1575
|
+
gitlab: options.token
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
});
|
|
1579
|
+
if (config.monorepo?.versionMode === "independent") {
|
|
1580
|
+
return await gitlabIndependentMode({
|
|
1581
|
+
config,
|
|
1582
|
+
dryRun,
|
|
1583
|
+
bumpResult: options.bumpResult,
|
|
1584
|
+
suffix: options.suffix,
|
|
1585
|
+
force: options.force ?? false
|
|
1728
1586
|
});
|
|
1729
1587
|
}
|
|
1588
|
+
const rootPackageBase = readPackageJson(config.cwd);
|
|
1589
|
+
if (!rootPackageBase) {
|
|
1590
|
+
throw new Error("Failed to read root package.json");
|
|
1591
|
+
}
|
|
1592
|
+
const newVersion = options.bumpResult?.newVersion || rootPackageBase.version;
|
|
1593
|
+
const { from, to } = await resolveTags({
|
|
1594
|
+
config,
|
|
1595
|
+
step: "provider-release",
|
|
1596
|
+
newVersion,
|
|
1597
|
+
pkg: rootPackageBase
|
|
1598
|
+
});
|
|
1599
|
+
const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
|
|
1600
|
+
config,
|
|
1601
|
+
force: options.force ?? false,
|
|
1602
|
+
suffix: options.suffix,
|
|
1603
|
+
changelog: true,
|
|
1604
|
+
from,
|
|
1605
|
+
to
|
|
1606
|
+
});
|
|
1607
|
+
logger.debug(`Root package: ${getIndependentTag({ name: rootPackage.name, version: newVersion })}`);
|
|
1608
|
+
return await gitlabUnified({
|
|
1609
|
+
config,
|
|
1610
|
+
dryRun,
|
|
1611
|
+
rootPackage,
|
|
1612
|
+
bumpResult: options.bumpResult
|
|
1613
|
+
});
|
|
1614
|
+
} catch (error) {
|
|
1615
|
+
logger.error("Error publishing GitLab release:", error);
|
|
1616
|
+
throw error;
|
|
1730
1617
|
}
|
|
1731
|
-
return bumpedPackages;
|
|
1732
1618
|
}
|
|
1733
1619
|
|
|
1734
|
-
function
|
|
1735
|
-
|
|
1620
|
+
function isGraduatingToStableBetweenVersion(version, newVersion) {
|
|
1621
|
+
const isSameBase = semver.major(version) === semver.major(newVersion) && semver.minor(version) === semver.minor(newVersion) && semver.patch(version) === semver.patch(newVersion);
|
|
1622
|
+
const fromPrerelease = semver.prerelease(version) !== null;
|
|
1623
|
+
const toStable = semver.prerelease(newVersion) === null;
|
|
1624
|
+
return isSameBase && fromPrerelease && toStable;
|
|
1736
1625
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
noStdout: true,
|
|
1744
|
-
noSuccess: true,
|
|
1745
|
-
cwd
|
|
1626
|
+
function determineSemverChange(commits, types) {
|
|
1627
|
+
let [hasMajor, hasMinor, hasPatch] = [false, false, false];
|
|
1628
|
+
for (const commit of commits) {
|
|
1629
|
+
const commitType = types[commit.type];
|
|
1630
|
+
if (!commitType) {
|
|
1631
|
+
continue;
|
|
1746
1632
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1633
|
+
const semverType = commitType.semver;
|
|
1634
|
+
if (semverType === "major" || commit.isBreaking) {
|
|
1635
|
+
hasMajor = true;
|
|
1636
|
+
} else if (semverType === "minor") {
|
|
1637
|
+
hasMinor = true;
|
|
1638
|
+
} else if (semverType === "patch") {
|
|
1639
|
+
hasPatch = true;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
return hasMajor ? "major" : hasMinor ? "minor" : hasPatch ? "patch" : void 0;
|
|
1751
1643
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
logLevel,
|
|
1755
|
-
noStderr: true,
|
|
1756
|
-
noStdout: true,
|
|
1757
|
-
noSuccess: true,
|
|
1758
|
-
cwd
|
|
1759
|
-
});
|
|
1760
|
-
const lastTag = stdout.trim();
|
|
1761
|
-
logger.debug("Last tag:", lastTag || "No tags found");
|
|
1762
|
-
return lastTag;
|
|
1644
|
+
function detectReleaseTypeFromCommits(commits, types) {
|
|
1645
|
+
return determineSemverChange(commits, types);
|
|
1763
1646
|
}
|
|
1764
|
-
function
|
|
1765
|
-
if (
|
|
1766
|
-
return
|
|
1647
|
+
function validatePrereleaseDowngrade(currentVersion, targetPreid, configuredType) {
|
|
1648
|
+
if (configuredType !== "prerelease" || !targetPreid || !isPrerelease(currentVersion)) {
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
const testVersion = semver.inc(currentVersion, "prerelease", targetPreid);
|
|
1652
|
+
const isNotUpgrade = testVersion && !semver.gt(testVersion, currentVersion);
|
|
1653
|
+
if (isNotUpgrade) {
|
|
1654
|
+
throw new Error(`Unable to graduate from ${currentVersion} to ${testVersion}, it's not a valid prerelease`);
|
|
1767
1655
|
}
|
|
1768
|
-
return getLastTag({ logLevel: options?.logLevel, cwd: options?.cwd });
|
|
1769
1656
|
}
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
}
|
|
1793
|
-
);
|
|
1794
|
-
const tag = stdout.trim();
|
|
1795
|
-
return tag || null;
|
|
1796
|
-
} catch {
|
|
1797
|
-
return null;
|
|
1657
|
+
function handleStableVersionWithReleaseType(commits, types, force) {
|
|
1658
|
+
if (!commits?.length && !force) {
|
|
1659
|
+
logger.debug('No commits found for stable version with "release" type, skipping bump');
|
|
1660
|
+
return void 0;
|
|
1661
|
+
}
|
|
1662
|
+
const detectedType = commits?.length ? detectReleaseTypeFromCommits(commits, types) : void 0;
|
|
1663
|
+
if (!detectedType && !force) {
|
|
1664
|
+
logger.debug("No significant commits found, skipping bump");
|
|
1665
|
+
return void 0;
|
|
1666
|
+
}
|
|
1667
|
+
logger.debug(`Auto-detected release type from commits: ${detectedType}`);
|
|
1668
|
+
return detectedType;
|
|
1669
|
+
}
|
|
1670
|
+
function handleStableVersionWithPrereleaseType(commits, types, force) {
|
|
1671
|
+
if (!commits?.length && !force) {
|
|
1672
|
+
logger.debug('No commits found for stable version with "prerelease" type, skipping bump');
|
|
1673
|
+
return void 0;
|
|
1674
|
+
}
|
|
1675
|
+
const detectedType = commits?.length ? detectReleaseTypeFromCommits(commits, types) : void 0;
|
|
1676
|
+
if (!detectedType) {
|
|
1677
|
+
logger.debug("No significant commits found, using prepatch as default");
|
|
1678
|
+
return "prepatch";
|
|
1798
1679
|
}
|
|
1680
|
+
const prereleaseType = `pre${detectedType}`;
|
|
1681
|
+
logger.debug(`Auto-detected prerelease type from commits: ${prereleaseType}`);
|
|
1682
|
+
return prereleaseType;
|
|
1799
1683
|
}
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
graduating,
|
|
1804
|
-
logLevel
|
|
1805
|
-
}) {
|
|
1806
|
-
const lastPackageTag = await getLastPackageTag({
|
|
1807
|
-
packageName,
|
|
1808
|
-
onlyStable: graduating,
|
|
1809
|
-
logLevel
|
|
1810
|
-
});
|
|
1811
|
-
if (!lastPackageTag) {
|
|
1812
|
-
return getFirstCommit(cwd);
|
|
1813
|
-
}
|
|
1814
|
-
return lastPackageTag;
|
|
1684
|
+
function handlePrereleaseVersionToStable(currentVersion) {
|
|
1685
|
+
logger.debug(`Graduating from prerelease ${currentVersion} to stable release`);
|
|
1686
|
+
return "release";
|
|
1815
1687
|
}
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1688
|
+
function handlePrereleaseVersionWithPrereleaseType({ currentVersion, preid, commits, force }) {
|
|
1689
|
+
const currentPreid = getPreid(currentVersion);
|
|
1690
|
+
const hasChangedPreid = preid && currentPreid && currentPreid !== preid;
|
|
1691
|
+
if (hasChangedPreid) {
|
|
1692
|
+
const testVersion = semver.inc(currentVersion, "prerelease", preid);
|
|
1693
|
+
if (!testVersion) {
|
|
1694
|
+
throw new Error(`Unable to change preid from ${currentPreid} to ${preid} for version ${currentVersion}`);
|
|
1695
|
+
}
|
|
1696
|
+
const isUpgrade = semver.gt(testVersion, currentVersion);
|
|
1697
|
+
if (!isUpgrade) {
|
|
1698
|
+
throw new Error(`Unable to change preid from ${currentVersion} to ${testVersion}, it's not a valid upgrade (cannot downgrade from ${currentPreid} to ${preid})`);
|
|
1699
|
+
}
|
|
1700
|
+
return "prerelease";
|
|
1701
|
+
}
|
|
1702
|
+
if (!commits?.length && !force) {
|
|
1703
|
+
logger.debug("No commits found for prerelease version, skipping bump");
|
|
1704
|
+
return void 0;
|
|
1705
|
+
}
|
|
1706
|
+
logger.debug(`Incrementing prerelease version: ${currentVersion}`);
|
|
1707
|
+
return "prerelease";
|
|
1823
1708
|
}
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
step,
|
|
1828
|
-
packageName,
|
|
1829
|
-
graduating,
|
|
1830
|
-
logLevel
|
|
1709
|
+
function handleExplicitReleaseType({
|
|
1710
|
+
releaseType,
|
|
1711
|
+
currentVersion
|
|
1831
1712
|
}) {
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
}
|
|
1837
|
-
from = await resolveFromTagIndependent({
|
|
1838
|
-
cwd: config.cwd,
|
|
1839
|
-
packageName,
|
|
1840
|
-
graduating,
|
|
1841
|
-
logLevel
|
|
1842
|
-
});
|
|
1713
|
+
const isCurrentPrerelease = isPrerelease(currentVersion);
|
|
1714
|
+
const isGraduatingToStable = isCurrentPrerelease && isStableReleaseType(releaseType);
|
|
1715
|
+
if (isGraduatingToStable) {
|
|
1716
|
+
logger.debug(`Graduating from prerelease ${currentVersion} to stable with type: ${releaseType}`);
|
|
1843
1717
|
} else {
|
|
1844
|
-
|
|
1845
|
-
config,
|
|
1846
|
-
graduating,
|
|
1847
|
-
logLevel
|
|
1848
|
-
});
|
|
1718
|
+
logger.debug(`Using explicit release type: ${releaseType}`);
|
|
1849
1719
|
}
|
|
1850
|
-
|
|
1851
|
-
return config.from || from;
|
|
1720
|
+
return releaseType;
|
|
1852
1721
|
}
|
|
1853
|
-
function
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1722
|
+
function determineReleaseType({
|
|
1723
|
+
currentVersion,
|
|
1724
|
+
commits,
|
|
1725
|
+
releaseType,
|
|
1726
|
+
preid,
|
|
1727
|
+
types,
|
|
1728
|
+
force
|
|
1859
1729
|
}) {
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1730
|
+
if (releaseType === "release" && preid) {
|
|
1731
|
+
throw new Error('You cannot use a "release" type with a "preid", to use a preid you must use a "prerelease" type');
|
|
1732
|
+
}
|
|
1733
|
+
validatePrereleaseDowngrade(currentVersion, preid, releaseType);
|
|
1734
|
+
if (force) {
|
|
1735
|
+
logger.debug(`Force flag enabled, using configured type: ${releaseType}`);
|
|
1736
|
+
return releaseType;
|
|
1737
|
+
}
|
|
1738
|
+
const isCurrentPrerelease = isPrerelease(currentVersion);
|
|
1739
|
+
if (!isCurrentPrerelease) {
|
|
1740
|
+
if (releaseType === "release") {
|
|
1741
|
+
return handleStableVersionWithReleaseType(commits, types, force);
|
|
1867
1742
|
}
|
|
1868
|
-
if (
|
|
1869
|
-
|
|
1743
|
+
if (releaseType === "prerelease") {
|
|
1744
|
+
return handleStableVersionWithPrereleaseType(commits, types, force);
|
|
1870
1745
|
}
|
|
1871
|
-
|
|
1872
|
-
} else {
|
|
1873
|
-
to = newVersion ? config.templates.tagBody.replace("{{newVersion}}", newVersion) : getCurrentGitRef(config.cwd);
|
|
1746
|
+
return handleExplicitReleaseType({ releaseType, currentVersion });
|
|
1874
1747
|
}
|
|
1875
|
-
|
|
1876
|
-
|
|
1748
|
+
if (releaseType === "release") {
|
|
1749
|
+
return handlePrereleaseVersionToStable(currentVersion);
|
|
1750
|
+
}
|
|
1751
|
+
if (releaseType === "prerelease") {
|
|
1752
|
+
return handlePrereleaseVersionWithPrereleaseType({ currentVersion, preid, commits, force });
|
|
1753
|
+
}
|
|
1754
|
+
return handleExplicitReleaseType({ releaseType, currentVersion });
|
|
1877
1755
|
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
}
|
|
1896
|
-
const to = resolveToTag({
|
|
1897
|
-
config,
|
|
1898
|
-
versionMode,
|
|
1899
|
-
newVersion,
|
|
1900
|
-
step,
|
|
1901
|
-
packageName: pkg.name
|
|
1902
|
-
});
|
|
1903
|
-
logger.debug(`[${versionMode}](${step}) Using tags: ${from} \u2192 ${to}`);
|
|
1904
|
-
return { from, to };
|
|
1756
|
+
function writeVersion(pkgPath, newVersion, dryRun = false) {
|
|
1757
|
+
const packageJsonPath = join(pkgPath, "package.json");
|
|
1758
|
+
try {
|
|
1759
|
+
logger.debug(`Writing ${newVersion} to ${pkgPath}`);
|
|
1760
|
+
const content = readFileSync(packageJsonPath, "utf8");
|
|
1761
|
+
const packageJson = JSON.parse(content);
|
|
1762
|
+
const oldVersion = packageJson.version;
|
|
1763
|
+
packageJson.version = newVersion;
|
|
1764
|
+
if (dryRun) {
|
|
1765
|
+
logger.info(`[dry-run] Updated ${packageJson.name}: ${oldVersion} \u2192 ${newVersion}`);
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
writeFileSync(packageJsonPath, `${formatJson(packageJson)}
|
|
1769
|
+
`, "utf8");
|
|
1770
|
+
logger.info(`Updated ${packageJson.name}: ${oldVersion} \u2192 ${newVersion}`);
|
|
1771
|
+
} catch (error) {
|
|
1772
|
+
throw new Error(`Unable to write version to ${packageJsonPath}: ${error}`);
|
|
1773
|
+
}
|
|
1905
1774
|
}
|
|
1775
|
+
function getPackageNewVersion({
|
|
1776
|
+
currentVersion,
|
|
1777
|
+
releaseType,
|
|
1778
|
+
preid,
|
|
1779
|
+
suffix
|
|
1780
|
+
}) {
|
|
1781
|
+
let newVersion = semver.inc(currentVersion, releaseType, preid);
|
|
1782
|
+
if (!newVersion) {
|
|
1783
|
+
throw new Error(`Unable to bump version "${currentVersion}" with release type "${releaseType}"
|
|
1906
1784
|
|
|
1907
|
-
|
|
1908
|
-
|
|
1785
|
+
You should use an explicit release type (use flag: --major, --minor, --patch, --premajor, --preminor, --prepatch, --prerelease)`);
|
|
1786
|
+
}
|
|
1787
|
+
if (isPrereleaseReleaseType(releaseType) && suffix) {
|
|
1788
|
+
newVersion = newVersion.replace(/\.(\d+)$/, `.${suffix}`);
|
|
1789
|
+
}
|
|
1790
|
+
const isValidVersion = semver.gt(newVersion, currentVersion);
|
|
1791
|
+
if (!isValidVersion) {
|
|
1792
|
+
throw new Error(`Unable to bump version "${currentVersion}" to "${newVersion}", new version is not greater than current version`);
|
|
1793
|
+
}
|
|
1794
|
+
if (isGraduating(currentVersion, releaseType)) {
|
|
1795
|
+
logger.info(`Graduating from prerelease ${currentVersion} to stable ${newVersion}`);
|
|
1796
|
+
}
|
|
1797
|
+
if (isChangedPreid(currentVersion, preid)) {
|
|
1798
|
+
logger.debug(`Graduating from ${getPreid(currentVersion)} to ${preid}`);
|
|
1799
|
+
}
|
|
1800
|
+
return newVersion;
|
|
1801
|
+
}
|
|
1802
|
+
function updateLernaVersion({
|
|
1803
|
+
rootDir,
|
|
1804
|
+
versionMode,
|
|
1805
|
+
version,
|
|
1806
|
+
dryRun = false
|
|
1807
|
+
}) {
|
|
1808
|
+
const lernaJsonExists = hasLernaJson(rootDir);
|
|
1809
|
+
if (!lernaJsonExists) {
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1812
|
+
const lernaJsonPath = join(rootDir, "lerna.json");
|
|
1813
|
+
if (!existsSync(lernaJsonPath)) {
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1909
1816
|
try {
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
if (["npm", "pnpm", "yarn", "bun"].includes(pmName)) {
|
|
1918
|
-
logger.debug(`Detected package manager from package.json: ${pmName}`);
|
|
1919
|
-
return pmName;
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
} catch (e) {
|
|
1923
|
-
const errorString = e instanceof Error ? e.message : String(e);
|
|
1924
|
-
logger.debug(`Failed to parse package.json: ${errorString}`);
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
const lockFiles = {
|
|
1928
|
-
pnpm: "pnpm-lock.yaml",
|
|
1929
|
-
yarn: "yarn.lock",
|
|
1930
|
-
npm: "package-lock.json",
|
|
1931
|
-
bun: "bun.lockb"
|
|
1932
|
-
};
|
|
1933
|
-
for (const [manager, file] of Object.entries(lockFiles)) {
|
|
1934
|
-
if (existsSync(join(cwd, file))) {
|
|
1935
|
-
logger.debug(`Detected package manager from lockfile: ${manager}`);
|
|
1936
|
-
return manager;
|
|
1937
|
-
}
|
|
1817
|
+
logger.debug("Updating lerna.json version");
|
|
1818
|
+
const content = readFileSync(lernaJsonPath, "utf8");
|
|
1819
|
+
const lernaJson = JSON.parse(content);
|
|
1820
|
+
const oldVersion = lernaJson.version;
|
|
1821
|
+
if (lernaJson.version === "independent" || versionMode === "independent") {
|
|
1822
|
+
logger.debug("Lerna version is independent or version mode is independent, skipping update");
|
|
1823
|
+
return;
|
|
1938
1824
|
}
|
|
1939
|
-
|
|
1940
|
-
if (
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
logger.debug(`Detected package manager from user agent: ${match[1]}`);
|
|
1944
|
-
return match[1];
|
|
1945
|
-
}
|
|
1825
|
+
lernaJson.version = version;
|
|
1826
|
+
if (dryRun) {
|
|
1827
|
+
logger.info(`[dry-run] update lerna.json: ${oldVersion} \u2192 ${version}`);
|
|
1828
|
+
return;
|
|
1946
1829
|
}
|
|
1947
|
-
|
|
1948
|
-
|
|
1830
|
+
writeFileSync(lernaJsonPath, `${formatJson(lernaJson)}
|
|
1831
|
+
`, "utf8");
|
|
1832
|
+
logger.success(`Updated lerna.json: ${oldVersion} \u2192 ${version}`);
|
|
1949
1833
|
} catch (error) {
|
|
1950
|
-
logger.fail(`
|
|
1951
|
-
return "npm";
|
|
1834
|
+
logger.fail(`Unable to update lerna.json: ${error}`);
|
|
1952
1835
|
}
|
|
1953
1836
|
}
|
|
1954
|
-
function
|
|
1955
|
-
|
|
1956
|
-
if (
|
|
1957
|
-
|
|
1958
|
-
}
|
|
1959
|
-
if (isPrerelease(version) && !configTag) {
|
|
1960
|
-
logger.warn('You are about to publish a "prerelease" version with the "latest" tag. To avoid mistake, the tag is set to "next"');
|
|
1961
|
-
tag = "next";
|
|
1962
|
-
}
|
|
1963
|
-
if (isPrerelease(version) && configTag === "latest") {
|
|
1964
|
-
logger.warn('Please note, you are about to publish a "prerelease" version with the "latest" tag.');
|
|
1837
|
+
function extractVersionFromPackageTag(tag) {
|
|
1838
|
+
const atIndex = tag.lastIndexOf("@");
|
|
1839
|
+
if (atIndex === -1) {
|
|
1840
|
+
return null;
|
|
1965
1841
|
}
|
|
1966
|
-
return tag;
|
|
1842
|
+
return tag.slice(atIndex + 1);
|
|
1967
1843
|
}
|
|
1968
|
-
function
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
if (pkgJson.version === rootVersion) {
|
|
1974
|
-
packagesToPublish.push(pkg);
|
|
1975
|
-
}
|
|
1976
|
-
}
|
|
1977
|
-
return packagesToPublish;
|
|
1844
|
+
function isPrerelease(version) {
|
|
1845
|
+
if (!version)
|
|
1846
|
+
return false;
|
|
1847
|
+
const prerelease = semver.prerelease(version);
|
|
1848
|
+
return prerelease ? prerelease.length > 0 : false;
|
|
1978
1849
|
}
|
|
1979
|
-
|
|
1980
|
-
const
|
|
1981
|
-
|
|
1982
|
-
const { from, to } = await resolveTags({
|
|
1983
|
-
config,
|
|
1984
|
-
step: "publish",
|
|
1985
|
-
pkg,
|
|
1986
|
-
newVersion: pkg.version
|
|
1987
|
-
});
|
|
1988
|
-
if (pkg.commits.length > 0) {
|
|
1989
|
-
packagesToPublish.push(pkg);
|
|
1990
|
-
logger.debug(`${pkg.name}: ${pkg.commits.length} commit(s) since ${from} \u2192 ${to}`);
|
|
1991
|
-
}
|
|
1992
|
-
}
|
|
1993
|
-
return packagesToPublish;
|
|
1850
|
+
function isStableReleaseType(releaseType) {
|
|
1851
|
+
const stableTypes = ["release", "major", "minor", "patch"];
|
|
1852
|
+
return stableTypes.includes(releaseType);
|
|
1994
1853
|
}
|
|
1995
|
-
function
|
|
1996
|
-
|
|
1854
|
+
function isPrereleaseReleaseType(releaseType) {
|
|
1855
|
+
const prereleaseTypes = ["prerelease", "premajor", "preminor", "prepatch"];
|
|
1856
|
+
return prereleaseTypes.includes(releaseType);
|
|
1997
1857
|
}
|
|
1998
|
-
function
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
const
|
|
2005
|
-
if (
|
|
2006
|
-
|
|
2007
|
-
} else if (packageManager === "yarn") {
|
|
2008
|
-
args.push("--non-interactive");
|
|
2009
|
-
if (isYarnBerry())
|
|
2010
|
-
args.push("--no-git-checks");
|
|
2011
|
-
} else if (packageManager === "npm") {
|
|
2012
|
-
args.push("--yes");
|
|
1858
|
+
function isGraduating(currentVersion, releaseType) {
|
|
1859
|
+
return isPrerelease(currentVersion) && isStableReleaseType(releaseType);
|
|
1860
|
+
}
|
|
1861
|
+
function getPreid(version) {
|
|
1862
|
+
if (!version)
|
|
1863
|
+
return null;
|
|
1864
|
+
const prerelease = semver.prerelease(version);
|
|
1865
|
+
if (!prerelease || prerelease.length === 0) {
|
|
1866
|
+
return null;
|
|
2013
1867
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
1868
|
+
return prerelease[0];
|
|
1869
|
+
}
|
|
1870
|
+
function isChangedPreid(currentVersion, targetPreid) {
|
|
1871
|
+
if (!targetPreid || !isPrerelease(currentVersion)) {
|
|
1872
|
+
return false;
|
|
2017
1873
|
}
|
|
2018
|
-
const
|
|
2019
|
-
if (
|
|
2020
|
-
|
|
1874
|
+
const currentPreid = getPreid(currentVersion);
|
|
1875
|
+
if (!currentPreid) {
|
|
1876
|
+
return false;
|
|
2021
1877
|
}
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
1878
|
+
return currentPreid !== targetPreid;
|
|
1879
|
+
}
|
|
1880
|
+
function getBumpedPackageIndependently({
|
|
1881
|
+
pkg,
|
|
1882
|
+
dryRun
|
|
1883
|
+
}) {
|
|
1884
|
+
logger.debug(`Analyzing ${pkg.name}`);
|
|
1885
|
+
const currentVersion = pkg.version || "0.0.0";
|
|
1886
|
+
const newVersion = pkg.newVersion;
|
|
1887
|
+
if (!newVersion) {
|
|
1888
|
+
return { bumped: false };
|
|
2025
1889
|
}
|
|
2026
|
-
|
|
1890
|
+
logger.debug(`Bumping ${pkg.name} from ${currentVersion} to ${newVersion}`);
|
|
1891
|
+
writeVersion(pkg.path, newVersion, dryRun);
|
|
1892
|
+
return { bumped: true, newVersion, oldVersion: currentVersion };
|
|
2027
1893
|
}
|
|
2028
|
-
function
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
1894
|
+
function displayRootAndLernaUpdates({
|
|
1895
|
+
versionMode,
|
|
1896
|
+
currentVersion,
|
|
1897
|
+
newVersion,
|
|
1898
|
+
dryRun,
|
|
1899
|
+
lernaJsonExists
|
|
1900
|
+
}) {
|
|
1901
|
+
if (versionMode !== "independent" && currentVersion && newVersion) {
|
|
1902
|
+
logger.log(`${dryRun ? "[dry-run] " : ""}Root package.json: ${currentVersion} \u2192 ${newVersion}`);
|
|
1903
|
+
logger.log("");
|
|
1904
|
+
if (lernaJsonExists) {
|
|
1905
|
+
logger.log(`${dryRun ? "[dry-run] " : ""}lerna.json: ${currentVersion} \u2192 ${newVersion}`);
|
|
1906
|
+
logger.log("");
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
2033
1909
|
}
|
|
2034
|
-
function
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
}
|
|
2042
|
-
clearTimeout(timer);
|
|
2043
|
-
resolve(otp);
|
|
2044
|
-
}).catch((error) => {
|
|
2045
|
-
clearTimeout(timer);
|
|
2046
|
-
reject(error);
|
|
2047
|
-
});
|
|
1910
|
+
function displayUnifiedModePackages({
|
|
1911
|
+
packages,
|
|
1912
|
+
newVersion,
|
|
1913
|
+
force
|
|
1914
|
+
}) {
|
|
1915
|
+
logger.log(`${packages.length} package(s):`);
|
|
1916
|
+
packages.forEach((pkg) => {
|
|
1917
|
+
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} ${force ? "(force)" : ""}`);
|
|
2048
1918
|
});
|
|
1919
|
+
logger.log("");
|
|
2049
1920
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
1921
|
+
function displaySelectiveModePackages({
|
|
1922
|
+
packages,
|
|
1923
|
+
newVersion,
|
|
1924
|
+
force
|
|
1925
|
+
}) {
|
|
1926
|
+
if (force) {
|
|
1927
|
+
logger.log(`${packages.length} package(s):`);
|
|
1928
|
+
packages.forEach((pkg) => {
|
|
1929
|
+
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} (force)`);
|
|
1930
|
+
});
|
|
1931
|
+
logger.log("");
|
|
1932
|
+
} else {
|
|
1933
|
+
const packagesWithCommits = packages.filter((p) => "reason" in p && p.reason === "commits");
|
|
1934
|
+
const packagesAsDependents = packages.filter((p) => "reason" in p && p.reason === "dependency");
|
|
1935
|
+
const packagesAsGraduation = packages.filter((p) => "reason" in p && p.reason === "graduation");
|
|
1936
|
+
if (packagesWithCommits.length > 0) {
|
|
1937
|
+
logger.log(`${packagesWithCommits.length} package(s) with commits:`);
|
|
1938
|
+
packagesWithCommits.forEach((pkg) => {
|
|
1939
|
+
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} (${pkg.commits.length} commits) ${force ? "(force)" : ""}`);
|
|
1940
|
+
});
|
|
1941
|
+
logger.log("");
|
|
1942
|
+
}
|
|
1943
|
+
if (packagesAsDependents.length > 0) {
|
|
1944
|
+
logger.log(`${packagesAsDependents.length} dependent package(s):`);
|
|
1945
|
+
packagesAsDependents.forEach((pkg) => {
|
|
1946
|
+
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} ${force ? "(force)" : ""}`);
|
|
1947
|
+
});
|
|
1948
|
+
logger.log("");
|
|
1949
|
+
}
|
|
1950
|
+
if (packagesAsGraduation.length > 0) {
|
|
1951
|
+
logger.log(`${packagesAsGraduation.length} graduation package(s):`);
|
|
1952
|
+
packagesAsGraduation.forEach((pkg) => {
|
|
1953
|
+
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} ${force ? "(force)" : ""}`);
|
|
1954
|
+
});
|
|
1955
|
+
logger.log("");
|
|
1956
|
+
}
|
|
2054
1957
|
}
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
logger.
|
|
2062
|
-
|
|
1958
|
+
}
|
|
1959
|
+
function displayIndependentModePackages({
|
|
1960
|
+
packages,
|
|
1961
|
+
force
|
|
1962
|
+
}) {
|
|
1963
|
+
if (force) {
|
|
1964
|
+
logger.log(`${packages.length} package(s):`);
|
|
1965
|
+
packages.forEach((pkg) => {
|
|
1966
|
+
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} (force)`);
|
|
1967
|
+
});
|
|
1968
|
+
logger.log("");
|
|
1969
|
+
} else {
|
|
1970
|
+
const packagesWithCommits = packages.filter((p) => "reason" in p && p.reason === "commits");
|
|
1971
|
+
const packagesAsDependents = packages.filter((p) => "reason" in p && p.reason === "dependency");
|
|
1972
|
+
const packagesAsGraduation = packages.filter((p) => "reason" in p && p.reason === "graduation");
|
|
1973
|
+
if (packagesWithCommits.length > 0) {
|
|
1974
|
+
logger.log(`${packagesWithCommits.length} package(s) with commits:`);
|
|
1975
|
+
packagesWithCommits.forEach((pkg) => {
|
|
1976
|
+
pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} (${pkg.commits.length} commits) ${force ? "(force)" : ""}`);
|
|
1977
|
+
});
|
|
1978
|
+
logger.log("");
|
|
1979
|
+
}
|
|
1980
|
+
if (packagesAsDependents.length > 0) {
|
|
1981
|
+
logger.log(`${packagesAsDependents.length} dependent package(s):`);
|
|
1982
|
+
packagesAsDependents.forEach((pkg) => {
|
|
1983
|
+
pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} ${force ? "(force)" : ""}`);
|
|
1984
|
+
});
|
|
1985
|
+
logger.log("");
|
|
1986
|
+
}
|
|
1987
|
+
if (packagesAsGraduation.length > 0) {
|
|
1988
|
+
logger.log(`${packagesAsGraduation.length} graduation package(s):`);
|
|
1989
|
+
packagesAsGraduation.forEach((pkg) => {
|
|
1990
|
+
pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} ${force ? "(force)" : ""}`);
|
|
1991
|
+
});
|
|
1992
|
+
logger.log("");
|
|
1993
|
+
}
|
|
2063
1994
|
}
|
|
2064
1995
|
}
|
|
2065
|
-
async function
|
|
2066
|
-
|
|
2067
|
-
packageNameAndVersion,
|
|
2068
|
-
pkg,
|
|
1996
|
+
async function confirmBump({
|
|
1997
|
+
versionMode,
|
|
2069
1998
|
config,
|
|
1999
|
+
packages,
|
|
2000
|
+
force,
|
|
2001
|
+
currentVersion,
|
|
2002
|
+
newVersion,
|
|
2070
2003
|
dryRun
|
|
2071
2004
|
}) {
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
logger.info(`[dry-run] ${packageNameAndVersion}: Run ${command}`);
|
|
2005
|
+
if (packages.length === 0) {
|
|
2006
|
+
logger.debug("No packages to bump");
|
|
2075
2007
|
return;
|
|
2076
2008
|
}
|
|
2077
|
-
const
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2009
|
+
const lernaJsonExists = hasLernaJson(config.cwd);
|
|
2010
|
+
logger.log("");
|
|
2011
|
+
logger.info(`${dryRun ? "[dry-run] " : ""}The following packages will be updated:
|
|
2012
|
+
`);
|
|
2013
|
+
displayRootAndLernaUpdates({
|
|
2014
|
+
versionMode,
|
|
2015
|
+
currentVersion,
|
|
2016
|
+
newVersion,
|
|
2017
|
+
lernaJsonExists,
|
|
2018
|
+
dryRun
|
|
2082
2019
|
});
|
|
2083
|
-
if (
|
|
2084
|
-
|
|
2020
|
+
if (versionMode === "unified") {
|
|
2021
|
+
if (!newVersion) {
|
|
2022
|
+
throw new Error("Cannot confirm bump in unified mode without a new version");
|
|
2023
|
+
}
|
|
2024
|
+
displayUnifiedModePackages({ packages, newVersion, force });
|
|
2025
|
+
} else if (versionMode === "selective") {
|
|
2026
|
+
if (!newVersion) {
|
|
2027
|
+
throw new Error("Cannot confirm bump in selective mode without a new version");
|
|
2028
|
+
}
|
|
2029
|
+
displaySelectiveModePackages({ packages, newVersion, force });
|
|
2030
|
+
} else if (versionMode === "independent") {
|
|
2031
|
+
displayIndependentModePackages({ packages, force });
|
|
2085
2032
|
}
|
|
2086
|
-
|
|
2033
|
+
try {
|
|
2034
|
+
const confirmed = await confirm({
|
|
2035
|
+
message: `${dryRun ? "[dry-run] " : ""}Do you want to proceed with these version updates?`,
|
|
2036
|
+
default: true
|
|
2037
|
+
});
|
|
2038
|
+
if (!confirmed) {
|
|
2039
|
+
logger.log("");
|
|
2040
|
+
logger.fail("Bump refused");
|
|
2041
|
+
process.exit(0);
|
|
2042
|
+
}
|
|
2043
|
+
} catch (error) {
|
|
2044
|
+
const userHasExited = error instanceof Error && error.name === "ExitPromptError";
|
|
2045
|
+
if (userHasExited) {
|
|
2046
|
+
logger.log("");
|
|
2047
|
+
logger.fail("Bump cancelled");
|
|
2048
|
+
process.exit(0);
|
|
2049
|
+
}
|
|
2050
|
+
logger.fail("Error while confirming bump");
|
|
2051
|
+
process.exit(1);
|
|
2052
|
+
}
|
|
2053
|
+
logger.log("");
|
|
2087
2054
|
}
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
config,
|
|
2091
|
-
packageManager,
|
|
2055
|
+
function getBumpedIndependentPackages({
|
|
2056
|
+
packages,
|
|
2092
2057
|
dryRun
|
|
2093
2058
|
}) {
|
|
2094
|
-
const
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
config,
|
|
2106
|
-
otp: dynamicOtp
|
|
2107
|
-
});
|
|
2108
|
-
const command = `${baseCommand} ${args.join(" ")}`;
|
|
2109
|
-
logger.debug(`Publishing ${packageNameAndVersion} with tag '${tag}' with command: ${command}`);
|
|
2110
|
-
process.chdir(pkg.path);
|
|
2111
|
-
await executePublishCommand({
|
|
2112
|
-
command,
|
|
2113
|
-
packageNameAndVersion,
|
|
2114
|
-
pkg,
|
|
2115
|
-
config,
|
|
2116
|
-
dryRun
|
|
2059
|
+
const bumpedPackages = [];
|
|
2060
|
+
for (const pkgToBump of packages) {
|
|
2061
|
+
logger.debug(`Bumping ${pkgToBump.name} from ${pkgToBump.version} to ${pkgToBump.newVersion} (reason: ${pkgToBump.reason})`);
|
|
2062
|
+
const result = getBumpedPackageIndependently({
|
|
2063
|
+
pkg: pkgToBump,
|
|
2064
|
+
dryRun
|
|
2065
|
+
});
|
|
2066
|
+
if (result.bumped) {
|
|
2067
|
+
bumpedPackages.push({
|
|
2068
|
+
...pkgToBump,
|
|
2069
|
+
version: result.oldVersion
|
|
2117
2070
|
});
|
|
2118
|
-
if (dynamicOtp && !sessionOtp) {
|
|
2119
|
-
sessionOtp = dynamicOtp;
|
|
2120
|
-
logger.debug("OTP stored for session");
|
|
2121
|
-
}
|
|
2122
|
-
return;
|
|
2123
|
-
} catch (error) {
|
|
2124
|
-
if (isOtpError(error) && attempt < maxAttempts - 1) {
|
|
2125
|
-
dynamicOtp = await handleOtpError();
|
|
2126
|
-
} else {
|
|
2127
|
-
logger.error(`Failed to publish ${packageNameAndVersion}:`, error);
|
|
2128
|
-
throw error;
|
|
2129
|
-
}
|
|
2130
|
-
} finally {
|
|
2131
|
-
process.chdir(config.cwd);
|
|
2132
2071
|
}
|
|
2133
2072
|
}
|
|
2073
|
+
return bumpedPackages;
|
|
2134
2074
|
}
|
|
2135
2075
|
|
|
2136
|
-
function
|
|
2137
|
-
|
|
2138
|
-
if (!existsSync(packageJsonPath)) {
|
|
2139
|
-
logger.fail(`package.json not found at ${packageJsonPath}`);
|
|
2140
|
-
return;
|
|
2141
|
-
}
|
|
2142
|
-
if (!statSync(packagePath).isDirectory()) {
|
|
2143
|
-
logger.fail(`Not a directory: ${packagePath}`);
|
|
2144
|
-
return;
|
|
2145
|
-
}
|
|
2146
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
2147
|
-
if (!packageJson.name || !packageJson.version) {
|
|
2148
|
-
throw new Error(`Invalid package.json at ${packagePath}`);
|
|
2149
|
-
}
|
|
2150
|
-
return {
|
|
2151
|
-
name: packageJson.name,
|
|
2152
|
-
version: packageJson.version,
|
|
2153
|
-
private: packageJson.private || false,
|
|
2154
|
-
path: packagePath
|
|
2155
|
-
};
|
|
2076
|
+
function getIndependentTag({ version, name }) {
|
|
2077
|
+
return `${name}@${version}`;
|
|
2156
2078
|
}
|
|
2157
|
-
async function
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
const packageJson = readPackageJson(config.cwd);
|
|
2167
|
-
if (!packageJson) {
|
|
2168
|
-
throw new Error("Failed to read root package.json");
|
|
2169
|
-
}
|
|
2170
|
-
const commits = await getPackageCommits({
|
|
2171
|
-
pkg: packageJson,
|
|
2172
|
-
from,
|
|
2173
|
-
to,
|
|
2174
|
-
config,
|
|
2175
|
-
changelog
|
|
2176
|
-
});
|
|
2177
|
-
let newVersion;
|
|
2178
|
-
if (config.monorepo?.versionMode !== "independent") {
|
|
2179
|
-
const releaseType = determineReleaseType({
|
|
2180
|
-
currentVersion: packageJson.version,
|
|
2181
|
-
commits,
|
|
2182
|
-
releaseType: config.bump.type,
|
|
2183
|
-
preid: config.bump.preid,
|
|
2184
|
-
types: config.types,
|
|
2185
|
-
force
|
|
2186
|
-
});
|
|
2187
|
-
if (!releaseType) {
|
|
2188
|
-
logger.fail("No commits require a version bump");
|
|
2189
|
-
process.exit(0);
|
|
2190
|
-
}
|
|
2191
|
-
newVersion = getPackageNewVersion({
|
|
2192
|
-
currentVersion: packageJson.version,
|
|
2193
|
-
releaseType,
|
|
2194
|
-
preid: config.bump.preid,
|
|
2195
|
-
suffix
|
|
2196
|
-
});
|
|
2079
|
+
async function getLastStableTag({ logLevel, cwd }) {
|
|
2080
|
+
const { stdout } = await execPromise(
|
|
2081
|
+
`git tag --sort=-creatordate | grep -E '^[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+$' | head -n 1`,
|
|
2082
|
+
{
|
|
2083
|
+
logLevel,
|
|
2084
|
+
noStderr: true,
|
|
2085
|
+
noStdout: true,
|
|
2086
|
+
noSuccess: true,
|
|
2087
|
+
cwd
|
|
2197
2088
|
}
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2089
|
+
);
|
|
2090
|
+
const lastTag = stdout.trim();
|
|
2091
|
+
logger.debug("Last stable tag:", lastTag || "No stable tags found");
|
|
2092
|
+
return lastTag;
|
|
2093
|
+
}
|
|
2094
|
+
async function getLastTag({ logLevel, cwd }) {
|
|
2095
|
+
const { stdout } = await execPromise(`git tag --sort=-creatordate | head -n 1`, {
|
|
2096
|
+
logLevel,
|
|
2097
|
+
noStderr: true,
|
|
2098
|
+
noStdout: true,
|
|
2099
|
+
noSuccess: true,
|
|
2100
|
+
cwd
|
|
2101
|
+
});
|
|
2102
|
+
const lastTag = stdout.trim();
|
|
2103
|
+
logger.debug("Last tag:", lastTag || "No tags found");
|
|
2104
|
+
return lastTag;
|
|
2105
|
+
}
|
|
2106
|
+
function getLastRepoTag(options) {
|
|
2107
|
+
if (options?.onlyStable) {
|
|
2108
|
+
return getLastStableTag({ logLevel: options?.logLevel, cwd: options?.cwd });
|
|
2208
2109
|
}
|
|
2110
|
+
return getLastTag({ logLevel: options?.logLevel, cwd: options?.cwd });
|
|
2209
2111
|
}
|
|
2210
|
-
function
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2112
|
+
async function getLastPackageTag({
|
|
2113
|
+
packageName,
|
|
2114
|
+
onlyStable,
|
|
2115
|
+
logLevel,
|
|
2116
|
+
cwd
|
|
2214
2117
|
}) {
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
try {
|
|
2223
|
-
const matches = fastGlob.sync(pattern, {
|
|
2224
|
-
cwd,
|
|
2225
|
-
onlyDirectories: true,
|
|
2226
|
-
absolute: true,
|
|
2227
|
-
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"]
|
|
2228
|
-
});
|
|
2229
|
-
for (const matchPath of matches) {
|
|
2230
|
-
if (foundPaths.has(matchPath))
|
|
2231
|
-
continue;
|
|
2232
|
-
const packageBase = readPackageJson(matchPath);
|
|
2233
|
-
if (!packageBase || packageBase.private || ignorePackageNames?.includes(packageBase.name))
|
|
2234
|
-
continue;
|
|
2235
|
-
foundPaths.add(matchPath);
|
|
2236
|
-
packages.push({
|
|
2237
|
-
...packageBase,
|
|
2238
|
-
path: matchPath
|
|
2239
|
-
});
|
|
2240
|
-
}
|
|
2241
|
-
} catch (error) {
|
|
2242
|
-
logger.error(error);
|
|
2118
|
+
try {
|
|
2119
|
+
const escapedPackageName = packageName.replace(/[@/]/g, "\\$&");
|
|
2120
|
+
let grepPattern;
|
|
2121
|
+
if (onlyStable) {
|
|
2122
|
+
grepPattern = `^${escapedPackageName}@[0-9]+\\.[0-9]+\\.[0-9]+$`;
|
|
2123
|
+
} else {
|
|
2124
|
+
grepPattern = `^${escapedPackageName}@`;
|
|
2243
2125
|
}
|
|
2126
|
+
const { stdout } = await execPromise(
|
|
2127
|
+
`git tag --sort=-creatordate | grep -E '${grepPattern}' | sed -n '1p'`,
|
|
2128
|
+
{
|
|
2129
|
+
logLevel,
|
|
2130
|
+
noStderr: true,
|
|
2131
|
+
noStdout: true,
|
|
2132
|
+
noSuccess: true,
|
|
2133
|
+
cwd
|
|
2134
|
+
}
|
|
2135
|
+
);
|
|
2136
|
+
const tag = stdout.trim();
|
|
2137
|
+
return tag || null;
|
|
2138
|
+
} catch {
|
|
2139
|
+
return null;
|
|
2244
2140
|
}
|
|
2245
|
-
return packages;
|
|
2246
2141
|
}
|
|
2247
|
-
function
|
|
2248
|
-
|
|
2142
|
+
async function resolveFromTagIndependent({
|
|
2143
|
+
cwd,
|
|
2144
|
+
packageName,
|
|
2145
|
+
graduating,
|
|
2146
|
+
logLevel
|
|
2147
|
+
}) {
|
|
2148
|
+
const lastPackageTag = await getLastPackageTag({
|
|
2149
|
+
packageName,
|
|
2150
|
+
onlyStable: graduating,
|
|
2151
|
+
logLevel
|
|
2152
|
+
});
|
|
2153
|
+
if (!lastPackageTag) {
|
|
2154
|
+
return getFirstCommit(cwd);
|
|
2155
|
+
}
|
|
2156
|
+
return lastPackageTag;
|
|
2157
|
+
}
|
|
2158
|
+
async function resolveFromTagUnified({
|
|
2249
2159
|
config,
|
|
2250
|
-
|
|
2160
|
+
graduating,
|
|
2161
|
+
logLevel
|
|
2251
2162
|
}) {
|
|
2252
|
-
const
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2163
|
+
const from = await getLastRepoTag({ onlyStable: graduating, logLevel }) || getFirstCommit(config.cwd);
|
|
2164
|
+
return from;
|
|
2165
|
+
}
|
|
2166
|
+
async function resolveFromTag({
|
|
2167
|
+
config,
|
|
2168
|
+
versionMode,
|
|
2169
|
+
step,
|
|
2170
|
+
packageName,
|
|
2171
|
+
graduating,
|
|
2172
|
+
logLevel
|
|
2173
|
+
}) {
|
|
2174
|
+
let from;
|
|
2175
|
+
if (versionMode === "independent") {
|
|
2176
|
+
if (!packageName) {
|
|
2177
|
+
throw new Error("Package name is required for independent version mode");
|
|
2178
|
+
}
|
|
2179
|
+
from = await resolveFromTagIndependent({
|
|
2180
|
+
cwd: config.cwd,
|
|
2181
|
+
packageName,
|
|
2182
|
+
graduating,
|
|
2183
|
+
logLevel
|
|
2184
|
+
});
|
|
2185
|
+
} else {
|
|
2186
|
+
from = await resolveFromTagUnified({
|
|
2187
|
+
config,
|
|
2188
|
+
graduating,
|
|
2189
|
+
logLevel
|
|
2261
2190
|
});
|
|
2262
2191
|
}
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2192
|
+
logger.debug(`[${versionMode}](${step}) Using from tag: ${from}`);
|
|
2193
|
+
return config.from || from;
|
|
2194
|
+
}
|
|
2195
|
+
function resolveToTag({
|
|
2196
|
+
config,
|
|
2197
|
+
versionMode,
|
|
2198
|
+
newVersion,
|
|
2199
|
+
step,
|
|
2200
|
+
packageName
|
|
2201
|
+
}) {
|
|
2202
|
+
const isUntaggedStep = step === "bump" || step === "changelog";
|
|
2203
|
+
let to;
|
|
2204
|
+
if (isUntaggedStep) {
|
|
2205
|
+
to = getCurrentGitRef(config.cwd);
|
|
2206
|
+
} else if (versionMode === "independent") {
|
|
2207
|
+
if (!packageName) {
|
|
2208
|
+
throw new Error("Package name is required for independent version mode");
|
|
2209
|
+
}
|
|
2210
|
+
if (!newVersion) {
|
|
2211
|
+
throw new Error("New version is required for independent version mode");
|
|
2212
|
+
}
|
|
2213
|
+
to = getIndependentTag({ version: newVersion, name: packageName });
|
|
2214
|
+
} else {
|
|
2215
|
+
to = newVersion ? config.templates.tagBody.replace("{{newVersion}}", newVersion) : getCurrentGitRef(config.cwd);
|
|
2269
2216
|
}
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
commits: pkg.commits,
|
|
2273
|
-
releaseType,
|
|
2274
|
-
preid: config.bump.preid,
|
|
2275
|
-
types: config.types,
|
|
2276
|
-
force
|
|
2277
|
-
});
|
|
2217
|
+
logger.debug(`[${versionMode}](${step}) Using to tag: ${to}`);
|
|
2218
|
+
return config.to || to;
|
|
2278
2219
|
}
|
|
2279
|
-
async function
|
|
2280
|
-
patterns,
|
|
2220
|
+
async function resolveTags({
|
|
2281
2221
|
config,
|
|
2282
|
-
|
|
2283
|
-
|
|
2222
|
+
step,
|
|
2223
|
+
pkg,
|
|
2224
|
+
newVersion
|
|
2284
2225
|
}) {
|
|
2285
|
-
const
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2226
|
+
const versionMode = config.monorepo?.versionMode || "standalone";
|
|
2227
|
+
const logLevel = config.logLevel;
|
|
2228
|
+
logger.debug(`[${versionMode}](${step}) Resolving tags`);
|
|
2229
|
+
const releaseType = config.bump.type;
|
|
2230
|
+
const graduating = typeof newVersion === "string" ? isGraduatingToStableBetweenVersion(pkg.version, newVersion) : isGraduating(pkg.version, releaseType);
|
|
2231
|
+
const from = await resolveFromTag({
|
|
2232
|
+
config,
|
|
2233
|
+
versionMode,
|
|
2234
|
+
step,
|
|
2235
|
+
packageName: pkg.name,
|
|
2236
|
+
graduating,
|
|
2237
|
+
logLevel
|
|
2289
2238
|
});
|
|
2290
|
-
const
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2239
|
+
const to = resolveToTag({
|
|
2240
|
+
config,
|
|
2241
|
+
versionMode,
|
|
2242
|
+
newVersion,
|
|
2243
|
+
step,
|
|
2244
|
+
packageName: pkg.name
|
|
2245
|
+
});
|
|
2246
|
+
logger.debug(`[${versionMode}](${step}) Using tags: ${from} \u2192 ${to}`);
|
|
2247
|
+
return { from, to };
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
let sessionOtp;
|
|
2251
|
+
function detectPackageManager(cwd = process.cwd()) {
|
|
2252
|
+
try {
|
|
2253
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
2254
|
+
if (existsSync(packageJsonPath)) {
|
|
2255
|
+
try {
|
|
2256
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
2257
|
+
const pmField = packageJson.packageManager;
|
|
2258
|
+
if (typeof pmField === "string") {
|
|
2259
|
+
const pmName = pmField.split("@")[0];
|
|
2260
|
+
if (["npm", "pnpm", "yarn", "bun"].includes(pmName)) {
|
|
2261
|
+
logger.debug(`Detected package manager from package.json: ${pmName}`);
|
|
2262
|
+
return pmName;
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
} catch (e) {
|
|
2266
|
+
const errorString = e instanceof Error ? e.message : String(e);
|
|
2267
|
+
logger.debug(`Failed to parse package.json: ${errorString}`);
|
|
2314
2268
|
}
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2269
|
+
}
|
|
2270
|
+
const lockFiles = {
|
|
2271
|
+
pnpm: "pnpm-lock.yaml",
|
|
2272
|
+
yarn: "yarn.lock",
|
|
2273
|
+
npm: "package-lock.json",
|
|
2274
|
+
bun: "bun.lockb"
|
|
2275
|
+
};
|
|
2276
|
+
for (const [manager, file] of Object.entries(lockFiles)) {
|
|
2277
|
+
if (existsSync(join(cwd, file))) {
|
|
2278
|
+
logger.debug(`Detected package manager from lockfile: ${manager}`);
|
|
2279
|
+
return manager;
|
|
2318
2280
|
}
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2281
|
+
}
|
|
2282
|
+
const ua = process.env.npm_config_user_agent;
|
|
2283
|
+
if (ua) {
|
|
2284
|
+
const match = /(pnpm|yarn|npm|bun)/.exec(ua);
|
|
2285
|
+
if (match) {
|
|
2286
|
+
logger.debug(`Detected package manager from user agent: ${match[1]}`);
|
|
2287
|
+
return match[1];
|
|
2322
2288
|
}
|
|
2323
|
-
const { from, to } = await resolveTags({
|
|
2324
|
-
config,
|
|
2325
|
-
step: "bump",
|
|
2326
|
-
pkg: packageBase,
|
|
2327
|
-
newVersion: void 0
|
|
2328
|
-
});
|
|
2329
|
-
const commits = await getPackageCommits({
|
|
2330
|
-
pkg: packageBase,
|
|
2331
|
-
from,
|
|
2332
|
-
to,
|
|
2333
|
-
config,
|
|
2334
|
-
changelog: false
|
|
2335
|
-
});
|
|
2336
|
-
foundPaths.add(matchPath);
|
|
2337
|
-
const dependencies = getPackageDependencies({
|
|
2338
|
-
packagePath: matchPath,
|
|
2339
|
-
allPackageNames: new Set(readedPackages.map((p) => p.name)),
|
|
2340
|
-
dependencyTypes: config.bump?.dependencyTypes
|
|
2341
|
-
});
|
|
2342
|
-
packages.set(packageBase.name, {
|
|
2343
|
-
...packageBase,
|
|
2344
|
-
path: matchPath,
|
|
2345
|
-
fromTag: from,
|
|
2346
|
-
dependencies,
|
|
2347
|
-
commits,
|
|
2348
|
-
reason: commits.length > 0 ? "commits" : void 0,
|
|
2349
|
-
dependencyChain: void 0,
|
|
2350
|
-
newVersion: void 0
|
|
2351
|
-
});
|
|
2352
2289
|
}
|
|
2290
|
+
logger.debug("No package manager detected, defaulting to npm");
|
|
2291
|
+
return "npm";
|
|
2292
|
+
} catch (error) {
|
|
2293
|
+
logger.fail(`Error detecting package manager: ${error}, defaulting to npm`);
|
|
2294
|
+
return "npm";
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
function determinePublishTag(version, configTag) {
|
|
2298
|
+
let tag = "latest";
|
|
2299
|
+
if (configTag) {
|
|
2300
|
+
tag = configTag;
|
|
2301
|
+
}
|
|
2302
|
+
if (isPrerelease(version) && !configTag) {
|
|
2303
|
+
logger.warn('You are about to publish a "prerelease" version with the "latest" tag. To avoid mistake, the tag is set to "next"');
|
|
2304
|
+
tag = "next";
|
|
2353
2305
|
}
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
const expandedPackages = expandPackagesToBumpWithDependents({
|
|
2357
|
-
allPackages: packagesArray,
|
|
2358
|
-
packagesWithCommits
|
|
2359
|
-
});
|
|
2360
|
-
for (const pkg of expandedPackages) {
|
|
2361
|
-
packages.set(pkg.name, pkg);
|
|
2306
|
+
if (isPrerelease(version) && configTag === "latest") {
|
|
2307
|
+
logger.warn('Please note, you are about to publish a "prerelease" version with the "latest" tag.');
|
|
2362
2308
|
}
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2309
|
+
return tag;
|
|
2310
|
+
}
|
|
2311
|
+
function getPackagesToPublishInSelectiveMode(sortedPackages, rootVersion) {
|
|
2312
|
+
const packagesToPublish = [];
|
|
2313
|
+
for (const pkg of sortedPackages) {
|
|
2314
|
+
const pkgJsonPath = join(pkg.path, "package.json");
|
|
2315
|
+
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
2316
|
+
if (pkgJson.version === rootVersion) {
|
|
2317
|
+
packagesToPublish.push(pkg);
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
return packagesToPublish;
|
|
2321
|
+
}
|
|
2322
|
+
async function getPackagesToPublishInIndependentMode(sortedPackages, config) {
|
|
2323
|
+
const packagesToPublish = [];
|
|
2324
|
+
for (const pkg of sortedPackages) {
|
|
2325
|
+
const { from, to } = await resolveTags({
|
|
2366
2326
|
config,
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
currentVersion: pkg.version,
|
|
2371
|
-
releaseType,
|
|
2372
|
-
preid: config.bump.preid,
|
|
2373
|
-
suffix
|
|
2374
|
-
}) : void 0;
|
|
2375
|
-
const graduating = releaseType && isGraduating(pkg.version, releaseType) || isChangedPreid(pkg.version, config.bump.preid);
|
|
2376
|
-
packages.set(pkg.name, {
|
|
2377
|
-
...pkg,
|
|
2378
|
-
newVersion,
|
|
2379
|
-
reason: pkg.reason || releaseType && graduating && "graduation" || void 0
|
|
2327
|
+
step: "publish",
|
|
2328
|
+
pkg,
|
|
2329
|
+
newVersion: pkg.newVersion || pkg.version
|
|
2380
2330
|
});
|
|
2331
|
+
if (pkg.commits.length > 0) {
|
|
2332
|
+
packagesToPublish.push(pkg);
|
|
2333
|
+
logger.debug(`${pkg.name}: ${pkg.commits.length} commit(s) since ${from} \u2192 ${to}`);
|
|
2334
|
+
}
|
|
2381
2335
|
}
|
|
2382
|
-
|
|
2383
|
-
if (packagesToBump.length === 0) {
|
|
2384
|
-
logger.debug("No packages to bump");
|
|
2385
|
-
return [];
|
|
2386
|
-
}
|
|
2387
|
-
return packagesToBump;
|
|
2336
|
+
return packagesToPublish;
|
|
2388
2337
|
}
|
|
2389
|
-
function
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2338
|
+
function isYarnBerry() {
|
|
2339
|
+
return existsSync(path.join(process.cwd(), ".yarnrc.yml"));
|
|
2340
|
+
}
|
|
2341
|
+
function getCommandArgs({
|
|
2342
|
+
packageManager,
|
|
2343
|
+
tag,
|
|
2344
|
+
config,
|
|
2345
|
+
otp
|
|
2393
2346
|
}) {
|
|
2394
|
-
|
|
2395
|
-
|
|
2347
|
+
const args = ["publish", "--tag", tag];
|
|
2348
|
+
if (packageManager === "pnpm") {
|
|
2349
|
+
args.push("--no-git-checks");
|
|
2350
|
+
} else if (packageManager === "yarn") {
|
|
2351
|
+
args.push("--non-interactive");
|
|
2352
|
+
if (isYarnBerry())
|
|
2353
|
+
args.push("--no-git-checks");
|
|
2354
|
+
} else if (packageManager === "npm") {
|
|
2355
|
+
args.push("--yes");
|
|
2396
2356
|
}
|
|
2397
|
-
|
|
2398
|
-
|
|
2357
|
+
const registry = config.publish.registry;
|
|
2358
|
+
if (registry) {
|
|
2359
|
+
args.push("--registry", registry);
|
|
2399
2360
|
}
|
|
2400
|
-
|
|
2401
|
-
|
|
2361
|
+
const access = config.publish.access;
|
|
2362
|
+
if (access) {
|
|
2363
|
+
args.push("--access", access);
|
|
2402
2364
|
}
|
|
2403
|
-
|
|
2365
|
+
const finalOtp = otp ?? sessionOtp ?? config.publish.otp;
|
|
2366
|
+
if (finalOtp) {
|
|
2367
|
+
args.push("--otp", finalOtp);
|
|
2368
|
+
}
|
|
2369
|
+
return args;
|
|
2404
2370
|
}
|
|
2405
|
-
|
|
2371
|
+
function isOtpError(error) {
|
|
2372
|
+
if (typeof error !== "object" || error === null)
|
|
2373
|
+
return false;
|
|
2374
|
+
const errorMessage = "message" in error && typeof error.message === "string" ? error.message.toLowerCase() : "";
|
|
2375
|
+
return errorMessage.includes("otp") || errorMessage.includes("one-time password") || errorMessage.includes("eotp");
|
|
2376
|
+
}
|
|
2377
|
+
function promptOtpWithTimeout(timeout = 9e4) {
|
|
2378
|
+
return new Promise((resolve, reject) => {
|
|
2379
|
+
const timer = setTimeout(() => {
|
|
2380
|
+
reject(new Error("OTP input timeout"));
|
|
2381
|
+
}, timeout);
|
|
2382
|
+
input({
|
|
2383
|
+
message: "This operation requires a one-time password (OTP). Please enter your OTP:"
|
|
2384
|
+
}).then((otp) => {
|
|
2385
|
+
clearTimeout(timer);
|
|
2386
|
+
resolve(otp);
|
|
2387
|
+
}).catch((error) => {
|
|
2388
|
+
clearTimeout(timer);
|
|
2389
|
+
reject(error);
|
|
2390
|
+
});
|
|
2391
|
+
});
|
|
2392
|
+
}
|
|
2393
|
+
async function handleOtpError() {
|
|
2394
|
+
if (isInCI()) {
|
|
2395
|
+
logger.error("OTP required but running in CI environment. Please provide OTP via config or `--otp` flag");
|
|
2396
|
+
throw new Error("OTP required in CI environment");
|
|
2397
|
+
}
|
|
2398
|
+
logger.warn("Publish failed: OTP required");
|
|
2399
|
+
try {
|
|
2400
|
+
const otp = await promptOtpWithTimeout();
|
|
2401
|
+
logger.debug("OTP received, retrying publish...");
|
|
2402
|
+
return otp;
|
|
2403
|
+
} catch (promptError) {
|
|
2404
|
+
logger.error("Failed to get OTP:", promptError);
|
|
2405
|
+
throw promptError;
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
async function executePublishCommand({
|
|
2409
|
+
command,
|
|
2410
|
+
packageNameAndVersion,
|
|
2406
2411
|
pkg,
|
|
2407
|
-
from,
|
|
2408
|
-
to,
|
|
2409
2412
|
config,
|
|
2410
|
-
|
|
2413
|
+
dryRun
|
|
2411
2414
|
}) {
|
|
2412
|
-
logger.debug(`
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
to
|
|
2417
|
-
};
|
|
2418
|
-
const rawCommits = await getGitDiff(from, to, changelogConfig.cwd);
|
|
2419
|
-
const allCommits = parseCommits(rawCommits, changelogConfig);
|
|
2420
|
-
const hasBreakingChanges = allCommits.some((commit) => commit.isBreaking);
|
|
2421
|
-
logger.debug(`Has breaking changes: ${hasBreakingChanges}`);
|
|
2422
|
-
const rootPackage = readPackageJson(changelogConfig.cwd);
|
|
2423
|
-
if (!rootPackage) {
|
|
2424
|
-
throw new Error("Failed to read root package.json");
|
|
2415
|
+
logger.debug(`Executing publish command (${command}) in ${pkg.path}`);
|
|
2416
|
+
if (dryRun) {
|
|
2417
|
+
logger.info(`[dry-run] ${packageNameAndVersion}: Run ${command}`);
|
|
2418
|
+
return;
|
|
2425
2419
|
}
|
|
2426
|
-
const
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
if (pkg.path === changelogConfig.cwd || pkg.name === rootPackage.name) {
|
|
2432
|
-
return true;
|
|
2433
|
-
}
|
|
2434
|
-
const packageRelativePath = relative(changelogConfig.cwd, pkg.path);
|
|
2435
|
-
const scopeMatches = commit.scope === pkg.name;
|
|
2436
|
-
const bodyContainsPath = commit.body.includes(packageRelativePath);
|
|
2437
|
-
return scopeMatches || bodyContainsPath;
|
|
2420
|
+
const { stdout } = await execPromise(command, {
|
|
2421
|
+
noStderr: true,
|
|
2422
|
+
noStdout: true,
|
|
2423
|
+
logLevel: config.logLevel,
|
|
2424
|
+
cwd: pkg.path
|
|
2438
2425
|
});
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
logger.debug(`${pkg.name}: ${commits.length} commit(s) found`);
|
|
2442
|
-
} else {
|
|
2443
|
-
logger.debug(`${pkg.name}: No commits found`);
|
|
2426
|
+
if (stdout) {
|
|
2427
|
+
logger.debug(stdout);
|
|
2444
2428
|
}
|
|
2445
|
-
|
|
2429
|
+
logger.info(`Published ${packageNameAndVersion}`);
|
|
2446
2430
|
}
|
|
2447
|
-
function
|
|
2448
|
-
|
|
2449
|
-
|
|
2431
|
+
async function publishPackage({
|
|
2432
|
+
pkg,
|
|
2433
|
+
config,
|
|
2434
|
+
packageManager,
|
|
2435
|
+
dryRun
|
|
2436
|
+
}) {
|
|
2437
|
+
const tag = determinePublishTag(pkg.newVersion || pkg.version, config.publish.tag);
|
|
2438
|
+
const packageNameAndVersion = getIndependentTag({ name: pkg.name, version: pkg.newVersion || pkg.version });
|
|
2439
|
+
const baseCommand = packageManager === "yarn" && isYarnBerry() ? "yarn npm" : packageManager;
|
|
2440
|
+
logger.debug(`Building publish command for ${pkg.name}`);
|
|
2441
|
+
let dynamicOtp;
|
|
2442
|
+
const maxAttempts = 2;
|
|
2443
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
2444
|
+
try {
|
|
2445
|
+
const args = getCommandArgs({
|
|
2446
|
+
packageManager,
|
|
2447
|
+
tag,
|
|
2448
|
+
config,
|
|
2449
|
+
otp: dynamicOtp
|
|
2450
|
+
});
|
|
2451
|
+
const command = `${baseCommand} ${args.join(" ")}`;
|
|
2452
|
+
logger.debug(`Publishing ${packageNameAndVersion} with tag '${tag}' with command: ${command}`);
|
|
2453
|
+
process.chdir(pkg.path);
|
|
2454
|
+
await executePublishCommand({
|
|
2455
|
+
command,
|
|
2456
|
+
packageNameAndVersion,
|
|
2457
|
+
pkg,
|
|
2458
|
+
config,
|
|
2459
|
+
dryRun
|
|
2460
|
+
});
|
|
2461
|
+
if (dynamicOtp && !sessionOtp) {
|
|
2462
|
+
sessionOtp = dynamicOtp;
|
|
2463
|
+
logger.debug("OTP stored for session");
|
|
2464
|
+
}
|
|
2465
|
+
return;
|
|
2466
|
+
} catch (error) {
|
|
2467
|
+
if (isOtpError(error) && attempt < maxAttempts - 1) {
|
|
2468
|
+
dynamicOtp = await handleOtpError();
|
|
2469
|
+
} else {
|
|
2470
|
+
logger.error(`Failed to publish ${packageNameAndVersion}:`, error);
|
|
2471
|
+
throw error;
|
|
2472
|
+
}
|
|
2473
|
+
} finally {
|
|
2474
|
+
process.chdir(config.cwd);
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2450
2477
|
}
|
|
2451
2478
|
|
|
2452
2479
|
async function bumpUnifiedMode({
|
|
@@ -2744,22 +2771,6 @@ async function bump(options = {}) {
|
|
|
2744
2771
|
}
|
|
2745
2772
|
}
|
|
2746
2773
|
|
|
2747
|
-
async function getPackagesToGenerateChangelogFor({
|
|
2748
|
-
config,
|
|
2749
|
-
bumpResult,
|
|
2750
|
-
suffix,
|
|
2751
|
-
force
|
|
2752
|
-
}) {
|
|
2753
|
-
if (bumpResult?.bumpedPackages && bumpResult.bumpedPackages.length > 0) {
|
|
2754
|
-
return bumpResult.bumpedPackages;
|
|
2755
|
-
}
|
|
2756
|
-
return await getPackages({
|
|
2757
|
-
config,
|
|
2758
|
-
patterns: config.monorepo?.packages,
|
|
2759
|
-
suffix,
|
|
2760
|
-
force
|
|
2761
|
-
});
|
|
2762
|
-
}
|
|
2763
2774
|
async function generateIndependentRootChangelog({
|
|
2764
2775
|
packages,
|
|
2765
2776
|
config,
|
|
@@ -2819,10 +2830,11 @@ async function generateSimpleRootChangelog({
|
|
|
2819
2830
|
if (!rootPackageRead) {
|
|
2820
2831
|
throw new Error("Failed to read root package.json");
|
|
2821
2832
|
}
|
|
2833
|
+
const newVersion = bumpResult?.newVersion || rootPackageRead.version;
|
|
2822
2834
|
const { from, to } = await resolveTags({
|
|
2823
2835
|
config,
|
|
2824
2836
|
step: "changelog",
|
|
2825
|
-
newVersion
|
|
2837
|
+
newVersion,
|
|
2826
2838
|
pkg: rootPackageRead
|
|
2827
2839
|
});
|
|
2828
2840
|
const fromTag = bumpResult?.fromTag || from;
|
|
@@ -2835,7 +2847,6 @@ async function generateSimpleRootChangelog({
|
|
|
2835
2847
|
to
|
|
2836
2848
|
});
|
|
2837
2849
|
logger.debug(`Generating ${rootPackage.name} changelog (${fromTag}...${to})`);
|
|
2838
|
-
const newVersion = bumpResult?.newVersion || rootPackage.version;
|
|
2839
2850
|
const rootChangelog = await generateChangelog({
|
|
2840
2851
|
pkg: rootPackage,
|
|
2841
2852
|
config,
|
|
@@ -2876,7 +2887,7 @@ async function changelog(options = {}) {
|
|
|
2876
2887
|
logger.start("Start generating changelogs");
|
|
2877
2888
|
if (config.changelog?.rootChangelog && config.monorepo) {
|
|
2878
2889
|
if (config.monorepo.versionMode === "independent") {
|
|
2879
|
-
const packages2 = await
|
|
2890
|
+
const packages2 = await getPackagesOrBumpedPackages({
|
|
2880
2891
|
config,
|
|
2881
2892
|
bumpResult: options.bumpResult,
|
|
2882
2893
|
suffix: options.suffix,
|
|
@@ -2900,27 +2911,28 @@ async function changelog(options = {}) {
|
|
|
2900
2911
|
logger.debug("Skipping root changelog generation");
|
|
2901
2912
|
}
|
|
2902
2913
|
logger.debug("Generating package changelogs...");
|
|
2903
|
-
const packages =
|
|
2914
|
+
const packages = await getPackagesOrBumpedPackages({
|
|
2904
2915
|
config,
|
|
2905
|
-
|
|
2916
|
+
bumpResult: options.bumpResult,
|
|
2906
2917
|
suffix: options.suffix,
|
|
2907
2918
|
force: options.force ?? false
|
|
2908
2919
|
});
|
|
2909
2920
|
logger.debug(`Processing ${packages.length} package(s)`);
|
|
2910
2921
|
let generatedCount = 0;
|
|
2911
2922
|
for await (const pkg of packages) {
|
|
2923
|
+
const newVersion = options.bumpResult?.bumpedPackages?.find((p) => p.name === pkg.name)?.newVersion || pkg.newVersion || pkg.version;
|
|
2912
2924
|
const { from, to } = await resolveTags({
|
|
2913
2925
|
config,
|
|
2914
2926
|
step: "changelog",
|
|
2915
2927
|
pkg,
|
|
2916
|
-
newVersion
|
|
2928
|
+
newVersion
|
|
2917
2929
|
});
|
|
2918
2930
|
logger.debug(`Processing ${pkg.name} (${from}...${to})`);
|
|
2919
2931
|
const changelog2 = await generateChangelog({
|
|
2920
2932
|
pkg,
|
|
2921
2933
|
config,
|
|
2922
2934
|
dryRun,
|
|
2923
|
-
newVersion
|
|
2935
|
+
newVersion
|
|
2924
2936
|
});
|
|
2925
2937
|
if (changelog2) {
|
|
2926
2938
|
writeChangelogToFile({
|
|
@@ -3064,9 +3076,9 @@ async function publish(options = {}) {
|
|
|
3064
3076
|
throw new Error("Failed to read root package.json");
|
|
3065
3077
|
}
|
|
3066
3078
|
logger.start("Start publishing packages");
|
|
3067
|
-
const packages =
|
|
3079
|
+
const packages = await getPackagesOrBumpedPackages({
|
|
3068
3080
|
config,
|
|
3069
|
-
|
|
3081
|
+
bumpResult: options.bumpResult,
|
|
3070
3082
|
suffix: options.suffix,
|
|
3071
3083
|
force: options.force ?? false
|
|
3072
3084
|
});
|
|
@@ -3259,7 +3271,7 @@ async function release(options = {}) {
|
|
|
3259
3271
|
tag: config.publish.tag,
|
|
3260
3272
|
access: config.publish.access,
|
|
3261
3273
|
otp: config.publish.otp,
|
|
3262
|
-
|
|
3274
|
+
bumpResult,
|
|
3263
3275
|
dryRun,
|
|
3264
3276
|
config,
|
|
3265
3277
|
configName: options.configName,
|
|
@@ -3314,4 +3326,4 @@ Git provider: ${provider}`);
|
|
|
3314
3326
|
}
|
|
3315
3327
|
}
|
|
3316
3328
|
|
|
3317
|
-
export {
|
|
3329
|
+
export { determineSemverChange as $, createGitlabRelease as A, gitlab as B, detectPackageManager as C, determinePublishTag as D, getPackagesToPublishInSelectiveMode as E, getPackagesToPublishInIndependentMode as F, publishPackage as G, readPackageJson as H, getRootPackage as I, readPackages as J, getPackages as K, getPackageCommits as L, hasLernaJson as M, getIndependentTag as N, getLastStableTag as O, getLastTag as P, getLastRepoTag as Q, getLastPackageTag as R, resolveTags as S, executeHook as T, isInCI as U, getCIName as V, executeFormatCmd as W, executeBuildCmd as X, isBumpedPackage as Y, getPackagesOrBumpedPackages as Z, isGraduatingToStableBetweenVersion as _, providerRelease as a, determineReleaseType as a0, writeVersion as a1, getPackageNewVersion as a2, updateLernaVersion as a3, extractVersionFromPackageTag as a4, isPrerelease as a5, isStableReleaseType as a6, isPrereleaseReleaseType as a7, isGraduating as a8, getPreid as a9, isChangedPreid as aa, getBumpedPackageIndependently as ab, confirmBump as ac, getBumpedIndependentPackages as ad, bump as b, changelog as c, publish as d, getDefaultConfig as e, defineConfig as f, generateChangelog as g, getPackageDependencies as h, getDependentsOf as i, expandPackagesToBumpWithDependents as j, getGitStatus as k, loadRelizyConfig as l, checkGitStatusIfDirty as m, fetchGitTags as n, detectGitProvider as o, providerReleaseSafetyCheck as p, parseGitRemoteUrl as q, release as r, createCommitAndTags as s, topologicalSort as t, pushCommitAndTags as u, getFirstCommit as v, writeChangelogToFile as w, getCurrentGitBranch as x, getCurrentGitRef as y, github as z };
|