Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/actions/python-package/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: 'Python package'
description: 'Builds and published a Python package to pypi'
inputs:
pypiApiToken:
required: true
description: API token for publish pypi package
version:
required: true
description: Package version

runs:
using: 'composite'
steps:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
cache: 'pip'
cache-dependency-path: pyproject.toml

- name: Build the package
run: |
echo "Build package with detected version: ${PACKAGE_VERSION}"
pip install -e .[build]
python -m build
env:
PACKAGE_VERSION: ${{ inputs.version }}
shell: bash

- name: Publish to PyPI
if: startsWith(github.ref, 'refs/tags/')
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ inputs.pypiApiToken }}
run: |
twine upload dist/*
shell: bash
35 changes: 14 additions & 21 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,42 +1,35 @@
name: Publish Python Package

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

on:
release:
types: [published]

permissions:
contents: read
packages: write

jobs:
build-and-publish:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
cache: 'pip'

- name: Install build tools
run: |
pip install -r requirements.txt
fetch-depth: 0

- name: Extract version from tag
id: version
run: |
TAG=${GITHUB_REF##*/}
echo "PACKAGE_VERSION=${TAG#v}" >> $GITHUB_ENV
shell: bash

- name: Build the package
run: |
python setup.py sdist bdist_wheel
env:
PACKAGE_VERSION: ${{ env.PACKAGE_VERSION }}

- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
twine upload dist/*
- uses: ./.github/actions/python-package
with:
pypiApiToken: ${{ secrets.PYPI_API_TOKEN }}
version: ${{ env.PACKAGE_VERSION }}
65 changes: 65 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Test and build

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:

permissions:
contents: read
issues: read
checks: write
pull-requests: write

jobs:
test-and-build:
name: "Test"
runs-on: ubuntu-latest
permissions: write-all
steps:
- uses: actions/checkout@v4

- name: Run docker-compose
uses: hoverkraft-tech/compose-action@v2.0.1
with:
compose-file: "./test/docker-compose.yml"

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
cache: "pip"
cache-dependency-path: pyproject.toml

- run: pip install -e .[test]

- name: Run tests
env:
PYTHONPATH: "."
run: |
pytest --junitxml=./test-results/tests.xml --cov=./owasp_dt ./test

- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: |
test-results/**/*.xml

