@take-out/scripts 0.0.93 → 0.0.95
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 +6 -4
- package/src/build-initial.ts +76 -81
- package/src/clean.ts +21 -21
- package/src/cmd.ts +85 -0
- package/src/dev-tunnel.ts +141 -159
- package/src/ensure-port.ts +62 -70
- package/src/ensure-tunnel.ts +13 -9
- package/src/env-pull.ts +49 -47
- package/src/env-update.ts +140 -175
- package/src/exec-with-env.ts +14 -11
- package/src/helpers/args.ts +4 -4
- package/src/helpers/get-test-env.ts +5 -3
- package/src/node-version-check.ts +9 -5
- package/src/release.ts +429 -404
- package/src/sst-get-environment.ts +5 -1
- package/src/typecheck.ts +15 -16
- package/src/up.ts +361 -374
- package/src/update-changelog.ts +39 -43
- package/src/update-local-env.ts +139 -158
- package/src/wait-for-dev.ts +21 -20
package/src/update-changelog.ts
CHANGED
|
@@ -1,61 +1,56 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
* @description Update changelog.mdx with recent git commits using Claude Code
|
|
5
|
-
*
|
|
6
|
-
* outputs a prompt for claude code to investigate commits and update changelog
|
|
7
|
-
* run: bun tko update-changelog
|
|
8
|
-
*/
|
|
3
|
+
import { cmd } from './cmd'
|
|
9
4
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
await cmd`update changelog with recent git commits`.run(async ({ path }) => {
|
|
6
|
+
const { execSync } = await import('node:child_process')
|
|
7
|
+
const { existsSync, readFileSync } = await import('node:fs')
|
|
13
8
|
|
|
14
|
-
const CHANGELOG_PATH = join(process.cwd(), 'src/features/site/docs/changelog.mdx')
|
|
9
|
+
const CHANGELOG_PATH = path.join(process.cwd(), 'src/features/site/docs/changelog.mdx')
|
|
15
10
|
|
|
16
|
-
function getLastSha(): string | null {
|
|
17
|
-
|
|
11
|
+
function getLastSha(): string | null {
|
|
12
|
+
if (!existsSync(CHANGELOG_PATH)) return null
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
try {
|
|
15
|
+
const content = readFileSync(CHANGELOG_PATH, 'utf-8')
|
|
16
|
+
const match = content.match(/\{\/\* last updated: ([a-f0-9]+) \*\/\}/)
|
|
17
|
+
return match?.[1] || null
|
|
18
|
+
} catch {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
25
21
|
}
|
|
26
|
-
}
|
|
27
22
|
|
|
28
|
-
function getLatestSha(): string {
|
|
29
|
-
|
|
30
|
-
}
|
|
23
|
+
function getLatestSha(): string {
|
|
24
|
+
return execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).trim()
|
|
25
|
+
}
|
|
31
26
|
|
|
32
|
-
function getCommitCount(fromSha: string, toSha: string): number {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
function getCommitCount(fromSha: string, toSha: string): number {
|
|
28
|
+
try {
|
|
29
|
+
const result = execSync(`git rev-list --count ${fromSha}..${toSha} --no-merges`, {
|
|
30
|
+
encoding: 'utf-8',
|
|
31
|
+
})
|
|
32
|
+
return parseInt(result.trim(), 10)
|
|
33
|
+
} catch {
|
|
34
|
+
return 0
|
|
35
|
+
}
|
|
40
36
|
}
|
|
41
|
-
}
|
|
42
37
|
|
|
43
|
-
const lastSha = getLastSha()
|
|
44
|
-
const latestSha = getLatestSha()
|
|
38
|
+
const lastSha = getLastSha()
|
|
39
|
+
const latestSha = getLatestSha()
|
|
45
40
|
|
|
46
|
-
if (!lastSha) {
|
|
47
|
-
|
|
48
|
-
}
|
|
41
|
+
if (!lastSha) {
|
|
42
|
+
console.info(`no last sha found in changelog, defaulting to 4 weeks ago`)
|
|
43
|
+
}
|
|
49
44
|
|
|
50
|
-
const fromRef = lastSha || '$(git log --since="4 weeks ago" --format="%H" | tail -1)'
|
|
51
|
-
const commitCount = lastSha ? getCommitCount(lastSha, latestSha) : '~30'
|
|
45
|
+
const fromRef = lastSha || '$(git log --since="4 weeks ago" --format="%H" | tail -1)'
|
|
46
|
+
const commitCount = lastSha ? getCommitCount(lastSha, latestSha) : '~30'
|
|
52
47
|
|
|
53
|
-
if (commitCount === 0) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
48
|
+
if (commitCount === 0) {
|
|
49
|
+
console.info('no new commits since last update')
|
|
50
|
+
process.exit(0)
|
|
51
|
+
}
|
|
57
52
|
|
|
58
|
-
console.info(`
|
|
53
|
+
console.info(`
|
|
59
54
|
Update the changelog at: ${CHANGELOG_PATH}
|
|
60
55
|
|
|
61
56
|
Commit range: ${fromRef}..${latestSha} (${commitCount} commits)
|
|
@@ -77,3 +72,4 @@ Archiving old entries:
|
|
|
77
72
|
- when over 10 weeks, move oldest entries to changelog-YYYY.mdx (by year, e.g. changelog-2025.mdx)
|
|
78
73
|
- add a link at the bottom of changelog.mdx: "See [2025 changes](/docs/changelog-2025)" etc (use absolute path)
|
|
79
74
|
`)
|
|
75
|
+
})
|
package/src/update-local-env.ts
CHANGED
|
@@ -1,183 +1,164 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
3
|
+
import { cmd } from './cmd'
|
|
4
|
+
|
|
5
|
+
await cmd`sync auto-generated env vars to local .env file`.run(async ({ path }) => {
|
|
6
|
+
const {
|
|
7
|
+
existsSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
copyFileSync,
|
|
11
|
+
renameSync,
|
|
12
|
+
unlinkSync,
|
|
13
|
+
} = await import('node:fs')
|
|
14
|
+
const { getZeroVersion } = await import('./helpers/zero-get-version')
|
|
15
|
+
|
|
16
|
+
// skip in CI environments
|
|
17
|
+
if (process.env.CI === 'true') {
|
|
18
|
+
console.info('Skipping bootstrap in CI environment')
|
|
19
|
+
process.exit(0)
|
|
44
20
|
}
|
|
45
21
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
} catch (error) {
|
|
51
|
-
console.error('Failed to create .env from .env.template:', error)
|
|
52
|
-
return false
|
|
53
|
-
}
|
|
54
|
-
}
|
|
22
|
+
const ENV_PATH = path.join(process.cwd(), '.env')
|
|
23
|
+
const ENV_TEMPLATE_PATH = path.join(process.cwd(), '.env.template')
|
|
24
|
+
const ENV_BACKUP_PATH = path.join(process.cwd(), '.env.backup')
|
|
25
|
+
const ENV_TEMP_PATH = path.join(process.cwd(), '.env.tmp')
|
|
55
26
|
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
console.warn('Could not determine Zero version')
|
|
60
|
-
return ''
|
|
61
|
-
}
|
|
27
|
+
// auto-generated section markers
|
|
28
|
+
const BEGIN_MARKER = '# ---- BEGIN AUTO-GENERATED (DO NOT EDIT) ----'
|
|
29
|
+
const END_MARKER = '# ---- END AUTO-GENERATED ----'
|
|
62
30
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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')
|
|
31
|
+
function createEnvFromTemplate(): boolean {
|
|
32
|
+
if (!existsSync(ENV_TEMPLATE_PATH)) {
|
|
33
|
+
console.info('No .env.template found, skipping .env creation')
|
|
34
|
+
return false
|
|
82
35
|
}
|
|
83
|
-
}
|
|
84
36
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
37
|
+
try {
|
|
38
|
+
copyFileSync(ENV_TEMPLATE_PATH, ENV_PATH)
|
|
39
|
+
console.info('Created .env from .env.template')
|
|
40
|
+
return true
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error('Failed to create .env from .env.template:', error)
|
|
43
|
+
return false
|
|
89
44
|
}
|
|
45
|
+
}
|
|
90
46
|
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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()
|
|
47
|
+
function getAutoGeneratedContent(): string {
|
|
48
|
+
const zeroVersion = getZeroVersion()
|
|
49
|
+
if (!zeroVersion) {
|
|
50
|
+
console.warn('Could not determine Zero version')
|
|
51
|
+
return ''
|
|
120
52
|
}
|
|
121
53
|
|
|
122
|
-
|
|
123
|
-
|
|
54
|
+
const lines = [
|
|
55
|
+
BEGIN_MARKER,
|
|
56
|
+
`# Generated at: ${new Date().toISOString()}`,
|
|
57
|
+
`ZERO_VERSION=${zeroVersion}`,
|
|
58
|
+
END_MARKER,
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
return lines.join('\n')
|
|
62
|
+
}
|
|
124
63
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (!
|
|
128
|
-
|
|
64
|
+
function updateEnvFile(): void {
|
|
65
|
+
// ensure .env exists
|
|
66
|
+
if (!existsSync(ENV_PATH)) {
|
|
67
|
+
const created = createEnvFromTemplate()
|
|
68
|
+
if (!created && !existsSync(ENV_PATH)) {
|
|
69
|
+
writeFileSync(ENV_PATH, '')
|
|
70
|
+
console.info('Created empty .env file')
|
|
71
|
+
}
|
|
129
72
|
}
|
|
130
73
|
|
|
131
|
-
|
|
132
|
-
|
|
74
|
+
try {
|
|
75
|
+
// create backup
|
|
76
|
+
if (existsSync(ENV_PATH)) {
|
|
77
|
+
copyFileSync(ENV_PATH, ENV_BACKUP_PATH)
|
|
78
|
+
}
|
|
133
79
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
80
|
+
const currentContent = readFileSync(ENV_PATH, 'utf-8')
|
|
81
|
+
|
|
82
|
+
const beginIndex = currentContent.indexOf(BEGIN_MARKER)
|
|
83
|
+
const endIndex = currentContent.indexOf(END_MARKER)
|
|
84
|
+
|
|
85
|
+
let newContent: string
|
|
86
|
+
|
|
87
|
+
if (beginIndex !== -1 && endIndex !== -1 && endIndex > beginIndex) {
|
|
88
|
+
// replace existing auto-generated section
|
|
89
|
+
const beforeSection = currentContent.substring(0, beginIndex).trimEnd()
|
|
90
|
+
const afterSection = currentContent
|
|
91
|
+
.substring(endIndex + END_MARKER.length)
|
|
92
|
+
.trimStart()
|
|
93
|
+
|
|
94
|
+
newContent = [beforeSection, getAutoGeneratedContent(), afterSection]
|
|
95
|
+
.filter(Boolean)
|
|
96
|
+
.join('\n\n')
|
|
97
|
+
} else if (beginIndex !== -1 || endIndex !== -1) {
|
|
98
|
+
// malformed markers - preserve content and append new section
|
|
99
|
+
console.warn('Found malformed auto-generated section, appending new section')
|
|
100
|
+
newContent = currentContent.trimEnd() + '\n\n' + getAutoGeneratedContent()
|
|
101
|
+
} else {
|
|
102
|
+
// no existing section - append to end
|
|
103
|
+
const trimmedContent = currentContent.trimEnd()
|
|
104
|
+
newContent = trimmedContent
|
|
105
|
+
? trimmedContent + '\n\n' + getAutoGeneratedContent()
|
|
106
|
+
: getAutoGeneratedContent()
|
|
139
107
|
}
|
|
140
|
-
}
|
|
141
108
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
109
|
+
// write to temp file first (atomic operation)
|
|
110
|
+
writeFileSync(ENV_TEMP_PATH, newContent)
|
|
111
|
+
|
|
112
|
+
// validate temp file
|
|
113
|
+
const tempContent = readFileSync(ENV_TEMP_PATH, 'utf-8')
|
|
114
|
+
if (!tempContent.includes(BEGIN_MARKER) || !tempContent.includes(END_MARKER)) {
|
|
115
|
+
throw new Error('Generated content validation failed')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// atomic replace
|
|
119
|
+
renameSync(ENV_TEMP_PATH, ENV_PATH)
|
|
120
|
+
|
|
121
|
+
if (existsSync(ENV_BACKUP_PATH)) {
|
|
122
|
+
try {
|
|
123
|
+
unlinkSync(ENV_BACKUP_PATH)
|
|
124
|
+
} catch {
|
|
125
|
+
// ignore cleanup errors
|
|
126
|
+
}
|
|
153
127
|
}
|
|
154
|
-
}
|
|
155
128
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
129
|
+
console.info('Updated .env auto-generated section')
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error('Failed to update .env file:', error)
|
|
132
|
+
|
|
133
|
+
// attempt to restore backup
|
|
134
|
+
if (existsSync(ENV_BACKUP_PATH)) {
|
|
135
|
+
try {
|
|
136
|
+
copyFileSync(ENV_BACKUP_PATH, ENV_PATH)
|
|
137
|
+
console.info('Restored .env from backup')
|
|
138
|
+
} catch (restoreError) {
|
|
139
|
+
console.error('Failed to restore backup:', restoreError)
|
|
140
|
+
}
|
|
162
141
|
}
|
|
142
|
+
|
|
143
|
+
// clean up temp file
|
|
144
|
+
if (existsSync(ENV_TEMP_PATH)) {
|
|
145
|
+
try {
|
|
146
|
+
unlinkSync(ENV_TEMP_PATH)
|
|
147
|
+
} catch {
|
|
148
|
+
// ignore cleanup errors
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// don't fail the install process
|
|
153
|
+
process.exit(0)
|
|
163
154
|
}
|
|
155
|
+
}
|
|
164
156
|
|
|
165
|
-
|
|
157
|
+
try {
|
|
158
|
+
updateEnvFile()
|
|
159
|
+
} catch (error) {
|
|
160
|
+
// catch any unexpected errors and exit gracefully
|
|
161
|
+
console.error('Bootstrap script error:', error)
|
|
166
162
|
process.exit(0)
|
|
167
163
|
}
|
|
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
|
-
}
|
|
164
|
+
})
|
package/src/wait-for-dev.ts
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
3
|
+
import { cmd } from './cmd'
|
|
4
|
+
|
|
5
|
+
await cmd`wait for dev server to be available`.run(async () => {
|
|
6
|
+
const { sleep } = await import('@take-out/helpers')
|
|
7
|
+
|
|
8
|
+
const ONE_SERVER_URL = process.env.ONE_SERVER_URL || 'http://localhost:8081'
|
|
9
|
+
const CHECK_INTERVAL = 2000
|
|
10
|
+
|
|
11
|
+
async function checkServer(): Promise<boolean> {
|
|
12
|
+
try {
|
|
13
|
+
await fetch(ONE_SERVER_URL, {
|
|
14
|
+
signal: AbortSignal.timeout(5000),
|
|
15
|
+
}).then((res) => res.text())
|
|
16
|
+
// give it a couple seconds to build initial route
|
|
17
|
+
await sleep(2000)
|
|
18
|
+
return true
|
|
19
|
+
} catch (error) {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
18
22
|
}
|
|
19
|
-
}
|
|
20
23
|
|
|
21
|
-
async function waitForServer(): Promise<void> {
|
|
22
24
|
process.stdout.write(`Waiting for server at ${ONE_SERVER_URL}...\n`)
|
|
23
25
|
|
|
24
26
|
while (true) {
|
|
@@ -34,7 +36,6 @@ async function waitForServer(): Promise<void> {
|
|
|
34
36
|
`Waiting to start dev watch until after dev server (wait ${CHECK_INTERVAL / 1000}s)...\n`
|
|
35
37
|
)
|
|
36
38
|
}
|
|
37
|
-
}
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
process.exit(0)
|
|
41
|
+
})
|