Skip to content

Commit 16504f6

Browse files
👷 add a script to easily create an access token
1 parent 32e3655 commit 16504f6

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { createInterface } from 'node:readline/promises'
2+
import fs from 'node:fs'
3+
import path from 'node:path'
4+
import os from 'node:os'
5+
import { Writable } from 'node:stream'
6+
import { printError, printLog, runMain } from '../lib/executionUtils.ts'
7+
import { findPackageJsonFiles } from '../lib/filesUtils.ts'
8+
9+
const TOKEN_NAME = 'browser-sdk-granular'
10+
11+
runMain(async () => {
12+
const accessToken = findAccessTokenFromNpmrc()
13+
if (!accessToken) {
14+
printError('Could not find NPM token in ~/.npmrc. Make sure you have logged in with `npm login`')
15+
process.exit(1)
16+
}
17+
18+
const password = await prompt({ question: 'Password: ', showOutput: false })
19+
const otp = await prompt({ question: 'OTP: ' })
20+
21+
const { objects: tokens } = (await callNpmApi({
22+
route: 'tokens',
23+
method: 'GET',
24+
otp,
25+
accessToken,
26+
})) as {
27+
objects: Array<{ name: string; key: string }>
28+
}
29+
30+
const existingToken = tokens.find((token) => token.name === TOKEN_NAME)
31+
if (existingToken) {
32+
printLog('Token already exists, removing it')
33+
await callNpmApi({
34+
route: `tokens/token/${existingToken.key}`,
35+
method: 'DELETE',
36+
otp,
37+
accessToken,
38+
})
39+
}
40+
41+
await callNpmApi({
42+
route: 'tokens',
43+
method: 'POST',
44+
body: {
45+
password,
46+
name: TOKEN_NAME,
47+
token_description: 'Token used to publish Browser SDK packages (@datadog/browser-*) from the CI.',
48+
expires: 90,
49+
bypass_2fa: true,
50+
packages: findPublisheablePackages(),
51+
packages_and_scopes_permission: 'read-write',
52+
},
53+
otp,
54+
accessToken,
55+
})
56+
57+
printLog('Token created')
58+
})
59+
60+
async function callNpmApi({
61+
route,
62+
method,
63+
body,
64+
otp,
65+
accessToken,
66+
}: {
67+
route: string
68+
method: string
69+
body?: any
70+
otp: string
71+
accessToken: string
72+
}): Promise<any> {
73+
// https://api-docs.npmjs.com/#tag/Tokens/operation/createToken
74+
const response = await fetch(`https://registry.npmjs.org/-/npm/v1/${route}`, {
75+
method,
76+
headers: {
77+
'Content-Type': 'application/json',
78+
Accept: 'application/json',
79+
Authorization: `Bearer ${accessToken}`,
80+
'npm-otp': otp,
81+
},
82+
body: body && JSON.stringify(body),
83+
})
84+
85+
const responseBody: any = await response.text().then((text) => {
86+
try {
87+
return JSON.parse(text) as unknown
88+
} catch {
89+
return text
90+
}
91+
})
92+
93+
if (!response.ok) {
94+
printError('Failed to renew token.')
95+
printError(`NPM API '${method} ${route}' responded with ${response.statusText}`)
96+
printError(responseBody.error || responseBody)
97+
process.exit(1)
98+
}
99+
100+
return responseBody
101+
}
102+
103+
function findPublisheablePackages() {
104+
const files = findPackageJsonFiles()
105+
return files
106+
.filter(({ content }) => content.version && content.name && !content.private)
107+
.map(({ content }) => content.name as string)
108+
}
109+
110+
function findAccessTokenFromNpmrc() {
111+
const npmrcPath = path.join(os.homedir(), '.npmrc')
112+
try {
113+
return fs.readFileSync(npmrcPath, 'utf-8').match(/_authToken=(.*)/)?.[1]
114+
} catch (error) {
115+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
116+
return undefined
117+
}
118+
throw error
119+
}
120+
}
121+
122+
async function prompt({ question, showOutput = true }: { question: string; showOutput?: boolean }) {
123+
if (!showOutput) {
124+
process.stdout.write(question)
125+
}
126+
127+
const readline = createInterface({
128+
input: process.stdin,
129+
output: showOutput
130+
? process.stdout
131+
: new Writable({
132+
write(_chunk, _encoding, callback) {
133+
callback()
134+
},
135+
}),
136+
terminal: true,
137+
})
138+
139+
const value = await readline.question(question)
140+
readline.close()
141+
if (!showOutput) {
142+
process.stdout.write('\n')
143+
}
144+
return value
145+
}

0 commit comments

Comments
 (0)