#!/usr/bin/env bash # Build console container image with SBOM and optional attestations # Usage: ./build-console-image.sh [tag] [registry] # Example: ./build-console-image.sh 2025.10.0-edge ghcr.io/stellaops set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" TAG="${1:-$(date +%Y%m%dT%H%M%S)}" REGISTRY="${2:-registry.stella-ops.org/stellaops}" IMAGE_NAME="console" FULL_IMAGE="${REGISTRY}/${IMAGE_NAME}:${TAG}" # Freeze timestamps for reproducibility export SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH:-1704067200} echo "==> Building console image: ${FULL_IMAGE}" # Build using the existing Dockerfile.console docker build \ --file "${REPO_ROOT}/ops/devops/docker/Dockerfile.console" \ --build-arg APP_DIR=src/Web/StellaOps.Web \ --build-arg APP_PORT=8080 \ --tag "${FULL_IMAGE}" \ --label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ --label "org.opencontainers.image.revision=$(git -C "${REPO_ROOT}" rev-parse HEAD 2>/dev/null || echo 'unknown')" \ --label "org.opencontainers.image.source=https://github.com/stellaops/stellaops" \ --label "org.opencontainers.image.title=StellaOps Console" \ --label "org.opencontainers.image.description=StellaOps Angular Console (non-root nginx)" \ "${REPO_ROOT}" # Get digest DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "${FULL_IMAGE}" 2>/dev/null || echo "${FULL_IMAGE}") echo "==> Image built: ${FULL_IMAGE}" echo "==> Digest: ${DIGEST}" # Output metadata for CI mkdir -p "${SCRIPT_DIR}/../artifacts/console" cat > "${SCRIPT_DIR}/../artifacts/console/build-metadata.json" </dev/null || echo 'unknown')", "sourceDateEpoch": "${SOURCE_DATE_EPOCH}" } EOF echo "==> Build metadata written to ops/devops/artifacts/console/build-metadata.json" # Generate SBOM if syft is available if command -v syft &>/dev/null; then echo "==> Generating SBOM..." syft "${FULL_IMAGE}" -o spdx-json > "${SCRIPT_DIR}/../artifacts/console/console.spdx.json" syft "${FULL_IMAGE}" -o cyclonedx-json > "${SCRIPT_DIR}/../artifacts/console/console.cdx.json" echo "==> SBOMs written to ops/devops/artifacts/console/" else echo "==> Skipping SBOM generation (syft not found)" fi # Sign and attest if cosign is available and key is set if command -v cosign &>/dev/null; then if [[ -n "${COSIGN_KEY:-}" ]]; then echo "==> Signing image with cosign..." cosign sign --key "${COSIGN_KEY}" "${FULL_IMAGE}" if [[ -f "${SCRIPT_DIR}/../artifacts/console/console.spdx.json" ]]; then echo "==> Attesting SBOM..." cosign attest --predicate "${SCRIPT_DIR}/../artifacts/console/console.spdx.json" \ --type spdx --key "${COSIGN_KEY}" "${FULL_IMAGE}" fi echo "==> Image signed and attested" else echo "==> Skipping signing (COSIGN_KEY not set)" fi else echo "==> Skipping signing (cosign not found)" fi echo "==> Console image build complete" echo " Image: ${FULL_IMAGE}"