Skip to content

Commit

Permalink
build: setup preview builds for dev-app (#23825)
Browse files Browse the repository at this point in the history
* 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

(cherry picked from commit 7ec0139)
  • Loading branch information
devversion authored and wagnermaciel committed Oct 27, 2021
1 parent d8c84d6 commit 37c39ed
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ var_15: &ignore_presubmit_branch_filter
var_16: &setup_bazel_remote_execution
run:
name: "Setup bazel RBE remote execution"
command: ./scripts/circleci/bazel/setup-remote-execution.sh
command: ./scripts/bazel/setup-remote-execution.sh

# Sets up the bazel binary globally. We don't want to access bazel through Yarn and NodeJS
# because it could mean that the Bazel child process only has access to limited memory.
Expand Down
18 changes: 18 additions & 0 deletions .github/actions/yarn-install/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: "Installing Yarn dependencies"
description: "Installs the dependencies using Yarn"

runs:
using: "composite"
steps:
- uses: actions/cache@v2
with:
path: |
**/node_modules
# Cache key. Whenever the postinstall patches change, the cache needs to be invalidated.
# If just the `yarn.lock` file changes, the most recent cache can be restored though.
# See: https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#example-using-the-cache-action.
key: v2-${{hashFiles('tools/postinstall/apply-patches.js')}}-${{hashFiles('yarn.lock')}}
restore-keys: v2-${{hashFiles('tools/postinstall/apply-patches.js')}}-

- run: yarn install --frozen-lockfile --non-interactive
shell: bash
50 changes: 50 additions & 0 deletions .github/workflows/build-dev-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# This workflow builds the dev-app for pull requests when a certain label is applied.
# The actual deployment happens as part of a dedicated second workflow to avoid security
# issues where the building would otherwise occur in an authorized context where secrets
# could be leaked. More details can be found here:

# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/.

name: Build dev-app for deployment

on:
pull_request:
types: [synchronize, labeled]

jobs:
dev-app-build:
runs-on: ubuntu-latest
# We only want to build and deploy the dev-app if the `dev-app preview` label has been
# added, or if the label is already applied and new changes have been made in the PR.
if: |
(github.event.action == 'labeled' && github.event.label.name == 'dev-app preview') ||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'dev-app preview'))
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/yarn-install

- run: ./scripts/bazel/setup-remote-execution.sh
env:
GCP_DECRYPT_TOKEN: angular

