shieldops-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. shieldops_sdk-0.1.0/.gitattributes +36 -0
  2. shieldops_sdk-0.1.0/.github/CODEOWNERS +22 -0
  3. shieldops_sdk-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  4. shieldops_sdk-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
  5. shieldops_sdk-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +37 -0
  6. shieldops_sdk-0.1.0/.github/ISSUE_TEMPLATE/security_advisory.md +22 -0
  7. shieldops_sdk-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +44 -0
  8. shieldops_sdk-0.1.0/.github/dependabot.yml +62 -0
  9. shieldops_sdk-0.1.0/.github/scripts/assemble_release_evidence.sh +110 -0
  10. shieldops_sdk-0.1.0/.github/workflows/ci.yml +62 -0
  11. shieldops_sdk-0.1.0/.github/workflows/release.yml +253 -0
  12. shieldops_sdk-0.1.0/CHANGELOG.md +79 -0
  13. shieldops_sdk-0.1.0/CODE_OF_CONDUCT.md +37 -0
  14. shieldops_sdk-0.1.0/CONTRIBUTING.md +155 -0
  15. shieldops_sdk-0.1.0/HITL-SETUP.md +164 -0
  16. shieldops_sdk-0.1.0/LICENSE +19 -0
  17. shieldops_sdk-0.1.0/MAINTAINERS.md +75 -0
  18. shieldops_sdk-0.1.0/PKG-INFO +221 -0
  19. shieldops_sdk-0.1.0/README.md +169 -0
  20. shieldops_sdk-0.1.0/ROADMAP.md +81 -0
  21. shieldops_sdk-0.1.0/SECURITY.md +106 -0
  22. shieldops_sdk-0.1.0/docs/api-reference.md +462 -0
  23. shieldops_sdk-0.1.0/docs/configuration.md +222 -0
  24. shieldops_sdk-0.1.0/docs/onboarding.md +115 -0
  25. shieldops_sdk-0.1.0/docs/troubleshooting.md +254 -0
  26. shieldops_sdk-0.1.0/eval/README.md +41 -0
  27. shieldops_sdk-0.1.0/examples/README.md +93 -0
  28. shieldops_sdk-0.1.0/examples/crewai_crew.py +198 -0
  29. shieldops_sdk-0.1.0/examples/custom_policies.py +236 -0
  30. shieldops_sdk-0.1.0/examples/fastapi_app.py +253 -0
  31. shieldops_sdk-0.1.0/examples/langchain_agent.py +161 -0
  32. shieldops_sdk-0.1.0/examples/standalone_interceptor.py +160 -0
  33. shieldops_sdk-0.1.0/pyproject.toml +101 -0
  34. shieldops_sdk-0.1.0/src/shieldops_sdk/__init__.py +50 -0
  35. shieldops_sdk-0.1.0/src/shieldops_sdk/_policy/__init__.py +41 -0
  36. shieldops_sdk-0.1.0/src/shieldops_sdk/_policy/_defaults.py +33 -0
  37. shieldops_sdk-0.1.0/src/shieldops_sdk/_response.py +72 -0
  38. shieldops_sdk-0.1.0/src/shieldops_sdk/async_client.py +83 -0
  39. shieldops_sdk-0.1.0/src/shieldops_sdk/client.py +85 -0
  40. shieldops_sdk-0.1.0/src/shieldops_sdk/config.py +95 -0
  41. shieldops_sdk-0.1.0/src/shieldops_sdk/exceptions.py +85 -0
  42. shieldops_sdk-0.1.0/src/shieldops_sdk/experimental/__init__.py +22 -0
  43. shieldops_sdk-0.1.0/src/shieldops_sdk/experimental/autogen.py +114 -0
  44. shieldops_sdk-0.1.0/src/shieldops_sdk/experimental/openai_agents.py +96 -0
  45. shieldops_sdk-0.1.0/src/shieldops_sdk/integrations/__init__.py +3 -0
  46. shieldops_sdk-0.1.0/src/shieldops_sdk/integrations/crewai.py +83 -0
  47. shieldops_sdk-0.1.0/src/shieldops_sdk/integrations/langchain.py +91 -0
  48. shieldops_sdk-0.1.0/src/shieldops_sdk/integrations/llamaindex.py +86 -0
  49. shieldops_sdk-0.1.0/src/shieldops_sdk/interceptor.py +217 -0
  50. shieldops_sdk-0.1.0/src/shieldops_sdk/models.py +120 -0
  51. shieldops_sdk-0.1.0/src/shieldops_sdk/resources/__init__.py +31 -0
  52. shieldops_sdk-0.1.0/src/shieldops_sdk/resources/agents.py +90 -0
  53. shieldops_sdk-0.1.0/src/shieldops_sdk/resources/investigations.py +137 -0
  54. shieldops_sdk-0.1.0/src/shieldops_sdk/resources/remediations.py +218 -0
  55. shieldops_sdk-0.1.0/src/shieldops_sdk/resources/security.py +168 -0
  56. shieldops_sdk-0.1.0/src/shieldops_sdk/resources/vulnerabilities.py +253 -0
  57. shieldops_sdk-0.1.0/src/shieldops_sdk/telemetry.py +196 -0
  58. shieldops_sdk-0.1.0/tests/__init__.py +0 -0
  59. shieldops_sdk-0.1.0/tests/integration/__init__.py +0 -0
  60. shieldops_sdk-0.1.0/tests/integration/sigstore/__init__.py +0 -0
  61. shieldops_sdk-0.1.0/tests/integration/sigstore/conftest.py +89 -0
  62. shieldops_sdk-0.1.0/tests/integration/sigstore/test_staging_round_trip.py +167 -0
  63. shieldops_sdk-0.1.0/tests/test_async_client.py +74 -0
  64. shieldops_sdk-0.1.0/tests/test_client.py +232 -0
  65. shieldops_sdk-0.1.0/tests/test_config.py +62 -0
  66. shieldops_sdk-0.1.0/tests/test_crewai_integration.py +167 -0
  67. shieldops_sdk-0.1.0/tests/test_experimental.py +124 -0
  68. shieldops_sdk-0.1.0/tests/test_interceptor.py +125 -0
  69. shieldops_sdk-0.1.0/tests/test_langchain_integration.py +175 -0
  70. shieldops_sdk-0.1.0/tests/test_llamaindex_integration.py +170 -0
  71. shieldops_sdk-0.1.0/tests/test_models.py +132 -0
  72. shieldops_sdk-0.1.0/tests/test_packaging.py +84 -0
  73. shieldops_sdk-0.1.0/tests/test_policy_defaults.py +120 -0
  74. shieldops_sdk-0.1.0/tests/test_resources.py +158 -0
  75. shieldops_sdk-0.1.0/tests/test_telemetry.py +169 -0
  76. shieldops_sdk-0.1.0/tests/test_telemetry_modes.py +196 -0
