Skip to content

Commit

Permalink
Use fnm to switch between node version (#253)
Browse files Browse the repository at this point in the history
Pre-install the current nodejs LTS versions (18, 20, 22) and allow
switching between them using `fnm`. The default remains at 18.

pulumi/pulumi PR pulumi/pulumi#17060

Fixes #223
  • Loading branch information
julienp authored Sep 6, 2024
1 parent 4dd0ed3 commit 2659de1
Show file tree
Hide file tree
Showing 27 changed files with 312 additions and 21 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Include fnm and Nodejs 18, 20 and 22 in the kitchen sink image
([#253](https://github.com/pulumi/pulumi-docker-containers/pull/253)

## 3.131.0

- Add per-language versions of the `pulumi/pulumi-dotnet` image
Expand Down
26 changes: 13 additions & 13 deletions docker/pulumi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,6 @@ RUN \
kubectl && \
rm -rf /var/lib/apt/lists/*

# Install nodejs and associated tools
RUN \
# Add yarn repo
curl -fsSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
# Add nodejs repo
curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
# Install packages
apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
nodejs \
yarn && \
rm -rf /var/lib/apt/lists/*

# Install Go
RUN curl -fsSLo /tmp/go.tgz https://golang.org/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz && \
echo "${GOLANG_SHA256} /tmp/go.tgz" | sha256sum -c - && \
Expand Down Expand Up @@ -132,6 +119,19 @@ RUN ln -s /usr/local/share/pypoetry/bin/poetry /usr/local/bin/
# poetry will create virtual environments using the python version used by poetry itself.
RUN poetry config virtualenvs.prefer-active-python true

# Install default nodejs versions and associated tools
RUN curl -fsSL https://fnm.vercel.app/install | bash -s -- --install-dir "/usr/local/share/fnm" --skip-shell && \
ln -s /usr/local/share/fnm/fnm /usr/local/bin/fnm
ENV FNM_COREPACK_ENABLED="true"
ENV FNM_VERSION_FILE_STRATEGY="recursive"
ENV FNM_DIR=/usr/local/share/fnm
RUN fnm install 18 && \
fnm install 20 && \
fnm install 22 && \
fnm alias 18 default
ENV PATH=/usr/local/share/fnm/aliases/default/bin:$PATH
RUN corepack install --global pnpm yarn

# Passing --build-arg PULUMI_VERSION=vX.Y.Z will use that version
# of the SDK. Otherwise, we use whatever get.pulumi.com thinks is
# the latest
Expand Down
55 changes: 47 additions & 8 deletions tests/containers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,34 +122,73 @@ func TestPulumiTemplateTests(t *testing.T) {
example := base.With(integration.ProgramTestOptions{
Dir: e.RootPath,
Config: test.config,
// `pulumi new` already runs `pulumi install for us, don't attempt to `yarn link`
// the SDK into the test.
PrepareProject: func(info *engine.Projinfo) error {
return nil
},
})

integration.ProgramTest(t, &example)
})
}
}

func TestKitchenSinkPythonVersions(t *testing.T) {
func TestKitchenSinkLanguageVersions(t *testing.T) {
if !isKitchenSink(t) {
t.Skip("Only running python version tests on kitchen sink")
t.Skip("Only language version tests on kitchen sink")
}
t.Parallel()

dirs, err := testdata.ReadDir("testdata")
require.NoError(t, err)

t.Run("node-default", func(t *testing.T) {
// We need to run the `node-default` test first, before the other tests which modify
// the container's default node version.
p := filepath.Join("testdata", "node-default")
copyTestData(t, p)
integration.ProgramTest(t, &integration.ProgramTestOptions{
NoParallel: true,
Dir: p,
Quick: true,
SkipRefresh: true,
PrepareProject: func(info *engine.Projinfo) error {
cmd := exec.Command("pulumi", "install", "--use-language-version-tools")
cmd.Dir = info.Root
out, err := cmd.CombinedOutput()
if err != nil {
t.Logf("install failed: %s: %s", err, out)
}
return err
},
})
})

for _, dir := range dirs {
dir := dir
t.Run(dir.Name(), func(t *testing.T) {
if dir.Name() == "node-default" {
// The `node-default` test is run first, so we skip it here.
t.Skip()
}
p := filepath.Join("testdata", dir.Name())
copyTestData(t, p)
integration.ProgramTest(t, &integration.ProgramTestOptions{
// We can't run the node tests in parallel because setting the node version is a
// global for the container.
NoParallel: strings.HasPrefix(dir.Name(), "node-"),
Dir: p,
Quick: true,
SkipRefresh: true,
PrepareProject: func(info *engine.Projinfo) error {
cmd := exec.Command("pulumi", "install", "--use-language-version-tools")
cmd.Dir = info.Root
return cmd.Run()
out, err := cmd.CombinedOutput()
if err != nil {
t.Logf("install failed: %s: %s", err, out)
}
return err
},
})
})
Expand Down Expand Up @@ -283,26 +322,26 @@ func TestEnvironment(t *testing.T) {
name: "node",
expectedDebian: "/usr/local/bin/node",
expectedUbi: "/usr/bin/node",
expectedKitchen: "/usr/bin/node",
expectedKitchen: "/usr/local/share/fnm/aliases/default/bin/node",
},
{
name: "npm",
expectedDebian: "/usr/local/bin/npm",
expectedUbi: "/usr/local/bin/npm",
expectedKitchen: "/usr/bin/npm",
expectedKitchen: "/usr/local/share/fnm/aliases/default/bin/npm",
},

{
name: "yarn",
expectedDebian: "/usr/local/bin/yarn",
expectedUbi: "/usr/local/bin/yarn",
expectedKitchen: "/usr/bin/yarn",
expectedKitchen: "/usr/local/share/fnm/aliases/default/bin/yarn",
},
{
name: "corepack",
expectedDebian: "/usr/local/bin/corepack",
expectedUbi: "/usr/bin/corepack",
expectedKitchen: "/usr/bin/corepack",
expectedKitchen: "/usr/local/share/fnm/aliases/default/bin/corepack",
},
} {
testCase := testCase
Expand All @@ -328,7 +367,7 @@ func TestEnvironment(t *testing.T) {
// Install scripts for various tools can sometimes modify PATH, usually by adding entries
// to ~/.bashrc. This test ensures that we notice such modifications.
expectedPaths := map[string]string{
"pulumi": "/usr/local/share/pyenv/shims:/usr/local/share/pyenv/bin:/usr/share/dotnet:/pulumi/bin:/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"pulumi": "/usr/local/share/fnm/aliases/default/bin:/usr/local/share/pyenv/shims:/usr/local/share/pyenv/bin:/usr/share/dotnet:/pulumi/bin:/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"pulumi-debian-dotnet": "/root/.dotnet:/pulumi/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"pulumi-debian-go": "/pulumi/bin:/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"pulumi-debian-java": "/pulumi/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
Expand Down
1 change: 1 addition & 0 deletions tests/testdata/node-18/.node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
18
10 changes: 10 additions & 0 deletions tests/testdata/node-18/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: node-default
runtime:
name: nodejs
options:
packagemanager: npm
description: A minimal TypeScript Pulumi program
config:
pulumi:tags:
value:
pulumi:template: typescript
10 changes: 10 additions & 0 deletions tests/testdata/node-18/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as process from "node:process";
import * as semver from "semver";

const version = semver.parse(process.version, {
loose: true
});

if (version?.major != 18) {
throw new Error(`Expected node version 18.x.x, got ${process.version}`);
}
12 changes: 12 additions & 0 deletions tests/testdata/node-18/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "node-default",
"main": "index.ts",
"devDependencies": {
"@types/node": "^18",
"typescript": "^5.0.0"
},
"dependencies": {
"@pulumi/pulumi": "^3.113.0",
"semver": "^7.6.3"
}
}
18 changes: 18 additions & 0 deletions tests/testdata/node-18/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"strict": true,
"outDir": "bin",
"target": "es2020",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.ts"
]
}
1 change: 1 addition & 0 deletions tests/testdata/node-20/.node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20
10 changes: 10 additions & 0 deletions tests/testdata/node-20/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: node-default
runtime:
name: nodejs
options:
packagemanager: npm
description: A minimal TypeScript Pulumi program
config:
pulumi:tags:
value:
pulumi:template: typescript
10 changes: 10 additions & 0 deletions tests/testdata/node-20/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as process from "node:process";
import * as semver from "semver";

const version = semver.parse(process.version, {
loose: true
});

if (version?.major != 20) {
throw new Error(`Expected node version 20.x.x, got ${process.version}`);
}
12 changes: 12 additions & 0 deletions tests/testdata/node-20/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "node-default",
"main": "index.ts",
"devDependencies": {
"@types/node": "^18",
"typescript": "^5.0.0"
},
"dependencies": {
"@pulumi/pulumi": "^3.113.0",
"semver": "^7.6.3"
}
}
18 changes: 18 additions & 0 deletions tests/testdata/node-20/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"strict": true,
"outDir": "bin",
"target": "es2020",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.ts"
]
}
1 change: 1 addition & 0 deletions tests/testdata/node-22.5.1/.node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22.5.1
10 changes: 10 additions & 0 deletions tests/testdata/node-22.5.1/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: node-default
runtime:
name: nodejs
options:
packagemanager: npm
description: A minimal TypeScript Pulumi program
config:
pulumi:tags:
value:
pulumi:template: typescript
5 changes: 5 additions & 0 deletions tests/testdata/node-22.5.1/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as process from "node:process";

if (process.version != "v22.5.1") {
throw new Error(`Expected node version 22.5.1 got ${process.version}`);
}
12 changes: 12 additions & 0 deletions tests/testdata/node-22.5.1/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "node-default",
"main": "index.ts",
"devDependencies": {
"@types/node": "^18",
"typescript": "^5.0.0"
},
"dependencies": {
"@pulumi/pulumi": "^3.113.0",
"semver": "^7.6.3"
}
}
18 changes: 18 additions & 0 deletions tests/testdata/node-22.5.1/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"strict": true,
"outDir": "bin",
"target": "es2020",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.ts"
]
}
1 change: 1 addition & 0 deletions tests/testdata/node-22/.node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
10 changes: 10 additions & 0 deletions tests/testdata/node-22/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: node-default
runtime:
name: nodejs
options:
packagemanager: npm
description: A minimal TypeScript Pulumi program
config:
pulumi:tags:
value:
pulumi:template: typescript
10 changes: 10 additions & 0 deletions tests/testdata/node-22/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as process from "node:process";
import * as semver from "semver";

const version = semver.parse(process.version, {
loose: true
});

if (version?.major != 22) {
throw new Error(`Expected node version 22.x.x, got ${process.version}`);
}
12 changes: 12 additions & 0 deletions tests/testdata/node-22/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "node-default",
"main": "index.ts",
"devDependencies": {
"@types/node": "^18",
"typescript": "^5.0.0"
},
"dependencies": {
"@pulumi/pulumi": "^3.113.0",
"semver": "^7.6.3"
}
}
18 changes: 18 additions & 0 deletions tests/testdata/node-22/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"strict": true,
"outDir": "bin",
"target": "es2020",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.ts"
]
}
10 changes: 10 additions & 0 deletions tests/testdata/node-default/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: node-default
runtime:
name: nodejs
options:
packagemanager: npm
description: A minimal TypeScript Pulumi program
config:
pulumi:tags:
value:
pulumi:template: typescript
Loading

0 comments on commit 2659de1

Please sign in to comment.