Skip to content

feat(scripts): validate required fields in deployment manifests - #249

Merged
karagozemin merged 1 commit into
karagozemin:masterfrom
egekoca:codex/issue-234-validate-manifest-required-fields
Aug 31, 2026
Merged

feat(scripts): validate required fields in deployment manifests#249
karagozemin merged 1 commit into
karagozemin:masterfrom
egekoca:codex/issue-234-validate-manifest-required-fields

Conversation

@egekoca

@egekoca egekoca commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Closes #234

The gap

scripts/validate-deployments.mjs already checked network and contract identifiers, but the staking-asset block was validated only when present:

if (stellar.resolverRegistryConfig != null && v.object(...)) { /* checks */ }

So a manifest that named no asset at all passed. Deleting resolverRegistryConfig from deployments.testnet.json — removing the stake asset contract, the minimum stake, the admin, and the slash beneficiary — and running the validator on master:

Validated 1 deployment file(s): deployments.testnet.json
exit=0

The resolver registry cannot run without those identifiers, and the manifest is the only place they are recorded.

There were also no tests. The rules lived inside a script that calls main() on import and reads from disk, so they could not be exercised without a real file on the filesystem.

The change

Asset identifiers are required. resolverRegistryConfig must be present, and stakeAsset, minStake, minStakeXLM, admin, and slashBeneficiary are each checked by name. The same manifest now reports:

Deployment validation failed:
- deployments.testnet.json:$.stellar.resolverRegistryConfig is required: it names the staking asset and minimum stake
exit=1

The rules are now testable. They move into scripts/lib/deployment-manifest.mjs, a read-only module that takes an already parsed manifest and touches neither the filesystem nor the network:

validateDeploymentManifest(manifest, { expectedNetwork }) // → { ok, errors: [{ path, message }] }

validate-deployments.mjs keeps its exact CLI behaviour and now only finds files, parses them, and prints what the validator reports.

One error per field. A field that already failed its type check is no longer reported a second time for its network-specific value — a missing chainId used to produce both "must be a finite number" and "must be 11155111 for testnet deployments". The output now reads as a checklist of what to fix.

The shipped deployments.testnet.json is unchanged and still validates.

Tests

26 new tests (scripts/lib/deployment-manifest.test.mjs, node --test, no dependencies).

Valid manifests — a complete testnet manifest; the manifest actually shipped in the repository; optional blocks being absent.

Incomplete manifests — missing network, unknown network, network disagreeing with the file name, missing chain id, missing passphrase, an entire chain section absent; each Ethereum and Stellar contract missing by name; a missing contracts block; and every asset identifier above, including the case that regressed.

Malformed values — an EVM address where a Soroban contract id belongs and the reverse, a Stellar account id where a contract id belongs, a non-http endpoint, a malformed date, a malformed transaction hash, and a manifest that is not an object.

Reporting — that three independent missing fields produce three errors in path order rather than only the first, and that formatManifestError renders file:path message.

CI

Added pnpm test:scripts, run in ci.yml immediately before the Validate deployment JSON step — the validator's own tests pass before it is trusted to gate the manifest.

🤖 Generated with Claude Code

https://claude.ai/code/session_016LpGd5DNEG334ZnjyNhmzm

A manifest with every staking-asset identifier removed validated cleanly:
`resolverRegistryConfig` was checked only when present, so a deploy could ship
naming no asset contract, no minimum stake, and no slash beneficiary at all.
That block is now required, and each identifier inside it is checked by name.

The rules move out of the CLI into `scripts/lib/deployment-manifest.mjs`, a
read-only module that takes an already parsed manifest and touches neither the
filesystem nor the network. `validate-deployments.mjs` keeps its behaviour and
now only finds files, parses them, and prints what the validator reports.

Errors are `{ path, message }` pairs against the JSON path of the offending
field, and a field that already failed its type check is no longer reported a
second time for its network-specific value, so the output reads as a checklist
of what to fix.

Adds 26 tests covering a complete manifest, the manifest shipped in the repo,
and incomplete ones: missing network, chain, contract, and asset identifiers;
addresses from the wrong chain; malformed endpoints, dates, and transaction
hashes; and that every bad field is reported rather than only the first. Wired
as `pnpm test:scripts` and run in CI before the manifest gate itself.

Closes karagozemin#234

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016LpGd5DNEG334ZnjyNhmzm
@vercel

vercel Bot commented Aug 31, 2026

Copy link
Copy Markdown

@egekoca is attempting to deploy a commit to the karagoz's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Aug 31, 2026

Copy link
Copy Markdown

@egekoca Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@karagozemin
karagozemin merged commit 8e0163d into karagozemin:master Aug 31, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SCF high][easy] Validate required fields in deployment manifests

2 participants