From bde48682ee933f05d355b0f325061db4cf6745a4 Mon Sep 17 00:00:00 2001 From: Mark Ottaviani Date: Thu, 16 Jul 2026 12:32:06 -0400 Subject: [PATCH 1/2] Add npm ci support with opt-in strict mode npm ci gives reproducible installs (exact versions from package-lock.json) where npm install can silently drift. Fall back to npm install by default for backward compatibility with apps lacking a valid lockfile; set NPM_CI_STRICT=true to hard-fail the build instead of falling back. --- 18-minimal/s2i/bin/assemble | 24 ++++++++++++++++++++++-- 18/s2i/bin/assemble | 24 ++++++++++++++++++++++-- 20-minimal/s2i/bin/assemble | 24 ++++++++++++++++++++++-- 20/s2i/bin/assemble | 24 ++++++++++++++++++++++-- 22-minimal/README.md | 3 +++ 22-minimal/s2i/bin/assemble | 24 ++++++++++++++++++++++-- 22/README.md | 3 +++ 22/s2i/bin/assemble | 24 ++++++++++++++++++++++-- 24-minimal/README.md | 3 +++ 24-minimal/s2i/bin/assemble | 24 ++++++++++++++++++++++-- 24/README.md | 3 +++ 24/s2i/bin/assemble | 24 ++++++++++++++++++++++-- test/run | 8 ++++++++ 13 files changed, 196 insertions(+), 16 deletions(-) diff --git a/18-minimal/s2i/bin/assemble b/18-minimal/s2i/bin/assemble index a5767210..f854dddd 100755 --- a/18-minimal/s2i/bin/assemble +++ b/18-minimal/s2i/bin/assemble @@ -81,12 +81,32 @@ fi if [ "$NODE_ENV" != "production" ]; then echo "---> Building your Node application from source" - npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + npm install + fi else echo "---> Installing all dependencies" - NODE_ENV=development npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + NODE_ENV=development npm install + fi #do not fail when there is no build script echo "---> Building in production mode" diff --git a/18/s2i/bin/assemble b/18/s2i/bin/assemble index f644a811..2997740c 100755 --- a/18/s2i/bin/assemble +++ b/18/s2i/bin/assemble @@ -81,12 +81,32 @@ fi if [ "$NODE_ENV" != "production" ]; then echo "---> Building your Node application from source" - npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + npm install + fi else echo "---> Installing all dependencies" - NODE_ENV=development npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + NODE_ENV=development npm install + fi #do not fail when there is no build script echo "---> Building in production mode" diff --git a/20-minimal/s2i/bin/assemble b/20-minimal/s2i/bin/assemble index 3230d038..c5e6a450 100755 --- a/20-minimal/s2i/bin/assemble +++ b/20-minimal/s2i/bin/assemble @@ -88,12 +88,32 @@ fi if [ "$NODE_ENV" != "production" ]; then echo "---> Building your Node application from source" - npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + npm install + fi else echo "---> Installing all dependencies" - NODE_ENV=development npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + NODE_ENV=development npm install + fi #do not fail when there is no build script echo "---> Building in production mode" diff --git a/20/s2i/bin/assemble b/20/s2i/bin/assemble index 3230d038..c5e6a450 100755 --- a/20/s2i/bin/assemble +++ b/20/s2i/bin/assemble @@ -88,12 +88,32 @@ fi if [ "$NODE_ENV" != "production" ]; then echo "---> Building your Node application from source" - npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + npm install + fi else echo "---> Installing all dependencies" - NODE_ENV=development npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + NODE_ENV=development npm install + fi #do not fail when there is no build script echo "---> Building in production mode" diff --git a/22-minimal/README.md b/22-minimal/README.md index 8cd29926..01e43862 100644 --- a/22-minimal/README.md +++ b/22-minimal/README.md @@ -237,6 +237,9 @@ Application developers can use the following environment variables to configure **`NPM_TOKEN`** Use authentication token for a custom NPM registry mirror +**`NPM_CI_STRICT`** + When set to "true", a failed `npm ci` (e.g. missing or out-of-sync `package-lock.json`) causes the build to fail immediately instead of falling back to `npm install` (default: "false") + One way to define a set of environment variables is to include them as key value pairs in your repo's `.s2i/environment` file. Example: DATABASE_USER=sampleUser diff --git a/22-minimal/s2i/bin/assemble b/22-minimal/s2i/bin/assemble index 3230d038..c5e6a450 100755 --- a/22-minimal/s2i/bin/assemble +++ b/22-minimal/s2i/bin/assemble @@ -88,12 +88,32 @@ fi if [ "$NODE_ENV" != "production" ]; then echo "---> Building your Node application from source" - npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + npm install + fi else echo "---> Installing all dependencies" - NODE_ENV=development npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + NODE_ENV=development npm install + fi #do not fail when there is no build script echo "---> Building in production mode" diff --git a/22/README.md b/22/README.md index 03d0da72..f17141d5 100644 --- a/22/README.md +++ b/22/README.md @@ -165,6 +165,9 @@ Application developers can use the following environment variables to configure **`NPM_TOKEN`** Use authentication token for a custom NPM registry mirror +**`NPM_CI_STRICT`** + When set to "true", a failed `npm ci` (e.g. missing or out-of-sync `package-lock.json`) causes the build to fail immediately instead of falling back to `npm install` (default: "false") + One way to define a set of environment variables is to include them as key value pairs in your repo's `.s2i/environment` file. Example: DATABASE_USER=sampleUser diff --git a/22/s2i/bin/assemble b/22/s2i/bin/assemble index 3230d038..c5e6a450 100755 --- a/22/s2i/bin/assemble +++ b/22/s2i/bin/assemble @@ -88,12 +88,32 @@ fi if [ "$NODE_ENV" != "production" ]; then echo "---> Building your Node application from source" - npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + npm install + fi else echo "---> Installing all dependencies" - NODE_ENV=development npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + NODE_ENV=development npm install + fi #do not fail when there is no build script echo "---> Building in production mode" diff --git a/24-minimal/README.md b/24-minimal/README.md index 928c9f1f..cc4ed93d 100644 --- a/24-minimal/README.md +++ b/24-minimal/README.md @@ -237,6 +237,9 @@ Application developers can use the following environment variables to configure **`NPM_TOKEN`** Use authentication token for a custom NPM registry mirror +**`NPM_CI_STRICT`** + When set to "true", a failed `npm ci` (e.g. missing or out-of-sync `package-lock.json`) causes the build to fail immediately instead of falling back to `npm install` (default: "false") + One way to define a set of environment variables is to include them as key value pairs in your repo's `.s2i/environment` file. Example: DATABASE_USER=sampleUser diff --git a/24-minimal/s2i/bin/assemble b/24-minimal/s2i/bin/assemble index 3230d038..c5e6a450 100755 --- a/24-minimal/s2i/bin/assemble +++ b/24-minimal/s2i/bin/assemble @@ -88,12 +88,32 @@ fi if [ "$NODE_ENV" != "production" ]; then echo "---> Building your Node application from source" - npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + npm install + fi else echo "---> Installing all dependencies" - NODE_ENV=development npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + NODE_ENV=development npm install + fi #do not fail when there is no build script echo "---> Building in production mode" diff --git a/24/README.md b/24/README.md index d10e7ea0..33059826 100644 --- a/24/README.md +++ b/24/README.md @@ -165,6 +165,9 @@ Application developers can use the following environment variables to configure **`NPM_TOKEN`** Use authentication token for a custom NPM registry mirror +**`NPM_CI_STRICT`** + When set to "true", a failed `npm ci` (e.g. missing or out-of-sync `package-lock.json`) causes the build to fail immediately instead of falling back to `npm install` (default: "false") + One way to define a set of environment variables is to include them as key value pairs in your repo's `.s2i/environment` file. Example: DATABASE_USER=sampleUser diff --git a/24/s2i/bin/assemble b/24/s2i/bin/assemble index 3230d038..c5e6a450 100755 --- a/24/s2i/bin/assemble +++ b/24/s2i/bin/assemble @@ -88,12 +88,32 @@ fi if [ "$NODE_ENV" != "production" ]; then echo "---> Building your Node application from source" - npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + npm install + fi else echo "---> Installing all dependencies" - NODE_ENV=development npm install + + # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true + echo "---> Installing dependencies with npm ci" + if ! npm ci; then + if [ "${NPM_CI_STRICT}" == "true" ]; then + echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 + exit 1 + fi + echo "---> WARNING: npm ci failed, falling back to npm install (set NPM_CI_STRICT=true to disable this fallback)" >&2 + NODE_ENV=development npm install + fi #do not fail when there is no build script echo "---> Building in production mode" diff --git a/test/run b/test/run index 77f87160..b499c8d3 100755 --- a/test/run +++ b/test/run @@ -156,6 +156,14 @@ TEST_SET=${TESTS:-$TEST_LIST_NPM_TOKEN} ct_run_tests_from_testset "node_env_toke cleanup +prepare app +check_prep_result $? app || exit +echo "Testing strict npm ci mode fails the build without a lockfile: s2i build -e NPM_CI_STRICT=true" +run_s2i_build "-e NPM_CI_STRICT=true" +evaluate_build_result $? "-should-fail-npm-ci-strict" + +cleanup + run_s2i_build "-e NODE_ENV=development" evaluate_build_result $? "-e NODE_ENV=development" From c45b19d6a02764103f71e8679d0ea8734fb95fc8 Mon Sep 17 00:00:00 2001 From: Mark Ottaviani Date: Thu, 16 Jul 2026 21:15:21 -0400 Subject: [PATCH 2/2] Fixed the NODE_ENV=development gap on the primary npm ci call --- 18-minimal/s2i/bin/assemble | 2 +- 18/s2i/bin/assemble | 2 +- 20-minimal/s2i/bin/assemble | 2 +- 20/s2i/bin/assemble | 2 +- 22-minimal/s2i/bin/assemble | 2 +- 22/s2i/bin/assemble | 2 +- 24-minimal/s2i/bin/assemble | 2 +- 24/s2i/bin/assemble | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/18-minimal/s2i/bin/assemble b/18-minimal/s2i/bin/assemble index f854dddd..10420738 100755 --- a/18-minimal/s2i/bin/assemble +++ b/18-minimal/s2i/bin/assemble @@ -99,7 +99,7 @@ else # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true echo "---> Installing dependencies with npm ci" - if ! npm ci; then + if ! NODE_ENV=development npm ci; then if [ "${NPM_CI_STRICT}" == "true" ]; then echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 exit 1 diff --git a/18/s2i/bin/assemble b/18/s2i/bin/assemble index 2997740c..651f9057 100755 --- a/18/s2i/bin/assemble +++ b/18/s2i/bin/assemble @@ -99,7 +99,7 @@ else # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true echo "---> Installing dependencies with npm ci" - if ! npm ci; then + if ! NODE_ENV=development npm ci; then if [ "${NPM_CI_STRICT}" == "true" ]; then echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 exit 1 diff --git a/20-minimal/s2i/bin/assemble b/20-minimal/s2i/bin/assemble index c5e6a450..68ab6081 100755 --- a/20-minimal/s2i/bin/assemble +++ b/20-minimal/s2i/bin/assemble @@ -106,7 +106,7 @@ else # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true echo "---> Installing dependencies with npm ci" - if ! npm ci; then + if ! NODE_ENV=development npm ci; then if [ "${NPM_CI_STRICT}" == "true" ]; then echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 exit 1 diff --git a/20/s2i/bin/assemble b/20/s2i/bin/assemble index c5e6a450..68ab6081 100755 --- a/20/s2i/bin/assemble +++ b/20/s2i/bin/assemble @@ -106,7 +106,7 @@ else # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true echo "---> Installing dependencies with npm ci" - if ! npm ci; then + if ! NODE_ENV=development npm ci; then if [ "${NPM_CI_STRICT}" == "true" ]; then echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 exit 1 diff --git a/22-minimal/s2i/bin/assemble b/22-minimal/s2i/bin/assemble index c5e6a450..68ab6081 100755 --- a/22-minimal/s2i/bin/assemble +++ b/22-minimal/s2i/bin/assemble @@ -106,7 +106,7 @@ else # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true echo "---> Installing dependencies with npm ci" - if ! npm ci; then + if ! NODE_ENV=development npm ci; then if [ "${NPM_CI_STRICT}" == "true" ]; then echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 exit 1 diff --git a/22/s2i/bin/assemble b/22/s2i/bin/assemble index c5e6a450..68ab6081 100755 --- a/22/s2i/bin/assemble +++ b/22/s2i/bin/assemble @@ -106,7 +106,7 @@ else # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true echo "---> Installing dependencies with npm ci" - if ! npm ci; then + if ! NODE_ENV=development npm ci; then if [ "${NPM_CI_STRICT}" == "true" ]; then echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 exit 1 diff --git a/24-minimal/s2i/bin/assemble b/24-minimal/s2i/bin/assemble index c5e6a450..68ab6081 100755 --- a/24-minimal/s2i/bin/assemble +++ b/24-minimal/s2i/bin/assemble @@ -106,7 +106,7 @@ else # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true echo "---> Installing dependencies with npm ci" - if ! npm ci; then + if ! NODE_ENV=development npm ci; then if [ "${NPM_CI_STRICT}" == "true" ]; then echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 exit 1 diff --git a/24/s2i/bin/assemble b/24/s2i/bin/assemble index c5e6a450..68ab6081 100755 --- a/24/s2i/bin/assemble +++ b/24/s2i/bin/assemble @@ -106,7 +106,7 @@ else # Prefer npm ci for reproducible installs; fall back unless NPM_CI_STRICT=true echo "---> Installing dependencies with npm ci" - if ! npm ci; then + if ! NODE_ENV=development npm ci; then if [ "${NPM_CI_STRICT}" == "true" ]; then echo "---> ERROR: npm ci failed and NPM_CI_STRICT=true; not falling back to npm install" >&2 exit 1