rollbridge 0.1.10 → 0.1.11

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": "rollbridge",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Zero-downtime process supervisor and local traffic switcher for deploy-managed apps.",
5
5
  "keywords": [
6
6
  "deploy",
@@ -27,10 +27,11 @@
27
27
  },
28
28
  "scripts": {
29
29
  "all-checks": "npm run typecheck && npm run lint && npm test",
30
- "lint": "eslint",
30
+ "lint": "npm run eslint && npm run typecheck",
31
31
  "release:patch": "release-patch",
32
32
  "test": "node --test test/*.test.js",
33
- "typecheck": "tsc --noEmit"
33
+ "typecheck": "tsc --noEmit",
34
+ "eslint": "eslint"
34
35
  },
35
36
  "engines": {
36
37
  "node": ">=20"
@@ -91,8 +91,9 @@ export default class ReleaseGroup extends EventEmitter {
91
91
  }
92
92
  } catch (error) {
93
93
  this.state = "failed"
94
- this.logStartupFailure(error instanceof Error ? error : String(error))
94
+ this.logStartupFailure(error instanceof Error ? error : String(error), {phase: "before cleanup"})
95
95
  await this.stop()
96
+ this.logStartupFailure(error instanceof Error ? error : String(error), {phase: "after cleanup"})
96
97
  throw error
97
98
  }
98
99
  }
@@ -125,13 +126,15 @@ export default class ReleaseGroup extends EventEmitter {
125
126
  }
126
127
 
127
128
  /**
128
- * Logs process diagnostics before failed startup cleanup stops and removes the release processes.
129
+ * Logs process diagnostics around failed startup cleanup.
129
130
  * @param {Error | string} error - Startup failure.
131
+ * @param {{phase: string}} options - Diagnostic phase.
130
132
  * @returns {void}
131
133
  */
132
- logStartupFailure(error) {
134
+ logStartupFailure(error, {phase}) {
133
135
  this.logger("release startup failed", {
134
136
  error: error instanceof Error ? error.message : error,
137
+ phase,
135
138
  releaseId: this.releaseId
136
139
  })
137
140
 
@@ -143,6 +146,7 @@ export default class ReleaseGroup extends EventEmitter {
143
146
  exitCode: status.exitCode ?? null,
144
147
  exitSignal: status.exitSignal ?? null,
145
148
  logs: status.logs,
149
+ phase,
146
150
  pid: status.pid ?? null,
147
151
  processId: status.id,
148
152
  releaseId: this.releaseId,
@@ -123,8 +123,8 @@ test("wildcard proxy bind host targets release processes through loopback", asyn
123
123
  }
124
124
  })
125
125
 
126
- test("failed release startup logs process output before cleanup", async () => {
127
- const fixture = await createFixture({webCommand: `${JSON.stringify(process.execPath)} -e "console.log('startup stdout'); console.error('startup stderr'); const http = require('node:http'); http.createServer((_request, response) => { response.writeHead(500); response.end('bad') }).listen(Number(process.env.ROLLBRIDGE_PORT), '127.0.0.1')"`, webHealthTimeoutMs: 500})
126
+ test("failed release startup logs process output and cleanup status", async () => {
127
+ const fixture = await createFixture({handoffService: true, webCommand: `${JSON.stringify(process.execPath)} -e "console.log('startup stdout'); console.error('startup stderr'); const http = require('node:http'); http.createServer((_request, response) => { response.writeHead(500); response.end('bad') }).listen(Number(process.env.ROLLBRIDGE_PORT), '127.0.0.1')"`, webHealthTimeoutMs: 500})
128
128
  /** @type {Array<{data?: Record<string, import("../src/json.js").JsonValue>, message: string}>} */
129
129
  const logs = []
130
130
  const daemon = new RollbridgeDaemon({
@@ -140,13 +140,22 @@ test("failed release startup logs process output before cleanup", async () => {
140
140
  /Health check failed/
141
141
  )
142
142
 
143
- const processStatusLog = logs.find((entry) => entry.message === "release startup process status" && entry.data?.processId === "web")
143
+ const processStatusLog = logs.find((entry) => entry.message === "release startup process status" && entry.data?.phase === "before cleanup" && entry.data?.processId === "web")
144
+ const cleanupProcessStatusLog = logs.find((entry) => entry.message === "release startup process status" && entry.data?.phase === "after cleanup" && entry.data?.processId === "web")
145
+ const handoffServiceStatusLog = logs.find((entry) => entry.message === "release startup process status" && entry.data?.phase === "after cleanup" && entry.data?.processId === "beacon")
144
146
 
145
147
  assert.ok(processStatusLog, "expected failed web process diagnostics to be logged")
146
148
  assert.ok(processStatusLog.data, "expected diagnostic data")
147
149
  assert.ok(Array.isArray(processStatusLog.data.logs), "expected retained process output in diagnostics")
148
150
  assert.ok(processStatusLog.data.logs.some((entry) => typeof entry === "object" && entry && "line" in entry && entry.line === "startup stdout"))
149
151
  assert.ok(processStatusLog.data.logs.some((entry) => typeof entry === "object" && entry && "line" in entry && entry.line === "startup stderr"))
152
+ assert.equal(processStatusLog.data.state, "running")
153
+ assert.ok(cleanupProcessStatusLog, "expected failed web cleanup diagnostics to be logged")
154
+ assert.equal(cleanupProcessStatusLog.data?.state, "stopped")
155
+ assert.equal(cleanupProcessStatusLog.data?.exitSignal, "SIGTERM")
156
+ assert.ok(handoffServiceStatusLog, "expected handoff service cleanup diagnostics to be logged")
157
+ assert.equal(handoffServiceStatusLog.data?.state, "stopped")
158
+ assert.equal(handoffServiceStatusLog.data?.exitSignal, "SIGTERM")
150
159
  } finally {
151
160
  await daemon.shutdown()
152
161
  await fs.rm(fixture.root, {force: true, recursive: true})