- name: Generate coverage report
run: coverage xml

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
continue-on-error: true
with:
#fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml
verbose: true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
build
owasp_dependency_track_client.egg-info
openapi.json
test-results
.coverage
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,28 @@ projects = get_projects.sync(client=client)
assert len(projects) > 0
```

## CLI
## More OWASP Dependency Track utils

If you're looking for a CLI tool: [owasp-dependency-track-cli](https://github.com/mreiche/owasp-dependency-track-cli)
This library is part of a wider OWASP Dependency Track tool chain:
- OWASP Dependency Track Python API client: https://github.com/mreiche/owasp-dependency-track-python-client
- OWASP Dependency Track CLI: https://github.com/mreiche/owasp-dependency-track-cli
- OWASP Dependency Track Azure DevOps Sync: https://github.com/mreiche/owasp-dependency-track-azure-devops

## Update the library
## Development

### Update the library

1. Install the requirements: `pip install -r requirements.txt`
2. Start a OWASP DT instance locally (like via. Docker-Compose): https://docs.dependencytrack.org/getting-started/deploy-docker/
3. Run `regenerate-api-client.sh`
4. Check if bug https://github.com/openapi-generators/openapi-python-client/issues/1256 is still in effect
5. Publish this library with the API version tag

### Start the test environment
```shell
cd test
podman|docker compose up
```

- Preconfigured user: `admin:admin2`
- Preconfigured API key: see `test/test.env`
4 changes: 4 additions & 0 deletions owasp_dt/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Contains all the data models used in inputs/outputs"""

from .about import About
from .about_provider_data import AboutProviderData
from .about_provider_data_additional_property import AboutProviderDataAdditionalProperty
from .acl_mapping_request import AclMappingRequest
from .affected_component import AffectedComponent
from .affected_component_identity_type import AffectedComponentIdentityType
Expand Down Expand Up @@ -199,6 +201,8 @@

__all__ = (
"About",
"AboutProviderData",
"AboutProviderDataAdditionalProperty",
"AclMappingRequest",
"AffectedComponent",
"AffectedComponentIdentityType",
Expand Down
18 changes: 18 additions & 0 deletions owasp_dt/models/about.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from ..types import UNSET, Unset

if TYPE_CHECKING:
from ..models.about_provider_data import AboutProviderData
from ..models.framework import Framework


Expand All @@ -28,6 +29,7 @@ class About:
uuid (Union[Unset, str]):
application (Union[Unset, str]):
framework (Union[Unset, Framework]):
provider_data (Union[Unset, AboutProviderData]):
"""

version: Union[Unset, str] = UNSET
Expand All @@ -36,6 +38,7 @@ class About:
uuid: Union[Unset, str] = UNSET
application: Union[Unset, str] = UNSET
framework: Union[Unset, "Framework"] = UNSET
provider_data: Union[Unset, "AboutProviderData"] = UNSET
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)

def to_dict(self) -> dict[str, Any]:
Expand All @@ -53,6 +56,10 @@ def to_dict(self) -> dict[str, Any]:
if not isinstance(self.framework, Unset):
framework = self.framework.to_dict()

provider_data: Union[Unset, dict[str, Any]] = UNSET
if not isinstance(self.provider_data, Unset):
provider_data = self.provider_data.to_dict()

field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update({})
Expand All @@ -68,11 +75,14 @@ def to_dict(self) -> dict[str, Any]:
field_dict["application"] = application
if framework is not UNSET:
field_dict["framework"] = framework
if provider_data is not UNSET:
field_dict["providerData"] = provider_data

return field_dict

@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
from ..models.about_provider_data import AboutProviderData
from ..models.framework import Framework

d = dict(src_dict)
Expand All @@ -93,13 +103,21 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
else:
framework = Framework.from_dict(_framework)

_provider_data = d.pop("providerData", UNSET)
provider_data: Union[Unset, AboutProviderData]
if isinstance(_provider_data, Unset):
provider_data = UNSET
else:
provider_data = AboutProviderData.from_dict(_provider_data)

about = cls(
version=version,
timestamp=timestamp,
system_uuid=system_uuid,
uuid=uuid,
application=application,
framework=framework,
provider_data=provider_data,
)

about.additional_properties = d
Expand Down
71 changes: 71 additions & 0 deletions owasp_dt/models/about_provider_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from collections.abc import Mapping
from typing import (
TYPE_CHECKING,
Any,
TypeVar,
)

from attrs import define as _attrs_define
from attrs import field as _attrs_field

if TYPE_CHECKING:
from ..models.about_provider_data_additional_property import (
AboutProviderDataAdditionalProperty,
)


T = TypeVar("T", bound="AboutProviderData")


@_attrs_define
class AboutProviderData:
""" """

additional_properties: dict[str, "AboutProviderDataAdditionalProperty"] = (
_attrs_field(init=False, factory=dict)
)

def to_dict(self) -> dict[str, Any]:
field_dict: dict[str, Any] = {}
for prop_name, prop in self.additional_properties.items():
field_dict[prop_name] = prop.to_dict()

return field_dict

@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
from ..models.about_provider_data_additional_property import (
AboutProviderDataAdditionalProperty,
)

d = dict(src_dict)
about_provider_data = cls()

additional_properties = {}
for prop_name, prop_dict in d.items():
additional_property = AboutProviderDataAdditionalProperty.from_dict(
prop_dict
)

additional_properties[prop_name] = additional_property

about_provider_data.additional_properties = additional_properties
return about_provider_data

@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())

def __getitem__(self, key: str) -> "AboutProviderDataAdditionalProperty":
return self.additional_properties[key]

def __setitem__(
self, key: str, value: "AboutProviderDataAdditionalProperty"
) -> None:
self.additional_properties[key] = value

def __delitem__(self, key: str) -> None:
del self.additional_properties[key]

def __contains__(self, key: str) -> bool:
return key in self.additional_properties
Loading