diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index 17bab504b4..c21b7b7106 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -201,7 +201,7 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - # Creates the next available tag with format "release---[.]" + # Grabs the version in the root package.json and creates a tag on GitHub - name: Create a release tag id: create_tag run: node scripts/create-release-tag.js diff --git a/package.json b/package.json index d30453add0..345de5770d 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "lint-staged": "^12.2.0", "minimist": "^1.2.5", "prettier": "^2.2.1", + "semver": "^7.3.2", "shx": "^0.3.2", "ts-node": "^10.4.0", "typescript": "~4.5.4", diff --git a/scripts/create-release-tag.js b/scripts/create-release-tag.js index aa06464958..a71afe513b 100755 --- a/scripts/create-release-tag.js +++ b/scripts/create-release-tag.js @@ -34,26 +34,12 @@ async function main() { const octokit = new Octokit({ auth: GITHUB_TOKEN }); - const date = new Date(); - const yyyy = date.getUTCFullYear(); - const mm = String(date.getUTCMonth() + 1).padStart(2, '0'); - const dd = String(date.getUTCDate()).padStart(2, '0'); - const baseTagName = `release-${yyyy}-${mm}-${dd}`; + const rootPath = path.resolve(__dirname, '..'); + const { version: currentVersion } = await fs.readJson( + path.join(rootPath, 'package.json'), + ); - console.log('Requesting existing tags'); - - const existingTags = await octokit.repos.listTags({ - ...baseOptions, - per_page: 100, - }); - const existingTagNames = existingTags.data.map(obj => obj.name); - - let tagName = baseTagName; - let index = 0; - while (existingTagNames.includes(tagName)) { - index += 1; - tagName = `${baseTagName}.${index}`; - } + const tagName = `v${currentVersion}`; console.log(`Creating release tag ${tagName}`); @@ -65,11 +51,22 @@ async function main() { type: 'commit', }); - await octokit.git.createRef({ - ...baseOptions, - ref: `refs/tags/${tagName}`, - sha: annotatedTag.data.sha, - }); + try { + await octokit.git.createRef({ + ...baseOptions, + ref: `refs/tags/${tagName}`, + sha: annotatedTag.data.sha, + }); + } catch (ex) { + if ( + ex.status === 422 && + ex.response.data.message === 'Reference already exists' + ) { + throw new Error(`Tag ${tagName} already exists in repository`); + } + console.error(`Tag creation for ${tagName} failed`); + throw ex; + } console.log(`::set-output name=tag_name::${tagName}`); } diff --git a/scripts/prepare-release.js b/scripts/prepare-release.js index 3d4f142a8d..dd147daca0 100755 --- a/scripts/prepare-release.js +++ b/scripts/prepare-release.js @@ -20,6 +20,10 @@ const fs = require('fs-extra'); const semver = require('semver'); const { getPackages } = require('@manypkg/get-packages'); const path = require('path'); +const { execFile: execFileCb } = require('child_process'); +const { promisify } = require('util'); + +const execFile = promisify(execFileCb); const DEPENDENCY_TYPES = [ 'dependencies', @@ -28,9 +32,14 @@ const DEPENDENCY_TYPES = [ 'peerDependencies', ]; -async function main() { +/** + * Bumps up the versions of packages to account for + * the base versions that are set in .changeset/patched.json. + * This may be needed when we have made emergency releases. + */ +async function updatePatchVersions() { const patchedJsonPath = path.resolve('.changeset', 'patched.json'); - const { currentReleaseVersion } = await fs.readJSON(patchedJsonPath); + const { currentReleaseVersion } = await fs.readJson(patchedJsonPath); if (Object.keys(currentReleaseVersion).length === 0) { console.log('No currentReleaseVersion overrides found, skipping.'); return; @@ -91,7 +100,7 @@ async function main() { } if (hasChanges) { - await fs.writeJSON(path.resolve(dir, 'package.json'), packageJson, { + await fs.writeJson(path.resolve(dir, 'package.json'), packageJson, { spaces: 2, }); } @@ -104,6 +113,69 @@ async function main() { ); } +/** + * Returns the mode and tag that is currently set + * in the .changeset/pre.json file + */ +async function getPreInfo(rootPath) { + const pre = path.join(rootPath, '.changeset', 'pre.json'); + if (!(await fs.pathExists(pre))) { + return { mode: undefined, tag: undefined }; + } + + const { mode, tag } = await fs.readJson(pre); + return { mode, tag }; +} + +/** + * Bumps the release version in the root package.json. + * + * This takes into account whether we're in pre-release mode or on a patch branch. + */ +async function updateBackstageReleaseVersion() { + const rootPath = path.resolve(__dirname, '..'); + const branchName = await execFile( + 'git', + ['rev-parse', '--abbrev-ref', 'HEAD'], + { shell: true }, + ).then(({ stdout }) => stdout.trim()); + const { mode: preMode, tag: preTag } = await getPreInfo(rootPath); + + const packagePath = path.join(rootPath, 'package.json'); + const package = await fs.readJson(packagePath); + const { version: currentVersion } = package; + + let nextVersion; + if (branchName === 'master') { + if (preMode === 'pre') { + if (semver.prerelease(currentVersion)) { + nextVersion = semver.inc(currentVersion, 'pre', preTag); + } else { + nextVersion = semver.inc(currentVersion, 'preminor', preTag); + } + } else if (preMode === 'exit') { + nextVersion = semver.inc(currentVersion, 'patch'); + } else { + nextVersion = semver.inc(currentVersion, 'minor'); + } + } else { + if (preMode) { + throw new Error(`Unexpected pre mode ${preMode} on branch ${branchName}`); + } + nextVersion = semver.inc(currentVersion, 'patch'); + } + + await fs.writeJson(packagePath, { + ...package, + version: nextVersion, + }); +} + +async function main() { + await updatePatchVersions(); + await updateBackstageReleaseVersion(); +} + main().catch(error => { console.error(error.stack); process.exit(1);