Some checks failed
Tests / Build and Push CICD Base Image (push) Failing after 23m13s
Tests / Build and Push CICD Complete Image (push) Has been skipped
Tests / Trailing Whitespace Check (push) Has been skipped
Tests / Pyright Type Check (push) Has been skipped
Tests / End of File Check (push) Has been skipped
Tests / YAML Syntax Check (push) Has been skipped
Tests / TOML Syntax Check (push) Has been skipped
Tests / Mixed Line Ending Check (push) Has been skipped
Tests / TOML Formatting Check (push) Has been skipped
Tests / Ruff Linting (push) Has been skipped
Tests / Ruff Format Check (push) Has been skipped
Tests / Darglint Docstring Check (push) Has been skipped
Tests / No Docstring Types Check (push) Has been skipped
Tests / ESLint Check (push) Has been skipped
Tests / Prettier Format Check (push) Has been skipped
Tests / TypeScript Type Check (push) Has been skipped
Tests / TSDoc Lint Check (push) Has been skipped
Tests / Backend Tests (push) Has been skipped
Tests / Frontend Tests (push) Has been skipped
Tests / Backend Doctests (push) Has been skipped
Tests / End-to-End Tests (push) Has been skipped
Tests / Integration Tests (push) Has been skipped
Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
324 lines
9.8 KiB
Bash
Executable File
324 lines
9.8 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Build CICD Images Locally - Multi-Stage Build Script
|
|
# This script builds both the base and complete CICD images locally for testing
|
|
|
|
set -euo pipefail
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuration
|
|
BASE_IMAGE_TAG="cicd-base:local"
|
|
COMPLETE_IMAGE_TAG="cicd:local"
|
|
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
TEMP_SSH_KEY="/tmp/cicd_build_ssh_key"
|
|
|
|
# Functions
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
log_warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
cleanup() {
|
|
if [[ -f "$TEMP_SSH_KEY" ]]; then
|
|
rm -f "$TEMP_SSH_KEY"
|
|
log_info "Cleaned up temporary SSH key"
|
|
fi
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
|
|
show_usage() {
|
|
cat << EOF
|
|
Usage: $0 [OPTIONS]
|
|
|
|
Build CICD images locally for testing
|
|
|
|
Options:
|
|
-h, --help Show this help message
|
|
-b, --base-only Build only the base image
|
|
-c, --complete-only Build only the complete image (requires base image)
|
|
-f, --force Force rebuild even if images exist
|
|
--no-cache Build without using Docker layer cache
|
|
--ssh-key FILE Path to SSH private key (default: ~/.ssh/id_rsa)
|
|
--push Push images to registry after building
|
|
|
|
Examples:
|
|
$0 # Build both base and complete images
|
|
$0 --base-only # Build only base image
|
|
$0 --complete-only # Build only complete image
|
|
$0 --force --no-cache # Force rebuild with no cache
|
|
EOF
|
|
}
|
|
|
|
check_requirements() {
|
|
log_info "Checking requirements..."
|
|
|
|
# Check Docker
|
|
if ! command -v docker &> /dev/null; then
|
|
log_error "Docker is not installed or not in PATH"
|
|
exit 1
|
|
fi
|
|
|
|
# Check Docker BuildKit support
|
|
if ! docker buildx version &> /dev/null; then
|
|
log_error "Docker BuildKit is not available"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if we're in the right directory
|
|
if [[ ! -f "$PROJECT_DIR/Dockerfile.cicd-base" ]]; then
|
|
log_error "Dockerfile.cicd-base not found. Are you in the project directory?"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$PROJECT_DIR/Dockerfile.cicd" ]]; then
|
|
log_error "Dockerfile.cicd not found. Are you in the project directory?"
|
|
exit 1
|
|
fi
|
|
|
|
log_success "Requirements check passed"
|
|
}
|
|
|
|
build_base_image() {
|
|
log_info "Building CICD base image (includes Playwright browsers - may take longer)..."
|
|
log_warning "This will download ~400MB+ of browser binaries on first build"
|
|
|
|
local cache_flag=""
|
|
if [[ "$NO_CACHE" == "true" ]]; then
|
|
cache_flag="--no-cache"
|
|
fi
|
|
|
|
local start_time=$(date +%s)
|
|
|
|
# Calculate base Dockerfile hash for tagging
|
|
local base_hash=$(sha256sum "$PROJECT_DIR/Dockerfile.cicd-base" | cut -d' ' -f1 | head -c16)
|
|
log_info "Base Dockerfile hash: $base_hash"
|
|
|
|
# Build base image
|
|
log_info "Building base image with system dependencies and Playwright browsers..."
|
|
docker build -f "$PROJECT_DIR/Dockerfile.cicd-base" \
|
|
$cache_flag \
|
|
--build-arg BASE_IMAGE_VERSION="v1.0.0-local-$base_hash" \
|
|
-t "$BASE_IMAGE_TAG" \
|
|
-t "cicd-base:$base_hash" \
|
|
"$PROJECT_DIR"
|
|
|
|
local end_time=$(date +%s)
|
|
local duration=$((end_time - start_time))
|
|
|
|
log_success "Base image built successfully in ${duration}s"
|
|
log_info "Tagged as: $BASE_IMAGE_TAG and cicd-base:$base_hash"
|
|
|
|
# Show image size
|
|
local image_size=$(docker images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}" | grep "$BASE_IMAGE_TAG" | awk '{print $2}')
|
|
log_info "Base image size: $image_size (includes Playwright browsers)"
|
|
}build_complete_image() {
|
|
log_info "Building CICD complete image..."
|
|
|
|
# Check if base image exists
|
|
if ! docker image inspect "$BASE_IMAGE_TAG" &> /dev/null; then
|
|
log_error "Base image $BASE_IMAGE_TAG not found. Build it first with --base-only or run without --complete-only"
|
|
exit 1
|
|
fi
|
|
|
|
# Check for SSH key
|
|
if [[ ! -f "$SSH_KEY_PATH" ]]; then
|
|
log_error "SSH key not found at $SSH_KEY_PATH"
|
|
log_error "Specify path with --ssh-key or ensure ~/.ssh/id_rsa exists"
|
|
exit 1
|
|
fi
|
|
|
|
# Prepare SSH key for BuildKit secrets
|
|
cp "$SSH_KEY_PATH" "$TEMP_SSH_KEY"
|
|
chmod 600 "$TEMP_SSH_KEY"
|
|
|
|
local cache_flag=""
|
|
if [[ "$NO_CACHE" == "true" ]]; then
|
|
cache_flag="--no-cache"
|
|
fi
|
|
|
|
local start_time=$(date +%s)
|
|
local git_sha=$(git rev-parse HEAD 2>/dev/null || echo "unknown")
|
|
|
|
log_info "Using Git SHA: $git_sha"
|
|
log_info "Using SSH key: $SSH_KEY_PATH"
|
|
|
|
# Build complete image
|
|
DOCKER_BUILDKIT=1 docker build -f "$PROJECT_DIR/Dockerfile.cicd" \
|
|
$cache_flag \
|
|
--secret id=ssh_private_key,src="$TEMP_SSH_KEY" \
|
|
--build-arg GITHUB_SHA="$git_sha" \
|
|
--build-arg CICD_BASE_IMAGE="$BASE_IMAGE_TAG" \
|
|
-t "$COMPLETE_IMAGE_TAG" \
|
|
"$PROJECT_DIR"
|
|
|
|
local end_time=$(date +%s)
|
|
local duration=$((end_time - start_time))
|
|
|
|
log_success "Complete image built successfully in ${duration}s"
|
|
log_info "Tagged as: $COMPLETE_IMAGE_TAG"
|
|
|
|
# Show image size
|
|
local image_size=$(docker images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}" | grep "$COMPLETE_IMAGE_TAG" | awk '{print $2}')
|
|
log_info "Complete image size: $image_size"
|
|
}
|
|
|
|
push_images() {
|
|
log_warning "Push functionality not implemented yet"
|
|
log_info "To push manually:"
|
|
log_info " docker tag $BASE_IMAGE_TAG dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:latest"
|
|
log_info " docker tag $COMPLETE_IMAGE_TAG dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:latest"
|
|
log_info " docker push dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:latest"
|
|
log_info " docker push dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:latest"
|
|
}
|
|
|
|
test_images() {
|
|
log_info "Testing built images..."
|
|
|
|
if [[ "$BUILD_BASE" == "true" ]] && docker image inspect "$BASE_IMAGE_TAG" &> /dev/null; then
|
|
log_info "Testing base image..."
|
|
if docker run --rm "$BASE_IMAGE_TAG" python3.13 --version && \
|
|
docker run --rm "$BASE_IMAGE_TAG" node --version && \
|
|
docker run --rm "$BASE_IMAGE_TAG" yarn --version && \
|
|
docker run --rm "$BASE_IMAGE_TAG" uv --version && \
|
|
docker run --rm "$BASE_IMAGE_TAG" playwright --version; then
|
|
log_success "Base image tests passed (includes Playwright via npm)"
|
|
# Test that browsers are installed
|
|
if docker run --rm "$BASE_IMAGE_TAG" playwright install --dry-run chromium >/dev/null 2>&1; then
|
|
log_success "Playwright browsers verified in base image"
|
|
else
|
|
log_warning "Playwright browsers may not be fully installed in base image"
|
|
fi
|
|
else
|
|
log_error "Base image tests failed"
|
|
fi
|
|
fi
|
|
|
|
if [[ "$BUILD_COMPLETE" == "true" ]] && docker image inspect "$COMPLETE_IMAGE_TAG" &> /dev/null; then
|
|
log_info "Testing complete image..."
|
|
if docker run --rm "$COMPLETE_IMAGE_TAG" bash -c "cd /workspace/backend && uv run python --version" && \
|
|
docker run --rm "$COMPLETE_IMAGE_TAG" bash -c "cd /workspace/frontend && yarn --version"; then
|
|
log_success "Complete image tests passed"
|
|
else
|
|
log_warning "Complete image tests had issues (may be expected if frontend deps failed)"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Default options
|
|
BUILD_BASE="true"
|
|
BUILD_COMPLETE="true"
|
|
FORCE_BUILD="false"
|
|
NO_CACHE="false"
|
|
SSH_KEY_PATH="$HOME/.ssh/id_rsa"
|
|
PUSH_IMAGES="false"
|
|
|
|
# Parse command line arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-h|--help)
|
|
show_usage
|
|
exit 0
|
|
;;
|
|
-b|--base-only)
|
|
BUILD_BASE="true"
|
|
BUILD_COMPLETE="false"
|
|
shift
|
|
;;
|
|
-c|--complete-only)
|
|
BUILD_BASE="false"
|
|
BUILD_COMPLETE="true"
|
|
shift
|
|
;;
|
|
-f|--force)
|
|
FORCE_BUILD="true"
|
|
shift
|
|
;;
|
|
--no-cache)
|
|
NO_CACHE="true"
|
|
shift
|
|
;;
|
|
--ssh-key)
|
|
SSH_KEY_PATH="$2"
|
|
shift 2
|
|
;;
|
|
--push)
|
|
PUSH_IMAGES="true"
|
|
shift
|
|
;;
|
|
*)
|
|
log_error "Unknown option: $1"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Main execution
|
|
main() {
|
|
log_info "Starting CICD multi-stage build..."
|
|
log_info "Project directory: $PROJECT_DIR"
|
|
log_info "Build base: $BUILD_BASE"
|
|
log_info "Build complete: $BUILD_COMPLETE"
|
|
log_info "Force build: $FORCE_BUILD"
|
|
log_info "No cache: $NO_CACHE"
|
|
|
|
check_requirements
|
|
|
|
# Check if images already exist (unless forced)
|
|
if [[ "$FORCE_BUILD" == "false" ]]; then
|
|
if [[ "$BUILD_BASE" == "true" ]] && docker image inspect "$BASE_IMAGE_TAG" &> /dev/null; then
|
|
log_warning "Base image $BASE_IMAGE_TAG already exists. Use --force to rebuild."
|
|
BUILD_BASE="false"
|
|
fi
|
|
|
|
if [[ "$BUILD_COMPLETE" == "true" ]] && docker image inspect "$COMPLETE_IMAGE_TAG" &> /dev/null; then
|
|
log_warning "Complete image $COMPLETE_IMAGE_TAG already exists. Use --force to rebuild."
|
|
BUILD_COMPLETE="false"
|
|
fi
|
|
fi
|
|
|
|
# Build images
|
|
if [[ "$BUILD_BASE" == "true" ]]; then
|
|
build_base_image
|
|
fi
|
|
|
|
if [[ "$BUILD_COMPLETE" == "true" ]]; then
|
|
build_complete_image
|
|
fi
|
|
|
|
# Test images
|
|
test_images
|
|
|
|
# Push if requested
|
|
if [[ "$PUSH_IMAGES" == "true" ]]; then
|
|
push_images
|
|
fi
|
|
|
|
log_success "Build process completed!"
|
|
|
|
# Show final image listing
|
|
log_info "Built images:"
|
|
docker images | grep -E "(cicd|cicd-base)" | grep -E "(local|$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown'))"
|
|
}
|
|
|
|
# Run main function
|
|
main "$@"
|