@0xobelisk/sui-cli 1.2.0-pre.98 → 2.0.0

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.
@@ -1,7 +1,8 @@
1
1
  import type { CommandModule } from 'yargs';
2
- import { execSync } from 'child_process';
2
+ import { execFileSync, execSync } from 'child_process';
3
3
  import { DubheConfig, loadConfig } from '@0xobelisk/sui-common';
4
4
  import { handlerExit } from './shell';
5
+ import { lintSystemGuards, formatLintWarnings } from '../utils';
5
6
 
6
7
  /**
7
8
  * Returns the active Sui client environment (e.g. "localnet", "testnet").
@@ -15,12 +16,56 @@ function getActiveSuiEnv(): string {
15
16
  }
16
17
  }
17
18
 
18
- type Options = {
19
+ export type RunMoveTestOptions = {
20
+ /** Substring matched against each test's fully qualified name (`addr::module::fun`). */
21
+ filter?: string;
22
+ 'gas-limit'?: string;
23
+ /** Same as `gas-limit` (programmatic API). */
24
+ gasLimit?: string;
25
+ buildEnv?: string;
26
+ /** When true, passes `-l` to list tests instead of running them. */
27
+ list?: boolean;
28
+ };
29
+
30
+ type CliOptions = {
19
31
  'config-path': string;
32
+ /** Positional from `test [filter]` */
33
+ filter?: string;
20
34
  test?: string;
21
- 'gas-limit'?: string;
35
+ 'gas-limit': string;
36
+ list?: boolean;
22
37
  };
23
38
 
39
+ /**
40
+ * Builds argv for `sui move test` (argument array — no shell interpolation).
41
+ *
42
+ * Sui expects an optional filter as a **positional** argument at the end of the command.
43
+ * The `--test` flag on `sui move test` is unrelated (it enables compiling the `tests/` tree).
44
+ *
45
+ * @see `sui move test --help`
46
+ */
47
+ export function buildSuiMoveTestArgv(options: {
48
+ projectPath: string;
49
+ gasLimit: string;
50
+ buildEnv?: string;
51
+ filter?: string;
52
+ list?: boolean;
53
+ }): string[] {
54
+ const args = ['move', 'test'];
55
+ if (options.buildEnv) {
56
+ args.push('--build-env', options.buildEnv);
57
+ }
58
+ args.push('--path', options.projectPath);
59
+ if (options.list) {
60
+ args.push('-l');
61
+ }
62
+ args.push('--gas-limit', options.gasLimit);
63
+ if (options.filter && !options.list) {
64
+ args.push(options.filter);
65
+ }
66
+ return args;
67
+ }
68
+
24
69
  /**
25
70
  * Core Move test runner for Dubhe contracts.
26
71
  * Runs `sui move test` against the package at `src/<dubheConfig.name>`.
@@ -29,58 +74,82 @@ type Options = {
29
74
  */
30
75
  export async function testHandler(
31
76
  dubheConfig: DubheConfig,
32
- test?: string,
33
- gasLimit: string = '100000000',
34
- buildEnv?: string
77
+ options: RunMoveTestOptions = {}
35
78
  ): Promise<string> {
79
+ const gasLimit = options['gas-limit'] ?? options.gasLimit ?? '100000000';
36
80
  const cwd = process.cwd();
37
81
  const projectPath = `${cwd}/src/${dubheConfig.name}`;
38
- // --build-env overrides the active Sui client environment for dependency resolution.
39
- // Required for localnet (which is not in Move.toml [environments]) when the
40
- // active client env has been set to localnet from a previous run.
41
- const buildEnvFlag = buildEnv ? `--build-env ${buildEnv}` : '';
42
- const command = `sui move test ${buildEnvFlag} --path ${projectPath} ${
43
- test ? `--test ${test}` : ''
44
- } --gas-limit ${gasLimit}`;
45
- return execSync(command, { stdio: 'pipe', encoding: 'utf-8' });
82
+ const argv = buildSuiMoveTestArgv({
83
+ projectPath,
84
+ gasLimit,
85
+ buildEnv: options.buildEnv,
86
+ filter: options.filter,
87
+ list: options.list
88
+ });
89
+ return execFileSync('sui', argv, { stdio: 'pipe', encoding: 'utf-8' });
90
+ }
91
+
92
+ function resolveTestFilter(argv: { filter?: string; test?: string }): string | undefined {
93
+ return argv.filter ?? argv.test;
46
94
  }
47
95
 
