From b8d986cbd65662215b9be4b94ee894278120fe1a Mon Sep 17 00:00:00 2001 From: Amir Pourmand Date: Sat, 23 May 2026 06:09:15 +0330 Subject: [PATCH] fix(docker): fix Docker image `latest` tag and production runtime errors; add dev compose file, Codespace support, and semver release tagging (#7) (#4277) * fix: also tag Docker image as latest on main branch push Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/bbc0f66e-7488-4d9d-976b-434588b3faa8 * fix: production Dockerfile missing patches/.env.web; add compose.dev.yaml for dev hot-reload Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/e27bba4e-b8ea-4694-b44d-6308aa778d41 * fix: add devcontainer to init submodules; add clear Dockerfile error when submodules missing Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/16d24ae0-d2e8-4523-b47e-4969dd587c50 * simplify production-stage: COPY --from=build /app /app instead of selective copies Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/356fabe4-f944-48b6-a409-41640480d52f * fix: add CI=true to dev compose to prevent pnpm no-TTY abort Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/ddc30fef-2c76-452d-aad6-d688ca912a1a * fix: add semver Docker image version tags on release Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/18cbc3c6-ba92-4e63-bef9-93dcf6698c21 --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> --- .devcontainer/devcontainer.json | 6 ++++++ .github/workflows/docker-image.yml | 8 ++++++-- Dockerfile | 13 ++++--------- docker/README.md | 23 +++++++++++++---------- docker/compose.dev.yaml | 24 ++++++++++++++++++++++++ docker/compose.yaml | 8 -------- 6 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 docker/compose.dev.yaml diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..93207710 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,6 @@ +{ + "name": "Readest", + // Initialize only the submodules required for Docker builds. + // tauri/tauri-plugins are skipped here since they're only needed for desktop builds. + "postCreateCommand": "git submodule update --init packages/foliate-js packages/simplecc-wasm" +} diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index ea678714..d510dd53 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -135,8 +135,10 @@ jobs: images: ghcr.io/${{ github.repository_owner }}/readest tags: | type=raw,value=main,enable={{is_default_branch}} + type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable=${{ github.event_name == 'release' }} - type=raw,value=${{ github.event.release.tag_name }},enable=${{ github.event_name == 'release' }} + type=semver,pattern={{version}},enable=${{ github.event_name == 'release' }} + type=semver,pattern={{major}}.{{minor}},enable=${{ github.event_name == 'release' }} type=sha,prefix=sha- - name: Extract image metadata (GHCR + Docker Hub) @@ -149,8 +151,10 @@ jobs: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/readest tags: | type=raw,value=main,enable={{is_default_branch}} + type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable=${{ github.event_name == 'release' }} - type=raw,value=${{ github.event.release.tag_name }},enable=${{ github.event_name == 'release' }} + type=semver,pattern={{version}},enable=${{ github.event_name == 'release' }} + type=semver,pattern={{major}}.{{minor}},enable=${{ github.event_name == 'release' }} type=sha,prefix=sha- - name: Create manifest list and push (GHCR only) diff --git a/Dockerfile b/Dockerfile index 9e5dcb2e..c83788cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,9 @@ COPY apps/readest-app/package.json ./apps/readest-app/ COPY patches/ ./patches/ COPY packages/ ./packages/ RUN --mount=type=cache,id=pnpm,sharing=locked,target=/pnpm/store pnpm install --frozen-lockfile +RUN test -f packages/foliate-js/vendor/pdfjs/annotation_layer_builder.css \ + && test -d packages/simplecc-wasm/dist/web \ + || { printf '\nERROR: Required git submodules are not initialized in the source directory.\nEnsure submodules are initialized before running docker build.\nRun: git submodule update --init packages/foliate-js packages/simplecc-wasm\n\n'; exit 1; } RUN pnpm --filter @readest/readest-app setup-vendors FROM docker.io/library/node:24-slim@sha256:24dc26ef1e3c3690f27ebc4136c9c186c3133b25563ae4d7f0692e4d1fe5db0e AS development-stage @@ -50,15 +53,7 @@ ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable RUN corepack prepare pnpm@11.1.1 --activate WORKDIR /app -# Only copy what next start needs — omit source, Rust, tests, patches, etc. -COPY --from=build /app/package.json /app/package.json -COPY --from=build /app/pnpm-workspace.yaml /app/pnpm-workspace.yaml -COPY --from=build /app/node_modules /app/node_modules -COPY --from=build /app/apps/readest-app/package.json /app/apps/readest-app/package.json -COPY --from=build /app/apps/readest-app/next.config.mjs /app/apps/readest-app/next.config.mjs -COPY --from=build /app/apps/readest-app/node_modules /app/apps/readest-app/node_modules -COPY --from=build /app/apps/readest-app/.next /app/apps/readest-app/.next -COPY --from=build /app/apps/readest-app/public /app/apps/readest-app/public +COPY --from=build /app /app WORKDIR /app/apps/readest-app ENTRYPOINT ["pnpm", "start-web", "-H", "0.0.0.0"] EXPOSE 3000 diff --git a/docker/README.md b/docker/README.md index 020e0163..81b19fa2 100644 --- a/docker/README.md +++ b/docker/README.md @@ -63,13 +63,19 @@ replace `your-dockerhub-username` with the Docker Hub namespace that publishes y for official images, use the namespace configured for this repository's Docker Hub publishing secrets. published tags: -- `latest`: published from release events +- `latest`: rolling image from the default branch and from release events - `` (for example `v1.2.3`): published from release events - `main`: rolling image from the default branch - `sha-`: immutable commit tag ### Build locally instead of pulling +> **Prerequisites for local builds**: the `packages/foliate-js` and `packages/simplecc-wasm` git submodules must be initialized before building: +> ```bash +> git submodule update --init packages/foliate-js packages/simplecc-wasm +> ``` +> In GitHub Codespaces this is done automatically via `.devcontainer/devcontainer.json`. + ```bash cd docker docker compose -f compose.yaml -f compose.build.yaml up --build -d @@ -82,16 +88,13 @@ docker compose -f compose.yaml -f compose.build.yaml up --build -d ### Hot Reload (development) -to develop using the compose stack, use local builds (`compose.yaml` + `compose.build.yaml`) and set the build target on `client` to `development-stage` in `compose.build.yaml`, which runs the next.js dev server. to enable hot reload, uncomment the `volumes` block in the `client` service in `compose.yaml`: +> **Prerequisites**: submodules must be initialized (see above). -```yaml -volumes: - - ../:/app - - /app/node_modules - - /app/apps/readest-app/node_modules - - /app/apps/readest-app/public/vendor - - /app/apps/readest-app/.next - - /app/packages/foliate-js/node_modules +to develop using the compose stack, use `compose.dev.yaml` which sets the build target to `development-stage` (Next.js dev server) and mounts your local repo for hot reload: + +```bash +cd docker +docker compose -f compose.yaml -f compose.dev.yaml up --build -d ``` the first mount overlays your local repo into the container. the remaining anonymous volumes shadow the directories that were pre-built inside the image, so the container's installed deps and vendor assets are used instead of what's on your host. diff --git a/docker/compose.dev.yaml b/docker/compose.dev.yaml new file mode 100644 index 00000000..55230e7d --- /dev/null +++ b/docker/compose.dev.yaml @@ -0,0 +1,24 @@ +# Overlay on top of compose.yaml. +# Usage (from the docker/ directory): +# docker compose -f compose.yaml -f compose.dev.yaml up --build -d +services: + client: + build: + context: .. + target: development-stage + args: + NEXT_PUBLIC_APP_PLATFORM: web + environment: + # Required so pnpm does not abort when purging node_modules without a TTY + CI: "true" + # Mount the source code for hot reload. + # Anonymous volumes shadow pre-built dirs inside the image so the + # container's installed deps and vendor assets are used instead of + # what's on the host. + volumes: + - ../:/app + - /app/node_modules + - /app/apps/readest-app/node_modules + - /app/apps/readest-app/public/vendor + - /app/apps/readest-app/.next + - /app/packages/foliate-js/node_modules diff --git a/docker/compose.yaml b/docker/compose.yaml index 28c75d47..74d439d4 100644 --- a/docker/compose.yaml +++ b/docker/compose.yaml @@ -124,14 +124,6 @@ services: restart: unless-stopped ports: - "3000:3000" -# to enable hot reload, mount the source code as a volume, and artifacts as anon volumes -# volumes: -# - ../:/app -# - /app/node_modules -# - /app/apps/readest-app/node_modules -# - /app/apps/readest-app/public/vendor -# - /app/apps/readest-app/.next -# - /app/packages/foliate-js/node_modules environment: SUPABASE_URL: http://kong:8000 SUPABASE_PUBLIC_URL: http://${HOST_IP}:${KONG_HTTP_PORT:-8000}