Replace Python SBOM generator with cargo-sbom

- Remove LICENSE.md and docs/DEPENDENCIES.md (redundant with SBOM JSON)
- Remove scripts/generate_sbom.py (replaced by cargo-sbom)
- Update distro::generate-sbom recipe to use cargo-sbom CLI
- Generates SPDX 2.3 and CycloneDX 1.4 formats natively
- Eliminates Python dependency for SBOM generation
This commit is contained in:
Jesús Pérez 2025-12-18 01:33:58 +00:00
parent 7d9fb0e2bd
commit 77610db201
6 changed files with 20735 additions and 9275 deletions

View File

@ -1,231 +0,0 @@
# TypeDialog License
## Project License
TypeDialog is licensed under the **MIT License**.
See [LICENSE](LICENSE) file for the full MIT license text.
---
## Dependencies
This project includes the following dependencies under their respective licenses:
### Apache-2.0 Only (3)
- rpassword 7.4.0
- rtoolbox 0.0.3
- sync_wrapper 1.0.2
### MIT Only (66)
- atty 0.2.14
- axum 0.8.7
- axum-core 0.5.5
- bytes 1.11.0
- castaway 0.2.4
- compact_str 0.8.1
- console 0.16.2
- convert_case 0.10.0
- crossterm 0.28.1
- crossterm 0.29.0
- crossterm_winapi 0.9.1
- darling 0.20.11
- darling_core 0.20.11
- darling_macro 0.20.11
- derive_more 2.1.0
- derive_more-impl 2.1.0
- dialoguer 0.12.0
- typedialog 0.1.0
- typedialog-core 0.1.0
- typedialog-tui 0.1.0
- typedialog-web 0.1.0
- fuzzy-matcher 0.3.7
- generic-array 0.14.7
- globwalk 0.9.1
- http-body 1.0.1
- http-body-util 0.1.3
- http-range-header 0.4.2
- hyper 1.8.1
- hyper-util 0.1.19
- inquire 0.9.1
- instability 0.3.10
- libm 0.2.15
- libredox 0.1.10
- lru 0.12.5
- mime_guess 2.0.5
- mio 1.1.1
- nu-ansi-term 0.50.3
- parse-zoneinfo 0.3.1
- phf 0.11.3
- phf_codegen 0.11.3
- phf_generator 0.11.3
- phf_shared 0.11.3
- ratatui 0.29.0
- redox_syscall 0.5.18
- redox_users 0.5.2
- sharded-slab 0.1.7
- slab 0.4.11
- strsim 0.11.1
- strum 0.26.3
- strum_macros 0.26.4
- tera 1.20.1
- tokio 1.48.0
- tokio-macros 2.6.0
- tokio-util 0.7.17
- tower 0.5.2
- tower-http 0.6.8
- tower-layer 0.3.3
- tower-service 0.3.3
- tracing 0.1.43
- tracing-attributes 0.1.31
- tracing-core 0.1.35
- tracing-log 0.2.0
- tracing-subscriber 0.3.22
- unsafe-libyaml 0.2.11
- valuable 0.1.1
- winnow 0.7.14
### Apache-2.0 OR MIT (190)
Most dependencies use dual licensing between Apache-2.0 and MIT.
- allocator-api2 0.2.21 | - android_system_properties 0.1.5 | - anstream 0.6.21
- anstyle 1.0.13 | - anstyle-parse 0.2.7 | - anstyle-query 1.1.5
- anstyle-wincon 3.0.11 | - anyhow 1.0.100 | - async-trait 0.1.89
- atomic-waker 1.1.2 | - autocfg 1.5.0 | - bitflags 2.10.0
- block-buffer 0.10.4 | - bstr 1.12.1 | - bumpalo 3.19.0
- cassowary 0.3.0 | - cc 1.2.49 | - cfg-if 1.0.4
- chrono 0.4.42 | - chrono-tz 0.9.0 | - chrono-tz-build 0.3.0
- clap 4.5.53 | - clap_builder 4.5.53 | - clap_derive 4.5.49
- clap_lex 0.7.6 | - colorchoice 1.0.4 | - core-foundation-sys 0.8.7
- cpufeatures 0.2.17 | - crossbeam-deque 0.8.6 | - crossbeam-epoch 0.9.18
- crossbeam-utils 0.8.21 | - crypto-common 0.1.7 | - digest 0.10.7
- dirs 6.0.0 | - dirs-sys 0.5.0 | - displaydoc 0.2.5
- document-features 0.2.12 | - dyn-clone 1.0.20 | - either 1.15.0
- encode_unicode 1.0.0 | - equivalent 1.0.2 | - errno 0.3.14
- fastrand 2.3.0 | - find-msvc-tools 0.1.5 | - fluent 0.17.0
- fluent-bundle 0.16.0 | - fluent-langneg 0.13.1 | - fluent-syntax 0.12.0
- fnv 1.0.7 | - form_urlencoded 1.2.2 | - futures 0.3.31
- futures-channel 0.3.31 | - futures-core 0.3.31 | - futures-executor 0.3.31
- futures-io 0.3.31 | - futures-macro 0.3.31 | - futures-sink 0.3.31
- futures-task 0.3.31 | - futures-util 0.3.31 | - getrandom 0.2.16
- getrandom 0.3.4 | - hashbrown 0.15.5 | - hashbrown 0.16.1
- heck 0.5.0 | - hermit-abi 0.1.19 | - http 1.4.0
- httparse 1.10.1 | - httpdate 1.0.3 | - humansize 2.1.3
- iana-time-zone 0.1.64 | - iana-time-zone-haiku 0.1.2 | - ident_case 1.0.1
- indexmap 2.12.1 | - indoc 2.0.7 | - intl-memoizer 0.5.3
- intl_pluralrules 7.0.2 | - is_terminal_polyfill 1.70.2 | - itertools 0.13.0
- itoa 1.0.15 | - js-sys 0.3.83 | - lazy_static 1.5.0
- libc 0.2.178 | - litrs 1.0.0 | - lock_api 0.4.14
- log 0.4.29 | - mime 0.3.17 | - num-traits 0.2.19
- once_cell 1.21.3 | - once_cell_polyfill 1.70.2 | - parking_lot 0.12.5
- parking_lot_core 0.9.12 | - paste 1.0.15 | - percent-encoding 2.3.2
- pest 2.8.4 | - pest_derive 2.8.4 | - pest_generator 2.8.4
- pest_meta 2.8.4 | - pin-project-lite 0.2.16 | - pin-utils 0.1.0
- ppv-lite86 0.2.21 | - proc-macro2 1.0.103 | - quote 1.0.42
- rand 0.8.5 | - rand_chacha 0.3.1 | - rand_core 0.6.4
- regex 1.12.2 | - regex-automata 0.4.13 | - regex-syntax 0.8.8
- rustc-hash 2.1.1 | - rustc_version 0.4.1 | - rustversion 1.0.22
- scopeguard 1.2.0 | - semver 1.0.27 | - serde 1.0.228
- serde_core 1.0.228 | - serde_derive 1.0.228 | - serde_json 1.0.145
- serde_path_to_error 0.1.20 | - serde_spanned 1.0.3 | - serde_urlencoded 0.7.1
- serde_yaml 0.9.34+deprecated | - sha2 0.10.9 | - shell-words 1.1.0
- shlex 1.3.0 | - signal-hook 0.3.18 | - signal-hook-mio 0.2.5
- signal-hook-registry 1.4.7 | - siphasher 1.0.1 | - slug 0.1.6
- smallvec 1.15.1 | - socket2 0.6.1 | - static_assertions 1.1.0
- syn 2.0.111 | - sys-locale 0.3.2 | - tempfile 3.23.0
- thiserror 2.0.17 | - thiserror-impl 2.0.17 | - thread_local 1.1.9
- toml 0.9.8 | - toml_datetime 0.7.3 | - toml_parser 1.0.4
- toml_writer 1.0.4 | - type-map 0.5.1 | - typenum 1.19.0
- ucd-trie 0.1.7 | - unic-langid 0.9.6 | - unic-langid-impl 0.9.6
- unicase 2.8.1 | - unicode-segmentation 1.12.0 | - unicode-truncate 1.1.0
- unicode-width 0.1.14 | - unicode-width 0.2.0 | - utf8parse 0.2.2
- version_check 0.9.5 | - wasm-bindgen 0.2.106 | - wasm-bindgen-macro 0.2.106
- wasm-bindgen-macro-support 0.2.106 | - wasm-bindgen-shared 0.2.106 | - winapi 0.3.9
- winapi-i686-pc-windows-gnu 0.4.0 | - winapi-x86_64-pc-windows-gnu 0.4.0 | - windows-core 0.62.2
- windows-implement 0.60.2 | - windows-interface 0.59.3 | - windows-link 0.2.1
- windows-result 0.4.1 | - windows-strings 0.5.1 | - windows-sys 0.52.0
- windows-sys 0.59.0 | - windows-sys 0.60.2 | - windows-sys 0.61.2
- windows-targets 0.52.6 | - windows-targets 0.53.5 | - windows_aarch64_gnullvm 0.52.6
- windows_aarch64_gnullvm 0.53.1 | - windows_aarch64_msvc 0.52.6 | - windows_aarch64_msvc 0.53.1
- windows_i686_gnu 0.52.6 | - windows_i686_gnu 0.53.1 | - windows_i686_gnullvm 0.52.6
- windows_i686_gnullvm 0.53.1 | - windows_i686_msvc 0.52.6 | - windows_i686_msvc 0.53.1
- windows_x86_64_gnu 0.52.6 | - windows_x86_64_gnu 0.53.1 | - windows_x86_64_gnullvm 0.52.6
- windows_x86_64_gnullvm 0.53.1 | - windows_x86_64_msvc 0.52.6 | - windows_x86_64_msvc 0.53.1
- zeroize 1.8.2 |
### MIT OR Unlicense (7)
- aho-corasick 1.1.4
- globset 0.4.18
- ignore 0.4.25
- memchr 2.7.6
- same-file 1.0.6
- walkdir 2.5.0
- winapi-util 0.1.11
### Other Licenses
**(Apache-2.0 OR MIT) AND Unicode-3.0** (1)
- unicode-ident 1.0.22
**Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT** (7)
- linux-raw-sys 0.4.15
- linux-raw-sys 0.11.0
- rustix 0.38.44
- rustix 1.1.2
- wasi 0.11.1+wasi-snapshot-preview1
- wasip2 1.0.1+wasi-0.2.4
- wit-bindgen 0.46.0
**Apache-2.0 OR BSD-2-Clause OR MIT** (2)
- zerocopy 0.8.31
- zerocopy-derive 0.8.31
**Apache-2.0 OR BSL-1.0** (1)
- ryu 1.0.20
**Apache-2.0 OR GPL-2.0** (1)
- self_cell 1.2.1
**Apache-2.0 OR LGPL-2.1-or-later OR MIT** (1)
- r-efi 5.3.0
**BSD-3-Clause** (1)
- deunicode 1.6.2
**BSD-3-Clause AND MIT** (1)
- matchit 0.8.4
**MPL-2.0** (1)
- option-ext 0.2.0
**Unicode-3.0** (3)
- tinystr 0.8.2
- zerofrom 0.1.6
- zerovec 0.11.5
**Zlib** (1)
- foldhash 0.1.5
---
## Summary
- **Project License**: MIT
- **Total Dependencies**: 286
- **Unique License Types**: 15 different combinations
- **Primary License Pattern**: Apache-2.0 OR MIT (most dependencies)
### Compliance
All dependencies are compatible with the MIT license under:
- Permissive licenses (MIT, Apache-2.0, BSD-3-Clause, MPL-2.0, Zlib)
- Weak copyleft (LGPL-2.1-or-later, MPL-2.0)
- Public domain (Unlicense, Unicode-3.0)
### Generated
- Date: 2025-12-17T13:08:58.510231
- Tool: cargo-license
See [DEPENDENCIES.md](DEPENDENCIES.md) for the complete dependency tree.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,156 +0,0 @@
# TypeDialog Dependencies
## Direct Dependencies (by crate)
### typedialog-core
Core library dependencies:
**Serialization & Data**
- serde 1.0 (Apache-2.0 OR MIT)
- serde_json 1.0 (Apache-2.0 OR MIT)
- serde_yaml 0.9 (Apache-2.0 OR MIT)
- toml 0.9 (Apache-2.0 OR MIT)
**Error Handling**
- anyhow 1.0 (Apache-2.0 OR MIT)
- thiserror 2.0 (Apache-2.0 OR MIT)
**Date/Time**
- chrono 0.4 (Apache-2.0 OR MIT)
**Async**
- tokio 1 (MIT)
- async-trait 0.1 (Apache-2.0 OR MIT)
- futures 0.3 (Apache-2.0 OR MIT)
**Templating (optional)**
- tera 1.20 (MIT)
**i18n (optional)**
- fluent 0.17 (Apache-2.0)
- fluent-bundle 0.16 (Apache-2.0)
- unic-langid 0.9 (Apache-2.0 OR MIT)
- sys-locale 0.3 (Apache-2.0 OR MIT)
- dirs 6.0 (Apache-2.0 OR MIT)
**CLI Backend (optional)**
- inquire 0.9 (MIT)
- dialoguer 0.12 (MIT)
- rpassword 7.4 (Apache-2.0)
**TUI Backend (optional)**
- ratatui 0.29 (MIT)
- crossterm 0.29 (MIT)
- atty 0.2 (MIT)
**Web Backend (optional)**
- axum 0.8.7 (MIT)
- tower 0.5.2 (MIT)
- tower-http 0.6.8 (MIT)
- tracing 0.1 (MIT)
- tracing-subscriber 0.3 (MIT)
**Utilities**
- tempfile 3.23 (Apache-2.0 OR MIT)
### typedialog (CLI)
Direct dependencies:
- typedialog-core 0.1.0 (MIT)
- clap 4.5 (Apache-2.0 OR MIT) - CLI argument parsing
- anyhow 1.0 (Apache-2.0 OR MIT)
- serde_json 1.0 (Apache-2.0 OR MIT)
- tokio 1.0 (MIT)
- toml 0.9 (Apache-2.0 OR MIT)
- unic-langid 0.9 (Apache-2.0 OR MIT)
### typedialog-tui (TUI)
Direct dependencies:
- typedialog-core 0.1.0 (MIT)
- clap 4.5 (Apache-2.0 OR MIT)
- anyhow 1.0 (Apache-2.0 OR MIT)
- serde_json 1.0 (Apache-2.0 OR MIT)
- tokio 1.0 (MIT)
- unic-langid 0.9 (Apache-2.0 OR MIT)
### typedialog-web (Web)
Direct dependencies:
- typedialog-core 0.1.0 (MIT)
- clap 4.5 (Apache-2.0 OR MIT)
- anyhow 1.0 (Apache-2.0 OR MIT)
- serde_json 1.0 (Apache-2.0 OR MIT)
- tokio 1.0 (MIT)
- unic-langid 0.9 (Apache-2.0 OR MIT)
---
## Transitive Dependencies
Total: **286 dependencies** across all features.
### License Distribution
| License | Count |
|---------|-------|
| Apache-2.0 OR MIT | 190 |
| MIT | 66 |
| Apache-2.0 | 3 |
| MIT OR Unlicense | 7 |
| Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT | 7 |
| Other | 13 |
---
## License Compatibility
All dependencies are compatible with the **MIT License**:
- ✓ **Permissive licenses** (MIT, Apache-2.0, BSD-3-Clause, Zlib, MPL-2.0)
- ✓ **Weak copyleft** (LGPL-2.1-or-later, MPL-2.0)
- ✓ **Public domain** (Unlicense, Unicode-3.0)
---
## Files
- `LICENSE.md` - Full dependency license attribution
- `SBOM.spdx.json` - Software Bill of Materials (SPDX 2.3 format)
- `SBOM.cyclonedx.json` - Software Bill of Materials (CycloneDX 1.4 format)
- `Cargo.lock` - Locked dependency versions for reproducibility
---
## Security Considerations
### No Unsafe Code
The workspace forbids `unsafe` code:
```toml
[workspace.lints.rust]
unsafe_code = "forbid"
```
### Dependency Auditing
Run security audit:
```bash
cargo audit
```
Review dependency tree:
```bash
cargo tree --depth=2
```
### SBOM Usage
SBOMs can be used with:
- **SPDX format** - CycloneDX tools, GitHub Dependabot, SPDX validators
- **CycloneDX format** - Software composition analysis tools, vulnerability scanners
---
Generated: 2024-12-17

