Skip to content

chore(install-dynamic-plugins): consume installer via yarn through dynamic-plugins workspace#4908

Open
gustavolira wants to merge 4 commits into
redhat-developer:mainfrom
gustavolira:chore/install-dynamic-plugins-npm-install
Open

chore(install-dynamic-plugins): consume installer via yarn through dynamic-plugins workspace#4908
gustavolira wants to merge 4 commits into
redhat-developer:mainfrom
gustavolira:chore/install-dynamic-plugins-npm-install

Conversation

@gustavolira
Copy link
Copy Markdown
Member

@gustavolira gustavolira commented Jun 1, 2026

Ready for review — merge gated on rhdh-plugins publish

The Containerfile change is finalized and the diff is small enough to review now. Cannot merge yet, though: the matching rhdh-plugins PR (#3246) needs to land first and publish @red-hat-developer-hub/cli-module-install-dynamic-plugins@0.1.0 on npm. Once that's published, the sequencing in § Sequencing plays out.

Known: Build Image is failing — and that's expected

The Build Image GitHub Actions check is red because dynamic-plugins/yarn.lock doesn't yet have an entry for @red-hat-developer-hub/cli-module-install-dynamic-plugins — the package isn't published, so yarn install --immutable can't resolve it.

This unblocks itself the moment rhdh-plugins#3246 merges and publishes:

cd dynamic-plugins/
yarn install                        # resolves against the now-published 0.1.0
git add yarn.lock
git commit -m "chore: regen yarn.lock for cli-module-install-dynamic-plugins"

After that commit, Build Image should go green.

What this PR does

build/containerfiles/Containerfile swaps the COPY of scripts/install-dynamic-plugins/{install-dynamic-plugins.cjs,install-dynamic-plugins.sh} for a yarn dependency declared in dynamic-plugins/package.json. The existing yarn install --immutable at line 151 of this Containerfile pulls the installer as part of the same yarn run that already brings in the rest of the dynamic-plugins deps, so the bin lands at /opt/app-root/src/dynamic-plugins/node_modules/.bin/install-dynamic-plugins. The final stage writes a small shim at /opt/app-root/src/install-dynamic-plugins.sh that delegates to that bin — Helm chart and Operator init-container spec keep invoking ./install-dynamic-plugins.sh /dynamic-plugins-root unchanged.

Why yarn (and not npm install)

scripts/local-hermeto-build.sh:213 already prefetches yarn deps for ./dynamic-plugins via cachi2/hermeto. Adding the installer as a yarn dep there reuses that existing prefetch — no infra change required in this repo or in the midstream Konflux pipeline.

An earlier revision of this PR used RUN npm install ... directly, which hit a real hermetic-build blocker: hermeto doesn't prefetch npm today, and Konflux runs with networking disabled. Wiring npm into hermeto would have spanned two repos (this one + the midstream). The yarn approach sidesteps that entirely.

Why we want to do this

scripts/install-dynamic-plugins/ is vendored in this repo today as a ~7000-line carry-over from the Python era. Consuming the package from npm means:

  • One source of truth (overlay repos and other RHDH consumers can npm install the same package).
  • This repo can drop the ~7000 lines of vendored installer in a follow-up cleanup PR.

The matching rhdh-plugins PR (#3246) ships the package the way Backstage cli-modules normally do: plain backstage-cli package build, no custom esbuild config, no keytar stub. So this PR doesn't need a "fix it later" follow-up on that side.

Trade-offs (cold-start benchmark)

hyperfine, 50 runs warm cache, empty dynamic-plugins.yaml:

Variant Bundle / install size Median σ
Bundled .cjs (current main) 231 KB single file 59 ms ±3 ms
npm install (cli-module dispatch) 25 MB node_modules 176 ms ±21 ms
npm install (fast-path bin, what this PR consumes) 25 MB node_modules 101 ms ±15 ms

~42 ms gap per pod start vs the current bundled approach. On a 20-plugin install where skopeo pulls dominate (~60 s of total work) that's 0.07% — invisible in practice. Image build picks up ~25 MB from the new layer (one-time, cached in OCI registry).

Containerfile change details

  • Install into /opt/app-root/src/dynamic-plugins/node_modules/.bin/install-dynamic-plugins via the existing yarn install --immutable step at line 151.
  • Build-time install-dynamic-plugins --help smoke check so a missing or renamed CLI entrypoint fails the image build instead of the init-container at pod start.
  • Shim at the original /opt/app-root/src/install-dynamic-plugins.sh path delegates to npm's .bin symlink so the Helm chart and Operator init-container spec keep working unchanged.

Sequencing

  1. rhdh-plugins#3246 merges → publishes @red-hat-developer-hub/cli-module-install-dynamic-plugins@0.1.0.
  2. yarn install in dynamic-plugins/, commit the updated yarn.lock to this branch — Build Image goes green.
  3. Merge this PR.

Follow-ups

  • Delete scripts/install-dynamic-plugins/ from this repo once the npm path is in place.

🤖 Generated with Claude Code

Replaces the COPY of scripts/install-dynamic-plugins/{install-dynamic-plugins.cjs,
install-dynamic-plugins.sh} with an `npm install` of
@red-hat-developer-hub/cli-module-install-dynamic-plugins (built and published
out of redhat-developer/rhdh-plugins).

This unblocks the cli-module structure on the rhdh-plugins side — it lets
that package use the standard `backstage-cli package build` (unbundled,
multi-file dist) instead of a custom esbuild bundle with a keytar stub. See
the conversation context: redhat-developer/rhdh-plugins#3254

Backward compatibility is preserved by writing a tiny
`/opt/app-root/src/install-dynamic-plugins.sh` shim that delegates to the
npm-installed bin, so the Helm chart and Operator init-container spec
continue to invoke `./install-dynamic-plugins.sh /dynamic-plugins-root`
unchanged.

DRAFT — DO NOT MERGE: blocked on
redhat-developer/rhdh-plugins#3254 (or the unbundled successor) being
merged and published to npm. Opened for review of the consumption pattern
and to back the cold-start benchmark posted in Slack.

Trade-off summary (cold-start benchmark on empty config):

- Current (bundled .cjs, 231 KB single file): ~89 ms warm cache (median)
- Proposed (npm install, 25 MB node_modules): ~180 ms warm cache (median)

The ~90 ms gap is the module-resolution overhead of unbundled Node — paid
once per pod start. Image build time also gets +`npm install` of ~25 MB
(one extra layer), offset by deleting ~7000 lines of vendored installer
script from this repo in a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented Jun 1, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 55.35%. Comparing base (c1acb63) to head (889413e).
⚠️ Report is 12 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4908      +/-   ##
==========================================
- Coverage   55.82%   55.35%   -0.48%     
==========================================
  Files         121      118       -3     
  Lines        2350     2325      -25     
  Branches      562      535      -27     
==========================================
- Hits         1312     1287      -25     
- Misses       1032     1033       +1     
+ Partials        6        5       -1     
Flag Coverage Δ
rhdh 55.35% <ø> (-0.48%) ⬇️

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c1acb63...889413e. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

… step

- Install into /opt/dynamic-plugins-installer (its own dir, no package.json)
  instead of /opt/app-root/src so npm cannot honor the yarn workspace and
  perturb the production tree that `yarn workspaces focus` built at line 208.
- Delegate the shim to `node_modules/.bin/install-dynamic-plugins` (the
  symlink npm creates from the package's bin field) instead of reaching
  into the package's internal layout.
- Add `--no-save --omit=dev` so npm doesn't write a package-lock.json into
  the installer dir and doesn't fetch devDependencies.
- Pin the installer to an exact version (0.1.0) so image builds are
  reproducible.
- Add a build-time smoke check (`install-dynamic-plugins --help`) so a
  missing or renamed CLI entrypoint fails the image build instead of the
  init container at pod start.

The hermetic-build concern (npm reaching the public registry when this
Containerfile runs under Konflux with networking disabled) is acknowledged
separately in the PR description — it's the real gating work and is not
addressed by this commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 1, 2026

The container image build workflow finished with status: cancelled.

The unbundled cli-module variant ships a fast-path bin that calls the
installer directly (bypassing @backstage/cli-node's runCliModule dispatch),
so the published binary takes the dynamic-plugins-root as a positional
without a subcommand prefix — matching the original CLI surface.

Verified locally:
  $ /opt/dynamic-plugins-installer/node_modules/.bin/install-dynamic-plugins /dynamic-plugins-root
exits 0 on an empty config.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gustavolira gustavolira marked this pull request as ready for review June 1, 2026 15:35
@gustavolira gustavolira changed the title [DRAFT] chore(install-dynamic-plugins): consume installer from npm chore(install-dynamic-plugins): consume installer from npm Jun 1, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 1, 2026

The container image build workflow finished with status: cancelled.

@openshift-ci openshift-ci Bot requested review from JessicaJHee and PatAKnight June 1, 2026 15:35
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 1, 2026

The container image build workflow finished with status: failure.

…gins yarn workspace

Pivots redhat-developer#4908 from `RUN npm install …` to a plain yarn dependency in
dynamic-plugins/package.json. The existing `yarn install --immutable` at
line 151 of this Containerfile pulls the installer as part of the same
yarn run that already brings in the rest of the dynamic-plugins deps, so
the bin lands at /opt/app-root/src/dynamic-plugins/node_modules/.bin/install-dynamic-plugins
and the final stage just writes a shim pointing there.

Why yarn and not npm: scripts/local-hermeto-build.sh:213 already
prefetches yarn deps for ./dynamic-plugins via cachi2/hermeto. No infra
change is needed in this repo or in the midstream Konflux pipeline — the
hermetic build "just works". The earlier npm-install approach required
wiring npm into hermeto's fetch-deps, which is real work spanning two
repos.

Validated locally: yarn install of the unbundled cli-module variant
(via a tarball of the fast-path build) produces .bin/install-dynamic-plugins
and the smoke invocation on an empty dynamic-plugins.yaml exits 0.

dynamic-plugins/yarn.lock is intentionally NOT regenerated in this
commit — that has to happen against the published
@red-hat-developer-hub/cli-module-install-dynamic-plugins@0.1.0 once
redhat-developer/rhdh-plugins ships it. Marking the PR as Draft until
then.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gustavolira gustavolira marked this pull request as draft June 3, 2026 19:10
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 3, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 3, 2026

The container image build workflow finished with status: failure.

@gustavolira gustavolira changed the title chore(install-dynamic-plugins): consume installer from npm chore(install-dynamic-plugins): consume installer via yarn through dynamic-plugins workspace Jun 4, 2026
gustavolira added a commit to gustavolira/rhdh-plugins that referenced this pull request Jun 4, 2026
…nbundled cli-module

Replaces the dual build (esbuild bundle + backstage-cli) with plain
`backstage-cli package build` — the standard Backstage cli-module pattern.
The bundle and the keytar gymnastics only existed to satisfy RHDH's
init-container `COPY` of a single self-contained `.cjs`; that consumption
model is moving to `npm install` (redhat-developer/rhdh#4908), so the
single-file requirement is going away.

What's left in the package:

- `src/installer.ts`: install pipeline + `main(args, programName)`.
- `src/index.ts`: `createCliModule(...)` default export, registers the
  `install` command. Discovered by `backstage-cli` when this package is a
  dependency of a host project.
- `src/command.ts`: thin loader that calls `installer.main`.
- `bin/install-dynamic-plugins`: fast-path shim that loads
  `dist/installer.cjs.js` directly and runs `main(process.argv.slice(2))`,
  bypassing `@backstage/cli-node`'s `runCliModule` dispatch — saves ~80 ms
  of cold start for direct/`npx`/init-container invocations. The
  cli-module discovery path still goes through `runCliModule` and pays the
  dispatch cost where it belongs.

Removed:
- `esbuild.config.mjs` (the custom bundle config)
- `src/cli.ts` (the esbuild entry)
- `dist/install-dynamic-plugins.cjs` from `files` (no longer produced)
- `esbuild` devDependency

166/166 tests pass; tsc/lint/prettier/api-reports clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
gustavolira added a commit to gustavolira/rhdh-plugins that referenced this pull request Jun 4, 2026
…undle

The README and the createCliModule docstring still referenced the
self-contained bundle that the package no longer ships:
- `npm run build` description and committed-bundle CI-check section.
- `node install-dynamic-plugins.cjs "$1"` wrapper line in "How RHDH
  consumes it" — replaced with a pointer to redhat-developer/rhdh#4908.
- `src/index.ts` describing the bin path as the bundle.
- Source layout was missing `index.ts` / `command.ts` / `installer.ts`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gustavolira gustavolira marked this pull request as ready for review June 4, 2026 19:35
@openshift-ci openshift-ci Bot requested review from kim-tsao and rohitkrai03 June 4, 2026 19:35
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented Jun 4, 2026

@gustavolira: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-ocp-helm 889413e link true /test e2e-ocp-helm

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant