47
loading...
This website collects cookies to deliver better user experience
web
and api
apps into a format that can be put on a server. I'll choose Docker for this as it's basically the de-factor standard these days, and it's easy to run the images locally to verify they would work if placed into a Docker-compatible environment.ℹ️ BTW, if you just want to jump to the end-result you can browse the final branch result via VSCode on GitHub1s.com
pnpm
, and our repository was configured to build its projects to Javascript. Let's first add basic Docker to apps/web
:$ cd apps/web
$ cat Dockerfile
FROM node:16-alpine
RUN npm --global install pnpm
WORKDIR /root/monorepo
COPY ../.. .
# ↑ Copy the whole repository and let pnpm filter what to run
RUN pnpm install --filter "@mono/web..."
RUN pnpm build --filter "@mono/web..."
RUN pnpm test --if-present --filter "@mono/web"
$ docker build . -t web
=> [4/6] COPY ../.. . 0.8s
=> ERROR [5/6] RUN pnpm install --filter "@mono/web..."
2.9s
------
> [5/6] RUN pnpm install:
#8 1.985 Progress: resolved 1, reused 0, downloaded 0, added 0
#8 2.441 ERROR In : No matching version found for @mono/types@* inside the workspace
COPY ../.. .
step doesn't copy the repository root at all (it acts like COPY . .
), so pnpm install
fails because libs/types
doesn't exist inside the Docker image.$ cat Dockerfile
WORKDIR /root/monorepo
COPY . .
RUN pnpm install --filter "@mono/web..."
RUN pnpm build --filter "@mono/web..."
RUN pnpm test --if-present --filter "@mono/web"
WORKDIR /root/monorepo/apps/web
$ tar --exclude='node_modules' --exclude='dist' --exclude='.git' -cf - ../.. | docker build -f apps/web/Dockerfile - -t web
$ docker run --rm -it -p3000:3000 web
running on port 3000
tar
command though, let's break it down:--exclude
the folders "node_modules", "dist", and ".git" because they take up a lot of space that Docker shouldn't have to process.-cf - ../..
are tar-arguments to create (-c
) a tarball, from repository root (../..
), and send it to stdout (f -
).|
pipes the tarball to Dockerdocker build -f <path>
instructs Docker where to find the Dockerfile (because the context is now relative to the repository root we have to tell it which file to build), and the -
lets Docker read context from stdin.ℹ️ BTW, there's lots we could optimize: We include a lot of superfluous files, we should install dependencies before copying source-code, we should remove dev-dependencies after testing, and more! But it quickly gets messy setting all that up manually, so I hope by leaving it unoptimized here we can dive into more tool/script-assisted optimizations in later articles.
apps/api
and the libraries so no need to show that here, but you can explore the final result if you'd like.$ cd ../..
$ cat .github/ci/ci.yml
name: CI
on:
push:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v2
with:
version: 6.9.1
- run: pnpm run -r --if-present --parallel docker:build
$ cat .github/ci/ci.yml
jobs:
build:
strategy:
matrix:
package: ["@mono/api", "@mono/web", "@mono/analytics", "@mono/logging", "@mono/types", ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v2
with:
version: 6.9.1
- run: pnpm run --filter ${{ matrix.package }} docker:build
$ cat package.json
"scripts": {
"list-packages": "echo [$(pnpm -s m ls --depth -1 | tr \" \" \"\n\" | grep -o \"@.*@\" | rev | cut -c 2- | rev | sed -e 's/\\(.*\\)/\"\\1\"/' | paste -sd, - )]",
},
}
$ cat .github/ci/ci.yml
jobs:
packages:
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- id: set-matrix
run: echo "::set-output name=matrix::{\"package\":$(pnpm -s list-packages)}"
build:
needs: packages
strategy:
matrix: ${{ fromJson(needs.packages.outputs.matrix) }}
steps:
- run: pnpm run --filter ${{ matrix.package }} docker:build
matrix.package
variable, which then gets consumed in the build
jobs. Wonderful!list-packages
script is a bit of a terrifying shell-oneliner though, I think it's best we don't get into its details right now or we could be here all day. But if you'd like to see it explained or if you see a better way to do it please leave a comment.apps/web
has changed it shouldn't have to also reinstall its dependencies.