Tomáš Vavřinka
Tomáš Vavřinka

Reputation: 633

Yarn workspaces build with docker

Consider following file structure of yarn workspaces:

.
├── docker-compose.yaml
├── package.json
├── packages
│   └── pkg-1
│       ├── dist
│       ├── package.json
│       ├── src
│       └── tsconfig.json
├── services
│   ├── api-1
│   │   ├── dist
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   ├── src
│   │   ├── tsconfig.json
│   │   └── yarn.lock
│   └── client-1
│       ├── package.json
│       ├── src
│       └── yarn.lock
├── tsconfig.json
└── yarn.lock

I have written Dockerfile to create image for api-1:

ARG APP_DIR=/usr/app

# Build stage
FROM node:16.2-alpine AS build

ARG APP_DIR

WORKDIR ${APP_DIR}
COPY package.json ./
COPY yarn.lock ./
COPY tsconfig.json ./

WORKDIR ${APP_DIR}/packages/pkg-1
COPY packages/pkg-1/package.json ./
RUN yarn --pure-lockfile --non-interactive
COPY packages/pkg-1/tsconfig.json ./
COPY packages/pkg-1/src/ ./src
RUN yarn build

WORKDIR ${APP_DIR}/services/api-1
COPY services/api-1/package.json ./
COPY services/api-1/yarn.lock ./
RUN yarn --pure-lockfile --non-interactive
COPY services/api-1/tsconfig.json ./
COPY services/api-1/src/ ./src
RUN yarn build

# Production stage
FROM node:16.2-alpine AS prod

ARG APP_DIR

WORKDIR ${APP_DIR}
COPY --from=build ${APP_DIR}/package.json ./
COPY --from=build ${APP_DIR}/yarn.lock ./

WORKDIR ${APP_DIR}/packages/pkg-1
COPY --from=build ${APP_DIR}/packages/pkg-1/package.json ./
RUN yarn --pure-lockfile --non-interactive --production
COPY --from=build ${APP_DIR}/packages/pkg-1/dist ./dist

WORKDIR ${APP_DIR}/services/api-1
COPY --from=build ${APP_DIR}/services/api-1/package.json ./
COPY --from=build ${APP_DIR}/services/api-1/yarn.lock ./
RUN yarn --pure-lockfile --non-interactive --production
COPY --from=build ${APP_DIR}/services/api-1/dist ./dist

CMD ["node", "dist"]

Build is running from root docker-compose.yaml to have proper context:

services:
  api-1:
    image: project/api-1
    container_name: api-1
    build:
      context: ./
      dockerfile: ./services/api-1/Dockerfile
      target: prod
    ports:
      - 3000:3000

It is working but this way there will be a lot of repetition while application grow. Problem is the way how packages are building.

Package can be for example normalized components collection used among client services or collection of normalized errors used among api services.

Whenever I will build some service I need to first build its depending packages which is unnecessarily repetitive task. Not mention that building steps of respective package are defined over and over again in Dockerfile of every single service that uses the package.

So my question is. Is there a way how to create for example image of package that will be used for building a service to avoid defining build steps of package in service Dockerfile?

Upvotes: 7

Views: 8829

Answers (2)

Anthony Garcia-Labiad
Anthony Garcia-Labiad

Reputation: 3721

A while ago I have posted an answer detailing how I structured a monorepo with multiple services and packages.

The "trick" is to copy all the packages that your service depends on, as well as the project root package.json. Then running yarn --pure-lockfile --non-interactive --production once will install the dependencies for the all the sub-packages since they are part of the workspace.

The example linked isn't using typescript, but I believe this could be easily achieved with a postinstall script in every package.json that would run yarn build.

Upvotes: 1

MauriceNino
MauriceNino

Reputation: 6757

Seems like you are looking for something that gives you the option to have a "parent" package.json, so you only have to invoke "build" on one and with that build the whole dependency tree.

e.g:

- package.json // root package
  | - a
    | - package.json // module a package
  | - b
    | - package.json // module b package

You might want to look into the following:

Both support structures like the one mentioned, lerna has just a lot more features. To get a quick grasp on the differences, look here: Is Lerna needed anymore with NPM 7.0.0's workspaces?

Upvotes: 0

Related Questions