graphcheck 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 (230) hide show
  1. graphcheck-0.1.0/.gitattributes +1 -0
  2. graphcheck-0.1.0/.github/CODEOWNERS +5 -0
  3. graphcheck-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
  4. graphcheck-0.1.0/.github/ISSUE_TEMPLATE/deliverable.md +15 -0
  5. graphcheck-0.1.0/.github/ISSUE_TEMPLATE/docs.md +19 -0
  6. graphcheck-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +18 -0
  7. graphcheck-0.1.0/.github/actions/graphcheck-action/README.md +94 -0
  8. graphcheck-0.1.0/.github/actions/graphcheck-action/action.yml +127 -0
  9. graphcheck-0.1.0/.github/actions/graphcheck-action/generate_profile.py +53 -0
  10. graphcheck-0.1.0/.github/actions/graphcheck-action/write_summary.py +102 -0
  11. graphcheck-0.1.0/.github/pull_request_template.md +16 -0
  12. graphcheck-0.1.0/.github/scripts/first-run.sh +43 -0
  13. graphcheck-0.1.0/.github/workflows/ci.yml +269 -0
  14. graphcheck-0.1.0/.github/workflows/graphcheck.yml +33 -0
  15. graphcheck-0.1.0/.github/workflows/release.yml +73 -0
  16. graphcheck-0.1.0/.gitignore +230 -0
  17. graphcheck-0.1.0/.gitmodules +3 -0
  18. graphcheck-0.1.0/.pre-commit-config.yaml +7 -0
  19. graphcheck-0.1.0/.python-version +1 -0
  20. graphcheck-0.1.0/CHANGELOG.md +330 -0
  21. graphcheck-0.1.0/CONTRIBUTING.md +95 -0
  22. graphcheck-0.1.0/LICENSE +202 -0
  23. graphcheck-0.1.0/PKG-INFO +187 -0
  24. graphcheck-0.1.0/README.md +153 -0
  25. graphcheck-0.1.0/docker-compose.yml +57 -0
  26. graphcheck-0.1.0/docs/agents.md +263 -0
  27. graphcheck-0.1.0/docs/check-reference.md +161 -0
  28. graphcheck-0.1.0/docs/ci-setup.md +89 -0
  29. graphcheck-0.1.0/docs/clean-machine-test-protocol.md +105 -0
  30. graphcheck-0.1.0/docs/compatibility.md +61 -0
  31. graphcheck-0.1.0/docs/docker-quickstart.md +79 -0
  32. graphcheck-0.1.0/docs/graphcheck-demo.gif +0 -0
  33. graphcheck-0.1.0/docs/llms.txt +93 -0
  34. graphcheck-0.1.0/docs/quickstart.md +27 -0
  35. graphcheck-0.1.0/docs/releasing.md +53 -0
  36. graphcheck-0.1.0/docs/samples/report-clean.html +1528 -0
  37. graphcheck-0.1.0/docs/samples/report-findings.html +1528 -0
  38. graphcheck-0.1.0/docs/specs/SPEC-01-results-json.md +166 -0
  39. graphcheck-0.1.0/docs/specs/SPEC-02-check-yaml.md +102 -0
  40. graphcheck-0.1.0/docs/specs/SPEC-03-connector.md +330 -0
  41. graphcheck-0.1.0/docs/specs/SPEC-04 Engine.md +687 -0
  42. graphcheck-0.1.0/docs/specs/SPEC-05-profiler-baseline.md +397 -0
  43. graphcheck-0.1.0/docs/specs/SPEC-06-scorer-html-report.md +445 -0
  44. graphcheck-0.1.0/docs/specs/SPEC-09-packs.md +393 -0
  45. graphcheck-0.1.0/docs/specs/SPEC-10-telemetry-events.md +790 -0
  46. graphcheck-0.1.0/docs/specs/SPEC-11-generate.md +1286 -0
  47. graphcheck-0.1.0/docs/specs/SPEC-12-observability-support +417 -0
  48. graphcheck-0.1.0/docs/specs/check.envelope.schema.json +404 -0
  49. graphcheck-0.1.0/docs/specs/check.schema.json +1130 -0
  50. graphcheck-0.1.0/docs/specs/pack.schema.json +604 -0
  51. graphcheck-0.1.0/docs/specs/profile.schema.json +419 -0
  52. graphcheck-0.1.0/docs/specs/results.schema.json +772 -0
  53. graphcheck-0.1.0/docs/telemetry.md +259 -0
  54. graphcheck-0.1.0/docs/troubleshooting.md +71 -0
  55. graphcheck-0.1.0/docs/user-guide.md +453 -0
  56. graphcheck-0.1.0/examples/checks/example.yml +20 -0
  57. graphcheck-0.1.0/graphcheck.yml +4 -0
  58. graphcheck-0.1.0/monitoring/README.md +71 -0
  59. graphcheck-0.1.0/monitoring/docker-compose.yml +35 -0
  60. graphcheck-0.1.0/monitoring/grafana/dashboards/graphcheck-database-health.json +229 -0
  61. graphcheck-0.1.0/monitoring/grafana/provisioning/dashboards/dashboards.yml +11 -0
  62. graphcheck-0.1.0/monitoring/grafana/provisioning/datasources/prometheus-datasource.yml +9 -0
  63. graphcheck-0.1.0/monitoring/prometheus/README.md +21 -0
  64. graphcheck-0.1.0/monitoring/prometheus/prometheus.yml +13 -0
  65. graphcheck-0.1.0/profiles.yml +15 -0
  66. graphcheck-0.1.0/pyproject.toml +91 -0
  67. graphcheck-0.1.0/scripts/generate_sample_reports.py +375 -0
  68. graphcheck-0.1.0/src/graphcheck/__init__.py +9 -0
  69. graphcheck-0.1.0/src/graphcheck/application/__init__.py +0 -0
  70. graphcheck-0.1.0/src/graphcheck/application/artifacts.py +145 -0
  71. graphcheck-0.1.0/src/graphcheck/application/paths.py +6 -0
  72. graphcheck-0.1.0/src/graphcheck/application/run.py +233 -0
  73. graphcheck-0.1.0/src/graphcheck/application/suites.py +44 -0
  74. graphcheck-0.1.0/src/graphcheck/baselines.py +228 -0
  75. graphcheck-0.1.0/src/graphcheck/bootstrap.py +19 -0
  76. graphcheck-0.1.0/src/graphcheck/cli.py +2339 -0
  77. graphcheck-0.1.0/src/graphcheck/connection_profiles.py +119 -0
  78. graphcheck-0.1.0/src/graphcheck/contracts/__init__.py +0 -0
  79. graphcheck-0.1.0/src/graphcheck/contracts/check.py +258 -0
  80. graphcheck-0.1.0/src/graphcheck/contracts/profile.py +290 -0
  81. graphcheck-0.1.0/src/graphcheck/contracts/results.py +409 -0
  82. graphcheck-0.1.0/src/graphcheck/contracts/scalars.py +23 -0
  83. graphcheck-0.1.0/src/graphcheck/contracts/schemas.py +140 -0
  84. graphcheck-0.1.0/src/graphcheck/debug_diagnostics.py +155 -0
  85. graphcheck-0.1.0/src/graphcheck/diff.py +420 -0
  86. graphcheck-0.1.0/src/graphcheck/engine/__init__.py +64 -0
  87. graphcheck-0.1.0/src/graphcheck/engine/baseline.py +262 -0
  88. graphcheck-0.1.0/src/graphcheck/engine/compiler.py +577 -0
  89. graphcheck-0.1.0/src/graphcheck/engine/core_pack.py +681 -0
  90. graphcheck-0.1.0/src/graphcheck/engine/evaluator.py +982 -0
  91. graphcheck-0.1.0/src/graphcheck/engine/executor.py +192 -0
  92. graphcheck-0.1.0/src/graphcheck/engine/identifiers.py +25 -0
  93. graphcheck-0.1.0/src/graphcheck/engine/parameters.py +99 -0
  94. graphcheck-0.1.0/src/graphcheck/engine/pii_pack.py +318 -0
  95. graphcheck-0.1.0/src/graphcheck/engine/runner.py +1798 -0
  96. graphcheck-0.1.0/src/graphcheck/engine/sampling.py +316 -0
  97. graphcheck-0.1.0/src/graphcheck/errors.py +69 -0
  98. graphcheck-0.1.0/src/graphcheck/generation/__init__.py +1 -0
  99. graphcheck-0.1.0/src/graphcheck/generation/client.py +383 -0
  100. graphcheck-0.1.0/src/graphcheck/generation/config.py +100 -0
  101. graphcheck-0.1.0/src/graphcheck/generation/disclosure.py +115 -0
  102. graphcheck-0.1.0/src/graphcheck/generation/prompts.py +112 -0
  103. graphcheck-0.1.0/src/graphcheck/generation/proposals.py +271 -0
  104. graphcheck-0.1.0/src/graphcheck/generation/service.py +379 -0
  105. graphcheck-0.1.0/src/graphcheck/generation/transmission.py +311 -0
  106. graphcheck-0.1.0/src/graphcheck/generation/writer.py +116 -0
  107. graphcheck-0.1.0/src/graphcheck/mcp/adapter.py +133 -0
  108. graphcheck-0.1.0/src/graphcheck/mcp/server.py +62 -0
  109. graphcheck-0.1.0/src/graphcheck/neo4j_adapter.py +1431 -0
  110. graphcheck-0.1.0/src/graphcheck/observability/__init__.py +13 -0
  111. graphcheck-0.1.0/src/graphcheck/observability/collector.py +31 -0
  112. graphcheck-0.1.0/src/graphcheck/observability/health.py +69 -0
  113. graphcheck-0.1.0/src/graphcheck/observability/metrics.py +34 -0
  114. graphcheck-0.1.0/src/graphcheck/observability/runner.py +28 -0
  115. graphcheck-0.1.0/src/graphcheck/observability/server.py +18 -0
  116. graphcheck-0.1.0/src/graphcheck/packs/__init__.py +213 -0
  117. graphcheck-0.1.0/src/graphcheck/packs/catalog.py +106 -0
  118. graphcheck-0.1.0/src/graphcheck/packs/core.yml +113 -0
  119. graphcheck-0.1.0/src/graphcheck/packs/metadata.py +329 -0
  120. graphcheck-0.1.0/src/graphcheck/packs/pii.yml +77 -0
  121. graphcheck-0.1.0/src/graphcheck/profiler.py +822 -0
  122. graphcheck-0.1.0/src/graphcheck/project.py +115 -0
  123. graphcheck-0.1.0/src/graphcheck/reporting/__init__.py +41 -0
  124. graphcheck-0.1.0/src/graphcheck/reporting/explorer.py +372 -0
  125. graphcheck-0.1.0/src/graphcheck/reporting/history.py +527 -0
  126. graphcheck-0.1.0/src/graphcheck/reporting/html.py +2239 -0
  127. graphcheck-0.1.0/src/graphcheck/reporting/presentation.py +156 -0
  128. graphcheck-0.1.0/src/graphcheck/reporting/redaction.py +321 -0
  129. graphcheck-0.1.0/src/graphcheck/reporting/writer.py +97 -0
  130. graphcheck-0.1.0/src/graphcheck/scoring.py +156 -0
  131. graphcheck-0.1.0/src/graphcheck/telemetry/__init__.py +28 -0
  132. graphcheck-0.1.0/src/graphcheck/telemetry/collector.py +296 -0
  133. graphcheck-0.1.0/src/graphcheck/telemetry/consent.py +208 -0
  134. graphcheck-0.1.0/src/graphcheck/telemetry/events.py +417 -0
  135. graphcheck-0.1.0/src/graphcheck/telemetry/inactive.py +130 -0
  136. graphcheck-0.1.0/src/graphcheck/telemetry/policy.py +769 -0
  137. graphcheck-0.1.0/src/graphcheck/telemetry/posthog.py +253 -0
  138. graphcheck-0.1.0/src/graphcheck/telemetry/release.py +10 -0
  139. graphcheck-0.1.0/src/graphcheck/telemetry/runtime.py +375 -0
  140. graphcheck-0.1.0/src/graphcheck/telemetry/types.py +222 -0
  141. graphcheck-0.1.0/src/graphcheck/yaml_loader.py +43 -0
  142. graphcheck-0.1.0/tests/__init__.py +1 -0
  143. graphcheck-0.1.0/tests/conftest.py +196 -0
  144. graphcheck-0.1.0/tests/contracts/fixtures/agent-suite.yml +20 -0
  145. graphcheck-0.1.0/tests/contracts/fixtures/baseline.json +114 -0
  146. graphcheck-0.1.0/tests/contracts/fixtures/results.clean.json +124 -0
  147. graphcheck-0.1.0/tests/contracts/fixtures/results.complete.json +191 -0
  148. graphcheck-0.1.0/tests/contracts/fixtures/results.complete.rendered.json +191 -0
  149. graphcheck-0.1.0/tests/contracts/fixtures/results.failed.json +39 -0
  150. graphcheck-0.1.0/tests/contracts/fixtures/results.generated-only.json +94 -0
  151. graphcheck-0.1.0/tests/contracts/fixtures/results.partial.json +122 -0
  152. graphcheck-0.1.0/tests/contracts/fixtures/suite.invalid-bad-expect.yml +6 -0
  153. graphcheck-0.1.0/tests/contracts/fixtures/suite.invalid-duplicate-key.yml +2 -0
  154. graphcheck-0.1.0/tests/contracts/fixtures/suite.invalid-unknown-check.yml +5 -0
  155. graphcheck-0.1.0/tests/contracts/fixtures/suite.invalid-unknown-key.yml +6 -0
  156. graphcheck-0.1.0/tests/contracts/fixtures/suite.valid.yml +21 -0
  157. graphcheck-0.1.0/tests/contracts/test_agents_example.py +13 -0
  158. graphcheck-0.1.0/tests/contracts/test_check_validation.py +646 -0
  159. graphcheck-0.1.0/tests/contracts/test_profile.py +305 -0
  160. graphcheck-0.1.0/tests/contracts/test_results.py +486 -0
  161. graphcheck-0.1.0/tests/engine/test_baseline.py +362 -0
  162. graphcheck-0.1.0/tests/engine/test_compiler.py +240 -0
  163. graphcheck-0.1.0/tests/engine/test_core_pack_compiler.py +363 -0
  164. graphcheck-0.1.0/tests/engine/test_evaluator.py +867 -0
  165. graphcheck-0.1.0/tests/engine/test_executor.py +153 -0
  166. graphcheck-0.1.0/tests/engine/test_identifiers.py +37 -0
  167. graphcheck-0.1.0/tests/engine/test_parameters.py +176 -0
  168. graphcheck-0.1.0/tests/engine/test_pii_pack_runtime.py +555 -0
  169. graphcheck-0.1.0/tests/engine/test_runner.py +1156 -0
  170. graphcheck-0.1.0/tests/engine/test_sampling.py +276 -0
  171. graphcheck-0.1.0/tests/engine/test_sampling_runner.py +177 -0
  172. graphcheck-0.1.0/tests/generation/test_client.py +548 -0
  173. graphcheck-0.1.0/tests/generation/test_generation.py +361 -0
  174. graphcheck-0.1.0/tests/generation/test_service.py +372 -0
  175. graphcheck-0.1.0/tests/integration/conftest.py +62 -0
  176. graphcheck-0.1.0/tests/integration/test_generate_integration.py +211 -0
  177. graphcheck-0.1.0/tests/integration/test_generate_live.py +56 -0
  178. graphcheck-0.1.0/tests/integration/test_integration_engine.py +279 -0
  179. graphcheck-0.1.0/tests/integration/test_integration_neo4j_adapter.py +280 -0
  180. graphcheck-0.1.0/tests/integration/test_mcp_agent_surface.py +57 -0
  181. graphcheck-0.1.0/tests/integration/test_mcp_failure_isolation.py +189 -0
  182. graphcheck-0.1.0/tests/integration/test_mcp_neo4j_workflow.py +78 -0
  183. graphcheck-0.1.0/tests/integration/test_performance_plans.py +235 -0
  184. graphcheck-0.1.0/tests/observability/__init__.py +1 -0
  185. graphcheck-0.1.0/tests/observability/test_collector.py +253 -0
  186. graphcheck-0.1.0/tests/observability/test_health.py +104 -0
  187. graphcheck-0.1.0/tests/observability/test_monitoring_config.py +89 -0
  188. graphcheck-0.1.0/tests/observability/test_runner.py +109 -0
  189. graphcheck-0.1.0/tests/observability/test_server.py +63 -0
  190. graphcheck-0.1.0/tests/performance/__init__.py +1 -0
  191. graphcheck-0.1.0/tests/performance/budgets.json +50 -0
  192. graphcheck-0.1.0/tests/performance/gates.py +118 -0
  193. graphcheck-0.1.0/tests/performance/helpers.py +322 -0
  194. graphcheck-0.1.0/tests/performance/test_cli_startup.py +138 -0
  195. graphcheck-0.1.0/tests/performance/test_engine_budget.py +335 -0
  196. graphcheck-0.1.0/tests/performance/test_measurement_helpers.py +126 -0
  197. graphcheck-0.1.0/tests/performance/test_memory_gates.py +119 -0
  198. graphcheck-0.1.0/tests/performance/test_regression_gates.py +76 -0
  199. graphcheck-0.1.0/tests/property/test_evaluator_properties.py +320 -0
  200. graphcheck-0.1.0/tests/telemetry/test_boundary_payloads.py +190 -0
  201. graphcheck-0.1.0/tests/telemetry/test_cli_boundary_integration.py +800 -0
  202. graphcheck-0.1.0/tests/telemetry/test_cli_controls.py +146 -0
  203. graphcheck-0.1.0/tests/telemetry/test_engine_events.py +302 -0
  204. graphcheck-0.1.0/tests/telemetry/test_events.py +171 -0
  205. graphcheck-0.1.0/tests/telemetry/test_policy.py +165 -0
  206. graphcheck-0.1.0/tests/telemetry/test_posthog.py +171 -0
  207. graphcheck-0.1.0/tests/telemetry/test_privacy_properties.py +54 -0
  208. graphcheck-0.1.0/tests/telemetry/test_transparency_doc.py +38 -0
  209. graphcheck-0.1.0/tests/test_baselines.py +215 -0
  210. graphcheck-0.1.0/tests/test_cli.py +1061 -0
  211. graphcheck-0.1.0/tests/test_cli_lazy_imports.py +187 -0
  212. graphcheck-0.1.0/tests/test_debug_diagnostics.py +223 -0
  213. graphcheck-0.1.0/tests/test_diff.py +303 -0
  214. graphcheck-0.1.0/tests/test_docker_quickstart.py +99 -0
  215. graphcheck-0.1.0/tests/test_generate_cli.py +86 -0
  216. graphcheck-0.1.0/tests/test_mcp_adapter.py +37 -0
  217. graphcheck-0.1.0/tests/test_monitor_cli.py +124 -0
  218. graphcheck-0.1.0/tests/test_neo4j_adapter.py +2174 -0
  219. graphcheck-0.1.0/tests/test_packs.py +607 -0
  220. graphcheck-0.1.0/tests/test_profiler.py +889 -0
  221. graphcheck-0.1.0/tests/test_project_profiles.py +255 -0
  222. graphcheck-0.1.0/tests/test_redaction.py +251 -0
  223. graphcheck-0.1.0/tests/test_report_explorer.py +255 -0
  224. graphcheck-0.1.0/tests/test_report_history.py +393 -0
  225. graphcheck-0.1.0/tests/test_reporting.py +1131 -0
  226. graphcheck-0.1.0/tests/test_result_presentation.py +187 -0
  227. graphcheck-0.1.0/tests/test_run_cli.py +1696 -0
  228. graphcheck-0.1.0/tests/test_sample_reports.py +293 -0
  229. graphcheck-0.1.0/tests/test_scoring.py +144 -0
  230. graphcheck-0.1.0/uv.lock +2291 -0
