Skip to content

Commit 7ec0139

Browse files
authored
build: setup preview builds for dev-app (#23825)
* build: setup preview builds for dev-app Sets up preview builds for the dev-app. Whenever the `dev-app preview` label is applied to pull requests, a Github action will build the dev-app using RBE and deploy it to a preview channel within a Firebase project. The deployment and building is split up into two individual workflows to guarantee a secure exeuction of these steps. This follows the concept as outlined in https://securitylab.github.com/research/github-actions-preventing-pwn-requests/. In the future, we can try extracting some of this logic into a common tool in the dev-infra repository.. allowing preview builds to be used for other things, or in other repositories as well (or switching AIO away from the rather-complicated docker preview build setup). * fixup! build: setup preview builds for dev-app Address feedback * fixup! build: setup preview builds for dev-app Update old links
1 parent a931de5 commit 7ec0139

File tree

8 files changed

+187
-23
lines changed

8 files changed

+187
-23
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ var_15: &ignore_presubmit_branch_filter
103103
var_16: &setup_bazel_remote_execution
104104
run:
105105
name: "Setup bazel RBE remote execution"
106-
command: ./scripts/circleci/bazel/setup-remote-execution.sh
106+
command: ./scripts/bazel/setup-remote-execution.sh
107107

108108
# Sets up the bazel binary globally. We don't want to access bazel through Yarn and NodeJS
109109
# because it could mean that the Bazel child process only has access to limited memory.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: "Installing Yarn dependencies"
2+
description: "Installs the dependencies using Yarn"
3+
4+
runs:
5+
using: "composite"
6+
steps:
7+
- uses: actions/cache@v2
8+
with:
9+
path: |
10+
**/node_modules
11+
# Cache key. Whenever the postinstall patches change, the cache needs to be invalidated.
12+
# If just the `yarn.lock` file changes, the most recent cache can be restored though.
13+
# See: https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#example-using-the-cache-action.
14+
key: v2-${{hashFiles('tools/postinstall/apply-patches.js')}}-${{hashFiles('yarn.lock')}}
15+
restore-keys: v2-${{hashFiles('tools/postinstall/apply-patches.js')}}-
16+
17+
- run: yarn install --frozen-lockfile --non-interactive
18+
shell: bash

.github/workflows/build-dev-app.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# This workflow builds the dev-app for pull requests when a certain label is applied.
2+
# The actual deployment happens as part of a dedicated second workflow to avoid security
3+
# issues where the building would otherwise occur in an authorized context where secrets
4+
# could be leaked. More details can be found here:
5+
6+
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/.
7+
8+
name: Build dev-app for deployment
9+
10+
on:
11+
pull_request:
12+
types: [synchronize, labeled]
13+
14+
jobs:
15+
dev-app-build:
16+
runs-on: ubuntu-latest
17+
# We only want to build and deploy the dev-app if the `dev-app preview` label has been
18+
# added, or if the label is already applied and new changes have been made in the PR.
19+
if: |
20+
(github.event.action == 'labeled' && github.event.label.name == 'dev-app preview') ||
21+
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'dev-app preview'))
22+
steps:
23+
- uses: actions/checkout@v2
24+
- uses: ./.github/actions/yarn-install
25+
26+
- run: ./scripts/bazel/setup-remote-execution.sh
27+
env:
28+
GCP_DECRYPT_TOKEN: angular
29+
30+
# Build the web package. Note that we also need to make the Github environment
31+
# variables available so that the RBE is configured.
32+
- name: Building dev-app
33+
run: |
34+
source ${GITHUB_ENV}
35+
bazel build //src/dev-app:web_package --symlink_prefix=dist/
36+
37+
# Prepare the workflow artifact that is available for the deploy workflow. We store the pull
38+
# request number and SHA in a file that can be read by the deploy workflow. This is necessary
39+
# so that the deploy workflow can create a comment on the PR that triggered the deploy.
40+
- run: |
41+
mkdir -p dist/devapp
42+
cp -R dist/bin/src/dev-app/web_package/* dist/devapp
43+
echo ${{github.event.pull_request.number}} > dist/devapp/pr_number
44+
echo ${{github.event.pull_request.head.sha}} > dist/devapp/pr_sha
45+
46+
# Upload the generated dev-app archive.
47+
- uses: actions/upload-artifact@v2
48+
with:
49+
name: devapp
50+
path: dist/devapp

.github/workflows/deploy-dev-app.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# This workflow runs whenever the dev-app build workflow has completed. Deployment happens
2+
# as part of a dedicated second workflow to avoid security issues where the building would
3+
# otherwise occur in an authorized context where secrets could be leaked.
4+
#
5+
# More details can be found here:
6+
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/.
7+
8+
name: Deploying dev-app to Firebase previews
9+
10+
on:
11+
workflow_run:
12+
workflows: [Build dev-app for deployment]
13+
types: [completed]
14+
15+
jobs:
16+
deploy-dev-app:
17+
runs-on: ubuntu-latest
18+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
19+
steps:
20+
- uses: actions/checkout@v2
21+
- uses: ./.github/actions/yarn-install
22+
23+
- name: 'Download artifact from build job'
24+
run: |
25+
./scripts/github/fetch-workflow-artifact.mjs ${{secrets.GITHUB_TOKEN}} \
26+
${{github.event.workflow_run.id}} devapp > devapp.zip
27+
28+
- name: Extracting workflow artifact into Firebase public directory.
29+
run: |
30+
mkdir -p dist/dev-app-web-pkg
31+
unzip devapp.zip -d dist/dev-app-web-pkg
32+
33+
- name: Extracting pull request from extracted workflow artifact.
34+
id: pr_info
35+
run: |
36+
echo "::set-output name=number::$(cat ./dist/dev-app-web-pkg/pr_number)"
37+
echo "::set-output name=sha::$(cat ./dist/dev-app-web-pkg/pr_sha)"
38+
39+
- uses: FirebaseExtended/action-hosting-deploy@v0
40+
id: deploy
41+
with:
42+
repoToken: '${{secrets.GITHUB_TOKEN}}'
43+
firebaseServiceAccount: '${{secrets.FIREBASE_PREVIEW_SERVICE_TOKEN}}'
44+
expires: 20d
45+
projectId: angular-components-test
46+
channelId: pr-${{steps.pr_info.outputs.number}}-${{steps.pr_info.outputs.sha}}
47+
48+
- uses: marocchino/sticky-pull-request-comment@v2
49+
with:
50+
message: |
51+
Deployed dev-app to: ${{ steps.deploy.outputs.details_url }}
52+
number: ${{ steps.pr_info.outputs.number }}
File renamed without changes.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/bash
2+
3+
# The script should immediately exit if any command in the script fails.
4+
set -e
5+
6+
if [[ -z "${GCP_DECRYPT_TOKEN}" ]]; then
7+
echo "Please specify the \"GCP_DECRYPT_TOKEN\" environment variable when setting up remote " \
8+
"execution"
9+
exit 1
10+
fi
11+
12+
# Decode the GCP token that is needed to authenticate the Bazel remote execution.
13+
openssl aes-256-cbc -d -in scripts/bazel/gcp_token -md md5 -k ${GCP_DECRYPT_TOKEN} \
14+
-out $HOME/.gcp_credentials
15+
16+
# Set the "GOOGLE_APPLICATION_CREDENTIALS" environment variable. It should point to the GCP credentials
17+
# file. Bazel will then automatically picks up the credentials from that variable.
18+
# https://docs.bazel.build/versions/main/command-line-reference.html#flag--google_default_credentials
19+
# https://cloud.google.com/docs/authentication/production.
20+
if [[ ! -z "${BASH_ENV}" ]]; then
21+
# CircleCI uses the `BASH_ENV` variable for environment variables.
22+
echo "export GOOGLE_APPLICATION_CREDENTIALS=${HOME}/.gcp_credentials" >> ${BASH_ENV}
23+
elif [[ ! -z "${GITHUB_ENV}" ]]; then
24+
# Github actions use the `GITHUB_ENV` variable for environment variables.
25+
echo "GOOGLE_APPLICATION_CREDENTIALS=${HOME}/.gcp_credentials" >> ${GITHUB_ENV}
26+
fi
27+
28+
# Update the project Bazel configuration to always use remote execution.
29+
echo "build --config=remote" >> .bazelrc

scripts/circleci/bazel/setup-remote-execution.sh

Lines changed: 0 additions & 22 deletions
This file was deleted.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Fetches a specified artifact by name from the given workflow and writes
5+
* the downloaded zip file to the stdout.
6+
*
7+
* Command line usage:
8+
* ./fetch-workflow-artifact.js <gh-token> <workflow-id> <artifact-name>
9+
*/
10+
11+
import octokit from '@octokit/rest';
12+
13+
async function main() {
14+
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/', 2);
15+
const [token, workflowId, artifactName] = process.argv.slice(2);
16+
const github = new octokit.Octokit({auth: token});
17+
const artifacts = await github.actions.listWorkflowRunArtifacts({
18+
owner,
19+
repo,
20+
run_id: workflowId,
21+
});
22+
23+
const matchArtifact = artifacts.data.artifacts.find(
24+
artifact => artifact.name === artifactName,
25+
);
26+
27+
const download = await github.actions.downloadArtifact({
28+
owner,
29+
repo,
30+
artifact_id: matchArtifact.id,
31+
archive_format: 'zip',
32+
});
33+
34+
process.stdout.write(Buffer.from(download.data));
35+
}
36+
37+
await main();

0 commit comments

Comments
 (0)