diff --git a/aio/scripts/deploy-to-firebase/index.mjs b/aio/scripts/deploy-to-firebase/index.mjs index 65d140932c3..c544fca7786 100644 --- a/aio/scripts/deploy-to-firebase/index.mjs +++ b/aio/scripts/deploy-to-firebase/index.mjs @@ -28,14 +28,16 @@ * | from? | | | redirectVersionDomainToRc(*) | * | |--------|---------------------------------|---------------------------------| * | | MASTER | next | next | - * | | | | | + * | | | redirectVersionDomainToNext(**) | redirectVersionDomainToNext(**) | * |-----------|--------|---------------------------------|---------------------------------| * * (*): Only if `v` > `v`. + * (**): Only if (no active RC and `v` > `v`) or (active RC and `v` > `v`). * * NOTES: * - The `v-angular-io-site` Firebase site should be created (and connected to the - * `v.angular.io` subdomain) before a new RC branch is created. + * `v.angular.io` subdomain) before the version in the `master` branch's `package.json` is + * updated to a new major. * - When a new major version is released, the deploy CI jobs for the new stable branch (prev. RC * or next) and the old stable branch must be run AFTER the new stable version has been * published to NPM, because the NPM info is used to determine what the stable version is. @@ -57,6 +59,7 @@ sh.set('-e'); // Constants const DIRNAME = u.getDirname(import.meta.url); +const ROOT_PKG_PATH = `${DIRNAME}/../../../package.json`; // Exports export { @@ -129,7 +132,10 @@ function computeDeploymentsInfo( } // The deployment mode is computed based on the branch we are building. - const currentBranchMajorVersion = u.computeMajorVersion(currentBranch); + const currentVersionPattern = /^\d+\.\d+\.x$/.test(currentBranch) ? + currentBranch : // The current branch name is a version pattern. + u.loadJson(ROOT_PKG_PATH).version; // We need to retrieve the version from `package.json`. + const currentBranchMajorVersion = u.computeMajorVersion(currentVersionPattern); const stableBranchMajorVersion = u.computeMajorVersion(stableBranch); const deploymentInfoPerTarget = { // PRIMARY DEPLOY TARGETS @@ -189,6 +195,16 @@ function computeDeploymentsInfo( // Since there can be multiple secondary deployments (each tweaking the primary one in different // ways), it is a good idea to ensure that any pre-deploy actions are undone in the post-deploy // phase. + redirectVersionDomainToNext: { + name: 'redirectVersionDomainToNext', + type: 'secondary', + deployEnv: 'next', + projectId: 'angular-io', + siteId: `v${currentBranchMajorVersion}-angular-io-site`, + deployedUrl: `https://v${currentBranchMajorVersion}.angular.io/`, + preDeployActions: [pre.redirectAllToNext], + postDeployActions: [pre.undo.redirectAllToNext, post.testRedirectToNext], + }, redirectVersionDomainToRc: { name: 'redirectVersionDomainToRc', type: 'secondary', @@ -228,15 +244,33 @@ function computeDeploymentsInfo( }, }; - // If the current branch is `master`, deploy as `next`. - if (currentBranch === 'master') { - return [deploymentInfoPerTarget.next]; - } - // Determine if there is an active RC version by checking whether the most recent minor branch is // the stable branch or not. const mostRecentMinorBranch = u.getMostRecentMinorBranch(); const rcBranch = (mostRecentMinorBranch !== stableBranch) ? mostRecentMinorBranch : null; + const isRcActive = rcBranch !== null; + + // If the current branch is `master`, deploy as `next`. + if (currentBranch === 'master') { + // In order to determine whether to also deploy to `v-angular-io-site` we need to compare + // `v` with either `v` (if there is an active RC) or `v`. + const otherVersion = isRcActive ? u.computeMajorVersion(rcBranch) : stableBranchMajorVersion; + + return (currentBranchMajorVersion > otherVersion) ? + // The next major version is greater than the RC or stable major version. + // Deploy to both `next-angular-io-site` and `v-angular-io-site`. + [ + deploymentInfoPerTarget.next, + deploymentInfoPerTarget.redirectVersionDomainToNext, + ] : + // The next major version is not greater than the RC or stable major version. + // Only deploy to `next-angular-io-site` (since `v-angular-io-site` is probably + // `v-angular-io-site` or `v-angular-io-site` and we don't want to overwrite the + // RC or stable deployment). + [ + deploymentInfoPerTarget.next, + ]; + } // If the current branch is the RC branch, deploy as `rc`. if (currentBranch === rcBranch) { @@ -257,7 +291,7 @@ function computeDeploymentsInfo( // If the current branch is the stable branch, deploy as `stable`. if (currentBranch === stableBranch) { - return (rcBranch !== null) ? + return isRcActive ? // There is an active RC version. Only deploy to the `stable` projects/sites. [ deploymentInfoPerTarget.stable, diff --git a/aio/scripts/deploy-to-firebase/index.spec.mjs b/aio/scripts/deploy-to-firebase/index.spec.mjs index cd9d600c3ae..b627d4195fd 100644 --- a/aio/scripts/deploy-to-firebase/index.spec.mjs +++ b/aio/scripts/deploy-to-firebase/index.spec.mjs @@ -79,7 +79,13 @@ describe('deploy-to-firebase:', () => { ]); }); - it('master - deploy success', () => { + it('master - deploy success - no active RC, major higher than stable', () => { + const mostRecentMajorVersion = u.computeMajorVersion(mostRecentMinorBranch); + const fakeMasterMajorVersion = mostRecentMajorVersion + 1; + + // Fake the `package.json` version. + spyOn(u, 'loadJson').and.returnValue({version: `${fakeMasterMajorVersion}.0.0-next.42`}); + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', @@ -87,6 +93,108 @@ describe('deploy-to-firebase:', () => { CI_BRANCH: 'master', CI_STABLE_BRANCH: mostRecentMinorBranch, CI_COMMIT: latestCommits.master, + })).toEqual([ + { + name: 'next', + type: 'primary', + deployEnv: 'next', + projectId: 'angular-io', + siteId: 'next-angular-io-site', + deployedUrl: 'https://next.angular.io/', + preDeployActions: ['function:build', 'function:checkPayloadSize'], + postDeployActions: ['function:testPwaScore'], + }, + { + name: 'redirectVersionDomainToNext', + type: 'secondary', + deployEnv: 'next', + projectId: 'angular-io', + siteId: `v${fakeMasterMajorVersion}-angular-io-site`, + deployedUrl: `https://v${fakeMasterMajorVersion}.angular.io/`, + preDeployActions: ['function:redirectAllToNext'], + postDeployActions: ['function:undoRedirectAllToNext', 'function:testRedirectToNext'], + }, + ]); + }); + + it('master - deploy success - no active RC, major same as stable', () => { + const mostRecentMajorVersion = u.computeMajorVersion(mostRecentMinorBranch); + + // Fake the `package.json` version. + spyOn(u, 'loadJson').and.returnValue({version: `${mostRecentMajorVersion}.42.0`}); + + expect(getDeploymentsInfoFor({ + CI_REPO_OWNER: 'angular', + CI_REPO_NAME: 'angular', + CI_PULL_REQUEST: 'false', + CI_BRANCH: 'master', + CI_STABLE_BRANCH: mostRecentMinorBranch, + CI_COMMIT: latestCommits.master, + })).toEqual([ + { + name: 'next', + type: 'primary', + deployEnv: 'next', + projectId: 'angular-io', + siteId: 'next-angular-io-site', + deployedUrl: 'https://next.angular.io/', + preDeployActions: ['function:build', 'function:checkPayloadSize'], + postDeployActions: ['function:testPwaScore'], + }, + ]); + }); + + it('master - deploy success - active RC, major higher than RC and stable', () => { + const mostRecentMajorVersion = u.computeMajorVersion(mostRecentMinorBranch); + const fakeMasterMajorVersion = mostRecentMajorVersion + 1; + + // Fake the `package.json` version. + spyOn(u, 'loadJson').and.returnValue({version: `${fakeMasterMajorVersion}.0.0-next.42`}); + + expect(getDeploymentsInfoFor({ + CI_REPO_OWNER: 'angular', + CI_REPO_NAME: 'angular', + CI_PULL_REQUEST: 'false', + CI_BRANCH: 'master', + CI_STABLE_BRANCH: '4.4.x', + CI_COMMIT: latestCommits.master, + })).toEqual([ + { + name: 'next', + type: 'primary', + deployEnv: 'next', + projectId: 'angular-io', + siteId: 'next-angular-io-site', + deployedUrl: 'https://next.angular.io/', + preDeployActions: ['function:build', 'function:checkPayloadSize'], + postDeployActions: ['function:testPwaScore'], + }, + { + name: 'redirectVersionDomainToNext', + type: 'secondary', + deployEnv: 'next', + projectId: 'angular-io', + siteId: `v${fakeMasterMajorVersion}-angular-io-site`, + deployedUrl: `https://v${fakeMasterMajorVersion}.angular.io/`, + preDeployActions: ['function:redirectAllToNext'], + postDeployActions: ['function:undoRedirectAllToNext', 'function:testRedirectToNext'], + }, + ]); + }); + + it('master - deploy success - active RC, major same as RC and higher than stable', () => { + const mostRecentMajorVersion = u.computeMajorVersion(mostRecentMinorBranch); + + // Fake the `package.json` version. + spyOn(u, 'loadJson').and.returnValue({version: `${mostRecentMajorVersion}.0.0-next.42`}); + + expect(getDeploymentsInfoFor({ + CI_REPO_OWNER: 'angular', + CI_REPO_NAME: 'angular', + CI_PULL_REQUEST: 'false', + CI_BRANCH: 'master', + CI_STABLE_BRANCH: '4.4.x', + CI_COMMIT: latestCommits.master, })).toEqual([ { name: 'next', diff --git a/aio/scripts/deploy-to-firebase/utils.mjs b/aio/scripts/deploy-to-firebase/utils.mjs index c1df41cf38a..8096cdbd482 100644 --- a/aio/scripts/deploy-to-firebase/utils.mjs +++ b/aio/scripts/deploy-to-firebase/utils.mjs @@ -1,3 +1,4 @@ +import fs from 'fs'; import {dirname} from 'path'; import sh from 'shelljs'; import {fileURLToPath} from 'url'; @@ -25,6 +26,7 @@ const exp = { getLatestCommit, getMostRecentMinorBranch, getRemoteRefs, + loadJson, logSectionHeader, nameFunction, yarn, @@ -32,8 +34,8 @@ const exp = { export default exp; // Helpers -function computeMajorVersion(branchName) { - return +branchName.split('.', 1)[0]; +function computeMajorVersion(versionPattern) { + return +versionPattern.split('.', 1)[0]; } function getDirname(fileUrl) { @@ -82,6 +84,10 @@ function getLatestCommit(branchName, options = undefined) { return exp.getRemoteRefs(branchName, options)[0].slice(0, 40); } +function loadJson(filePath) { + return JSON.parse(fs.readFileSync(filePath, 'utf8')); +} + function logSectionHeader(message) { console.log(`\n\n\n==== ${message} ====\n`); } diff --git a/aio/scripts/deploy-to-firebase/utils.spec.mjs b/aio/scripts/deploy-to-firebase/utils.spec.mjs index e19ed35cf33..df6be6dd4a5 100644 --- a/aio/scripts/deploy-to-firebase/utils.spec.mjs +++ b/aio/scripts/deploy-to-firebase/utils.spec.mjs @@ -1,3 +1,4 @@ +import fs from 'fs'; import sh from 'shelljs'; import u from './utils.mjs'; @@ -226,6 +227,17 @@ describe('deploy-to-firebase/utils:', () => { }); }); + describe('loadJson()', () => { + let readFileSyncSpy; + + beforeEach(() => readFileSyncSpy = spyOn(fs, 'readFileSync')); + + it('should load and parse a JSON file', () => { + readFileSyncSpy.withArgs('/foo/bar.json', 'utf8').and.returnValue('{"foo": "bar"}'); + expect(u.loadJson('/foo/bar.json')).toEqual({foo: 'bar'}); + }); + }); + describe('logSectionHeader()', () => { let logSpy;