@take-out/scripts 0.4.0 → 0.4.1-1775377981053

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-1775377981053",
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-1775377981053",
33
+ "@take-out/run": "0.4.1-1775377981053",
34
34
  "picocolors": "^1.1.1"
35
35
  }
36
36
  }
@@ -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
+ }