create-meith 0.25.1 → 0.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +328 -56
- package/package.json +1 -1
- package/src/bin.ts +1 -1
- package/src/scaffold-extension.ts +2 -2
- package/src/scaffold.ts +327 -53
package/dist/bin.mjs
CHANGED
|
@@ -53,7 +53,7 @@ var AT_ROOT_IGNORES = `# What \`forum-web ${AT_ROOT_FLAG}\` writes into this dir
|
|
|
53
53
|
# alone by the build and still never committed, so it works locally and is
|
|
54
54
|
# simply absent from the deploy, which builds from what git has. Extend the
|
|
55
55
|
# board with a plugin or a theme instead \u2014 the forum loads those from
|
|
56
|
-
#
|
|
56
|
+
# meith.config.ts, and they are yours to commit. forum-web prints a
|
|
57
57
|
# warning naming any file of yours it finds there.
|
|
58
58
|
#
|
|
59
59
|
# For the rest, a build refuses rather than overwriting a file it did not
|
|
@@ -326,7 +326,9 @@ var SELF_HOST_DEPLOY_KIT = [
|
|
|
326
326
|
".github/dependabot.yml",
|
|
327
327
|
".github/workflows/build.yml",
|
|
328
328
|
"Dockerfile",
|
|
329
|
+
"Dockerfile.prebuilt",
|
|
329
330
|
"docker-compose.yaml",
|
|
331
|
+
"docker-compose.prebuilt.yaml",
|
|
330
332
|
"docker-entrypoint.sh",
|
|
331
333
|
"docker-healthcheck.sh"
|
|
332
334
|
];
|
|
@@ -425,7 +427,7 @@ save-exact=true
|
|
|
425
427
|
`
|
|
426
428
|
);
|
|
427
429
|
files.set(
|
|
428
|
-
"
|
|
430
|
+
"meith.config.ts",
|
|
429
431
|
`/**
|
|
430
432
|
* The board's build-time registry.
|
|
431
433
|
*
|
|
@@ -436,7 +438,7 @@ save-exact=true
|
|
|
436
438
|
* not there at all.
|
|
437
439
|
*
|
|
438
440
|
* Adding a theme is: \`npm install\` it, add a line here, redeploy. Adding a
|
|
439
|
-
* plugin is the same, through board.plugins.json and
|
|
441
|
+
* plugin is the same, through board.plugins.json and meith.plugins.ts \u2014
|
|
440
442
|
* see docs/customization/plugins.md.
|
|
441
443
|
*/
|
|
442
444
|
import { defineForumConfig } from '@meith/web/config'
|
|
@@ -448,7 +450,7 @@ import {
|
|
|
448
450
|
LIGHT_TOKENS,
|
|
449
451
|
} from '@meith/theme-default'
|
|
450
452
|
|
|
451
|
-
import { INSTALLED_PLUGINS } from './
|
|
453
|
+
import { INSTALLED_PLUGINS } from './meith.plugins'
|
|
452
454
|
|
|
453
455
|
export default defineForumConfig({
|
|
454
456
|
themes: {
|
|
@@ -470,7 +472,7 @@ export default defineForumConfig({
|
|
|
470
472
|
files.set("board.plugins.json", `${JSON.stringify({ plugins: [] }, null, 2)}
|
|
471
473
|
`);
|
|
472
474
|
files.set(
|
|
473
|
-
"
|
|
475
|
+
"meith.plugins.ts",
|
|
474
476
|
`/**
|
|
475
477
|
* The board's installed-plugin list.
|
|
476
478
|
*
|
|
@@ -535,7 +537,87 @@ updates:
|
|
|
535
537
|
"Dockerfile",
|
|
536
538
|
`# syntax=docker/dockerfile:1.7-labs
|
|
537
539
|
# check=skip=InvalidDefaultArgInFrom
|
|
538
|
-
# ${name}'s deploy image
|
|
540
|
+
# ${name}'s quick-start deploy image \u2014 built from source, with nothing to
|
|
541
|
+
# set up first.
|
|
542
|
+
#
|
|
543
|
+
# FROM node:26-alpine directly rather than a published Meith base image:
|
|
544
|
+
# Coolify (or a plain \`docker build\`) builds this from this repository, so
|
|
545
|
+
# there is no registry account, no image tag to paste anywhere, and no
|
|
546
|
+
# \`.github/workflows/build.yml\` run to wait on. The cost of that zero setup
|
|
547
|
+
# is that this installs the board's full dependency closure itself (see the
|
|
548
|
+
# \`npm install\` below), so a build here is heavier than \`Dockerfile.prebuilt\`'s
|
|
549
|
+
# thin delta \u2014 that image, pulled rather than built, is the trade the advanced
|
|
550
|
+
# path takes for a low-spec build server or a faster deploy (see \`README.md\`
|
|
551
|
+
# and, in the meith repository, docs/getting-started/deployment/docker-compose.md,
|
|
552
|
+
# "Custom boards").
|
|
553
|
+
#
|
|
554
|
+
# Two stages, not three: unlike the official image, this does not prune down
|
|
555
|
+
# to Next's own standalone output. The migrate role below runs \`community
|
|
556
|
+
# migrate\`, and \`community\` materializes @meith/cli's sources and runs them
|
|
557
|
+
# with tsx at the moment it runs (see the meith repository's
|
|
558
|
+
# docs/contributing/development.md, "Consuming the board from a workspace") \u2014 it needs
|
|
559
|
+
# the full, un-pruned node_modules tree this board installed, not what Next
|
|
560
|
+
# traced as reachable from the web server alone. The tick itself is driven
|
|
561
|
+
# by docker-compose.yaml's own \`worker\` service \u2014 a lightweight loop against
|
|
562
|
+
# /api/system/tick, not a compiled worker process, because @meith/worker is
|
|
563
|
+
# not published (see the meith repository's docs/contributing/release.md).
|
|
564
|
+
FROM node:26-alpine@sha256:aadf416b2cdce311a8811ba3f0608a61b77dbf997500e2eafe781b51f6a0b019 AS deps
|
|
565
|
+
WORKDIR /board
|
|
566
|
+
|
|
567
|
+
# This board's own manifest, cached independently of its source \u2014 editing
|
|
568
|
+
# meith.config.ts should not re-run npm install. Nothing warms node_modules
|
|
569
|
+
# ahead of this the way \`Dockerfile.prebuilt\`'s base image does: the full
|
|
570
|
+
# @meith/web, @meith/cli and @meith/theme-default closure this board depends
|
|
571
|
+
# on is installed here, from scratch, which is the heavier half of the
|
|
572
|
+
# quick-start trade.
|
|
573
|
+
COPY package.json ./
|
|
574
|
+
RUN npm install
|
|
575
|
+
|
|
576
|
+
FROM deps AS runtime
|
|
577
|
+
WORKDIR /board
|
|
578
|
+
COPY . .
|
|
579
|
+
|
|
580
|
+
ENV NEXT_TELEMETRY_DISABLED=1
|
|
581
|
+
ENV NODE_ENV=production
|
|
582
|
+
|
|
583
|
+
# DATA_SOURCE is scoped to this one RUN, not declared with ENV \u2014 an ENV
|
|
584
|
+
# persists into every container started from this image afterward, and this
|
|
585
|
+
# Dockerfile has no later stage to reset it in (see "Two stages, not three"
|
|
586
|
+
# above). The build needs neither a database nor a production secret (see
|
|
587
|
+
# the meith repository's docs/contributing/development.md, "Fixture mode"), but baking
|
|
588
|
+
# DATA_SOURCE=fixture into the image itself would silently force fixture
|
|
589
|
+
# mode \u2014 and with it the in-memory queue driver \u2014 at runtime too, no matter
|
|
590
|
+
# what DATABASE_URL an operator supplies to \`docker run\`.
|
|
591
|
+
RUN DATA_SOURCE=fixture npx forum-web build
|
|
592
|
+
|
|
593
|
+
ENV PORT=3000
|
|
594
|
+
ENV HOSTNAME=0.0.0.0
|
|
595
|
+
EXPOSE 3000
|
|
596
|
+
|
|
597
|
+
# node:alpine already carries a non-root "node" user; the board's own files
|
|
598
|
+
# are copied in as root above, so they need handing over before this drops
|
|
599
|
+
# privilege.
|
|
600
|
+
RUN chown -R node:node /board
|
|
601
|
+
USER node
|
|
602
|
+
|
|
603
|
+
COPY --chown=node:node docker-entrypoint.sh docker-healthcheck.sh ./
|
|
604
|
+
RUN chmod +x docker-entrypoint.sh docker-healthcheck.sh
|
|
605
|
+
|
|
606
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \\
|
|
607
|
+
CMD ["./docker-healthcheck.sh"]
|
|
608
|
+
|
|
609
|
+
ENTRYPOINT ["./docker-entrypoint.sh"]
|
|
610
|
+
`
|
|
611
|
+
);
|
|
612
|
+
files.set(
|
|
613
|
+
"Dockerfile.prebuilt",
|
|
614
|
+
`# syntax=docker/dockerfile:1.7-labs
|
|
615
|
+
# check=skip=InvalidDefaultArgInFrom
|
|
616
|
+
# ${name}'s advanced deploy image \u2014 built by \`.github/workflows/build.yml\` and
|
|
617
|
+
# pulled by \`docker-compose.prebuilt.yaml\`. A quick-start board never builds
|
|
618
|
+
# this file directly; it can delete this file, \`docker-compose.prebuilt.yaml\`
|
|
619
|
+
# and \`.github/workflows/build.yml\` outright and keep only \`Dockerfile\` and
|
|
620
|
+
# \`docker-compose.yaml\` (see README.md, "Deploy").
|
|
539
621
|
#
|
|
540
622
|
# FROM the published framework base image \u2014 deps + framework layers only,
|
|
541
623
|
# locked to this exact release (see the meith repository's
|
|
@@ -560,7 +642,7 @@ FROM ghcr.io/meith-dev/meith-base:\${MEITH_VERSION} AS deps
|
|
|
560
642
|
WORKDIR /board
|
|
561
643
|
|
|
562
644
|
# This board's own manifest, cached independently of its source \u2014 editing
|
|
563
|
-
#
|
|
645
|
+
# meith.config.ts should not re-run npm install. The base image above
|
|
564
646
|
# already carries node_modules for @meith/web, @meith/cli and
|
|
565
647
|
# @meith/theme-default at this exact version, so installing this file on top
|
|
566
648
|
# of it only fetches what changed: a plugin newly added to \`dependencies\`,
|
|
@@ -666,10 +748,10 @@ node -e "fetch('http://127.0.0.1:3000/api/ready').then(r=>process.exit(r.ok?0:1)
|
|
|
666
748
|
);
|
|
667
749
|
files.set(
|
|
668
750
|
".github/workflows/build.yml",
|
|
669
|
-
`#
|
|
670
|
-
#
|
|
671
|
-
#
|
|
672
|
-
#
|
|
751
|
+
`# The advanced/prebuilt path (see README.md, "Deploy"). Builds this board's
|
|
752
|
+
# Dockerfile.prebuilt and pushes it to your own GHCR, on every push to main.
|
|
753
|
+
# No secret to configure: GITHUB_TOKEN is provided automatically by GitHub
|
|
754
|
+
# Actions and is enough to push to ghcr.io/<this repository>.
|
|
673
755
|
name: Build and push
|
|
674
756
|
|
|
675
757
|
on:
|
|
@@ -704,7 +786,7 @@ jobs:
|
|
|
704
786
|
echo "::error::@meith/web in package.json is '$MEITH_VERSION', not an exact X.Y.Z version \u2014 that is not a legal Docker image tag. Upgrade with \\\`npm install --save-exact\\\` (see README.md, Upgrading) so this dependency always resolves to one."
|
|
705
787
|
exit 1
|
|
706
788
|
fi
|
|
707
|
-
docker build --build-arg MEITH_VERSION="$MEITH_VERSION" -t "$IMAGE:\${{ github.sha }}" -t "$IMAGE:latest" .
|
|
789
|
+
docker build -f Dockerfile.prebuilt --build-arg MEITH_VERSION="$MEITH_VERSION" -t "$IMAGE:\${{ github.sha }}" -t "$IMAGE:latest" .
|
|
708
790
|
docker push "$IMAGE:\${{ github.sha }}"
|
|
709
791
|
docker push "$IMAGE:latest"
|
|
710
792
|
|
|
@@ -744,9 +826,141 @@ jobs:
|
|
|
744
826
|
);
|
|
745
827
|
files.set(
|
|
746
828
|
"docker-compose.yaml",
|
|
747
|
-
`# ${name}, deployed by Coolify \u2014 the same shape as the meith
|
|
748
|
-
# docker/compose.coolify.yml: db, migrate, web, worker. See
|
|
749
|
-
# the
|
|
829
|
+
`# ${name}, quick-start deployed by Coolify \u2014 the same shape as the meith
|
|
830
|
+
# repository's own docker/compose.coolify.yml: db, migrate, web, worker. See
|
|
831
|
+
# README.md for the deploy story this file is the last step of.
|
|
832
|
+
#
|
|
833
|
+
# Coolify builds \`Dockerfile\` from this repository itself, so there is no
|
|
834
|
+
# MEITH_IMAGE to set here \u2014 every deploy is a build from source. For a
|
|
835
|
+
# low-spec build server or a faster deploy, use \`docker-compose.prebuilt.yaml\`
|
|
836
|
+
# instead, which pulls the image \`.github/workflows/build.yml\` pushes to GHCR.
|
|
837
|
+
#
|
|
838
|
+
# No published ports \u2014 Coolify's proxy routes to the container and issues
|
|
839
|
+
# the certificate. The two secrets and the database password are Coolify's
|
|
840
|
+
# own "magic variables": it fills them in on the first deploy and shows them
|
|
841
|
+
# in the panel, so nothing here needs a value typed into it. Requires Coolify
|
|
842
|
+
# v4.0.0-beta.411 or newer, which is when magic variables in a compose file
|
|
843
|
+
# from a Git source arrived.
|
|
844
|
+
services:
|
|
845
|
+
postgres:
|
|
846
|
+
image: postgres:18-alpine@sha256:d3e1620b530c944afa6e887d22eb899824da68e19c52024bf98f5220c88a65b2
|
|
847
|
+
restart: unless-stopped
|
|
848
|
+
mem_limit: \${POSTGRES_MEM_LIMIT:-1g}
|
|
849
|
+
cpus: \${POSTGRES_CPUS:-1}
|
|
850
|
+
environment:
|
|
851
|
+
POSTGRES_USER: community
|
|
852
|
+
POSTGRES_PASSWORD: $SERVICE_PASSWORD_POSTGRES
|
|
853
|
+
POSTGRES_DB: community
|
|
854
|
+
volumes:
|
|
855
|
+
- pgdata:/var/lib/postgresql
|
|
856
|
+
healthcheck:
|
|
857
|
+
test: ['CMD-SHELL', 'pg_isready -U community -d community']
|
|
858
|
+
interval: 10s
|
|
859
|
+
timeout: 5s
|
|
860
|
+
retries: 5
|
|
861
|
+
|
|
862
|
+
# Runs to completion, then exits. web waits for it, so the schema is
|
|
863
|
+
# always applied before the first request rather than racing it.
|
|
864
|
+
migrate:
|
|
865
|
+
build: .
|
|
866
|
+
image: ${name}
|
|
867
|
+
environment:
|
|
868
|
+
COMMUNITY_ROLE: migrate
|
|
869
|
+
DATABASE_URL: postgres://community:$SERVICE_PASSWORD_POSTGRES@postgres:5432/community
|
|
870
|
+
AUTH_SECRET: $SERVICE_BASE64_64_AUTH
|
|
871
|
+
TICK_SECRET: $SERVICE_BASE64_64_TICK
|
|
872
|
+
depends_on:
|
|
873
|
+
postgres:
|
|
874
|
+
condition: service_healthy
|
|
875
|
+
restart: 'no'
|
|
876
|
+
|
|
877
|
+
web:
|
|
878
|
+
build: .
|
|
879
|
+
image: ${name}
|
|
880
|
+
restart: unless-stopped
|
|
881
|
+
mem_limit: \${WEB_MEM_LIMIT:-1g}
|
|
882
|
+
cpus: \${WEB_CPUS:-2}
|
|
883
|
+
# A readiness probe Coolify gates a rolling deploy on: with "Rolling
|
|
884
|
+
# update" enabled on the resource, the new container must answer
|
|
885
|
+
# /api/ready before the old one is retired, so a redeploy swaps in with no
|
|
886
|
+
# gap. Without it Coolify recreates the stack \u2014 old removed, then new
|
|
887
|
+
# started \u2014 and the board is down while the new web boots.
|
|
888
|
+
healthcheck:
|
|
889
|
+
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/ready').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
|
890
|
+
interval: 30s
|
|
891
|
+
timeout: 5s
|
|
892
|
+
start_period: 20s
|
|
893
|
+
retries: 3
|
|
894
|
+
environment:
|
|
895
|
+
# Ask Coolify for a domain on port 3000, then hand the board the same
|
|
896
|
+
# thing with a scheme in front.
|
|
897
|
+
- SERVICE_FQDN_WEB_3000
|
|
898
|
+
- APP_URL=$SERVICE_URL_WEB
|
|
899
|
+
- DATABASE_URL=postgres://community:$SERVICE_PASSWORD_POSTGRES@postgres:5432/community
|
|
900
|
+
- AUTH_SECRET=$SERVICE_BASE64_64_AUTH
|
|
901
|
+
- TICK_SECRET=$SERVICE_BASE64_64_TICK
|
|
902
|
+
- QUEUE_DRIVER=postgres
|
|
903
|
+
- CACHE_DRIVER=next
|
|
904
|
+
- FILESTORE_DRIVER=local
|
|
905
|
+
# Left unset, mail is configured on the board itself \u2014 the installer
|
|
906
|
+
# asks on first run. Set MAIL_DRIVER here and this file wins instead.
|
|
907
|
+
- MAIL_DRIVER=\${MAIL_DRIVER:-log}
|
|
908
|
+
- MAIL_SMTP_HOST=\${MAIL_SMTP_HOST:-}
|
|
909
|
+
- MAIL_SMTP_PORT=\${MAIL_SMTP_PORT:-}
|
|
910
|
+
- MAIL_SMTP_SECURITY=\${MAIL_SMTP_SECURITY:-}
|
|
911
|
+
- MAIL_SMTP_USERNAME=\${MAIL_SMTP_USERNAME:-}
|
|
912
|
+
- MAIL_SMTP_PASSWORD=\${MAIL_SMTP_PASSWORD:-}
|
|
913
|
+
- MAIL_FROM=\${MAIL_FROM:-}
|
|
914
|
+
volumes:
|
|
915
|
+
- uploads:/app/.uploads
|
|
916
|
+
depends_on:
|
|
917
|
+
postgres:
|
|
918
|
+
condition: service_healthy
|
|
919
|
+
migrate:
|
|
920
|
+
condition: service_completed_successfully
|
|
921
|
+
|
|
922
|
+
# @meith/worker is not published (see the meith repository's
|
|
923
|
+
# docs/contributing/release.md), so there is no compiled worker binary a scaffolded
|
|
924
|
+
# board can run \u2014 this drives the tick the alternative way the meith
|
|
925
|
+
# repository documents in docs/getting-started/deployment/docker-compose.md, "Running the tick without
|
|
926
|
+
# a second set of credentials": a small loop calling /api/system/tick.
|
|
927
|
+
worker:
|
|
928
|
+
image: alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
|
|
929
|
+
restart: unless-stopped
|
|
930
|
+
mem_limit: \${WORKER_MEM_LIMIT:-64m}
|
|
931
|
+
cpus: \${WORKER_CPUS:-0.25}
|
|
932
|
+
environment:
|
|
933
|
+
TICK_SECRET: $SERVICE_BASE64_64_TICK
|
|
934
|
+
command:
|
|
935
|
+
- sh
|
|
936
|
+
- -c
|
|
937
|
+
- |
|
|
938
|
+
apk add --no-cache curl >/dev/null
|
|
939
|
+
while true; do
|
|
940
|
+
curl -fsS -m 55 -H "Authorization: Bearer $$TICK_SECRET" \\
|
|
941
|
+
http://web:3000/api/system/tick >/dev/null 2>&1 \\
|
|
942
|
+
|| echo "tick failed at $$(date -Is)"
|
|
943
|
+
sleep 60
|
|
944
|
+
done
|
|
945
|
+
depends_on:
|
|
946
|
+
- web
|
|
947
|
+
|
|
948
|
+
volumes:
|
|
949
|
+
pgdata:
|
|
950
|
+
uploads:
|
|
951
|
+
`
|
|
952
|
+
);
|
|
953
|
+
files.set(
|
|
954
|
+
"docker-compose.prebuilt.yaml",
|
|
955
|
+
`# ${name}, deployed by Coolify from a prebuilt image \u2014 the advanced path: point
|
|
956
|
+
# Coolify's compose-file at this file instead of docker-compose.yaml once
|
|
957
|
+
# \`.github/workflows/build.yml\` has pushed an image, and set MEITH_IMAGE to
|
|
958
|
+
# what its Summary printed. This trades the quick-start's heavier
|
|
959
|
+
# build-on-every-deploy for a low-spec build server or a faster deploy \u2014 see
|
|
960
|
+
# README.md, "Deploy".
|
|
961
|
+
#
|
|
962
|
+
# Same shape as the meith repository's own docker/compose.coolify.yml: db,
|
|
963
|
+
# migrate, web, worker.
|
|
750
964
|
#
|
|
751
965
|
# No published ports \u2014 Coolify's proxy routes to the container and issues
|
|
752
966
|
# the certificate. The two secrets and the database password are Coolify's
|
|
@@ -778,6 +992,10 @@ services:
|
|
|
778
992
|
# always applied before the first request rather than racing it.
|
|
779
993
|
migrate:
|
|
780
994
|
image: \${MEITH_IMAGE:?set this to the image the build workflow's Summary just printed, e.g. ghcr.io/<you>/${name}:latest}
|
|
995
|
+
# Pull the tag on every deploy. Compose keeps an image it already has, so
|
|
996
|
+
# a rebuilt \`:latest\` is otherwise never fetched and a redeploy quietly
|
|
997
|
+
# runs the old code.
|
|
998
|
+
pull_policy: always
|
|
781
999
|
environment:
|
|
782
1000
|
COMMUNITY_ROLE: migrate
|
|
783
1001
|
DATABASE_URL: postgres://community:$SERVICE_PASSWORD_POSTGRES@postgres:5432/community
|
|
@@ -790,9 +1008,21 @@ services:
|
|
|
790
1008
|
|
|
791
1009
|
web:
|
|
792
1010
|
image: \${MEITH_IMAGE:?set this to the image the build workflow's Summary just printed, e.g. ghcr.io/<you>/${name}:latest}
|
|
1011
|
+
pull_policy: always
|
|
793
1012
|
restart: unless-stopped
|
|
794
1013
|
mem_limit: \${WEB_MEM_LIMIT:-1g}
|
|
795
1014
|
cpus: \${WEB_CPUS:-2}
|
|
1015
|
+
# A readiness probe Coolify gates a rolling deploy on: with "Rolling
|
|
1016
|
+
# update" enabled on the resource, the new container must answer
|
|
1017
|
+
# /api/ready before the old one is retired, so a redeploy swaps in with no
|
|
1018
|
+
# gap. Without it Coolify recreates the stack \u2014 old removed, then new
|
|
1019
|
+
# started \u2014 and the board is down while the new web boots.
|
|
1020
|
+
healthcheck:
|
|
1021
|
+
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/ready').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
|
1022
|
+
interval: 30s
|
|
1023
|
+
timeout: 5s
|
|
1024
|
+
start_period: 20s
|
|
1025
|
+
retries: 3
|
|
796
1026
|
environment:
|
|
797
1027
|
# Ask Coolify for a domain on port 3000, then hand the board the same
|
|
798
1028
|
# thing with a scheme in front.
|
|
@@ -860,15 +1090,55 @@ A forum, built on [Meith](${repositoryUrl}).
|
|
|
860
1090
|
|
|
861
1091
|
## Deploy
|
|
862
1092
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
server
|
|
867
|
-
one
|
|
1093
|
+
Two paths onto [Coolify](https://coolify.io), both ending at the same
|
|
1094
|
+
\`/install\`. **Quick start** is the default and needs nothing but a push;
|
|
1095
|
+
**advanced/prebuilt** moves the build off the server, onto GitHub Actions, for
|
|
1096
|
+
a low-spec build server or a faster deploy. Pick one \u2014 a board only ever runs
|
|
1097
|
+
one of them at a time.
|
|
1098
|
+
|
|
1099
|
+
### Quick start (default)
|
|
1100
|
+
|
|
1101
|
+
Coolify builds the image itself, from this repository, every time it
|
|
1102
|
+
deploys \u2014 there is nothing to push anywhere first and no image tag to paste
|
|
1103
|
+
in. Two steps:
|
|
1104
|
+
|
|
1105
|
+
1. **Push this repository to GitHub.**
|
|
1106
|
+
|
|
1107
|
+
2. **Point Coolify at \`docker-compose.yaml\`** \u2014 a **Public Git repository**
|
|
1108
|
+
resource with **Docker Compose** as its build pack, this repository as its
|
|
1109
|
+
source. The name is Coolify's own default, so its **Compose file** field is
|
|
1110
|
+
already right when the form opens, and the file already carries Coolify's
|
|
1111
|
+
own "magic variables" for \`AUTH_SECRET\`, \`TICK_SECRET\` and the database
|
|
1112
|
+
password, generated on the first deploy and never typed in. Nothing else to
|
|
1113
|
+
set: \`docker-compose.yaml\` builds \`web\` and \`migrate\` from \`Dockerfile\`
|
|
1114
|
+
itself, so there is no \`MEITH_IMAGE\` here at all.
|
|
1115
|
+
|
|
1116
|
+
3. **Deploy, then \`/install\` on your own domain.** Coolify issues the
|
|
1117
|
+
certificate; the installer from there is the one
|
|
1118
|
+
[docs/getting-started/deployment/coolify.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/coolify.md#4-run-the-installer)
|
|
1119
|
+
walks through, screen for screen. It seals itself when it finishes, and
|
|
1120
|
+
\`/install\` answers 404 from then on \u2014 run it **against the database you
|
|
1121
|
+
are going to keep**. Every push to \`main\` after this is picked up the next
|
|
1122
|
+
time Coolify's own **Redeploy** button runs \u2014 pushing alone does not
|
|
1123
|
+
rebuild it.
|
|
1124
|
+
|
|
1125
|
+
The trade for that zero setup is a heavier build: \`Dockerfile\` installs this
|
|
1126
|
+
board's full dependency closure on the server itself, on every deploy, rather
|
|
1127
|
+
than starting from a warm base image. A 2 GB VPS can OOM on it. If that is
|
|
1128
|
+
your server, use the advanced path below instead.
|
|
1129
|
+
|
|
1130
|
+
A quick-start board never needs \`Dockerfile.prebuilt\`,
|
|
1131
|
+
\`docker-compose.prebuilt.yaml\` or \`.github/workflows/build.yml\` \u2014 delete all
|
|
1132
|
+
three.
|
|
1133
|
+
|
|
1134
|
+
### Advanced / prebuilt \u2014 for a low-spec server or a faster deploy
|
|
1135
|
+
|
|
1136
|
+
Something else builds the image ahead of time; the server only ever pulls
|
|
1137
|
+
one. Three steps, nothing to configure by hand beyond one value only you know:
|
|
868
1138
|
|
|
869
1139
|
1. **Push this repository to GitHub.** \`.github/workflows/build.yml\` builds
|
|
870
|
-
\`Dockerfile\` on every push to \`main\` and pushes the result to your
|
|
871
|
-
GitHub Container Registry, \`ghcr.io/<you>/${name}\` \u2014 using only the
|
|
1140
|
+
\`Dockerfile.prebuilt\` on every push to \`main\` and pushes the result to your
|
|
1141
|
+
own GitHub Container Registry, \`ghcr.io/<you>/${name}\` \u2014 using only the
|
|
872
1142
|
\`GITHUB_TOKEN\` every GitHub Actions run already carries. No secret to
|
|
873
1143
|
add, no registry account beyond the GitHub account you already have.
|
|
874
1144
|
|
|
@@ -879,29 +1149,26 @@ one value only you know:
|
|
|
879
1149
|
repository usually lands public already, and a private one fails
|
|
880
1150
|
Coolify's pull with an authentication error no operator can act on.
|
|
881
1151
|
|
|
882
|
-
2. **Point
|
|
1152
|
+
2. **Point Coolify at \`docker-compose.prebuilt.yaml\`** \u2014 a
|
|
883
1153
|
**Public Git repository** resource with **Docker Compose** as its build
|
|
884
|
-
pack, this repository as its source
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
\`AUTH_SECRET\`, \`TICK_SECRET\` and the database password,
|
|
888
|
-
the first deploy and never typed in. The one thing Coolify
|
|
889
|
-
generate is the image step 1 just pushed: set \`MEITH_IMAGE\` in the
|
|
1154
|
+
pack, this repository as its source, and its **Compose file** field
|
|
1155
|
+
changed from Coolify's default of \`docker-compose.yaml\` to
|
|
1156
|
+
\`docker-compose.prebuilt.yaml\`. That file carries Coolify's own "magic
|
|
1157
|
+
variables" for \`AUTH_SECRET\`, \`TICK_SECRET\` and the database password,
|
|
1158
|
+
generated on the first deploy and never typed in. The one thing Coolify
|
|
1159
|
+
cannot generate is the image step 1 just pushed: set \`MEITH_IMAGE\` in the
|
|
890
1160
|
resource's own environment to one of the two values that run's Summary
|
|
891
|
-
printed (\`docker-compose.yaml\` refuses to start without it, with
|
|
892
|
-
message saying why). \`ghcr.io/<you>/${name}:\${{ github.sha }}\` names
|
|
1161
|
+
printed (\`docker-compose.prebuilt.yaml\` refuses to start without it, with
|
|
1162
|
+
a message saying why). \`ghcr.io/<you>/${name}:\${{ github.sha }}\` names
|
|
893
1163
|
that one build and nothing else, ever; \`ghcr.io/<you>/${name}:latest\`
|
|
894
1164
|
follows \`main\` instead, so installing a plugin later is a push and a
|
|
895
|
-
**Redeploy** \u2014 the trade
|
|
896
|
-
|
|
1165
|
+
**Redeploy** \u2014 the trade this path takes, at the cost of an unrelated
|
|
1166
|
+
redeploy pulling whatever \`main\` most recently built.
|
|
897
1167
|
|
|
898
|
-
3. **Deploy, then \`/install\` on your own domain.**
|
|
899
|
-
certificate; the installer from there is the one
|
|
1168
|
+
3. **Deploy, then \`/install\` on your own domain.** Same installer, same
|
|
900
1169
|
[docs/getting-started/deployment/coolify.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/coolify.md#4-run-the-installer)
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
are going to keep**. Every push to \`main\` after this rebuilds the
|
|
904
|
-
image; Coolify's own **Redeploy** button is what actually pulls it \u2014
|
|
1170
|
+
walk-through, same one-time seal. Every push to \`main\` after this rebuilds
|
|
1171
|
+
the image; Coolify's own **Redeploy** button is what actually pulls it \u2014
|
|
905
1172
|
pushing alone does not.
|
|
906
1173
|
|
|
907
1174
|
No Docker Hub, no paid CI: GitHub Actions' free tier and GHCR are the whole
|
|
@@ -909,22 +1176,23 @@ build side of this, for a board of any size.
|
|
|
909
1176
|
|
|
910
1177
|
**Building it yourself**: works on any machine with Docker, if you would
|
|
911
1178
|
rather not use GitHub Actions for the build \u2014 push the result wherever
|
|
912
|
-
\`docker-compose.yaml\`'s \`MEITH_IMAGE\` can reach.
|
|
1179
|
+
\`docker-compose.prebuilt.yaml\`'s \`MEITH_IMAGE\` can reach.
|
|
913
1180
|
|
|
914
1181
|
\`\`\`sh
|
|
915
|
-
docker build --build-arg MEITH_VERSION=$(node -p "require('./package.json').dependencies['@meith/web']") -t ${name} .
|
|
1182
|
+
docker build -f Dockerfile.prebuilt --build-arg MEITH_VERSION=$(node -p "require('./package.json').dependencies['@meith/web']") -t ${name} .
|
|
916
1183
|
\`\`\`
|
|
917
1184
|
|
|
918
1185
|
**Without a panel**: [docs/getting-started/deployment/docker-compose.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/docker-compose.md)
|
|
919
1186
|
is the same four containers by hand \u2014 your own \`.env\`, a reverse proxy you
|
|
920
1187
|
already run, no Coolify. \`Dockerfile\` and \`docker-compose.yaml\` here are this
|
|
921
|
-
board's own version of exactly that shape.
|
|
1188
|
+
board's own version of exactly that shape (or \`Dockerfile.prebuilt\` and
|
|
1189
|
+
\`docker-compose.prebuilt.yaml\`, for the advanced path).
|
|
922
1190
|
|
|
923
|
-
Two things nothing configures for you:
|
|
1191
|
+
Two things nothing configures for you, on either path:
|
|
924
1192
|
|
|
925
1193
|
- **Mail.** Until \`MAIL_DRIVER\` and its three settings exist, every message is
|
|
926
1194
|
written to the log and delivered to nobody, so password reset fails silently.
|
|
927
|
-
- **The tick.**
|
|
1195
|
+
- **The tick.** The compose file's \`worker\` service drives it here \u2014 a small
|
|
928
1196
|
loop calling \`/api/system/tick\` once a minute, since \`@meith/web\`'s own
|
|
929
1197
|
worker package is not something a board outside the meith monorepo can
|
|
930
1198
|
depend on yet. Deploy some other way and something still has to call that
|
|
@@ -952,7 +1220,7 @@ echo "<password>" | npm run community -- user:create --username <name> --email <
|
|
|
952
1220
|
|
|
953
1221
|
## Configuring
|
|
954
1222
|
|
|
955
|
-
- **\`
|
|
1223
|
+
- **\`meith.config.ts\`** \u2014 installed themes and plugins. Everything installable
|
|
956
1224
|
is named here so the bundler can see it; nothing is found by scanning a
|
|
957
1225
|
directory at runtime.
|
|
958
1226
|
- **\`/admin\`** \u2014 settings, forums, groups, members, themes, maintenance. An
|
|
@@ -985,14 +1253,18 @@ weekly pull request bumping the actions pinned in
|
|
|
985
1253
|
\`.github/workflows/build.yml\`, which is a safe, independent update the two
|
|
986
1254
|
commands above never touch.
|
|
987
1255
|
|
|
988
|
-
|
|
989
|
-
\`
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1256
|
+
On the quick-start path there is no version to keep in sync by hand:
|
|
1257
|
+
\`Dockerfile\` runs \`npm install\` straight from this \`package.json\` on every
|
|
1258
|
+
build, so a rebuild always picks up whatever is pinned there. On the
|
|
1259
|
+
advanced/prebuilt path, that \`package.json\` change is the whole pin:
|
|
1260
|
+
\`Dockerfile.prebuilt\`'s own \`FROM\` line takes the version as a build argument,
|
|
1261
|
+
and \`.github/workflows/build.yml\` reads it straight out of \`package.json\`'s
|
|
1262
|
+
own \`@meith/web\` dependency when it rebuilds \u2014 nothing in
|
|
1263
|
+
\`Dockerfile.prebuilt\` itself to keep in sync by hand. \`--save-exact\` matters
|
|
1264
|
+
either way: npm's default \`save-prefix\` is \`^\`, and a caret range is not a
|
|
1265
|
+
legal Docker image tag for the advanced path \u2014 without it, this exact command
|
|
1266
|
+
would write \`"^0.18.0"\` and the next \`Dockerfile.prebuilt\` build would fail
|
|
1267
|
+
with \`invalid reference format\` instead of building. This
|
|
996
1268
|
project's own \`.npmrc\` sets \`save-exact=true\` for the same reason, so an
|
|
997
1269
|
\`npm install\` of anything else here \u2014 a plugin, say \u2014 stays pinned too; the
|
|
998
1270
|
build workflow also refuses to build from anything but an exact version, as
|
|
@@ -1607,7 +1879,7 @@ Scaffold a board next to this directory if you do not have one
|
|
|
1607
1879
|
npm installs a local directory as a symlink, so edits here are picked up by
|
|
1608
1880
|
the board's next build without reinstalling.
|
|
1609
1881
|
|
|
1610
|
-
Register the plugin in the board's \`
|
|
1882
|
+
Register the plugin in the board's \`meith.plugins.ts\` \u2014 the comment at
|
|
1611
1883
|
the top of that file shows the shape:
|
|
1612
1884
|
|
|
1613
1885
|
import { messages as ${camel}Messages, plugin as ${camel}Plugin } from '${name}'
|
|
@@ -1663,7 +1935,7 @@ Scaffold a board next to this directory if you do not have one
|
|
|
1663
1935
|
npm installs a local directory as a symlink, so edits here are picked up by
|
|
1664
1936
|
the board's next build without reinstalling.
|
|
1665
1937
|
|
|
1666
|
-
Register the theme in the board's \`
|
|
1938
|
+
Register the theme in the board's \`meith.config.ts\`, beside the
|
|
1667
1939
|
default entry:
|
|
1668
1940
|
|
|
1669
1941
|
import { defaultMessages } from '@meith/theme-default'
|
|
@@ -1890,7 +2162,7 @@ async function run(argv, version) {
|
|
|
1890
2162
|
}
|
|
1891
2163
|
|
|
1892
2164
|
// src/bin.ts
|
|
1893
|
-
var result = await run(process.argv.slice(2), "0.
|
|
2165
|
+
var result = await run(process.argv.slice(2), "0.26.1");
|
|
1894
2166
|
for (const line of result.lines) {
|
|
1895
2167
|
if (result.code === 0) console.log(line);
|
|
1896
2168
|
else console.error(line);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-meith",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "Scaffold a Meith board, plugin or theme — npx create-meith <name> writes a deployable board workspace; --plugin and --theme write extension workspaces built on the published kits.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
package/src/bin.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { run } from './cli'
|
|
3
3
|
|
|
4
|
-
const result = await run(process.argv.slice(2), '0.
|
|
4
|
+
const result = await run(process.argv.slice(2), '0.26.1')
|
|
5
5
|
for (const line of result.lines) {
|
|
6
6
|
if (result.code === 0) console.log(line)
|
|
7
7
|
else console.error(line)
|
|
@@ -192,7 +192,7 @@ Scaffold a board next to this directory if you do not have one
|
|
|
192
192
|
npm installs a local directory as a symlink, so edits here are picked up by
|
|
193
193
|
the board's next build without reinstalling.
|
|
194
194
|
|
|
195
|
-
Register the plugin in the board's \`
|
|
195
|
+
Register the plugin in the board's \`meith.plugins.ts\` — the comment at
|
|
196
196
|
the top of that file shows the shape:
|
|
197
197
|
|
|
198
198
|
import { messages as ${camel}Messages, plugin as ${camel}Plugin } from '${name}'
|
|
@@ -249,7 +249,7 @@ Scaffold a board next to this directory if you do not have one
|
|
|
249
249
|
npm installs a local directory as a symlink, so edits here are picked up by
|
|
250
250
|
the board's next build without reinstalling.
|
|
251
251
|
|
|
252
|
-
Register the theme in the board's \`
|
|
252
|
+
Register the theme in the board's \`meith.config.ts\`, beside the
|
|
253
253
|
default entry:
|
|
254
254
|
|
|
255
255
|
import { defaultMessages } from '@meith/theme-default'
|
package/src/scaffold.ts
CHANGED
|
@@ -65,7 +65,7 @@ const AT_ROOT_IGNORES = `# What \`forum-web ${AT_ROOT_FLAG}\` writes into this d
|
|
|
65
65
|
# alone by the build and still never committed, so it works locally and is
|
|
66
66
|
# simply absent from the deploy, which builds from what git has. Extend the
|
|
67
67
|
# board with a plugin or a theme instead — the forum loads those from
|
|
68
|
-
#
|
|
68
|
+
# meith.config.ts, and they are yours to commit. forum-web prints a
|
|
69
69
|
# warning naming any file of yours it finds there.
|
|
70
70
|
#
|
|
71
71
|
# For the rest, a build refuses rather than overwriting a file it did not
|
|
@@ -353,7 +353,9 @@ const SELF_HOST_DEPLOY_KIT = [
|
|
|
353
353
|
'.github/dependabot.yml',
|
|
354
354
|
'.github/workflows/build.yml',
|
|
355
355
|
'Dockerfile',
|
|
356
|
+
'Dockerfile.prebuilt',
|
|
356
357
|
'docker-compose.yaml',
|
|
358
|
+
'docker-compose.prebuilt.yaml',
|
|
357
359
|
'docker-entrypoint.sh',
|
|
358
360
|
'docker-healthcheck.sh',
|
|
359
361
|
] as const
|
|
@@ -460,7 +462,7 @@ save-exact=true
|
|
|
460
462
|
)
|
|
461
463
|
|
|
462
464
|
files.set(
|
|
463
|
-
'
|
|
465
|
+
'meith.config.ts',
|
|
464
466
|
`/**
|
|
465
467
|
* The board's build-time registry.
|
|
466
468
|
*
|
|
@@ -471,7 +473,7 @@ save-exact=true
|
|
|
471
473
|
* not there at all.
|
|
472
474
|
*
|
|
473
475
|
* Adding a theme is: \`npm install\` it, add a line here, redeploy. Adding a
|
|
474
|
-
* plugin is the same, through board.plugins.json and
|
|
476
|
+
* plugin is the same, through board.plugins.json and meith.plugins.ts —
|
|
475
477
|
* see docs/customization/plugins.md.
|
|
476
478
|
*/
|
|
477
479
|
import { defineForumConfig } from '@meith/web/config'
|
|
@@ -483,7 +485,7 @@ import {
|
|
|
483
485
|
LIGHT_TOKENS,
|
|
484
486
|
} from '@meith/theme-default'
|
|
485
487
|
|
|
486
|
-
import { INSTALLED_PLUGINS } from './
|
|
488
|
+
import { INSTALLED_PLUGINS } from './meith.plugins'
|
|
487
489
|
|
|
488
490
|
export default defineForumConfig({
|
|
489
491
|
themes: {
|
|
@@ -506,7 +508,7 @@ export default defineForumConfig({
|
|
|
506
508
|
files.set('board.plugins.json', `${JSON.stringify({ plugins: [] }, null, 2)}\n`)
|
|
507
509
|
|
|
508
510
|
files.set(
|
|
509
|
-
'
|
|
511
|
+
'meith.plugins.ts',
|
|
510
512
|
`/**
|
|
511
513
|
* The board's installed-plugin list.
|
|
512
514
|
*
|
|
@@ -575,7 +577,88 @@ updates:
|
|
|
575
577
|
'Dockerfile',
|
|
576
578
|
`# syntax=docker/dockerfile:1.7-labs
|
|
577
579
|
# check=skip=InvalidDefaultArgInFrom
|
|
578
|
-
# ${name}'s deploy image
|
|
580
|
+
# ${name}'s quick-start deploy image — built from source, with nothing to
|
|
581
|
+
# set up first.
|
|
582
|
+
#
|
|
583
|
+
# FROM node:26-alpine directly rather than a published Meith base image:
|
|
584
|
+
# Coolify (or a plain \`docker build\`) builds this from this repository, so
|
|
585
|
+
# there is no registry account, no image tag to paste anywhere, and no
|
|
586
|
+
# \`.github/workflows/build.yml\` run to wait on. The cost of that zero setup
|
|
587
|
+
# is that this installs the board's full dependency closure itself (see the
|
|
588
|
+
# \`npm install\` below), so a build here is heavier than \`Dockerfile.prebuilt\`'s
|
|
589
|
+
# thin delta — that image, pulled rather than built, is the trade the advanced
|
|
590
|
+
# path takes for a low-spec build server or a faster deploy (see \`README.md\`
|
|
591
|
+
# and, in the meith repository, docs/getting-started/deployment/docker-compose.md,
|
|
592
|
+
# "Custom boards").
|
|
593
|
+
#
|
|
594
|
+
# Two stages, not three: unlike the official image, this does not prune down
|
|
595
|
+
# to Next's own standalone output. The migrate role below runs \`community
|
|
596
|
+
# migrate\`, and \`community\` materializes @meith/cli's sources and runs them
|
|
597
|
+
# with tsx at the moment it runs (see the meith repository's
|
|
598
|
+
# docs/contributing/development.md, "Consuming the board from a workspace") — it needs
|
|
599
|
+
# the full, un-pruned node_modules tree this board installed, not what Next
|
|
600
|
+
# traced as reachable from the web server alone. The tick itself is driven
|
|
601
|
+
# by docker-compose.yaml's own \`worker\` service — a lightweight loop against
|
|
602
|
+
# /api/system/tick, not a compiled worker process, because @meith/worker is
|
|
603
|
+
# not published (see the meith repository's docs/contributing/release.md).
|
|
604
|
+
FROM node:26-alpine@sha256:aadf416b2cdce311a8811ba3f0608a61b77dbf997500e2eafe781b51f6a0b019 AS deps
|
|
605
|
+
WORKDIR /board
|
|
606
|
+
|
|
607
|
+
# This board's own manifest, cached independently of its source — editing
|
|
608
|
+
# meith.config.ts should not re-run npm install. Nothing warms node_modules
|
|
609
|
+
# ahead of this the way \`Dockerfile.prebuilt\`'s base image does: the full
|
|
610
|
+
# @meith/web, @meith/cli and @meith/theme-default closure this board depends
|
|
611
|
+
# on is installed here, from scratch, which is the heavier half of the
|
|
612
|
+
# quick-start trade.
|
|
613
|
+
COPY package.json ./
|
|
614
|
+
RUN npm install
|
|
615
|
+
|
|
616
|
+
FROM deps AS runtime
|
|
617
|
+
WORKDIR /board
|
|
618
|
+
COPY . .
|
|
619
|
+
|
|
620
|
+
ENV NEXT_TELEMETRY_DISABLED=1
|
|
621
|
+
ENV NODE_ENV=production
|
|
622
|
+
|
|
623
|
+
# DATA_SOURCE is scoped to this one RUN, not declared with ENV — an ENV
|
|
624
|
+
# persists into every container started from this image afterward, and this
|
|
625
|
+
# Dockerfile has no later stage to reset it in (see "Two stages, not three"
|
|
626
|
+
# above). The build needs neither a database nor a production secret (see
|
|
627
|
+
# the meith repository's docs/contributing/development.md, "Fixture mode"), but baking
|
|
628
|
+
# DATA_SOURCE=fixture into the image itself would silently force fixture
|
|
629
|
+
# mode — and with it the in-memory queue driver — at runtime too, no matter
|
|
630
|
+
# what DATABASE_URL an operator supplies to \`docker run\`.
|
|
631
|
+
RUN DATA_SOURCE=fixture npx forum-web build
|
|
632
|
+
|
|
633
|
+
ENV PORT=3000
|
|
634
|
+
ENV HOSTNAME=0.0.0.0
|
|
635
|
+
EXPOSE 3000
|
|
636
|
+
|
|
637
|
+
# node:alpine already carries a non-root "node" user; the board's own files
|
|
638
|
+
# are copied in as root above, so they need handing over before this drops
|
|
639
|
+
# privilege.
|
|
640
|
+
RUN chown -R node:node /board
|
|
641
|
+
USER node
|
|
642
|
+
|
|
643
|
+
COPY --chown=node:node docker-entrypoint.sh docker-healthcheck.sh ./
|
|
644
|
+
RUN chmod +x docker-entrypoint.sh docker-healthcheck.sh
|
|
645
|
+
|
|
646
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \\
|
|
647
|
+
CMD ["./docker-healthcheck.sh"]
|
|
648
|
+
|
|
649
|
+
ENTRYPOINT ["./docker-entrypoint.sh"]
|
|
650
|
+
`,
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
files.set(
|
|
654
|
+
'Dockerfile.prebuilt',
|
|
655
|
+
`# syntax=docker/dockerfile:1.7-labs
|
|
656
|
+
# check=skip=InvalidDefaultArgInFrom
|
|
657
|
+
# ${name}'s advanced deploy image — built by \`.github/workflows/build.yml\` and
|
|
658
|
+
# pulled by \`docker-compose.prebuilt.yaml\`. A quick-start board never builds
|
|
659
|
+
# this file directly; it can delete this file, \`docker-compose.prebuilt.yaml\`
|
|
660
|
+
# and \`.github/workflows/build.yml\` outright and keep only \`Dockerfile\` and
|
|
661
|
+
# \`docker-compose.yaml\` (see README.md, "Deploy").
|
|
579
662
|
#
|
|
580
663
|
# FROM the published framework base image — deps + framework layers only,
|
|
581
664
|
# locked to this exact release (see the meith repository's
|
|
@@ -600,7 +683,7 @@ FROM ghcr.io/meith-dev/meith-base:\${MEITH_VERSION} AS deps
|
|
|
600
683
|
WORKDIR /board
|
|
601
684
|
|
|
602
685
|
# This board's own manifest, cached independently of its source — editing
|
|
603
|
-
#
|
|
686
|
+
# meith.config.ts should not re-run npm install. The base image above
|
|
604
687
|
# already carries node_modules for @meith/web, @meith/cli and
|
|
605
688
|
# @meith/theme-default at this exact version, so installing this file on top
|
|
606
689
|
# of it only fetches what changed: a plugin newly added to \`dependencies\`,
|
|
@@ -710,10 +793,10 @@ node -e "fetch('http://127.0.0.1:3000/api/ready').then(r=>process.exit(r.ok?0:1)
|
|
|
710
793
|
|
|
711
794
|
files.set(
|
|
712
795
|
'.github/workflows/build.yml',
|
|
713
|
-
`#
|
|
714
|
-
#
|
|
715
|
-
#
|
|
716
|
-
#
|
|
796
|
+
`# The advanced/prebuilt path (see README.md, "Deploy"). Builds this board's
|
|
797
|
+
# Dockerfile.prebuilt and pushes it to your own GHCR, on every push to main.
|
|
798
|
+
# No secret to configure: GITHUB_TOKEN is provided automatically by GitHub
|
|
799
|
+
# Actions and is enough to push to ghcr.io/<this repository>.
|
|
717
800
|
name: Build and push
|
|
718
801
|
|
|
719
802
|
on:
|
|
@@ -748,7 +831,7 @@ jobs:
|
|
|
748
831
|
echo "::error::@meith/web in package.json is '$MEITH_VERSION', not an exact X.Y.Z version — that is not a legal Docker image tag. Upgrade with \\\`npm install --save-exact\\\` (see README.md, Upgrading) so this dependency always resolves to one."
|
|
749
832
|
exit 1
|
|
750
833
|
fi
|
|
751
|
-
docker build --build-arg MEITH_VERSION="$MEITH_VERSION" -t "$IMAGE:\${{ github.sha }}" -t "$IMAGE:latest" .
|
|
834
|
+
docker build -f Dockerfile.prebuilt --build-arg MEITH_VERSION="$MEITH_VERSION" -t "$IMAGE:\${{ github.sha }}" -t "$IMAGE:latest" .
|
|
752
835
|
docker push "$IMAGE:\${{ github.sha }}"
|
|
753
836
|
docker push "$IMAGE:latest"
|
|
754
837
|
|
|
@@ -789,9 +872,142 @@ jobs:
|
|
|
789
872
|
|
|
790
873
|
files.set(
|
|
791
874
|
'docker-compose.yaml',
|
|
792
|
-
`# ${name}, deployed by Coolify — the same shape as the meith
|
|
793
|
-
# docker/compose.coolify.yml: db, migrate, web, worker. See
|
|
794
|
-
# the
|
|
875
|
+
`# ${name}, quick-start deployed by Coolify — the same shape as the meith
|
|
876
|
+
# repository's own docker/compose.coolify.yml: db, migrate, web, worker. See
|
|
877
|
+
# README.md for the deploy story this file is the last step of.
|
|
878
|
+
#
|
|
879
|
+
# Coolify builds \`Dockerfile\` from this repository itself, so there is no
|
|
880
|
+
# MEITH_IMAGE to set here — every deploy is a build from source. For a
|
|
881
|
+
# low-spec build server or a faster deploy, use \`docker-compose.prebuilt.yaml\`
|
|
882
|
+
# instead, which pulls the image \`.github/workflows/build.yml\` pushes to GHCR.
|
|
883
|
+
#
|
|
884
|
+
# No published ports — Coolify's proxy routes to the container and issues
|
|
885
|
+
# the certificate. The two secrets and the database password are Coolify's
|
|
886
|
+
# own "magic variables": it fills them in on the first deploy and shows them
|
|
887
|
+
# in the panel, so nothing here needs a value typed into it. Requires Coolify
|
|
888
|
+
# v4.0.0-beta.411 or newer, which is when magic variables in a compose file
|
|
889
|
+
# from a Git source arrived.
|
|
890
|
+
services:
|
|
891
|
+
postgres:
|
|
892
|
+
image: postgres:18-alpine@sha256:d3e1620b530c944afa6e887d22eb899824da68e19c52024bf98f5220c88a65b2
|
|
893
|
+
restart: unless-stopped
|
|
894
|
+
mem_limit: \${POSTGRES_MEM_LIMIT:-1g}
|
|
895
|
+
cpus: \${POSTGRES_CPUS:-1}
|
|
896
|
+
environment:
|
|
897
|
+
POSTGRES_USER: community
|
|
898
|
+
POSTGRES_PASSWORD: $SERVICE_PASSWORD_POSTGRES
|
|
899
|
+
POSTGRES_DB: community
|
|
900
|
+
volumes:
|
|
901
|
+
- pgdata:/var/lib/postgresql
|
|
902
|
+
healthcheck:
|
|
903
|
+
test: ['CMD-SHELL', 'pg_isready -U community -d community']
|
|
904
|
+
interval: 10s
|
|
905
|
+
timeout: 5s
|
|
906
|
+
retries: 5
|
|
907
|
+
|
|
908
|
+
# Runs to completion, then exits. web waits for it, so the schema is
|
|
909
|
+
# always applied before the first request rather than racing it.
|
|
910
|
+
migrate:
|
|
911
|
+
build: .
|
|
912
|
+
image: ${name}
|
|
913
|
+
environment:
|
|
914
|
+
COMMUNITY_ROLE: migrate
|
|
915
|
+
DATABASE_URL: postgres://community:$SERVICE_PASSWORD_POSTGRES@postgres:5432/community
|
|
916
|
+
AUTH_SECRET: $SERVICE_BASE64_64_AUTH
|
|
917
|
+
TICK_SECRET: $SERVICE_BASE64_64_TICK
|
|
918
|
+
depends_on:
|
|
919
|
+
postgres:
|
|
920
|
+
condition: service_healthy
|
|
921
|
+
restart: 'no'
|
|
922
|
+
|
|
923
|
+
web:
|
|
924
|
+
build: .
|
|
925
|
+
image: ${name}
|
|
926
|
+
restart: unless-stopped
|
|
927
|
+
mem_limit: \${WEB_MEM_LIMIT:-1g}
|
|
928
|
+
cpus: \${WEB_CPUS:-2}
|
|
929
|
+
# A readiness probe Coolify gates a rolling deploy on: with "Rolling
|
|
930
|
+
# update" enabled on the resource, the new container must answer
|
|
931
|
+
# /api/ready before the old one is retired, so a redeploy swaps in with no
|
|
932
|
+
# gap. Without it Coolify recreates the stack — old removed, then new
|
|
933
|
+
# started — and the board is down while the new web boots.
|
|
934
|
+
healthcheck:
|
|
935
|
+
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/ready').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
|
936
|
+
interval: 30s
|
|
937
|
+
timeout: 5s
|
|
938
|
+
start_period: 20s
|
|
939
|
+
retries: 3
|
|
940
|
+
environment:
|
|
941
|
+
# Ask Coolify for a domain on port 3000, then hand the board the same
|
|
942
|
+
# thing with a scheme in front.
|
|
943
|
+
- SERVICE_FQDN_WEB_3000
|
|
944
|
+
- APP_URL=$SERVICE_URL_WEB
|
|
945
|
+
- DATABASE_URL=postgres://community:$SERVICE_PASSWORD_POSTGRES@postgres:5432/community
|
|
946
|
+
- AUTH_SECRET=$SERVICE_BASE64_64_AUTH
|
|
947
|
+
- TICK_SECRET=$SERVICE_BASE64_64_TICK
|
|
948
|
+
- QUEUE_DRIVER=postgres
|
|
949
|
+
- CACHE_DRIVER=next
|
|
950
|
+
- FILESTORE_DRIVER=local
|
|
951
|
+
# Left unset, mail is configured on the board itself — the installer
|
|
952
|
+
# asks on first run. Set MAIL_DRIVER here and this file wins instead.
|
|
953
|
+
- MAIL_DRIVER=\${MAIL_DRIVER:-log}
|
|
954
|
+
- MAIL_SMTP_HOST=\${MAIL_SMTP_HOST:-}
|
|
955
|
+
- MAIL_SMTP_PORT=\${MAIL_SMTP_PORT:-}
|
|
956
|
+
- MAIL_SMTP_SECURITY=\${MAIL_SMTP_SECURITY:-}
|
|
957
|
+
- MAIL_SMTP_USERNAME=\${MAIL_SMTP_USERNAME:-}
|
|
958
|
+
- MAIL_SMTP_PASSWORD=\${MAIL_SMTP_PASSWORD:-}
|
|
959
|
+
- MAIL_FROM=\${MAIL_FROM:-}
|
|
960
|
+
volumes:
|
|
961
|
+
- uploads:/app/.uploads
|
|
962
|
+
depends_on:
|
|
963
|
+
postgres:
|
|
964
|
+
condition: service_healthy
|
|
965
|
+
migrate:
|
|
966
|
+
condition: service_completed_successfully
|
|
967
|
+
|
|
968
|
+
# @meith/worker is not published (see the meith repository's
|
|
969
|
+
# docs/contributing/release.md), so there is no compiled worker binary a scaffolded
|
|
970
|
+
# board can run — this drives the tick the alternative way the meith
|
|
971
|
+
# repository documents in docs/getting-started/deployment/docker-compose.md, "Running the tick without
|
|
972
|
+
# a second set of credentials": a small loop calling /api/system/tick.
|
|
973
|
+
worker:
|
|
974
|
+
image: alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
|
|
975
|
+
restart: unless-stopped
|
|
976
|
+
mem_limit: \${WORKER_MEM_LIMIT:-64m}
|
|
977
|
+
cpus: \${WORKER_CPUS:-0.25}
|
|
978
|
+
environment:
|
|
979
|
+
TICK_SECRET: $SERVICE_BASE64_64_TICK
|
|
980
|
+
command:
|
|
981
|
+
- sh
|
|
982
|
+
- -c
|
|
983
|
+
- |
|
|
984
|
+
apk add --no-cache curl >/dev/null
|
|
985
|
+
while true; do
|
|
986
|
+
curl -fsS -m 55 -H "Authorization: Bearer $$TICK_SECRET" \\
|
|
987
|
+
http://web:3000/api/system/tick >/dev/null 2>&1 \\
|
|
988
|
+
|| echo "tick failed at $$(date -Is)"
|
|
989
|
+
sleep 60
|
|
990
|
+
done
|
|
991
|
+
depends_on:
|
|
992
|
+
- web
|
|
993
|
+
|
|
994
|
+
volumes:
|
|
995
|
+
pgdata:
|
|
996
|
+
uploads:
|
|
997
|
+
`,
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
files.set(
|
|
1001
|
+
'docker-compose.prebuilt.yaml',
|
|
1002
|
+
`# ${name}, deployed by Coolify from a prebuilt image — the advanced path: point
|
|
1003
|
+
# Coolify's compose-file at this file instead of docker-compose.yaml once
|
|
1004
|
+
# \`.github/workflows/build.yml\` has pushed an image, and set MEITH_IMAGE to
|
|
1005
|
+
# what its Summary printed. This trades the quick-start's heavier
|
|
1006
|
+
# build-on-every-deploy for a low-spec build server or a faster deploy — see
|
|
1007
|
+
# README.md, "Deploy".
|
|
1008
|
+
#
|
|
1009
|
+
# Same shape as the meith repository's own docker/compose.coolify.yml: db,
|
|
1010
|
+
# migrate, web, worker.
|
|
795
1011
|
#
|
|
796
1012
|
# No published ports — Coolify's proxy routes to the container and issues
|
|
797
1013
|
# the certificate. The two secrets and the database password are Coolify's
|
|
@@ -823,6 +1039,10 @@ services:
|
|
|
823
1039
|
# always applied before the first request rather than racing it.
|
|
824
1040
|
migrate:
|
|
825
1041
|
image: \${MEITH_IMAGE:?set this to the image the build workflow's Summary just printed, e.g. ghcr.io/<you>/${name}:latest}
|
|
1042
|
+
# Pull the tag on every deploy. Compose keeps an image it already has, so
|
|
1043
|
+
# a rebuilt \`:latest\` is otherwise never fetched and a redeploy quietly
|
|
1044
|
+
# runs the old code.
|
|
1045
|
+
pull_policy: always
|
|
826
1046
|
environment:
|
|
827
1047
|
COMMUNITY_ROLE: migrate
|
|
828
1048
|
DATABASE_URL: postgres://community:$SERVICE_PASSWORD_POSTGRES@postgres:5432/community
|
|
@@ -835,9 +1055,21 @@ services:
|
|
|
835
1055
|
|
|
836
1056
|
web:
|
|
837
1057
|
image: \${MEITH_IMAGE:?set this to the image the build workflow's Summary just printed, e.g. ghcr.io/<you>/${name}:latest}
|
|
1058
|
+
pull_policy: always
|
|
838
1059
|
restart: unless-stopped
|
|
839
1060
|
mem_limit: \${WEB_MEM_LIMIT:-1g}
|
|
840
1061
|
cpus: \${WEB_CPUS:-2}
|
|
1062
|
+
# A readiness probe Coolify gates a rolling deploy on: with "Rolling
|
|
1063
|
+
# update" enabled on the resource, the new container must answer
|
|
1064
|
+
# /api/ready before the old one is retired, so a redeploy swaps in with no
|
|
1065
|
+
# gap. Without it Coolify recreates the stack — old removed, then new
|
|
1066
|
+
# started — and the board is down while the new web boots.
|
|
1067
|
+
healthcheck:
|
|
1068
|
+
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/ready').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
|
1069
|
+
interval: 30s
|
|
1070
|
+
timeout: 5s
|
|
1071
|
+
start_period: 20s
|
|
1072
|
+
retries: 3
|
|
841
1073
|
environment:
|
|
842
1074
|
# Ask Coolify for a domain on port 3000, then hand the board the same
|
|
843
1075
|
# thing with a scheme in front.
|
|
@@ -906,15 +1138,55 @@ A forum, built on [Meith](${repositoryUrl}).
|
|
|
906
1138
|
|
|
907
1139
|
## Deploy
|
|
908
1140
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
server
|
|
913
|
-
one
|
|
1141
|
+
Two paths onto [Coolify](https://coolify.io), both ending at the same
|
|
1142
|
+
\`/install\`. **Quick start** is the default and needs nothing but a push;
|
|
1143
|
+
**advanced/prebuilt** moves the build off the server, onto GitHub Actions, for
|
|
1144
|
+
a low-spec build server or a faster deploy. Pick one — a board only ever runs
|
|
1145
|
+
one of them at a time.
|
|
1146
|
+
|
|
1147
|
+
### Quick start (default)
|
|
1148
|
+
|
|
1149
|
+
Coolify builds the image itself, from this repository, every time it
|
|
1150
|
+
deploys — there is nothing to push anywhere first and no image tag to paste
|
|
1151
|
+
in. Two steps:
|
|
1152
|
+
|
|
1153
|
+
1. **Push this repository to GitHub.**
|
|
1154
|
+
|
|
1155
|
+
2. **Point Coolify at \`docker-compose.yaml\`** — a **Public Git repository**
|
|
1156
|
+
resource with **Docker Compose** as its build pack, this repository as its
|
|
1157
|
+
source. The name is Coolify's own default, so its **Compose file** field is
|
|
1158
|
+
already right when the form opens, and the file already carries Coolify's
|
|
1159
|
+
own "magic variables" for \`AUTH_SECRET\`, \`TICK_SECRET\` and the database
|
|
1160
|
+
password, generated on the first deploy and never typed in. Nothing else to
|
|
1161
|
+
set: \`docker-compose.yaml\` builds \`web\` and \`migrate\` from \`Dockerfile\`
|
|
1162
|
+
itself, so there is no \`MEITH_IMAGE\` here at all.
|
|
1163
|
+
|
|
1164
|
+
3. **Deploy, then \`/install\` on your own domain.** Coolify issues the
|
|
1165
|
+
certificate; the installer from there is the one
|
|
1166
|
+
[docs/getting-started/deployment/coolify.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/coolify.md#4-run-the-installer)
|
|
1167
|
+
walks through, screen for screen. It seals itself when it finishes, and
|
|
1168
|
+
\`/install\` answers 404 from then on — run it **against the database you
|
|
1169
|
+
are going to keep**. Every push to \`main\` after this is picked up the next
|
|
1170
|
+
time Coolify's own **Redeploy** button runs — pushing alone does not
|
|
1171
|
+
rebuild it.
|
|
1172
|
+
|
|
1173
|
+
The trade for that zero setup is a heavier build: \`Dockerfile\` installs this
|
|
1174
|
+
board's full dependency closure on the server itself, on every deploy, rather
|
|
1175
|
+
than starting from a warm base image. A 2 GB VPS can OOM on it. If that is
|
|
1176
|
+
your server, use the advanced path below instead.
|
|
1177
|
+
|
|
1178
|
+
A quick-start board never needs \`Dockerfile.prebuilt\`,
|
|
1179
|
+
\`docker-compose.prebuilt.yaml\` or \`.github/workflows/build.yml\` — delete all
|
|
1180
|
+
three.
|
|
1181
|
+
|
|
1182
|
+
### Advanced / prebuilt — for a low-spec server or a faster deploy
|
|
1183
|
+
|
|
1184
|
+
Something else builds the image ahead of time; the server only ever pulls
|
|
1185
|
+
one. Three steps, nothing to configure by hand beyond one value only you know:
|
|
914
1186
|
|
|
915
1187
|
1. **Push this repository to GitHub.** \`.github/workflows/build.yml\` builds
|
|
916
|
-
\`Dockerfile\` on every push to \`main\` and pushes the result to your
|
|
917
|
-
GitHub Container Registry, \`ghcr.io/<you>/${name}\` — using only the
|
|
1188
|
+
\`Dockerfile.prebuilt\` on every push to \`main\` and pushes the result to your
|
|
1189
|
+
own GitHub Container Registry, \`ghcr.io/<you>/${name}\` — using only the
|
|
918
1190
|
\`GITHUB_TOKEN\` every GitHub Actions run already carries. No secret to
|
|
919
1191
|
add, no registry account beyond the GitHub account you already have.
|
|
920
1192
|
|
|
@@ -925,29 +1197,26 @@ one value only you know:
|
|
|
925
1197
|
repository usually lands public already, and a private one fails
|
|
926
1198
|
Coolify's pull with an authentication error no operator can act on.
|
|
927
1199
|
|
|
928
|
-
2. **Point
|
|
1200
|
+
2. **Point Coolify at \`docker-compose.prebuilt.yaml\`** — a
|
|
929
1201
|
**Public Git repository** resource with **Docker Compose** as its build
|
|
930
|
-
pack, this repository as its source
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
\`AUTH_SECRET\`, \`TICK_SECRET\` and the database password,
|
|
934
|
-
the first deploy and never typed in. The one thing Coolify
|
|
935
|
-
generate is the image step 1 just pushed: set \`MEITH_IMAGE\` in the
|
|
1202
|
+
pack, this repository as its source, and its **Compose file** field
|
|
1203
|
+
changed from Coolify's default of \`docker-compose.yaml\` to
|
|
1204
|
+
\`docker-compose.prebuilt.yaml\`. That file carries Coolify's own "magic
|
|
1205
|
+
variables" for \`AUTH_SECRET\`, \`TICK_SECRET\` and the database password,
|
|
1206
|
+
generated on the first deploy and never typed in. The one thing Coolify
|
|
1207
|
+
cannot generate is the image step 1 just pushed: set \`MEITH_IMAGE\` in the
|
|
936
1208
|
resource's own environment to one of the two values that run's Summary
|
|
937
|
-
printed (\`docker-compose.yaml\` refuses to start without it, with
|
|
938
|
-
message saying why). \`ghcr.io/<you>/${name}:\${{ github.sha }}\` names
|
|
1209
|
+
printed (\`docker-compose.prebuilt.yaml\` refuses to start without it, with
|
|
1210
|
+
a message saying why). \`ghcr.io/<you>/${name}:\${{ github.sha }}\` names
|
|
939
1211
|
that one build and nothing else, ever; \`ghcr.io/<you>/${name}:latest\`
|
|
940
1212
|
follows \`main\` instead, so installing a plugin later is a push and a
|
|
941
|
-
**Redeploy** — the trade
|
|
942
|
-
|
|
1213
|
+
**Redeploy** — the trade this path takes, at the cost of an unrelated
|
|
1214
|
+
redeploy pulling whatever \`main\` most recently built.
|
|
943
1215
|
|
|
944
|
-
3. **Deploy, then \`/install\` on your own domain.**
|
|
945
|
-
certificate; the installer from there is the one
|
|
1216
|
+
3. **Deploy, then \`/install\` on your own domain.** Same installer, same
|
|
946
1217
|
[docs/getting-started/deployment/coolify.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/coolify.md#4-run-the-installer)
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
are going to keep**. Every push to \`main\` after this rebuilds the
|
|
950
|
-
image; Coolify's own **Redeploy** button is what actually pulls it —
|
|
1218
|
+
walk-through, same one-time seal. Every push to \`main\` after this rebuilds
|
|
1219
|
+
the image; Coolify's own **Redeploy** button is what actually pulls it —
|
|
951
1220
|
pushing alone does not.
|
|
952
1221
|
|
|
953
1222
|
No Docker Hub, no paid CI: GitHub Actions' free tier and GHCR are the whole
|
|
@@ -955,22 +1224,23 @@ build side of this, for a board of any size.
|
|
|
955
1224
|
|
|
956
1225
|
**Building it yourself**: works on any machine with Docker, if you would
|
|
957
1226
|
rather not use GitHub Actions for the build — push the result wherever
|
|
958
|
-
\`docker-compose.yaml\`'s \`MEITH_IMAGE\` can reach.
|
|
1227
|
+
\`docker-compose.prebuilt.yaml\`'s \`MEITH_IMAGE\` can reach.
|
|
959
1228
|
|
|
960
1229
|
\`\`\`sh
|
|
961
|
-
docker build --build-arg MEITH_VERSION=$(node -p "require('./package.json').dependencies['@meith/web']") -t ${name} .
|
|
1230
|
+
docker build -f Dockerfile.prebuilt --build-arg MEITH_VERSION=$(node -p "require('./package.json').dependencies['@meith/web']") -t ${name} .
|
|
962
1231
|
\`\`\`
|
|
963
1232
|
|
|
964
1233
|
**Without a panel**: [docs/getting-started/deployment/docker-compose.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/docker-compose.md)
|
|
965
1234
|
is the same four containers by hand — your own \`.env\`, a reverse proxy you
|
|
966
1235
|
already run, no Coolify. \`Dockerfile\` and \`docker-compose.yaml\` here are this
|
|
967
|
-
board's own version of exactly that shape.
|
|
1236
|
+
board's own version of exactly that shape (or \`Dockerfile.prebuilt\` and
|
|
1237
|
+
\`docker-compose.prebuilt.yaml\`, for the advanced path).
|
|
968
1238
|
|
|
969
|
-
Two things nothing configures for you:
|
|
1239
|
+
Two things nothing configures for you, on either path:
|
|
970
1240
|
|
|
971
1241
|
- **Mail.** Until \`MAIL_DRIVER\` and its three settings exist, every message is
|
|
972
1242
|
written to the log and delivered to nobody, so password reset fails silently.
|
|
973
|
-
- **The tick.**
|
|
1243
|
+
- **The tick.** The compose file's \`worker\` service drives it here — a small
|
|
974
1244
|
loop calling \`/api/system/tick\` once a minute, since \`@meith/web\`'s own
|
|
975
1245
|
worker package is not something a board outside the meith monorepo can
|
|
976
1246
|
depend on yet. Deploy some other way and something still has to call that
|
|
@@ -998,7 +1268,7 @@ echo "<password>" | npm run community -- user:create --username <name> --email <
|
|
|
998
1268
|
|
|
999
1269
|
## Configuring
|
|
1000
1270
|
|
|
1001
|
-
- **\`
|
|
1271
|
+
- **\`meith.config.ts\`** — installed themes and plugins. Everything installable
|
|
1002
1272
|
is named here so the bundler can see it; nothing is found by scanning a
|
|
1003
1273
|
directory at runtime.
|
|
1004
1274
|
- **\`/admin\`** — settings, forums, groups, members, themes, maintenance. An
|
|
@@ -1031,14 +1301,18 @@ weekly pull request bumping the actions pinned in
|
|
|
1031
1301
|
\`.github/workflows/build.yml\`, which is a safe, independent update the two
|
|
1032
1302
|
commands above never touch.
|
|
1033
1303
|
|
|
1034
|
-
|
|
1035
|
-
\`
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1304
|
+
On the quick-start path there is no version to keep in sync by hand:
|
|
1305
|
+
\`Dockerfile\` runs \`npm install\` straight from this \`package.json\` on every
|
|
1306
|
+
build, so a rebuild always picks up whatever is pinned there. On the
|
|
1307
|
+
advanced/prebuilt path, that \`package.json\` change is the whole pin:
|
|
1308
|
+
\`Dockerfile.prebuilt\`'s own \`FROM\` line takes the version as a build argument,
|
|
1309
|
+
and \`.github/workflows/build.yml\` reads it straight out of \`package.json\`'s
|
|
1310
|
+
own \`@meith/web\` dependency when it rebuilds — nothing in
|
|
1311
|
+
\`Dockerfile.prebuilt\` itself to keep in sync by hand. \`--save-exact\` matters
|
|
1312
|
+
either way: npm's default \`save-prefix\` is \`^\`, and a caret range is not a
|
|
1313
|
+
legal Docker image tag for the advanced path — without it, this exact command
|
|
1314
|
+
would write \`"^0.18.0"\` and the next \`Dockerfile.prebuilt\` build would fail
|
|
1315
|
+
with \`invalid reference format\` instead of building. This
|
|
1042
1316
|
project's own \`.npmrc\` sets \`save-exact=true\` for the same reason, so an
|
|
1043
1317
|
\`npm install\` of anything else here — a plugin, say — stays pinned too; the
|
|
1044
1318
|
build workflow also refuses to build from anything but an exact version, as
|