@@ -0,0 +1,36 @@
1
+ # Normalize line endings on commit; check out with platform-native endings.
2
+ * text=auto eol=lf
3
+
4
+ # Source / docs / config — always LF, always text.
5
+ *.py text eol=lf
6
+ *.pyi text eol=lf
7
+ *.md text eol=lf
8
+ *.rst text eol=lf
9
+ *.txt text eol=lf
10
+ *.toml text eol=lf
11
+ *.yaml text eol=lf
12
+ *.yml text eol=lf
13
+ *.json text eol=lf
14
+ *.cfg text eol=lf
15
+ *.ini text eol=lf
16
+
17
+ # Shell scripts must stay LF even on Windows clones.
18
+ *.sh text eol=lf
19
+ Makefile text eol=lf
20
+
21
+ # Common binaries — never normalize.
22
+ *.png binary
23
+ *.jpg binary
24
+ *.jpeg binary
25
+ *.gif binary
26
+ *.ico binary
27
+ *.pdf binary
28
+ *.zip binary
29
+ *.gz binary
30
+ *.tar binary
31
+ *.whl binary
32
+
33
+ # Linguist hints for GitHub's language stats.
34
+ docs/** linguist-documentation
35
+ examples/** linguist-documentation
36
+ tests/** linguist-detectable=false
@@ -0,0 +1,22 @@
1
+ # CODEOWNERS for shieldops-sdk
2
+ #
3
+ # Every path in this repository is owned by at least one reviewer listed
4
+ # below. Pull requests automatically request review from the matching
5
+ # owner. Order matters: the last matching pattern wins for a given path.
6
+ #
7
+ # TODO: replace @ghantakiran with team handles (e.g. @shieldops/maintainers,
8
+ # @shieldops/security) after the GitHub organization is provisioned and
9
+ # the maintainers/security teams exist. See HITL-SETUP.md.
10
+
11
+ * @ghantakiran
12
+
13
+ # Security-sensitive paths require a security reviewer in addition to
14
+ # the default owner. After org setup, add @shieldops/security here.
15
+ /SECURITY.md @ghantakiran
16
+ /.github/ @ghantakiran
17
+ /src/shieldops_sdk/ @ghantakiran
18
+
19
+ # Release + license docs require a maintainer review.
20
+ /LICENSE @ghantakiran
21
+ /CHANGELOG.md @ghantakiran
22
+ /pyproject.toml @ghantakiran
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: Bug report
3
+ about: Report a defect in shieldops-sdk
4
+ title: "[bug] "
5
+ labels: ["bug", "triage"]
6
+ assignees: []
7
+ ---
8
+
9
+ ## Reproduction
10
+
11
+ Steps to reproduce the behavior. A minimal code sample is ideal.
12
+
13
+ ```python
14
+ # paste minimal repro here
15
+ ```
16
+
17
+ ## Expected
18
+
19
+ What you expected to happen.
20
+
21
+ ## Actual
22
+
23
+ What actually happened. Include the full traceback if an exception was
24
+ raised. Redact any API keys, tokens, or customer data before posting.
25
+
26
+ ## Environment
27
+
28
+ | Field | Value |
29
+ |-------|-------|
30
+ | Python version | e.g. 3.12.2 |
31
+ | `shieldops-sdk` version | e.g. 1.0.0 |
32
+ | Integration framework | e.g. LangChain 0.2.5 / CrewAI 0.30 / LlamaIndex 0.10 / none |
33
+ | Operating system | e.g. macOS 14.4 / Ubuntu 22.04 / Windows 11 |
34
+ | SDK mode | `audit` or `enforce` |
35
+
36
+ ## Additional context
37
+
38
+ Logs, screenshots, or anything else that helps. Remember: do not paste
39
+ secrets.
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Security vulnerability report
4
+ url: https://github.com/shieldops/shieldops-sdk/blob/main/SECURITY.md
5
+ about: Do not report security issues in public GitHub issues. See SECURITY.md for the private disclosure process and our response SLAs.
6
+ - name: Usage questions and discussions
7
+ url: https://github.com/shieldops/shieldops-sdk/discussions
8
+ about: For general questions about using shieldops-sdk, open a discussion instead of an issue.
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: Feature request
3
+ about: Propose an enhancement to shieldops-sdk
4
+ title: "[feat] "
5
+ labels: ["enhancement", "triage"]
6
+ assignees: []
7
+ ---
8
+
9
+ ## Use case
10
+
11
+ What are you trying to accomplish? Describe the user or system problem,
12
+ not the solution.
13
+
14
+ ## Current workaround
15
+
16
+ How are you solving this today, if at all? Include code if helpful.
17
+
18
+ ## Proposed API
19
+
20
+ What would the ideal public API look like? Sketch the shape in code:
21
+
22
+ ```python
23
+ # your proposed usage
24
+ ```
25
+
26
+ Call out any new public classes, methods, config fields, or env vars.
27
+
28
+ ## Alternatives considered
29
+
30
+ What other approaches did you evaluate? Why did you rule them out?
31
+
32
+ ## Additional context
33
+
34
+ Links to related issues, upstream framework docs, prior art in other
35
+ SDKs, or benchmarks. If this is a new framework integration, also read
36
+ the "Proposing a new framework integration" section of
37
+ `CONTRIBUTING.md` before filing.
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: Security issue (do not use)
3
+ about: Redirects to SECURITY.md
4
+ title: "[security] DO NOT FILE HERE"
5
+ labels: ["security"]
6
+ assignees: []
7
+ ---
8
+
9
+ ## Do not report security issues here
10
+
11
+ Public issues are visible to everyone, including attackers. Reporting a
12
+ vulnerability here can put users at risk before a patch is available.
13
+
14
+ Instead, follow the process in [`SECURITY.md`](../../SECURITY.md):
15
+
16
+ - Email **security@shieldops.io** with details.
17
+ - Encrypt sensitive reports with the PGP key referenced in `SECURITY.md`.
18
+ - Expect an acknowledgement within the SLA published in that document.
19
+
20
+ If you opened this template by mistake, please close it and redirect
21
+ your report to the email address above. Thank you for helping keep
22
+ `shieldops-sdk` users safe.
@@ -0,0 +1,44 @@
1
+ ## Summary
2
+
3
+ One-paragraph description of what this PR changes and why. Focus on the
4
+ "why" -- the diff already shows the "what".
5
+
6
+ ## Linked issue
7
+
8
+ Closes #<issue-number>
9
+
10
+ (Use `Closes` / `Fixes` for bug fixes, `Refs` for partial work.)
11
+
12
+ ## Tests added
13
+
14
+ - [ ] Unit tests for new code paths
15
+ - [ ] Integration tests updated if public behavior changed
16
+ - [ ] Coverage on changed lines is >= 80%
17
+ - [ ] `pytest tests/ -v` passes locally
18
+
19
+ Describe what the new tests cover and how they exercise the change.
20
+
21
+ ## DCO signed
22
+
23
+ - [ ] Every commit on this branch is signed off with `git commit -s`
24
+ (adds a `Signed-off-by:` trailer). The DCO bot blocks merge
25
+ otherwise. See `CONTRIBUTING.md` for details.
26
+
27
+ ## Breaking change?
28
+
29
+ - [ ] Yes -- describe the break and the migration path below.
30
+ - [ ] No.
31
+
32
+ If yes: document the before/after API, whether a deprecation period
33
+ was offered, and link the RFC issue that approved the break.
34
+
35
+ ## Checklist
36
+
37
+ - [ ] `ruff check src/ tests/` passes with no new warnings
38
+ - [ ] `ruff format --check src/ tests/` passes
39
+ - [ ] `pytest tests/ -v --tb=short` passes
40
+ - [ ] Type hints on all new public functions and methods
41
+ - [ ] Docstrings on all new public classes, functions, and methods
42
+ - [ ] `CHANGELOG.md` updated under `[Unreleased]`
43
+ - [ ] Documentation updated in `docs/` if public behavior changed
44
+ - [ ] No secrets, API keys, or customer data in the diff or tests
@@ -0,0 +1,62 @@
1
+ # Dependabot config for shieldops-sdk (PRD-028 PR α).
2
+ #
3
+ # Authored in the private monorepo at sdk/.github/dependabot.yml; rides
4
+ # the carve-out mirror to shieldops-io/shieldops-sdk.
5
+ #
6
+ # SLA per PRD-028 §"PR α / Acceptance criteria":
7
+ # - high-severity advisories → 7 days
8
+ # - medium-severity advisories → 30 days
9
+ #
10
+ # Dependabot itself respects the GitHub Advisory Database for severity
11
+ # labeling; the timelines above are enforced by team policy + the
12
+ # auto-merge schedule below (informational `commit-message.prefix`
13
+ # tagging that monitoring can grep).
14
+
15
+ version: 2
16
+
17
+ updates:
18
+ # ── Python (the SDK itself) ──────────────────────────────────────────
19
+ - package-ecosystem: "pip"
20
+ directory: "/"
21
+ schedule:
22
+ interval: "weekly"
23
+ day: "monday"
24
+ open-pull-requests-limit: 10
25
+ commit-message:
26
+ prefix: "deps(pip)"
27
+ include: "scope"
28
+ groups:
29
+ runtime:
30
+ patterns:
31
+ - "*"
32
+ exclude-patterns:
33
+ - "pytest*"
34
+ - "ruff"
35
+ - "mypy"
36
+ - "hatch*"
37
+ - "build"
38
+ dev:
39
+ patterns:
40
+ - "pytest*"
41
+ - "ruff"
42
+ - "mypy"
43
+ - "hatch*"
44
+ - "build"
45
+ # Security-only updates run on a tighter cadence — Dependabot opens
46
+ # those PRs immediately on advisory publish.
47
+ labels:
48
+ - "dependencies"
49
+ - "python"
50
+
51
+ # ── GitHub Actions (release.yml + ci.yml) ────────────────────────────
52
+ - package-ecosystem: "github-actions"
53
+ directory: "/"
54
+ schedule:
55
+ interval: "weekly"
56
+ day: "monday"
57
+ open-pull-requests-limit: 5
58
+ commit-message:
59
+ prefix: "deps(actions)"
60
+ labels:
61
+ - "dependencies"
62
+ - "github-actions"
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env bash
2
+ # sdk/.github/scripts/assemble_release_evidence.sh — PRD-028 PR α companion.
3
+ #
4
+ # Invoked by release.yml after verify-published succeeds. Collects build
5
+ # metadata + SBOM digest + SLSA provenance URL + Sigstore bundle ref +
6
+ # private-source SHA, writes RELEASE_EVIDENCE.md, and attaches it as a
7
+ # GitHub Release asset.
8
+ #
9
+ # Row schema is forward-compatible with PRD-013 Merkle ledger.
10
+ #
11
+ # Required environment:
12
+ # GITHUB_REF_NAME — the sdk-v<X.Y.Z> tag
13
+ # GITHUB_SHA — the tagged commit SHA on the public repo
14
+ # SDK_VERSION — X.Y.Z (resolved by release.yml/prepare)
15
+ # IS_RC — "true" or "false"
16
+ # SDIST_SHA256 — from release.yml/build
17
+ # WHEEL_SHA256 — from release.yml/build
18
+ # SBOM_PATH — path to sbom.cdx.json (downloaded artifact)
19
+ # SLSA_PROVENANCE_URL — URL to the .intoto.jsonl provenance asset
20
+ # SIGSTORE_BUNDLE — Sigstore signing bundle ref (set by publish job)
21
+ # PRIVATE_REPO_READ_TOKEN — fine-grained PAT, read-only, single path on
22
+ # ghantakiran/ShieldOps; used to resolve the
23
+ # matching private source SHA from
24
+ # tools/SDK_TREE_PINS.txt.
25
+ #
26
+ # Output:
27
+ # RELEASE_EVIDENCE.md in repo root, then `gh release upload`'d.
28
+
29
+ set -euo pipefail
30
+
31
+ : "${GITHUB_REF_NAME:?}"
32
+ : "${GITHUB_SHA:?}"
33
+ : "${SDK_VERSION:?}"
34
+ : "${SDIST_SHA256:?}"
35
+ : "${WHEEL_SHA256:?}"
36
+
37
+ # Optional with sensible defaults so the script is testable locally.
38
+ IS_RC="${IS_RC:-false}"
39
+ SBOM_PATH="${SBOM_PATH:-sbom.cdx.json}"
40
+ SLSA_PROVENANCE_URL="${SLSA_PROVENANCE_URL:-pending}"
41
+ SIGSTORE_BUNDLE="${SIGSTORE_BUNDLE:-pending}"
42
+
43
+ # Resolve the matching private-source SHA from tools/SDK_TREE_PINS.txt
44
+ # on the private monorepo using the single-path read-only PAT. The pin
45
+ # file is the only file this token can read.
46
+ PRIVATE_SOURCE_SHA="unknown"
47
+ if [[ -n "${PRIVATE_REPO_READ_TOKEN:-}" ]]; then
48
+ PINS=$(curl -sf \
49
+ -H "Accept: application/vnd.github.raw" \
50
+ -H "Authorization: Bearer ${PRIVATE_REPO_READ_TOKEN}" \
51
+ "https://api.github.com/repos/ghantakiran/ShieldOps/contents/tools/SDK_TREE_PINS.txt" \
52
+ || echo "")
53
+ if [[ -n "${PINS}" ]]; then
54
+ PRIVATE_SOURCE_SHA=$(echo "${PINS}" \
55
+ | awk -v ver="${SDK_VERSION}" '
56
+ $0 ~ "^sdk-v"ver"$" { found=1; next }
57
+ found && /^private_main_sha:/ { print $2; exit }
58
+ ' || echo "lookup-failed")
59
+ fi
60
+ fi
61
+
62
+ SBOM_DIGEST="unknown"
63
+ if [[ -f "${SBOM_PATH}" ]]; then
64
+ SBOM_DIGEST=$(sha256sum "${SBOM_PATH}" | cut -d' ' -f1)
65
+ fi
66
+
67
+ cat > RELEASE_EVIDENCE.md <<EOF
68
+ # Release Evidence — ${GITHUB_REF_NAME}
69
+
70
+ This file is auto-generated by .github/scripts/assemble_release_evidence.sh.
71
+ Schema is forward-compatible with the PRD-013 Merkle ledger.
72
+
73
+ | Field | Value |
74
+ |---|---|
75
+ | Tag | \`${GITHUB_REF_NAME}\` |
76
+ | SDK version | \`${SDK_VERSION}\` |
77
+ | Release candidate | \`${IS_RC}\` |
78
+ | Public release SHA | \`${GITHUB_SHA}\` |
79
+ | Private source SHA | \`${PRIVATE_SOURCE_SHA}\` |
80
+ | sdist SHA-256 | \`${SDIST_SHA256}\` |
81
+ | wheel SHA-256 | \`${WHEEL_SHA256}\` |
82
+ | SBOM (CycloneDX) digest | \`${SBOM_DIGEST}\` |
83
+ | SLSA provenance | ${SLSA_PROVENANCE_URL} |
84
+ | Sigstore bundle | ${SIGSTORE_BUNDLE} |
85
+ | Generated at | $(date -u +%Y-%m-%dT%H:%M:%SZ) |
86
+
87
+ ## Verifying this release
88
+
89
+ \`\`\`bash
90
+ # Verify the wheel matches the published artifact
91
+ pip download --no-deps shieldops-sdk==${SDK_VERSION} -d /tmp/verify
92
+ sha256sum /tmp/verify/shieldops_sdk-*.whl # should match wheel SHA-256 above
93
+
94
+ # Verify Sigstore signature (after PR β #647 lands)
95
+ python -m sigstore verify identity \\
96
+ --bundle "\${SIGSTORE_BUNDLE_PATH}" \\
97
+ --cert-identity-regexp '^https://github.com/shieldops-io/shieldops-sdk/' \\
98
+ --cert-oidc-issuer https://token.actions.githubusercontent.com \\
99
+ /tmp/verify/shieldops_sdk-*.whl
100
+
101
+ # Verify SLSA provenance via slsa-verifier
102
+ slsa-verifier verify-artifact /tmp/verify/shieldops_sdk-*.whl \\
103
+ --provenance-path provenance.intoto.jsonl \\
104
+ --source-uri github.com/shieldops-io/shieldops-sdk \\
105
+ --source-tag ${GITHUB_REF_NAME}
106
+ \`\`\`
107
+ EOF
108
+
109
+ echo "RELEASE_EVIDENCE.md written:"
110
+ cat RELEASE_EVIDENCE.md
@@ -0,0 +1,62 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ push:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+ pull-requests: read
12
+
13
+ concurrency:
14
+ group: ci-${{ github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ test:
19
+ name: test (py${{ matrix.python-version }} / ${{ matrix.os }})
20
+ runs-on: ${{ matrix.os }}
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ python-version: ["3.10", "3.11", "3.12"]
25
+ os: [ubuntu-latest, macos-latest]
26
+
27
+ steps:
28
+ - name: Checkout
29
+ uses: actions/checkout@v4
30
+
31
+ - name: Set up Python ${{ matrix.python-version }}
32
+ uses: actions/setup-python@v5
33
+ with:
34
+ python-version: ${{ matrix.python-version }}
35
+ cache: pip
36
+
37
+ - name: Install
38
+ run: |
39
+ python -m pip install --upgrade pip
40
+ pip install -e ".[dev]"
41
+
42
+ - name: Lint (ruff check)
43
+ run: ruff check src/ tests/
44
+
45
+ - name: Format (ruff format --check)
46
+ run: ruff format --check src/ tests/
47
+
48
+ - name: Test (pytest)
49
+ run: pytest tests/ -v --tb=short
50
+
51
+ dco:
52
+ name: DCO sign-off check
53
+ runs-on: ubuntu-latest
54
+ if: github.event_name == 'pull_request'
55
+ steps:
56
+ - name: Checkout
57
+ uses: actions/checkout@v4
58
+ with:
59
+ fetch-depth: 0
60
+
61
+ - name: Verify DCO
62
+ uses: tim-actions/dco@v1.1.0