From 53bd832c778b420063cd8ebcf00521e6c38a8df9 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Mon, 30 Nov 2020 13:59:11 +0200 Subject: [PATCH] refactor(docs-infra): support multiple deployments per run (#39853) Previously, the `deploy-to-firebase.js` script would only perform one deployment operation on each run. This commit adds support for performing multiple deployment operations. NOTE: In a subsequent commit, this will be leveraged fix an issue with `rc.angular.io` redirects in the presence of a ServiceWorker by deploying the same artifacts to multiple Firebase projects/sites. See #39760 for more details. PR Close #39853 --- aio/scripts/deploy-to-firebase.js | 74 +++--- aio/scripts/deploy-to-firebase.spec.js | 298 ++++++++++++++----------- 2 files changed, 213 insertions(+), 159 deletions(-) diff --git a/aio/scripts/deploy-to-firebase.js b/aio/scripts/deploy-to-firebase.js index f700b061d5..0869a70cca 100644 --- a/aio/scripts/deploy-to-firebase.js +++ b/aio/scripts/deploy-to-firebase.js @@ -15,7 +15,7 @@ const NG_REMOTE_URL = `https://github.com/${REPO_SLUG}.git`; // Exports module.exports = { - computeDeploymentInfo, + computeDeploymentsInfo, computeInputVars, getLatestCommit, }; @@ -24,26 +24,36 @@ module.exports = { if (require.main === module) { const isDryRun = process.argv[2] === '--dry-run'; const inputVars = computeInputVars(process.env); - const deploymentInfo = computeDeploymentInfo(inputVars); + const deploymentsInfo = computeDeploymentsInfo(inputVars); + const totalDeployments = deploymentsInfo.length; - if (deploymentInfo.skipped) { - console.log(deploymentInfo.reason); - } else { + console.log(`Total deployments: ${totalDeployments}`); + + deploymentsInfo.forEach((deploymentInfo, idx) => { console.log( - `Git branch : ${inputVars.currentBranch}\n` + - `Git commit : ${inputVars.currentCommit}\n` + - `Build/deploy mode : ${deploymentInfo.deployEnv}\n` + - `Firebase project : ${deploymentInfo.projectId}\n` + - `Firebase site : ${deploymentInfo.siteId}\n` + - `Pre-deploy actions : ${serializeActions(deploymentInfo.preDeployActions)}\n` + - `Post-deploy actions : ${serializeActions(deploymentInfo.postDeployActions)}\n` + - `Deployment URLs : ${deploymentInfo.deployedUrl}\n` + - ` https://${deploymentInfo.siteId}.web.app/`); + `\n\n\nDeployment ${idx + 1} of ${totalDeployments}\n` + + '-----------------'); - if (!isDryRun) { - deploy({...inputVars, ...deploymentInfo}); + if (deploymentInfo.skipped) { + console.log(deploymentInfo.reason); + } else { + console.log( + `Git branch : ${inputVars.currentBranch}\n` + + `Git commit : ${inputVars.currentCommit}\n` + + `Build/deploy mode : ${deploymentInfo.deployEnv}\n` + + `Firebase project : ${deploymentInfo.projectId}\n` + + `Firebase site : ${deploymentInfo.siteId}\n` + + `Pre-deploy actions : ${serializeActions(deploymentInfo.preDeployActions)}\n` + + `Post-deploy actions : ${serializeActions(deploymentInfo.postDeployActions)}\n` + + `Deployment URLs : ${deploymentInfo.deployedUrl}\n` + + ` https://${deploymentInfo.siteId}.web.app/`); + + if (!isDryRun) { + deploy({...inputVars, ...deploymentInfo}); + } } - } + }); + } // Helpers @@ -63,23 +73,25 @@ function checkPayloadSize() { yarn('payload-size'); } -function computeDeploymentInfo( +function computeDeploymentsInfo( {currentBranch, currentCommit, isPullRequest, repoName, repoOwner, stableBranch}) { // Do not deploy if we are running in a fork. if (`${repoOwner}/${repoName}` !== REPO_SLUG) { - return skipDeployment(`Skipping deploy because this is not ${REPO_SLUG}.`); + return [skipDeployment(`Skipping deploy because this is not ${REPO_SLUG}.`)]; } // Do not deploy if this is a PR. PRs are deployed in the `aio_preview` CircleCI job. if (isPullRequest) { - return skipDeployment('Skipping deploy because this is a PR build.'); + return [skipDeployment('Skipping deploy because this is a PR build.')]; } // Do not deploy if the current commit is not the latest on its branch. const latestCommit = getLatestCommit(currentBranch); if (currentCommit !== latestCommit) { - return skipDeployment( - `Skipping deploy because ${currentCommit} is not the latest commit (${latestCommit}).`); + return [ + skipDeployment( + `Skipping deploy because ${currentCommit} is not the latest commit (${latestCommit}).`), + ]; } // The deployment mode is computed based on the branch we are building. @@ -120,9 +132,9 @@ function computeDeploymentInfo( }; if (currentBranch === 'master') { - return deploymentInfoPerTarget.next; + return [deploymentInfoPerTarget.next]; } else if (currentBranch === stableBranch) { - return deploymentInfoPerTarget.stable; + return [deploymentInfoPerTarget.stable]; } else { const stableBranchMajorVersion = computeMajorVersion(stableBranch); @@ -140,20 +152,22 @@ function computeDeploymentInfo( // Do not deploy if it is not the latest branch for the given major version. // NOTE: At this point, we know the current branch is not the stable branch. if (currentBranch !== mostRecentMinorVersionBranch) { - return skipDeployment( - `Skipping deploy of branch "${currentBranch}" to Firebase.\n` + - 'There is a more recent branch with the same major version: ' + - `"${mostRecentMinorVersionBranch}"`); + return [ + skipDeployment( + `Skipping deploy of branch "${currentBranch}" to Firebase.\n` + + 'There is a more recent branch with the same major version: ' + + `"${mostRecentMinorVersionBranch}"`), + ]; } return (currentBranchMajorVersion < stableBranchMajorVersion) ? // This is the latest minor version for a major that is less than the stable major version: // Deploy as `archive`. - deploymentInfoPerTarget.archive : + [deploymentInfoPerTarget.archive] : // This is the latest minor version for a major that is equal or greater than the stable // major version, but not the stable version itself: // Deploy as `rc`. - deploymentInfoPerTarget.rc; + [deploymentInfoPerTarget.rc]; } // We should never get here. diff --git a/aio/scripts/deploy-to-firebase.spec.js b/aio/scripts/deploy-to-firebase.spec.js index b7523fcc68..250ba7eca1 100644 --- a/aio/scripts/deploy-to-firebase.spec.js +++ b/aio/scripts/deploy-to-firebase.spec.js @@ -2,283 +2,317 @@ 'use strict'; const {execSync} = require('child_process'); -const {computeDeploymentInfo, computeInputVars, getLatestCommit} = require('./deploy-to-firebase'); +const {computeDeploymentsInfo, computeInputVars, getLatestCommit} = require('./deploy-to-firebase'); describe('deploy-to-firebase:', () => { // Helpers const jsonFunctionReplacer = (_key, val) => (typeof val === 'function') ? `function:${val.name}` : val; - const getDeploymentInfoFor = env => { - const deploymentInfo = computeDeploymentInfo(computeInputVars(env)); - return JSON.parse(JSON.stringify(deploymentInfo, jsonFunctionReplacer)); + const getDeploymentsInfoFor = env => { + const deploymentsInfo = computeDeploymentsInfo(computeInputVars(env)); + return JSON.parse(JSON.stringify(deploymentsInfo, jsonFunctionReplacer)); }; it('master - skip deploy - not angular', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'notangular', - })).toEqual({ - skipped: true, - reason: 'Skipping deploy because this is not angular/angular.', - }); + })).toEqual([ + { + skipped: true, + reason: 'Skipping deploy because this is not angular/angular.', + }, + ]); }); it('master - skip deploy - angular fork', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'notangular', CI_REPO_NAME: 'angular', - })).toEqual({ - skipped: true, - reason: 'Skipping deploy because this is not angular/angular.', - }); + })).toEqual([ + { + skipped: true, + reason: 'Skipping deploy because this is not angular/angular.', + }, + ]); }); it('master - skip deploy - pull request', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'true', - })).toEqual({ - skipped: true, - reason: 'Skipping deploy because this is a PR build.', - }); + })).toEqual([ + { + skipped: true, + reason: 'Skipping deploy because this is a PR build.', + }, + ]); }); it('master - deploy success', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: 'master', CI_COMMIT: getLatestCommit('master'), - })).toEqual({ - deployEnv: 'next', - projectId: 'angular-io', - siteId: 'next-angular-io-site', - deployedUrl: 'https://next.angular.io/', - preDeployActions: ['function:build', 'function:checkPayloadSize'], - postDeployActions: ['function:testPwaScore'], - }); + })).toEqual([ + { + 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 - skip deploy - commit not HEAD', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: 'master', CI_COMMIT: 'DUMMY_TEST_COMMIT', - })).toEqual({ - skipped: true, - reason: - 'Skipping deploy because DUMMY_TEST_COMMIT is not the latest commit ' + - `(${getLatestCommit('master')}).`, - }); + })).toEqual([ + { + skipped: true, + reason: + 'Skipping deploy because DUMMY_TEST_COMMIT is not the latest commit ' + + `(${getLatestCommit('master')}).`, + }, + ]); }); it('stable - deploy success', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: '4.3.x', CI_STABLE_BRANCH: '4.3.x', CI_COMMIT: getLatestCommit('4.3.x'), - })).toEqual({ - deployEnv: 'stable', - projectId: 'angular-io', - siteId: 'v4-angular-io-site', - deployedUrl: 'https://angular.io/', - preDeployActions: ['function:build', 'function:checkPayloadSize'], - postDeployActions: ['function:testPwaScore'], - }); + })).toEqual([ + { + deployEnv: 'stable', + projectId: 'angular-io', + siteId: 'v4-angular-io-site', + deployedUrl: 'https://angular.io/', + preDeployActions: ['function:build', 'function:checkPayloadSize'], + postDeployActions: ['function:testPwaScore'], + }, + ]); }); it('stable - skip deploy - commit not HEAD', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: '4.3.x', CI_STABLE_BRANCH: '4.3.x', CI_COMMIT: 'DUMMY_TEST_COMMIT', - })).toEqual({ - skipped: true, - reason: - 'Skipping deploy because DUMMY_TEST_COMMIT is not the latest commit ' + - `(${getLatestCommit('4.3.x')}).`, - }); + })).toEqual([ + { + skipped: true, + reason: + 'Skipping deploy because DUMMY_TEST_COMMIT is not the latest commit ' + + `(${getLatestCommit('4.3.x')}).`, + }, + ]); }); it('archive - deploy success', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: '2.4.x', CI_STABLE_BRANCH: '4.3.x', CI_COMMIT: getLatestCommit('2.4.x'), - })).toEqual({ - deployEnv: 'archive', - projectId: 'angular-io', - siteId: 'v2-angular-io-site', - deployedUrl: 'https://v2.angular.io/', - preDeployActions: ['function:build', 'function:checkPayloadSize'], - postDeployActions: ['function:testPwaScore'], - }); + })).toEqual([ + { + deployEnv: 'archive', + projectId: 'angular-io', + siteId: 'v2-angular-io-site', + deployedUrl: 'https://v2.angular.io/', + preDeployActions: ['function:build', 'function:checkPayloadSize'], + postDeployActions: ['function:testPwaScore'], + }, + ]); }); // v9 used to be special-cased, because it was piloting the Firebase hosting "multisites" setup. // See https://angular-team.atlassian.net/browse/DEV-125 for more info. it('archive - deploy success (no special case for v9)', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: '9.1.x', CI_STABLE_BRANCH: '10.0.x', CI_COMMIT: getLatestCommit('9.1.x'), - })).toEqual({ - deployEnv: 'archive', - projectId: 'angular-io', - siteId: 'v9-angular-io-site', - deployedUrl: 'https://v9.angular.io/', - preDeployActions: ['function:build', 'function:checkPayloadSize'], - postDeployActions: ['function:testPwaScore'], - }); + })).toEqual([ + { + deployEnv: 'archive', + projectId: 'angular-io', + siteId: 'v9-angular-io-site', + deployedUrl: 'https://v9.angular.io/', + preDeployActions: ['function:build', 'function:checkPayloadSize'], + postDeployActions: ['function:testPwaScore'], + }, + ]); }); it('archive - skip deploy - commit not HEAD', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: '2.4.x', CI_STABLE_BRANCH: '4.3.x', CI_COMMIT: 'DUMMY_TEST_COMMIT', - })).toEqual({ - skipped: true, - reason: - 'Skipping deploy because DUMMY_TEST_COMMIT is not the latest commit ' + - `(${getLatestCommit('2.4.x')}).`, - }); + })).toEqual([ + { + skipped: true, + reason: + 'Skipping deploy because DUMMY_TEST_COMMIT is not the latest commit ' + + `(${getLatestCommit('2.4.x')}).`, + }, + ]); }); it('archive - skip deploy - major same as stable, minor less than stable', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: '2.1.x', CI_STABLE_BRANCH: '2.2.x', CI_COMMIT: getLatestCommit('2.1.x'), - })).toEqual({ - skipped: true, - reason: - 'Skipping deploy of branch "2.1.x" to Firebase.\n' + - 'There is a more recent branch with the same major version: "2.4.x"', - }); + })).toEqual([ + { + skipped: true, + reason: + 'Skipping deploy of branch "2.1.x" to Firebase.\n' + + 'There is a more recent branch with the same major version: "2.4.x"', + }, + ]); }); it('archive - skip deploy - major lower than stable, minor not latest', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: '2.1.x', CI_STABLE_BRANCH: '4.3.x', CI_COMMIT: getLatestCommit('2.1.x'), - })).toEqual({ - skipped: true, - reason: - 'Skipping deploy of branch "2.1.x" to Firebase.\n' + - 'There is a more recent branch with the same major version: "2.4.x"', - }); + })).toEqual([ + { + skipped: true, + reason: + 'Skipping deploy of branch "2.1.x" to Firebase.\n' + + 'There is a more recent branch with the same major version: "2.4.x"', + }, + ]); }); it('rc - deploy success - major higher than stable', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: '4.4.x', CI_STABLE_BRANCH: '2.2.x', CI_COMMIT: getLatestCommit('4.4.x'), - })).toEqual({ - deployEnv: 'rc', - projectId: 'angular-io', - siteId: 'rc-angular-io-site', - deployedUrl: 'https://rc.angular.io/', - preDeployActions: ['function:build', 'function:checkPayloadSize'], - postDeployActions: ['function:testPwaScore'], - }); + })).toEqual([ + { + deployEnv: 'rc', + projectId: 'angular-io', + siteId: 'rc-angular-io-site', + deployedUrl: 'https://rc.angular.io/', + preDeployActions: ['function:build', 'function:checkPayloadSize'], + postDeployActions: ['function:testPwaScore'], + }, + ]); }); it('rc - deploy success - major same as stable, minor higher', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: '2.4.x', CI_STABLE_BRANCH: '2.2.x', CI_COMMIT: getLatestCommit('2.4.x'), - })).toEqual({ - deployEnv: 'rc', - projectId: 'angular-io', - siteId: 'rc-angular-io-site', - deployedUrl: 'https://rc.angular.io/', - preDeployActions: ['function:build', 'function:checkPayloadSize'], - postDeployActions: ['function:testPwaScore'], - }); + })).toEqual([ + { + deployEnv: 'rc', + projectId: 'angular-io', + siteId: 'rc-angular-io-site', + deployedUrl: 'https://rc.angular.io/', + preDeployActions: ['function:build', 'function:checkPayloadSize'], + postDeployActions: ['function:testPwaScore'], + }, + ]); }); it('rc - skip deploy - commit not HEAD', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: '2.4.x', CI_STABLE_BRANCH: '2.2.x', CI_COMMIT: 'DUMMY_TEST_COMMIT', - })).toEqual({ - skipped: true, - reason: - 'Skipping deploy because DUMMY_TEST_COMMIT is not the latest commit ' + - `(${getLatestCommit('2.4.x')}).`, - }); + })).toEqual([ + { + skipped: true, + reason: + 'Skipping deploy because DUMMY_TEST_COMMIT is not the latest commit ' + + `(${getLatestCommit('2.4.x')}).`, + }, + ]); }); it('rc - skip deploy - major same as stable, minor not latest', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: '2.1.x', CI_STABLE_BRANCH: '2.0.x', CI_COMMIT: getLatestCommit('2.1.x'), - })).toEqual({ - skipped: true, - reason: - 'Skipping deploy of branch "2.1.x" to Firebase.\n' + - 'There is a more recent branch with the same major version: "2.4.x"', - }); + })).toEqual([ + { + skipped: true, + reason: + 'Skipping deploy of branch "2.1.x" to Firebase.\n' + + 'There is a more recent branch with the same major version: "2.4.x"', + }, + ]); }); it('rc - skip deploy - major higher than stable, minor not latest', () => { - expect(getDeploymentInfoFor({ + expect(getDeploymentsInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: '4.3.x', CI_STABLE_BRANCH: '2.4.x', CI_COMMIT: getLatestCommit('4.3.x'), - })).toEqual({ - skipped: true, - reason: - 'Skipping deploy of branch "4.3.x" to Firebase.\n' + - 'There is a more recent branch with the same major version: "4.4.x"', - }); + })).toEqual([ + { + skipped: true, + reason: + 'Skipping deploy of branch "4.3.x" to Firebase.\n' + + 'There is a more recent branch with the same major version: "4.4.x"', + }, + ]); }); it('integration - should run the main script without error', () => { @@ -293,6 +327,12 @@ describe('deploy-to-firebase:', () => { }; const result = execSync(cmd, {encoding: 'utf8', env}).trim(); expect(result).toBe( + 'Total deployments: 1\n' + + '\n' + + '\n' + + '\n' + + 'Deployment 1 of 1\n' + + '-----------------\n' + 'Git branch : master\n' + `Git commit : ${commit}\n` + 'Build/deploy mode : next\n' +