@iexec/iapp-maker 0.0.1-alpha-nightly-c808af829ce4b10cbbecdbc64ec7fa9b8cf3558d → 0.0.1-alpha-nightly-79e0105f6025d93833a45abc311c73fb2b5b6c26

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iexec/iapp-maker",
3
- "version": "0.0.1-alpha-nightly-c808af829ce4b10cbbecdbc64ec7fa9b8cf3558d",
3
+ "version": "0.0.1-alpha-nightly-79e0105f6025d93833a45abc311c73fb2b5b6c26",
4
4
  "description": "A CLI to guide you through the process of building an iExec iApp",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1,5 +1,6 @@
1
1
  import { readIAppConfig, writeIAppConfig } from '../utils/iAppConfigFile.js';
2
2
  import { CONFIG_FILE } from '../config/config.js';
3
+ import * as color from './color.js';
3
4
 
4
5
  /**
5
6
  * @returns {Promise<string | null>}
@@ -9,11 +10,15 @@ export async function askForAppSecret({ spinner }) {
9
10
  const { appSecret: savedAppSecret } = config;
10
11
 
11
12
  if (savedAppSecret === null) {
12
- spinner.log(`"No app secret" is configured (from "${CONFIG_FILE}")`);
13
+ spinner.log(
14
+ `"No app secret" is configured ${color.comment(`(from ${color.file(CONFIG_FILE)})`)}`
15
+ );
13
16
  return savedAppSecret;
14
17
  }
15
18
  if (savedAppSecret !== undefined) {
16
- spinner.log(`Using saved appSecret (from "${CONFIG_FILE}")`);
19
+ spinner.log(
20
+ `Using saved appSecret ${color.comment(`(from ${color.file(CONFIG_FILE)})`)}`
21
+ );
17
22
  return savedAppSecret;
18
23
  }
19
24
 
@@ -37,7 +42,7 @@ export async function askForAppSecret({ spinner }) {
37
42
  if (saveNull) {
38
43
  config.appSecret = null;
39
44
  await writeIAppConfig(config);
40
- spinner.log(`"No appSecret" choice saved to "${CONFIG_FILE}"`);
45
+ spinner.log(`"No appSecret" choice saved to ${color.file(CONFIG_FILE)}`);
41
46
  }
42
47
  return null;
43
48
  }
@@ -61,7 +66,7 @@ export async function askForAppSecret({ spinner }) {
61
66
  if (saveAppSecret) {
62
67
  config.appSecret = appSecret;
63
68
  await writeIAppConfig(config);
64
- spinner.log(`appSecret saved to "${CONFIG_FILE}"`);
69
+ spinner.log(`appSecret saved to ${color.file(CONFIG_FILE)}`);
65
70
  }
66
71
 
67
72
  return appSecret;
@@ -1,40 +1,37 @@
1
- import chalk from 'chalk';
2
1
  import { readIAppConfig, writeIAppConfig } from '../utils/iAppConfigFile.js';
3
2
  import { CONFIG_FILE } from '../config/config.js';
3
+ import * as color from './color.js';
4
4
 
5
5
  export async function askForDockerhubAccessToken({ spinner }) {
6
6
  const config = await readIAppConfig();
7
7
 
8
8
  const dockerhubAccessToken = config.dockerhubAccessToken || '';
9
9
  if (dockerhubAccessToken) {
10
- spinner.log(`Using saved dockerhubAccessToken (from "${CONFIG_FILE}")`);
10
+ spinner.log(
11
+ `Using saved dockerhubAccessToken ${color.comment(`(from ${color.file(CONFIG_FILE)})`)}`
12
+ );
11
13
  return dockerhubAccessToken;
12
14
  }
13
15
 
14
- spinner.log(
15
- 'Go to your docker hub account: https://hub.docker.com/settings/security'
16
- );
17
- spinner.log('click on "Personal access tokens"');
18
- spinner.log('click on "Generate new token"');
19
- spinner.log('you can name it "Test iExec iApp CLI"');
20
- spinner.log('and select "Read & Write" Access permissions');
21
16
  const { dockerHubAccessTokenAnswer } = await spinner.prompt({
22
17
  type: 'password',
23
18
  name: 'dockerHubAccessTokenAnswer',
24
- message:
25
- 'What is your DockerHub access token? (It will be used to push the docker image to your account)',
19
+ message: `What is your DockerHub access token?
20
+ ${color.promptHelper(`You need to provide a Personal access token with ${color.emphasis('Read & Write')} access
21
+ This token will be used to push your iApp docker images to your account
22
+ You can create a new token by visiting ${color.link('https://app.docker.com/settings/personal-access-tokens/create')}`)}`,
26
23
  });
27
24
 
28
25
  // TODO check token against API
29
26
  if (!/[a-zA-Z0-9-]+/.test(dockerHubAccessTokenAnswer)) {
30
- spinner.log(chalk.red('Invalid DockerHub access token.'));
27
+ spinner.log(color.error('Invalid DockerHub access token.'));
31
28
  return askForDockerhubAccessToken({ spinner });
32
29
  }
33
30
 
34
31
  // Save it into JSON config file
35
32
  config.dockerhubAccessToken = dockerHubAccessTokenAnswer;
36
33
  await writeIAppConfig(config);
37
- spinner.log(`dockerhubAccessToken saved to "${CONFIG_FILE}"`);
34
+ spinner.log(`dockerhubAccessToken saved to ${color.file(CONFIG_FILE)}`);
38
35
 
39
36
  return dockerHubAccessTokenAnswer;
40
37
  }
@@ -1,6 +1,6 @@
1
- import chalk from 'chalk';
2
1
  import { readIAppConfig, writeIAppConfig } from '../utils/iAppConfigFile.js';
3
2
  import { CONFIG_FILE } from '../config/config.js';
3
+ import * as color from './color.js';
4
4
 
5
5
  export async function askForDockerhubUsername({ spinner }) {
6
6
  const config = await readIAppConfig();
@@ -8,7 +8,7 @@ export async function askForDockerhubUsername({ spinner }) {
8
8
  const dockerhubUsername = config.dockerhubUsername || '';
9
9
  if (dockerhubUsername) {
10
10
  spinner.log(
11
- `Using saved dockerhubUsername (from "${CONFIG_FILE}") -> ${dockerhubUsername}`
11
+ `Using saved dockerhubUsername ${color.comment(`(from ${color.file(CONFIG_FILE)})`)} -> ${dockerhubUsername}`
12
12
  );
13
13
  return dockerhubUsername;
14
14
  }
@@ -16,15 +16,14 @@ export async function askForDockerhubUsername({ spinner }) {
16
16
  const { dockerHubUserNameAnswer } = await spinner.prompt({
17
17
  type: 'text',
18
18
  name: 'dockerHubUserNameAnswer',
19
- message:
20
- 'What is your username on DockerHub? (It will be used to properly tag the Docker image)',
19
+ message: `What is your username on DockerHub? ${color.promptHelper('(It will be used to properly tag the Docker image)')}`,
21
20
  });
22
21
 
23
22
  // TODO check username against API
24
23
  if (!/[a-zA-Z0-9-]+/.test(dockerHubUserNameAnswer)) {
25
24
  spinner.log(
26
- chalk.red(
27
- 'Invalid DockerHub username. Login to https://hub.docker.com/repositories, your username is what gets added to this URL.'
25
+ color.error(
26
+ `Invalid DockerHub username. Login to ${color.link('https://hub.docker.com')} to check your username.`
28
27
  )
29
28
  );
30
29
  return askForDockerhubUsername({ spinner });
@@ -33,7 +32,7 @@ export async function askForDockerhubUsername({ spinner }) {
33
32
  // Save it into JSON config file
34
33
  config.dockerhubUsername = dockerHubUserNameAnswer;
35
34
  await writeIAppConfig(config);
36
- spinner.log(`dockerhubUsername saved to "${CONFIG_FILE}"`);
35
+ spinner.log(`dockerhubUsername saved to ${color.file(CONFIG_FILE)}`);
37
36
 
38
37
  return dockerHubUserNameAnswer;
39
38
  }
@@ -1,13 +1,15 @@
1
- import chalk from 'chalk';
1
+ import { isAddress } from 'ethers';
2
2
  import { readIAppConfig, writeIAppConfig } from '../utils/iAppConfigFile.js';
3
3
  import { CONFIG_FILE } from '../config/config.js';
4
- import { isAddress } from 'ethers';
4
+ import * as color from './color.js';
5
5
 
6
6
  export async function askForWalletAddress({ spinner }) {
7
7
  const config = await readIAppConfig();
8
8
  const walletAddress = config.walletAddress || '';
9
9
  if (walletAddress) {
10
- spinner.log(`Using saved walletAddress (from "${CONFIG_FILE}")`);
10
+ spinner.log(
11
+ `Using saved walletAddress ${color.comment(`(from ${color.file(CONFIG_FILE)})`)}`
12
+ );
11
13
  return walletAddress;
12
14
  }
13
15
 
@@ -19,7 +21,7 @@ export async function askForWalletAddress({ spinner }) {
19
21
 
20
22
  if (!isAddress(walletAddressAnswer)) {
21
23
  spinner.log(
22
- chalk.red(
24
+ color.error(
23
25
  'Invalid wallet address. Ex: 0xC248cCe0a656a90F2Ae27ccfa8Bd11843c8e0f3c'
24
26
  )
25
27
  );
@@ -29,7 +31,7 @@ export async function askForWalletAddress({ spinner }) {
29
31
  // Save it into JSON config file
30
32
  config.walletAddress = walletAddressAnswer;
31
33
  await writeIAppConfig(config);
32
- spinner.log(`walletAddress saved to "${CONFIG_FILE}"`);
34
+ spinner.log(`walletAddress saved to ${color.file(CONFIG_FILE)}`);
33
35
 
34
36
  return walletAddressAnswer;
35
37
  }
@@ -1,14 +1,16 @@
1
- import chalk from 'chalk';
2
1
  import { Wallet } from 'ethers';
3
2
  import { readIAppConfig, writeIAppConfig } from '../utils/iAppConfigFile.js';
4
3
  import { CONFIG_FILE } from '../config/config.js';
4
+ import * as color from './color.js';
5
5
 
6
6
  export async function askForWalletPrivateKey({ spinner }) {
7
7
  const config = await readIAppConfig();
8
8
 
9
9
  const walletPrivateKey = config.walletPrivateKey || '';
10
10
  if (walletPrivateKey) {
11
- spinner.log(`Using saved walletPrivateKey (from "${CONFIG_FILE}")`);
11
+ spinner.log(
12
+ `Using saved walletPrivateKey ${color.comment(`(from ${color.file(CONFIG_FILE)})`)}`
13
+ );
12
14
  return walletPrivateKey;
13
15
  }
14
16
 
@@ -22,7 +24,7 @@ export async function askForWalletPrivateKey({ spinner }) {
22
24
  try {
23
25
  new Wallet(walletPrivateKeyAnswer);
24
26
  } catch {
25
- spinner.log(chalk.red('Invalid wallet private key'));
27
+ spinner.log(color.error('Invalid wallet private key'));
26
28
  return askForWalletPrivateKey({ spinner });
27
29
  }
28
30
 
@@ -41,7 +43,7 @@ export async function askForWalletPrivateKey({ spinner }) {
41
43
 
42
44
  config.walletPrivateKey = walletPrivateKeyAnswer;
43
45
  await writeIAppConfig(config);
44
- spinner.log(`walletPrivateKey saved to "${CONFIG_FILE}"`);
46
+ spinner.log(`walletPrivateKey saved to ${color.file(CONFIG_FILE)}`);
45
47
 
46
48
  return walletPrivateKeyAnswer;
47
49
  }
@@ -1,12 +1,13 @@
1
1
  import { readdir } from 'node:fs/promises';
2
2
  import { getDeterministicOutputAsText } from '../utils/deterministicOutput.js';
3
+ import * as color from './color.js';
3
4
 
4
5
  export async function askShowResult({ spinner, outputPath }) {
5
6
  // Prompt user to view result
6
7
  const continueAnswer = await spinner.prompt({
7
8
  type: 'confirm',
8
9
  name: 'continue',
9
- message: `Would you like to see the result? (View ./${outputPath}/)`,
10
+ message: `Would you like to see the result? ${color.promptHelper(`(View ${color.file(`./${outputPath}/`)})`)}`,
10
11
  initial: true,
11
12
  });
12
13
  if (continueAnswer.continue) {
@@ -16,13 +17,13 @@ export async function askShowResult({ spinner, outputPath }) {
16
17
  spinner.warn('output directory is empty');
17
18
  } else {
18
19
  spinner.info(
19
- `output directory content:\n${files.map((file) => ' - ' + file).join('\n')}`
20
+ `output directory content:\n${files.map((file) => ' - ' + color.file(file)).join('\n')}`
20
21
  );
21
22
  // best effort display deterministic output file if it's an utf8 encoded file
22
23
  await getDeterministicOutputAsText({ outputPath })
23
24
  .then(({ text, path }) => {
24
25
  spinner.newLine();
25
- spinner.info(`${path}:\n${text}`);
26
+ spinner.info(`${color.file(path)}:\n${text}`);
26
27
  })
27
28
  .catch(() => {});
28
29
  }
@@ -0,0 +1,14 @@
1
+ import boxen from 'boxen';
2
+
3
+ export function hintBox(message) {
4
+ return boxen(message, {
5
+ padding: 1,
6
+ margin: 1,
7
+ borderStyle: 'round',
8
+ borderColor: 'cyan',
9
+ });
10
+ }
11
+
12
+ export function objectBox(message) {
13
+ return boxen(message, { margin: 1 });
14
+ }
@@ -20,3 +20,11 @@ export const emphasis = chalk.green;
20
20
  * use to color file name
21
21
  */
