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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 4 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Code owners of the Didit Python SDK.
# The Backend team owns the server SDKs. GitHub requests its review only once the team
# has write access to this repository (Settings > Collaborators and teams).
* @didit-protocol/backend
67 changes: 67 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Bug report
description: Something in the Didit Python SDK does not work as documented.
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
**Never paste API keys, webhook secrets, access or refresh tokens, or personal data**
(names, documents, images, addresses, phone numbers, emails) into this issue.
Replace them with placeholders such as `YOUR_API_KEY`.

To report a security vulnerability, do not open an issue: see [SECURITY.md](https://github.com/didit-protocol/sdk-python/blob/main/SECURITY.md).
- type: input
id: sdk-version
attributes:
label: SDK version
description: "Output of `python -c \"import didit; print(didit.__version__)\"`."
placeholder: "0.1.0"
validations:
required: true
- type: input
id: python-version
attributes:
label: Python version
placeholder: "3.12.7"
validations:
required: true
- type: input
id: os
attributes:
label: Operating system
placeholder: "Ubuntu 24.04, macOS 15, Windows 11"
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Minimal reproduction
description: The smallest code that shows the problem, with secrets and personal data removed.
render: python
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behaviour
validations:
required: true
- type: textarea
id: actual
attributes:
label: Actual behaviour
description: What happened instead, including the full traceback.
validations:
required: true
- type: input
id: request-id
attributes:
label: Request id
description: The `request_id` of the failing API call, if any (from `APIStatusError.request_id` or the response headers).
- type: checkboxes
id: no-secrets
attributes:
label: Confirmation
options:
- label: I removed every API key, secret, token and piece of personal data from this issue.
required: true
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Didit documentation
url: https://docs.didit.me
about: API reference, guides and quickstarts.
- name: Report a security vulnerability
url: https://github.com/didit-protocol/sdk-python/security/advisories/new
about: Report vulnerabilities privately, never in a public issue. See SECURITY.md.
34 changes: 34 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Feature request
description: Suggest an improvement to the Didit Python SDK.
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
The SDK is generated from the public Didit API, so a missing API capability is a
request for the API itself; describe it here anyway and we will route it.
Do not paste API keys, secrets or personal data.
- type: textarea
id: problem
attributes:
label: Problem
description: What are you trying to do, and what makes it hard today?
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
description: The API you would like, ideally as a short code example.
render: python
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
- type: textarea
id: context
attributes:
label: Additional context
description: Links, related issues, or other Didit SDKs (Node, Go, Java, PHP, Ruby, .NET) where this matters too.
26 changes: 26 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Weekly dependency updates, one grouped pull request per ecosystem.
# Commit messages follow conventional commits: "ci: ..." for actions,
# "chore(deps): ..." and "chore(deps-dev): ..." for Python packages.
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
groups:
github-actions:
patterns: ["*"]
commit-message:
prefix: ci

- package-ecosystem: uv
directory: /
schedule:
interval: weekly
groups:
python-dependencies:
patterns: ["*"]
commit-message:
prefix: chore
prefix-development: chore
include: scope
31 changes: 31 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Summary

<!-- What changes and why. Link the issue or the backend change, if any. -->

## Type of change

<!-- The conventional commit type of the pull request title. It decides the next version. -->

- [ ] `fix`: a bug fix (patch)
- [ ] `feat`: a new capability (minor)
- [ ] `feat!` or `BREAKING CHANGE:`: a breaking change to the public SDK surface (major; minor while 0.x)
- [ ] `docs`, `test`, `refactor`, `perf`, `build`, `ci` or `chore`: no release on its own

## Tests run

<!-- The commands you ran, for example: -->

- [ ] `uv run ruff check .` and `uv run ruff format --check .`
- [ ] `uv run mypy` and `uv run pyright`
- [ ] `uv run python codegen/generate.py --check`
- [ ] `uv run pytest`
- [ ] `uv run pytest -m live` (sandbox, only if relevant)

## Checklist

- [ ] Generated code (`src/didit/types`, `src/didit/resources`, `tests/generated`) was regenerated with `uv run python codegen/generate.py`, not edited by hand.
- [ ] The upstream copies in `openapi/` (`verification.json`, `account.json`, `webhook-vectors.json`) were not edited by hand.
- [ ] Public API changes have tests and a README update.
- [ ] No secrets, API keys or real customer data (fixtures are synthetic).

Parity: same change in the other six SDK repositories, or not applicable because ...
220 changes: 220 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
# Continuous integration for the Didit Python SDK (docs/DESIGN.md section 11).
# Every third-party action is pinned by full commit SHA; Dependabot keeps the pins current.
name: CI

on:
push:
branches: [main]
pull_request:
# Manual runs also start the live smoke tests (job live-smoke) when the
# DIDIT_SANDBOX_API_KEY secret is set.
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
# Cancel superseded runs of a pull request; every push to main gets its own run.
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
FORCE_COLOR: "1"
# Use uv.lock exactly as committed. The lint job checks that it matches pyproject.toml.
UV_FROZEN: "1"

defaults:
run:
shell: bash

jobs:
lint:
name: Lint (ruff)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up uv
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
- name: Check that uv.lock matches pyproject.toml
run: env -u UV_FROZEN uv lock --check
- name: Install dependencies
run: uv sync --frozen
- name: ruff check
run: uv run ruff check --output-format=github .
- name: ruff format --check
run: uv run ruff format --check .

typecheck:
name: Type check (mypy, pyright)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up uv
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
- name: Install dependencies
run: uv sync --frozen
- name: mypy --strict
run: uv run mypy
- name: pyright
run: uv run pyright

codegen:
name: Generated code is up to date
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up uv
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
- name: Install dependencies
run: uv sync --frozen
- name: Regenerate and compare with the committed code
# Fails when src/didit/types, src/didit/resources or tests/generated differ from
# what codegen/ produces from openapi/. Fix: uv run python codegen/generate.py
run: uv run python codegen/generate.py --check

test:
name: Test (Python ${{ matrix.python-version }}, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
include:
# Adds the coverage gate to the existing Linux 3.12 job.
- os: ubuntu-latest
python-version: "3.12"
coverage: true
- os: macos-latest
python-version: "3.12"
- os: windows-latest
python-version: "3.12"
steps:
- name: Check out the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up uv and Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync --frozen
- name: pytest
if: ${{ !matrix.coverage }}
run: uv run pytest -m "not live"
- name: pytest with the core coverage gate (90 %)
if: ${{ matrix.coverage }}
run: uv run pytest -m "not live" --cov --cov-report=term-missing --cov-fail-under=90

build:
name: Build and install the wheel
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up uv
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
- name: Build the sdist and the wheel
run: uv build
- name: Install the wheel into a fresh virtual environment
run: |
uv venv --python 3.12 "$RUNNER_TEMP/wheel-venv"
uv pip install --python "$RUNNER_TEMP/wheel-venv" dist/*.whl
- name: Import didit without the optional cryptography dependency
# Run outside the checkout so that the import resolves to the installed wheel.
working-directory: ${{ runner.temp }}
run: |
py="$RUNNER_TEMP/wheel-venv/bin/python"
if "$py" -c "import cryptography" 2>/dev/null; then
echo "::error::cryptography is installed without the auth extra; it must stay optional (didit-sdk[auth])."
exit 1
fi
"$py" -c "import didit; print(didit.__version__)"

secret-scan:
name: Secret scan (gitleaks, full history)
runs-on: ubuntu-latest
timeout-minutes: 10
env:
# The official gitleaks-action needs a paid licence for organisations, so the
# release binary is downloaded and verified against its published SHA-256.
GITLEAKS_VERSION: "8.30.1"
GITLEAKS_SHA256: "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb"
steps:
- name: Check out the repository with its full history
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Install gitleaks ${{ env.GITLEAKS_VERSION }}
run: |
archive="$RUNNER_TEMP/gitleaks.tar.gz"
curl -sSfL --retry 3 -o "$archive" \
"https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
echo "${GITLEAKS_SHA256} ${archive}" | sha256sum --check --strict
tar -xzf "$archive" -C "$RUNNER_TEMP" gitleaks
"$RUNNER_TEMP/gitleaks" version
- name: Scan every commit
# `gitleaks git` is the current name of `gitleaks detect` (deprecated in 8.19).
run: '"$RUNNER_TEMP/gitleaks" git --config .gitleaks.toml --redact --verbose --no-banner .'

live-smoke:
name: Live smoke tests (sandbox)
# Never part of default CI (docs/DESIGN.md section 11): manual runs only, and only
# when the DIDIT_SANDBOX_API_KEY repository secret is set.
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check for the sandbox API key
id: key
env:
DIDIT_SANDBOX_API_KEY: ${{ secrets.DIDIT_SANDBOX_API_KEY }}
run: |
if [ -n "$DIDIT_SANDBOX_API_KEY" ]; then
echo "present=true" >> "$GITHUB_OUTPUT"
else
echo "present=false" >> "$GITHUB_OUTPUT"
echo "::notice::DIDIT_SANDBOX_API_KEY is not set; the live smoke tests are skipped."
fi
- name: Check out the repository
if: steps.key.outputs.present == 'true'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up uv
if: steps.key.outputs.present == 'true'
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
- name: Install dependencies
if: steps.key.outputs.present == 'true'
run: uv sync --frozen
- name: pytest -m live
if: steps.key.outputs.present == 'true'
# The key reaches only this step, through the environment; it is never printed.
# Optional repository variables: DIDIT_SANDBOX_WORKFLOW_ID, DIDIT_SANDBOX_BASE_URL.
env:
DIDIT_SANDBOX_API_KEY: ${{ secrets.DIDIT_SANDBOX_API_KEY }}
DIDIT_SANDBOX_WORKFLOW_ID: ${{ vars.DIDIT_SANDBOX_WORKFLOW_ID }}
SANDBOX_BASE_URL: ${{ vars.DIDIT_SANDBOX_BASE_URL }}
run: |
if [ -n "$SANDBOX_BASE_URL" ]; then
export DIDIT_SANDBOX_BASE_URL="$SANDBOX_BASE_URL"
fi
uv run pytest -m live
Loading
Loading