desktop/scripts/publish/publish.ts (181 lines of code) (raw):

import { exec } from "child_process"; import "colors"; import * as fs from "fs"; import * as path from "path"; import * as yesno from "yesno"; import { createIssue, createPullRequest, getMilestone, githubToken, listMilestoneIssues, listPullRequests, } from "./github-api"; const MAIN_BRANCH = "main"; const root = path.resolve(path.join(__dirname, "../../..")); const allMessages: string[] = []; const repoName = "Azure/BatchExplorer"; const newIssueBody = ` - [x] Update version in package.json - [x] Update changelog - [x] Update third party notices if needed - [ ] Double check the prod build is working`; function log(text: string) { allMessages.push(text); // eslint-disable no-console console.log(text); } function failure(message: string) { log(`✘ ${message}`.red); } function success(message: string) { log(`✔ ${message}`.green); } async function run(command: string): Promise<{ stdout: string, stderr: string }> { return new Promise<{ stdout: string, stderr: string }>((resolve, reject) => { exec(command, { maxBuffer: 100_000_000 }, (error, stdout, stderr) => { if (error) { reject(error); return; } resolve({ stdout, stderr }); }); }); } function checkGithubToken() { if (!githubToken) { failure("GH_TOKEN environment variable is not set." + "Please create a new env var with a github token to use the github api"); } else { success("GH_TOKEN has a github token"); } } /** * This goes back to the main branch and pulls the latest */ async function gotoMainBranch() { await run(`git checkout ${MAIN_BRANCH}`); await run("git pull"); success(`Checkout to ${MAIN_BRANCH} branch and pulled latest`); } async function loadMilestone(milestoneId: number) { return getMilestone(repoName, milestoneId); } async function getCurrentBranch(): Promise<string> { const { stdout } = await run(`git symbolic-ref --short -q HEAD`); return stdout.trim(); } function getMilestoneId() { if (process.argv.length < 3) { throw new Error("No milestone id was provided."); } return parseInt(process.argv[2], 10); } async function confirmVersion(version: string): Promise<void> { const ok = await yesno({ question: `Bump to version ${version} (From milestone title) [Y/n]`, defaultValue: true }); if (ok) { success(`A new release for version ${version} will be prepared`); return; } throw new Error("milestone version wasn't confirmed. Please change milestone title"); } function calcNextVersion(version: string) { const match = /^(\d+\.)(\d+)(\.\d+)$/.exec(version); return `${match[1]}${parseInt(match[2], 10) + 1}${match[3]}`; } function getPreparationBranchName(version: string) { return `release/prepare-${version}`; } async function switchToNewBranch(branchName: string) { await run(`git checkout -B ${branchName}`); success(`Created a new branch ${branchName}`); } async function bumpVersion(version) { const currentBranch = await getCurrentBranch(); const nextVersion = calcNextVersion(version); const bumpBranch = `release/bump-${nextVersion}`; await gotoMainBranch(); await switchToNewBranch(bumpBranch); await run(`npm run version --no-git-tag-version --allow-same-version ${nextVersion}`); await run(`git commit -am "Bump version to ${nextVersion}"`); await run(`git push --set-upstream origin ${bumpBranch}`); await run(`git checkout "${currentBranch}"`); success(`Updated version in package.json to ${nextVersion} (branch: ${bumpBranch})`); } async function updateChangeLog(version, milestoneId) { const { stdout } = await run(`gh-changelog-gen --repo ${repoName} ${milestoneId} --formatter markdown`); const changelogFile = path.join(root, "CHANGELOG.md"); const changelogContent = fs.readFileSync(changelogFile); if (changelogContent.indexOf(`## ${version}`) === -1) { fs.writeFileSync(changelogFile, `${stdout}\n${changelogContent}`); success("Added changes to the changelog"); } else { success("Changelog already contains the changes for this version"); } } async function updateThirdParty() { await run(`npm run ts scripts/lca/generate-third-party`); success("Updated ThirdPartyNotices.txt"); } async function commitChanges() { await run(`git commit --allow-empty -am "Updated changelog and version."`); } async function push(branchName: string) { await run(`git push --set-upstream origin ${branchName}`); } async function createIssueIfNot(milestoneId, version) { const title = `Prepare for release of version ${version}`; const issues = await listMilestoneIssues(repoName, milestoneId); let issue = issues.filter(x => x.title === title)[0]; if (issue) { success(`Issue was already created earlier ${issue.html_url}`); } else { issue = await createIssue(repoName, title, newIssueBody, milestoneId); success(`Created a new issue ${issue.html_url}`); } return issue; } async function createPullrequestIfNot(version, releaseBranch, issue) { const title = `Prepare for release ${version}`; const body = `fix #${issue.number}`; const prs = await listPullRequests(repoName, releaseBranch); let pr = prs[0]; if (pr) { success(`There is already a pr created ${pr.html_url}`); } else { pr = await createPullRequest(repoName, title, body, releaseBranch); success(`Create a new pull request ${pr.html_url}`); } return pr; } async function buildApp() { // eslint-disable no-console console.log("Building the app with npm run build:package..."); await run("npm run build:package"); success("Build the app successfully. Starting it now, double check it is working correctly"); await run(path.join(root, "release/win-unpacked/BatchExplorer.exe")); } async function startPublish() { checkGithubToken(); const milestoneId = getMilestoneId(); const milestone = await loadMilestone(milestoneId); if (!milestone.title && milestone["message"]) { throw new Error(`Error fetching milestone: ${milestone["message"]}`); } const version = milestone.title; await confirmVersion(version); const releaseBranch = getPreparationBranchName(version); const branch = await getCurrentBranch(); if (branch !== releaseBranch) { await gotoMainBranch(); await switchToNewBranch(releaseBranch); } await updateChangeLog(version, milestoneId); await updateThirdParty(); await commitChanges(); await push(releaseBranch); const issue = await createIssueIfNot(milestoneId, version); await createPullrequestIfNot(version, releaseBranch, issue); await buildApp(); await bumpVersion(version); } startPublish().then(() => { success("First step of publishing is completed. Now wait for the pull request to complete."); process.exit(0); }).catch((error) => { failure(error.message); process.exit(1); });