mobbdev 0.0.28 → 0.0.30
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/.env +4 -4
- package/README.md +9 -9
- package/bin/cli.mjs +1 -1
- package/dist/index.js +1271 -0
- package/package.json +76 -55
- package/index.mjs +0 -34
- package/src/commands/index.mjs +0 -101
- package/src/constants.mjs +0 -60
- package/src/features/analysis/callback-server.mjs +0 -61
- package/src/features/analysis/git.mjs +0 -50
- package/src/features/analysis/github.mjs +0 -106
- package/src/features/analysis/gql.mjs +0 -198
- package/src/features/analysis/index.mjs +0 -292
- package/src/features/analysis/pack.mjs +0 -31
- package/src/features/analysis/prompts.mjs +0 -55
- package/src/features/analysis/snyk.mjs +0 -110
- package/src/features/analysis/upload-file.mjs +0 -37
- package/src/utils.mjs +0 -30
- package/src/yargs.mjs +0 -141
package/package.json
CHANGED
|
@@ -1,57 +1,78 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
2
|
+
"name": "mobbdev",
|
|
3
|
+
"version": "0.0.30",
|
|
4
|
+
"description": "Automated secure code remediation tool",
|
|
5
|
+
"repository": "https://github.com/mobb-dev/bugsy",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc && tsup-node --env.NODE_ENV production",
|
|
10
|
+
"build:dev": "tsup-node --env.NODE_ENV development",
|
|
11
|
+
"test": "TOKEN=$(../../scripts/login_auth0.sh) vitest run",
|
|
12
|
+
"test:watch": "TOKEN=$(../../scripts/login_auth0.sh) vitest",
|
|
13
|
+
"lint": "eslint --cache --max-warnings 0 --ignore-path .eslintignore --ext .ts,.tsx,.jsx .",
|
|
14
|
+
"lint:fix": "eslint --fix --cache --max-warnings 0 --ignore-path .eslintignore --ext .js,.ts,.tsx,.jsx .",
|
|
15
|
+
"lint:fix:files": "eslint --fix --cache --max-warnings 0 --ignore-path .eslintignore --ext .js,.ts,.tsx,.jsx",
|
|
16
|
+
"prepack": "pnpm build && dotenv-vault pull production .env"
|
|
17
|
+
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"mobbdev": "bin/cli.mjs"
|
|
20
|
+
},
|
|
21
|
+
"author": "",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"adm-zip": "0.5.10",
|
|
25
|
+
"chalk-animation": "2.0.3",
|
|
26
|
+
"chalk": "5.3.0",
|
|
27
|
+
"configstore": "6.0.0",
|
|
28
|
+
"debug": "4.3.4",
|
|
29
|
+
"dotenv": "16.0.3",
|
|
30
|
+
"extract-zip": "2.0.1",
|
|
31
|
+
"globby": "13.2.2",
|
|
32
|
+
"graphql-request": "5.0.0",
|
|
33
|
+
"graphql": "16.6.0",
|
|
34
|
+
"inquirer": "9.2.7",
|
|
35
|
+
"nanospinner": "1.1.0",
|
|
36
|
+
"node-fetch": "3.3.1",
|
|
37
|
+
"octokit": "2.0.14",
|
|
38
|
+
"open": "8.4.2",
|
|
39
|
+
"semver": "7.5.0",
|
|
40
|
+
"simple-git": "3.19.1",
|
|
41
|
+
"snyk": "1.1118.0",
|
|
42
|
+
"supports-color": "9.4.0",
|
|
43
|
+
"tmp": "0.2.1",
|
|
44
|
+
"yargs": "17.7.2",
|
|
45
|
+
"zod": "3.21.4"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@octokit/plugin-rest-endpoint-methods": "7.0.1",
|
|
49
|
+
"@octokit/request-error": "3.0.3",
|
|
50
|
+
"@types/adm-zip": "0.5.0",
|
|
51
|
+
"@types/chalk-animation": "1.6.1",
|
|
52
|
+
"@types/configstore": "6.0.0",
|
|
53
|
+
"@types/debug": "4.1.8",
|
|
54
|
+
"@types/inquirer": "9.0.3",
|
|
55
|
+
"@types/semver": "7.5.0",
|
|
56
|
+
"@types/tmp": "0.2.3",
|
|
57
|
+
"@types/yargs": "17.0.24",
|
|
58
|
+
"@typescript-eslint/eslint-plugin": "5.44.0",
|
|
59
|
+
"@typescript-eslint/parser": "5.44.0",
|
|
60
|
+
"eslint-plugin-import": "2.27.5",
|
|
61
|
+
"eslint-plugin-prettier": "4.2.1",
|
|
62
|
+
"eslint-plugin-simple-import-sort": "10.0.0",
|
|
63
|
+
"eslint": "8.36.0",
|
|
64
|
+
"prettier": "2.8.4",
|
|
65
|
+
"tsup": "7.2.0",
|
|
66
|
+
"typescript": "4.9.3",
|
|
67
|
+
"vitest": "0.26.2"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=12.20.0"
|
|
71
|
+
},
|
|
72
|
+
"type": "module",
|
|
73
|
+
"files": [
|
|
74
|
+
"bin",
|
|
75
|
+
"dist",
|
|
76
|
+
".env"
|
|
77
|
+
]
|
|
57
78
|
}
|
package/index.mjs
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { analyze, scan, CliError } from './src/commands/index.mjs';
|
|
2
|
-
import { parseArgs } from './src/yargs.mjs';
|
|
3
|
-
import { hideBin } from 'yargs/helpers';
|
|
4
|
-
|
|
5
|
-
async function run() {
|
|
6
|
-
const args = await parseArgs(hideBin(process.argv));
|
|
7
|
-
const [command] = args._;
|
|
8
|
-
if (command === 'scan') {
|
|
9
|
-
const { yes, ...restArgs } = args;
|
|
10
|
-
await scan(restArgs, { skipPrompts: yes });
|
|
11
|
-
}
|
|
12
|
-
if (command === 'analyze') {
|
|
13
|
-
const { yes, ...restArgs } = args;
|
|
14
|
-
await analyze(restArgs, { skipPrompts: yes });
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
(async () => {
|
|
19
|
-
try {
|
|
20
|
-
await run();
|
|
21
|
-
process.exit(0);
|
|
22
|
-
} catch (err) {
|
|
23
|
-
if (err instanceof CliError) {
|
|
24
|
-
console.error(err.message);
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
// unexpected error - print stack trace
|
|
28
|
-
console.error(
|
|
29
|
-
'Something went wrong, please try again or contact support if issue persists.'
|
|
30
|
-
);
|
|
31
|
-
console.error(err);
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
})();
|
package/src/commands/index.mjs
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import chalkAnimation from 'chalk-animation';
|
|
5
|
-
import { choseScanner } from '../features/analysis/prompts.mjs';
|
|
6
|
-
import { SCANNERS, mobbAscii } from '../constants.mjs';
|
|
7
|
-
import { runAnalysis } from '../features/analysis/index.mjs';
|
|
8
|
-
import { sleep } from '../utils.mjs';
|
|
9
|
-
import path from 'path';
|
|
10
|
-
|
|
11
|
-
const GITHUB_REPO_URL_PATTERN = new RegExp(
|
|
12
|
-
'https://github.com/[\\w-]+/[\\w-]+'
|
|
13
|
-
);
|
|
14
|
-
export class CliError extends Error {}
|
|
15
|
-
|
|
16
|
-
const UrlZ = z
|
|
17
|
-
.string({
|
|
18
|
-
invalid_type_error: 'is not a valid github URL',
|
|
19
|
-
})
|
|
20
|
-
.regex(GITHUB_REPO_URL_PATTERN, {
|
|
21
|
-
message: 'is not a valid github URL',
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
function throwRepoUrlErrorMessage({ error, repoUrl, command }) {
|
|
25
|
-
const errorMessage = error.issues[error.issues.length - 1].message;
|
|
26
|
-
const formattedErrorMessage = `\nError: ${chalk.bold(
|
|
27
|
-
repoUrl
|
|
28
|
-
)} is ${errorMessage}
|
|
29
|
-
Example: \n\tmobbdev ${command} -r ${chalk.bold(
|
|
30
|
-
'https://github.com/WebGoat/WebGoat'
|
|
31
|
-
)}`;
|
|
32
|
-
throw new CliError(formattedErrorMessage);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export async function analyze(
|
|
36
|
-
{ repo, scanFile, ref, apiKey, ci, commitHash, srcPath },
|
|
37
|
-
{ skipPrompts = false } = {}
|
|
38
|
-
) {
|
|
39
|
-
const { success, error } = UrlZ.safeParse(repo);
|
|
40
|
-
|
|
41
|
-
if (!success && !srcPath) {
|
|
42
|
-
throwRepoUrlErrorMessage({
|
|
43
|
-
error,
|
|
44
|
-
repoUrl: repo,
|
|
45
|
-
command: 'analyze',
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (!fs.existsSync(scanFile)) {
|
|
50
|
-
throw new CliError(`\nCan't access ${chalk.bold(scanFile)}`);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (path.extname(scanFile) !== '.json') {
|
|
54
|
-
throw new CliError(`\n${chalk.bold(scanFile)} is not a json file`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
!ci && (await showWelcomeMessage(skipPrompts));
|
|
58
|
-
|
|
59
|
-
await runAnalysis(
|
|
60
|
-
{
|
|
61
|
-
repo,
|
|
62
|
-
scanFile,
|
|
63
|
-
ref,
|
|
64
|
-
apiKey,
|
|
65
|
-
ci,
|
|
66
|
-
commitHash,
|
|
67
|
-
srcPath,
|
|
68
|
-
},
|
|
69
|
-
{ skipPrompts }
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export async function scan(
|
|
74
|
-
{ repo, scanner, branch, apiKey, ci },
|
|
75
|
-
{ skipPrompts = false } = {}
|
|
76
|
-
) {
|
|
77
|
-
const { success, error } = UrlZ.safeParse(repo);
|
|
78
|
-
if (ci && !apiKey) {
|
|
79
|
-
throw new CliError(
|
|
80
|
-
'\nError: --ci flag requires --api-key to be provided as well'
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (!success) {
|
|
85
|
-
throwRepoUrlErrorMessage({ error, repoUrl: repo, command: 'scan' });
|
|
86
|
-
}
|
|
87
|
-
!ci && (await showWelcomeMessage(skipPrompts));
|
|
88
|
-
scanner ??= scanner || (await choseScanner());
|
|
89
|
-
if (scanner !== SCANNERS.Snyk) {
|
|
90
|
-
throw new CliError(
|
|
91
|
-
'Vulnerability scanning via Bugsy is available only with Snyk at the moment. Additional scanners will follow soon.'
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
await runAnalysis({ repo, scanner, branch, apiKey }, { skipPrompts });
|
|
95
|
-
}
|
|
96
|
-
async function showWelcomeMessage(skipPrompts = false) {
|
|
97
|
-
console.log(mobbAscii);
|
|
98
|
-
const welcome = chalkAnimation.rainbow('\n\t\t\tWelcome to Bugsy\n');
|
|
99
|
-
skipPrompts ? await sleep(100) : await sleep(2000);
|
|
100
|
-
welcome.stop();
|
|
101
|
-
}
|
package/src/constants.mjs
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
-
import * as dotenv from 'dotenv';
|
|
4
|
-
import { z } from 'zod';
|
|
5
|
-
import Debug from 'debug';
|
|
6
|
-
|
|
7
|
-
const debug = Debug('mobbdev:constants');
|
|
8
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
dotenv.config({ path: path.join(__dirname, '../.env') });
|
|
10
|
-
|
|
11
|
-
export const SCANNERS = {
|
|
12
|
-
Checkmarx: 'checkmarx',
|
|
13
|
-
Codeql: 'codeql',
|
|
14
|
-
Fortify: 'fortify',
|
|
15
|
-
Snyk: 'snyk',
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const envVariablesSchema = z
|
|
19
|
-
.object({
|
|
20
|
-
WEB_LOGIN_URL: z.string(),
|
|
21
|
-
WEB_APP_URL: z.string(),
|
|
22
|
-
API_URL: z.string(),
|
|
23
|
-
})
|
|
24
|
-
.required();
|
|
25
|
-
|
|
26
|
-
const envVariables = envVariablesSchema.parse(process.env);
|
|
27
|
-
debug('config %o', envVariables);
|
|
28
|
-
|
|
29
|
-
export const mobbAscii = `
|
|
30
|
-
..
|
|
31
|
-
..........
|
|
32
|
-
.................
|
|
33
|
-
...........................
|
|
34
|
-
..............................
|
|
35
|
-
................................
|
|
36
|
-
..................................
|
|
37
|
-
....................................
|
|
38
|
-
.....................................
|
|
39
|
-
.............................................
|
|
40
|
-
.................................................
|
|
41
|
-
............................... .................
|
|
42
|
-
.................................. ............
|
|
43
|
-
.................. ............. ..........
|
|
44
|
-
......... ........ ......... ......
|
|
45
|
-
............... ....
|
|
46
|
-
.... ..
|
|
47
|
-
|
|
48
|
-
. ...
|
|
49
|
-
..............
|
|
50
|
-
......................
|
|
51
|
-
...........................
|
|
52
|
-
................................
|
|
53
|
-
......................................
|
|
54
|
-
...............................
|
|
55
|
-
.................
|
|
56
|
-
`;
|
|
57
|
-
|
|
58
|
-
export const WEB_LOGIN_URL = envVariables.WEB_LOGIN_URL;
|
|
59
|
-
export const WEB_APP_URL = envVariables.WEB_APP_URL;
|
|
60
|
-
export const API_URL = envVariables.API_URL;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import Debug from 'debug';
|
|
2
|
-
import { setTimeout, clearTimeout } from 'node:timers';
|
|
3
|
-
import http from 'node:http';
|
|
4
|
-
import querystring from 'node:querystring';
|
|
5
|
-
import open from 'open';
|
|
6
|
-
|
|
7
|
-
const debug = Debug('mobbdev:web-login');
|
|
8
|
-
|
|
9
|
-
export async function callbackServer(url, redirectUrl) {
|
|
10
|
-
debug('web login start');
|
|
11
|
-
|
|
12
|
-
let responseResolver;
|
|
13
|
-
let responseRejecter;
|
|
14
|
-
const responseAwaiter = new Promise((resolve, reject) => {
|
|
15
|
-
responseResolver = resolve;
|
|
16
|
-
responseRejecter = reject;
|
|
17
|
-
});
|
|
18
|
-
const timerHandler = setTimeout(() => {
|
|
19
|
-
debug('timeout happened');
|
|
20
|
-
responseRejecter(new Error('No login happened in three minutes.'));
|
|
21
|
-
}, 180000);
|
|
22
|
-
|
|
23
|
-
const server = http.createServer((req, res) => {
|
|
24
|
-
debug('incoming request');
|
|
25
|
-
let body = '';
|
|
26
|
-
|
|
27
|
-
req.on('data', (chunk) => {
|
|
28
|
-
debug('http server get chunk %s', chunk);
|
|
29
|
-
body += chunk;
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
req.on('end', () => {
|
|
33
|
-
debug('http server end %s', body);
|
|
34
|
-
res.writeHead(301, {
|
|
35
|
-
Location: redirectUrl,
|
|
36
|
-
}).end();
|
|
37
|
-
|
|
38
|
-
responseResolver(querystring.parse(body));
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
debug('http server starting');
|
|
43
|
-
const port = await new Promise((resolve) => {
|
|
44
|
-
server.listen(0, '127.0.0.1', () => {
|
|
45
|
-
resolve(server.address().port);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
debug('http server started on port %d', port);
|
|
49
|
-
|
|
50
|
-
debug('opening the browser on %s', `${url}?port=${port}`);
|
|
51
|
-
await open(`${url}?port=${port}`);
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
debug('waiting for http request');
|
|
55
|
-
return await responseAwaiter;
|
|
56
|
-
} finally {
|
|
57
|
-
debug('http server close');
|
|
58
|
-
clearTimeout(timerHandler);
|
|
59
|
-
server.close();
|
|
60
|
-
}
|
|
61
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { simpleGit } from 'simple-git';
|
|
2
|
-
import Debug from 'debug';
|
|
3
|
-
|
|
4
|
-
const debug = Debug('mobbdev:git');
|
|
5
|
-
|
|
6
|
-
export async function getGitInfo(srcDirPath) {
|
|
7
|
-
debug('getting git info for %s', srcDirPath);
|
|
8
|
-
|
|
9
|
-
const git = simpleGit({
|
|
10
|
-
baseDir: srcDirPath,
|
|
11
|
-
// binary: 'git123',
|
|
12
|
-
maxConcurrentProcesses: 1,
|
|
13
|
-
trimmed: true,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
let repoUrl = '';
|
|
17
|
-
let hash = '';
|
|
18
|
-
let reference = '';
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
repoUrl = (await git.getConfig('remote.origin.url')).value || '';
|
|
22
|
-
hash = (await git.revparse(['HEAD'])) || '';
|
|
23
|
-
reference = (await git.revparse(['--abbrev-ref', 'HEAD'])) || '';
|
|
24
|
-
} catch (e) {
|
|
25
|
-
debug('failed to run git %o', e);
|
|
26
|
-
if (e.message && e.message.includes(' spawn ')) {
|
|
27
|
-
debug('git cli not installed');
|
|
28
|
-
} else if (e.message && e.message.includes(' not a git repository ')) {
|
|
29
|
-
debug('folder is not a git repo');
|
|
30
|
-
} else {
|
|
31
|
-
throw e;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Normalize git URL. We may need more generic code here, but it's not very
|
|
36
|
-
// important at the moment.
|
|
37
|
-
if (repoUrl.endsWith('.git')) {
|
|
38
|
-
repoUrl = repoUrl.slice(0, -'.git'.length);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (repoUrl.startsWith('git@github.com:')) {
|
|
42
|
-
repoUrl = repoUrl.replace('git@github.com:', 'https://github.com/');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
repoUrl,
|
|
47
|
-
hash,
|
|
48
|
-
reference,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import stream from 'node:stream';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { promisify } from 'node:util';
|
|
6
|
-
import fetch from 'node-fetch';
|
|
7
|
-
import extract from 'extract-zip';
|
|
8
|
-
import Debug from 'debug';
|
|
9
|
-
import { Spinner } from '../../utils.mjs';
|
|
10
|
-
const pipeline = promisify(stream.pipeline);
|
|
11
|
-
const debug = Debug('mobbdev:github');
|
|
12
|
-
|
|
13
|
-
async function getRepo(slug, { token } = {}) {
|
|
14
|
-
try {
|
|
15
|
-
return fetch(`https://api.github.com/repos/${slug}`, {
|
|
16
|
-
method: 'GET',
|
|
17
|
-
headers: {
|
|
18
|
-
Accept: 'application/vnd.github+json',
|
|
19
|
-
'X-GitHub-Api-Version': '2022-11-28',
|
|
20
|
-
...(token && { Authorization: `bearer ${token}` }),
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
} catch (e) {
|
|
24
|
-
debug(`error fetching the repo ${slug}`, e);
|
|
25
|
-
throw e;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function extractSlug(repoUrl) {
|
|
30
|
-
debug('get default branch %s', repoUrl);
|
|
31
|
-
let slug = repoUrl.replace(/https?:\/\/github\.com\//i, '');
|
|
32
|
-
|
|
33
|
-
if (slug.endsWith('/')) {
|
|
34
|
-
slug = slug.substring(0, slug.length - 1);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (slug.endsWith('.git')) {
|
|
38
|
-
slug = slug.substring(0, slug.length - '.git'.length);
|
|
39
|
-
}
|
|
40
|
-
debug('slug %s', slug);
|
|
41
|
-
return slug;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export async function canReachRepo(repoUrl, { token } = {}) {
|
|
45
|
-
const slug = extractSlug(repoUrl);
|
|
46
|
-
const response = await getRepo(slug, { token });
|
|
47
|
-
return response.ok;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export async function getDefaultBranch(repoUrl, { token } = {}) {
|
|
51
|
-
const slug = extractSlug(repoUrl);
|
|
52
|
-
const response = await getRepo(slug, { token });
|
|
53
|
-
if (!response.ok) {
|
|
54
|
-
debug('GH request failed %s %s', response.body, response.status);
|
|
55
|
-
throw new Error(
|
|
56
|
-
`Can't get default branch, make sure you have access to : ${repoUrl}.`
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const repoInfo = await response.json();
|
|
61
|
-
debug('GH request ok %o', repoInfo);
|
|
62
|
-
|
|
63
|
-
return repoInfo.default_branch;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export async function downloadRepo(
|
|
67
|
-
{ repoUrl, reference, dirname, ci },
|
|
68
|
-
{ token } = {}
|
|
69
|
-
) {
|
|
70
|
-
const { createSpinner } = Spinner({ ci });
|
|
71
|
-
const repoSpinner = createSpinner('💾 Downloading Repo').start();
|
|
72
|
-
debug('download repo %s %s %s', repoUrl, reference, dirname);
|
|
73
|
-
const zipFilePath = path.join(dirname, 'repo.zip');
|
|
74
|
-
const response = await fetch(`${repoUrl}/zipball/${reference}`, {
|
|
75
|
-
method: 'GET',
|
|
76
|
-
headers: {
|
|
77
|
-
...(token && { Authorization: `bearer ${token}` }),
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
if (!response.ok) {
|
|
81
|
-
debug(
|
|
82
|
-
'GH zipball request failed %s %s',
|
|
83
|
-
response.body,
|
|
84
|
-
response.status
|
|
85
|
-
);
|
|
86
|
-
repoSpinner.error({ text: '💾 Repo download failed' });
|
|
87
|
-
throw new Error(
|
|
88
|
-
`Can't access the the branch ${chalk.bold(
|
|
89
|
-
reference
|
|
90
|
-
)} on ${chalk.bold(repoUrl)} make sure it exits.`
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const fileWriterStream = fs.createWriteStream(zipFilePath);
|
|
95
|
-
|
|
96
|
-
await pipeline(response.body, fileWriterStream);
|
|
97
|
-
await extract(zipFilePath, { dir: dirname });
|
|
98
|
-
|
|
99
|
-
const repoRoot = fs
|
|
100
|
-
.readdirSync(dirname, { withFileTypes: true })
|
|
101
|
-
.filter((dirent) => dirent.isDirectory())
|
|
102
|
-
.map((dirent) => dirent.name)[0];
|
|
103
|
-
debug('repo root %s', repoRoot);
|
|
104
|
-
repoSpinner.success({ text: '💾 Repo downloaded successfully' });
|
|
105
|
-
return path.join(dirname, repoRoot);
|
|
106
|
-
}
|