feat(ci): Docker build rework #1
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: 'Docker Build' | |
on: | |
push: | |
branches: ['main'] | |
pull_request: | |
branches: ['main'] | |
jobs: | |
docker-build: | |
name: Build & Push Docker Images | |
runs-on: ubuntu-latest | |
# We do a matrix of "docker-express", "docker-next", "docker-static", etc. | |
# If you have more, just add them here: | |
strategy: | |
fail-fast: false | |
matrix: | |
dockerTarget: | |
[ | |
docker-express, | |
docker-next, | |
docker-static, | |
docker-playwright, | |
docker-jest, | |
] | |
steps: | |
# 1) Check out the code | |
- name: Checkout | |
uses: actions/checkout@v3 | |
# 2) Set up Node (reading version from your package.json) | |
- name: Set up Node | |
uses: actions/setup-node@v4 | |
with: | |
node-version-file: 'package.json' | |
# 3) Install dependencies for Nx | |
- name: Install dependencies | |
run: | | |
corepack enable | |
yarn install --frozen-lockfile | |
# 4) Log in to ECR using official AWS action | |
- name: ECR Login | |
uses: aws-actions/amazon-ecr-login@v2 | |
with: | |
# Adjust region if needed | |
region: eu-west-1 | |
# 5) Set up QEMU (for multi-arch in the future). | |
# Right now you only do amd64, but let's keep this so adding arm64 is easy later: | |
- name: Set up QEMU | |
uses: docker/setup-qemu-action@v3 | |
with: | |
# If you don’t need multi-arch at all yet, you could remove this step. | |
image: 'public.ecr.aws/m3u4c4h9/island-is/eks-distro-build-tooling/binfmt-misc:qemu-v6.1.0' | |
# 6) Set up Docker Buildx so we can do buildx build | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
with: | |
# If you store a BuildKit image in ECR, you can specify driver-opts here. | |
# Otherwise, the default is fine. | |
install: true | |
# 7) Gather Nx projects that have the target in `matrix.dockerTarget` | |
- name: Find Nx Projects | |
id: nx | |
run: | | |
set -eo pipefail | |
# We do an Nx "affected" or just "show projects". | |
# For simplicity, let's just do `nx show projects --with-target=<dockerTarget>`. | |
PROJECTS=$(npx nx show projects --with-target=${{ matrix.dockerTarget }} | xargs) | |
# We'll write these projects to outputs so we can iterate in the next step. | |
echo "projects=$PROJECTS" | |
echo "projects=$PROJECTS" >> "$GITHUB_OUTPUT" | |
# 8) Build & push each Nx project in a loop | |
- name: Build and push images | |
run: | | |
# if "Find Nx Projects" found none, skip | |
if [ -z "${{ steps.nx.outputs.projects }}" ]; then | |
echo "No projects found for target='${{ matrix.dockerTarget }}'. Skipping..." | |
exit 0 | |
fi | |
# Split the space-delimited project list | |
for project in ${{ steps.nx.outputs.projects }}; do | |
echo "------------------------" | |
echo "Building Docker image for project '$project'..." | |
echo "------------------------" | |
# -------------- 1) Decide on the ECR repo & tags -------------- | |
# Typically you do ECR_REGISTRY= 821090935708.dkr.ecr.eu-west-1.amazonaws.com | |
# so each project is: <ECR_REGISTRY>/<project> | |
ECR_REGISTRY="821090935708.dkr.ecr.eu-west-1.amazonaws.com" | |
IMAGE_NAME="$ECR_REGISTRY/$project" | |
# We'll build up multiple tags. For example: | |
# - "sha-<shortSha>" | |
# - "branch-<branchName>" | |
# - "pr-<prNumber>" | |
# - "YYYYMMDD" | |
# - "<branchName>_<shortSha>_<runNumber>" | |
GIT_SHA="${GITHUB_SHA}" | |
SHORT_SHA="${GIT_SHA:0:10}" # first 10 chars | |
BRANCH_NAME="${GITHUB_REF_NAME}" # e.g. main, release-33-2-0, etc. | |
RUN_NUM="${GITHUB_RUN_NUMBER}" | |
# If it's a PR, GITHUB_REF_NAME might be "refs/pull/123/merge" or similar | |
# You can parse a PR number like this: | |
PR_NUMBER="" | |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
PR_NUMBER="${{ github.event.pull_request.number }}" | |
fi | |
# Parse a date tag (YYYYMMDD): | |
DATE_TAG=$(date +%Y%m%d) | |
# If you have a "release version" in the branch name (e.g. "release-33-2-0"), you can parse it: | |
# For example, if BRANCH_NAME starts with "release-", we’ll grab the rest: | |
RELEASE_TAG="" | |
if [[ "$BRANCH_NAME" =~ ^release- ]]; then | |
RELEASE_TAG="${BRANCH_NAME#release-}" # e.g. 33-2-0 | |
fi | |
# We'll collect them into a single `TAGS` arg for buildx: | |
# The main tag (the one you prefer to "latest" or "dev") can be first: | |
# Note: if you want them all to go to the same repo, you just pass multiple --tag arguments. | |
# Docker Buildx supports `--tag <image>:tag1 --tag <image>:tag2` | |
TAG_ARGS=" --tag $IMAGE_NAME:$BRANCH_NAME-$SHORT_SHA-$RUN_NUM " # e.g. main_abc1234567_1034 | |
TAG_ARGS+=" --tag $IMAGE_NAME:sha-$SHORT_SHA " # e.g. sha-abc1234567 | |
TAG_ARGS+=" --tag $IMAGE_NAME:date-$DATE_TAG " # e.g. date-20250109 | |
if [ -n "$PR_NUMBER" ]; then | |
TAG_ARGS+=" --tag $IMAGE_NAME:pr-$PR_NUMBER " | |
fi | |
if [ -n "$RELEASE_TAG" ]; then | |
TAG_ARGS+=" --tag $IMAGE_NAME:release-$RELEASE_TAG " | |
fi | |
# -------------- 2) Actually build the Docker image -------------- | |
# We'll specify a build target that corresponds to the Nx target. For example: | |
# docker-express => output-express | |
# docker-next => output-next | |
# ... | |
# Just a little case statement for clarity: | |
case "${{ matrix.dockerTarget }}" in | |
docker-express) DOCKER_TARGET="output-express" ;; | |
docker-next) DOCKER_TARGET="output-next" ;; | |
docker-static) DOCKER_TARGET="output-static" ;; | |
docker-playwright) DOCKER_TARGET="output-playwright" ;; | |
docker-jest) DOCKER_TARGET="output-jest" ;; | |
*) | |
echo "Unrecognized docker target '${{ matrix.dockerTarget }}'!" | |
exit 1 | |
;; | |
esac | |
# If your Dockerfile needs certain build args (like NODE_IMAGE_TAG), add them: | |
# For example: | |
NODE_IMAGE_TAG="16-alpine" | |
EXTRA_ARGS="--build-arg NODE_IMAGE_TAG=$NODE_IMAGE_TAG" | |
EXTRA_ARGS+=" --build-arg APP=$project" | |
EXTRA_ARGS+=" --build-arg GIT_BRANCH=$BRANCH_NAME" | |
EXTRA_ARGS+=" --build-arg GIT_COMMIT_SHA=$GIT_SHA" | |
EXTRA_ARGS+=" --build-arg DOCKER_IMAGE_REGISTRY=$ECR_REGISTRY" | |
echo "Running Docker buildx build with tags:" | |
echo " $TAG_ARGS" | |
echo "And build target: $DOCKER_TARGET" | |
# If you want to pull a cache-from from ECR, you can do something like: | |
# --cache-from type=registry,ref=$IMAGE_NAME:cache-latest | |
# and push a cache-to for later builds. That’s beyond scope, but you’d do: | |
# --cache-to type=registry,ref=$IMAGE_NAME:cache-latest,mode=max | |
# | |
# If you do NOT want to rely on GitHub cache but do want your own ECR-based | |
# Docker layer caching, you can do it here. Or skip caching altogether. | |
# -------------- 3) Do the Docker buildx build + push -------------- | |
docker buildx build \ | |
--platform linux/amd64 \ | |
--file scripts/ci/Dockerfile \ | |
--target "$DOCKER_TARGET" \ | |
$TAG_ARGS \ | |
$EXTRA_ARGS \ | |
--push \ | |
. | |
echo "Docker image built & pushed: $IMAGE_NAME (project: $project)" | |
echo | |
done | |
echo "All done building for dockerTarget='${{ matrix.dockerTarget }}'." |