From acf1ef13bf31651436d52ecd5613b33fc30aaa91 Mon Sep 17 00:00:00 2001 From: Zach Butler Date: Sat, 6 Jul 2024 20:39:55 -0400 Subject: [PATCH] Copy eosnetworkfoundation/devhub:efe68144b62271f0b70dd689970844879c89d6c1/.github --- .github/workflows/frontend-aws-context.sh | 24 ++++++++ .github/workflows/frontend-build.sh | 68 ++++++++++++++++++++++ .github/workflows/frontend-publish.sh | 58 +++++++++++++++++++ .github/workflows/frontend.yml | 70 +++++++++++++++++++++++ 4 files changed, 220 insertions(+) create mode 100755 .github/workflows/frontend-aws-context.sh create mode 100755 .github/workflows/frontend-build.sh create mode 100755 .github/workflows/frontend-publish.sh create mode 100644 .github/workflows/frontend.yml diff --git a/.github/workflows/frontend-aws-context.sh b/.github/workflows/frontend-aws-context.sh new file mode 100755 index 0000000..f977822 --- /dev/null +++ b/.github/workflows/frontend-aws-context.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -eo pipefail +if [[ "$GITHUB_REF_TYPE" == 'tag' ]]; then + echo "Found git $GITHUB_REF_TYPE \"$GITHUB_REF_NAME,\" attempting a production deployment." + export GIT_TAG="$(git --no-pager tag --points-at HEAD)" + export FRONTEND_VERSION="v$(cat frontend/package.json | jq -r '.version')" + if [[ "$FRONTEND_VERSION" != "$GIT_TAG" || "$GIT_TAG" != "$GITHUB_REF_NAME" ]]; then + echo '::error title=Version String Mismatch:: Frontend package.json version string does not match the git tag!' + echo "FRONTEND_VERSION='$FRONTEND_VERSION'" + echo "GITHUB_REF_NAME='$GITHUB_REF_NAME'" + echo "GIT_TAG='$GIT_TAG'" + cat frontend/package.json | jq '.' + exit 10 + fi + echo '::notice title=Deploying to Production::This build will attempt to deploy to production. This is the real deal!' + echo '::set-output name=dry-run::false' + echo "::set-output name=role-arn::$DEVHUB_FRONTEND_PROD_IAM_ARN" +else + echo "Found git $GITHUB_REF_TYPE \"$GITHUB_REF_NAME,\" performing a dry-run." + echo '::notice title=Dry Run::This build is performing a dry run. A dry run attemps to verify everything is good to go without actually changing anything.' + echo '::set-output name=dry-run::true' + echo "::set-output name=role-arn::$DEVHUB_FRONTEND_RO_IAM_ARN" +fi +echo 'Done. - frontend-aws-role.sh' diff --git a/.github/workflows/frontend-build.sh b/.github/workflows/frontend-build.sh new file mode 100755 index 0000000..fdc0fb2 --- /dev/null +++ b/.github/workflows/frontend-build.sh @@ -0,0 +1,68 @@ +#!/bin/bash +set -eo pipefail + +function ee() +{ + echo "$ $*" + eval "$@" +} + +exec 9>&1 # enable tee to write to STDOUT as a file +# print debugging code +ee node --version +ee yarn --version +ee npm --version +if [[ ! -z "$DEVHUB_BACKEND_API" ]]; then + ee 'printf "$DEVHUB_BACKEND_API" | wc -c' + export BACKEND_API_TEST='curl -fsSL "$DEVHUB_BACKEND_API/test"' + echo "$ $BACKEND_API_TEST" + export BACKEND_UP="$(eval "$BACKEND_API_TEST" | tee >(cat - >&9))" + echo + if [[ "$BACKEND_UP" == 'true' ]]; then + echo 'DevHub backend API is up!' + else + printf '\e[93mWARNING: Failed to connect to DevHub backend API!\e[0m\n' + echo '::warning title=Failed to Connect to DevHub Backend API::Failed to connect to DevHub backend API!' + fi +else + printf '\e[93mWARNING: DEVHUB_BACKEND_API is not defined!\e[0m\n' + echo '::warning title=DevHub Backend API Endpoint Missing::DEVHUB_BACKEND_API is not defined!' +fi +# init +ee pushd frontend +ee yarn --frozen-lockfile +# generate static site +ee yarn generate --fail-on-error +# add metadata +echo 'Packing website metadata into distribution.' +cat package.json | jq -c \ + --arg actor "$GITHUB_ACTOR" \ + --arg branch "$(git branch --show-current)" \ + --arg branchFromTag "$(git branch --contains 'tags/v0.1.0' | tail -n +2 | tail -n 1 | tr -d '[:space:]')" \ + --arg build "$GITHUB_RUN_NUMBER" \ + --arg build_id "$GITHUB_RUN_ID" \ + --arg commit "$(git rev-parse HEAD)" \ + --arg email "$(git log -n 1 --pretty=format:%ae)" \ + --arg node "$(node --version)" \ + --arg ref_type "$GITHUB_REF_TYPE" \ + --arg repo "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" \ + --arg tag "$(git --no-pager tag --points-at HEAD)" \ + --arg triggering_actor "$GITHUB_TRIGGERING_ACTOR" \ + '.git += { + $actor, + branch: (if $branch != "" then $branch elif $branchFromTag != "" then $branchFromTag else null end), + build: ($build | tonumber), + build_id: ($build_id | tonumber), + build_url: ($repo + "/actions/runs/" + $build_id), + $commit, + $email, + $node, + $ref_type, + $repo, + tag: ($tag | if . == "" then null else . end), + $triggering_actor + }' > dist/package.json +ee 'cat dist/package.json | jq .git' +# pack dist folder +ee 'tar -czf dist.tar.gz dist/*' +echo 'Done! - frontend-build.sh' diff --git a/.github/workflows/frontend-publish.sh b/.github/workflows/frontend-publish.sh new file mode 100755 index 0000000..ff4bd9d --- /dev/null +++ b/.github/workflows/frontend-publish.sh @@ -0,0 +1,58 @@ +#!/bin/bash +set -eo pipefail + +function ee() +{ + echo "$ $*" + eval "$@" +} + +exec 9>&1 # enable tee to write to STDOUT as a file +ee 'aws --version' +ee 'git log -1' +ee "git branch --contains 'tags/$(git --no-pager tag --points-at HEAD)' || :" +echo 'Finding newest matrix artifact.' +ee 'ls -la' +export DIST_DL_FOLDER="$(find . -maxdepth '1' -name 'dist*' -type 'd' | sort -r | head -n '1')" +echo "Identified \"$DIST_DL_FOLDER\" as the matrix build artifact from the most recent nodeJS version, unpacking." +ee "pushd '$DIST_DL_FOLDER'" +ee 'tar -xzf dist.tar.gz' +ee 'pushd dist' +ee 'ls -la' +echo 'Uploading website distribution to Amazon S3.' +export S3_SYNC='aws s3 sync "." "s3://$S3_BUCKET" --delete' +if [[ "$DRY_RUN" != 'false' ]]; then + export S3_SYNC="$S3_SYNC --dryrun" +fi +ee "$S3_SYNC" +echo 'Tagging website objects.' +export TAGS="$(jq -n -c --argjson git "$(cat package.json | jq -c .git)" '{"billing-use": "devrel", "branch": ($git | .branch | tostring), "build-url": ($git | .build_url), "commit": ($git | .commit), "email": ($git | .email), "manual": "false", "tag": ($git | .tag | tostring), "terraform": "false"}')" +ee 'echo "$TAGS" | jq .' +export AWS_TAG_FORMAT="$(echo "$TAGS" | jq -c '{TagSet: (. | to_entries)}' | sed 's/"key"/"Key"/g' | sed 's/"value"/"Value"/g')" +export S3_LIST='aws s3api list-objects-v2 --bucket "$S3_BUCKET" --query "Contents[].{Key:Key}" --output text' +export S3_TAG_OBJECT='aws s3api put-object-tagging --bucket "$S3_BUCKET"' +if [[ "$DRY_RUN" != 'false' ]]; then + echo 'AWS CLI dry run support is inconsistent and this command does not have it, printing object tag command with no dry run.' + for OBJECT in $(eval "$S3_LIST") + do + echo "$ $S3_TAG_OBJECT --key '$OBJECT' --tagging '$AWS_TAG_FORMAT'" + done +else + for OBJECT in $(eval "$S3_LIST") + do + ee "$S3_TAG_OBJECT --key '$OBJECT' --tagging '$AWS_TAG_FORMAT'" + done +fi +echo 'Refreshing AWS Cloudfront (CDN) Edge Nodes' +export AWS_CDN_REFRESH="aws cloudfront create-invalidation --distribution-id \"\$CF_DISTRIBUTION\" --paths '/*'" +if [[ "$DRY_RUN" != 'false' ]]; then + echo 'AWS CLI dry run support is inconsistent and this command does not have it, printing CDN refresh command with no dry run.' + echo "$ $AWS_CDN_REFRESH" +else + echo "$ $AWS_CDN_REFRESH" + export INVALIDATION_ID="$(eval "$AWS_CDN_REFRESH" | tee >(cat - >&9) | jq -r '.Invalidation.Id')" + echo 'Waiting for CDN edge nodes to refresh...' + ee "aws cloudfront wait invalidation-completed --distribution-id \"\$CF_DISTRIBUTION\" --id '$INVALIDATION_ID'" + echo "::notice title=Deployed v$(cat package.json | jq -r .version) to Production Worldwide::Frontend v$(cat package.json | jq -r .version) has been successfully uploaded and the content delivery network has been refreshed worldwide. This is live in production." +fi +echo 'Done! - frontend-publish.sh' diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml new file mode 100644 index 0000000..f265d75 --- /dev/null +++ b/.github/workflows/frontend.yml @@ -0,0 +1,70 @@ +name: DevHub Frontend CICD + +on: [push, workflow_dispatch] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [16] + + name: Static Frontend Build - nodeJS v${{ matrix.node-version }} + + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Setup node v${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Static Frontend Build - node v${{ matrix.node-version }} + run: .github/workflows/frontend-build.sh + env: + DEVHUB_BACKEND_API: ${{ secrets.DEVHUB_BACKEND_API }} + + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: dist-node-${{ matrix.node-version }} + path: frontend/dist.tar.gz + + aws: + name: AWS Authentication + runs-on: ubuntu-latest + needs: build + permissions: + id-token: write + contents: read + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + + - name: Determine AWS Context + id: aws-context + run: .github/workflows/frontend-aws-context.sh + env: + DEVHUB_FRONTEND_PROD_IAM_ARN: ${{ secrets.DEVHUB_FRONTEND_PROD_IAM_ARN }} + DEVHUB_FRONTEND_RO_IAM_ARN: ${{ secrets.DEVHUB_FRONTEND_RO_IAM_ARN }} + + - name: Authenticate to AWS + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + aws-region: us-east-1 + role-to-assume: ${{ steps.aws-context.outputs.role-arn }} + role-duration-seconds: 900 + + - name: Download Matrix Artifacts + uses: actions/download-artifact@v3 + + - name: Publish Frontend + run: .github/workflows/frontend-publish.sh + env: + CF_DISTRIBUTION: ${{ secrets.DEVHUB_FRONTEND_PROD_CF_DISTRIBUTION }} + DRY_RUN: ${{ steps.aws-context.outputs.dry-run }} + S3_BUCKET: ${{ secrets.DEVHUB_FRONTEND_PROD_S3_BUCKET }}