@atomist/atomist-sdm
Version:
Atomist SDM to deliver our own projects
382 lines (381 loc) • 20.8 kB
JavaScript
"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 });
/* tslint:disable:max-file-line-count */
const automation_client_1 = require("@atomist/automation-client");
const sdm_1 = require("@atomist/sdm");
const sdm_core_1 = require("@atomist/sdm-core");
const sdm_pack_docker_1 = require("@atomist/sdm-pack-docker");
const sdm_pack_issue_1 = require("@atomist/sdm-pack-issue");
const sdm_pack_node_1 = require("@atomist/sdm-pack-node");
const npmBuilder_1 = require("@atomist/sdm-pack-node/lib/build/npmBuilder");
const sdm_pack_spring_1 = require("@atomist/sdm-pack-spring");
const fs = require("fs-extra");
const path = require("path");
const addAtomistHeader_1 = require("../autofix/addAtomistHeader");
const CodeSnippetInline_1 = require("../autofix/CodeSnippetInline");
const importsFix_1 = require("../autofix/imports/importsFix");
const ReadmeSampleListingAutofix_1 = require("../autofix/ReadmeSampleListingAutofix");
const testNamingFix_1 = require("../autofix/test/testNamingFix");
const updateSupportFiles_1 = require("../autofix/updateSupportFiles");
const deleteDistTagOnBranchDeletion_1 = require("../event/deleteDistTagOnBranchDeletion");
const executeLogger_1 = require("../support/executeLogger");
const identityPushTests_1 = require("../support/identityPushTests");
const transformToProjectListener_1 = require("../support/transformToProjectListener");
const dependenciesToPeerDependencies_1 = require("../transform/dependenciesToPeerDependencies");
const rewriteImports_1 = require("../transform/rewriteImports");
const tryToUpdateAtomistDependencies_1 = require("../transform/tryToUpdateAtomistDependencies");
const tryToUpdateAtomistPeerDependencies_1 = require("../transform/tryToUpdateAtomistPeerDependencies");
const tryToUpdateDependency_1 = require("../transform/tryToUpdateDependency");
const updatePackageAuthor_1 = require("../transform/updatePackageAuthor");
const updatePackageVersion_1 = require("../transform/updatePackageVersion");
const goals_1 = require("./goals");
const release_1 = require("./release");
const NodeDefaultOptions = {
pushTest: sdm_1.allSatisfied(sdm_pack_node_1.IsNode, sdm_1.not(sdm_pack_spring_1.IsMaven)),
logInterpreter: sdm_1.LogSuppressor,
progressReporter: sdm_pack_node_1.NpmProgressReporter,
};
/**
* Add Node.js implementations of goals to SDM.
*
* @param sdm Software Delivery machine to modify
* @return modified software delivery machine
*/
function addNodeSupport(sdm) {
goals_1.version.with(Object.assign({}, NodeDefaultOptions, { name: "npm-versioner-global-sdm", versioner: (sdmGoal, p, log) => __awaiter(this, void 0, void 0, function* () {
const branch = sdmGoal.branch;
if (branch === sdmGoal.push.repo.defaultBranch) {
sdmGoal.branch = "global";
}
let v;
try {
v = yield sdm_pack_node_1.NodeProjectVersioner(sdmGoal, p, log);
}
finally {
sdmGoal.branch = branch;
}
return v;
}), pushTest: sdm_1.allSatisfied(sdm_pack_node_1.IsNode, identityPushTests_1.isOrgNamed("atomisthq"), identityPushTests_1.isNamed("global-sdm")) })).with(Object.assign({}, NodeDefaultOptions, { name: "npm-versioner", versioner: sdm_pack_node_1.NodeProjectVersioner, pushTest: sdm_1.allSatisfied(sdm_pack_node_1.IsNode, sdm_1.not(sdm_1.allSatisfied(identityPushTests_1.isOrgNamed("atomisthq"), identityPushTests_1.isNamed("global-sdm")))) }));
goals_1.autofix.with(addAtomistHeader_1.AddAtomistTypeScriptHeader)
.with(sdm_pack_node_1.TslintAutofix)
.with(importsFix_1.TypeScriptImports)
.with(sdm_pack_node_1.PackageLockUrlRewriteAutofix)
.with(testNamingFix_1.RenameTestFix)
.with(sdm_pack_node_1.AddThirdPartyLicenseAutofix)
.with(updateSupportFiles_1.UpdateSupportFilesAutofix)
.with(ReadmeSampleListingAutofix_1.ReadmeSampleListingAutofix)
.with(CodeSnippetInline_1.CodeSnippetInlineAutofix)
.withProjectListener(npmBuilder_1.npmInstallProjectListener({ scope: npmBuilder_1.CacheScope.Repository }));
goals_1.build.with(Object.assign({}, NodeDefaultOptions, { name: "npm-run-build", builder: sdm_pack_node_1.nodeBuilder({ command: "npm", args: ["run", "compile"] }, { command: "npm", args: ["test"] }), pushTest: NodeDefaultOptions.pushTest }))
.withProjectListener(npmBuilder_1.npmInstallProjectListener({ scope: npmBuilder_1.CacheScope.Repository }));
goals_1.autoCodeInspection.with(sdm_pack_node_1.TslintInspection)
.with(sdm_pack_node_1.npmAuditInspection())
.withProjectListener(npmBuilder_1.npmInstallProjectListener({ scope: npmBuilder_1.CacheScope.Repository }))
.withListener(sdm_pack_issue_1.singleIssuePerCategoryManaging(sdm.configuration.name, false, () => true))
.withListener(sdm_1.ApproveGoalIfErrorComments);
goals_1.publish.with(Object.assign({}, NodeDefaultOptions, { name: "npm-publish", goalExecutor: sdm_pack_node_1.executePublish(sdm_pack_node_1.NodeProjectIdentifier, sdm.configuration.sdm.npm) }))
.withProjectListener(transformToProjectListener_1.transformToProjectListener(dependenciesToPeerDependencies_1.dependenciesToPeerDependenciesTransform(/@atomist\/sdm.*/, /@atomist\/automation-client.*/), "package.json rewrite", sdm_1.allSatisfied(sdm_pack_node_1.IsNode, identityPushTests_1.isOrgNamed("atomist"), identityPushTests_1.isNamed("uhura"))))
.withProjectListener(npmBuilder_1.npmInstallProjectListener({ scope: npmBuilder_1.CacheScope.Repository }))
.withProjectListener(sdm_pack_node_1.NpmVersionProjectListener)
.withProjectListener(sdm_pack_node_1.NpmCompileProjectListener);
goals_1.publishWithApproval.with(Object.assign({}, NodeDefaultOptions, { name: "npm-publish", goalExecutor: sdm_pack_node_1.executePublish(sdm_pack_node_1.NodeProjectIdentifier, sdm.configuration.sdm.npm) }))
.withProjectListener(transformToProjectListener_1.transformToProjectListener(dependenciesToPeerDependencies_1.dependenciesToPeerDependenciesTransform(/@atomist\/sdm.*/, /@atomist\/automation-client.*/), "package.json rewrite", sdm_1.allSatisfied(sdm_pack_node_1.IsNode, identityPushTests_1.isOrgNamed("atomist"), identityPushTests_1.isNamed("uhura"))))
.withProjectListener(npmBuilder_1.npmInstallProjectListener({ scope: npmBuilder_1.CacheScope.Repository }))
.withProjectListener(sdm_pack_node_1.NpmVersionProjectListener)
.withProjectListener(sdm_pack_node_1.NpmCompileProjectListener);
goals_1.dockerBuild.with(Object.assign({}, NodeDefaultOptions, { name: "npm-docker-build-global-sdm", options: Object.assign({}, sdm.configuration.sdm.docker.t095sffbk, { push: true, builder: "docker" }), pushTest: sdm_1.allSatisfied(sdm_pack_node_1.IsNode, sdm_pack_docker_1.HasDockerfile, identityPushTests_1.isOrgNamed("atomisthq"), identityPushTests_1.isNamed("global-sdm")) })).with(Object.assign({}, NodeDefaultOptions, { name: "npm-docker-build", options: Object.assign({}, sdm.configuration.sdm.docker.hub, { push: true, builder: "docker" }), pushTest: sdm_1.allSatisfied(sdm_pack_node_1.IsNode, sdm_pack_docker_1.HasDockerfile, sdm_1.not(sdm_1.allSatisfied(identityPushTests_1.isOrgNamed("atomisthq"), identityPushTests_1.isNamed("global-sdm")))) }))
.withProjectListener(npmBuilder_1.npmInstallProjectListener({ scope: npmBuilder_1.CacheScope.Repository }))
.withProjectListener(sdm_pack_node_1.NpmVersionProjectListener)
.withProjectListener(sdm_pack_node_1.NpmCompileProjectListener);
goals_1.release.with(Object.assign({}, NodeDefaultOptions, { name: "npm-release", goalExecutor: executeReleaseNpm(sdm_pack_node_1.NodeProjectIdentifier, exports.NpmReleasePreparations, sdm.configuration.sdm.npm) }));
goals_1.releaseDocs.with(Object.assign({}, NodeDefaultOptions, { name: "npm-docs-release", goalExecutor: executeReleaseTypeDocs(exports.DocsReleasePreparations) }));
goals_1.releaseVersion.with(Object.assign({}, NodeDefaultOptions, { name: "npm-release-version", goalExecutor: release_1.executeReleaseVersion(sdm_pack_node_1.NodeProjectIdentifier, npmIncrementPatch) }));
sdm.addEvent(deleteDistTagOnBranchDeletion_1.deleteDistTagOnBranchDeletion(sdm.configuration.sdm.projectLoader, sdm.configuration.sdm.npm));
sdm.addCodeTransformCommand(tryToUpdateAtomistDependencies_1.TryToUpdateAtomistDependencies)
.addCodeTransformCommand(tryToUpdateDependency_1.TryToUpdateDependency)
.addCodeTransformCommand(updatePackageVersion_1.UpdatePackageVersion)
.addCodeTransformCommand(tryToUpdateAtomistPeerDependencies_1.TryToUpdateAtomistPeerDependencies)
.addCodeTransformCommand(updatePackageAuthor_1.UpdatePackageAuthor)
.addCodeTransformCommand(updateSupportFiles_1.UpdateSupportFilesTransform)
.addCodeTransformCommand(rewriteImports_1.RewriteImports)
.addCodeTransformCommand(testNamingFix_1.RenameTest);
return sdm;
}
exports.addNodeSupport = addNodeSupport;
/**
* Increment the patch version of a Node project managed by NPM.
*/
function npmIncrementPatch(p, log) {
return __awaiter(this, void 0, void 0, function* () {
return sdm_1.spawnLog("npm", ["version", "--no-git-tag-version", "patch"], { cwd: p.baseDir, log });
});
}
function npmPackageUrl(p) {
return `${p.registry}/${p.name}/-/${p.name}-${p.version}.tgz`;
}
/**
* Download the pre-release NPM package related to this project and
* goal invocation and provide information back about it. The caller
* is responsible for cleaning up the downloaded files and its parent
* directory.
*
* @param p Project to download NPM package for
* @param gi Download package generated by this goal invocation
* @return information about the NPM package
*/
function downloadNpmPackage(p, gi, v) {
return __awaiter(this, void 0, void 0, function* () {
const pjFile = yield p.getFile("package.json");
if (!pjFile) {
const msg = `NPM project does not have a package.json`;
automation_client_1.logger.error(msg);
throw new Error(msg);
}
const pjContents = yield pjFile.getContent();
let pj;
try {
pj = JSON.parse(pjContents);
}
catch (e) {
e.message = `Unable to parse package.json '${pjContents}': ${e.message}`;
automation_client_1.logger.error(e.message);
throw e;
}
if (!pj.name) {
const msg = `Unable to get NPM package name from package.json '${pjContents}'`;
automation_client_1.logger.error(msg);
throw new Error(msg);
}
const pkgVersion = (v) ? v : yield release_1.rwlcVersion(gi);
const npmOptions = automation_client_1.configurationValue("sdm.npm");
if (!npmOptions.registry) {
throw new Error(`No NPM registry defined in NPM options`);
}
const pkgUrl = npmPackageUrl({
registry: npmOptions.registry,
name: pj.name,
version: pkgVersion,
});
const tmpDir = path.join((process.env.TMPDIR || "/tmp"), `${p.name}-${automation_client_1.guid()}`);
const tgz = path.join(tmpDir, "package.tgz");
const cmds = [
{
cmd: { command: "curl", args: ["--output", tgz, "--silent", "--fail", "--create-dirs", pkgUrl] },
},
];
const els = cmds.map(executeLogger_1.spawnExecuteLogger);
const result = yield executeLogger_1.executeLoggers(els, gi.progressLog);
if (result.code !== 0) {
throw new Error(`Failed to download NPM package ${pkgUrl}: ${result.message}`);
}
return {
path: tgz,
url: pkgUrl,
version: pkgVersion,
};
});
}
exports.downloadNpmPackage = downloadNpmPackage;
/**
* Download published pre-release NPM package, replace the contents of
* project with the contents of the package, and set the version to
* the release version.
*/
function npmReleasePreparation(p, gi) {
return __awaiter(this, void 0, void 0, function* () {
const pkgInfo = yield downloadNpmPackage(p, gi);
const versionRelease = release_1.releaseOrPreRelease(pkgInfo.version, gi);
const tmpDir = path.dirname(pkgInfo.path);
const cmds = [
{
cmd: { command: "tar", args: ["-x", "-z", "-f", pkgInfo.path] },
cwd: tmpDir,
},
{
cmd: { command: "bash", args: ["-c", "rm -r *"] },
cwd: p.baseDir,
},
{
cmd: { command: "cp", args: ["-r", "package/.", p.baseDir] },
cwd: tmpDir,
},
{
cmd: { command: "npm", args: ["--no-git-tag-version", "version", versionRelease] },
cwd: p.baseDir,
},
{
cmd: { command: "rm", args: ["-rf", tmpDir] },
},
];
const els = cmds.map(executeLogger_1.spawnExecuteLogger);
return executeLogger_1.executeLoggers(els, gi.progressLog);
});
}
exports.npmReleasePreparation = npmReleasePreparation;
exports.NpmReleasePreparations = [npmReleasePreparation];
function executeReleaseNpm(projectIdentifier, preparations = exports.NpmReleasePreparations, options) {
if (!options.npmrc) {
throw new Error(`No npmrc defined in NPM options`);
}
return (gi) => __awaiter(this, void 0, void 0, function* () {
const { configuration, credentials, id, context } = gi;
return configuration.sdm.projectLoader.doWithProject({
credentials,
id,
context,
readOnly: false,
}, (project) => __awaiter(this, void 0, void 0, function* () {
if (!(yield sdm_1.projectConfigurationValue("npm.publish.enabled", project, true))) {
return {
code: 0,
description: "Publish disabled",
state: sdm_1.SdmGoalState.success,
};
}
yield fs.writeFile(path.join(project.baseDir, ".npmrc"), options.npmrc);
for (const preparation of preparations) {
const pResult = yield preparation(project, gi);
if (pResult && pResult.code !== 0) {
return pResult;
}
}
const args = [
"publish",
"--registry", options.registry,
"--access", options.access ? options.access : "restricted",
];
const v = yield release_1.rwlcVersion(gi);
const versionRelease = release_1.releaseOrPreRelease(v, gi);
if (release_1.isNextVersion(versionRelease)) {
args.push("--tag", "next");
}
const result = yield sdm_1.spawnLog("npm", args, {
cwd: project.baseDir,
log: gi.progressLog,
});
if (result.error) {
return result;
}
const pi = yield projectIdentifier(project);
const url = npmPackageUrl({
registry: options.registry,
name: pi.name,
version: pi.version,
});
if (options.status) {
yield sdm_core_1.github.createStatus(credentials.token, id, {
context: "npm/atomist/package",
description: "NPM package",
target_url: url,
state: "success",
});
}
const egr = {
code: result.code,
message: result.message,
targetUrl: url,
};
return egr;
}));
});
}
exports.executeReleaseNpm = executeReleaseNpm;
function typedocDir(baseDir) {
const oldDir = path.join(baseDir, "build", "typedoc");
const dir = path.join(baseDir, "doc");
if (fs.existsSync(oldDir)) {
return oldDir;
}
return dir;
}
function docsReleasePreparation(p, gi) {
return __awaiter(this, void 0, void 0, function* () {
const cmds = [
{
cmd: {
command: "npm",
args: ["ci"],
options: Object.assign({}, sdm_pack_node_1.DevelopmentEnvOptions, { log: gi.progressLog }),
},
cwd: p.baseDir,
},
{
cmd: { command: "npm", args: ["run", "compile"] },
cwd: p.baseDir,
},
{
cmd: { command: "npm", args: ["run", "doc"] },
cwd: p.baseDir,
},
];
const els = cmds.map(executeLogger_1.spawnExecuteLogger);
return executeLogger_1.executeLoggers(els, gi.progressLog);
});
}
exports.docsReleasePreparation = docsReleasePreparation;
exports.DocsReleasePreparations = [docsReleasePreparation];
/**
* Publish TypeDoc to gh-pages branch.
*/
function executeReleaseTypeDocs(preparations = exports.DocsReleasePreparations) {
return (gi) => __awaiter(this, void 0, void 0, function* () {
const { configuration, credentials, id, context } = gi;
return configuration.sdm.projectLoader.doWithProject({
credentials,
id,
context,
readOnly: false,
}, (project) => __awaiter(this, void 0, void 0, function* () {
for (const preparation of preparations) {
const pResult = yield preparation(project, gi);
if (pResult && pResult.code !== 0) {
return pResult;
}
}
const v = yield release_1.rwlcVersion(gi);
const versionRelease = release_1.releaseOrPreRelease(v, gi);
const commitMsg = `TypeDoc: publishing for version ${versionRelease}
[atomist:generated]`;
const docDir = typedocDir(project.baseDir);
const els = [executeLogger_1.spawnExecuteLogger({
cmd: { command: "touch", args: [path.join(docDir, ".nojekyll")] },
cwd: project.baseDir,
})];
const docProject = yield automation_client_1.NodeFsLocalProject.fromExistingDirectory(project.id, docDir);
const docGitProject = automation_client_1.GitCommandGitProject.fromProject(docProject, credentials);
const targetUrl = `https://${docGitProject.id.owner}.github.io/${docGitProject.id.repo}`;
const rrr = project.id;
els.push(executeLogger_1.gitExecuteLogger(docGitProject, () => docGitProject.init(), "init"), executeLogger_1.gitExecuteLogger(docGitProject, () => docGitProject.commit(commitMsg), "commit"), executeLogger_1.gitExecuteLogger(docGitProject, () => docGitProject.createBranch("gh-pages"), "createBranch"), executeLogger_1.gitExecuteLogger(docGitProject, () => docGitProject.setRemote(rrr.cloneUrl(credentials)), "setRemote"), executeLogger_1.gitExecuteLogger(docGitProject, () => docGitProject.push({ force: true }), "push"));
const gitRes = yield executeLogger_1.executeLoggers(els, gi.progressLog);
if (gitRes.code !== 0) {
return gitRes;
}
return Object.assign({}, automation_client_1.Success, { targetUrl });
}));
});
}
exports.executeReleaseTypeDocs = executeReleaseTypeDocs;
//# sourceMappingURL=nodeSupport.js.map