relizy 0.2.5-beta.11 → 0.2.5-beta.13
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 +9 -7
- package/dist/index.d.mts +43 -8
- package/dist/index.d.ts +43 -8
- package/dist/index.mjs +6 -6
- package/dist/shared/{relizy.DLoE22sp.mjs → relizy.qmU37FU2.mjs} +1156 -1056
- package/package.json +18 -16
|
@@ -1,439 +1,891 @@
|
|
|
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
|
+
name: packageJson.name,
|
|
191
|
+
currentVersion: packageJson.version,
|
|
192
|
+
releaseType,
|
|
193
|
+
preid: config.bump.preid,
|
|
194
|
+
suffix
|
|
195
|
+
});
|
|
193
196
|
}
|
|
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
197
|
return {
|
|
205
|
-
|
|
206
|
-
|
|
198
|
+
...packageJson,
|
|
199
|
+
path: config.cwd,
|
|
200
|
+
fromTag: from,
|
|
201
|
+
commits,
|
|
202
|
+
newVersion
|
|
207
203
|
};
|
|
204
|
+
} catch (error) {
|
|
205
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
206
|
+
throw new Error(errorMessage);
|
|
208
207
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
208
|
+
}
|
|
209
|
+
function readPackages({
|
|
210
|
+
cwd,
|
|
211
|
+
patterns,
|
|
212
|
+
ignorePackageNames
|
|
213
|
+
}) {
|
|
214
|
+
const packages = [];
|
|
215
|
+
const foundPaths = /* @__PURE__ */ new Set();
|
|
216
|
+
const patternsSet = new Set(patterns);
|
|
217
|
+
if (!patterns)
|
|
218
|
+
patternsSet.add(".");
|
|
219
|
+
logger.debug(`Read package.json files from patterns: ${patternsSet.values()}`);
|
|
220
|
+
for (const pattern of patternsSet) {
|
|
221
|
+
try {
|
|
222
|
+
const matches = fastGlob.sync(pattern, {
|
|
223
|
+
cwd,
|
|
224
|
+
onlyDirectories: true,
|
|
225
|
+
absolute: true,
|
|
226
|
+
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"]
|
|
227
|
+
});
|
|
228
|
+
for (const matchPath of matches) {
|
|
229
|
+
if (foundPaths.has(matchPath))
|
|
230
|
+
continue;
|
|
231
|
+
const packageBase = readPackageJson(matchPath);
|
|
232
|
+
if (!packageBase || packageBase.private || ignorePackageNames?.includes(packageBase.name))
|
|
233
|
+
continue;
|
|
234
|
+
foundPaths.add(matchPath);
|
|
235
|
+
packages.push({
|
|
236
|
+
...packageBase,
|
|
237
|
+
path: matchPath
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
} catch (error) {
|
|
241
|
+
logger.error(error);
|
|
242
|
+
}
|
|
215
243
|
}
|
|
216
|
-
return
|
|
244
|
+
return packages;
|
|
217
245
|
}
|
|
218
|
-
|
|
246
|
+
function getPackageReleaseType({
|
|
247
|
+
pkg,
|
|
219
248
|
config,
|
|
220
|
-
|
|
221
|
-
bumpedPackages,
|
|
222
|
-
newVersion,
|
|
223
|
-
dryRun,
|
|
224
|
-
logLevel
|
|
249
|
+
force
|
|
225
250
|
}) {
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
251
|
+
const releaseType = config.bump.type;
|
|
252
|
+
if (force) {
|
|
253
|
+
return determineReleaseType({
|
|
254
|
+
currentVersion: pkg.version,
|
|
255
|
+
commits: pkg.commits,
|
|
256
|
+
releaseType,
|
|
257
|
+
preid: config.bump.preid,
|
|
258
|
+
types: config.types,
|
|
259
|
+
force
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (pkg.reason === "dependency") {
|
|
263
|
+
if (isStableReleaseType(releaseType))
|
|
264
|
+
return "patch";
|
|
265
|
+
if (isPrerelease(pkg.version))
|
|
266
|
+
return "prerelease";
|
|
267
|
+
return "prepatch";
|
|
268
|
+
}
|
|
269
|
+
return determineReleaseType({
|
|
270
|
+
currentVersion: pkg.version,
|
|
271
|
+
commits: pkg.commits,
|
|
272
|
+
releaseType,
|
|
273
|
+
preid: config.bump.preid,
|
|
274
|
+
types: config.types,
|
|
275
|
+
force
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
async function getPackages({
|
|
279
|
+
patterns,
|
|
280
|
+
config,
|
|
281
|
+
suffix,
|
|
282
|
+
force
|
|
283
|
+
}) {
|
|
284
|
+
const readedPackages = readPackages({
|
|
285
|
+
cwd: config.cwd,
|
|
286
|
+
patterns,
|
|
287
|
+
ignorePackageNames: config.monorepo?.ignorePackageNames
|
|
288
|
+
});
|
|
289
|
+
const packages = /* @__PURE__ */ new Map();
|
|
290
|
+
const foundPaths = /* @__PURE__ */ new Set();
|
|
291
|
+
const patternsSet = new Set(patterns);
|
|
292
|
+
if (!patterns)
|
|
293
|
+
patternsSet.add(".");
|
|
294
|
+
logger.debug(`Getting packages from patterns: ${patternsSet.values()}`);
|
|
295
|
+
for (const pattern of patternsSet) {
|
|
296
|
+
const matches = fastGlob.sync(pattern, {
|
|
297
|
+
cwd: config.cwd,
|
|
298
|
+
onlyDirectories: true,
|
|
299
|
+
absolute: true,
|
|
300
|
+
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"]
|
|
301
|
+
});
|
|
302
|
+
for (const matchPath of matches) {
|
|
303
|
+
if (foundPaths.has(matchPath))
|
|
304
|
+
continue;
|
|
305
|
+
const packageBase = readPackageJson(matchPath);
|
|
306
|
+
if (!packageBase) {
|
|
307
|
+
logger.debug(`Failed to read package.json at ${matchPath} - ignored`);
|
|
241
308
|
continue;
|
|
242
309
|
}
|
|
243
|
-
if (
|
|
244
|
-
logger.
|
|
310
|
+
if (packageBase.private) {
|
|
311
|
+
logger.debug(`${packageBase.name} is private and will be ignored`);
|
|
245
312
|
continue;
|
|
246
313
|
}
|
|
247
|
-
if (
|
|
248
|
-
logger.
|
|
314
|
+
if (config.monorepo?.ignorePackageNames?.includes(packageBase.name)) {
|
|
315
|
+
logger.debug(`${packageBase.name} ignored by config (monorepo.ignorePackageNames)`);
|
|
249
316
|
continue;
|
|
250
317
|
}
|
|
251
|
-
|
|
252
|
-
logger.
|
|
253
|
-
|
|
254
|
-
} catch {
|
|
318
|
+
if (!packageBase.version) {
|
|
319
|
+
logger.warn(`${packageBase.name} has no version and will be ignored`);
|
|
320
|
+
continue;
|
|
255
321
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
322
|
+
const { from, to } = await resolveTags({
|
|
323
|
+
config,
|
|
324
|
+
step: "bump",
|
|
325
|
+
pkg: packageBase,
|
|
326
|
+
newVersion: void 0
|
|
327
|
+
});
|
|
328
|
+
const commits = await getPackageCommits({
|
|
329
|
+
pkg: packageBase,
|
|
330
|
+
from,
|
|
331
|
+
to,
|
|
332
|
+
config,
|
|
333
|
+
changelog: false
|
|
334
|
+
});
|
|
335
|
+
foundPaths.add(matchPath);
|
|
336
|
+
const dependencies = getPackageDependencies({
|
|
337
|
+
packagePath: matchPath,
|
|
338
|
+
allPackageNames: new Set(readedPackages.map((p) => p.name)),
|
|
339
|
+
dependencyTypes: config.bump?.dependencyTypes
|
|
340
|
+
});
|
|
341
|
+
packages.set(packageBase.name, {
|
|
342
|
+
...packageBase,
|
|
343
|
+
path: matchPath,
|
|
344
|
+
fromTag: from,
|
|
345
|
+
dependencies,
|
|
346
|
+
commits,
|
|
347
|
+
reason: commits.length > 0 ? "commits" : void 0,
|
|
348
|
+
dependencyChain: void 0,
|
|
349
|
+
newVersion: void 0
|
|
275
350
|
});
|
|
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
351
|
}
|
|
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
352
|
}
|
|
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"
|
|
353
|
+
const packagesArray = Array.from(packages.values());
|
|
354
|
+
const packagesWithCommits = packagesArray.filter((p) => p.commits.length > 0);
|
|
355
|
+
const expandedPackages = expandPackagesToBumpWithDependents({
|
|
356
|
+
allPackages: packagesArray,
|
|
357
|
+
packagesWithCommits
|
|
368
358
|
});
|
|
369
|
-
|
|
359
|
+
for (const pkg of expandedPackages) {
|
|
360
|
+
packages.set(pkg.name, pkg);
|
|
361
|
+
}
|
|
362
|
+
for (const pkg of Array.from(packages.values())) {
|
|
363
|
+
const releaseType = getPackageReleaseType({
|
|
364
|
+
pkg,
|
|
365
|
+
config,
|
|
366
|
+
force
|
|
367
|
+
});
|
|
368
|
+
const newVersion = releaseType ? getPackageNewVersion({
|
|
369
|
+
name: pkg.name,
|
|
370
|
+
currentVersion: pkg.version,
|
|
371
|
+
releaseType,
|
|
372
|
+
preid: config.bump.preid,
|
|
373
|
+
suffix
|
|
374
|
+
}) : void 0;
|
|
375
|
+
const graduating = releaseType && isGraduating(pkg.version, releaseType) || isChangedPreid(pkg.version, config.bump.preid);
|
|
376
|
+
packages.set(pkg.name, {
|
|
377
|
+
...pkg,
|
|
378
|
+
newVersion,
|
|
379
|
+
reason: pkg.reason || releaseType && graduating && "graduation" || void 0
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
const packagesToBump = Array.from(packages.values()).filter((p) => p.reason || force);
|
|
383
|
+
if (packagesToBump.length === 0) {
|
|
384
|
+
logger.debug("No packages to bump");
|
|
385
|
+
return [];
|
|
386
|
+
}
|
|
387
|
+
return packagesToBump;
|
|
370
388
|
}
|
|
371
|
-
function
|
|
372
|
-
|
|
373
|
-
|
|
389
|
+
function isAllowedCommit({
|
|
390
|
+
commit,
|
|
391
|
+
type,
|
|
392
|
+
changelog
|
|
393
|
+
}) {
|
|
394
|
+
if (commit.type === "chore" && ["deps", "release"].includes(commit.scope) && !commit.isBreaking) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
if (typeof type === "object") {
|
|
398
|
+
return !!type.semver || changelog && !!type.title;
|
|
399
|
+
}
|
|
400
|
+
if (typeof type === "boolean") {
|
|
401
|
+
return type;
|
|
402
|
+
}
|
|
403
|
+
return false;
|
|
374
404
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
commits,
|
|
378
|
-
config,
|
|
405
|
+
async function getPackageCommits({
|
|
406
|
+
pkg,
|
|
379
407
|
from,
|
|
380
408
|
to,
|
|
381
|
-
|
|
409
|
+
config,
|
|
410
|
+
changelog
|
|
382
411
|
}) {
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
const breakingChanges = [];
|
|
386
|
-
const updatedConfig = {
|
|
412
|
+
logger.debug(`Analyzing commits for ${pkg.name} since ${from} to ${to}`);
|
|
413
|
+
const changelogConfig = {
|
|
387
414
|
...config,
|
|
388
415
|
from,
|
|
389
416
|
to
|
|
390
417
|
};
|
|
391
|
-
const
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
});
|
|
399
|
-
markdown.push(formattedCompareLink);
|
|
418
|
+
const rawCommits = await getGitDiff(from, to, changelogConfig.cwd);
|
|
419
|
+
const allCommits = parseCommits(rawCommits, changelogConfig);
|
|
420
|
+
const hasBreakingChanges = allCommits.some((commit) => commit.isBreaking);
|
|
421
|
+
logger.debug(`Has breaking changes: ${hasBreakingChanges}`);
|
|
422
|
+
const rootPackage = readPackageJson(changelogConfig.cwd);
|
|
423
|
+
if (!rootPackage) {
|
|
424
|
+
throw new Error("Failed to read root package.json");
|
|
400
425
|
}
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
if (!
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
if (typeof updatedConfig.types[type] === "boolean") {
|
|
407
|
-
continue;
|
|
426
|
+
const commits = allCommits.filter((commit) => {
|
|
427
|
+
const type = changelogConfig?.types[commit.type];
|
|
428
|
+
if (!isAllowedCommit({ commit, type, changelog })) {
|
|
429
|
+
return false;
|
|
408
430
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const line = formatCommit(commit, updatedConfig);
|
|
412
|
-
markdown.push(line);
|
|
413
|
-
if (commit.isBreaking) {
|
|
414
|
-
breakingChanges.push(line);
|
|
415
|
-
}
|
|
431
|
+
if (pkg.path === changelogConfig.cwd || pkg.name === rootPackage.name) {
|
|
432
|
+
return true;
|
|
416
433
|
}
|
|
434
|
+
const packageRelativePath = relative(changelogConfig.cwd, pkg.path);
|
|
435
|
+
const scopeMatches = commit.scope === pkg.name;
|
|
436
|
+
const bodyContainsPath = commit.body.includes(packageRelativePath);
|
|
437
|
+
return scopeMatches || bodyContainsPath;
|
|
438
|
+
});
|
|
439
|
+
logger.debug(`Found ${commits.length} commit(s) for ${pkg.name} from ${from} to ${to}`);
|
|
440
|
+
if (commits.length > 0) {
|
|
441
|
+
logger.debug(`${pkg.name}: ${commits.length} commit(s) found`);
|
|
442
|
+
} else {
|
|
443
|
+
logger.debug(`${pkg.name}: No commits found`);
|
|
417
444
|
}
|
|
418
|
-
|
|
419
|
-
|
|
445
|
+
return commits;
|
|
446
|
+
}
|
|
447
|
+
function hasLernaJson(rootDir) {
|
|
448
|
+
const lernaJsonPath = join(rootDir, "lerna.json");
|
|
449
|
+
return existsSync(lernaJsonPath);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
async function executeHook(hook, config, dryRun, params) {
|
|
453
|
+
const hookInput = config.hooks?.[hook];
|
|
454
|
+
if (!hookInput) {
|
|
455
|
+
logger.debug(`Hook ${hook} not found`);
|
|
456
|
+
return;
|
|
420
457
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
458
|
+
if (typeof hookInput === "function") {
|
|
459
|
+
logger.info(`Executing hook ${hook}`);
|
|
460
|
+
const result = await hookInput(config, dryRun, params);
|
|
461
|
+
if (result)
|
|
462
|
+
logger.debug(`Hook ${hook} returned: ${result}`);
|
|
463
|
+
logger.info(`Hook ${hook} executed`);
|
|
464
|
+
return result;
|
|
465
|
+
}
|
|
466
|
+
if (typeof hookInput === "string") {
|
|
467
|
+
logger.info(`Executing hook ${hook}`);
|
|
468
|
+
const result = await execPromise(hookInput, {
|
|
469
|
+
logLevel: config.logLevel,
|
|
470
|
+
cwd: config.cwd,
|
|
471
|
+
noStderr: true,
|
|
472
|
+
noStdout: true
|
|
473
|
+
});
|
|
474
|
+
if (result)
|
|
475
|
+
logger.debug(`Hook ${hook} returned: ${result}`);
|
|
476
|
+
logger.info(`Hook ${hook} executed`);
|
|
477
|
+
return result;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function isInCI() {
|
|
481
|
+
return Boolean(
|
|
482
|
+
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"
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
function getCIName() {
|
|
486
|
+
if (process.env.GITHUB_ACTIONS === "true")
|
|
487
|
+
return "GitHub Actions";
|
|
488
|
+
if (process.env.GITLAB_CI === "true")
|
|
489
|
+
return "GitLab CI";
|
|
490
|
+
if (process.env.CIRCLECI === "true")
|
|
491
|
+
return "CircleCI";
|
|
492
|
+
if (process.env.TRAVIS === "true")
|
|
493
|
+
return "Travis CI";
|
|
494
|
+
if (process.env.JENKINS_HOME || process.env.JENKINS_URL)
|
|
495
|
+
return "Jenkins";
|
|
496
|
+
if (process.env.TF_BUILD === "True")
|
|
497
|
+
return "Azure Pipelines";
|
|
498
|
+
if (process.env.TEAMCITY_VERSION)
|
|
499
|
+
return "TeamCity";
|
|
500
|
+
if (process.env.BITBUCKET_BUILD_NUMBER)
|
|
501
|
+
return "Bitbucket Pipelines";
|
|
502
|
+
if (process.env.DRONE === "true")
|
|
503
|
+
return "Drone";
|
|
504
|
+
if (process.env.APPVEYOR)
|
|
505
|
+
return "AppVeyor";
|
|
506
|
+
if (process.env.BUILDKITE === "true")
|
|
507
|
+
return "Buildkite";
|
|
508
|
+
if (process.env.CODEBUILD_BUILD_ID)
|
|
509
|
+
return "AWS CodeBuild";
|
|
510
|
+
if (process.env.NETLIFY === "true")
|
|
511
|
+
return "Netlify";
|
|
512
|
+
if (process.env.VERCEL === "1")
|
|
513
|
+
return "Vercel";
|
|
514
|
+
if (process.env.HEROKU_TEST_RUN_ID)
|
|
515
|
+
return "Heroku CI";
|
|
516
|
+
if (process.env.BUDDY === "true")
|
|
517
|
+
return "Buddy";
|
|
518
|
+
if (process.env.SEMAPHORE === "true")
|
|
519
|
+
return "Semaphore";
|
|
520
|
+
if (process.env.CF_BUILD_ID)
|
|
521
|
+
return "Codefresh";
|
|
522
|
+
if (process.env.bamboo_buildKey)
|
|
523
|
+
return "Bamboo";
|
|
524
|
+
if (process.env.BUILD_ID && process.env.PROJECT_ID)
|
|
525
|
+
return "Google Cloud Build";
|
|
526
|
+
if (process.env.SCREWDRIVER === "true")
|
|
527
|
+
return "Screwdriver";
|
|
528
|
+
if (process.env.STRIDER === "true")
|
|
529
|
+
return "Strider";
|
|
530
|
+
if (process.env.CI === "true")
|
|
531
|
+
return "Unknown CI";
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
async function executeFormatCmd({
|
|
535
|
+
config,
|
|
536
|
+
dryRun
|
|
537
|
+
}) {
|
|
538
|
+
if (config.changelog?.formatCmd) {
|
|
539
|
+
logger.info("Running format command");
|
|
540
|
+
logger.debug(`Running format command: ${config.changelog.formatCmd}`);
|
|
541
|
+
try {
|
|
542
|
+
if (!dryRun) {
|
|
543
|
+
await execPromise(config.changelog.formatCmd, {
|
|
544
|
+
noStderr: true,
|
|
545
|
+
noStdout: true,
|
|
546
|
+
logLevel: config.logLevel,
|
|
547
|
+
cwd: config.cwd
|
|
548
|
+
});
|
|
549
|
+
logger.info("Format completed");
|
|
550
|
+
} else {
|
|
551
|
+
logger.log("[dry-run] exec format command: ", config.changelog.formatCmd);
|
|
552
|
+
}
|
|
553
|
+
} catch (error) {
|
|
554
|
+
throw new Error(`Format command failed: ${error}`);
|
|
555
|
+
}
|
|
556
|
+
} else {
|
|
557
|
+
logger.debug("No format command specified");
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
async function executeBuildCmd({
|
|
561
|
+
config,
|
|
562
|
+
dryRun
|
|
563
|
+
}) {
|
|
564
|
+
if (config.publish?.buildCmd) {
|
|
565
|
+
logger.info("Running build command");
|
|
566
|
+
logger.debug(`Running build command: ${config.publish.buildCmd}`);
|
|
567
|
+
if (!dryRun) {
|
|
568
|
+
await execPromise(config.publish.buildCmd, {
|
|
569
|
+
noStderr: true,
|
|
570
|
+
noStdout: true,
|
|
571
|
+
logLevel: config.logLevel,
|
|
572
|
+
cwd: config.cwd
|
|
573
|
+
});
|
|
574
|
+
logger.info("Build completed");
|
|
575
|
+
} else {
|
|
576
|
+
logger.log("[dry-run] exec build command: ", config.publish.buildCmd);
|
|
577
|
+
}
|
|
578
|
+
} else {
|
|
579
|
+
logger.debug("No build command specified");
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
function isBumpedPackage(pkg) {
|
|
583
|
+
return "oldVersion" in pkg && !!pkg.oldVersion;
|
|
584
|
+
}
|
|
585
|
+
async function getPackagesOrBumpedPackages({
|
|
586
|
+
config,
|
|
587
|
+
bumpResult,
|
|
588
|
+
suffix,
|
|
589
|
+
force
|
|
590
|
+
}) {
|
|
591
|
+
if (bumpResult?.bumpedPackages && bumpResult.bumpedPackages.length > 0) {
|
|
592
|
+
return bumpResult.bumpedPackages;
|
|
593
|
+
}
|
|
594
|
+
return await getPackages({
|
|
595
|
+
config,
|
|
596
|
+
patterns: config.monorepo?.packages,
|
|
597
|
+
suffix,
|
|
598
|
+
force
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function getGitStatus(cwd) {
|
|
603
|
+
return execSync("git status --porcelain", {
|
|
604
|
+
cwd,
|
|
605
|
+
encoding: "utf8"
|
|
606
|
+
}).trim();
|
|
607
|
+
}
|
|
608
|
+
function checkGitStatusIfDirty() {
|
|
609
|
+
logger.debug("Checking git status");
|
|
610
|
+
const dirty = getGitStatus();
|
|
611
|
+
if (dirty) {
|
|
612
|
+
logger.debug("git status:", `
|
|
613
|
+
${dirty.trim().split("\n").map((line) => line.trim()).join("\n")}`);
|
|
614
|
+
const error = `Git status is dirty!
|
|
615
|
+
|
|
616
|
+
Please commit or stash your changes before bumping or use --no-clean flag.
|
|
617
|
+
|
|
618
|
+
Unstaged files:
|
|
619
|
+
|
|
620
|
+
${dirty.trim()}`;
|
|
621
|
+
throw new Error(error);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
async function fetchGitTags(cwd) {
|
|
625
|
+
logger.debug("Fetching git tags from remote");
|
|
626
|
+
try {
|
|
627
|
+
await execPromise("git fetch --tags", { cwd, noStderr: true, noStdout: true, noSuccess: true });
|
|
628
|
+
logger.debug("Git tags fetched successfully");
|
|
629
|
+
} catch (error) {
|
|
630
|
+
logger.fail("Failed to fetch some git tags from remote (tags might already exist locally)", error);
|
|
631
|
+
logger.info("Continuing with local tags");
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
function detectGitProvider(cwd = process.cwd()) {
|
|
635
|
+
try {
|
|
636
|
+
const remoteUrl = execSync("git remote get-url origin", {
|
|
637
|
+
cwd,
|
|
638
|
+
encoding: "utf8"
|
|
639
|
+
}).trim();
|
|
640
|
+
if (remoteUrl.includes("github.com")) {
|
|
641
|
+
return "github";
|
|
642
|
+
}
|
|
643
|
+
if (remoteUrl.includes("gitlab.com") || remoteUrl.includes("gitlab")) {
|
|
644
|
+
return "gitlab";
|
|
645
|
+
}
|
|
646
|
+
return null;
|
|
647
|
+
} catch {
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
function parseGitRemoteUrl(remoteUrl) {
|
|
652
|
+
const sshRegex = /git@[\w.-]+:([\w.-]+)\/([\w.-]+?)(?:\.git)?$/;
|
|
653
|
+
const httpsRegex = /https?:\/\/[\w.-]+\/([\w.-]+)\/([\w.-]+?)(?:\.git)?$/;
|
|
654
|
+
const sshMatch = remoteUrl.match(sshRegex);
|
|
655
|
+
if (sshMatch) {
|
|
656
|
+
return {
|
|
657
|
+
owner: sshMatch[1],
|
|
658
|
+
repo: sshMatch[2]
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
const httpsMatch = remoteUrl.match(httpsRegex);
|
|
662
|
+
if (httpsMatch) {
|
|
663
|
+
return {
|
|
664
|
+
owner: httpsMatch[1],
|
|
665
|
+
repo: httpsMatch[2]
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
return null;
|
|
669
|
+
}
|
|
670
|
+
async function createCommitAndTags({
|
|
671
|
+
config,
|
|
672
|
+
noVerify,
|
|
673
|
+
bumpedPackages,
|
|
674
|
+
newVersion,
|
|
675
|
+
dryRun,
|
|
676
|
+
logLevel
|
|
677
|
+
}) {
|
|
678
|
+
const internalConfig = config || await loadRelizyConfig();
|
|
679
|
+
try {
|
|
680
|
+
await executeHook("before:commit-and-tag", internalConfig, dryRun ?? false);
|
|
681
|
+
const filePatternsToAdd = [
|
|
682
|
+
"package.json",
|
|
683
|
+
"lerna.json",
|
|
684
|
+
"CHANGELOG.md",
|
|
685
|
+
"**/CHANGELOG.md",
|
|
686
|
+
"**/package.json"
|
|
687
|
+
];
|
|
688
|
+
logger.start("Start commit and tag");
|
|
689
|
+
logger.debug("Adding files to git staging area...");
|
|
690
|
+
for (const pattern of filePatternsToAdd) {
|
|
691
|
+
if (pattern === "lerna.json" && !hasLernaJson(internalConfig.cwd)) {
|
|
692
|
+
logger.verbose(`Skipping lerna.json as it doesn't exist`);
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
if ((pattern === "lerna.json" || pattern === "CHANGELOG.md") && !existsSync(join(internalConfig.cwd, pattern))) {
|
|
696
|
+
logger.verbose(`Skipping ${pattern} as it doesn't exist`);
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
if (dryRun) {
|
|
700
|
+
logger.info(`[dry-run] git add ${pattern}`);
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
try {
|
|
704
|
+
logger.debug(`git add ${pattern}`);
|
|
705
|
+
execSync(`git add ${pattern}`);
|
|
706
|
+
} catch {
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
const rootPackage = readPackageJson(internalConfig.cwd);
|
|
710
|
+
if (!rootPackage) {
|
|
711
|
+
throw new Error("Failed to read root package.json");
|
|
712
|
+
}
|
|
713
|
+
newVersion = newVersion || rootPackage.version;
|
|
714
|
+
const versionForMessage = internalConfig.monorepo?.versionMode === "independent" ? bumpedPackages?.map((pkg) => getIndependentTag({ name: pkg.name, version: pkg.newVersion || pkg.version })).join(", ") || "unknown" : newVersion || "unknown";
|
|
715
|
+
const commitMessage = internalConfig.templates.commitMessage?.replaceAll("{{newVersion}}", versionForMessage) || `chore(release): bump version to ${versionForMessage}`;
|
|
716
|
+
const noVerifyFlag = noVerify ? "--no-verify " : "";
|
|
717
|
+
logger.debug(`No verify: ${noVerify}`);
|
|
718
|
+
if (dryRun) {
|
|
719
|
+
logger.info(`[dry-run] git commit ${noVerifyFlag}-m "${commitMessage}"`);
|
|
720
|
+
} else {
|
|
721
|
+
logger.debug(`Executing: git commit ${noVerifyFlag}-m "${commitMessage}"`);
|
|
722
|
+
await execPromise(`git commit ${noVerifyFlag}-m "${commitMessage}"`, {
|
|
723
|
+
logLevel,
|
|
724
|
+
noStderr: true,
|
|
725
|
+
noStdout: true,
|
|
726
|
+
cwd: internalConfig.cwd
|
|
727
|
+
});
|
|
728
|
+
logger.success(`Committed: ${commitMessage}${noVerify ? " (--no-verify)" : ""}`);
|
|
729
|
+
}
|
|
730
|
+
const signTags = internalConfig.signTags ? "-s" : "";
|
|
731
|
+
logger.debug(`Sign tags: ${internalConfig.signTags}`);
|
|
732
|
+
const createdTags = [];
|
|
733
|
+
if (internalConfig.monorepo?.versionMode === "independent" && bumpedPackages && bumpedPackages.length > 0 && internalConfig.release.gitTag) {
|
|
734
|
+
logger.debug(`Creating ${bumpedPackages.length} independent package tags`);
|
|
735
|
+
for (const pkg of bumpedPackages) {
|
|
736
|
+
if (!pkg.newVersion) {
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
const tagName = getIndependentTag({ version: pkg.newVersion, name: pkg.name });
|
|
740
|
+
const tagMessage = internalConfig.templates?.tagMessage?.replaceAll("{{newVersion}}", pkg.newVersion) || tagName;
|
|
741
|
+
if (dryRun) {
|
|
742
|
+
logger.info(`[dry-run] git tag ${signTags} -a ${tagName} -m "${tagMessage}"`);
|
|
743
|
+
} else {
|
|
744
|
+
const cmd = `git tag ${signTags} -a ${tagName} -m "${tagMessage}"`;
|
|
745
|
+
logger.debug(`Executing: ${cmd}`);
|
|
746
|
+
try {
|
|
747
|
+
await execPromise(cmd, {
|
|
748
|
+
logLevel,
|
|
749
|
+
noStderr: true,
|
|
750
|
+
noStdout: true,
|
|
751
|
+
cwd: internalConfig.cwd
|
|
752
|
+
});
|
|
753
|
+
logger.debug(`Tag created: ${tagName}`);
|
|
754
|
+
} catch (error) {
|
|
755
|
+
logger.error(`Failed to create tag ${tagName}:`, error);
|
|
756
|
+
throw error;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
createdTags.push(tagName);
|
|
760
|
+
}
|
|
761
|
+
logger.success(`Created ${createdTags.length} tags for independent packages, ${createdTags.join(", ")}`);
|
|
762
|
+
} else if (internalConfig.release.gitTag) {
|
|
763
|
+
const tagName = internalConfig.templates.tagBody?.replaceAll("{{newVersion}}", newVersion);
|
|
764
|
+
const tagMessage = internalConfig.templates?.tagMessage?.replaceAll("{{newVersion}}", newVersion) || tagName;
|
|
765
|
+
if (dryRun) {
|
|
766
|
+
logger.info(`[dry-run] git tag ${signTags} -a ${tagName} -m "${tagMessage}"`);
|
|
767
|
+
} else {
|
|
768
|
+
const cmd = `git tag ${signTags} -a ${tagName} -m "${tagMessage}"`;
|
|
769
|
+
logger.debug(`Executing: ${cmd}`);
|
|
770
|
+
try {
|
|
771
|
+
await execPromise(cmd, {
|
|
772
|
+
logLevel,
|
|
773
|
+
noStderr: true,
|
|
774
|
+
noStdout: true,
|
|
775
|
+
cwd: internalConfig.cwd
|
|
776
|
+
});
|
|
777
|
+
logger.debug(`Tag created: ${tagName}`);
|
|
778
|
+
} catch (error) {
|
|
779
|
+
logger.error(`Failed to create tag ${tagName}:`, error);
|
|
780
|
+
throw error;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
createdTags.push(tagName);
|
|
784
|
+
}
|
|
785
|
+
logger.debug("Created Tags:", createdTags.join(", "));
|
|
786
|
+
logger.success("Commit and tag completed!");
|
|
787
|
+
await executeHook("success:commit-and-tag", internalConfig, dryRun ?? false);
|
|
788
|
+
return createdTags;
|
|
789
|
+
} catch (error) {
|
|
790
|
+
logger.error("Error committing and tagging:", error);
|
|
791
|
+
await executeHook("error:commit-and-tag", internalConfig, dryRun ?? false);
|
|
792
|
+
throw error;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
async function pushCommitAndTags({ config, dryRun, logLevel, cwd }) {
|
|
796
|
+
logger.start("Start push changes and tags");
|
|
797
|
+
const command = config.release.gitTag ? "git push --follow-tags" : "git push";
|
|
798
|
+
if (dryRun) {
|
|
799
|
+
logger.info(`[dry-run] ${command}`);
|
|
800
|
+
} else {
|
|
801
|
+
logger.debug(`Executing: ${command}`);
|
|
802
|
+
await execPromise(command, { noStderr: true, noStdout: true, logLevel, cwd });
|
|
803
|
+
}
|
|
804
|
+
logger.success("Pushing changes and tags completed!");
|
|
805
|
+
}
|
|
806
|
+
function getFirstCommit(cwd) {
|
|
807
|
+
const result = execSync(
|
|
808
|
+
"git rev-list --max-parents=0 HEAD",
|
|
809
|
+
{
|
|
810
|
+
cwd,
|
|
811
|
+
encoding: "utf8"
|
|
812
|
+
}
|
|
813
|
+
);
|
|
814
|
+
return result.trim();
|
|
815
|
+
}
|
|
816
|
+
function getCurrentGitBranch(cwd) {
|
|
817
|
+
const result = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
818
|
+
cwd,
|
|
819
|
+
encoding: "utf8"
|
|
820
|
+
});
|
|
821
|
+
return result.trim();
|
|
822
|
+
}
|
|
823
|
+
function getCurrentGitRef(cwd) {
|
|
824
|
+
const branch = getCurrentGitBranch(cwd);
|
|
825
|
+
return branch || "HEAD";
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
async function generateMarkDown({
|
|
829
|
+
commits,
|
|
830
|
+
config,
|
|
831
|
+
from,
|
|
832
|
+
to,
|
|
833
|
+
isFirstCommit
|
|
834
|
+
}) {
|
|
835
|
+
const typeGroups = groupBy(commits, "type");
|
|
836
|
+
const markdown = [];
|
|
837
|
+
const breakingChanges = [];
|
|
838
|
+
const updatedConfig = {
|
|
839
|
+
...config,
|
|
840
|
+
from,
|
|
841
|
+
to
|
|
842
|
+
};
|
|
843
|
+
const versionTitle = updatedConfig.to;
|
|
844
|
+
const changelogTitle = `${updatedConfig.from}...${updatedConfig.to}`;
|
|
845
|
+
markdown.push("", `## ${changelogTitle}`, "");
|
|
846
|
+
if (updatedConfig.repo && updatedConfig.from && versionTitle) {
|
|
847
|
+
const formattedCompareLink = formatCompareChanges(versionTitle, {
|
|
848
|
+
...updatedConfig,
|
|
849
|
+
from: isFirstCommit ? getFirstCommit(updatedConfig.cwd) : updatedConfig.from
|
|
850
|
+
});
|
|
851
|
+
markdown.push(formattedCompareLink);
|
|
852
|
+
}
|
|
853
|
+
for (const type in updatedConfig.types) {
|
|
854
|
+
const group = typeGroups[type];
|
|
855
|
+
if (!group || group.length === 0) {
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
if (typeof updatedConfig.types[type] === "boolean") {
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
markdown.push("", `### ${updatedConfig.types[type]?.title}`, "");
|
|
862
|
+
for (const commit of group.reverse()) {
|
|
863
|
+
const line = formatCommit(commit, updatedConfig);
|
|
864
|
+
markdown.push(line);
|
|
865
|
+
if (commit.isBreaking) {
|
|
866
|
+
breakingChanges.push(line);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
if (breakingChanges.length > 0) {
|
|
871
|
+
markdown.push("", "#### \u26A0\uFE0F Breaking Changes", "", ...breakingChanges);
|
|
872
|
+
}
|
|
873
|
+
const _authors = /* @__PURE__ */ new Map();
|
|
874
|
+
for (const commit of commits) {
|
|
875
|
+
if (!commit.author) {
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
878
|
+
const name = formatName(commit.author.name);
|
|
879
|
+
if (!name || name.includes("[bot]")) {
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
if (updatedConfig.excludeAuthors && updatedConfig.excludeAuthors.some(
|
|
883
|
+
(v) => name.includes(v) || commit.author.email?.includes(v)
|
|
884
|
+
)) {
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
if (_authors.has(name)) {
|
|
888
|
+
const entry = _authors.get(name);
|
|
437
889
|
entry?.email.add(commit.author.email);
|
|
438
890
|
} else {
|
|
439
891
|
_authors.set(name, { email: /* @__PURE__ */ new Set([commit.author.email]), name });
|
|
@@ -541,9 +993,8 @@ async function generateChangelog({
|
|
|
541
993
|
if (isFirstCommit) {
|
|
542
994
|
fromTag = config.monorepo?.versionMode === "independent" ? getIndependentTag({ version: "0.0.0", name: pkg.name }) : config.templates.tagBody.replace("{{newVersion}}", "0.0.0");
|
|
543
995
|
}
|
|
544
|
-
newVersion = newVersion || pkg.newVersion;
|
|
545
996
|
let toTag = config.to;
|
|
546
|
-
if (!toTag
|
|
997
|
+
if (!toTag) {
|
|
547
998
|
toTag = config.monorepo?.versionMode === "independent" ? getIndependentTag({ version: newVersion, name: pkg.name }) : config.templates.tagBody.replace("{{newVersion}}", newVersion);
|
|
548
999
|
}
|
|
549
1000
|
if (!toTag) {
|
|
@@ -661,7 +1112,8 @@ function getDefaultConfig() {
|
|
|
661
1112
|
},
|
|
662
1113
|
publish: {
|
|
663
1114
|
private: false,
|
|
664
|
-
args: []
|
|
1115
|
+
args: [],
|
|
1116
|
+
safetyCheck: false
|
|
665
1117
|
},
|
|
666
1118
|
tokens: {
|
|
667
1119
|
gitlab: process$1.env.RELIZY_GITLAB_TOKEN || process$1.env.GITLAB_TOKEN || process$1.env.GITLAB_API_TOKEN || process$1.env.CI_JOB_TOKEN,
|
|
@@ -675,7 +1127,8 @@ function getDefaultConfig() {
|
|
|
675
1127
|
push: true,
|
|
676
1128
|
clean: true,
|
|
677
1129
|
providerRelease: true,
|
|
678
|
-
noVerify: false
|
|
1130
|
+
noVerify: false,
|
|
1131
|
+
gitTag: true
|
|
679
1132
|
},
|
|
680
1133
|
logLevel: "default",
|
|
681
1134
|
safetyCheck: true
|
|
@@ -715,141 +1168,23 @@ async function loadRelizyConfig(options) {
|
|
|
715
1168
|
name: configName,
|
|
716
1169
|
packageJson: true,
|
|
717
1170
|
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
|
-
}
|
|
1171
|
+
overrides: overridesConfig
|
|
1172
|
+
});
|
|
1173
|
+
if (!results._configFile) {
|
|
1174
|
+
logger.debug(`No config file found with name "${configName}" - using standalone mode`);
|
|
1175
|
+
if (options?.configName) {
|
|
1176
|
+
logger.error(`No config file found with name "${configName}"`);
|
|
1177
|
+
process$1.exit(1);
|
|
809
1178
|
}
|
|
810
1179
|
}
|
|
811
|
-
|
|
1180
|
+
setupLogger(options?.overrides?.logLevel || results.config.logLevel);
|
|
1181
|
+
logger.verbose("User config:", formatJson(results.config.changelog));
|
|
1182
|
+
const resolvedConfig = await resolveConfig(results.config, cwd);
|
|
1183
|
+
logger.debug("Resolved config:", formatJson(resolvedConfig));
|
|
1184
|
+
return resolvedConfig;
|
|
812
1185
|
}
|
|
813
|
-
function
|
|
814
|
-
|
|
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;
|
|
1186
|
+
function defineConfig(config) {
|
|
1187
|
+
return config;
|
|
853
1188
|
}
|
|
854
1189
|
|
|
855
1190
|
async function githubIndependentMode({
|
|
@@ -867,10 +1202,10 @@ async function githubIndependentMode({
|
|
|
867
1202
|
if (!config.tokens.github && !config.repo?.token) {
|
|
868
1203
|
throw new Error("No GitHub token specified. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
|
|
869
1204
|
}
|
|
870
|
-
const packages =
|
|
871
|
-
suffix,
|
|
872
|
-
patterns: config.monorepo?.packages,
|
|
1205
|
+
const packages = await getPackagesOrBumpedPackages({
|
|
873
1206
|
config,
|
|
1207
|
+
bumpResult,
|
|
1208
|
+
suffix,
|
|
874
1209
|
force
|
|
875
1210
|
});
|
|
876
1211
|
logger.info(`Creating ${packages.length} GitHub release(s)`);
|
|
@@ -944,12 +1279,13 @@ async function githubUnified({
|
|
|
944
1279
|
if (!config.tokens.github && !config.repo?.token) {
|
|
945
1280
|
throw new Error("No GitHub token specified. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
|
|
946
1281
|
}
|
|
947
|
-
const
|
|
1282
|
+
const newVersion = bumpResult?.newVersion || rootPackage.version;
|
|
1283
|
+
const to = config.to || config.templates.tagBody.replace("{{newVersion}}", newVersion);
|
|
948
1284
|
const changelog = await generateChangelog({
|
|
949
1285
|
pkg: rootPackage,
|
|
950
1286
|
config,
|
|
951
1287
|
dryRun,
|
|
952
|
-
newVersion
|
|
1288
|
+
newVersion
|
|
953
1289
|
});
|
|
954
1290
|
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
955
1291
|
const release = {
|
|
@@ -1012,10 +1348,11 @@ async function github(options) {
|
|
|
1012
1348
|
if (!rootPackageBase) {
|
|
1013
1349
|
throw new Error("Failed to read root package.json");
|
|
1014
1350
|
}
|
|
1351
|
+
const newVersion = options.bumpResult?.newVersion || rootPackageBase.version;
|
|
1015
1352
|
const { from, to } = await resolveTags({
|
|
1016
1353
|
config,
|
|
1017
1354
|
step: "provider-release",
|
|
1018
|
-
newVersion
|
|
1355
|
+
newVersion,
|
|
1019
1356
|
pkg: rootPackageBase
|
|
1020
1357
|
});
|
|
1021
1358
|
const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
|
|
@@ -1107,9 +1444,9 @@ async function gitlabIndependentMode({
|
|
|
1107
1444
|
force
|
|
1108
1445
|
}) {
|
|
1109
1446
|
logger.debug(`GitLab token: ${config.tokens.gitlab || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
|
|
1110
|
-
const packages =
|
|
1111
|
-
patterns: config.monorepo?.packages,
|
|
1447
|
+
const packages = await getPackagesOrBumpedPackages({
|
|
1112
1448
|
config,
|
|
1449
|
+
bumpResult,
|
|
1113
1450
|
suffix,
|
|
1114
1451
|
force
|
|
1115
1452
|
});
|
|
@@ -1180,12 +1517,13 @@ async function gitlabUnified({
|
|
|
1180
1517
|
bumpResult
|
|
1181
1518
|
}) {
|
|
1182
1519
|
logger.debug(`GitLab token: ${config.tokens.gitlab || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
|
|
1183
|
-
const
|
|
1520
|
+
const newVersion = bumpResult?.newVersion || rootPackage.newVersion || rootPackage.version;
|
|
1521
|
+
const to = config.templates.tagBody.replace("{{newVersion}}", newVersion);
|
|
1184
1522
|
const changelog = await generateChangelog({
|
|
1185
1523
|
pkg: rootPackage,
|
|
1186
1524
|
config,
|
|
1187
1525
|
dryRun,
|
|
1188
|
-
newVersion
|
|
1526
|
+
newVersion
|
|
1189
1527
|
});
|
|
1190
1528
|
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
1191
1529
|
logger.debug("Getting current branch...");
|
|
@@ -1222,7 +1560,7 @@ async function gitlabUnified({
|
|
|
1222
1560
|
name: to,
|
|
1223
1561
|
tag: to,
|
|
1224
1562
|
version: to,
|
|
1225
|
-
prerelease: isPrerelease(
|
|
1563
|
+
prerelease: isPrerelease(newVersion)
|
|
1226
1564
|
}];
|
|
1227
1565
|
}
|
|
1228
1566
|
async function gitlab(options = {}) {
|
|
@@ -1254,10 +1592,11 @@ async function gitlab(options = {}) {
|
|
|
1254
1592
|
if (!rootPackageBase) {
|
|
1255
1593
|
throw new Error("Failed to read root package.json");
|
|
1256
1594
|
}
|
|
1595
|
+
const newVersion = options.bumpResult?.newVersion || rootPackageBase.version;
|
|
1257
1596
|
const { from, to } = await resolveTags({
|
|
1258
1597
|
config,
|
|
1259
1598
|
step: "provider-release",
|
|
1260
|
-
newVersion
|
|
1599
|
+
newVersion,
|
|
1261
1600
|
pkg: rootPackageBase
|
|
1262
1601
|
});
|
|
1263
1602
|
const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
|
|
@@ -1268,7 +1607,7 @@ async function gitlab(options = {}) {
|
|
|
1268
1607
|
from,
|
|
1269
1608
|
to
|
|
1270
1609
|
});
|
|
1271
|
-
logger.debug(`Root package: ${getIndependentTag({ name: rootPackage.name, version:
|
|
1610
|
+
logger.debug(`Root package: ${getIndependentTag({ name: rootPackage.name, version: newVersion })}`);
|
|
1272
1611
|
return await gitlabUnified({
|
|
1273
1612
|
config,
|
|
1274
1613
|
dryRun,
|
|
@@ -1281,6 +1620,12 @@ async function gitlab(options = {}) {
|
|
|
1281
1620
|
}
|
|
1282
1621
|
}
|
|
1283
1622
|
|
|
1623
|
+
function isGraduatingToStableBetweenVersion(version, newVersion) {
|
|
1624
|
+
const isSameBase = semver.major(version) === semver.major(newVersion) && semver.minor(version) === semver.minor(newVersion) && semver.patch(version) === semver.patch(newVersion);
|
|
1625
|
+
const fromPrerelease = semver.prerelease(version) !== null;
|
|
1626
|
+
const toStable = semver.prerelease(newVersion) === null;
|
|
1627
|
+
return isSameBase && fromPrerelease && toStable;
|
|
1628
|
+
}
|
|
1284
1629
|
function determineSemverChange(commits, types) {
|
|
1285
1630
|
let [hasMajor, hasMinor, hasPatch] = [false, false, false];
|
|
1286
1631
|
for (const commit of commits) {
|
|
@@ -1411,26 +1756,27 @@ function determineReleaseType({
|
|
|
1411
1756
|
}
|
|
1412
1757
|
return handleExplicitReleaseType({ releaseType, currentVersion });
|
|
1413
1758
|
}
|
|
1414
|
-
function writeVersion(pkgPath,
|
|
1759
|
+
function writeVersion(pkgPath, newVersion, dryRun = false) {
|
|
1415
1760
|
const packageJsonPath = join(pkgPath, "package.json");
|
|
1416
1761
|
try {
|
|
1417
|
-
logger.debug(`Writing ${
|
|
1762
|
+
logger.debug(`Writing ${newVersion} to ${pkgPath}`);
|
|
1418
1763
|
const content = readFileSync(packageJsonPath, "utf8");
|
|
1419
1764
|
const packageJson = JSON.parse(content);
|
|
1420
1765
|
const oldVersion = packageJson.version;
|
|
1421
|
-
packageJson.version =
|
|
1766
|
+
packageJson.version = newVersion;
|
|
1422
1767
|
if (dryRun) {
|
|
1423
|
-
logger.info(`[dry-run] Updated ${packageJson.name}: ${oldVersion} \u2192 ${
|
|
1768
|
+
logger.info(`[dry-run] Updated ${packageJson.name}: ${oldVersion} \u2192 ${newVersion}`);
|
|
1424
1769
|
return;
|
|
1425
1770
|
}
|
|
1426
1771
|
writeFileSync(packageJsonPath, `${formatJson(packageJson)}
|
|
1427
1772
|
`, "utf8");
|
|
1428
|
-
logger.info(`Updated ${packageJson.name}: ${oldVersion} \u2192 ${
|
|
1773
|
+
logger.info(`Updated ${packageJson.name}: ${oldVersion} \u2192 ${newVersion}`);
|
|
1429
1774
|
} catch (error) {
|
|
1430
1775
|
throw new Error(`Unable to write version to ${packageJsonPath}: ${error}`);
|
|
1431
1776
|
}
|
|
1432
1777
|
}
|
|
1433
1778
|
function getPackageNewVersion({
|
|
1779
|
+
name,
|
|
1434
1780
|
currentVersion,
|
|
1435
1781
|
releaseType,
|
|
1436
1782
|
preid,
|
|
@@ -1438,7 +1784,7 @@ function getPackageNewVersion({
|
|
|
1438
1784
|
}) {
|
|
1439
1785
|
let newVersion = semver.inc(currentVersion, releaseType, preid);
|
|
1440
1786
|
if (!newVersion) {
|
|
1441
|
-
throw new Error(`Unable to bump version "${currentVersion}" with release type "${releaseType}"
|
|
1787
|
+
throw new Error(`Unable to bump "${name}" version "${currentVersion}" with release type "${releaseType}"
|
|
1442
1788
|
|
|
1443
1789
|
You should use an explicit release type (use flag: --major, --minor, --patch, --premajor, --preminor, --prepatch, --prerelease)`);
|
|
1444
1790
|
}
|
|
@@ -1447,13 +1793,13 @@ You should use an explicit release type (use flag: --major, --minor, --patch, --
|
|
|
1447
1793
|
}
|
|
1448
1794
|
const isValidVersion = semver.gt(newVersion, currentVersion);
|
|
1449
1795
|
if (!isValidVersion) {
|
|
1450
|
-
throw new Error(`Unable to bump version "${currentVersion}" to "${newVersion}", new version is not greater than current version`);
|
|
1796
|
+
throw new Error(`Unable to bump "${name}" version "${currentVersion}" to "${newVersion}", new version is not greater than current version`);
|
|
1451
1797
|
}
|
|
1452
1798
|
if (isGraduating(currentVersion, releaseType)) {
|
|
1453
|
-
logger.info(`Graduating from prerelease ${currentVersion} to stable ${newVersion}`);
|
|
1799
|
+
logger.info(`Graduating "${name}" from prerelease ${currentVersion} to stable ${newVersion}`);
|
|
1454
1800
|
}
|
|
1455
1801
|
if (isChangedPreid(currentVersion, preid)) {
|
|
1456
|
-
logger.debug(`Graduating from ${getPreid(currentVersion)} to ${preid}`);
|
|
1802
|
+
logger.debug(`Graduating "${name}" from ${getPreid(currentVersion)} to ${preid}`);
|
|
1457
1803
|
}
|
|
1458
1804
|
return newVersion;
|
|
1459
1805
|
}
|
|
@@ -1885,12 +2231,13 @@ async function resolveTags({
|
|
|
1885
2231
|
const logLevel = config.logLevel;
|
|
1886
2232
|
logger.debug(`[${versionMode}](${step}) Resolving tags`);
|
|
1887
2233
|
const releaseType = config.bump.type;
|
|
2234
|
+
const graduating = typeof newVersion === "string" ? isGraduatingToStableBetweenVersion(pkg.version, newVersion) : isGraduating(pkg.version, releaseType);
|
|
1888
2235
|
const from = await resolveFromTag({
|
|
1889
2236
|
config,
|
|
1890
2237
|
versionMode,
|
|
1891
2238
|
step,
|
|
1892
2239
|
packageName: pkg.name,
|
|
1893
|
-
graduating
|
|
2240
|
+
graduating,
|
|
1894
2241
|
logLevel
|
|
1895
2242
|
});
|
|
1896
2243
|
const to = resolveToTag({
|
|
@@ -1930,523 +2277,252 @@ function detectPackageManager(cwd = process.cwd()) {
|
|
|
1930
2277
|
npm: "package-lock.json",
|
|
1931
2278
|
bun: "bun.lockb"
|
|
1932
2279
|
};
|
|
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
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
const ua = process.env.npm_config_user_agent;
|
|
1940
|
-
if (ua) {
|
|
1941
|
-
const match = /(pnpm|yarn|npm|bun)/.exec(ua);
|
|
1942
|
-
if (match) {
|
|
1943
|
-
logger.debug(`Detected package manager from user agent: ${match[1]}`);
|
|
1944
|
-
return match[1];
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
|
-
logger.debug("No package manager detected, defaulting to npm");
|
|
1948
|
-
return "npm";
|
|
1949
|
-
} catch (error) {
|
|
1950
|
-
logger.fail(`Error detecting package manager: ${error}, defaulting to npm`);
|
|
1951
|
-
return "npm";
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
function determinePublishTag(version, configTag) {
|
|
1955
|
-
let tag = "latest";
|
|
1956
|
-
if (configTag) {
|
|
1957
|
-
tag = configTag;
|
|
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.');
|
|
1965
|
-
}
|
|
1966
|
-
return tag;
|
|
1967
|
-
}
|
|
1968
|
-
function getPackagesToPublishInSelectiveMode(sortedPackages, rootVersion) {
|
|
1969
|
-
const packagesToPublish = [];
|
|
1970
|
-
for (const pkg of sortedPackages) {
|
|
1971
|
-
const pkgJsonPath = join(pkg.path, "package.json");
|
|
1972
|
-
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
1973
|
-
if (pkgJson.version === rootVersion) {
|
|
1974
|
-
packagesToPublish.push(pkg);
|
|
1975
|
-
}
|
|
1976
|
-
}
|
|
1977
|
-
return packagesToPublish;
|
|
1978
|
-
}
|
|
1979
|
-
async function getPackagesToPublishInIndependentMode(sortedPackages, config) {
|
|
1980
|
-
const packagesToPublish = [];
|
|
1981
|
-
for (const pkg of sortedPackages) {
|
|
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;
|
|
1994
|
-
}
|
|
1995
|
-
function isYarnBerry() {
|
|
1996
|
-
return existsSync(path.join(process.cwd(), ".yarnrc.yml"));
|
|
1997
|
-
}
|
|
1998
|
-
function getCommandArgs({
|
|
1999
|
-
packageManager,
|
|
2000
|
-
tag,
|
|
2001
|
-
config,
|
|
2002
|
-
otp
|
|
2003
|
-
}) {
|
|
2004
|
-
const args = ["publish", "--tag", tag];
|
|
2005
|
-
if (packageManager === "pnpm") {
|
|
2006
|
-
args.push("--no-git-checks");
|
|
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");
|
|
2013
|
-
}
|
|
2014
|
-
const registry = config.publish.registry;
|
|
2015
|
-
if (registry) {
|
|
2016
|
-
args.push("--registry", registry);
|
|
2017
|
-
}
|
|
2018
|
-
const access = config.publish.access;
|
|
2019
|
-
if (access) {
|
|
2020
|
-
args.push("--access", access);
|
|
2021
|
-
}
|
|
2022
|
-
const finalOtp = otp ?? sessionOtp ?? config.publish.otp;
|
|
2023
|
-
if (finalOtp) {
|
|
2024
|
-
args.push("--otp", finalOtp);
|
|
2025
|
-
}
|
|
2026
|
-
return args;
|
|
2027
|
-
}
|
|
2028
|
-
function isOtpError(error) {
|
|
2029
|
-
if (typeof error !== "object" || error === null)
|
|
2030
|
-
return false;
|
|
2031
|
-
const errorMessage = "message" in error && typeof error.message === "string" ? error.message.toLowerCase() : "";
|
|
2032
|
-
return errorMessage.includes("otp") || errorMessage.includes("one-time password") || errorMessage.includes("eotp");
|
|
2033
|
-
}
|
|
2034
|
-
function promptOtpWithTimeout(timeout = 9e4) {
|
|
2035
|
-
return new Promise((resolve, reject) => {
|
|
2036
|
-
const timer = setTimeout(() => {
|
|
2037
|
-
reject(new Error("OTP input timeout"));
|
|
2038
|
-
}, timeout);
|
|
2039
|
-
input({
|
|
2040
|
-
message: "This operation requires a one-time password (OTP). Please enter your OTP:"
|
|
2041
|
-
}).then((otp) => {
|
|
2042
|
-
clearTimeout(timer);
|
|
2043
|
-
resolve(otp);
|
|
2044
|
-
}).catch((error) => {
|
|
2045
|
-
clearTimeout(timer);
|
|
2046
|
-
reject(error);
|
|
2047
|
-
});
|
|
2048
|
-
});
|
|
2049
|
-
}
|
|
2050
|
-
async function handleOtpError() {
|
|
2051
|
-
if (isInCI()) {
|
|
2052
|
-
logger.error("OTP required but running in CI environment. Please provide OTP via config or `--otp` flag");
|
|
2053
|
-
throw new Error("OTP required in CI environment");
|
|
2054
|
-
}
|
|
2055
|
-
logger.warn("Publish failed: OTP required");
|
|
2056
|
-
try {
|
|
2057
|
-
const otp = await promptOtpWithTimeout();
|
|
2058
|
-
logger.debug("OTP received, retrying publish...");
|
|
2059
|
-
return otp;
|
|
2060
|
-
} catch (promptError) {
|
|
2061
|
-
logger.error("Failed to get OTP:", promptError);
|
|
2062
|
-
throw promptError;
|
|
2063
|
-
}
|
|
2064
|
-
}
|
|
2065
|
-
async function executePublishCommand({
|
|
2066
|
-
command,
|
|
2067
|
-
packageNameAndVersion,
|
|
2068
|
-
pkg,
|
|
2069
|
-
config,
|
|
2070
|
-
dryRun
|
|
2071
|
-
}) {
|
|
2072
|
-
logger.debug(`Executing publish command (${command}) in ${pkg.path}`);
|
|
2073
|
-
if (dryRun) {
|
|
2074
|
-
logger.info(`[dry-run] ${packageNameAndVersion}: Run ${command}`);
|
|
2075
|
-
return;
|
|
2076
|
-
}
|
|
2077
|
-
const { stdout } = await execPromise(command, {
|
|
2078
|
-
noStderr: true,
|
|
2079
|
-
noStdout: true,
|
|
2080
|
-
logLevel: config.logLevel,
|
|
2081
|
-
cwd: pkg.path
|
|
2082
|
-
});
|
|
2083
|
-
if (stdout) {
|
|
2084
|
-
logger.debug(stdout);
|
|
2085
|
-
}
|
|
2086
|
-
logger.info(`Published ${packageNameAndVersion}`);
|
|
2087
|
-
}
|
|
2088
|
-
async function publishPackage({
|
|
2089
|
-
pkg,
|
|
2090
|
-
config,
|
|
2091
|
-
packageManager,
|
|
2092
|
-
dryRun
|
|
2093
|
-
}) {
|
|
2094
|
-
const tag = determinePublishTag(pkg.version, config.publish.tag);
|
|
2095
|
-
const packageNameAndVersion = getIndependentTag({ name: pkg.name, version: pkg.newVersion || pkg.version });
|
|
2096
|
-
const baseCommand = packageManager === "yarn" && isYarnBerry() ? "yarn npm" : packageManager;
|
|
2097
|
-
logger.debug(`Building publish command for ${pkg.name}`);
|
|
2098
|
-
let dynamicOtp;
|
|
2099
|
-
const maxAttempts = 2;
|
|
2100
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
2101
|
-
try {
|
|
2102
|
-
const args = getCommandArgs({
|
|
2103
|
-
packageManager,
|
|
2104
|
-
tag,
|
|
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
|
|
2117
|
-
});
|
|
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;
|
|
2280
|
+
for (const [manager, file] of Object.entries(lockFiles)) {
|
|
2281
|
+
if (existsSync(join(cwd, file))) {
|
|
2282
|
+
logger.debug(`Detected package manager from lockfile: ${manager}`);
|
|
2283
|
+
return manager;
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
const ua = process.env.npm_config_user_agent;
|
|
2287
|
+
if (ua) {
|
|
2288
|
+
const match = /(pnpm|yarn|npm|bun)/.exec(ua);
|
|
2289
|
+
if (match) {
|
|
2290
|
+
logger.debug(`Detected package manager from user agent: ${match[1]}`);
|
|
2291
|
+
return match[1];
|
|
2129
2292
|
}
|
|
2130
|
-
} finally {
|
|
2131
|
-
process.chdir(config.cwd);
|
|
2132
2293
|
}
|
|
2294
|
+
logger.debug("No package manager detected, defaulting to npm");
|
|
2295
|
+
return "npm";
|
|
2296
|
+
} catch (error) {
|
|
2297
|
+
logger.fail(`Error detecting package manager: ${error}, defaulting to npm`);
|
|
2298
|
+
return "npm";
|
|
2133
2299
|
}
|
|
2134
2300
|
}
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
logger.fail(`package.json not found at ${packageJsonPath}`);
|
|
2140
|
-
return;
|
|
2301
|
+
function determinePublishTag(version, configTag) {
|
|
2302
|
+
let tag = "latest";
|
|
2303
|
+
if (configTag) {
|
|
2304
|
+
tag = configTag;
|
|
2141
2305
|
}
|
|
2142
|
-
if (
|
|
2143
|
-
logger.
|
|
2144
|
-
|
|
2306
|
+
if (isPrerelease(version) && !configTag) {
|
|
2307
|
+
logger.warn('You are about to publish a "prerelease" version with the "latest" tag. To avoid mistake, the tag is set to "next"');
|
|
2308
|
+
tag = "next";
|
|
2145
2309
|
}
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
throw new Error(`Invalid package.json at ${packagePath}`);
|
|
2310
|
+
if (isPrerelease(version) && configTag === "latest") {
|
|
2311
|
+
logger.warn('Please note, you are about to publish a "prerelease" version with the "latest" tag.');
|
|
2149
2312
|
}
|
|
2150
|
-
return
|
|
2151
|
-
name: packageJson.name,
|
|
2152
|
-
version: packageJson.version,
|
|
2153
|
-
private: packageJson.private || false,
|
|
2154
|
-
path: packagePath
|
|
2155
|
-
};
|
|
2313
|
+
return tag;
|
|
2156
2314
|
}
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
}) {
|
|
2165
|
-
try {
|
|
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
|
-
});
|
|
2315
|
+
function getPackagesToPublishInSelectiveMode(sortedPackages, rootVersion) {
|
|
2316
|
+
const packagesToPublish = [];
|
|
2317
|
+
for (const pkg of sortedPackages) {
|
|
2318
|
+
const pkgJsonPath = join(pkg.path, "package.json");
|
|
2319
|
+
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
2320
|
+
if (pkgJson.version === rootVersion) {
|
|
2321
|
+
packagesToPublish.push(pkg);
|
|
2197
2322
|
}
|
|
2198
|
-
return {
|
|
2199
|
-
...packageJson,
|
|
2200
|
-
path: config.cwd,
|
|
2201
|
-
fromTag: from,
|
|
2202
|
-
commits,
|
|
2203
|
-
newVersion
|
|
2204
|
-
};
|
|
2205
|
-
} catch (error) {
|
|
2206
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2207
|
-
throw new Error(errorMessage);
|
|
2208
2323
|
}
|
|
2324
|
+
return packagesToPublish;
|
|
2209
2325
|
}
|
|
2210
|
-
function
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
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);
|
|
2326
|
+
async function getPackagesToPublishInIndependentMode(sortedPackages, config) {
|
|
2327
|
+
const packagesToPublish = [];
|
|
2328
|
+
for (const pkg of sortedPackages) {
|
|
2329
|
+
const { from, to } = await resolveTags({
|
|
2330
|
+
config,
|
|
2331
|
+
step: "publish",
|
|
2332
|
+
pkg,
|
|
2333
|
+
newVersion: pkg.newVersion || pkg.version
|
|
2334
|
+
});
|
|
2335
|
+
if (pkg.commits.length > 0) {
|
|
2336
|
+
packagesToPublish.push(pkg);
|
|
2337
|
+
logger.debug(`${pkg.name}: ${pkg.commits.length} commit(s) since ${from} \u2192 ${to}`);
|
|
2243
2338
|
}
|
|
2244
2339
|
}
|
|
2245
|
-
return
|
|
2340
|
+
return packagesToPublish;
|
|
2246
2341
|
}
|
|
2247
|
-
function
|
|
2248
|
-
|
|
2249
|
-
config,
|
|
2250
|
-
force
|
|
2251
|
-
}) {
|
|
2252
|
-
const releaseType = config.bump.type;
|
|
2253
|
-
if (force) {
|
|
2254
|
-
return determineReleaseType({
|
|
2255
|
-
currentVersion: pkg.version,
|
|
2256
|
-
commits: pkg.commits,
|
|
2257
|
-
releaseType,
|
|
2258
|
-
preid: config.bump.preid,
|
|
2259
|
-
types: config.types,
|
|
2260
|
-
force
|
|
2261
|
-
});
|
|
2262
|
-
}
|
|
2263
|
-
if (pkg.reason === "dependency") {
|
|
2264
|
-
if (isStableReleaseType(releaseType))
|
|
2265
|
-
return "patch";
|
|
2266
|
-
if (isPrerelease(pkg.version))
|
|
2267
|
-
return "prerelease";
|
|
2268
|
-
return "prepatch";
|
|
2269
|
-
}
|
|
2270
|
-
return determineReleaseType({
|
|
2271
|
-
currentVersion: pkg.version,
|
|
2272
|
-
commits: pkg.commits,
|
|
2273
|
-
releaseType,
|
|
2274
|
-
preid: config.bump.preid,
|
|
2275
|
-
types: config.types,
|
|
2276
|
-
force
|
|
2277
|
-
});
|
|
2342
|
+
function isYarnBerry() {
|
|
2343
|
+
return existsSync(path.join(process.cwd(), ".yarnrc.yml"));
|
|
2278
2344
|
}
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
const
|
|
2292
|
-
const
|
|
2293
|
-
if (
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
});
|
|
2303
|
-
for (const matchPath of matches) {
|
|
2304
|
-
if (foundPaths.has(matchPath))
|
|
2305
|
-
continue;
|
|
2306
|
-
const packageBase = readPackageJson(matchPath);
|
|
2307
|
-
if (!packageBase) {
|
|
2308
|
-
logger.debug(`Failed to read package.json at ${matchPath} - ignored`);
|
|
2309
|
-
continue;
|
|
2310
|
-
}
|
|
2311
|
-
if (packageBase.private) {
|
|
2312
|
-
logger.debug(`${packageBase.name} is private and will be ignored`);
|
|
2313
|
-
continue;
|
|
2314
|
-
}
|
|
2315
|
-
if (config.monorepo?.ignorePackageNames?.includes(packageBase.name)) {
|
|
2316
|
-
logger.debug(`${packageBase.name} ignored by config (monorepo.ignorePackageNames)`);
|
|
2317
|
-
continue;
|
|
2318
|
-
}
|
|
2319
|
-
if (!packageBase.version) {
|
|
2320
|
-
logger.warn(`${packageBase.name} has no version and will be ignored`);
|
|
2321
|
-
continue;
|
|
2322
|
-
}
|
|
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
|
-
});
|
|
2345
|
+
function getCommandArgs({
|
|
2346
|
+
packageManager,
|
|
2347
|
+
tag,
|
|
2348
|
+
config,
|
|
2349
|
+
otp,
|
|
2350
|
+
type
|
|
2351
|
+
}) {
|
|
2352
|
+
const args = type === "publish" ? ["publish", "--tag", tag] : ["whoami"];
|
|
2353
|
+
const registry = config.publish.registry;
|
|
2354
|
+
if (registry) {
|
|
2355
|
+
args.push("--registry", registry);
|
|
2356
|
+
}
|
|
2357
|
+
const isPnpmOrNpm = packageManager === "pnpm" || packageManager === "npm";
|
|
2358
|
+
const publishToken = config.publish.token;
|
|
2359
|
+
if (publishToken) {
|
|
2360
|
+
if (!registry) {
|
|
2361
|
+
logger.warn("Publish token provided but no registry specified");
|
|
2362
|
+
} else if (!isPnpmOrNpm) {
|
|
2363
|
+
logger.warn("Publish token only supported for pnpm and npm");
|
|
2364
|
+
} else {
|
|
2365
|
+
const registryUrl = new URL(registry);
|
|
2366
|
+
const authTokenKey = `--//${registryUrl.host}${registryUrl.pathname}:_authToken=${publishToken}`;
|
|
2367
|
+
args.push(authTokenKey);
|
|
2352
2368
|
}
|
|
2353
2369
|
}
|
|
2354
|
-
const
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
allPackages: packagesArray,
|
|
2358
|
-
packagesWithCommits
|
|
2359
|
-
});
|
|
2360
|
-
for (const pkg of expandedPackages) {
|
|
2361
|
-
packages.set(pkg.name, pkg);
|
|
2370
|
+
const finalOtp = otp ?? sessionOtp ?? config.publish.otp;
|
|
2371
|
+
if (finalOtp) {
|
|
2372
|
+
args.push("--otp", finalOtp);
|
|
2362
2373
|
}
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
pkg,
|
|
2366
|
-
config,
|
|
2367
|
-
force
|
|
2368
|
-
});
|
|
2369
|
-
const newVersion = releaseType ? getPackageNewVersion({
|
|
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
|
|
2380
|
-
});
|
|
2374
|
+
if (type === "auth") {
|
|
2375
|
+
return args;
|
|
2381
2376
|
}
|
|
2382
|
-
const
|
|
2383
|
-
if (
|
|
2384
|
-
|
|
2385
|
-
return [];
|
|
2377
|
+
const access = config.publish.access;
|
|
2378
|
+
if (access) {
|
|
2379
|
+
args.push("--access", access);
|
|
2386
2380
|
}
|
|
2387
|
-
|
|
2381
|
+
if (packageManager === "pnpm") {
|
|
2382
|
+
args.push("--no-git-checks");
|
|
2383
|
+
} else if (packageManager === "yarn") {
|
|
2384
|
+
args.push("--non-interactive");
|
|
2385
|
+
if (isYarnBerry())
|
|
2386
|
+
args.push("--no-git-checks");
|
|
2387
|
+
} else if (packageManager === "npm") {
|
|
2388
|
+
args.push("--yes");
|
|
2389
|
+
}
|
|
2390
|
+
return args;
|
|
2388
2391
|
}
|
|
2389
|
-
function
|
|
2390
|
-
|
|
2391
|
-
type,
|
|
2392
|
-
changelog
|
|
2393
|
-
}) {
|
|
2394
|
-
if (commit.type === "chore" && ["deps", "release"].includes(commit.scope) && !commit.isBreaking) {
|
|
2392
|
+
function isOtpError(error) {
|
|
2393
|
+
if (typeof error !== "object" || error === null)
|
|
2395
2394
|
return false;
|
|
2395
|
+
const errorMessage = "message" in error && typeof error.message === "string" ? error.message.toLowerCase() : "";
|
|
2396
|
+
return errorMessage.includes("otp") || errorMessage.includes("one-time password") || errorMessage.includes("eotp");
|
|
2397
|
+
}
|
|
2398
|
+
function promptOtpWithTimeout(timeout = 9e4) {
|
|
2399
|
+
return new Promise((resolve, reject) => {
|
|
2400
|
+
const timer = setTimeout(() => {
|
|
2401
|
+
reject(new Error("OTP input timeout"));
|
|
2402
|
+
}, timeout);
|
|
2403
|
+
input({
|
|
2404
|
+
message: "This operation requires a one-time password (OTP). Please enter your OTP:"
|
|
2405
|
+
}).then((otp) => {
|
|
2406
|
+
clearTimeout(timer);
|
|
2407
|
+
resolve(otp);
|
|
2408
|
+
}).catch((error) => {
|
|
2409
|
+
clearTimeout(timer);
|
|
2410
|
+
reject(error);
|
|
2411
|
+
});
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2414
|
+
async function handleOtpError() {
|
|
2415
|
+
if (isInCI()) {
|
|
2416
|
+
logger.error("OTP required but running in CI environment. Please provide OTP via config or `--otp` flag");
|
|
2417
|
+
throw new Error("OTP required in CI environment");
|
|
2396
2418
|
}
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
return
|
|
2419
|
+
logger.warn("Publish failed: OTP required");
|
|
2420
|
+
try {
|
|
2421
|
+
const otp = await promptOtpWithTimeout();
|
|
2422
|
+
logger.debug("OTP received, retrying publish...");
|
|
2423
|
+
return otp;
|
|
2424
|
+
} catch (promptError) {
|
|
2425
|
+
logger.error("Failed to get OTP:", promptError);
|
|
2426
|
+
throw promptError;
|
|
2402
2427
|
}
|
|
2403
|
-
return false;
|
|
2404
2428
|
}
|
|
2405
|
-
async function
|
|
2429
|
+
async function executePublishCommand({
|
|
2430
|
+
command,
|
|
2431
|
+
packageNameAndVersion,
|
|
2406
2432
|
pkg,
|
|
2407
|
-
from,
|
|
2408
|
-
to,
|
|
2409
2433
|
config,
|
|
2410
|
-
|
|
2434
|
+
dryRun
|
|
2411
2435
|
}) {
|
|
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");
|
|
2436
|
+
logger.debug(`Executing publish command (${command}) in ${pkg.path}`);
|
|
2437
|
+
if (dryRun) {
|
|
2438
|
+
logger.info(`[dry-run] ${packageNameAndVersion}: Run ${command}`);
|
|
2439
|
+
return;
|
|
2425
2440
|
}
|
|
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;
|
|
2441
|
+
const { stdout } = await execPromise(command, {
|
|
2442
|
+
noStderr: true,
|
|
2443
|
+
noStdout: true,
|
|
2444
|
+
logLevel: config.logLevel,
|
|
2445
|
+
cwd: pkg.path
|
|
2438
2446
|
});
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
logger.debug(`${pkg.name}: ${commits.length} commit(s) found`);
|
|
2442
|
-
} else {
|
|
2443
|
-
logger.debug(`${pkg.name}: No commits found`);
|
|
2447
|
+
if (stdout) {
|
|
2448
|
+
logger.debug(stdout);
|
|
2444
2449
|
}
|
|
2445
|
-
|
|
2450
|
+
logger.info(`Published ${packageNameAndVersion}`);
|
|
2446
2451
|
}
|
|
2447
|
-
function
|
|
2448
|
-
|
|
2449
|
-
|
|
2452
|
+
function getAuthCommand({
|
|
2453
|
+
packageManager,
|
|
2454
|
+
config,
|
|
2455
|
+
otp
|
|
2456
|
+
}) {
|
|
2457
|
+
const args = getCommandArgs({
|
|
2458
|
+
packageManager,
|
|
2459
|
+
tag: void 0,
|
|
2460
|
+
config,
|
|
2461
|
+
otp,
|
|
2462
|
+
type: "auth"
|
|
2463
|
+
});
|
|
2464
|
+
return `${packageManager} ${args.join(" ")}`;
|
|
2465
|
+
}
|
|
2466
|
+
function getPublishCommand({
|
|
2467
|
+
packageManager,
|
|
2468
|
+
tag,
|
|
2469
|
+
config,
|
|
2470
|
+
otp
|
|
2471
|
+
}) {
|
|
2472
|
+
const args = getCommandArgs({
|
|
2473
|
+
packageManager,
|
|
2474
|
+
tag,
|
|
2475
|
+
config,
|
|
2476
|
+
otp,
|
|
2477
|
+
type: "publish"
|
|
2478
|
+
});
|
|
2479
|
+
const baseCommand = packageManager === "yarn" && isYarnBerry() ? "yarn npm" : packageManager;
|
|
2480
|
+
return `${baseCommand} ${args.join(" ")}`;
|
|
2481
|
+
}
|
|
2482
|
+
async function publishPackage({
|
|
2483
|
+
pkg,
|
|
2484
|
+
config,
|
|
2485
|
+
packageManager,
|
|
2486
|
+
dryRun
|
|
2487
|
+
}) {
|
|
2488
|
+
const tag = determinePublishTag(pkg.newVersion || pkg.version, config.publish.tag);
|
|
2489
|
+
const packageNameAndVersion = getIndependentTag({ name: pkg.name, version: pkg.newVersion || pkg.version });
|
|
2490
|
+
logger.debug(`Building publish command for ${pkg.name}`);
|
|
2491
|
+
let dynamicOtp;
|
|
2492
|
+
const maxAttempts = 2;
|
|
2493
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
2494
|
+
try {
|
|
2495
|
+
const command = getPublishCommand({
|
|
2496
|
+
packageManager,
|
|
2497
|
+
tag,
|
|
2498
|
+
config,
|
|
2499
|
+
otp: dynamicOtp
|
|
2500
|
+
});
|
|
2501
|
+
logger.debug(`Publishing ${packageNameAndVersion} with tag '${tag}' with command: ${command}`);
|
|
2502
|
+
process.chdir(pkg.path);
|
|
2503
|
+
await executePublishCommand({
|
|
2504
|
+
command,
|
|
2505
|
+
packageNameAndVersion,
|
|
2506
|
+
pkg,
|
|
2507
|
+
config,
|
|
2508
|
+
dryRun
|
|
2509
|
+
});
|
|
2510
|
+
if (dynamicOtp && !sessionOtp) {
|
|
2511
|
+
sessionOtp = dynamicOtp;
|
|
2512
|
+
logger.debug("OTP stored for session");
|
|
2513
|
+
}
|
|
2514
|
+
return;
|
|
2515
|
+
} catch (error) {
|
|
2516
|
+
if (isOtpError(error) && attempt < maxAttempts - 1) {
|
|
2517
|
+
dynamicOtp = await handleOtpError();
|
|
2518
|
+
} else {
|
|
2519
|
+
logger.error(`Failed to publish ${packageNameAndVersion}:`, error);
|
|
2520
|
+
throw error;
|
|
2521
|
+
}
|
|
2522
|
+
} finally {
|
|
2523
|
+
process.chdir(config.cwd);
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2450
2526
|
}
|
|
2451
2527
|
|
|
2452
2528
|
async function bumpUnifiedMode({
|
|
@@ -2744,22 +2820,6 @@ async function bump(options = {}) {
|
|
|
2744
2820
|
}
|
|
2745
2821
|
}
|
|
2746
2822
|
|
|
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
2823
|
async function generateIndependentRootChangelog({
|
|
2764
2824
|
packages,
|
|
2765
2825
|
config,
|
|
@@ -2819,10 +2879,11 @@ async function generateSimpleRootChangelog({
|
|
|
2819
2879
|
if (!rootPackageRead) {
|
|
2820
2880
|
throw new Error("Failed to read root package.json");
|
|
2821
2881
|
}
|
|
2882
|
+
const newVersion = bumpResult?.newVersion || rootPackageRead.version;
|
|
2822
2883
|
const { from, to } = await resolveTags({
|
|
2823
2884
|
config,
|
|
2824
2885
|
step: "changelog",
|
|
2825
|
-
newVersion
|
|
2886
|
+
newVersion,
|
|
2826
2887
|
pkg: rootPackageRead
|
|
2827
2888
|
});
|
|
2828
2889
|
const fromTag = bumpResult?.fromTag || from;
|
|
@@ -2835,7 +2896,6 @@ async function generateSimpleRootChangelog({
|
|
|
2835
2896
|
to
|
|
2836
2897
|
});
|
|
2837
2898
|
logger.debug(`Generating ${rootPackage.name} changelog (${fromTag}...${to})`);
|
|
2838
|
-
const newVersion = bumpResult?.newVersion || rootPackage.version;
|
|
2839
2899
|
const rootChangelog = await generateChangelog({
|
|
2840
2900
|
pkg: rootPackage,
|
|
2841
2901
|
config,
|
|
@@ -2876,7 +2936,7 @@ async function changelog(options = {}) {
|
|
|
2876
2936
|
logger.start("Start generating changelogs");
|
|
2877
2937
|
if (config.changelog?.rootChangelog && config.monorepo) {
|
|
2878
2938
|
if (config.monorepo.versionMode === "independent") {
|
|
2879
|
-
const packages2 = await
|
|
2939
|
+
const packages2 = await getPackagesOrBumpedPackages({
|
|
2880
2940
|
config,
|
|
2881
2941
|
bumpResult: options.bumpResult,
|
|
2882
2942
|
suffix: options.suffix,
|
|
@@ -2900,27 +2960,28 @@ async function changelog(options = {}) {
|
|
|
2900
2960
|
logger.debug("Skipping root changelog generation");
|
|
2901
2961
|
}
|
|
2902
2962
|
logger.debug("Generating package changelogs...");
|
|
2903
|
-
const packages =
|
|
2963
|
+
const packages = await getPackagesOrBumpedPackages({
|
|
2904
2964
|
config,
|
|
2905
|
-
|
|
2965
|
+
bumpResult: options.bumpResult,
|
|
2906
2966
|
suffix: options.suffix,
|
|
2907
2967
|
force: options.force ?? false
|
|
2908
2968
|
});
|
|
2909
2969
|
logger.debug(`Processing ${packages.length} package(s)`);
|
|
2910
2970
|
let generatedCount = 0;
|
|
2911
2971
|
for await (const pkg of packages) {
|
|
2972
|
+
const newVersion = options.bumpResult?.bumpedPackages?.find((p) => p.name === pkg.name)?.newVersion || pkg.newVersion || pkg.version;
|
|
2912
2973
|
const { from, to } = await resolveTags({
|
|
2913
2974
|
config,
|
|
2914
2975
|
step: "changelog",
|
|
2915
2976
|
pkg,
|
|
2916
|
-
newVersion
|
|
2977
|
+
newVersion
|
|
2917
2978
|
});
|
|
2918
2979
|
logger.debug(`Processing ${pkg.name} (${from}...${to})`);
|
|
2919
2980
|
const changelog2 = await generateChangelog({
|
|
2920
2981
|
pkg,
|
|
2921
2982
|
config,
|
|
2922
2983
|
dryRun,
|
|
2923
|
-
newVersion
|
|
2984
|
+
newVersion
|
|
2924
2985
|
});
|
|
2925
2986
|
if (changelog2) {
|
|
2926
2987
|
writeChangelogToFile({
|
|
@@ -2960,11 +3021,11 @@ function providerReleaseSafetyCheck({ config, provider }) {
|
|
|
2960
3021
|
} else if (internalProvider === "gitlab") {
|
|
2961
3022
|
token = config.tokens?.gitlab || config.repo?.token;
|
|
2962
3023
|
} else {
|
|
2963
|
-
logger.error(`Unsupported Git provider: ${internalProvider || "unknown"}`);
|
|
3024
|
+
logger.error(`[provider-release-safety-check] Unsupported Git provider: ${internalProvider || "unknown"}`);
|
|
2964
3025
|
process.exit(1);
|
|
2965
3026
|
}
|
|
2966
3027
|
if (!token) {
|
|
2967
|
-
logger.error(`No token provided for ${internalProvider || "unknown"} - The release will not be published - Please refer to the documentation: https://louismazel.github.io/relizy/guide/installation#environment-setup`);
|
|
3028
|
+
logger.error(`[provider-release-safety-check] No token provided for ${internalProvider || "unknown"} - The release will not be published - Please refer to the documentation: https://louismazel.github.io/relizy/guide/installation#environment-setup`);
|
|
2968
3029
|
process.exit(1);
|
|
2969
3030
|
}
|
|
2970
3031
|
}
|
|
@@ -3031,6 +3092,40 @@ async function providerRelease(options = {}) {
|
|
|
3031
3092
|
}
|
|
3032
3093
|
}
|
|
3033
3094
|
|
|
3095
|
+
async function publishSafetyCheck({ config }) {
|
|
3096
|
+
logger.debug("[publish-safety-check] Running publish safety check");
|
|
3097
|
+
if (!config.safetyCheck || !config.release.publish || !config.publish.safetyCheck) {
|
|
3098
|
+
logger.debug("[publish-safety-check] Safety check disabled or publish disabled");
|
|
3099
|
+
return;
|
|
3100
|
+
}
|
|
3101
|
+
const packageManager = config.publish.packageManager || detectPackageManager(config.cwd);
|
|
3102
|
+
if (!packageManager) {
|
|
3103
|
+
logger.error("[publish-safety-check] Unable to detect package manager");
|
|
3104
|
+
process.exit(1);
|
|
3105
|
+
}
|
|
3106
|
+
const isPnpmOrNpm = packageManager === "pnpm" || packageManager === "npm";
|
|
3107
|
+
if (isPnpmOrNpm) {
|
|
3108
|
+
const authCommand = getAuthCommand({
|
|
3109
|
+
packageManager,
|
|
3110
|
+
config,
|
|
3111
|
+
otp: config.publish.otp
|
|
3112
|
+
});
|
|
3113
|
+
try {
|
|
3114
|
+
logger.debug("[publish-safety-check] Authenticating to package registry...");
|
|
3115
|
+
await execPromise(authCommand, {
|
|
3116
|
+
cwd: config.cwd,
|
|
3117
|
+
noStderr: true,
|
|
3118
|
+
noStdout: true,
|
|
3119
|
+
logLevel: config.logLevel,
|
|
3120
|
+
noSuccess: true
|
|
3121
|
+
});
|
|
3122
|
+
logger.info("[publish-safety-check] Successfully authenticated to package registry");
|
|
3123
|
+
} catch (error) {
|
|
3124
|
+
logger.error("[publish-safety-check] Failed to authenticate to package registry:", error);
|
|
3125
|
+
process.exit(1);
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3034
3129
|
async function publish(options = {}) {
|
|
3035
3130
|
const config = await loadRelizyConfig({
|
|
3036
3131
|
configName: options.configName,
|
|
@@ -3041,14 +3136,16 @@ async function publish(options = {}) {
|
|
|
3041
3136
|
otp: options.otp,
|
|
3042
3137
|
registry: options.registry,
|
|
3043
3138
|
tag: options.tag,
|
|
3044
|
-
buildCmd: options.buildCmd
|
|
3139
|
+
buildCmd: options.buildCmd,
|
|
3140
|
+
token: options.token
|
|
3045
3141
|
},
|
|
3046
|
-
logLevel: options.logLevel
|
|
3142
|
+
logLevel: options.logLevel,
|
|
3143
|
+
safetyCheck: options.safetyCheck
|
|
3047
3144
|
}
|
|
3048
3145
|
});
|
|
3049
3146
|
const dryRun = options.dryRun ?? false;
|
|
3050
3147
|
logger.debug(`Dry run: ${dryRun}`);
|
|
3051
|
-
const packageManager = detectPackageManager(
|
|
3148
|
+
const packageManager = config.publish.packageManager || detectPackageManager(config.cwd);
|
|
3052
3149
|
logger.debug(`Package manager: ${packageManager}`);
|
|
3053
3150
|
logger.info(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
|
|
3054
3151
|
if (config.publish.registry) {
|
|
@@ -3059,14 +3156,15 @@ async function publish(options = {}) {
|
|
|
3059
3156
|
}
|
|
3060
3157
|
try {
|
|
3061
3158
|
await executeHook("before:publish", config, dryRun);
|
|
3159
|
+
await publishSafetyCheck({ config });
|
|
3062
3160
|
const rootPackage = readPackageJson(config.cwd);
|
|
3063
3161
|
if (!rootPackage) {
|
|
3064
3162
|
throw new Error("Failed to read root package.json");
|
|
3065
3163
|
}
|
|
3066
3164
|
logger.start("Start publishing packages");
|
|
3067
|
-
const packages =
|
|
3165
|
+
const packages = await getPackagesOrBumpedPackages({
|
|
3068
3166
|
config,
|
|
3069
|
-
|
|
3167
|
+
bumpResult: options.bumpResult,
|
|
3070
3168
|
suffix: options.suffix,
|
|
3071
3169
|
force: options.force ?? false
|
|
3072
3170
|
});
|
|
@@ -3150,7 +3248,8 @@ function getReleaseConfig(options = {}) {
|
|
|
3150
3248
|
otp: options.otp,
|
|
3151
3249
|
registry: options.registry,
|
|
3152
3250
|
tag: options.tag,
|
|
3153
|
-
buildCmd: options.buildCmd
|
|
3251
|
+
buildCmd: options.buildCmd,
|
|
3252
|
+
token: options.publishToken
|
|
3154
3253
|
},
|
|
3155
3254
|
release: {
|
|
3156
3255
|
commit: options.commit,
|
|
@@ -3166,7 +3265,7 @@ function getReleaseConfig(options = {}) {
|
|
|
3166
3265
|
}
|
|
3167
3266
|
});
|
|
3168
3267
|
}
|
|
3169
|
-
function releaseSafetyCheck({
|
|
3268
|
+
async function releaseSafetyCheck({
|
|
3170
3269
|
config,
|
|
3171
3270
|
provider
|
|
3172
3271
|
}) {
|
|
@@ -3174,6 +3273,7 @@ function releaseSafetyCheck({
|
|
|
3174
3273
|
return;
|
|
3175
3274
|
}
|
|
3176
3275
|
providerReleaseSafetyCheck({ config, provider });
|
|
3276
|
+
await publishSafetyCheck({ config });
|
|
3177
3277
|
}
|
|
3178
3278
|
async function release(options = {}) {
|
|
3179
3279
|
const dryRun = options.dryRun ?? false;
|
|
@@ -3183,7 +3283,7 @@ async function release(options = {}) {
|
|
|
3183
3283
|
const config = await getReleaseConfig(options);
|
|
3184
3284
|
logger.debug(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
|
|
3185
3285
|
logger.debug(`Push: ${config.release.push}, Publish: ${config.release.publish}, Provider Release: ${config.release.providerRelease}`);
|
|
3186
|
-
releaseSafetyCheck({ config, provider: options.provider });
|
|
3286
|
+
await releaseSafetyCheck({ config, provider: options.provider });
|
|
3187
3287
|
try {
|
|
3188
3288
|
await executeHook("before:release", config, dryRun);
|
|
3189
3289
|
logger.box("Step 1/6: Bump versions");
|
|
@@ -3259,7 +3359,7 @@ async function release(options = {}) {
|
|
|
3259
3359
|
tag: config.publish.tag,
|
|
3260
3360
|
access: config.publish.access,
|
|
3261
3361
|
otp: config.publish.otp,
|
|
3262
|
-
|
|
3362
|
+
bumpResult,
|
|
3263
3363
|
dryRun,
|
|
3264
3364
|
config,
|
|
3265
3365
|
configName: options.configName,
|
|
@@ -3314,4 +3414,4 @@ Git provider: ${provider}`);
|
|
|
3314
3414
|
}
|
|
3315
3415
|
}
|
|
3316
3416
|
|
|
3317
|
-
export {
|
|
3417
|
+
export { getPackagesOrBumpedPackages as $, github as A, createGitlabRelease as B, gitlab as C, detectPackageManager as D, determinePublishTag as E, getPackagesToPublishInSelectiveMode as F, getPackagesToPublishInIndependentMode as G, getAuthCommand as H, publishPackage as I, readPackageJson as J, getRootPackage as K, readPackages as L, getPackages as M, getPackageCommits as N, hasLernaJson as O, getIndependentTag as P, getLastStableTag as Q, getLastTag as R, getLastRepoTag as S, getLastPackageTag as T, resolveTags as U, executeHook as V, isInCI as W, getCIName as X, executeFormatCmd as Y, executeBuildCmd as Z, isBumpedPackage as _, providerRelease as a, isGraduatingToStableBetweenVersion as a0, determineSemverChange as a1, determineReleaseType as a2, writeVersion as a3, getPackageNewVersion as a4, updateLernaVersion as a5, extractVersionFromPackageTag as a6, isPrerelease as a7, isStableReleaseType as a8, isPrereleaseReleaseType as a9, isGraduating as aa, getPreid as ab, isChangedPreid as ac, getBumpedPackageIndependently as ad, confirmBump as ae, getBumpedIndependentPackages as af, bump as b, changelog as c, publishSafetyCheck as d, publish as e, getDefaultConfig as f, generateChangelog as g, defineConfig as h, getPackageDependencies as i, getDependentsOf as j, expandPackagesToBumpWithDependents as k, loadRelizyConfig as l, getGitStatus as m, checkGitStatusIfDirty as n, fetchGitTags as o, providerReleaseSafetyCheck as p, detectGitProvider as q, release as r, parseGitRemoteUrl as s, topologicalSort as t, createCommitAndTags as u, pushCommitAndTags as v, writeChangelogToFile as w, getFirstCommit as x, getCurrentGitBranch as y, getCurrentGitRef as z };
|