From b3bd985d0b96872cd97aa716684e87d4141ceb29 Mon Sep 17 00:00:00 2001 From: kabo2468 <28654659+kabo2468@users.noreply.github.com> Date: Tue, 28 Jan 2025 05:55:44 +0900 Subject: [PATCH 1/7] dockerize --- .dockerignore | 17 +++++++++++++++++ .gitignore | 4 ++-- Dockerfile | 44 ++++++++++++++++++++++++++++++++++++++++++++ compose.yaml | 37 +++++++++++++++++++++++++++++++++++++ package.json | 6 ++++-- pnpm-lock.yaml | 6 +++--- 6 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 compose.yaml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..dccd2f6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,17 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.idea +**/.vscode +**/.cache +**/docker-compose* +**/compose.y*ml +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/secrets.dev.yaml +**/values.dev.yaml +**/built +LICENSE +README.md diff --git a/.gitignore b/.gitignore index 11c06a3..1b2a6b7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,8 @@ node_modules .vscode test built -config.json5 +config.* mecab valentine.json - .idea/* +*.secret diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6cc6251 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,44 @@ +FROM node:22-slim AS base +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" + +RUN corepack enable +COPY . /app +WORKDIR /app + +# hadolint ignore=DL3008 +RUN apt-get update \ + && apt-get install -y --no-install-recommends g++ gcc make python3 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +FROM base AS prod-deps +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile + +FROM base AS build +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile \ + && pnpm run build + +FROM base +ENV NODE_ENV=production + +COPY --from=prod-deps /app/node_modules /app/node_modules +COPY --from=build /app/built /app/built + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] +# hadolint ignore=DL3008 +RUN apt-get update \ + && apt-get install -y --no-install-recommends mecab libmecab-dev mecab-ipadic-utf8 \ + git make curl ca-certificates xz-utils file patch sudo openssl \ + && apt-get clean && rm -rf /var/lib/apt/lists/* \ + && git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git \ + && cd mecab-ipadic-neologd \ + && curl https://patch-diff.githubusercontent.com/raw/neologd/mecab-ipadic-neologd/pull/91.patch | git apply -v \ + && ./bin/install-mecab-ipadic-neologd -n -y -a \ + && cd .. \ + && rm -rf mecab-ipadic-neologd \ + && apt-get purge -y --auto-remove git make curl ca-certificates xz-utils file patch openssl gcc g++ python3 + +RUN chown -R node:node /app +USER node +CMD ["pnpm", "migrateandstart"] diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..c6a011d --- /dev/null +++ b/compose.yaml @@ -0,0 +1,37 @@ +services: + bot: + build: + context: . + volumes: + - ./config.json5:/app/config.json5:ro + - ./valentine.json:/app/valentine.json:rw + depends_on: + db: + condition: service_healthy + restart: always + + db: + image: postgres:alpine + restart: always + user: postgres + secrets: + - db-password + volumes: + - db-data:/var/lib/postgresql/data + environment: + - POSTGRES_DB=postgres + - POSTGRES_PASSWORD_FILE=/run/secrets/db-password + expose: + - 5432 + healthcheck: + test: ["CMD", "pg_isready"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + db-data: + +secrets: + db-password: + file: db_password.secret diff --git a/package.json b/package.json index 21bdaf2..d5cb60e 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,12 @@ "build": "npm run clean && npm run tsc", "lint": "biome lint src --write", "format": "biome format src --write", - "migration": "node built/migration.js" + "migration": "node built/migration.js", + "migrateandstart": "pnpm run migration && pnpm start" }, "dependencies": { "got": "^14.4.4", + "iconv-lite": "^0.6.3", "json5": "^2.2.3", "memory-streams": "^0.1.3", "mfm-js": "^0.24.0", @@ -48,5 +50,5 @@ "bufferutil": "^4.0.8", "utf-8-validate": "^6.0.5" }, - "packageManager": "pnpm@9.13.2+sha512.88c9c3864450350e65a33587ab801acf946d7c814ed1134da4a924f6df5a2120fd36b46aab68f7cd1d413149112d53c7db3a4136624cfd00ff1846a0c6cef48a" + "packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e585d64..7ba5c10 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: got: specifier: ^14.4.4 version: 14.4.4 + iconv-lite: + specifier: ^0.6.3 + version: 0.6.3 json5: specifier: ^2.2.3 version: 2.2.3 @@ -67,9 +70,6 @@ importers: '@types/ws': specifier: ^8.5.13 version: 8.5.13 - iconv-lite: - specifier: ^0.6.3 - version: 0.6.3 tsx: specifier: ^4.19.2 version: 4.19.2 From a5011a93b14dbf83389908f4d60e0662623792a7 Mon Sep 17 00:00:00 2001 From: kabo2468 <28654659+kabo2468@users.noreply.github.com> Date: Tue, 28 Jan 2025 07:02:13 +0900 Subject: [PATCH 2/7] =?UTF-8?q?=E3=82=A4=E3=83=A1=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=82=B5=E3=82=A4=E3=82=BA=E3=82=92=E5=B0=91=E3=81=97=E6=B8=9B?= =?UTF-8?q?=E3=82=89=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6cc6251..9a29763 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,11 @@ FROM node:22-slim AS base + ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable + +FROM base AS init COPY . /app WORKDIR /app @@ -12,32 +15,35 @@ RUN apt-get update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -FROM base AS prod-deps +FROM init AS prod-deps RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile -FROM base AS build +FROM init AS build RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile \ && pnpm run build FROM base ENV NODE_ENV=production +WORKDIR /app COPY --from=prod-deps /app/node_modules /app/node_modules COPY --from=build /app/built /app/built +COPY config.json5 ngwords.txt package.json /app/ SHELL ["/bin/bash", "-o", "pipefail", "-c"] # hadolint ignore=DL3008 RUN apt-get update \ && apt-get install -y --no-install-recommends mecab libmecab-dev mecab-ipadic-utf8 \ git make curl ca-certificates xz-utils file patch sudo openssl \ - && apt-get clean && rm -rf /var/lib/apt/lists/* \ && git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git \ && cd mecab-ipadic-neologd \ && curl https://patch-diff.githubusercontent.com/raw/neologd/mecab-ipadic-neologd/pull/91.patch | git apply -v \ && ./bin/install-mecab-ipadic-neologd -n -y -a \ && cd .. \ && rm -rf mecab-ipadic-neologd \ - && apt-get purge -y --auto-remove git make curl ca-certificates xz-utils file patch openssl gcc g++ python3 + && apt-get purge -y --auto-remove git make curl ca-certificates xz-utils file patch openssl \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* RUN chown -R node:node /app USER node From e9fe744426f15ec7f695a2d8e2b730e47446b76e Mon Sep 17 00:00:00 2001 From: kabo2468 <28654659+kabo2468@users.noreply.github.com> Date: Tue, 28 Jan 2025 07:27:52 +0900 Subject: [PATCH 3/7] Create docker.yml --- .github/workflows/docker.yml | 93 ++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..fe3dd1a --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,93 @@ +name: Docker + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +on: + push: + # Publish semver tags as releases. + tags: [ 'v*.*.*' ] + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0 + with: + cosign-release: 'v2.2.4' + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign + - name: Sign the published Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + # This step uses the identity token to provision an ephemeral certificate + # against the sigstore community Fulcio instance. + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} From 1bafeb8cda17cfbe838a4a83cd7beae7883c32db Mon Sep 17 00:00:00 2001 From: kabo2468 <28654659+kabo2468@users.noreply.github.com> Date: Tue, 28 Jan 2025 08:12:48 +0900 Subject: [PATCH 4/7] Update README.md --- README.md | 14 ++++++++++++++ compose.yaml | 1 + db_password.secret.example | 1 + example.docker.json5 | 17 +++++++++++++++++ 4 files changed, 33 insertions(+) create mode 100644 db_password.secret.example create mode 100644 example.docker.json5 diff --git a/README.md b/README.md index c1ffe5f..431e542 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ TLから言葉を学び、それをおいしいかまずいか決めて、投稿 ## 使い方 +Docker を使う場合は、[Docker](#docker) を参照してください。 + ### Misskey で @oishiibot を作る この ID じゃないと動きません @@ -97,3 +99,15 @@ npm start Xeltica さんの Citrine から参考にさせていただきました。 + +## Docker + +Dockerを使う場合は、以下の手順で起動できます。 + +1. `example.docker.json5` を `config.json5` にコピーして編集する (上記の [config.json5](#config.json) を参照) +2. `db_password.secret.example` のように `db_password.secret` にDBのパスワードを書く +3. `docker compose up -d` で起動する + +デフォルトでは、既にビルドされたイメージを使うようになっています。 + +ローカルのソースコードを使いたい場合は、`compose.yaml`内の`services.bot.image`をコメントアウトしてください。 diff --git a/compose.yaml b/compose.yaml index c6a011d..3ce3ef0 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,5 +1,6 @@ services: bot: + image: ghcr.io/kabo2468/oishii-bot build: context: . volumes: diff --git a/db_password.secret.example b/db_password.secret.example new file mode 100644 index 0000000..dcc74b2 --- /dev/null +++ b/db_password.secret.example @@ -0,0 +1 @@ +PASSWORD diff --git a/example.docker.json5 b/example.docker.json5 new file mode 100644 index 0000000..14cecc8 --- /dev/null +++ b/example.docker.json5 @@ -0,0 +1,17 @@ +{ + url: 'https://misskey.io', + apiKey: '', + databaseUrl: 'postgresql://postgres:PASSWORD@db:5432/postgres', + dbSSL: false, + ownerUsernames: ['kabo'], + post: { + autoPostInterval: 60, + tlPostProbability: 0.4, + rateLimitSec: 60, + rateLimitPost: 5, + }, + mecab: { + binPath: '/usr/bin/mecab', + dicPath: '/usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd', + }, +} From 31631ab4bd06c952a1c106438a9c2d779dea344b Mon Sep 17 00:00:00 2001 From: kabo2468 <28654659+kabo2468@users.noreply.github.com> Date: Tue, 28 Jan 2025 09:03:24 +0900 Subject: [PATCH 5/7] =?UTF-8?q?DB=E3=81=AB=E9=A3=9F=E3=81=B9=E7=89=A9?= =?UTF-8?q?=E3=81=8C=E5=B0=91=E3=81=AA=E3=81=84=E3=81=A8=E3=81=8D=E3=81=AB?= =?UTF-8?q?=E8=BF=94=E4=BA=8B=E3=81=8C=E3=81=AA=E3=81=8F=E3=81=AA=E3=82=8B?= =?UTF-8?q?=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bot.ts | 8 ++++++-- src/modules/commands/info.ts | 4 ++-- src/modules/hungry.ts | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/bot.ts b/src/bot.ts index d75e224..f186949 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -206,8 +206,12 @@ export class Bot { const learned = Math.random() < 0.2; const res = await this.getRandomFood({ learned }); - const food = res.rows[0].name; - const good = res.rows[0].good; + const food = res.rows[0]?.name; + const good = res.rows[0]?.good; + if (!food || good === undefined) { + this.log('sayFood: not found'); + return; + } this.log(`sayFood: ${food} (${good})`); const text = messages.food.say(food, good); diff --git a/src/modules/commands/info.ts b/src/modules/commands/info.ts index 4eddb80..207bd92 100644 --- a/src/modules/commands/info.ts +++ b/src/modules/commands/info.ts @@ -22,8 +22,8 @@ export default class extends Module { text.push(`Node.js: ${process.version}`); // Records - const fl = Number(res.rows[0].count); - const tl = Number(res.rows[1].count); + const fl = Number(res.rows[0]?.count) || 0; + const tl = Number(res.rows[1]?.count) || 0; const all = fl + tl; const recordText = `Records: ${all} (Learned: ${tl})`; this.log(recordText); diff --git a/src/modules/hungry.ts b/src/modules/hungry.ts index 8e3c69c..fdd1edb 100644 --- a/src/modules/hungry.ts +++ b/src/modules/hungry.ts @@ -15,8 +15,8 @@ export default class extends Module { const res = await bot.getRandomFood({ good: _g }); - const food = res.rows[0].name; - const good = res.rows[0].good; + const food = res.rows[0]?.name; + const good = res.rows[0]?.good; if (!food || good === undefined) { note.reply({ text: messages.food.idk }); return; From 13e28060bbff1351e56aab1591abbdd22d200786 Mon Sep 17 00:00:00 2001 From: kabo2468 <28654659+kabo2468@users.noreply.github.com> Date: Tue, 28 Jan 2025 09:39:16 +0900 Subject: [PATCH 6/7] =?UTF-8?q?info=E3=82=B3=E3=83=9E=E3=83=B3=E3=83=89?= =?UTF-8?q?=E3=81=AECommit=20Hash=E3=82=92=E7=92=B0=E5=A2=83=E5=A4=89?= =?UTF-8?q?=E6=95=B0=E3=81=8B=E3=82=89=E8=AA=AD=E3=82=81=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker.yml | 2 ++ Dockerfile | 3 +++ README.md | 2 ++ compose.yaml | 2 ++ src/modules/commands/info.ts | 24 ++++++++++++++++-------- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fe3dd1a..6d07795 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,6 +76,8 @@ jobs: labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + build-args: | + GIT_SHA=${{ github.sha }} # Sign the resulting Docker image digest except on PRs. # This will only write to the public Rekor transparency log when the Docker diff --git a/Dockerfile b/Dockerfile index 9a29763..89f9c1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,8 @@ FROM node:22-slim AS base +ARG GIT_SHA="unknown" +ENV GIT_SHA=$GIT_SHA + ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" diff --git a/README.md b/README.md index 431e542..30e109d 100644 --- a/README.md +++ b/README.md @@ -111,3 +111,5 @@ Dockerを使う場合は、以下の手順で起動できます。 デフォルトでは、既にビルドされたイメージを使うようになっています。 ローカルのソースコードを使いたい場合は、`compose.yaml`内の`services.bot.image`をコメントアウトしてください。 + +ビルドするときは、`docker compose build --build-arg GIT_SHA=$(git rev-parse HEAD)` を実行してください。 diff --git a/compose.yaml b/compose.yaml index 3ce3ef0..1a20fdf 100644 --- a/compose.yaml +++ b/compose.yaml @@ -3,6 +3,8 @@ services: image: ghcr.io/kabo2468/oishii-bot build: context: . + args: + - GIT_SHA=unknown volumes: - ./config.json5:/app/config.json5:ro - ./valentine.json:/app/valentine.json:rw diff --git a/src/modules/commands/info.ts b/src/modules/commands/info.ts index 207bd92..3df09c3 100644 --- a/src/modules/commands/info.ts +++ b/src/modules/commands/info.ts @@ -30,14 +30,22 @@ export default class extends Module { text.push(recordText); // Commit Hash - const rev = readFileSync('.git/HEAD').toString(); - const branchHash = readFileSync('.git/' + rev.substring(5).trim()) - .toString() - .trim(); - const hash = rev.indexOf(':') === -1 ? rev : branchHash; - const hashText = `Commit hash: ${hash.substring(0, 7)}`; - this.log(hashText); - text.push(hashText); + if (process.env.GIT_SHA) { + this.log('from process.env.GIT_SHA'); + const hashText = `Commit hash: ${process.env.GIT_SHA.substring(0, 7)}`; + this.log(hashText); + text.push(hashText); + } else { + this.log('from .git'); + const rev = readFileSync('.git/HEAD').toString(); + const branchHash = readFileSync('.git/' + rev.substring(5).trim()) + .toString() + .trim(); + const hash = rev.indexOf(':') === -1 ? rev : branchHash; + const hashText = `Commit hash: ${hash.substring(0, 7)}`; + this.log(hashText); + text.push(hashText); + } // uptime const uptime = process.uptime(); From b1e7b0956ccd59df60b51317aea1ca58730d13ed Mon Sep 17 00:00:00 2001 From: kabo2468 <28654659+kabo2468@users.noreply.github.com> Date: Tue, 28 Jan 2025 09:45:56 +0900 Subject: [PATCH 7/7] =?UTF-8?q?Dockerfile=E3=81=AE=E3=82=B3=E3=83=9E?= =?UTF-8?q?=E3=83=B3=E3=83=89=E3=81=AE=E9=A0=86=E7=95=AA=E3=82=92=E3=82=AD?= =?UTF-8?q?=E3=83=A3=E3=83=83=E3=82=B7=E3=83=A5=E3=81=8C=E5=8A=B9=E3=81=8F?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 89f9c1b..4f46d3b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,13 +26,6 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile && pnpm run build FROM base -ENV NODE_ENV=production -WORKDIR /app - -COPY --from=prod-deps /app/node_modules /app/node_modules -COPY --from=build /app/built /app/built -COPY config.json5 ngwords.txt package.json /app/ - SHELL ["/bin/bash", "-o", "pipefail", "-c"] # hadolint ignore=DL3008 RUN apt-get update \ @@ -48,6 +41,13 @@ RUN apt-get update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* +ENV NODE_ENV=production +WORKDIR /app + +COPY --from=prod-deps /app/node_modules /app/node_modules +COPY --from=build /app/built /app/built +COPY config.json5 ngwords.txt package.json /app/ + RUN chown -R node:node /app USER node CMD ["pnpm", "migrateandstart"]