@criticalmanufacturing/cli 2.0.0-3
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 +29 -0
- package/package.json +41 -0
- package/postinstall.js +157 -0
- package/run.js +33 -0
- package/utils.js +89 -0
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# CMF Command Line tool
|
|
2
|
+
|
|
3
|
+
WARNING: cmf-cli is alpha software. Buckle up.
|
|
4
|
+
|
|
5
|
+
## Installing
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
npm install --global @criticalmanufacturing/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
After installation, a new command `cmf` will be added to your PATH.
|
|
13
|
+
|
|
14
|
+
## Commands
|
|
15
|
+
|
|
16
|
+
The tool includes a help feature which details the available commands. Run
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
cmf
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
to get more information. Feature sets also have help menus, e.g.:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
cmf -h
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Full documentation in [docs](https://github.com/criticalmanufacturing/cli/tree/main/docs)
|
|
29
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@criticalmanufacturing/cli",
|
|
3
|
+
"version": "2.0.0-3",
|
|
4
|
+
"description": "Critical Manufacturing command line client",
|
|
5
|
+
"bin": {
|
|
6
|
+
"cmf": "./run.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"postinstall": "node postinstall.js install",
|
|
10
|
+
"preuninstall": "node postinstall.js uninstall"
|
|
11
|
+
},
|
|
12
|
+
"author": "Critical Manufacturing, SA",
|
|
13
|
+
"license": "BSD-3-Clause",
|
|
14
|
+
"goBinary": {
|
|
15
|
+
"name": "cmf",
|
|
16
|
+
"path": "./bin",
|
|
17
|
+
"url": "https://github.com/criticalmanufacturing/cli/releases/download/{{version}}/cmf-cli.{{platform}}-{{arch}}.zip"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/criticalmanufacturing/cli#readme",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/criticalmanufacturing/cli.git"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"postinstall.js",
|
|
27
|
+
"run.js",
|
|
28
|
+
"utils.js"
|
|
29
|
+
],
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"adm-zip": "^0.5.5",
|
|
32
|
+
"axios": "^0.21.1",
|
|
33
|
+
"debug": "^4.3.1",
|
|
34
|
+
"env-paths": "^2.2.1",
|
|
35
|
+
"is-installed-globally": "^0.4.0",
|
|
36
|
+
"mkdirp": "^1.0.4",
|
|
37
|
+
"node_modules-path": "^2.0.5",
|
|
38
|
+
"rimraf": "^3.0.2",
|
|
39
|
+
"tmp": "^0.2.1"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const path = require('path'),
|
|
6
|
+
mkdirp = require('mkdirp'),
|
|
7
|
+
envPaths = require('env-paths'),
|
|
8
|
+
rimraf = require('rimraf'),
|
|
9
|
+
fs = require('fs'),
|
|
10
|
+
axios = require('axios'),
|
|
11
|
+
AdmZip = require("adm-zip"),
|
|
12
|
+
tmp = require('tmp'),
|
|
13
|
+
dbg= require('debug'),
|
|
14
|
+
node_modules = require('node_modules-path'),
|
|
15
|
+
{ parsePackageJson, PLATFORM_MAPPING, ARCH_MAPPING } = require('./utils');
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
const debug = dbg("cmf:debug");
|
|
19
|
+
const error = dbg("cmf:debug:error");
|
|
20
|
+
|
|
21
|
+
async function getInstallationPath() {
|
|
22
|
+
debug("Getting installation path...");
|
|
23
|
+
if (!!process.env.npm_config_global) {
|
|
24
|
+
debug("Install is global, so targeting home directory for binaries");
|
|
25
|
+
// install into home:
|
|
26
|
+
// win: /AppData/Local/CMF/cmf-cli
|
|
27
|
+
// linux: ~/.local/share/cmf-cli
|
|
28
|
+
// osx: ~/Library/Application Support/cmf-cli
|
|
29
|
+
const paths = envPaths("cmf-cli", {suffix: ""});
|
|
30
|
+
debug(`Install at ${paths.data}. Making sure path exists...`);
|
|
31
|
+
await mkdirp(paths.data);
|
|
32
|
+
debug(`Install path exists!`);
|
|
33
|
+
return paths.data;
|
|
34
|
+
} else {
|
|
35
|
+
debug("Install is local, so targeting node_modules/.bin/cmf-cli. Making sure path exists...");
|
|
36
|
+
// install into node_modules/.bin/cmf-cli
|
|
37
|
+
const value = path.join(node_modules(), ".bin");
|
|
38
|
+
const dir = path.join(value, "cmf-cli");
|
|
39
|
+
await mkdirp(dir);
|
|
40
|
+
debug(`Install path exists!`);
|
|
41
|
+
return dir;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function verifyAndPlaceBinary(binName, binPath, callback) {
|
|
46
|
+
if (!fs.existsSync(path.join(binPath, binName))) return callback('Downloaded binary does not contain the binary specified in configuration - ' + binName);
|
|
47
|
+
return callback(null);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Reads the configuration from application's package.json,
|
|
52
|
+
* validates properties, copied the binary from the package and stores at
|
|
53
|
+
* ./bin in the package's root. NPM already has support to install binary files
|
|
54
|
+
* specific locations when invoked with "npm install -g"
|
|
55
|
+
*
|
|
56
|
+
* See: https://docs.npmjs.com/files/package.json#bin
|
|
57
|
+
*/
|
|
58
|
+
var INVALID_INPUT = "Invalid inputs";
|
|
59
|
+
async function install(callback) {
|
|
60
|
+
|
|
61
|
+
var opts = parsePackageJson(".");
|
|
62
|
+
if (!opts) return callback(INVALID_INPUT);
|
|
63
|
+
console.info(`Copying the relevant binary for your platform ${process.platform}`);
|
|
64
|
+
const src= `./dist/${PLATFORM_MAPPING[process.platform]}-${ARCH_MAPPING[process.arch]}`;
|
|
65
|
+
|
|
66
|
+
if (!fs.existsSync("./dist")) {
|
|
67
|
+
// download respective release zip from github
|
|
68
|
+
const pkgUrl = opts.binUrl.replace("{{version}}", opts.version).replace("{{platform}}", PLATFORM_MAPPING[process.platform]).replace("{{arch}}", ARCH_MAPPING[process.arch]);
|
|
69
|
+
console.info(`Getting release archive from ${pkgUrl} into ${path.resolve(src)}`);
|
|
70
|
+
try {
|
|
71
|
+
const response = await axios({
|
|
72
|
+
url: pkgUrl,
|
|
73
|
+
method: 'GET',
|
|
74
|
+
responseType: 'arraybuffer', // to do this with streaming we must deal with chunking
|
|
75
|
+
});
|
|
76
|
+
const zip = tmp.tmpNameSync();
|
|
77
|
+
debug(`Writing temporary zip file to ${zip}`);
|
|
78
|
+
fs.writeFileSync(zip, response.data);
|
|
79
|
+
debug(`Extracting zip file ${zip} to ${src}`);
|
|
80
|
+
(new AdmZip(zip)).extractAllTo(src);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
error(e);
|
|
83
|
+
callback(`Could not install version ${opts.version} on your platform ${process.platform}/${process.arch}: ${e.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const installPath = await getInstallationPath();
|
|
88
|
+
if (process.platform === "win32") {
|
|
89
|
+
debug("Installing for windows");
|
|
90
|
+
await execShellCommand(`robocopy ${src.replace(/\//g, "\\")} "${installPath}" /e /is /it`, [1]);
|
|
91
|
+
} else {
|
|
92
|
+
debug("Installing for *NIX: " + process.platform);
|
|
93
|
+
await execShellCommand(`cp -r ${src}/** "${installPath}"`);
|
|
94
|
+
await execShellCommand(`chmod +x "${installPath}/cmf"`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
await verifyAndPlaceBinary(opts.binName, installPath, callback);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function uninstall(callback) {
|
|
101
|
+
var opts = parsePackageJson(".");
|
|
102
|
+
try {
|
|
103
|
+
const installationPath = await getInstallationPath();
|
|
104
|
+
debug("Deleting binaries from " + installationPath);
|
|
105
|
+
rimraf.sync(installationPath);
|
|
106
|
+
} catch (ex) {
|
|
107
|
+
console.log(ex);
|
|
108
|
+
callback(ex);
|
|
109
|
+
// Ignore errors when deleting the file.
|
|
110
|
+
}
|
|
111
|
+
console.info("Uninstalled cli successfully");
|
|
112
|
+
return callback(null);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Parse command line arguments and call the right method
|
|
116
|
+
var actions = {
|
|
117
|
+
"install": install,
|
|
118
|
+
"uninstall": uninstall
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Executes a shell command and return it as a Promise.
|
|
122
|
+
* @param cmd {string}
|
|
123
|
+
* @return {Promise<string>}
|
|
124
|
+
*/
|
|
125
|
+
function execShellCommand(cmd, valid_error_codes = null) {
|
|
126
|
+
const exec = require('child_process').exec;
|
|
127
|
+
return new Promise((resolve, reject) => {
|
|
128
|
+
exec(cmd, (error, stdout, stderr) => {
|
|
129
|
+
if (error) {
|
|
130
|
+
let shouldPrint = true;
|
|
131
|
+
if (valid_error_codes != null) {
|
|
132
|
+
shouldPrint = valid_error_codes.indexOf(error.code) < 0;
|
|
133
|
+
}
|
|
134
|
+
shouldPrint && console.warn(error);
|
|
135
|
+
}
|
|
136
|
+
resolve(stdout? stdout : stderr);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
var argv = process.argv;
|
|
142
|
+
if (argv && argv.length > 2) {
|
|
143
|
+
var cmd = process.argv[2];
|
|
144
|
+
if (!actions[cmd]) {
|
|
145
|
+
console.log("Invalid command. `install` and `uninstall` are the only supported commands");
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
actions[cmd](function (err) {
|
|
150
|
+
if (err) {
|
|
151
|
+
console.error(err);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
} else {
|
|
154
|
+
process.exit(0);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
package/run.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const envPaths = require('env-paths');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const isInstalledGlobally = require('is-installed-globally');
|
|
8
|
+
const node_modules = require('node_modules-path');
|
|
9
|
+
const dbg = require('debug');
|
|
10
|
+
const { parsePackageJson, ARCH_MAPPING, PLATFORM_MAPPING } = require('./utils');
|
|
11
|
+
|
|
12
|
+
const opts = parsePackageJson(__dirname);
|
|
13
|
+
const debug = dbg("cmf:debug");
|
|
14
|
+
|
|
15
|
+
debug("Executing cmf-cli");
|
|
16
|
+
let exePath = null;
|
|
17
|
+
debug("Determining if cli is installed globally or locally...");
|
|
18
|
+
if (isInstalledGlobally) {
|
|
19
|
+
debug("cli is installed globally. Getting binary location from user profile...");
|
|
20
|
+
const paths = envPaths("cmf-cli", {suffix: ""});
|
|
21
|
+
exePath = path.join(paths.data, opts.binName);
|
|
22
|
+
} else {
|
|
23
|
+
debug("cli is installed locally. Getting binary from node_modules/.bin/cmf-cli...");
|
|
24
|
+
exePath = path.join(node_modules(), ".bin", "cmf-cli", opts.binName);
|
|
25
|
+
}
|
|
26
|
+
debug("Obtained binary path: " + exePath);
|
|
27
|
+
|
|
28
|
+
debug(`Spawning cmf-cli from ${exePath} with args ${process.argv.slice(2)} and piping.`);
|
|
29
|
+
const child = spawn(exePath, process.argv.slice(2), {stdio: "inherit"});
|
|
30
|
+
child.on('close', (code) => {
|
|
31
|
+
debug("Process exited with code " + code);
|
|
32
|
+
process.exitCode = code;
|
|
33
|
+
});
|
package/utils.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Thanks to author of https://github.com/sanathkr/go-npm, we were able to modify his code to work with private packages
|
|
2
|
+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
3
|
+
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
// Mapping from Node's `process.arch` to dotnet's `RID`
|
|
8
|
+
var ARCH_MAPPING = {
|
|
9
|
+
"ia32": "x86",
|
|
10
|
+
"x64": "x64",
|
|
11
|
+
// "arm": "arm" - linux RID? can only find win-arm which is not useful
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// Mapping between Node's `process.platform` to dotnet's RID
|
|
15
|
+
var PLATFORM_MAPPING = {
|
|
16
|
+
"darwin": "osx",
|
|
17
|
+
"linux": "linux",
|
|
18
|
+
"win32": "win"
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function validateConfiguration(packageJson) {
|
|
22
|
+
|
|
23
|
+
if (!packageJson.version) {
|
|
24
|
+
return "'version' property must be specified";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!packageJson.goBinary || _typeof(packageJson.goBinary) !== "object") {
|
|
28
|
+
return "'goBinary' property must be defined and be an object";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!packageJson.goBinary.name) {
|
|
32
|
+
return "'name' property is necessary";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!packageJson.goBinary.path) {
|
|
36
|
+
return "'path' property is necessary";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parsePackageJson(root) {
|
|
41
|
+
if (!(process.arch in ARCH_MAPPING)) {
|
|
42
|
+
console.error("Installation is not supported for this architecture: " + process.arch);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!(process.platform in PLATFORM_MAPPING)) {
|
|
47
|
+
console.error("Installation is not supported for this platform: " + process.platform);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
var packageJsonPath = path.join(root, "package.json");
|
|
52
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
53
|
+
console.error("Unable to find package.json. " + "Please run this script at root of the package you want to be installed");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
var packageJson = JSON.parse(fs.readFileSync(packageJsonPath));
|
|
58
|
+
var error = validateConfiguration(packageJson);
|
|
59
|
+
if (error && error.length > 0) {
|
|
60
|
+
console.error("Invalid package.json: " + error);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// We have validated the config. It exists in all its glory
|
|
65
|
+
var binName = packageJson.goBinary.name;
|
|
66
|
+
var binPath = packageJson.goBinary.path;
|
|
67
|
+
var binUrl = packageJson.goBinary.url;
|
|
68
|
+
var version = packageJson.version;
|
|
69
|
+
if (version[0] === 'v') version = version.substr(1); // strip the 'v' if necessary v0.0.1 => 0.0.1
|
|
70
|
+
|
|
71
|
+
// Binary name on Windows has .exe suffix
|
|
72
|
+
if (process.platform === "win32") {
|
|
73
|
+
binName += ".exe";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
binName: binName,
|
|
79
|
+
binPath: binPath,
|
|
80
|
+
binUrl: binUrl,
|
|
81
|
+
version: version
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = {
|
|
86
|
+
parsePackageJson,
|
|
87
|
+
ARCH_MAPPING,
|
|
88
|
+
PLATFORM_MAPPING
|
|
89
|
+
}
|