From 4c49265cce719dba7d72ce0b6a4aea03823887b7 Mon Sep 17 00:00:00 2001 From: Jib Date: Fri, 12 Dec 2025 13:52:58 -0500 Subject: [PATCH 1/3] PYTHON-5433: Create an sbom-requirements.txt file to capture optional dependencies --- .github/workflows/sbom.yml | 14 ++++-- tools/generate_sbom_requirements.py | 74 +++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 tools/generate_sbom_requirements.py diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index c99196dc87..d50e74573a 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -8,9 +8,12 @@ name: Generate SBOM on: workflow_dispatch: {} push: - branches: ['master'] + branches: ["master"] paths: - - 'requirements.txt' + - "requirements.txt" + - "requirements/**.txt" + - "!requirements/docs.txt" + - "!requirements/test.txt" permissions: contents: write @@ -39,7 +42,8 @@ jobs: run: | python -m venv .venv source .venv/bin/activate - pip install -r requirements.txt + python tools/generate_sbom_requirements.py + pip install -r sbom-requirements.txt pip install . pip uninstall -y pip setuptools deactivate @@ -73,10 +77,10 @@ jobs: uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 with: token: ${{ secrets.GITHUB_TOKEN }} - commit-message: 'chore: Update SBOM after dependency changes' + commit-message: "chore: Update SBOM after dependency changes" branch: auto-update-sbom-${{ github.run_id }} delete-branch: true - title: 'chore: Update SBOM' + title: "chore: Update SBOM" body: | ## Automated SBOM Update diff --git a/tools/generate_sbom_requirements.py b/tools/generate_sbom_requirements.py new file mode 100644 index 0000000000..131605ab88 --- /dev/null +++ b/tools/generate_sbom_requirements.py @@ -0,0 +1,74 @@ +""" +Concatenate requirements files into sbom-requirements.txt at repository root. + +- Includes repo_root/requirements.txt if present +- Includes all files matching repo_root/requirements/**/*.txt +- Excludes docs.txt and test.txt in the requirements folder +- Writes output to sbom-requirements.txt (overwrites) +""" +from __future__ import annotations + +from pathlib import Path + +EXCLUDED_NAMES = {"docs.txt", "test.txt"} + + +def collect_files(root: Path) -> list[Path]: + """Collect requirement files to include in SBOM requirements. + + Args: + root (Path): The root directory of the repository. + + Returns: + list[Path]: A list of Paths to requirement files to include in the SBOM. + """ + files = [] + + # requirements.txt + all requirements/**/*.txt + for p in [root / "requirements.txt", *root.glob("requirements/**/*.txt")]: + if p.is_file() and p.name not in EXCLUDED_NAMES: + files.append(p) + + return files + + +def write_combined_req_files(root: Path, files: list[Path], outname: str) -> Path: + """Concatenate requirement files and write to outname file. + + Args: + root (Path): The root directory of the repository. + files (list[Path]): A list of Paths to requirement files to include in the SBOM. + outname (str): The name of the output file. + + Raises: + RuntimeError: If writing to the output file fails. + + Returns: + Path: The path to the output file. + """ + outpath = root / outname + try: + with outpath.open("w", encoding="utf-8") as f: + for p in files: + with p.open("r", encoding="utf-8") as r: + content = r.read().rstrip() + if content: + f.write(content + "\n") + return outpath + except Exception as e: + raise RuntimeError(f"Failed to write {outpath}: {e}") from e + + +def main(): + root = Path(__file__).parent.parent.resolve() + + files = collect_files(root) + if not files: + raise FileNotFoundError(f"No requirement files found from {root}") + + outpath = write_combined_req_files(root, files, "sbom-requirements.txt") + print(f"Wrote concatenated requirements to: {outpath}") + + +if __name__ == "__main__": + main() From bf44daefbb56c27146f483fe1fca3fe5e01af532 Mon Sep 17 00:00:00 2001 From: Jib Date: Fri, 12 Dec 2025 13:56:43 -0500 Subject: [PATCH 2/3] remove sbom-requirements.txt during cleanup phase --- .github/workflows/sbom.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index d50e74573a..ef371ef5cf 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -64,7 +64,7 @@ jobs: - name: Cleanup if: always() - run: rm -rf .venv .venv-sbom + run: rm -rf .venv .venv-sbom sbom-requirements.txt - name: Upload SBOM artifact uses: actions/upload-artifact@v5 From bb958ad4b0e87cb9183e788f74ba7f5c96026251 Mon Sep 17 00:00:00 2001 From: Jib Date: Fri, 12 Dec 2025 14:00:40 -0500 Subject: [PATCH 3/3] updated sbom file --- .github/workflows/sbom.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index ef371ef5cf..6d2b708e04 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -8,12 +8,12 @@ name: Generate SBOM on: workflow_dispatch: {} push: - branches: ["master"] + branches: ['master'] paths: - - "requirements.txt" - - "requirements/**.txt" - - "!requirements/docs.txt" - - "!requirements/test.txt" + - 'requirements.txt' + - 'requirements/**.txt' + - '!requirements/docs.txt' + - '!requirements/test.txt' permissions: contents: write @@ -77,10 +77,10 @@ jobs: uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 with: token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "chore: Update SBOM after dependency changes" + commit-message: 'chore: Update SBOM after dependency changes' branch: auto-update-sbom-${{ github.run_id }} delete-branch: true - title: "chore: Update SBOM" + title: 'Automation: Update SBOM' body: | ## Automated SBOM Update