create-meith 0.26.0 → 0.27.0
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 +387 -73
- package/package.json +1 -1
- package/src/bin.ts +1 -1
- package/src/cli.ts +1 -1
- package/src/scaffold-extension.ts +1 -1
- package/src/scaffold.ts +386 -70
package/dist/bin.mjs
CHANGED
|
@@ -23,7 +23,7 @@ var MATERIALIZED_AT_ROOT = [
|
|
|
23
23
|
"tsconfig.json",
|
|
24
24
|
"next-env.d.ts"
|
|
25
25
|
];
|
|
26
|
-
var VERCEL_BUILD_COMMAND = `
|
|
26
|
+
var VERCEL_BUILD_COMMAND = `meith migrate && forum-web build ${AT_ROOT_FLAG}`;
|
|
27
27
|
var TICK_PATH = "/api/system/tick";
|
|
28
28
|
var TICK_SCHEDULE = "0 3 * * *";
|
|
29
29
|
var RESEND_SENDER_MAILBOX = "noreply";
|
|
@@ -85,7 +85,7 @@ var ENV_DATABASE_URL_PROSE = `# Your Postgres connection string.
|
|
|
85
85
|
# database rather than the cause. Your own Postgres, with a fixed number of
|
|
86
86
|
# processes in front of it, does not need one.`;
|
|
87
87
|
var ENV_DIRECT_DATABASE_URL_PROSE = `# The other half of that pair: the DIRECT (non-pooler) string, used only by
|
|
88
|
-
# \`
|
|
88
|
+
# \`meith migrate\` and \`meith backup\`. Migrations hold a session-level
|
|
89
89
|
# advisory lock so that two deploys landing together queue instead of both
|
|
90
90
|
# applying the same migration, and a transaction-mode pooler cannot hold that
|
|
91
91
|
# lock: it takes the connection back the moment the lock statement ends, which
|
|
@@ -213,7 +213,7 @@ DATABASE_URL=
|
|
|
213
213
|
${ENV_DIRECT_DATABASE_URL_PROSE}
|
|
214
214
|
#
|
|
215
215
|
# On Vercel this is not optional, and it is no longer yours to copy. DATABASE_URL
|
|
216
|
-
# here is the pooler string, the build runs \`
|
|
216
|
+
# here is the pooler string, the build runs \`meith migrate\` against it, and
|
|
217
217
|
# /install takes the second of those two session locks on first run. Left blank,
|
|
218
218
|
# the board reads Neon's own direct string \u2014 \`DATABASE_URL_UNPOOLED\` first, then
|
|
219
219
|
# \`POSTGRES_URL_NON_POOLING\` \u2014 and refuses to boot if neither is there, naming
|
|
@@ -250,7 +250,7 @@ CRON_SECRET=
|
|
|
250
250
|
#
|
|
251
251
|
# BLOB_READ_WRITE_TOKEN is the other way in, and you make it yourself on the
|
|
252
252
|
# store. Set it when something has to reach the store from OUTSIDE a Vercel
|
|
253
|
-
# deployment \u2014 \`
|
|
253
|
+
# deployment \u2014 \`meith backup\` run on your own machine is the case that
|
|
254
254
|
# matters \u2014 because there is no OIDC identity there to borrow. Set both and the
|
|
255
255
|
# board prefers the store id, unless the token names a different store, in which
|
|
256
256
|
# case the token wins: naming another store is a deliberate act.
|
|
@@ -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
|
];
|
|
@@ -399,7 +401,7 @@ function scaffold(options) {
|
|
|
399
401
|
dev: `forum-web dev${atRootFlag}`,
|
|
400
402
|
build: `forum-web build${atRootFlag}`,
|
|
401
403
|
start: `forum-web start${atRootFlag}`,
|
|
402
|
-
|
|
404
|
+
meith: "meith"
|
|
403
405
|
},
|
|
404
406
|
dependencies: {
|
|
405
407
|
"@meith/web": version,
|
|
@@ -487,7 +489,7 @@ export default defineForumConfig({
|
|
|
487
489
|
* ]
|
|
488
490
|
*
|
|
489
491
|
* and the matching entry in board.plugins.json, which is what
|
|
490
|
-
* \`
|
|
492
|
+
* \`meith plugin:add\`/\`plugin:remove\` read inside the monorepo \u2014 kept
|
|
491
493
|
* here too so the two files agree about what is installed.
|
|
492
494
|
*/
|
|
493
495
|
import type { InstalledPlugin } from '@meith/web/config'
|
|
@@ -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 \`meith
|
|
556
|
+
# migrate\`, and \`meith\` 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
|
|
@@ -546,8 +628,8 @@ updates:
|
|
|
546
628
|
# of minutes rather than a cold toolchain build.
|
|
547
629
|
#
|
|
548
630
|
# Two stages, not three: unlike the official image, this does not prune down
|
|
549
|
-
# to Next's own standalone output. The migrate role below runs \`
|
|
550
|
-
# migrate\`, and \`
|
|
631
|
+
# to Next's own standalone output. The migrate role below runs \`meith
|
|
632
|
+
# migrate\`, and \`meith\` materializes @meith/cli's sources and runs them
|
|
551
633
|
# with tsx at the moment it runs (see the meith repository's
|
|
552
634
|
# docs/contributing/development.md, "Consuming the board from a workspace") \u2014 it needs
|
|
553
635
|
# the full, un-pruned node_modules tree this board installed, not what Next
|
|
@@ -617,22 +699,22 @@ ENTRYPOINT ["./docker-entrypoint.sh"]
|
|
|
617
699
|
set -e
|
|
618
700
|
|
|
619
701
|
# An explicit command wins over the role, the same as the official image \u2014
|
|
620
|
-
# \`docker run <image> node_modules/.bin/
|
|
702
|
+
# \`docker run <image> node_modules/.bin/meith --help\` should still run
|
|
621
703
|
# the CLI rather than silently starting the web server.
|
|
622
704
|
if [ "$#" -gt 0 ]; then
|
|
623
705
|
exec "$@"
|
|
624
706
|
fi
|
|
625
707
|
|
|
626
|
-
case "\${
|
|
708
|
+
case "\${MEITH_ROLE:-web}" in
|
|
627
709
|
migrate)
|
|
628
710
|
# Runs to completion and exits; compose's one-shot service waits on it.
|
|
629
|
-
exec node_modules/.bin/
|
|
711
|
+
exec node_modules/.bin/meith migrate
|
|
630
712
|
;;
|
|
631
713
|
web)
|
|
632
714
|
exec node_modules/.bin/forum-web start
|
|
633
715
|
;;
|
|
634
716
|
*)
|
|
635
|
-
echo "Unknown
|
|
717
|
+
echo "Unknown MEITH_ROLE: \${MEITH_ROLE}. Expected 'web' or 'migrate'." >&2
|
|
636
718
|
exit 1
|
|
637
719
|
;;
|
|
638
720
|
esac
|
|
@@ -646,7 +728,7 @@ esac
|
|
|
646
728
|
# a health probe taken while it runs has no opinion.
|
|
647
729
|
set -e
|
|
648
730
|
|
|
649
|
-
if [ "\${
|
|
731
|
+
if [ "\${MEITH_ROLE:-web}" = "migrate" ]; then
|
|
650
732
|
exit 0
|
|
651
733
|
fi
|
|
652
734
|
|
|
@@ -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
|
+
MEITH_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,8 +992,12 @@ 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
|
+
MEITH_ROLE: migrate
|
|
783
1001
|
DATABASE_URL: postgres://community:$SERVICE_PASSWORD_POSTGRES@postgres:5432/community
|
|
784
1002
|
AUTH_SECRET: $SERVICE_BASE64_64_AUTH
|
|
785
1003
|
TICK_SECRET: $SERVICE_BASE64_64_TICK
|
|
@@ -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,26 +1176,27 @@ 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
|
|
931
|
-
route (or run \`
|
|
1199
|
+
route (or run \`meith task:run\`) every minute, or nothing catches up
|
|
932
1200
|
and nothing errors.
|
|
933
1201
|
|
|
934
1202
|
## Local
|
|
@@ -946,8 +1214,8 @@ Posting needs Postgres. Copy \`.env.example\` to \`.env.local\`, set
|
|
|
946
1214
|
\`DATABASE_URL\` and the two secrets in it, then:
|
|
947
1215
|
|
|
948
1216
|
\`\`\`sh
|
|
949
|
-
npm run
|
|
950
|
-
echo "<password>" | npm run
|
|
1217
|
+
npm run meith -- migrate
|
|
1218
|
+
echo "<password>" | npm run meith -- user:create --username <name> --email <address> --group administrators
|
|
951
1219
|
\`\`\`
|
|
952
1220
|
|
|
953
1221
|
## Configuring
|
|
@@ -958,9 +1226,51 @@ echo "<password>" | npm run community -- user:create --username <name> --email <
|
|
|
958
1226
|
- **\`/admin\`** \u2014 settings, forums, groups, members, themes, maintenance. An
|
|
959
1227
|
administrator re-enters their password to get in, and again for anything
|
|
960
1228
|
destructive.
|
|
961
|
-
- **\`npm run
|
|
1229
|
+
- **\`npm run meith -- --help\`** \u2014 the operator CLI. Everything the panel does
|
|
962
1230
|
and a few things it cannot, without a browser.
|
|
963
1231
|
|
|
1232
|
+
## Installing plugins and themes
|
|
1233
|
+
|
|
1234
|
+
Nothing installs into a running container \u2014 a plugin or theme has to be
|
|
1235
|
+
built into the image, the same as any other dependency:
|
|
1236
|
+
|
|
1237
|
+
1. **In this repository**, install it:
|
|
1238
|
+
|
|
1239
|
+
\`\`\`sh
|
|
1240
|
+
npm install --save-exact @meith/plugin-dues
|
|
1241
|
+
\`\`\`
|
|
1242
|
+
|
|
1243
|
+
(a theme is the same command with its own package, e.g.
|
|
1244
|
+
\`@meith/theme-midnight\`).
|
|
1245
|
+
|
|
1246
|
+
2. **Register it.** A **theme** goes in \`meith.config.ts\`, in the \`themes\`
|
|
1247
|
+
map, following the shape of the \`default\` entry already there. A
|
|
1248
|
+
**plugin** goes in \`meith.plugins.ts\`: import its \`plugin\` and
|
|
1249
|
+
\`messages\` exports and add \`{ key, enabled: true, plugin, messages }\`
|
|
1250
|
+
to \`INSTALLED_PLUGINS\` \u2014 or run
|
|
1251
|
+
|
|
1252
|
+
\`\`\`sh
|
|
1253
|
+
npm run meith -- plugin:add @meith/plugin-dues
|
|
1254
|
+
\`\`\`
|
|
1255
|
+
|
|
1256
|
+
which edits \`board.plugins.json\` and regenerates \`meith.plugins.ts\`
|
|
1257
|
+
for you.
|
|
1258
|
+
|
|
1259
|
+
3. **Commit and push**, then **Redeploy** from Coolify \u2014 pushing alone does
|
|
1260
|
+
not rebuild. Quick start builds the new image on that redeploy; advanced/prebuilt
|
|
1261
|
+
waits for \`.github/workflows/build.yml\` to finish first, and Redeploy is
|
|
1262
|
+
what actually pulls the result.
|
|
1263
|
+
|
|
1264
|
+
4. **Once it is up, run its migrations one time:**
|
|
1265
|
+
|
|
1266
|
+
\`\`\`sh
|
|
1267
|
+
docker compose run --rm web meith upgrade
|
|
1268
|
+
\`\`\`
|
|
1269
|
+
|
|
1270
|
+
See [docs/customization/plugins.md](${repositoryUrl}/blob/main/docs/customization/plugins.md)
|
|
1271
|
+
and [docs/customization/themes.md](${repositoryUrl}/blob/main/docs/customization/themes.md)
|
|
1272
|
+
for the full reference.
|
|
1273
|
+
|
|
964
1274
|
## Upgrading
|
|
965
1275
|
|
|
966
1276
|
\`\`\`sh
|
|
@@ -985,19 +1295,23 @@ weekly pull request bumping the actions pinned in
|
|
|
985
1295
|
\`.github/workflows/build.yml\`, which is a safe, independent update the two
|
|
986
1296
|
commands above never touch.
|
|
987
1297
|
|
|
988
|
-
|
|
989
|
-
\`
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1298
|
+
On the quick-start path there is no version to keep in sync by hand:
|
|
1299
|
+
\`Dockerfile\` runs \`npm install\` straight from this \`package.json\` on every
|
|
1300
|
+
build, so a rebuild always picks up whatever is pinned there. On the
|
|
1301
|
+
advanced/prebuilt path, that \`package.json\` change is the whole pin:
|
|
1302
|
+
\`Dockerfile.prebuilt\`'s own \`FROM\` line takes the version as a build argument,
|
|
1303
|
+
and \`.github/workflows/build.yml\` reads it straight out of \`package.json\`'s
|
|
1304
|
+
own \`@meith/web\` dependency when it rebuilds \u2014 nothing in
|
|
1305
|
+
\`Dockerfile.prebuilt\` itself to keep in sync by hand. \`--save-exact\` matters
|
|
1306
|
+
either way: npm's default \`save-prefix\` is \`^\`, and a caret range is not a
|
|
1307
|
+
legal Docker image tag for the advanced path \u2014 without it, this exact command
|
|
1308
|
+
would write \`"^0.18.0"\` and the next \`Dockerfile.prebuilt\` build would fail
|
|
1309
|
+
with \`invalid reference format\` instead of building. This
|
|
996
1310
|
project's own \`.npmrc\` sets \`save-exact=true\` for the same reason, so an
|
|
997
1311
|
\`npm install\` of anything else here \u2014 a plugin, say \u2014 stays pinned too; the
|
|
998
1312
|
build workflow also refuses to build from anything but an exact version, as
|
|
999
1313
|
a second line of defense. Once the rebuilt image is deployed, run
|
|
1000
|
-
\`npm run
|
|
1314
|
+
\`npm run meith -- upgrade\` against it for the plugin migrations \u2014 see
|
|
1001
1315
|
[the operator CLI](${repositoryUrl}/blob/main/docs/guides/operations/operating.md#the-operator-cli)
|
|
1002
1316
|
for running it against this deployment.
|
|
1003
1317
|
|
|
@@ -1240,9 +1554,9 @@ A board must stay movable, and the Blob store is the one part of this shape that
|
|
|
1240
1554
|
is not portable: Neon and Upstash hand out ordinary Postgres and Redis strings
|
|
1241
1555
|
that any host accepts, but a Vercel Blob store is reachable only through Vercel's
|
|
1242
1556
|
own API and there is no bucket to sync out of it. **The uploads are the thing you
|
|
1243
|
-
have to carry out deliberately, and \`
|
|
1557
|
+
have to carry out deliberately, and \`meith backup\` is how.**
|
|
1244
1558
|
|
|
1245
|
-
Under \`FILESTORE_DRIVER=blob\`, \`
|
|
1559
|
+
Under \`FILESTORE_DRIVER=blob\`, \`meith backup\` includes the uploads **by
|
|
1246
1560
|
default** \u2014 it walks the Blob store, pulls every object, and puts them in the
|
|
1247
1561
|
bundle beside the database dump. This is the opposite of the \`s3\` default, which
|
|
1248
1562
|
skips them, because a bucket has its own backup story you can drive yourself and
|
|
@@ -1253,7 +1567,7 @@ DATABASE_URL=\u2026 # Neon's pooled string
|
|
|
1253
1567
|
DIRECT_DATABASE_URL=\u2026 # Neon's DATABASE_URL_UNPOOLED
|
|
1254
1568
|
FILESTORE_DRIVER=blob
|
|
1255
1569
|
BLOB_READ_WRITE_TOKEN=\u2026 # create one on the store; see below
|
|
1256
|
-
npm run
|
|
1570
|
+
npm run meith -- backup
|
|
1257
1571
|
\`\`\`
|
|
1258
1572
|
|
|
1259
1573
|
Run that from a checkout of this repository, with those four values in the
|
|
@@ -1274,10 +1588,10 @@ so the same bundle moves the board either onward or away:
|
|
|
1274
1588
|
|
|
1275
1589
|
\`\`\`sh
|
|
1276
1590
|
# onto a self-hosted board with a bucket
|
|
1277
|
-
FILESTORE_DRIVER=s3 S3_BUCKET=\u2026 RESTORE_DATABASE_URL=\u2026 npm run
|
|
1591
|
+
FILESTORE_DRIVER=s3 S3_BUCKET=\u2026 RESTORE_DATABASE_URL=\u2026 npm run meith -- restore bundle.tar.gz
|
|
1278
1592
|
|
|
1279
1593
|
# onto a board that keeps uploads on its own disk
|
|
1280
|
-
RESTORE_DATABASE_URL=\u2026 npm run
|
|
1594
|
+
RESTORE_DATABASE_URL=\u2026 npm run meith -- restore bundle.tar.gz --uploads-dir ./uploads
|
|
1281
1595
|
\`\`\`
|
|
1282
1596
|
|
|
1283
1597
|
Take one before you need it. A Blob store deleted with the Vercel project takes
|
|
@@ -1621,7 +1935,7 @@ and add the matching entry to \`board.plugins.json\`:
|
|
|
1621
1935
|
{ "plugins": [{ "key": "${name}", "package": "${name}", "enabled": true }] }
|
|
1622
1936
|
|
|
1623
1937
|
Rebuild the board (\`npm run build\`) and, because this plugin ships a
|
|
1624
|
-
migration, run \`npx
|
|
1938
|
+
migration, run \`npx meith migrate\`. The plugin then appears under
|
|
1625
1939
|
**Admin \u2192 Plugins**.
|
|
1626
1940
|
|
|
1627
1941
|
## Publish and list it
|
|
@@ -1880,7 +2194,7 @@ async function run(argv, version) {
|
|
|
1880
2194
|
...kind === null ? [
|
|
1881
2195
|
"Then set DATABASE_URL, AUTH_SECRET and TICK_SECRET and deploy.",
|
|
1882
2196
|
"Something must run the tick every minute \u2014 the worker process, or",
|
|
1883
|
-
"`
|
|
2197
|
+
"`meith task:run`. Without it nothing catches up, and nothing errors."
|
|
1884
2198
|
] : [
|
|
1885
2199
|
`Then follow README.md \u2014 it walks through running the ${kind} inside a`,
|
|
1886
2200
|
"scaffolded board and submitting it to the meith.dev marketplace."
|
|
@@ -1890,7 +2204,7 @@ async function run(argv, version) {
|
|
|
1890
2204
|
}
|
|
1891
2205
|
|
|
1892
2206
|
// src/bin.ts
|
|
1893
|
-
var result = await run(process.argv.slice(2), "0.
|
|
2207
|
+
var result = await run(process.argv.slice(2), "0.27.0");
|
|
1894
2208
|
for (const line of result.lines) {
|
|
1895
2209
|
if (result.code === 0) console.log(line);
|
|
1896
2210
|
else console.error(line);
|