Skip to content

Commit

Permalink
feat(github-actions): introduce actions for safely deploying previews
Browse files Browse the repository at this point in the history
This action will be used to supersede/simplify the existing Github action
workflows in `angular/components` and the AIO docker VM setup.
  • Loading branch information
devversion committed Nov 28, 2022
1 parent 361be06 commit a818bed
Show file tree
Hide file tree
Showing 15 changed files with 11,399 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ github-actions/post-approval-changes/main.js
github-actions/org-file-sync/main.js
github-actions/labels-sync/main.js
github-actions/branch-manager/main.js

github-actions/deploy-previews/pack-and-upload-artifact/inject-artifact-metadata.js
github-actions/deploy-previews/upload-artifacts-to-firebase/extract-artifact-metadata.js
github-actions/deploy-previews/upload-artifacts-to-firebase/fetch-workflow-artifact.js

.github/local-actions/branch-manager/main.js
.github/local-actions/changelog/main.js

Expand Down
9 changes: 9 additions & 0 deletions github-actions/deploy-previews/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("//tools:defaults.bzl", "ts_library")

package(default_visibility = ["//github-actions/deploy-previews:__subpackages__"])

ts_library(
name = "constants_lib",
srcs = ["constants.ts"],
deps = [],
)
12 changes: 12 additions & 0 deletions github-actions/deploy-previews/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

export const artifactMetadata = {
'pull-number': './__metadata__pull_number.txt',
'build-revision': './__metadata__build_revision.txt',
} as const;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
load("//tools:defaults.bzl", "esbuild_checked_in")

esbuild_checked_in(
name = "inject-artifact-metadata",
entry_point = "//github-actions/deploy-previews/pack-and-upload-artifact/lib:inject-artifact-metadata.ts",
target = "node16",
deps = [
"//github-actions/deploy-previews/pack-and-upload-artifact/lib:inject_artifact_metadata_lib",
],
)
49 changes: 49 additions & 0 deletions github-actions/deploy-previews/pack-and-upload-artifact/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Pack and upload Artifacts to Workflow
author: 'Angular'
description: |
Action that takes a built artifact, injects metadata, zips it up, uploads
it to the current GitHub action workflow.
Following best-practices from:
https://securitylab.github.com/research/github-actions-preventing-pwn-requests
# NOTE: All inputs here are considered unsafe since this action is expected
# to run in workflows from forked builds (allowing for arbitrary changes).
inputs:
workflow-artifact-name:
required: true
description: |
Name of the artifact that should be deployed. A workflow may contain
multiple artifacts but only a single one can be picked for deployment.
pull-number:
required: true
description: Pull request for which the artifact is built for.

artifact-build-revision:
required: true
description: |
A unique identifier describing the revision of the artifact. This is
usually the Git SHA for which the artifact has been built.
deploy-directory:
required: true
description: |
Project-relative path to the directory contents that should be deployed.
This is usually the distribution directory, like `dist/my-app/`.
runs:
using: composite
steps:
- name: Injecting artifact metadata
shell: bash
run: |
node ${{github.action_path}}/inject-artifact-metadata.js \
'${{inputs.deploy-directory}}' \
'${{inputs.pull-number}}' \
'${{inputs.artifact-build-revision}}'
- uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # renovate: tag=v2.0.0
with:
name: '${{inputs.workflow-artifact-name}}'
path: '${{inputs.deploy-directory}}'
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

import {createRequire as __cjsCompatRequire} from 'module';
const require = __cjsCompatRequire(import.meta.url);


//
import path from "path";
import fs from "fs";

//
var artifactMetadata = {
"pull-number": "./__metadata__pull_number.txt",
"build-revision": "./__metadata__build_revision.txt"
};

//
async function main() {
const [deployDirPath, prNumber, buildRevision] = process.argv.slice(2);
await fs.promises.writeFile(path.join(deployDirPath, artifactMetadata["pull-number"]), prNumber);
await fs.promises.writeFile(path.join(deployDirPath, artifactMetadata["build-revision"]), buildRevision);
}
try {
await main();
} catch (e) {
console.error(e);
process.exit(1);
}
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
load("//tools:defaults.bzl", "ts_library")

package(default_visibility = ["//github-actions/deploy-previews:__subpackages__"])

