Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Deploy production frontend on release & correctly time staging deployment #1556

Merged
merged 10 commits into from
Jul 12, 2022
136 changes: 136 additions & 0 deletions .github/actions/production-frontend-deploy/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
### !!!
# This file is automatically generated using Terraform.
# Do not update it manually. If changes need to be made,
# please request help from a maintainer to generate the
# updated version.
### !!!

name: openverse/production-frontend-nuxt-deploy
description: Update the task definition for a production-frontend-nuxt deployment

inputs:
tag:
required: true
description: Image tag to deploy.
aws-access-key-id:
required: true
aws-secret-access-key:
required: true
slack-webhook:
required: false

runs:
using: "composite"

steps:
- name: Notify deployment start
uses: slackapi/slack-github-action@v1.19.0
with:
payload: |
{
"text": "Starting deployment of production-frontend",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":spinning-cd: A deployment of production-frontend is starting using the `${{ inputs.tag }}` tag."
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Click here to monitor the deployment>."
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ inputs.slack-webhook }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ inputs.aws-access-key-id }}
aws-secret-access-key: ${{ inputs.aws-secret-access-key }}
aws-region: us-east-1

- name: Download task definition template
shell: bash
run: |
aws ecs describe-task-definition \
--task-definition production-frontend-template \
--query taskDefinition > task-definition.json

- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: nuxt
image: ghcr.io/wordpress/openverse-frontend:${{ inputs.tag }}

- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: production-frontend
cluster: production-default
wait-for-service-stability: true

- name: Notify deployment finished
uses: slackapi/slack-github-action@v1.19.0
with:
payload: |
{
"text": "Deployment of production-frontend successful",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":tadaco: The deployment of production-frontend using the `${{ inputs.tag }}` tag *succeeded*."
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Click here to review the completed deployment workflow>."
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ inputs.slack-webhook }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

- name: Notify deployment failure
if: failure()
uses: slackapi/slack-github-action@v1.19.0
with:
payload: |
{
"text": "Deployment of production-frontend failed!",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":alert: The deployment of production-frontend using the `${{ inputs.tag }}` tag *failed* :alert:"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Click here to review the failed deployment workflow>."
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ inputs.slack-webhook }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ on:
- main
pull_request:

# Cancels all previous workflow runs for pull requests that have not completed.
concurrency:
# The concurrency group contains the workflow name and the branch name for pull requests
# or the commit hash for any other events.
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
cancel-in-progress: true

jobs:
types:
name: Check types
Expand Down
78 changes: 74 additions & 4 deletions .github/workflows/ghcr.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: GitHub Container Registry
name: GitHub Container Registry & Deployment

on:
push:
Expand All @@ -7,15 +7,33 @@ on:
pull_request:
branches:
- main
release:
types: [published]

# Cancels all previous workflow runs for pull requests that have not completed.
concurrency:
# The concurrency group contains the workflow name and the branch name for pull requests
# or the commit hash for any other events.
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
cancel-in-progress: true

jobs:
push-to-ghcr:
name: Build and push image for commit
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == 'WordPress'
# prevent running on fork PRs
if: github.event_name == 'push' || github.event_name == 'release' || github.event.pull_request.head.repo.owner.login == 'WordPress'

steps:
- uses: actions/checkout@v3
- name: Checkout branch or push to main
uses: actions/checkout@v3
if: github.event_name != 'release'

- name: Checkout release
uses: actions/checkout@v3
if: github.event_name == 'release'
with:
ref: ${{ github.event.release.tag_name }}

- uses: docker/setup-buildx-action@v2
with:
Expand All @@ -34,11 +52,63 @@ jobs:
tags: |
type=sha
type=ref,event=tag
${{ github.event_name == 'push' && 'type=raw,value=main' || '' }}
type=raw,value=main,enabled=${{ github.event_name == 'push' }}
type=raw,value=main
sarayourfriend marked this conversation as resolved.
Show resolved Hide resolved

- name: Get relevant tag for internal image version
id: get-version
shell: python
run: |
import json
metadata = json.loads('${{ steps.metadata.outputs.json }}')
version = metadata["labels"]["org.opencontainers.image.version"]
if version == "main":
# get first version that isn't main
for tag in metadata["tags"]:
if ":main" not in tag:
version = tag.split(":")[1]

print(f"::set-output name=version::{version}")

- uses: docker/build-push-action@v3
with:
context: .
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
push: true
build-args: |
RELEASE=${{ steps.get-version.outputs.version }}

deploy-staging:
name: Deploy staging frontend
runs-on: ubuntu-latest
if: github.event_name == 'push'
Copy link
Member

Choose a reason for hiding this comment

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

Will this deploy to staging on each push in a pull request (to main)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No because the push event is scoped to the main branch.

You can confirm this is true by seeing that this workflow step was skipped in this PR: https://github.com/WordPress/openverse-frontend/runs/7283335174?check_suite_focus=true

Copy link
Member

Choose a reason for hiding this comment

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

I see, makes sense! Though then is the on: -> pull_request: ... scope doing something in this workflow? 🤔
I remember we used to publish an image for each PR but seems that has been lost at some point.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We do still publish the image for each step. You can see that happening on this PR 🙂 The pull_request trigger does just that.

We just skip deploying for anything that isn't a push to main (for staging) or a release.

needs: push-to-ghcr

steps:
- uses: actions/checkout@v3

- uses: ./.github/actions/staging-frontend-deploy
with:
tag: "main"
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK_URL }}

deploy-production:
name: Deploy production frontend
runs-on: ubuntu-latest
if: github.event_name == 'release'
needs: push-to-ghcr

steps:
- uses: actions/checkout@v3
with:
ref: main

- uses: ./.github/actions/production-frontend-deploy
with:
tag: ${{ github.event.release.tag_name }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
39 changes: 0 additions & 39 deletions .github/workflows/push_production.yaml

This file was deleted.

33 changes: 0 additions & 33 deletions .github/workflows/push_staging.yaml

This file was deleted.

51 changes: 51 additions & 0 deletions .github/workflows/rollback.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Rollback Frontend

on:
workflow_dispatch:
inputs:
environment:
required: true
description: The environment to roll back. `staging` or `production`.
tag:
required: true
description: The GHCR image tag to which the environment should be rolled back

jobs:
rollback:
name: Perform Rollback
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Validate `tag` input
uses: actions/github-script@v6
with:
script: |
const { data: versions } = await github.rest.packages.getAllPackageVersionsForPackageOwnedByOrg({
package_type: 'container',
package_name: 'openverse-frontend',
org: 'WordPress',
})

const exists = versions.some(v => v.name === "${{ inputs.tag }}")

if (!exists) {
throw new Error("`${{ inputs.tag }}` does not appear to be a valid tag for the ghcr.io/wordpress/openverse-frontend image.")
}

- uses: ./github/actions/production-frontend-deploy
if: inputs.environment == 'production'
with:
ref: ${{ inputs.tag }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK_URL }}

- uses: ./github/actions/staging-frontend-deploy
if: inputs.environment == 'staging'
with:
ref: ${{ inputs.tag }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
Loading