@servicetitan/install 32.5.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.
- package/bin/index.js +24 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +48 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/install.d.ts +15 -0
- package/dist/cli/install.d.ts.map +1 -0
- package/dist/cli/install.js +150 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/ci.d.ts +2 -0
- package/dist/utils/ci.d.ts.map +1 -0
- package/dist/utils/ci.js +15 -0
- package/dist/utils/ci.js.map +1 -0
- package/dist/utils/debug.d.ts +2 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +18 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/get-version.d.ts +2 -0
- package/dist/utils/get-version.d.ts.map +1 -0
- package/dist/utils/get-version.js +22 -0
- package/dist/utils/get-version.js.map +1 -0
- package/dist/utils/git.d.ts +10 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +76 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +26 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/npm.d.ts +4 -0
- package/dist/utils/npm.d.ts.map +1 -0
- package/dist/utils/npm.js +37 -0
- package/dist/utils/npm.js.map +1 -0
- package/dist/utils/os.d.ts +5 -0
- package/dist/utils/os.d.ts.map +1 -0
- package/dist/utils/os.js +39 -0
- package/dist/utils/os.js.map +1 -0
- package/dist/utils/read-json.d.ts +2 -0
- package/dist/utils/read-json.d.ts.map +1 -0
- package/dist/utils/read-json.js +23 -0
- package/dist/utils/read-json.js.map +1 -0
- package/package.json +36 -0
- package/src/cli/index.ts +34 -0
- package/src/cli/install.ts +124 -0
- package/src/index.ts +1 -0
- package/src/utils/ci.ts +3 -0
- package/src/utils/debug.ts +7 -0
- package/src/utils/get-version.ts +6 -0
- package/src/utils/git.ts +47 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/npm.ts +18 -0
- package/src/utils/os.ts +18 -0
- package/src/utils/read-json.ts +5 -0
package/bin/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
let installPath;
|
|
4
|
+
try {
|
|
5
|
+
installPath = require.resolve('@servicetitan/install');
|
|
6
|
+
} catch {} // eslint-disable-line no-empty
|
|
7
|
+
|
|
8
|
+
if (installPath && (installPath.includes('node_modules') || installPath.includes('.yalc'))) {
|
|
9
|
+
require('../dist/cli/index.js');
|
|
10
|
+
} else {
|
|
11
|
+
/*
|
|
12
|
+
* Workaround to prevent sporadic ReadSingleBytecodeData errors inside V8
|
|
13
|
+
* ts-node is using v8-compile-cache-lib to cache require statements
|
|
14
|
+
* See: https://github.com/nodejs/node/issues/51555
|
|
15
|
+
*/
|
|
16
|
+
if (process.env.CI) {
|
|
17
|
+
process.env.DISABLE_V8_COMPILE_CACHE = '1';
|
|
18
|
+
}
|
|
19
|
+
require('ts-node').register({
|
|
20
|
+
project: require.resolve('../tsconfig.json'),
|
|
21
|
+
transpileOnly: true,
|
|
22
|
+
});
|
|
23
|
+
require('../src/cli/index.ts');
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _util = require("util");
|
|
6
|
+
const _install = require("./install");
|
|
7
|
+
const _utils = require("../utils");
|
|
8
|
+
const args = function getArgs() {
|
|
9
|
+
const { values, tokens } = (0, _util.parseArgs)({
|
|
10
|
+
args: process.argv.slice(2),
|
|
11
|
+
options: {
|
|
12
|
+
'fix': {
|
|
13
|
+
type: 'boolean'
|
|
14
|
+
},
|
|
15
|
+
'quiet': {
|
|
16
|
+
type: 'boolean'
|
|
17
|
+
},
|
|
18
|
+
'token': {
|
|
19
|
+
type: 'boolean'
|
|
20
|
+
},
|
|
21
|
+
'no-token': {
|
|
22
|
+
type: 'boolean'
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
tokens: true
|
|
26
|
+
});
|
|
27
|
+
tokens.forEach((token)=>{
|
|
28
|
+
if (token.kind === 'option') {
|
|
29
|
+
if (token.name.startsWith('no-')) {
|
|
30
|
+
const name = token.name.slice(3);
|
|
31
|
+
values[name] = false;
|
|
32
|
+
delete values[token.name];
|
|
33
|
+
} else {
|
|
34
|
+
var _token_value;
|
|
35
|
+
values[token.name] = (_token_value = token.value) !== null && _token_value !== void 0 ? _token_value : true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
...values
|
|
41
|
+
};
|
|
42
|
+
}();
|
|
43
|
+
(0, _utils.debug)('install:args', {
|
|
44
|
+
args
|
|
45
|
+
});
|
|
46
|
+
new _install.Install(args).execute();
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["import { parseArgs } from 'util';\nimport { Install } from './install';\nimport { debug } from '../utils';\n\nconst args = (function getArgs() {\n const { values, tokens } = parseArgs({\n args: process.argv.slice(2),\n options: {\n 'fix': { type: 'boolean' },\n 'quiet': { type: 'boolean' },\n 'token': { type: 'boolean' },\n 'no-token': { type: 'boolean' },\n },\n tokens: true,\n });\n\n tokens.forEach(token => {\n if (token.kind === 'option') {\n if (token.name.startsWith('no-')) {\n const name = token.name.slice(3) as keyof typeof values;\n values[name] = false;\n delete values[token.name];\n } else {\n values[token.name] = token.value ?? true;\n }\n }\n });\n\n return { ...values };\n})();\n\ndebug('install:args', { args });\n\nnew Install(args).execute();\n"],"names":["args","getArgs","values","tokens","parseArgs","process","argv","slice","options","type","forEach","token","kind","name","startsWith","value","debug","Install","execute"],"mappings":";;;;sBAA0B;yBACF;uBACF;AAEtB,MAAMA,OAAO,AAAC,SAASC;IACnB,MAAM,EAAEC,MAAM,EAAEC,MAAM,EAAE,GAAGC,IAAAA,eAAS,EAAC;QACjCJ,MAAMK,QAAQC,IAAI,CAACC,KAAK,CAAC;QACzBC,SAAS;YACL,OAAO;gBAAEC,MAAM;YAAU;YACzB,SAAS;gBAAEA,MAAM;YAAU;YAC3B,SAAS;gBAAEA,MAAM;YAAU;YAC3B,YAAY;gBAAEA,MAAM;YAAU;QAClC;QACAN,QAAQ;IACZ;IAEAA,OAAOO,OAAO,CAACC,CAAAA;QACX,IAAIA,MAAMC,IAAI,KAAK,UAAU;YACzB,IAAID,MAAME,IAAI,CAACC,UAAU,CAAC,QAAQ;gBAC9B,MAAMD,OAAOF,MAAME,IAAI,CAACN,KAAK,CAAC;gBAC9BL,MAAM,CAACW,KAAK,GAAG;gBACf,OAAOX,MAAM,CAACS,MAAME,IAAI,CAAC;YAC7B,OAAO;oBACkBF;gBAArBT,MAAM,CAACS,MAAME,IAAI,CAAC,GAAGF,CAAAA,eAAAA,MAAMI,KAAK,cAAXJ,0BAAAA,eAAe;YACxC;QACJ;IACJ;IAEA,OAAO;QAAE,GAAGT,MAAM;IAAC;AACvB;AAEAc,IAAAA,YAAK,EAAC,gBAAgB;IAAEhB;AAAK;AAE7B,IAAIiB,gBAAO,CAACjB,MAAMkB,OAAO"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface Args {
|
|
2
|
+
debug?: boolean;
|
|
3
|
+
fix?: boolean;
|
|
4
|
+
quiet?: boolean;
|
|
5
|
+
token?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare class Install {
|
|
8
|
+
args: Args;
|
|
9
|
+
constructor(args: Args);
|
|
10
|
+
execute(): void;
|
|
11
|
+
private configureNpmToken;
|
|
12
|
+
private fetchNpmToken;
|
|
13
|
+
private installPackages;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/cli/install.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,IAAI;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,OAAO;IAChB,IAAI,EAAE,IAAI,CAAC;gBAEC,IAAI,EAAE,IAAI;IAItB,OAAO;IASP,OAAO,CAAC,iBAAiB;IAoCzB,OAAO,CAAC,aAAa;IAmCrB,OAAO,CAAC,eAAe;CAmB1B"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/* eslint-disable no-console */ "use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "Install", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return Install;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
|
|
12
|
+
const _fs = /*#__PURE__*/ _interop_require_default(require("fs"));
|
|
13
|
+
const _os = /*#__PURE__*/ _interop_require_default(require("os"));
|
|
14
|
+
const _utils = require("../utils");
|
|
15
|
+
function _define_property(obj, key, value) {
|
|
16
|
+
if (key in obj) {
|
|
17
|
+
Object.defineProperty(obj, key, {
|
|
18
|
+
value: value,
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
writable: true
|
|
22
|
+
});
|
|
23
|
+
} else {
|
|
24
|
+
obj[key] = value;
|
|
25
|
+
}
|
|
26
|
+
return obj;
|
|
27
|
+
}
|
|
28
|
+
function _interop_require_default(obj) {
|
|
29
|
+
return obj && obj.__esModule ? obj : {
|
|
30
|
+
default: obj
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const REPO_NAME = 'frontend-dev-config';
|
|
34
|
+
const AUTH_TOKEN_KEY = '//registry.npmjs.org/:_authToken';
|
|
35
|
+
const AUTH_TOKEN_REGEX = /^\/\/registry\.npmjs\.org\/:_authToken=\s*\${([^}]+)}/m;
|
|
36
|
+
class Install {
|
|
37
|
+
execute() {
|
|
38
|
+
if (!this.args.quiet) {
|
|
39
|
+
console.log(`install cli v${(0, _utils.getVersion)()}`);
|
|
40
|
+
}
|
|
41
|
+
const env = this.configureNpmToken();
|
|
42
|
+
this.installPackages(env);
|
|
43
|
+
}
|
|
44
|
+
configureNpmToken() {
|
|
45
|
+
if ((0, _utils.isCI)() || this.args.fix || this.args.token === false) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!this.args.quiet) {
|
|
49
|
+
console.log('Configuring NPM token ...');
|
|
50
|
+
}
|
|
51
|
+
if (this.args.token !== true) {
|
|
52
|
+
if ((0, _utils.npmWhoAmI)() === 'st-team') {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const token = this.fetchNpmToken();
|
|
57
|
+
if (!token) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
(0, _utils.runCommand)('npm', [
|
|
61
|
+
'config',
|
|
62
|
+
'set',
|
|
63
|
+
`${AUTH_TOKEN_KEY}=${token}`
|
|
64
|
+
]);
|
|
65
|
+
if (!_fs.default.existsSync('.npmrc')) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const npmrc = _fs.default.readFileSync('.npmrc', 'utf-8');
|
|
69
|
+
const match = AUTH_TOKEN_REGEX.exec(npmrc);
|
|
70
|
+
(0, _utils.debug)({
|
|
71
|
+
match
|
|
72
|
+
});
|
|
73
|
+
if (match) {
|
|
74
|
+
return {
|
|
75
|
+
[match[1]]: token
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
(0, _utils.runCommand)('npm', [
|
|
79
|
+
'config',
|
|
80
|
+
'delete',
|
|
81
|
+
'--location=project',
|
|
82
|
+
AUTH_TOKEN_KEY
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
fetchNpmToken() {
|
|
86
|
+
const tempDirPath = _fs.default.mkdtempSync(_path.default.join(_os.default.tmpdir(), 'st-install-'));
|
|
87
|
+
try {
|
|
88
|
+
if (!(0, _utils.gitCloneRepo)({
|
|
89
|
+
destination: tempDirPath,
|
|
90
|
+
name: REPO_NAME
|
|
91
|
+
})) {
|
|
92
|
+
throw new Error(`could not clone servicetitan/${REPO_NAME}`);
|
|
93
|
+
}
|
|
94
|
+
const npmJson = (0, _utils.readJson)(_path.default.join(tempDirPath, '.npm.json'));
|
|
95
|
+
(0, _utils.debug)('install:fetch-npm-token', {
|
|
96
|
+
npmJson
|
|
97
|
+
});
|
|
98
|
+
if (!(npmJson && typeof npmJson === 'object' || Array.isArray(npmJson))) {
|
|
99
|
+
throw new Error('.npm.json is not an object');
|
|
100
|
+
}
|
|
101
|
+
const { readOnlyToken: authToken } = npmJson;
|
|
102
|
+
if (!authToken) {
|
|
103
|
+
throw new Error('.npm.json does not contain auth token');
|
|
104
|
+
}
|
|
105
|
+
if (typeof authToken !== 'string') {
|
|
106
|
+
throw new Error('.npm.json auth token is not a string');
|
|
107
|
+
}
|
|
108
|
+
return Buffer.from(authToken, 'base64').toString('utf-8');
|
|
109
|
+
} catch (e) {
|
|
110
|
+
console.warn(String(e));
|
|
111
|
+
} finally{
|
|
112
|
+
try {
|
|
113
|
+
_fs.default.rmSync(tempDirPath, {
|
|
114
|
+
recursive: true,
|
|
115
|
+
force: true
|
|
116
|
+
});
|
|
117
|
+
} catch (e) {
|
|
118
|
+
// ignore
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
installPackages(env) {
|
|
123
|
+
if (this.args.token) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const npmArguments = [
|
|
127
|
+
(0, _utils.isCI)() ? 'ci' : 'i',
|
|
128
|
+
'--audit=false',
|
|
129
|
+
'--fund=false',
|
|
130
|
+
'--legacy-peer-deps',
|
|
131
|
+
...this.args.fix ? [
|
|
132
|
+
'--package-lock-only',
|
|
133
|
+
'--prefer-dedupe'
|
|
134
|
+
] : []
|
|
135
|
+
];
|
|
136
|
+
if (!this.args.quiet) {
|
|
137
|
+
console.log(`Running npm ${npmArguments.join(' ')}`);
|
|
138
|
+
}
|
|
139
|
+
(0, _utils.runCommand)('npm', npmArguments, {
|
|
140
|
+
env,
|
|
141
|
+
stdio: 'inherit'
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
constructor(args){
|
|
145
|
+
_define_property(this, "args", void 0);
|
|
146
|
+
this.args = args;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/install.ts"],"sourcesContent":["/* eslint-disable no-console */\nimport path from 'path';\nimport fs from 'fs';\nimport os from 'os';\nimport { debug, getVersion, gitCloneRepo, isCI, npmWhoAmI, readJson, runCommand } from '../utils';\n\nconst REPO_NAME = 'frontend-dev-config';\nconst AUTH_TOKEN_KEY = '//registry.npmjs.org/:_authToken';\nconst AUTH_TOKEN_REGEX = /^\\/\\/registry\\.npmjs\\.org\\/:_authToken=\\s*\\${([^}]+)}/m;\n\nexport interface Args {\n debug?: boolean;\n fix?: boolean;\n quiet?: boolean;\n token?: boolean;\n}\n\nexport class Install {\n args: Args;\n\n constructor(args: Args) {\n this.args = args;\n }\n\n execute() {\n if (!this.args.quiet) {\n console.log(`install cli v${getVersion()}`);\n }\n\n const env = this.configureNpmToken();\n this.installPackages(env);\n }\n\n private configureNpmToken() {\n if (isCI() || this.args.fix || this.args.token === false) {\n return;\n }\n\n if (!this.args.quiet) {\n console.log('Configuring NPM token ...');\n }\n\n if (this.args.token !== true) {\n if (npmWhoAmI() === 'st-team') {\n return;\n }\n }\n\n const token = this.fetchNpmToken();\n if (!token) {\n return;\n }\n\n runCommand('npm', ['config', 'set', `${AUTH_TOKEN_KEY}=${token}`]);\n\n if (!fs.existsSync('.npmrc')) {\n return;\n }\n\n const npmrc = fs.readFileSync('.npmrc', 'utf-8');\n const match = AUTH_TOKEN_REGEX.exec(npmrc);\n debug({ match });\n if (match) {\n return { [match[1]]: token };\n }\n\n runCommand('npm', ['config', 'delete', '--location=project', AUTH_TOKEN_KEY]);\n }\n\n private fetchNpmToken() {\n const tempDirPath = fs.mkdtempSync(path.join(os.tmpdir(), 'st-install-'));\n try {\n if (!gitCloneRepo({ destination: tempDirPath, name: REPO_NAME })) {\n throw new Error(`could not clone servicetitan/${REPO_NAME}`);\n }\n\n const npmJson = readJson(path.join(tempDirPath, '.npm.json'));\n debug('install:fetch-npm-token', { npmJson });\n\n if (!((npmJson && typeof npmJson === 'object') || Array.isArray(npmJson))) {\n throw new Error('.npm.json is not an object');\n }\n\n const { readOnlyToken: authToken } = npmJson;\n if (!authToken) {\n throw new Error('.npm.json does not contain auth token');\n }\n\n if (typeof authToken !== 'string') {\n throw new Error('.npm.json auth token is not a string');\n }\n\n return Buffer.from(authToken, 'base64').toString('utf-8');\n } catch (e) {\n console.warn(String(e));\n } finally {\n try {\n fs.rmSync(tempDirPath, { recursive: true, force: true });\n } catch {\n // ignore\n }\n }\n }\n\n private installPackages(env?: Record<string, string>) {\n if (this.args.token) {\n return;\n }\n\n const npmArguments = [\n isCI() ? 'ci' : 'i',\n '--audit=false',\n '--fund=false',\n '--legacy-peer-deps',\n ...(this.args.fix ? ['--package-lock-only', '--prefer-dedupe'] : []),\n ];\n\n if (!this.args.quiet) {\n console.log(`Running npm ${npmArguments.join(' ')}`);\n }\n\n runCommand('npm', npmArguments, { env, stdio: 'inherit' });\n }\n}\n"],"names":["Install","REPO_NAME","AUTH_TOKEN_KEY","AUTH_TOKEN_REGEX","execute","args","quiet","console","log","getVersion","env","configureNpmToken","installPackages","isCI","fix","token","npmWhoAmI","fetchNpmToken","runCommand","fs","existsSync","npmrc","readFileSync","match","exec","debug","tempDirPath","mkdtempSync","path","join","os","tmpdir","gitCloneRepo","destination","name","Error","npmJson","readJson","Array","isArray","readOnlyToken","authToken","Buffer","from","toString","e","warn","String","rmSync","recursive","force","npmArguments","stdio"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
get gitCloneRepo () {
|
|
13
|
+
return _utils.gitCloneRepo;
|
|
14
|
+
},
|
|
15
|
+
get gitIsReachable () {
|
|
16
|
+
return _utils.gitIsReachable;
|
|
17
|
+
},
|
|
18
|
+
get isCI () {
|
|
19
|
+
return _utils.isCI;
|
|
20
|
+
},
|
|
21
|
+
get npmWhoAmI () {
|
|
22
|
+
return _utils.npmWhoAmI;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const _utils = require("./utils");
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { gitCloneRepo, gitIsReachable, isCI, npmWhoAmI } from './utils';\n"],"names":["gitCloneRepo","gitIsReachable","isCI","npmWhoAmI"],"mappings":";;;;;;;;;;;QAASA;eAAAA,mBAAY;;QAAEC;eAAAA,qBAAc;;QAAEC;eAAAA,WAAI;;QAAEC;eAAAA,gBAAS;;;uBAAQ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci.d.ts","sourceRoot":"","sources":["../../src/utils/ci.ts"],"names":[],"mappings":"AAAA,wBAAgB,IAAI,YAEnB"}
|
package/dist/utils/ci.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "isCI", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return isCI;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
function isCI() {
|
|
12
|
+
return !!process.env.CI || !!process.env.TEAMCITY_VERSION;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=ci.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/ci.ts"],"sourcesContent":["export function isCI() {\n return !!process.env.CI || !!process.env.TEAMCITY_VERSION;\n}\n"],"names":["isCI","process","env","CI","TEAMCITY_VERSION"],"mappings":";;;;+BAAgBA;;;eAAAA;;;AAAT,SAASA;IACZ,OAAO,CAAC,CAACC,QAAQC,GAAG,CAACC,EAAE,IAAI,CAAC,CAACF,QAAQC,GAAG,CAACE,gBAAgB;AAC7D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../../src/utils/debug.ts"],"names":[],"mappings":"AACA,wBAAgB,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,QAKnC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* istanbul ignore next: debug only */ "use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "debug", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return debug;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
function debug(...args) {
|
|
12
|
+
if (process.env.DEBUG && /install/.test(process.env.DEBUG)) {
|
|
13
|
+
// eslint-disable-next-line no-console
|
|
14
|
+
console.debug(...args);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=debug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/debug.ts"],"sourcesContent":["/* istanbul ignore next: debug only */\nexport function debug(...args: any[]) {\n if (process.env.DEBUG && /install/.test(process.env.DEBUG)) {\n // eslint-disable-next-line no-console\n console.debug(...args);\n }\n}\n"],"names":["debug","args","process","env","DEBUG","test","console"],"mappings":"AAAA,oCAAoC;;;;+BACpBA;;;eAAAA;;;AAAT,SAASA,MAAM,GAAGC,IAAW;IAChC,IAAIC,QAAQC,GAAG,CAACC,KAAK,IAAI,UAAUC,IAAI,CAACH,QAAQC,GAAG,CAACC,KAAK,GAAG;QACxD,sCAAsC;QACtCE,QAAQN,KAAK,IAAIC;IACrB;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-version.d.ts","sourceRoot":"","sources":["../../src/utils/get-version.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,QAEzB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "getVersion", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return getVersion;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
|
|
12
|
+
const _readjson = require("./read-json");
|
|
13
|
+
function _interop_require_default(obj) {
|
|
14
|
+
return obj && obj.__esModule ? obj : {
|
|
15
|
+
default: obj
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function getVersion() {
|
|
19
|
+
return (0, _readjson.readJson)(_path.default.join(__dirname, '../../package.json')).version;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=get-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/get-version.ts"],"sourcesContent":["import path from 'path';\nimport { readJson } from './read-json';\n\nexport function getVersion() {\n return readJson(path.join(__dirname, '../../package.json')).version;\n}\n"],"names":["getVersion","readJson","path","join","__dirname","version"],"mappings":";;;;+BAGgBA;;;eAAAA;;;6DAHC;0BACQ;;;;;;AAElB,SAASA;IACZ,OAAOC,IAAAA,kBAAQ,EAACC,aAAI,CAACC,IAAI,CAACC,WAAW,uBAAuBC,OAAO;AACvE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Repo {
|
|
2
|
+
owner?: string;
|
|
3
|
+
name: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function gitCloneRepo(params: Repo & {
|
|
6
|
+
destination: string;
|
|
7
|
+
}): boolean;
|
|
8
|
+
export declare function gitIsReachable({ owner, name }: Repo): boolean;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAGA,UAAU,IAAI;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,WAkBlE;AAED,wBAAgB,cAAc,CAAC,EAAE,KAAsB,EAAE,IAAI,EAAE,EAAE,IAAI,WASpE"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
get gitCloneRepo () {
|
|
13
|
+
return gitCloneRepo;
|
|
14
|
+
},
|
|
15
|
+
get gitIsReachable () {
|
|
16
|
+
return gitIsReachable;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const _ci = require("./ci");
|
|
20
|
+
const _os = require("./os");
|
|
21
|
+
function gitCloneRepo(params) {
|
|
22
|
+
const { destination, name, owner = 'servicetitan' } = params;
|
|
23
|
+
const gitUrls = getGitUrls({
|
|
24
|
+
owner,
|
|
25
|
+
name
|
|
26
|
+
});
|
|
27
|
+
for(let i = 0, n = gitUrls.length; i < n; i++){
|
|
28
|
+
try {
|
|
29
|
+
(0, _os.runCommand)('git', [
|
|
30
|
+
'clone',
|
|
31
|
+
'-q',
|
|
32
|
+
gitUrls[i],
|
|
33
|
+
destination
|
|
34
|
+
], {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
36
|
+
env: {
|
|
37
|
+
GIT_TERMINAL_PROMPT: '0'
|
|
38
|
+
},
|
|
39
|
+
stdio: i === n - 1 ? 'inherit' : 'ignore'
|
|
40
|
+
});
|
|
41
|
+
return true;
|
|
42
|
+
} catch (e) {
|
|
43
|
+
// ignore error
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
function gitIsReachable({ owner = 'servicetitan', name }) {
|
|
49
|
+
return getGitUrls({
|
|
50
|
+
owner,
|
|
51
|
+
name
|
|
52
|
+
}).some((url)=>{
|
|
53
|
+
try {
|
|
54
|
+
(0, _os.runCommand)('git', [
|
|
55
|
+
'ls-remote',
|
|
56
|
+
'-qt',
|
|
57
|
+
url
|
|
58
|
+
]);
|
|
59
|
+
return true;
|
|
60
|
+
} catch (e) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function getGitUrls({ owner, name }) {
|
|
66
|
+
const webUrl = `https://github.com/${owner}/${name}.git`;
|
|
67
|
+
const sshUrl = `git@github.com:${owner}/${name}.git`;
|
|
68
|
+
return (0, _ci.isCI)() && !!process.env.GITHUB_TOKEN ? [
|
|
69
|
+
webUrl.replace('github.com', `oauth2:${process.env.GITHUB_TOKEN}@github.com`)
|
|
70
|
+
] : [
|
|
71
|
+
sshUrl,
|
|
72
|
+
webUrl
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/git.ts"],"sourcesContent":["import { isCI } from './ci';\nimport { runCommand } from './os';\n\ninterface Repo {\n owner?: string;\n name: string;\n}\n\nexport function gitCloneRepo(params: Repo & { destination: string }) {\n const { destination, name, owner = 'servicetitan' } = params;\n const gitUrls = getGitUrls({ owner, name });\n\n for (let i = 0, n = gitUrls.length; i < n; i++) {\n try {\n runCommand('git', ['clone', '-q', gitUrls[i], destination], {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n env: { GIT_TERMINAL_PROMPT: '0' },\n stdio: i === n - 1 ? 'inherit' : 'ignore', // show errors on last attempt\n });\n return true;\n } catch {\n // ignore error\n }\n }\n\n return false;\n}\n\nexport function gitIsReachable({ owner = 'servicetitan', name }: Repo) {\n return getGitUrls({ owner, name }).some(url => {\n try {\n runCommand('git', ['ls-remote', '-qt', url]);\n return true;\n } catch {\n return false;\n }\n });\n}\n\nfunction getGitUrls({ owner, name }: Repo) {\n const webUrl = `https://github.com/${owner}/${name}.git`;\n const sshUrl = `git@github.com:${owner}/${name}.git`;\n\n return isCI() && !!process.env.GITHUB_TOKEN\n ? [webUrl.replace('github.com', `oauth2:${process.env.GITHUB_TOKEN}@github.com`)]\n : [sshUrl, webUrl];\n}\n"],"names":["gitCloneRepo","gitIsReachable","params","destination","name","owner","gitUrls","getGitUrls","i","n","length","runCommand","env","GIT_TERMINAL_PROMPT","stdio","some","url","webUrl","sshUrl","isCI","process","GITHUB_TOKEN","replace"],"mappings":";;;;;;;;;;;QAQgBA;eAAAA;;QAoBAC;eAAAA;;;oBA5BK;oBACM;AAOpB,SAASD,aAAaE,MAAsC;IAC/D,MAAM,EAAEC,WAAW,EAAEC,IAAI,EAAEC,QAAQ,cAAc,EAAE,GAAGH;IACtD,MAAMI,UAAUC,WAAW;QAAEF;QAAOD;IAAK;IAEzC,IAAK,IAAII,IAAI,GAAGC,IAAIH,QAAQI,MAAM,EAAEF,IAAIC,GAAGD,IAAK;QAC5C,IAAI;YACAG,IAAAA,cAAU,EAAC,OAAO;gBAAC;gBAAS;gBAAML,OAAO,CAACE,EAAE;gBAAEL;aAAY,EAAE;gBACxD,gEAAgE;gBAChES,KAAK;oBAAEC,qBAAqB;gBAAI;gBAChCC,OAAON,MAAMC,IAAI,IAAI,YAAY;YACrC;YACA,OAAO;QACX,EAAE,UAAM;QACJ,eAAe;QACnB;IACJ;IAEA,OAAO;AACX;AAEO,SAASR,eAAe,EAAEI,QAAQ,cAAc,EAAED,IAAI,EAAQ;IACjE,OAAOG,WAAW;QAAEF;QAAOD;IAAK,GAAGW,IAAI,CAACC,CAAAA;QACpC,IAAI;YACAL,IAAAA,cAAU,EAAC,OAAO;gBAAC;gBAAa;gBAAOK;aAAI;YAC3C,OAAO;QACX,EAAE,UAAM;YACJ,OAAO;QACX;IACJ;AACJ;AAEA,SAAST,WAAW,EAAEF,KAAK,EAAED,IAAI,EAAQ;IACrC,MAAMa,SAAS,CAAC,mBAAmB,EAAEZ,MAAM,CAAC,EAAED,KAAK,IAAI,CAAC;IACxD,MAAMc,SAAS,CAAC,eAAe,EAAEb,MAAM,CAAC,EAAED,KAAK,IAAI,CAAC;IAEpD,OAAOe,IAAAA,QAAI,OAAM,CAAC,CAACC,QAAQR,GAAG,CAACS,YAAY,GACrC;QAACJ,OAAOK,OAAO,CAAC,cAAc,CAAC,OAAO,EAAEF,QAAQR,GAAG,CAACS,YAAY,CAAC,WAAW,CAAC;KAAE,GAC/E;QAACH;QAAQD;KAAO;AAC1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,MAAM,CAAC;AACrB,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,OAAO,CAAC;AACtB,cAAc,OAAO,CAAC;AACtB,cAAc,MAAM,CAAC;AACrB,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
_export_star(require("./ci"), exports);
|
|
6
|
+
_export_star(require("./debug"), exports);
|
|
7
|
+
_export_star(require("./get-version"), exports);
|
|
8
|
+
_export_star(require("./git"), exports);
|
|
9
|
+
_export_star(require("./npm"), exports);
|
|
10
|
+
_export_star(require("./os"), exports);
|
|
11
|
+
_export_star(require("./read-json"), exports);
|
|
12
|
+
function _export_star(from, to) {
|
|
13
|
+
Object.keys(from).forEach(function(k) {
|
|
14
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
15
|
+
Object.defineProperty(to, k, {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function() {
|
|
18
|
+
return from[k];
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return from;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/index.ts"],"sourcesContent":["export * from './ci';\nexport * from './debug';\nexport * from './get-version';\nexport * from './git';\nexport * from './npm';\nexport * from './os';\nexport * from './read-json';\n"],"names":[],"mappings":";;;;qBAAc;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"npm.d.ts","sourceRoot":"","sources":["../../src/utils/npm.ts"],"names":[],"mappings":"AAGA,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,sBAcjE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "npmWhoAmI", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return npmWhoAmI;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _debug = require("./debug");
|
|
12
|
+
const _os = require("./os");
|
|
13
|
+
function npmWhoAmI({ registry } = {}) {
|
|
14
|
+
try {
|
|
15
|
+
const result = (0, _os.runCommand)('npm', [
|
|
16
|
+
'whoami',
|
|
17
|
+
...registry ? [
|
|
18
|
+
`--registry=${registry}`
|
|
19
|
+
] : []
|
|
20
|
+
], {
|
|
21
|
+
stdio: [
|
|
22
|
+
'pipe',
|
|
23
|
+
'pipe',
|
|
24
|
+
'ignore'
|
|
25
|
+
],
|
|
26
|
+
timeout: 10000
|
|
27
|
+
}).trim();
|
|
28
|
+
(0, _debug.debug)('install:whoami', {
|
|
29
|
+
result
|
|
30
|
+
});
|
|
31
|
+
return result.trim();
|
|
32
|
+
} catch (e) {
|
|
33
|
+
// ignore
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=npm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/npm.ts"],"sourcesContent":["import { debug } from './debug';\nimport { runCommand } from './os';\n\nexport function npmWhoAmI({ registry }: { registry?: string } = {}) {\n try {\n const result = runCommand(\n 'npm',\n ['whoami', ...(registry ? [`--registry=${registry}`] : [])],\n { stdio: ['pipe', 'pipe', 'ignore'], timeout: 10000 }\n ).trim();\n\n debug('install:whoami', { result });\n\n return result.trim();\n } catch {\n // ignore\n }\n}\n"],"names":["npmWhoAmI","registry","result","runCommand","stdio","timeout","trim","debug"],"mappings":";;;;+BAGgBA;;;eAAAA;;;uBAHM;oBACK;AAEpB,SAASA,UAAU,EAAEC,QAAQ,EAAyB,GAAG,CAAC,CAAC;IAC9D,IAAI;QACA,MAAMC,SAASC,IAAAA,cAAU,EACrB,OACA;YAAC;eAAcF,WAAW;gBAAC,CAAC,WAAW,EAAEA,UAAU;aAAC,GAAG,EAAE;SAAE,EAC3D;YAAEG,OAAO;gBAAC;gBAAQ;gBAAQ;aAAS;YAAEC,SAAS;QAAM,GACtDC,IAAI;QAENC,IAAAA,YAAK,EAAC,kBAAkB;YAAEL;QAAO;QAEjC,OAAOA,OAAOI,IAAI;IACtB,EAAE,UAAM;IACJ,SAAS;IACb;AACJ"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ExecSyncOptionsWithBufferEncoding } from 'child_process';
|
|
2
|
+
type RunCommandOptions = ExecSyncOptionsWithBufferEncoding;
|
|
3
|
+
export declare function runCommand(command: string, args: string[], options?: RunCommandOptions): string;
|
|
4
|
+
export {};
|
|
5
|
+
//# sourceMappingURL=os.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"os.d.ts","sourceRoot":"","sources":["../../src/utils/os.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,iCAAiC,EAAE,MAAM,eAAe,CAAC;AAGhF,KAAK,iBAAiB,GAAG,iCAAiC,CAAC;AAE3D,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,iBAAsB,UAG1F"}
|
package/dist/utils/os.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "runCommand", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return runCommand;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _child_process = require("child_process");
|
|
12
|
+
const _debug = require("./debug");
|
|
13
|
+
function runCommand(command, args, options = {}) {
|
|
14
|
+
var _execFileSync;
|
|
15
|
+
(0, _debug.debug)('install:run', {
|
|
16
|
+
command,
|
|
17
|
+
args,
|
|
18
|
+
options
|
|
19
|
+
});
|
|
20
|
+
var _execFileSync_toString;
|
|
21
|
+
return (_execFileSync_toString = (_execFileSync = (0, _child_process.execFileSync)(command, args, getOptions(command, options))) === null || _execFileSync === void 0 ? void 0 : _execFileSync.toString()) !== null && _execFileSync_toString !== void 0 ? _execFileSync_toString : '';
|
|
22
|
+
}
|
|
23
|
+
function getOptions(file, options) {
|
|
24
|
+
return {
|
|
25
|
+
// On Windows, npm is a command script (npm.CMD) that requires a shell
|
|
26
|
+
...file === 'npm' && process.platform === 'win32' ? {
|
|
27
|
+
shell: true
|
|
28
|
+
} : {},
|
|
29
|
+
...options,
|
|
30
|
+
...options.env ? {
|
|
31
|
+
env: {
|
|
32
|
+
...process.env,
|
|
33
|
+
...options.env
|
|
34
|
+
}
|
|
35
|
+
} : {}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=os.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/os.ts"],"sourcesContent":["import { execFileSync, ExecSyncOptionsWithBufferEncoding } from 'child_process';\nimport { debug } from './debug';\n\ntype RunCommandOptions = ExecSyncOptionsWithBufferEncoding;\n\nexport function runCommand(command: string, args: string[], options: RunCommandOptions = {}) {\n debug('install:run', { command, args, options });\n return execFileSync(command, args, getOptions(command, options))?.toString() ?? '';\n}\n\nfunction getOptions(file: string, options: RunCommandOptions) {\n return {\n // On Windows, npm is a command script (npm.CMD) that requires a shell\n ...(file === 'npm' && process.platform === 'win32' ? { shell: true } : {}),\n ...options,\n ...(options.env ? { env: { ...process.env, ...options.env } } : {}),\n };\n}\n"],"names":["runCommand","command","args","options","execFileSync","debug","getOptions","toString","file","process","platform","shell","env"],"mappings":";;;;+BAKgBA;;;eAAAA;;;+BALgD;uBAC1C;AAIf,SAASA,WAAWC,OAAe,EAAEC,IAAc,EAAEC,UAA6B,CAAC,CAAC;QAEhFC;IADPC,IAAAA,YAAK,EAAC,eAAe;QAAEJ;QAASC;QAAMC;IAAQ;QACvCC;IAAP,OAAOA,CAAAA,0BAAAA,gBAAAA,IAAAA,2BAAY,EAACH,SAASC,MAAMI,WAAWL,SAASE,uBAAhDC,oCAAAA,cAA2DG,QAAQ,gBAAnEH,oCAAAA,yBAAyE;AACpF;AAEA,SAASE,WAAWE,IAAY,EAAEL,OAA0B;IACxD,OAAO;QACH,sEAAsE;QACtE,GAAIK,SAAS,SAASC,QAAQC,QAAQ,KAAK,UAAU;YAAEC,OAAO;QAAK,IAAI,CAAC,CAAC;QACzE,GAAGR,OAAO;QACV,GAAIA,QAAQS,GAAG,GAAG;YAAEA,KAAK;gBAAE,GAAGH,QAAQG,GAAG;gBAAE,GAAGT,QAAQS,GAAG;YAAC;QAAE,IAAI,CAAC,CAAC;IACtE;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-json.d.ts","sourceRoot":"","sources":["../../src/utils/read-json.ts"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,OAEpC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "readJson", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return readJson;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _fs = /*#__PURE__*/ _interop_require_default(require("fs"));
|
|
12
|
+
function _interop_require_default(obj) {
|
|
13
|
+
return obj && obj.__esModule ? obj : {
|
|
14
|
+
default: obj
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function readJson(file) {
|
|
18
|
+
return JSON.parse(_fs.default.readFileSync(file, {
|
|
19
|
+
encoding: 'utf-8'
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=read-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/read-json.ts"],"sourcesContent":["import fs from 'fs';\n\nexport function readJson(file: string) {\n return JSON.parse(fs.readFileSync(file, { encoding: 'utf-8' }));\n}\n"],"names":["readJson","file","JSON","parse","fs","readFileSync","encoding"],"mappings":";;;;+BAEgBA;;;eAAAA;;;2DAFD;;;;;;AAER,SAASA,SAASC,IAAY;IACjC,OAAOC,KAAKC,KAAK,CAACC,WAAE,CAACC,YAAY,CAACJ,MAAM;QAAEK,UAAU;IAAQ;AAChE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@servicetitan/install",
|
|
3
|
+
"version": "32.5.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"homepage": "https://docs.st.dev/docs/frontend/install",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/servicetitan/uikit.git",
|
|
9
|
+
"directory": "packages/install"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=20.19 <21 || >=22.17",
|
|
13
|
+
"npm": ">=9"
|
|
14
|
+
},
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"type": "commonjs",
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"typings": "./dist/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"bin",
|
|
21
|
+
"dist",
|
|
22
|
+
"src",
|
|
23
|
+
"!**/__tests__"
|
|
24
|
+
],
|
|
25
|
+
"bin": "./bin/index.js",
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"cli": {
|
|
30
|
+
"webpack": false
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"ts-node": "~10.9.2"
|
|
34
|
+
},
|
|
35
|
+
"gitHead": "9ba97491f520e7b5914179aa061d1c3c3285079d"
|
|
36
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { parseArgs } from 'util';
|
|
2
|
+
import { Install } from './install';
|
|
3
|
+
import { debug } from '../utils';
|
|
4
|
+
|
|
5
|
+
const args = (function getArgs() {
|
|
6
|
+
const { values, tokens } = parseArgs({
|
|
7
|
+
args: process.argv.slice(2),
|
|
8
|
+
options: {
|
|
9
|
+
'fix': { type: 'boolean' },
|
|
10
|
+
'quiet': { type: 'boolean' },
|
|
11
|
+
'token': { type: 'boolean' },
|
|
12
|
+
'no-token': { type: 'boolean' },
|
|
13
|
+
},
|
|
14
|
+
tokens: true,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
tokens.forEach(token => {
|
|
18
|
+
if (token.kind === 'option') {
|
|
19
|
+
if (token.name.startsWith('no-')) {
|
|
20
|
+
const name = token.name.slice(3) as keyof typeof values;
|
|
21
|
+
values[name] = false;
|
|
22
|
+
delete values[token.name];
|
|
23
|
+
} else {
|
|
24
|
+
values[token.name] = token.value ?? true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return { ...values };
|
|
30
|
+
})();
|
|
31
|
+
|
|
32
|
+
debug('install:args', { args });
|
|
33
|
+
|
|
34
|
+
new Install(args).execute();
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { debug, getVersion, gitCloneRepo, isCI, npmWhoAmI, readJson, runCommand } from '../utils';
|
|
6
|
+
|
|
7
|
+
const REPO_NAME = 'frontend-dev-config';
|
|
8
|
+
const AUTH_TOKEN_KEY = '//registry.npmjs.org/:_authToken';
|
|
9
|
+
const AUTH_TOKEN_REGEX = /^\/\/registry\.npmjs\.org\/:_authToken=\s*\${([^}]+)}/m;
|
|
10
|
+
|
|
11
|
+
export interface Args {
|
|
12
|
+
debug?: boolean;
|
|
13
|
+
fix?: boolean;
|
|
14
|
+
quiet?: boolean;
|
|
15
|
+
token?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class Install {
|
|
19
|
+
args: Args;
|
|
20
|
+
|
|
21
|
+
constructor(args: Args) {
|
|
22
|
+
this.args = args;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
execute() {
|
|
26
|
+
if (!this.args.quiet) {
|
|
27
|
+
console.log(`install cli v${getVersion()}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const env = this.configureNpmToken();
|
|
31
|
+
this.installPackages(env);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private configureNpmToken() {
|
|
35
|
+
if (isCI() || this.args.fix || this.args.token === false) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!this.args.quiet) {
|
|
40
|
+
console.log('Configuring NPM token ...');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (this.args.token !== true) {
|
|
44
|
+
if (npmWhoAmI() === 'st-team') {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const token = this.fetchNpmToken();
|
|
50
|
+
if (!token) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
runCommand('npm', ['config', 'set', `${AUTH_TOKEN_KEY}=${token}`]);
|
|
55
|
+
|
|
56
|
+
if (!fs.existsSync('.npmrc')) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const npmrc = fs.readFileSync('.npmrc', 'utf-8');
|
|
61
|
+
const match = AUTH_TOKEN_REGEX.exec(npmrc);
|
|
62
|
+
debug({ match });
|
|
63
|
+
if (match) {
|
|
64
|
+
return { [match[1]]: token };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
runCommand('npm', ['config', 'delete', '--location=project', AUTH_TOKEN_KEY]);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private fetchNpmToken() {
|
|
71
|
+
const tempDirPath = fs.mkdtempSync(path.join(os.tmpdir(), 'st-install-'));
|
|
72
|
+
try {
|
|
73
|
+
if (!gitCloneRepo({ destination: tempDirPath, name: REPO_NAME })) {
|
|
74
|
+
throw new Error(`could not clone servicetitan/${REPO_NAME}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const npmJson = readJson(path.join(tempDirPath, '.npm.json'));
|
|
78
|
+
debug('install:fetch-npm-token', { npmJson });
|
|
79
|
+
|
|
80
|
+
if (!((npmJson && typeof npmJson === 'object') || Array.isArray(npmJson))) {
|
|
81
|
+
throw new Error('.npm.json is not an object');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const { readOnlyToken: authToken } = npmJson;
|
|
85
|
+
if (!authToken) {
|
|
86
|
+
throw new Error('.npm.json does not contain auth token');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (typeof authToken !== 'string') {
|
|
90
|
+
throw new Error('.npm.json auth token is not a string');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return Buffer.from(authToken, 'base64').toString('utf-8');
|
|
94
|
+
} catch (e) {
|
|
95
|
+
console.warn(String(e));
|
|
96
|
+
} finally {
|
|
97
|
+
try {
|
|
98
|
+
fs.rmSync(tempDirPath, { recursive: true, force: true });
|
|
99
|
+
} catch {
|
|
100
|
+
// ignore
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private installPackages(env?: Record<string, string>) {
|
|
106
|
+
if (this.args.token) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const npmArguments = [
|
|
111
|
+
isCI() ? 'ci' : 'i',
|
|
112
|
+
'--audit=false',
|
|
113
|
+
'--fund=false',
|
|
114
|
+
'--legacy-peer-deps',
|
|
115
|
+
...(this.args.fix ? ['--package-lock-only', '--prefer-dedupe'] : []),
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
if (!this.args.quiet) {
|
|
119
|
+
console.log(`Running npm ${npmArguments.join(' ')}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
runCommand('npm', npmArguments, { env, stdio: 'inherit' });
|
|
123
|
+
}
|
|
124
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { gitCloneRepo, gitIsReachable, isCI, npmWhoAmI } from './utils';
|
package/src/utils/ci.ts
ADDED
package/src/utils/git.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { isCI } from './ci';
|
|
2
|
+
import { runCommand } from './os';
|
|
3
|
+
|
|
4
|
+
interface Repo {
|
|
5
|
+
owner?: string;
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function gitCloneRepo(params: Repo & { destination: string }) {
|
|
10
|
+
const { destination, name, owner = 'servicetitan' } = params;
|
|
11
|
+
const gitUrls = getGitUrls({ owner, name });
|
|
12
|
+
|
|
13
|
+
for (let i = 0, n = gitUrls.length; i < n; i++) {
|
|
14
|
+
try {
|
|
15
|
+
runCommand('git', ['clone', '-q', gitUrls[i], destination], {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
17
|
+
env: { GIT_TERMINAL_PROMPT: '0' },
|
|
18
|
+
stdio: i === n - 1 ? 'inherit' : 'ignore', // show errors on last attempt
|
|
19
|
+
});
|
|
20
|
+
return true;
|
|
21
|
+
} catch {
|
|
22
|
+
// ignore error
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function gitIsReachable({ owner = 'servicetitan', name }: Repo) {
|
|
30
|
+
return getGitUrls({ owner, name }).some(url => {
|
|
31
|
+
try {
|
|
32
|
+
runCommand('git', ['ls-remote', '-qt', url]);
|
|
33
|
+
return true;
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getGitUrls({ owner, name }: Repo) {
|
|
41
|
+
const webUrl = `https://github.com/${owner}/${name}.git`;
|
|
42
|
+
const sshUrl = `git@github.com:${owner}/${name}.git`;
|
|
43
|
+
|
|
44
|
+
return isCI() && !!process.env.GITHUB_TOKEN
|
|
45
|
+
? [webUrl.replace('github.com', `oauth2:${process.env.GITHUB_TOKEN}@github.com`)]
|
|
46
|
+
: [sshUrl, webUrl];
|
|
47
|
+
}
|
package/src/utils/npm.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { debug } from './debug';
|
|
2
|
+
import { runCommand } from './os';
|
|
3
|
+
|
|
4
|
+
export function npmWhoAmI({ registry }: { registry?: string } = {}) {
|
|
5
|
+
try {
|
|
6
|
+
const result = runCommand(
|
|
7
|
+
'npm',
|
|
8
|
+
['whoami', ...(registry ? [`--registry=${registry}`] : [])],
|
|
9
|
+
{ stdio: ['pipe', 'pipe', 'ignore'], timeout: 10000 }
|
|
10
|
+
).trim();
|
|
11
|
+
|
|
12
|
+
debug('install:whoami', { result });
|
|
13
|
+
|
|
14
|
+
return result.trim();
|
|
15
|
+
} catch {
|
|
16
|
+
// ignore
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/utils/os.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { execFileSync, ExecSyncOptionsWithBufferEncoding } from 'child_process';
|
|
2
|
+
import { debug } from './debug';
|
|
3
|
+
|
|
4
|
+
type RunCommandOptions = ExecSyncOptionsWithBufferEncoding;
|
|
5
|
+
|
|
6
|
+
export function runCommand(command: string, args: string[], options: RunCommandOptions = {}) {
|
|
7
|
+
debug('install:run', { command, args, options });
|
|
8
|
+
return execFileSync(command, args, getOptions(command, options))?.toString() ?? '';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getOptions(file: string, options: RunCommandOptions) {
|
|
12
|
+
return {
|
|
13
|
+
// On Windows, npm is a command script (npm.CMD) that requires a shell
|
|
14
|
+
...(file === 'npm' && process.platform === 'win32' ? { shell: true } : {}),
|
|
15
|
+
...options,
|
|
16
|
+
...(options.env ? { env: { ...process.env, ...options.env } } : {}),
|
|
17
|
+
};
|
|
18
|
+
}
|