Skip to content

Commit

Permalink
Merge pull request #54 from clouddrove/internal_357
Browse files Browse the repository at this point in the history
Feature: Updated SST Workflow with Add-ons
  • Loading branch information
clouddrove-ci authored Jul 24, 2023
2 parents 7a232b8 + e495b23 commit c3f1647
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 66 deletions.
53 changes: 53 additions & 0 deletions .github/actions/yarn-nm-install
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
########################################################################################
# "yarn install" composite action for yarn 2/3/4+ and "nodeLinker: node-modules" #
#--------------------------------------------------------------------------------------#
# Cache: #
# - Downloaded zip archive (multi-arch, preserved across yarn.lock changes) #
# - Yarn install state (discarded on yarn.lock changes) #
# References: #
# - bench: https://gist.github.com/belgattitude/0ecd26155b47e7be1be6163ecfbb0f0b #
# - vs @setup/node: https://github.com/actions/setup-node/issues/325 #
########################################################################################

name: 'Yarn install'
description: 'Run yarn install with node_modules linker and cache enabled'

runs:
using: 'composite'
steps:
- name: Expose yarn config as "$GITHUB_OUTPUT"
id: yarn-config
shell: bash
run: |
echo "CACHE_FOLDER=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT

# Yarn rotates the downloaded cache archives, @see https://github.com/actions/setup-node/issues/325
# Yarn cache is also reusable between arch and os.
- name: Restore yarn cache
uses: actions/cache@v3
id: yarn-download-cache
with:
path: ${{ steps.yarn-config.outputs.CACHE_FOLDER }}
key: yarn-download-cache-${{ hashFiles('yarn.lock') }}
restore-keys: |
yarn-download-cache-

# Invalidated on yarn.lock changes
- name: Restore yarn install state
id: yarn-install-state-cache
uses: actions/cache@v3
with:
path: .yarn/ci-cache/
key: ${{ runner.os }}-yarn-install-state-cache-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}

- name: Install dependencies
shell: bash
run: |
yarn install --immutable --inline-builds
env:
# CI optimizations. Overrides yarnrc.yml options (or their defaults) in the CI action.
YARN_ENABLE_GLOBAL_CACHE: 'false' # Use local cache folder to keep downloaded archives
YARN_NM_MODE: 'hardlinks-local' # Hardlinks-(local|global) reduces io / node_modules size
YARN_INSTALL_STATE_PATH: .yarn/ci-cache/install-state.gz # Very small speedup when lock does not change
# Other environment variables
HUSKY: '0' # By default do not run HUSKY install
8 changes: 4 additions & 4 deletions .github/workflows/helm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ on:
type: string
description: 'Environment name for rollback'
secrets:
aws-access-key-id:
AWS_ACCESS_KEY_ID:
description: 'AWS Access Key ID'
required: false
aws-secret-access-key:
AWS_SECRET_ACCESS_KEY:
description: 'AWS Secret Access Key'
required: false
AZURE_CREDENTIALS:
Expand All @@ -79,8 +79,8 @@ jobs:
if: ${{ inputs.provider == 'aws' }}
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.aws-access-key-id }}
aws-secret-access-key: ${{ secrets.aws-secret-access-key }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ inputs.aws-region }}

- name: Install Azure CLI
Expand Down
140 changes: 110 additions & 30 deletions .github/workflows/sst_workflow.yml
Original file line number Diff line number Diff line change
@@ -1,65 +1,145 @@
name: Shared workflow- SST APP
name: Shared Workflow - SST Deploy

on:
workflow_call:
inputs:
app-env:
description: 'Application environment'
description: 'Application environment'
required: true
type: string
preview:
description: 'Create or destroy preview env'
required: false
type: string
default: false
working-directory:
description: 'Working directory in the repository'
required: true
description: 'Working directory in repo'
required: false
type: string
default: ./
stack-name:
description: 'Stack name'
required: false
default: ""
type: string
yarn-cache:
description: 'Cache required or not for yarn install'
type: string
default: false
deploy:
description: 'Default deploy otherwise run diff command to detect changes in stacks'
type: string
default: true
self-hosted:
description: 'Deploy stack with github self hosted runner or not'
type: string
default: true

secrets:
aws-access-key-id:
description: 'AWS Access Key ID'
required: true
aws-secret-access-key:
description: 'AWS Secret Access Key'
token:
description: 'GitHub Token'
required: false
env-vars:
description: 'Environment-variables to store in .env file'
required: false
build-role:
description: 'Assume role arn'
required: true


