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.
- graphcheck-0.1.0/.gitattributes +1 -0
- graphcheck-0.1.0/.github/CODEOWNERS +5 -0
- graphcheck-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
- graphcheck-0.1.0/.github/ISSUE_TEMPLATE/deliverable.md +15 -0
- graphcheck-0.1.0/.github/ISSUE_TEMPLATE/docs.md +19 -0
- graphcheck-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +18 -0
- graphcheck-0.1.0/.github/actions/graphcheck-action/README.md +94 -0
- graphcheck-0.1.0/.github/actions/graphcheck-action/action.yml +127 -0
- graphcheck-0.1.0/.github/actions/graphcheck-action/generate_profile.py +53 -0
- graphcheck-0.1.0/.github/actions/graphcheck-action/write_summary.py +102 -0
- graphcheck-0.1.0/.github/pull_request_template.md +16 -0
- graphcheck-0.1.0/.github/scripts/first-run.sh +43 -0
- graphcheck-0.1.0/.github/workflows/ci.yml +269 -0
- graphcheck-0.1.0/.github/workflows/graphcheck.yml +33 -0
- graphcheck-0.1.0/.github/workflows/release.yml +73 -0
- graphcheck-0.1.0/.gitignore +230 -0
- graphcheck-0.1.0/.gitmodules +3 -0
- graphcheck-0.1.0/.pre-commit-config.yaml +7 -0
- graphcheck-0.1.0/.python-version +1 -0
- graphcheck-0.1.0/CHANGELOG.md +330 -0
- graphcheck-0.1.0/CONTRIBUTING.md +95 -0
- graphcheck-0.1.0/LICENSE +202 -0
- graphcheck-0.1.0/PKG-INFO +187 -0
- graphcheck-0.1.0/README.md +153 -0
- graphcheck-0.1.0/docker-compose.yml +57 -0
- graphcheck-0.1.0/docs/agents.md +263 -0
- graphcheck-0.1.0/docs/check-reference.md +161 -0
- graphcheck-0.1.0/docs/ci-setup.md +89 -0
- graphcheck-0.1.0/docs/clean-machine-test-protocol.md +105 -0
- graphcheck-0.1.0/docs/compatibility.md +61 -0
- graphcheck-0.1.0/docs/docker-quickstart.md +79 -0
- graphcheck-0.1.0/docs/graphcheck-demo.gif +0 -0
- graphcheck-0.1.0/docs/llms.txt +93 -0
- graphcheck-0.1.0/docs/quickstart.md +27 -0
- graphcheck-0.1.0/docs/releasing.md +53 -0
- graphcheck-0.1.0/docs/samples/report-clean.html +1528 -0
- graphcheck-0.1.0/docs/samples/report-findings.html +1528 -0
- graphcheck-0.1.0/docs/specs/SPEC-01-results-json.md +166 -0
- graphcheck-0.1.0/docs/specs/SPEC-02-check-yaml.md +102 -0
- graphcheck-0.1.0/docs/specs/SPEC-03-connector.md +330 -0
- graphcheck-0.1.0/docs/specs/SPEC-04 Engine.md +687 -0
- graphcheck-0.1.0/docs/specs/SPEC-05-profiler-baseline.md +397 -0
- graphcheck-0.1.0/docs/specs/SPEC-06-scorer-html-report.md +445 -0
- graphcheck-0.1.0/docs/specs/SPEC-09-packs.md +393 -0
- graphcheck-0.1.0/docs/specs/SPEC-10-telemetry-events.md +790 -0
- graphcheck-0.1.0/docs/specs/SPEC-11-generate.md +1286 -0
- graphcheck-0.1.0/docs/specs/SPEC-12-observability-support +417 -0
- graphcheck-0.1.0/docs/specs/check.envelope.schema.json +404 -0
- graphcheck-0.1.0/docs/specs/check.schema.json +1130 -0
- graphcheck-0.1.0/docs/specs/pack.schema.json +604 -0
- graphcheck-0.1.0/docs/specs/profile.schema.json +419 -0
- graphcheck-0.1.0/docs/specs/results.schema.json +772 -0
- graphcheck-0.1.0/docs/telemetry.md +259 -0
- graphcheck-0.1.0/docs/troubleshooting.md +71 -0
- graphcheck-0.1.0/docs/user-guide.md +453 -0
- graphcheck-0.1.0/examples/checks/example.yml +20 -0
- graphcheck-0.1.0/graphcheck.yml +4 -0
- graphcheck-0.1.0/monitoring/README.md +71 -0
- graphcheck-0.1.0/monitoring/docker-compose.yml +35 -0
- graphcheck-0.1.0/monitoring/grafana/dashboards/graphcheck-database-health.json +229 -0
- graphcheck-0.1.0/monitoring/grafana/provisioning/dashboards/dashboards.yml +11 -0
- graphcheck-0.1.0/monitoring/grafana/provisioning/datasources/prometheus-datasource.yml +9 -0
- graphcheck-0.1.0/monitoring/prometheus/README.md +21 -0
- graphcheck-0.1.0/monitoring/prometheus/prometheus.yml +13 -0
- graphcheck-0.1.0/profiles.yml +15 -0
- graphcheck-0.1.0/pyproject.toml +91 -0
- graphcheck-0.1.0/scripts/generate_sample_reports.py +375 -0
- graphcheck-0.1.0/src/graphcheck/__init__.py +9 -0
- graphcheck-0.1.0/src/graphcheck/application/__init__.py +0 -0
- graphcheck-0.1.0/src/graphcheck/application/artifacts.py +145 -0
- graphcheck-0.1.0/src/graphcheck/application/paths.py +6 -0
- graphcheck-0.1.0/src/graphcheck/application/run.py +233 -0
- graphcheck-0.1.0/src/graphcheck/application/suites.py +44 -0
- graphcheck-0.1.0/src/graphcheck/baselines.py +228 -0
- graphcheck-0.1.0/src/graphcheck/bootstrap.py +19 -0
- graphcheck-0.1.0/src/graphcheck/cli.py +2339 -0
- graphcheck-0.1.0/src/graphcheck/connection_profiles.py +119 -0
- graphcheck-0.1.0/src/graphcheck/contracts/__init__.py +0 -0
- graphcheck-0.1.0/src/graphcheck/contracts/check.py +258 -0
- graphcheck-0.1.0/src/graphcheck/contracts/profile.py +290 -0
- graphcheck-0.1.0/src/graphcheck/contracts/results.py +409 -0
- graphcheck-0.1.0/src/graphcheck/contracts/scalars.py +23 -0
- graphcheck-0.1.0/src/graphcheck/contracts/schemas.py +140 -0
- graphcheck-0.1.0/src/graphcheck/debug_diagnostics.py +155 -0
- graphcheck-0.1.0/src/graphcheck/diff.py +420 -0
- graphcheck-0.1.0/src/graphcheck/engine/__init__.py +64 -0
- graphcheck-0.1.0/src/graphcheck/engine/baseline.py +262 -0
- graphcheck-0.1.0/src/graphcheck/engine/compiler.py +577 -0
- graphcheck-0.1.0/src/graphcheck/engine/core_pack.py +681 -0
- graphcheck-0.1.0/src/graphcheck/engine/evaluator.py +982 -0
- graphcheck-0.1.0/src/graphcheck/engine/executor.py +192 -0
- graphcheck-0.1.0/src/graphcheck/engine/identifiers.py +25 -0
- graphcheck-0.1.0/src/graphcheck/engine/parameters.py +99 -0
- graphcheck-0.1.0/src/graphcheck/engine/pii_pack.py +318 -0
- graphcheck-0.1.0/src/graphcheck/engine/runner.py +1798 -0
- graphcheck-0.1.0/src/graphcheck/engine/sampling.py +316 -0
- graphcheck-0.1.0/src/graphcheck/errors.py +69 -0
- graphcheck-0.1.0/src/graphcheck/generation/__init__.py +1 -0
- graphcheck-0.1.0/src/graphcheck/generation/client.py +383 -0
- graphcheck-0.1.0/src/graphcheck/generation/config.py +100 -0
- graphcheck-0.1.0/src/graphcheck/generation/disclosure.py +115 -0
- graphcheck-0.1.0/src/graphcheck/generation/prompts.py +112 -0
- graphcheck-0.1.0/src/graphcheck/generation/proposals.py +271 -0
- graphcheck-0.1.0/src/graphcheck/generation/service.py +379 -0
- graphcheck-0.1.0/src/graphcheck/generation/transmission.py +311 -0
- graphcheck-0.1.0/src/graphcheck/generation/writer.py +116 -0
- graphcheck-0.1.0/src/graphcheck/mcp/adapter.py +133 -0
- graphcheck-0.1.0/src/graphcheck/mcp/server.py +62 -0
- graphcheck-0.1.0/src/graphcheck/neo4j_adapter.py +1431 -0
- graphcheck-0.1.0/src/graphcheck/observability/__init__.py +13 -0
- graphcheck-0.1.0/src/graphcheck/observability/collector.py +31 -0
- graphcheck-0.1.0/src/graphcheck/observability/health.py +69 -0
- graphcheck-0.1.0/src/graphcheck/observability/metrics.py +34 -0
- graphcheck-0.1.0/src/graphcheck/observability/runner.py +28 -0
- graphcheck-0.1.0/src/graphcheck/observability/server.py +18 -0
- graphcheck-0.1.0/src/graphcheck/packs/__init__.py +213 -0
- graphcheck-0.1.0/src/graphcheck/packs/catalog.py +106 -0
- graphcheck-0.1.0/src/graphcheck/packs/core.yml +113 -0
- graphcheck-0.1.0/src/graphcheck/packs/metadata.py +329 -0
- graphcheck-0.1.0/src/graphcheck/packs/pii.yml +77 -0
- graphcheck-0.1.0/src/graphcheck/profiler.py +822 -0
- graphcheck-0.1.0/src/graphcheck/project.py +115 -0
- graphcheck-0.1.0/src/graphcheck/reporting/__init__.py +41 -0
- graphcheck-0.1.0/src/graphcheck/reporting/explorer.py +372 -0
- graphcheck-0.1.0/src/graphcheck/reporting/history.py +527 -0
- graphcheck-0.1.0/src/graphcheck/reporting/html.py +2239 -0
- graphcheck-0.1.0/src/graphcheck/reporting/presentation.py +156 -0
- graphcheck-0.1.0/src/graphcheck/reporting/redaction.py +321 -0
- graphcheck-0.1.0/src/graphcheck/reporting/writer.py +97 -0
- graphcheck-0.1.0/src/graphcheck/scoring.py +156 -0
- graphcheck-0.1.0/src/graphcheck/telemetry/__init__.py +28 -0
- graphcheck-0.1.0/src/graphcheck/telemetry/collector.py +296 -0
- graphcheck-0.1.0/src/graphcheck/telemetry/consent.py +208 -0
- graphcheck-0.1.0/src/graphcheck/telemetry/events.py +417 -0
- graphcheck-0.1.0/src/graphcheck/telemetry/inactive.py +130 -0
- graphcheck-0.1.0/src/graphcheck/telemetry/policy.py +769 -0
- graphcheck-0.1.0/src/graphcheck/telemetry/posthog.py +253 -0
- graphcheck-0.1.0/src/graphcheck/telemetry/release.py +10 -0
- graphcheck-0.1.0/src/graphcheck/telemetry/runtime.py +375 -0
- graphcheck-0.1.0/src/graphcheck/telemetry/types.py +222 -0
- graphcheck-0.1.0/src/graphcheck/yaml_loader.py +43 -0
- graphcheck-0.1.0/tests/__init__.py +1 -0
- graphcheck-0.1.0/tests/conftest.py +196 -0
- graphcheck-0.1.0/tests/contracts/fixtures/agent-suite.yml +20 -0
- graphcheck-0.1.0/tests/contracts/fixtures/baseline.json +114 -0
- graphcheck-0.1.0/tests/contracts/fixtures/results.clean.json +124 -0
- graphcheck-0.1.0/tests/contracts/fixtures/results.complete.json +191 -0
- graphcheck-0.1.0/tests/contracts/fixtures/results.complete.rendered.json +191 -0
- graphcheck-0.1.0/tests/contracts/fixtures/results.failed.json +39 -0
- graphcheck-0.1.0/tests/contracts/fixtures/results.generated-only.json +94 -0
- graphcheck-0.1.0/tests/contracts/fixtures/results.partial.json +122 -0
- graphcheck-0.1.0/tests/contracts/fixtures/suite.invalid-bad-expect.yml +6 -0
- graphcheck-0.1.0/tests/contracts/fixtures/suite.invalid-duplicate-key.yml +2 -0
- graphcheck-0.1.0/tests/contracts/fixtures/suite.invalid-unknown-check.yml +5 -0
- graphcheck-0.1.0/tests/contracts/fixtures/suite.invalid-unknown-key.yml +6 -0
- graphcheck-0.1.0/tests/contracts/fixtures/suite.valid.yml +21 -0
- graphcheck-0.1.0/tests/contracts/test_agents_example.py +13 -0
- graphcheck-0.1.0/tests/contracts/test_check_validation.py +646 -0
- graphcheck-0.1.0/tests/contracts/test_profile.py +305 -0
- graphcheck-0.1.0/tests/contracts/test_results.py +486 -0
- graphcheck-0.1.0/tests/engine/test_baseline.py +362 -0
- graphcheck-0.1.0/tests/engine/test_compiler.py +240 -0
- graphcheck-0.1.0/tests/engine/test_core_pack_compiler.py +363 -0
- graphcheck-0.1.0/tests/engine/test_evaluator.py +867 -0
- graphcheck-0.1.0/tests/engine/test_executor.py +153 -0
- graphcheck-0.1.0/tests/engine/test_identifiers.py +37 -0
- graphcheck-0.1.0/tests/engine/test_parameters.py +176 -0
- graphcheck-0.1.0/tests/engine/test_pii_pack_runtime.py +555 -0
- graphcheck-0.1.0/tests/engine/test_runner.py +1156 -0
- graphcheck-0.1.0/tests/engine/test_sampling.py +276 -0
- graphcheck-0.1.0/tests/engine/test_sampling_runner.py +177 -0
- graphcheck-0.1.0/tests/generation/test_client.py +548 -0
- graphcheck-0.1.0/tests/generation/test_generation.py +361 -0
- graphcheck-0.1.0/tests/generation/test_service.py +372 -0
- graphcheck-0.1.0/tests/integration/conftest.py +62 -0
- graphcheck-0.1.0/tests/integration/test_generate_integration.py +211 -0
- graphcheck-0.1.0/tests/integration/test_generate_live.py +56 -0
- graphcheck-0.1.0/tests/integration/test_integration_engine.py +279 -0
- graphcheck-0.1.0/tests/integration/test_integration_neo4j_adapter.py +280 -0
- graphcheck-0.1.0/tests/integration/test_mcp_agent_surface.py +57 -0
- graphcheck-0.1.0/tests/integration/test_mcp_failure_isolation.py +189 -0
- graphcheck-0.1.0/tests/integration/test_mcp_neo4j_workflow.py +78 -0
- graphcheck-0.1.0/tests/integration/test_performance_plans.py +235 -0
- graphcheck-0.1.0/tests/observability/__init__.py +1 -0
- graphcheck-0.1.0/tests/observability/test_collector.py +253 -0
- graphcheck-0.1.0/tests/observability/test_health.py +104 -0
- graphcheck-0.1.0/tests/observability/test_monitoring_config.py +89 -0
- graphcheck-0.1.0/tests/observability/test_runner.py +109 -0
- graphcheck-0.1.0/tests/observability/test_server.py +63 -0
- graphcheck-0.1.0/tests/performance/__init__.py +1 -0
- graphcheck-0.1.0/tests/performance/budgets.json +50 -0
- graphcheck-0.1.0/tests/performance/gates.py +118 -0
- graphcheck-0.1.0/tests/performance/helpers.py +322 -0
- graphcheck-0.1.0/tests/performance/test_cli_startup.py +138 -0
- graphcheck-0.1.0/tests/performance/test_engine_budget.py +335 -0
- graphcheck-0.1.0/tests/performance/test_measurement_helpers.py +126 -0
- graphcheck-0.1.0/tests/performance/test_memory_gates.py +119 -0
- graphcheck-0.1.0/tests/performance/test_regression_gates.py +76 -0
- graphcheck-0.1.0/tests/property/test_evaluator_properties.py +320 -0
- graphcheck-0.1.0/tests/telemetry/test_boundary_payloads.py +190 -0
- graphcheck-0.1.0/tests/telemetry/test_cli_boundary_integration.py +800 -0
- graphcheck-0.1.0/tests/telemetry/test_cli_controls.py +146 -0
- graphcheck-0.1.0/tests/telemetry/test_engine_events.py +302 -0
- graphcheck-0.1.0/tests/telemetry/test_events.py +171 -0
- graphcheck-0.1.0/tests/telemetry/test_policy.py +165 -0
- graphcheck-0.1.0/tests/telemetry/test_posthog.py +171 -0
- graphcheck-0.1.0/tests/telemetry/test_privacy_properties.py +54 -0
- graphcheck-0.1.0/tests/telemetry/test_transparency_doc.py +38 -0
- graphcheck-0.1.0/tests/test_baselines.py +215 -0
- graphcheck-0.1.0/tests/test_cli.py +1061 -0
- graphcheck-0.1.0/tests/test_cli_lazy_imports.py +187 -0
- graphcheck-0.1.0/tests/test_debug_diagnostics.py +223 -0
- graphcheck-0.1.0/tests/test_diff.py +303 -0
- graphcheck-0.1.0/tests/test_docker_quickstart.py +99 -0
- graphcheck-0.1.0/tests/test_generate_cli.py +86 -0
- graphcheck-0.1.0/tests/test_mcp_adapter.py +37 -0
- graphcheck-0.1.0/tests/test_monitor_cli.py +124 -0
- graphcheck-0.1.0/tests/test_neo4j_adapter.py +2174 -0
- graphcheck-0.1.0/tests/test_packs.py +607 -0
- graphcheck-0.1.0/tests/test_profiler.py +889 -0
- graphcheck-0.1.0/tests/test_project_profiles.py +255 -0
- graphcheck-0.1.0/tests/test_redaction.py +251 -0
- graphcheck-0.1.0/tests/test_report_explorer.py +255 -0
- graphcheck-0.1.0/tests/test_report_history.py +393 -0
- graphcheck-0.1.0/tests/test_reporting.py +1131 -0
- graphcheck-0.1.0/tests/test_result_presentation.py +187 -0
- graphcheck-0.1.0/tests/test_run_cli.py +1696 -0
- graphcheck-0.1.0/tests/test_sample_reports.py +293 -0
- graphcheck-0.1.0/tests/test_scoring.py +144 -0
- graphcheck-0.1.0/uv.lock +2291 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*.sh text eol=lf
|
|
@@ -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
|