Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I run TypeScript `tsc` before `COPY` in Dockerfile.template?

I have a working solution to build a TypeScript Node app when deploying to docker via the Dockerfile.template file:

# Thanks: https://github.com/balenalabs/multicontainer-getting-started
FROM balenalib/%%BALENA_MACHINE_NAME%%-node

# Defines our working directory in container
WORKDIR /usr/src/app

# Install packages
RUN install_packages build-essential libraspberrypi-bin

# Copies the package.json first for better cache on later pushes
COPY package.json package.json

# Install npm dependencies on the balena.io build server,
# making sure to clean up the artifacts it creates in order to reduce the image size.
#NOTE: I removed --production option because I need to run tsc after COPY..., so larger image size remains for now.
RUN JOBS=MAX npm i

# This will copy all files in our root to the working directory in the container
COPY . ./

# Build from TypeScript
# TODO: This feels messy. How may I run `npm i --production` followed by `tsc` before copying files in Docker?
# Best answer so far for the next line: https://stackoverflow.com/questions/51083134/how-to-compile-typescript-in-dockerfile
RUN ./node_modules/typescript/bin/tsc -p ./tsconfig.json

# server.js will run when container starts up on the device
# in package.json: "serve": "node dist/server.js"
CMD ["npm", "run", "serve"]

I'd prefer to run tsc before copying files over to the app directory. Also, running npm i --production so that I don't have to have dev dependencies in the app. This doesn't seem to work however after an npm i -g typescript (before COPY), without a command to be found. What's the best way to proceed?

Update With Solution

With the chosen solution, here's an updated script that works for the next person who searches for this:

###################################
# First Stage: Compile TypeScript #
###################################

# Thanks: https://stackoverflow.com/questions/60916271/how-do-i-run-typescript-tsc-before-copy-in-dockerfile-template/60917273#60917273
FROM balenalib/%%BALENA_MACHINE_NAME%%-node AS build

# Install needed packages to build raspicam Node dependencies.
RUN install_packages build-essential libraspberrypi-bin

WORKDIR /usr/src/app

# Install the Javascript dependencies, including all devDependencies.
COPY package.json .
RUN npm i

# Copy the rest of the application in and build it.
COPY . ./

# RUN TypeScript build
RUN ./node_modules/typescript/bin/tsc -p ./tsconfig.json

# Clean up node_modules to not include dev dependencies.
RUN rm -rf ./node_modules
RUN JOBS=MAX npm i --production

##################################
# Second Stage: Prepare Dist App #
##################################

FROM balenalib/%%BALENA_MACHINE_NAME%%-node

# Defines our working directory in container
WORKDIR /usr/src/app

# Install packages
RUN install_packages build-essential libraspberrypi-bin

# This will copy all files in our root to the working directory in the container
COPY --from=build /usr/src/app/dist dist
COPY package.json package.json

# server.js will run when container starts up on the device
CMD ["npm", "run", "serve"]
like image 538
Christopher Stevens Avatar asked Mar 29 '20 15:03

Christopher Stevens


1 Answers

You can use a multi-stage build for this. The first stage includes all of the development dependencies, including tsc; the second stage only includes the files that are needed to run the built application.

(I'm not familiar with the specific build environment you're using so this will be in terms of the standard node image.)

# First stage: compile things.
FROM node:12 AS build
WORKDIR /usr/src/app

# (Install OS dependencies; include -dev packages if needed.)

# Install the Javascript dependencies, including all devDependencies.
COPY package.json .
RUN npm install

# Copy the rest of the application in and build it.
COPY . .
# RUN npm build
RUN npx tsc -p ./tsconfig.json

# Now /usr/src/app/dist has the built files.

# Second stage: run things.
FROM node:12
WORKDIR /usr/src/app

# (Install OS dependencies; just libraries.)

# Install the Javascript dependencies, only runtime libraries.
COPY package.json .
RUN npm install --production

# Copy the dist tree from the first stage.
COPY --from=build /usr/src/app/dist dist

# Run the built application when the container starts.
EXPOSE 3000
CMD ["npm", "run", "serve"]
like image 150
David Maze Avatar answered Nov 14 '22 17:11

David Maze