Skip to content

Content Release

Content Release #317

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/configure-aws-credentials@v4
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/configure-aws-credentials@v4
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/configure-aws-credentials@v4
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/configure-aws-credentials@v4
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