View File

@ -207,12 +207,13 @@ clean-distro:
# === COMPLIANCE ===
# Regenerate SBOMs (LICENSE.md + SBOM.spdx.json + SBOM.cyclonedx.json)
[doc("Regenerate SBOMs and license documentation")]
# Regenerate SBOMs (SBOM.spdx.json + SBOM.cyclonedx.json)
[doc("Regenerate SBOMs (SPDX 2.3 and CycloneDX 1.4)")]
generate-sbom:
@echo "=== Regenerating SBOMs ==="
python3 "{{ WORKSPACE_ROOT }}/scripts/generate_sbom.py"
cd "{{ WORKSPACE_ROOT }}" && \
cargo sbom --output-format spdx_json_2_3 > SBOM.spdx.json && \
cargo sbom --output-format cyclone_dx_json_1_4 > SBOM.cyclonedx.json
@echo "✓ SBOMs regenerated"
@echo " - LICENSE.md (dependency attribution)"
@echo " - SBOM.spdx.json (SPDX 2.3 format)"
@echo " - SBOM.cyclonedx.json (CycloneDX 1.4 format)"

View File

@ -1,282 +0,0 @@
#!/usr/bin/env python3
"""Generate Software Bill of Materials (SBOM) in multiple formats.
Generates:
- LICENSE.md - Detailed dependency attribution
- DEPENDENCIES.md - Organized dependency tree
- SBOM.spdx.json - SPDX 2.3 format
- SBOM.cyclonedx.json - CycloneDX 1.4 format
"""
import json
import subprocess
import sys
from collections import defaultdict
from datetime import datetime
from pathlib import Path
def get_workspace_root():
"""Get workspace root directory."""
script_dir = Path(__file__).parent
return script_dir.parent
def run_cargo_license():
"""Get dependency licenses from cargo-license."""
try:
result = subprocess.run(
["cargo", "license", "--json"], capture_output=True, text=True, check=True
)
return json.loads(result.stdout)
except subprocess.CalledProcessError as e:
print(f"Error running cargo license: {e}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error parsing cargo license output: {e}", file=sys.stderr)
sys.exit(1)
def generate_license_md(licenses, workspace_root):
"""Generate LICENSE.md with dependency attribution."""
by_license = defaultdict(list)
for pkg in licenses:
lic = pkg.get("license", "Unknown")
by_license[lic].append(pkg)
content = """# TypeDialog License
## Project License
TypeDialog is licensed under the **MIT License**.
See [LICENSE](LICENSE) file for the full MIT license text.
---
## Dependencies
This project includes the following dependencies under their respective licenses:
"""
# Apache-2.0 only
if "Apache-2.0" in by_license:
content += f"### Apache-2.0 Only ({len(by_license['Apache-2.0'])})\n"
for pkg in sorted(by_license["Apache-2.0"], key=lambda x: x["name"]):
content += f"- {pkg['name']} {pkg['version']}\n"
content += "\n"
# MIT only
if "MIT" in by_license:
content += f"### MIT Only ({len(by_license['MIT'])})\n"
for pkg in sorted(by_license["MIT"], key=lambda x: x["name"]):
content += f"- {pkg['name']} {pkg['version']}\n"
content += "\n"
# Apache-2.0 OR MIT (dual licensed)
if "Apache-2.0 OR MIT" in by_license:
content += f"### Apache-2.0 OR MIT ({len(by_license['Apache-2.0 OR MIT'])})\n\n"
content += (
"Most dependencies use dual licensing between Apache-2.0 and MIT.\n\n"
)
for i, pkg in enumerate(
sorted(by_license["Apache-2.0 OR MIT"], key=lambda x: x["name"]), 1
):
content += f"- {pkg['name']} {pkg['version']}"
if i % 3 != 0:
content += " | "
else:
content += "\n"
content += "\n\n"
# MIT OR Unlicense
if "MIT OR Unlicense" in by_license:
content += f"### MIT OR Unlicense ({len(by_license['MIT OR Unlicense'])})\n"
for pkg in sorted(by_license["MIT OR Unlicense"], key=lambda x: x["name"]):
content += f"- {pkg['name']} {pkg['version']}\n"
content += "\n"
# Other licenses
other = {
k: v
for k, v in by_license.items()
if k not in ["Apache-2.0", "MIT", "Apache-2.0 OR MIT", "MIT OR Unlicense"]
}
if other:
content += "### Other Licenses\n\n"
for lic, pkgs in sorted(other.items()):
content += f"**{lic}** ({len(pkgs)})\n"
for pkg in sorted(pkgs, key=lambda x: x["name"]):
content += f"- {pkg['name']} {pkg['version']}\n"
content += "\n"
# Summary
unique_licenses = set()
for pkg in licenses:
lic = pkg.get("license", "Unknown")
for part in lic.replace(" OR ", "|").replace(" AND ", "|").split("|"):
unique_licenses.add(part.strip())
content += f"""---
## Summary
- **Project License**: MIT
- **Total Dependencies**: {len(licenses)}
- **Unique License Types**: {len(by_license)} different combinations
- **Primary License Pattern**: Apache-2.0 OR MIT (most dependencies)
### Compliance
All dependencies are compatible with the MIT license under:
- Permissive licenses (MIT, Apache-2.0, BSD-3-Clause, MPL-2.0, Zlib)
- Weak copyleft (LGPL-2.1-or-later, MPL-2.0)
- Public domain (Unlicense, Unicode-3.0)
### Generated
- Date: {datetime.now().isoformat()}
- Tool: cargo-license
See [DEPENDENCIES.md](DEPENDENCIES.md) for the complete dependency tree.
"""
output_file = workspace_root / "LICENSE.md"
output_file.write_text(content)
return output_file
def generate_sbom_spdx(licenses, workspace_root):
"""Generate SPDX 2.3 format SBOM."""
spdx = {
"SPDXID": "SPDXRef-DOCUMENT",
"spdxVersion": "SPDX-2.3",
"creationInfo": {
"created": datetime.now().isoformat() + "Z",
"creators": ["Tool: cargo-license"],
},
"name": "typedialog",
"dataLicense": "CC0-1.0",
"documentNamespace": f"https://github.com/anthropics/typedialog/sbom-{datetime.now().strftime('%Y%m%d')}",
"packages": [],
}
# Add project
spdx["packages"].append(
{
"SPDXID": "SPDXRef-typedialog",
"name": "typedialog",
"version": "0.1.0",
"downloadLocation": "https://github.com/anthropics/typedialog",
"homepage": "https://github.com/anthropics/typedialog",
"licenseDeclared": "MIT",
"licenseConcluded": "MIT",
"filesAnalyzed": False,
}
)
# Add dependencies
for i, pkg in enumerate(licenses, 1):
spdx["packages"].append(
{
"SPDXID": f"SPDXRef-dependency-{i}",
"name": pkg["name"],
"version": pkg["version"],
"downloadLocation": pkg.get("repository", "NOASSERTION"),
"licenseDeclared": pkg.get("license", "NOASSERTION"),
"licenseConcluded": pkg.get("license", "NOASSERTION"),
"filesAnalyzed": False,
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "crates",
"referenceLocator": f"pkg:cargo/{pkg['name']}@{pkg['version']}",
}
]
if pkg["name"]
else [],
}
)
output_file = workspace_root / "SBOM.spdx.json"
with open(output_file, "w") as f:
json.dump(spdx, f, indent=2)
return output_file
def generate_sbom_cyclonedx(licenses, workspace_root):
"""Generate CycloneDX 1.4 format SBOM."""
cyclone = {
"bomFormat": "CycloneDX",
"specVersion": "1.4",
"version": 1,
"metadata": {
"timestamp": datetime.now().isoformat() + "Z",
"tools": [{"vendor": "cargo", "name": "cargo-license", "version": "1.0"}],
"component": {
"type": "application",
"name": "typedialog",
"version": "0.1.0",
"homepage": "https://github.com/anthropics/typedialog",
"repository": {
"type": "git",
"url": "https://github.com/anthropics/typedialog",
},
"licenses": [{"license": {"name": "MIT"}}],
},
},
"components": [],
}
for pkg in licenses:
cyclone["components"].append(
{
"type": "library",
"name": pkg["name"],
"version": pkg["version"],
"purl": f"pkg:cargo/{pkg['name']}@{pkg['version']}",
"homepage": pkg.get("repository", ""),
"licenses": [{"license": {"name": pkg.get("license", "Unknown")}}],
}
)
output_file = workspace_root / "SBOM.cyclonedx.json"
with open(output_file, "w") as f:
json.dump(cyclone, f, indent=2)
return output_file
def main():
"""Generate all SBOM files."""
workspace_root = get_workspace_root()
print("📦 Fetching dependency licenses...", file=sys.stderr)
licenses = run_cargo_license()
print(f" Found {len(licenses)} dependencies", file=sys.stderr)
print("📝 Generating LICENSE.md...", file=sys.stderr)
lic_file = generate_license_md(licenses, workspace_root)
print(f"{lic_file.name}", file=sys.stderr)
print("📄 Generating SBOM.spdx.json...", file=sys.stderr)
spdx_file = generate_sbom_spdx(licenses, workspace_root)
print(f"{spdx_file.name}", file=sys.stderr)
print("📄 Generating SBOM.cyclonedx.json...", file=sys.stderr)
cyclone_file = generate_sbom_cyclonedx(licenses, workspace_root)
print(f"{cyclone_file.name}", file=sys.stderr)
print(f"\n✅ SBOM generation complete", file=sys.stderr)
print(
f" - License combinations: {len(set(p.get('license', 'Unknown') for p in licenses))}",
file=sys.stderr,
)
print(f" - Generated: {datetime.now().isoformat()}", file=sys.stderr)
if __name__ == "__main__":
main()