jobs:
deploy:
runs-on: ubuntu-20.04
environment:
name: ${{ github.head_ref }}
url: ${{ env.API_ENDPOINT_URL }}
setup:
runs-on: ubuntu-latest
outputs:
runner: ${{ steps.step1.outputs.runner }}
steps:
- name: Check branch
id: step1
run: |
if [ ${{ inputs.self-hosted }} == 'true' ]; then
echo "runner=${{ inputs.app-env }}" >> "$GITHUB_OUTPUT"
else
echo "runner=ubuntu-latest" >> "$GITHUB_OUTPUT"
fi
sst-deploy:
needs: [setup]
runs-on: ${{ needs.setup.outputs.runner }}
environment:
name: ${{ (((github.event.action == 'opened' || github.event.action == 'synchronize') && inputs.preview == 'true') || (github.event.pull_request.merged == true && inputs.preview == 'false' && inputs.app-env == 'staging') || (inputs.app-env == 'production' && startsWith(github.ref, 'refs/tags/v'))) && ((inputs.preview == 'true' && (inputs.stack-name != '' && github.head_ref-inputs.stack-name || github.head_ref) || inputs.app-env)) || '' }}
url: ${{ ((github.event.action == 'opened' && inputs.preview == 'true') || (github.event.action == 'synchronize' && inputs.preview == 'true') || (github.event.pull_request.merged == true && inputs.preview == 'false' && inputs.app-env == 'staging') || (inputs.app-env == 'production' && startsWith(github.ref, 'refs/tags/v'))) && env.API_ENDPOINT_URL }}
defaults:
run:
working-directory: ${{ inputs.working-directory }}

name: Deploy SST APP
name: Run sst-deploy
steps:
- name: Checkout git repo
uses: actions/checkout@v3

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
- name: update environment variable in .env file
run: |
if [ -n "${{ secrets.env-vars }}" ]; then
echo -e "${{ secrets.env-vars }}" > ./.env
fi
- name: Configure AWS Creds via role
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-access-key-id: ${{ secrets.aws-access-key-id }}
aws-secret-access-key: ${{ secrets.aws-secret-access-key }}
aws-region: us-east-2
aws-region: us-west-2
role-to-assume: ${{ secrets.build-role }}
role-duration-seconds: 900
role-skip-session-tagging: true

- name: Install yarn
run: sudo npm install -g yarn

- name: Install dependencies (yarn install)
run: yarn install

- name: Extract branch name
- name: Install dependencies
if: ${{ inputs.yarn-cache != 'true' }}
run: yarn install --frozen-lockfile

- name: Install dependencies with yarn cache
if: ${{ inputs.yarn-cache == 'true' }}
uses: ./.github/actions/yarn-nm-install

- name: Set branch name
run: |
BRANCH_NAME=$(echo "${{ github.head_ref }}" | cut -d'/' -f3)
BRANCH_NAME=$(echo "${{ github.head_ref }}" | cut -d'|' -f2)
echo "BRANCH_NAME=${BRANCH_NAME}"
SLUG_BRANCH_NAME=$(echo "${BRANCH_NAME}" | sed 's/[^[:alnum:]]/-/g' | tr -s '-' | tr A-Z a-z)
echo "SLUG_BRANCH_NAME=${SLUG_BRANCH_NAME}"
echo "GITHUB_HEAD_REF_SLUG=${SLUG_BRANCH_NAME}" >> $GITHUB_ENV
- name: check diffrence in deployed and local stacks
if: ${{ inputs.deploy != 'true' }}
run: yarn sst diff --stage ${{ inputs.app-env }}

- name: Deploy and get API endpoint
if: ${{ (github.event.action == 'opened' || github.event.action == 'synchronize' && inputs.app-env == 'preview') || ( github.event.pull_request.merged == true && (inputs.app-env == 'prod' || inputs.app-env == 'stage')) }}
if: ${{ inputs.deploy == 'true' && ((github.event.action == 'opened' && inputs.preview == 'true') || (github.event.action == 'synchronize' && inputs.preview == 'true') || (github.event.pull_request.merged == true && inputs.preview == 'false' && inputs.app-env == 'staging') || (inputs.app-env == 'production' && startsWith(github.ref, 'refs/tags/v'))) }}
run: |
api_endpoint=$(yarn sst deploy --stage pr-${{ github.event.number }}-${{ env.GITHUB_HEAD_REF_SLUG }} | egrep "ApiEndpoint|SiteUrl" | awk '{print $2}')
if [[ ${{ inputs.preview }} == true ]]; then
if [[ -n "${{ inputs.stack-name }}" ]]; then
yarn sst deploy --stage pr-${{ github.event.number }}-${{ env.GITHUB_HEAD_REF_SLUG }} ${{ inputs.stack-name }} | tee deploy-output.log
else
yarn sst deploy --stage pr-${{ github.event.number }}-${{ env.GITHUB_HEAD_REF_SLUG }} | tee deploy-output.log
fi
else
if [[ -n "${{ inputs.stack-name }}" ]]; then
yarn sst deploy --stage ${{ inputs.app-env }} ${{ inputs.stack-name }} | tee deploy-output.log
else
yarn sst deploy --stage ${{ inputs.app-env }} | tee deploy-output.log
fi
fi
api_endpoint=$(cat deploy-output.log | egrep "ApiEndpoint|SiteUrl" | awk '{print $2}')
echo "API endpoint: $api_endpoint"
echo "API_ENDPOINT_URL=$api_endpoint" >> $GITHUB_ENV
- name: Destroy SST App for Preview app environment
if: ${{ ( github.event.action == 'labeled' && github.event.label.name == 'destroy' && inputs.app-env == 'preview' ) || (github.event.action == 'closed' && inputs.app-env == 'preview' || github.event.pull_request.merged == true && inputs.app-env == 'preview') }}
- name: Destroy preview env
if: ${{ ( github.event.action == 'labeled' && github.event.label.name == 'destroy' && inputs.preview == 'true' ) || (github.event.action == 'closed' && inputs.preview == 'true' || github.event.pull_request.merged == true && inputs.preview == 'true' ) }}
run: yarn sst remove --stage pr-${{ github.event.number }}-${{ env.GITHUB_HEAD_REF_SLUG }}

