feat(codemod): SDK upgrade codemods (Codemod-OSS / JSSG) for 3.1.0#447
Draft
zama-cremaud wants to merge 10 commits into
Draft
feat(codemod): SDK upgrade codemods (Codemod-OSS / JSSG) for 3.1.0#447zama-cremaud wants to merge 10 commits into
zama-cremaud wants to merge 10 commits into
Conversation
…ds/ workspace) A Codemod (codemod.com) workflow package upgrades @zama-fhe/sdk / @zama-fhe/react-sdk consumer code, in the dedicated root codemods/ workspace. `pnpm codemod -t <path>` runs it. Packages follow a one-per-breaking-release convention (see codemods/README.md): each is keyed to the SDK release that introduced the breaks, not a from->to couple. First package: codemods/codemods/3.1.0 (@zama-fhe/sdk-upgrade-3.1.0), assuming a 3.0.x floor. - deterministic by default: 7 renames as native ast-grep steps (existing rule files) + 2 structural rewrites as JSSG transforms (scripts/*.ts). JSSG (ast-grep node API + imperative commitEdits) covers the ordered/structural cases plain ast-grep can't, so jscodeshift isn't our own dep. - optional AI tail: an `ai` workflow step, gated behind `--param ai=true` and run only after the deterministic steps, applies the non-mechanical 3.1.0 changes (useUserDecrypt/useEncrypt reshapes, removed EIP-712/keypair hooks, the ZamaSDK capability refactor). Off by default so the standard run + tests stay deterministic; configured via env (LLM_API_KEY / LLM_PROVIDER / LLM_MODEL). - the codemod CLI is a devDependency; it pulls jscodeshift transitively, whose @babel/core -> semver@6.3.1 is pinned to the attested 7.x line via a pnpm override (pnpm-workspace.yaml), keeping no-downgrade intact. - the codemods/ workspace is outside the main pnpm workspace globs (no effect on the main install); its tests run in the `scripts` vitest project (codemods/**/*.test.mjs) against the fixtures (apply + oxfmt == output). - scope api-report-derived: released stable 2.5.0/3.0.0/3.0.1 are API-identical, so 3.1.0 is the first release with breaking changes; provisional (from the v3.0.1 -> v3.1.0-alpha.14 diff). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hape-aware
Testing the workflow on examples/react-wagmi surfaced two bugs in the
ast-grep rules, now ported to import-/AST-aware JSSG scripts:
- createZamaConfig->createConfig was a bare textual match: it renamed the
local alias in `import { createConfig as createZamaConfig }`, producing the
self-collision `createConfig as createConfig` (and clashing with wagmi's own
createConfig). Now only rewrites the actual `createZamaConfig` export from a
@zama-fhe module, leaving local aliases of the new export untouched.
- The query-hook tokenAddress->address rules used exact single-property,
single-argument patterns, so real call sites silently slipped through:
multi-property configs ({ tokenAddress, account }), a trailing options
argument, and shorthand ({ tokenAddress }). The JSSG port rewrites the
first-argument object on the AST and handles all shapes + drop-wrapper.
Also hardened both this and the existing config-object->positional script to
skip a leading comment node (tree-sitter marks comments as named children),
which was silently skipping useShield/useUnshield/useResumeUnshield calls that
document the wrapper==token invariant above their config.
Removes the two superseded ast-grep rule files; extends the field-rename
fixture (multi-prop, second arg, shorthand, leading comment) and adds a
createConfig-alias-preserved fixture. Workflow fixture suite: 10/10 pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per the Codemod package-structure docs (https://docs.codemod.com/package-structure), a codemod is a single flat package directory, and the `name` field must match `/^[a-z0-9-_/]+$/` — dots are explicitly disallowed. The previous `@zama-fhe/sdk-upgrade-3.1.0` name was therefore invalid (it only slipped past `workflow validate`, which checks workflow.yaml, not codemod.yaml). - move codemods/codemods/3.1.0 -> codemods/sdk-upgrade-v3-1-0 (flat) - codemod.yaml + package.json name -> @zama-fhe/sdk-upgrade-v3-1-0 (3.1.0 -> v3-1-0) - pnpm-workspace.yaml glob -> codemods/* ; root `codemod` script + lockfile key updated - tests/workflow.test.mjs: REPO_ROOT recomputed for the shallower (flat) layout - READMEs: flat-slug layout, dot-free naming note, and corrected ast-grep/JSSG split (createZamaConfig + query-hook token-field are JSSG now) Workflow validates and the fixture suite passes (10/10) from the new location. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… harness Replace the bespoke vitest workflow harness with the official JSSG testing approach (https://docs.codemod.com/jssg/testing): per-script tests/<name>/ fixture dirs (input.tsx/expected.tsx), and a test.sh that discovers scripts/*.ts and runs `codemod jssg test --strictness ast` for each (no hand-maintained list). Drops the vitest integration test and the codemods glob from vitest.config.ts. The ast-grep rule fixtures keep their input/expected pairs but are not yet wired to a runner (noted in the README). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow https://docs.codemod.com/jssg/advanced: replace the hand-built edit literals ({startPos,endPos,insertedText}) with node.replace()/Edit, and export a getSelector per script so JSSG pre-filters files (skips any that don't mention the target hook/import/type). Each selector is a superset of what the transform edits, so this is behavior-preserving — `pnpm test` (codemod jssg test) is green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ulti-prop calls
The `codemod jssg test` harness only ran the JSSG scripts/, so the rules/*.yml
fixtures were never executed. Add test-rules.mjs (same tests/<rule>/ convention,
plus an idempotency check) and wire it into test.sh.
Running it surfaced that use-delegation-status only matched a single-property
object (`{ tokenAddress: $V }`); realistic calls — useDelegationStatus is
essentially always called as `{ tokenAddress, delegateAddress }` — slipped
through unchanged. Rewrite the rule to scope a pair/shorthand rewrite inside the
hook call (relational `inside`), so any object shape and key order is handled
while other call sites' tokenAddress is left untouched. Strengthen the fixture
to cover those shapes.
Exempt codemods/** from no-console (dev tooling, like scripts/**) so the runner lints clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Idempotency: add an `idempotent` fixture case to each JSSG transform (its own migrated output, which must round-trip unchanged) so the README's idempotency claim is enforced by the native harness. - End-to-end: test-e2e.mjs applies the whole chain in workflow.yaml order to a multi-file tests/_e2e fixture, asserts convergence to the expected tree, then re-applies and asserts a no-op — catching step-ordering / cross-transform regressions the per-transform tests can't. - README: document the rule + e2e runners, the partner run flow, and two known limitations (tsx-language rules under-cover type positions in plain .ts files; the `ai` node isn't formally gated by the param). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The codemod package's tests (JSSG harness + ast-grep rule runner + e2e chain) ran nowhere in CI — `pnpm test:coverage` (vitest) doesn't cover them. Add a path-filtered workflow that runs `pnpm --filter @zama-fhe/sdk-upgrade-v3-1-0 test`. No SDK build needed; the transforms are syntactic (ast-grep / JSSG on fixtures). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Public API Changes✅ No public API changes detected. |
Coverage Report
File CoverageNo changed files found. |
Copied from lint.yml (which is called by other workflows); nothing calls the codemod workflow, so the trigger is dead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
oxfmt-format the e2e expected fixtures + README (CI format:check flagged them). The codemods don't format their output, so the e2e runner now formats its result with oxfmt before comparing to the (repo-formatted) expected tree — an exact match that also mirrors the real "run the codemod, then your formatter" flow. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
SDK-upgrade codemods for the breaking changes in
@zama-fhe/sdk/@zama-fhe/react-sdk3.1.0, on the Codemod OSS engine — acodemods/sdk-upgrade-v3-1-0workspace package. It applies the mechanical subset deterministically; the non-mechanical tail is left to an opt-inaistep + manual review.rules/*.yml) for symbol/type renames + config-key changes.scripts/*.ts) for structural/context-sensitive rewrites (import-awarecreateZamaConfig→createConfig, config-object→positional, token-field any-shape,UseZamaConfigremoval).sdk-upgrade-v3-1-0, O(N)); distributed via the Codemod registry (npx codemod @zama-fhe/sdk-upgrade-v3-1-0 -t ./src). No SDK repo / Claude Code dependency for consumers.Authorship: the codemod engine + transforms are @ghermet's (commits preserved). The commits on top (tests, idempotency, a fix, CI) are the test/hardening pass.
Added on top (this pass)
codemod jssg testonly ran the JSSGscripts/; the 5rules/*.ymlhad fixtures but no runner. Addedtest-rules.mjs(sametests/<rule>/convention + idempotency), wired intotest.sh.use-delegation-statusonly matched a single-property object ({ tokenAddress: $V }), so realisticuseDelegationStatus({ tokenAddress, delegateAddress })calls slipped through. Rewrote it as a scoped relationalinsiderule (any object shape / key order; other call sites untouched).idempotentfixture case per transform (migrated output must round-trip).test-e2e.mjsruns the whole chain in workflow order on a multi-file fixture, asserting convergence + chain-level idempotency.pnpm test:coverageis vitest-only); added.github/workflows/codemod.yml.All green: 4 JSSG transforms (+ idempotent cases) + 5 rules + e2e convergence/idempotency.
Open items for review
.tstype-position gap (known limitation):language: tsxrules under-cover plain.tsfiles — the engine renames import specifiers but misses type-position occurrences (function f(h: Handle)keepsHandle). Works in.tsx. Fix =language: typescriptrule variants (or map.ts→tsx in engine config). Documented in the package README; e2e fixtures are.tsxfor this reason.ainode gating: the README says it's off-by-default via--param ai=true, butworkflow.yamlhas no node condition — it's a no-op only because no LLM is configured. Consider explicit gating.feat(codemod)/fix(codemod). Confirm semantic-release does not cut an SDK release for thecodemods/workspace (scope/path), or adjust types.🤖 Generated with Claude Code