When building an image that needs to be compiled from typescript, I get this error.
sh: 1: tsc: not found
The command '/bin/sh -c npm run tsc' returned a non-zero code: 127
Here is the relevant code:
docker-compose.yaml
version: '3.1'
services:
nodeserver:
build:
context: .
target: prod
ports:
- "3000:3000"
volumes:
- ./src:/app/src
- ./public:/app/public
- ./templates:/app/templates
Dockerfile
FROM node:15.11.0 AS base
EXPOSE 3000
ENV NODE_ENV=production
WORKDIR /app
COPY package*.json ./
RUN npm install --only=production && npm cache clean --force
##########################################################################################
FROM base AS dev
ENV NODE_ENV=development
RUN npm install --only=development
CMD npm run dev
##########################################################################################
FROM dev AS source
COPY dist dist
COPY templates templates
COPY public public
RUN npm run tsc
##########################################################################################
FROM base AS test
COPY --from=source /app/node_modules /app/node_modules
COPY --from=source /app/templates /app/templates
COPY --from=source /app/public /app/public
COPY --from=source /app/dist /app/dist
CMD npm run test
##########################################################################################
FROM test AS prod
CMD npm start
package.json
{
"name": "nodeserver",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node ./dist/app.js",
"deploy": "git add . && git commit -m Heroku && git push heroku main",
"tsc": "tsc --outDir ./dist",
"dev": "npm run ts-watch",
"test": "npm run jest --runInBand",
"ts-watch": "tsc-watch --project . --outDir ./dist --onSuccess \"nodemon ./dist/app.js\""
},
"jest": {
"testEnvironment": "node"
},
"repository": {
"type": "git",
"url": "git+https://github.com/MiquelPiza/nodeserver.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/MiquelPiza/nodeserver/issues"
},
"homepage": "https://github.com/MiquelPiza/nodeserver#readme",
"dependencies": {
"@sendgrid/mail": "^7.4.2",
"bcryptjs": "^2.4.3",
"express": "^4.17.1",
"handlebars": "^4.7.7",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.20",
"mongodb": "^3.6.4",
"mongoose": "^5.11.19",
"multer": "^1.4.2",
"socket.io": "^4.0.0",
"validator": "^13.5.2"
},
"devDependencies": {
"@types/bcryptjs": "^2.4.2",
"@types/express": "^4.17.11",
"@types/jsonwebtoken": "^8.5.0",
"@types/lodash": "^4.14.168",
"@types/mongoose": "^5.10.3",
"@types/multer": "^1.4.5",
"@types/node": "^14.14.33",
"@types/sendgrid": "^4.3.0",
"@types/validator": "^13.1.3",
"env-cmd": "^10.1.0",
"jest": "^26.6.3",
"nodemon": "^2.0.7",
"supertest": "^6.1.3",
"tsc-watch": "^4.2.9",
"typescript": "^4.2.3"
},
"engines": {
"node": "15.11.0"
}
}
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"strictNullChecks": false,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
},
"include": ["src"]
}
This dockerfile works:
FROM node:15.11.0 AS build
WORKDIR /app
COPY package.json .
RUN npm install
ADD . .
RUN npm run tsc
FROM node:15.11.0
WORKDIR /app
COPY package.json .
RUN npm install --production
ADD public ./public
ADD templates ./templates
COPY --from=build /app/dist dist
EXPOSE 3000
CMD npm start
I'm using this dockerfile for reference, from a Docker course: https://github.com/BretFisher/docker-mastery-for-nodejs/blob/master/typescript/Dockerfile I don't see what I'm doing wrong, the source stage should have the dev dependencies, among them typescript, so it should be able to run tsc.
Any help appreciated. Thanks.
EDIT:
In addition to using npm ci instead of npm install, I had to copy tsconfig.json to the working directory (and copy src directory instead of dist, which is created by tsc) for tsc to work properly. This is the modified source stage in the Dockerfile:
FROM dev AS source
COPY src src
COPY templates templates
COPY public public
COPY tsconfig.json tsconfig.json
RUN npm run tsc
I am getting tsc: command not found when trying to run build. This is part of my package.json Tried to run npm install --only=dev && npm run build && npm prune --production Your build job failed because it was terminated. This often happens due to resource exhaustion.
docker build works by running each Dockerfile step in a container. At the end of each step, that container is committed to a new image. The first step's container is created from the image specified in FROM.
Nobody could reproduce the problem locally with Docker: it only happened in the CI/CD pipeline. What is going on? We needed to debug the Docker build on the CI/CD server. docker build works by running each Dockerfile step in a container. At the end of each step, that container is committed to a new image.
docker build supports caching. If the same build step had already run successfully in the past, then that step will be skipped (no container will be spawned), and the image produced last time will be selected. If a certain step does not hit the cache, then all subsequent steps won't hit the cache either.
EDIT 2:
This issue has been resolved. If your package-lock.json file is corrupted, you may be able to fix it with the utility fix-has-install-script.
Original Answer:
Use npm ci
(or add package-lock.json
to your .dockerignore
file, or delete package-lock.json
in your local environment before building). The why is answered here.
EDIT 1:
Here's what I believe is going on. Disclaimer, I'm not an expert on nodejs
or npm
-- in fact I'm something of a novice. And all of this is conjecture based on some experiments.
npm
is not linking the binaries for the dev dependencies via sym links in node_modules/.bin
because the package-lock.json
file has gotten into a corrupted state where the (prod) dependencies are in lockfileVersion 2 format, and the dev dependencies are still in lockfileVersion 1 format.
Note: Making a bunch of assumptions here.
Your local host using using npm 6, and the docker container is using npm 7.
Because of this, the existing package-lock.json
is in lockfileVersion: 1
which doesn't include a bin
section for dependencies that have binaries. Version 2 does save the bin:
section, which npm
must use to determine what binaries to install/link.
When you run the production dependency install (e.g. NODE_ENV=production npm install
), using npm
version 7, npm
is upgrading the version of your package-lock.json
to lockfileVersion: 2
, part of this includes saving bin:
sections for the dependencies that install binaries. Importantly, it updates only the production dependencies. Now the package-lock.json
file is corrupted because it claims to be in version 2 format, but all the dev dependencies are either still in version 1 or at least don't have the bin:
section correctly applied.
When you now try to install your dev dependencies, npm
sees that the package-lock.json
is in lockfileVersion: 2
, so it assumes that the dev dependencies have been upgraded as well (but they haven't been, or at least not correctly). It doesn't find the bin:
sections because they don't exist and so it doesn't link the binaries to the node_modules/.bin/
directory.
You can perform a minimum reproduction of it using this Dockerfile
:
FROM node:14 as npm6
WORKDIR /app
# Create a node project using npm 6 and install a dev dependency
# that contains a binary.
RUN npm init --yes && \
npm install --save-dev typescript
FROM node:15 as npm7
COPY --from=npm6 /app/package*.json /app/
WORKDIR /app
# Install production dependencies, then all dependencies. This should
# link the binaries for typescript in (e.g. tsc) under node_modules/.bin.
RUN npm install -g [email protected] && \
npm install --production && \
npm install
# Causes error, tsc not found.
CMD ["npx", "-c", "tsc --version"]
I couldn't find an existing bug ticket so I created one here. Perhaps it will get fixed.
It's probably a NODE_ENV
environment variable problem.
ENV NODE_ENV=production
If you set this way, the dependencies in devDependencies
will not be installed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With