22
22
  export const file = chalk.cyan;
23
+ /**
24
+ * use to color web links
25
+ */
26
+ export const link = chalk.blue.underline;
27
+ /**
28
+ * use to color errors
29
+ */
30
+ export const error = chalk.red;
package/src/cmd/deploy.js CHANGED
@@ -15,6 +15,8 @@ import { askForWalletPrivateKey } from '../cli-helpers/askForWalletPrivateKey.js
15
15
  import { Wallet } from 'ethers';
16
16
  import { getIExecDebug } from '../utils/iexec.js';
17
17
  import { goToProjectRoot } from '../cli-helpers/goToProjectRoot.js';
18
+ import * as color from '../cli-helpers/color.js';
19
+ import { hintBox } from '../cli-helpers/box.js';
18
20
 
19
21
  export async function deploy() {
20
22
  const spinner = getSpinner();
@@ -67,7 +69,7 @@ export async function deploy() {
67
69
  tag: imageTag,
68
70
  progressCallback: (msg) => {
69
71
  buildLogs.push(msg); // do we want to show build logs after build is successful?
70
- spinner.text = spinner.text + msg;
72
+ spinner.text = spinner.text + color.comment(msg);
71
73
  },
72
74
  });
73
75
  spinner.succeed(`Docker image built (${imageId}) and tagged ${imageTag}`);
