@o/scripts 0.16.19-canary.1790165522484
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/LICENSE +21 -0
- package/package.json +55 -0
- package/src/build-initial.ts +93 -0
- package/src/clean.ts +29 -0
- package/src/cmd.ts +105 -0
- package/src/dev-tunnel.ts +162 -0
- package/src/ensure-port.ts +79 -0
- package/src/ensure-tunnel.ts +19 -0
- package/src/ensure-zero-sqlite.ts +31 -0
- package/src/env-pull.ts +58 -0
- package/src/env-update.ts +342 -0
- package/src/exec-with-env.ts +61 -0
- package/src/helpers/args.ts +97 -0
- package/src/helpers/check-port.ts +22 -0
- package/src/helpers/deploy-health.ts +227 -0
- package/src/helpers/deploy-lock.ts +231 -0
- package/src/helpers/ensure-s3-bucket.ts +88 -0
- package/src/helpers/env-load.ts +43 -0
- package/src/helpers/get-docker-host.ts +37 -0
- package/src/helpers/get-test-env.ts +46 -0
- package/src/helpers/github-tail.ts +249 -0
- package/src/helpers/multipass.ts +174 -0
- package/src/helpers/parse-env-file.ts +71 -0
- package/src/helpers/process-compose-env.ts +57 -0
- package/src/helpers/resolve-dep-version.ts +11 -0
- package/src/helpers/run.ts +340 -0
- package/src/helpers/ssh.ts +50 -0
- package/src/helpers/uncloud-deploy.ts +162 -0
- package/src/helpers/uncloud.ts +197 -0
- package/src/helpers/wait-for-port.ts +33 -0
- package/src/helpers/zero-get-version.ts +8 -0
- package/src/release.ts +544 -0
- package/src/sst-get-environment.ts +38 -0
- package/src/typecheck.ts +21 -0
- package/src/up.ts +440 -0
- package/src/update-changelog.ts +92 -0
- package/src/wait-for-dev.ts +41 -0
package/src/release.ts
ADDED
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { cmd } from './cmd'
|
|
4
|
+
|
|
5
|
+
// avoid emitter error
|
|
6
|
+
process.setMaxListeners(50)
|
|
7
|
+
process.stderr.setMaxListeners(50)
|
|
8
|
+
process.stdout.setMaxListeners(50)
|
|
9
|
+
|
|
10
|
+
await cmd`publish takeout packages to npm`
|
|
11
|
+
.args(
|
|
12
|
+
`--patch boolean --minor boolean --major boolean --canary boolean
|
|
13
|
+
--rerun boolean --republish boolean --finish boolean --skip-finish boolean
|
|
14
|
+
--dry-run boolean --skip-test boolean --skip-build boolean --skip-version boolean --skip-publish boolean
|
|
15
|
+
--dirty boolean --tamagui-git-user boolean
|
|
16
|
+
--undocumented boolean --skip-all boolean`
|
|
17
|
+
)
|
|
18
|
+
.run(async ({ args, $, run, path, os }) => {
|
|
19
|
+
const fs = (await import('fs-extra')).default
|
|
20
|
+
const { writeJSON } = await import('fs-extra')
|
|
21
|
+
const pMap = (await import('p-map')).default
|
|
22
|
+
|
|
23
|
+
// for failed publishes that need to re-run
|
|
24
|
+
const reRun = args.rerun
|
|
25
|
+
const rePublish = reRun || args.republish
|
|
26
|
+
const finish = args.finish
|
|
27
|
+
const skipAll = args.skipAll
|
|
28
|
+
const undocumented = args.undocumented
|
|
29
|
+
const skipFinish = args.skipFinish || skipAll || undocumented
|
|
30
|
+
|
|
31
|
+
const canary = args.canary
|
|
32
|
+
const skipVersion = finish || rePublish || args.skipVersion
|
|
33
|
+
const shouldMajor = args.major
|
|
34
|
+
const shouldMinor = args.minor
|
|
35
|
+
const shouldPatch = args.patch
|
|
36
|
+
const dirty = finish || undocumented || args.dirty
|
|
37
|
+
const skipTest =
|
|
38
|
+
finish ||
|
|
39
|
+
rePublish ||
|
|
40
|
+
skipAll ||
|
|
41
|
+
args.skipTest ||
|
|
42
|
+
process.argv.includes('--skip-tests')
|
|
43
|
+
const skipBuild = finish || rePublish || skipAll || args.skipBuild
|
|
44
|
+
const skipPublish = args.skipPublish
|
|
45
|
+
const dryRun = args.dryRun
|
|
46
|
+
const tamaguiGitUser = args.tamaguiGitUser
|
|
47
|
+
async function getWorkspacePackages() {
|
|
48
|
+
// read workspaces from root package.json
|
|
49
|
+
const rootPackageJson = await fs.readJSON(path.join(process.cwd(), 'package.json'))
|
|
50
|
+
const workspaceGlobs = rootPackageJson.workspaces || []
|
|
51
|
+
|
|
52
|
+
// resolve workspace paths
|
|
53
|
+
const packagePaths: { name: string; location: string }[] = []
|
|
54
|
+
for (const glob of workspaceGlobs) {
|
|
55
|
+
if (glob.includes('*')) {
|
|
56
|
+
// handle glob patterns like "./packages/*"
|
|
57
|
+
const baseDir = glob.replace('/*', '')
|
|
58
|
+
const fullPath = path.join(process.cwd(), baseDir)
|
|
59
|
+
if (await fs.pathExists(fullPath)) {
|
|
60
|
+
const dirs = await fs.readdir(fullPath)
|
|
61
|
+
for (const dir of dirs) {
|
|
62
|
+
const pkgPath = path.join(fullPath, dir, 'package.json')
|
|
63
|
+
if (await fs.pathExists(pkgPath)) {
|
|
64
|
+
const pkg = await fs.readJSON(pkgPath)
|
|
65
|
+
packagePaths.push({
|
|
66
|
+
name: pkg.name,
|
|
67
|
+
location: path.join(baseDir, dir),
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
// handle direct paths like "./src/start"
|
|
74
|
+
const pkgPath = path.join(process.cwd(), glob, 'package.json')
|
|
75
|
+
if (await fs.pathExists(pkgPath)) {
|
|
76
|
+
const pkg = await fs.readJSON(pkgPath)
|
|
77
|
+
packagePaths.push({
|
|
78
|
+
name: pkg.name,
|
|
79
|
+
location: glob,
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return packagePaths
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function loadPackageJsons(packagePaths: { name: string; location: string }[]) {
|
|
89
|
+
const allPackageJsons = await Promise.all(
|
|
90
|
+
packagePaths
|
|
91
|
+
.filter((i) => i.location !== '.' && !i.name.startsWith('@takeout'))
|
|
92
|
+
.map(async ({ name, location }) => {
|
|
93
|
+
const cwd = path.join(process.cwd(), location)
|
|
94
|
+
const json = await fs.readJSON(path.join(cwd, 'package.json'))
|
|
95
|
+
return {
|
|
96
|
+
name,
|
|
97
|
+
cwd,
|
|
98
|
+
json,
|
|
99
|
+
path: path.join(cwd, 'package.json'),
|
|
100
|
+
directory: location,
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
const publishablePackages = allPackageJsons.filter(
|
|
106
|
+
(x) => !x.json.skipPublish && !x.json.private
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return { allPackageJsons, publishablePackages }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// main release flow
|
|
113
|
+
const curVersion = fs.readJSONSync('./packages/helpers/package.json').version
|
|
114
|
+
|
|
115
|
+
// must specify version (unless republishing):
|
|
116
|
+
if (
|
|
117
|
+
!rePublish &&
|
|
118
|
+
!skipVersion &&
|
|
119
|
+
!canary &&
|
|
120
|
+
!shouldPatch &&
|
|
121
|
+
!shouldMinor &&
|
|
122
|
+
!shouldMajor
|
|
123
|
+
) {
|
|
124
|
+
console.error(`Must specify one of --patch, --minor, or --major`)
|
|
125
|
+
process.exit(1)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const nextVersion = (() => {
|
|
129
|
+
if (rePublish || skipVersion) {
|
|
130
|
+
return curVersion
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (canary) {
|
|
134
|
+
return `${curVersion.replace(/(-\d+)+$/, '')}-${Date.now()}`
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const curMajor = +curVersion.split('.')[0] || 0
|
|
138
|
+
const curMinor = +curVersion.split('.')[1] || 0
|
|
139
|
+
const patchAndCanary = curVersion.split('.')[2]
|
|
140
|
+
const [curPatch] = patchAndCanary.split('-')
|
|
141
|
+
const patchVersion = shouldPatch ? +curPatch + 1 : 0
|
|
142
|
+
const minorVersion = curMinor + (shouldMinor ? 1 : 0)
|
|
143
|
+
const majorVersion = curMajor + (shouldMajor ? 1 : 0)
|
|
144
|
+
const next = `${majorVersion}.${minorVersion}.${patchVersion}`
|
|
145
|
+
|
|
146
|
+
return next
|
|
147
|
+
})()
|
|
148
|
+
|
|
149
|
+
if (!skipVersion) {
|
|
150
|
+
console.info(` 🚀 Releasing:`)
|
|
151
|
+
console.info(' Current:', curVersion)
|
|
152
|
+
console.info(` Next: ${nextVersion}`)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let restorePackageJsons: (() => Promise<void>) | undefined
|
|
156
|
+
let publishSucceeded = false
|
|
157
|
+
const publishedNames: string[] = []
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const hasTrustedPublishingIdentity =
|
|
161
|
+
process.env.GITHUB_ACTIONS === 'true' &&
|
|
162
|
+
Boolean(process.env.ACTIONS_ID_TOKEN_REQUEST_URL) &&
|
|
163
|
+
Boolean(process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN)
|
|
164
|
+
|
|
165
|
+
if (!finish && !skipPublish && !dryRun && !hasTrustedPublishingIdentity) {
|
|
166
|
+
try {
|
|
167
|
+
await run(`npm whoami`, { silent: true, captureOutput: true })
|
|
168
|
+
} catch {
|
|
169
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
'npm authentication is required to publish. Run `npm login` in a terminal, then retry the release.'
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
console.info(
|
|
176
|
+
'npm authentication is required. Complete the browser login to continue.'
|
|
177
|
+
)
|
|
178
|
+
await run(`npm login`, { interactive: true })
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
await run(`npm whoami`, { silent: true, captureOutput: true })
|
|
182
|
+
} catch {
|
|
183
|
+
throw new Error(
|
|
184
|
+
'npm authentication still failed after `npm login`. Verify `npm whoami` succeeds, then retry the release.'
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ensure we are up to date
|
|
191
|
+
// ensure we are on main
|
|
192
|
+
if (!canary && !process.env.CI) {
|
|
193
|
+
if ((await run(`git rev-parse --abbrev-ref HEAD`)).stdout.trim() !== 'main') {
|
|
194
|
+
throw new Error(`Not on main`)
|
|
195
|
+
}
|
|
196
|
+
if (!dirty && !rePublish && !finish) {
|
|
197
|
+
await run(`git pull --rebase origin main`)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const packagePaths = await getWorkspacePackages()
|
|
202
|
+
const { allPackageJsons, publishablePackages: packageJsons } =
|
|
203
|
+
await loadPackageJsons(packagePaths)
|
|
204
|
+
const originalPackageJsons = new Map<string, string>()
|
|
205
|
+
await Promise.all(
|
|
206
|
+
allPackageJsons.map(async ({ path: pkgPath }) => {
|
|
207
|
+
originalPackageJsons.set(pkgPath, await fs.readFile(pkgPath, 'utf8'))
|
|
208
|
+
})
|
|
209
|
+
)
|
|
210
|
+
restorePackageJsons = async () => {
|
|
211
|
+
await Promise.all(
|
|
212
|
+
[...originalPackageJsons].map(([pkgPath, contents]) =>
|
|
213
|
+
fs.writeFile(pkgPath, contents)
|
|
214
|
+
)
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!finish) {
|
|
219
|
+
console.info(
|
|
220
|
+
`Publishing in order:\n\n${packageJsons.map((x) => x.name).join('\n')}`
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function checkDistDirs() {
|
|
225
|
+
await Promise.all(
|
|
226
|
+
packageJsons.map(async ({ cwd, json }) => {
|
|
227
|
+
const distDir = path.join(cwd, 'dist')
|
|
228
|
+
if (json.scripts?.build) {
|
|
229
|
+
if (!(await fs.pathExists(distDir))) {
|
|
230
|
+
console.warn('no dist dir!', distDir)
|
|
231
|
+
process.exit(1)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
})
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (tamaguiGitUser) {
|
|
239
|
+
await run(`git config --global user.name 'Tamagui'`)
|
|
240
|
+
await run(`git config --global user.email 'tamagui@users.noreply.github.com`)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
console.info('install and build')
|
|
244
|
+
|
|
245
|
+
if (!rePublish && !finish) {
|
|
246
|
+
await run(process.env.CI ? `bun install --frozen-lockfile` : `bun install`)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!skipBuild && !finish) {
|
|
250
|
+
await run(`bun clean`)
|
|
251
|
+
await run(`bun run build`)
|
|
252
|
+
await checkDistDirs()
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (!finish) {
|
|
256
|
+
console.info('run checks')
|
|
257
|
+
|
|
258
|
+
if (!skipTest) {
|
|
259
|
+
await run(`bun lint`)
|
|
260
|
+
await run(`bun check`)
|
|
261
|
+
// only in packages
|
|
262
|
+
// await run(`bun test`)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (!dirty && !dryRun && !rePublish) {
|
|
267
|
+
const out = await run(`git status --porcelain`)
|
|
268
|
+
if (out.stdout) {
|
|
269
|
+
throw new Error(`Has unsaved git changes: ${out.stdout}`)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// snapshot workspace:* deps before mutation (shallow copy mutates originals)
|
|
274
|
+
const workspaceDeps = new Map<string, Record<string, Record<string, string>>>()
|
|
275
|
+
for (const { json, path: pkgPath } of allPackageJsons) {
|
|
276
|
+
const deps: Record<string, Record<string, string>> = {}
|
|
277
|
+
for (const field of [
|
|
278
|
+
'dependencies',
|
|
279
|
+
'devDependencies',
|
|
280
|
+
'optionalDependencies',
|
|
281
|
+
'peerDependencies',
|
|
282
|
+
]) {
|
|
283
|
+
if (!json[field]) continue
|
|
284
|
+
for (const depName in json[field]) {
|
|
285
|
+
if (json[field][depName].startsWith('workspace:')) {
|
|
286
|
+
deps[field] ??= {}
|
|
287
|
+
deps[field][depName] = json[field][depName]
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (Object.keys(deps).length) workspaceDeps.set(pkgPath, deps)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (!skipVersion && !finish) {
|
|
295
|
+
await Promise.all(
|
|
296
|
+
allPackageJsons.map(async ({ json, path: pkgPath }) => {
|
|
297
|
+
const next = { ...json }
|
|
298
|
+
|
|
299
|
+
next.version = nextVersion
|
|
300
|
+
|
|
301
|
+
for (const field of [
|
|
302
|
+
'dependencies',
|
|
303
|
+
'devDependencies',
|
|
304
|
+
'optionalDependencies',
|
|
305
|
+
'peerDependencies',
|
|
306
|
+
]) {
|
|
307
|
+
const nextDeps = next[field]
|
|
308
|
+
if (!nextDeps) continue
|
|
309
|
+
for (const depName in nextDeps) {
|
|
310
|
+
if (allPackageJsons.some((p) => p.name === depName)) {
|
|
311
|
+
nextDeps[depName] = nextVersion
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
await writeJSON(pkgPath, next, { spaces: 2 })
|
|
317
|
+
})
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (!finish && !rePublish) {
|
|
322
|
+
await run(`git diff`)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (!finish && !skipPublish) {
|
|
326
|
+
const packDir = path.join(os.tmpdir(), `takeout-release-${nextVersion}`)
|
|
327
|
+
await fs.remove(packDir)
|
|
328
|
+
await fs.ensureDir(packDir)
|
|
329
|
+
|
|
330
|
+
const isPublished = async ({ name }: (typeof packageJsons)[number]) => {
|
|
331
|
+
try {
|
|
332
|
+
const result = await run(`npm view ${name}@${nextVersion} version --json`, {
|
|
333
|
+
cwd: packDir,
|
|
334
|
+
captureOutput: true,
|
|
335
|
+
silent: true,
|
|
336
|
+
})
|
|
337
|
+
return JSON.parse(result.stdout.trim()) === nextVersion
|
|
338
|
+
} catch (error) {
|
|
339
|
+
const message = String(error)
|
|
340
|
+
const cause = error instanceof Error ? (error.cause as any) : undefined
|
|
341
|
+
const output = `${message}\n${cause?.stdout || ''}\n${cause?.stderr || ''}`
|
|
342
|
+
if (/E404|404 Not Found|is not in this registry/i.test(output)) {
|
|
343
|
+
return false
|
|
344
|
+
}
|
|
345
|
+
throw new Error(`Could not verify ${name}@${nextVersion} on npm:\n${output}`)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
console.info(`Checking ${packageJsons.length} package versions on npm...`)
|
|
350
|
+
const publishedChecks = await pMap(
|
|
351
|
+
packageJsons,
|
|
352
|
+
async (pkg) => ({ pkg, published: await isPublished(pkg) }),
|
|
353
|
+
{ concurrency: 8 }
|
|
354
|
+
)
|
|
355
|
+
const pendingPackages = publishedChecks
|
|
356
|
+
.filter(({ pkg, published }) => {
|
|
357
|
+
if (published) {
|
|
358
|
+
console.info(`Skipping ${pkg.name}: this version is already published`)
|
|
359
|
+
return false
|
|
360
|
+
}
|
|
361
|
+
return true
|
|
362
|
+
})
|
|
363
|
+
.map(({ pkg }) => pkg)
|
|
364
|
+
|
|
365
|
+
const prepareOne = async ({ name, cwd, json }: (typeof packageJsons)[number]) => {
|
|
366
|
+
const publishOptions = [canary && `--tag canary`, dryRun && `--dry-run`]
|
|
367
|
+
.filter(Boolean)
|
|
368
|
+
.join(' ')
|
|
369
|
+
const tgzPath = path.join(packDir, `${name.replace('/', '-')}.tgz`)
|
|
370
|
+
const packageJsonPath = path.join(cwd, 'package.json')
|
|
371
|
+
const packageJsonBeforePack = await fs.readFile(packageJsonPath, 'utf8')
|
|
372
|
+
const packageJsonForPublish = JSON.parse(packageJsonBeforePack)
|
|
373
|
+
packageJsonForPublish.repository = {
|
|
374
|
+
type: 'git',
|
|
375
|
+
url: 'https://github.com/tamagui/takeout2.git',
|
|
376
|
+
directory: path.relative(process.cwd(), cwd),
|
|
377
|
+
}
|
|
378
|
+
await writeJSON(packageJsonPath, packageJsonForPublish, { spaces: 2 })
|
|
379
|
+
|
|
380
|
+
// pack with bun (properly converts workspace:* to versions)
|
|
381
|
+
// use swap-exports for packages with build scripts, otherwise just pack
|
|
382
|
+
try {
|
|
383
|
+
if (json.scripts?.build) {
|
|
384
|
+
await run(
|
|
385
|
+
`bun run build --swap-exports -- bun pm pack --filename ${tgzPath}`,
|
|
386
|
+
{
|
|
387
|
+
cwd,
|
|
388
|
+
silent: true,
|
|
389
|
+
}
|
|
390
|
+
)
|
|
391
|
+
} else {
|
|
392
|
+
await run(`bun pm pack --filename ${tgzPath}`, {
|
|
393
|
+
cwd,
|
|
394
|
+
silent: true,
|
|
395
|
+
})
|
|
396
|
+
}
|
|
397
|
+
} finally {
|
|
398
|
+
await fs.writeFile(packageJsonPath, packageJsonBeforePack)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const workspaceDir = path.join(packDir, 'workspaces', name.replace('/', '_'))
|
|
402
|
+
await fs.ensureDir(workspaceDir)
|
|
403
|
+
await run(
|
|
404
|
+
`tar -xzf ${JSON.stringify(tgzPath)} -C ${JSON.stringify(workspaceDir)} --strip-components=1`,
|
|
405
|
+
{ silent: true }
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
return {
|
|
409
|
+
workspace: path.relative(packDir, workspaceDir),
|
|
410
|
+
publishOptions,
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (pendingPackages.length > 0) {
|
|
415
|
+
if (process.stdin.isTTY && process.stdout.isTTY && !dryRun) {
|
|
416
|
+
console.info(
|
|
417
|
+
'npm will open the browser for 2FA once. Select “do not challenge for the next 5 minutes” so the same short-lived approval can publish the remaining packages.'
|
|
418
|
+
)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const prepared = await pMap(pendingPackages, prepareOne, { concurrency: 8 })
|
|
422
|
+
await writeJSON(
|
|
423
|
+
path.join(packDir, 'package.json'),
|
|
424
|
+
{
|
|
425
|
+
name: 'takeout-release',
|
|
426
|
+
private: true,
|
|
427
|
+
workspaces: prepared.map(({ workspace }) => workspace),
|
|
428
|
+
},
|
|
429
|
+
{ spaces: 2 }
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
const publishOptions = prepared[0]?.publishOptions || ''
|
|
433
|
+
const webAuthCache = path.join(process.cwd(), 'scripts/cache-npm-webauth.cjs')
|
|
434
|
+
const nodeOptions = [process.env.NODE_OPTIONS, `--require=${webAuthCache}`]
|
|
435
|
+
.filter(Boolean)
|
|
436
|
+
.join(' ')
|
|
437
|
+
|
|
438
|
+
try {
|
|
439
|
+
await run(
|
|
440
|
+
`npm publish --workspaces --ignore-scripts --access public ${publishOptions}`.trim(),
|
|
441
|
+
{
|
|
442
|
+
cwd: packDir,
|
|
443
|
+
env: { NODE_OPTIONS: nodeOptions },
|
|
444
|
+
interactive: true,
|
|
445
|
+
}
|
|
446
|
+
)
|
|
447
|
+
publishedNames.push(...pendingPackages.map(({ name }) => name))
|
|
448
|
+
} catch (error) {
|
|
449
|
+
const postflight = await pMap(
|
|
450
|
+
pendingPackages,
|
|
451
|
+
async (pkg) => ({ pkg, published: await isPublished(pkg) }),
|
|
452
|
+
{ concurrency: 8 }
|
|
453
|
+
)
|
|
454
|
+
const completed = postflight.filter(({ published }) => published)
|
|
455
|
+
const missing = postflight.filter(({ published }) => !published)
|
|
456
|
+
publishedNames.push(...completed.map(({ pkg }) => pkg.name))
|
|
457
|
+
|
|
458
|
+
throw new Error(
|
|
459
|
+
`Publish stopped after ${completed.length} packages. Still missing:\n${missing.map(({ pkg }) => pkg.name).join('\n')}\n\nRe-run with --republish to retry only these packages.`,
|
|
460
|
+
{ cause: error }
|
|
461
|
+
)
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
publishSucceeded = true
|
|
466
|
+
console.info(`✅ ${dryRun ? '[dry-run] ' : ''}Published\n`)
|
|
467
|
+
|
|
468
|
+
// restore workspace:* protocols after publishing
|
|
469
|
+
if (!dryRun) {
|
|
470
|
+
await Promise.all(
|
|
471
|
+
allPackageJsons.map(async ({ path: pkgPath }) => {
|
|
472
|
+
const saved = workspaceDeps.get(pkgPath)
|
|
473
|
+
if (!saved) return
|
|
474
|
+
const current = await fs.readJSON(pkgPath)
|
|
475
|
+
for (const field in saved) {
|
|
476
|
+
if (!current[field]) continue
|
|
477
|
+
for (const depName in saved[field]) {
|
|
478
|
+
current[field][depName] = saved[field][depName]
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
await writeJSON(pkgPath, current, { spaces: 2 })
|
|
482
|
+
})
|
|
483
|
+
)
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// revert version changes after dry-run
|
|
487
|
+
if (dryRun && !rePublish) {
|
|
488
|
+
await run(`git checkout -- packages/*/package.json`, { silent: true })
|
|
489
|
+
console.info('Reverted version changes\n')
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// restore package.json files for undocumented releases (no git history)
|
|
493
|
+
if (undocumented) {
|
|
494
|
+
console.info('restoring package.json files...')
|
|
495
|
+
await run(`git checkout -- packages/*/package.json`, { silent: true })
|
|
496
|
+
console.info(`✅ restored package.json files (undocumented release)\n`)
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (!skipFinish && !dryRun) {
|
|
501
|
+
// then git tag, commit, push
|
|
502
|
+
if (!finish) {
|
|
503
|
+
await run(`bun install`)
|
|
504
|
+
}
|
|
505
|
+
const tagPrefix = canary ? 'canary' : 'v'
|
|
506
|
+
const gitTag = `${tagPrefix}${nextVersion}`
|
|
507
|
+
|
|
508
|
+
await finishAndCommit()
|
|
509
|
+
|
|
510
|
+
async function finishAndCommit(cwd = process.cwd()) {
|
|
511
|
+
if (!rePublish || reRun || finish) {
|
|
512
|
+
await run(`git add -A`, { cwd })
|
|
513
|
+
|
|
514
|
+
await run(`git commit -m ${gitTag}`, { cwd })
|
|
515
|
+
|
|
516
|
+
if (!canary) {
|
|
517
|
+
await run(`git tag ${gitTag}`, { cwd })
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (!dirty) {
|
|
521
|
+
// pull once more before pushing so if there was a push in interim we get it
|
|
522
|
+
await run(`git pull --rebase origin HEAD`, { cwd })
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
await run(`git push origin head`, { cwd })
|
|
526
|
+
if (!canary) {
|
|
527
|
+
await run(`git push origin ${gitTag}`, { cwd })
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
console.info(`✅ Pushed and versioned\n`)
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
console.info(`✅ Done\n`)
|
|
536
|
+
} catch (err) {
|
|
537
|
+
if (!publishSucceeded && publishedNames.length === 0 && restorePackageJsons) {
|
|
538
|
+
await restorePackageJsons()
|
|
539
|
+
console.info('restored package.json files after failed release')
|
|
540
|
+
}
|
|
541
|
+
console.info('\nError:\n', err)
|
|
542
|
+
process.exit(1)
|
|
543
|
+
}
|
|
544
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from 'node:child_process'
|
|
4
|
+
|
|
5
|
+
export function getEnvironment(resourceName: string) {
|
|
6
|
+
if (!resourceName) {
|
|
7
|
+
console.error(`No resouce name given:
|
|
8
|
+
|
|
9
|
+
bun scripts/get-environment.ts [resourceName]
|
|
10
|
+
`)
|
|
11
|
+
process.exit(1)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
console.info(`Getting environment for ${resourceName}...`)
|
|
15
|
+
const result = spawnSync('bun', ['sst', 'state', 'export', '--stage', 'production'], {
|
|
16
|
+
encoding: 'utf-8',
|
|
17
|
+
})
|
|
18
|
+
const state = JSON.parse(result.stdout || '{}')
|
|
19
|
+
|
|
20
|
+
const resource = state.latest.resources.find(
|
|
21
|
+
(x: any) => x.outputs?._dev?.title === resourceName
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if (!resource) {
|
|
25
|
+
console.error(`Can't find resource ${resourceName}`)
|
|
26
|
+
process.exit(1)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return resource.outputs._dev.environment as Record<string, string>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (import.meta.main) {
|
|
33
|
+
const { cmd } = await import('./cmd')
|
|
34
|
+
|
|
35
|
+
await cmd`get environment variables from SST production state`.run(async () => {
|
|
36
|
+
console.info(getEnvironment(process.argv[2] || ''))
|
|
37
|
+
})
|
|
38
|
+
}
|
package/src/typecheck.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { cmd } from './cmd'
|
|
4
|
+
|
|
5
|
+
await cmd`run typescript type checking`.run(async ({ run }) => {
|
|
6
|
+
const { sleep } = await import('@o/helpers')
|
|
7
|
+
|
|
8
|
+
const useTsgo = process.env.TSGO === '1'
|
|
9
|
+
const args = process.argv.slice(2)
|
|
10
|
+
|
|
11
|
+
// hugely cpu intensive, let the dev server start first before using:
|
|
12
|
+
if (process.env.LAZY_TYPECHECK) {
|
|
13
|
+
await sleep(6000)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (useTsgo) {
|
|
17
|
+
await run(`bun tsgo --project ./tsconfig.json --noEmit --preserveWatchOutput ${args}`)
|
|
18
|
+
} else {
|
|
19
|
+
await run(`bun tsc --project ./tsconfig.json --noEmit --preserveWatchOutput ${args}`)
|
|
20
|
+
}
|
|
21
|
+
})
|