ts_library(
name = "inject_artifact_metadata_lib",
srcs = ["inject-artifact-metadata.ts"],
deps = [
"//github-actions/deploy-previews:constants_lib",
"@npm//@types/node",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

/**
* Injects metadata information into the given unpacked artifact. An artifact
* is expected to contain metadata such as the pull request it was built for.
*
* The deploy job later will extract this information when it fetches the artifact.
*
* See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow.
*/

import path from 'path';
import fs from 'fs';

import {artifactMetadata} from '../../constants.js';

async function main() {
const [deployDirPath, prNumber, buildRevision] = process.argv.slice(2);

await fs.promises.writeFile(path.join(deployDirPath, artifactMetadata['pull-number']), prNumber);
await fs.promises.writeFile(
path.join(deployDirPath, artifactMetadata['build-revision']),
buildRevision,
);
}

try {
await main();
} catch (e) {
console.error(e);
process.exit(1);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
load("//tools:defaults.bzl", "esbuild_checked_in")

esbuild_checked_in(
name = "fetch-workflow-artifact",
entry_point = "//github-actions/deploy-previews/upload-artifacts-to-firebase/lib:fetch-workflow-artifact.ts",
target = "node16",
deps = [
"//github-actions/deploy-previews/upload-artifacts-to-firebase/lib:fetch_workflow_artifact_lib",
],
)

esbuild_checked_in(
name = "extract-artifact-metadata",
entry_point = "//github-actions/deploy-previews/upload-artifacts-to-firebase/lib:extract-artifact-metadata.ts",
target = "node16",
deps = [
"//github-actions/deploy-previews/upload-artifacts-to-firebase/lib:extract_artifact_metadata_lib",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Upload Artifacts to Firebase previews
author: 'Angular'
description: |
Action that downloads a named artifact from a GitHub workflow and uploads
it to a Firebase preview channel. The deployment to Firebase happens through
a separate upload artifact workflow to avoid executing build processes in
privileged environments.
Following best-practices from:
https://securitylab.github.com/research/github-actions-preventing-pwn-requests
# NOTE: There are two categories of input data to this action:
#
# - TRUSTED: Values for such inputs should not be changeable by third-parties.
# This is information usually hard-coded in the privileged job running from `main`.
#
# - RISK: These inputs are considered unsafe and values may be abused by third-parties.
# This is usually information coming directly from the build job portion.
inputs:
github-token:
required: true
description: |
TRUSTED: GitHub Token used for creating the commit statuses.
workflow-artifact-name:
required: true
description: |
TRUSTED: Name of the artifact that should be deployed. A workflow may contain
multiple artifacts but only a single one can be picked for deployment.
firebase-config-dir:
default: './'
description: |
TRUSTED: Project-relative path to the directory containing the `firebase.json` file.
firebase-public-dir:
required: true
description: |
TRUSTED: Project-relative path to the directory where artifacts should be put into.
firebase-project-id:
required: true
description: |
TRUSTED: ID of the Firebase project used for deployment.
firebase-service-key:
required: true
description: |
TRUSTED: Contents of a Firebase service key authorizing the deployment.
runs:
using: composite
steps:
- name: 'Download artifact from build job'
shell: bash
run: |
node ${{github.action_path}}/fetch-workflow-artifact.js \
'${{github.event.workflow_run.id}}' '${{inputs.workflow-artifact-name}}' > unsafe-artifact.zip"
env:
GITHUB_TOKEN: '${{inputs.github-token}}'

# RISK: The downloaded `unsafe-artifact` is of input category `RISK`.
- name: Extracting workflow artifact into Firebase public directory.
shell: bash
run: |
mkdir -p '${{inputs.firebase-public-dir}}'
unzip unsafe-artifact.zip -d '${{inputs.firebase-public-dir}}'
- name: Extracting artifact metadata
id: artifact-info
shell: bash
run: node ${{github.action_path}}/extract-artifact-metadata.js '${{inputs.firebase-public-dir}}'

- uses: FirebaseExtended/action-hosting-deploy@276388dd6c2cde23455b30293105cc866c22282d # renovate: tag=v0.0.0
id: deploy
with:
# Note: No token used here as the action otherwise may attempt to post a
# comment. We use our own sticky non-spam comments below.
repoToken: ''
firebaseServiceAccount: '${{inputs.firebase-service-key}}'
expires: 20d
projectId: '${{inputs.firebase-project-id}}'
entryPoint: '${{inputs.firebase-config-dir}'
channelId: pr-${{steps.artifact-info.outputs.pull-number}}-${{steps.artifact-info.outputs.build-revision}}

- uses: marocchino/sticky-pull-request-comment@39c5b5dc7717447d0cba270cd115037d32d28443 # renovate: tag=v2.0.0
with:
message: |
Deployed ${{inputs.workflow-artifact-name}} to: ${{steps.deploy.outputs.details_url}}
number: ${{steps.artifact-info.outputs.pull-number}}
Loading

0 comments on commit a818bed

Please sign in to comment.