@take-out/scripts 0.4.0 → 0.4.1-1775539666978

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@take-out/scripts",
3
- "version": "0.4.0",
3
+ "version": "0.4.1-1775539666978",
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.0",
33
- "@take-out/run": "0.4.0",
32
+ "@take-out/helpers": "0.4.1-1775539666978",
33
+ "@take-out/run": "0.4.1-1775539666978",
34
34
  "picocolors": "^1.1.1"
35
35
  }
36
36
  }
package/src/env-update.ts CHANGED
@@ -24,33 +24,27 @@ await cmd`sync environment variables from src/env.ts to matching files`
24
24
  .run(async ({ args }) => {
25
25
  // import env var definitions from src/env.ts (the single source of truth)
26
26
  const envModule = await import(path.resolve('src/env.ts'))
27
- const envVars = envModule.envVars as Record<string, string | symbol>
28
- const envDeps = (envModule.envDeps || {}) as Record<string, string>
27
+ const production = envModule.production as Record<string, string | symbol>
28
+ const versions = (envModule.versions || {}) as Record<string, string>
29
29
 
30
- if (!envVars || typeof envVars !== 'object') {
31
- console.error('no envVars export found in src/env.ts')
30
+ if (!production || typeof production !== 'object') {
31
+ console.error('no production export found in src/env.ts')
32
32
  process.exit(1)
33
33
  }
34
34
 
35
35
  // the expected symbol from @take-out/env
36
36
  const expectedSymbol = Symbol.for('take-out/env/expected')
37
37
 
38
- // resolve dep versions from package.json dependencies
38
+ // merge resolved versions into the var registry
39
39
  const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'))
40
- const depResolved: Record<string, string> = {}
41
- for (const [key, depName] of Object.entries(envDeps)) {
42
- const version = packageJson.dependencies?.[depName]?.replace(/^[\^~]/, '')
43
- if (version) {
44
- depResolved[key] = version
45
- // add dep-resolved vars to envVars with resolved value
46
- envVars[key] = version
47
- }
40
+ const depResolved = { ...versions }
41
+ for (const [key, value] of Object.entries(versions)) {
42
+ production[key] = value
48
43
  }
49
44
 
50
- // normalize envVars: expected symbol → true (required), string → string
51
- // for compatibility with strategy functions
45
+ // normalize: expected symbol → required, string → defaultValue
52
46
  type EnvEntry = { key: string; required: boolean; defaultValue: string }
53
- const entries: EnvEntry[] = Object.entries(envVars)
47
+ const entries: EnvEntry[] = Object.entries(production)
54
48
  .map(([key, value]) => ({
55
49
  key,
56
50
  required: value === expectedSymbol,
@@ -56,3 +56,53 @@ export async function releaseDeployLock(
56
56
  // best-effort, lock will auto-expire
57
57
  }
58
58
  }
59
+
60
+ /**
61
+ * run `fn` with the deploy lock held, releasing on ANY exit path —
62
+ * normal return, thrown error, or process signal (SIGTERM from CI cancel,
63
+ * SIGINT from ctrl-c, SIGHUP from terminal close).
64
+ *
65
+ * prefer this over raw acquire/release: a bare try/finally does NOT catch
66
+ * signals, so a CI-cancelled deploy would leak the lock for 15 minutes and
67
+ * block the next run. this helper installs signal handlers that release
68
+ * before exit, then removes them once `fn` completes.
69
+ */
70
+ export async function withDeployLock<T>(
71
+ ssh: string,
72
+ fn: () => Promise<T>,
73
+ opts?: DeployLockOptions,
74
+ ): Promise<T> {
75
+ await acquireDeployLock(ssh, opts)
76
+
77
+ let released = false
78
+ const release = async () => {
79
+ if (released) return
80
+ released = true
81
+ await releaseDeployLock(ssh, opts)
82
+ }
83
+
84
+ // signal handlers — fire-and-forget release with a safety timeout so the
85
+ // process always exits even if the ssh release hangs
86
+ const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT', 'SIGHUP']
87
+ const handlers = new Map<NodeJS.Signals, () => void>()
88
+ for (const sig of signals) {
89
+ const handler = () => {
90
+ const done = release().catch(() => {})
91
+ const timeout = new Promise<void>((r) => setTimeout(r, 5_000))
92
+ Promise.race([done, timeout]).finally(() => {
93
+ process.exit(sig === 'SIGINT' ? 130 : 143)
94
+ })
95
+ }
96
+ handlers.set(sig, handler)
97
+ process.once(sig, handler)
98
+ }
99
+
100
+ try {
101
+ return await fn()
102
+ } finally {
103
+ for (const [sig, handler] of handlers) {
104
+ process.off(sig, handler)
105
+ }
106
+ await release()
107
+ }
108
+ }