@0xobelisk/sui-cli 1.2.0-pre.117 ā 1.2.0-pre.119
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.
- package/dist/dubhe.js +120 -111
- package/dist/dubhe.js.map +1 -1
- package/package.json +3 -3
- package/src/commands/build.ts +7 -1
- package/src/commands/convertJson.ts +28 -14
- package/src/commands/generate.ts +13 -4
- package/src/commands/publish.ts +23 -1
- package/src/commands/test.ts +6 -0
- package/src/commands/upgrade.ts +25 -3
- package/src/utils/publishHandler.ts +6 -2
- package/src/utils/storeConfig.ts +20 -0
- package/src/utils/upgradeHandler.ts +36 -21
- package/src/utils/utils.ts +200 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xobelisk/sui-cli",
|
|
3
|
-
"version": "1.2.0-pre.
|
|
3
|
+
"version": "1.2.0-pre.119",
|
|
4
4
|
"description": "Tookit for interacting with move eps framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sui",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"yargs": "^17.7.1",
|
|
48
48
|
"zod": "^3.22.3",
|
|
49
49
|
"zod-validation-error": "^1.3.0",
|
|
50
|
-
"@0xobelisk/sui-client": "1.2.0-pre.
|
|
51
|
-
"@0xobelisk/sui-common": "1.2.0-pre.
|
|
50
|
+
"@0xobelisk/sui-client": "1.2.0-pre.119",
|
|
51
|
+
"@0xobelisk/sui-common": "1.2.0-pre.119"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/cli-progress": "^3.11.5",
|
package/src/commands/build.ts
CHANGED
|
@@ -4,7 +4,7 @@ import nodePath from 'path';
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { DubheConfig, loadConfig } from '@0xobelisk/sui-common';
|
|
6
6
|
import { handlerExit } from './shell';
|
|
7
|
-
import { getDefaultNetwork, switchEnv } from '../utils';
|
|
7
|
+
import { getDefaultNetwork, switchEnv, lintSystemGuards, formatLintWarnings } from '../utils';
|
|
8
8
|
|
|
9
9
|
type Options = {
|
|
10
10
|
'config-path': string;
|
|
@@ -78,6 +78,12 @@ const commandModule: CommandModule<Options, Options> = {
|
|
|
78
78
|
console.log('š Running move build');
|
|
79
79
|
const dubheConfig = (await loadConfig(configPath)) as DubheConfig;
|
|
80
80
|
await switchEnv(network);
|
|
81
|
+
|
|
82
|
+
const projectPath = nodePath.join(process.cwd(), 'src', dubheConfig.name);
|
|
83
|
+
const lintResults = lintSystemGuards(projectPath);
|
|
84
|
+
const warnings = formatLintWarnings(lintResults);
|
|
85
|
+
if (warnings) process.stdout.write(warnings);
|
|
86
|
+
|
|
81
87
|
const output = await buildHandler(dubheConfig, network, dumpBytecodeAsBase64);
|
|
82
88
|
console.log(output);
|
|
83
89
|
exec(`pnpm dubhe convert-json --config-path ${configPath}`);
|
|
@@ -10,6 +10,33 @@ type Options = {
|
|
|
10
10
|
'output-path': string;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
const RUNTIME_FIELDS = [
|
|
14
|
+
'original_package_id',
|
|
15
|
+
'dubhe_object_id',
|
|
16
|
+
'original_dubhe_package_id',
|
|
17
|
+
'dapp_key',
|
|
18
|
+
'start_checkpoint'
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export function mergeConfigJsonRuntimeFields(
|
|
22
|
+
schemaJson: Record<string, unknown>,
|
|
23
|
+
existing: Record<string, unknown>
|
|
24
|
+
): Record<string, unknown> {
|
|
25
|
+
const merged: Record<string, unknown> = { ...schemaJson };
|
|
26
|
+
for (const field of RUNTIME_FIELDS) {
|
|
27
|
+
if (existing[field] !== undefined) {
|
|
28
|
+
merged[field] = existing[field];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// If dapp_key is missing but original_package_id is present, compute it.
|
|
32
|
+
// This handles configs created before dapp_key was introduced.
|
|
33
|
+
if (!merged['dapp_key'] && merged['original_package_id']) {
|
|
34
|
+
const hex = (merged['original_package_id'] as string).replace(/^0x/i, '').padStart(64, '0');
|
|
35
|
+
merged['dapp_key'] = `${hex}::dapp_key::DappKey`;
|
|
36
|
+
}
|
|
37
|
+
return merged;
|
|
38
|
+
}
|
|
39
|
+
|
|
13
40
|
const commandModule: CommandModule<Options, Options> = {
|
|
14
41
|
command: 'convert-json',
|
|
15
42
|
describe: 'Convert JSON from Dubhe config to config.json',
|
|
@@ -34,14 +61,6 @@ const commandModule: CommandModule<Options, Options> = {
|
|
|
34
61
|
const dubheConfig = (await loadConfig(configPath)) as DubheConfig;
|
|
35
62
|
const schemaJson = JSON.parse(generateConfigJson(dubheConfig));
|
|
36
63
|
|
|
37
|
-
// Preserve runtime fields written by publishHandler (package IDs, checkpoint, etc.)
|
|
38
|
-
// so that re-running convert-json after publish does not wipe deployment info.
|
|
39
|
-
const RUNTIME_FIELDS = [
|
|
40
|
-
'original_package_id',
|
|
41
|
-
'dubhe_object_id',
|
|
42
|
-
'original_dubhe_package_id',
|
|
43
|
-
'start_checkpoint'
|
|
44
|
-
];
|
|
45
64
|
let existing: Record<string, unknown> = {};
|
|
46
65
|
if (fs.existsSync(outputPath)) {
|
|
47
66
|
try {
|
|
@@ -50,12 +69,7 @@ const commandModule: CommandModule<Options, Options> = {
|
|
|
50
69
|
// ignore parse errors ā start fresh
|
|
51
70
|
}
|
|
52
71
|
}
|
|
53
|
-
const merged
|
|
54
|
-
for (const field of RUNTIME_FIELDS) {
|
|
55
|
-
if (existing[field] !== undefined) {
|
|
56
|
-
merged[field] = existing[field];
|
|
57
|
-
}
|
|
58
|
-
}
|
|
72
|
+
const merged = mergeConfigJsonRuntimeFields(schemaJson, existing);
|
|
59
73
|
|
|
60
74
|
fs.writeFileSync(outputPath, JSON.stringify(merged, null, 2));
|
|
61
75
|
} catch (error: any) {
|
package/src/commands/generate.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CommandModule } from 'yargs';
|
|
2
|
-
import {
|
|
2
|
+
import { codegen, loadConfig, DubheConfig } from '@0xobelisk/sui-common';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { handlerExit } from './shell';
|
|
@@ -8,11 +8,13 @@ import { getDefaultNetwork } from '../utils';
|
|
|
8
8
|
type Options = {
|
|
9
9
|
'config-path'?: string;
|
|
10
10
|
network?: 'mainnet' | 'testnet' | 'devnet' | 'localnet' | 'default';
|
|
11
|
+
mode?: 'user_pays' | 'dapp_subsidizes';
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
const commandModule: CommandModule<Options, Options> = {
|
|
15
|
+
command: 'generate',
|
|
14
16
|
// 'schemagen' kept as a deprecated alias for backward compatibility
|
|
15
|
-
|
|
17
|
+
aliases: ['schemagen'],
|
|
16
18
|
|
|
17
19
|
describe: 'Generate Move code from dubhe.config.ts',
|
|
18
20
|
|
|
@@ -27,10 +29,16 @@ const commandModule: CommandModule<Options, Options> = {
|
|
|
27
29
|
choices: ['mainnet', 'testnet', 'devnet', 'localnet', 'default'] as const,
|
|
28
30
|
default: 'default',
|
|
29
31
|
desc: 'Node network (mainnet/testnet/devnet/localnet)'
|
|
32
|
+
},
|
|
33
|
+
mode: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
choices: ['user_pays', 'dapp_subsidizes'] as const,
|
|
36
|
+
default: 'user_pays',
|
|
37
|
+
desc: 'Initial settlement mode for this DApp (only applies on first generate)'
|
|
30
38
|
}
|
|
31
39
|
},
|
|
32
40
|
|
|
33
|
-
async handler({ 'config-path': configPath, network }) {
|
|
41
|
+
async handler({ 'config-path': configPath, network, mode }) {
|
|
34
42
|
try {
|
|
35
43
|
if (!configPath) throw new Error('Config path is required');
|
|
36
44
|
if (network == 'default') {
|
|
@@ -39,7 +47,8 @@ const commandModule: CommandModule<Options, Options> = {
|
|
|
39
47
|
}
|
|
40
48
|
const dubheConfig = (await loadConfig(configPath)) as DubheConfig;
|
|
41
49
|
const rootDir = path.dirname(configPath);
|
|
42
|
-
|
|
50
|
+
const initialMode: 0 | 1 = mode === 'dapp_subsidizes' ? 0 : 1;
|
|
51
|
+
await codegen(rootDir, dubheConfig, network, initialMode);
|
|
43
52
|
handlerExit();
|
|
44
53
|
} catch (error: any) {
|
|
45
54
|
console.log(chalk.red('Generate failed!'));
|
package/src/commands/publish.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import type { CommandModule } from 'yargs';
|
|
2
2
|
import { logError } from '../utils/errors';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getDefaultNetwork,
|
|
5
|
+
publishHandler,
|
|
6
|
+
lintSystemGuards,
|
|
7
|
+
formatLintWarnings,
|
|
8
|
+
confirm
|
|
9
|
+
} from '../utils';
|
|
4
10
|
import { loadConfig, DubheConfig } from '@0xobelisk/sui-common';
|
|
5
11
|
import { execSync } from 'child_process';
|
|
12
|
+
import { join as pathJoin } from 'path';
|
|
6
13
|
import { handlerExit } from './shell';
|
|
7
14
|
import chalk from 'chalk';
|
|
8
15
|
|
|
@@ -51,6 +58,21 @@ const commandModule: CommandModule<Options, Options> = {
|
|
|
51
58
|
console.log(chalk.yellow(`Use default network: [${network}]`));
|
|
52
59
|
}
|
|
53
60
|
const dubheConfig = (await loadConfig(configPath)) as DubheConfig;
|
|
61
|
+
|
|
62
|
+
const projectPath = pathJoin(process.cwd(), 'src', dubheConfig.name);
|
|
63
|
+
const lintResults = lintSystemGuards(projectPath);
|
|
64
|
+
if (lintResults.length > 0) {
|
|
65
|
+
process.stdout.write(formatLintWarnings(lintResults));
|
|
66
|
+
const proceed = await confirm(
|
|
67
|
+
'Some entry functions are missing ensure_latest_version. Proceed with publish anyway?'
|
|
68
|
+
);
|
|
69
|
+
if (!proceed) {
|
|
70
|
+
console.log(chalk.red('Publish cancelled.'));
|
|
71
|
+
handlerExit(1);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
54
76
|
execSync(`pnpm dubhe convert-json --config-path ${configPath}`, { encoding: 'utf-8' });
|
|
55
77
|
await publishHandler(dubheConfig, network, force, gasBudget);
|
|
56
78
|
} catch (error: any) {
|
package/src/commands/test.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { CommandModule } from 'yargs';
|
|
|
2
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").
|
|
@@ -135,6 +136,11 @@ const commandModule: CommandModule<CliOptions, CliOptions> = {
|
|
|
135
136
|
console.log(list ? 'š Listing Move unit tests' : 'š Running move test');
|
|
136
137
|
const dubheConfig = (await loadConfig(configPath)) as DubheConfig;
|
|
137
138
|
|
|
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
|
+
|
|
138
144
|
const activeEnv = getActiveSuiEnv();
|
|
139
145
|
const buildEnv = activeEnv === 'localnet' || activeEnv === 'devnet' ? 'testnet' : undefined;
|
|
140
146
|
|
package/src/commands/upgrade.ts
CHANGED
|
@@ -3,12 +3,14 @@ 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;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
const commandModule: CommandModule<Options, Options> = {
|
|
@@ -28,18 +30,38 @@ const commandModule: CommandModule<Options, Options> = {
|
|
|
28
30
|
type: 'string',
|
|
29
31
|
default: 'dubhe.config.ts',
|
|
30
32
|
decs: 'Path to the config file'
|
|
33
|
+
},
|
|
34
|
+
'bump-version': {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
default: false,
|
|
37
|
+
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)'
|
|
31
38
|
}
|
|
32
39
|
});
|
|
33
40
|
},
|
|
34
41
|
|
|
35
|
-
async handler({ network, 'config-path': configPath }) {
|
|
42
|
+
async handler({ network, 'config-path': configPath, 'bump-version': bumpVersion }) {
|
|
36
43
|
try {
|
|
37
44
|
if (network == 'default') {
|
|
38
45
|
network = await getDefaultNetwork();
|
|
39
46
|
console.log(chalk.yellow(`Use default network: [${network}]`));
|
|
40
47
|
}
|
|
41
48
|
const dubheConfig = (await loadConfig(configPath)) as DubheConfig;
|
|
42
|
-
|
|
49
|
+
|
|
50
|
+
const projectPath = pathJoin(process.cwd(), 'src', dubheConfig.name);
|
|
51
|
+
const lintResults = lintSystemGuards(projectPath);
|
|
52
|
+
if (lintResults.length > 0) {
|
|
53
|
+
process.stdout.write(formatLintWarnings(lintResults));
|
|
54
|
+
const proceed = await confirm(
|
|
55
|
+
'Some entry functions are missing ensure_latest_version. Proceed with upgrade anyway?'
|
|
56
|
+
);
|
|
57
|
+
if (!proceed) {
|
|
58
|
+
console.log(chalk.red('Upgrade cancelled.'));
|
|
59
|
+
handlerExit(1);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await upgradeHandler(dubheConfig, dubheConfig.name, network, bumpVersion);
|
|
43
65
|
} catch (error: any) {
|
|
44
66
|
logError(error);
|
|
45
67
|
handlerExit(1);
|
|
@@ -493,6 +493,7 @@ async function publishContract(
|
|
|
493
493
|
network,
|
|
494
494
|
startCheckpoint,
|
|
495
495
|
packageId,
|
|
496
|
+
packageId, // originalPackageId: first publish, so original == current
|
|
496
497
|
frameworkDappHubId,
|
|
497
498
|
upgradeCapId,
|
|
498
499
|
version,
|
|
@@ -517,6 +518,10 @@ async function publishContract(
|
|
|
517
518
|
config.original_dubhe_package_id =
|
|
518
519
|
dubheConfig.name === 'dubhe' ? packageId : await getOriginalDubhePackageId(network);
|
|
519
520
|
config.start_checkpoint = startCheckpoint;
|
|
521
|
+
// Canonical dapp_key type string: stable across upgrades, no "0x" prefix, padded to 64 hex chars.
|
|
522
|
+
// Matches the Move type_name::with_defining_ids<DappKey>().into_string() format.
|
|
523
|
+
const pkgHex = packageId.replace(/^0x/i, '').padStart(64, '0');
|
|
524
|
+
config.dapp_key = `${pkgHex}::dapp_key::DappKey`;
|
|
520
525
|
// Persist the DappStorage object ID so store-config can include it in deployment.ts
|
|
521
526
|
// and upgrade transactions can reference it without reading from .history.
|
|
522
527
|
if (dappStorageId) {
|
|
@@ -601,8 +606,6 @@ export async function publishDubheFramework(
|
|
|
601
606
|
|
|
602
607
|
let modules: any, dependencies: any;
|
|
603
608
|
try {
|
|
604
|
-
// For localnet: use --build-env testnet (no pubfile needed ā dubhe has no local deps).
|
|
605
|
-
// For testnet/mainnet: use -e <network> as usual.
|
|
606
609
|
[modules, dependencies] = buildContract(projectPath, network);
|
|
607
610
|
} finally {
|
|
608
611
|
// Always restore Published.toml and Move.toml (successful build or error)
|
|
@@ -683,6 +686,7 @@ export async function publishDubheFramework(
|
|
|
683
686
|
network,
|
|
684
687
|
startCheckpoint,
|
|
685
688
|
packageId,
|
|
689
|
+
packageId, // originalPackageId: first publish, original == current
|
|
686
690
|
dappHubId,
|
|
687
691
|
upgradeCapId,
|
|
688
692
|
version,
|
package/src/utils/storeConfig.ts
CHANGED
|
@@ -3,9 +3,19 @@ import { dirname } from 'path';
|
|
|
3
3
|
import { DubheConfig } from '@0xobelisk/sui-common';
|
|
4
4
|
import { getDeploymentJson, getDubheDappHubId, getOriginalDubhePackageId } from './utils';
|
|
5
5
|
|
|
6
|
+
/** Derive the stable dapp_key type string from the original (genesis) package ID.
|
|
7
|
+
* Matches the Move-side `type_name::with_defining_ids<DappKey>().into_string()` output:
|
|
8
|
+
* "<hex64>::dapp_key::DappKey" (no "0x" prefix, address zero-padded to 64 hex chars).
|
|
9
|
+
*/
|
|
10
|
+
function buildDappKey(originalPackageId: string): string {
|
|
11
|
+
const hex = originalPackageId.replace(/^0x/i, '').padStart(64, '0');
|
|
12
|
+
return `${hex}::dapp_key::DappKey`;
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
async function storeConfig(
|
|
7
16
|
network: string,
|
|
8
17
|
packageId: string,
|
|
18
|
+
originalPackageId: string,
|
|
9
19
|
dappStorageId: string,
|
|
10
20
|
outputPath: string
|
|
11
21
|
) {
|
|
@@ -25,10 +35,16 @@ async function storeConfig(
|
|
|
25
35
|
? `\n// Published package ID of the dubhe framework ā required for proxy operations.\nexport const FrameworkPackageId: string | undefined = '${frameworkPackageId}';\n`
|
|
26
36
|
: `\n// Published package ID of the dubhe framework ā required for proxy operations.\n// For testnet/mainnet the SDK resolves this automatically via getDefaultConfig().\nexport const FrameworkPackageId: string | undefined = undefined;\n`;
|
|
27
37
|
|
|
38
|
+
const dappKey = buildDappKey(originalPackageId);
|
|
39
|
+
|
|
28
40
|
const code = `type NetworkType = 'testnet' | 'mainnet' | 'devnet' | 'localnet';
|
|
29
41
|
|
|
30
42
|
export const Network: NetworkType = '${network}';
|
|
31
43
|
export const PackageId = '${packageId}';
|
|
44
|
+
/** The first-published (original) package ID ā stable across upgrades. Used for dapp_key and indexer filtering. */
|
|
45
|
+
export const OriginalPackageId = '${originalPackageId}';
|
|
46
|
+
/** Canonical dapp_key type string derived from OriginalPackageId. Pass to the Dubhe SDK and GraphQL queries. */
|
|
47
|
+
export const DappKey = '${dappKey}';
|
|
32
48
|
export const DappHubId = '${dappHubId}';
|
|
33
49
|
export const DappStorageId = '${dappStorageId}';
|
|
34
50
|
${frameworkIdLine}`;
|
|
@@ -57,9 +73,13 @@ export async function storeConfigHandler(
|
|
|
57
73
|
const path = process.cwd();
|
|
58
74
|
const contractPath = `${path}/src/${dubheConfig.name}`;
|
|
59
75
|
const deployment = await getDeploymentJson(contractPath, network);
|
|
76
|
+
// Prefer the persisted originalPackageId; fall back to packageId for old deployments
|
|
77
|
+
// that were created before this field was introduced.
|
|
78
|
+
const originalPackageId = deployment.originalPackageId ?? deployment.packageId;
|
|
60
79
|
await storeConfig(
|
|
61
80
|
deployment.network,
|
|
62
81
|
deployment.packageId,
|
|
82
|
+
originalPackageId,
|
|
63
83
|
deployment.dappStorageId ?? '',
|
|
64
84
|
outputPath
|
|
65
85
|
);
|
|
@@ -75,7 +75,8 @@ function replaceEnvField(
|
|
|
75
75
|
export async function upgradeHandler(
|
|
76
76
|
config: DubheConfig,
|
|
77
77
|
name: string,
|
|
78
|
-
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet'
|
|
78
|
+
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet',
|
|
79
|
+
bumpVersion: boolean = false
|
|
79
80
|
) {
|
|
80
81
|
await switchEnv(network);
|
|
81
82
|
|
|
@@ -103,13 +104,15 @@ export async function upgradeHandler(
|
|
|
103
104
|
});
|
|
104
105
|
|
|
105
106
|
const tables = pendingMigration.map((migration) => migration.name);
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
// Trigger migration when new resources were detected (schema change) OR when the
|
|
108
|
+
// caller explicitly requested a version bump (--bump-version flag). The latter
|
|
109
|
+
// covers breaking logic changes and security fixes that must invalidate old clients
|
|
110
|
+
// even though no new resources were added.
|
|
111
|
+
const needsMigration = tables.length > 0 || bumpVersion;
|
|
112
|
+
if (needsMigration) {
|
|
113
|
+
if (bumpVersion && tables.length === 0) {
|
|
114
|
+
console.log(chalk.yellow('--bump-version: forcing version bump with no schema changes.'));
|
|
115
|
+
}
|
|
113
116
|
appendMigrateFunction(projectPath, config.name, oldVersion + 1);
|
|
114
117
|
}
|
|
115
118
|
|
|
@@ -278,6 +281,7 @@ export async function upgradeHandler(
|
|
|
278
281
|
network,
|
|
279
282
|
startCheckpoint,
|
|
280
283
|
newPackageId,
|
|
284
|
+
original_published_id, // originalPackageId: stable across all upgrades
|
|
281
285
|
dappHubId,
|
|
282
286
|
upgradeCap,
|
|
283
287
|
oldVersion + 1,
|
|
@@ -287,16 +291,26 @@ export async function upgradeHandler(
|
|
|
287
291
|
dappStorageId || undefined
|
|
288
292
|
);
|
|
289
293
|
|
|
290
|
-
// Only run the migration transaction if there are pending schema changes
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
// new
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
294
|
+
// Only run the migration transaction if there are pending schema changes or a
|
|
295
|
+
// forced version bump was requested via --bump-version.
|
|
296
|
+
// A pure "bug-fix" upgrade with no new fields and no --bump-version flag does
|
|
297
|
+
// not need a migration call ā old and new package can coexist safely.
|
|
298
|
+
if (needsMigration) {
|
|
299
|
+
if (pendingMigration.length > 0) {
|
|
300
|
+
console.log(`\nš Starting Migration Process...`);
|
|
301
|
+
pendingMigration.forEach((migration) => {
|
|
302
|
+
console.log('š Added Fields:', JSON.stringify(migration, null, 2));
|
|
303
|
+
});
|
|
304
|
+
} else {
|
|
305
|
+
console.log(`\nš Starting Migration Process (forced version bump)...`);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// On localnet the indexer may lag behind the chain; give it time to register
|
|
309
|
+
// the newly upgraded package before the migration transaction references it.
|
|
310
|
+
// On testnet/mainnet the validator processes state immediately ā no delay needed.
|
|
311
|
+
if (network === 'localnet') {
|
|
312
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
313
|
+
}
|
|
300
314
|
|
|
301
315
|
if (!dappStorageId) {
|
|
302
316
|
console.warn(
|
|
@@ -314,8 +328,7 @@ export async function upgradeHandler(
|
|
|
314
328
|
arguments: [
|
|
315
329
|
migrateTx.object(dappHubId),
|
|
316
330
|
migrateTx.object(dappStorageId),
|
|
317
|
-
migrateTx.pure.address(newPackageId)
|
|
318
|
-
migrateTx.pure.u32(newVersion)
|
|
331
|
+
migrateTx.pure.address(newPackageId)
|
|
319
332
|
]
|
|
320
333
|
});
|
|
321
334
|
|
|
@@ -335,7 +348,9 @@ export async function upgradeHandler(
|
|
|
335
348
|
console.log(`\nā
No schema changes ā migration step skipped.`);
|
|
336
349
|
// Brief delay to allow localnet to index the upgraded package before
|
|
337
350
|
// subsequent on-chain queries.
|
|
338
|
-
|
|
351
|
+
if (network === 'localnet') {
|
|
352
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
353
|
+
}
|
|
339
354
|
}
|
|
340
355
|
} catch (error: any) {
|
|
341
356
|
// Restore Published.toml to original state on failure (persistent networks only)
|