@take-out/scripts 0.0.38 → 0.0.39
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 +2 -2
- package/src/build-initial.ts +104 -0
- package/src/update-local-env.ts +183 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@take-out/scripts",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.39",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@take-out/helpers": "0.0.
|
|
27
|
+
"@take-out/helpers": "0.0.39"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"vxrn": "*"
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @description Bootstrap project workspace and build initial packages. This is
|
|
5
|
+
* mostly important for Takeout starter kit itself.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, symlinkSync } from 'node:fs'
|
|
9
|
+
import { exists } from 'node:fs/promises'
|
|
10
|
+
import { join } from 'node:path'
|
|
11
|
+
|
|
12
|
+
import { $ } from 'bun'
|
|
13
|
+
|
|
14
|
+
const hasPackages = await exists(`./packages`)
|
|
15
|
+
|
|
16
|
+
// only run once:
|
|
17
|
+
if (!(await exists(`./node_modules/.bin/tko`))) {
|
|
18
|
+
if (hasPackages) {
|
|
19
|
+
symlinkBins()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// check if critical packages are built - both helpers and takeout are needed for tko to work
|
|
24
|
+
if (hasPackages) {
|
|
25
|
+
const needsBuild =
|
|
26
|
+
!(await exists(`./packages/helpers/dist`)) ||
|
|
27
|
+
!(await exists(`./packages/takeout/dist/esm`))
|
|
28
|
+
|
|
29
|
+
if (needsBuild) {
|
|
30
|
+
// build helpers first as other packages depend on it
|
|
31
|
+
await $`cd packages/helpers && bun run build`
|
|
32
|
+
|
|
33
|
+
// then build all other packages in parallel
|
|
34
|
+
await $`bun ./packages/scripts/src/run.ts build --no-root`
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// show welcome message if not onboarded
|
|
39
|
+
checkAndShowWelcome()
|
|
40
|
+
|
|
41
|
+
function checkAndShowWelcome(cwd: string = process.cwd()): void {
|
|
42
|
+
try {
|
|
43
|
+
const packagePath = join(cwd, 'package.json')
|
|
44
|
+
const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'))
|
|
45
|
+
|
|
46
|
+
if (pkg.takeout?.onboarded === false) {
|
|
47
|
+
console.info()
|
|
48
|
+
console.info(`
|
|
49
|
+
████████╗ █████╗ ██╗ ██╗███████╗ ██████╗ ██╗ ██╗████████╗
|
|
50
|
+
╚══██╔══╝██╔══██╗██║ ██╔╝██╔════╝██╔═══██╗██║ ██║╚══██╔══╝
|
|
51
|
+
██║ ███████║█████╔╝ █████╗ ██║ ██║██║ ██║ ██║
|
|
52
|
+
██║ ██╔══██║██╔═██╗ ██╔══╝ ██║ ██║██║ ██║ ██║
|
|
53
|
+
██║ ██║ ██║██║ ██╗███████╗╚██████╔╝╚██████╔╝ ██║
|
|
54
|
+
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝
|
|
55
|
+
麵 碼 飯
|
|
56
|
+
`)
|
|
57
|
+
console.info()
|
|
58
|
+
console.info(' welcome to takeout! 🥡')
|
|
59
|
+
console.info()
|
|
60
|
+
console.info(' run \x1b[32mbun onboard\x1b[0m to get things set up')
|
|
61
|
+
console.info()
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// silently fail if package.json doesn't exist or is malformed
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function symlinkBins() {
|
|
69
|
+
// workaround for https://github.com/oven-sh/bun/issues/19782
|
|
70
|
+
// bun doesn't create symlinks for workspace packages properly
|
|
71
|
+
const packagesWithCLI = [
|
|
72
|
+
{ name: 'takeout', cliFile: 'cli.mjs', alias: 'tko' },
|
|
73
|
+
{ name: 'postgres', cliFile: 'cli.cjs' },
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
const binDir = join(process.cwd(), 'node_modules', '.bin')
|
|
77
|
+
|
|
78
|
+
if (!existsSync(binDir)) {
|
|
79
|
+
mkdirSync(binDir, { recursive: true })
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
for (const pkg of packagesWithCLI) {
|
|
83
|
+
const binPath = join(binDir, pkg.name)
|
|
84
|
+
const sourcePath = join(process.cwd(), 'packages', pkg.name, pkg.cliFile)
|
|
85
|
+
symlinkTo(sourcePath, binPath)
|
|
86
|
+
|
|
87
|
+
if (pkg.alias) {
|
|
88
|
+
const aliasPath = join(binDir, pkg.alias)
|
|
89
|
+
// create alias symlink pointing to the source
|
|
90
|
+
symlinkTo(sourcePath, aliasPath)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// helper function to create symlink with existence check
|
|
95
|
+
function symlinkTo(source: string, target: string): void {
|
|
96
|
+
if (existsSync(target)) {
|
|
97
|
+
console.info(`✓ Symlink already exists: ${target}`)
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
symlinkSync(source, target)
|
|
102
|
+
console.info(`→ Created symlink: ${source} ⇢ ${target}`)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @description Bootstrap project dependencies and workspace packages
|
|
5
|
+
*
|
|
6
|
+
* This script runs automatically during `bun install` via the prepare lifecycle.
|
|
7
|
+
* It manages the .env file configuration with the following responsibilities:
|
|
8
|
+
*
|
|
9
|
+
* 1. Creates .env from .env.template if it doesn't exist
|
|
10
|
+
* 2. Maintains an auto-generated section in .env with package metadata
|
|
11
|
+
* 3. Currently syncs ZERO_VERSION from package.json dependencies
|
|
12
|
+
* 4. Preserves all user-defined environment variables
|
|
13
|
+
* 5. Creates backups before modifications for safety
|
|
14
|
+
*
|
|
15
|
+
* The auto-generated section is clearly marked and should not be edited manually.
|
|
16
|
+
* All operations are defensive and will not fail the install process.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
existsSync,
|
|
21
|
+
readFileSync,
|
|
22
|
+
writeFileSync,
|
|
23
|
+
copyFileSync,
|
|
24
|
+
renameSync,
|
|
25
|
+
unlinkSync,
|
|
26
|
+
} from 'node:fs'
|
|
27
|
+
import { join } from 'node:path'
|
|
28
|
+
|
|
29
|
+
import { getZeroVersion } from '@take-out/scripts/helpers/zero-get-version'
|
|
30
|
+
|
|
31
|
+
const ENV_PATH = join(process.cwd(), '.env')
|
|
32
|
+
const ENV_TEMPLATE_PATH = join(process.cwd(), '.env.template')
|
|
33
|
+
const ENV_BACKUP_PATH = join(process.cwd(), '.env.backup')
|
|
34
|
+
const ENV_TEMP_PATH = join(process.cwd(), '.env.tmp')
|
|
35
|
+
|
|
36
|
+
// auto-generated section markers
|
|
37
|
+
const BEGIN_MARKER = '# ---- BEGIN AUTO-GENERATED (DO NOT EDIT) ----'
|
|
38
|
+
const END_MARKER = '# ---- END AUTO-GENERATED ----'
|
|
39
|
+
|
|
40
|
+
function createEnvFromTemplate(): boolean {
|
|
41
|
+
if (!existsSync(ENV_TEMPLATE_PATH)) {
|
|
42
|
+
console.info('No .env.template found, skipping .env creation')
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
copyFileSync(ENV_TEMPLATE_PATH, ENV_PATH)
|
|
48
|
+
console.info('Created .env from .env.template')
|
|
49
|
+
return true
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error('Failed to create .env from .env.template:', error)
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getAutoGeneratedContent(): string {
|
|
57
|
+
const zeroVersion = getZeroVersion()
|
|
58
|
+
if (!zeroVersion) {
|
|
59
|
+
console.warn('Could not determine Zero version')
|
|
60
|
+
return ''
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// build the auto-generated content
|
|
64
|
+
const lines = [
|
|
65
|
+
BEGIN_MARKER,
|
|
66
|
+
`# Generated at: ${new Date().toISOString()}`,
|
|
67
|
+
`ZERO_VERSION=${zeroVersion}`,
|
|
68
|
+
END_MARKER,
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
return lines.join('\n')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function updateEnvFile(): void {
|
|
75
|
+
// ensure .env exists
|
|
76
|
+
if (!existsSync(ENV_PATH)) {
|
|
77
|
+
const created = createEnvFromTemplate()
|
|
78
|
+
if (!created && !existsSync(ENV_PATH)) {
|
|
79
|
+
// create empty .env if no template exists
|
|
80
|
+
writeFileSync(ENV_PATH, '')
|
|
81
|
+
console.info('Created empty .env file')
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
// create backup
|
|
87
|
+
if (existsSync(ENV_PATH)) {
|
|
88
|
+
copyFileSync(ENV_PATH, ENV_BACKUP_PATH)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// read current content
|
|
92
|
+
const currentContent = readFileSync(ENV_PATH, 'utf-8')
|
|
93
|
+
|
|
94
|
+
// find existing auto-generated section
|
|
95
|
+
const beginIndex = currentContent.indexOf(BEGIN_MARKER)
|
|
96
|
+
const endIndex = currentContent.indexOf(END_MARKER)
|
|
97
|
+
|
|
98
|
+
let newContent: string
|
|
99
|
+
|
|
100
|
+
if (beginIndex !== -1 && endIndex !== -1 && endIndex > beginIndex) {
|
|
101
|
+
// replace existing auto-generated section
|
|
102
|
+
const beforeSection = currentContent.substring(0, beginIndex).trimEnd()
|
|
103
|
+
const afterSection = currentContent
|
|
104
|
+
.substring(endIndex + END_MARKER.length)
|
|
105
|
+
.trimStart()
|
|
106
|
+
|
|
107
|
+
newContent = [beforeSection, getAutoGeneratedContent(), afterSection]
|
|
108
|
+
.filter(Boolean)
|
|
109
|
+
.join('\n\n')
|
|
110
|
+
} else if (beginIndex !== -1 || endIndex !== -1) {
|
|
111
|
+
// malformed markers - preserve content and append new section
|
|
112
|
+
console.warn('Found malformed auto-generated section, appending new section')
|
|
113
|
+
newContent = currentContent.trimEnd() + '\n\n' + getAutoGeneratedContent()
|
|
114
|
+
} else {
|
|
115
|
+
// no existing section - append to end
|
|
116
|
+
const trimmedContent = currentContent.trimEnd()
|
|
117
|
+
newContent = trimmedContent
|
|
118
|
+
? trimmedContent + '\n\n' + getAutoGeneratedContent()
|
|
119
|
+
: getAutoGeneratedContent()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// write to temp file first (atomic operation)
|
|
123
|
+
writeFileSync(ENV_TEMP_PATH, newContent)
|
|
124
|
+
|
|
125
|
+
// validate temp file
|
|
126
|
+
const tempContent = readFileSync(ENV_TEMP_PATH, 'utf-8')
|
|
127
|
+
if (!tempContent.includes(BEGIN_MARKER) || !tempContent.includes(END_MARKER)) {
|
|
128
|
+
throw new Error('Generated content validation failed')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// atomic replace
|
|
132
|
+
renameSync(ENV_TEMP_PATH, ENV_PATH)
|
|
133
|
+
|
|
134
|
+
if (existsSync(ENV_BACKUP_PATH)) {
|
|
135
|
+
try {
|
|
136
|
+
unlinkSync(ENV_BACKUP_PATH)
|
|
137
|
+
} catch {
|
|
138
|
+
// ignore cleanup errors
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.info('Updated .env auto-generated section')
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error('Failed to update .env file:', error)
|
|
145
|
+
|
|
146
|
+
// attempt to restore backup
|
|
147
|
+
if (existsSync(ENV_BACKUP_PATH)) {
|
|
148
|
+
try {
|
|
149
|
+
copyFileSync(ENV_BACKUP_PATH, ENV_PATH)
|
|
150
|
+
console.info('Restored .env from backup')
|
|
151
|
+
} catch (restoreError) {
|
|
152
|
+
console.error('Failed to restore backup:', restoreError)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// clean up temp file
|
|
157
|
+
if (existsSync(ENV_TEMP_PATH)) {
|
|
158
|
+
try {
|
|
159
|
+
unlinkSync(ENV_TEMP_PATH)
|
|
160
|
+
} catch {
|
|
161
|
+
// ignore cleanup errors
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// don't fail the install process
|
|
166
|
+
process.exit(0)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// main execution
|
|
171
|
+
// skip bootstrap in CI environments
|
|
172
|
+
if (process.env.CI === 'true') {
|
|
173
|
+
console.info('Skipping bootstrap in CI environment')
|
|
174
|
+
process.exit(0)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
updateEnvFile()
|
|
179
|
+
} catch (error) {
|
|
180
|
+
// catch any unexpected errors and exit gracefully
|
|
181
|
+
console.error('Bootstrap script error:', error)
|
|
182
|
+
process.exit(0)
|
|
183
|
+
}
|