UNPKG

@atomist/sdm-pack-changelog

Version:

Extension Pack for an Atomist SDM to manage changelogs

252 lines (247 loc) 10.3 kB
"use strict"; /* * Copyright © 2019 Atomist, Inc. * * 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. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const automation_client_1 = require("@atomist/automation-client"); const sdm_1 = require("@atomist/sdm"); const sdm_core_1 = require("@atomist/sdm-core"); const semver = require("semver"); function loglog(log, msg) { return __awaiter(this, void 0, void 0, function* () { automation_client_1.logger.debug(msg); log.write(`${msg}\n`); yield log.flush(); }); } function releaseVersion(version) { return version.replace(/-.*/, ""); } function rwlcVersion(gi) { return __awaiter(this, void 0, void 0, function* () { const version = yield sdm_core_1.readSdmVersion(gi.goalEvent.repo.owner, gi.goalEvent.repo.name, gi.goalEvent.repo.providerId, gi.goalEvent.sha, gi.goalEvent.branch, gi.context); return version; }); } function releaseOrPreRelease(version, gi) { const prVersion = preReleaseVersion(gi); if (prVersion) { return prVersion; } else { return releaseVersion(version); } } function preReleaseVersion(gi) { if (gi.goalEvent.push.after.tags) { const tag = gi.goalEvent.push.after.tags.find(t => { const preRelease = semver.prerelease(t.name); if (preRelease && ["M", "RC"].includes(preRelease[0])) { return true; } else { return false; } }); if (tag) { return tag.name; } } return undefined; } /** * Transform a SpawnWatchCommand into an ExecuteLogger suitable for * execution by executeLoggers. The operation is awaited and any * thrown exceptions are caught and transformed into an error result. * If an error occurs, it is logged. The result of the operation is * transformed into a ExecuteGoalResult. If an exception is caught, * the returned code is guaranteed to be non-zero. */ function spawnExecuteLogger(swc) { return (log) => __awaiter(this, void 0, void 0, function* () { let res; try { res = yield sdm_1.spawnLog(swc.cmd, swc.args, { log, cwd: swc.cwd }); } catch (e) { res = { code: -1, message: `Spawned command errored: ${swc.cmd} ${swc.args.join(" ")}: ${e.message}`, }; } if (res.error) { if (!res.message) { res.message = `Spawned command failed (status:${res.code}): ${swc.cmd} ` + swc.args.join(" "); } automation_client_1.logger.error(res.message); log.write(res.message); } return res; }); } /** * Transform a GitCommandGitProject operation into an ExecuteLogger * suitable for execution by executeLoggers. The operation is awaited * and any thrown exceptions are caught and transformed into an error * result. The returned standard out and standard error are written * to the log. If an error occurs, it is logged. The result of the * operation is transformed into a ExecuteGoalResult. If an error is * returned or exception caught, the returned code is guaranteed to be * non-zero. */ function gitExecuteLogger(gp, op) { return (log) => __awaiter(this, void 0, void 0, function* () { try { yield op(); } catch (e) { return automation_client_1.Failure; } return automation_client_1.Success; }); } /** * Execute an array of logged commands, creating a line-delimited * progress log beforehand, flushing after each command, and closing * it at the end. If any command fails, bail out and return the * failure result. Otherwise return Success. */ function executeLoggers(els, progressLog) { return __awaiter(this, void 0, void 0, function* () { const log = new sdm_1.DelimitedWriteProgressLogDecorator(progressLog, "\n"); for (const cmd of els) { const res = yield cmd(log); yield log.flush(); if (res.code !== 0) { yield log.close(); return res; } } yield log.close(); return automation_client_1.Success; }); } /** * Return today's date in a format that does not suck. * * @return today's date in YYYY-MM-DD format */ function formatDate(date) { const now = (date) ? date : new Date(); const year = now.getFullYear(); const monthDay = now.toLocaleDateString("en-US", { month: "2-digit", day: "2-digit" }).replace("/", "-"); return `${year}-${monthDay}`; } exports.formatDate = formatDate; /** * Modify changelog text to add release. * * @param changelog original changelog content * @param version release version * @return new changelog content */ function changelogAddRelease(changelog, version) { const releaseRegExp = new RegExp(`^## \\[${version}\\]`, "m"); if (releaseRegExp.test(changelog)) { return changelog; } const date = formatDate(); return changelog.replace(/^\[Unreleased\]:\s*(http.*\/compare)\/(\d+\.\d+\.\d+(?:-\S+)?)\.{3}HEAD/m, `[Unreleased]: \$1/${version}...HEAD ## [${version}][] - ${date} [${version}]: \$1/\$2...${version}`) .replace(/^##\s*\[Unreleased\]\((http.*\/compare)\/(\d+\.\d+\.\d+(?:-\S+)?)\.{3}HEAD\)/m, `## [Unreleased](\$1/${version}...HEAD) ## [${version}](\$1/\$2...${version}) - ${date}`) .replace(/^##\s*\[Unreleased\]\((http.*)\/tree\/HEAD\)/m, `## [Unreleased](\$1/compare/${version}...HEAD) ## [${version}](\$1/tree/${version}) - ${date}`); } exports.changelogAddRelease = changelogAddRelease; /** * Create entry in changelog for release. */ function executeReleaseChangelog() { return (gi) => __awaiter(this, void 0, void 0, function* () { const { credentials, id, context, configuration } = gi; return configuration.sdm.projectLoader.doWithProject({ credentials, id, context, readOnly: false }, (p) => __awaiter(this, void 0, void 0, function* () { const version = yield rwlcVersion(gi); const versionRelease = releaseOrPreRelease(version, gi); const gp = p; const log = new sdm_1.DelimitedWriteProgressLogDecorator(gi.progressLog, "\n"); const slug = `${gp.id.owner}/${gp.id.repo}`; const branch = gi.goalEvent.branch; const remote = gp.remote || "origin"; const preEls = [ gitExecuteLogger(gp, () => gp.checkout(branch)), spawnExecuteLogger({ cmd: "git", args: ["pull", remote, branch], cwd: gp.baseDir }), ]; yield loglog(log, `Pulling branch ${branch} of ${slug}`); const preRes = yield executeLoggers(preEls, gi.progressLog); if (preRes.code !== 0) { return preRes; } gp.branch = branch; const changelogPath = "CHANGELOG.md"; yield loglog(log, `Preparing changelog in ${slug} for release ${versionRelease}`); const egr = { code: 0 }; try { const changelogFile = yield gp.findFile(changelogPath); const changelog = yield changelogFile.getContent(); const newChangelog = changelogAddRelease(changelog, versionRelease); const compareUrlRegExp = new RegExp(`^\\[${versionRelease}\\]: (http\\S*)`, "m"); const compareUrlMatch = compareUrlRegExp.exec(newChangelog); if (compareUrlMatch && compareUrlMatch.length > 1 && compareUrlMatch[1]) { egr.externalUrls = [{ label: "Changelog", url: compareUrlMatch[1] }]; } if (newChangelog === changelog) { egr.message = `Changelog already contains release ${versionRelease}`; return egr; } yield changelogFile.setContent(newChangelog); egr.message = `Successfully added release ${versionRelease} to changelog`; } catch (e) { const message = `Failed to update changelog for release ${versionRelease}: ${e.message}`; automation_client_1.logger.error(message); log.write(`${message}\n`); yield log.flush(); yield log.close(); return { code: 1, message }; } yield loglog(log, egr.message); const postEls = [ gitExecuteLogger(gp, () => gp.commit(`Changelog: add release ${versionRelease} [atomist:generated]`)), gitExecuteLogger(gp, () => gp.push()), ]; yield loglog(log, `Committing and pushing changelog for ${slug} release ${versionRelease}`); const postRes = yield executeLoggers(postEls, gi.progressLog); if (postRes.code !== 0) { return postRes; } return egr; })); }); } exports.executeReleaseChangelog = executeReleaseChangelog; //# sourceMappingURL=releaseGoal.js.map