From 86f5cc79b78a68f3477e10a9b5bd14728fe0f42d Mon Sep 17 00:00:00 2001 From: Prashanth Manthena Date: Tue, 16 Jun 2026 00:37:51 -0700 Subject: [PATCH] Scurity Hardening #36 --- .bandit | 8 ++ .flake8 | 13 +++ .gitattributes | 5 ++ .github/workflows/build.yml | 2 +- .github/workflows/lint.yml | 18 +++- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 4 +- .openapi-generator-ignore | 22 +++++ CONTRIBUTING.md | 87 ++++++++++--------- Makefile | 43 +++++++++ README.md | 78 ++++++++--------- .../graphiant_api_docs_v26.5.0.json | 0 pyproject.toml | 6 ++ scripts/generate.sh | 70 +++++++++++++++ 14 files changed, 270 insertions(+), 88 deletions(-) create mode 100644 .bandit create mode 100644 .flake8 create mode 100644 .gitattributes create mode 100644 Makefile rename graphiant_api_docs_v26.5.0.json => api/graphiant_api_docs_v26.5.0.json (100%) create mode 100755 scripts/generate.sh diff --git a/.bandit b/.bandit new file mode 100644 index 00000000..8410342a --- /dev/null +++ b/.bandit @@ -0,0 +1,8 @@ +[bandit] +# B101: assert is standard in pytest/unittest — all assert findings are in tests/. +# B110: try/except/pass used intentionally in token_parsing.py multi-fallback chain; +# each pass falls through to the next extraction method. +# B404: subprocess import is informational only; usage is for playwright CLI install. +# B603: subprocess.run with fixed args ([sys.executable, "-m", "playwright", ...]) — no user input. +# B105/B106: test fixture token values (gr-auth-xyz, tok-b, wrong-not-used) are not real credentials. +skips = B101,B110,B404,B603,B105,B106 diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..a1ddc919 --- /dev/null +++ b/.flake8 @@ -0,0 +1,13 @@ +[flake8] +# Only check hand-written source; generated models/api are excluded. +exclude = + graphiant_sdk/models, + graphiant_sdk/api/default_api.py, + graphiant_sdk/__init__.py, + build, + dist, + .tox, + .venv, + venv +max-line-length = 127 +max-complexity = 10 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..8a49a7a7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Generated OpenAPI client files — collapsed in GitHub diffs/PRs +graphiant_sdk/models/*.py linguist-generated=true +graphiant_sdk/api/default_api.py linguist-generated=true +graphiant_sdk/__init__.py linguist-generated=true +docs/*.md linguist-generated=true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9bfaf009..6eae5b8c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '3.13' cache: 'pip' - name: Install build dependencies diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e4c3ab96..727f076a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -41,7 +41,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '3.13' cache: 'pip' - name: Install dependencies @@ -52,8 +52,18 @@ jobs: - name: Run flake8 run: | - flake8 graphiant_sdk/ --count --select=E9,F63,F7,F82 --show-source --statistics - flake8 graphiant_sdk/ --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + # Fatal errors on hand-written code only (config in .flake8 excludes generated files) + flake8 --count --select=E9,F63,F7,F82 --show-source --statistics \ + graphiant_cli/ \ + graphiant_sdk/api_client.py graphiant_sdk/configuration.py \ + graphiant_sdk/exceptions.py graphiant_sdk/rest.py graphiant_sdk/api_response.py \ + tests/ + # Style warnings across same set (non-fatal) + flake8 --count --exit-zero --statistics \ + graphiant_cli/ \ + graphiant_sdk/api_client.py graphiant_sdk/configuration.py \ + graphiant_sdk/exceptions.py graphiant_sdk/rest.py graphiant_sdk/api_response.py \ + tests/ mypy: name: MyPy Type Checking @@ -65,7 +75,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '3.13' cache: 'pip' - name: Install dependencies diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1a7590f9..cffb6b8a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,7 +50,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '3.13' cache: 'pip' - name: Install build dependencies diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f2c76063..e7ff4ba4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,6 +11,7 @@ on: - 'requirements.txt' - 'test-requirements.txt' - 'setup.py' + - 'pyproject.toml' push: branches: - main @@ -24,6 +25,7 @@ on: - 'requirements.txt' - 'test-requirements.txt' - 'setup.py' + - 'pyproject.toml' schedule: # Run nightly at 2 AM UTC - cron: '0 2 * * *' @@ -80,7 +82,7 @@ jobs: - name: Upload coverage reports uses: codecov/codecov-action@v4 - if: matrix.python-version == '3.12' + if: matrix.python-version == '3.13' with: file: ./coverage.xml flags: unittests diff --git a/.openapi-generator-ignore b/.openapi-generator-ignore index bcb34393..786d3945 100644 --- a/.openapi-generator-ignore +++ b/.openapi-generator-ignore @@ -3,8 +3,30 @@ LICENSE .travis.yml pyproject.toml requirements.txt +test-requirements.txt setup.py README.md +CHANGELOG.md +CONTRIBUTING.md +SECURITY.md .gitignore +.gitattributes +.flake8 +.bandit .gitlab-ci.yml .github/workflows/* +Makefile +scripts/generate.sh +# Hand-written SDK core files — never overwrite +graphiant_sdk/api_client.py +graphiant_sdk/api_response.py +graphiant_sdk/configuration.py +graphiant_sdk/exceptions.py +graphiant_sdk/rest.py +graphiant_sdk/py.typed +# Entire CLI package is hand-written +graphiant_cli/* +graphiant_cli/**/* +# Hand-written tests +tests/* +tests/**/* diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 169dfad2..e3d4144a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,13 +12,8 @@ Thank you for your interest in contributing! ``` 3. **Set up development environment:** ```bash - # Create virtual environment - python3 -m venv venv - source venv/bin/activate # On Windows: venv\Scripts\activate - - # Install dependencies - pip install --upgrade pip setuptools wheel - pip install -r requirements.txt + python3 -m venv venv && source venv/bin/activate + make install # pip install -e ".[dev]" ``` ## Development Workflow @@ -30,32 +25,18 @@ Thank you for your interest in contributing! 2. **Make your changes** and ensure they pass local checks: ```bash - # Format code (using black or autopep8) - black graphiant_sdk/ # if using black - # or - autopep8 --in-place --recursive graphiant_sdk/ - - # Run linting - flake8 graphiant_sdk/ - mypy graphiant_sdk/ - - # Run static analysis - pylint graphiant_sdk/ # optional - - # Run tests - pytest --cov=graphiant_sdk --cov-report=html + make test # pytest --cov=graphiant_sdk ... + make lint # flake8 on hand-written files (generated excluded via .flake8) + make type-check # mypy (generated models excluded via pyproject.toml) + make build # python -m build (wheel + sdist) + + # Or individually: + pytest -v tests/ + flake8 graphiant_cli/ graphiant_sdk/api_client.py graphiant_sdk/configuration.py ... + mypy graphiant_sdk/ graphiant_cli/ ``` -3. **Verify package builds:** - ```bash - # Build distribution - python setup.py sdist bdist_wheel - - # Verify package - twine check dist/* - ``` - -4. **Commit with clear messages:** +3. **Commit with clear messages:** ```bash git commit -m "Add: description of changes" ``` @@ -70,9 +51,10 @@ The project uses multiple linting tools to ensure code quality: | Tool | Purpose | Target | CI/CD | |------|---------|--------|-------| -| `flake8` | Python style guide (PEP 8) | All `.py` files | Yes (lint stage) | -| `mypy` | Static type checking | All `.py` files | Yes (lint stage) | -| `pylint` | Python code analysis | All `.py` files | Optional (local only) | +| `flake8` | Python style guide (PEP 8) | Hand-written files only (`.flake8` excludes generated models) | Yes (lint stage) | +| `mypy` | Static type checking | Hand-written files (generated models excluded via `pyproject.toml`) | Yes (lint stage) | + +Generated files (`graphiant_sdk/models/`, `default_api.py`, `__init__.py`) are excluded from linting and type-checking. Run `make lint` and `make type-check` locally to verify. **Note:** All linting tools run automatically in CI/CD on every pull request and push to main/develop branches. @@ -99,7 +81,7 @@ pytest tests/test_default_api.py::test_function_name ### Test Structure -- `tests/` directory contains all test files +- `tests/` directory contains all test files (hand-written; listed in `.openapi-generator-ignore`) - Tests use the `pytest` framework - Tests are automatically run in CI/CD across Python 3.10, 3.11, 3.12, and 3.13 @@ -125,6 +107,28 @@ def test_error_handling(): pass ``` +## Code Generation + +Most files in this repo (`graphiant_sdk/models/`, `graphiant_sdk/api/default_api.py`, `graphiant_sdk/__init__.py`, `docs/`) are auto-generated from the OpenAPI spec. **Do not edit them directly** — your changes will be overwritten on the next generation run. + +The hand-written files are protected by `.openapi-generator-ignore`: +- `graphiant_cli/` — entire CLI package +- `graphiant_sdk/api_client.py`, `configuration.py`, `exceptions.py`, `rest.py`, `api_response.py`, `py.typed` +- `tests/` — all tests +- `pyproject.toml`, `setup.py`, `requirements.txt`, `README.md`, `CHANGELOG.md`, tooling files + +To regenerate after a spec update: + +```bash +# Place the new spec in api/ then: +make generate +# or: OPENAPI_SPEC=api/my-new-spec.json bash scripts/generate.sh +``` + +`scripts/generate.sh` auto-detects `openapi-generator` (Homebrew) or `openapi-generator-cli` (npm), reads `packageVersion` from `pyproject.toml`, and passes `--git-user-id`/`--git-repo-id` so generated docs never contain `GIT_USER_ID` placeholders. + +Review `git diff` carefully after generation — pay particular attention to files in `.openapi-generator-ignore` to confirm they were not overwritten. + ## Code Standards ### Python Code @@ -225,17 +229,16 @@ class DeviceManager: ## Pull Request Checklist -- [ ] Code follows PEP 8 style guidelines -- [ ] Code is formatted (black/autopep8) -- [ ] All tests pass locally -- [ ] Linting passes (`flake8`, `mypy`) -- [ ] Type hints are included for all functions -- [ ] Docstrings are included for all classes and functions +- [ ] `make test` passes (all tests green) +- [ ] `make lint` passes (no new flake8 errors in hand-written files) +- [ ] `make type-check` passes (no new mypy errors) +- [ ] `make build` succeeds (wheel and sdist build cleanly) +- [ ] If adding/changing generated code: `make generate` was run and only expected files changed +- [ ] Type hints included for all new functions - [ ] Commit messages are clear - [ ] Commits are signed with GPG (required) - [ ] Branch is rebased (no merge commits allowed) - [ ] All CI/CD checks pass (lint, test, build) -- [ ] Package builds successfully ## Branch Protection Requirements diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..76ee8e52 --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +.PHONY: install test lint type-check build generate clean help + +## install: Install package in editable mode with dev dependencies +install: + pip install -e ".[dev]" + +## test: Run pytest with coverage +test: + pytest --cov=graphiant_sdk --cov-report=term --cov-report=xml + +## lint: Run flake8 on hand-written files only (generated files excluded via .flake8) +lint: + flake8 graphiant_cli/ \ + graphiant_sdk/api_client.py \ + graphiant_sdk/configuration.py \ + graphiant_sdk/exceptions.py \ + graphiant_sdk/rest.py \ + graphiant_sdk/api_response.py \ + tests/ + +## type-check: Run mypy (generated models excluded via pyproject.toml) +type-check: + mypy graphiant_sdk/ graphiant_cli/ + +## build: Build wheel and source distribution +build: + python -m build + +## generate: Regenerate SDK from the OpenAPI spec +generate: + @bash scripts/generate.sh + +## clean: Remove build artifacts +clean: + rm -rf dist/ build/ *.egg-info/ + find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true + find . -name "*.pyc" -delete 2>/dev/null || true + +## help: Show this help +help: + @grep -E '^## ' Makefile | sed 's/## / /' + +.DEFAULT_GOAL := test diff --git a/README.md b/README.md index 1a1baf92..f48e15a5 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ More product and platform context: [Graphiant Docs](https://docs.graphiant.com). | **Automation** | [Graphiant Automation](https://docs.graphiant.com/docs/automation) | | **REST API** | [Graphiant Portal REST API](https://docs.graphiant.com/docs/graphiant-portal-rest-api) | | **Method index (repo)** | [DefaultApi.md](https://github.com/Graphiant-Inc/graphiant-sdk-python/blob/main/docs/DefaultApi.md) | -| **OpenAPI bundle (this build)** | [`graphiant_api_docs_v26.5.0.json`](https://github.com/Graphiant-Inc/graphiant-sdk-python/blob/main/graphiant_api_docs_v26.5.0.json) — source for generated paths and models | +| **OpenAPI bundle (this build)** | [`api/graphiant_api_docs_v26.5.0.json`](https://github.com/Graphiant-Inc/graphiant-sdk-python/blob/main/api/graphiant_api_docs_v26.5.0.json) — source for generated paths and models | | **Model docs (`*.md`)** | [`docs/`](https://github.com/Graphiant-Inc/graphiant-sdk-python/tree/main/docs) (same names as Python classes, e.g. `V1EdgesSummaryGetResponse.md`) | | **PyPI** | [graphiant-sdk](https://pypi.org/project/graphiant-sdk) | | **Changelog** | [CHANGELOG.md](https://github.com/Graphiant-Inc/graphiant-sdk-python/blob/main/CHANGELOG.md) | @@ -423,16 +423,16 @@ def get_device_info(api, bearer_token, device_id): ### Prerequisites -- Python 3.10+ (3.12+ recommended) +- Python 3.10+ (3.13 recommended) - Git -- OpenAPI Generator (for code generation) +- OpenAPI Generator (for code generation) — `brew install openapi-generator` ### CI/CD Workflows This repository uses GitHub Actions for continuous integration and deployment: - **Linting** ([lint.yml](https://github.com/Graphiant-Inc/graphiant-sdk-python/blob/main/.github/workflows/lint.yml)): Runs Flake8 and MyPy type checking on pull requests and pushes -- **Testing** ([test.yml](https://github.com/Graphiant-Inc/graphiant-sdk-python/blob/main/.github/workflows/test.yml)): Runs pytest with coverage across Python 3.10, 3.11, 3.12, and 3.13 +- **Testing** ([test.yml](https://github.com/Graphiant-Inc/graphiant-sdk-python/blob/main/.github/workflows/test.yml)): Runs pytest with coverage across Python 3.10, 3.11, 3.12, and 3.13; coverage uploaded on 3.13 - **Building** ([build.yml](https://github.com/Graphiant-Inc/graphiant-sdk-python/blob/main/.github/workflows/build.yml)): Builds wheel and source distributions - **Releasing** ([release.yml](https://github.com/Graphiant-Inc/graphiant-sdk-python/blob/main/.github/workflows/release.yml)): Publishes to PyPI (manual trigger, admin-only) @@ -445,19 +445,27 @@ See [.github/workflows/README.md](https://github.com/Graphiant-Inc/graphiant-sdk git clone git@github.com:Graphiant-Inc/graphiant-sdk-python.git cd graphiant-sdk-python -# Create virtual environment -python3 -m venv venv -source venv/bin/activate # On Windows: venv\Scripts\activate +# Create virtual environment and install with dev dependencies +python3 -m venv venv && source venv/bin/activate +make install # pip install -e ".[dev]" -# Install dependencies -pip install --upgrade pip setuptools wheel -pip install -r requirements.txt +# Run tests +make test # pytest --cov=graphiant_sdk ... + +# Lint (hand-written files only; generated models excluded via .flake8) +make lint -# Build distribution -python setup.py sdist bdist_wheel +# Type check (generated models excluded via pyproject.toml) +make type-check -# Install locally -pip install dist/*.tar.gz +# Build wheel and source distribution +make build # python -m build +``` + +Or without `make`: + +```bash +pip install -e ".[dev]" && pytest --cov=graphiant_sdk ``` ### Code Generation @@ -465,37 +473,32 @@ pip install dist/*.tar.gz To regenerate the SDK from the latest API specification: ```bash -# Install OpenAPI Generator -brew install openapi-generator # macOS -# or download from: https://github.com/OpenAPITools/openapi-generator - -# Generate SDK -openapi-generator generate \ - -i graphiant_api_docs_v26.5.0.json \ - -g python \ - --git-user-id Graphiant-Inc \ - --git-repo-id graphiant-sdk-python \ - --package-name graphiant_sdk \ - --additional-properties=packageVersion=26.5.0 +# Quickest path — uses scripts/generate.sh with api/openapi.yaml +make generate + +# Or run the script directly (supports OPENAPI_SPEC override) +OPENAPI_SPEC=api/graphiant_api_docs_v26.5.0.json bash scripts/generate.sh ``` -> **Note:** Download the latest API bundle from the Graphiant portal under **Support Hub** → **Developer Tools**. Set **`packageVersion`** to the SDK release you are publishing (this branch: **26.5.0**). The **`-i`** filename reflects the API doc bundle version (here `graphiant_api_docs_v26.5.0.json`) and may stay the same across patch releases when the spec is unchanged. +`scripts/generate.sh` wraps the full `openapi-generator-cli generate` invocation. It auto-detects `openapi-generator` (Homebrew) or `openapi-generator-cli` (npm), reads the current version from `pyproject.toml`, and passes `--git-user-id`/`--git-repo-id` so generated docs never contain `GIT_USER_ID` placeholders. + +> **Note:** Download the latest API bundle from the Graphiant portal under **Support Hub** → **Developer Tools** and place it in `api/`. The versioned JSON bundle (`api/graphiant_api_docs_v26.5.0.json`) is the snapshot used for this release; `api/openapi.yaml` is the primary YAML spec. Hand-written files listed in `.openapi-generator-ignore` (`graphiant_cli/`, `graphiant_sdk/api_client.py`, `configuration.py`, etc.) are never overwritten by the generator. ### Testing ```bash -# Run tests -python -m pytest tests/ +make test # pytest --cov=graphiant_sdk --cov-report=term --cov-report=xml -# Run with coverage -python -m pytest tests/ --cov=graphiant_sdk --cov-report=html +# Or directly: +pytest -v tests/ +pytest tests/ --cov=graphiant_sdk --cov-report=html ``` ## 📖 API Reference ### Source of truth (this release) -Operations and schemas are generated from **`graphiant_api_docs_v26.5.0.json`** (repo root and PyPI wheel). For a newer portal/API, download the current bundle (Support Hub → Developer Tools) and diff paths before relying on URLs here. +Operations and schemas are generated from **`api/graphiant_api_docs_v26.5.0.json`** (in `api/` and bundled in the PyPI wheel). For a newer portal/API, download the current bundle (Support Hub → Developer Tools) and diff paths before relying on URLs here. | How to explore | Where | |----------------|-------| @@ -574,14 +577,11 @@ We welcome contributions! Please follow these steps: 1. Fork the repository 2. Create a feature branch (`git checkout -b feature/amazing-feature`) -3. Make your changes and ensure they pass local tests: +3. Make your changes and ensure they pass local checks: ```bash - # Run linting - flake8 graphiant_sdk/ - mypy graphiant_sdk/ - - # Run tests - pytest --cov=graphiant_sdk + make test # run tests with coverage + make lint # flake8 on hand-written files (generated excluded via .flake8) + make type-check # mypy (generated models excluded via pyproject.toml) ``` 4. Commit your changes with a clear message (`git commit -m 'Add amazing feature'`) 5. Push to the branch (`git push origin feature/amazing-feature`) diff --git a/graphiant_api_docs_v26.5.0.json b/api/graphiant_api_docs_v26.5.0.json similarity index 100% rename from graphiant_api_docs_v26.5.0.json rename to api/graphiant_api_docs_v26.5.0.json diff --git a/pyproject.toml b/pyproject.toml index d9b16a96..c13565a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,12 @@ files = [ #"test", # auto-generated tests "tests", # hand-written tests ] +# Skip generated models and API file — too large and not hand-maintained +exclude = [ + "graphiant_sdk/models/", + "graphiant_sdk/api/default_api\\.py", + "graphiant_sdk/__init__\\.py", +] # TODO: enable "strict" once all these individual checks are passing # strict = true diff --git a/scripts/generate.sh b/scripts/generate.sh new file mode 100755 index 00000000..93324c70 --- /dev/null +++ b/scripts/generate.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# Regenerate the graphiant-sdk-python from the OpenAPI specification. +# +# Prerequisites: +# - Java 11+ on PATH +# - openapi-generator-cli (install via: brew install openapi-generator +# or: npm install @openapitools/openapi-generator-cli -g) +# +# Usage: +# bash scripts/generate.sh # uses api/openapi.yaml +# OPENAPI_SPEC=api/graphiant_api_docs_v26.5.0.json bash scripts/generate.sh + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" + +# Resolve default spec: prefer api/openapi.yaml (canonical), fall back to the +# versioned JSON bundle (api/graphiant_api_docs_*.json) if yaml is absent. +if [ -z "${OPENAPI_SPEC:-}" ]; then + if [ -f "${REPO_ROOT}/api/openapi.yaml" ]; then + OPENAPI_SPEC="${REPO_ROOT}/api/openapi.yaml" + else + OPENAPI_SPEC="$(ls "${REPO_ROOT}"/api/graphiant_api_docs_*.json 2>/dev/null | sort | tail -1)" + fi +fi + +PACKAGE_NAME="${PACKAGE_NAME:-graphiant_sdk}" + +# Read SDK version from pyproject.toml so generated docs/UserAgent stay correct. +SDK_VERSION=$(grep -E '^version\s*=' "${REPO_ROOT}/pyproject.toml" | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') + +# Resolve generator: honour explicit $GENERATOR, then try common install locations. +if [ -n "${GENERATOR:-}" ]; then + : # use what was passed +elif command -v openapi-generator &>/dev/null; then + GENERATOR="openapi-generator" # Homebrew: brew install openapi-generator +elif command -v openapi-generator-cli &>/dev/null; then + GENERATOR="openapi-generator-cli" # npm: npm i -g @openapitools/openapi-generator-cli +else + echo "❌ openapi-generator not found. Install via one of:" + echo " brew install openapi-generator # macOS Homebrew" + echo " npm install -g @openapitools/openapi-generator-cli" + echo " https://openapi-generator.tech/docs/installation" + exit 1 +fi + +if [ ! -f "${OPENAPI_SPEC}" ]; then + echo "❌ OpenAPI spec not found: ${OPENAPI_SPEC}" + exit 1 +fi + +echo "🔄 Generating SDK v${SDK_VERSION} from ${OPENAPI_SPEC}..." + +"${GENERATOR}" generate \ + --input-spec "${OPENAPI_SPEC}" \ + --generator-name python \ + --output "${REPO_ROOT}" \ + --git-user-id Graphiant-Inc \ + --git-repo-id graphiant-sdk-python \ + --additional-properties="packageName=${PACKAGE_NAME},projectName=graphiant-sdk,packageVersion=${SDK_VERSION},generateSourceCodeOnly=false" + +echo "🔧 Reinstalling package..." +cd "${REPO_ROOT}" +pip install -e ".[dev]" --quiet 2>/dev/null || pip install -e . --quiet + +echo "✅ SDK generation complete." +echo " Files listed in .openapi-generator-ignore are NOT overwritten, including:" +echo " - graphiant_cli/ (hand-written CLI)" +echo " - graphiant_sdk/api_client.py, configuration.py, exceptions.py, rest.py, api_response.py" +echo " Review the diff before committing."