flightdeck 0.1.2 → 0.2.0

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.
@@ -198,8 +198,7 @@ class FlightDeck {
198
198
  }
199
199
  this.getLatestRelease();
200
200
  if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
201
- this.logger.info(`All services are ready to update!`);
202
- this.stopAllServices();
201
+ this.tryUpdate();
203
202
  return;
204
203
  }
205
204
  for (const entry of toEntries(this.services)) {
@@ -236,13 +235,40 @@ class FlightDeck {
236
235
  this.logger.info(`Server started on port ${port}`);
237
236
  });
238
237
  }
239
- this.startAllServices();
238
+ this.startAllServices().then(() => {
239
+ this.logger.info(`All services started.`);
240
+ }).catch((thrown) => {
241
+ if (thrown instanceof Error) {
242
+ this.logger.error(`Failed to start all services:`, thrown.message);
243
+ }
244
+ });
245
+ }
246
+ tryUpdate() {
247
+ if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
248
+ this.logger.info(`All services are ready to update.`);
249
+ this.stopAllServices().then(() => {
250
+ this.logger.info(`All services stopped; starting up fresh...`);
251
+ this.startAllServices().then(() => {
252
+ this.logger.info(`All services started; we're back online.`);
253
+ }).catch((thrown) => {
254
+ if (thrown instanceof Error) {
255
+ this.logger.error(`Failed to start all services:`, thrown.message);
256
+ }
257
+ });
258
+ }).catch((thrown) => {
259
+ if (thrown instanceof Error) {
260
+ this.logger.error(`Failed to stop all services:`, thrown.message);
261
+ }
262
+ });
263
+ }
240
264
  }
241
265
  startAllServices() {
242
266
  this.logger.info(`Starting all services...`);
267
+ this.servicesShouldRestart = true;
243
268
  for (const [serviceName] of toEntries(this.services)) {
244
269
  this.startService(serviceName);
245
270
  }
271
+ return this.live;
246
272
  }