48
- const commandModule: CommandModule<Options, Options> = {
49
- command: 'test',
96
+ const commandModule: CommandModule<CliOptions, CliOptions> = {
97
+ command: 'test [filter]',
50
98
 
51
99
  describe: 'Run Move unit tests in Dubhe contracts',
52
100
 
53
101
  builder(yargs) {
54
- return yargs.options({
55
- 'config-path': {
102
+ return yargs
103
+ .positional('filter', {
56
104
  type: 'string',
57
- default: 'dubhe.config.ts',
58
- description: 'Path to the Dubhe config file'
59
- },
60
- test: {
61
- type: 'string',
62
- desc: 'Run a specific test by name'
63
- },
64
- 'gas-limit': {
65
- type: 'string',
66
- desc: 'Set the gas limit for the test',
67
- default: '100000000'
68
- }
69
- });
105
+ describe:
106
+ 'Substring of fully qualified test name (see `sui move test --help`); optional when using --list'
107
+ })
108
+ .options({
109
+ 'config-path': {
110
+ type: 'string',
111
+ default: 'dubhe.config.ts',
112
+ description: 'Path to the Dubhe config file'
113
+ },
114
+ test: {
115
+ type: 'string',
116
+ describe: 'Same as positional [filter] (kept for backward compatibility)'
117
+ },
118
+ 'gas-limit': {
119
+ type: 'string',
120
+ desc: 'Set the gas limit for the test',
121
+ default: '100000000'
122
+ },
123
+ list: {
124
+ type: 'boolean',
125
+ default: false,
126
+ describe: 'List all Move unit tests (`sui move test -l`)'
127
+ }
128
+ });
70
129
  },
71
130
 
72
- async handler({ 'config-path': configPath, test, 'gas-limit': gasLimit }) {
131
+ async handler(argv) {
132
+ const { 'config-path': configPath, 'gas-limit': gasLimit, list } = argv;
133
+ const filter = resolveTestFilter(argv);
134
+
73
135
  try {
74
- console.log('🚀 Running move test');
136
+ console.log(list ? '🚀 Listing Move unit tests' : '🚀 Running move test');
75
137
  const dubheConfig = (await loadConfig(configPath)) as DubheConfig;
76
138
 
77
- // Ephemeral networks (localnet/devnet) are not defined in Move.toml [environments].
78
- // Use --build-env testnet for dependency resolution so `sui move test` can resolve
79
- // git dependencies without requiring a localnet env entry.
139
+ const projectPath = `${process.cwd()}/src/${dubheConfig.name}`;
140
+ const lintResults = lintSystemGuards(projectPath);
141
+ const warnings = formatLintWarnings(lintResults);
142
+ if (warnings) process.stdout.write(warnings);
143
+
80
144
  const activeEnv = getActiveSuiEnv();
81
145
  const buildEnv = activeEnv === 'localnet' || activeEnv === 'devnet' ? 'testnet' : undefined;
82
146
 
83
- const output = await testHandler(dubheConfig, test, gasLimit, buildEnv);
147
+ const output = await testHandler(dubheConfig, {
148
+ filter,
149
+ 'gas-limit': gasLimit,
150
+ buildEnv,
151
+ list
152
+ });
84
153
  if (output) process.stdout.write(output);
85
154
  } catch (error: any) {
86
155
  if (error.stdout) process.stdout.write(error.stdout);
@@ -3,12 +3,15 @@ import { logError } from '../utils/errors';
3
3
  import { upgradeHandler } from '../utils/upgradeHandler';
4
4
  import { DubheConfig, loadConfig } from '@0xobelisk/sui-common';
5
5
  import { handlerExit } from './shell';
6
- import { getDefaultNetwork } from '../utils';
6
+ import { getDefaultNetwork, lintSystemGuards, formatLintWarnings, confirm } from '../utils';
7
+ import { join as pathJoin } from 'path';
7
8
  import chalk from 'chalk';
8
9
 
9
10
  type Options = {
10
11
  network: any;
11
12
  'config-path': string;
13
+ 'bump-version': boolean;
14
+ 'rpc-url'?: string;
12
15
  };
13
16
 
14
17
  const commandModule: CommandModule<Options, Options> = {
@@ -28,18 +31,48 @@ const commandModule: CommandModule<Options, Options> = {
28
31
  type: 'string',
29
32
  default: 'dubhe.config.ts',
30
33
  decs: 'Path to the config file'
34
+ },
35
+ 'bump-version': {
36
+ type: 'boolean',
37
+ default: false,
38
+ desc: 'Force a version bump even when no new resources were added (use for breaking logic changes or security fixes that must invalidate old clients)'
39
+ },
40
+ 'rpc-url': {
41
+ type: 'string',
42
+ desc: 'Custom RPC endpoint URL (overrides the default for the selected network)'
31
43
  }
32
44
  });
33
45
  },
34
46
 
35
- async handler({ network, 'config-path': configPath }) {
47
+ async handler({
48
+ network,
49
+ 'config-path': configPath,
50
+ 'bump-version': bumpVersion,
51
+ 'rpc-url': rpcUrl
52
+ }) {
36
53
  try {
37
54
  if (network == 'default') {
38
55
  network = await getDefaultNetwork();
39
56
  console.log(chalk.yellow(`Use default network: [${network}]`));
40
57
  }
41
58
  const dubheConfig = (await loadConfig(configPath)) as DubheConfig;
42
- await upgradeHandler(dubheConfig, dubheConfig.name, network);
59
+
60
+ const projectPath = pathJoin(process.cwd(), 'src', dubheConfig.name);
61
+ const lintResults = lintSystemGuards(projectPath);
62
+ if (lintResults.length > 0) {
63
+ process.stdout.write(formatLintWarnings(lintResults));
64
+ const proceed = await confirm(
65
+ 'Some entry functions are missing ensure_latest_version. Proceed with upgrade anyway?'
66
+ );
67
+ if (!proceed) {
68
+ console.log(chalk.red('Upgrade cancelled.'));
69
+ handlerExit(1);
70
+ return;
71
+ }
72
+ }
73
+
74
+ const fullnodeUrls = rpcUrl ? [rpcUrl] : undefined;
75
+ await upgradeHandler(dubheConfig, dubheConfig.name, network, bumpVersion, fullnodeUrls);
43
76
  } catch (error: any) {
44
77
  logError(error);
45
78
  handlerExit(1);
@@ -15,17 +15,17 @@ const commandModule: CommandModule = {
15
15
  async handler() {
16
16
  const configFilePath = 'dubhe.config.ts';
17
17
 
18
- const runSchemagen = () => {
19
- exec('pnpm dubhe schemagen', (error, stdout, stderr) => {
18
+ const runGenerate = () => {
19
+ exec('pnpm dubhe generate', (error, stdout, stderr) => {
20
20
  if (error) {
21
- console.error(`Error executing schemagen: ${error.message}`);
21
+ console.error(`Error executing generate: ${error.message}`);
22
22
  return;
23
23
  }
24
24
  if (stderr) {
25
- console.error(`schemagen stderr: ${stderr}`);
25
+ console.error(`generate stderr: ${stderr}`);
26
26
  return;
27
27
  }
28
- console.log(`schemagen stdout: ${stdout}`);
28
+ console.log(`generate stdout: ${stdout}`);
29
29
  });
30
30
  };
31
31
 
@@ -34,8 +34,8 @@ const commandModule: CommandModule = {
34
34
  });
35
35
 
36
36
  watcher.on('change', (path) => {
37
- console.log(`${path} has been changed. Running schemagen...`);
38
- runSchemagen();
37
+ console.log(`${path} has been changed. Running generate...`);
38
+ runGenerate();
39
39
  });
40
40
 
41
41
  console.log(`Watching for changes in ${configFilePath}...`);
package/src/dubhe.ts CHANGED
File without changes
@@ -4,10 +4,14 @@ import { initializeDubhe } from './utils';
4
4
  import { DubheCliError } from './errors';
5
5
  dotenv.config();
6
6
 
7
- export async function checkBalanceHandler(network: 'mainnet' | 'testnet' | 'devnet' | 'localnet') {
7
+ export async function checkBalanceHandler(
8
+ network: 'mainnet' | 'testnet' | 'devnet' | 'localnet',
9
+ fullnodeUrls?: string[]
10
+ ) {
8
11
  try {
9
12
  const dubhe = initializeDubhe({
10
- network
13
+ network,
14
+ fullnodeUrls
11
15
  });
12
16
 
13
17
  const balance = await dubhe.getBalance();
@@ -1,5 +1,9 @@
1
- export const TESTNET_DUBHE_HUB_OBJECT_ID =
2
- '0xfef203de9d3a2980429e91df535a0503ccf8d3c05aa3815936984243dc96f19f';
1
+ export {
2
+ TESTNET_DUBHE_HUB_OBJECT_ID,
3
+ TESTNET_DUBHE_FRAMEWORK_PACKAGE_ID,
4
+ MAINNET_DUBHE_HUB_OBJECT_ID,
5
+ MAINNET_DUBHE_FRAMEWORK_PACKAGE_ID
6
+ } from '@0xobelisk/sui-client';
3
7
 
4
- export const TESTNET_ORIGINAL_DUBHE_PACKAGE_ID =
5
- '0x8817b4976b6c607da01cea49d728f71d09274c82e9b163fa20c2382586f8aefc';
8
+ // Keep legacy alias for backwards compatibility within this package
9
+ export { TESTNET_DUBHE_FRAMEWORK_PACKAGE_ID as TESTNET_ORIGINAL_DUBHE_PACKAGE_ID } from '@0xobelisk/sui-client';
@@ -4,13 +4,14 @@ import { getOldPackageId, saveMetadata } from './utils';
4
4
  export async function loadMetadataHandler(
5
5
  dubheConfig: DubheConfig,
6
6
  network: 'mainnet' | 'testnet' | 'devnet' | 'localnet',
7
- packageId?: string
7
+ packageId?: string,
8
+ fullnodeUrls?: string[]
8
9
  ) {
9
10
  if (packageId) {
10
- await saveMetadata(dubheConfig.name, network, packageId);
11
+ await saveMetadata(dubheConfig.name, network, packageId, fullnodeUrls);
11
12
  } else {
12
13
  const projectPath = `${process.cwd()}/src/${dubheConfig.name}`;
13
14
  const packageId = await getOldPackageId(projectPath, network);
14
- await saveMetadata(dubheConfig.name, network, packageId);
15
+ await saveMetadata(dubheConfig.name, network, packageId, fullnodeUrls);
15
16
  }
16
17
  }