Skip to content

Build Release

Build Release #268

Workflow file for this run

---
name: "Build Release"
on:
workflow_dispatch:
inputs:
releaseBranch:
description: >-
releaseBranch: Override the branch on which a release is based.
Default to the selected reference in the `Use workflow from` drop-down when empty.
required: false
default: ""
releaseVersion:
description: >-
releaseVersion: Override the release version. Default to promoting the current
X.Y.Z-SNAPSHOT to X.Y.Z when empty.
required: false
default: ""
developmentVersion:
description: >-
developmentVersion: Override the next development iteration version.
Default to X.(Y+1).0-SNAPSHOT of the release version X.Y.Z when empty.
required: false
default: ""
awsRegion:
description: >-
awsRegion: Override the AWS Region destination for uploaded artifacts.
Default to `us-east-1`.
default: us-east-1
type: choice
options:
- us-east-1
- us-west-2
required: true
forceRelease:
description: >-
forceRelease: Override creation of the GitHub Release object.
Default to creating release objects when `releaseVersion` does not contain the hyphen
character ('-'), indicating a pre-release.
default: false
required: false
type: boolean
permissions:
id-token: write # This is required for requesting the AWS IAM OIDC JWT
contents: write # This is required for actions/checkout
env:
AWS_REGION: ${{ inputs.awsRegion }}
BFD_RELEASE_OVERRIDE: ${{ inputs.releaseVersion }}
BFD_DEV_VERSION_OVERRIDE: ${{ inputs.developmentVersion }}
JIB_IMAGES: |
[
"bfd-server",
"bfd-pipeline-app",
"bfd-db-migrator"
]
ACCOUNT_ROLE_MAP: |
{
"prod": "${{ secrets.PROD_ACCOUNT_GHA_ROLE_ARN }}",
"non-prod": "${{ secrets.NON_PROD_ACCOUNT_GHA_ROLE_ARN }}"
}
defaults:
run:
shell: bash
jobs:
compute-version-strings:
runs-on: ubuntu-24.04
outputs:
bfd_release: ${{ steps.bfd-version-strings.outputs.BFD_RELEASE }}
bfd_dev_version: ${{ steps.bfd-version-strings.outputs.BFD_DEV_VERSION }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.releaseBranch || github.ref_name }}
- name: "Install yq"
run: |
sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq
sudo chmod +x /usr/bin/yq
- name: Set and Validate Version Strings
id: bfd-version-strings
run: |
# Set default values for bfd-parent version based on existing version string in apps/pom.xml
BFD_PARENT_POM_VERSION="$(yq --output-format=yaml .project.version apps/pom.xml)"
## Use override OR promote default by removing '-SNAPSHOT' suffix
BFD_RELEASE_DEFAULT="$(yq 'split("-") | .[0]' <<< "$BFD_PARENT_POM_VERSION")"
BFD_RELEASE="${BFD_RELEASE_OVERRIDE:-$BFD_RELEASE_DEFAULT}"
## Use override OR increment default value Y in X.Y.Z formatted release version, attach '-SNAPSHOT' suffix
BFD_DEV_VERSION_DEFAULT="$(yq 'split("-") | .[0] | split(".") | map(. type = "!!int") | [.[0], .[1]+1, .[2]] | join(".")' <<< "$BFD_PARENT_POM_VERSION")-SNAPSHOT"
BFD_DEV_VERSION="${BFD_DEV_VERSION_OVERRIDE:-$BFD_DEV_VERSION_DEFAULT}"
# Validate and set BFD_RELASE and BFD_DEV_VERSION
echo "$BFD_RELEASE" | grep -P '^\d+\.\d+\.\d+$|^\d+\.\d+\.\d+-[a-zA-Z0-9-]+$'
echo BFD_RELEASE="${BFD_RELEASE}" >> "$GITHUB_OUTPUT"
echo "$BFD_DEV_VERSION" | grep -P '^\d+\.\d+\.\d+-SNAPSHOT$'
echo BFD_DEV_VERSION="${BFD_DEV_VERSION_OVERRIDE:-$BFD_DEV_VERSION_DEFAULT}" >> "$GITHUB_OUTPUT"
build-base-images:
uses: ./.github/workflows/build-container-images.yml
needs: compute-version-strings
permissions:
contents: read
id-token: write
with:
branch: ${{ inputs.releaseBranch || github.ref_name }}
versionTag: ${{ needs.compute-version-strings.outputs.bfd_release }}
awsRegion: ${{ inputs.awsRegion }}
# Build the base images prior to building the application images with jib in "run-mvn-release"
# See .github/workflows/build_container_images_matrix.json for list of buildable images
imagesCsv: "bfd-platform-base-java, bfd-platform-base-python"
cleanupImageArtifacts: false # Keep the built images around so that they can be used for Jib
skipBasePull: true # We're building the base images, so don't pull them
secrets: inherit
run-mvn-release:
runs-on: ubuntu-24.04
needs: [compute-version-strings, build-base-images]
env:
BFD_RELEASE: ${{ needs.compute-version-strings.outputs.bfd_release }}
BFD_DEV_VERSION: ${{ needs.compute-version-strings.outputs.bfd_dev_version }}
steps:
- name: "Generate an App Token"
id: generate_token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.BFD_RELEASE_APP_ID }}
private-key: ${{ secrets.BFD_RELEASE_APP_KEY }}
- name: Checkout
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.releaseBranch || github.ref_name }}
token: ${{ steps.generate_token.outputs.token }}
- name: Retrieve base java image
uses: actions/download-artifact@v4
with:
pattern: '*bfd-platform-base-java*'
path: ${{ runner.temp }}
- name: Load base image into Docker
run: |
docker load --input "${{ runner.temp }}/bfd-platform-base-java.tar"
- name: Install gitleaks
run: |
curl -s https://api.github.com/repos/gitleaks/gitleaks/releases/latest \
| grep "browser_download_url.*linux_x64.tar.gz" \
| cut -d : -f 2,3 \
| tr -d \" \
| wget -qi -
sudo tar -xzf "$(find -iname 'gitleaks*.tar.gz')" -C /usr/bin gitleaks
sudo chmod +x /usr/bin/gitleaks
- name: Setup JDK
uses: actions/setup-java@v4
with:
java-version: "21"
distribution: corretto
- name: Configure the git user
run: |
git config --global user.email "actions@github.com"
git config --global user.name "GitHub Actions"
- name: Configure additional maven settings.xml
run: |-
cat <<"EOF" > ~/.m2/settings.xml
<settings xmlns="http://maven.apache.org/settings/1.0.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
xsi:schemalocation="http://maven.apache.org/settings/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>github</id>
<username>${env.GITHUB_ACTOR}</username>
<password>${env.GITHUB_TOKEN}</password>
</server>
</servers>
</settings>
EOF
- name: "Prepare Release"
if: github.event_name == 'workflow_dispatch'
run: |-
# We set preparationGoals to an empty string because we don't want to build during the
# release:prepare since we will need to build again during release:perform. We expect that the
# source code has been verified to compile and pass tests prior to merging to master, so building it
# unnecessarily during the prepare phase is redundant.
mvn --batch-mode --activate-profiles prepare-release \
-Dtag="$BFD_RELEASE" \
-DreleaseVersion="$BFD_RELEASE" \
-DdevelopmentVersion="$BFD_DEV_VERSION" \
-DpreparationGoals="" \
release:prepare
working-directory: ./apps
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: "Perform Release"
if: github.event_name == 'workflow_dispatch'
run: |-
mvn --batch-mode --activate-profiles perform-release \
-Dtag="$BFD_RELEASE" \
-DreleaseVersion="$BFD_RELEASE" \
-DdevelopmentVersion="$BFD_DEV_VERSION" \
release:perform
working-directory: ./apps
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Export jib images
run: |
readarray -t images < <(echo "$JIB_IMAGES" | jq -r -c '.[]')
for image in "${images[@]}"
do
docker save "$image:$BFD_RELEASE" > "${image}.tar"
done
working-directory: ${{ runner.temp }}
- name: Upload jib image artifacts
uses: actions/upload-artifact@v4
with:
name: jib-images
path: ${{ runner.temp }}/*.tar
retention-days: 1
- name: "Perform Exceptional Rollback"
if: failure()
run: mvn release:rollback
working-directory: ./apps
push-jib-images:
needs: [compute-version-strings, run-mvn-release]
strategy:
matrix:
account: ["prod", "non-prod"]
runs-on: codebuild-bfd-${{ matrix.account }}-platform-docker-${{ github.run_id }}-${{ github.run_attempt }}
env:
BFD_RELEASE: ${{ needs.compute-version-strings.outputs.bfd_release }}
BFD_DEV_VERSION: ${{ needs.compute-version-strings.outputs.bfd_dev_version }}
steps:
- name: Get role ARN
id: get-role-arn
run: |
role_arn="$(jq -r --arg account_type "${{ matrix.account }}" '.[$account_type]' <<<"$ACCOUNT_ROLE_MAP")"
echo "::add-mask::$role_arn"
echo "role-arn=$role_arn" >> "$GITHUB_OUTPUT"
- name: Download jib image artifacts
uses: actions/download-artifact@v4
with:
name: jib-images
path: ${{ runner.temp }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ steps.get-role-arn.outputs.role-arn }}
role-session-name: build-release-${{ github.run_id }}-${{ github.run_attempt }}
aws-region: ${{ env.AWS_REGION }}
- name: Login to ECR
id: ecr-login
uses: aws-actions/amazon-ecr-login@v2
- name: Push jib images
env:
REGISTRY: ${{ steps.ecr-login.outputs.registry }}
PUSH_LATEST: ${{ !contains(needs.compute-version-strings.outputs.bfd_release, '-') }}
run: |
echo "::add-mask::$REGISTRY"
for image_filename in ./*.tar
do
docker load --input "$image_filename"
image="$(basename "$image_filename" .tar)"
docker tag "$image:$BFD_RELEASE" "$REGISTRY/$image:$BFD_RELEASE"
if [[ $PUSH_LATEST == "true" ]]; then
# Remove the "latest" tag from the existing latest image
aws ecr batch-delete-image --repository-name "$IMAGE" --image-ids imageTag=latest
# Tag the new release image as "latest"
docker tag "$image:$BFD_RELEASE" "$REGISTRY/$image:latest"
# Push "latest" tag
docker push "$REGISTRY/$image:latest"
fi
docker push "$REGISTRY/$image:$BFD_RELEASE"
done
working-directory: ${{ runner.temp }}
cleanup-artifacts:
if: ${{ !cancelled() }}
needs: push-jib-images
runs-on: ubuntu-24.04
steps:
- name: Delete all image artifacts
uses: GeekyEggo/delete-artifact@v5.1.0
with:
name: '*'
failOnError: false
build-other-images:
uses: ./.github/workflows/build-container-images.yml
needs: [compute-version-strings, run-mvn-release, cleanup-artifacts]
permissions:
contents: read
id-token: write
with:
branch: ${{ inputs.releaseBranch || github.ref_name }}
versionTag: ${{ needs.compute-version-strings.outputs.bfd_release }}
awsRegion: ${{ inputs.awsRegion }}
# Build other images, like Lambda images, Python applications, etc.
# See .github/workflows/build_container_images_matrix.json for list of buildable images
imagesCsv: >-
bfd-platform-server-regression,
bfd-platform-mount-certstores,
bfd-platform-server-fluent-bit,
bfd-platform-run-locust,
bfd-platform-eft-sftp-outbound-transfer-lambda,
bfd-platform-pipeline-ccw-manifests-verifier-lambda,
bfd-platform-pipeline-ccw-runner
baseImagesVersion: ${{ needs.compute-version-strings.outputs.bfd_release }}
secrets: inherit
create-gh-release:
if: ${{ !contains(needs.compute-version-strings.outputs.bfd_release, '-') || inputs.forceRelease }}
runs-on: ubuntu-24.04
needs:
[
compute-version-strings,
build-base-images,
run-mvn-release,
push-jib-images,
build-other-images,
]
steps:
- name: Pull Release Files
run: |
readarray -t assets < <(echo "$CA_ASSETS" | jq -r -c '.[]')
for asset in "${assets[@]}"
do
aws codeartifact get-package-version-asset \
--domain-owner ${{ secrets.AWS_ACCOUNT_ID }} \
--domain "$CA_DOMAIN" \
--repository "$CA_REPOSITORY" \
--asset "$asset" \
--package-version "$BFD_RELEASE" \
--package "$CA_PACKAGE" \
--namespace "$CA_NAMESPACE" \
--format maven \
--region "$AWS_REGION" \
"${asset/$CA_PACKAGE-${BFD_RELEASE}-/}" 1>/dev/null
done
# rename data dictionary release assets to follow historical naming conventions
for item in ./*data-dictionary*
do
filename=$(basename -- "$item")
extension="${filename##*.}"
filename="$(echo "${filename%.*}" | sed -E 's/^v([0-9]+.*)$/V\1/')"
mv "$item" "$filename-${BFD_RELEASE}.$extension"
done
mv ./openapi.yaml "openapi-${BFD_RELEASE}.yaml"
env:
BFD_RELEASE: ${{ needs.compute-version-strings.outputs.bfd_release }}
CA_NAMESPACE: gov.cms.bfd
CA_PACKAGE: bfd-server-war
CA_ASSETS: |
[
"bfd-server-war-${{ needs.compute-version-strings.outputs.bfd_release }}-v1-data-dictionary.csv",
"bfd-server-war-${{ needs.compute-version-strings.outputs.bfd_release }}-v2-data-dictionary.csv",
"bfd-server-war-${{ needs.compute-version-strings.outputs.bfd_release }}-v1-data-dictionary.json",
"bfd-server-war-${{ needs.compute-version-strings.outputs.bfd_release }}-v2-data-dictionary.json",
"bfd-server-war-${{ needs.compute-version-strings.outputs.bfd_release }}-data-dictionary.xlsx",
"bfd-server-war-${{ needs.compute-version-strings.outputs.bfd_release }}-openapi.yaml"
]
- name: Release
uses: ncipollo/release-action@v1
with:
# NOTE: Prevent automatic promotion of pre-release objects to latest
makeLatest: "${{ !contains(needs.compute-version-strings.outputs.bfd_release, '-') }}"
generateReleaseNotes: true
artifactErrorsFailBuild: true
tag: ${{ needs.compute-version-strings.outputs.bfd_release }}
name: "v${{ needs.compute-version-strings.outputs.bfd_release }}"
artifacts: "*.csv,*.json,*.xlsx,*.yaml"