Content Release #331
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Content Release | |
permissions: | |
id-token: write # This is required for requesting the JWT for AWS creds | |
contents: read # This is required for actions/checkout | |
on: | |
repository_dispatch: | |
types: [content-release] | |
workflow_run: | |
workflows: ['Create Production Tag'] | |
types: [completed] | |
branches: [main] | |
workflow_call: | |
inputs: | |
build_type: | |
type: string | |
description: "Environment this workflow runs against" | |
required: true | |
default: 'PROD' | |
workflow_dispatch: | |
inputs: | |
build_type: | |
type: choice | |
description: "Environment this workflow runs against" | |
required: true | |
default: 'PROD' | |
options: | |
- dev | |
- staging | |
- prod | |
concurrency: | |
group: ${{ inputs.build_type || 'prod' }} | |
env: | |
SLACK_CHANNEL: C06DSBT7CBW #status-next-build | |
jobs: | |
# This is necessary to get credentials to use the private ECR image defined in the build job container. | |
login-to-amazon-ecr: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Configure AWS credentials | |
uses: aws-actions/[email protected] | |
with: | |
aws-region: us-gov-west-1 | |
role-to-assume: ${{ vars.AWS_ASSUME_ROLE }} | |
mask-aws-account-id: 'false' | |
- name: Login to Amazon ECR | |
id: login-ecr | |
uses: aws-actions/amazon-ecr-login@v2 | |
with: | |
mask-password: 'false' | |
outputs: | |
docker_username: ${{ steps.login-ecr.outputs.docker_username_008577686731_dkr_ecr_us_gov_west_1_amazonaws_com }} | |
docker_password: ${{ steps.login-ecr.outputs.docker_password_008577686731_dkr_ecr_us_gov_west_1_amazonaws_com }} | |
# Ensure latest build from main is passing CI, grab the latest published tag for content-release | |
validate-build-status: | |
name: Validate Build Status | |
runs-on: ubuntu-latest | |
outputs: | |
REF: ${{ steps.get-latest-release.outputs.target_commitish }} | |
TAG: ${{ steps.get-latest-release.outputs.tag_name }} | |
APPROX_WORKFLOW_START_TIME: ${{ steps.export-approx-workflow-start-time.outputs.APPROX_WORKFLOW_START_TIME }} | |
timeout-minutes: 15 | |
steps: | |
- name: Export approximate workflow start time | |
id: export-approx-workflow-start-time | |
run: echo APPROX_WORKFLOW_START_TIME=$(date +"%s") >> $GITHUB_OUTPUT | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- uses: actions/setup-node@v4 | |
with: | |
node-version: 20 # the validate-build-status requires node12, node16, node20... | |
- name: Setup Yarn | |
run: | | |
corepack enable | |
corepack install -g --cache-only .yarn/releases/corepack.tgz | |
- name: Install dependencies | |
run: yarn | |
- name: Get latest release | |
id: get-latest-release | |
uses: ./.github/workflows/fetch-latest-release | |
- name: Validate build status | |
run: node ./scripts/github-actions/validate-build-status.js ${{ steps.get-latest-release.outputs.target_commitish }} | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
notify-start-slack: | |
name: Notify Start (Slack) | |
runs-on: ubuntu-latest | |
needs: validate-build-status | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Notify Slack | |
uses: department-of-veterans-affairs/platform-release-tools-actions/slack-notify@8c496a4b0c9158d18edcd9be8722ed0f79e8c5b4 # main | |
continue-on-error: true | |
with: | |
payload: '{"attachments": [{"color": "#2EB67D","blocks": [{"type": "section","text": {"type": "mrkdwn","text": "Stand by, content release for next-build coming up (using ${{ needs.validate-build-status.outputs.TAG }}). <https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}>"}}]}]}' | |
channel_id: ${{ env.SLACK_CHANNEL }} | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
build: | |
name: Build static pages & deploy to S3 | |
timeout-minutes: 60 | |
runs-on: [self-hosted, asg] | |
needs: [validate-build-status, login-to-amazon-ecr] | |
outputs: | |
DEPLOY_END_TIME: ${{ steps.export-deploy-end-time.outputs.DEPLOY_END_TIME }} | |
DEPLOY_START_TIME: ${{ steps.export-deploy-start-time.outputs.DEPLOY_START_TIME }} | |
BUILD_START_TIME: ${{ steps.export-build-start-time.outputs.BUILD_START_TIME }} | |
BUILD_END_TIME: ${{ steps.export-build-end-time.outputs.BUILD_END_TIME }} | |
SETUP_START_TIME: ${{ steps.export-setup-start-time.outputs.SETUP_START_TIME }} | |
SETUP_END_TIME: ${{ steps.export-setup-end-time.outputs.SETUP_END_TIME }} | |
YARN_OUTPUT: ${{ steps.export-yarn-output.outputs.YARN_OUTPUT }} | |
container: | |
image: 008577686731.dkr.ecr.us-gov-west-1.amazonaws.com/node-18.19.0-bullseye:latest | |
credentials: | |
username: ${{ needs.login-to-amazon-ecr.outputs.docker_username }} | |
password: ${{ needs.login-to-amazon-ecr.outputs.docker_password }} | |
# Certs added for the self hosted runner | |
env: | |
NODE_EXTRA_CA_CERTS: /etc/ssl/certs/ca-certificates.crt | |
APP_ENV: ${{ inputs.build_type || 'prod' }} | |
ports: | |
- 80 | |
volumes: | |
- /etc/ssl/certs:/etc/ssl/certs | |
steps: | |
- name: Export setup start time | |
id: export-setup-start-time | |
run: echo SETUP_START_TIME=$(date +"%s") >> $GITHUB_OUTPUT | |
- name: Checkout next-build | |
uses: actions/checkout@v4 | |
with: | |
repository: department-of-veterans-affairs/next-build | |
#ref: ${{ needs.validate-build-status.outputs.TAG }} | |
ref: ${{ github.head_ref || github.ref_name }} | |
path: main | |
sparse-checkout-cone-mode: false | |
- name: Checkout vets-website | |
uses: actions/checkout@v4 | |
with: | |
repository: department-of-veterans-affairs/vets-website | |
path: vets-website | |
- name: Install awscli | |
run: | | |
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" | |
unzip -o awscliv2.zip | |
./aws/install | |
- uses: actions/setup-node@v4 | |
with: | |
node-version: 18 | |
- name: Setup Yarn | |
run: | | |
cd main | |
corepack enable | |
corepack install -g --cache-only .yarn/releases/corepack.tgz | |
- name: Install dependencies | |
run: cd main && yarn | |
- name: Export setup end time | |
id: export-setup-end-time | |
run: echo SETUP_END_TIME=$(date +"%s") >> $GITHUB_OUTPUT | |
- name: Export build start time | |
id: export-build-start-time | |
run: echo BUILD_START_TIME=$(date +"%s") >> $GITHUB_OUTPUT | |
- name: Gather vets-website assets | |
run: cd main && yarn setup | |
- name: Build site | |
id: export-yarn-output | |
uses: nick-fields/retry@v3 | |
with: | |
max_attempts: 3 | |
retry_on: error | |
timeout_minutes: 70 | |
command: | | |
cd main && BUILD_OPTION=static yarn export --DRUPAL_CLIENT_ID ${{ secrets.PROD_DRUPAL_CLIENT_ID }} --DRUPAL_CLIENT_SECRET ${{ secrets.PROD_DRUPAL_CLIENT_SECRET }} --no-USE_REDIS | tee yarnoutput | |
echo YARN_OUTPUT=$(<yarnoutput) >> $GITHUB_OUTPUT | |
ls | |
ls out | |
if [[ $? -ne 0 ]]; then | |
echo "One or more pages failed to build. Exiting." | |
exit 1 | |
else | |
cd out | |
ls -l | |
fi | |
- name: Build sitemap | |
run: cd main && yarn build:sitemap | |
- name: Configure AWS Credentials | |
uses: aws-actions/[email protected] | |
with: | |
aws-region: us-gov-west-1 | |
role-to-assume: ${{ vars.AWS_ASSUME_ROLE }} | |
role-duration-seconds: 900 | |
- name: Export build end time | |
id: export-build-end-time | |
run: echo BUILD_END_TIME=$(date +"%s") >> $GITHUB_OUTPUT | |
- name: Export deploy start time | |
id: export-deploy-start-time | |
run: echo DEPLOY_START_TIME=$(date +"%s") >> $GITHUB_OUTPUT | |
- if: inputs.build_type == 'dev' | |
name: Deploy to S3 | |
run: | | |
cd main && ./scripts/github-actions/deploy.sh -s $SRC -d $DEST -v | |
ls | |
env: | |
SRC: ./out/ | |
DEST: s3://next-content.dev.va.gov | |
- if: inputs.build_type == 'staging' | |
name: Deploy to S3 | |
run: | | |
cd main && ./scripts/github-actions/deploy.sh -s $SRC -d $DEST -v | |
ls | |
cd out | |
ls -l | |
env: | |
SRC: ./out/ | |
DEST: s3://next-content.staging.va.gov | |
- if: inputs.build_type == 'prod' | |
name: Deploy to S3 | |
run: cd main && ./scripts/github-actions/deploy.sh -s $SRC -d $DEST -v | |
env: | |
SRC: ./out/ | |
DEST: s3://next-content.www.va.gov | |
- if: inputs.build_type == '' | |
name: Deploy to S3 | |
run: cd main && ./scripts/github-actions/deploy.sh -s $SRC -d $DEST -v | |
env: | |
SRC: ./out/ | |
DEST: s3://next-content.www.va.gov | |
- name: Export deploy end time | |
id: export-deploy-end-time | |
run: echo DEPLOY_END_TIME=$(date +"%s") >> $GITHUB_OUTPUT | |
notify-success: | |
name: Notify Success | |
needs: [validate-build-status, build] | |
runs-on: ubuntu-latest | |
steps: | |
- name: Check out repo | |
uses: actions/checkout@v4 | |
- name: Notify Slack | |
uses: department-of-veterans-affairs/platform-release-tools-actions/slack-notify@8c496a4b0c9158d18edcd9be8722ed0f79e8c5b4 # main | |
continue-on-error: true | |
with: | |
payload: '{"attachments": [{"color": "#07711E","blocks": [{"type": "section","text": {"type": "mrkdwn","text": "Successfully released content to S3 with next-build release: ${{ needs.validate-build-status.outputs.TAG }}"}}]}]}' | |
channel_id: ${{ env.SLACK_CHANNEL }} | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
- name: Configure AWS credentials | |
uses: aws-actions/[email protected] | |
with: | |
aws-region: us-gov-west-1 | |
role-to-assume: ${{ vars.AWS_ASSUME_ROLE }} | |
role-duration-seconds: 900 | |
- name: Get Datadog token from Parameter Store | |
uses: department-of-veterans-affairs/action-inject-ssm-secrets@d8e6de3bde4dd728c9d732baef58b3c854b8c4bb # latest | |
with: | |
# The parameter name here is misleading. In Datadog, it's defined as CMS_CI_DD_API_KEY, for use across all CMS CI, not just content-build. | |
ssm_parameter: /dsva-vagov/content-build/GHA_CONTENT_BUILD_DATADOG_API_KEY | |
env_variable_name: GHA_CONTENT_BUILD_DATADOG_API_KEY | |
- name: Build JSON object | |
run: | | |
jq --null-input '{}' | \ | |
jq '.title = "VA.gov CMS content release was successful (next-build)"' | \ | |
jq '.text = "VA.gov Content release ${{github.run_id}} completed at \(now|strftime("%Y-%m-%d %H:%M:%S"))! https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}"' | \ | |
jq '.date_happened = now' | \ | |
jq '.aggregation_key = "content release ${{github.run_id}}"' | \ | |
jq '.tags[0] = "project:vagov"' | \ | |
jq '.tags[1] = "repo:next-build"' | \ | |
jq '.tags[2] = "workflow:content-release"' | \ | |
jq '.tags[3] = "env:${{ inputs.build_type || 'prod' }}"' | \ | |
jq '.tags[5] = "status:${{needs.build.result}}"' | \ | |
jq '.alert_type = "success"' > event.json | |
- name: Send event to Datadog | |
run: | | |
curl -X POST "https://api.ddog-gov.com/api/v1/events" \ | |
-H "Content-Type: text/json" \ | |
-H "DD-API-KEY: ${{ env.GHA_CONTENT_BUILD_DATADOG_API_KEY }}" \ | |
-d @- < event.json | |
notify-failure: | |
name: Notify Failure | |
runs-on: ubuntu-latest | |
needs: build | |
if: ${{ failure() || cancelled() }} | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Notify Slack | |
uses: department-of-veterans-affairs/platform-release-tools-actions/slack-notify@8c496a4b0c9158d18edcd9be8722ed0f79e8c5b4 # main | |
continue-on-error: true | |
with: | |
payload: '{"attachments": [{"color": "#D33834","blocks": [{"type": "section","text": {"type": "mrkdwn","text": ">:warning: The latest content-release failed to complete: <https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}>"}}]}]}' | |
channel_id: ${{ env.SLACK_CHANNEL }} | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
- name: Configure AWS credentials | |
uses: aws-actions/[email protected] | |
with: | |
aws-region: us-gov-west-1 | |
role-to-assume: ${{ vars.AWS_ASSUME_ROLE }} | |
role-duration-seconds: 900 | |
- name: Get Datadog token from Parameter Store | |
uses: department-of-veterans-affairs/action-inject-ssm-secrets@d8e6de3bde4dd728c9d732baef58b3c854b8c4bb # latest | |
with: | |
# The parameter name here is misleading. In Datadog, it's defined as CMS_CI_DD_API_KEY, for use across all CMS CI, not just content-build. | |
ssm_parameter: /dsva-vagov/content-build/GHA_CONTENT_BUILD_DATADOG_API_KEY | |
env_variable_name: GHA_CONTENT_BUILD_DATADOG_API_KEY | |
- name: Build JSON object | |
run: | | |
jq --null-input '{}' | \ | |
jq '.title = "VA.gov CMS content release has failed (next-build)"' | \ | |
jq '.text = "VA.gov Content release ${{github.run_id}} failed at \(now|strftime("%Y-%m-%d %H:%M:%S"))! https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}"' | \ | |
jq '.date_happened = now' | \ | |
jq '.aggregation_key = "content release ${{github.run_id}}"' | \ | |
jq '.tags[0] = "project:vagov"' | \ | |
jq '.tags[1] = "repo:next-build"' | \ | |
jq '.tags[2] = "workflow:content-release"' | \ | |
jq '.tags[3] = "env:${{ inputs.build_type || 'prod' }}"' | \ | |
jq '.tags[5] = "status:${{needs.build.result}}"' | \ | |
jq '.alert_type = "error"' > event.json | |
- name: Send event to Datadog | |
run: | | |
curl -X POST "https://api.ddog-gov.com/api/v1/events" \ | |
-H "Content-Type: text/json" \ | |
-H "DD-API-KEY: ${{ env.GHA_CONTENT_BUILD_DATADOG_API_KEY }}" \ | |
-d @- < event.json | |
record-metrics: | |
name: Record metrics in Datadog | |
runs-on: [ubuntu-latest] | |
needs: | |
- build | |
- validate-build-status | |
env: | |
METRIC_NAMESPACE: dsva_vagov.next_build | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
#uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 | |
- name: Get current timestamp | |
run: echo "NOW=$(date +"%s")" >> $GITHUB_ENV | |
- name: Set vars for CMS-triggered runs | |
if: ${{ github.event_name == 'repository_dispatch' }} | |
run: | | |
echo "DEPLOY_ENV=${{ inputs.build_type || 'prod' }}" >> $GITHUB_ENV | |
echo "BUILD_TRIGGER=cms" >> $GITHUB_ENV | |
# Calculate all of our metrics that we send to data dog | |
- name: Calculate durations | |
run: | | |
echo "SETUP_DURATION=$(expr ${{needs.build.outputs.SETUP_END_TIME}} - ${{needs.build.outputs.SETUP_START_TIME}})" >> $GITHUB_ENV | |
echo "BUILD_DURATION=$(expr ${{needs.build.outputs.BUILD_END_TIME}} - ${{needs.build.outputs.BUILD_START_TIME}})" >> $GITHUB_ENV | |
echo "NEXT_BUILD_DURATION=$(expr ${{needs.build.outputs.BUILD_END_TIME}} - ${{needs.build.outputs.BUILD_START_TIME}})" >> $GITHUB_ENV | |
echo "OVERALL_DURATION=$(expr ${{needs.build.outputs.DEPLOY_END_TIME}} - ${{needs.validate-build-status.outputs.APPROX_WORKFLOW_START_TIME}})" >> $GITHUB_ENV | |
echo "DEPLOY_DURATION=$(expr ${{needs.build.outputs.DEPLOY_END_TIME}} - ${{needs.build.outputs.DEPLOY_START_TIME}})" >> $GITHUB_ENV | |
echo "${{needs.build.outputs.YARN_OUTPUT}}" >> yarnoutput | |
echo "BUILT_PAGES=$(grep -oE "*\/([0-9]{4,})" ./yarnoutput| head -1 | sed 's/\///g')" >> $GITHUB_ENV | |
- name: Build JSON object | |
run: | | |
jq --null-input '{}' | \ | |
jq '.series[0].metric = "${{env.METRIC_NAMESPACE}}.setup.duration"' | \ | |
jq '.series[0].points[0] = [${{env.NOW}}, ${{env.SETUP_DURATION}}]' | \ | |
jq '.series[1].metric = "${{env.METRIC_NAMESPACE}}.build.duration"' | \ | |
jq '.series[1].points[0] = [${{env.NOW}}, ${{env.BUILD_DURATION}}]' | \ | |
jq '.series[2].metric = "${{env.METRIC_NAMESPACE}}.deploy.duration"' | \ | |
jq '.series[2].points[0] = [${{env.NOW}}, ${{env.DEPLOY_DURATION}}]' | \ | |
jq '.series[3].metric = "${{env.METRIC_NAMESPACE}}.overall.duration"' | \ | |
jq '.series[3].points[0] = [${{env.NOW}}, ${{env.OVERALL_DURATION}}]' | \ | |
jq '.series[4].metric = "${{env.METRIC_NAMESPACE}}.built.pages"' | \ | |
jq '.series[4].points[0] = [${{env.NOW}}, ${{env.BUILT_PAGES}}]' | \ | |
jq '.series[].tags[0] = "env:${{env.DEPLOY_ENV}}"' | \ | |
jq '.series[].tags[1] = "build_number:${{github.run_number}}"' | \ | |
jq '.series[].tags[2] = "status:${{needs.deploy.result}}"' | \ | |
jq '.series[].tags[3] = "trigger:${{env.BUILD_TRIGGER}}"' > metrics.json | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 | |
with: | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
aws-region: us-gov-west-1 | |
- name: Get Datadog api key from Parameter Store | |
uses: department-of-veterans-affairs/action-inject-ssm-secrets@d8e6de3bde4dd728c9d732baef58b3c854b8c4bb # latest | |
with: | |
ssm_parameter: /dsva-vagov/content-build/GHA_CONTENT_BUILD_DATADOG_API_KEY | |
env_variable_name: GHA_CONTENT_BUILD_DATADOG_API_KEY | |
- name: Get Datadog app key from Parameter Store | |
uses: department-of-veterans-affairs/action-inject-ssm-secrets@d8e6de3bde4dd728c9d732baef58b3c854b8c4bb # latest | |
with: | |
ssm_parameter: /dsva-vagov/content-build/GHA_CONTENT_BUILD_DATADOG_APP_KEY | |
env_variable_name: GHA_CONTENT_BUILD_DATADOG_APP_KEY | |
- name: Send metrics to Datadog | |
run: | | |
curl -X POST "https://api.ddog-gov.com/api/v1/series" \ | |
-H "Content-Type: text/json" \ | |
-H "DD-API-KEY: ${{ env.GHA_CONTENT_BUILD_DATADOG_API_KEY }}" \ | |
-d @- < metrics.json |