release-please
Version:
generate release PRs based on the conventionalcommits.org spec
352 lines • 15.7 kB
JavaScript
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeWorkspace = void 0;
const package_lock_json_1 = require("../updaters/node/package-lock-json");
const version_1 = require("../version");
const pull_request_title_1 = require("../util/pull-request-title");
const pull_request_body_1 = require("../util/pull-request-body");
const branch_name_1 = require("../util/branch-name");
const changelog_1 = require("../updaters/changelog");
const workspace_1 = require("./workspace");
const composite_1 = require("../updaters/composite");
const package_json_1 = require("../updaters/node/package-json");
const versioning_strategy_1 = require("../versioning-strategy");
/**
* The plugin analyzed a cargo workspace and will bump dependencies
* of managed packages if those dependencies are being updated.
*
* If multiple node packages are being updated, it will merge them
* into a single node package.
*/
class NodeWorkspace extends workspace_1.WorkspacePlugin {
constructor(github, targetBranch, repositoryConfig, options = {}) {
super(github, targetBranch, repositoryConfig, options);
this.strategiesByPath = {};
this.releasesByPath = {};
this.alwaysLinkLocal = options.alwaysLinkLocal === false ? false : true;
this.updatePeerDependencies = options.updatePeerDependencies === true;
}
async buildAllPackages(candidates) {
var _a;
const candidatesByPath = new Map();
for (const candidate of candidates) {
candidatesByPath.set(candidate.path, candidate);
}
const candidatesByPackage = {};
const packagesByPath = new Map();
for (const path in this.repositoryConfig) {
const config = this.repositoryConfig[path];
if (config.releaseType !== 'node') {
continue;
}
const candidate = candidatesByPath.get(path);
if (candidate) {
this.logger.debug(`Found candidate pull request for path: ${candidate.path}`);
const packagePath = (0, workspace_1.addPath)(candidate.path, 'package.json');
const packageUpdate = candidate.pullRequest.updates.find(update => update.path === packagePath);
const contents = (_a = packageUpdate === null || packageUpdate === void 0 ? void 0 : packageUpdate.cachedFileContents) !== null && _a !== void 0 ? _a : (await this.github.getFileContentsOnBranch(packagePath, this.targetBranch));
const packageJson = JSON.parse(contents.parsedContent);
const pkg = {
name: packageJson.name,
path,
version: packageJson.version,
dependencies: packageJson.dependencies || {},
devDependencies: packageJson.devDependencies || {},
peerDependencies: packageJson.peerDependencies || {},
optionalDependencies: packageJson.optionalDependencies || {},
jsonContent: contents.parsedContent,
};
packagesByPath.set(candidate.path, pkg);
candidatesByPackage[pkg.name] = candidate;
// }
}
else {
const packagePath = (0, workspace_1.addPath)(path, 'package.json');
this.logger.debug(`No candidate pull request for path: ${path} - inspect package from ${packagePath}`);
const contents = await this.github.getFileContentsOnBranch(packagePath, this.targetBranch);
const packageJson = JSON.parse(contents.parsedContent);
const pkg = {
name: packageJson.name,
path,
version: packageJson.version,
dependencies: packageJson.dependencies || {},
devDependencies: packageJson.devDependencies || {},
peerDependencies: packageJson.peerDependencies || {},
optionalDependencies: packageJson.optionalDependencies || {},
jsonContent: contents.parsedContent,
};
packagesByPath.set(path, pkg);
}
}
const allPackages = Array.from(packagesByPath.values());
return {
allPackages,
candidatesByPackage,
};
}
bumpVersion(pkg) {
const version = version_1.Version.parse(pkg.version);
const strategy = this.strategiesByPath[pkg.path];
if (strategy)
return strategy.versioningStrategy.bump(version, []);
return new versioning_strategy_1.PatchVersionUpdate().bump(version);
}
updateCandidate(existingCandidate, pkg, updatedVersions) {
// Update version of the package
const newVersion = updatedVersions.get(pkg.name);
if (!newVersion) {
throw new Error(`Didn't find updated version for ${pkg.name}`);
}
const updatedPackage = {
...pkg,
version: newVersion.toString(),
};
const updater = new package_json_1.PackageJson({
version: newVersion,
versionsMap: updatedVersions,
updatePeerDependencies: this.updatePeerDependencies,
});
const dependencyNotes = getChangelogDepsNotes(pkg, updatedPackage, updatedVersions, this.logger);
existingCandidate.pullRequest.updates =
existingCandidate.pullRequest.updates.map(update => {
if (update.path === (0, workspace_1.addPath)(existingCandidate.path, 'package.json')) {
update.updater = new composite_1.CompositeUpdater(update.updater, updater);
}
else if (update.path === (0, workspace_1.addPath)(existingCandidate.path, 'package-lock.json')) {
update.updater = new package_lock_json_1.PackageLockJson({
version: newVersion,
versionsMap: updatedVersions,
});
}
else if (update.updater instanceof changelog_1.Changelog) {
if (dependencyNotes) {
update.updater.changelogEntry =
(0, workspace_1.appendDependenciesSectionToChangelog)(update.updater.changelogEntry, dependencyNotes, this.logger);
}
}
return update;
});
// append dependency notes
if (dependencyNotes) {
if (existingCandidate.pullRequest.body.releaseData.length > 0) {
existingCandidate.pullRequest.body.releaseData[0].notes =
(0, workspace_1.appendDependenciesSectionToChangelog)(existingCandidate.pullRequest.body.releaseData[0].notes, dependencyNotes, this.logger);
}
else {
existingCandidate.pullRequest.body.releaseData.push({
component: updatedPackage.name,
version: existingCandidate.pullRequest.version,
notes: (0, workspace_1.appendDependenciesSectionToChangelog)('', dependencyNotes, this.logger),
});
}
}
return existingCandidate;
}
async newCandidate(pkg, updatedVersions) {
// Update version of the package
const newVersion = updatedVersions.get(pkg.name);
if (!newVersion) {
throw new Error(`Didn't find updated version for ${pkg.name}`);
}
const updatedPackage = {
...pkg,
version: newVersion.toString(),
};
const dependencyNotes = getChangelogDepsNotes(pkg, updatedPackage, updatedVersions, this.logger);
const strategy = this.strategiesByPath[updatedPackage.path];
const latestRelease = this.releasesByPath[updatedPackage.path];
const basePullRequest = strategy
? await strategy.buildReleasePullRequest([], latestRelease, false, [], {
newVersion,
})
: undefined;
if (basePullRequest) {
return this.updateCandidate({
path: pkg.path,
pullRequest: basePullRequest,
config: {
releaseType: 'node',
},
}, pkg, updatedVersions);
}
const pullRequest = {
title: pull_request_title_1.PullRequestTitle.ofTargetBranch(this.targetBranch),
body: new pull_request_body_1.PullRequestBody([
{
component: updatedPackage.name,
version: newVersion,
notes: (0, workspace_1.appendDependenciesSectionToChangelog)('', dependencyNotes, this.logger),
},
]),
updates: [
{
path: (0, workspace_1.addPath)(updatedPackage.path, 'package.json'),
createIfMissing: false,
updater: new package_json_1.PackageJson({
version: newVersion,
versionsMap: updatedVersions,
updatePeerDependencies: this.updatePeerDependencies,
}),
},
{
path: (0, workspace_1.addPath)(updatedPackage.path, 'package-lock.json'),
createIfMissing: false,
updater: new package_json_1.PackageJson({
version: newVersion,
versionsMap: updatedVersions,
updatePeerDependencies: this.updatePeerDependencies,
}),
},
{
path: (0, workspace_1.addPath)(updatedPackage.path, 'CHANGELOG.md'),
createIfMissing: false,
updater: new changelog_1.Changelog({
version: newVersion,
changelogEntry: (0, workspace_1.appendDependenciesSectionToChangelog)('', dependencyNotes, this.logger),
}),
},
],
labels: [],
headRefName: branch_name_1.BranchName.ofTargetBranch(this.targetBranch).toString(),
version: newVersion,
draft: false,
};
return {
path: updatedPackage.path,
pullRequest,
config: {
releaseType: 'node',
},
};
}
postProcessCandidates(candidates, _updatedVersions) {
if (candidates.length === 0) {
return candidates;
}
const [candidate] = candidates;
// check for root lock file in pull request
let hasRootLockFile;
for (let i = 0; i < candidate.pullRequest.updates.length; i++) {
if (candidate.pullRequest.updates[i].path === '.package-lock.json' ||
candidate.pullRequest.updates[i].path === './package-lock.json' ||
candidate.pullRequest.updates[i].path === 'package-lock.json' ||
candidate.pullRequest.updates[i].path === '/package-lock.json') {
hasRootLockFile = true;
break;
}
}
// if there is a root lock file, then there is no additional pull request update necessary.
if (hasRootLockFile) {
return candidates;
}
candidate.pullRequest.updates.push({
path: 'package-lock.json',
createIfMissing: false,
updater: new package_lock_json_1.PackageLockJson({
versionsMap: _updatedVersions,
}),
});
return candidates;
}
async buildGraph(allPackages) {
const graph = new Map();
const workspacePackageNames = new Set(allPackages.map(packageJson => packageJson.name));
for (const packageJson of allPackages) {
const allDeps = Object.keys(this.combineDeps(packageJson));
const workspaceDeps = allDeps.filter(dep => workspacePackageNames.has(dep));
graph.set(packageJson.name, {
deps: workspaceDeps,
value: packageJson,
});
}
return graph;
}
inScope(candidate) {
return candidate.config.releaseType === 'node';
}
packageNameFromPackage(pkg) {
return pkg.name;
}
pathFromPackage(pkg) {
return pkg.path;
}
combineDeps(packageJson) {
var _a, _b, _c, _d;
return {
...((_a = packageJson.dependencies) !== null && _a !== void 0 ? _a : {}),
...((_b = packageJson.devDependencies) !== null && _b !== void 0 ? _b : {}),
...((_c = packageJson.optionalDependencies) !== null && _c !== void 0 ? _c : {}),
...(this.updatePeerDependencies
? (_d = packageJson.peerDependencies) !== null && _d !== void 0 ? _d : {}
: {}),
};
}
async preconfigure(strategiesByPath, _commitsByPath, _releasesByPath) {
// Using preconfigure to siphon releases and strategies.
this.strategiesByPath = strategiesByPath;
this.releasesByPath = _releasesByPath;
return strategiesByPath;
}
}
exports.NodeWorkspace = NodeWorkspace;
function getChangelogDepsNotes(original, updated, updateVersions, logger) {
var _a;
let depUpdateNotes = '';
const depTypes = [
'dependencies',
'devDependencies',
'peerDependencies',
'optionalDependencies',
];
const updates = new Map();
for (const depType of depTypes) {
const depUpdates = [];
const pkgDepTypes = updated[depType];
if (pkgDepTypes === undefined) {
continue;
}
for (const [depName, currentDepVer] of Object.entries(pkgDepTypes)) {
const newVersion = updateVersions.get(depName);
if (!newVersion) {
logger.debug(`${depName} was not bumped, ignoring`);
continue;
}
const origDepVer = (_a = original[depType]) === null || _a === void 0 ? void 0 : _a[depName];
const newVersionString = (0, package_json_1.newVersionWithRange)(origDepVer, newVersion);
if (currentDepVer.startsWith('workspace:')) {
depUpdates.push(`\n * ${depName} bumped to ${newVersionString}`);
}
else if (newVersionString !== origDepVer) {
depUpdates.push(`\n * ${depName} bumped from ${origDepVer} to ${newVersionString}`);
//handle case when "workspace:" version is used
}
}
if (depUpdates.length > 0) {
updates.set(depType, depUpdates);
}
}
for (const [dt, notes] of updates) {
depUpdateNotes += `\n * ${dt}`;
for (const note of notes) {
depUpdateNotes += note;
}
}
if (depUpdateNotes) {
return `* The following workspace dependencies were updated${depUpdateNotes}`;
}
return '';
}
//# sourceMappingURL=node-workspace.js.map
;