release-please
Version:
generate release PRs based on the conventionalcommits.org spec
253 lines (250 loc) • 11.5 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.PHPYoshi = void 0;
const base_1 = require("./base");
const changelog_1 = require("../updaters/changelog");
const root_composer_update_packages_1 = require("../updaters/php/root-composer-update-packages");
const php_client_version_1 = require("../updaters/php/php-client-version");
const version_1 = require("../version");
const commit_1 = require("../commit");
const commit_split_1 = require("../util/commit-split");
const default_1 = require("../updaters/default");
const tag_name_1 = require("../util/tag-name");
const pull_request_title_1 = require("../util/pull-request-title");
const branch_name_1 = require("../util/branch-name");
const pull_request_body_1 = require("../util/pull-request-body");
const errors_1 = require("../errors");
const CHANGELOG_SECTIONS = [
{ type: 'feat', section: 'Features' },
{ type: 'fix', section: 'Bug Fixes' },
{ type: 'perf', section: 'Performance Improvements' },
{ type: 'revert', section: 'Reverts' },
{ type: 'docs', section: 'Documentation' },
{ type: 'misc', section: 'Miscellaneous' },
{ type: 'chore', section: 'Chores', hidden: true },
{ type: 'style', section: 'Styles', hidden: true },
{ type: 'refactor', section: 'Code Refactoring', hidden: true },
{ type: 'test', section: 'Tests', hidden: true },
{ type: 'build', section: 'Build System', hidden: true },
{ type: 'ci', section: 'Continuous Integration', hidden: true },
];
class PHPYoshi extends base_1.BaseStrategy {
constructor(options) {
super({
...options,
changelogSections: CHANGELOG_SECTIONS,
});
}
async buildReleasePullRequest(commits, latestRelease, draft, labels = [], bumpOnlyOptions) {
var _a, _b, _c;
const conventionalCommits = await this.postProcessCommits((0, commit_1.parseConventionalCommits)(commits, this.logger));
if (!bumpOnlyOptions && conventionalCommits.length === 0) {
this.logger.info(`No commits for path: ${this.path}, skipping`);
return undefined;
}
const versionOverrides = {};
commits.forEach(commit => {
var _a;
Object.entries(parseVersionOverrides(((_a = commit.pullRequest) === null || _a === void 0 ? void 0 : _a.body) || '')).forEach(([directory, version]) => {
versionOverrides[directory] = version;
});
});
const newVersion = latestRelease
? await this.versioningStrategy.bump(latestRelease.tag.version, conventionalCommits)
: this.initialReleaseVersion();
const cs = new commit_split_1.CommitSplit();
const splitCommits = cs.split(conventionalCommits);
const topLevelDirectories = Object.keys(splitCommits).sort();
const versionsMap = new Map();
const directoryVersionContents = {};
const component = await this.getComponent();
const newVersionTag = new tag_name_1.TagName(newVersion, component, this.tagSeparator, this.includeVInTag);
let releaseNotesBody = `## ${newVersion.toString()}`;
for (const directory of topLevelDirectories) {
try {
const contents = await this.github.getFileContentsOnBranch(this.addPath(`${directory}/VERSION`), this.targetBranch);
const composer = await this.github.getFileJson(this.addPath(`${directory}/composer.json`), this.targetBranch);
directoryVersionContents[directory] = {
versionContents: contents,
composer,
};
const newVersion = versionOverrides[directory]
? version_1.Version.parse(versionOverrides[directory])
: await this.versioningStrategy.bump(version_1.Version.parse(contents.parsedContent), splitCommits[directory]);
versionsMap.set(composer.name, newVersion);
const partialReleaseNotes = await this.changelogNotes.buildNotes(splitCommits[directory], {
host: this.changelogHost,
owner: this.repository.owner,
repository: this.repository.repo,
version: newVersion.toString(),
previousTag: (_a = latestRelease === null || latestRelease === void 0 ? void 0 : latestRelease.tag) === null || _a === void 0 ? void 0 : _a.toString(),
currentTag: newVersionTag.toString(),
targetBranch: this.targetBranch,
changelogSections: this.changelogSections,
});
releaseNotesBody = updatePHPChangelogEntry(`${composer.name} ${newVersion.toString()}`, releaseNotesBody, partialReleaseNotes);
}
catch (err) {
if (err instanceof errors_1.FileNotFoundError) {
// if the updated path has no VERSION, assume this isn't a
// module that needs updating.
continue;
}
else {
throw err;
}
}
}
const pullRequestTitle = pull_request_title_1.PullRequestTitle.ofComponentTargetBranchVersion(component || '', this.targetBranch, newVersion);
const branchName = component
? branch_name_1.BranchName.ofComponentTargetBranch(component, this.targetBranch)
: branch_name_1.BranchName.ofTargetBranch(this.targetBranch);
const updates = await this.buildUpdates({
changelogEntry: releaseNotesBody,
newVersion,
versionsMap,
latestVersion: latestRelease === null || latestRelease === void 0 ? void 0 : latestRelease.tag.version,
commits: conventionalCommits, // TODO(@bcoe): these commits will need to be divided into multiple changelog.json updates.
});
for (const directory in directoryVersionContents) {
const componentInfo = directoryVersionContents[directory];
const version = versionsMap.get(componentInfo.composer.name);
if (!version) {
this.logger.warn(`No version found for ${componentInfo.composer.name}`);
continue;
}
updates.push({
path: this.addPath(`${directory}/VERSION`),
createIfMissing: false,
cachedFileContents: componentInfo.versionContents,
updater: new default_1.DefaultUpdater({
version,
}),
});
updates.push({
path: this.addPath(`${directory}/composer.json`),
createIfMissing: false,
updater: new root_composer_update_packages_1.RootComposerUpdatePackages({
version,
}),
});
if ((_c = (_b = componentInfo.composer.extra) === null || _b === void 0 ? void 0 : _b.component) === null || _c === void 0 ? void 0 : _c.entry) {
updates.push({
path: this.addPath(`${directory}/${componentInfo.composer.extra.component.entry}`),
createIfMissing: false,
updater: new php_client_version_1.PHPClientVersion({
version,
}),
});
}
}
// TODO use pullrequest header here?
const pullRequestBody = new pull_request_body_1.PullRequestBody([
{
component,
version: newVersion,
notes: releaseNotesBody,
},
]);
return {
title: pullRequestTitle,
body: pullRequestBody,
updates,
labels: [...labels, ...this.extraLabels],
headRefName: branchName.toString(),
version: newVersion,
draft: draft !== null && draft !== void 0 ? draft : false,
};
}
async parsePullRequestBody(pullRequestBody) {
const body = pull_request_body_1.PullRequestBody.parse(pullRequestBody, this.logger);
if (!body) {
return undefined;
}
const component = await this.getComponent();
const notes = body.releaseData
.map(release => {
var _a;
return `<details><summary>${release.component}: ${(_a = release.version) === null || _a === void 0 ? void 0 : _a.toString()}</summary>\n\n${release.notes}\n</details>`;
})
.join('\n\n');
return new pull_request_body_1.PullRequestBody([{ component, notes }], {
footer: body.footer,
header: body.header,
});
}
async buildUpdates(options) {
const updates = [];
const version = options.newVersion;
const versionsMap = options.versionsMap;
updates.push({
path: this.addPath(this.changelogPath),
createIfMissing: true,
updater: new changelog_1.Changelog({
version,
changelogEntry: options.changelogEntry,
}),
});
// update VERSION file
updates.push({
path: this.addPath('VERSION'),
createIfMissing: false,
updater: new default_1.DefaultUpdater({
version,
}),
});
// update the aggregate package information in the root composer.json
updates.push({
path: this.addPath('composer.json'),
createIfMissing: false,
updater: new root_composer_update_packages_1.RootComposerUpdatePackages({
version,
versionsMap,
}),
});
return updates;
}
}
exports.PHPYoshi = PHPYoshi;
function parseVersionOverrides(body) {
// look for 'BEGIN_VERSION_OVERRIDE' section of pull request body
const versionOverrides = {};
if (body) {
const overrideMessage = (body.split('BEGIN_VERSION_OVERRIDE')[1] || '')
.split('END_VERSION_OVERRIDE')[0]
.trim();
if (overrideMessage) {
overrideMessage.split('\n').forEach(line => {
const [directory, version] = line.split(':');
versionOverrides[directory.trim()] = version.trim();
});
}
}
return versionOverrides;
}
function updatePHPChangelogEntry(pkgKey, changelogEntry, entryUpdate) {
// Remove the first line of the entry, in favor of <summary>.
// This also allows us to use the same regex for extracting release
// notes (since the string "## v0.0.0" doesn't show up multiple times).
const entryUpdateSplit = entryUpdate.split(/\r?\n/);
entryUpdateSplit.shift();
entryUpdate = entryUpdateSplit.join('\n');
return `${changelogEntry}
<details><summary>${pkgKey}</summary>
${entryUpdate}
</details>`;
}
//# sourceMappingURL=php-yoshi.js.map