vscode-dev-containers
vscode-dev-containers copied to clipboard
Publish Node images with no global packages
I'd like to see published Node images that don't install any global packages (currently eslint, typescript, tslint). This could mean revising the existing javascript-node and typescript-node definitions, or maybe just creating a new core node definition that includes everything from javascript-node except the last step.
Global packages in general have fallen out of favour, and most docs now strongly encourage local package installation. I think it would be beneficial to lead by example and encourage dev container maintainers to do so as well.
It's much easier and more natural to manage local packages than global ones. For example, to add an ESLint plugin to a dev container:
- Global: Maintainer modifies
Dockerfile(and hopefully includes a version tag!), everybody rebuilds the container - Local: Maintainer just runs
npm i -D <plugin>, everybody else just runsnpm i
It also reduces compatibility issues, since package.json versions packages by default (as opposed to the non-versioned eslint and typescript packages currently defined in base.Dockerfile), and package-lock.json generally ensures everyone ends up with the same dependency tree (and as of NPM 7 guarantees it).
I wanted to file this request to gauge 👍/👎 reaction.
Yeah, I've been debating this one myself - on the one hand, having these there allows you to just get started with a blank project and get going. On the other hand, as you said, globals are generally not favored once you have a project created. So its mainly sandbox scenarios that would benefit from what exists now.
Generally tslint is on life support, we left it there during the transition period (along with the extension), but we could probably remove at this point.
I guess the question is, if you have package.json and either yarn.lock or package-lock.json already, does having these globally cause any problems?
I guess the question is, if you have package.json and either yarn.lock or package-lock.json already, does having these globally cause any problems?
Well, as just one example, the main reason I noticed the global ESLint package was the VSCode plugin picking it up by default. Thankfully it at least asked me if I wanted to use it; accidentally running eslint instead of npx eslint wouldn't be as noticeable a mistake.
So its mainly sandbox scenarios that would benefit from what exists now.
That's why I figured a new node definition with no global packages might be the best path forward; leave the existing images for said sandbox scenarios, but quickly rebuild them atop a shared core image with no global packages, and slowly revise docs to recommend using that core image in .devcontainer/Dockerfile.
For now I've just added RUN sudo -u node npm uninstall -g eslint && npm cache clean --force > /dev/null 2>&1 to my Dockerfile, since that's a lot easier than copying javascript-node/.devcontainer/base.Dockerfile and then manually keeping up with changes as this (awesome) project continues to evolve.
Hey @Chuxel, I've been doing a bit more thinking here, and I just wanted to share some additional food for thought.
A couple more of my own goals you might like to consider, since they may be shared by many others:
- I'd like to be able to align the Node.js point release between our development container and our production build environment. It seems NVM +
.nvmrcis probably the best way to accomplish this. - I often find myself working in metered-bandwidth situations, so I've been thinking about how to reduce the bandwidth used during container builds.
I looked to see what's inside the node image, and I was surprised at how simple it is:
https://github.com/nodejs/docker-node/blob/702fcf0f5cb33b1772e80bb4ae406fd6f6d237a3/16/buster/Dockerfile
This just creates a user, installs Node.js, and installs Yarn; your project can already do all 3 itself with ease.
So, would anything of value be lost by cutting out the node image altogether, and simply rolling your own Node.js container image based on NVM instead?
Pseudocode
base.Dockerfile
FROM buildpack-deps:buster
# Install needed packages, yarn, nvm and setup non-root user. Use a separate RUN statement to add your own dependencies.
# [...]
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# Install common packages, non-root user, update yarn and install nvm
# [...]
# Configure global npm install location, use group to adapt to UID/GID changes
# [...]
# Clean up
Dockerfile
FROM mcr.microsoft.com/vscode/devcontainers/node
# Install node using nvm
ARG NODE_VERSION=node
RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION}"
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
# [Optional] Uncomment if you want to install more global node modules
# RUN su node -c "npm install -g <your-package-list-here>"
I think this could potentially reduce image size and install time, while also improving the ability to control the Node.js version, and adding the ability to upgrade the Node.js version without rebuilding the container (as long as you configure this with .nvmrc instead of in Dockerfile).
I might pursue a solution locally based on your Debian or Ubuntu container; but an image like this seems like it would be worth including out of the box.
As OP I'd say this is now taken care of with features.
Our old image was 1.06 GB:
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0.203.4-16-bullseye
# Uninstall global ESLint package
RUN su node -c "npm uninstall -g eslint" \
&& npm cache clean --force > /dev/null 2>&1
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# Install additional OS packages
&& apt-get -y install --no-install-recommends dnsutils \
# Clean up to reduce image size
&& apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/*
Our new image is a comparably svelte 564 MB:
FROM mcr.microsoft.com/vscode/devcontainers/base:0.202.3-focal
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# Configure system with nvm, Yarn, and node-gyp
&& bash -c "$(curl -fsSL "https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/script-library/node-debian.sh")" -- "" "none" \
# Install additional OS packages
&& apt-get -y install --no-install-recommends dnsutils \
# Clean up to reduce image size
&& apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/*
I still think an official image would be nice to ship; but you've given us the tools to create our own, which is the next-best thing.