conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

Dockerfile (3151B)


      1 # deploy/Dockerfile - the conductor
      2 #
      3 # Build from the repository root, since the context is the whole project:
      4 #   docker build -f deploy/Dockerfile -t finwo/conductor .
      5 #
      6 # Alpine rather than the official node image, because that one is only
      7 # published for amd64, arm64 and ppc64le, and this is meant to run on
      8 # riscv64 as well. Alpine 3.23 carries node 24 on every architecture we
      9 # publish for, so the base is the same everywhere and nothing has to be
     10 # fetched from a second source at build time.
     11 #
     12 # State lives in /data: the sqlite database, the git mirrors, the log spool
     13 # and, unless object storage is configured, artifacts. Mount a volume there
     14 # or none of it survives a restart.
     15 
     16 ARG ALPINE_VERSION=3.23
     17 
     18 # npm is only needed to resolve dependencies, so it stays in this stage and
     19 # out of the image that ships.
     20 FROM alpine:${ALPINE_VERSION} AS deps
     21 
     22 RUN apk add --no-cache nodejs npm
     23 
     24 WORKDIR /app
     25 
     26 # Dependencies before source, so editing code does not reinstall them.
     27 # mysql2 and pg are optional and pure javascript; both are installed so a
     28 # single image covers every database backend.
     29 COPY package.json ./
     30 RUN npm install --omit=dev --no-audit --no-fund \
     31     && npm cache clean --force
     32 
     33 # The bundle is the same bytes on every architecture, so it is built once on
     34 # whatever is doing the building. esbuild has no riscv64 release, and
     35 # emulating one to produce an identical file would be slow for nothing.
     36 FROM --platform=$BUILDPLATFORM alpine:${ALPINE_VERSION} AS web
     37 
     38 RUN apk add --no-cache nodejs npm
     39 
     40 WORKDIR /app
     41 
     42 # The dev dependencies the deps stage deliberately skipped.
     43 COPY package.json ./
     44 RUN npm install --no-audit --no-fund
     45 
     46 COPY web/ ./web/
     47 RUN node web/build.cjs
     48 
     49 FROM alpine:${ALPINE_VERSION}
     50 
     51 # git is needed for the mirrors the conductor reads pipelines and source
     52 # archives out of. Everything else it does is node, and node's sqlite is
     53 # built in, so there is no database client to install.
     54 RUN apk add --no-cache ca-certificates git nodejs
     55 
     56 # Fixed uid/gid so a mounted volume keeps working across rebuilds and
     57 # hosts, where an automatic id would drift.
     58 RUN addgroup -S -g 1000 conductor \
     59     && adduser -S -u 1000 -G conductor -h /app conductor
     60 
     61 WORKDIR /app
     62 
     63 COPY --from=deps /app/node_modules ./node_modules
     64 COPY package.json ./
     65 COPY src/ ./src/
     66 COPY migrations/ ./migrations/
     67 COPY --from=web /app/web/dist ./web/dist
     68 
     69 # Paths point into the volume rather than at the defaults, which are
     70 # relative to the working directory.
     71 ENV NODE_ENV=production \
     72     CONDUCTOR_HOST=0.0.0.0 \
     73     CONDUCTOR_PORT=8080 \
     74     CONDUCTOR_DATABASE_PATH=/data/conductor.db \
     75     CONDUCTOR_STORAGE_PATH=/data/storage \
     76     CONDUCTOR_MIRROR_PATH=/data/mirrors \
     77     CONDUCTOR_LOG_PATH=/data/logs
     78 
     79 RUN mkdir -p /data && chown -R conductor:conductor /data
     80 
     81 USER conductor
     82 VOLUME ["/data"]
     83 EXPOSE 8080
     84 
     85 # No curl in the image, so the check is made with node itself.
     86 HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
     87     CMD node -e "fetch('http://127.0.0.1:'+(process.env.CONDUCTOR_PORT||8080)+'/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
     88 
     89 CMD ["node", "src/conductor/index.js"]