Setting the file. One moment.
Config · Sandbox Bench · vercel/next.js · Skills Docs
ContentsBack to the top of the page 24.4
Bench Common
scripts/ config.mjs
JavaScript · 150 lines · 5 KB
15
// nextRepo existing next.js checkout to use (default: auto-clone
16 // into <cacheDir>/next)
17 // reactRepoUrl clone/fetch URL (default https://github.com/react/react.git)
18 // nextRepoUrl clone/fetch URL (default https://github.com/vercel/next.js.git)
19 // vercelBin vercel CLI binary (default "vercel")
20 //
21 // CLI: node config.mjs show
22 // node config.mjs set team=<slug> project=<name> [key=value...]
23 import fs from 'node:fs'
24 import os from 'node:os'
25 import path from 'node:path'
26 import { execFileSync } from 'node:child_process'
27
28 const CONFIG_DIR = path. join (os. homedir (), '.config' , 'sandbox-bench' )
29 const CONFIG_FILE = path. join ( CONFIG_DIR , 'config.json' )
30
31 export function loadConfig ({ requireScope = true } = {}) {
32 let cfg = {}
33 if (fs. existsSync ( CONFIG_FILE )) {
34 cfg = JSON . parse (fs. readFileSync ( CONFIG_FILE , 'utf8' ))
35 }
36 // Env overrides for CI or one-off use.
37 cfg.team = process.env. SANDBOX_BENCH_TEAM ?? cfg.team
38 cfg.project = process.env. SANDBOX_BENCH_PROJECT ?? cfg.project
39 cfg.cacheDir = expandHome (
40 process.env. SANDBOX_BENCH_CACHE ?? cfg.cacheDir ?? '~/.cache/sandbox-bench'
41 )
42 cfg.reactRepo = expandHome (
43 process.env. SANDBOX_BENCH_REACT_REPO ?? cfg.reactRepo ?? ''
44 )
45 cfg.nextRepo = expandHome (
46 process.env. SANDBOX_BENCH_NEXT_REPO ?? cfg.nextRepo ?? ''
47 )
48 cfg.reactRepoUrl = cfg.reactRepoUrl ?? 'https://github.com/react/react.git'
49 cfg.nextRepoUrl = cfg.nextRepoUrl ?? 'https://github.com/vercel/next.js.git'
50 cfg.vercelBin = cfg.vercelBin ?? 'vercel'
51 if (requireScope && ( ! cfg.team || ! cfg.project)) {
52 throw new Error (
53 'sandbox-bench is not configured: team and project are required. \n ' +
54 'Ask the user which Vercel team + project the sandbox VMs should ' +
55 'run under, then save them with: \n ' +
56 ` node ${ path . relative ( process . cwd (), new URL ( import . meta . url ). pathname ) } ` +
57 'set team=<team-slug> project=<project-name> \n ' +
58 '(Stored in ~/.config/sandbox-bench/config.json, not in the repo.)'
59 )
60 }
61 return cfg
62 }
63
64 export function saveConfig ( patch ) {
65 fs. mkdirSync ( CONFIG_DIR , { recursive: true })
66 const existing = fs. existsSync ( CONFIG_FILE )
67 ? JSON . parse (fs. readFileSync ( CONFIG_FILE , 'utf8' ))
68 : {}
69 const merged = { ... existing, ... patch }
70 fs. writeFileSync ( CONFIG_FILE , JSON . stringify (merged, null , 2 ) + ' \n ' )
71 return merged
72 }
73
74 function expandHome ( p ) {
75 if ( ! p) return p
76 return p. startsWith ( '~/' ) ? path. join (os. homedir (), p. slice ( 2 )) : p
77 }
78
79 // Scope flags appended to every `vercel sandbox` call. Never widen scope
80 // beyond the configured team/project: the project may be shared.
81 export function sandboxScope ( cfg ) {
82 return [ '--team' , cfg.team, '--project' , cfg.project]
83 }
84
85 // Repos: use the configured checkout, else clone lazily into the cache.
86 function ensureRepo ( cfg , checkout , url , name ) {
87 if (checkout && fs. existsSync (path. join (checkout, '.git' ))) return checkout
88 const dest = path. join (cfg.cacheDir, name)
89 if (fs. existsSync (path. join (dest, '.git' ))) return dest
90 fs. mkdirSync (cfg.cacheDir, { recursive: true })
91 console. error ( `cloning ${ url } into ${ dest } (one-time)...` )
92 execFileSync ( 'git' , [ 'clone' , '--filter=blob:none' , url, dest], {
93 stdio: 'inherit' ,
94 })
95 return dest
96 }
97 export function ensureReactRepo ( cfg ) {
98 return ensureRepo (cfg, cfg.reactRepo, cfg.reactRepoUrl, 'react' )
99 }
100 export function ensureNextRepo ( cfg ) {
101 return ensureRepo (cfg, cfg.nextRepo, cfg.nextRepoUrl, 'next' )
102 }
103
104 // ------------------------------------------------------------------ CLI
105 // realpath both sides: /tmp vs /private/tmp (macOS) or any symlinked
106 // invocation path must not silently skip the CLI (a no-op "config set"
107 // would leave stale team/project in effect with exit 0).
108 function isMain () {
109 if ( ! process.argv[ 1 ]) return false
110 try {
111 return (
112 fs. realpathSync (process.argv[ 1 ]) ===
113 fs. realpathSync ( new URL ( import . meta .url).pathname)
114 )
115 } catch {
116 return false
117 }
118 }
119 if ( isMain ()) {
120 const [ cmd , ... rest ] = process.argv. slice ( 2 )
121 if (cmd === 'show' ) {
122 let cfg
123 try {
124 cfg = loadConfig ({ requireScope: false })
125 } catch (e) {
126 console. error (e.message)
127 process. exit ( 1 )
128 }
129 console. log ( JSON . stringify (cfg, null , 2 ))
130 if ( ! cfg.team || ! cfg.project) {
131 console. error ( ' \n NOT CONFIGURED: team/project missing (see file header).' )
132 process. exit ( 2 )
133 }
134 } else if (cmd === 'set' ) {
135 const patch = {}
136 for ( const kv of rest) {
137 const i = kv. indexOf ( '=' )
138 if (i < 0 ) {
139 console. error ( `bad argument "${ kv }", want key=value` )
140 process. exit ( 1 )
141 }
142 patch[kv. slice ( 0 , i)] = kv. slice (i + 1 )
143 }
144 const merged = saveConfig (patch)
145 console. log ( `saved: ${ JSON . stringify ( merged , null , 2 ) }` )
146 } else {
147 console. error ( 'usage: node config.mjs show | set key=value [key=value...]' )
148 process. exit ( 1 )
149 }
150 }