Speed up scratch image builds with publish-first contexts
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build hardened images for the core services using the shared template/matrix (DOCKER-44-001)
|
||||
# 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=()
|
||||
|
||||
@@ -10,6 +14,12 @@ 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
|
||||
@@ -17,9 +27,75 @@ if [[ ! -f "${MATRIX}" ]]; then
|
||||
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
|
||||
@@ -28,13 +104,15 @@ while IFS='|' read -r service dockerfile project binary port; do
|
||||
fi
|
||||
|
||||
if [[ "${dockerfile}" == *"Dockerfile.console"* ]]; then
|
||||
# Angular console build uses its dedicated Dockerfile
|
||||
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 \
|
||||
@@ -53,7 +131,6 @@ while IFS='|' read -r service dockerfile project binary port; do
|
||||
FAILED+=("${service}")
|
||||
echo "FAILED: ${service}" >&2
|
||||
fi
|
||||
|
||||
done < "${MATRIX}"
|
||||
|
||||
echo "" >&2
|
||||
@@ -65,4 +142,5 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user