@atomist/atomist-sdm
Version:
Atomist SDM to deliver our own projects
184 lines • 9.84 kB
JavaScript
;
/*
* 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) {
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) : new P(function (resolve) { resolve(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 ProjectOperationCredentials_1 = require("@atomist/automation-client/lib/operations/common/ProjectOperationCredentials");
const sdm_1 = require("@atomist/sdm");
const sdm_pack_node_1 = require("@atomist/sdm-pack-node");
const crypto = require("crypto");
const fs = require("fs-extra");
const path = require("path");
const goals_1 = require("./goals");
const nodeSupport_1 = require("./nodeSupport");
const release_1 = require("./release");
const homebrewFormulaGlob = ".atomist/homebrew/*.rb";
exports.HasHomebrewFormula = sdm_1.pushTest("Has Homebrew formula template", (pi) => __awaiter(this, void 0, void 0, function* () { return automation_client_1.projectUtils.fileExists(pi.project, homebrewFormulaGlob, () => true); }));
/**
* Compute SHA256 hash of file contents.
*
* @param file path to file
* @return hex SHA256 of file contents
*/
function fileSha256(file) {
return __awaiter(this, void 0, void 0, function* () {
const hash = crypto.createHash("sha256");
const fileBuffer = yield fs.readFile(file);
return hash.update(fileBuffer).digest("hex");
});
}
exports.fileSha256 = fileSha256;
/**
* Create the Homebrew formula and commit it to the tap.
*/
function executeReleaseHomebrew(projectIdentifier) {
return (gi) => __awaiter(this, void 0, void 0, function* () {
const { configuration, credentials, id, context } = gi;
return configuration.sdm.projectLoader.doWithProject({ credentials, id, context, readOnly: true }, (project) => __awaiter(this, void 0, void 0, function* () {
const log = gi.progressLog;
try {
const version = yield release_1.rwlcVersion(gi);
const versionRelease = release_1.releaseOrPreRelease(version, gi);
const pkgInfo = yield nodeSupport_1.downloadNpmPackage(project, gi, versionRelease);
log.write(`Creating Homebrew formula for ${project.name} version ${versionRelease}`);
const pkgSha = yield fileSha256(pkgInfo.path);
log.write(`Calculated SHA256 for ${path.basename(pkgInfo.path)}: ${pkgSha}`);
try {
yield sdm_1.execPromise("rm", ["-rf", path.dirname(pkgInfo.path)]);
}
catch (e) {
const errMsg = `Failed to remove downloaded NPM package: ${e.message}`;
automation_client_1.logger.warn(errMsg);
log.write(`${errMsg}\n${e.stdout}\n${e.stderr}`);
}
const formulae = {};
yield automation_client_1.projectUtils.doWithFiles(project, homebrewFormulaGlob, (f) => __awaiter(this, void 0, void 0, function* () {
log.write(`Creating Homebrew formula ${f.name}`);
const content = yield f.getContent();
formulae[f.name] = content.replace(/%URL%/g, pkgInfo.url)
.replace(/%VERSION%/g, versionRelease)
.replace(/%SHA256%/g, pkgSha);
}));
if (Object.keys(formulae).length < 1) {
log.write(`No formulae updated`);
return {
code: 0,
message: "Found no formula to update",
};
}
const formulaeRepo = automation_client_1.GitHubRepoRef.from({
owner: id.owner,
repo: "homebrew-core",
});
log.write(`Cloning ${formulaeRepo.owner}/${formulaeRepo.repo}`);
const formulaeProject = yield automation_client_1.GitCommandGitProject.cloned(credentials, formulaeRepo, { depth: 1000 });
const execOpts = { cwd: formulaeProject.baseDir };
const logOutput = yield sdm_1.execPromise("git", ["log", "-1", "--format=%at"], execOpts);
const sinceTimestamp = Number.parseInt(logOutput.stdout.trim(), 10) - 120;
const brewSlug = "Homebrew/homebrew-core";
const brewRemote = "brew";
const brewRemoteUrl = `https://github.com/${brewSlug}.git`;
const brewBranch = "brew-master";
log.write(`Adding Homebrew remote`);
yield sdm_1.execPromise("git", ["remote", "add", brewRemote, brewRemoteUrl], execOpts);
log.write(`Fetching Homebrew remote master`);
yield sdm_1.execPromise("git", ["fetch", "--no-tags", `--shallow-since=${sinceTimestamp}`, brewRemote, `+master:${brewBranch}`], execOpts);
log.write(`Rebasing onto Homebrew remote master`);
yield sdm_1.execPromise("git", ["rebase", brewBranch], execOpts);
log.write(`Pushing changes from Homebrew remote master`);
yield formulaeProject.push();
const firstFormulaBasename = Object.keys(formulae)[0].replace(/\.rb$/, "");
const prBranch = `${firstFormulaBasename}-${versionRelease}`;
log.write(`Creating branch ${prBranch} for PR`);
yield formulaeProject.createBranch(prBranch);
for (const [formulaName, formulaContent] of Object.entries(formulae)) {
const formulaPath = `Formula/${formulaName}`;
log.write(`Updating ${formulaPath}`);
const formulaFile = yield formulaeProject.getFile(formulaPath);
if (formulaFile) {
yield formulaFile.setContent(formulaContent);
}
else {
yield formulaeProject.addFile(formulaPath, formulaContent);
}
}
log.write(`Committing Homebrew formula changes: ${Object.keys(formulae).join(" ")}`);
const title = `${firstFormulaBasename} ${versionRelease}`;
yield formulaeProject.commit(title);
log.write(`Pushing Homebrew formula changes`);
yield formulaeProject.push();
const httpClient = configuration.http.client.factory.create();
if (!ProjectOperationCredentials_1.isTokenCredentials(credentials)) {
log.write("Provided credentials do not contain GitHub.com token");
return {
code: 1,
message: "Unable to create PR because credentials did not contain token",
};
}
const brewPrUrl = `https://api.github.com/repos/${brewSlug}/pulls`;
const brewPrOptions = {
body: {
base: "master",
body: "Created by @atomist-bot.",
head: `${formulaeRepo.owner}:${prBranch}`,
title,
},
headers: {
Accept: "application/vnd.github.v3+json",
Authorization: `token ${credentials.token}`,
},
method: automation_client_1.HttpMethod.Post,
};
log.write(`Creating PR in ${brewSlug}`);
const prResp = yield httpClient.exchange(brewPrUrl, brewPrOptions);
const prNumber = prResp.body.number;
const prUrl = prResp.body.html_url;
log.write(`Created PR ${brewSlug}#${prNumber} ${prUrl}`);
return {
code: 0,
message: `Successfully created PR against ${brewSlug}`,
externalUrls: [{ label: `${brewSlug}#${prNumber}`, url: prUrl }],
};
}
catch (e) {
const msg = `Failed to update Homebrew formulae: ${e.message}`;
automation_client_1.logger.error(msg);
log.write(msg);
throw e;
}
}));
});
}
exports.executeReleaseHomebrew = executeReleaseHomebrew;
function addHomebrewSupport(sdm) {
goals_1.releaseHomebrew.with({
name: "homebrew-release",
pushTest: sdm_1.allSatisfied(sdm_pack_node_1.IsNode, exports.HasHomebrewFormula),
logInterpreter: sdm_1.LogSuppressor,
goalExecutor: executeReleaseHomebrew(sdm_pack_node_1.NodeProjectIdentifier),
});
return sdm;
}
exports.addHomebrewSupport = addHomebrewSupport;
//# sourceMappingURL=homebrewSupport.js.map