Files
plex-playlist/scripts/build-cicd-local.sh
Cliff Hill 772c4cfab5
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
Fixing the optimization
Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
2025-10-31 12:48:40 -04:00

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 "$@"