Last active
July 14, 2025 11:24
-
-
Save jakzal/578fffea6834845bca209b848cb430f1 to your computer and use it in GitHub Desktop.
Build, package, deploy a versioned service (example)
This file contains hidden or 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: Build | |
on: | |
push: | |
branches: ["main"] | |
tags: ["*"] | |
pull_request: | |
jobs: | |
version: | |
name: Version | |
uses: ./.github/workflows/version.yml | |
test: | |
name: Test | |
run: make build test | |
package: | |
name: Build images | |
uses: ./.github/workflows/package.yml | |
needs: version | |
with: | |
image_version: ${{ needs.version.outputs.next_version }} | |
image_prefix: "acme/foo" | |
image_archive: "foo-images.tar" | |
build_cmd: "IMAGE_VERSION=${{ needs.version.outputs.next_version }} make build-production VERSION=${{ needs.version.outputs.next_version }}" | |
vulnerability-scan: | |
# since we do not fail the build for now, but the report is to get feedback, only run it on the main branch and tags | |
if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) && github.repository_owner == 'acme' | |
name: Vulnerability scan | |
uses: ./.github/workflows/vulnerability-scan.yml | |
needs: package | |
with: | |
image_archive: "foo-images.tar" | |
images_json: ${{ needs.package.outputs.images_json }} | |
publish: | |
if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) && github.repository_owner == 'acme' | |
runs-on: ubuntu-latest | |
name: Publish containers | |
needs: [package, test] | |
env: | |
DOCKER_BUILDKIT: 1 | |
COMPOSE_DOCKER_CLI_BUILD: 1 | |
IMAGES: ${{ needs.package.outputs.images }} | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Download Docker Images | |
uses: actions/download-artifact@v4 | |
with: | |
name: docker-images | |
- name: Load Docker Images | |
run: docker load -i foo-images.tar | |
- name: Set up AWS CLI | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
aws-region: eu-west-2 | |
- name: Login to Amazon ECR | |
id: login-ecr | |
uses: aws-actions/amazon-ecr-login@v2 | |
- name: Tag and Push Docker Images filtered | |
run: | | |
FILTERED_IMAGES=$(echo "$IMAGES" | tr ' ' '\n' | grep -v 'foo-fluentbit' | tr '\n' ' ') | |
echo "Filtered IMAGES: $FILTERED_IMAGES" | |
make aws-push-to-ecr AWS_ACCOUNT_ID="${{ secrets.AWS_ACCOUNT_ID}}" IMAGES="$FILTERED_IMAGES" | |
- name: Extract version from IMAGES and update SSM | |
run: | | |
TAG=$(echo "$IMAGES" | awk '{print $1}' | cut -d: -f2) | |
echo "Detected tag: $TAG" | |
aws ssm put-parameter --name "foo-service-version" --value "$TAG" --type String --overwrite |
This file contains hidden or 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
# Builds Docker images with the supplied build command (`build_cmd`), | |
# verifies images built properly with the supplied verification command (`verify_cmd`), | |
# tags them with the supplied image version (`image_version`), | |
# packages images that match the supplied prefix (`image_prefix`), | |
# and uploads them as a build artifact under the supplied archive name (`image_archive`). | |
# Packaged image names are exposed as `images` and `images_json`. | |
name: Package | |
on: | |
workflow_call: | |
inputs: | |
image_version: | |
description: The Docker image version | |
required: true | |
type: string | |
image_prefix: | |
description: The prefix of images to export" | |
required: true | |
type: string | |
image_archive: | |
description: The image archive name to upload | |
required: true | |
type: string | |
image_artifact: | |
description: The image artifact name to upload the archive with | |
required: false | |
type: string | |
default: docker-images | |
build_cmd: | |
description: The command to use to build Docker image(s) | |
required: true | |
type: string | |
verify_cmd: | |
description: The command to use to verify images built successfully | |
required: false | |
type: string | |
default: true | |
outputs: | |
images: | |
description: List of images that were built | |
value: ${{ jobs.package.outputs.images }} | |
images_json: | |
description: List of images that were built (JSON) | |
value: ${{ jobs.package.outputs.images_json }} | |
jobs: | |
package: | |
name: Build images | |
runs-on: ubuntu-latest | |
env: | |
DOCKER_BUILDKIT: 1 | |
COMPOSE_DOCKER_CLI_BUILD: 1 | |
outputs: | |
images: ${{ steps.save.outputs.images }} | |
images_json: ${{ steps.save.outputs.images_json }} | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Build | |
run: ${{ inputs.build_cmd }} | |
- name: Verify | |
run: ${{ inputs.verify_cmd }} | |
- name: Save Docker Images | |
id: save | |
run: | | |
IMAGES=$(docker images | awk '$1 ~ /'${IMAGE_PREFIX//\//\\/}'/ && $2 ~ /^'$IMAGE_VERSION'$/ { print $1":"$2}' | tr '\n' ' ') | |
IMAGES_JSON=$(echo $IMAGES | tr " " "\n" | jq -R . | jq -c -s .) | |
echo "Images to be saved: $IMAGES" | |
echo "Images to be saved (json): $IMAGES_JSON" | |
docker save -o "$IMAGE_ARCHIVE" $IMAGES | |
echo "images=${IMAGES}" >> $GITHUB_OUTPUT | |
echo "images_json=${IMAGES_JSON}" >> $GITHUB_OUTPUT | |
env: | |
IMAGE_PREFIX: ${{ inputs.image_prefix }} | |
IMAGE_ARCHIVE: ${{ inputs.image_archive }} | |
IMAGE_VERSION: ${{ inputs.image_version }} | |
- name: Upload Docker Images as Artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ inputs.image_artifact }} | |
path: ${{ inputs.image_archive }} |
This file contains hidden or 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
# Determines the next version for the new release of the code base. | |
# The version is exposed as the `next_version`. | |
# The tag is used if the current commit is tagged (i.e. 1.2.3). | |
# Otherwise, the last known tag is used with the current commit suffix (i.e. 1.2.3-4a5ae30). | |
name: Version | |
on: | |
workflow_call: | |
outputs: | |
next_version: | |
description: "The next version to be released" | |
value: ${{ jobs.version.outputs.next_version }} | |
jobs: | |
version: | |
name: Determine version | |
runs-on: ubuntu-latest | |
outputs: | |
next_version: ${{ steps.version.outputs.next_version }} | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: Determine the next version | |
id: version | |
run: | | |
COMMIT_REF=$(if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then echo "${{ github.event.pull_request.head.sha }}"; else echo "${GITHUB_SHA:-HEAD}"; fi) | |
CURRENT_COMMIT=$(git rev-parse --short "$COMMIT_REF") | |
CURRENT_TAG=$([[ "${GITHUB_REF:0:10}" = "refs/tags/" ]] && echo ${GITHUB_REF#refs/tags/} || echo "") | |
LAST_TAG=$(git for-each-ref refs/tags --sort=-creatordate --format='%(refname:short)' --count=1) | |
NEXT_VERSION=$([[ "${CURRENT_TAG}" = "" ]] && echo "${LAST_TAG:-0.0.0}-${CURRENT_COMMIT}" || echo "$CURRENT_TAG") | |
echo "Next version: $NEXT_VERSION" | |
echo "next_version=${NEXT_VERSION}" >> $GITHUB_OUTPUT |
This file contains hidden or 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: Vulnerability scan | |
on: | |
workflow_call: | |
inputs: | |
images_json: | |
description: JSON List of images to scan | |
required: true | |
type: string | |
image_archive: | |
description: Image export archive name | |
required: true | |
type: string | |
image_artifact: | |
description: Artifact name that contains the image export | |
required: false | |
type: string | |
default: docker-images | |
jobs: | |
vulnerability-scan: | |
runs-on: ubuntu-latest | |
name: Scan for vulnerabilities | |
strategy: | |
matrix: | |
image: ${{ fromJSON(inputs.images_json) }} | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Download Docker Images | |
uses: actions/download-artifact@v4 | |
with: | |
name: ${{ inputs.image_artifact }} | |
- name: Load Docker Images | |
run: docker load -i ${{ inputs.image_archive }} | |
- name: Determine the category | |
id: category | |
run: | | |
image=${{ matrix.image }} | |
category=${image%%:*} | |
echo "category=$category" >> $GITHUB_OUTPUT | |
# As below, using grype directly and reporting output to terminal, because we cannot use github advanced security | |
- name: Install Grype | |
run: | | |
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin | |
- name: Scan ${{ matrix.image }} with Grype | |
run: | | |
image="${{ matrix.image }}" | |
safe_name="${image//[\/:]/_}" | |
grype "$image" -o table > "grype-${safe_name}.txt" | |
echo "SAFE_NAME=$safe_name" >> $GITHUB_ENV | |
- name: Upload Grype Report | |
uses: actions/upload-artifact@v4 | |
with: | |
name: grype-report-${{ env.SAFE_NAME }} | |
path: grype-${{ env.SAFE_NAME }}.txt | |
# Commented out because we do not have github advanced security | |
# Either need to pay for a seat, or for this repo to become public at which point it is free | |
# | |
# - name: Scan ${{ matrix.image }} | |
# uses: anchore/scan-action@v5 | |
# id: scan | |
# with: | |
# image: "${{ matrix.image }}" | |
# fail-build: false | |
# severity-cutoff: critical | |
# output-format: sarif | |
# - name: Upload Image Vulnerability Reports | |
# uses: github/codeql-action/upload-sarif@v3 | |
# with: | |
# sarif_file: ${{ steps.scan.outputs.sarif }} | |
# category: ${{ steps.category.outputs.category }} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment