147 lines
4.4 KiB
Bash
147 lines
4.4 KiB
Bash
#!/usr/bin/env bash
|
|
# Build hardened images for the core services using the shared template/matrix.
|
|
# The default path publishes .NET services locally and builds runtime-only
|
|
# images from small temporary contexts to avoid repeatedly sending the full
|
|
# monorepo into Docker.
|
|
set -uo pipefail
|
|
|
|
FAILED=()
|
|
SUCCEEDED=()
|
|
|
|
ROOT=${ROOT:-"$(git rev-parse --show-toplevel)"}
|
|
MATRIX=${MATRIX:-"${ROOT}/devops/docker/services-matrix.env"}
|
|
REGISTRY=${REGISTRY:-"stellaops"}
|
|
TAG_SUFFIX=${TAG_SUFFIX:-"dev"}
|
|
SDK_IMAGE=${SDK_IMAGE:-"mcr.microsoft.com/dotnet/sdk:10.0-noble"}
|
|
RUNTIME_IMAGE=${RUNTIME_IMAGE:-"mcr.microsoft.com/dotnet/aspnet:10.0-noble"}
|
|
USE_LEGACY_REPO_CONTEXT=${USE_LEGACY_REPO_CONTEXT:-"false"}
|
|
PUBLISH_NO_RESTORE=${PUBLISH_NO_RESTORE:-"false"}
|
|
SERVICES=${SERVICES:-""}
|
|
FAST_CONTEXT_ROOT=${FAST_CONTEXT_ROOT:-"${TMPDIR:-/tmp}/stellaops-fast-images"}
|
|
RUNTIME_DOCKERFILE="${ROOT}/devops/docker/Dockerfile.hardened.runtime"
|
|
HEALTHCHECK_SCRIPT="${ROOT}/devops/docker/healthcheck.sh"
|
|
|
|
if [[ ! -f "${MATRIX}" ]]; then
|
|
echo "matrix file not found: ${MATRIX}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Building services from ${MATRIX} -> ${REGISTRY}/<service>:${TAG_SUFFIX}" >&2
|
|
if [[ -n "${SERVICES}" ]]; then
|
|
echo "Service filter: ${SERVICES}" >&2
|
|
fi
|
|
|
|
cleanup_context() {
|
|
local context_dir="${1:-}"
|
|
[[ -n "${context_dir}" && -d "${context_dir}" ]] && rm -rf "${context_dir}"
|
|
}
|
|
|
|
should_build_service() {
|
|
local service="$1"
|
|
[[ -z "${SERVICES}" ]] && return 0
|
|
|
|
IFS=',' read -r -a requested <<< "${SERVICES}"
|
|
for candidate in "${requested[@]}"; do
|
|
local trimmed="${candidate// /}"
|
|
[[ "${trimmed}" == "${service}" ]] && return 0
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
build_published_service_image() {
|
|
local service="$1"
|
|
local project="$2"
|
|
local binary="$3"
|
|
local port="$4"
|
|
local image="$5"
|
|
local context_dir="${FAST_CONTEXT_ROOT}/${service}"
|
|
|
|
cleanup_context "${context_dir}"
|
|
mkdir -p "${context_dir}/app"
|
|
|
|
local publish_args=(
|
|
publish "${ROOT}/${project}"
|
|
-c Release
|
|
-o "${context_dir}/app"
|
|
/p:UseAppHost=false
|
|
/p:PublishTrimmed=false
|
|
--nologo
|
|
)
|
|
|
|
if [[ "${PUBLISH_NO_RESTORE}" == "true" ]]; then
|
|
publish_args+=(--no-restore)
|
|
fi
|
|
|
|
dotnet "${publish_args[@]}" || {
|
|
cleanup_context "${context_dir}"
|
|
return 1
|
|
}
|
|
|
|
cp "${RUNTIME_DOCKERFILE}" "${context_dir}/Dockerfile"
|
|
cp "${HEALTHCHECK_SCRIPT}" "${context_dir}/healthcheck.sh"
|
|
|
|
docker build \
|
|
-f "${context_dir}/Dockerfile" "${context_dir}" \
|
|
--build-arg "RUNTIME_IMAGE=${RUNTIME_IMAGE}" \
|
|
--build-arg "APP_BINARY=${binary}" \
|
|
--build-arg "APP_PORT=${port}" \
|
|
-t "${image}"
|
|
local build_status=$?
|
|
cleanup_context "${context_dir}"
|
|
return ${build_status}
|
|
}
|
|
|
|
while IFS='|' read -r service dockerfile project binary port; do
|
|
[[ -z "${service}" || "${service}" =~ ^# ]] && continue
|
|
should_build_service "${service}" || continue
|
|
|
|
image="${REGISTRY}/${service}:${TAG_SUFFIX}"
|
|
df_path="${ROOT}/${dockerfile}"
|
|
if [[ ! -f "${df_path}" ]]; then
|
|
echo "skipping ${service}: dockerfile missing (${df_path})" >&2
|
|
continue
|
|
fi
|
|
|
|
if [[ "${dockerfile}" == *"Dockerfile.console"* ]]; then
|
|
echo "[console] ${service} -> ${image}" >&2
|
|
docker build \
|
|
-f "${df_path}" "${ROOT}" \
|
|
--build-arg APP_DIR="${project}" \
|
|
--build-arg APP_PORT="${port}" \
|
|
-t "${image}"
|
|
elif [[ "${USE_LEGACY_REPO_CONTEXT}" != "true" && "${dockerfile}" == *"Dockerfile.hardened.template"* ]]; then
|
|
echo "[service fast] ${service} -> ${image}" >&2
|
|
build_published_service_image "${service}" "${project}" "${binary}" "${port}" "${image}"
|
|
else
|
|
echo "[service] ${service} -> ${image}" >&2
|
|
docker build \
|
|
-f "${df_path}" "${ROOT}" \
|
|
--build-arg SDK_IMAGE="${SDK_IMAGE}" \
|
|
--build-arg RUNTIME_IMAGE="${RUNTIME_IMAGE}" \
|
|
--build-arg APP_PROJECT="${project}" \
|
|
--build-arg APP_BINARY="${binary}" \
|
|
--build-arg APP_PORT="${port}" \
|
|
-t "${image}"
|
|
fi
|
|
|
|
if [[ $? -eq 0 ]]; then
|
|
SUCCEEDED+=("${service}")
|
|
else
|
|
FAILED+=("${service}")
|
|
echo "FAILED: ${service}" >&2
|
|
fi
|
|
done < "${MATRIX}"
|
|
|
|
echo "" >&2
|
|
echo "=== BUILD RESULTS ===" >&2
|
|
echo "Succeeded (${#SUCCEEDED[@]}): ${SUCCEEDED[*]:-none}" >&2
|
|
echo "Failed (${#FAILED[@]}): ${FAILED[*]:-none}" >&2
|
|
echo "" >&2
|
|
if [[ ${#FAILED[@]} -gt 0 ]]; then
|
|
echo "Some builds failed. Fix the issues and re-run." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Build complete. Remember to enforce readOnlyRootFilesystem at deploy time and run sbom_attest.sh (DOCKER-44-002)." >&2
|