Skip to content
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

refactor: separate the CDN upload process and add a retry script #563

Merged
merged 9 commits into from
Jun 19, 2024
65 changes: 6 additions & 59 deletions .github/workflows/compile-and-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ permissions:
id-token: write
contents: read

env:
CDN_BUCKET: gc-design-system-production-cdn
CDN_REGION: ca-central-1

jobs:
publish-web:
name: Publish @cdssnc/gcds-components
Expand Down Expand Up @@ -49,31 +45,8 @@ jobs:
token: ${{ secrets.NPM_TOKEN }}
package: ${{ matrix.dist }}

- name: Configure AWS credentials using OIDC
if: steps.publish.outputs.id != ''
uses: aws-actions/configure-aws-credentials@50ac8dd1e1b10d09dac7b8727528b91bed831ac0 # v3.0.2
with:
role-to-assume: arn:aws:iam::307395567143:role/gcds-components-apply
role-session-name: CDNPublish
aws-region: ${{ env.CDN_REGION }}

- name: Update CDN ${{ matrix.name }}
if: steps.publish.outputs.id != ''
run: |
PUBLISHED_PACKAGE="${{ steps.publish.outputs.id }}"

mkdir -p ./tmp \
&& sleep 60 \
&& npm install --prefix ./tmp "$PUBLISHED_PACKAGE" \
&& cd ./tmp/node_modules

aws s3 sync ./${{ matrix.name }} s3://${{ env.CDN_BUCKET }}/"$PUBLISHED_PACKAGE" --delete
aws s3 sync ./${{ matrix.name }} s3://${{ env.CDN_BUCKET }}/${{ matrix.name }}@latest --delete
aws s3api head-object --bucket ${{ env.CDN_BUCKET }} --key "$PUBLISHED_PACKAGE"/package.json
aws s3api head-object --bucket ${{ env.CDN_BUCKET }} --key ${{ matrix.name }}@latest/package.json

aws cloudfront create-invalidation --distribution-id ${{ secrets.CDN_CLOUDFRONT_DIST_ID }} --paths "/*"
working-directory: ${{ matrix.package }}
- name: Sleep for 60 seconds to give more time for NPM to complete publishing before proceeding to publish the rest
run: sleep 60

- name: Report deployment to Sentinel
if: steps.publish.outputs.id != ''
Expand All @@ -84,15 +57,14 @@ jobs:
log_analytics_workspace_id: ${{ secrets.LOG_ANALYTICS_WORKSPACE_ID }}
log_analytics_workspace_key: ${{ secrets.LOG_ANALYTICS_WORKSPACE_KEY }}


- name: Slack notify on failure
if: failure()
run: |
json='{"blocks":[{"type":"section","text":{"type":"mrkdwn","text":":red: Publish ${{ matrix.name }} failed: <https://github.com/cds-snc/gcds-components/actions/workflows/compile-and-publish.yml|Publish packages>"}}]}'
curl -X POST -H 'Content-type: application/json' --data "$json" ${{ secrets.SLACK_WEBHOOK_OPS }}

publish-react-angular:
name: Publish @cdssnc/gcds-components-react and @cdssnc/gcds-components-angular
publish-react-angular-vue:
name: Publish @cdssnc/gcds-components-react, @cdssnc/gcds-components-angular, @cdssnc/gcds-components-vue
needs: publish-web
runs-on: ubuntu-latest
strategy:
Expand Down Expand Up @@ -120,10 +92,10 @@ jobs:
with:
node-version: 18

- name: Install monorepo (web, react, angular)
- name: Install monorepo (web, react, angular, vue)
run: npm install

- name: Build gcds-components (web, react, angular)
- name: Build gcds-components (web, react, angular, vue)
run: npm run build

- name: Publish ${{ matrix.name }}
Expand All @@ -133,31 +105,6 @@ jobs:
token: ${{ secrets.NPM_TOKEN }}
package: ${{ matrix.dist }}

- name: Configure AWS credentials using OIDC
if: steps.publish.outputs.id != ''
uses: aws-actions/configure-aws-credentials@50ac8dd1e1b10d09dac7b8727528b91bed831ac0 # v3.0.2
with:
role-to-assume: arn:aws:iam::307395567143:role/gcds-components-apply
role-session-name: CDNPublish
aws-region: ${{ env.CDN_REGION }}

- name: Update CDN ${{ matrix.name }}
if: steps.publish.outputs.id != ''
run: |
PUBLISHED_PACKAGE="${{ steps.publish.outputs.id }}"
mkdir -p ./tmp \
&& sleep 60 \
&& npm install --prefix ./tmp "$PUBLISHED_PACKAGE" \
&& cd ./tmp/node_modules

aws s3 sync ./${{ matrix.name }} s3://${{ env.CDN_BUCKET }}/"$PUBLISHED_PACKAGE" --delete
aws s3 sync ./${{ matrix.name }} s3://${{ env.CDN_BUCKET }}/${{ matrix.name }}@latest --delete
aws s3api head-object --bucket ${{ env.CDN_BUCKET }} --key "$PUBLISHED_PACKAGE"/package.json
aws s3api head-object --bucket ${{ env.CDN_BUCKET }} --key ${{ matrix.name }}@latest/package.json

aws cloudfront create-invalidation --distribution-id ${{ secrets.CDN_CLOUDFRONT_DIST_ID }} --paths "/*"
working-directory: ${{ matrix.package }}

