Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
- main
- nwm-main
- development
- development-pw
- release-candidate
push:
branches:
Expand All @@ -16,6 +17,7 @@ on:
- main
- nwm-main
- development
- development-pw
- release-candidate
workflow_dispatch:
inputs:
Expand Down Expand Up @@ -68,6 +70,7 @@ jobs:
alias_tag: ${{ steps.vars.outputs.alias_tag }}
clean_ref: ${{ steps.vars.outputs.clean_ref }}
default_ref: ${{ steps.vars.outputs.default_ref }}
default_base_tag: ${{ steps.vars.outputs.default_base_tag }}
ngen_image_digest: ${{ steps.vars.outputs.ngen_image_digest }}
ngen_image_revision: ${{ steps.vars.outputs.ngen_image_revision }}
ewts_revision: ${{ steps.vars.outputs.ewts_revision }}
Expand Down Expand Up @@ -104,17 +107,28 @@ jobs:
SHORT_SHA="${REAL_SHA:0:7}"

# default source-repo ref: follow the release-line branch we're building from
# (development / ngwpc-candidate / ngwpc-release); tags and other branches fall
# back to development. Override per source repo via the *_REF dispatch inputs.
# (development / development-pw / ngwpc-candidate / ngwpc-release); tags and
# other branches fall back to development. Override per source repo via the
# *_REF dispatch inputs.
case "${GITHUB_REF_TYPE}:${GITHUB_REF_NAME}" in
branch:development|branch:ngwpc-candidate|branch:ngwpc-release)
branch:development|branch:development-pw|branch:ngwpc-candidate|branch:ngwpc-release)
DEFAULT_REF="$GITHUB_REF_NAME" ;;
*)
DEFAULT_REF="development" ;;
esac
# use an explicit *_REF input if provided, else DEFAULT_REF
ref_or_default() { [ -n "$1" ] && echo "$1" || echo "$DEFAULT_REF"; }

# base-image alias follows the lane: development-pw (or a PR into it) pulls
# the rocky 'latest-pw' alias, every other ref the bookworm 'latest'. An
# explicit NGEN_IMAGE_TAG input always wins over this default.
LANE_REF="${GITHUB_BASE_REF:-$GITHUB_REF_NAME}"
if [ "$LANE_REF" = "development-pw" ]; then
DEFAULT_BASE_TAG="latest-pw"
else
DEFAULT_BASE_TAG="latest"
fi

# logic for the tags:
# test_image_tag (commit short sha): used for the initial build and test
# alias_tag: used for final tagging on successful tests
Expand All @@ -140,7 +154,8 @@ jobs:
fi

# base image (ngen) metadata for Dockerfile labels
NGEN_IMAGE_TAG="${{ inputs.NGEN_IMAGE_TAG || 'latest' }}"
NGEN_IMAGE_TAG="${{ inputs.NGEN_IMAGE_TAG }}"
NGEN_IMAGE_TAG="${NGEN_IMAGE_TAG:-$DEFAULT_BASE_TAG}"
NGEN_IMAGE="ghcr.io/${ORG}/ngen:${NGEN_IMAGE_TAG}"
NGEN_INSPECT=$(skopeo inspect --override-os linux --override-arch amd64 "docker://${NGEN_IMAGE}" 2>/dev/null || echo '{}')
NGEN_IMAGE_DIGEST=$(echo "$NGEN_INSPECT" | jq -r '.Digest // "unknown"')
Expand All @@ -158,6 +173,9 @@ jobs:
echo "${sha:-unknown}"
}

# Each *_REVISION is the dependency's resolved commit SHA: it labels the
# image AND doubles as the per-dependency cache-bust build-arg, so a new
# commit on the requested ref reinstalls that dep instead of a stale cache.
EWTS_REVISION=$(resolve_sha "https://github.com/${{ inputs.EWTS_ORG || github.repository_owner }}/nwm-ewts.git" "$(ref_or_default "${{ inputs.EWTS_REF }}")")
MSW_MGR_REVISION=$(resolve_sha "https://github.com/${{ inputs.MSW_MGR_ORG || github.repository_owner }}/nwm-msw-mgr.git" "$(ref_or_default "${{ inputs.MSW_MGR_REF }}")")

Expand All @@ -171,6 +189,7 @@ jobs:
commit_sha_short=${SHORT_SHA}
clean_ref=${CLEAN_REF}
default_ref=${DEFAULT_REF}
default_base_tag=${DEFAULT_BASE_TAG}
ngen_image_digest=${NGEN_IMAGE_DIGEST}
ngen_image_revision=${NGEN_IMAGE_REVISION}
ewts_revision=${EWTS_REVISION}
Expand Down Expand Up @@ -233,9 +252,9 @@ jobs:
tags: ${{ needs.setup.outputs.image_base }}:${{ needs.setup.outputs.test_image_tag }}
build-args: |
GHCR_ORG=${{ inputs.GHCR_ORG || needs.setup.outputs.org }}
NGEN_IMAGE_TAG=${{ inputs.NGEN_IMAGE_TAG || 'latest' }}
BASE_IMAGE_DIGEST=${{ needs.setup.outputs.ngen_image_digest }}
BASE_IMAGE_REVISION=${{ needs.setup.outputs.ngen_image_revision }}
NGEN_IMAGE=ghcr.io/${{ needs.setup.outputs.org }}/ngen:${{ inputs.NGEN_IMAGE_TAG || needs.setup.outputs.default_base_tag }}
NGEN_IMAGE_DIGEST=${{ needs.setup.outputs.ngen_image_digest }}
NGEN_IMAGE_REVISION=${{ needs.setup.outputs.ngen_image_revision }}
EWTS_ORG=${{ inputs.EWTS_ORG || github.repository_owner }}
EWTS_REF=${{ inputs.EWTS_REF || needs.setup.outputs.default_ref }}
EWTS_REVISION=${{ needs.setup.outputs.ewts_revision }}
Expand Down Expand Up @@ -338,23 +357,33 @@ jobs:
--all \
"docker://${IMAGE_BASE}:${TEST_TAG}" "docker://${IMAGE_BASE}:${ALIAS_TAG}"

