Backstage Software Catalog export
Generate Backstage catalog YAML from a local folder scan. CCE combines:
- Repository discovery — ownership candidates, README/TechDocs, OpenAPI/Swagger specs
- Cloud entitlement scan — SDK-proven dependencies as
Resourceentities
The result is multi-document YAML you can commit to a central catalog repo or register with catalog-info.yaml.
CCE is a standalone CLI — install with Homebrew, point it at a checkout, and write YAML to a file. Explicit CLI flags and .cce/backstage.yaml always win over discovery.
What you get
| Output | Purpose |
|---|---|
| Component | One service/repo — description, owner, lifecycle, SCM/TechDocs annotations, README link, providesApis, dependsOn |
| API (one per valid OpenAPI/Swagger file) | Discoverable contracts with inline spec.definition and Component providesApis links |
| Resource (one per deduplicated entitlement) | Cloud dependency referenced from Component dependsOn |
Ownership, docs, and APIs are the majority of Backstage value. Cloud resources remain useful when SDK usage exists, but repos without cloud clients still produce a useful Component (+ APIs/docs when present).
Install
brew tap stackgenhq/homebrew-stackgen
brew install stackgenhq/homebrew-stackgen/cce
cce -version
See Get started for container and source builds.
Prerequisites
cceon yourPATH(Homebrew or downloaded binary).- A local checkout of the repository to scan (
--folder .from the repo root, or any path). - Optional:
.cce/backstage.yamlwithowner_mappingsso CODEOWNERS / git authors become registered BackstageGroup/Userrefs.
Quick start
From your machine, in an existing clone:
cd ./checkout-api
cce --folder . \
--language GO \
--filter cloud \
--format backstage \
--output catalog.yaml \
--backstage-name checkout-api \
--classification live-service
Discovery fills description, SCM URL/slug, commit SHA, TechDocs (when mkdocs.yml + docs exist), OpenAPI APIs, and mapped ownership. Override any field explicitly when needed:
cce --folder . --language GO --filter cloud --format backstage --output catalog.yaml \
--scm github \
--source-url https://github.com/acme/checkout-api \
--project-slug acme/checkout-api \
--backstage-owner group:default/payments-team
Print to stdout:
cce --folder . --language AUTO --filter cloud --format backstage --output -
Use double-dash flags
Always write --folder, --language, --format. A single -folder is parsed incorrectly by the CLI.
What CCE discovers automatically
| Signal | Behavior |
|---|---|
| Git remote / HEAD | Source URL, SCM host, project slug, last commit (via go-git; no system git binary required) |
| Default branch | Remote origin/HEAD when present (so TechDocs/README URLs stay on main/master even if you scan a feature branch) |
| README | First meaningful paragraph → metadata.description (reads up to 64 KiB); README link when Source URL is known |
| MkDocs | If mkdocs.yml/mkdocs.yaml and a docs index exist → backstage.io/techdocs-ref (url:… when Source URL known, else dir:.) |
| OpenAPI / Swagger | Tracked openapi|swagger .yaml/.yml/.json files (skips vendor, node_modules, …) → API entities + providesApis. Specs larger than 2 MiB are skipped with a warning |
| CODEOWNERS | Root/general owners mapped via owner_mappings |
| Git history | Recent authors (bounded) mapped via owner_mappings when CODEOWNERS did not resolve |
Precedence for every field: CLI flags > .cce/backstage.yaml > discovery > defaults.
Ownership mapping
Backstage spec.owner must reference a registered Group/User. CCE never invents org entities. Configure mappings in .cce/backstage.yaml:
owner_mappings:
"@acme/payments-team": group:default/payments-team
"Jane Doe": group:default/payments-team
Resolution order when owner is unset:
- Mapped CODEOWNERS candidate (
*first, then other patterns) - Highest-frequency mapped recent commit author
unknown(with a stderr diagnostic when candidates existed but none mapped)
Only a non-sensitive cce.stackgen.com/owner-inference annotation (codeowners or git-history) is written — never emails.
APIs (OpenAPI / Swagger only)
This increment discovers spec files, not framework route annotations (Spring/FastAPI). Annotation-derived contracts cannot reliably fill Backstage’s required spec.definition across languages; that remains a later increment.
Duplicate titles get collision-safe names using the parent directory (for example store-api-user-service).
Choose language and filter
| Repo profile | Recommended --language |
--filter |
|---|---|---|
| Docs/API catalog with little cloud usage | AUTO or primary language |
cloud (Resources may be empty — that is fine) |
Go microservice (SDK in internal/) |
GO |
cloud |
| Python service (boto3 / azure SDK) | AUTO or PYTHON |
cloud |
| Go + large frontend monorepo | GO (not AUTO alone) |
cloud |
Mixed-language monorepos
--language AUTO can miss Go SDK calls when the tree is dominated by TypeScript or other languages. For Go+React repos, pass --language GO explicitly if the service’s cloud usage is in Go.
Use cce plan --folder . --language GO to confirm which source files will be parsed before a long scan.
Workflow
flowchart LR
checkout[Local git checkout]
discover[Discover ownership docs APIs]
scan[Scan cloud entitlements]
yaml[Multi-doc catalog YAML]
pr[Open PR to catalog repo]
bs[Backstage ingests entities]
checkout --> discover --> scan --> yaml --> pr --> bs
- Check out the service repo.
- Run
cce --format backstage(optional identity flags /owner_mappings). - Review owner, description, API count, TechDocs annotation, and
dependsOn. - Commit under your catalog repo layout.
- Register via Backstage catalog locations.
Scan the current directory
cd ~/src/checkout-api
cce --folder . --language GO --filter cloud --format backstage \
--output catalog.yaml \
--backstage-name checkout-api
GitLab or other SCM hosts
CCE clones GitHub with --repo. For GitLab, clone locally then scan; discovery reads origin when present:
git clone https://gitlab.example.com/acme/checkout-api.git ~/src/checkout-api
cce --folder ~/src/checkout-api --language GO --filter cloud --format backstage \
--output checkout-api.yaml \
--backstage-name checkout-api \
--backstage-owner group:default/platform-team
Repo-local metadata (.cce/backstage.yaml)
# .cce/backstage.yaml
name: checkout-api
description: Payment checkout microservice
owner: group:default/payments-team
type: service
lifecycle: production
system: commerce
tags:
- payments
- go
owner_mappings:
"@acme/payments-team": group:default/payments-team
CLI flags override file values. See example/backstage-config.yaml.
Defaults when unset: owner: unknown, type: service, lifecycle: production. --classification dormant or junk sets lifecycle: experimental unless lifecycle is already set.
CLI flags reference
Scan flags (same as JSON/SARIF)
| Flag | Default | Notes |
|---|---|---|
--folder |
. |
Path to local checkout |
--repo |
— | GitHub owner/repo clone + scan (GitHub only) |
--ref |
main |
Branch/tag with --repo |
--language |
GO |
GO, PYTHON, JAVA, JAVASCRIPT, AUTO |
--filter |
all |
Use cloud for cloud Resource lists |
--format |
text |
Set to backstage |
--output |
stdout | File path or - |
--mapper-file |
— | Optional custom lens (with --filter all) |
Backstage identity flags
| Flag | Purpose |
|---|---|
--backstage-config |
Path to .cce/backstage.yaml (default: <folder>/.cce/backstage.yaml) |
--backstage-name |
metadata.name (required; default from project slug or folder basename) |
--backstage-description |
Component description (else README discovery) |
--backstage-owner |
spec.owner (else mapped discovery / unknown) |
--backstage-type |
spec.type (default service) |
--backstage-lifecycle |
spec.lifecycle (default production) |
--backstage-system |
spec.system |
--backstage-namespace |
Entity namespace (default default) |
--backstage-language |
Language tag annotation |
--source-url |
Canonical repo URL (else git remote) |
--scm |
github or gitlab (else inferred from remote) |
--project-slug |
org/repo (else inferred from remote) |
--last-commit |
Commit SHA (else git rev-parse HEAD) |
--classification |
live-service, dormant, or junk |
Example output
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: checkout-api
description: Handles payment checkout for the commerce platform.
annotations:
cce.stackgen.com/scm: github
cce.stackgen.com/source-url: https://github.com/acme/checkout-api
backstage.io/source-location: url:https://github.com/acme/checkout-api/
backstage.io/techdocs-ref: url:https://github.com/acme/checkout-api/tree/main/
github.com/project-slug: acme/checkout-api
cce.stackgen.com/owner-inference: codeowners
links:
- url: https://github.com/acme/checkout-api/blob/main/README.md
title: README
icon: docs
spec:
type: service
lifecycle: production
owner: group:default/payments-team
providesApis:
- checkout-api
dependsOn:
- resource:default/aws-s3
---
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
name: checkout-api
title: Checkout API
spec:
type: openapi
lifecycle: production
owner: group:default/payments-team
definition: |
openapi: 3.0.3
info:
title: Checkout API
version: 1.0.0
paths: {}
---
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
name: aws-s3
tags: [aws, s3]
spec:
type: storage
owner: group:default/payments-team
Full entity field reference: Backstage catalog entity spec.
Register with Backstage
- Add the generated YAML to your catalog repository.
- Point a Backstage Location at the file or directory.
- Validate in the UI — Component should show APIs, docs, and resource dependencies.
Keep the JSON entitlement report alongside YAML when auditors need file/line proof:
cce --folder . --language GO --filter cloud --format json --output entitlements.json
cce --folder . --language GO --filter cloud --format backstage --output catalog.yaml
CI example
- name: Generate Backstage catalog fragment
run: |
cce --folder . --language GO --filter cloud --format backstage \
--output catalog-fragment.yaml \
--backstage-name "${{ github.event.repository.name }}" \
--last-commit "${{ github.sha }}"
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
owner: unknown |
No mappings for CODEOWNERS/authors | Add owner_mappings or --backstage-owner |
| No API entities | Spec files missing, invalid, or oversized | Ensure OpenAPI/Swagger root openapi/swagger + info; keep specs ≤ 2 MiB |
| Warning: OpenAPI skipped (size) | Spec exceeds the 2 MiB embed limit | Split or host the definition elsewhere; CCE embeds inline for central catalogs |
| TechDocs URL points at wrong branch | Missing origin/HEAD in shallow clones |
Ensure the clone records the remote default branch, or set Source URL / branch via metadata |
| No TechDocs annotation | Incomplete MkDocs layout | Need mkdocs.yml and docs/index.md (or configured docs_dir) |
Empty dependsOn |
No cloud SDK usage / wrong language | Expected for many repos; use --language appropriately |
--repo fails on GitLab |
GitHub-only clone helper | git clone locally, use --folder |
Limitations
- Proves static SDK entitlements, not live ARNs or runtime traffic.
- Ownership requires configured mappings to registered Backstage identities.
- API discovery is spec-file based (OpenAPI/Swagger), not framework annotations.
- Oversized OpenAPI files (>2 MiB) are omitted from the catalog with a diagnostic.
- Does not emit
System,Domain,User, orGroupentities. - Relations are written once: Component
dependsOn/providesApis(no reversedependencyOfedges).
Related docs
| Topic | Page |
|---|---|
| Release history | Changelog |
| Entity schema & annotations | Backstage catalog entity spec |
| Cloud entitlement JSON | Cloud entitlements |
| As-built dependency map | As-built dependency map |
| Enterprise catalogs | Enterprise lenses & catalogs |