Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, {
ignorePatterns: ['templates/**'],
ignorePatterns: ['templates/**', 'coverage/**', '**/lcov-report/**'],
rules: {
'no-console': 0,
},
Expand Down
91 changes: 87 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,90 @@
# @red-hat-developer-hub/cli
# Changelog

## 0.0.2
<!-- markdownlint-disable MD013 MD024 -->

### Patch Changes
All notable changes to `@red-hat-developer-hub/cli` are documented here. This project no longer uses Changesets; releases are versioned in `package.json` and described here (and in conventional commit messages / PR titles).

- 66e629b: fix missing node-stdlib-browser update in scalprumConfig.ts
## 1.10.3

### Added

- **`prepare` lifecycle**: when `dist/` is missing (for example after cloning or installing from a git ref), runs `yarn build` so colleagues can try a PR via `npx` / `yarn dlx` without a separate build step. Skipped when `dist/` already exists (for example the published npm tarball).
- **Plugin export — workspace `resolutions`**: `plugin export` merges `resolutions` from the `package.json` next to the same `yarn.lock` used for the export (plugin package or monorepo root). Non-portable values (`workspace:`, `portal:`, `link:` at value start, and non-string shapes) are omitted with a warning. Merge order: built-in AWS workarounds, then packed manifest, then workspace, then backend `additionalResolutions` (embedded `file:` still wins on conflicts).
- **Documentation**: maintainer guide for the export pipeline under [`doc/plugin-export/`](doc/plugin-export/README.md).

### Changed

- **Frontend dynamic plugin export**: Yarn project setup and install behavior aligned with backend export (lockfile, **generated minimal** Berry **`.yarnrc.yml`**, logging where applicable).
- **Dependencies**: `fast-xml-parser` 5.5.7 (#87).

### Fixed

- **Yarn Berry `dist-dynamic/.yarnrc.yml`**: no longer a copy of the monorepo file; the exporter writes a **minimal generated** config (**`httpTimeout`** + **`nodeLinker: node-modules`**) so **`yarn install`** does not fail when the parent repo lists Berry **plugins** (e.g. under **`.yarn/plugins/`**) that are not present in the export tree.
- **Yarn lockfile** for frontend plugin export installs (`yarn.lock` consistency).

## 1.10.2

### Changed

- **@backstage/cli** updated to **0.35.4** (#86).

### Added

- **jest-environment-jsdom** (dev) for the test toolchain.

## 1.10.1

### Changed — 1.10.1

- **Node.js**: CI and tooling moved to **Node 22** (#54).
- **Dependencies**: broad updates including **ESLint 9**, **webpack** / **@backstage/cli-common**, **axios**, **lodash** / **lodash-es**, **tar** (dev), **fast-xml-parser**, **ajv**, **undici**, **diff**, **jsonpath**, **node-forge**, **jws**, and others (Dependabot and manual bumps through #57–#62, #65–#66, #68–#69, #81, #83).

### Fixed

- **`plugin export`**: clearer handling when **`yarn install`** fails under `dist-dynamic` (log path, fail fast) — RHDHBUGS-2819 (#70, #73; see also #77).

## 1.10.0

### Changed

- **Version** aligned with RHDH **1.10** line.

### Fixed

- **E2E tests** for community plugin build (#52).
- **Webpack** dependency alignment across the tree (#39).

## 1.9.1

### Fixed — 1.9.1

- **Webpack** / **webpack-dev-server** version inconsistency in published metadata (#35 follow-up).

## 1.9.0

### Added

- **`--generate-module-federation-assets`** (and inverse) for **frontend** `plugin export`, emitting standard Module Federation assets alongside Scalprum (#31).
- **`generate-types`** script and **Backstage CLI** dependency bump for the build.

### Changed

- **Version** aligned with RHDH **1.9** line.

### Fixed

- **CLI binary** name / packaging (`rhdh-cli`) (#26).

## 1.8.0 and earlier

Releases **1.8.x** and below used the same tagging process (`tagRelease.sh` / automated bump PRs). Notable older changes include:

- **`plugin package`**: correct export command in user-facing text (#22).
- **README**: versioning notes; move away from Changesets toward conventional commits (#20).
- **E2E**: optional archive download instead of git clone for community plugin build tests.

## Historic (0.x)

### 0.0.2

- Fix missing **node-stdlib-browser** update in `scalprumConfig.ts` (66e629b; Changesets-era entry).
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ This repository hosts the source code for the rhdh-cli utility, a new command-li

This new CLI aims to offer more flexibility and ease of use compared to the previous @janus-idp/cli.

## Documentation

- **[Plugin export pipeline](doc/plugin-export/README.md)** — how `plugin export` works for backend and frontend plugins (`dist-dynamic`, packaging, and related CLI flags).

<!-- prettier breaks the formating for GitHub Markdown callout, this is why this whole block is ignored -->
<!-- prettier-ignore-start -->
> [!TIP]
Expand Down
73 changes: 73 additions & 0 deletions doc/plugin-export/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# `plugin export` — documentation index

This folder describes how **`rhdh-cli plugin export`** repackages a Backstage plugin into **`./dist-dynamic`** for use as a dynamic plugin. It is aimed at maintainers and contributors who need to understand or explain the pipeline.

## Quick links

| Document | Contents |
| -------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| [command-flow.md](./command-flow.md) | Shared steps after backend vs frontend dispatch: config schema, `supported-versions`, `--dev` |
| [backend-export.md](./backend-export.md) | Backend / backend-module export phases (`backend.ts`) |
| [frontend-export.md](./frontend-export.md) | Frontend / frontend-module export phases (`frontend.ts`) |
| [shared-packaging.md](./shared-packaging.md) | `productionPack`, `customizeForDynamicUse`, workspace `resolutions` inheritance, `initializeYarnProject` |

## Command and entrypoints

- **CLI registration**: [`src/commands/index.ts`](../../src/commands/index.ts) (`plugin export` subcommand and flags).
- **Orchestrator**: [`src/commands/export-dynamic-plugin/command.ts`](../../src/commands/export-dynamic-plugin/command.ts) — reads `backstage.role`, calls `backend()` or `frontend()`, then writes config schema, checks `supported-versions`, applies `--dev`.

**Example** (from the plugin package directory):

```bash
npx @red-hat-developer-hub/cli plugin export
```

## Prerequisites

- The **current working directory** (or Backstage CLI target) must be a package whose **`package.json`** defines **`backstage.role`**.
- **Supported roles** (see `command.ts`): `backend-plugin`, `backend-plugin-module`, `frontend-plugin`, `frontend-plugin-module`. Other roles are rejected.

## Output

- Primary artifact: **`dist-dynamic/`** under the plugin package, containing a derived **`package.json`**, optional **`yarn.lock`** / **`.yarnrc.yml`**, **`packageManager`**, and built assets (layout differs by role; see backend vs frontend docs).
- **Config schema** JSON is written under paths that depend on role (see [command-flow.md](./command-flow.md)).

## CLI options matrix

Flags are registered on `plugin export` for all roles; **only some are honored** depending on implementation.

| Option | Backend | Frontend | Notes |
| -------------------------------------------------------------------------------- | :-----: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--embed-package` | Yes | — | |
| `--shared-package` | Yes | — | Moves matching deps to `peerDependencies` (plus default `@backstage/*`). |
| `--allow-native-package` | Yes | — | |
| `--suppress-native-package` | Yes | — | |
| `--ignore-version-check` | Yes | — | Relaxes semver checks when embedding / hoisting peers. |
| `--no-install` / `--install` | Yes | Yes | Commander exposes `--no-install`. Backend help says “backend only” but **frontend** [`handlePackageInstall`](../../src/commands/export-dynamic-plugin/frontend.ts) also respects it. |
| `--no-build` / `--build` | Yes | — | |
| `--clean` | Yes | Yes | |
| `--dev` | Yes | Yes | Symlink `src` for **node** platform; frontend gets symlink/copy without `src` link. See [command-flow.md](./command-flow.md). |
| `--dynamic-plugins-root` | Yes | Yes | Used with `--dev` when copying instead of symlinking. |
| `--scalprum-config` | — | Yes | |
| `--track-dynamic-manifest-and-lock-file` | Yes | Yes | Whitelists `package.json`, `yarn.lock`, and `.yarnrc.yml` in `dist-dynamic/.gitignore`. Name refers to manifest + lockfile; **`.yarnrc.yml` is included** for Yarn Berry standalone config. |
| `--generate-scalprum-assets` / `--no-generate-scalprum-assets` | — | Yes | |
| `--generate-module-federation-assets` / `--no-generate-module-federation-assets` | — | Yes | At least one of Scalprum or MF must stay enabled. |

For exact strings, see [`src/commands/index.ts`](../../src/commands/index.ts).

## Source layout (reference)

```text
src/commands/export-dynamic-plugin/
command.ts # role dispatch + post-export steps
backend.ts # backend export implementation
frontend.ts # frontend export implementation
common-utils.ts # customizeForDynamicUse, initializeYarnProject
dev.ts # --dev symlink / install into dynamic plugins root
backend-utils.ts
types.ts
```

## Related commands

- **`plugin package`** — builds a container image / registry workflow from exported plugins ([`src/commands/index.ts`](../../src/commands/index.ts)); not covered here.
75 changes: 75 additions & 0 deletions doc/plugin-export/backend-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Backend dynamic plugin export

Walkthrough of [`backend.ts`](../../src/commands/export-dynamic-plugin/backend.ts): **`backend-plugin`** and **`backend-plugin-module`** packages. The result is a self-contained tree under **`dist-dynamic/`** suitable for loading as a Node dynamic plugin.

← [Back to index](./README.md) · Shared packaging: [shared-packaging.md](./shared-packaging.md)

## 1. Inputs and guards

- Parse the plugin **`package.json`**; reject if **`bundled: true`** (dynamic backend plugins must not be bundled in this sense).
- Resolve CLI lists: **`--embed-package`**, **`--shared-package`**, **`--allow-native-package`**, **`--suppress-native-package`**, **`--ignore-version-check`**.
- Load monorepo layout with **`@manypkg/get-packages`** from **`paths.targetDir`**.
- **`searchEmbedded`** ([`backend.ts`](../../src/commands/export-dynamic-plugin/backend.ts) + [`backend-utils`](../../src/commands/export-dynamic-plugin/backend-utils.ts)) resolves which packages to embed from the dependency graph and optional **`--embed-package`** names; may auto-include related **`-common`** / **`-node`** packages depending on role.

## 2. Shared dependency rules

- **`sharedPackagesRules`**: by default, **`@backstage/*`** dependencies are treated as **shared** (moved to **`peerDependencies`** during [`customizeForDynamicUse`](./shared-packaging.md)).
- **`--shared-package`** adds patterns (string or `/regex/`); entries prefixed with **`!`** go to **`exclude`** (do not force to peer).
- **Embedded package names** are always excluded from the “move to peer” rule so they stay private/embedded as intended.

## 3. Prepare `dist-dynamic`

- Optional **`--clean`**: remove **`dist-dynamic`** entirely.
- Recreate directory and write **`.gitignore`** (ignore all by default).
- With **`--track-dynamic-manifest-and-lock-file`**, whitelist **`package.json`**, **`yarn.lock`**, and **`.yarnrc.yml`** in **`dist-dynamic/.gitignore`** so they can be committed (productization). The flag name refers to manifest and lockfile; **`.yarnrc.yml` is included** for the generated minimal Yarn Berry config.

## 4. Suppress native (`--suppress-native-package`)

For each suppressed name, materialize a stub under **`embedded/<name>/`** (minimal **`package.json`** + **`index.js`** that throws) so resolutions can point at a non-native placeholder.

## 5. Embedded packages loop

For each resolved embedded package:

- Optional **`yarn build`** in the embedded package dir when **`--build`** (default install path uses `opts.build`).
- **`productionPack`** into **`dist-dynamic/embedded/<normalized-name>/`**, or **recursive copy** if already packed.
- Remove **`node_modules`** under the embedded copy if present.
- **`customizeForDynamicUse`** on the embedded **`package.json`** (private, version suffix `+embedded`, shared/peer rules, workspace resolution, Yarn v1 **`file:`** for embedded deps when applicable), including [lockfile-adjacent workspace **`resolutions`**](./shared-packaging.md#workspace-resolutions-inheritance) merged before backend-only **`additionalResolutions`**.
- Collect **peer dependencies** from embedded packages for later hoisting onto the main package.

## 6. Main package

- Optional **`yarn build`** at **`paths.targetDir`** when **`--build`**.
- **`productionPack`** with **`packageDir: ''`** so file resolution uses the **Backstage CLI target directory** (plugin root) as the pack root, output into **`dist-dynamic`**.
- Remove nested **`dist-dynamic/dist-dynamic`** if **`files`** accidentally included it.
- **`customizeForDynamicUse`** on the main **`package.json`** (same [workspace \*\*`resolutions` inheritance](./shared-packaging.md#workspace-resolutions-inheritance); embedded **`file:`** resolutions still win on conflicts):
- Rename to **`<original-name>-dynamic`**
- **`bundleDependencies: true`**
- Clear **`scripts`**
- **`resolutions`** / **`yarn`** resolutions wiring **`file:./embedded/...`** for embedded packages (and suppressed native stubs)
- Hoist collected embedded **peer** requirements onto the main **`peerDependencies`** when non-empty

Details of manifest rewriting: [shared-packaging.md](./shared-packaging.md).

## 7. Yarn project metadata (`initializeYarnProject`)

See [shared-packaging.md](./shared-packaging.md). Summaries: copy **`yarn.lock`** if missing, **generate** a minimal Berry **`.yarnrc.yml`** in **`dist-dynamic`** (**`httpTimeout`** + **`nodeLinker: node-modules`**; see shared doc), set **`packageManager`**.

## 8. `yarn install`

- Skipped when **`--no-install`**; user is warned the lockfile may be stale until they install manually.
- Otherwise run **`yarn install`** in **`dist-dynamic`** with Yarn **1.x** vs **Berry**-appropriate flags; log to a temp file on backend, then remove **`.yarn`** under **`dist-dynamic`** after success.

## 9. Post-install validation (install path only)

1. **Shared vs private**: scan **`yarn.lock`** so no **shared** package (per rules) appears as a private dependency; on failure, suggest **`--shared-package !...`** or **`--embed-package`** with a derived hint list.
2. **Native modules**: **`gatherNativeModules`**; fail unless listed in **`--allow-native-package`**.
3. **Entry points**: **`validatePluginEntryPoints`** ([`backend-utils.ts`](../../src/commands/export-dynamic-plugin/backend-utils.ts)) — ensures expected backend plugin entry surface.

On success, the yarn install log is removed (see **`logFile`** / **`fs.remove`** usage at the end of the install block in **`backend.ts`**).

## 10. Return value

Returns the absolute **`dist-dynamic`** path for **`command.ts`** to append config schema and dev steps.

← [Back to index](./README.md)
Loading
Loading