(core) add machinery for self-managed flavor of Grist

Summary:
Currently, we have two ways that we deliver Grist. One is grist-core,
which has simple defaults and is relatively easy for third parties to
deploy. The second is our internal build for our SaaS, which is the
opposite. For self-managed Grist, a planned paid on-premise version
of Grist, I adopt the following approach:

 * Use the `grist-core` build mechanism, extending it to accept an
   overlay of extra code if present.
 * Extra code is supplied in a self-contained `ext` directory, with
   an `ext/app` directory that is of same structure as core `app`
   and `stubs/app`.
 * The `ext` directory also contains information about extra
   node dependencies needed beyond that of `grist-core`.
 * The `ext` directory is contained within our monorepo rather than
   `grist-core` since it may contain material not under the Apache
   license.

Docker builds are achieved in our monorepo by using the `--build-context`
functionality to add in `ext` during the regular `grist-core` build:

```
docker buildx build --load -t gristlabs/grist-ee --build-context=ext=../ext .
```

Incremental builds in our monorepo are achieved with the `build_core.sh` helper,
like:

```
buildtools/build_core.sh /tmp/self-managed
cd /tmp/self-managed
yarn start
```

The initial `ext` directory contains material for snapshotting to S3.
If you build the docker image as above, and have S3 access, you can
do something like:

```
docker run -p 8484:8484 --env GRIST_SESSION_SECRET=a-secret \
  --env GRIST_DOCS_S3_BUCKET=grist-docs-test \
  --env GRIST_DOCS_S3_PREFIX=self-managed \
  -v $HOME/.aws:/root/.aws -it gristlabs/grist-ee
```

This will start a version of Grist that is like `grist-core` but with
S3 snapshots enabled. To release this code to `grist-core`, it would
just need to move from `ext/app` to `app` within core.

I tried a lot of ways of organizing self-managed Grist, and this was
what made me happiest. There are a lot of trade-offs, but here is what
I was looking for:

 * Only OSS-code in grist-core. Adding mixed-license material there
   feels unfair to people already working with the repo. That said,
   a possible future is to move away from our private monorepo to
   a public mixed-licence repo, which could have the same relationship
   with grist-core as the monorepo has.
 * Minimal differences between self-managed builds and one of our
   existing builds, ideally hewing as close to grist-core as possible
   for ease of documentation, debugging, and maintenance.
 * Ideally, docker builds without copying files around (the new
   `--build-context` functionality made that possible).
 * Compatibility with monorepo build.

Expressing dependencies of the extra code in `ext` proved tricky to
do in a clean way. Yarn/npm fought me every step of the way - everything
related to optional dependencies was unsatisfactory in some respect.
Yarn2 is flexible but smells like it might be overreach. In the end,
organizing to install non-core dependencies one directory up from the
main build was a good simple trick that saved my bacon.

This diff gets us to the point of building `grist-ee` images conveniently,
but there isn't a public repo people can go look at to see its source. This
could be generated by taking `grist-core`, adding the `ext` directory
to it, and pushing to a distinct repository. I'm not in a hurry to do that,
since a PR to that repo would be hard to sync with our monorepo and
`grist-core`. Also, we don't have any licensing text ready for the `ext`
directory. So leaving that for future work.

Test Plan: manual

Reviewers: georgegevoian, alexmojaki

Reviewed By: georgegevoian, alexmojaki

Differential Revision: https://phab.getgrist.com/D3415
This commit is contained in:
Paul Fitzpatrick
2022-05-12 11:24:48 -04:00
parent b878395c21
commit e6983e9209
18 changed files with 221 additions and 83 deletions

View File

@@ -1,3 +1,11 @@
################################################################################
## The Grist source can be extended. This is a stub that can be overridden
## from command line, as:
## docker buildx build -t ... --build-context=ext=<path> .
## The code in <path> will then be built along with the rest of Grist.
################################################################################
FROM scratch as ext
################################################################################
## Javascript build stage
################################################################################
@@ -5,17 +13,25 @@
FROM node:14-buster as builder
# Install all node dependencies.
ADD package.json package.json
ADD yarn.lock yarn.lock
RUN yarn install --frozen-lockfile
WORKDIR /grist
COPY package.json yarn.lock /grist/
RUN yarn install --frozen-lockfile --verbose
# Install any extra node dependencies (at root level, to avoid having to wrestle
# with merging them).
COPY --from=ext / /grist/ext
RUN \
mkdir /node_modules && \
cd /grist/ext && \
{ if [ -e package.json ] ; then yarn install --frozen-lockfile --modules-folder=/node_modules --verbose ; fi }
# Build node code.
ADD tsconfig.json tsconfig.json
ADD app app
ADD stubs stubs
ADD buildtools buildtools
ADD static static
ADD test/tsconfig.json test/tsconfig.json
COPY tsconfig.json /grist
COPY tsconfig-ext.json /grist
COPY test/tsconfig.json /grist/test/tsconfig.json
COPY app /grist/app
COPY stubs /grist/stubs
COPY buildtools /grist/buildtools
RUN yarn run build:prod
################################################################################
@@ -63,9 +79,10 @@ RUN \
RUN mkdir -p /persist/docs
# Copy node files.
COPY --from=builder /node_modules node_modules
COPY --from=builder /_build _build
COPY --from=builder /static static
COPY --from=builder /node_modules /node_modules
COPY --from=builder /grist/node_modules /grist/node_modules
COPY --from=builder /grist/_build /grist/_build
COPY --from=builder /grist/static /grist/static-built
# Copy python files.
COPY --from=collector /usr/bin/python2.7 /usr/bin/python2.7
@@ -84,11 +101,19 @@ RUN \
COPY --from=sandbox /runsc /usr/bin/runsc
# Add files needed for running server.
ADD package.json package.json
ADD ormconfig.js ormconfig.js
ADD bower_components bower_components
ADD sandbox sandbox
ADD plugins plugins
ADD package.json /grist/package.json
ADD ormconfig.js /grist/ormconfig.js
ADD bower_components /grist/bower_components
ADD sandbox /grist/sandbox
ADD plugins /grist/plugins
ADD static /grist/static
# Finalize static directory
RUN \
mv /grist/static-built/* /grist/static && \
rmdir /grist/static-built
WORKDIR /grist
# Set some default environment variables to give a setup that works out of the box when
# started as: