ic-mops 0.37.0-pre.0 → 0.37.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.
Files changed (101) hide show
  1. package/bin/moc-wrapper.sh +1 -52
  2. package/cache.ts +2 -2
  3. package/cli.ts +27 -6
  4. package/commands/add.ts +3 -1
  5. package/commands/bench-replica.ts +5 -3
  6. package/commands/docs.ts +9 -6
  7. package/commands/install-all.ts +7 -2
  8. package/commands/install.ts +5 -1
  9. package/commands/publish.ts +2 -0
  10. package/commands/test/test.ts +42 -19
  11. package/commands/toolchain/index.ts +214 -42
  12. package/commands/toolchain/moc.ts +19 -4
  13. package/commands/toolchain/pocket-ic.ts +43 -6
  14. package/commands/toolchain/toolchain-utils.ts +14 -5
  15. package/commands/toolchain/wasmtime.ts +19 -4
  16. package/dist/cache.js +2 -2
  17. package/dist/cli.js +24 -6
  18. package/dist/commands/add.js +2 -1
  19. package/dist/commands/bench-replica.d.ts +1 -1
  20. package/dist/commands/bench-replica.js +5 -3
  21. package/dist/commands/docs.js +9 -6
  22. package/dist/commands/install-all.js +5 -2
  23. package/dist/commands/install.js +5 -1
  24. package/dist/commands/publish.js +1 -0
  25. package/dist/commands/test/test.js +41 -19
  26. package/dist/commands/toolchain/index.d.ts +17 -3
  27. package/dist/commands/toolchain/index.js +187 -40
  28. package/dist/commands/toolchain/moc.d.ts +5 -1
  29. package/dist/commands/toolchain/moc.js +16 -4
  30. package/dist/commands/toolchain/mocv.js +0 -1
  31. package/dist/commands/toolchain/pocket-ic.d.ts +9 -1
  32. package/dist/commands/toolchain/pocket-ic.js +40 -6
  33. package/dist/commands/toolchain/toolchain-utils.d.ts +1 -1
  34. package/dist/commands/toolchain/toolchain-utils.js +13 -4
  35. package/dist/commands/toolchain/wasmtime.d.ts +5 -1
  36. package/dist/commands/toolchain/wasmtime.js +16 -4
  37. package/dist/integrity.js +40 -15
  38. package/dist/mops.js +1 -1
  39. package/dist/package.json +2 -2
  40. package/dist/pic-js/examples/clock/tests/clock/index.d.ts +1 -0
  41. package/dist/pic-js/examples/clock/tests/clock/index.js +5 -0
  42. package/dist/pic-js/examples/clock/tests/jest.config.d.ts +3 -0
  43. package/dist/pic-js/examples/clock/tests/jest.config.js +8 -0
  44. package/dist/pic-js/examples/clock/tests/src/clock.spec.d.ts +1 -0
  45. package/dist/pic-js/examples/clock/tests/src/clock.spec.js +48 -0
  46. package/dist/pic-js/examples/counter/tests/counter/index.d.ts +1 -0
  47. package/dist/pic-js/examples/counter/tests/counter/index.js +5 -0
  48. package/dist/pic-js/examples/counter/tests/jest.config.d.ts +3 -0
  49. package/dist/pic-js/examples/counter/tests/jest.config.js +8 -0
  50. package/dist/pic-js/examples/counter/tests/src/counter.spec.d.ts +1 -0
  51. package/dist/pic-js/examples/counter/tests/src/counter.spec.js +80 -0
  52. package/dist/pic-js/examples/todo/tests/jest.config.d.ts +3 -0
  53. package/dist/pic-js/examples/todo/tests/jest.config.js +8 -0
  54. package/dist/pic-js/examples/todo/tests/src/todo.spec.d.ts +1 -0
  55. package/dist/pic-js/examples/todo/tests/src/todo.spec.js +211 -0
  56. package/dist/pic-js/examples/todo/tests/todo/index.d.ts +1 -0
  57. package/dist/pic-js/examples/todo/tests/todo/index.js +5 -0
  58. package/dist/pic-js/packages/pic/src/error.d.ts +12 -0
  59. package/dist/pic-js/packages/pic/src/error.js +36 -0
  60. package/dist/pic-js/packages/pic/src/http-client.d.ts +15 -0
  61. package/dist/pic-js/packages/pic/src/http-client.js +37 -0
  62. package/dist/pic-js/packages/pic/src/identity.d.ts +66 -0
  63. package/dist/pic-js/packages/pic/src/identity.js +86 -0
  64. package/dist/pic-js/packages/pic/src/index.d.ts +4 -0
  65. package/dist/pic-js/packages/pic/src/index.js +8 -0
  66. package/dist/pic-js/packages/pic/src/management-canister.d.ts +30 -0
  67. package/dist/pic-js/packages/pic/src/management-canister.js +43 -0
  68. package/dist/pic-js/packages/pic/src/pocket-ic-actor.d.ts +83 -0
  69. package/dist/pic-js/packages/pic/src/pocket-ic-actor.js +58 -0
  70. package/dist/pic-js/packages/pic/src/pocket-ic-client-types.d.ts +61 -0
  71. package/dist/pic-js/packages/pic/src/pocket-ic-client-types.js +2 -0
  72. package/dist/pic-js/packages/pic/src/pocket-ic-client.d.ts +24 -0
  73. package/dist/pic-js/packages/pic/src/pocket-ic-client.js +123 -0
  74. package/dist/pic-js/packages/pic/src/pocket-ic-server.d.ts +10 -0
  75. package/dist/pic-js/packages/pic/src/pocket-ic-server.js +55 -0
  76. package/dist/pic-js/packages/pic/src/pocket-ic-types.d.ts +40 -0
  77. package/dist/pic-js/packages/pic/src/pocket-ic-types.js +2 -0
  78. package/dist/pic-js/packages/pic/src/pocket-ic.d.ts +447 -0
  79. package/dist/pic-js/packages/pic/src/pocket-ic.js +551 -0
  80. package/dist/pic-js/packages/pic/src/util/candid.d.ts +1 -0
  81. package/dist/pic-js/packages/pic/src/util/candid.js +7 -0
  82. package/dist/pic-js/packages/pic/src/util/encoding.d.ts +5 -0
  83. package/dist/pic-js/packages/pic/src/util/encoding.js +19 -0
  84. package/dist/pic-js/packages/pic/src/util/fs.d.ts +4 -0
  85. package/dist/pic-js/packages/pic/src/util/fs.js +29 -0
  86. package/dist/pic-js/packages/pic/src/util/index.d.ts +5 -0
  87. package/dist/pic-js/packages/pic/src/util/index.js +21 -0
  88. package/dist/pic-js/packages/pic/src/util/os.d.ts +4 -0
  89. package/dist/pic-js/packages/pic/src/util/os.js +19 -0
  90. package/dist/pic-js/packages/pic/src/util/poll.d.ts +5 -0
  91. package/dist/pic-js/packages/pic/src/util/poll.js +28 -0
  92. package/dist/templates/mops-test.yml +4 -4
  93. package/dist/vessel.js +5 -1
  94. package/integrity.ts +57 -17
  95. package/mops.ts +1 -1
  96. package/package.json +2 -2
  97. package/templates/mops-test.yml +4 -4
  98. package/vessel.ts +6 -1
  99. package/bun.lockb +0 -0
  100. package/commands/toolchain/mocv.ts +0 -313
  101. package/moc-wrapper.ts +0 -10
@@ -2,9 +2,10 @@ import { execSync } from 'node:child_process';
2
2
  import path from 'node:path';
3
3
  import fs from 'node:fs';
4
4
  import { execaCommand } from 'execa';
5
- import { PocketIc } from '@hadronous/pic';
5
+ import { PocketIc } from 'pic-ic';
6
6
  import { getRootDir } from '../mops.js';
7
7
  import { createActor, idlFactory } from '../declarations/bench/index.js';
8
+ import { toolchain } from './toolchain/index.js';
8
9
  export class BenchReplica {
9
10
  constructor(type, verbose = false) {
10
11
  this.verbose = false;
@@ -21,7 +22,8 @@ export class BenchReplica {
21
22
  execSync('dfx start --background --clean --artificial-delay 0' + (this.verbose ? '' : ' -qqqq'), { cwd: dir, stdio: ['inherit', this.verbose ? 'inherit' : 'ignore', 'inherit'] });
22
23
  }
23
24
  else {
24
- this.pocketIc = await PocketIc.create();
25
+ let pocketIcBin = await toolchain.bin('pocket-ic');
26
+ this.pocketIc = await PocketIc.create(pocketIcBin);
25
27
  }
26
28
  }
27
29
  async stop() {
@@ -30,7 +32,7 @@ export class BenchReplica {
30
32
  execSync('dfx stop' + (this.verbose ? '' : ' -qqqq'), { cwd: dir, stdio: ['pipe', this.verbose ? 'inherit' : 'ignore', 'pipe'] });
31
33
  }
32
34
  else if (this.pocketIc) {
33
- this.pocketIc.tearDown();
35
+ await this.pocketIc.tearDown();
34
36
  }
35
37
  }
36
38
  async deploy(name, wasm, cwd = process.cwd()) {
@@ -1,4 +1,4 @@
1
- import { spawn, execSync } from 'node:child_process';
1
+ import { spawn } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import chalk from 'chalk';
@@ -7,22 +7,25 @@ import { deleteSync } from 'del';
7
7
  import tar from 'tar';
8
8
  import streamToPromise from 'stream-to-promise';
9
9
  import { getRootDir } from '../mops.js';
10
- let moDoc;
10
+ import { toolchain } from './toolchain/index.js';
11
+ let moDocPath;
11
12
  export async function docs({ silent = false } = {}) {
12
13
  let rootDir = getRootDir();
13
14
  let docsDir = path.join(rootDir, '.mops/.docs');
14
15
  let docsDirRelative = path.relative(process.cwd(), docsDir);
15
16
  deleteSync([docsDir], { force: true });
16
- // detect mocv
17
+ // detect mocv (legacy)
17
18
  if (process.env.DFX_MOC_PATH && process.env.DFX_MOC_PATH.includes('mocv/versions')) {
18
- moDoc = process.env.DFX_MOC_PATH.replace(/\/moc$/, '/mo-doc');
19
+ moDocPath = process.env.DFX_MOC_PATH.replace(/\/moc$/, '/mo-doc');
19
20
  }
20
21
  else {
21
- moDoc = execSync('dfx cache show').toString().trim() + '/mo-doc';
22
+ // fallbacks to dfx moc if not specified in config
23
+ let mocPath = await toolchain.bin('moc');
24
+ moDocPath = mocPath.replace(/\/moc$/, '/mo-doc');
22
25
  }
23
26
  // generate docs
24
27
  await new Promise((resolve) => {
25
- let proc = spawn(moDoc, [`--source=${path.join(rootDir, 'src')}`, `--output=${docsDirRelative}`, '--format=adoc']);
28
+ let proc = spawn(moDocPath, [`--source=${path.join(rootDir, 'src')}`, `--output=${docsDirRelative}`, '--format=adoc']);
26
29
  // stdout
27
30
  proc.stdout.on('data', (data) => {
28
31
  let text = data.toString().trim();
@@ -1,5 +1,6 @@
1
1
  import chalk from 'chalk';
2
- import logUpdate from 'log-update';
2
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3
+ import { createLogUpdate } from 'log-update';
3
4
  import { checkConfigFile, readConfig } from '../mops.js';
4
5
  import { install } from './install.js';
5
6
  import { installFromGithub } from '../vessel.js';
@@ -26,6 +27,8 @@ export async function installAll({ verbose = false, silent = false, lock } = {})
26
27
  installedPackages = { ...installedPackages, ...res };
27
28
  }
28
29
  }
30
+ let logUpdate = createLogUpdate(process.stdout, { showCursor: true });
31
+ // let logUpdate = l;
29
32
  if (!silent && lock !== 'ignore') {
30
33
  logUpdate('Checking integrity...');
31
34
  }
@@ -35,6 +38,6 @@ export async function installAll({ verbose = false, silent = false, lock } = {})
35
38
  ]);
36
39
  if (!silent) {
37
40
  logUpdate.clear();
38
- console.log(chalk.green('All packages installed'));
41
+ console.log(chalk.green('Packages installed'));
39
42
  }
40
43
  }
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import fs from 'node:fs';
3
- import logUpdate from 'log-update';
3
+ import { createLogUpdate } from 'log-update';
4
4
  import chalk from 'chalk';
5
5
  import { checkConfigFile, formatDir, progressBar, readConfig } from '../mops.js';
6
6
  import { getHighestVersion } from '../api/getHighestVersion.js';
@@ -13,6 +13,7 @@ export async function install(pkg, version = '', { verbose = false, silent = fal
13
13
  if (!checkConfigFile()) {
14
14
  return false;
15
15
  }
16
+ let logUpdate = createLogUpdate(process.stdout, { showCursor: true });
16
17
  // progress
17
18
  let total = Infinity;
18
19
  let step = 0;
@@ -75,6 +76,9 @@ export async function install(pkg, version = '', { verbose = false, silent = fal
75
76
  if (verbose) {
76
77
  silent || logUpdate.done();
77
78
  }
79
+ else {
80
+ logUpdate.clear();
81
+ }
78
82
  // install dependencies
79
83
  let ok = true;
80
84
  let config = readConfig(path.join(dir, 'mops.toml'));
@@ -289,6 +289,7 @@ export async function publish(options = {}) {
289
289
  fs.rmSync(path.join(rootDir, '.mops/.docs'), { force: true, recursive: true });
290
290
  // finish
291
291
  progress();
292
+ logUpdate.done();
292
293
  let res = await actor.finishPublish(puiblishingId);
293
294
  if ('err' in res) {
294
295
  console.log(chalk.red('Error: ') + res.err);
@@ -1,4 +1,4 @@
1
- import { spawn, execSync } from 'node:child_process';
1
+ import { spawn } from 'node:child_process';
2
2
  import path from 'node:path';
3
3
  import fs from 'node:fs';
4
4
  import os from 'node:os';
@@ -7,7 +7,7 @@ import { globSync } from 'glob';
7
7
  import chokidar from 'chokidar';
8
8
  import debounce from 'debounce';
9
9
  import { sources } from '../sources.js';
10
- import { getRootDir } from '../../mops.js';
10
+ import { getRootDir, readConfig } from '../../mops.js';
11
11
  import { parallel } from '../../parallel.js';
12
12
  import { MMF1 } from './mmf1.js';
13
13
  import { absToRel } from './utils.js';
@@ -15,6 +15,7 @@ import { VerboseReporter } from './reporters/verbose-reporter.js';
15
15
  import { FilesReporter } from './reporters/files-reporter.js';
16
16
  import { CompactReporter } from './reporters/compact-reporter.js';
17
17
  import { SilentReporter } from './reporters/silent-reporter.js';
18
+ import { toolchain } from '../toolchain/index.js';
18
19
  let ignore = [
19
20
  '**/node_modules/**',
20
21
  '**/.mops/**',
@@ -57,7 +58,8 @@ export async function test(filter = '', { watch = false, reporter = 'verbose', m
57
58
  }
58
59
  }
59
60
  }
60
- let mocPath = process.env.DFX_MOC_PATH;
61
+ let mocPath = '';
62
+ let wasmtimePath = '';
61
63
  export async function runAll(reporterName = 'verbose', filter = '', mode = 'interpreter') {
62
64
  let reporter;
63
65
  if (reporterName == 'compact') {
@@ -99,19 +101,29 @@ export async function testWithReporter(reporter, filter = '', mode = 'interprete
99
101
  return false;
100
102
  }
101
103
  reporter.addFiles(files);
104
+ let config = readConfig();
102
105
  let sourcesArr = await sources();
103
106
  if (!mocPath) {
104
- mocPath = execSync('dfx cache show').toString().trim() + '/moc';
107
+ mocPath = await toolchain.bin('moc');
105
108
  }
106
109
  let wasmDir = `${getRootDir()}/.mops/.test/`;
107
110
  fs.mkdirSync(wasmDir, { recursive: true });
108
111
  await parallel(os.cpus().length, files, async (file) => {
109
112
  let mmf = new MMF1('store', absToRel(file));
110
113
  let wasiMode = mode === 'wasi' || fs.readFileSync(file, 'utf8').startsWith('// @testmode wasi');
111
- let promise = new Promise((resolve) => {
112
- if (!mocPath) {
113
- mocPath = 'moc';
114
+ if (wasiMode && !wasmtimePath) {
115
+ // ensure wasmtime is installed or specified in config
116
+ if (config.toolchain?.wasmtime) {
117
+ wasmtimePath = await toolchain.bin('wasmtime');
118
+ }
119
+ // fallback wasmtime to global binary if not specified in config (legacy)
120
+ else {
121
+ wasmtimePath = 'wasmtime';
122
+ console.log(chalk.yellow('Warning:'), 'Wasmtime is not specified in config. Using global binary "wasmtime". This will be removed in the future.');
123
+ console.log(`Run ${chalk.green('mops toolchain use wasmtime')} or add ${chalk.green('wasmtime = "<version>"')} in mops.toml to avoid breaking changes with future versions of mops.`);
114
124
  }
125
+ }
126
+ let promise = new Promise((resolve) => {
115
127
  let mocArgs = ['--hide-warnings', '--error-detail=2', ...sourcesArr.join(' ').split(' '), file].filter(x => x);
116
128
  // build and run wasm
117
129
  if (wasiMode) {
@@ -123,18 +135,28 @@ export async function testWithReporter(reporter, filter = '', mode = 'interprete
123
135
  return;
124
136
  }
125
137
  // run
126
- let proc = spawn('wasmtime', [
127
- '--max-wasm-stack=2000000',
128
- '--enable-cranelift-nan-canonicalization',
129
- '--wasm-features',
130
- 'multi-memory,bulk-memory',
131
- wasmFile,
132
- ], {
133
- env: {
134
- ...process.env,
135
- WASMTIME_NEW_CLI: '0',
136
- }
137
- });
138
+ let wasmtimeArgs = [];
139
+ if (config.toolchain?.wasmtime && config.toolchain?.wasmtime >= '14.0.0') {
140
+ wasmtimeArgs = [
141
+ '-S', 'preview2=n',
142
+ '-C', 'cache=n',
143
+ '-W', 'bulk-memory',
144
+ '-W', 'multi-memory',
145
+ '-W', 'max-wasm-stack=2000000',
146
+ '-W', 'nan-canonicalization=y',
147
+ wasmFile,
148
+ ];
149
+ }
150
+ else {
151
+ wasmtimeArgs = [
152
+ '--max-wasm-stack=2000000',
153
+ '--enable-cranelift-nan-canonicalization',
154
+ '--wasm-features',
155
+ 'multi-memory,bulk-memory',
156
+ wasmFile,
157
+ ];
158
+ }
159
+ let proc = spawn(wasmtimePath, wasmtimeArgs);
138
160
  await pipeMMF(proc, mmf);
139
161
  }).finally(() => {
140
162
  fs.rmSync(wasmFile, { force: true });
@@ -1,12 +1,26 @@
1
1
  import { Tool } from '../../types.js';
2
- declare function init({ reset }?: {
2
+ declare function ensureToolchainInited({ strict }?: {
3
+ strict?: boolean | undefined;
4
+ }): Promise<boolean>;
5
+ declare function init({ reset, silent }?: {
3
6
  reset?: boolean | undefined;
7
+ silent?: boolean | undefined;
4
8
  }): Promise<void>;
5
- declare function use(tool: Tool, version: string): Promise<void>;
6
- declare function bin(tool: Tool): Promise<void>;
9
+ declare function installAll({ silent, verbose }?: {
10
+ silent?: boolean | undefined;
11
+ verbose?: boolean | undefined;
12
+ }): Promise<void>;
13
+ declare function use(tool: Tool, version?: string): Promise<void>;
14
+ declare function update(tool?: Tool): Promise<void>;
15
+ declare function bin(tool: Tool, { fallback }?: {
16
+ fallback?: boolean | undefined;
17
+ }): Promise<string>;
7
18
  export declare let toolchain: {
8
19
  init: typeof init;
9
20
  use: typeof use;
21
+ update: typeof update;
10
22
  bin: typeof bin;
23
+ installAll: typeof installAll;
24
+ ensureToolchainInited: typeof ensureToolchainInited;
11
25
  };
12
26
  export {};
@@ -2,11 +2,52 @@ import path from 'node:path';
2
2
  import fs from 'node:fs';
3
3
  import os from 'node:os';
4
4
  import { execSync } from 'node:child_process';
5
- import { getClosestConfigFile, globalCacheDir, readConfig, writeConfig } from '../../mops.js';
6
- import * as moc from './moc.js';
7
5
  import chalk from 'chalk';
6
+ import prompts from 'prompts';
7
+ import { createLogUpdate } from 'log-update';
8
+ import { checkConfigFile, getClosestConfigFile, getRootDir, globalCacheDir, readConfig, writeConfig } from '../../mops.js';
9
+ import * as moc from './moc.js';
10
+ import * as pocketIc from './pocket-ic.js';
11
+ import * as wasmtime from './wasmtime.js';
12
+ function getToolUtils(tool) {
13
+ if (tool === 'moc') {
14
+ return moc;
15
+ }
16
+ else if (tool === 'pocket-ic') {
17
+ return pocketIc;
18
+ }
19
+ else if (tool === 'wasmtime') {
20
+ return wasmtime;
21
+ }
22
+ else {
23
+ console.error(`Unknown tool '${tool}'`);
24
+ process.exit(1);
25
+ }
26
+ }
27
+ async function ensureToolchainInited({ strict = true } = {}) {
28
+ // auto init in CI
29
+ if (process.env.CI) {
30
+ await init({ silent: true });
31
+ return true;
32
+ }
33
+ // for non-stict perform check only if dfx.json exists and moc is listed in [toolchain] section
34
+ let rootDir = getRootDir();
35
+ let config = readConfig();
36
+ if (!strict && (!config.toolchain?.moc || rootDir && !fs.existsSync(path.join(rootDir, 'dfx.json')))) {
37
+ return true;
38
+ }
39
+ try {
40
+ let res = execSync('which moc-wrapper').toString().trim();
41
+ if (res && process.env.DFX_MOC_PATH === 'moc-wrapper') {
42
+ return true;
43
+ }
44
+ }
45
+ catch { }
46
+ console.error('Toolchain management is not initialized. Run "mops toolchain init"');
47
+ process.exit(1);
48
+ }
8
49
  // update shell config files to set DFX_MOC_PATH to moc-wrapper
9
- async function init({ reset = false } = {}) {
50
+ async function init({ reset = false, silent = false } = {}) {
10
51
  if (process.platform == 'win32') {
11
52
  console.error('Windows is not supported. Please use WSL');
12
53
  process.exit(1);
@@ -24,7 +65,7 @@ async function init({ reset = false } = {}) {
24
65
  catch { }
25
66
  let zshrc = path.join(os.homedir(), '.zshrc');
26
67
  let bashrc = path.join(os.homedir(), '.bashrc');
27
- let shellConfigFiles = [bashrc, zshrc].filter((file) => {
68
+ let shellConfigFiles = [bashrc, zshrc, process.env.GITHUB_ENV || ''].map(x => x).filter((file) => {
28
69
  return fs.existsSync(file);
29
70
  });
30
71
  if (shellConfigFiles.length === 0) {
@@ -32,18 +73,18 @@ async function init({ reset = false } = {}) {
32
73
  process.exit(1);
33
74
  }
34
75
  // update all existing shell config files
35
- for (let configFile of shellConfigFiles) {
36
- let text = fs.readFileSync(configFile).toString();
37
- let setDfxLine = '\nexport DFX_MOC_PATH="moc-wrapper"';
76
+ for (let shellConfigFile of shellConfigFiles) {
77
+ let text = fs.readFileSync(shellConfigFile).toString();
78
+ let setDfxMocPathLine = '\nexport DFX_MOC_PATH=moc-wrapper';
38
79
  let newLines = [
39
- setDfxLine,
80
+ setDfxMocPathLine,
40
81
  ];
41
82
  let oldLines = [
42
83
  // legacy mocv lines
43
84
  `\nexport DFX_MOC_PATH=${path.join(path.join(os.homedir(), '.cache/mocv'), 'versions/current')}/moc`,
44
85
  '\nexport DFX_MOC_PATH="$HOME/.cache/mocv/versions/current/moc"',
45
86
  // new
46
- setDfxLine,
87
+ setDfxMocPathLine,
47
88
  ];
48
89
  // remove old lines
49
90
  for (let oldLine of oldLines) {
@@ -58,70 +99,176 @@ async function init({ reset = false } = {}) {
58
99
  text += '\n';
59
100
  }
60
101
  for (let newLine of newLines) {
102
+ if (shellConfigFile === process.env.GITHUB_ENV) {
103
+ newLine = newLine.replace('export ', '');
104
+ }
61
105
  text += newLine;
62
106
  }
63
107
  text += '\n';
64
108
  }
65
- fs.writeFileSync(configFile, text);
109
+ fs.writeFileSync(shellConfigFile, text);
110
+ }
111
+ if (!silent) {
112
+ console.log(chalk.green('Success!'));
113
+ console.log('Restart terminal to apply changes');
66
114
  }
67
- console.log(chalk.green('Success!'));
68
115
  }
69
- async function download(tool, version) {
70
- if (tool === 'moc') {
71
- await moc.download(version);
116
+ async function download(tool, version, { silent = false, verbose = false } = {}) {
117
+ let toolUtils = getToolUtils(tool);
118
+ await toolUtils.download(version, { silent, verbose });
119
+ }
120
+ async function installAll({ silent = false, verbose = false } = {}) {
121
+ let config = readConfig();
122
+ if (!config.toolchain) {
123
+ return;
124
+ }
125
+ let logUpdate = createLogUpdate(process.stdout, { showCursor: true });
126
+ let log = (...args) => {
127
+ if (silent) {
128
+ return;
129
+ }
130
+ if (verbose) {
131
+ console.log(...args);
132
+ }
133
+ else {
134
+ logUpdate(...args);
135
+ }
136
+ };
137
+ log('Installing toolchain...');
138
+ if (config.toolchain?.moc) {
139
+ log('Installing moc', config.toolchain.moc);
140
+ await download('moc', config.toolchain.moc, { verbose });
141
+ }
142
+ if (config.toolchain?.wasmtime) {
143
+ log('Installing wasmtime', config.toolchain.wasmtime);
144
+ await download('wasmtime', config.toolchain.wasmtime, { verbose });
145
+ }
146
+ if (config.toolchain?.['pocket-ic']) {
147
+ log('Installing pocket-ic', config.toolchain['pocket-ic']);
148
+ await download('pocket-ic', config.toolchain['pocket-ic'], { verbose });
149
+ }
150
+ if (!silent) {
151
+ logUpdate.clear();
152
+ console.log(chalk.green('Toolchain installed'));
72
153
  }
73
154
  }
74
- // async function downloadAll() {
75
- // let config = readConfig();
76
- // if (config.toolchain?.moc) {
77
- // await download('moc', config.toolchain.moc);
78
- // }
79
- // if (config.toolchain?.wasmtime) {
80
- // await download('wasmtime', config.toolchain.wasmtime);
81
- // }
82
- // if (config.toolchain?.['pocket-ic']) {
83
- // await download('pocket-ic', config.toolchain['pocket-ic']);
84
- // }
85
- // }
155
+ async function promptVersion(tool) {
156
+ let config = readConfig();
157
+ config.toolchain = config.toolchain || {};
158
+ let current = config.toolchain[tool];
159
+ let toolUtils = getToolUtils(tool);
160
+ let releases = await toolUtils.getReleases();
161
+ let versions = releases.map((item) => item.tag_name);
162
+ let currentIndex = versions.indexOf(current);
163
+ let res = await prompts({
164
+ type: 'select',
165
+ name: 'version',
166
+ message: `Select ${tool} version`,
167
+ choices: releases.map((release, i) => {
168
+ let date = new Date(release.published_at).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });
169
+ return {
170
+ title: release.tag_name + chalk.gray(` ${date}${currentIndex === i ? chalk.italic(' (current)') : ''}`),
171
+ value: release.tag_name,
172
+ };
173
+ }),
174
+ initial: currentIndex == -1 ? 0 : currentIndex,
175
+ });
176
+ return res.version;
177
+ }
86
178
  // download binary and set version in mops.toml
87
179
  async function use(tool, version) {
180
+ if (tool === 'moc') {
181
+ await ensureToolchainInited();
182
+ }
183
+ if (!version) {
184
+ version = await promptVersion(tool);
185
+ }
186
+ if (!version) {
187
+ return;
188
+ }
189
+ if (version === 'latest') {
190
+ version = await getToolUtils(tool).getLatestReleaseTag();
191
+ }
88
192
  await download(tool, version);
89
193
  let config = readConfig();
90
194
  config.toolchain = config.toolchain || {};
195
+ let oldVersion = config.toolchain[tool];
91
196
  config.toolchain[tool] = version;
92
197
  writeConfig(config);
198
+ if (oldVersion === version) {
199
+ console.log((`${tool} ${version} is already installed`));
200
+ }
201
+ else {
202
+ console.log(chalk.green(`Installed ${tool} ${version}`));
203
+ }
204
+ }
205
+ // download latest binary and set version in mops.toml
206
+ async function update(tool) {
207
+ if (tool === 'moc') {
208
+ await ensureToolchainInited();
209
+ }
210
+ let config = readConfig();
211
+ config.toolchain = config.toolchain || {};
212
+ let tools = tool ? [tool] : Object.keys(config.toolchain);
213
+ for (let tool of tools) {
214
+ if (!config.toolchain[tool]) {
215
+ console.error(`Tool '${tool}' is not defined in [toolchain] section in mops.toml`);
216
+ process.exit(1);
217
+ }
218
+ let toolUtils = getToolUtils(tool);
219
+ let version = await toolUtils.getLatestReleaseTag();
220
+ await download(tool, version);
221
+ let oldVersion = config.toolchain[tool];
222
+ config.toolchain[tool] = version;
223
+ writeConfig(config);
224
+ if (oldVersion === version) {
225
+ console.log((`Latest ${tool} ${version} is already installed`));
226
+ }
227
+ else {
228
+ console.log(chalk.green(`Installed ${tool} ${version}`));
229
+ }
230
+ }
93
231
  }
94
232
  // return current version from mops.toml
95
- async function bin(tool) {
233
+ async function bin(tool, { fallback = false } = {}) {
96
234
  let hasConfig = getClosestConfigFile();
97
235
  // fallback to dfx moc
98
236
  if (!hasConfig) {
99
- console.log(execSync('dfx cache show').toString().trim() + '/moc');
100
- return;
237
+ if (tool === 'moc' && fallback) {
238
+ return execSync('dfx cache show').toString().trim() + '/moc';
239
+ }
240
+ checkConfigFile();
241
+ process.exit(1);
101
242
  }
102
243
  let config = readConfig();
103
244
  let version = config.toolchain?.[tool];
104
- if (!version) {
105
- // fallback to dfx moc
245
+ if (version) {
106
246
  if (tool === 'moc') {
107
- console.log(execSync('dfx cache show').toString().trim() + '/moc');
108
- return;
247
+ await ensureToolchainInited();
109
248
  }
110
- console.error(`Toolchain '${tool}' is not defined in mops.toml`);
111
- process.exit(1);
112
- }
113
- if (version) {
114
- await download(tool, version);
249
+ await download(tool, version, { silent: true });
115
250
  if (tool === 'moc') {
116
- console.log(path.join(globalCacheDir, 'moc', version, 'moc'));
251
+ return path.join(globalCacheDir, 'moc', version, tool);
117
252
  }
118
253
  else {
119
- console.log(path.join(globalCacheDir, tool, version, 'moc'));
254
+ return path.join(globalCacheDir, tool, version, tool);
120
255
  }
121
256
  }
257
+ else {
258
+ // fallback to dfx moc
259
+ if (tool === 'moc' && fallback) {
260
+ return execSync('dfx cache show').toString().trim() + '/moc';
261
+ }
262
+ console.error(`Tool '${tool}' is not defined in [toolchain] section in mops.toml`);
263
+ console.log(`Run ${chalk.green(`mops toolchain use ${tool}`)} to install it`);
264
+ process.exit(1);
265
+ }
122
266
  }
123
267
  export let toolchain = {
124
268
  init,
125
269
  use,
270
+ update,
126
271
  bin,
272
+ installAll,
273
+ ensureToolchainInited,
127
274
  };
@@ -1,4 +1,8 @@
1
+ export declare let repo: string;
2
+ export declare let getLatestReleaseTag: () => Promise<string>;
3
+ export declare let getReleases: () => Promise<any>;
1
4
  export declare let isCached: (version: string) => boolean;
2
- export declare let download: (version: string, { silent }?: {
5
+ export declare let download: (version: string, { silent, verbose }?: {
3
6
  silent?: boolean | undefined;
7
+ verbose?: boolean | undefined;
4
8
  }) => Promise<void>;
@@ -1,13 +1,20 @@
1
1
  import path from 'node:path';
2
2
  import fs from 'fs-extra';
3
3
  import { globalCacheDir } from '../../mops.js';
4
- import { downloadAndExtract } from './toolchain-utils.js';
4
+ import * as toolchainUtils from './toolchain-utils.js';
5
5
  let cacheDir = path.join(globalCacheDir, 'moc');
6
+ export let repo = 'dfinity/motoko';
7
+ export let getLatestReleaseTag = async () => {
8
+ return toolchainUtils.getLatestReleaseTag(repo);
9
+ };
10
+ export let getReleases = async () => {
11
+ return toolchainUtils.getReleases(repo);
12
+ };
6
13
  export let isCached = (version) => {
7
14
  let dir = path.join(cacheDir, version);
8
15
  return fs.existsSync(dir) && fs.existsSync(path.join(dir, 'moc'));
9
16
  };
10
- export let download = async (version, { silent = false } = {}) => {
17
+ export let download = async (version, { silent = false, verbose = false } = {}) => {
11
18
  if (process.platform == 'win32') {
12
19
  console.error('Windows is not supported. Please use WSL');
13
20
  process.exit(1);
@@ -17,6 +24,9 @@ export let download = async (version, { silent = false } = {}) => {
17
24
  process.exit(1);
18
25
  }
19
26
  if (isCached(version)) {
27
+ if (verbose) {
28
+ console.log(`moc ${version} is already installed`);
29
+ }
20
30
  return;
21
31
  }
22
32
  let url;
@@ -31,6 +41,8 @@ export let download = async (version, { silent = false } = {}) => {
31
41
  let platfrom = process.platform == 'darwin' ? 'macos' : 'linux64';
32
42
  url = `https://github.com/dfinity/motoko/releases/download/${version}/motoko-${platfrom}-${version}.tar.gz`;
33
43
  }
34
- silent || console.log(`Downloading ${url}`);
35
- await downloadAndExtract(url, path.join(cacheDir, version));
44
+ if (verbose && !silent) {
45
+ console.log(`Downloading ${url}`);
46
+ }
47
+ await toolchainUtils.downloadAndExtract(url, path.join(cacheDir, version));
36
48
  };
@@ -228,7 +228,6 @@ let updateShellConfig = async ({ reset = false, yes = false } = {}) => {
228
228
  }
229
229
  fs.writeFileSync(configFile, data);
230
230
  }
231
- ;
232
231
  console.log('Success!');
233
232
  // console.log(`Run "source ${configFile}" to apply changes`);
234
233
  console.log('Restart terminal to apply changes');
@@ -1,4 +1,12 @@
1
+ export declare let repo: string;
2
+ export declare let getLatestReleaseTag: () => Promise<string>;
3
+ export declare let getReleases: () => Promise<{
4
+ tag_name: string;
5
+ published_at: Date;
6
+ draft: boolean;
7
+ }[]>;
1
8
  export declare let isCached: (version: string) => boolean;
2
- export declare let download: (version: string, { silent }?: {
9
+ export declare let download: (version: string, { silent, verbose }?: {
3
10
  silent?: boolean | undefined;
11
+ verbose?: boolean | undefined;
4
12
  }) => Promise<void>;