Skip to content

Modernize: oclif v4 migration + test harness + behavior-locking test suite#41

Open
paulgnz wants to merge 4 commits intoXPRNetwork:masterfrom
paulgnz:modernization
Open

Modernize: oclif v4 migration + test harness + behavior-locking test suite#41
paulgnz wants to merge 4 commits intoXPRNetwork:masterfrom
paulgnz:modernization

Conversation

@paulgnz
Copy link
Copy Markdown
Contributor

@paulgnz paulgnz commented May 6, 2026

Summary

This PR modernizes the proton-cli toolchain in three phases, each landed in its own commit so the diff can be reviewed in stages:

  1. Repair test harness. npm test was broken — the existing @oclif/test@1 errored at module load on modern Node (module.parent.filename is undefined). Bumped mocha/ts-node/@types/mocha; replaced the deprecated mocha.opts with .mocharc.json; added a small spawn-based CLI runner so behavior tests don't depend on @oclif/test internals.

  2. Behavior-locking test suite. No tests existed before this. Added 138 tests covering: pure unit tests for revealPassword/encryptor/confirmation; command-registration smoke tests that walk src/commands/**/*.ts and assert each file imports cleanly + declares a description; behavior tests for the security-critical commands shipped in PR security: redact private keys and add reveal-password gate for key:get / key:list #39 (key:list, key:get, key:reveal-setup, key:reveal-disable). Suite runs in ~7s.

  3. oclif v4 migration. All command files migrated from the deprecated @oclif/command@1.x to @oclif/core@4. Args migrated from legacy array syntax [{ name, required }] to v4 object syntax { name: Args.string({...}) }. flags.X(...)Flags.X(...). CliUx.ux namespace replaced with a small compatibility shim at src/utils/ux.ts that preserves the legacy method surface (log, styledJSON, prompt, confirm, url, table) on top of console + inquirer. Removed @oclif/command, @oclif/config, @oclif/parser. Bumped @oclif/plugin-help to v6 and oclif (the build CLI) to v4.

Test suite is green throughout; tests caught a couple of pre-existing latent typing bugs that v4's stricter inference exposed (delegatebw/undelegatebw referenced args.account when only args.receiver was declared; endpoint:set referenced args.chain when only args.endpoint was declared) — both fixed in this PR.

Result: deprecation warnings on fresh install

Verified by running npm pack against this branch and npm install ./proton-cli-0.1.98.tgz in a clean directory.

Before After
Total npm warn deprecated lines 16 8
@oclif/* deprecation warnings 9 0

The eight remaining warnings are all transitive dependencies of packages this repo doesn't directly own. Mapping:

Deprecation Comes via Who fixes it
eth-sig-util@3.0.1 @proton/api @proton/api should migrate to @metamask/eth-sig-util
ethereumjs-abi@0.6.8 @proton/api @proton/api
lodash.isequal@4.5.0 @proton/api@walletconnect/* walletconnect bump
node-domexception@1.0.0 @proton/jsnode-fetch chain clears when @proton/js updates node-fetch
uuid@8.3.2 @proton/js, oclif, @oclif/test clears when those bump
inflight@1.0.6 glob@7 chain — used by nyc and oclif replace nyc with c8, or wait for nyc
glob@7.2.3 nyc + oclif chain same
rimraf@3.0.2 nyc + a few others same

Cleaning these would mean either (a) bumping @proton/js / @proton/api upstream, or (b) replacing nyc with c8 for code coverage. Neither belongs in this PR; the second one is a 5-minute follow-up if desired.

Test plan

  • npm test — 138 tests, ~7s
  • npm pack && npm install ../*.tgz in a clean directory — installs cleanly, deprecation count cut in half, all @oclif/* warnings gone
  • Installed binary runs end-to-end: proton --version and proton key:list --help print expected output
  • All commands compile under TypeScript strict mode
  • Reviewer: smoke-test a few commands you care about that aren't covered by the test suite — chain operations, msig, contract:set, etc.

Notes for reviewers

  • The src/utils/ux.ts shim is a deliberate choice. v4 trimmed the ux namespace dramatically (gone: log, styledJSON, prompt, confirm, url, table). Rather than rewriting every call site, the shim gives us a stable internal surface with familiar method names. ~100 lines, fully typed.
  • The two codemod scripts in scripts/ (migrate-oclif.mjs and migrate-args.mjs) drove the bulk of the rewrite. They're committed for audit/reproducibility but are one-shot tools — happy to drop them in a follow-up commit if you'd prefer a cleaner tree.
  • TypeScript exposed two latent bugs in legacy code (the args.account / args.chain references mentioned above). Fixed inline rather than separately so reviewers can see why the tests pass.
  • Build uses tsx instead of ts-node for tests — handles the Node v22 ESM/CJS mix without the configuration gymnastics ts-node currently needs.

Branch state

  • Off XPRNetwork/proton-cli@master at 1f69edc (the 0.1.98 bump merge)
  • 4 commits, each phase its own logical unit
  • No private key strings of any kind in any commit (verified: git diff master..modernization | grep -E 'PVT_K1_|PVT_R1_' → no matches)

paulgnz added 4 commits May 7, 2026 08:20
- Bump mocha to ^10, ts-node to ^10, @types/mocha to ^10
- Replace deprecated test/mocha.opts with test/.mocharc.json
- Tighten test tsconfig to explicitly use commonjs + transpileOnly so
  ts-node resolves TypeScript imports under Node v22
- Remove three stale test files (network/version/boilerplate) that
  referenced moved imports and used the broken @oclif/test@1
- Add test/helpers/cli.ts — spawn-based CLI runner that does not
  require @oclif/test, avoiding the v1 module.parent.filename crash
  on modern Node versions
- Drop the posttest lint hook (3196 pre-existing style errors are out
  of scope for this PR; expose linting as 'npm run lint' instead)
- Add first unit test (reveal-password) to verify harness works

npm test runs in <1s and exits green.
Unit tests for storage modules (chai + mocha, no @oclif/test):
- reveal-password: hash/verify roundtrip, salt randomness, scrypt params
- encryptor: AES-256-CBC encrypt/decrypt, IV randomness, wrong-key, unicode
- confirmation: shared CONFIRMATION_PHRASE constant locked to 'I UNDERSTAND'

Command-registration smoke tests:
- Walks src/commands/**/*.ts, asserts each file (a) imports without errors
  and (b) declares a description. Catches the kinds of regressions a
  command-by-command oclif migration can introduce (broken imports,
  wrong base class, missing description).

