#!/usr/bin/env bash set -euo pipefail repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" publish_workflow="$repo_root/.gitea/workflows/image_publish.yml" legacy_publish_workflow="$repo_root/.gitea/workflows/production_image_publish.yml" staging_deploy_workflow="$repo_root/.gitea/workflows/staging_image_deploy.yml" deploy_workflow="$repo_root/.gitea/workflows/production_image_deploy.yml" image_compose="$repo_root/deploy/templates/docker-compose.images.yaml" bundle_script="$repo_root/scripts/deploy/build_image_deploy_bundle.sh" remote_deploy_script="$repo_root/scripts/deploy/upload_and_run_image_deploy.sh" works_image_download_script="$repo_root/scripts/docker-image/download_works_drive.sh" fail() { echo "$1" >&2 exit 1 } [[ -f "$publish_workflow" ]] || fail "shared image publish workflow must exist." [[ ! -f "$legacy_publish_workflow" ]] || fail "production-only image publish workflow must be renamed to image_publish.yml." [[ -f "$staging_deploy_workflow" ]] || fail "staging image deploy workflow must exist." [[ -f "$deploy_workflow" ]] || fail "production image deploy workflow must exist." [[ -f "$image_compose" ]] || fail "image-based production compose template must exist." [[ -f "$bundle_script" ]] || fail "shared image deployment bundle script must exist." [[ -f "$remote_deploy_script" ]] || fail "shared image remote deploy script must exist." [[ -f "$works_image_download_script" ]] || fail "shared WORKS Drive image download script must exist." grep -Fq "name: Publish Baron SSO Images" "$publish_workflow" \ || fail "publish workflow must have the shared stage/production name." grep -Fq "workflow_dispatch:" "$publish_workflow" \ || fail "publish workflow must be manually dispatchable." if grep -Fq "source_ref:" "$publish_workflow"; then fail "publish workflow must not accept an arbitrary source_ref; production images must be built from dev." fi grep -Fq "ref: dev" "$publish_workflow" \ || fail "publish workflow must checkout the dev branch." grep -Fq "version_prefix:" "$publish_workflow" \ || fail "publish workflow must accept a version prefix." grep -Fq 'git rev-parse --short=4 HEAD' "$publish_workflow" \ || fail "publish workflow must derive the final image tag from the checked-out commit hash." grep -Fq 'image_tag="${VERSION_PREFIX}.${short_sha}"' "$publish_workflow" \ || fail "publish workflow must append the 4-character commit hash as the last version segment." grep -Fq "steps.version.outputs.image_tag" "$publish_workflow" \ || fail "publish workflow must use the computed image tag for built image archives." grep -Fq "Upload built images to WORKS Drive archive" "$publish_workflow" \ || fail "publish workflow must archive locally built images to WORKS Drive." grep -Fq "scripts/docker-image/upload_works_drive.sh" "$publish_workflow" \ || fail "publish workflow must use the shared WORKS Drive image archive script." grep -Fq "docker/build-push-action@v5" "$publish_workflow" \ || fail "publish workflow must build images." grep -Fq "load: true" "$publish_workflow" \ || fail "publish workflow must load built images into the local Docker daemon for WORKS archive upload." for image in backend userfront adminfront devfront orgfront; do grep -Fq "baron_sso/${image}:" "$publish_workflow" \ || fail "publish workflow must build ${image} image." done grep -Fq "WORKS_DRIVE_ACCESS_TOKEN_INPUT: \${{ secrets.WORKS_DRIVE_ACCESS_TOKEN }}" "$publish_workflow" \ || fail "publish workflow must support direct WORKS Drive access token auth." grep -Fq "WORKS_DRIVE_OAUTH_CLIENT_SECRET: \${{ secrets.WORKS_OAUTH_CLIENT_SECRET }}" "$publish_workflow" \ || fail "publish workflow must use the Gitea-compatible WORKS OAuth client secret name." grep -Fq "WORKS_DRIVE_OAUTH_REFRESH_TOKEN: \${{ secrets.WORKS_DRIVE_REFRESH_TOKEN }}" "$publish_workflow" \ || fail "publish workflow must support WORKS Drive refresh-token auth." grep -Fq "WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID: \${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID }}" "$publish_workflow" \ || fail "publish workflow must use the Docker-image-specific WORKS Drive ID variable." grep -Fq 'WORKS_DRIVE_SHARED_DRIVE_ID="${WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID}"' "$publish_workflow" \ || fail "publish workflow must map WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID into the shared upload script." grep -Fq "Resolve WORKS Drive access token" "$publish_workflow" \ || fail "publish workflow must resolve a short-lived WORKS Drive access token before uploads." grep -Fq "WORKS_DRIVE_ACCESS_TOKEN_INPUT" "$publish_workflow" \ || fail "publish workflow must avoid passing the long-lived access-token secret name into upload steps directly." grep -Fq "grant_type=refresh_token" "$publish_workflow" \ || fail "publish workflow must support issuing an access token from WORKS_DRIVE_OAUTH_REFRESH_TOKEN." grep -Fq "WORKS_DRIVE_ACCESS_TOKEN=" "$publish_workflow" \ || fail "publish workflow must export the resolved access token through GITHUB_ENV." grep -Fq "::add-mask::" "$publish_workflow" \ || fail "publish workflow must mask resolved WORKS tokens in logs." grep -Fq "rotated_refresh_token_file" "$publish_workflow" \ || fail "publish workflow must capture rotated refresh tokens when WORKS returns one." if grep -Eiq 'harbor|docker/login-action|push:[[:space:]]*true|docker pull|docker push|HARBOR_' "$publish_workflow"; then fail "shared image publish workflow must not depend on Harbor registry login/push/pull." fi grep -Fq "name: Deploy Baron SSO Staging Images" "$staging_deploy_workflow" \ || fail "staging deploy workflow must have the expected name." grep -Fq "image_tag:" "$staging_deploy_workflow" \ || fail "staging deploy workflow must accept the same immutable image tag as production." grep -Fq "IMAGE_TAG must look like vX.YYMM.ab12" "$bundle_script" \ || fail "shared bundle script must validate the commit-hash image tag format." grep -Fq "IMAGE_DEPLOY_ENV: stage" "$staging_deploy_workflow" \ || fail "staging deploy workflow must select the stage deployment environment." grep -Fq "deploy/templates/docker-compose.images.yaml" "$staging_deploy_workflow" \ || fail "staging deploy workflow must pass the image-based compose template through the shared bundle script." grep -Fq "scripts/deploy/build_image_deploy_bundle.sh" "$staging_deploy_workflow" \ || fail "staging deploy workflow must use the shared bundle script." grep -Fq "scripts/deploy/upload_and_run_image_deploy.sh" "$staging_deploy_workflow" \ || fail "staging deploy workflow must use the shared remote deploy script." grep -Fq "WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID: \${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID }}" "$staging_deploy_workflow" \ || fail "staging deploy workflow must pass the Docker-image-specific WORKS Drive ID variable." grep -Fq "name: Deploy Baron SSO Production Images" "$deploy_workflow" \ || fail "deploy workflow must have the expected name." grep -Fq "image_tag:" "$deploy_workflow" \ || fail "deploy workflow must accept an image tag." grep -Fq "v1.2606.ab12" "$deploy_workflow" \ || fail "deploy workflow must document the commit-hash image tag format with an example." grep -Fq "IMAGE_TAG must look like vX.YYMM.ab12" "$bundle_script" \ || fail "deploy workflow must rely on the shared commit-hash image tag validation." grep -Fq "IMAGE_DEPLOY_ENV: production" "$deploy_workflow" \ || fail "deploy workflow must select the production deployment environment." grep -Fq 'APP_ENV=${app_env}' "$bundle_script" \ || fail "shared bundle script must write APP_ENV from the selected deployment environment." grep -Fq "deploy/templates/docker-compose.images.yaml" "$deploy_workflow" \ || fail "deploy workflow must pass the image-based compose template through the shared bundle script." grep -Fq "scripts/deploy/build_image_deploy_bundle.sh" "$deploy_workflow" \ || fail "production deploy workflow must use the shared bundle script." grep -Fq "scripts/deploy/upload_and_run_image_deploy.sh" "$deploy_workflow" \ || fail "production deploy workflow must use the shared remote deploy script." grep -Fq "WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID: \${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID }}" "$deploy_workflow" \ || fail "production deploy workflow must pass the Docker-image-specific WORKS Drive ID variable." grep -Fq "Same image tag contract as staging" "$deploy_workflow" \ || fail "production deploy workflow must document that it uses the same image tag as staging." grep -Fq "TRAEFIK_PUBLIC_NETWORK=traefik-public" "$bundle_script" \ || fail "shared bundle script must write Traefik public network env." grep -Fq "scripts/docker-image/download_works_drive.sh" "$remote_deploy_script" \ || fail "shared remote deploy script must load requested image archives from WORKS Drive before running." grep -Fq "docker load" "$works_image_download_script" \ || fail "WORKS Drive image download script must load downloaded archives into Docker." grep -Fq 'baron-sso/${IMAGE_TAG}/${image}.${IMAGE_TAG}.tar.zst' "$works_image_download_script" \ || fail "WORKS Drive image download script must document the normalized archive path." grep -Fq "docker compose --env-file .env -f docker-compose.yml up -d" "$remote_deploy_script" \ || fail "shared remote deploy script must start the stack after pulling images." if grep -Eiq 'harbor|docker login|docker compose --env-file .env -f docker-compose.yml pull|HARBOR_' "$staging_deploy_workflow" "$deploy_workflow" "$remote_deploy_script"; then fail "image deploy workflows/scripts must not depend on Harbor registry login or compose pull." fi if grep -Eq 'docker (build|commit)' "$staging_deploy_workflow" "$deploy_workflow"; then fail "staging/production deploy workflows must never build or commit images remotely." fi if grep -Eq '^[[:space:]]+build:' "$image_compose"; then fail "image-based production compose template must not contain build sections." fi for image_var in BACKEND_IMAGE_NAME USERFRONT_IMAGE_NAME ADMINFRONT_IMAGE_NAME DEVFRONT_IMAGE_NAME ORGFRONT_IMAGE_NAME; do grep -Fq "\${${image_var}}:\${IMAGE_TAG}" "$image_compose" \ || fail "image compose must use ${image_var} with IMAGE_TAG." done grep -Fq "APP_ENV=\${APP_ENV:-production}" "$image_compose" \ || fail "image compose must run services with production APP_ENV default." grep -Fq "traefik_public:" "$image_compose" \ || fail "image compose must keep Traefik public network wiring." tmp_dir="$(mktemp -d)" trap 'rm -rf "$tmp_dir"' EXIT cp "$repo_root/deploy/templates/.env.template" "$tmp_dir/.env" cp "$image_compose" "$tmp_dir/docker-compose.yml" sed -i 's/{{INSTANCE_NAME}}/policy/g; s/{{PORT_PREFIX}}/26/g' "$tmp_dir/.env" IMAGE_TAG=v9.9999.ab12 \ BACKEND_IMAGE_NAME=registry.example/baron_sso/backend \ USERFRONT_IMAGE_NAME=registry.example/baron_sso/userfront \ ADMINFRONT_IMAGE_NAME=registry.example/baron_sso/adminfront \ DEVFRONT_IMAGE_NAME=registry.example/baron_sso/devfront \ ORGFRONT_IMAGE_NAME=registry.example/baron_sso/orgfront \ docker compose --env-file "$tmp_dir/.env" -f "$tmp_dir/docker-compose.yml" config >/dev/null