From 6e6eee6d5ba5033611759ca5f42d550db583a3a8 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Mon, 30 Nov 2020 13:59:10 +0200 Subject: [PATCH] refactor(docs-infra): decouple deploying from other operations in `deploy-to-firebase.js` (#39853) Previously, the `deploy()` function in `deploy-to-firebase.js` would also perform other operations (beyond deploying), such as building the app, checking the generated payload size, testing the PWA score of the deployed app. This commit decouples these operations, so that deploying can be performed independently. 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 | 78 +++++++++++++------ aio/scripts/deploy-to-firebase.spec.js | 104 +++++++++++++++---------- 2 files changed, 119 insertions(+), 63 deletions(-) diff --git a/aio/scripts/deploy-to-firebase.js b/aio/scripts/deploy-to-firebase.js index b2975f9a33..f700b061d5 100644 --- a/aio/scripts/deploy-to-firebase.js +++ b/aio/scripts/deploy-to-firebase.js @@ -30,13 +30,15 @@ if (require.main === module) { 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` + - `Deployment URLs : ${deploymentInfo.deployedUrl}\n` + - ` https://${deploymentInfo.siteId}.web.app/`); + `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}); @@ -45,6 +47,22 @@ if (require.main === module) { } // Helpers +function build({deployedUrl, deployEnv}) { + console.log('\n\n\n==== Build the AIO app. ====\n'); + yarn(`build --configuration=${deployEnv} --progress=false`); + + console.log('\n\n\n==== Add any mode-specific files into the AIO distribution. ====\n'); + cp('-rf', `src/extra-files/${deployEnv}/.`, 'dist/'); + + console.log('\n\n\n==== Update opensearch descriptor for AIO with `deployedUrl`. ====\n'); + yarn(`set-opensearch-url ${deployedUrl.replace(/[^/]$/, '$&/')}`); // The URL must end with `/`. +} + +function checkPayloadSize() { + console.log('\n\n\n==== Check payload size and upload the numbers to Firebase DB. ====\n'); + yarn('payload-size'); +} + function computeDeploymentInfo( {currentBranch, currentCommit, isPullRequest, repoName, repoOwner, stableBranch}) { // Do not deploy if we are running in a fork. @@ -72,24 +90,32 @@ function computeDeploymentInfo( projectId: 'angular-io', siteId: 'next-angular-io-site', deployedUrl: 'https://next.angular.io/', + preDeployActions: [build, checkPayloadSize], + postDeployActions: [testPwaScore], }, rc: { deployEnv: 'rc', projectId: 'angular-io', siteId: 'rc-angular-io-site', deployedUrl: 'https://rc.angular.io/', + preDeployActions: [build, checkPayloadSize], + postDeployActions: [testPwaScore], }, stable: { deployEnv: 'stable', projectId: 'angular-io', siteId: `v${currentBranchMajorVersion}-angular-io-site`, deployedUrl: 'https://angular.io/', + preDeployActions: [build, checkPayloadSize], + postDeployActions: [testPwaScore], }, archive: { deployEnv: 'archive', projectId: 'angular-io', siteId: `v${currentBranchMajorVersion}-angular-io-site`, deployedUrl: `https://v${currentBranchMajorVersion}.angular.io/`, + preDeployActions: [build, checkPayloadSize], + postDeployActions: [testPwaScore], }, }; @@ -160,21 +186,20 @@ function computeMajorVersion(branchName) { return +branchName.split('.', 1)[0]; } -function deploy( - {currentCommit, deployedUrl, deployEnv, firebaseToken, minPwaScore, projectId, siteId}) { +function deploy(data) { + const { + currentCommit, + firebaseToken, + postDeployActions, + preDeployActions, + projectId, + siteId, + } = data; + cd(`${__dirname}/..`); - console.log('\n\n\n==== Build the AIO app. ====\n'); - yarn(`build --configuration=${deployEnv} --progress=false`); - - console.log('\n\n\n==== Add any mode-specific files into the AIO distribution. ====\n'); - cp('-rf', `src/extra-files/${deployEnv}/.`, 'dist/'); - - console.log('\n\n\n==== Update opensearch descriptor for AIO with `deployedUrl`. ====\n'); - yarn(`set-opensearch-url ${deployedUrl.replace(/[^/]$/, '$&/')}`); // The URL must end with `/`. - - console.log('\n\n\n==== Check payload size and upload the numbers to Firebase DB. ====\n'); - yarn('payload-size'); + console.log('\n\n\n==== Run pre-deploy actions. ====\n'); + preDeployActions.forEach(fn => fn(data)); console.log('\n\n\n==== Deploy AIO to Firebase hosting. ====\n'); yarn(`firebase use "${projectId}" --token "${firebaseToken}"`); @@ -183,8 +208,8 @@ function deploy( `firebase deploy --only hosting:aio --message "Commit: ${currentCommit}" --non-interactive ` + `--token "${firebaseToken}"`); - console.log('\n\n\n==== Run PWA-score tests. ====\n'); - yarn(`test-pwa-score "${deployedUrl}" "${minPwaScore}"`); + console.log('\n\n\n==== Run post-deploy actions. ====\n'); + postDeployActions.forEach(fn => fn(data)); } function getRemoteRefs(refOrPattern, remote = NG_REMOTE_URL) { @@ -195,10 +220,19 @@ function getLatestCommit(branchName, remote = undefined) { return getRemoteRefs(branchName, remote)[0].slice(0, 40); } +function serializeActions(actions) { + return actions.map(fn => fn.name).join(', '); +} + function skipDeployment(reason) { return {reason, skipped: true}; } +function testPwaScore({deployedUrl, minPwaScore}) { + console.log('\n\n\n==== Run PWA-score tests. ====\n'); + yarn(`test-pwa-score "${deployedUrl}" "${minPwaScore}"`); +} + function yarn(cmd) { // Using `--silent` to ensure no secret env variables are printed. // diff --git a/aio/scripts/deploy-to-firebase.spec.js b/aio/scripts/deploy-to-firebase.spec.js index 696e7c6652..b7523fcc68 100644 --- a/aio/scripts/deploy-to-firebase.spec.js +++ b/aio/scripts/deploy-to-firebase.spec.js @@ -6,60 +6,70 @@ const {computeDeploymentInfo, computeInputVars, getLatestCommit} = require('./de 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)); + }; + it('master - skip deploy - not angular', () => { - expect(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'notangular', - }))).toEqual({ + })).toEqual({ skipped: true, reason: 'Skipping deploy because this is not angular/angular.', }); }); it('master - skip deploy - angular fork', () => { - expect(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ CI_REPO_OWNER: 'notangular', CI_REPO_NAME: 'angular', - }))).toEqual({ + })).toEqual({ skipped: true, reason: 'Skipping deploy because this is not angular/angular.', }); }); it('master - skip deploy - pull request', () => { - expect(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'true', - }))).toEqual({ + })).toEqual({ skipped: true, reason: 'Skipping deploy because this is a PR build.', }); }); it('master - deploy success', () => { - expect(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: 'master', CI_COMMIT: getLatestCommit('master'), - }))).toEqual({ + })).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(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ CI_REPO_OWNER: 'angular', CI_REPO_NAME: 'angular', CI_PULL_REQUEST: 'false', CI_BRANCH: 'master', CI_COMMIT: 'DUMMY_TEST_COMMIT', - }))).toEqual({ + })).toEqual({ skipped: true, reason: 'Skipping deploy because DUMMY_TEST_COMMIT is not the latest commit ' + @@ -68,30 +78,32 @@ describe('deploy-to-firebase:', () => { }); it('stable - deploy success', () => { - expect(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ 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({ + })).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(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ 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({ + })).toEqual({ skipped: true, reason: 'Skipping deploy because DUMMY_TEST_COMMIT is not the latest commit ' + @@ -100,48 +112,52 @@ describe('deploy-to-firebase:', () => { }); it('archive - deploy success', () => { - expect(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ 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({ + })).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(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ 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({ + })).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(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ 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({ + })).toEqual({ skipped: true, reason: 'Skipping deploy because DUMMY_TEST_COMMIT is not the latest commit ' + @@ -150,14 +166,14 @@ describe('deploy-to-firebase:', () => { }); it('archive - skip deploy - major same as stable, minor less than stable', () => { - expect(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ 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({ + })).toEqual({ skipped: true, reason: 'Skipping deploy of branch "2.1.x" to Firebase.\n' + @@ -166,14 +182,14 @@ describe('deploy-to-firebase:', () => { }); it('archive - skip deploy - major lower than stable, minor not latest', () => { - expect(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ 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({ + })).toEqual({ skipped: true, reason: 'Skipping deploy of branch "2.1.x" to Firebase.\n' + @@ -182,46 +198,50 @@ describe('deploy-to-firebase:', () => { }); it('rc - deploy success - major higher than stable', () => { - expect(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ 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({ + })).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(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ 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({ + })).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(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ 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({ + })).toEqual({ skipped: true, reason: 'Skipping deploy because DUMMY_TEST_COMMIT is not the latest commit ' + @@ -230,14 +250,14 @@ describe('deploy-to-firebase:', () => { }); it('rc - skip deploy - major same as stable, minor not latest', () => { - expect(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ 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({ + })).toEqual({ skipped: true, reason: 'Skipping deploy of branch "2.1.x" to Firebase.\n' + @@ -246,14 +266,14 @@ describe('deploy-to-firebase:', () => { }); it('rc - skip deploy - major higher than stable, minor not latest', () => { - expect(computeDeploymentInfo(computeInputVars({ + expect(getDeploymentInfoFor({ 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({ + })).toEqual({ skipped: true, reason: 'Skipping deploy of branch "4.3.x" to Firebase.\n' + @@ -273,12 +293,14 @@ describe('deploy-to-firebase:', () => { }; const result = execSync(cmd, {encoding: 'utf8', env}).trim(); expect(result).toBe( - 'Git branch : master\n' + - `Git commit : ${commit}\n` + - 'Build/deploy mode : next\n' + - 'Firebase project : angular-io\n' + - 'Firebase site : next-angular-io-site\n' + - 'Deployment URLs : https://next.angular.io/\n' + - ' https://next-angular-io-site.web.app/'); + 'Git branch : master\n' + + `Git commit : ${commit}\n` + + 'Build/deploy mode : next\n' + + 'Firebase project : angular-io\n' + + 'Firebase site : next-angular-io-site\n' + + 'Pre-deploy actions : build, checkPayloadSize\n' + + 'Post-deploy actions : testPwaScore\n' + + 'Deployment URLs : https://next.angular.io/\n' + + ' https://next-angular-io-site.web.app/'); }); });