@@ -78,7 +80,7 @@ export async function deploy() {
78
80
  dockerhubAccessToken,
79
81
  dockerhubUsername,
80
82
  progressCallback: (msg) => {
81
- spinner.text = spinner.text + msg;
83
+ spinner.text = spinner.text + color.comment(msg);
82
84
  },
83
85
  });
84
86
  spinner.succeed(`Pushed image ${imageTag} on dockerhub`);
@@ -104,6 +106,12 @@ export async function deploy() {
104
106
  - Docker image: ${sconifiedImage}
105
107
  - iApp address: ${appContractAddress}`
106
108
  );
109
+
110
+ spinner.log(
111
+ hintBox(
112
+ `Run ${color.command(`iapp run ${appContractAddress}`)} to execute your iApp on an iExec TEE worker`
113
+ )
114
+ );
107
115
  } catch (error) {
108
116
  handleCliError({ spinner, error });
109
117
  }
package/src/cmd/init.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import chalk from 'chalk';
2
- import boxen from 'boxen';
3
2
  import figlet from 'figlet';
4
3
  import { mkdir } from 'node:fs/promises';
5
4
  import { folderExists } from '../utils/fs.utils.js';
@@ -8,6 +7,7 @@ import { getSpinner } from '../cli-helpers/spinner.js';
8
7
  import { handleCliError } from '../cli-helpers/handleCliError.js';
9
8
  import { generateWallet } from '../utils/generateWallet.js';
10
9
  import * as color from '../cli-helpers/color.js';
10
+ import { hintBox } from '../cli-helpers/box.js';
11
11
 
12
12
  const targetDir = 'hello-world';
13
13
 
@@ -170,14 +170,7 @@ export async function init() {
170
170
  ${color.command('$ iapp run <iapp-address>')}
171
171
  `;
172
172
 
173
- spinner.log(
174
- boxen(output, {
175
- padding: 1,
176
- margin: 1,
177
- borderStyle: 'round',
178
- borderColor: 'cyan',
179
- })
180
- );
173
+ spinner.log(hintBox(output));
181
174
  } catch (error) {
182
175
  handleCliError({ spinner, error });
183
176
  }
@@ -1,7 +1,5 @@
1
1
  import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
- import chalk from 'chalk';
4
- import boxen from 'boxen';
5
3
  import { getSpinner } from '../cli-helpers/spinner.js';
6
4
  import { fileExists } from '../utils/fs.utils.js';
7
5
  import { PROTECTED_DATA_MOCK_DIR } from '../config/config.js';
@@ -12,6 +10,8 @@ import {
12
10
  ALLOWED_KEY_NAMES_REGEXP,
13
11
  } from '../libs/dataprotector.js';
14
12
  import { goToProjectRoot } from '../cli-helpers/goToProjectRoot.js';
13
+ import * as color from '../cli-helpers/color.js';
14
+ import { hintBox, objectBox } from '../cli-helpers/box.js';
15
15
 
16
16
  export async function mockProtectedData() {
17
17
  const spinner = getSpinner();
@@ -163,7 +163,7 @@ export async function mockProtectedData() {
163
163
 
164
164
  spinner.info(
165
165
  `This is how your protectedData looks so far:
166
- ${boxen(JSON.stringify(dataSchema, null, 2), { margin: 1 })}`
166
+ ${objectBox(JSON.stringify(dataSchema, null, 2))}`
167
167
  );
168
168
 
169
169
  const { addMore } = await spinner.prompt([
@@ -196,7 +196,7 @@ ${boxen(JSON.stringify(dataSchema, null, 2), { margin: 1 })}`
196
196
  }
197
197
 
198
198
  spinner.start(
199
- `Creating protectedData mock file in \`${PROTECTED_DATA_MOCK_DIR}\` directory...`
199
+ `Creating protectedData mock file in ${color.file(PROTECTED_DATA_MOCK_DIR)} directory...`
200
200
  );
201
201
 
202
202
  const unencryptedData = await createZipFromObject(data);
@@ -204,21 +204,15 @@ ${boxen(JSON.stringify(dataSchema, null, 2), { margin: 1 })}`
204
204
  await mkdir(PROTECTED_DATA_MOCK_DIR, { recursive: true });
205
205
  await writeFile(join(PROTECTED_DATA_MOCK_DIR, mockName), unencryptedData);
206
206
  spinner.succeed(
207
- `Mocked protectedData "${mockName}" created in \`${PROTECTED_DATA_MOCK_DIR}\` directory`
207
+ `Mocked protectedData ${color.file(mockName)} created in ${color.file(PROTECTED_DATA_MOCK_DIR)} directory`
208
208
  );
209
209
  spinner.log(
210
- boxen(
210
+ hintBox(
211
211
  `protectedData mock "${mockName}" schema:
212
- ${chalk.yellow(boxen(JSON.stringify(schema, null, 2)))}
212
+ ${color.command(objectBox(JSON.stringify(schema, null, 2)))}
213
213
 
214
214
  Use your mock in tests:
215
- ${chalk.yellow(`iapp test --protectedData "${mockName}"`)}`,
216
- {
217
- padding: 1,
218
- margin: 1,
219
- borderStyle: 'round',
220
- borderColor: 'cyan',
221
- }
215
+ ${color.command(`iapp test --protectedData "${mockName}"`)}`
222
216
  )
223
217
  );
224
218
  } catch (error) {
package/src/cmd/run.js CHANGED
@@ -1,4 +1,3 @@
1
- import chalk from 'chalk';
2
1
  import { v4 as uuidV4 } from 'uuid';
3
2
  import { ethers } from 'ethers';
4
3
  import { mkdir, rm } from 'node:fs/promises';
@@ -16,6 +15,7 @@ import { getIExecDebug } from '../utils/iexec.js';
16
15
  import { extractZipToFolder } from '../utils/extractZipToFolder.js';
17
16
  import { askShowResult } from '../cli-helpers/askShowResult.js';
18
17
  import { goToProjectRoot } from '../cli-helpers/goToProjectRoot.js';
18
+ import * as color from '../cli-helpers/color.js';
19
19
 
20
20
  export async function run({
21
21
  iAppAddress,
@@ -51,23 +51,17 @@ export async function runInDebug({
51
51
  }) {
52
52
  // Is valid iApp address
53
53
  if (!ethers.isAddress(iAppAddress)) {
54
- spinner.log(
55
- chalk.red(
56
- 'The iApp address is invalid. Be careful ENS name is not implemented yet ...'
57
- )
54
+ throw Error(
55
+ 'The iApp address is invalid. Be careful ENS name is not implemented yet ...'
58
56
  );
59
- return;
60
57
  }
61
58
 
62
59
  if (protectedData) {
63
60
  // Is valid protectedData address
64
61
  if (!ethers.isAddress(protectedData)) {
65
- spinner.log(
66
- chalk.red(
67
- 'The protectedData address is invalid. Be careful ENS name is not implemented yet ...'
68
- )
62
+ throw Error(
63
+ 'The protectedData address is invalid. Be careful ENS name is not implemented yet ...'
69
64
  );
70
- return;
71
65
  }
72
66
  }
73
67
 
@@ -89,18 +83,13 @@ export async function runInDebug({
89
83
  );
90
84
 
91
85
  if (!isSecretSet) {
92
- spinner.log(
93
- chalk.red(
94
- `Your protectedData secret key is not registered in the debug secret management service (SMS) of iexec protocol`
95
- )
86
+ throw Error(
87
+ `Your protectedData secret key is not registered in the debug secret management service (SMS) of iexec protocol`
96
88
  );
97
- return;
98
89
  }
99
90
  } catch (e) {
100
- spinner.log(
101
- chalk.red(
102
- `Error while running your iApp with your protectedData: ${e.message}`
103
- )
91
+ throw Error(
92
+ `Error while running your iApp with your protectedData: ${e.message}`
104
93
  );
105
94
  }
106
95
  }
@@ -130,9 +119,9 @@ export async function runInDebug({
130
119
  });
131
120
  const workerpoolorder = workerpoolOrderbook.orders[0]?.order;
132
121
  if (!workerpoolorder) {
133
- spinner.fail('No WorkerpoolOrder found');
134
- spinner.log(chalk.red('Wait until some workerpoolOrder come back'));
135
- return;
122
+ throw Error(
123
+ 'No WorkerpoolOrder found, Wait until some workerpoolOrder come back'
124
+ );
136
125
  }
137
126
  spinner.succeed('Workerpool order fetched');
138
127
 
@@ -163,13 +152,9 @@ export async function runInDebug({
163
152
  );
164
153
  datasetorder = datasetOrderbook.orders[0]?.order;
165
154
  if (!datasetorder) {
166
- spinner.fail('No matching ProtectedData access found');
167
- spinner.log(
168
- chalk.red(
169
- 'It seems your iApp is not allowed to access the protectedData, please grantAccess to it'
170
- )
155
+ throw Error(
156
+ 'No matching ProtectedData access found, It seems your iApp is not allowed to access the protectedData, please grantAccess to it'
171
157
  );
172
- return;
173
158
  }
174
159
  spinner.succeed('ProtectedData access found');
175
160
  }
@@ -223,14 +208,9 @@ export async function runInDebug({
223
208
  clearTimeout(taskTimeoutWarning);
224
209
  });
225
210
 
226
- spinner.succeed('Task finalized');
227
-
228
211
  const task = await iexec.task.show(taskId);
229
- spinner.log(
230
- chalk.green(
231
- `You can download the result of your task here: https://ipfs-gateway.v8-bellecour.iex.ec${task?.results?.location}`
232
- )
233
- );
212
+ spinner.succeed(`Task finalized
213
+ You can download the result of your task here: ${color.link(`https://ipfs-gateway.v8-bellecour.iex.ec${task?.results?.location}`)}`);
234
214
 
235
215
  const downloadAnswer = await spinner.prompt({
236
216
  type: 'confirm',
@@ -248,7 +228,7 @@ export async function runInDebug({
248
228
  const taskResult = await iexec.task.fetchResults(taskId);
249
229
  const resultBuffer = await taskResult.arrayBuffer();
250
230
  await extractZipToFolder(resultBuffer, outputFolder);
251
- spinner.succeed(`Result downloaded to ${outputFolder}`);
231
+ spinner.succeed(`Result downloaded to ${color.file(outputFolder)}`);
252
232
 
253
233
  await askShowResult({ spinner, outputPath: outputFolder });
254
234
  }
package/src/cmd/test.js CHANGED
@@ -23,6 +23,8 @@ import { askForAppSecret } from '../cli-helpers/askForAppSecret.js';
23
23
  import { askShowResult } from '../cli-helpers/askShowResult.js';
24
24
  import { copy, fileExists } from '../utils/fs.utils.js';
25
25
  import { goToProjectRoot } from '../cli-helpers/goToProjectRoot.js';
26
+ import * as color from '../cli-helpers/color.js';
27
+ import { hintBox } from '../cli-helpers/box.js';
26
28
 
27
29
  export async function test({
28
30
  args,
@@ -47,6 +49,12 @@ export async function test({
47
49
  });
48
50
  await checkTestOutput({ spinner });
49
51
  await askShowResult({ spinner, outputPath: TEST_OUTPUT_DIR });
52
+ // TODO: check test warnings and errors and adapt the message
53
+ spinner.log(
54
+ hintBox(
55
+ `When ready run ${color.command(`iapp deploy`)} to transform you app into a TEE app and deploy it on iExec`
56
+ )
57
+ );
50
58
  } catch (error) {
51
59
  handleCliError({ spinner, error });
52
60
  }
@@ -105,7 +113,7 @@ export async function testApp({
105
113
  const imageId = await dockerBuild({
106
114
  isForTest: true,
107
115
  progressCallback: (msg) => {
108
- spinner.text = spinner.text + msg;
116
+ spinner.text = spinner.text + +color.comment(msg);
109
117
  },
110
118
  });
111
119
  spinner.succeed(`App docker image built (${imageId})`);
@@ -129,7 +137,7 @@ export async function testApp({
129
137
  const mockExists = await fileExists(protectedDataMockPath);
130
138
  if (!mockExists) {
131
139
  throw Error(
132
- `No protectedData mock "${protectedDataMock}" found in ${PROTECTED_DATA_MOCK_DIR}, run \`iapp mock protectedData\` to create a new protectedData mock`
140
+ `No protectedData mock "${protectedDataMock}" found in ${PROTECTED_DATA_MOCK_DIR}, run ${color.command('iapp mock protectedData')} to create a new protectedData mock`
133
141
  );
134
142
  }
135
143
  await copy(
@@ -187,7 +195,7 @@ export async function testApp({
187
195
  memory: IEXEC_WORKER_HEAP_SIZE,
188
196
  logsCallback: (msg) => {
189
197
  appLogs.push(msg); // collect logs for future use
190
- spinner.text = spinner.text + msg; // and display realtime while app is running
198
+ spinner.text = spinner.text + color.comment(msg); // and display realtime while app is running
191
199
  },
192
200
  }).finally(() => {
193
201
  clearTimeout(taskTimeoutWarning);
@@ -195,7 +203,7 @@ export async function testApp({
195
203
  if (outOfMemory) {
196
204
  spinner.fail(
197
205
  `App docker image container ran out of memory.
198
- iExec worker's ${Math.floor(IEXEC_WORKER_HEAP_SIZE / (1024 * 1024))}Mb memory limit exceeded.
206
+ iExec worker's ${Math.floor(IEXEC_WORKER_HEAP_SIZE / (1024 * 1024))}MiB memory limit exceeded.
199
207
  You must refactor your app to run within the memory limit.`
200
208
  );
201
209
  } else if (exitCode === 0) {
@@ -213,7 +221,7 @@ export async function testApp({
213
221
  const showLogs = await spinner.prompt({
214
222
  type: 'confirm',
215
223
  name: 'continue',
216
- message: `Would you like to see the app logs? (${appLogs.length} lines)`,
224
+ message: `Would you like to see the app logs? ${color.promptHelper(`(${appLogs.length} lines)`)}`,
217
225
  initial: false,
218
226
  });
219
227
  if (showLogs.continue) {
package/src/index.js CHANGED
@@ -110,7 +110,7 @@ yargsInstance
110
110
  // Run a published docker image
111
111
  .command(
112
112
  'run <iAppAddress>',
113
- 'Run your iApp',
113
+ 'Run your deployed iApp',
114
114
  (yargs) => {
115
115
  return yargs
116
116
  .positional('iAppAddress', {