@@ -0,0 +1 @@
1
+ *.sh text eol=lf
@@ -0,0 +1,5 @@
1
+ * @ezhilvendhan
2
+ /docs/specs/ @ghilda-graphora @kev-graphora
3
+ /src/graphcheck/contracts/ @ghilda-graphora @kev-graphora
4
+ /tests/contracts/ @ghilda-graphora @kev-graphora
5
+ /tests/fixtures/ @jayachandra-bit @jananik-graphora
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: Bug report
3
+ about: Something isn't working as expected
4
+ labels: [bug]
5
+ ---
6
+
7
+ **Describe the bug**
8
+
9
+
10
+ **Steps to reproduce**
11
+
12
+ 1.
13
+ 2.
14
+ 3.
15
+
16
+ **Expected behavior**
17
+
18
+
19
+ **Actual behavior**
20
+
21
+
22
+ **Environment**
23
+
24
+ - GraphCheck version:
25
+ - Python version:
26
+ - OS:
27
+
28
+ ## Decision rights
29
+
30
+ Owner decides libraries within the component; the named reviewer approves. Cross-contract changes escalate to Ezhil (§13).
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: Week deliverable
3
+ about: A scoped deliverable with acceptance criteria
4
+ labels: []
5
+ ---
6
+
7
+ **Owner:**
8
+ **Milestone:**
9
+
10
+ ## Acceptance criteria
11
+
12
+
13
+ ## Decision rights
14
+
15
+ Owner decides libraries within the component; the named reviewer approves. Cross-contract changes escalate to Ezhil (§13).
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: Docs
3
+ about: Missing, unclear, or incorrect documentation
4
+ labels: [documentation]
5
+ ---
6
+
7
+ **Where**
8
+
9
+ (file/page/section)
10
+
11
+ **What's wrong or missing**
12
+
13
+
14
+ **Suggested fix**
15
+
16
+
17
+ ## Decision rights
18
+
19
+ Owner decides libraries within the component; the named reviewer approves. Cross-contract changes escalate to Ezhil (§13).
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: Feature request
3
+ about: Propose a new capability or enhancement
4
+ labels: [enhancement]
5
+ ---
6
+
7
+ **What problem does this solve?**
8
+
9
+
10
+ **Proposed solution**
11
+
12
+
13
+ **Alternatives considered**
14
+
15
+
16
+ ## Decision rights
17
+
18
+ Owner decides libraries within the component; the named reviewer approves. Cross-contract changes escalate to Ezhil (§13).
@@ -0,0 +1,94 @@
1
+ # GraphCheck Action
2
+
3
+ Run GraphCheck checks against your graph on every pull request, and get
4
+ a pass/fail summary posted straight to the PR's checks tab.
5
+
6
+ This is a thin wrapper around the GraphCheck CLI (graphcheck run) - it
7
+ adds no new checking behaviour of its own.
8
+
9
+ Not yet published to the GitHub Marketplace. Use the in-repo path shown
10
+ below; switch to graphora/graphcheck-action@v1 once it is extracted and
11
+ tagged.
12
+
13
+ ## Usage
14
+
15
+ ### Within this repository
16
+
17
+ - uses: ./.github/actions/graphcheck-action
18
+ with:
19
+ profile: ci
20
+ uri: bolt://localhost:7687
21
+ user: neo4j
22
+ database: neo4j
23
+ fail-fast: false
24
+ version: 0.1.0
25
+ env:
26
+ NEO4J_PASSWORD: ${{ secrets.NEO4J_PASSWORD }}
27
+
28
+ ### From another repository
29
+
30
+ Not yet extracted to its own repo, so pin to a commit SHA on this one:
31
+
32
+ - uses: graphora/graphcheck/.github/actions/graphcheck-action@COMMIT_SHA
33
+ with:
34
+ profile: ci
35
+ uri: bolt://localhost:7687
36
+ user: neo4j
37
+ database: neo4j
38
+ fail-fast: false
39
+ version: 0.1.0
40
+ env:
41
+ NEO4J_PASSWORD: ${{ secrets.NEO4J_PASSWORD }}
42
+
43
+ Switch to `graphora/graphcheck-action@v1` once this Action is extracted and tagged.
44
+
45
+ ## Inputs
46
+
47
+ | Input | Required | Default | Description |
48
+ |---|---|---|---|
49
+ | profile | no | ci | Profile name to generate and use, if profiles.yml does not already exist |
50
+ | uri | yes | - | Neo4j Bolt URI |
51
+ | user | yes | - | Neo4j username |
52
+ | database | no | neo4j | Neo4j database name |
53
+ | fail-fast | no | false | Stop after the first error-severity failure |
54
+ | suite | no | - | Suite name to run via --suite; skipped if empty |
55
+ | version | no | 0.1.0 | Exact GraphCheck version to install from PyPI |
56
+
57
+ ## What it does
58
+
59
+ 1. Installs the pinned GraphCheck version from PyPI.
60
+ 2. Resolves the artifacts directory from graphcheck.yml (defaults to
61
+ .graphcheck if not configured or the file is absent).
62
+ 3. If profiles.yml does not already exist, generates one using the
63
+ uri/user/database inputs. Only password_env: NEO4J_PASSWORD is
64
+ written - the real password is never in the generated file.
65
+ 4. Runs graphcheck run using the given profile. The Neo4j password is
66
+ read from the NEO4J_PASSWORD environment variable at runtime - set
67
+ it via a repo secret, never a plaintext input.
68
+ 5. Removes the generated profiles.yml (only if this Action created it).
69
+ 6. Captures the run's exit code. The job's final status matches this
70
+ exit code exactly (0 green; 1/2/3 red) - this is preserved even
71
+ though later steps always run.
72
+ 7. Uploads results.json and the HTML report as build artifacts, from
73
+ the resolved artifacts directory, whenever they were produced. If
74
+ an early failure produced none, the summary says so explicitly.
75
+ 8. Writes a pass/fail/errored/warn breakdown, read directly from
76
+ results.json (not inferred from the exit code), to the GitHub
77
+ Step Summary, including one evidence line per failing check.
78
+
79
+ ## Exit codes
80
+
81
+ | Exit | Meaning |
82
+ |---|---|
83
+ | 0 | Run completed, all checks passed or were skipped |
84
+ | 1 | A check failed, or an error-severity check errored |
85
+ | 2 | Incomplete coverage, or a warning |
86
+ | 3 | The run could not execute (bad config, no connection, setup failure) |
87
+
88
+ ## Notes
89
+
90
+ - This Action requires a graph reachable from the CI runner. Spinning
91
+ up a disposable Neo4j service for self-contained demo runs is not
92
+ yet supported.
93
+ - Not yet released: graphcheck itself is not published to PyPI, so the
94
+ install step will fail until [Release] ships it.
@@ -0,0 +1,127 @@
1
+ name: 'GraphCheck'
2
+ description: 'Run GraphCheck checks against your graph and report results in the PR.'
3
+
4
+ inputs:
5
+ profile:
6
+ description: 'Profile name to generate and use, if profiles.yml does not already exist'
7
+ required: false
8
+ default: 'ci'
9
+ uri:
10
+ description: 'Neo4j Bolt URI (e.g. bolt://localhost:7687)'
11
+ required: true
12
+ user:
13
+ description: 'Neo4j username'
14
+ required: true
15
+ database:
16
+ description: 'Neo4j database name'
17
+ required: false
18
+ default: 'neo4j'
19
+ fail-fast:
20
+ description: 'Stop on the first failing check'
21
+ required: false
22
+ default: 'false'
23
+ suite:
24
+ description: 'Run only this suite id (optional; runs all suites if not set)'
25
+ required: false
26
+ default: ''
27
+ version:
28
+ description: 'Exact GraphCheck version to install (e.g. 0.1.0)'
29
+ required: false
30
+ default: '0.1.0'
31
+
32
+ runs:
33
+ using: 'composite'
34
+ steps:
35
+ - name: Set up Python
36
+ if: inputs.version != ''
37
+ uses: actions/setup-python@v5
38
+ with:
39
+ python-version: '3.12'
40
+
41
+ - name: Install GraphCheck
42
+ shell: bash
43
+ env:
44
+ GC_VERSION: ${{ inputs.version }}
45
+ run: |
46
+ if [ -z "$GC_VERSION" ]; then
47
+ echo "version empty — using pre-installed graphcheck"
48
+ else
49
+ pip install "graphcheck==$GC_VERSION"
50
+ fi
51
+
52
+ - name: Resolve artifacts directory
53
+ shell: bash
54
+ run: |
55
+ if [ -f graphcheck.yml ]; then
56
+ dir=$(python -c "import yaml,sys; d=yaml.safe_load(open('graphcheck.yml')); print(d.get('artifacts','.graphcheck'))" 2>/dev/null || echo ".graphcheck")
57
+ else
58
+ dir=".graphcheck"
59
+ fi
60
+ if [[ "$dir" == *$'\n'* || "$dir" == *$'\r'* ]]; then
61
+ echo "::error::artifacts directory value from graphcheck.yml contains a line break; refusing to export it"
62
+ exit 1
63
+ fi
64
+ echo "GRAPHCHECK_ARTIFACTS_DIR=$dir" >> "$GITHUB_ENV"
65
+
66
+ - name: Generate profiles.yml
67
+ id: generate_profile
68
+ shell: bash
69
+ env:
70
+ GC_PROFILE: ${{ inputs.profile }}
71
+ GC_URI: ${{ inputs.uri }}
72
+ GC_USER: ${{ inputs.user }}
73
+ GC_DATABASE: ${{ inputs.database }}
74
+ run: python "${{ github.action_path }}/generate_profile.py"
75
+
76
+ - name: Run GraphCheck
77
+ shell: bash
78
+ id: graphcheck_run
79
+ env:
80
+ GC_PROFILE: ${{ inputs.profile }}
81
+ GC_FAIL_FAST: ${{ inputs.fail-fast }}
82
+ GC_SUITE: ${{ inputs.suite }}
83
+ run: |
84
+ set +e
85
+ args=(--profile "$GC_PROFILE")
86
+ [ -n "$GC_SUITE" ] && args+=(--suite "$GC_SUITE")
87
+ [ "$GC_FAIL_FAST" = "true" ] && args+=(--fail-fast)
88
+ graphcheck run "${args[@]}"
89
+ code=$?
90
+ echo "exit_code=$code" >> "$GITHUB_OUTPUT"
91
+ exit 0
92
+
93
+ - name: Clean up generated profiles.yml
94
+ if: always()
95
+ continue-on-error: true
96
+ shell: bash
97
+ env:
98
+ GENERATED_PROFILES: ${{ steps.generate_profile.outputs.generated_profiles }}
99
+ run: |
100
+ if [ "$GENERATED_PROFILES" = "true" ]; then
101
+ rm -f profiles.yml
102
+ echo "Removed generated profiles.yml"
103
+ fi
104
+
105
+ - name: Upload results
106
+ if: always()
107
+ continue-on-error: true
108
+ uses: actions/upload-artifact@v4
109
+ with:
110
+ name: graphcheck-results
111
+ path: |
112
+ ${{ env.GRAPHCHECK_ARTIFACTS_DIR }}/runs/latest/results.json
113
+ ${{ env.GRAPHCHECK_ARTIFACTS_DIR }}/runs/latest/report.html
114
+ if-no-files-found: ignore
115
+
116
+ - name: Write summary
117
+ if: always()
118
+ continue-on-error: true
119
+ shell: bash
120
+ run: python "${{ github.action_path }}/write_summary.py"
121
+ env:
122
+ GRAPHCHECK_ARTIFACTS_DIR: ${{ env.GRAPHCHECK_ARTIFACTS_DIR }}
123
+
124
+ - name: Set final job status
125
+ if: always()
126
+ shell: bash
127
+ run: exit ${{ steps.graphcheck_run.outputs.exit_code || 3 }}
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env python3
2
+ """Safely generates a minimal profiles.yml for CI use, via yaml.safe_dump
3
+ rather than hand-built shell text. Only writes if profiles.yml does not
4
+ already exist; the real password is never written, only the name of the
5
+ environment variable to read it from.
6
+ """
7
+
8
+ import os
9
+ import sys
10
+
11
+ try:
12
+ import yaml
13
+ except ImportError:
14
+ print("PyYAML is required to generate profiles.yml", file=sys.stderr)
15
+ sys.exit(1)
16
+
17
+
18
+ def main():
19
+ if os.path.exists("profiles.yml"):
20
+ print("profiles.yml already exists, using it as-is.")
21
+ return
22
+
23
+ profile = os.environ["GC_PROFILE"]
24
+ uri = os.environ["GC_URI"]
25
+ user = os.environ["GC_USER"]
26
+ database = os.environ["GC_DATABASE"]
27
+
28
+ data = {
29
+ "default": profile,
30
+ "profiles": {
31
+ profile: {
32
+ "uri": uri,
33
+ "user": user,
34
+ "password": None,
35
+ "password_env": "NEO4J_PASSWORD",
36
+ "database": database,
37
+ }
38
+ },
39
+ }
40
+
41
+ with open("profiles.yml", "w", encoding="utf-8") as f:
42
+ yaml.safe_dump(data, f, default_flow_style=False, sort_keys=False)
43
+
44
+ github_output = os.environ.get("GITHUB_OUTPUT")
45
+ if github_output:
46
+ with open(github_output, "a", encoding="utf-8") as f:
47
+ f.write("generated_profiles=true\n")
48
+
49
+ print("Generated profiles.yml")
50
+
51
+
52
+ if __name__ == "__main__":
53
+ main()
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env python3
2
+ """Reads GraphCheck's results.json and writes a pass/fail/error rollup
3
+ to the GitHub Step Summary. Never raises: if results.json is missing or
4
+ unreadable, it writes a clear "no results" message instead.
5
+ """
6
+
7
+ import json
8
+ import os
9
+
10
+ ARTIFACTS_DIR = os.environ.get("GRAPHCHECK_ARTIFACTS_DIR", ".graphcheck")
11
+ RESULTS_PATH = f"{ARTIFACTS_DIR}/runs/latest/results.json"
12
+
13
+
14
+ def main():
15
+ summary_path = os.environ.get("GITHUB_STEP_SUMMARY")
16
+ if not summary_path:
17
+ print("GITHUB_STEP_SUMMARY not set; nothing to write.")
18
+ return
19
+
20
+ lines = []
21
+
22
+ if not os.path.exists(RESULTS_PATH):
23
+ lines.append("## GraphCheck results\n")
24
+ lines.append(
25
+ "No results were produced. The run likely failed before it "
26
+ "could execute any checks (bad config, connection failure, "
27
+ "or setup/artifact error).\n"
28
+ )
29
+ _write(summary_path, lines)
30
+ return
31
+
32
+ try:
33
+ with open(RESULTS_PATH, encoding="utf-8") as f:
34
+ data = json.load(f)
35
+ except Exception as exc:
36
+ lines.append("## GraphCheck results\n")
37
+ lines.append(f"results.json exists but could not be read: {exc}\n")
38
+ _write(summary_path, lines)
39
+ return
40
+
41
+ totals = data.get("totals", {})
42
+ suites = data.get("suites", [])
43
+ checks = data.get("checks", [])
44
+ run = data.get("run", {})
45
+ score = data.get("score") or {}
46
+
47
+ lines.append("## GraphCheck results\n")
48
+ lines.append(
49
+ f"**Run status:** `{run.get('status', 'unknown')}` "
50
+ f" |  **Exit code:** `{run.get('exit_code', 'unknown')}` "
51
+ f" |  **Score:** {score.get('value', 'n/a')}\n"
52
+ )
53
+
54
+ lines.append(
55
+ f"**Totals:** {totals.get('checks', 0)} checks — "
56
+ f"{totals.get('pass', 0)} passed, "
57
+ f"{totals.get('fail', 0)} failed, "
58
+ f"{totals.get('errored', 0)} errored, "
59
+ f"{totals.get('warn', 0)} warned, "
60
+ f"{totals.get('skipped', 0)} skipped\n"
61
+ )
62
+
63
+ if suites:
64
+ lines.append("| Suite | Score | Pass | Fail | Errored | Warn | Skipped |")
65
+ lines.append("|---|---|---|---|---|---|---|")
66
+ for suite in suites:
67
+ t = suite.get("totals", {})
68
+ lines.append(
69
+ f"| {suite.get('id', '?')} | {suite.get('score', '?')} | "
70
+ f"{t.get('pass', 0)} | {t.get('fail', 0)} | "
71
+ f"{t.get('errored', 0)} | {t.get('warn', 0)} | "
72
+ f"{t.get('skipped', 0)} |"
73
+ )
74
+ lines.append("")
75
+
76
+ failing = [c for c in checks if c.get("verdict") in ("fail", "errored")]
77
+ if failing:
78
+ lines.append("### Failing / errored checks\n")
79
+ for c in failing:
80
+ evidence_msg = None
81
+ if c.get("evidence"):
82
+ evidence_msg = c["evidence"].get("message")
83
+ elif c.get("error"):
84
+ evidence_msg = c["error"].get("message")
85
+ evidence_msg = evidence_msg or "(no evidence message provided)"
86
+ lines.append(
87
+ f"- **{c.get('name', c.get('id', '?'))}** "
88
+ f"(`{c.get('suite_id', '?')}`, verdict: `{c.get('verdict')}`) "
89
+ f"— {evidence_msg}"
90
+ )
91
+ lines.append("")
92
+
93
+ _write(summary_path, lines)
94
+
95
+
96
+ def _write(path, lines):
97
+ with open(path, "a", encoding="utf-8") as f:
98
+ f.write("\n".join(lines) + "\n")
99
+
100
+
101
+ if __name__ == "__main__":
102
+ main()
@@ -0,0 +1,16 @@
1
+ ## What this changes
2
+
3
+
4
+ ## Definition of done
5
+
6
+ - [ ] Reviewed by the named CODEOWNER
7
+ - [ ] Unit tests written and passing locally + CI
8
+ - [ ] Integration tests pass against the fixture graph (where applicable)
9
+ - [ ] Coverage ≥ 80% on owned code
10
+ - [ ] No `print` / `console.log`; structured logger only
11
+ - [ ] No swallowed exceptions (`except: pass`)
12
+ - [ ] No un-issued TODOs
13
+ - [ ] Design detailed in `docs/specs/<name>.md` (where applicable)
14
+ - [ ] CHANGELOG.md entry
15
+ - [ ] No new third-party dependency without team review
16
+ - [ ] No agent meta-commentary or AI attribution anywhere
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ : "${GRAPHCHECK_BIN:?Set GRAPHCHECK_BIN to the isolated graphcheck executable}"
5
+ : "${GRAPHCHECK_PYTHON:?Set GRAPHCHECK_PYTHON to its Python executable}"
6
+
7
+ FIRST_RUN_DIR="${FIRST_RUN_DIR:-${RUNNER_TEMP:-/tmp}/graphcheck-first-run}"
8
+ mkdir -p "$FIRST_RUN_DIR"
9
+
10
+ ready=0
11
+ for ((attempt = 0; attempt < 60; attempt++)); do
12
+ if "$GRAPHCHECK_PYTHON" -c "from neo4j import GraphDatabase; d=GraphDatabase.driver('bolt://localhost:7687', auth=('neo4j', 'graphora'), connection_timeout=1); d.verify_connectivity(); d.close()" >/dev/null 2>&1; then
13
+ ready=1
14
+ break
15
+ fi
16
+ sleep 2
17
+ done
18
+ test "$ready" -eq 1
19
+
20
+ "$GRAPHCHECK_PYTHON" -c "from neo4j import GraphDatabase; d=GraphDatabase.driver('bolt://localhost:7687', auth=('neo4j', 'graphora')); d.execute_query(\"MERGE (:Customer {name: 'Ada'})\", database_='neo4j'); d.close()"
21
+
22
+ project="$FIRST_RUN_DIR/project"
23
+ mkdir -p "$project"
24
+ cd "$project"
25
+ started=$("$GRAPHCHECK_PYTHON" -c "import time; print(time.monotonic())")
26
+ "$GRAPHCHECK_BIN" init | tee init.log
27
+ "$GRAPHCHECK_BIN" profile | tee profile.log
28
+ "$GRAPHCHECK_BIN" run | tee run.log
29
+ elapsed=$("$GRAPHCHECK_PYTHON" -c "import time; print(time.monotonic() - $started)")
30
+
31
+ grep -q "Detected Neo4j" init.log
32
+ test -f graphcheck.yml
33
+ test -f profiles.yml
34
+ test -f checks/example.yml
35
+ baseline_found=0
36
+ for baseline in .graphcheck/baselines/*.json; do
37
+ test -f "$baseline" && baseline_found=1 && break
38
+ done
39
+ test "$baseline_found" -eq 1
40
+ test -f .graphcheck/runs/latest/results.json
41
+ test -f .graphcheck/runs/latest/report.html
42
+ "$GRAPHCHECK_PYTHON" -c "import json, pathlib; p=json.loads(pathlib.Path('.graphcheck/runs/latest/results.json').read_text()); assert p['run']['status']=='complete', p['run']; assert p['run']['exit_code']==0, p['run']; assert 0 <= float('$elapsed') < 600, 'first-run flow took $elapsed seconds'"
43
+ printf 'init -> profile -> run completed in %.3f seconds (budget: <600 seconds)\n' "$elapsed" | tee timing.txt