@take-out/scripts 0.4.4 → 0.4.6
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/package.json +3 -3
- package/src/ensure-zero-sqlite.ts +1 -1
- package/src/helpers/run.ts +1 -1
- package/src/release.ts +156 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@take-out/scripts",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/cmd.ts",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@clack/prompts": "^0.8.2",
|
|
32
|
-
"@take-out/helpers": "0.4.
|
|
33
|
-
"@take-out/run": "0.4.
|
|
32
|
+
"@take-out/helpers": "0.4.6",
|
|
33
|
+
"@take-out/run": "0.4.6",
|
|
34
34
|
"picocolors": "^1.1.1"
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -16,7 +16,7 @@ await cmd`ensure zero-sqlite3 native module is built`.run(async ({ $, fs }) => {
|
|
|
16
16
|
if (result.exitCode !== 0) {
|
|
17
17
|
console.error(
|
|
18
18
|
'zero-sqlite3 build failed, will fall back to wasm:',
|
|
19
|
-
result.stderr.toString()
|
|
19
|
+
result.stderr.toString(),
|
|
20
20
|
)
|
|
21
21
|
}
|
|
22
22
|
})
|
package/src/helpers/run.ts
CHANGED
|
@@ -204,7 +204,7 @@ export async function run(
|
|
|
204
204
|
console.error(`run() error: ${errorMsg}: ${stderr || ''}`)
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
const error = new Error(errorMsg, { cause: { exitCode } })
|
|
207
|
+
const error = new Error(errorMsg, { cause: { exitCode, stdout, stderr } })
|
|
208
208
|
Error.captureStackTrace(error, runInternal)
|
|
209
209
|
throw error
|
|
210
210
|
}
|
package/src/release.ts
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
import { cmd } from './cmd'
|
|
4
4
|
|
|
5
|
+
function isPublishAuthOrOtpError(error: unknown) {
|
|
6
|
+
return /EOTP|one-time password/i.test(getPublishErrorMessage(error))
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function redactNpmOtp(message: string) {
|
|
10
|
+
return message.replace(/--otp(?:=|\s+)\S+/g, '--otp=******')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getPublishErrorMessage(error: unknown) {
|
|
14
|
+
const cause = error instanceof Error ? (error.cause as any) : undefined
|
|
15
|
+
return redactNpmOtp(
|
|
16
|
+
[error instanceof Error ? error.message : String(error), cause?.stdout, cause?.stderr]
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.join('\n'),
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
5
22
|
// avoid emitter error
|
|
6
23
|
process.setMaxListeners(50)
|
|
7
24
|
process.stderr.setMaxListeners(50)
|
|
@@ -15,7 +32,7 @@ await cmd`publish takeout packages to npm`
|
|
|
15
32
|
--dirty boolean --tamagui-git-user boolean --sync-on-zero boolean --skip-on-zero-sync boolean
|
|
16
33
|
--undocumented boolean --skip-all boolean`,
|
|
17
34
|
)
|
|
18
|
-
.run(async ({ args, $, run, path, os }) => {
|
|
35
|
+
.run(async ({ args, $, run, path, os, prompt }) => {
|
|
19
36
|
const fs = (await import('fs-extra')).default
|
|
20
37
|
const { writeJSON } = await import('fs-extra')
|
|
21
38
|
const pMap = (await import('p-map')).default
|
|
@@ -49,6 +66,8 @@ await cmd`publish takeout packages to npm`
|
|
|
49
66
|
const tamaguiGitUser = args.tamaguiGitUser
|
|
50
67
|
const syncOnZeroOnly = args.syncOnZero
|
|
51
68
|
const skipOnZeroSync = args.skipOnZeroSync
|
|
69
|
+
const canPromptForNpmOtp =
|
|
70
|
+
!finish && !undocumented && !process.argv.includes('--ci') && !process.env.CI
|
|
52
71
|
|
|
53
72
|
async function syncOnZeroIn() {
|
|
54
73
|
if (!(await fs.pathExists(onZeroGithub))) return
|
|
@@ -277,6 +296,10 @@ await cmd`publish takeout packages to npm`
|
|
|
277
296
|
console.info(` Next: ${nextVersion}`)
|
|
278
297
|
}
|
|
279
298
|
|
|
299
|
+
let restorePackageJsons: (() => Promise<void>) | undefined
|
|
300
|
+
let publishSucceeded = false
|
|
301
|
+
const publishedNames: string[] = []
|
|
302
|
+
|
|
280
303
|
try {
|
|
281
304
|
// sync on-zero IN (before release)
|
|
282
305
|
if (!skipOnZeroSync && !finish && !rePublish) {
|
|
@@ -297,6 +320,19 @@ await cmd`publish takeout packages to npm`
|
|
|
297
320
|
const packagePaths = await getWorkspacePackages()
|
|
298
321
|
const { allPackageJsons, publishablePackages: packageJsons } =
|
|
299
322
|
await loadPackageJsons(packagePaths)
|
|
323
|
+
const originalPackageJsons = new Map<string, string>()
|
|
324
|
+
await Promise.all(
|
|
325
|
+
allPackageJsons.map(async ({ path: pkgPath }) => {
|
|
326
|
+
originalPackageJsons.set(pkgPath, await fs.readFile(pkgPath, 'utf8'))
|
|
327
|
+
}),
|
|
328
|
+
)
|
|
329
|
+
restorePackageJsons = async () => {
|
|
330
|
+
await Promise.all(
|
|
331
|
+
[...originalPackageJsons].map(([pkgPath, contents]) =>
|
|
332
|
+
fs.writeFile(pkgPath, contents),
|
|
333
|
+
),
|
|
334
|
+
)
|
|
335
|
+
}
|
|
300
336
|
|
|
301
337
|
if (!finish) {
|
|
302
338
|
console.info(
|
|
@@ -409,44 +445,129 @@ await cmd`publish takeout packages to npm`
|
|
|
409
445
|
const packDir = path.join(os.tmpdir(), `takeout-release-${nextVersion}`)
|
|
410
446
|
await fs.ensureDir(packDir)
|
|
411
447
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const
|
|
448
|
+
let cachedOtp = process.env.npm_config_otp || process.env.NPM_CONFIG_OTP
|
|
449
|
+
let otpPromptInFlight: Promise<string> | undefined
|
|
450
|
+
const getOtp = (reason: string, optional = false): Promise<string> => {
|
|
451
|
+
if (otpPromptInFlight) return otpPromptInFlight
|
|
452
|
+
otpPromptInFlight = (async () => {
|
|
453
|
+
console.info(`\n${reason}`)
|
|
454
|
+
const code = await prompt.text({
|
|
455
|
+
message: optional
|
|
456
|
+
? 'npm 2FA code (6 digits, empty to skip)'
|
|
457
|
+
: 'npm 2FA code (6 digits)',
|
|
458
|
+
validate: (value) => {
|
|
459
|
+
const next = String(value ?? '').trim()
|
|
460
|
+
if (optional && !next) return
|
|
461
|
+
if (/^\d{6}$/.test(next)) return
|
|
462
|
+
return 'Enter a 6-digit code'
|
|
463
|
+
},
|
|
464
|
+
})
|
|
465
|
+
if (prompt.isCancel(code)) {
|
|
466
|
+
prompt.cancel('Publish cancelled')
|
|
467
|
+
throw new Error('No OTP provided, aborting publish')
|
|
468
|
+
}
|
|
469
|
+
const otp = String(code ?? '').trim()
|
|
470
|
+
if (!otp) {
|
|
471
|
+
if (optional) return ''
|
|
472
|
+
throw new Error('No OTP provided, aborting publish')
|
|
473
|
+
}
|
|
474
|
+
cachedOtp = otp
|
|
475
|
+
return otp
|
|
476
|
+
})().finally(() => {
|
|
477
|
+
otpPromptInFlight = undefined
|
|
478
|
+
})
|
|
479
|
+
return otpPromptInFlight
|
|
480
|
+
}
|
|
419
481
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
482
|
+
if (!cachedOtp && canPromptForNpmOtp && !dryRun) {
|
|
483
|
+
await getOtp(
|
|
484
|
+
'Most Takeout npm publishes require 2FA. Provide the current code now so every package publish uses it.',
|
|
485
|
+
true,
|
|
486
|
+
)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const failedPublishes: string[] = []
|
|
490
|
+
const publishOne = async ({ name, cwd, json }: (typeof packageJsons)[number]) => {
|
|
491
|
+
const publishOptions = [canary && `--tag canary`, dryRun && `--dry-run`]
|
|
492
|
+
.filter(Boolean)
|
|
493
|
+
.join(' ')
|
|
494
|
+
const tgzPath = path.join(packDir, `${name.replace('/', '-')}.tgz`)
|
|
495
|
+
|
|
496
|
+
// pack with bun (properly converts workspace:* to versions)
|
|
497
|
+
// use swap-exports for packages with build scripts, otherwise just pack
|
|
498
|
+
if (json.scripts?.build) {
|
|
499
|
+
await run(
|
|
500
|
+
`bun run build --swap-exports -- bun pm pack --filename ${tgzPath}`,
|
|
501
|
+
{
|
|
432
502
|
cwd,
|
|
433
503
|
silent: true,
|
|
504
|
+
},
|
|
505
|
+
)
|
|
506
|
+
} else {
|
|
507
|
+
await run(`bun pm pack --filename ${tgzPath}`, {
|
|
508
|
+
cwd,
|
|
509
|
+
silent: true,
|
|
510
|
+
})
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
let attempt = 0
|
|
514
|
+
let otp = cachedOtp
|
|
515
|
+
while (true) {
|
|
516
|
+
attempt++
|
|
517
|
+
try {
|
|
518
|
+
const otpOption = otp ? `--otp=${otp}` : ''
|
|
519
|
+
await run(`npm publish ${tgzPath} ${publishOptions} ${otpOption}`.trim(), {
|
|
520
|
+
cwd: packDir,
|
|
521
|
+
silent: true,
|
|
522
|
+
captureOutput: true,
|
|
523
|
+
env: otp ? { npm_config_otp: otp } : undefined,
|
|
434
524
|
})
|
|
525
|
+
publishedNames.push(name)
|
|
526
|
+
console.info(`${dryRun ? '[dry-run] ' : ''}Published ${name}`)
|
|
527
|
+
return
|
|
528
|
+
} catch (err) {
|
|
529
|
+
if (isPublishAuthOrOtpError(err) && attempt < 3) {
|
|
530
|
+
if (otp && cachedOtp === otp) cachedOtp = undefined
|
|
531
|
+
otp = await getOtp(
|
|
532
|
+
attempt === 1
|
|
533
|
+
? `npm requires a 2FA code to publish ${name}`
|
|
534
|
+
: `npm 2FA code expired, need a fresh one for ${name}`,
|
|
535
|
+
)
|
|
536
|
+
continue
|
|
537
|
+
}
|
|
538
|
+
if (rePublish) {
|
|
539
|
+
console.warn(
|
|
540
|
+
`⚠️ ${name}: publish failed (likely already published), continuing`,
|
|
541
|
+
)
|
|
542
|
+
return
|
|
543
|
+
}
|
|
544
|
+
console.error(`Failed to publish ${name}:`, getPublishErrorMessage(err))
|
|
545
|
+
failedPublishes.push(name)
|
|
546
|
+
return
|
|
435
547
|
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
436
550
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
551
|
+
const [firstPackage, ...restPackages] = packageJsons
|
|
552
|
+
if (firstPackage) {
|
|
553
|
+
await publishOne(firstPackage)
|
|
554
|
+
}
|
|
555
|
+
if (failedPublishes.length > 0) {
|
|
556
|
+
throw new Error(
|
|
557
|
+
`Failed to publish ${failedPublishes.length} packages:\n${failedPublishes.join('\n')}\n\nRe-run after fixing the publish error.`,
|
|
558
|
+
)
|
|
559
|
+
}
|
|
560
|
+
await pMap(restPackages, publishOne, {
|
|
561
|
+
concurrency: 15,
|
|
562
|
+
})
|
|
442
563
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
)
|
|
564
|
+
if (failedPublishes.length > 0) {
|
|
565
|
+
throw new Error(
|
|
566
|
+
`Failed to publish ${failedPublishes.length} packages:\n${failedPublishes.join('\n')}\n\nRe-run with --republish to retry.`,
|
|
567
|
+
)
|
|
568
|
+
}
|
|
449
569
|
|
|
570
|
+
publishSucceeded = true
|
|
450
571
|
console.info(`✅ ${dryRun ? '[dry-run] ' : ''}Published\n`)
|
|
451
572
|
|
|
452
573
|
// restore workspace:* protocols after publishing
|
|
@@ -523,6 +644,10 @@ await cmd`publish takeout packages to npm`
|
|
|
523
644
|
|
|
524
645
|
console.info(`✅ Done\n`)
|
|
525
646
|
} catch (err) {
|
|
647
|
+
if (!publishSucceeded && publishedNames.length === 0 && restorePackageJsons) {
|
|
648
|
+
await restorePackageJsons()
|
|
649
|
+
console.info('restored package.json files after failed release')
|
|
650
|
+
}
|
|
526
651
|
console.info('\nError:\n', err)
|
|
527
652
|
process.exit(1)
|
|
528
653
|
}
|