@socketsecurity/cli 0.9.3 → 0.10.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/cli.js +2 -0
- package/lib/commands/audit-log/index.js +162 -0
- package/lib/commands/cdxgen/index.js +211 -0
- package/lib/commands/dependencies/index.js +150 -0
- package/lib/commands/index.js +9 -4
- package/lib/commands/info/index.js +123 -81
- package/lib/commands/login/index.js +1 -1
- package/lib/commands/logout/index.js +1 -1
- package/lib/commands/npm/index.js +8 -3
- package/lib/commands/npx/index.js +1 -1
- package/lib/commands/report/create.js +1 -1
- package/lib/commands/report/index.js +1 -1
- package/lib/commands/report/view.js +1 -1
- package/lib/commands/repos/create.js +166 -0
- package/lib/commands/repos/delete.js +93 -0
- package/lib/commands/repos/index.js +30 -0
- package/lib/commands/repos/list.js +170 -0
- package/lib/commands/repos/update.js +166 -0
- package/lib/commands/repos/view.js +128 -0
- package/lib/commands/scan/create.js +245 -0
- package/lib/commands/scan/delete.js +112 -0
- package/lib/commands/scan/index.js +30 -0
- package/lib/commands/scan/list.js +192 -0
- package/lib/commands/scan/metadata.js +113 -0
- package/lib/commands/scan/stream.js +115 -0
- package/lib/commands/wrapper/index.js +1 -1
- package/lib/shadow/npm-injection.cjs +11 -1
- package/lib/utils/format-issues.js +28 -1
- package/lib/utils/meow-with-subcommands.js +1 -2
- package/lib/utils/misc.js +0 -1
- package/lib/utils/path-resolve.js +30 -5
- package/lib/utils/sdk.js +0 -3
- package/package.json +77 -61
|
@@ -7,13 +7,13 @@ import ora from 'ora'
|
|
|
7
7
|
import { outputFlags, validationFlags } from '../../flags/index.js'
|
|
8
8
|
import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
|
|
9
9
|
import { ChalkOrMarkdown } from '../../utils/chalk-markdown.js'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { prepareFlags } from '../../utils/flags.js'
|
|
11
|
+
import { formatSeverityCount, getCountSeverity } from '../../utils/format-issues.js'
|
|
12
12
|
import { printFlagList } from '../../utils/formatting.js'
|
|
13
13
|
import { objectSome } from '../../utils/misc.js'
|
|
14
14
|
import { FREE_API_KEY, getDefaultKey, setupSdk } from '../../utils/sdk.js'
|
|
15
15
|
|
|
16
|
-
/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
|
|
16
|
+
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
|
|
17
17
|
export const info = {
|
|
18
18
|
description: 'Look up info regarding a package',
|
|
19
19
|
async run (argv, importMeta, { parentName }) {
|
|
@@ -21,9 +21,9 @@ export const info = {
|
|
|
21
21
|
|
|
22
22
|
const input = setupCommand(name, info.description, argv, importMeta)
|
|
23
23
|
if (input) {
|
|
24
|
-
const spinnerText =
|
|
24
|
+
const spinnerText = `Looking up data for packages: ${input.packages.join(', ')}\n`
|
|
25
25
|
const spinner = ora(spinnerText).start()
|
|
26
|
-
const packageData = await fetchPackageData(input.
|
|
26
|
+
const packageData = await fetchPackageData(input.packages, input.includeAlerts, spinner)
|
|
27
27
|
if (packageData) {
|
|
28
28
|
formatPackageDataOutput(packageData, { name, ...input }, spinner)
|
|
29
29
|
}
|
|
@@ -31,15 +31,31 @@ export const info = {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
const infoFlags = prepareFlags({
|
|
35
|
+
// At the moment in API v0, alerts and license do the same thing.
|
|
36
|
+
// The license parameter will be implemented later.
|
|
37
|
+
// license: {
|
|
38
|
+
// type: 'boolean',
|
|
39
|
+
// shortFlag: 'l',
|
|
40
|
+
// default: false,
|
|
41
|
+
// description: 'Include license - Default is false',
|
|
42
|
+
// },
|
|
43
|
+
alerts: {
|
|
44
|
+
type: 'boolean',
|
|
45
|
+
shortFlag: 'a',
|
|
46
|
+
default: false,
|
|
47
|
+
description: 'Include alerts - Default is false',
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
34
51
|
// Internal functions
|
|
35
52
|
|
|
36
53
|
/**
|
|
37
54
|
* @typedef CommandContext
|
|
38
|
-
* @property {boolean}
|
|
55
|
+
* @property {boolean} includeAlerts
|
|
39
56
|
* @property {boolean} outputJson
|
|
40
57
|
* @property {boolean} outputMarkdown
|
|
41
|
-
* @property {string}
|
|
42
|
-
* @property {string} pkgVersion
|
|
58
|
+
* @property {string[]} packages
|
|
43
59
|
* @property {boolean} strict
|
|
44
60
|
*/
|
|
45
61
|
|
|
@@ -54,18 +70,19 @@ function setupCommand (name, description, argv, importMeta) {
|
|
|
54
70
|
const flags = {
|
|
55
71
|
...outputFlags,
|
|
56
72
|
...validationFlags,
|
|
73
|
+
...infoFlags
|
|
57
74
|
}
|
|
58
75
|
|
|
59
76
|
const cli = meow(`
|
|
60
77
|
Usage
|
|
61
|
-
$ ${name} <name>
|
|
78
|
+
$ ${name} <ecosystem>:<name>@<version>
|
|
62
79
|
|
|
63
80
|
Options
|
|
64
81
|
${printFlagList(flags, 6)}
|
|
65
82
|
|
|
66
83
|
Examples
|
|
67
|
-
$ ${name} webtorrent
|
|
68
|
-
$ ${name} webtorrent@1.9.1
|
|
84
|
+
$ ${name} npm:webtorrent
|
|
85
|
+
$ ${name} npm:webtorrent@1.9.1
|
|
69
86
|
`, {
|
|
70
87
|
argv,
|
|
71
88
|
description,
|
|
@@ -74,138 +91,162 @@ function setupCommand (name, description, argv, importMeta) {
|
|
|
74
91
|
})
|
|
75
92
|
|
|
76
93
|
const {
|
|
77
|
-
|
|
94
|
+
alerts: includeAlerts,
|
|
78
95
|
json: outputJson,
|
|
79
96
|
markdown: outputMarkdown,
|
|
80
97
|
strict,
|
|
81
98
|
} = cli.flags
|
|
82
99
|
|
|
83
|
-
if (cli.input.length > 1) {
|
|
84
|
-
throw new InputError('Only one package lookup supported at once')
|
|
85
|
-
}
|
|
86
|
-
|
|
87
100
|
const [rawPkgName = ''] = cli.input
|
|
88
101
|
|
|
89
102
|
if (!rawPkgName) {
|
|
103
|
+
console.error(`${chalk.bgRed('Input error')}: Please provide an ecosystem and package name`)
|
|
90
104
|
cli.showHelp()
|
|
91
105
|
return
|
|
92
106
|
}
|
|
93
107
|
|
|
94
|
-
const
|
|
108
|
+
const /** @type {string[]} */inputPkgs = []
|
|
95
109
|
|
|
96
|
-
|
|
97
|
-
|
|
110
|
+
cli.input.map(pkg => {
|
|
111
|
+
const ecosystem = pkg.split(':')[0]
|
|
112
|
+
if (!ecosystem) {
|
|
113
|
+
console.error(`Package name ${pkg} formatted incorrectly.`)
|
|
114
|
+
return cli.showHelp()
|
|
115
|
+
} else {
|
|
116
|
+
const versionSeparator = pkg.lastIndexOf('@')
|
|
117
|
+
const ecosystemSeparator = pkg.lastIndexOf(ecosystem)
|
|
118
|
+
const pkgName = versionSeparator < 1 ? pkg.slice(ecosystemSeparator + ecosystem.length + 1) : pkg.slice(ecosystemSeparator + ecosystem.length + 1, versionSeparator)
|
|
119
|
+
const pkgVersion = versionSeparator < 1 ? 'latest' : pkg.slice(versionSeparator + 1)
|
|
120
|
+
inputPkgs.push(`${ecosystem}/${pkgName}@${pkgVersion}`)
|
|
121
|
+
}
|
|
122
|
+
return inputPkgs
|
|
123
|
+
})
|
|
98
124
|
|
|
99
125
|
return {
|
|
100
|
-
|
|
126
|
+
includeAlerts,
|
|
101
127
|
outputJson,
|
|
102
128
|
outputMarkdown,
|
|
103
|
-
|
|
104
|
-
pkgVersion,
|
|
129
|
+
packages: inputPkgs,
|
|
105
130
|
strict,
|
|
106
131
|
}
|
|
107
132
|
}
|
|
108
133
|
|
|
109
134
|
/**
|
|
110
135
|
* @typedef PackageData
|
|
111
|
-
* @property {import('@socketsecurity/sdk').SocketSdkReturnType<'
|
|
112
|
-
* @property {Record<import('../../utils/format-issues').SocketIssue['severity'], number>} severityCount
|
|
113
|
-
* @property {import('@socketsecurity/sdk').SocketSdkReturnType<'getScoreByNPMPackage'>["data"]} score
|
|
136
|
+
* @property {import('@socketsecurity/sdk').SocketSdkReturnType<'batchPackageFetch'>["data"]} data
|
|
114
137
|
*/
|
|
115
138
|
|
|
116
139
|
/**
|
|
117
|
-
* @param {string}
|
|
118
|
-
* @param {
|
|
119
|
-
* @param {Pick<CommandContext, 'includeAllIssues'>} context
|
|
140
|
+
* @param {string[]} packages
|
|
141
|
+
* @param {boolean} includeAlerts
|
|
120
142
|
* @param {import('ora').Ora} spinner
|
|
121
143
|
* @returns {Promise<void|PackageData>}
|
|
122
144
|
*/
|
|
123
|
-
async function fetchPackageData (
|
|
145
|
+
async function fetchPackageData (packages, includeAlerts, spinner) {
|
|
124
146
|
const socketSdk = await setupSdk(getDefaultKey() || FREE_API_KEY)
|
|
125
|
-
const result = await handleApiCall(socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion), 'looking up package')
|
|
126
|
-
const scoreResult = await handleApiCall(socketSdk.getScoreByNPMPackage(pkgName, pkgVersion), 'looking up package score')
|
|
127
147
|
|
|
128
|
-
|
|
129
|
-
return
|
|
130
|
-
}
|
|
148
|
+
const components = packages.map(pkg => {
|
|
149
|
+
return { 'purl': `pkg:${pkg}` }
|
|
150
|
+
})
|
|
131
151
|
|
|
132
|
-
|
|
133
|
-
|
|
152
|
+
const result = await handleApiCall(socketSdk.batchPackageFetch(
|
|
153
|
+
{ alerts: includeAlerts.toString() },
|
|
154
|
+
{
|
|
155
|
+
components
|
|
156
|
+
}), 'looking up package')
|
|
157
|
+
|
|
158
|
+
if (!result.success) {
|
|
159
|
+
return handleUnsuccessfulApiResponse('batchPackageFetch', result, spinner)
|
|
134
160
|
}
|
|
135
161
|
|
|
136
|
-
//
|
|
137
|
-
|
|
162
|
+
// @ts-ignore
|
|
163
|
+
result.data.map(pkg => {
|
|
164
|
+
const severityCount = pkg.alerts && getCountSeverity(pkg.alerts, includeAlerts ? undefined : 'high')
|
|
165
|
+
pkg.severityCount = severityCount
|
|
166
|
+
return pkg
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
spinner.stop()
|
|
138
170
|
|
|
139
171
|
return {
|
|
140
|
-
data: result.data
|
|
141
|
-
severityCount,
|
|
142
|
-
score: scoreResult.data
|
|
172
|
+
data: result.data
|
|
143
173
|
}
|
|
144
174
|
}
|
|
145
175
|
|
|
146
176
|
/**
|
|
147
|
-
* @param {
|
|
177
|
+
* @param {CommandContext} data
|
|
148
178
|
* @param {{ name: string } & CommandContext} context
|
|
149
179
|
* @param {import('ora').Ora} spinner
|
|
150
180
|
* @returns {void}
|
|
151
181
|
*/
|
|
152
|
-
|
|
182
|
+
function formatPackageDataOutput (/** @type {{ [key: string]: any }} */ { data }, { outputJson, outputMarkdown, strict }, spinner) {
|
|
153
183
|
if (outputJson) {
|
|
154
184
|
console.log(JSON.stringify(data, undefined, 2))
|
|
155
185
|
} else {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const issueSummary = formatSeverityCount(severityCount)
|
|
169
|
-
console.log('\n')
|
|
170
|
-
spinner[strict ? 'fail' : 'succeed'](`Package has these issues: ${issueSummary}`)
|
|
171
|
-
formatPackageIssuesDetails(data, outputMarkdown)
|
|
172
|
-
} else {
|
|
173
|
-
console.log('\n')
|
|
174
|
-
spinner.succeed('Package has no issues')
|
|
175
|
-
}
|
|
186
|
+
data.map((/** @type {{[key:string]: any}} */ d) => {
|
|
187
|
+
const { score, license, name, severityCount, version } = d
|
|
188
|
+
console.log(`\nPackage metrics for ${name}:`)
|
|
189
|
+
|
|
190
|
+
const scoreResult = {
|
|
191
|
+
'Supply Chain Risk': Math.floor(score.supplyChain * 100),
|
|
192
|
+
'Maintenance': Math.floor(score.maintenance * 100),
|
|
193
|
+
'Quality': Math.floor(score.quality * 100),
|
|
194
|
+
'Vulnerabilities': Math.floor(score.vulnerability * 100),
|
|
195
|
+
'License': Math.floor(score.license * 100),
|
|
196
|
+
'Overall': Math.floor(score.overall * 100)
|
|
197
|
+
}
|
|
176
198
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
console.log(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
199
|
+
Object.entries(scoreResult).map(score => console.log(`- ${score[0]}: ${formatScore(score[1])}`))
|
|
200
|
+
|
|
201
|
+
// Package license
|
|
202
|
+
console.log('\nPackage license:')
|
|
203
|
+
console.log(`${license}`)
|
|
204
|
+
|
|
205
|
+
// Package issues list
|
|
206
|
+
if (objectSome(severityCount)) {
|
|
207
|
+
const issueSummary = formatSeverityCount(severityCount)
|
|
208
|
+
console.log('\n')
|
|
209
|
+
spinner[strict ? 'fail' : 'succeed'](`Package has these issues: ${issueSummary}`)
|
|
210
|
+
formatPackageIssuesDetails(data.alerts, outputMarkdown)
|
|
211
|
+
} else if (severityCount && !objectSome(severityCount)) {
|
|
212
|
+
console.log('\n')
|
|
213
|
+
spinner.succeed('Package has no issues')
|
|
214
|
+
}
|
|
189
215
|
|
|
190
|
-
|
|
191
|
-
|
|
216
|
+
// Link to issues list
|
|
217
|
+
const format = new ChalkOrMarkdown(!!outputMarkdown)
|
|
218
|
+
const url = `https://socket.dev/npm/package/${name}/overview/${version}`
|
|
219
|
+
if (version === 'latest') {
|
|
220
|
+
console.log('\nDetailed info on socket.dev: ' + format.hyperlink(`${name}`, url, { fallbackToUrl: true }))
|
|
221
|
+
} else {
|
|
222
|
+
console.log('\nDetailed info on socket.dev: ' + format.hyperlink(`${name} v${version}`, url, { fallbackToUrl: true }))
|
|
223
|
+
}
|
|
224
|
+
if (!outputMarkdown) {
|
|
225
|
+
console.log(chalk.dim('\nOr rerun', chalk.italic(name), 'using the', chalk.italic('--json'), 'flag to get full JSON output'))
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (strict && objectSome(severityCount)) {
|
|
229
|
+
process.exit(1)
|
|
230
|
+
}
|
|
231
|
+
return d
|
|
232
|
+
})
|
|
192
233
|
}
|
|
193
234
|
}
|
|
194
235
|
|
|
195
236
|
/**
|
|
196
|
-
* @param {
|
|
237
|
+
* @param {{[key: string]: any}[]} alertsData
|
|
197
238
|
* @param {boolean} outputMarkdown
|
|
198
239
|
* @returns {void[]}
|
|
199
240
|
*/
|
|
200
|
-
function formatPackageIssuesDetails (
|
|
201
|
-
const issueDetails =
|
|
241
|
+
function formatPackageIssuesDetails (alertsData, outputMarkdown) {
|
|
242
|
+
const issueDetails = alertsData.filter(d => d['severity'] === 'high' || d['severity'] === 'critical')
|
|
202
243
|
|
|
203
244
|
const uniqueIssues = issueDetails.reduce((/** @type {{ [key: string]: {count: Number, label: string | undefined} }} */ acc, issue) => {
|
|
204
245
|
const { type } = issue
|
|
205
246
|
if (type) {
|
|
206
247
|
if (!acc[type]) {
|
|
207
248
|
acc[type] = {
|
|
208
|
-
label: issue
|
|
249
|
+
label: issue['type'],
|
|
209
250
|
count: 1
|
|
210
251
|
}
|
|
211
252
|
} else {
|
|
@@ -217,6 +258,7 @@ function formatPackageIssuesDetails (packageData, outputMarkdown) {
|
|
|
217
258
|
}, {})
|
|
218
259
|
|
|
219
260
|
const format = new ChalkOrMarkdown(!!outputMarkdown)
|
|
261
|
+
|
|
220
262
|
return Object.keys(uniqueIssues).map(issue => {
|
|
221
263
|
const issueWithLink = format.hyperlink(`${uniqueIssues[issue]?.label}`, `https://socket.dev/npm/issue/${issue}`, { fallbackToUrl: true })
|
|
222
264
|
if (uniqueIssues[issue]?.count === 1) {
|
|
@@ -12,7 +12,7 @@ import { getSetting, updateSetting } from '../../utils/settings.js'
|
|
|
12
12
|
|
|
13
13
|
const description = 'Socket API login'
|
|
14
14
|
|
|
15
|
-
/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
|
|
15
|
+
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
|
|
16
16
|
export const login = {
|
|
17
17
|
description,
|
|
18
18
|
run: async (argv, importMeta, { parentName }) => {
|
|
@@ -5,7 +5,7 @@ import { updateSetting } from '../../utils/settings.js'
|
|
|
5
5
|
|
|
6
6
|
const description = 'Socket API logout'
|
|
7
7
|
|
|
8
|
-
/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
|
|
8
|
+
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
|
|
9
9
|
export const logout = {
|
|
10
10
|
description,
|
|
11
11
|
run: async (argv, importMeta, { parentName }) => {
|
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import { spawn } from 'child_process'
|
|
1
|
+
import { spawn, execSync } from 'child_process'
|
|
2
2
|
import { fileURLToPath } from 'url'
|
|
3
3
|
|
|
4
4
|
const description = 'npm wrapper functionality'
|
|
5
5
|
|
|
6
|
-
/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
|
|
6
|
+
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
|
|
7
7
|
export const npm = {
|
|
8
8
|
description,
|
|
9
9
|
run: async (argv, _importMeta, _ctx) => {
|
|
10
|
+
const npmVersion = execSync('npm -v').toString()
|
|
10
11
|
const wrapperPath = fileURLToPath(new URL('../../shadow/npm-cli.cjs', import.meta.url))
|
|
11
12
|
process.exitCode = 1
|
|
12
13
|
spawn(process.execPath, [wrapperPath, ...argv], {
|
|
13
|
-
stdio: 'inherit'
|
|
14
|
+
stdio: 'inherit',
|
|
15
|
+
env: {
|
|
16
|
+
...process.env,
|
|
17
|
+
NPM_VERSION: npmVersion
|
|
18
|
+
}
|
|
14
19
|
}).on('exit', (code, signal) => {
|
|
15
20
|
if (signal) {
|
|
16
21
|
process.kill(process.pid, signal)
|
|
@@ -3,7 +3,7 @@ import { fileURLToPath } from 'url'
|
|
|
3
3
|
|
|
4
4
|
const description = 'npx wrapper functionality'
|
|
5
5
|
|
|
6
|
-
/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
|
|
6
|
+
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
|
|
7
7
|
export const npx = {
|
|
8
8
|
description,
|
|
9
9
|
run: async (argv, _importMeta, _ctx) => {
|
|
@@ -19,7 +19,7 @@ import { createDebugLogger } from '../../utils/misc.js'
|
|
|
19
19
|
import { getPackageFiles } from '../../utils/path-resolve.js'
|
|
20
20
|
import { setupSdk } from '../../utils/sdk.js'
|
|
21
21
|
|
|
22
|
-
/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
|
|
22
|
+
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
|
|
23
23
|
export const create = {
|
|
24
24
|
description: 'Create a project report',
|
|
25
25
|
async run (argv, importMeta, { parentName }) {
|
|
@@ -4,7 +4,7 @@ import { meowWithSubcommands } from '../../utils/meow-with-subcommands.js'
|
|
|
4
4
|
|
|
5
5
|
const description = 'Project report related commands'
|
|
6
6
|
|
|
7
|
-
/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
|
|
7
|
+
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
|
|
8
8
|
export const report = {
|
|
9
9
|
description,
|
|
10
10
|
run: async (argv, importMeta, { parentName }) => {
|
|
@@ -13,7 +13,7 @@ import { getSeverityCount, formatSeverityCount } from '../../utils/format-issues
|
|
|
13
13
|
import { printFlagList } from '../../utils/formatting.js'
|
|
14
14
|
import { setupSdk } from '../../utils/sdk.js'
|
|
15
15
|
|
|
16
|
-
/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
|
|
16
|
+
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
|
|
17
17
|
export const view = {
|
|
18
18
|
description: 'View a project report',
|
|
19
19
|
async run (argv, importMeta, { parentName }) {
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
import chalk from 'chalk'
|
|
4
|
+
import meow from 'meow'
|
|
5
|
+
import ora from 'ora'
|
|
6
|
+
|
|
7
|
+
import { outputFlags } from '../../flags/index.js'
|
|
8
|
+
import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
|
|
9
|
+
import { prepareFlags } from '../../utils/flags.js'
|
|
10
|
+
import { printFlagList } from '../../utils/formatting.js'
|
|
11
|
+
import { getDefaultKey, setupSdk } from '../../utils/sdk.js'
|
|
12
|
+
|
|
13
|
+
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
|
|
14
|
+
export const create = {
|
|
15
|
+
description: 'Create a repository in an organization',
|
|
16
|
+
async run (argv, importMeta, { parentName }) {
|
|
17
|
+
const name = parentName + ' create'
|
|
18
|
+
|
|
19
|
+
const input = setupCommand(name, create.description, argv, importMeta)
|
|
20
|
+
if (input) {
|
|
21
|
+
const spinnerText = 'Creating repository... \n'
|
|
22
|
+
const spinner = ora(spinnerText).start()
|
|
23
|
+
await createRepo(input.orgSlug, input, spinner)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const repositoryCreationFlags = prepareFlags({
|
|
29
|
+
repoName: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
shortFlag: 'n',
|
|
32
|
+
default: '',
|
|
33
|
+
description: 'Repository name',
|
|
34
|
+
},
|
|
35
|
+
repoDescription: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
shortFlag: 'd',
|
|
38
|
+
default: '',
|
|
39
|
+
description: 'Repository description',
|
|
40
|
+
},
|
|
41
|
+
homepage: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
shortFlag: 'h',
|
|
44
|
+
default: '',
|
|
45
|
+
description: 'Repository url',
|
|
46
|
+
},
|
|
47
|
+
defaultBranch: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
shortFlag: 'b',
|
|
50
|
+
default: 'main',
|
|
51
|
+
description: 'Repository default branch',
|
|
52
|
+
},
|
|
53
|
+
visibility: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
shortFlag: 'v',
|
|
56
|
+
default: 'private',
|
|
57
|
+
description: 'Repository visibility (Default Private)',
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
// Internal functions
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @typedef CommandContext
|
|
65
|
+
* @property {boolean} outputJson
|
|
66
|
+
* @property {boolean} outputMarkdown
|
|
67
|
+
* @property {string} orgSlug
|
|
68
|
+
* @property {string} name
|
|
69
|
+
* @property {string} description
|
|
70
|
+
* @property {string} homepage
|
|
71
|
+
* @property {string} default_branch
|
|
72
|
+
* @property {string} visibility
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {string} name
|
|
77
|
+
* @param {string} description
|
|
78
|
+
* @param {readonly string[]} argv
|
|
79
|
+
* @param {ImportMeta} importMeta
|
|
80
|
+
* @returns {void|CommandContext}
|
|
81
|
+
*/
|
|
82
|
+
function setupCommand (name, description, argv, importMeta) {
|
|
83
|
+
const flags = {
|
|
84
|
+
...outputFlags,
|
|
85
|
+
...repositoryCreationFlags
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const cli = meow(`
|
|
89
|
+
Usage
|
|
90
|
+
$ ${name} <org slug>
|
|
91
|
+
|
|
92
|
+
Options
|
|
93
|
+
${printFlagList(flags, 6)}
|
|
94
|
+
|
|
95
|
+
Examples
|
|
96
|
+
$ ${name} FakeOrg --repoName=test-repo
|
|
97
|
+
`, {
|
|
98
|
+
argv,
|
|
99
|
+
description,
|
|
100
|
+
importMeta,
|
|
101
|
+
flags
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const {
|
|
105
|
+
json: outputJson,
|
|
106
|
+
markdown: outputMarkdown,
|
|
107
|
+
repoName,
|
|
108
|
+
repoDescription,
|
|
109
|
+
homepage,
|
|
110
|
+
defaultBranch,
|
|
111
|
+
visibility
|
|
112
|
+
} = cli.flags
|
|
113
|
+
|
|
114
|
+
const [orgSlug = ''] = cli.input
|
|
115
|
+
|
|
116
|
+
if (!orgSlug) {
|
|
117
|
+
console.error(`${chalk.bgRed('Input error')}: Please provide an organization slug \n`)
|
|
118
|
+
cli.showHelp()
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!repoName) {
|
|
123
|
+
console.error(`${chalk.bgRed('Input error')}: Repository name is required. \n`)
|
|
124
|
+
cli.showHelp()
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
outputJson,
|
|
130
|
+
outputMarkdown,
|
|
131
|
+
orgSlug,
|
|
132
|
+
name: repoName,
|
|
133
|
+
description: repoDescription,
|
|
134
|
+
homepage,
|
|
135
|
+
default_branch: defaultBranch,
|
|
136
|
+
visibility
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @typedef RepositoryData
|
|
142
|
+
* @property {import('@socketsecurity/sdk').SocketSdkReturnType<'createOrgRepo'>["data"]} data
|
|
143
|
+
*/
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @param {string} orgSlug
|
|
147
|
+
* @param {CommandContext} input
|
|
148
|
+
* @param {import('ora').Ora} spinner
|
|
149
|
+
* @returns {Promise<void|RepositoryData>}
|
|
150
|
+
*/
|
|
151
|
+
async function createRepo (orgSlug, input, spinner) {
|
|
152
|
+
const socketSdk = await setupSdk(getDefaultKey())
|
|
153
|
+
const result = await handleApiCall(socketSdk.createOrgRepo(orgSlug, input), 'creating repository')
|
|
154
|
+
|
|
155
|
+
if (!result.success) {
|
|
156
|
+
return handleUnsuccessfulApiResponse('createOrgRepo', result, spinner)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
spinner.stop()
|
|
160
|
+
|
|
161
|
+
console.log('\n✅ Repository created successfully \n')
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
data: result.data
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
import chalk from 'chalk'
|
|
4
|
+
import meow from 'meow'
|
|
5
|
+
import ora from 'ora'
|
|
6
|
+
|
|
7
|
+
import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
|
|
8
|
+
import { getDefaultKey, setupSdk } from '../../utils/sdk.js'
|
|
9
|
+
|
|
10
|
+
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
|
|
11
|
+
export const del = {
|
|
12
|
+
description: 'Delete a repository in an organization',
|
|
13
|
+
async run (argv, importMeta, { parentName }) {
|
|
14
|
+
const name = parentName + ' del'
|
|
15
|
+
|
|
16
|
+
const input = setupCommand(name, del.description, argv, importMeta)
|
|
17
|
+
if (input) {
|
|
18
|
+
const spinnerText = 'Deleting repository... \n'
|
|
19
|
+
const spinner = ora(spinnerText).start()
|
|
20
|
+
await deleteRepository(input.orgSlug, input.repoName, spinner)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Internal functions
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef CommandContext
|
|
29
|
+
* @property {string} orgSlug
|
|
30
|
+
* @property {string} repoName
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {string} name
|
|
35
|
+
* @param {string} description
|
|
36
|
+
* @param {readonly string[]} argv
|
|
37
|
+
* @param {ImportMeta} importMeta
|
|
38
|
+
* @returns {void|CommandContext}
|
|
39
|
+
*/
|
|
40
|
+
function setupCommand (name, description, argv, importMeta) {
|
|
41
|
+
const cli = meow(`
|
|
42
|
+
Usage
|
|
43
|
+
$ ${name} <org slug> <repo slug>
|
|
44
|
+
|
|
45
|
+
Examples
|
|
46
|
+
$ ${name} FakeOrg test-repo
|
|
47
|
+
`, {
|
|
48
|
+
argv,
|
|
49
|
+
description,
|
|
50
|
+
importMeta
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const [orgSlug = '', repoName = ''] = cli.input
|
|
54
|
+
|
|
55
|
+
if (!orgSlug || !repoName) {
|
|
56
|
+
console.error(`${chalk.bgRed('Input error')}: Please provide an organization slug and repository slug \n`)
|
|
57
|
+
cli.showHelp()
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
orgSlug,
|
|
63
|
+
repoName
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @typedef RepositoryData
|
|
69
|
+
* @property {import('@socketsecurity/sdk').SocketSdkReturnType<'deleteOrgRepo'>["data"]} data
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {string} orgSlug
|
|
74
|
+
* @param {string} repoName
|
|
75
|
+
* @param {import('ora').Ora} spinner
|
|
76
|
+
* @returns {Promise<void|RepositoryData>}
|
|
77
|
+
*/
|
|
78
|
+
async function deleteRepository (orgSlug, repoName, spinner) {
|
|
79
|
+
const socketSdk = await setupSdk(getDefaultKey())
|
|
80
|
+
const result = await handleApiCall(socketSdk.deleteOrgRepo(orgSlug, repoName), 'deleting repository')
|
|
81
|
+
|
|
82
|
+
if (!result.success) {
|
|
83
|
+
return handleUnsuccessfulApiResponse('deleteOrgRepo', result, spinner)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
spinner.stop()
|
|
87
|
+
|
|
88
|
+
console.log('\n✅ Repository deleted successfully \n')
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
data: result.data
|
|
92
|
+
}
|
|
93
|
+
}
|