kaos-graph 0.1.0a1__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 (91) hide show
  1. kaos_graph-0.1.0a1/.github/CODEOWNERS +16 -0
  2. kaos_graph-0.1.0a1/.github/ISSUE_TEMPLATE/bug.yml +59 -0
  3. kaos_graph-0.1.0a1/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. kaos_graph-0.1.0a1/.github/ISSUE_TEMPLATE/feature.yml +26 -0
  5. kaos_graph-0.1.0a1/.github/PULL_REQUEST_TEMPLATE.md +23 -0
  6. kaos_graph-0.1.0a1/.github/dependabot.yml +56 -0
  7. kaos_graph-0.1.0a1/.github/workflows/ci.yml +240 -0
  8. kaos_graph-0.1.0a1/.github/workflows/release.yml +241 -0
  9. kaos_graph-0.1.0a1/.github/workflows/security.yml +113 -0
  10. kaos_graph-0.1.0a1/.gitignore +41 -0
  11. kaos_graph-0.1.0a1/.pre-commit-config.yaml +76 -0
  12. kaos_graph-0.1.0a1/CHANGELOG.md +222 -0
  13. kaos_graph-0.1.0a1/Cargo.lock +472 -0
  14. kaos_graph-0.1.0a1/Cargo.toml +51 -0
  15. kaos_graph-0.1.0a1/LICENSE +201 -0
  16. kaos_graph-0.1.0a1/NOTICE +8 -0
  17. kaos_graph-0.1.0a1/PKG-INFO +84 -0
  18. kaos_graph-0.1.0a1/README.md +50 -0
  19. kaos_graph-0.1.0a1/SECURITY.md +58 -0
  20. kaos_graph-0.1.0a1/deny.toml +58 -0
  21. kaos_graph-0.1.0a1/pyproject.toml +147 -0
  22. kaos_graph-0.1.0a1/python/kaos_graph/__init__.py +27 -0
  23. kaos_graph-0.1.0a1/python/kaos_graph/__main__.py +6 -0
  24. kaos_graph-0.1.0a1/python/kaos_graph/_rust/__init__.pyi +3 -0
  25. kaos_graph-0.1.0a1/python/kaos_graph/_rust/algorithms.pyi +95 -0
  26. kaos_graph-0.1.0a1/python/kaos_graph/_rust/graph.pyi +57 -0
  27. kaos_graph-0.1.0a1/python/kaos_graph/_rust/knowledge.pyi +17 -0
  28. kaos_graph-0.1.0a1/python/kaos_graph/_rust/rdf.pyi +20 -0
  29. kaos_graph-0.1.0a1/python/kaos_graph/algorithms/__init__.py +488 -0
  30. kaos_graph-0.1.0a1/python/kaos_graph/bridges/__init__.py +5 -0
  31. kaos_graph-0.1.0a1/python/kaos_graph/bridges/tabular.py +100 -0
  32. kaos_graph-0.1.0a1/python/kaos_graph/cli.py +32 -0
  33. kaos_graph-0.1.0a1/python/kaos_graph/errors.py +106 -0
  34. kaos_graph-0.1.0a1/python/kaos_graph/graph.py +240 -0
  35. kaos_graph-0.1.0a1/python/kaos_graph/io/__init__.py +18 -0
  36. kaos_graph-0.1.0a1/python/kaos_graph/io/adjacency.py +106 -0
  37. kaos_graph-0.1.0a1/python/kaos_graph/io/dot.py +66 -0
  38. kaos_graph-0.1.0a1/python/kaos_graph/io/gexf.py +304 -0
  39. kaos_graph-0.1.0a1/python/kaos_graph/io/graphml.py +277 -0
  40. kaos_graph-0.1.0a1/python/kaos_graph/io/mermaid.py +94 -0
  41. kaos_graph-0.1.0a1/python/kaos_graph/knowledge/__init__.py +295 -0
  42. kaos_graph-0.1.0a1/python/kaos_graph/programs/__init__.py +5 -0
  43. kaos_graph-0.1.0a1/python/kaos_graph/programs/convert.py +156 -0
  44. kaos_graph-0.1.0a1/python/kaos_graph/py.typed +0 -0
  45. kaos_graph-0.1.0a1/python/kaos_graph/rdf/__init__.py +303 -0
  46. kaos_graph-0.1.0a1/python/kaos_graph/rdf/sparql.py +267 -0
  47. kaos_graph-0.1.0a1/python/kaos_graph/schema.py +160 -0
  48. kaos_graph-0.1.0a1/python/kaos_graph/serve.py +134 -0
  49. kaos_graph-0.1.0a1/python/kaos_graph/settings.py +85 -0
  50. kaos_graph-0.1.0a1/python/kaos_graph/storage/__init__.py +5 -0
  51. kaos_graph-0.1.0a1/python/kaos_graph/storage/vfs.py +118 -0
  52. kaos_graph-0.1.0a1/python/kaos_graph/tools.py +1797 -0
  53. kaos_graph-0.1.0a1/python/kaos_graph/types.py +104 -0
  54. kaos_graph-0.1.0a1/rust/bindings/algorithms.rs +445 -0
  55. kaos_graph-0.1.0a1/rust/bindings/graph.rs +475 -0
  56. kaos_graph-0.1.0a1/rust/bindings/knowledge.rs +156 -0
  57. kaos_graph-0.1.0a1/rust/bindings/mod.rs +4 -0
  58. kaos_graph-0.1.0a1/rust/bindings/rdf.rs +106 -0
  59. kaos_graph-0.1.0a1/rust/core/algorithms/centrality.rs +845 -0
  60. kaos_graph-0.1.0a1/rust/core/algorithms/community.rs +691 -0
  61. kaos_graph-0.1.0a1/rust/core/algorithms/components.rs +182 -0
  62. kaos_graph-0.1.0a1/rust/core/algorithms/dag.rs +424 -0
  63. kaos_graph-0.1.0a1/rust/core/algorithms/mod.rs +7 -0
  64. kaos_graph-0.1.0a1/rust/core/algorithms/paths.rs +647 -0
  65. kaos_graph-0.1.0a1/rust/core/algorithms/structure.rs +629 -0
  66. kaos_graph-0.1.0a1/rust/core/algorithms/traversal.rs +319 -0
  67. kaos_graph-0.1.0a1/rust/core/edge.rs +27 -0
  68. kaos_graph-0.1.0a1/rust/core/graph.rs +1395 -0
  69. kaos_graph-0.1.0a1/rust/core/knowledge.rs +918 -0
  70. kaos_graph-0.1.0a1/rust/core/mod.rs +6 -0
  71. kaos_graph-0.1.0a1/rust/core/node.rs +11 -0
  72. kaos_graph-0.1.0a1/rust/core/rdf.rs +810 -0
  73. kaos_graph-0.1.0a1/rust/lib.rs +32 -0
  74. kaos_graph-0.1.0a1/tests/__init__.py +0 -0
  75. kaos_graph-0.1.0a1/tests/bench_performance.py +209 -0
  76. kaos_graph-0.1.0a1/tests/conftest.py +79 -0
  77. kaos_graph-0.1.0a1/tests/integration/__init__.py +0 -0
  78. kaos_graph-0.1.0a1/tests/integration/test_mcp_graph_pipeline.py +1043 -0
  79. kaos_graph-0.1.0a1/tests/security/__init__.py +0 -0
  80. kaos_graph-0.1.0a1/tests/security/test_audit_a2.py +647 -0
  81. kaos_graph-0.1.0a1/tests/unit/__init__.py +0 -0
  82. kaos_graph-0.1.0a1/tests/unit/test_algorithms.py +933 -0
  83. kaos_graph-0.1.0a1/tests/unit/test_bridges.py +24 -0
  84. kaos_graph-0.1.0a1/tests/unit/test_graph.py +347 -0
  85. kaos_graph-0.1.0a1/tests/unit/test_io.py +497 -0
  86. kaos_graph-0.1.0a1/tests/unit/test_knowledge.py +418 -0
  87. kaos_graph-0.1.0a1/tests/unit/test_programs.py +331 -0
  88. kaos_graph-0.1.0a1/tests/unit/test_rdf.py +243 -0
  89. kaos_graph-0.1.0a1/tests/unit/test_schema.py +162 -0
  90. kaos_graph-0.1.0a1/tests/unit/test_storage.py +68 -0
  91. kaos_graph-0.1.0a1/tests/unit/test_tools.py +858 -0
