diff --git a/.github/workflows/dev_deploy.yml b/.github/workflows/dev_deploy.yml index 6cffc01..8301b3d 100644 --- a/.github/workflows/dev_deploy.yml +++ b/.github/workflows/dev_deploy.yml @@ -40,7 +40,7 @@ jobs: - name: Install run: | rm -rf .cache - rm -rf build + rm -rf dist yarn config set cache-folder .yarn yarn install pip install awscli --upgrade --user diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml new file mode 100644 index 0000000..32a55ce --- /dev/null +++ b/.github/workflows/prod_deploy.yaml @@ -0,0 +1,79 @@ +name: Manual Deployment to Production + +# Run on pushes to main or PRs +on: + workflow_dispatch: + inputs: + tag: + description: Tagged version to deploy + required: true + type: string + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + deploy: + name: Deployment + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Tag checkout + run: | + git fetch --prune --unshallow --tags + git checkout ${{ github.event.inputs.tag }} + + - uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '20.3.0' + + - name: Install + run: | + rm -rf .cache + rm -rf dist + yarn config set cache-folder .yarn + yarn install + pip install awscli --upgrade --user + + - name: Build App + run: yarn build + + - name: Configure AWS Production credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.PROD_AWS_DEFAULT_REGION }} + + # Script to deploy to production environment + - name: 'Deploy to S3: Production' + run: | + aws s3 sync dist/ s3://${{ secrets.PROD_BUCKET_NAME }} --delete --exclude "*.html" --exclude "sitemap.xml" --cache-control max-age=86400,public + aws s3 sync dist/ s3://${{ secrets.PROD_BUCKET_NAME }} --delete --exclude "*" --include "*.html" --cache-control max-age=0,no-cache,no-store,must-revalidate --content-type text/html + aws s3 sync dist/ s3://${{ secrets.PROD_BUCKET_NAME }} --delete --exclude "*" --include "sitemap.xml" --cache-control max-age=0,no-cache,no-store,must-revalidate --content-type text/xml + + - name: 'Cloudfront Production: cache invalidation' + if: (startsWith(github.event.ref, 'refs/tags/v') || github.event_name == 'release') + run: | + aws cloudfront create-invalidation --distribution-id ${{ secrets.PROD_AWS_CLOUDFRONT_ID }} --paths "/*" + + notify: + uses: ./.github/workflows/slack_release_notification.yml + if: ${{ always() }} + needs: deploy + secrets: + RELEASES_SLACK_WEBHOOK_URL: ${{ secrets.RELEASES_SLACK_WEBHOOK_URL }} + with: + environment: Production + service: GC Shutter UI + success: ${{ contains(join(needs.*.result, ','), 'success') }} + message: "deploy service `GC Shutter UI` version `${{ inputs.tag }}`. Triggered by `${{ github.actor }}`." \ No newline at end of file diff --git a/.github/workflows/slack_release_notification.yml b/.github/workflows/slack_release_notification.yml new file mode 100644 index 0000000..f83d879 --- /dev/null +++ b/.github/workflows/slack_release_notification.yml @@ -0,0 +1,46 @@ +name: Slack Notify Release +on: + workflow_call: + secrets: + RELEASES_SLACK_WEBHOOK_URL: + required: true + inputs: + environment: + type: string + required: true + message: + type: string + required: true + service: + type: string + required: true + success: + type: boolean + required: true + +jobs: + notify: + name: Notify ${{ inputs.service }} release in ${{ inputs.environment }} + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + steps: + - name: Extract branch name + shell: bash + run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT + id: extract_branch + + - name: Extract commit + id: commit + uses: prompt/actions-commit-hash@v2 + + - name: Get current date + id: date + run: echo "::set-output name=date::$(date +'%Y-%m-%dT%H:%M:%S')" + + - id: slack + uses: slackapi/slack-github-action@v1.24.0 + with: + payload: "{\"username\":\"Releases\",\"icon_url\":\"https://avatars3.githubusercontent.com/u/134083290\",\"text\":\"${{ inputs.message }} - ${{ github.event.head_commit.message }}\",\"attachments\":[{\"text\":\"\",\"color\":\"${{ inputs.success == true && '#36a64f' || '#FF3131' }}\",\"author_name\":\"${{ inputs.service }}\",\"title\":\"\",\"fields\":[{\"title\":\"Environment\",\"short\":true,\"value\":\"`${{ inputs.environment }}`\"},{\"title\":\"Branch\",\"short\":true,\"value\":\"${{ steps.extract_branch.outputs.branch }}\"},{\"title\":\"Commit\",\"short\":true,\"value\":\"${{ steps.commit.outputs.short }}\"},{\"title\":\"Status\",\"short\":true,\"value\":\"${{ inputs.success == true && '🟢 SUCCEEDED' || '🔴 FAILED' }}\"},{\"title\":\"Time\",\"short\":true,\"value\":\"${{ steps.date.outputs.date }}\"}]}]}" + env: + SLACK_WEBHOOK_URL: ${{ secrets.RELEASES_SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK_TYPE: "INCOMING_WEBHOOK" \ No newline at end of file diff --git a/.github/workflows/tag_release.yml b/.github/workflows/tag_release.yml new file mode 100644 index 0000000..1dd035f --- /dev/null +++ b/.github/workflows/tag_release.yml @@ -0,0 +1,47 @@ +name: Create Github Release + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +# Permission can be added at job level or workflow level +permissions: + contents: write # This is required for actions/checkout and create release + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + release: + name: Github Release + runs-on: ubuntu-latest + + steps: + - name: Create Github Release + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + if (!${{ toJson(github.ref_name) }}) { + core.setFailed("RELEASE_TAG is not defined.") + + return; + } + try { + const response = await github.rest.repos.createRelease({ + name: ${{ toJson(github.ref_name) }}, + tag_name: ${{ toJson(github.ref_name) }}, + draft: false, + generate_release_notes: true, + owner: context.repo.owner, + prerelease: false, + repo: context.repo.repo, + }); + + core.exportVariable('RELEASE_ID', response.data.id); + core.exportVariable('RELEASE_UPLOAD_URL', response.data.upload_url); + } catch (error) { + core.setFailed(error.message); + } \ No newline at end of file