# Build the web package. Note that we also need to make the Github environment
# variables available so that the RBE is configured.
- name: Building dev-app
run: |
source ${GITHUB_ENV}
bazel build //src/dev-app:web_package --symlink_prefix=dist/
# Prepare the workflow artifact that is available for the deploy workflow. We store the pull
# request number and SHA in a file that can be read by the deploy workflow. This is necessary
# so that the deploy workflow can create a comment on the PR that triggered the deploy.
- run: |
mkdir -p dist/devapp
cp -R dist/bin/src/dev-app/web_package/* dist/devapp
echo ${{github.event.pull_request.number}} > dist/devapp/pr_number
echo ${{github.event.pull_request.head.sha}} > dist/devapp/pr_sha
# Upload the generated dev-app archive.
- uses: actions/upload-artifact@v2
with:
name: devapp
path: dist/devapp
52 changes: 52 additions & 0 deletions .github/workflows/deploy-dev-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# This workflow runs whenever the dev-app build workflow has completed. Deployment happens
# as part of a dedicated second workflow to avoid security issues where the building would
# otherwise occur in an authorized context where secrets could be leaked.
#
# More details can be found here:
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/.

name: Deploying dev-app to Firebase previews

on:
workflow_run:
workflows: [Build dev-app for deployment]
types: [completed]

jobs:
deploy-dev-app:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/yarn-install

- name: 'Download artifact from build job'
run: |
./scripts/github/fetch-workflow-artifact.mjs ${{secrets.GITHUB_TOKEN}} \
${{github.event.workflow_run.id}} devapp > devapp.zip
- name: Extracting workflow artifact into Firebase public directory.
run: |
mkdir -p dist/dev-app-web-pkg
unzip devapp.zip -d dist/dev-app-web-pkg
- name: Extracting pull request from extracted workflow artifact.
id: pr_info
run: |
echo "::set-output name=number::$(cat ./dist/dev-app-web-pkg/pr_number)"
echo "::set-output name=sha::$(cat ./dist/dev-app-web-pkg/pr_sha)"
- uses: FirebaseExtended/action-hosting-deploy@v0
id: deploy
with:
repoToken: '${{secrets.GITHUB_TOKEN}}'
firebaseServiceAccount: '${{secrets.FIREBASE_PREVIEW_SERVICE_TOKEN}}'
expires: 20d
projectId: angular-components-test
channelId: pr-${{steps.pr_info.outputs.number}}-${{steps.pr_info.outputs.sha}}

- uses: marocchino/sticky-pull-request-comment@v2
with:
message: |
Deployed dev-app to: ${{ steps.deploy.outputs.details_url }}
number: ${{ steps.pr_info.outputs.number }}
File renamed without changes.
29 changes: 29 additions & 0 deletions scripts/bazel/setup-remote-execution.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

# The script should immediately exit if any command in the script fails.
set -e

if [[ -z "${GCP_DECRYPT_TOKEN}" ]]; then
echo "Please specify the \"GCP_DECRYPT_TOKEN\" environment variable when setting up remote " \
"execution"
exit 1
fi

# Decode the GCP token that is needed to authenticate the Bazel remote execution.
openssl aes-256-cbc -d -in scripts/bazel/gcp_token -md md5 -k ${GCP_DECRYPT_TOKEN} \
-out $HOME/.gcp_credentials

# Set the "GOOGLE_APPLICATION_CREDENTIALS" environment variable. It should point to the GCP credentials
# file. Bazel will then automatically picks up the credentials from that variable.
# https://docs.bazel.build/versions/main/command-line-reference.html#flag--google_default_credentials
# https://cloud.google.com/docs/authentication/production.
if [[ ! -z "${BASH_ENV}" ]]; then
# CircleCI uses the `BASH_ENV` variable for environment variables.
echo "export GOOGLE_APPLICATION_CREDENTIALS=${HOME}/.gcp_credentials" >> ${BASH_ENV}
elif [[ ! -z "${GITHUB_ENV}" ]]; then
# Github actions use the `GITHUB_ENV` variable for environment variables.
echo "GOOGLE_APPLICATION_CREDENTIALS=${HOME}/.gcp_credentials" >> ${GITHUB_ENV}
fi

# Update the project Bazel configuration to always use remote execution.
echo "build --config=remote" >> .bazelrc
22 changes: 0 additions & 22 deletions scripts/circleci/bazel/setup-remote-execution.sh

This file was deleted.

37 changes: 37 additions & 0 deletions scripts/github/fetch-workflow-artifact.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env node

/**
* Fetches a specified artifact by name from the given workflow and writes
* the downloaded zip file to the stdout.
*
* Command line usage:
* ./fetch-workflow-artifact.js <gh-token> <workflow-id> <artifact-name>
*/

import octokit from '@octokit/rest';

async function main() {
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/', 2);
const [token, workflowId, artifactName] = process.argv.slice(2);
const github = new octokit.Octokit({auth: token});
const artifacts = await github.actions.listWorkflowRunArtifacts({
owner,
repo,
run_id: workflowId,
});

const matchArtifact = artifacts.data.artifacts.find(
artifact => artifact.name === artifactName,
);

const download = await github.actions.downloadArtifact({
owner,
repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});

process.stdout.write(Buffer.from(download.data));
}

await main();

0 comments on commit 37c39ed

Please sign in to comment.