From c85ec32eec0b96f2ea3c18265cf024b638df2698 Mon Sep 17 00:00:00 2001 From: Antonio Aranda <102337110+arandito@users.noreply.github.com> Date: Tue, 11 Aug 2026 10:51:50 -0400 Subject: [PATCH] Add testing infra and CI for runtime packages --- .github/workflows/check-license.yml | 39 +++++++++++++++ .github/workflows/ci.yml | 67 ++++++++++++++++++++++++++ .gitignore | 2 + .licenserc.yaml | 18 +++++++ Makefile | 21 +++++++- pyproject.toml | 59 +++++++++++++++++++++++ scripts/changelog/render.py | 2 +- scripts/changelog/utils.py | 29 ++++++----- scripts/docs/generate_all_doc_stubs.py | 3 ++ scripts/docs/generate_nav.py | 3 ++ scripts/docs/utils.py | 3 ++ 11 files changed, 233 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/check-license.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .licenserc.yaml create mode 100644 pyproject.toml diff --git a/.github/workflows/check-license.yml b/.github/workflows/check-license.yml new file mode 100644 index 0000000..a0d83af --- /dev/null +++ b/.github/workflows/check-license.yml @@ -0,0 +1,39 @@ +name: License Header Check + +on: + pull_request: + paths: + - "**/*.py" + - "!clients/**" + - ".github/workflows/check-license.yml" + - ".licenserc.yaml" + push: + branches: + - develop + paths: + - "**/*.py" + - "!clients/**" + - ".github/workflows/check-license.yml" + - ".licenserc.yaml" + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + check-license-header: + name: Check License Header + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + + - name: Check license headers + uses: apache/skywalking-eyes/header@eddd8f193e5c1739a76dad4074f50ede635a19fe + with: + mode: check diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3ff6b9f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,67 @@ +name: CI + +on: + pull_request: + paths: + - ".github/workflows/ci.yml" + - "Makefile" + - "packages/**" + - "pyproject.toml" + - "uv.lock" + push: + branches: + - develop + - main + paths: + - ".github/workflows/ci.yml" + - "Makefile" + - "packages/**" + - "pyproject.toml" + - "uv.lock" + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + ci: + name: Python ${{ matrix.python-version }} + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.python-version == '3.15' }} + strategy: + fail-fast: false + matrix: + python-version: ["3.12", "3.13", "3.14", "3.14t", "3.15"] + + steps: + - name: Checkout Repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + + - name: Install uv and set the Python version + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 + with: + python-version: ${{ matrix.python-version }} + activate-environment: true + enable-cache: true + + - name: Setup workspace + run: | + make install + + - name: Check python packages + if: ${{ matrix.python-version == '3.12' }} + run: | + make check-py + + - name: Test python packages + run: | + make test-py + + - name: Build python packages + run: | + make build-py diff --git a/.gitignore b/.gitignore index f4e85e7..89c18ce 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ venv # Caches __pycache__/ +.coverage +htmlcov/ .ruff_cache/ .pytest_cache/ diff --git a/.licenserc.yaml b/.licenserc.yaml new file mode 100644 index 0000000..e952c06 --- /dev/null +++ b/.licenserc.yaml @@ -0,0 +1,18 @@ +header: + license: + spdx-id: Apache-2.0 + copyright-owner: Amazon.com, Inc. or its affiliates. + content: | + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + SPDX-License-Identifier: Apache-2.0 + + paths: + - "**/*.py" + + paths-ignore: + - "clients/**" + + language: + Python: + extensions: + - ".py" diff --git a/Makefile b/Makefile index 68347b2..d4e6ac7 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,26 @@ DOCS_PORT ?= 8000 PYTHON_VERSION := 3.12 -.PHONY: docs docs-serve docs-clean docs-install docs-lock venv +.PHONY: build-py check-py docs docs-serve docs-clean docs-install docs-lock \ + install lint-py test-py venv + +install: + uv sync --all-packages --all-extras + +lint-py: + uv run ruff check packages --fix --config pyproject.toml + uv run ruff format packages --config pyproject.toml + +check-py: + uv run ruff check packages --config pyproject.toml + uv run ruff format --check packages --config pyproject.toml + uv run pyright packages + +test-py: + uv run pytest packages + +build-py: + uv build --all-packages venv: uv venv --python $(PYTHON_VERSION) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4890e02 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,59 @@ +[project] +name = "aws-sdk-python" +version = "0.1.0" +description = "AWS SDK for Python" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [] + +[dependency-groups] +test = [ + "pytest>=9.0.3", + "pytest-asyncio>=0.25.3", + "pytest-cov>=6.0.0", + "freezegun>=1.5.1", +] +lint = [ + "ruff>=0.9.7", +] +typing = [ + "pyright>=1.1.400", +] +docs = [ + "mkdocstrings[python]~=1.0.4", + "zensical~=0.0.53", +] + +[tool.uv] +required-version = ">=0.7.2" +package = false +default-groups = ["test", "lint", "typing", "docs"] + +[tool.uv.workspace] +members = ["packages/*"] + +[tool.pyright] +typeCheckingMode = "strict" +enableExperimentalFeatures = true +# TODO: Remove once generated clients ship a py.typed marker. +reportMissingTypeStubs = false + +[tool.pytest.ini_options] +asyncio_mode = "auto" # makes pytest run async tests without having to be marked with the @pytest.mark.asyncio decorator +addopts = [ "--import-mode=importlib", "--cov", "--cov-report=term-missing" ] +consider_namespace_packages = true + +[tool.ruff] +target-version = "py312" + +[tool.ruff.lint] +select = [ "ASYNC", "C4", "E1", "E4", "E7", "E9", "F", "FURB", "G", "I", "LOG", "PIE", "RUF", "S", "T", "UP" ] + +[tool.ruff.lint.isort] +classes = ["URI"] + +[tool.ruff.lint.per-file-ignores] +"**/{tests}/*" = ["S"] + +[tool.ruff.format] +docstring-code-format = true diff --git a/scripts/changelog/render.py b/scripts/changelog/render.py index bef27fb..5559e94 100755 --- a/scripts/changelog/render.py +++ b/scripts/changelog/render.py @@ -137,7 +137,7 @@ def main() -> int: "-p", "--package", required=True, - help="Package name (looks in clients//.changes)", + help="Package name (looks in clients//.changes or packages//.changes)", ) parser.add_argument( "-t", diff --git a/scripts/changelog/utils.py b/scripts/changelog/utils.py index 72638e7..9de2453 100644 --- a/scripts/changelog/utils.py +++ b/scripts/changelog/utils.py @@ -10,19 +10,26 @@ PROJECT_ROOT_DIR = Path(__file__).resolve().parent.parent.parent +# Directories holding releasable packages: generated service clients and +# hand-written runtime packages. +PACKAGE_ROOT_DIRS = ("clients", "packages") -def validate_package_name(package_name: str) -> None: - """Validate that the package exists in the clients directory.""" - package_path = PROJECT_ROOT_DIR / "clients" / package_name - if not package_path.exists(): - print( - f"Error: Package '{package_name}' not found in clients directory", - file=sys.stderr, - ) - sys.exit(1) + +def find_package_dir(package_name: str) -> Path: + """Find a package directory, searching each of the package roots in order.""" + for root in PACKAGE_ROOT_DIRS: + package_path = PROJECT_ROOT_DIR / root / package_name + if package_path.is_dir(): + return package_path + + searched = ", ".join(f"{root}/" for root in PACKAGE_ROOT_DIRS) + print( + f"Error: Package '{package_name}' not found in {searched}", + file=sys.stderr, + ) + sys.exit(1) def get_package_changes_dir(package_name: str) -> Path: """Get the .changes directory for a package.""" - validate_package_name(package_name) - return PROJECT_ROOT_DIR / "clients" / package_name / ".changes" + return find_package_dir(package_name) / ".changes" diff --git a/scripts/docs/generate_all_doc_stubs.py b/scripts/docs/generate_all_doc_stubs.py index 442c8c4..d5c44cb 100644 --- a/scripts/docs/generate_all_doc_stubs.py +++ b/scripts/docs/generate_all_doc_stubs.py @@ -1,3 +1,6 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + """ Generate documentation stubs for all AWS SDK Python clients. diff --git a/scripts/docs/generate_nav.py b/scripts/docs/generate_nav.py index 80179c5..38ead0e 100644 --- a/scripts/docs/generate_nav.py +++ b/scripts/docs/generate_nav.py @@ -1,3 +1,6 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + # scripts/docs/generate_nav.py """ Generate client documentation navigation dynamically. diff --git a/scripts/docs/utils.py b/scripts/docs/utils.py index 76d3710..a2c2dce 100644 --- a/scripts/docs/utils.py +++ b/scripts/docs/utils.py @@ -1,3 +1,6 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + """Utilities for reading service metadata from Smithy models.""" import json