UNPKG

13.5 kBJavaScriptView Raw
1'use strict';
2
3var fs = require('fs-extra');
4var chalk = require('chalk');
5var ora = require('ora');
6var semver = require('semver');
7var minimatch = require('minimatch');
8var errors = require('@backstage/errors');
9var path = require('path');
10var run = require('./run-a95417b1.cjs.js');
11var index = require('./index-09611511.cjs.js');
12var Lockfile = require('./Lockfile-48dc675e.cjs.js');
13var packages = require('./packages-f8fc8f67.cjs.js');
14var lint = require('./lint-08d4074b.cjs.js');
15var cliCommon = require('@backstage/cli-common');
16var parallel = require('./parallel-8286d3fa.cjs.js');
17var releaseManifests = require('@backstage/release-manifests');
18require('global-agent/bootstrap');
19require('child_process');
20require('util');
21require('commander');
22require('@yarnpkg/parsers');
23require('@yarnpkg/lockfile');
24require('@manypkg/get-packages');
25require('lodash/partition');
26require('os');
27require('worker_threads');
28
29function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
30
31var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
32var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
33var ora__default = /*#__PURE__*/_interopDefaultLegacy(ora);
34var semver__default = /*#__PURE__*/_interopDefaultLegacy(semver);
35var minimatch__default = /*#__PURE__*/_interopDefaultLegacy(minimatch);
36
37const DEP_TYPES = [
38 "dependencies",
39 "devDependencies",
40 "peerDependencies",
41 "optionalDependencies"
42];
43const DEFAULT_PATTERN_GLOB = "@backstage/*";
44var bump = async (opts) => {
45 var _a;
46 const lockfilePath = index.paths.resolveTargetRoot("yarn.lock");
47 const lockfile = await Lockfile.Lockfile.load(lockfilePath);
48 let pattern = opts.pattern;
49 if (!pattern) {
50 console.log(`Using default pattern glob ${DEFAULT_PATTERN_GLOB}`);
51 pattern = DEFAULT_PATTERN_GLOB;
52 } else {
53 console.log(`Using custom pattern glob ${pattern}`);
54 }
55 let findTargetVersion;
56 let releaseManifest;
57 if (semver__default["default"].valid(opts.release)) {
58 releaseManifest = await releaseManifests.getManifestByVersion({ version: opts.release });
59 findTargetVersion = createStrictVersionFinder({
60 releaseManifest
61 });
62 } else {
63 if (opts.release === "next") {
64 const next = await releaseManifests.getManifestByReleaseLine({
65 releaseLine: "next"
66 });
67 const main = await releaseManifests.getManifestByReleaseLine({
68 releaseLine: "main"
69 });
70 releaseManifest = semver__default["default"].gt(next.releaseVersion, main.releaseVersion) ? next : main;
71 } else {
72 releaseManifest = await releaseManifests.getManifestByReleaseLine({
73 releaseLine: opts.release
74 });
75 }
76 findTargetVersion = createVersionFinder({
77 releaseLine: opts.releaseLine,
78 releaseManifest
79 });
80 }
81 const dependencyMap = await packages.mapDependencies(index.paths.targetDir, pattern);
82 const versionBumps = /* @__PURE__ */ new Map();
83 const unlocked = Array();
84 await parallel.runParallelWorkers({
85 parallelismFactor: 4,
86 items: dependencyMap.entries(),
87 async worker([name, pkgs]) {
88 var _a2, _b;
89 let target;
90 try {
91 target = await findTargetVersion(name);
92 } catch (error) {
93 if (errors.isError(error) && error.name === "NotFoundError") {
94 console.log(`Package info not found, ignoring package ${name}`);
95 return;
96 }
97 throw error;
98 }
99 for (const pkg of pkgs) {
100 if (semver__default["default"].satisfies(target, pkg.range)) {
101 if (((_a2 = semver__default["default"].minVersion(pkg.range)) == null ? void 0 : _a2.version) !== target) {
102 unlocked.push({ name, range: pkg.range, target });
103 }
104 continue;
105 }
106 versionBumps.set(pkg.name, ((_b = versionBumps.get(pkg.name)) != null ? _b : []).concat({
107 name,
108 location: pkg.location,
109 range: `^${target}`,
110 target
111 }));
112 }
113 }
114 });
115 const filter = (name) => minimatch__default["default"](name, pattern);
116 await parallel.runParallelWorkers({
117 parallelismFactor: 4,
118 items: lockfile.keys(),
119 async worker(name) {
120 var _a2;
121 if (!filter(name)) {
122 return;
123 }
124 let target;
125 try {
126 target = await findTargetVersion(name);
127 } catch (error) {
128 if (errors.isError(error) && error.name === "NotFoundError") {
129 console.log(`Package info not found, ignoring package ${name}`);
130 return;
131 }
132 throw error;
133 }
134 for (const entry of (_a2 = lockfile.get(name)) != null ? _a2 : []) {
135 if (!semver__default["default"].satisfies(target, entry.range)) {
136 continue;
137 }
138 unlocked.push({ name, range: entry.range, target });
139 }
140 }
141 });
142 console.log();
143 if (versionBumps.size === 0 && unlocked.length === 0) {
144 console.log(chalk__default["default"].green("All Backstage packages are up to date!"));
145 } else {
146 console.log(chalk__default["default"].yellow("Some packages are outdated, updating"));
147 console.log();
148 if (unlocked.length > 0) {
149 const removed = /* @__PURE__ */ new Set();
150 for (const { name, range, target } of unlocked) {
151 const existingEntry = (_a = lockfile.get(name)) == null ? void 0 : _a.find((e) => e.range === range);
152 if ((existingEntry == null ? void 0 : existingEntry.version) === target) {
153 continue;
154 }
155 const key = JSON.stringify({ name, range });
156 if (!removed.has(key)) {
157 removed.add(key);
158 console.log(`${chalk__default["default"].magenta("unlocking")} ${name}@${chalk__default["default"].yellow(range)} ~> ${chalk__default["default"].yellow(target)}`);
159 lockfile.remove(name, range);
160 }
161 }
162 await lockfile.save();
163 }
164 const breakingUpdates = /* @__PURE__ */ new Map();
165 await parallel.runParallelWorkers({
166 parallelismFactor: 4,
167 items: versionBumps.entries(),
168 async worker([name, deps]) {
169 var _a2;
170 const pkgPath = path.resolve(deps[0].location, "package.json");
171 const pkgJson = await fs__default["default"].readJson(pkgPath);
172 for (const dep of deps) {
173 console.log(`${chalk__default["default"].cyan("bumping")} ${dep.name} in ${chalk__default["default"].cyan(name)} to ${chalk__default["default"].yellow(dep.range)}`);
174 for (const depType of DEP_TYPES) {
175 if (depType in pkgJson && dep.name in pkgJson[depType]) {
176 const oldRange = pkgJson[depType][dep.name];
177 pkgJson[depType][dep.name] = dep.range;
178 const lockfileEntry = (_a2 = lockfile.get(dep.name)) == null ? void 0 : _a2.find((entry) => entry.range === oldRange);
179 if (lockfileEntry) {
180 const from = lockfileEntry.version;
181 const to = dep.target;
182 if (!semver__default["default"].satisfies(to, `^${from}`)) {
183 breakingUpdates.set(dep.name, { from, to });
184 }
185 }
186 }
187 }
188 }
189 await fs__default["default"].writeJson(pkgPath, pkgJson, { spaces: 2 });
190 }
191 });
192 console.log();
193 if (pattern === DEFAULT_PATTERN_GLOB) {
194 await bumpBackstageJsonVersion(releaseManifest.releaseVersion);
195 } else {
196 console.log(chalk__default["default"].yellow(`Skipping backstage.json update as custom pattern is used`));
197 }
198 await runYarnInstall();
199 if (breakingUpdates.size > 0) {
200 console.log();
201 console.log(chalk__default["default"].yellow("\u26A0\uFE0F The following packages may have breaking changes:"));
202 console.log();
203 for (const [name, { from, to }] of Array.from(breakingUpdates.entries()).sort()) {
204 console.log(` ${chalk__default["default"].yellow(name)} : ${chalk__default["default"].yellow(from)} ~> ${chalk__default["default"].yellow(to)}`);
205 let path;
206 if (name.startsWith("@backstage/plugin-")) {
207 path = `plugins/${name.replace("@backstage/plugin-", "")}`;
208 } else if (name.startsWith("@backstage/")) {
209 path = `packages/${name.replace("@backstage/", "")}`;
210 }
211 if (path) {
212 console.log(` https://github.com/backstage/backstage/blob/master/${path}/CHANGELOG.md`);
213 }
214 console.log();
215 }
216 } else {
217 console.log();
218 }
219 console.log(chalk__default["default"].green("Version bump complete!"));
220 }
221 console.log();
222 const dedupLockfile = await Lockfile.Lockfile.load(lockfilePath);
223 const result = dedupLockfile.analyze({
224 filter
225 });
226 if (result.newVersions.length > 0) {
227 throw new Error("Duplicate versions present after package bump");
228 }
229 const forbiddenNewRanges = result.newRanges.filter(({ name }) => lint.forbiddenDuplicatesFilter(name));
230 if (forbiddenNewRanges.length > 0) {
231 throw new Error(`Version bump failed for ${forbiddenNewRanges.map((i) => i.name).join(", ")}`);
232 }
233};
234function createStrictVersionFinder(options) {
235 const releasePackages = new Map(options.releaseManifest.packages.map((p) => [p.name, p.version]));
236 return async function findTargetVersion(name) {
237 console.log(`Checking for updates of ${name}`);
238 const manifestVersion = releasePackages.get(name);
239 if (manifestVersion) {
240 return manifestVersion;
241 }
242 throw new errors.NotFoundError(`Package ${name} not found in release manifest`);
243 };
244}
245function createVersionFinder(options) {
246 const {
247 releaseLine = "latest",
248 packageInfoFetcher = packages.fetchPackageInfo,
249 releaseManifest
250 } = options;
251 const distTag = releaseLine === "main" ? "latest" : releaseLine;
252 const found = /* @__PURE__ */ new Map();
253 const releasePackages = new Map(releaseManifest == null ? void 0 : releaseManifest.packages.map((p) => [p.name, p.version]));
254 return async function findTargetVersion(name) {
255 const existing = found.get(name);
256 if (existing) {
257 return existing;
258 }
259 console.log(`Checking for updates of ${name}`);
260 const manifestVersion = releasePackages.get(name);
261 if (manifestVersion) {
262 return manifestVersion;
263 }
264 const info = await packageInfoFetcher(name);
265 const latestVersion = info["dist-tags"].latest;
266 if (!latestVersion) {
267 throw new Error(`No target 'latest' version found for ${name}`);
268 }
269 const taggedVersion = info["dist-tags"][distTag];
270 if (distTag === "latest" || !taggedVersion) {
271 found.set(name, latestVersion);
272 return latestVersion;
273 }
274 const latestVersionDateStr = info.time[latestVersion];
275 const taggedVersionDateStr = info.time[taggedVersion];
276 if (!latestVersionDateStr) {
277 throw new Error(`No time available for version '${latestVersion}' of ${name}`);
278 }
279 if (!taggedVersionDateStr) {
280 throw new Error(`No time available for version '${taggedVersion}' of ${name}`);
281 }
282 const latestVersionRelease = new Date(latestVersionDateStr).getTime();
283 const taggedVersionRelease = new Date(taggedVersionDateStr).getTime();
284 if (latestVersionRelease > taggedVersionRelease) {
285 found.set(name, latestVersion);
286 return latestVersion;
287 }
288 found.set(name, taggedVersion);
289 return taggedVersion;
290 };
291}
292async function bumpBackstageJsonVersion(version) {
293 const backstageJsonPath = index.paths.resolveTargetRoot(cliCommon.BACKSTAGE_JSON);
294 const backstageJson = await fs__default["default"].readJSON(backstageJsonPath).catch((e) => {
295 if (e.code === "ENOENT") {
296 return;
297 }
298 throw e;
299 });
300 const prevVersion = backstageJson == null ? void 0 : backstageJson.version;
301 if (prevVersion === version) {
302 return;
303 }
304 const { yellow, cyan, green } = chalk__default["default"];
305 if (prevVersion) {
306 const from = encodeURIComponent(prevVersion);
307 const to = encodeURIComponent(version);
308 const link = `https://backstage.github.io/upgrade-helper/?from=${from}&to=${to}`;
309 console.log(yellow(`Upgraded from release ${green(prevVersion)} to ${green(version)}, please review these template changes:`));
310 console.log();
311 console.log(` ${cyan(link)}`);
312 console.log();
313 } else {
314 console.log(yellow(`Your project is now at version ${version}, which has been written to ${cliCommon.BACKSTAGE_JSON}`));
315 }
316 await fs__default["default"].writeJson(backstageJsonPath, { ...backstageJson, version }, {
317 spaces: 2,
318 encoding: "utf8"
319 });
320}
321async function runYarnInstall() {
322 const spinner = ora__default["default"]({
323 prefixText: `Running ${chalk__default["default"].blue("yarn install")} to install new versions`,
324 spinner: "arc",
325 color: "green"
326 }).start();
327 const installOutput = new Array();
328 try {
329 await run.run("yarn", ["install"], {
330 env: {
331 FORCE_COLOR: "true",
332 ...Object.fromEntries(Object.entries(process.env).map(([name, value]) => name.startsWith("npm_") ? [name, void 0] : [name, value]))
333 },
334 stdoutLogFunc: (data) => installOutput.push(data),
335 stderrLogFunc: (data) => installOutput.push(data)
336 });
337 spinner.succeed();
338 } catch (error) {
339 spinner.fail();
340 process.stdout.write(Buffer.concat(installOutput));
341 throw error;
342 }
343}
344
345exports.bumpBackstageJsonVersion = bumpBackstageJsonVersion;
346exports.createStrictVersionFinder = createStrictVersionFinder;
347exports.createVersionFinder = createVersionFinder;
348exports["default"] = bump;
349//# sourceMappingURL=bump-db363289.cjs.js.map