# tag with 'latest' on development branch
# Branch-keyed promotion: development promotes 'latest' (bookworm/AWS
# lane); development-pw promotes 'latest-pw' (rocky/PW lane). Do not
# manually dispatch a cross-lane build from either branch: promotion
# keys off the branch name, so the tested image would promote under
# the wrong alias.
if [ "$GITHUB_REF_NAME" = "development" ]; then
skopeo copy \
--src-creds "${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}" \
--dest-creds "${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}" \
--all \
"docker://${IMAGE_BASE}:${TEST_TAG}" "docker://${IMAGE_BASE}:latest"
elif [ "$GITHUB_REF_NAME" = "development-pw" ]; then
skopeo copy \
--src-creds "${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}" \
--dest-creds "${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}" \
--all \
"docker://${IMAGE_BASE}:${TEST_TAG}" "docker://${IMAGE_BASE}:latest-pw"
fi

# Build the Apptainer .sif from the tested + promoted image and publish it to
# GHCR as an OCI artifact (oras://). AWS PCS compute nodes pull this onto the
# shared EFS and run it with `apptainer run`. SIF images are NOT baked into the
# compute AMI — the AMI carries only the Apptainer runtime; the images ship here
# compute AMI. The AMI carries only the Apptainer runtime; the images ship here
# and are staged on EFS. See nwm-ngencerf-infra-learning/docs/design-decisions.md.
build-sif:
name: build-sif
# Only on real branch builds (push) or manual dispatch not PRs (a multi-GB
# Only on real branch builds (push) or manual dispatch, not PRs (a multi-GB
# SIF build on every PR is wasteful). Runs after the image is tested + promoted.
if: |
(github.event_name == 'push') ||
Expand Down
17 changes: 11 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
############################################################################
# Change/Verify these values when adopting this Dockerfile into another org:
# GH_ORG, GHCR_ORG, IMAGE_NAMESPACE, APP_DIR,
# MSW_MGR_ORG, MSW_MGR_REF, EWTS_ORG, EWTS_REF
# MSW_MGR_ORG, MSW_MGR_REF, EWTS_ORG, EWTS_REF,
# USE_EWTS, EWTS_CACHE_BUST
############################################################################

# Ownership / branding overrides
Expand Down Expand Up @@ -62,25 +63,29 @@ ARG APP_DIR

# OCI Metadata Arguments
#
# BASE_IMAGE_* refers to the ngen image this image is built FROM.
ARG BASE_IMAGE_DIGEST="unknown"
ARG BASE_IMAGE_REVISION="unknown"
# NGEN_IMAGE_* refers to the ngen image this image is built FROM.
ARG NGEN_IMAGE_DIGEST="unknown"
ARG NGEN_IMAGE_REVISION="unknown"
ARG IMAGE_SOURCE="unknown"
ARG IMAGE_VENDOR="unknown"
ARG IMAGE_VERSION="unknown"
ARG IMAGE_REVISION="unknown"
ARG EWTS_REVISION="unknown"
ARG MSW_MGR_REVISION="unknown"

# Image Labels: OCI-spec annotations followed by custom source-repo metadata.
LABEL org.opencontainers.image.base.name="${NGEN_IMAGE}" \
org.opencontainers.image.base.digest="${BASE_IMAGE_DIGEST}" \
org.opencontainers.image.base.digest="${NGEN_IMAGE_DIGEST}" \
org.opencontainers.image.source="${IMAGE_SOURCE}" \
org.opencontainers.image.vendor="${IMAGE_VENDOR}" \
org.opencontainers.image.version="${IMAGE_VERSION}" \
org.opencontainers.image.revision="${IMAGE_REVISION}" \
org.opencontainers.image.title="NGEN Forecast/Hindcast Manager" \
org.opencontainers.image.description="Docker image for the NGEN Forecast/Hindcast application" \
io.${IMAGE_NAMESPACE}.image.base.revision="${BASE_IMAGE_REVISION}" \
io.${IMAGE_NAMESPACE}.image.base.revision="${NGEN_IMAGE_REVISION}" \
io.${IMAGE_NAMESPACE}.ewts.org="${EWTS_ORG}" \
io.${IMAGE_NAMESPACE}.ewts.ref="${EWTS_REF}" \
io.${IMAGE_NAMESPACE}.ewts.revision="${EWTS_REVISION}" \
io.${IMAGE_NAMESPACE}.msw.mgr.org="${MSW_MGR_ORG}" \
io.${IMAGE_NAMESPACE}.msw.mgr.ref="${MSW_MGR_REF}" \
io.${IMAGE_NAMESPACE}.msw.mgr.revision="${MSW_MGR_REVISION}"
Expand Down
Loading