diff --git a/.github/workflows/release-docker-assets.yml b/.github/workflows/release-docker-assets.yml new file mode 100644 index 000000000..fc03a72c8 --- /dev/null +++ b/.github/workflows/release-docker-assets.yml @@ -0,0 +1,80 @@ +name: Release Docker / OCI for Assets + +on: + push: + branches: + - 'main' + paths: + - 'apps/ui-server-auth/**' + workflow_dispatch: + +jobs: + ghcr_build_assets: + name: Release Assets (${{ matrix.assets_name }}) + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + strategy: + matrix: + include: + - assets_name: ui-server-auth + build_directory: ./apps/server/public/ui-server-auth + build_command: | + pnpm -F @proj-airi/ui-server-auth run build + steps: + # Why? + # + # failed to build archive at `/home/runner/work/airi/airi/target/x86_64-unknown-linux-gnu/release/deps/libapp_lib.rlib`: + # No space left on device (os error 28) + - name: Free Disk Space + uses: jlumbroso/free-disk-space@main + + - uses: actions/checkout@v6 + # Turborepo + - name: Cache turbo build setup + uses: actions/cache@v5 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v6 + with: + node-version: lts/* + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: ${{ matrix.build_command }} + + - uses: docker/metadata-action@v5 + id: meta + with: + images: ghcr.io/${{ github.repository }}/${{ matrix.assets_name }} + flavor: | + latest=true + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }} + + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: docker/build-push-action@v6 + with: + context: ./apps/server/public/ui-server-auth + file: ./apps/ui-server-auth/Dockerfile + build-args: | + VITE_ENABLE_POSTHOG=true + platforms: linux/amd64,linux/arm64,linux/arm64/v8 + cache-from: type=gha + cache-to: type=gha,mode=max + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/apps/server/Dockerfile b/apps/server/Dockerfile index 903d136b7..387be940b 100644 --- a/apps/server/Dockerfile +++ b/apps/server/Dockerfile @@ -1,45 +1,19 @@ -# Stage 1: build ui-server-auth from source so the HTML's Vite `base` and -# the server's route prefix are guaranteed to come from the SAME commit. -# Pulling a prebuilt `ghcr.io/moeru-ai/airi/ui-server-auth:latest` image -# previously let the two drift (server route changed to `/auth/`, prebuilt -# HTML still at `/_ui/server-auth/` → every asset 404 in prod). -# -# Debian slim (glibc), not alpine: the workspace pulls in `canvas` via -# jsdom→vitest. Canvas only ships prebuilt binaries for glibc, not musl; -# on alpine `prebuild-install` falls through to `node-gyp rebuild` which -# fails without `python3 make g++ cairo-dev pango-dev ...`. Slim is ~30MB -# heavier than alpine but is throwaway — only stage 2 ships. -FROM node:24-slim AS ui-build - -WORKDIR /app - -RUN corepack enable - -COPY . . - -RUN pnpm install --frozen-lockfile - -RUN pnpm -F @proj-airi/ui-server-auth run build - -# Stage 2: server runtime. Vite emits the UI dist into -# /app/apps/server/public/ui-server-auth via its `outDir`, so we COPY from -# that exact path in the builder. Server install can stay --ignore-scripts -# since the UI is already built and the server has no native deps that -# need postinstall. FROM node:24-alpine WORKDIR /app RUN corepack enable + COPY pnpm-lock.yaml pnpm-workspace.yaml package.json tsconfig.json ./ COPY patches/ ./patches/ COPY apps/server apps/server +COPY --from=ghcr.io/moeru-ai/airi/ui-server-auth:latest /app/airi/projects/ui/ui-server-auth apps/server/public/ui-server-auth COPY packages/server-schema packages/server-schema COPY packages/server-sdk-shared packages/server-sdk-shared -COPY --from=ui-build /app/apps/server/public/ui-server-auth apps/server/public/ui-server-auth -RUN pnpm install --frozen-lockfile --ignore-scripts +RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \ + pnpm install --frozen-lockfile --ignore-scripts RUN pnpm -F @proj-airi/server-schema run build RUN pnpm -F @proj-airi/server-sdk-shared run build diff --git a/apps/server/production/railway/Dockerfile b/apps/server/production/railway/Dockerfile index 903d136b7..5cc76d694 100644 --- a/apps/server/production/railway/Dockerfile +++ b/apps/server/production/railway/Dockerfile @@ -1,31 +1,3 @@ -# Stage 1: build ui-server-auth from source so the HTML's Vite `base` and -# the server's route prefix are guaranteed to come from the SAME commit. -# Pulling a prebuilt `ghcr.io/moeru-ai/airi/ui-server-auth:latest` image -# previously let the two drift (server route changed to `/auth/`, prebuilt -# HTML still at `/_ui/server-auth/` → every asset 404 in prod). -# -# Debian slim (glibc), not alpine: the workspace pulls in `canvas` via -# jsdom→vitest. Canvas only ships prebuilt binaries for glibc, not musl; -# on alpine `prebuild-install` falls through to `node-gyp rebuild` which -# fails without `python3 make g++ cairo-dev pango-dev ...`. Slim is ~30MB -# heavier than alpine but is throwaway — only stage 2 ships. -FROM node:24-slim AS ui-build - -WORKDIR /app - -RUN corepack enable - -COPY . . - -RUN pnpm install --frozen-lockfile - -RUN pnpm -F @proj-airi/ui-server-auth run build - -# Stage 2: server runtime. Vite emits the UI dist into -# /app/apps/server/public/ui-server-auth via its `outDir`, so we COPY from -# that exact path in the builder. Server install can stay --ignore-scripts -# since the UI is already built and the server has no native deps that -# need postinstall. FROM node:24-alpine WORKDIR /app @@ -34,10 +6,10 @@ RUN corepack enable COPY pnpm-lock.yaml pnpm-workspace.yaml package.json tsconfig.json ./ COPY patches/ ./patches/ +COPY --from=ghcr.io/moeru-ai/airi/ui-server-auth:latest /app/airi/projects/ui/ui-server-auth apps/server/public/ui-server-auth COPY apps/server apps/server COPY packages/server-schema packages/server-schema COPY packages/server-sdk-shared packages/server-sdk-shared -COPY --from=ui-build /app/apps/server/public/ui-server-auth apps/server/public/ui-server-auth RUN pnpm install --frozen-lockfile --ignore-scripts diff --git a/apps/server/railway.toml b/apps/server/railway.toml index 0d16b927e..28975201d 100644 --- a/apps/server/railway.toml +++ b/apps/server/railway.toml @@ -3,7 +3,6 @@ builder = "DOCKERFILE" dockerfilePath = "/apps/server/production/railway/Dockerfile" watchPatterns = [ "apps/server/**", - "apps/ui-server-auth/**", "packages/**", "pnpm-lock.yaml" ] diff --git a/apps/ui-server-auth/Dockerfile b/apps/ui-server-auth/Dockerfile new file mode 100644 index 000000000..3fe24b2bb --- /dev/null +++ b/apps/ui-server-auth/Dockerfile @@ -0,0 +1,4 @@ +FROM scratch + +WORKDIR /app/airi/projects/ui/ui-server-auth +COPY . /app/airi/projects/ui/ui-server-auth