@blocklet/cli 1.16.33 → 1.16.34-beta-20241120-080738-bbbe036c
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/README.md +32 -25
- package/bin/blocklet.js +292 -1
- package/config.example.yml +33 -0
- package/lib/arcblock.js +53 -0
- package/lib/commands/blocklet/add.js +124 -0
- package/lib/commands/blocklet/assets/git-ignore +28 -0
- package/lib/commands/blocklet/assets/index.html +9 -0
- package/lib/commands/blocklet/assets/index.js +14 -0
- package/lib/commands/blocklet/assets/logo.png +0 -0
- package/lib/commands/blocklet/bundle/bundle.js +184 -0
- package/lib/commands/blocklet/bundle/bundlers/blocklet.js +138 -0
- package/lib/commands/blocklet/bundle/bundlers/changelog.js +100 -0
- package/lib/commands/blocklet/bundle/bundlers/logo.js +56 -0
- package/lib/commands/blocklet/bundle/bundlers/markdown.js +241 -0
- package/lib/commands/blocklet/bundle/bundlers/preference.js +50 -0
- package/lib/commands/blocklet/bundle/bundlers/readme.js +43 -0
- package/lib/commands/blocklet/bundle/bundlers/screenshots.js +94 -0
- package/lib/commands/blocklet/bundle/bundlers/simple.js +70 -0
- package/lib/commands/blocklet/bundle/compact/bundle-compact-file.js +48 -0
- package/lib/commands/blocklet/bundle/compact/bundle-merge-extra.js +66 -0
- package/lib/commands/blocklet/bundle/compact/default-external.js +5 -0
- package/lib/commands/blocklet/bundle/compact/index.js +88 -0
- package/lib/commands/blocklet/bundle/index.js +139 -0
- package/lib/commands/blocklet/bundle/pack.js +8 -0
- package/lib/commands/blocklet/bundle/parse-external-dependencies.js +97 -0
- package/lib/commands/blocklet/bundle/simple/index.js +62 -0
- package/lib/commands/blocklet/bundle/zip/archive.js +35 -0
- package/lib/commands/blocklet/bundle/zip/dependencies.js +333 -0
- package/lib/commands/blocklet/bundle/zip/index.js +165 -0
- package/lib/commands/blocklet/bundle/zip/main.js +124 -0
- package/lib/commands/blocklet/bundle/zip/node.js +59 -0
- package/lib/commands/blocklet/bundle/zip/resolve.js +93 -0
- package/lib/commands/blocklet/cleanup.js +52 -0
- package/lib/commands/blocklet/config.js +108 -0
- package/lib/commands/blocklet/connect.js +87 -0
- package/lib/commands/blocklet/create.js +38 -0
- package/lib/commands/blocklet/deploy.js +435 -0
- package/lib/commands/blocklet/dev.js +1000 -0
- package/lib/commands/blocklet/document.js +39 -0
- package/lib/commands/blocklet/exec.js +106 -0
- package/lib/commands/blocklet/init.js +300 -0
- package/lib/commands/blocklet/meta.js +22 -0
- package/lib/commands/blocklet/remove.js +35 -0
- package/lib/commands/blocklet/test.js +201 -0
- package/lib/commands/blocklet/upload.js +105 -0
- package/lib/commands/blocklet/version.js +81 -0
- package/lib/commands/server/cleanup.js +32 -0
- package/lib/commands/server/command.js +131 -0
- package/lib/commands/server/info.js +92 -0
- package/lib/commands/server/init.js +433 -0
- package/lib/commands/server/logs.js +99 -0
- package/lib/commands/server/rescue.js +71 -0
- package/lib/commands/server/start.js +821 -0
- package/lib/commands/server/status.js +107 -0
- package/lib/commands/server/stop.js +163 -0
- package/lib/commands/server/upgrade.js +123 -0
- package/lib/constant.js +21 -2
- package/lib/debug.js +20 -0
- package/lib/manager/config.js +122 -0
- package/lib/manager/deploy.js +75 -0
- package/lib/manager/index.js +23 -0
- package/lib/manager/process.js +47 -0
- package/lib/node.js +214 -0
- package/lib/port.js +19 -0
- package/lib/postinstall.js +3 -0
- package/lib/process/daemon.js +196 -0
- package/lib/process/service.js +86 -0
- package/lib/ui.js +137 -0
- package/lib/util/blocklet/config.js +78 -0
- package/lib/util/blocklet/env.js +172 -0
- package/lib/util/blocklet/meta.js +36 -0
- package/lib/util/blocklet/payment.js +88 -0
- package/lib/util/blocklet/sign.js +21 -0
- package/lib/util/blocklet/tar.js +119 -0
- package/lib/util/convert-to-nosources-sourcemap.js +37 -0
- package/lib/util/docker-status-log.js +17 -0
- package/lib/util/exit-when-server-stopped.js +44 -0
- package/lib/util/get-cli-binary-name.js +8 -0
- package/lib/util/get-download-bundle-step.js +36 -0
- package/lib/util/get-service-instance-number.js +12 -0
- package/lib/util/index.js +626 -0
- package/lib/util/print-error.js +11 -0
- package/lib/util/print.js +9 -0
- package/lib/util/what-uri.js +40 -0
- package/package.json +123 -27
- package/lib/run.d.ts +0 -2
- package/lib/run.js +0 -73
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const prettyMs = require('pretty-ms-i18n');
|
|
3
|
+
const upperFirst = require('lodash/upperFirst');
|
|
4
|
+
const isEmpty = require('lodash/isEmpty');
|
|
5
|
+
const Table = require('cli-table3');
|
|
6
|
+
const pm2 = require('@abtnode/util/lib/async-pm2');
|
|
7
|
+
const { encode: encodeBase32 } = require('@abtnode/util/lib/base32');
|
|
8
|
+
const { fromBlockletStatus } = require('@blocklet/constant');
|
|
9
|
+
const { PROCESS_NAME_DAEMON, DEFAULT_DID_DOMAIN } = require('@abtnode/constant');
|
|
10
|
+
|
|
11
|
+
const { print, printWarning, printSuccess, printAccessUrls, getDaemonAccessUrls } = require('../../util');
|
|
12
|
+
const { getNode } = require('../../node');
|
|
13
|
+
const { wrapSpinner } = require('../../ui');
|
|
14
|
+
|
|
15
|
+
const getProcessInfo = (name) =>
|
|
16
|
+
new Promise((resolve, reject) => {
|
|
17
|
+
pm2.describe(name, (err, [info]) => {
|
|
18
|
+
if (err) {
|
|
19
|
+
return reject(err);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return resolve(info);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const statusMap = {
|
|
27
|
+
online: 'running',
|
|
28
|
+
launching: 'starting',
|
|
29
|
+
errored: 'error',
|
|
30
|
+
stopping: 'stopping',
|
|
31
|
+
stopped: 'stopped',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const colorMap = {
|
|
35
|
+
running: 'green',
|
|
36
|
+
error: 'red',
|
|
37
|
+
launching: 'yellow',
|
|
38
|
+
stopping: 'orange',
|
|
39
|
+
stopped: 'gray',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
exports.run = async () => {
|
|
43
|
+
const { node, getBaseUrls } = await getNode({ dir: process.cwd() });
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const info = await getProcessInfo(PROCESS_NAME_DAEMON);
|
|
47
|
+
if (!info) {
|
|
48
|
+
print('Blocklet Server is not started');
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const nodeInfo = await node.getNodeInfo();
|
|
53
|
+
const status = statusMap[info.pm2_env.status];
|
|
54
|
+
const statusFn = chalk[colorMap[status]];
|
|
55
|
+
const time = status === 'running' ? ` (${statusFn(prettyMs(Date.now() - Number(info.pm2_env.pm_uptime)))})` : '';
|
|
56
|
+
print();
|
|
57
|
+
print(`Blocklet Server status: ${statusFn(upperFirst(status))}${time}`);
|
|
58
|
+
print(`Blocklet Server mode: ${chalk.cyan(upperFirst(nodeInfo.mode))}`);
|
|
59
|
+
print(`Blocklet Server Data Directory: ${chalk.cyan(info.pm2_env.env.ABT_NODE_DATA_DIR)}`);
|
|
60
|
+
|
|
61
|
+
print();
|
|
62
|
+
if (nodeInfo.initialized) {
|
|
63
|
+
let blocklets = [];
|
|
64
|
+
|
|
65
|
+
await wrapSpinner('Fetching the blocklets...', async () => {
|
|
66
|
+
blocklets = await node.getBlocklets({ includeRuntimeInfo: false });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (blocklets.length) {
|
|
70
|
+
const table = new Table({
|
|
71
|
+
head: ['Name', 'Status', 'URL'],
|
|
72
|
+
style: { 'padding-left': 1, head: ['cyan', 'bold'] },
|
|
73
|
+
colWidths: [30, 15, 70],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
print('Blocklets Status:');
|
|
77
|
+
// eslint-disable-next-line no-shadow
|
|
78
|
+
blocklets.forEach(({ meta, status }) =>
|
|
79
|
+
table.push([
|
|
80
|
+
meta.title,
|
|
81
|
+
fromBlockletStatus(status),
|
|
82
|
+
`https://${encodeBase32(meta.did)}.${DEFAULT_DID_DOMAIN}`,
|
|
83
|
+
])
|
|
84
|
+
);
|
|
85
|
+
print(table.toString());
|
|
86
|
+
} else {
|
|
87
|
+
printWarning('No blocklets installed yet.');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (status === 'running') {
|
|
92
|
+
print();
|
|
93
|
+
const accessUrls = await getDaemonAccessUrls({ info: nodeInfo, getBaseUrls });
|
|
94
|
+
|
|
95
|
+
if (!isEmpty(accessUrls)) {
|
|
96
|
+
printSuccess('You can access your blocklet server with either of the following URLs:');
|
|
97
|
+
printAccessUrls(accessUrls);
|
|
98
|
+
} else {
|
|
99
|
+
printWarning('No accessible URL found.');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error(err);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
process.exit(0);
|
|
107
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const capitalize = require('lodash/capitalize');
|
|
3
|
+
|
|
4
|
+
const { clearRouterByConfigKeyword } = require('@abtnode/router-provider');
|
|
5
|
+
const tryWithTimeout = require('@abtnode/util/lib/try-with-timeout');
|
|
6
|
+
const sleep = require('@abtnode/util/lib/sleep');
|
|
7
|
+
const pm2 = require('@abtnode/util/lib/async-pm2');
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
PROCESS_NAME_DAEMON,
|
|
11
|
+
PROCESS_NAME_SERVICE,
|
|
12
|
+
PROCESS_NAME_EVENT_HUB,
|
|
13
|
+
CONFIG_FOLDER_NAME,
|
|
14
|
+
CONFIG_FOLDER_NAME_OLD,
|
|
15
|
+
EVENTS,
|
|
16
|
+
SERVER_STATUS,
|
|
17
|
+
} = require('@abtnode/constant');
|
|
18
|
+
|
|
19
|
+
const { BLOCKLET_MODES, BlockletStatus } = require('@blocklet/constant');
|
|
20
|
+
|
|
21
|
+
const debug = require('../../debug')('kill');
|
|
22
|
+
const {
|
|
23
|
+
printError,
|
|
24
|
+
printInfo,
|
|
25
|
+
printSuccess,
|
|
26
|
+
printWarning,
|
|
27
|
+
stopRouting,
|
|
28
|
+
getCLICommandName,
|
|
29
|
+
killPm2Process,
|
|
30
|
+
} = require('../../util');
|
|
31
|
+
const { getNode } = require('../../node');
|
|
32
|
+
const { checkRunning } = require('../../manager');
|
|
33
|
+
const { wrapSpinner } = require('../../ui');
|
|
34
|
+
|
|
35
|
+
exports.run = async ({ force = false }) => {
|
|
36
|
+
if (force) {
|
|
37
|
+
try {
|
|
38
|
+
const results = await Promise.all([
|
|
39
|
+
clearRouterByConfigKeyword(CONFIG_FOLDER_NAME),
|
|
40
|
+
clearRouterByConfigKeyword(CONFIG_FOLDER_NAME_OLD),
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
const clearedProvider = results.find(Boolean);
|
|
44
|
+
if (clearedProvider) {
|
|
45
|
+
printSuccess(`${capitalize(clearedProvider)} router is stopped successfully`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await pm2.killDaemonAsync();
|
|
49
|
+
printSuccess('Blocklet Server related processes stopped successfully');
|
|
50
|
+
} catch (error) {
|
|
51
|
+
printError(`Blocklet Server related processes stop failed: ${error.message}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const { node, publishEvent } = await getNode({ dir: process.cwd() });
|
|
58
|
+
|
|
59
|
+
const isRunning = await checkRunning();
|
|
60
|
+
if (!isRunning) {
|
|
61
|
+
printWarning('Blocklet Server is not running!');
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const stopOnReady = () =>
|
|
66
|
+
new Promise((resolve) => {
|
|
67
|
+
node.onReady(async () => {
|
|
68
|
+
let blocklets = [];
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
blocklets = await node.getBlocklets({ includeRuntimeInfo: false });
|
|
72
|
+
} catch (err) {
|
|
73
|
+
printWarning('Blocklets ignored because:', err.message);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Exit early if we have running blocklets in dev mode
|
|
77
|
+
// The node can only be stopped if no blocklet running in dev mode exist
|
|
78
|
+
const devBlocklet = blocklets.find((x) => x.mode === BLOCKLET_MODES.DEVELOPMENT);
|
|
79
|
+
if (devBlocklet && devBlocklet.status === BlockletStatus.running) {
|
|
80
|
+
printError(
|
|
81
|
+
`Unable to stop Blocklet Server, please stop the development by pressing ${chalk.cyan(
|
|
82
|
+
'Ctrl + C'
|
|
83
|
+
)} in the terminal of ${chalk.cyan(devBlocklet.meta.name)}`
|
|
84
|
+
);
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
await wrapSpinner('Sending shutdown notification to web dashboard users', async () => {
|
|
90
|
+
await publishEvent(EVENTS.NODE_STOPPED, {});
|
|
91
|
+
await sleep(2000);
|
|
92
|
+
}); // prettier-ignore
|
|
93
|
+
} catch (err) {
|
|
94
|
+
printError(`Failed to publish stop event to socket server: ${err.message}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const info = await node.getNodeInfo();
|
|
98
|
+
|
|
99
|
+
debug('blocklets to kill', blocklets);
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
await stopRouting({ info, routerDir: node.dataDirs.router });
|
|
103
|
+
printSuccess('Routing engine is stopped successfully');
|
|
104
|
+
} catch (err) {
|
|
105
|
+
printError('Failed to stop routing engine', err.message);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (let i = 0; i < blocklets.length; i++) {
|
|
109
|
+
const { title, did } = blocklets[i].meta;
|
|
110
|
+
if ([BlockletStatus.running, BlockletStatus.starting].includes(blocklets[i].status) === false) {
|
|
111
|
+
// eslint-disable-next-line no-continue
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
// eslint-disable-next-line no-await-in-loop
|
|
116
|
+
await node.stopBlocklet({ did, updateStatus: false, silent: true });
|
|
117
|
+
printSuccess(`Blocklet ${title} is stopped successfully`);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
printError(`Failed to stop blocklet ${title}`, err.message);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const actions = [
|
|
124
|
+
{ action: 'kill', processName: PROCESS_NAME_DAEMON },
|
|
125
|
+
{ action: 'kill', processName: PROCESS_NAME_SERVICE },
|
|
126
|
+
{ action: 'kill', processName: PROCESS_NAME_EVENT_HUB },
|
|
127
|
+
{ action: 'updateStatus' },
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
for (let i = 0; i < actions.length; i++) {
|
|
131
|
+
const { action, processName } = actions[i];
|
|
132
|
+
if (action === 'kill') {
|
|
133
|
+
try {
|
|
134
|
+
// eslint-disable-next-line no-await-in-loop
|
|
135
|
+
await killPm2Process(processName);
|
|
136
|
+
printSuccess(`${processName} is stopped successfully`);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
printError(`Failed to stop ${processName}`, err.message);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (action === 'updateStatus') {
|
|
143
|
+
// eslint-disable-next-line no-await-in-loop
|
|
144
|
+
await node.updateNodeStatus(SERVER_STATUS.STOPPED);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
printSuccess('Done!');
|
|
149
|
+
resolve(true);
|
|
150
|
+
process.exit(0);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const timeout = 60 * 1000 * 5; // 5 minute
|
|
155
|
+
try {
|
|
156
|
+
await tryWithTimeout(stopOnReady, timeout);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
printError(`Blocklet Server can not stop within ${timeout} ms`);
|
|
159
|
+
printInfo(`You can stop the node with ${chalk.cyan(`${getCLICommandName()} stop --force`)}`);
|
|
160
|
+
} finally {
|
|
161
|
+
process.exit(0);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const shell = require('shelljs');
|
|
3
|
+
const semver = require('semver');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
const { canReadAndWriteDir } = require('@abtnode/util/lib/fs');
|
|
8
|
+
const { getInstaller, getInstallCommands } = require('@abtnode/util/lib/get-installer');
|
|
9
|
+
const {
|
|
10
|
+
print,
|
|
11
|
+
printError,
|
|
12
|
+
printInfo,
|
|
13
|
+
printSuccess,
|
|
14
|
+
printWarning,
|
|
15
|
+
getCLIBinaryName,
|
|
16
|
+
getCLICommandName,
|
|
17
|
+
} = require('../../util');
|
|
18
|
+
const { version, name } = require('../../../package.json');
|
|
19
|
+
const { checkRunning, getRunningConfigDataDir } = require('../../manager');
|
|
20
|
+
const debug = require('../../debug')('upgrade');
|
|
21
|
+
|
|
22
|
+
// auto throw error when sync run shell.xxx
|
|
23
|
+
// must run sync
|
|
24
|
+
const wrap =
|
|
25
|
+
(shellFn) =>
|
|
26
|
+
(...args) => {
|
|
27
|
+
const { code, stdout, stderr } = shellFn.apply(shell, args);
|
|
28
|
+
if (code !== 0) {
|
|
29
|
+
throw new Error(stderr);
|
|
30
|
+
}
|
|
31
|
+
return stdout;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const getLatestVersion = () => {
|
|
35
|
+
const stdout = wrap(shell.exec)(`npm show ${name} version`, {
|
|
36
|
+
silent: true,
|
|
37
|
+
});
|
|
38
|
+
return stdout.trim();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const checkUpdate = (current, latest) => semver.gt(latest, current);
|
|
42
|
+
|
|
43
|
+
const stopNode = () => {
|
|
44
|
+
printInfo('Stopping Blocklet Server ...');
|
|
45
|
+
wrap(shell.exec)(`${getCLICommandName()} stop`, {
|
|
46
|
+
silent: true,
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const installNode = () => {
|
|
51
|
+
const commandName = getCLICommandName();
|
|
52
|
+
const packageName = commandName === 'abtnode' ? '@abtnode/cli' : '@blocklet/cli';
|
|
53
|
+
const binaryName = getCLIBinaryName();
|
|
54
|
+
const installer = getInstaller(binaryName);
|
|
55
|
+
const commands = getInstallCommands({ packageName });
|
|
56
|
+
const command = commands[installer];
|
|
57
|
+
|
|
58
|
+
printInfo(`Installing Blocklet Server by ${chalk.cyan(command)} ...`);
|
|
59
|
+
|
|
60
|
+
wrap(shell.exec)(command, {
|
|
61
|
+
silent: false,
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const restartNode = (originalRunningDataDir) => {
|
|
66
|
+
const currentWorkingDataDir = process.cwd();
|
|
67
|
+
debug('original running data directory', originalRunningDataDir);
|
|
68
|
+
debug('current working data directory', currentWorkingDataDir);
|
|
69
|
+
|
|
70
|
+
printInfo('Restarting Blocklet Server ...');
|
|
71
|
+
|
|
72
|
+
if (!fs.existsSync(originalRunningDataDir)) {
|
|
73
|
+
printWarning('Original running data directory does not exists, abort restart!');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
wrap(shell.exec)(`${getCLICommandName()} start`, {
|
|
78
|
+
silent: false,
|
|
79
|
+
cwd: originalRunningDataDir,
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
exports.run = async () => {
|
|
84
|
+
// save current bin path
|
|
85
|
+
const binaryName = getCLIBinaryName();
|
|
86
|
+
const commandName = getCLICommandName();
|
|
87
|
+
const curBinPath = wrap(shell.which)(binaryName, { silent: true });
|
|
88
|
+
printInfo('Using blocklet server from', chalk.cyan(curBinPath));
|
|
89
|
+
|
|
90
|
+
// Check permissions
|
|
91
|
+
printInfo('Checking permissions...');
|
|
92
|
+
if (canReadAndWriteDir(path.dirname(curBinPath)) === false) {
|
|
93
|
+
printError('Seems you do not have permission to upgrade Blocklet Server version');
|
|
94
|
+
printInfo(`Maybe you can try run ${chalk.cyan(`sudo ${commandName} upgrade`)}`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const latestVersion = await getLatestVersion();
|
|
99
|
+
const needUpdate = await checkUpdate(version, latestVersion);
|
|
100
|
+
if (!needUpdate) {
|
|
101
|
+
print(`The current version ${version} is the latest version, no need to upgrade`);
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
print(`Current version is ${version}, found latest version ${latestVersion}`);
|
|
105
|
+
print('Begin upgrade');
|
|
106
|
+
|
|
107
|
+
// Do not start/stop the node when run as root
|
|
108
|
+
const isRunning = await checkRunning();
|
|
109
|
+
const runningDataDir = await getRunningConfigDataDir();
|
|
110
|
+
|
|
111
|
+
const needRestart = isRunning;
|
|
112
|
+
if (isRunning) {
|
|
113
|
+
await stopNode();
|
|
114
|
+
}
|
|
115
|
+
await installNode();
|
|
116
|
+
|
|
117
|
+
if (needRestart) {
|
|
118
|
+
await restartNode(runningDataDir);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
printSuccess(`Blocklet Server upgrade success, current version is ${latestVersion}`);
|
|
122
|
+
process.exit(0);
|
|
123
|
+
};
|
package/lib/constant.js
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
|
-
const
|
|
1
|
+
const os = require('os');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const ABT_NODE_MAX_CLUSTER_SIZE = Math.min(Math.max(Number(process.env.ABT_NODE_MAX_CLUSTER_SIZE) || 1, 1), 32);
|
|
5
|
+
|
|
6
|
+
const CLI_MODE = 'blocklet';
|
|
7
|
+
const NODE_ENV = process.env.NODE_ENV || 'production';
|
|
8
|
+
const ABT_NODE_HOME = path.join(os.homedir(), '.arcblock');
|
|
9
|
+
const PM2_HOME = path.join(ABT_NODE_HOME, 'abtnode');
|
|
10
|
+
const ABT_NODE_CONFIG_FILE = path.join(ABT_NODE_HOME, 'blocklet.ini');
|
|
11
|
+
const INSTALL_FILE_PATH = path.join(ABT_NODE_HOME, '.is-new-server-install');
|
|
12
|
+
const HELP_DOCS_GITHUB_CODESPACES_URL =
|
|
13
|
+
'https://www.arcblock.io/docs/blocklet-developer/en/develop-with-github-codespaces';
|
|
2
14
|
|
|
3
15
|
module.exports = {
|
|
4
|
-
|
|
16
|
+
ABT_NODE_MAX_CLUSTER_SIZE,
|
|
17
|
+
CLI_MODE,
|
|
18
|
+
NODE_ENV,
|
|
19
|
+
ABT_NODE_HOME,
|
|
20
|
+
PM2_HOME,
|
|
21
|
+
ABT_NODE_CONFIG_FILE,
|
|
22
|
+
INSTALL_FILE_PATH,
|
|
23
|
+
HELP_DOCS_GITHUB_CODESPACES_URL,
|
|
5
24
|
};
|
package/lib/debug.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
const debug = require('debug');
|
|
4
|
+
|
|
5
|
+
if (process.env.FORGE_DEBUG) {
|
|
6
|
+
debug.enable(process.env.FORGE_DEBUG);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const instance = (name) => {
|
|
10
|
+
const tmp = debug(`@blocklet/cli:${name || 'info'}`);
|
|
11
|
+
tmp.error = (...args) => {
|
|
12
|
+
if (tmp.enabled) {
|
|
13
|
+
console.error(...args);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return tmp;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
module.exports = instance;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const expandTilde = require('expand-tilde');
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
CONFIG_FILENAME,
|
|
7
|
+
CONFIG_FOLDER_NAME,
|
|
8
|
+
CONFIG_FILENAME_OLD,
|
|
9
|
+
CONFIG_FOLDER_NAME_OLD,
|
|
10
|
+
} = require('@abtnode/constant');
|
|
11
|
+
|
|
12
|
+
const _join = (prefix, file) => {
|
|
13
|
+
return path.join(expandTilde(prefix), file);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const joinConfigDir = (dir) => _join(dir, CONFIG_FOLDER_NAME);
|
|
17
|
+
|
|
18
|
+
const joinConfigFile = (dir) => _join(dir, CONFIG_FILENAME);
|
|
19
|
+
|
|
20
|
+
const joinKFile = (dir) => _join(dir, '.sock');
|
|
21
|
+
|
|
22
|
+
const getDataDirectoryByConfigFile = (configFile) => {
|
|
23
|
+
if (!configFile) {
|
|
24
|
+
throw new Error('configFile can not be empty');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return path.dirname(configFile);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const getConfigDirFromCurrentDir = (currentDir) => {
|
|
31
|
+
const configDir = _join(currentDir, CONFIG_FOLDER_NAME);
|
|
32
|
+
const configDirOld = _join(currentDir, CONFIG_FOLDER_NAME_OLD);
|
|
33
|
+
|
|
34
|
+
if (fs.existsSync(configDir)) {
|
|
35
|
+
return configDir;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (fs.existsSync(configDirOld)) {
|
|
39
|
+
return configDirOld;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return '';
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const getConfigDirFromAncestors = (currentDir) => {
|
|
46
|
+
let dir = fs.statSync(currentDir).isDirectory() ? currentDir : path.dirname(currentDir);
|
|
47
|
+
|
|
48
|
+
do {
|
|
49
|
+
const configDir = getConfigDirFromCurrentDir(dir);
|
|
50
|
+
if (configDir) {
|
|
51
|
+
return configDir;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
dir = path.dirname(dir);
|
|
55
|
+
} while (path.basename(dir) !== '');
|
|
56
|
+
|
|
57
|
+
return '';
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const getConfigFileFromAncestors = (dir) => {
|
|
61
|
+
const configDir = getConfigDirFromAncestors(dir);
|
|
62
|
+
if (!configDir) {
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (configDir.endsWith(CONFIG_FOLDER_NAME_OLD)) {
|
|
67
|
+
return _join(configDir, CONFIG_FILENAME_OLD);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return _join(configDir, CONFIG_FILENAME);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* get base configuration data directory
|
|
75
|
+
* @param {string} dir directory to check
|
|
76
|
+
*/
|
|
77
|
+
const getBaseConfigDataDirectory = (dir) => {
|
|
78
|
+
if (!dir || !fs.existsSync(dir)) {
|
|
79
|
+
throw new Error(`directory ${dir} does not exist`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (dir.includes(CONFIG_FOLDER_NAME) || dir.includes(CONFIG_FOLDER_NAME_OLD)) {
|
|
83
|
+
return dir;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return '';
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* validate the `dir` directory has valid data directory, if not, throw error
|
|
91
|
+
* @param {string} dir the directory to validate
|
|
92
|
+
*/
|
|
93
|
+
const verifyDataDirectory = (dir) => {
|
|
94
|
+
if (!dir) {
|
|
95
|
+
throw new Error('dir should not be empty');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!fs.existsSync(dir)) {
|
|
99
|
+
throw new Error(`directory ${dir} does not exist`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const configFile = path.join(dir, CONFIG_FILENAME);
|
|
103
|
+
const configFileOld = path.join(dir, CONFIG_FILENAME_OLD);
|
|
104
|
+
if (!fs.existsSync(configFile) && !fs.existsSync(configFileOld)) {
|
|
105
|
+
throw new Error(`the config file does not exist in ${dir}`);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const getDaemonLogDir = (baseDir) => path.join(baseDir, 'logs', '_abtnode');
|
|
110
|
+
|
|
111
|
+
module.exports = {
|
|
112
|
+
joinConfigDir,
|
|
113
|
+
joinConfigFile,
|
|
114
|
+
joinKFile,
|
|
115
|
+
getBaseConfigDataDirectory,
|
|
116
|
+
getConfigDirFromAncestors,
|
|
117
|
+
getConfigDirFromCurrentDir,
|
|
118
|
+
getConfigFileFromAncestors,
|
|
119
|
+
getDaemonLogDir,
|
|
120
|
+
getDataDirectoryByConfigFile,
|
|
121
|
+
verifyDataDirectory,
|
|
122
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { minimatch } = require('minimatch');
|
|
3
|
+
|
|
4
|
+
const { print, printInfo } = require('../util');
|
|
5
|
+
|
|
6
|
+
const checkMatch = (file, match) => {
|
|
7
|
+
let m = match;
|
|
8
|
+
if (!m.includes('*')) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
if (!m.endsWith('*')) {
|
|
12
|
+
return minimatch(file, m);
|
|
13
|
+
}
|
|
14
|
+
if (!m.endsWith('**')) {
|
|
15
|
+
m = `${m}*`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return minimatch(file, m);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const checkInclude = (list, f) =>
|
|
22
|
+
f === '.' ||
|
|
23
|
+
f === '' || // root folder maybe '.' and ''
|
|
24
|
+
list.some(
|
|
25
|
+
(x) =>
|
|
26
|
+
x.indexOf(f) === 0 || // f is 'website' && x is 'website/public'
|
|
27
|
+
f.indexOf(x) === 0 || // f is website/public/index.html && x is website/public
|
|
28
|
+
checkMatch(f, x) // f is hooks/pre-start.js && x is hooks/*
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Whether a file should be deployed
|
|
33
|
+
* @param {string} file a file will be deployed
|
|
34
|
+
* @param {object} opts
|
|
35
|
+
* @param {array<string>} opts.diffList
|
|
36
|
+
* @param {BundleType} opts.bundleType
|
|
37
|
+
* @param {array<string>} opts.staticList
|
|
38
|
+
* @return {boolean} Should this file be deployed
|
|
39
|
+
*/
|
|
40
|
+
const fileFilter = (file, opts = {}) => {
|
|
41
|
+
const { diffList, bundleType, staticList } = opts;
|
|
42
|
+
// only include diffList if has one
|
|
43
|
+
if (diffList) {
|
|
44
|
+
return checkInclude(diffList, file);
|
|
45
|
+
}
|
|
46
|
+
// only include staticList if bundleType is static
|
|
47
|
+
if (bundleType === 'static') {
|
|
48
|
+
if (!staticList) {
|
|
49
|
+
throw new Error('staticList should not be empty when bundleType is static');
|
|
50
|
+
}
|
|
51
|
+
return checkInclude(staticList, file);
|
|
52
|
+
}
|
|
53
|
+
// include all files
|
|
54
|
+
return true;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const printDeployFileInfo = (adds, changes, deletes) => {
|
|
58
|
+
const padLeft = ' - ';
|
|
59
|
+
printInfo(`Added Files: ${chalk.cyan(adds.length)}`);
|
|
60
|
+
adds.forEach((f) => print(padLeft, f));
|
|
61
|
+
if (typeof changes !== 'undefined') {
|
|
62
|
+
printInfo(`Changed Files: ${chalk.cyan(changes.length)}`);
|
|
63
|
+
changes.forEach((f) => print(padLeft, f));
|
|
64
|
+
}
|
|
65
|
+
if (typeof deletes !== 'undefined') {
|
|
66
|
+
printInfo(`Deleted Files: ${chalk.cyan(deletes.length)}`);
|
|
67
|
+
deletes.forEach((f) => print(padLeft, f));
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
fileFilter,
|
|
73
|
+
printDeployFileInfo,
|
|
74
|
+
checkMatch,
|
|
75
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const processManager = require('./process');
|
|
2
|
+
const configManager = require('./config');
|
|
3
|
+
const deployManager = require('./deploy');
|
|
4
|
+
|
|
5
|
+
const getConfigFile = async (configPathArg = process.cwd()) => {
|
|
6
|
+
const runningConfigFile = await processManager.getRunningConfigFile();
|
|
7
|
+
if (runningConfigFile) {
|
|
8
|
+
return runningConfigFile;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return configManager.getConfigFileFromAncestors(configPathArg);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const getConfigDir = async (configPathArg = process.cwd()) => {
|
|
15
|
+
const runningConfigDir = await processManager.getRunningConfigDataDir();
|
|
16
|
+
if (runningConfigDir) {
|
|
17
|
+
return runningConfigDir;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return configManager.getConfigDirFromAncestors(configPathArg);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
module.exports = { ...processManager, ...configManager, deployManager, getConfigFile, getConfigDir };
|