diff --git a/.github/workflows/engine_build.yaml b/.github/workflows/engine_build.yaml new file mode 100644 index 0000000..af950c1 --- /dev/null +++ b/.github/workflows/engine_build.yaml @@ -0,0 +1,143 @@ +name: 🏗️ Build Engine Optimized + +on: + push: + paths: + - "engine/**" + - ".github/workflows/engine_build.yaml" + +env: + REGISTRY: ghcr.io + IMAGE_NAME: v3xlabs/v3x-property/engine + BINARY_NAME: v3x-property-engine + +jobs: + deploy: + runs-on: ubuntu-20.04 + permissions: + contents: read + packages: write + attestations: write + id-token: write + env: + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" + services: + # Label used to access the service container + postgres: + # Docker Hub image + image: postgres:17 + # Provide the password for postgres + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: property + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust + run: rustup toolchain install stable --profile minimal --no-self-update + + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + with: + workspaces: engine + + - name: Configure sccache + uses: actions/github-script@v7 + with: + script: | + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.6 + with: + version: "v0.8.2" + + - name: Create and populate .env + working-directory: ./engine + run: | + # Copy the example env file + cp .env.example .env + + # Replace the postgres url with the service container name + sed -i 's|DATABASE_URL=.*|DATABASE_URL=postgres://postgres:postgres@postgres:5432/property|g' .env + + - name: Run DB migrations + working-directory: ./engine + run: | + cargo install sqlx-cli --no-default-features --features native-tls,postgres \ + && sqlx migrate run + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + type=edge + type=sha + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Rust binary + working-directory: ./engine + env: + BINARY_NAME: ${{ env.BINARY_NAME }} + run: | + cargo build --target x86_64-unknown-linux-musl --release \ + && echo "path=$(target/x86_64-unknown-linux-musl/release/${BINARY_NAME})" >> $GITHUB_OUTPUT + + # Debug log the binary path + - name: Debug log binary path + run: | + echo "Binary path: ${{ steps.binary.outputs.path }}" + + # Add the built binary as an artifact + - name: Add built binary as artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.BINARY_NAME }} + path: ${{ steps.binary.outputs.path }} + + - name: Build docker image using github actions + uses: docker/build-push-action@v6 + id: push + with: + context: ./engine + file: ./engine/.build/Dockerfile.scratch + # Metadata + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + annotations: ${{ steps.meta.outputs.annotations }} + # Variables + build-args: | + BINARY_PATH=${{ steps.binary.outputs.path }} + # Testing new build setup + push: false + + # Testing new build setup + # - name: Generate artifact attestation + # uses: actions/attest-build-provenance@v1 + # with: + # subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + # subject-digest: ${{ steps.push.outputs.digest }} + # push-to-registry: true diff --git a/engine/.build/Dockerfile.scratch b/engine/.build/Dockerfile.scratch new file mode 100644 index 0000000..8c979c8 --- /dev/null +++ b/engine/.build/Dockerfile.scratch @@ -0,0 +1,40 @@ +# ===================================== +# Stage 1: User Setup +# Creates a non-root user for running the application securely +# ===================================== +FROM alpine:latest as usersetup +RUN addgroup -g 10001 -S dockergrp && \ + adduser -u 10001 -S dockeruser -G dockergrp + +# ===================================== +# Stage 2: Final Image +# Minimal scratch-based image containing only the essential components +# ===================================== +FROM scratch + +# Security: Copy user information and switch to non-root user +COPY --from=usersetup /etc/passwd /etc/passwd +USER dockeruser + +# Application Configuration +# ----------------------- +# BINARY_NAME: Name of the executable (default: v3x-property-engine) +# RUST_LOG: Logging configuration (default: error,{BINARY_NAME}=info) +ARG BINARY_NAME_DEFAULT=v3x-property-engine +ENV BINARY_NAME=$BINARY_NAME_DEFAULT +ENV RUST_LOG="error,$BINARY_NAME=info" + +# Binary Installation +# ----------------- +# BINARY_PATH: Path to the pre-compiled binary from GitHub Actions +ARG BINARY_PATH +COPY ${BINARY_PATH} /${BINARY_NAME} + +# Network Configuration +# ------------------- +EXPOSE 3000 + +# Application Startup +# ----------------- +# Using exec form of CMD as there is no shell in scratch image +CMD ["/$BINARY_NAME"]