Skip to content

Rhodri-Morgan/github-workflows

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GitHub Workflows

Reusable GitHub composite actions for projects.

Actions

Action Description
build Builds a Docker image with Buildx and optional registry-based layer caching
push Tags and pushes a local Docker image to an ECR registry
deploy-ecs Deploys a new image tag to an ECS service and updates SSM tag

GitHub Secrets

Consuming repositories need these secrets. They are configured per-repo in the terraform repo under github/vars/root.tfvars:

Secret Description
AWS_ACCOUNT_ID Target AWS account ID for the repo
AWS_ROLE_ARN IAM role ARN for OIDC authentication

Build Secrets vs Build Args

The build action accepts both build-args and secrets. Use the right one to avoid Docker layer cache invalidation.

build-args are baked into image layers. If a value changes between builds, all layers after the ARG declaration miss cache. Use for values that are stable or that sit below layers you expect to rebuild anyway.

secrets (via --mount=type=secret) are injected at runtime and excluded from the layer cache key. Use for credentials and tokens that rotate between builds — the layer will stay cached even when the secret value changes.

In the Dockerfile, consume secrets like this:

RUN --mount=type=secret,id=MY_SECRET \
    export MY_SECRET=$(cat /run/secrets/MY_SECRET) && \
    some-command

And pass them from the workflow:

- uses: Rhodri-Morgan/github-workflows/build@main
  with:
    image-repo: my-project
    secrets: |
      MY_SECRET=${{ env.MY_SECRET }}
Scenario Use Why
Auth tokens that rotate every build secrets Avoids cache bust on every build
Database URLs with credentials secrets Keeps credentials out of docker history
Static config (e.g. NODE_ENV) build-args Value is stable, fine to bake in

Usage

Build and Push

Before setting up build workflows, note the following:

  • If your project needs different images for dev and prod (e.g. statically replaced variables, build-time validation that requires environment-specific values), use a matrix strategy so dev and prod builds run in parallel.
  • If you have a monorepo, use separate jobs per image so they build concurrently on tag push.
  • Validate your Dockerfile layer caching. Check each layer for cache-busting pitfalls: changing commit SHAs baked into build args, rotating secrets passed as build args instead of --mount=type=secret, non-deterministic package installs (missing lockfiles), timestamps in generated files, and COPY . . placed before dependency installation layers.
  • Registry caching is optional. The aws-region, account-id, and role-arn inputs on the build action are only required if you want ECR-based registry layer caching. Without them, the build still works but won't cache layers to or from a remote registry.
  • Set image-tag only when you need a custom tag. If omitted, the build action falls back to the first 6 characters of GITHUB_SHA.
  • Set target only when you need a named Docker stage. If omitted, Docker builds the default final stage in the Dockerfile.

Example tag-triggered build and push workflow:

name: Build & Push to ECR

on:
  push:
    tags:
      - "v*.*.*"
      - "v*.*.*-*"

permissions:
  id-token: write
  contents: read

run-name: Build and push image ${{ github.ref_name }}

jobs:
  build-push:
    runs-on: ubuntu-latest
    name: Build and push
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Build image
        id: build
        uses: Rhodri-Morgan/github-workflows/build@main
        with:
          image-repo: your-ecr-repository
          image-tag: ${{ github.ref_name }}
          target: build
          aws-region: eu-west-1
          account-id: ${{ secrets.AWS_ACCOUNT_ID }}
          role-arn: ${{ secrets.AWS_ROLE_ARN }}

      - name: Run tests in built image
        run: docker run --rm local/your-ecr-repository:${{ steps.build.outputs.image-tag }} make ci-test

      - name: Push
        uses: Rhodri-Morgan/github-workflows/push@main
        with:
          image-repo: your-ecr-repository
          image-tag: ${{ steps.build.outputs.image-tag }}
          aws-region: eu-west-1
          account-id: ${{ secrets.AWS_ACCOUNT_ID }}
          role-arn: ${{ secrets.AWS_ROLE_ARN }}

Replace your-ecr-repository with your ECR repository name. Adjust the tag trigger, region, and test command to match your project.

Use target when you need to build a named Docker stage instead of the default final stage from the Dockerfile. In the example above, build is only the stage name used by that Dockerfile.

Deploy to ECS

This action deploys a new image tag to an ECS service by registering a new task definition with the updated image tag. The SSM parameter is only updated after a successful deployment. On failure, the action automatically rolls back to the previous task definition.

Example deploy workflow (manual dispatch):

name: Deploy

on:
  workflow_dispatch:
    inputs:
      deploy-tag:
        description: "Image Tag:"
        default: v2026.MM.DD-n
        required: true
        type: string

permissions:
  id-token: write
  contents: read

run-name: Deploy ${{ inputs.deploy-tag }}

concurrency:
  group: deploy-my-service
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    name: Deploy
    steps:
      - name: Deploy to ECS
        uses: Rhodri-Morgan/github-workflows/deploy-ecs@main
        with:
          image-tag: ${{ inputs.deploy-tag }}
          aws-region: eu-west-1
          role-arn: ${{ secrets.AWS_ROLE_ARN }}
          ecs-cluster: my-cluster
          ecs-service: my-service
          image-ssm-parameter: my-service-image-tag
          deployment-timeout: "600"

Scripts

Helper scripts to use alongside these workflows.

Script Description
scripts/tag-and-push.sh Creates and pushes a date-based git tag in vYYYY.MM.DD-n format

tag-and-push.sh

Creates a git tag using the standard date format vYYYY.MM.DD-n, with an incrementing suffix for multiple releases on the same day (for example v2026.03.11, v2026.03.11-1, v2026.03.11-2). Pushing a tag in this format can be used to trigger tag-based build workflows.

Then trigger a build and push with:

yarn bump

Makefile

Add a Makefile target that fetches the script via the GitHub API:

.PHONY: tag-and-push
tag-and-push:
	@gh api 'repos/Rhodri-Morgan/github-workflows/contents/scripts/tag-and-push.sh?ref=main' --jq '.content' | base64 -d > /tmp/tag-and-push.sh
	@sh /tmp/tag-and-push.sh

Then trigger a build and push with:

make tag-and-push

About

Composite workflows for projects

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages