Skip to content

Instantly share code, notes, and snippets.

@nickboldt
Last active November 19, 2025 18:58
Show Gist options
  • Select an option

  • Save nickboldt/bb7cfdd516f524391351a5dacbe15ce0 to your computer and use it in GitHub Desktop.

Select an option

Save nickboldt/bb7cfdd516f524391351a5dacbe15ce0 to your computer and use it in GitHub Desktop.
convert dynamic-plugins.default.yaml into a list of packages, from which we can then later recreate the dynamic-plugins.default.yaml using the latest pluginConfig content
Step 1: create a copy of ``dynamic-plugins.default.yaml` called `default.packages.yaml`
This file will remove all the pluginConfig content and keep only the `plugins[].package` content. Those plugins should then be moved into two arrays,
`packages.enabled[]` and `packages.disabled[], under which we will list the `package` values.
Step 2: using the list of packages, sorted into enabled and disabled groups in the new `default.packages.yaml` file, we now need to convert those package path values to actual package references.
For each `package` that refers to a path under `./dynamic-plugins/dist/`,
compute the associated package name from the related path under `dynamic-plugins/wrappers/` to get
the value of the related dependencies.
For example in `dynamic-plugins/wrappers/backstage-community-plugin-3scale-backend-dynamic/package.json`
we see the primary dependency in this plugin wrapper is "@backstage-community/plugin-3scale-backend"
So the resulting file would have
```
packages:
disabled:
- package: "@backstage-community/plugin-3scale-backend"
```
etc.
For values of `package` which already refer to a package, like `"@redhat/[email protected]"`, strip the version from the string so we have only
```
packages:
disabled:
- package: "@redhat/backstage-plugin-orchestrator"
```
Step 3: sort `default.packages.yaml` with the enabled packages listed before the disabled ones,
and the packages within each grouping sorted alphabetically
Step 4: save a copy of the transformation script into rhdh-plugin-catalog/build/scripts/extract_packages.py
#!/usr/bin/env python3
"""
Genarated-By: Cursor
Process dynamic-plugins.default.yaml to create default.packages.yaml
This script converts a dynamic-plugins.default.yaml file into a simplified
default.packages.yaml file that contains only package references (no config),
organized into enabled and disabled arrays.
For packages with local paths (./dynamic-plugins/dist/...), it resolves them
to their actual npm package names by reading the corresponding wrapper's package.json.
For packages with version strings (@scope/package@version), it strips the version.
Packages are sorted alphabetically (case-insensitive) within each group.
"""
import yaml
import json
import os
import re
from pathlib import Path
def get_package_from_wrapper(wrapper_name):
"""Get the primary dependency package from a wrapper's package.json"""
wrapper_path = Path(f"dynamic-plugins/wrappers/{wrapper_name}/package.json")
if not wrapper_path.exists():
print(f"Warning: Could not find {wrapper_path}")
return None
try:
with open(wrapper_path, 'r') as f:
pkg_data = json.load(f)
dependencies = pkg_data.get('dependencies', {})
# Filter out non-plugin dependencies
excluded_deps = [
'@mui/', '@material-ui/', 'react', '@types/',
'@backstage/core-', '@backstage/theme-'
]
plugin_deps = []
for dep in dependencies:
# Skip excluded dependencies
if any(dep.startswith(excl) for excl in excluded_deps):
continue
# Only include plugin-like dependencies
if any(dep.startswith(prefix) for prefix in [
'@backstage/', '@backstage-community/', '@roadiehq/',
'@red-hat-developer-hub/', '@redhat/', '@pagerduty/',
'@parfuemerie-douglas/', '@immobiliarelabs/'
]):
plugin_deps.append(dep)
if not plugin_deps:
return None
# Try to find the best match based on the wrapper name
# First, try to find an exact match (converting wrapper name to package format)
wrapper_base = wrapper_name.replace('-dynamic', '')
for dep in plugin_deps:
# Extract the package name without scope
dep_name = dep.split('/')[-1] if '/' in dep else dep
# Check if wrapper name contains or matches the package name
if wrapper_base in dep or dep_name.replace('backstage-', '').replace('plugin-', '') in wrapper_base:
# If there's a close match, prefer it
if wrapper_base.replace('-', '') in dep.replace('-', '').replace('/', '').replace('@', ''):
return dep
# Return the longest matching dependency (usually the most specific one)
plugin_deps.sort(key=len, reverse=True)
return plugin_deps[0]
except Exception as e:
print(f"Error reading {wrapper_path}: {e}")
return None
def process_package_string(package_str):
"""Convert a package string to a clean package name"""
# If it's a path under ./dynamic-plugins/dist/
if package_str.startswith('./dynamic-plugins/dist/'):
# Extract the wrapper name from the path
wrapper_name = package_str.replace('./dynamic-plugins/dist/', '')
# Get the package name from the wrapper's package.json
pkg_name = get_package_from_wrapper(wrapper_name)
if pkg_name:
return pkg_name
else:
print(f"Warning: Could not resolve package for {wrapper_name}")
return None
else:
# It's already a package reference, strip the version
# Pattern: @scope/package@version or package@version
package_no_version = re.sub(r'@[\d.]+.*$', '', package_str)
return package_no_version
def main():
# Load the original YAML
with open('dynamic-plugins.default.yaml', 'r') as f:
data = yaml.safe_load(f)
enabled_packages = []
disabled_packages = []
# Process each plugin
for plugin in data.get('plugins', []):
package_str = plugin.get('package', '')
disabled = plugin.get('disabled', False)
# Convert the package string
clean_package = process_package_string(package_str)
if clean_package:
if disabled:
disabled_packages.append(clean_package)
else:
enabled_packages.append(clean_package)
# Sort packages alphabetically (case-insensitive)
enabled_packages.sort(key=str.lower)
disabled_packages.sort(key=str.lower)
# Create the new structure
output = {
'packages': {
'enabled': [{'package': pkg} for pkg in enabled_packages],
'disabled': [{'package': pkg} for pkg in disabled_packages]
}
}
# Write the output
with open('default.packages.yaml', 'w') as f:
yaml.dump(output, f, default_flow_style=False, sort_keys=False, width=120)
print(f"Processed {len(enabled_packages)} enabled and {len(disabled_packages)} disabled packages")
print(f"Packages sorted alphabetically within each group")
print(f"Output written to default.packages.yaml")
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment