Skip to content

Commit b9b8631

Browse files
feat: add plugin metadata utility for dynamic plugins configuration (#22)
* feat: add plugin metadata utility for dynamic plugins configuration * feat: add plugin metadata utility for dynamic plugins configuration
1 parent dc063b4 commit b9b8631

File tree

18 files changed

+982
-29
lines changed

18 files changed

+982
-29
lines changed

docs/.vitepress/config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ export default defineConfig({
153153
text: "Environment Substitution",
154154
link: "/guide/utilities/environment-substitution",
155155
},
156+
{
157+
text: "Plugin Metadata",
158+
link: "/guide/utilities/plugin-metadata",
159+
},
156160
],
157161
},
158162
{
@@ -244,6 +248,7 @@ export default defineConfig({
244248
{ text: "Bash ($)", link: "/api/utils/bash" },
245249
{ text: "YAML Merging", link: "/api/utils/merge-yamls" },
246250
{ text: "envsubst", link: "/api/utils/common" },
251+
{ text: "Plugin Metadata", link: "/api/utils/plugin-metadata" },
247252
],
248253
},
249254
{

docs/api/deployment/rhdh-deployment.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,10 @@ async deploy(): Promise<void>
7575

7676
Deploy RHDH to the cluster. This:
7777
1. Merges configuration files
78-
2. Applies ConfigMaps and Secrets
79-
3. Installs RHDH via Helm or Operator
80-
4. Waits for deployment to be ready
78+
2. [Injects plugin metadata](/guide/configuration/config-files#plugin-metadata-injection) into dynamic plugins config
79+
3. Applies ConfigMaps and Secrets
80+
4. Installs RHDH via Helm or Operator
81+
5. Waits for deployment to be ready
8182

8283
```typescript
8384
await rhdh.deploy();

docs/api/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Complete API documentation for all exports from `rhdh-e2e-test-utils`.
5050
- [Bash ($)](/api/utils/bash) - Shell command execution
5151
- [YAML Merging](/api/utils/merge-yamls) - YAML utilities
5252
- [envsubst](/api/utils/common) - Environment substitution
53+
- [Plugin Metadata](/api/utils/plugin-metadata) - Plugin metadata injection
5354

5455
### ESLint
5556

docs/api/utils/plugin-metadata.md

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
# Plugin Metadata
2+
3+
Utilities for loading and injecting plugin metadata from Package CRD files into dynamic plugins configuration.
4+
5+
## Import
6+
7+
```typescript
8+
import {
9+
shouldInjectPluginMetadata,
10+
extractPluginName,
11+
getMetadataDirectory,
12+
parseAllMetadataFiles,
13+
injectMetadataConfig,
14+
generateDynamicPluginsConfigFromMetadata,
15+
loadAndInjectPluginMetadata,
16+
} from "rhdh-e2e-test-utils/utils";
17+
```
18+
19+
## Functions
20+
21+
### shouldInjectPluginMetadata()
22+
23+
Checks if plugin metadata handling should be enabled.
24+
25+
```typescript
26+
function shouldInjectPluginMetadata(): boolean
27+
```
28+
29+
**Returns:** `true` if metadata handling is enabled, `false` otherwise.
30+
31+
**Behavior:**
32+
- Returns `false` if `RHDH_SKIP_PLUGIN_METADATA_INJECTION` environment variable is set
33+
- Returns `false` if `JOB_NAME` contains `periodic-` (nightly/periodic builds)
34+
- Returns `true` otherwise (default for local dev and PR builds)
35+
36+
**Example:**
37+
38+
```typescript
39+
if (shouldInjectPluginMetadata()) {
40+
// Load and inject metadata
41+
}
42+
```
43+
44+
---
45+
46+
### extractPluginName()
47+
48+
Extracts the plugin name from a package path or OCI reference.
49+
50+
```typescript
51+
function extractPluginName(packageRef: string): string
52+
```
53+
54+
**Parameters:**
55+
| Parameter | Type | Description |
56+
|-----------|------|-------------|
57+
| `packageRef` | `string` | The package reference string |
58+
59+
**Returns:** The extracted plugin name.
60+
61+
**Supported Formats:**
62+
63+
| Format | Example | Extracted Name |
64+
|--------|---------|----------------|
65+
| Wrapper path | `./dynamic-plugins/dist/my-plugin` | `my-plugin` |
66+
| OCI with tag | `oci://quay.io/rhdh/my-plugin:1.0.0` | `my-plugin` |
67+
| OCI with digest | `oci://quay.io/rhdh/my-plugin@sha256:abc...` | `my-plugin` |
68+
| OCI with alias | `oci://quay.io/rhdh/my-plugin@sha256:abc!alias` | `my-plugin` |
69+
| GHCR | `ghcr.io/org/repo/my-plugin:tag` | `my-plugin` |
70+
71+
**Example:**
72+
73+
```typescript
74+
const name = extractPluginName("oci://quay.io/rhdh/backstage-community-plugin-tech-radar:1.0.0");
75+
// Returns: "backstage-community-plugin-tech-radar"
76+
```
77+
78+
---
79+
80+
### getMetadataDirectory()
81+
82+
Gets the metadata directory path.
83+
84+
```typescript
85+
function getMetadataDirectory(metadataPath?: string): string | null
86+
```
87+
88+
**Parameters:**
89+
| Parameter | Type | Default | Description |
90+
|-----------|------|---------|-------------|
91+
| `metadataPath` | `string` | `"../metadata"` | Path to metadata directory |
92+
93+
**Returns:** The resolved metadata directory path, or `null` if it doesn't exist.
94+
95+
**Example:**
96+
97+
```typescript
98+
const metadataDir = getMetadataDirectory();
99+
if (metadataDir) {
100+
console.log(`Found metadata at: ${metadataDir}`);
101+
}
102+
```
103+
104+
---
105+
106+
### parseAllMetadataFiles()
107+
108+
Parses all metadata files in a directory and builds a map of plugin name to config.
109+
110+
```typescript
111+
async function parseAllMetadataFiles(
112+
metadataDir: string
113+
): Promise<Map<string, PluginMetadata>>
114+
```
115+
116+
**Parameters:**
117+
| Parameter | Type | Description |
118+
|-----------|------|-------------|
119+
| `metadataDir` | `string` | Path to the metadata directory |
120+
121+
**Returns:** Map of plugin name to [`PluginMetadata`](#pluginmetadata).
122+
123+
**Example:**
124+
125+
```typescript
126+
const metadataDir = getMetadataDirectory();
127+
if (metadataDir) {
128+
const metadataMap = await parseAllMetadataFiles(metadataDir);
129+
console.log(`Found ${metadataMap.size} plugins`);
130+
}
131+
```
132+
133+
---
134+
135+
### injectMetadataConfig()
136+
137+
Injects plugin configurations from metadata into a dynamic plugins config.
138+
139+
```typescript
140+
function injectMetadataConfig(
141+
dynamicPluginsConfig: DynamicPluginsConfig,
142+
metadataMap: Map<string, PluginMetadata>
143+
): DynamicPluginsConfig
144+
```
145+
146+
**Parameters:**
147+
| Parameter | Type | Description |
148+
|-----------|------|-------------|
149+
| `dynamicPluginsConfig` | [`DynamicPluginsConfig`](#dynamicpluginsconfig) | The config to augment |
150+
| `metadataMap` | `Map<string, PluginMetadata>` | Map of plugin metadata |
151+
152+
**Returns:** Augmented configuration with injected pluginConfigs.
153+
154+
**Merge Behavior:** Metadata config serves as the base, user-provided pluginConfig overrides it.
155+
156+
---
157+
158+
### generateDynamicPluginsConfigFromMetadata()
159+
160+
Generates a complete dynamic-plugins configuration from metadata files.
161+
162+
```typescript
163+
async function generateDynamicPluginsConfigFromMetadata(
164+
metadataPath?: string
165+
): Promise<DynamicPluginsConfig>
166+
```
167+
168+
**Parameters:**
169+
| Parameter | Type | Default | Description |
170+
|-----------|------|---------|-------------|
171+
| `metadataPath` | `string` | `"../metadata"` | Path to metadata directory |
172+
173+
**Returns:** Complete dynamic plugins configuration with all plugins enabled.
174+
175+
**Behavior:**
176+
- Returns `{ plugins: [] }` if [`shouldInjectPluginMetadata()`](#shouldinjectpluginmetadata) returns `false`
177+
- Throws error if metadata directory not found
178+
- Throws error if no valid metadata files found
179+
- All generated plugins have `disabled: false`
180+
181+
**Example:**
182+
183+
```typescript
184+
const config = await generateDynamicPluginsConfigFromMetadata();
185+
console.log(`Generated config with ${config.plugins?.length} plugins`);
186+
```
187+
188+
---
189+
190+
### loadAndInjectPluginMetadata()
191+
192+
Main function to load and inject plugin metadata for PR builds.
193+
194+
```typescript
195+
async function loadAndInjectPluginMetadata(
196+
dynamicPluginsConfig: DynamicPluginsConfig,
197+
metadataPath?: string
198+
): Promise<DynamicPluginsConfig>
199+
```
200+
201+
**Parameters:**
202+
| Parameter | Type | Default | Description |
203+
|-----------|------|---------|-------------|
204+
| `dynamicPluginsConfig` | [`DynamicPluginsConfig`](#dynamicpluginsconfig) | - | The config to augment |
205+
| `metadataPath` | `string` | `"../metadata"` | Path to metadata directory |
206+
207+
**Returns:** Augmented configuration with metadata (for PR) or unchanged (for nightly).
208+
209+
**Behavior:**
210+
- Returns config unchanged if [`shouldInjectPluginMetadata()`](#shouldinjectpluginmetadata) returns `false`
211+
- Throws error if metadata directory not found
212+
- Throws error if no valid metadata files found
213+
- Only injects metadata for plugins already in the config
214+
215+
**Example:**
216+
217+
```typescript
218+
const config = { plugins: [{ package: "./dynamic-plugins/dist/my-plugin", disabled: false }] };
219+
const augmented = await loadAndInjectPluginMetadata(config);
220+
```
221+
222+
## Types
223+
224+
### PluginMetadata
225+
226+
```typescript
227+
interface PluginMetadata {
228+
/** The dynamic artifact path (e.g., ./dynamic-plugins/dist/plugin-name) */
229+
packagePath: string;
230+
/** The plugin configuration from appConfigExamples[0].content */
231+
pluginConfig: Record<string, unknown>;
232+
/** The package name (e.g., @backstage-community/plugin-tech-radar) */
233+
packageName: string;
234+
/** Source metadata file path */
235+
sourceFile: string;
236+
}
237+
```
238+
239+
### PluginEntry
240+
241+
```typescript
242+
interface PluginEntry {
243+
package: string;
244+
disabled?: boolean;
245+
pluginConfig?: Record<string, unknown>;
246+
[key: string]: unknown;
247+
}
248+
```
249+
250+
### DynamicPluginsConfig
251+
252+
```typescript
253+
interface DynamicPluginsConfig {
254+
plugins?: PluginEntry[];
255+
includes?: string[];
256+
[key: string]: unknown;
257+
}
258+
```
259+
260+
## Constants
261+
262+
### DEFAULT_METADATA_PATH
263+
264+
```typescript
265+
const DEFAULT_METADATA_PATH = "../metadata";
266+
```
267+
268+
Default metadata directory path relative to the e2e-tests directory.
269+
270+
## See Also
271+
272+
- [Configuration Files](/guide/configuration/config-files#plugin-metadata-injection) - How metadata injection works during deployment
273+
- [Environment Variables](/guide/configuration/environment-variables#plugin-metadata-variables) - Variables that control metadata handling

0 commit comments

Comments
 (0)