Skip to content

Commit

Permalink
Sc 28936 create a generic GitHub action to push frontend (#17)
Browse files Browse the repository at this point in the history
* test action

* test

* Test each scenario

* Adding inputs

* change input

* Finalizing inputs

* Adding readme and removed echos

* changed the env var

* test github context

* Debug

* Accept pull request number as input

* update readme

* Added separate steps for upload and delete based on user input

* Adding generic steps

* Check for case

* Bugfix with if statement

* bugfix

* bugfix

* debug if statements

* debug if statements

* bugfix

* Fixed bugs in expressions

* Fixed exlude-files input

* Update readme

* Adding newline

* Remove echos

* Update exclude files logic

Co-authored-by: Rashid N H M <[email protected]>

* Implementing changes based on review

Co-authored-by: Rashid N H M <[email protected]>
  • Loading branch information
sanchitbapat and rashidnhm authored Nov 14, 2022
1 parent cf96005 commit a931277
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 0 deletions.
69 changes: 69 additions & 0 deletions push-to-s3-and-invalidate-cloudfront/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Action to push files to an S3 bucket to deploy a static web application

This action can be used to update a static web application that is built using AWS S3 and Cloudfront. The action pushes built code to an S3 bucket that already exists. One of the intended use cases for this action is Pull Request previews.

Files are pushed to the S3 bucket depending on the user inputs supplied when the action is called. Some inputs have default values or may be inferred from the github context.
Following examples describe how to use the action.

## Uploading to S3 based on user input
This example will **upload** the specified folder (`build-directory`) to a folder named `blog` on the S3 bucket(`myS3Bucket`). The action will not exclude any files while uploading them. The action will also invalidate the cloudfront cache.

```yaml
- uses: XanaduAI/cloud-actions/push-to-s3-and-invalidate-cloudfront@main
with:
build-directory: build-dir
pull-request-number: 123
aws-cloudfront-distribution-id: ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }}
aws-region: ${{ secrets.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
s3-bucket: myS3Bucket
s3-action: "upload"
s3-dir-to-upload-to: "blog"
s3-files-to-exclude: ""
invalidate-cloudfront-cache: "true"
```
## Deleting from S3 based on user input
This example will **delete** the specified folder (`build-directory`) from the S3 bucket(`myS3Bucket`). The action will also invalidate the cloudfront cache.

```yaml
- uses: XanaduAI/cloud-actions/push-to-s3-and-invalidate-cloudfront@main
with:
build-directory: build-dir
pull-request-number: 123
aws-cloudfront-distribution-id: ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }}
aws-region: ${{ secrets.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
s3-bucket: myS3Bucket
s3-action: "delete"
s3-dir-to-delete-from: "blog"
invalidate-cloudfront-cache: "true"
```

## Uploading/Deleting to/from S3 with minimal user unput
If this action is called when a `pull_request` is `opened` or `syncronized`, then the example will **upload** the specified folder (`build-directory`) to a folder named `pr-previews/PR-123` on the S3 bucket (`myS3Bucket`). The action will exclude any files that start with the default `pr-previews` prefix. The action will not invalidate the cloudfront cache.

If this action is called when a `pull_request` is `closed`, then the example will **delete** the folder named `pr-previews/PR-123` on the S3 bucket (`myS3Bucket`).

```yaml
- uses: XanaduAI/cloud-actions/push-to-s3-and-invalidate-cloudfront@main
with:
build-directory: build-dir
pull-request-number: 123
aws-cloudfront-distribution-id: ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }}
aws-region: ${{ secrets.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
s3-bucket: myS3Bucket
```
## AWS Permissions
The IAM machine user needs the following permissions on the S3 bucket and the Cloudfront distribution:
```
s3:ListBucket
s3:GetObject
s3:PutObject
s3:DeleteObject
cloudfront:CreateInvalidation
```
133 changes: 133 additions & 0 deletions push-to-s3-and-invalidate-cloudfront/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
name: Push files to an S3 bucket destination and invalidate the cloudfront cache
description: |
This action will push files to a destination S3 bucket (or a folder within a bucket) and invalidates the cloudfront cache.
The action also removes any stale files from the bucket. The action can also delete files/folders from the bucket.
inputs:
build-directory:
description: The directory that contains the build files to upload to S3
required: true
aws-cloudfront-distribution-id:
description: The cloudfront distribution ID for the app
required: true
aws-region:
description: The aws region used by the IAM machine user
required: true
aws-access-key-id:
description: The aws access key id for the IAM machine user
required: true
aws-secret-access-key:
description: The aws secret access key for the IAM machine user
required: true
s3-bucket:
description: The name of the S3 bucket
required: true

pull-request-number:
description: The pull request number for which a preview needs to be generated/updated. This is required if you do not specify the s3-directory variable.
required: false
s3-action:
description: |
The action to take on the S3 bucket. Typically this should be `update` to push the latest files to S3 or `delete` to delete the folder from S3.
If this is not specified, the action will be infered from the $github.event.action variable
required: false
default: ""
s3-directory:
description: |
The S3 directory to upload the files to or delete the files from. If unspecified, the default `PR-$pull-request-number` will be used
or the directory may be infered from the github context. A value of `/` indicates the root of the bucket.
required: false
default: "pr-previews"
s3-files-to-exclude:
description: |
A pattern or a comma separated list of files to exclude when running the aws s3 sync command.
Refer to https://docs.aws.amazon.com/cli/latest/reference/s3/index.html#use-of-exclude-and-include-filters or more information
Set this variable to an empty string if you don't want to exlcude any files
If unspecified will default to `pr-previews/*`
required: false
default: "pr-previews/*"
invalidate-cloudfront-cache:
description: A flag to indicate if the cloudfront cache should be invalidated
required: false
default: "false"

outputs:
dir_name:
description: The S3 directory to upload the files to or delete files from. Computed based on user input.
value: ${{ steps.directory.dir_name }}
files_to_exlude:
description: Files to exclude from S3 when using the sync command.
value: ${{ steps.exclude.files_to_exclude }}

runs:
using: composite
steps:
- name: Input validation for pull-request-number
if: inputs.s3-directory == 'pr-previews' && inputs.pull-request-number == ''
uses: actions/github-script@v6
with:
script: |
core.setFailed('Either of the pull-request-number or the s3-directory are required')
- name: Compute the S3 directory to upload to or delete from
shell: bash
id: directory
run: |
if [[ "${{ inputs.s3-directory }}" == "pr-previews" ]]
then
dir_name="${{ inputs.s3-directory }}/PR-${{ inputs.pull-request-number }}"
elif [[ "${{ inputs.s3-directory }}" == "/" ]]
then
dir_name=""
else
dir_name="${{ inputs.s3-directory }}"
fi
echo "dir_name=$dir_name" >> $GITHUB_OUTPUT
- name: Compute files to exclude
shell: bash
id: exclude
run: |
files_to_exlude=""
if [[ "${{ inputs.s3-files-to-exclude }}" == *,* ]]
then
IFS=','
for i in `echo "${{ inputs.s3-files-to-exclude }}"`
do
files_to_exlude="$files_to_exlude --exclude \"$i\""
done
unset IFS
elif [[ "${{ inputs.s3-files-to-exclude }}" != "" ]]
then
files_to_exlude="--exclude \"${{ inputs.s3-files-to-exclude }}\""
fi
echo "files_to_exlude=$files_to_exlude" >> $GITHUB_OUTPUT
- name: Upload the files to S3
shell: bash
if: inputs.s3-action == 'upload' || (inputs.s3-action == '' && github.event_name == 'pull_request' && contains(fromJson('["opened", "synchronize"]'), github.event.action))
env:
AWS_REGION: ${{ inputs.aws-region }}
AWS_ACCESS_KEY_ID: ${{ inputs.aws-access-key-id }}
AWS_SECRET_ACCESS_KEY: ${{ inputs.aws-secret-access-key }}
run: |
aws s3 sync ${{ inputs.build-directory }}/ s3://${{ inputs.s3-bucket }}/${{ steps.directory.outputs.dir_name }} ${{ steps.exclude.outputs.files_to_exlude }}
- name: Delete the files on S3
shell: bash
if: inputs.s3-action == 'delete' || (inputs.s3-action == '' && github.event_name == 'pull_request' && github.event.action == 'closed')
env:
AWS_REGION: ${{ inputs.aws-region }}
AWS_ACCESS_KEY_ID: ${{ inputs.aws-access-key-id }}
AWS_SECRET_ACCESS_KEY: ${{ inputs.aws-secret-access-key }}
run: aws s3 rm s3://${{ inputs.s3-bucket }}/${{ steps.directory.outputs.dir_name }}/ --recursive ${{ steps.exclude.outputs.files_to_exlude }}

- name: Invalidate the cloudfront cache
shell: bash
if: inputs.invalidate-cloudfront-cache == 'true'
env:
AWS_REGION: ${{ inputs.aws-region }}
AWS_ACCESS_KEY_ID: ${{ inputs.aws-access-key-id }}
AWS_SECRET_ACCESS_KEY: ${{ inputs.aws-secret-access-key }}
run: |
aws cloudfront create-invalidation --distribution-id ${{ inputs.aws-cloudfront-distribution-id }} --paths "/${{ steps.directory.outputs.dir_name }}/*"

0 comments on commit a931277

Please sign in to comment.