247
273
  startService(serviceName) {
248
274
  this.logger.info(`Starting service ${this.options.packageName}::${serviceName}, try ${this.safety}/2...`);
@@ -270,10 +296,7 @@ class FlightDeck {
270
296
  this.services[serviceName].on(`readyToUpdate`, () => {
271
297
  this.serviceLoggers[serviceName].info(`Ready to update.`);
272
298
  this.servicesReadyToUpdate[serviceName] = true;
273
- if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
274
- this.logger.info(`All services are ready to update.`);
275
- this.stopAllServices();
276
- }
299
+ this.tryUpdate();
277
300
  });
278
301
  this.services[serviceName].on(`alive`, () => {
279
302
  this.servicesLive[this.serviceIdx[serviceName]].use(Promise.resolve());
@@ -285,7 +308,7 @@ class FlightDeck {
285
308
  }
286
309
  this.dead.use(Promise.all(this.servicesDead));
287
310
  });
288
- this.services[serviceName].process.on(`close`, (exitCode) => {
311
+ this.services[serviceName].process.once(`close`, (exitCode) => {
289
312
  this.serviceLoggers[serviceName].info(`Exited with code ${exitCode}`);
290
313
  this.services[serviceName] = null;
291
314
  if (!this.servicesShouldRestart) {
@@ -346,16 +369,25 @@ class FlightDeck {
346
369
  }
347
370
  stopAllServices() {
348
371
  this.logger.info(`Stopping all services...`);
372
+ this.servicesShouldRestart = false;
349
373
  for (const [serviceName] of toEntries(this.services)) {
350
374
  this.stopService(serviceName);
351
375
  }
376
+ return this.dead;
352
377
  }
353
378
  stopService(serviceName) {
354
- if (this.services[serviceName]) {
379
+ const service = this.services[serviceName];
380
+ if (service) {
355
381
  this.serviceLoggers[serviceName].info(`Stopping service...`);
356
- this.services[serviceName].process.kill(`SIGINT`);
357
- this.services[serviceName] = null;
358
- this.servicesDead[this.serviceIdx[serviceName]].use(Promise.resolve());
382
+ this.servicesDead[this.serviceIdx[serviceName]].use(new Promise((pass) => {
383
+ service.emit(`timeToStop`);
384
+ service.process.once(`close`, (exitCode) => {
385
+ this.logger.info(`\uD83D\uDEEC service ${serviceName} exited with code ${exitCode}`);
386
+ this.services[serviceName] = null;
387
+ pass();
388
+ });
389
+ }));
390
+ this.dead.use(Promise.all(this.servicesDead));
359
391
  this.servicesLive[this.serviceIdx[serviceName]] = new Future(() => {
360
392
  });
361
393
  if (this.live.done) {
@@ -367,11 +399,6 @@ class FlightDeck {
367
399
  this.serviceLoggers[serviceName].error(`Tried to stop service, but it wasn't running.`);
368
400
  }
369
401
  }
370
- shutdown() {
371
- this.logger.info(`Shutting down...`);
372
- this.servicesShouldRestart = false;
373
- this.stopAllServices();
374
- }
375
402
  }
376
403
 
377
404
  // src/flightdeck.bin.ts
@@ -463,8 +490,7 @@ switch (inputs.case) {
463
490
  default: {
464
491
  const flightDeck = new FlightDeck(inputs.opts);
465
492
  process.on(`close`, async () => {
466
- flightDeck.stopAllServices();
467
- await flightDeck.dead;
493
+ await flightDeck.stopAllServices();
468
494
  });
469
495
  }
470
496
  }
package/dist/lib.d.ts CHANGED
@@ -23,6 +23,7 @@ declare class FlightDeck<S extends string = string> {
23
23
  protected webhookServer: Server;
24
24
  protected services: {
25
25
  [service in S]: ChildSocket<{
26
+ timeToStop: [];
26
27
  updatesReady: [];
27
28
  }, {
28
29
  readyToUpdate: [];
@@ -50,13 +51,13 @@ declare class FlightDeck<S extends string = string> {
50
51
  protected restartTimes: number[];
51
52
  protected persistentStateDir: string;
52
53
  constructor(options: FlightDeckOptions<S>);
53
- protected startAllServices(): void;
54
+ protected tryUpdate(): void;
55
+ protected startAllServices(): Future<unknown>;
54
56
  protected startService(serviceName: S): void;
55
57
  protected applyUpdate(): void;
56
58
  protected getLatestRelease(): void;
57
- stopAllServices(): void;
59
+ stopAllServices(): Future<unknown>;
58
60
  stopService(serviceName: S): void;
59
- shutdown(): void;
60
61
  }
61
62
 
62
63
  type AlertOptions = {
package/dist/lib.js CHANGED
@@ -202,8 +202,7 @@ class FlightDeck {
202
202
  }
203
203
  this.getLatestRelease();
204
204
  if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
205
- this.logger.info(`All services are ready to update!`);
206
- this.stopAllServices();
205
+ this.tryUpdate();
207
206
  return;
208
207
  }
209
208
  for (const entry of toEntries(this.services)) {
@@ -240,13 +239,40 @@ class FlightDeck {
240
239
  this.logger.info(`Server started on port ${port}`);
241
240
  });
242
241
  }
243
- this.startAllServices();
242
+ this.startAllServices().then(() => {
243
+ this.logger.info(`All services started.`);
244
+ }).catch((thrown) => {
245
+ if (thrown instanceof Error) {
246
+ this.logger.error(`Failed to start all services:`, thrown.message);
247
+ }
248
+ });
249
+ }
250
+ tryUpdate() {
251
+ if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
252
+ this.logger.info(`All services are ready to update.`);
253
+ this.stopAllServices().then(() => {
254
+ this.logger.info(`All services stopped; starting up fresh...`);
255
+ this.startAllServices().then(() => {
256
+ this.logger.info(`All services started; we're back online.`);
257
+ }).catch((thrown) => {
258
+ if (thrown instanceof Error) {
259
+ this.logger.error(`Failed to start all services:`, thrown.message);
260
+ }
261
+ });
262
+ }).catch((thrown) => {
263
+ if (thrown instanceof Error) {
264
+ this.logger.error(`Failed to stop all services:`, thrown.message);
265
+ }
266
+ });
267
+ }
244
268
  }
245
269
  startAllServices() {
246
270
  this.logger.info(`Starting all services...`);
271
+ this.servicesShouldRestart = true;
247
272
  for (const [serviceName] of toEntries(this.services)) {
248
273
  this.startService(serviceName);
249
274
  }
275
+ return this.live;
250
276
  }
251
277
  startService(serviceName) {
252
278
  this.logger.info(`Starting service ${this.options.packageName}::${serviceName}, try ${this.safety}/2...`);
@@ -274,10 +300,7 @@ class FlightDeck {
274
300
  this.services[serviceName].on(`readyToUpdate`, () => {
275
301
  this.serviceLoggers[serviceName].info(`Ready to update.`);
276
302
  this.servicesReadyToUpdate[serviceName] = true;
277
- if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
278
- this.logger.info(`All services are ready to update.`);
279
- this.stopAllServices();
280
- }
303
+ this.tryUpdate();
281
304
  });
282
305
  this.services[serviceName].on(`alive`, () => {
283
306
  this.servicesLive[this.serviceIdx[serviceName]].use(Promise.resolve());
@@ -289,7 +312,7 @@ class FlightDeck {
289
312
  }
290
313
  this.dead.use(Promise.all(this.servicesDead));
291
314
  });
292
- this.services[serviceName].process.on(`close`, (exitCode) => {
315
+ this.services[serviceName].process.once(`close`, (exitCode) => {
293
316
  this.serviceLoggers[serviceName].info(`Exited with code ${exitCode}`);
294
317
  this.services[serviceName] = null;
295
318
  if (!this.servicesShouldRestart) {
@@ -350,16 +373,25 @@ class FlightDeck {
350
373
  }
351
374
  stopAllServices() {
352
375
  this.logger.info(`Stopping all services...`);
376
+ this.servicesShouldRestart = false;
353
377
  for (const [serviceName] of toEntries(this.services)) {
354
378
  this.stopService(serviceName);
355
379
  }
380
+ return this.dead;
356
381
  }
357
382
  stopService(serviceName) {
358
- if (this.services[serviceName]) {
383
+ const service = this.services[serviceName];
384
+ if (service) {
359
385
  this.serviceLoggers[serviceName].info(`Stopping service...`);
360
- this.services[serviceName].process.kill(`SIGINT`);
361
- this.services[serviceName] = null;
362
- this.servicesDead[this.serviceIdx[serviceName]].use(Promise.resolve());
386
+ this.servicesDead[this.serviceIdx[serviceName]].use(new Promise((pass) => {
387
+ service.emit(`timeToStop`);
388
+ service.process.once(`close`, (exitCode) => {
389
+ this.logger.info(`\uD83D\uDEEC service ${serviceName} exited with code ${exitCode}`);
390
+ this.services[serviceName] = null;
391
+ pass();
392
+ });
393
+ }));
394
+ this.dead.use(Promise.all(this.servicesDead));
363
395
  this.servicesLive[this.serviceIdx[serviceName]] = new Future(() => {
364
396
  });
365
397
  if (this.live.done) {
@@ -371,11 +403,6 @@ class FlightDeck {
371
403
  this.serviceLoggers[serviceName].error(`Tried to stop service, but it wasn't running.`);
372
404
  }
373
405
  }
374
- shutdown() {
375
- this.logger.info(`Shutting down...`);
376
- this.servicesShouldRestart = false;
377
- this.stopAllServices();
378
- }
379
406
  }
380
407
  // src/klaxon.lib.ts
381
408
  var exports_klaxon_lib = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flightdeck",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "author": {
6
6
  "name": "Jeremy Banka",
@@ -23,18 +23,18 @@
23
23
  "dependencies": {
24
24
  "@t3-oss/env-core": "0.11.1",
25
25
  "zod": "3.23.8",
26
- "atom.io": "0.30.1",
26
+ "atom.io": "0.30.2",
27
27
  "comline": "0.1.6"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/node": "22.7.9",
30
+ "@types/node": "22.9.0",
31
31
  "@types/tmp": "0.2.6",
32
- "bun-types": "1.1.33",
33
- "concurrently": "9.0.1",
32
+ "bun-types": "1.1.34",
33
+ "concurrently": "9.1.0",
34
34
  "rimraf": "6.0.1",
35
35
  "tmp": "0.2.3",
36
- "tsup": "8.3.0",
37
- "vitest": "2.1.3"
36
+ "tsup": "8.3.5",
37
+ "vitest": "2.1.4"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "rimraf dist && concurrently \"bun:build:*\" && concurrently \"bun:schema:*\"",
@@ -105,8 +105,7 @@ switch (inputs.case) {
105
105
  default: {
106
106
  const flightDeck = new FlightDeck(inputs.opts)
107
107
  process.on(`close`, async () => {
108
- flightDeck.stopAllServices()
109
- await flightDeck.dead
108
+ await flightDeck.stopAllServices()
110
109
  })
111
110
  }
112
111
  }
@@ -28,7 +28,7 @@ export class FlightDeck<S extends string = string> {
28
28
  protected webhookServer: Server
29
29
  protected services: {
30
30
  [service in S]: ChildSocket<
31
- { updatesReady: [] },
31
+ { timeToStop: []; updatesReady: [] },
32
32
  { readyToUpdate: []; alive: [] }
33
33
  > | null
34
34
  }
@@ -174,8 +174,7 @@ export class FlightDeck<S extends string = string> {
174
174
  ([, isReady]) => isReady,
175
175
  )
176
176
  ) {
177
- this.logger.info(`All services are ready to update!`)
178
- this.stopAllServices()
177
+ this.tryUpdate()
179
178
  return
180
179
  }
181
180
  for (const entry of toEntries(this.services)) {
@@ -216,13 +215,50 @@ export class FlightDeck<S extends string = string> {
216
215
  }
217
216
 
218
217
  this.startAllServices()
218
+ .then(() => {
219
+ this.logger.info(`All services started.`)
220
+ })
221
+ .catch((thrown) => {
222
+ if (thrown instanceof Error) {
223
+ this.logger.error(`Failed to start all services:`, thrown.message)
224
+ }
225
+ })
226
+ }
227
+
228
+ protected tryUpdate(): void {
229
+ if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
230
+ this.logger.info(`All services are ready to update.`)
231
+ this.stopAllServices()
232
+ .then(() => {
233
+ this.logger.info(`All services stopped; starting up fresh...`)
234
+ this.startAllServices()
235
+ .then(() => {
236
+ this.logger.info(`All services started; we're back online.`)
237
+ })
238
+ .catch((thrown) => {
239
+ if (thrown instanceof Error) {
240
+ this.logger.error(
241
+ `Failed to start all services:`,
242
+ thrown.message,
243
+ )
244
+ }
245
+ })
246
+ })
247
+ .catch((thrown) => {
248
+ if (thrown instanceof Error) {
249
+ this.logger.error(`Failed to stop all services:`, thrown.message)
250
+ }
251
+ })
252
+ }
219
253
  }
220
254
 
221
- protected startAllServices(): void {
255
+ protected startAllServices(): Future<unknown> {
222
256
  this.logger.info(`Starting all services...`)
257
+ this.servicesShouldRestart = true
223
258
  for (const [serviceName] of toEntries(this.services)) {
224
259
  this.startService(serviceName)
225
260
  }
261
+ return this.live
226
262
  }
227
263
 
228
264
  protected startService(serviceName: S): void {
@@ -261,12 +297,7 @@ export class FlightDeck<S extends string = string> {
261
297
  this.services[serviceName].on(`readyToUpdate`, () => {
262
298
  this.serviceLoggers[serviceName].info(`Ready to update.`)
263
299
  this.servicesReadyToUpdate[serviceName] = true
264
- if (
265
- toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)
266
- ) {
267
- this.logger.info(`All services are ready to update.`)
268
- this.stopAllServices()
269
- }
300
+ this.tryUpdate()
270
301
  })
271
302
  this.services[serviceName].on(`alive`, () => {
272
303
  this.servicesLive[this.serviceIdx[serviceName]].use(Promise.resolve())
@@ -276,7 +307,7 @@ export class FlightDeck<S extends string = string> {
276
307
  }
277
308
  this.dead.use(Promise.all(this.servicesDead))
278
309
  })
279
- this.services[serviceName].process.on(`close`, (exitCode) => {
310
+ this.services[serviceName].process.once(`close`, (exitCode) => {
280
311
  this.serviceLoggers[serviceName].info(`Exited with code ${exitCode}`)
281
312
  this.services[serviceName] = null
282
313
  if (!this.servicesShouldRestart) {
@@ -345,19 +376,32 @@ export class FlightDeck<S extends string = string> {
345
376
  }
346
377
  }
347
378
 
348
- public stopAllServices(): void {
379
+ public stopAllServices(): Future<unknown> {
349
380
  this.logger.info(`Stopping all services...`)
381
+ this.servicesShouldRestart = false
350
382
  for (const [serviceName] of toEntries(this.services)) {
351
383
  this.stopService(serviceName)
352
384
  }
385
+ return this.dead
353
386
  }
354
387
 
355
388
  public stopService(serviceName: S): void {
356
- if (this.services[serviceName]) {
389
+ const service = this.services[serviceName]
390
+ if (service) {
357
391
  this.serviceLoggers[serviceName].info(`Stopping service...`)
358
- this.services[serviceName].process.kill(`SIGINT`)
359
- this.services[serviceName] = null
360
- this.servicesDead[this.serviceIdx[serviceName]].use(Promise.resolve())
392
+ this.servicesDead[this.serviceIdx[serviceName]].use(
393
+ new Promise((pass) => {
394
+ service.emit(`timeToStop`)
395
+ service.process.once(`close`, (exitCode) => {
396
+ this.logger.info(
397
+ `🛬 service ${serviceName} exited with code ${exitCode}`,
398
+ )
399
+ this.services[serviceName] = null
400
+ pass()
401
+ })
402
+ }),
403
+ )
404
+ this.dead.use(Promise.all(this.servicesDead))
361
405
  this.servicesLive[this.serviceIdx[serviceName]] = new Future(() => {})
362
406
  if (this.live.done) {
363
407
  this.live = new Future(() => {})
@@ -369,10 +413,4 @@ export class FlightDeck<S extends string = string> {
369
413
  )
370
414
  }
371
415
  }
372
-
373
- public shutdown(): void {
374
- this.logger.info(`Shutting down...`)
375
- this.servicesShouldRestart = false
376
- this.stopAllServices()
377
- }
378
416
  }