UNPKG

@coveo/semantic-monorepo-tools

Version:

A library of helper functions to do SemVer2 compliant releases from Conventional Commits in monorepos

171 lines (154 loc) 5.32 kB
import { getLastTag, parseCommits, getCommits, getCurrentVersion, getNextVersion, npmBumpVersion, generateChangelog, writeChangelog, gitPushTags, gitTag, npmPublish, gitCreateBranch, gitCheckoutBranch, gitAdd, gitSetupSshRemote, gitSetupUser, getCurrentBranchName, getSHA1fromRef, gitWriteTree, gitCommitTree, gitUpdateRef, gitPublishBranch, gitSetRefOnCommit, gitPush, gitDeleteRemoteBranch, } from "@coveo/semantic-monorepo-tools"; import angularChangelogConvention from "conventional-changelog-angular"; import { Octokit } from "octokit"; import { createAppAuth } from "@octokit/auth-app"; // Get all commits since last release bump the root package.json version. (async () => { //#region Constants const PATH = "."; const VERSION_PREFIX = "v"; const CONVENTION = await angularChangelogConvention(); const REPO_OWNER = "coveo"; const REPO_NAME = "semantic-monorepo-tools"; const GIT_USERNAME = "developer-experience-bot[bot]"; const GIT_EMAIL = "91079284+developer-experience-bot[bot]@users.noreply.github.com"; const GIT_SSH_REMOTE = "deploy"; //#endregion // #region Setup Git await gitSetupSshRemote( REPO_OWNER, REPO_NAME, process.env.DEPLOY_KEY, GIT_SSH_REMOTE, ); await gitSetupUser(GIT_USERNAME, GIT_EMAIL); // #endregion //#region GitHub authentication const authSecrets = { appId: process.env.RELEASER_APP_ID, privateKey: process.env.RELEASER_PRIVATE_KEY, clientId: process.env.RELEASER_CLIENT_ID, clientSecret: process.env.RELEASER_CLIENT_SECRET, installationId: process.env.RELEASER_INSTALLATION_ID, }; const octokit = new Octokit({ authStrategy: createAppAuth, auth: authSecrets, }); //#endregion //#region Find current and new versions const lastTag = await getLastTag({ prefix: VERSION_PREFIX }); // Passing an empty string allow empty commits (i.e. that does not modify any files) to be included. const commits = await getCommits("", lastTag); const parsedCommits = parseCommits(commits, CONVENTION.parserOpts); const bumpInfo = CONVENTION.recommendedBumpOpts.whatBump(parsedCommits); const currentVersion = getCurrentVersion(PATH); const newVersion = getNextVersion(currentVersion, bumpInfo); const newVersionTag = `${VERSION_PREFIX}${newVersion}`; //#endregion // Bump the NPM version. await npmBumpVersion(newVersion, PATH); //#region Generate changelog if needed let changelog = ""; if (parsedCommits.length > 0) { changelog = await generateChangelog( parsedCommits, newVersion, { host: "https://github.com", owner: REPO_OWNER, repository: REPO_NAME, linkReferences: true, currentTag: newVersionTag, previousTag: lastTag, }, CONVENTION.writerOpts, ); await writeChangelog(PATH, changelog); } //#endregion //#region Commit changelog, tag version and push const tempBranchName = `release/${newVersion}`; const mainBranchName = await getCurrentBranchName(); const mainBranchCurrentSHA = await getSHA1fromRef(mainBranchName); // Create a temporary branch and check it out. await gitCreateBranch(tempBranchName); await gitCheckoutBranch(tempBranchName); // Stage all the changes (mainly the changelog)... await gitAdd("."); //... and create a Git tree object with the changes). const treeSHA = await gitWriteTree(); // Create a new commit that references the Git tree object. const commitTree = await gitCommitTree(treeSHA, tempBranchName, "tempcommit"); // Update the HEAD of the temp branch to point to the new commit, then publish the temp branch. await gitUpdateRef("HEAD", commitTree); await gitPublishBranch("origin", tempBranchName); /** * Once we pushed the temp branch, the tree object is then known to the remote repository. * We can now create a new commit that references the tree object using the GitHub API. * The fact that we use the API makes the commit 'verified'. * The commit is directly created on the GitHub repository, not on the local repository. */ const commit = await octokit.rest.git.createCommit({ message: `chore(release): ${newVersion} [skip ci]`, owner: REPO_OWNER, repo: REPO_NAME, tree: treeSHA, parents: [mainBranchCurrentSHA], }); // Forcefully reset `main` to the commit we just created with the GitHub API. await gitSetRefOnCommit( GIT_SSH_REMOTE, `refs/heads/${mainBranchName}`, commit.data.sha, ); // Push the branch using the SSH remote to bypass any GitHub checks. await gitCheckoutBranch(mainBranchName); await gitPush({ remote: GIT_SSH_REMOTE, refs: [mainBranchName] }); // Finally, delete the temp branch. await gitDeleteRemoteBranch(GIT_SSH_REMOTE, tempBranchName); //#endregion //#region Create & push tag await gitTag(newVersionTag); await gitPushTags(); //#endregion // Publish the new version on NPM await npmPublish(PATH, { provenance: true }); //#region Create GitHub Release on last tag const [, ...bodyArray] = changelog.split("\n"); await octokit.rest.repos.createRelease({ owner: REPO_OWNER, repo: REPO_NAME, tag_name: newVersionTag, name: `Release ${newVersionTag}`, body: bodyArray.join("\n"), }); //#endregion })();