@@ -0,0 +1,16 @@
1
+ # Default owner for everything in this repo
2
+ * @mjbommar
3
+
4
+ # Critical paths get explicit ownership for clarity
5
+ /.github/ @mjbommar
6
+ /pyproject.toml @mjbommar
7
+ /Cargo.toml @mjbommar
8
+ /Cargo.lock @mjbommar
9
+ /LICENSE @mjbommar
10
+ /NOTICE @mjbommar
11
+ /CHANGELOG.md @mjbommar
12
+ /SECURITY.md @mjbommar
13
+ /deny.toml @mjbommar
14
+
15
+ # Rust core / bindings — version-coupled with the Python wheel
16
+ /rust/ @mjbommar
@@ -0,0 +1,59 @@
1
+ name: Bug report
2
+ description: Report a defect in kaos-graph
3
+ title: "[bug] "
4
+ labels: ["type:bug", "needs-triage"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ Thanks for taking the time to file a bug report. Please fill out the sections below.
10
+
11
+ - type: textarea
12
+ id: what-happened
13
+ attributes:
14
+ label: What happened?
15
+ description: A clear description of the bug. Include any error messages or stack traces.
16
+ placeholder: When I call X with Y, Z happens instead of W.
17
+ validations:
18
+ required: true
19
+
20
+ - type: textarea
21
+ id: reproduce
22
+ attributes:
23
+ label: How to reproduce
24
+ description: Minimal code or commands that reproduce the bug.
25
+ render: python
26
+ validations:
27
+ required: true
28
+
29
+ - type: input
30
+ id: version
31
+ attributes:
32
+ label: kaos-graph version
33
+ description: Output of `python -c "import kaos_graph; print(kaos_graph.__version__)"`
34
+ placeholder: e.g. 0.1.0a1
35
+ validations:
36
+ required: true
37
+
38
+ - type: input
39
+ id: python-version
40
+ attributes:
41
+ label: Python version
42
+ description: Output of `python --version`
43
+ placeholder: e.g. 3.13.1
44
+ validations:
45
+ required: true
46
+
47
+ - type: input
48
+ id: os
49
+ attributes:
50
+ label: Operating system + architecture
51
+ placeholder: e.g. Ubuntu 24.04 x86_64, macOS 14 arm64, Windows 11 x86_64
52
+ validations:
53
+ required: true
54
+
55
+ - type: textarea
56
+ id: extra
57
+ attributes:
58
+ label: Additional context
59
+ description: Anything else that might help. For Rust-side issues, include the wheel platform tag (e.g. `manylinux_2_28_x86_64`).
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Security vulnerability
4
+ url: https://github.com/273v/kaos-graph/security/advisories/new
5
+ about: Report a security issue privately via GitHub Security Advisories. Do not file a public issue.
@@ -0,0 +1,26 @@
1
+ name: Feature request
2
+ description: Suggest a new capability or improvement for kaos-graph
3
+ title: "[feat] "
4
+ labels: ["type:feat", "needs-triage"]
5
+ body:
6
+ - type: textarea
7
+ id: problem
8
+ attributes:
9
+ label: What problem does this solve?
10
+ description: Describe the use case, not the implementation.
11
+ placeholder: I'm trying to do X and the current API forces me to Y.
12
+ validations:
13
+ required: true
14
+
15
+ - type: textarea
16
+ id: proposal
17
+ attributes:
18
+ label: Proposed solution
19
+ description: Optional. If you already have an API or implementation in mind, sketch it here.
20
+ render: python
21
+
22
+ - type: textarea
23
+ id: alternatives
24
+ attributes:
25
+ label: Alternatives considered
26
+ description: What workarounds did you try, and why aren't they enough?
@@ -0,0 +1,23 @@
1
+ ## Summary
2
+
3
+ <!-- One-paragraph description of what this PR does and why it's needed. -->
4
+
5
+ ## Type of change
6
+
7
+ - [ ] Bug fix (non-breaking change which fixes an issue)
8
+ - [ ] New feature (non-breaking change which adds functionality)
9
+ - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
10
+ - [ ] Documentation only
11
+
12
+ ## Checklist
13
+
14
+ - [ ] Commits are signed off (`git commit -s`) — DCO required
15
+ - [ ] Tests added/updated for any behavior change
16
+ - [ ] **Rust** — `cargo fmt --check`, `cargo clippy --no-default-features --all-targets -- -D warnings`, `cargo test --no-default-features --lib`
17
+ - [ ] **Python** — `uv run ruff format --check python/kaos_graph tests`, `uv run ruff check python/kaos_graph tests`, `uv run ty check python/kaos_graph tests`, `uv run pytest -m "not live and not network and not slow" tests/`
18
+ - [ ] **Build** — `uv run maturin develop --release` succeeds
19
+ - [ ] `CHANGELOG.md` updated under `[Unreleased]` if user-visible
20
+
21
+ ## Related issues
22
+
23
+ <!-- "Closes #123" or "Refs #123" -->
@@ -0,0 +1,56 @@
1
+ version: 2
2
+ updates:
3
+ # ────────────── GitHub Actions ──────────────
4
+ - package-ecosystem: github-actions
5
+ directory: /
6
+ schedule:
7
+ interval: monthly
8
+ day: monday
9
+ time: "06:00"
10
+ timezone: Etc/UTC
11
+ groups:
12
+ actions:
13
+ patterns: ["*"]
14
+ labels: ["type:deps", "ci"]
15
+ commit-message:
16
+ prefix: "ci(deps)"
17
+
18
+ # ────────────── Python (uv-managed via pyproject.toml) ──────────────
19
+ - package-ecosystem: pip
20
+ directory: /
21
+ schedule:
22
+ interval: monthly
23
+ day: monday
24
+ time: "06:00"
25
+ timezone: Etc/UTC
26
+ groups:
27
+ python:
28
+ patterns: ["*"]
29
+ update-types: ["minor", "patch"]
30
+ python-major:
31
+ patterns: ["*"]
32
+ update-types: ["major"]
33
+ open-pull-requests-limit: 5
34
+ labels: ["type:deps"]
35
+ commit-message:
36
+ prefix: "deps"
37
+
38
+ # ────────────── Rust (cargo) ──────────────
39
+ - package-ecosystem: cargo
40
+ directory: /
41
+ schedule:
42
+ interval: monthly
43
+ day: monday
44
+ time: "06:00"
45
+ timezone: Etc/UTC
46
+ groups:
47
+ rust:
48
+ patterns: ["*"]
49
+ update-types: ["minor", "patch"]
50
+ rust-major:
51
+ patterns: ["*"]
52
+ update-types: ["major"]
53
+ open-pull-requests-limit: 5
54
+ labels: ["type:deps", "rust"]
55
+ commit-message:
56
+ prefix: "deps(rust)"
@@ -0,0 +1,240 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ push:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ci-${{ github.workflow }}-${{ github.ref }}
14
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
15
+
16
+ jobs:
17
+ # ────────────── lint (Python + Rust) ──────────────
18
+ lint:
19
+ name: Lint
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - name: Checkout
23
+ uses: actions/checkout@v6
24
+
25
+ - name: Set up Rust toolchain
26
+ uses: dtolnay/rust-toolchain@stable
27
+ with:
28
+ components: rustfmt, clippy
29
+
30
+ - name: Cargo fmt
31
+ run: cargo fmt --check
32
+
33
+ - name: Cargo clippy (no PyO3 features)
34
+ run: cargo clippy --no-default-features --all-targets -- -D warnings
35
+
36
+ - name: Set up uv
37
+ uses: astral-sh/setup-uv@v7
38
+ with:
39
+ enable-cache: true
40
+
41
+ - name: Install Python 3.13
42
+ run: uv python install 3.13
43
+
44
+ - name: Install dependencies (rdf extra so ty resolves pyoxigraph)
45
+ run: uv sync --group dev --extra rdf --python 3.13
46
+
47
+ - name: Ruff format check
48
+ run: uv run ruff format --check python/kaos_graph tests
49
+
50
+ - name: Ruff lint
51
+ run: uv run ruff check python/kaos_graph tests
52
+
53
+ - name: Type check (ty)
54
+ run: uv run ty check python/kaos_graph tests
55
+
56
+ # ────────────── pre-commit ──────────────
57
+ pre-commit:
58
+ name: Pre-commit hooks
59
+ runs-on: ubuntu-latest
60
+ steps:
61
+ - name: Checkout
62
+ uses: actions/checkout@v6
63
+
64
+ - name: Set up Rust toolchain
65
+ uses: dtolnay/rust-toolchain@stable
66
+
67
+ - name: Set up uv
68
+ uses: astral-sh/setup-uv@v7
69
+ with:
70
+ enable-cache: true
71
+
72
+ - name: Install Python 3.13
73
+ run: uv python install 3.13
74
+
75
+ - name: Install dependencies (rdf extra so the ty hook resolves pyoxigraph)
76
+ run: uv sync --group dev --extra rdf --python 3.13
77
+
78
+ - name: Run pre-commit
79
+ run: uvx pre-commit run --all-files --show-diff-on-failure
80
+
81
+ # ────────────── Rust unit tests (pure Rust core) ──────────────
82
+ rust-test:
83
+ name: Rust tests (no PyO3)
84
+ runs-on: ubuntu-latest
85
+ steps:
86
+ - name: Checkout
87
+ uses: actions/checkout@v6
88
+
89
+ - name: Set up Rust toolchain
90
+ uses: dtolnay/rust-toolchain@stable
91
+
92
+ - name: Cargo test (--no-default-features)
93
+ run: cargo test --no-default-features --lib
94
+
95
+ # ────────────── Python tests (Python × maturin develop) ──────────────
96
+ test:
97
+ name: Test (Python ${{ matrix.python-version }})
98
+ runs-on: ubuntu-latest
99
+ strategy:
100
+ fail-fast: false
101
+ matrix:
102
+ include:
103
+ - python-version: "3.13"
104
+ experimental: false
105
+ - python-version: "3.14"
106
+ experimental: false
107
+ - python-version: "3.14t"
108
+ experimental: true
109
+ - python-version: "3.15"
110
+ experimental: true
111
+ continue-on-error: ${{ matrix.experimental }}
112
+ steps:
113
+ - name: Checkout
114
+ uses: actions/checkout@v6
115
+
116
+ - name: Set up Rust toolchain
117
+ uses: dtolnay/rust-toolchain@stable
118
+
119
+ - name: Set up uv
120
+ uses: astral-sh/setup-uv@v7
121
+ with:
122
+ enable-cache: true
123
+
124
+ - name: Install Python ${{ matrix.python-version }}
125
+ run: uv python install --preview ${{ matrix.python-version }}
126
+
127
+ - name: Install dependencies (rdf extra for SPARQL tests)
128
+ run: uv sync --group dev --extra rdf --python ${{ matrix.python-version }}
129
+
130
+ - name: Build PyO3 extension (maturin develop --release)
131
+ run: uv run maturin develop --release
132
+
133
+ - name: Run tests with coverage
134
+ run: |
135
+ uv run pytest \
136
+ -m "not live and not network and not slow" \
137
+ --cov=kaos_graph \
138
+ --cov-branch \
139
+ --cov-report=term-missing \
140
+ --cov-report=xml:coverage.xml \
141
+ tests/
142
+
143
+ - name: Upload coverage report
144
+ if: always() && !matrix.experimental
145
+ uses: actions/upload-artifact@v7
146
+ with:
147
+ name: coverage-${{ matrix.python-version }}
148
+ path: coverage.xml
149
+ retention-days: 14
150
+
151
+ # ────────────── min-deps ──────────────
152
+ min-deps:
153
+ name: Test against minimum dependencies
154
+ runs-on: ubuntu-latest
155
+ steps:
156
+ - name: Checkout
157
+ uses: actions/checkout@v6
158
+
159
+ - name: Set up Rust toolchain
160
+ uses: dtolnay/rust-toolchain@stable
161
+
162
+ - name: Set up uv
163
+ uses: astral-sh/setup-uv@v7
164
+
165
+ - name: Install Python 3.13
166
+ run: uv python install 3.13
167
+
168
+ - name: Install with lowest-direct resolution (rdf extra)
169
+ run: uv sync --group dev --extra rdf --resolution=lowest-direct --python 3.13
170
+
171
+ - name: Build PyO3 extension
172
+ run: uv run maturin develop --release
173
+
174
+ - name: Run tests
175
+ run: |
176
+ uv run pytest \
177
+ -m "not live and not network and not slow" \
178
+ --no-cov \
179
+ -q \
180
+ tests/
181
+
182
+ # ────────────── build + wheel smoke test ──────────────
183
+ build:
184
+ name: Build distribution + smoke test
185
+ runs-on: ubuntu-latest
186
+ needs: [lint, test, rust-test]
187
+ steps:
188
+ - name: Checkout
189
+ uses: actions/checkout@v6
190
+
191
+ - name: Set up Rust toolchain
192
+ uses: dtolnay/rust-toolchain@stable
193
+
194
+ - name: Set up uv
195
+ uses: astral-sh/setup-uv@v7
196
+ with:
197
+ enable-cache: true
198
+
199
+ - name: Install Python 3.13
200
+ run: uv python install 3.13
201
+
202
+ - name: Build wheel + sdist
203
+ run: uv build
204
+
205
+ - name: Verify metadata (twine check)
206
+ run: uvx --from twine twine check --strict dist/*
207
+
208
+ - name: Smoke-test the built wheel in a clean venv
209
+ run: |
210
+ uv venv --python 3.13 /tmp/smoke
211
+ uv pip install --python /tmp/smoke/bin/python dist/kaos_graph-*-cp*.whl
212
+ /tmp/smoke/bin/python -c "
213
+ import kaos_graph
214
+ from kaos_graph import Graph
215
+ assert kaos_graph.__version__, 'version missing from wheel'
216
+ assert len(kaos_graph.__all__) >= 6, f'unexpected public surface: {kaos_graph.__all__}'
217
+ # Round-trip a small graph through Rust + JSON.
218
+ g = Graph()
219
+ g.add_node('a', role='engineer')
220
+ g.add_node('b')
221
+ g.add_edge('a', 'b', weight=1.0)
222
+ assert g.n_nodes == 2 and g.n_edges == 1
223
+ js = g.to_json()
224
+ g2 = Graph.from_json(js)
225
+ assert g2.n_nodes == 2 and g2.n_edges == 1
226
+ # Run a real algorithm.
227
+ from kaos_graph.algorithms import pagerank
228
+ ranks = pagerank(g)
229
+ assert len(ranks) == 2
230
+ print(f'kaos-graph {kaos_graph.__version__}: smoke OK')
231
+ "
232
+ # Exercise the CLI entrypoint.
233
+ /tmp/smoke/bin/kaos-graph version
234
+
235
+ - name: Upload distribution artifacts
236
+ uses: actions/upload-artifact@v7
237
+ with:
238
+ name: dist-${{ github.sha }}
239
+ path: dist/
240
+ retention-days: 7
@@ -0,0 +1,241 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ # Manual fallback so a failed publish (e.g. transient PyPI 5xx) can be
8
+ # retried without re-tagging.
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ concurrency:
15
+ group: release-${{ github.ref }}
16
+ cancel-in-progress: false
17
+
18
+ jobs:
19
+ # ────────────── Pre-publish QA + sdist ──────────────
20
+ # One job that gates everything else: verifies version sync, runs the
21
+ # full QA gauntlet, builds + verifies the sdist (the format-agnostic
22
+ # source artifact). Wheel-matrix jobs depend on this; if the source
23
+ # tree is broken we never compile 8 wheels for nothing.
24
+ sdist:
25
+ name: Build sdist + pre-publish QA
26
+ runs-on: ubuntu-latest
27
+ outputs:
28
+ version: ${{ steps.version.outputs.version }}
29
+ steps:
30
+ - name: Checkout
31
+ uses: actions/checkout@v6
32
+ with:
33
+ fetch-depth: 0
34
+
35
+ - name: Set up Rust toolchain
36
+ uses: dtolnay/rust-toolchain@stable
37
+ with:
38
+ components: rustfmt, clippy
39
+
40
+ - name: Set up uv
41
+ uses: astral-sh/setup-uv@v7
42
+ with:
43
+ enable-cache: true
44
+
45
+ - name: Install Python 3.13
46
+ run: uv python install 3.13
47
+
48
+ - name: Install dependencies
49
+ run: uv sync --group dev --extra rdf --python 3.13
50
+
51
+ - name: Verify Cargo.toml version matches tag
52
+ id: version
53
+ if: startsWith(github.ref, 'refs/tags/v')
54
+ run: |
55
+ # Cargo SemVer "0.1.0-alpha.1" maps to PEP 440 "0.1.0a1".
56
+ CARGO_VERSION=$(awk -F\" '/^version = "/{print $2; exit}' Cargo.toml)
57
+ # Normalize Cargo SemVer "X.Y.Z-alpha.N" -> PEP 440 "X.Y.ZaN" etc.
58
+ PEP440_VERSION=$(echo "$CARGO_VERSION" \
59
+ | sed -E 's/-alpha\.([0-9]+)$/a\1/' \
60
+ | sed -E 's/-beta\.([0-9]+)$/b\1/' \
61
+ | sed -E 's/-rc\.([0-9]+)$/rc\1/')
62
+ TAG=${GITHUB_REF_NAME#v}
63
+ if [ "$PEP440_VERSION" != "$TAG" ]; then
64
+ echo "::error::Tag '$TAG' does not match Cargo.toml-derived PEP 440 version '$PEP440_VERSION' (Cargo SemVer '$CARGO_VERSION')"
65
+ exit 1
66
+ fi
67
+ echo "version=$PEP440_VERSION" >> "$GITHUB_OUTPUT"
68
+ echo "kaos-graph release: tag=$TAG, cargo=$CARGO_VERSION, pep440=$PEP440_VERSION"
69
+
70
+ - name: Pre-publish QA — Rust
71
+ run: |
72
+ cargo fmt --check
73
+ cargo clippy --no-default-features --all-targets -- -D warnings
74
+ cargo test --no-default-features --lib
75
+
76
+ - name: Pre-publish QA — Python (build extension first)
77
+ run: |
78
+ uv run maturin develop --release
79
+ uv run ruff format --check python/kaos_graph tests
80
+ uv run ruff check python/kaos_graph tests
81
+ uv run ty check python/kaos_graph tests
82
+ uv run pytest -m "not live and not network and not slow" --no-cov tests/
83
+
84
+ - name: Build sdist
85
+ run: uv run maturin sdist --out dist
86
+
87
+ - name: Verify sdist metadata
88
+ run: uvx --from twine twine check --strict dist/*.tar.gz
89
+
90
+ - name: Upload sdist
91
+ uses: actions/upload-artifact@v7
92
+ with:
93
+ name: sdist
94
+ path: dist/*.tar.gz
95
+ retention-days: 30
96
+
97
+ # ────────────── Wheel matrix ──────────────
98
+ # 8 wheels per release, per D017 (docs/oss/30-rust-packaging/wheel-matrix.md):
99
+ # - Linux x86_64 manylinux + musllinux
100
+ # - Linux aarch64 manylinux + musllinux
101
+ # - macOS arm64
102
+ # - Windows x86_64
103
+ # - Windows arm64
104
+ # macOS x86_64 deliberately skipped (Apple ended Intel sales in 2023).
105
+ wheels:
106
+ name: ${{ matrix.target }} ${{ matrix.linux-tag && '(' || '' }}${{ matrix.linux-tag }}${{ matrix.linux-tag && ')' || '' }}
107
+ needs: sdist
108
+ strategy:
109
+ fail-fast: false
110
+ matrix:
111
+ include:
112
+ - os: ubuntu-latest
113
+ target: x86_64-unknown-linux-gnu
114
+ linux-tag: manylinux_2_28
115
+ manylinux: "2_28"
116
+ - os: ubuntu-latest
117
+ target: x86_64-unknown-linux-musl
118
+ linux-tag: musllinux_1_2
119
+ manylinux: musllinux_1_2
120
+ - os: ubuntu-24.04-arm
121
+ target: aarch64-unknown-linux-gnu
122
+ linux-tag: manylinux_2_28
123
+ manylinux: "2_28"
124
+ - os: ubuntu-24.04-arm
125
+ target: aarch64-unknown-linux-musl
126
+ linux-tag: musllinux_1_2
127
+ manylinux: musllinux_1_2
128
+ - os: macos-14
129
+ target: aarch64-apple-darwin
130
+ linux-tag: ""
131
+ manylinux: ""
132
+ - os: windows-latest
133
+ target: x86_64-pc-windows-msvc
134
+ linux-tag: ""
135
+ manylinux: ""
136
+ - os: windows-11-arm
137
+ target: aarch64-pc-windows-msvc
138
+ linux-tag: ""
139
+ manylinux: ""
140
+ runs-on: ${{ matrix.os }}
141
+ steps:
142
+ - name: Checkout
143
+ uses: actions/checkout@v6
144
+
145
+ - name: Set up Python (host)
146
+ uses: actions/setup-python@v6
147
+ with:
148
+ python-version: "3.13"
149
+
150
+ - name: Build wheel via PyO3/maturin-action
151
+ uses: PyO3/maturin-action@v1
152
+ with:
153
+ target: ${{ matrix.target }}
154
+ # Empty for macOS/Windows; "2_28" or "musllinux_1_2" for Linux.
155
+ manylinux: ${{ matrix.manylinux }}
156
+ args: --release --strip --out dist
157
+ # Don't restore from cache — release builds must be reproducible
158
+ # from a clean state (per docs/oss/30-rust-packaging/wheel-matrix.md).
159
+ rust-toolchain: stable
160
+ sccache: "true"
161
+
162
+ - name: Upload wheel
163
+ uses: actions/upload-artifact@v7
164
+ with:
165
+ name: wheels-${{ matrix.target }}-${{ matrix.linux-tag || matrix.os }}
166
+ path: dist/*.whl
167
+ retention-days: 30
168
+
169
+ # ────────────── Publish to PyPI via OIDC ──────────────
170
+ publish-pypi:
171
+ name: Publish to PyPI
172
+ needs: [sdist, wheels]
173
+ if: startsWith(github.ref, 'refs/tags/v')
174
+ runs-on: ubuntu-latest
175
+ environment:
176
+ name: pypi
177
+ url: https://pypi.org/p/kaos-graph
178
+ permissions:
179
+ id-token: write # OIDC for Trusted Publishing
180
+ steps:
181
+ - name: Download sdist
182
+ uses: actions/download-artifact@v8
183
+ with:
184
+ name: sdist
185
+ path: dist/
186
+
187
+ - name: Download all wheels
188
+ uses: actions/download-artifact@v8
189
+ with:
190
+ pattern: wheels-*
191
+ path: dist/
192
+ merge-multiple: true
193
+
194
+ - name: Show artifact set
195
+ run: ls -la dist/
196
+
197
+ - name: Publish to PyPI
198
+ uses: pypa/gh-action-pypi-publish@release/v1
199
+ with:
200
+ attestations: true
201
+ skip-existing: false
202
+
203
+ # ────────────── GitHub Release ──────────────
204
+ github-release:
205
+ name: Create GitHub Release
206
+ needs: [sdist, wheels]
207
+ if: startsWith(github.ref, 'refs/tags/v')
208
+ runs-on: ubuntu-latest
209
+ permissions:
210
+ contents: write
211
+ steps:
212
+ - name: Checkout
213
+ uses: actions/checkout@v6
214
+
215
+ - name: Download sdist
216
+ uses: actions/download-artifact@v8
217
+ with:
218
+ name: sdist
219
+ path: dist/
220
+
221
+ - name: Download all wheels
222
+ uses: actions/download-artifact@v8
223
+ with:
224
+ pattern: wheels-*
225
+ path: dist/
226
+ merge-multiple: true
227
+
228
+ - name: Create GitHub Release
229
+ env:
230
+ GH_TOKEN: ${{ github.token }}
231
+ run: |
232
+ VERSION=${GITHUB_REF_NAME#v}
233
+ PRERELEASE_FLAG=""
234
+ if [[ "$VERSION" == *a* || "$VERSION" == *b* || "$VERSION" == *rc* ]]; then
235
+ PRERELEASE_FLAG="--prerelease"
236
+ fi
237
+ gh release create "$GITHUB_REF_NAME" \
238
+ --title "kaos-graph $VERSION" \
239
+ --notes-from-tag \
240
+ $PRERELEASE_FLAG \
241
+ dist/*