UNPKG

@atomist/atomist-sdm

Version:

Atomist SDM to deliver our own projects

184 lines 9.84 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) { 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