- name: Cleanup preview env deployment
if: ${{ ( github.event.action == 'labeled' && github.event.label.name == 'destroy' && inputs.preview == 'true' ) || (github.event.action == 'closed' && inputs.preview == 'true' || github.event.pull_request.merged == true && inputs.preview == 'true' ) }}
uses: strumwolf/[email protected]
with:
token: ${{ secrets.token }}
environment: ${{ github.head_ref }}
70 changes: 38 additions & 32 deletions docs/sst.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,50 @@
## [SST Workflow](https://github.com/clouddrove/github-shared-workflows/blob/master/.github/workflows/sst_workflow.yml)

This workflow is used to deploy serverless stack (SST) application on AWS environment. Workflows have been added in `.github/workflows/sst_workflow.yml`.
This workflow is used to deploy or destroy serverless stack (SST) application on AWS environment. Workflows have been added in `.github/workflows/sst_workflow.yml`.

#### Usage
Below workflow can be used to deploy SST in preview environment when pull request generated and it destroys the preview environment when pull request closed, merged and labeled as destroy, similarly staging and production is deployed using there defined branches.
The following workflow can be used to deploy the SST application in the staging environment when a pull request is generated on the base branch named "master". It will deploy the SST application in the production environment when a new tag is released. Additionally, it will destroy the preview environment when a pull request is closed, merged, and labeled as "destroy".

```yaml
name: SST Workflow

on:
pull_request:
types: [closed, merged, labeled]
workflow_dispatch:
jobs:
preview:
uses: clouddrove/github-shared-workflows/.github/workflows/sst_workflow.yml@master
secrets:
AWS_ACCESS_KEY_ID: # AWS Access Key ID for preview
AWS_SECRET_ACCESS_KEY: # AWS Secret Access Key for preview
with:
app-env: # preview
working-directory: # specify your working folder from repo
Inputs:
| Input name | Type | Required | Default | Comment |
|---|---|---|---|---|
| app-env | string | true | | Staging or Production |
| preview | string | false | false | If true SST deployed in preview environment |
| working-directory | string | false | ./ | SST code location path |
| stack-name | string | false | | Specify stack name for deployment |
| yarn-cache | string | false | false | Yarn stores packages in global cache |
| deploy | string | false | true | Plan app stacks or deploy. |
| self-hosted | string | false | true | Deploy stack with github runner or without it. |

staging:
if: ${{ github.base_ref == 'stage' }}
Secrets:
| Secret name | Required | Comment |
|---|---|---|
| token | false | GitHub token for environment deletion |
| env-vars | false | Stack environment variables |
| build-role | true | | AWS authentication role |

```yaml
staging-workflow:
if: ${{ github.event.pull_request.base.ref == 'master' }}
uses: clouddrove/github-shared-workflows/.github/workflows/sst_workflow.yml@master
secrets:
AWS_ACCESS_KEY_ID: # AWS Access Key ID for Stage
AWS_SECRET_ACCESS_KEY: # AWS Secret Access Key for stage
with:
app-env: # stage
working-directory: # specify your working folder from repo
app-env: staging

production:
if: ${{ github.base_ref == 'master' }}
production-workflow:
if: startsWith(github.event.ref, 'refs/tags/v')
uses: clouddrove/github-shared-workflows/.github/workflows/sst_workflow.yml@master
secrets:
AWS_ACCESS_KEY_ID: # AWS Access Key ID for prod
AWS_SECRET_ACCESS_KEY: # AWS Secret Access Key for prod
with:
app-env: # prod
working-directory: # specify your working folder from repo
app-env: production
```
##### Path: `clouddrove/github-shared-workflows/.github/workflows/sst_workflow.yml@master`

Should be used with `on: pull_request`. Includes the following:
1. Adds SST Deployed application link into the description of a pull request.
2. Appends Pull Request number and head branch name for the stage name when the preview environment is set to true.

Handles the following branch naming styles :
- `feature-123`
- `feature_123`
- `feature-123/feature-description`

0 comments on commit c3f1647

Please sign in to comment.