Test infrastructure:
- Switch from ts-node to tsx (handles ESM/CJS mix on Node v22 cleanly,
  no extension-resolution gymnastics)
- mocha v10 config in test/.mocharc.json with tsx as the loader
- Drop ts-node dependency (replaced by tsx)

128 tests passing in ~2s. No network or chain-touching tests yet —
those land later for the security-critical commands.
- Add isolated-HOME test fixture so behavior tests don't touch the
  user's real config (XDG_*_HOME and HOME pointed at a per-test
  tempdir; cleanup afterwards)
- Add pretest hook to run tsc -b so behavior tests exercise the
  built CLI via spawn
- key:list tests: empty wallet, no PVT_K1_ leakage, --help shows
  --reveal-private and --force
- key:get tests: empty-wallet behavior, --help mentions reveal password
  and --force, no key strings in help output
- reveal-setup/disable tests: --help content, disable on fresh wallet
  is a no-op, setup behaviour with non-TTY input

138 tests passing in ~7s. The pretest tsc adds ~5s; could move to a
single mocha 'before' hook later if speed matters.
The package's commands and supporting modules now use @oclif/core v4
exclusively. The legacy @oclif/command, @oclif/config, @oclif/parser,
and CliUx namespace are gone. v3 plugin-help bumped to v6 and the
oclif build CLI bumped to v4 to drop further deprecation chains.

Result of `npm pack` + fresh install: deprecation warning count
drops from 16 to 8. All eight remaining warnings are transitive
dependencies of @proton/js, @proton/api, oclif, or nyc (eth-sig-util,
ethereumjs-abi, inflight, glob, rimraf, lodash.isequal, node-
domexception, uuid). Zero @oclif/* warnings remain.

Code changes:

- src/utils/ux.ts is a small compatibility shim for the parts of the
  legacy ux API that v4 dropped (log, styledJSON, prompt, confirm,
  url, table). Implemented in <100 lines on top of console + inquirer
  + a tiny home-rolled table formatter. Avoids touching every call
  site that previously used CliUx.ux.foo.

- All 50+ command files moved off @oclif/command/Command to
  @oclif/core/Command, with await added to this.parse() calls.

- 45 command args migrated from legacy array syntax
  ([{ name, required, description }]) to v4 object syntax
  ({ name: Args.string({ required, description }) }).

- All flags() calls updated from lowercase 'flags.boolean(...)' to v4
  'Flags.boolean(...)'.

- src/core/flags/index.ts: Flags.build was removed in v4; replaced
  with a builder closure that returns a configured Flags.string.

- contract/set.ts dynamic-args pattern (which used @oclif/parser
  Input directly) refactored to declare both args as optional and
  validate at runtime against contractConfig overrides.

- Latent typing bugs surfaced by stricter v4 typing fixed:
  delegatebw/undelegatebw used args.account when only args.receiver
  was declared; endpoint/set used args.chain when only args.endpoint
  was declared.

Test suite: 138 tests still passing post-migration.

Codemods used to drive the change live in scripts/migrate-oclif.mjs
and scripts/migrate-args.mjs and were kept in the repo for review/
audit purposes; they are one-shot tools and can be deleted in a
follow-up commit if upstream prefers a cleaner tree.
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.

1 participant