diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index be92a4b..6cda40b 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -3,8 +3,7 @@ name: Build & Publish Images on: push: branches: [ main, dev ] - tags: - - '*' + tags: [ '*' ] workflow_dispatch: env: @@ -13,46 +12,93 @@ env: IMAGE_FRONTEND: papercrate-dms/papercrate-frontend jobs: - build: + publish: runs-on: ubuntu-latest - strategy: - matrix: - component: [backend, frontend] - platform: [linux/amd64, linux/arm64] - permissions: contents: read packages: write - id-token: write steps: - name: Check out repository uses: actions/checkout@v4 - - name: Set component metadata - run: | - if [ "${{ matrix.component }}" = "backend" ]; then - echo "COMPONENT_CONTEXT=./backend" >> "$GITHUB_ENV" - echo "COMPONENT_IMAGE=${{ env.IMAGE_BACKEND }}" >> "$GITHUB_ENV" - else - echo "COMPONENT_CONTEXT=./frontend" >> "$GITHUB_ENV" - echo "COMPONENT_IMAGE=${{ env.IMAGE_FRONTEND }}" >> "$GITHUB_ENV" - fi - echo "COMPONENT_NAME=${{ matrix.component }}" >> "$GITHUB_ENV" + - name: Cache Cargo + uses: actions/cache@v3 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + backend/target + key: ${{ runner.os }}-cargo-${{ hashFiles('backend/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- - - name: Compute tags + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y pkg-config libssl-dev libpq-dev zig + + - name: Install Rust targets and tools + run: | + rustup target add aarch64-unknown-linux-gnu + cargo install cargo-zigbuild cross + + - name: Build backend binaries (amd64) + working-directory: backend + run: | + cargo build --release --bin backend --bin worker --bin webdav --bin admin + cargo install diesel_cli --no-default-features --features postgres --root target/diesel-amd64 + + - name: Build backend binaries (arm64) + working-directory: backend + run: | + cargo zigbuild --target aarch64-unknown-linux-gnu --release --bin backend --bin worker --bin webdav --bin admin + cross install diesel_cli --no-default-features --features postgres --target aarch64-unknown-linux-gnu --root target/diesel-arm64 + + - name: Prepare backend artifacts + run: | + set -eux + mkdir -p build-artifacts/backend/amd64 build-artifacts/backend/arm64 + cp backend/target/release/backend build-artifacts/backend/amd64/papercrate-backend + cp backend/target/release/worker build-artifacts/backend/amd64/papercrate-worker + cp backend/target/release/webdav build-artifacts/backend/amd64/papercrate-webdav + cp backend/target/release/admin build-artifacts/backend/amd64/papercrate-admin + cp backend/target/diesel-amd64/bin/diesel build-artifacts/backend/amd64/diesel + cp backend/target/aarch64-unknown-linux-gnu/release/backend build-artifacts/backend/arm64/papercrate-backend + cp backend/target/aarch64-unknown-linux-gnu/release/worker build-artifacts/backend/arm64/papercrate-worker + cp backend/target/aarch64-unknown-linux-gnu/release/webdav build-artifacts/backend/arm64/papercrate-webdav + cp backend/target/aarch64-unknown-linux-gnu/release/admin build-artifacts/backend/arm64/papercrate-admin + cp backend/target/diesel-arm64/bin/diesel build-artifacts/backend/arm64/diesel + chmod +x build-artifacts/backend/*/* + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + cache-dependency-path: frontend/package-lock.json + + - name: Install frontend dependencies + working-directory: frontend + run: npm ci --no-audit --no-fund + + - name: Build frontend + working-directory: frontend + run: npm run build + + - name: Compute image tags + id: tags env: GITHUB_SHA: ${{ github.sha }} GITHUB_REF: ${{ github.ref }} REGISTRY: ${{ env.REGISTRY }} - PLATFORM: ${{ matrix.platform }} run: | - ARCH=${PLATFORM#*/} - TAGS=("${GITHUB_SHA::7}") + SHORT_SHA=${GITHUB_SHA::7} + TAGS=("$SHORT_SHA") BRANCH=${GITHUB_REF##*/} if [ "$BRANCH" = "dev" ]; then - TAGS+=("${GITHUB_SHA::7}-dev") + TAGS+=("${SHORT_SHA}-dev") fi if [[ "$GITHUB_REF" == refs/tags/* ]]; then @@ -80,12 +126,16 @@ jobs: printf '%s\n' "$tag" done echo "EOF" - echo "IMAGE_TAGS<> "$GITHUB_ENV" - name: Set up QEMU @@ -98,100 +148,27 @@ jobs: uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + username: papercrate-dms + password: ${{ secrets.GHCR_PAT }} - - name: Build & push ${{ env.COMPONENT_NAME }} (${{ matrix.platform }}) + - name: Build & push backend image uses: docker/build-push-action@v5 with: - context: ${{ env.COMPONENT_CONTEXT }} + context: . + file: backend/Dockerfile push: true - platforms: ${{ matrix.platform }} - tags: ${{ env.IMAGE_TAGS }} + platforms: linux/amd64,linux/arm64 + tags: ${{ env.BACKEND_TAGS }} + cache-from: type=gha,scope=backend + cache-to: type=gha,scope=backend,mode=max - manifest: - runs-on: ubuntu-latest - needs: build - strategy: - matrix: - component: [backend, frontend] - - permissions: - contents: read - packages: write - - env: - REGISTRY: ghcr.io - ARCHES: amd64 arm64 - - steps: - - name: Set component metadata - run: | - if [ "${{ matrix.component }}" = "backend" ]; then - echo "COMPONENT_IMAGE=${{ env.IMAGE_BACKEND }}" >> "$GITHUB_ENV" - else - echo "COMPONENT_IMAGE=${{ env.IMAGE_FRONTEND }}" >> "$GITHUB_ENV" - fi - echo "COMPONENT_NAME=${{ matrix.component }}" >> "$GITHUB_ENV" - - - name: Compute base tags - env: - GITHUB_SHA: ${{ github.sha }} - GITHUB_REF: ${{ github.ref }} - run: | - TAGS=("${GITHUB_SHA::7}") - - BRANCH=${GITHUB_REF##*/} - if [ "$BRANCH" = "dev" ]; then - TAGS+=("${GITHUB_SHA::7}-dev") - fi - - if [[ "$GITHUB_REF" == refs/tags/* ]]; then - TAGS+=("${GITHUB_REF#refs/tags/}") - fi - - UNIQUE=() - for tag in "${TAGS[@]}"; do - [[ -z "$tag" ]] && continue - skip=false - for seen in "${UNIQUE[@]}"; do - if [ "$tag" = "$seen" ]; then - skip=true - break - fi - done - if [ "$skip" = false ]; then - UNIQUE+=("$tag") - fi - done - - { - echo "BASE_TAGS<> "$GITHUB_ENV" - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GHCR - uses: docker/login-action@v3 + - name: Build & push frontend image + uses: docker/build-push-action@v5 with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Create manifests for ${{ env.COMPONENT_NAME }} - run: | - while IFS= read -r tag; do - [ -n "$tag" ] || continue - args=() - for arch in $ARCHES; do - args+=("$REGISTRY/$COMPONENT_IMAGE:${tag}-${arch}") - done - docker buildx imagetools create \ - --tag "$REGISTRY/$COMPONENT_IMAGE:$tag" \ - "${args[@]}" - done <<< "$BASE_TAGS" + context: . + file: frontend/Dockerfile + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ env.FRONTEND_TAGS }} + cache-from: type=gha,scope=frontend + cache-to: type=gha,scope=frontend,mode=max diff --git a/backend/Dockerfile b/backend/Dockerfile index 262d52c..0a08478 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,33 +1,13 @@ # syntax=docker/dockerfile:1 -FROM rust:1-slim AS builder -WORKDIR /app - -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - build-essential \ - pkg-config \ - libssl-dev \ - libpq-dev \ - libjpeg-dev \ - libpng-dev \ - curl \ - && rm -rf /var/lib/apt/lists/* - -COPY Cargo.toml Cargo.lock ./ -COPY src ./src -COPY migrations ./migrations -COPY tests ./tests -COPY diesel.toml ./ - -RUN cargo build --release --bin backend --bin worker --bin webdav --bin admin -RUN cargo install diesel_cli --no-default-features --features postgres - +ARG TARGETARCH FROM debian:trixie-slim AS runtime +ARG TARGETARCH WORKDIR /app -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ ca-certificates \ curl \ libssl3 \ @@ -37,29 +17,33 @@ RUN apt-get update \ ocrmypdf \ tesseract-ocr \ ghostscript \ - qpdf \ - && rm -rf /var/lib/apt/lists/* \ - && mkdir -p /usr/local/lib \ - && curl -fsSL https://github.com/bblanchon/pdfium-binaries/releases/latest/download/pdfium-linux-arm64.tgz -o /tmp/pdfium.tgz \ - && mkdir -p /tmp/pdfium \ - && tar -xzf /tmp/pdfium.tgz -C /tmp/pdfium --strip-components=1 \ - && pdfium_so="$(find /tmp/pdfium -name libpdfium.so -type f | head -n1)" \ - && [ -n "${pdfium_so}" ] \ - && mv "${pdfium_so}" /usr/local/lib/libpdfium.so \ - && ldconfig \ - && rm -rf /tmp/pdfium.tgz /tmp/pdfium \ - && useradd --system --create-home --uid 10001 appuser + qpdf; \ + rm -rf /var/lib/apt/lists/*; \ + case "$TARGETARCH" in \ + amd64) pdf_pkg=pdfium-linux-x64.tgz ;; \ + arm64) pdf_pkg=pdfium-linux-arm64.tgz ;; \ + *) echo "Unsupported architecture: $TARGETARCH" >&2; exit 1 ;; \ + esac; \ + curl -fsSL "https://github.com/bblanchon/pdfium-binaries/releases/latest/download/${pdf_pkg}" -o /tmp/pdfium.tgz; \ + mkdir -p /tmp/pdfium; \ + tar -xzf /tmp/pdfium.tgz -C /tmp/pdfium --strip-components=1; \ + pdfium_so="$(find /tmp/pdfium -name libpdfium.so -type f | head -n1)"; \ + [ -n "$pdfium_so" ]; \ + mv "$pdfium_so" /usr/local/lib/libpdfium.so; \ + ldconfig; \ + rm -rf /tmp/pdfium /tmp/pdfium.tgz; \ + useradd --system --create-home --uid 10001 appuser + +COPY build-artifacts/backend/${TARGETARCH}/papercrate-backend /usr/local/bin/papercrate-backend +COPY build-artifacts/backend/${TARGETARCH}/papercrate-worker /usr/local/bin/papercrate-worker +COPY build-artifacts/backend/${TARGETARCH}/papercrate-webdav /usr/local/bin/papercrate-webdav +COPY build-artifacts/backend/${TARGETARCH}/papercrate-admin /usr/local/bin/papercrate-admin +COPY build-artifacts/backend/${TARGETARCH}/diesel /usr/local/bin/diesel -COPY --from=builder /app/target/release/backend /usr/local/bin/papercrate-backend -COPY --from=builder /app/target/release/worker /usr/local/bin/papercrate-worker -COPY --from=builder /app/target/release/webdav /usr/local/bin/papercrate-webdav -COPY --from=builder /app/target/release/admin /usr/local/bin/papercrate-admin -COPY --from=builder /usr/local/cargo/bin/diesel /usr/local/bin/diesel COPY migrations ./migrations COPY diesel.toml ./ ENV RUST_LOG=info USER appuser EXPOSE 3000 - ENTRYPOINT ["/usr/local/bin/papercrate-backend"] diff --git a/frontend/Dockerfile b/frontend/Dockerfile index b05cba1..6a4809e 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,18 +1,9 @@ # syntax=docker/dockerfile:1 -FROM node:20-alpine AS build -WORKDIR /app - -COPY package.json package-lock.json ./ -RUN npm ci --no-audit --no-fund - -COPY . . -RUN npm run build - FROM nginx:alpine WORKDIR /usr/share/nginx/html -COPY --from=build /app/dist ./ +COPY dist ./ ENV API_PROXY_PASS=""