diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index ae361f656..bd2019b43 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -37,31 +37,84 @@ jobs: - name: Cache cargo tools id: cache-tools - uses: actions/cache@v3 + uses: actions/cache@v4 with: - path: ~/.cargo/bin - key: ${{ runner.os }}-cargo-tools-cross-trunk + path: | + ~/.cargo/bin/cross + ~/.cargo/bin/trunk + ~/.cargo/bin/wasm-bindgen + key: ${{ runner.os }}-cargo-tools-${{ hashFiles('**/Cargo.lock') }}-v2 + restore-keys: | + ${{ runner.os }}-cargo-tools- - name: Install cross if: steps.cache-tools.outputs.cache-hit != 'true' run: cargo install cross --git https://github.com/cross-rs/cross - - name: Install trunk + - name: Install trunk and wasm-bindgen-cli if: steps.cache-tools.outputs.cache-hit != 'true' - run: cargo install trunk + run: | + cargo install trunk + cargo install wasm-bindgen-cli - - name: Cache cargo registry - uses: actions/cache@v3 + - name: Cache cargo registry and dependencies + id: cache-cargo-deps + uses: actions/cache@v4 with: path: | ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git/db target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + backend/target + frontend/target + shared/target + key: ${{ runner.os }}-cargo-deps-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('**/Cargo.toml') }} restore-keys: | - ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - ${{ runner.os }}-cargo- + ${{ runner.os }}-cargo-deps-${{ hashFiles('**/Cargo.lock') }}- + ${{ runner.os }}-cargo-deps- + + - name: Cache Docker buildx + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Cache frontend dist + id: cache-frontend + uses: actions/cache@v4 + with: + path: frontend/dist + key: ${{ runner.os }}-frontend-${{ hashFiles('frontend/**/*', 'shared/**/*') }}-${{ hashFiles('frontend/Cargo.toml', 'frontend/Trunk.toml') }} + restore-keys: | + ${{ runner.os }}-frontend- + + - name: Cache cross-compilation tools + uses: actions/cache@v4 + with: + path: | + ~/.cross + ~/.rustup/toolchains/*/lib/rustlib/x86_64-unknown-linux-musl + ~/.rustup/toolchains/*/lib/rustlib/aarch64-unknown-linux-musl + key: ${{ runner.os }}-cross-${{ hashFiles('**/Cargo.lock') }}-v2 + restore-keys: | + ${{ runner.os }}-cross- + + - name: Cache built resources + uses: actions/cache@v4 + with: + path: | + resources/*.ts + key: ${{ runner.os }}-resources-${{ hashFiles('resources/*.jpg') }}-v1 + restore-keys: | + ${{ runner.os }}-resources- + + - name: Install ffmpeg + run: | + sudo apt-get update + sudo apt-get install -y ffmpeg - name: Extract branch name shell: bash @@ -77,4 +130,14 @@ jobs: run: chmod +x ./bin/build_docker.sh - name: Build and push Docker images + env: + FRONTEND_CACHE_HIT: ${{ steps.cache-frontend.outputs.cache-hit }} + CARGO_DEPS_CACHE_HIT: ${{ steps.cache-cargo-deps.outputs.cache-hit }} + BUILDX_CACHE_FROM: type=local,src=/tmp/.buildx-cache + BUILDX_CACHE_TO: type=local,dest=/tmp/.buildx-cache-new,mode=max run: ./bin/build_docker.sh ${{ steps.extract_branch.outputs.branch }} + + - name: Move buildx cache + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache || true diff --git a/bin/build_docker.sh b/bin/build_docker.sh index a9368caf5..67ddfc22e 100644 --- a/bin/build_docker.sh +++ b/bin/build_docker.sh @@ -75,21 +75,38 @@ fi echo "๐Ÿ“ฆ Version: ${VERSION}" -# Build resources if needed -if [ ! -f "${BIN_DIR}/build_resources.sh" ]; then +# Build resources if needed (check if resources are already built) +# Note: Docker build handles resource creation with its own ffmpeg container +RESOURCES_BUILT=true +for resource in "channel_unavailable.ts" "user_connections_exhausted.ts" "provider_connections_exhausted.ts" "user_account_expired.ts"; do + if [ ! -f "${RESOURCES_DIR}/${resource}" ]; then + RESOURCES_BUILT=false + break + fi +done + +if [ "$RESOURCES_BUILT" = "false" ] && [ -f "${BIN_DIR}/build_resources.sh" ]; then echo "๐Ÿ› ๏ธ Building resources..." - "${BIN_DIR}/build_resources.sh" + if ! "${BIN_DIR}/build_resources.sh"; then + echo "โš ๏ธ Resource building failed, but Docker build will handle resource creation" + fi +elif [ "$RESOURCES_BUILT" = "true" ]; then + echo "๐Ÿ› ๏ธ Resources already built, skipping..." fi -# Build frontend -echo "๐ŸŽจ Building frontend..." -rm -rf "${FRONTEND_BUILD_DIR}" -cd "${FRONTEND_DIR}" && env RUSTFLAGS="--remap-path-prefix $HOME=~" trunk build --release +# Build frontend (skip if cached) +if [ "${FRONTEND_CACHE_HIT:-false}" = "true" ] && [ -d "${FRONTEND_BUILD_DIR}" ]; then + echo "๐ŸŽจ Frontend build found in cache, skipping build..." +else + echo "๐ŸŽจ Building frontend..." + rm -rf "${FRONTEND_BUILD_DIR}" + cd "${FRONTEND_DIR}" && env RUSTFLAGS="--remap-path-prefix $HOME=~" trunk build --release -# Check if the frontend build directory exists -if [ ! -d "${FRONTEND_BUILD_DIR}" ]; then - echo "๐Ÿงจ Error: Frontend build directory '${FRONTEND_BUILD_DIR}' does not exist" - exit 1 + # Check if the frontend build directory exists + if [ ! -d "${FRONTEND_BUILD_DIR}" ]; then + echo "๐Ÿงจ Error: Frontend build directory '${FRONTEND_BUILD_DIR}' does not exist" + exit 1 + fi fi cd "$WORKING_DIR" @@ -101,8 +118,15 @@ for PLATFORM in "${!ARCHITECTURES[@]}"; do echo "๐Ÿ”จ Building binary for architecture: $ARCHITECTURE" - cargo clean || true - env RUSTFLAGS="--remap-path-prefix $HOME=~" cross build -p tuliprox --release --target "$ARCHITECTURE" + # Don't clean if we have cached dependencies + if [ -z "${CARGO_DEPS_CACHE_HIT:-}" ]; then + cargo clean || true + fi + + # Use incremental compilation and enable cache-friendly flags + env RUSTFLAGS="--remap-path-prefix $HOME=~ -C incremental=/tmp/rust-incremental-${ARCHITECTURE}" \ + CARGO_INCREMENTAL=1 \ + cross build -p tuliprox --release --target "$ARCHITECTURE" BINARY_PATH="${WORKING_DIR}/target/${ARCHITECTURE}/release/tuliprox" if [ ! -f "$BINARY_PATH" ]; then @@ -136,17 +160,28 @@ for IMAGE_NAME in "${!MULTI_PLATFORM_IMAGES[@]}"; do echo "๐ŸŽฏ Building multi-platform image: ${IMAGE_NAME} with target ${BUILD_TARGET}" - # Build and push multi-platform image directly + # Prepare tags based on branch + DOCKER_TAGS="" + if [ "$BRANCH" = "master" ]; then + # For master branch: create both version and latest tags + DOCKER_TAGS="-t ghcr.io/euzu/${IMAGE_NAME}:${VERSION} -t ghcr.io/euzu/${IMAGE_NAME}:${TAG_SUFFIX}" + BUILT_IMAGES+=("ghcr.io/euzu/${IMAGE_NAME}:${VERSION}") + BUILT_IMAGES+=("ghcr.io/euzu/${IMAGE_NAME}:${TAG_SUFFIX}") + elif [ "$BRANCH" = "develop" ]; then + # For develop branch: create only dev tag (no version tag) + DOCKER_TAGS="-t ghcr.io/euzu/${IMAGE_NAME}:${TAG_SUFFIX}" + BUILT_IMAGES+=("ghcr.io/euzu/${IMAGE_NAME}:${TAG_SUFFIX}") + fi + + # Build and push multi-platform image directly with cache docker buildx build -f Dockerfile-manual \ - -t "ghcr.io/euzu/${IMAGE_NAME}:${VERSION}" \ - -t "ghcr.io/euzu/${IMAGE_NAME}:${TAG_SUFFIX}" \ + ${DOCKER_TAGS} \ --target "$BUILD_TARGET" \ --platform "linux/amd64,linux/arm64" \ + --cache-from "${BUILDX_CACHE_FROM:-}" \ + --cache-to "${BUILDX_CACHE_TO:-}" \ --push \ . - - BUILT_IMAGES+=("ghcr.io/euzu/${IMAGE_NAME}:${VERSION}") - BUILT_IMAGES+=("ghcr.io/euzu/${IMAGE_NAME}:${TAG_SUFFIX}") done # Clean up Docker context diff --git a/docker/Dockerfile b/docker/Dockerfile index 4254aba6a..900627256 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -23,13 +23,23 @@ RUN rustup update && rustup target add $RUST_TARGET # Set Rust compiler flags for better optimization and reproducibility ENV RUSTFLAGS='--remap-path-prefix $HOME=~ -C target-feature=+crt-static' -# Copy only Cargo.toml and Cargo.lock for dependency caching +# Copy dependency files first for better layer caching WORKDIR /src COPY Cargo.toml Cargo.lock ./ +COPY backend/Cargo.toml ./backend/ +COPY frontend/Cargo.toml ./frontend/ +COPY shared/Cargo.toml ./shared/ -# Create a dummy src/main.rs to build dependencies only -RUN mkdir src && echo "fn main() {}" > src/main.rs +# Create dummy source files to build dependencies only +RUN mkdir -p src backend/src frontend/src shared/src && \ + echo "fn main() {}" > src/main.rs && \ + echo "fn main() {}" > backend/src/main.rs && \ + echo "fn main() {}" > frontend/src/main.rs && \ + echo "pub fn dummy() {}" > shared/src/lib.rs + +# Build dependencies (this layer will be cached unless dependencies change) RUN cargo build -p tuliprox --target $RUST_TARGET --release || true +RUN cargo build -p shared --target $RUST_TARGET --release || true # Now copy the actual source code and build the project COPY . . @@ -54,8 +64,22 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ RUN rustup target add wasm32-unknown-unknown RUN cargo install --locked trunk wasm-bindgen-cli +# Copy dependency files first for better layer caching +COPY Cargo.toml Cargo.lock ./ +COPY frontend/Cargo.toml frontend/Trunk.toml ./frontend/ +COPY shared/Cargo.toml ./shared/ + +# Create dummy source files +RUN mkdir -p frontend/src shared/src && \ + echo "fn main() {}" > frontend/src/main.rs && \ + echo "pub fn dummy() {}" > shared/src/lib.rs + +# Pre-build dependencies +WORKDIR /src/frontend +RUN cargo build --target wasm32-unknown-unknown --release || true # Now copy the actual source code and build the project +WORKDIR /src COPY . . WORKDIR /src/frontend