diff --git a/.gitea/workflows/build-and-publish.yaml b/.gitea/workflows/build-and-publish.yaml index 1c8cbfd..28ead19 100644 --- a/.gitea/workflows/build-and-publish.yaml +++ b/.gitea/workflows/build-and-publish.yaml @@ -8,40 +8,8 @@ on: - '*' jobs: - test: - name: Lint, Build & Unit Tests - runs-on: ubuntu-latest - if: github.ref != 'refs/heads/main' - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: '1.23' - - - name: Run Backend Lint & Unit Tests - run: | - go mod tidy - go vet ./... - go test -v ./... - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '22' - - - name: Typecheck & Build Frontend - run: | - cd frontend - npm install --no-audit --no-fund - npx tsc --noEmit - npm run build - - build-docker: - name: Build & Push Docker Image - needs: test + build-test-push: + name: Build, Test in Container & Push runs-on: ubuntu-latest if: github.ref != 'refs/heads/main' steps: @@ -69,10 +37,7 @@ jobs: echo "image=${IMAGE_NAME}" >> "$GITHUB_OUTPUT" echo "tag=${DOCKER_TAG}" >> "$GITHUB_OUTPUT" echo "actor=${ACTOR_NAME}" >> "$GITHUB_OUTPUT" - echo "Building Docker image: ${IMAGE_NAME}:${DOCKER_TAG} (Registry: ${REGISTRY_HOST}, Actor: ${ACTOR_NAME})" - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + echo "Target Docker image: ${IMAGE_NAME}:${DOCKER_TAG} (Registry: ${REGISTRY_HOST}, Actor: ${ACTOR_NAME})" - name: Login to Gitea Container Registry uses: docker/login-action@v3 @@ -81,11 +46,10 @@ jobs: username: ${{ steps.prep.outputs.actor }} password: ${{ secrets.PUSH_TOKEN || secrets.GITEA_TOKEN || github.token }} - - name: Build and Push Multi-Arch Image - uses: docker/build-push-action@v6 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: | - ${{ steps.prep.outputs.image }}:${{ steps.prep.outputs.tag }} + - name: Build & Run All Checks in Container, then Push + run: | + FULL_IMAGE="${{ steps.prep.outputs.image }}:${{ steps.prep.outputs.tag }}" + echo "🚀 Building container & executing Frontend/Backend checks inside image: ${FULL_IMAGE}" + DOCKER_BUILDKIT=1 docker build --progress=plain -t "${FULL_IMAGE}" . + echo "✅ All checks & tests passed inside container! Pushing ${FULL_IMAGE}..." + docker push "${FULL_IMAGE}" diff --git a/Dockerfile b/Dockerfile index 4730f78..4f9bfe4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,29 +1,47 @@ # ============================================================================== -# Stage 1: Frontend Build (Runs natively on $BUILDPLATFORM — zero QEMU overhead) +# Stage 1: Frontend Check & Build (Cached npm dependencies layer) # ============================================================================== -FROM --platform=$BUILDPLATFORM node:22-alpine AS frontend-builder +FROM node:22-alpine AS frontend-builder WORKDIR /app/frontend COPY frontend/package*.json ./ RUN npm install --no-audit --no-fund COPY frontend/ ./ -RUN npm run build +RUN npx tsc --noEmit && npm run build # ============================================================================== -# Stage 2: Go Backend Build (Native Cross-Compile via TARGETOS / TARGETARCH) +# Stage 2: Go Backend Lint, Unit Tests & Binary Build (Cached Go module & sqlite layer) # ============================================================================== -FROM --platform=$BUILDPLATFORM golang:1.23-alpine AS backend-builder -ARG TARGETOS=linux -ARG TARGETARCH=amd64 +FROM golang:1.23-alpine AS backend-builder WORKDIR /app RUN apk add --no-cache git ca-certificates + +# Cache Go modules & pre-compile heavy dependencies (modernc.org/sqlite) in a dedicated layer COPY go.mod ./ +RUN go get github.com/go-chi/chi/v5@v5.2.0 \ + github.com/go-chi/cors@v1.2.1 \ + github.com/golang-jwt/jwt/v5@v5.2.1 \ + github.com/gorilla/websocket@v1.5.3 \ + golang.org/x/crypto/bcrypt@v0.31.0 \ + modernc.org/sqlite@v1.34.4 && \ + CGO_ENABLED=0 go build \ + modernc.org/sqlite \ + github.com/go-chi/chi/v5 \ + github.com/go-chi/cors \ + github.com/golang-jwt/jwt/v5 \ + github.com/gorilla/websocket \ + golang.org/x/crypto/bcrypt + +# Copy application source & compiled frontend SPA assets for go:embed COPY cmd/ ./cmd/ COPY internal/ ./internal/ COPY migrations/ ./migrations/ -# Copy compiled frontend SPA assets into cmd/server/dist for go:embed COPY --from=frontend-builder /app/frontend/dist ./cmd/server/dist -RUN go mod tidy -RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-s -w" -o /app/mindsift ./cmd/server + +# Run go mod tidy, static analysis (go vet), unit tests (go test), and compile final binary +RUN go mod tidy && \ + CGO_ENABLED=0 go vet ./... && \ + CGO_ENABLED=0 go test -v ./... && \ + CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/mindsift ./cmd/server # ============================================================================== # Stage 3: Minimal Production Runtime (< 35 MB)