@yahoo/uds 2.1.3 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- package/cli/bin/uds-darwin-arm64-baseline +0 -0
- package/cli/bin/uds-linux-arm64 +0 -0
- package/cli/bin/uds-linux-x64-baseline +0 -0
- package/cli/cli.ts +25 -8
- package/cli/utils/auth.ts +39 -202
- package/dist/fixtures.cjs +1 -1
- package/dist/fixtures.js +1 -1
- package/package.json +5 -6
- package/cli/README.md +0 -208
- package/cli/utils/client_secrets.json +0 -23
- package/cli/utils/client_secrets.json.enc +0 -0
- package/cli/utils/secrets.ts +0 -40
Binary file
|
package/cli/bin/uds-linux-arm64
CHANGED
Binary file
|
Binary file
|
package/cli/cli.ts
CHANGED
@@ -2,14 +2,25 @@
|
|
2
2
|
// Start bluebun to run the correct CLI command
|
3
3
|
import { parseArgs } from 'node:util';
|
4
4
|
|
5
|
-
import { print, red
|
5
|
+
import { cli, print, red } from 'bluebun';
|
6
|
+
import updateNotifier from 'simple-update-notifier';
|
6
7
|
|
8
|
+
import packageJson from '../package.json' assert { type: 'json' };
|
7
9
|
import { getAuthenticatedUser } from './utils/auth';
|
8
10
|
|
11
|
+
const EXEMPT_FROM_AUTH = ['login'];
|
12
|
+
|
9
13
|
async function main() {
|
14
|
+
if (process.env.CI !== '1') {
|
15
|
+
await updateNotifier({
|
16
|
+
pkg: packageJson,
|
17
|
+
shouldNotifyInNpmScript: true,
|
18
|
+
});
|
19
|
+
}
|
20
|
+
|
10
21
|
// We need to pass the path from Node to the Bun binaries
|
11
22
|
// because there's an issue where the Bun binaries struggle to resolve
|
12
|
-
const { values
|
23
|
+
const { values } = parseArgs({
|
13
24
|
args: Bun.argv,
|
14
25
|
options: {
|
15
26
|
commandsPath: {
|
@@ -21,10 +32,19 @@ async function main() {
|
|
21
32
|
});
|
22
33
|
|
23
34
|
const cliPath = values.commandsPath!.toString();
|
24
|
-
const isLoginCmd = positionals.includes('login');
|
25
35
|
|
26
|
-
|
36
|
+
const { command, props } = await cli({
|
37
|
+
name: 'uds',
|
38
|
+
cliPath,
|
39
|
+
});
|
40
|
+
|
41
|
+
// Don't require auth when running root `--help`
|
42
|
+
const isRootHelp = command.name === 'uds' && props.options.help;
|
43
|
+
const shouldRequireAuth = !isRootHelp && !EXEMPT_FROM_AUTH.includes(command.name);
|
44
|
+
|
45
|
+
if (shouldRequireAuth) {
|
27
46
|
const user = await getAuthenticatedUser(cliPath);
|
47
|
+
|
28
48
|
if (!user) {
|
29
49
|
print(
|
30
50
|
red(
|
@@ -35,10 +55,7 @@ async function main() {
|
|
35
55
|
}
|
36
56
|
}
|
37
57
|
|
38
|
-
run(
|
39
|
-
name: 'uds',
|
40
|
-
cliPath,
|
41
|
-
});
|
58
|
+
await command.run(props);
|
42
59
|
}
|
43
60
|
|
44
61
|
main();
|
package/cli/utils/auth.ts
CHANGED
@@ -1,179 +1,47 @@
|
|
1
|
+
import child_process from 'node:child_process';
|
1
2
|
import { unlink } from 'node:fs/promises';
|
2
3
|
import path from 'node:path';
|
4
|
+
import util from 'node:util';
|
3
5
|
|
4
|
-
import { print, red } from 'bluebun';
|
6
|
+
import { ask, print, red } from 'bluebun';
|
5
7
|
import Bun from 'bun';
|
6
|
-
import { getApps, initializeApp } from 'firebase/app';
|
7
|
-
import {
|
8
|
-
connectAuthEmulator,
|
9
|
-
getAuth,
|
10
|
-
GoogleAuthProvider,
|
11
|
-
signInWithCredential,
|
12
|
-
} from 'firebase/auth';
|
13
|
-
import { google, oauth2_v2 } from 'googleapis';
|
14
|
-
import http from 'http';
|
15
|
-
import open from 'open';
|
16
8
|
import { type FirebaseUser } from 'root/database/firebase';
|
17
9
|
import { v5 as uuidv5 } from 'uuid';
|
18
10
|
|
19
|
-
|
11
|
+
const exec = util.promisify(child_process.exec);
|
20
12
|
|
21
|
-
type User =
|
22
|
-
type LoginProvider = 'google' | 'firebase' | 'configurator';
|
23
|
-
|
24
|
-
const LOGIN_PROVIDER: LoginProvider =
|
25
|
-
(process.env.LOGIN_PROVIDER as LoginProvider) ?? 'configurator';
|
26
|
-
|
27
|
-
const REDIRECT_URL = clientSecrets.web.redirect_uris[0];
|
28
|
-
const { port: PORT, origin: SERVER_ORIGIN } = new URL(REDIRECT_URL);
|
29
|
-
|
30
|
-
const configuratorUrlOrigin =
|
31
|
-
process.env.NODE_ENV === 'development' ? 'http://localhost:4001' : 'https://config.uds.build';
|
13
|
+
type User = Partial<FirebaseUser>;
|
32
14
|
|
33
15
|
const CACHE_FILEPATH = '.uds/user.json';
|
34
16
|
const DEFAULT_CLI_PATH = path.resolve(import.meta.dir, '..');
|
35
17
|
const CACHED_USER_FILE = path.resolve(DEFAULT_CLI_PATH, CACHE_FILEPATH);
|
36
18
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
const
|
41
|
-
? clientSecrets.firebaseConfig
|
42
|
-
: {
|
43
|
-
projectId: 'uds-poc',
|
44
|
-
apiKey: 'DUMMY_API_KEY',
|
45
|
-
authDomain: 'uds-poc.firebaseapp.com',
|
46
|
-
};
|
47
|
-
|
48
|
-
const firebaseApp = getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];
|
49
|
-
const auth = getAuth(firebaseApp);
|
50
|
-
|
51
|
-
if (isEmulator) {
|
52
|
-
connectAuthEmulator(auth, 'http://127.0.0.1:9099');
|
53
|
-
}
|
54
|
-
|
55
|
-
// Credentials can be found in https://console.cloud.google.com/apis/credentials/oauthclient/700524957090-6uju02gp6i0rlm3p17nt1v6vbrretfka.apps.googleusercontent.com?project=uds-poc.
|
56
|
-
const oauth2Client = new google.auth.OAuth2(
|
57
|
-
clientSecrets.web.client_id,
|
58
|
-
clientSecrets.web.client_secret,
|
59
|
-
REDIRECT_URL,
|
60
|
-
);
|
61
|
-
|
62
|
-
function isYahooEmployee(user?: User) {
|
63
|
-
return user?.email?.endsWith('@yahooinc.com');
|
64
|
-
}
|
65
|
-
|
66
|
-
function getAuthorizeUrl(loginProvider = LOGIN_PROVIDER) {
|
67
|
-
if (loginProvider === 'configurator') {
|
68
|
-
return `${configuratorUrlOrigin}/login?continue=${REDIRECT_URL}`;
|
69
|
-
}
|
70
|
-
|
71
|
-
return oauth2Client.generateAuthUrl({
|
72
|
-
access_type: 'offline',
|
73
|
-
scope: [
|
74
|
-
'https://www.googleapis.com/auth/userinfo.profile',
|
75
|
-
'https://www.googleapis.com/auth/userinfo.email',
|
76
|
-
].join(' '),
|
77
|
-
hd: 'yahooinc.com',
|
78
|
-
include_granted_scopes: true,
|
79
|
-
});
|
80
|
-
}
|
81
|
-
|
82
|
-
function onPostHandler(
|
83
|
-
this: http.Server,
|
84
|
-
req: http.IncomingMessage,
|
85
|
-
res: http.ServerResponse,
|
86
|
-
resolve: (user: User) => void,
|
87
|
-
reject: (reason?: unknown) => void,
|
88
|
-
) {
|
89
|
-
const corsHeaders = {
|
90
|
-
'Access-Control-Allow-Origin': configuratorUrlOrigin,
|
91
|
-
'Access-Control-Allow-Methods': 'OPTIONS, POST',
|
92
|
-
'Access-Control-Allow-Headers': '*',
|
93
|
-
'Access-Control-Request-Method': '*',
|
94
|
-
};
|
95
|
-
|
96
|
-
for (const [header, value] of Object.entries(corsHeaders)) {
|
97
|
-
res.setHeader(header, value);
|
98
|
-
}
|
99
|
-
|
100
|
-
if (req.headers.origin !== configuratorUrlOrigin) {
|
101
|
-
res.writeHead(405).end(`Request origin not allowed.`);
|
102
|
-
reject(`Request origin not allowed.`);
|
103
|
-
return;
|
104
|
-
}
|
105
|
-
|
106
|
-
if (req.method === 'OPTIONS') {
|
107
|
-
res.writeHead(204).end();
|
108
|
-
return;
|
109
|
-
}
|
110
|
-
|
111
|
-
if (req.method !== 'POST') {
|
112
|
-
// Set the 405 status and Allow header
|
113
|
-
res.writeHead(405, { Allow: 'POST' }).end('Method Not Allowed');
|
114
|
-
reject('Method Not Allowed');
|
115
|
-
return;
|
116
|
-
}
|
117
|
-
|
118
|
-
let data = '';
|
119
|
-
|
120
|
-
req.on('data', (chunk) => {
|
121
|
-
data += chunk.toString();
|
122
|
-
});
|
123
|
-
|
124
|
-
req.on('error', (err: NodeJS.ErrnoException) => {
|
125
|
-
reject(err);
|
126
|
-
});
|
19
|
+
async function getGitEmail(scope: 'local' | 'global' | 'system' = 'global') {
|
20
|
+
let email;
|
21
|
+
try {
|
22
|
+
const { stdout, stderr } = await exec(`git config --${scope} user.email`);
|
127
23
|
|
128
|
-
|
129
|
-
|
130
|
-
const user: FirebaseUser = JSON.parse(data);
|
131
|
-
res.end('Authentication successful! Please close this window.');
|
132
|
-
resolve(user);
|
133
|
-
} catch (err) {
|
134
|
-
reject(err);
|
135
|
-
} finally {
|
136
|
-
this.close();
|
24
|
+
if (stderr) {
|
25
|
+
throw new Error(`Git config error: ${stderr}`);
|
137
26
|
}
|
138
|
-
|
139
|
-
}
|
140
|
-
|
141
|
-
async function onGETHandler(
|
142
|
-
this: http.Server,
|
143
|
-
req: http.IncomingMessage,
|
144
|
-
res: http.ServerResponse,
|
145
|
-
resolve: (user: User) => void,
|
146
|
-
reject: (reason?: unknown) => void,
|
147
|
-
) {
|
148
|
-
try {
|
149
|
-
const code = new URL(req.url || '', SERVER_ORIGIN).searchParams.get('code');
|
150
|
-
if (!code) {
|
151
|
-
res.end('There was no code parameter on the url.');
|
152
|
-
this.close();
|
153
|
-
return;
|
27
|
+
email = stdout.trim();
|
28
|
+
if (!isYahooEmployee({ email })) {
|
29
|
+
throw new Error(`Git config email is not a @yahooinc.com email address.`);
|
154
30
|
}
|
31
|
+
} catch {
|
32
|
+
email = await ask('Please enter your @yahooinc.com email address:', {
|
33
|
+
validation: (answer) => {
|
34
|
+
const warning = red(`🚨 Email was not a @yahooinc.com address.`);
|
35
|
+
return isYahooEmployee({ email: answer }) || warning;
|
36
|
+
},
|
37
|
+
});
|
38
|
+
}
|
155
39
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
const { tokens } = await oauth2Client.getToken(code);
|
160
|
-
oauth2Client.setCredentials(tokens);
|
161
|
-
|
162
|
-
let user: User;
|
163
|
-
|
164
|
-
if (LOGIN_PROVIDER === 'firebase') {
|
165
|
-
// Build Firebase credential using the Google ID token.
|
166
|
-
const credential = GoogleAuthProvider.credential(tokens.id_token);
|
167
|
-
user = (await signInWithCredential(auth, credential)).user;
|
168
|
-
} else {
|
169
|
-
const oauth2 = google.oauth2({ version: 'v2', auth: oauth2Client });
|
170
|
-
user = (await oauth2.userinfo.get()).data;
|
171
|
-
}
|
40
|
+
return email ?? buildCIUser().email; // fallback to CI user if don't have an email for some reason.
|
41
|
+
}
|
172
42
|
|
173
|
-
|
174
|
-
|
175
|
-
reject(err);
|
176
|
-
}
|
43
|
+
function isYahooEmployee(user?: User) {
|
44
|
+
return Boolean(user?.email?.endsWith('@yahooinc.com'));
|
177
45
|
}
|
178
46
|
|
179
47
|
/**
|
@@ -182,41 +50,14 @@ async function onGETHandler(
|
|
182
50
|
* @returns
|
183
51
|
*/
|
184
52
|
async function authenticateUser(): Promise<User> {
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
server.listen(PORT, async () => {
|
192
|
-
const authorizeUrl = getAuthorizeUrl();
|
193
|
-
|
194
|
-
print(`Please visit the following URL if it didn't open automatically:\n${authorizeUrl}`);
|
195
|
-
|
196
|
-
const childProcess = await open(authorizeUrl, { wait: false });
|
197
|
-
childProcess.unref();
|
198
|
-
|
199
|
-
process.on('SIGINT', () => {
|
200
|
-
server.close();
|
201
|
-
reject('Received SIGINT.');
|
202
|
-
});
|
203
|
-
});
|
204
|
-
|
205
|
-
server.on('error', (err: NodeJS.ErrnoException) => {
|
206
|
-
if (err.code && err.code.includes('EADDRINUSE')) {
|
207
|
-
print(
|
208
|
-
red(`🚨 Port ${PORT} already in use. Cannot start local server to handle OAuth flow.`),
|
209
|
-
);
|
210
|
-
server.close();
|
211
|
-
}
|
212
|
-
});
|
53
|
+
// Fetch firebase user data from the configurator.
|
54
|
+
const email = await getGitEmail('local');
|
55
|
+
const resp = await Bun.fetch(`https://config.uds.build/api/cli-analytics?email=${email}`);
|
56
|
+
if (resp.status !== 200) {
|
57
|
+
throw new Error(`Failed to fetch firebase user data from endpoint. Status ${resp.status}`);
|
58
|
+
}
|
213
59
|
|
214
|
-
|
215
|
-
server.on('request', (req, res) => onPostHandler.call(server, req, res, resolve, reject));
|
216
|
-
} else {
|
217
|
-
server.on('request', (req, res) => onGETHandler.call(server, req, res, resolve, reject));
|
218
|
-
}
|
219
|
-
});
|
60
|
+
return (await resp.json()) as User;
|
220
61
|
}
|
221
62
|
|
222
63
|
async function logout(cachePath?: string) {
|
@@ -242,8 +83,7 @@ async function login() {
|
|
242
83
|
user = await authenticateUser();
|
243
84
|
await Bun.write(CACHED_USER_FILE, JSON.stringify(user, null, 2));
|
244
85
|
} catch (err) {
|
245
|
-
console.error(
|
246
|
-
throw err;
|
86
|
+
console.error((err as NodeJS.ErrnoException).message);
|
247
87
|
}
|
248
88
|
}
|
249
89
|
|
@@ -251,16 +91,16 @@ async function login() {
|
|
251
91
|
}
|
252
92
|
|
253
93
|
/**
|
254
|
-
* Builds a
|
94
|
+
* Builds a firebase user object for a Continuous Integration (CI) user.
|
255
95
|
*
|
256
96
|
* This function generates a CI user with an email based on the UDS_TEAM_SLUG
|
257
97
|
* environment variable. If the UDS_TEAM_SLUG is not defined, it defaults to 'unknown'.
|
258
98
|
* The generated user has a predefined first name, display name, and an empty avatar.
|
259
99
|
* The analytics context includes the team and a role set to 'other'.
|
260
100
|
*
|
261
|
-
* @returns {
|
101
|
+
* @returns {user} The generated CI user object.
|
262
102
|
*/
|
263
|
-
function buildCIUser():
|
103
|
+
function buildCIUser(): User {
|
264
104
|
const team = process.env.UDS_TEAM_SLUG;
|
265
105
|
|
266
106
|
if (!team) {
|
@@ -325,14 +165,11 @@ async function getAuthenticatedUser(cliPath = DEFAULT_CLI_PATH): Promise<User |
|
|
325
165
|
export {
|
326
166
|
authenticateUser,
|
327
167
|
buildCIUser,
|
328
|
-
|
168
|
+
exec,
|
329
169
|
getAuthenticatedUser,
|
330
|
-
|
170
|
+
getGitEmail,
|
331
171
|
isYahooEmployee,
|
332
172
|
login,
|
333
|
-
type LoginProvider,
|
334
173
|
logout,
|
335
|
-
onGETHandler,
|
336
|
-
onPostHandler,
|
337
174
|
type User,
|
338
175
|
};
|
package/dist/fixtures.cjs
CHANGED
package/dist/fixtures.js
CHANGED
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@yahoo/uds",
|
3
3
|
"description": "Yahoo Universal System",
|
4
|
-
"version": "2.1
|
4
|
+
"version": "2.2.1",
|
5
5
|
"type": "module",
|
6
6
|
"bin": {
|
7
7
|
"uds": "./cli/uds-cli"
|
@@ -9,6 +9,7 @@
|
|
9
9
|
"files": [
|
10
10
|
"cli/**",
|
11
11
|
"!cli/**/*.test.*",
|
12
|
+
"!cli/README.md",
|
12
13
|
"dist/**",
|
13
14
|
"fonts/**"
|
14
15
|
],
|
@@ -116,13 +117,14 @@
|
|
116
117
|
},
|
117
118
|
"scripts": {
|
118
119
|
"build": "bun run ./scripts/build.ts",
|
119
|
-
"build:cli": "bun
|
120
|
+
"build:cli": "bun ./cli/compile.ts",
|
120
121
|
"ci:publish": "bun semantic-release -e semantic-release-monorepo",
|
121
122
|
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist && rm -rf .coverage && rm -rf cli/bin",
|
122
123
|
"dev": "concurrently bun:dev:*",
|
123
124
|
"dev:fixtures": "bun run ./scripts/buildFixtures.ts --watch",
|
124
125
|
"dev:purgeCSSData": "bun run ./scripts/generatePurgeCSSData.ts --watch",
|
125
126
|
"dev:ts": "tsup --watch",
|
127
|
+
"format:pkg": "cd ../../ && bun syncpack format",
|
126
128
|
"lint": "eslint -c eslint.config.mjs .",
|
127
129
|
"lint:fix": "bun run lint --fix",
|
128
130
|
"lint:pkg": "bun publint",
|
@@ -143,15 +145,13 @@
|
|
143
145
|
"@ariakit/react": "^0.4.12",
|
144
146
|
"bluebun": "^0.0.34",
|
145
147
|
"clsx": "^2.1.1",
|
146
|
-
"firebase": "^11.0.0",
|
147
148
|
"framer-motion": "^11.11.4",
|
148
|
-
"googleapis": "^144.0.0",
|
149
149
|
"imurmurhash": "^0.1.4",
|
150
150
|
"lodash-es": "^4.17.21",
|
151
|
-
"open": "^10.1.0",
|
152
151
|
"prompts": "^2.4.2",
|
153
152
|
"react-toastify": "^11.0.2",
|
154
153
|
"semver": "^7.6.3",
|
154
|
+
"simple-update-notifier": "^2.0.0",
|
155
155
|
"tailwind-merge": "^2.5.3",
|
156
156
|
"ts-morph": "^25.0.0",
|
157
157
|
"uuid": "^11.0.3"
|
@@ -168,7 +168,6 @@
|
|
168
168
|
"autoprefixer": "^10.4.20",
|
169
169
|
"chalk": "^5.3.0",
|
170
170
|
"concurrently": "^9.0.1",
|
171
|
-
"node-mocks-http": "^1.16.1",
|
172
171
|
"postcss": "^8.4.47",
|
173
172
|
"tailwindcss": "^3.4.13",
|
174
173
|
"terser": "^5.34.1",
|
package/cli/README.md
DELETED
@@ -1,208 +0,0 @@
|
|
1
|
-
import { DocTabs } from '~/components/DocTabs';
|
2
|
-
import { Box, VStack } from '@yahoo/uds';
|
3
|
-
import { RequiredChip } from '~/components/RequiredChip';
|
4
|
-
import { DocImage } from '~/components/DocImage';
|
5
|
-
import { PACKAGE_MANAGERS, CommandSnippet } from '~/components/CommandSnippet';
|
6
|
-
import { toPageTitle } from '~/utils/toPageTitle';
|
7
|
-
|
8
|
-
export const metadata = {
|
9
|
-
title: toPageTitle({ title: 'CLI', category: 'Tools' }),
|
10
|
-
};
|
11
|
-
|
12
|
-
# Universal CLI
|
13
|
-
|
14
|
-
> **Update**: The CLI tool no longer requires Bun. You can use any node package manager to run the CLI (e.g. `npx uds sync`).
|
15
|
-
|
16
|
-
The Universal CLI is available as command, `uds`. The tool was created with [Bluebun](https://github.com/jamonholmgren/bluebun/) (inspired by [Gluegun](https://github.com/infinitered/gluegun)). The UDS CLI is a standalone binary that doesn't require having Bun installed locally in order to use it.
|
17
|
-
|
18
|
-
<Box justifyContent="center">
|
19
|
-
<DocImage light={{ src: '/images/cli-screenshot.png', width: 500, height: 356 }} />
|
20
|
-
</Box>
|
21
|
-
|
22
|
-
## Requirements
|
23
|
-
|
24
|
-
1. To use the CLI, first [install](/docs/getting-started/quick-start#installation) the `@yahoo/uds` package as a dependency in your package.json, which has the UDS CLI as a binary.
|
25
|
-
|
26
|
-
2. Log in to the CLI
|
27
|
-
|
28
|
-
<VStack spacingBottom="2">
|
29
|
-
<DocTabs
|
30
|
-
items={PACKAGE_MANAGERS.map((packageManager) => ({
|
31
|
-
label: packageManager,
|
32
|
-
content: <CommandSnippet name={packageManager} command="bun uds login" />,
|
33
|
-
}))}
|
34
|
-
/>
|
35
|
-
</VStack>
|
36
|
-
|
37
|
-
## Commands
|
38
|
-
|
39
|
-
> **Note**: If you are _not_ running the CLI from a package.json script you will need to call your package manager execute command before the binary in order to run it directly. i.e. `npx uds purge`
|
40
|
-
|
41
|
-
<VStack spacingBottom="2">
|
42
|
-
<DocTabs
|
43
|
-
items={PACKAGE_MANAGERS.map((packageManager) => ({
|
44
|
-
label: packageManager,
|
45
|
-
content: <CommandSnippet name={packageManager} command="bunx uds purge" />,
|
46
|
-
}))}
|
47
|
-
/>
|
48
|
-
</VStack>
|
49
|
-
|
50
|
-
The following command are available after installing `@yahoo/uds`:
|
51
|
-
|
52
|
-
### Sync
|
53
|
-
|
54
|
-
The `uds sync` command fetches the latest design config from the [Configurator](/docs/getting-started/using-configurator) and writes it to a file in your project.
|
55
|
-
|
56
|
-
#### Flags
|
57
|
-
|
58
|
-
| Flag | Description | Default | Required |
|
59
|
-
| --------- | --------------------------- | --------------- | ---------------- |
|
60
|
-
| `id` | ID from the Configurator | | <RequiredChip /> |
|
61
|
-
| `outFile` | File to write the output to | ./uds.config.ts | |
|
62
|
-
|
63
|
-
**Example:**
|
64
|
-
|
65
|
-
```shell
|
66
|
-
uds sync --id [id] --outFile [path]
|
67
|
-
uds sync --id [id]
|
68
|
-
uds sync --id [id] --outFile=uds.config.js # pass a file with a JS extension if you are using Javascript
|
69
|
-
```
|
70
|
-
|
71
|
-
#### Environment variables
|
72
|
-
|
73
|
-
Alternatively, you can use environment variables instead of flags.
|
74
|
-
|
75
|
-
| Variable | Description | Default | Required |
|
76
|
-
| -------------- | --------------------------- | --------------- | ---------------- |
|
77
|
-
| `UDS_ID` | ID from the Configurator | | <RequiredChip /> |
|
78
|
-
| `UDS_OUT_FILE` | File to write the output to | ./uds.config.ts | |
|
79
|
-
|
80
|
-
**Example:**
|
81
|
-
|
82
|
-
```shell
|
83
|
-
UDS_ID=[id] UDS_OUT_FILE=[path] uds sync
|
84
|
-
```
|
85
|
-
|
86
|
-
### Purge
|
87
|
-
|
88
|
-
The `uds purge` command is used to optimize and reduce the amount of CSS produced by your app.
|
89
|
-
|
90
|
-
Running this command produces a `./dist/safelist.ts` file in the root of your project. This file imported in your `tailwind.config.js` and passed to the `safelist` configuration option.
|
91
|
-
|
92
|
-
```typescript
|
93
|
-
/** @type {import('tailwindcss').Config} */
|
94
|
-
const { safelist } = require(`${__dirname}/dist/safelist.ts`);
|
95
|
-
|
96
|
-
module.exports = {
|
97
|
-
content: ['./pages/**/*.{html,jsx,tsx}', './components/**/*.{html,jsx,tsx}'],
|
98
|
-
safelist,
|
99
|
-
// ...
|
100
|
-
};
|
101
|
-
```
|
102
|
-
|
103
|
-
For more information about safelisting classes in Tailwind, visit the [official documentation](https://tailwindcss.com/docs/content-configuration#safelisting-classes)
|
104
|
-
|
105
|
-
**Example:**
|
106
|
-
|
107
|
-
```shell
|
108
|
-
uds purge
|
109
|
-
```
|
110
|
-
|
111
|
-
#### Flags
|
112
|
-
|
113
|
-
| Flag | Description | Default | Required |
|
114
|
-
| -------- | --------------------------- | ------------------ | -------- |
|
115
|
-
| `entry` | The entry to source files | ./src/ | |
|
116
|
-
| `output` | Output path of the safelist | ./dist/safelist.ts | |
|
117
|
-
| `config` | UDS config | ./uds.config.ts | |
|
118
|
-
|
119
|
-
**Example:**
|
120
|
-
|
121
|
-
```shell
|
122
|
-
uds purge --output output/dir
|
123
|
-
```
|
124
|
-
|
125
|
-
#### Environment variables
|
126
|
-
|
127
|
-
| Variable | Description | Default | Required |
|
128
|
-
| ------------------------------- | ------------------------------------------- | ------- | -------- |
|
129
|
-
| `ENABLED_SCALE_AND_COLOR_MODES` | Selects which color and scale modes to keep | all | |
|
130
|
-
|
131
|
-
**Example:**
|
132
|
-
|
133
|
-
```shell
|
134
|
-
ENABLED_SCALE_AND_COLOR_MODES="dark,large" uds purge
|
135
|
-
```
|
136
|
-
|
137
|
-
### Codemod
|
138
|
-
|
139
|
-
The `uds codemod` command is here to help you run one-off codemods. In the future, we'll likely apply codemodes via `uds migrate` for you, but this will provide more fine grained control over applying codemods to help you deal with breaking changes to our system. We're flying fast right now, and investing too much energy in full blown migrations is silly as things will just keep changing, but this is a step into an _easy_ migration future!
|
140
|
-
|
141
|
-
Any file added to commands/codemod will be available in prompt land.
|
142
|
-
|
143
|
-
```shell
|
144
|
-
uds codemod
|
145
|
-
```
|
146
|
-
|
147
|
-
### Login
|
148
|
-
|
149
|
-
The `uds login` command is used to authenticate the user to the CLI. We use
|
150
|
-
Google's OAuth2 flow, which is the same login flow used by the [Configurator](/docs/getting-started/using-configurator) and the documentation site. Logging in to the CLI helps us identify and track CLI usage and metrics.
|
151
|
-
|
152
|
-
### Circumventing Login
|
153
|
-
|
154
|
-
There may be situations (such as CI runs) where you can't authenticate your personal account with the CLI. For this case, providing the `CI=true` and `UDS_TEAM_SLUG` env variables is required when using the CLI in a CI environment:
|
155
|
-
|
156
|
-
```env
|
157
|
-
CI=true
|
158
|
-
UDS_TEAM_SLUG=<your-team-slug> uds <command>
|
159
|
-
```
|
160
|
-
|
161
|
-
Your team slug can be found in your team's Configurator URL path:
|
162
|
-
|
163
|
-
<DocImage light={{ src: '/images/team-slug-screenshot.png' }} />
|
164
|
-
|
165
|
-
### Expo (WIP)
|
166
|
-
|
167
|
-
The `uds expo` command is for building and launching React Native apps using Expo.
|
168
|
-
This command is still a WIP and is only used internally at the moment.
|
169
|
-
|
170
|
-
```shell
|
171
|
-
uds expo build --profile [profile] --platform [ios|android]
|
172
|
-
uds expo dev --profile [profile] --platform [ios|android]
|
173
|
-
uds expo launch --profile [profile] --platform [ios|android]
|
174
|
-
uds expo update --profile [profile] --platform [ios|android]
|
175
|
-
uds expo --help
|
176
|
-
```
|
177
|
-
|
178
|
-
## Development
|
179
|
-
|
180
|
-
The CLI is created with [Bluebun](https://github.com/jamonholmgren/bluebun/) (inspired by [Gluegun](https://github.com/infinitered/gluegun)), but specifically designed to be used with [Bun](https://bun.sh). Bluebun relies on Bun APIs and is designed to be extremely fast, with no-dependencies.
|
181
|
-
|
182
|
-
### Adding a command
|
183
|
-
|
184
|
-
Commands are organized in a tree structure in `uds/cli/commands`. The root command is the name of the CLI (`uds`) and in `uds.ts`, which subcommand being in its own file. See the Bluebun [Command docs](https://github.com/jamonholmgren/bluebun/blob/main/docs/commands.md) for more information on usage docs and creating command files.
|
185
|
-
|
186
|
-
**Important note:**
|
187
|
-
|
188
|
-
Adding nested commands, i.e. `uds expo dev` does work correctly when UDS is consumed from npm. As a workaround, please see code for expo/expo.ts for re-routing sub-commands from the root command file. To verify your CLI command works correctly you should run `npm pack` within the packages/uds directory. Once you have your generated tarball you should copy that tarball to a test application such as https://github.com/yahoo-uds/uds-nextjs-demo, then add `"@yahoo/uds": "file:./tarball-generated-from-npm-pack.tgz` to it's dependencies and run an install. Now you should be able to run `bun uds [your command name]` to test your functionality.
|
189
|
-
|
190
|
-
### Testing the login flow
|
191
|
-
|
192
|
-
To test the login flow, the CLI starts a web server and opens browser window to
|
193
|
-
a login page in Configurator. In prod, it opens https://config.uds.build/login.
|
194
|
-
In local dev, http://localhost:4001/login is opened.
|
195
|
-
|
196
|
-
In the root of the UDS monorepo, run the following commands:
|
197
|
-
|
198
|
-
```
|
199
|
-
# Start configurator app web server:
|
200
|
-
turbo --filter uds-configurator dev
|
201
|
-
|
202
|
-
# Run the CLI login command:
|
203
|
-
bun run --cwd packages/uds uds login
|
204
|
-
```
|
205
|
-
|
206
|
-
### API Reference
|
207
|
-
|
208
|
-
Bluebun comes with a number of built-in utilities that are exported from the main `bluebun` package. See [reference guide](https://github.com/jamonholmgren/bluebun/blob/main/docs/reference.md) for more.
|
@@ -1,23 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"web": {
|
3
|
-
"client_id": "700524957090-6uju02gp6i0rlm3p17nt1v6vbrretfka.apps.googleusercontent.com",
|
4
|
-
"project_id": "uds-poc",
|
5
|
-
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
6
|
-
"token_uri": "https://oauth2.googleapis.com/token",
|
7
|
-
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
8
|
-
"client_secret": "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",
|
9
|
-
"redirect_uris": [
|
10
|
-
"http://localhost:3000/oauth2callback",
|
11
|
-
"https://uds.build/oauth2callback",
|
12
|
-
"https://config.uds.build/oauth2callback"
|
13
|
-
]
|
14
|
-
},
|
15
|
-
"firebaseConfig": {
|
16
|
-
"apiKey": "AIzaSyDZEsm1GQ1lkK7T8NxJ9D_Pqmcz5u5D2hc",
|
17
|
-
"authDomain": "uds-poc.firebaseapp.com",
|
18
|
-
"projectId": "uds-poc",
|
19
|
-
"storageBucket": "uds-poc.appspot.com",
|
20
|
-
"messagingSenderId": "700524957090",
|
21
|
-
"appId": "1:700524957090:web:b4b5cdba42f694bb5cf6fb"
|
22
|
-
}
|
23
|
-
}
|
Binary file
|
package/cli/utils/secrets.ts
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
import path from 'node:path';
|
2
|
-
|
3
|
-
// eslint-disable-next-line n/no-unpublished-import
|
4
|
-
import { loadEnvConfig } from '@next/env';
|
5
|
-
import { $ } from 'bun';
|
6
|
-
|
7
|
-
loadEnvConfig(process.cwd());
|
8
|
-
|
9
|
-
const secretsFile = path.resolve(import.meta.dir, 'client_secrets.json');
|
10
|
-
const encryptedFilename = `${secretsFile}.enc`;
|
11
|
-
|
12
|
-
async function encrypt() {
|
13
|
-
await $`openssl enc -aes-256-cbc -in ${secretsFile} -out ${encryptedFilename}`;
|
14
|
-
}
|
15
|
-
|
16
|
-
async function decrypt() {
|
17
|
-
const f = Bun.file(secretsFile);
|
18
|
-
if (!(await f.exists())) {
|
19
|
-
await $`openssl enc -aes-256-cbc -in ${encryptedFilename} -out ${secretsFile} -d -pass env:UDS_CLI_SECRETS_PW`;
|
20
|
-
}
|
21
|
-
}
|
22
|
-
|
23
|
-
async function main() {
|
24
|
-
if (!process.env.UDS_CLI_SECRETS_PW) {
|
25
|
-
console.error('Please set the UDS_CLI_SECRETS_PW env variable. The password is in LastPass.');
|
26
|
-
process.exitCode = 1;
|
27
|
-
return;
|
28
|
-
}
|
29
|
-
|
30
|
-
const cmd = Bun.argv[2];
|
31
|
-
if (cmd === 'encrypt') {
|
32
|
-
await encrypt();
|
33
|
-
console.log(`Secrets file encrypted to ${encryptedFilename}`);
|
34
|
-
} else if (cmd === 'decrypt') {
|
35
|
-
await decrypt();
|
36
|
-
console.log(`Secrets file saved to ${secretsFile}`);
|
37
|
-
}
|
38
|
-
}
|
39
|
-
|
40
|
-
main();
|