- name: Report deployment to Sentinel
if: steps.publish.outputs.id != ''
uses: cds-snc/sentinel-forward-data-action@main
Expand Down
78 changes: 78 additions & 0 deletions .github/workflows/upload-cdn.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Upload packages to CDN

on:
workflow_dispatch:
workflow_run:
workflows: ["Publish packages"]
types:
- completed

permissions:
id-token: write
contents: read

env:
CDN_BUCKET: gc-design-system-production-cdn
CDN_REGION: ca-central-1

jobs:
unable-to-deploy:
# If this was triggered by a workflow run ("Publish packages" as noted above) and the workflow run failed
if: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'failure' }}
runs-on: ubuntu-latest
steps:
- name: Exit and notify Slack if publishing failed and we can't upload to the CDN
run: |
json='{"blocks":[{"type":"section","text":{"type":"mrkdwn","text":":red: Publishing workflow failed, unable to upload to CDN <https://github.com/cds-snc/gcds-components/actions/workflows/upload-cdn.yml|Upload packages to CDN>"}}]}'
curl -X POST -H 'Content-type: application/json' --data "$json" ${{ secrets.SLACK_WEBHOOK_OPS }}
exit 1

upload-to-cdn:
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
patheard marked this conversation as resolved.
Show resolved Hide resolved

strategy:
fail-fast: false
matrix:
include:
- name: "web"
package: "@cdssnc/gcds-components"
dist: "./packages/web"

- name: "react"
package: "@cdssnc/gcds-components-react"
dist: "./packages/react"

- name: "angular"
package: "@cdssnc/gcds-components-angular"
dist: "./packages/angular/dist"

- name: "vue"
package: "@cdssnc/gcds-components-vue"
dist: "./packages/vue"

steps:
- name: Checkout code
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6

- name: Make Upload to CDN script executable
run: chmod +x ./utils/scripts/upload_to_cdn.sh

- name: Configure AWS credentials using OIDC
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
role-to-assume: arn:aws:iam::307395567143:role/gcds-components-apply
role-session-name: CDNPublish
aws-region: ${{ env.CDN_REGION }}

- name: Upload file to S3
run: ./utils/scripts/upload_to_cdn.sh ${{ matrix.package }}
env:
CDN_CLOUDFRONT_DIST_ID: ${{secrets.CDN_CLOUDFRONT_DIST_ID}}
PACKAGE_PATH: ${{ matrix.dist }}

- name: Slack notify on failure
if: failure()
run: |
json='{"blocks":[{"type":"section","text":{"type":"mrkdwn","text":":red: CDN upload ${{ matrix.package }} failed: <https://github.com/cds-snc/gcds-components/actions/workflows/upload-cdn.yml|Upload packages to CDN>"}}]}'
curl -X POST -H 'Content-type: application/json' --data "$json" ${{ secrets.SLACK_WEBHOOK_OPS }}
69 changes: 69 additions & 0 deletions utils/scripts/upload_to_cdn.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/bin/bash

PACKAGE_NAME=$1

## Path to lerna.json
LERNA_JSON="lerna.json"

# Check if lerna.json exists
if [ ! -f "$LERNA_JSON" ]; then
echo "lerna.json not found!"
exit 1
fi

echo "Current working directory: $(pwd)"

## Read and process lerna.json using jq to get the package version
PACKAGE_VERSION=$(jq -r '.version' $LERNA_JSON)

echo "PACKAGE_VERSION: $PACKAGE_VERSION"
echo "CDN_BUCKET: $CDN_BUCKET"
echo "PACKAGE_PATH: $PACKAGE_PATH"
echo "PACKAGE_NAME: $PACKAGE_NAME"

# Get the published package name and version
PUBLISHED_PACKAGE=$PACKAGE_NAME@$PACKAGE_VERSION

# Install the package from npm
install_package(){
echo "Installing package: $PUBLISHED_PACKAGE"
mkdir -p ./tmp \
&& npm install --prefix ./tmp "$PUBLISHED_PACKAGE" \
&& cd ./tmp/node_modules
}

# Upload the package files to the CDN
upload_to_cdn() {
echo "Uploading published package to CDN: $PUBLISHED_PACKAGE"
aws s3 sync ./$PACKAGE_NAME s3://$CDN_BUCKET/"$PUBLISHED_PACKAGE" --delete
aws s3 sync ./$PACKAGE_NAME s3://$CDN_BUCKET/$PACKAGE_NAME@latest --delete
aws s3api head-object --bucket $CDN_BUCKET --key "$PUBLISHED_PACKAGE"/package.json
aws s3api head-object --bucket $CDN_BUCKET --key $PACKAGE_NAME@latest/package.json

aws cloudfront create-invalidation --distribution-id $CDN_CLOUDFRONT_DIST_ID --paths "/*"
}

# Retry function
retry() {
local retries=$1
local delay=$2
local count=0

while [[ $count -lt $retries ]]; do
# Upload the package to the CDN if the package is available and was installed
if install_package; then
upload_to_cdn
return 0
fi

count=$((count + 1))
echo "Retry $count/$retries failed. Retrying in $delay seconds..."
sleep $delay
done

echo "All $retries retries failed."
return 1
}

# Retry 3 times with a 5-second delay in between
retry 3 5
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this pattern - it's a nice way to work around the problem of the package not being instantly available.