-
Notifications
You must be signed in to change notification settings - Fork 18
Verify the graph-node API schema snapshot in CI #2854
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" | ||
| snapshot=crates/subgraph/schema/raindex.graphql | ||
| generated=raindex.graphql.generated | ||
| subgraph_name=rain/raindex | ||
|
|
||
| # Deployed in place, from the manifest as committed. Nothing here passes | ||
| # `graph build --network`: subgraph.yaml already carries its network, address | ||
| # and startBlock, and `--network` is what would write a networks.json entry | ||
| # back into it. | ||
| cd "$root/subgraph" | ||
| npm ci | ||
| graph build | ||
| graph create --node http://localhost:8020/ "$subgraph_name" | ||
| graph deploy \ | ||
| --node http://localhost:8020/ \ | ||
| --ipfs http://localhost:5001 \ | ||
| --version-label ci \ | ||
| "$subgraph_name" | ||
|
|
||
| # graph-node derives the API schema at deploy time but refuses queries until | ||
| # the deployment has ingested a block, so print-api-schema.js retries. Anvil's | ||
| # block 0 is the whole of what has to be ingested. | ||
| node ./print-api-schema.js "http://localhost:8000/subgraphs/name/$subgraph_name" \ | ||
| > "$root/$generated" | ||
|
|
||
| cd "$root" | ||
| if ! diff -u "$snapshot" "$generated"; then | ||
| echo "$snapshot is not the API schema graph-node derives from subgraph/schema.graphql." >&2 | ||
| echo "Copy $generated (uploaded by this job as an artifact) over it." >&2 | ||
| exit 1 | ||
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # A SEPARATE compose file from ./docker-compose.yml: rainix's `subgraph-test` | ||
| # runs `docker compose up --abort-on-container-exit` over the default file in | ||
| # this directory, so these long-lived services must not land in it. | ||
| services: | ||
| postgres: | ||
| image: postgres:14 | ||
| command: postgres -cshared_preload_libraries=pg_stat_statements | ||
| environment: | ||
| POSTGRES_USER: graph-node | ||
| POSTGRES_PASSWORD: let-me-in | ||
| POSTGRES_DB: graph-node | ||
| POSTGRES_INITDB_ARGS: -E UTF8 --locale=C | ||
| healthcheck: | ||
| test: pg_isready -U graph-node | ||
| interval: 2s | ||
| timeout: 5s | ||
| retries: 30 | ||
| ipfs: | ||
| image: ipfs/kubo:v0.17.0 | ||
| ports: | ||
| - 5001:5001 | ||
| graph-node: | ||
| image: graphprotocol/graph-node:v0.35.1 | ||
| depends_on: | ||
| postgres: | ||
| condition: service_healthy | ||
| ipfs: | ||
| condition: service_started | ||
|
Comment on lines
+18
to
+28
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win Wait for the IPFS API before graph-node deployment.
🤖 Prompt for AI Agents |
||
| # graph-node will not accept a deployment naming a chain it has no adapter | ||
| # for, so anvil runs on the host and is reached over the docker gateway. | ||
| extra_hosts: | ||
| - host.docker.internal:host-gateway | ||
| ports: | ||
| - 8000:8000 | ||
| - 8020:8020 | ||
| - 8030:8030 | ||
| environment: | ||
| postgres_host: postgres | ||
| postgres_user: graph-node | ||
| postgres_pass: let-me-in | ||
| postgres_db: graph-node | ||
| ipfs: ipfs:5001 | ||
| # Named for the network the committed manifest declares, so the manifest | ||
| # deploys as committed and nothing has to rewrite it. | ||
| ethereum: sepolia:http://host.docker.internal:8545 | ||
| GRAPH_LOG: info | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| const { | ||
| buildClientSchema, | ||
| getIntrospectionQuery, | ||
| lexicographicSortSchema, | ||
| printSchema, | ||
| } = require("graphql"); | ||
|
|
||
| const endpoint = process.argv[2]; | ||
| const attempts = 30; | ||
| const delayMs = 2000; | ||
| const timeoutMs = 10000; | ||
|
|
||
| async function introspect() { | ||
| const response = await fetch(endpoint, { | ||
| method: "POST", | ||
| headers: { "content-type": "application/json" }, | ||
| body: JSON.stringify({ query: getIntrospectionQuery() }), | ||
| // Bounded, because a graph-node that accepts the connection and then | ||
| // stalls would hang this await and the retry below would never run. The | ||
| // signal covers reading the body, not just the headers. | ||
| signal: AbortSignal.timeout(timeoutMs), | ||
| }); | ||
| if (!response.ok) { | ||
| throw new Error(`HTTP ${response.status}`); | ||
| } | ||
| const body = await response.json(); | ||
| if (body.errors) { | ||
| throw new Error(JSON.stringify(body.errors)); | ||
| } | ||
| return body.data; | ||
| } | ||
|
|
||
| async function main() { | ||
| if (!endpoint) { | ||
| throw new Error("usage: print-api-schema.js <graphql endpoint>"); | ||
| } | ||
| let last; | ||
| for (let attempt = 1; attempt <= attempts; attempt++) { | ||
| try { | ||
| const introspection = await introspect(); | ||
| // Sorted, because the order introspection reports types and fields in is | ||
| // graph-node's own and is not what the snapshot is asserting. | ||
| process.stdout.write( | ||
| printSchema(lexicographicSortSchema(buildClientSchema(introspection))), | ||
| ); | ||
| return; | ||
| } catch (error) { | ||
| last = error; | ||
| console.error( | ||
| `${endpoint}: attempt ${attempt}/${attempts}: ${error.message}`, | ||
| ); | ||
| await new Promise((resolve) => setTimeout(resolve, delayMs)); | ||
| } | ||
| } | ||
| throw last; | ||
| } | ||
|
|
||
| main().catch((error) => { | ||
| console.error(error.message); | ||
| process.exit(1); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Run the required artifact regeneration before the drift check.
Add the
rainix-copy-artifactsstep sequence innix develop github:rainlanguage/rainix#sol-shellbefore this assertion. Then check all generated artifacts, not onlysubgraph/, for uncommitted drift.As per coding guidelines: “Regenerate artifacts — run the
rainix-copy-artifactsstep sequence ... Stage ALL changed artifacts orcopy-artifactsdrifts red.”🧰 Tools
🪛 zizmor (1.29.0)
[warning] 1-53: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
[warning] 13-53: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
🤖 Prompt for AI Agents
Source: Coding guidelines