Skip to content

build: setup preview builds for dev-app #23825

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();