diff --git a/AGENTS.md b/AGENTS.md index 0bf4a52..dce93eb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,6 +38,30 @@ import prefix, typescript:S4624 nested template literals) before they accumulate its own `.command('action')` with only the flags that apply to it. See `plugin dev` in `src/commands/index.ts` and `src/commands/dev/` for the reference implementation. +- **RHDH-to-Backstage version mapping**: RHDH versions (e.g. `2.1.0`) are + not Backstage versions. The CLI resolves RHDH versions to Backstage + release versions via a 3-tier strategy: (1) remote metadata from the + RHDH GitHub release branch, (2) a static compatibility matrix + (`RHDH_COMPATIBILITY_MATRIX` in `src/lib/rhdhVersion.ts`), (3) Backstage + release manifest fetch from `versions.backstage.io`. If neither tier 1 + nor tier 2 resolves a Backstage version, an error is raised. Bare + version numbers like `1.54.0` that do not match a + known RHDH release are rejected — users must prefix with `backstage:` to + target a Backstage version directly (e.g. `backstage:1.54.0`). The static + matrix must be updated manually each RHDH release cycle. +- **Offline vs air-gapped**: `RHDH_OFFLINE=true` skips + the GitHub metadata lookup (tier 1) and falls back to the static + compatibility matrix (tier 2), but the Backstage release manifest still + fetches from `versions.backstage.io`. For true air-gapped use, users must + also supply `--manifest-file ` (or set `BACKSTAGE_MANIFEST_FILE`) + pointing to a local copy of the Backstage release manifest JSON. +- **Error handling in plugin commands**: Plugin command functions (under + `src/commands/`) signal non-zero exit by throwing `ExitCodeError` from + `src/lib/errors.ts`. The `lazy()` wrapper in `src/commands/index.ts` + catches it and calls `process.exit(error.code)`. This keeps command + functions testable — tests can catch the error without `process.exit()` + killing the test runner. Intent-based action commands use + `handleCommandError` from `intent-errors.ts` instead. ## CLI UX Design Conventions @@ -170,6 +194,59 @@ subprocess error. as symlinks in the staged copy rather than followed, which would cause a self-copy error on repeated `update` runs. +### `rhdhVersion.ts` — version resolution engine + +`src/lib/rhdhVersion.ts` is the core abstraction that maps RHDH version +queries to Backstage release versions and their package manifests. All +commands that depend on a target RHDH version (`check-versions`, `upgrade`, +`new`) call `resolveRhdhVersion()` as their entry point. + +**3-tier resolution:** + +1. **Remote metadata (tier 1)** — fetches `build-metadata.json` from the + RHDH GitHub repository's release branch (e.g. `release-2.0` for RHDH + `2.0.x`). Extracts the Backstage version from the `card` object. + Skipped when `RHDH_OFFLINE=true` or `{ offline: true }`. +2. **Static compatibility matrix (tier 2)** — `RHDH_COMPATIBILITY_MATRIX` + maps known RHDH releases to Backstage versions. Used as fallback when + remote lookup fails, times out, or is skipped. This matrix must be + updated manually each RHDH release cycle. +3. **Backstage release manifest (tier 3)** — once a Backstage version is + determined (by tier 1 or 2), the manifest is fetched from + `versions.backstage.io` (or from `BACKSTAGE_VERSIONS_BASE_URL` / + `--manifest-file`) to get concrete package versions for dependency + alignment. + +**Caching:** Resolved versions are cached by a composite key of +`normalizedVersion + manifestFile + versionsBaseUrl + offline` so +different resolution contexts (e.g. different base URLs) produce +separate cache entries. + +Key files: + +- `src/lib/rhdhVersion.ts` — RHDH version normalization, GitHub ref + mapping, remote metadata fetch, static matrix lookup, and the main + `resolveRhdhVersion` entry point. +- `src/lib/backstageVersion.ts` — Backstage manifest fetching, + `backstage:^` protocol resolution, `backstage.json` version detection. + +### `check-versions` — dependency alignment command + +`src/commands/check-versions/` owns `rhdh-cli plugin check-versions`. It +audits a plugin's `package.json` dependencies against the Backstage release +manifest for a target RHDH version. Dependency status is one of: `match`, +`mismatch`, `unmanifested` (a `@backstage/` package not in the manifest), +or `unverifiable` (`backstage:^` peer dependencies that cannot be resolved +without `backstage.json`). + +Key files: + +- `command.ts` — `checkPluginDependencies()` audit logic, human-readable + tabular output, and JSON mode. +- `command.test.ts` — test patterns using `jest.mock` to stub + `resolveRhdhVersion` at the module boundary, allowing unit tests of + audit logic and CLI output without triggering any network calls. + ## Pattern References - New command group: `src/commands/intent-based-actions/catalog.ts` @@ -177,6 +254,10 @@ self-copy error on repeated `update` runs. - Human/JSON output formatting: `src/commands/intent-based-actions/format.ts` - Structured CLI errors: `src/commands/intent-based-actions/intent-errors.ts` - Repeatable `key=value` and JSON input parsing: `src/commands/intent-based-actions/kv.ts` +- Version-aware CLI command: `src/commands/check-versions/command.ts` +- RHDH-to-Backstage version resolution: `src/lib/rhdhVersion.ts` +- Test patterns with mocked fetch responses: `src/lib/rhdhVersion.test.ts` +- Backstage manifest and `backstage:^` resolution: `src/lib/backstageVersion.ts` ## CI & Packaging