flightdeck 0.0.3 → 0.0.4

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.
@@ -29,7 +29,7 @@
29
29
  "additionalProperties": false
30
30
  }
31
31
  },
32
- "flightDeckRootDir": {
32
+ "flightdeckRootDir": {
33
33
  "type": "string"
34
34
  },
35
35
  "downloadPackageToUpdatesCmd": {
@@ -43,7 +43,7 @@
43
43
  "secret",
44
44
  "packageName",
45
45
  "services",
46
- "flightDeckRootDir",
46
+ "flightdeckRootDir",
47
47
  "downloadPackageToUpdatesCmd"
48
48
  ],
49
49
  "additionalProperties": false,
@@ -26,13 +26,15 @@ class FlightDeck {
26
26
  defaultServicesReadyToUpdate;
27
27
  servicesReadyToUpdate;
28
28
  servicesShouldRestart;
29
- restartTimes = [];
29
+ logger;
30
+ serviceLoggers;
30
31
  servicesLive;
31
32
  servicesDead;
32
33
  live = new Future(() => {
33
34
  });
34
35
  dead = new Future(() => {
35
36
  });
37
+ restartTimes = [];
36
38
  currentServiceDir;
37
39
  updateServiceDir;
38
40
  backupServiceDir;
@@ -48,6 +50,31 @@ class FlightDeck {
48
50
  ]));
49
51
  this.servicesReadyToUpdate = { ...this.defaultServicesReadyToUpdate };
50
52
  this.servicesShouldRestart = true;
53
+ this.logger = {
54
+ info: (...args) => {
55
+ console.log(`${this.options.packageName}:`, ...args);
56
+ },
57
+ warn: (...args) => {
58
+ console.warn(`${this.options.packageName}:`, ...args);
59
+ },
60
+ error: (...args) => {
61
+ console.error(`${this.options.packageName}:`, ...args);
62
+ }
63
+ };
64
+ this.serviceLoggers = fromEntries(servicesEntries.map(([serviceName]) => [
65
+ serviceName,
66
+ {
67
+ info: (...args) => {
68
+ console.log(`${this.options.packageName}::${serviceName}:`, ...args);
69
+ },
70
+ warn: (...args) => {
71
+ console.warn(`${this.options.packageName}::${serviceName}:`, ...args);
72
+ },
73
+ error: (...args) => {
74
+ console.error(`${this.options.packageName}::${serviceName}:`, ...args);
75
+ }
76
+ }
77
+ ]));
51
78
  this.servicesLive = servicesEntries.map(() => new Future(() => {
52
79
  }));
53
80
  this.servicesDead = servicesEntries.map(() => new Future(() => {
@@ -62,7 +89,6 @@ class FlightDeck {
62
89
  req.on(`data`, (chunk) => {
63
90
  data.push(chunk instanceof Buffer ? chunk : Buffer.from(chunk));
64
91
  }).on(`end`, () => {
65
- console.log(req.headers);
66
92
  const authHeader = req.headers.authorization;
67
93
  try {
68
94
  if (typeof req.url === `undefined`)
@@ -70,19 +96,18 @@ class FlightDeck {
70
96
  if (authHeader !== `Bearer ${secret}`)
71
97
  throw 401;
72
98
  const url = new URL(req.url, ORIGIN);
73
- console.log(req.method, url.pathname);
99
+ this.logger.info(req.method, url.pathname);
74
100
  switch (req.method) {
75
101
  case `POST`:
76
102
  {
77
- console.log(`received post, url is ${url.pathname}`);
78
103
  switch (url.pathname) {
79
104
  case `/`:
80
105
  {
81
106
  res.writeHead(200);
82
107
  res.end();
83
- this.fetchLatestRelease();
108
+ this.getLatestRelease();
84
109
  if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
85
- console.log(`All services are ready to update!`);
110
+ this.logger.info(`All services are ready to update!`);
86
111
  this.stopAllServices();
87
112
  return;
88
113
  }
@@ -107,7 +132,7 @@ class FlightDeck {
107
132
  throw 405;
108
133
  }
109
134
  } catch (thrown) {
110
- console.error(thrown, req.url);
135
+ this.logger.error(thrown, req.url);
111
136
  if (typeof thrown === `number`) {
112
137
  res.writeHead(thrown);
113
138
  res.end();
@@ -117,25 +142,25 @@ class FlightDeck {
117
142
  }
118
143
  });
119
144
  }).listen(PORT, () => {
120
- console.log(`Server started on port ${PORT}`);
145
+ this.logger.info(`Server started on port ${PORT}`);
121
146
  });
122
147
  this.startAllServices();
123
148
  }
124
149
  startAllServices() {
125
- console.log(`Starting all services...`);
150
+ this.logger.info(`Starting all services...`);
126
151
  for (const [serviceName] of toEntries(this.services)) {
127
152
  this.startService(serviceName);
128
153
  }
129
154
  }
130
155
  startService(serviceName) {
131
- console.log(`Starting service ${this.options.packageName}::${serviceName}, try ${this.safety}/2...`);
132
- if (this.safety > 2) {
156
+ this.logger.info(`Starting service ${this.options.packageName}::${serviceName}, try ${this.safety}/2...`);
157
+ if (this.safety >= 2) {
133
158
  throw new Error(`Out of tries...`);
134
159
  }
135
160
  this.safety++;
136
161
  if (!existsSync(this.currentServiceDir)) {
137
- console.log(`Tried to start service but failed: Service ${this.options.packageName} is not yet installed.`);
138
- this.fetchLatestRelease();
162
+ this.logger.info(`Tried to start service but failed: could not find ${this.currentServiceDir}`);
163
+ this.getLatestRelease();
139
164
  this.applyUpdate();
140
165
  this.startService(serviceName);
141
166
  return;
@@ -148,13 +173,13 @@ class FlightDeck {
148
173
  });
149
174
  this.services[serviceName] = new ChildSocket(serviceProcess, `${this.options.packageName}::${serviceName}`, console);
150
175
  this.services[serviceName].onAny((...messages) => {
151
- console.log(`${this.options.packageName}::${serviceName} \uD83D\uDCAC`, ...messages);
176
+ this.logger.info(`\uD83D\uDCAC`, ...messages);
152
177
  });
153
178
  this.services[serviceName].on(`readyToUpdate`, () => {
154
- console.log(`Service ${this.options.packageName}::${serviceName} is ready to update.`);
179
+ this.serviceLoggers[serviceName].info(`Ready to update.`);
155
180
  this.servicesReadyToUpdate[serviceName] = true;
156
181
  if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
157
- console.log(`All services are ready to update!`);
182
+ this.logger.info(`All services are ready to update.`);
158
183
  this.stopAllServices();
159
184
  }
160
185
  });
@@ -169,15 +194,15 @@ class FlightDeck {
169
194
  this.dead.use(Promise.all(this.servicesDead));
170
195
  });
171
196
  this.services[serviceName].process.on(`close`, (exitCode) => {
172
- console.log(`${this.options.packageName}::${serviceName} exited with code ${exitCode}`);
197
+ this.serviceLoggers[serviceName].info(`Exited with code ${exitCode}`);
173
198
  this.services[serviceName] = null;
174
199
  if (!this.servicesShouldRestart) {
175
- console.log(`Service ${this.options.packageName}::${serviceName} will not be restarted.`);
200
+ this.serviceLoggers[serviceName].info(`Will not be restarted.`);
176
201
  return;
177
202
  }
178
203
  const updatesAreReady = existsSync(this.updateServiceDir);
179
204
  if (updatesAreReady) {
180
- console.log(`${this.options.packageName}::${serviceName} will be updated before startup...`);
205
+ this.serviceLoggers[serviceName].info(`Updating before startup...`);
181
206
  this.restartTimes = [];
182
207
  this.applyUpdate();
183
208
  this.startService(serviceName);
@@ -187,21 +212,21 @@ class FlightDeck {
187
212
  this.restartTimes = this.restartTimes.filter((time) => time > fiveMinutesAgo);
188
213
  this.restartTimes.push(now);
189
214
  if (this.restartTimes.length < 5) {
190
- console.log(`Service ${this.options.packageName}::${serviceName} crashed. Restarting...`);
215
+ this.serviceLoggers[serviceName].info(`Crashed. Restarting...`);
191
216
  this.startService(serviceName);
192
217
  } else {
193
- console.log(`Service ${this.options.packageName}::${serviceName} crashed too many times. Not restarting.`);
218
+ this.serviceLoggers[serviceName].info(`Crashed 5 times in 5 minutes. Not restarting.`);
194
219
  }
195
220
  }
196
221
  });
197
222
  this.safety = 0;
198
223
  }
199
224
  applyUpdate() {
200
- console.log(`Installing latest version of service ${this.options.packageName}...`);
225
+ this.logger.info(`Applying update...`);
201
226
  if (existsSync(this.updateServiceDir)) {
202
227
  const runningServices = toEntries(this.services).filter(([, service]) => service);
203
228
  if (runningServices.length > 0) {
204
- console.log(`Tried to apply update to ${this.options.packageName} but failed. The following services are currently running: [${runningServices.map(([serviceName]) => serviceName).join(`, `)}]`);
229
+ this.logger.error(`Tried to apply update but failed. The following services are currently running: [${runningServices.map(([serviceName]) => serviceName).join(`, `)}]`);
205
230
  return;
206
231
  }
207
232
  if (existsSync(this.currentServiceDir)) {
@@ -216,29 +241,29 @@ class FlightDeck {
216
241
  this.restartTimes = [];
217
242
  this.servicesReadyToUpdate = { ...this.defaultServicesReadyToUpdate };
218
243
  } else {
219
- console.log(`Service ${this.options.packageName} is already up to date.`);
244
+ this.logger.error(`Tried to apply update but failed: could not find update directory ${this.updateServiceDir}`);
220
245
  }
221
246
  }
222
- fetchLatestRelease() {
223
- console.log(`Downloading latest version of service ${this.options.packageName}...`);
247
+ getLatestRelease() {
248
+ this.logger.info(`Getting latest release...`);
224
249
  try {
225
250
  execSync(this.options.downloadPackageToUpdatesCmd.join(` `));
226
251
  } catch (thrown) {
227
252
  if (thrown instanceof Error) {
228
- console.error(`Failed to fetch the latest release: ${thrown.message}`);
253
+ this.logger.error(`Failed to get the latest release: ${thrown.message}`);
229
254
  }
230
255
  return;
231
256
  }
232
257
  }
233
258
  stopAllServices() {
234
- console.log(`Stopping all services...`);
259
+ this.logger.info(`Stopping all services...`);
235
260
  for (const [serviceName] of toEntries(this.services)) {
236
261
  this.stopService(serviceName);
237
262
  }
238
263
  }
239
264
  stopService(serviceName) {
240
265
  if (this.services[serviceName]) {
241
- console.log(`Stopping service ${this.options.packageName}::${serviceName}...`);
266
+ this.serviceLoggers[serviceName].info(`Stopping service...`);
242
267
  this.services[serviceName].process.kill();
243
268
  this.services[serviceName] = null;
244
269
  this.servicesDead[this.serviceIdx[serviceName]].use(Promise.resolve());
@@ -250,7 +275,7 @@ class FlightDeck {
250
275
  }
251
276
  this.live.use(Promise.all(this.servicesLive));
252
277
  } else {
253
- console.error(`Failed to stop service ${this.options.packageName}::${serviceName}: Service is not running.`);
278
+ this.serviceLoggers[serviceName].error(`Tried to stop service, but it wasn't running.`);
254
279
  }
255
280
  }
256
281
  shutdown() {
@@ -265,7 +290,7 @@ var FLIGHTDECK_MANUAL = {
265
290
  secret: z.string(),
266
291
  packageName: z.string(),
267
292
  services: z.record(z.object({ run: z.array(z.string()), waitFor: z.boolean() })),
268
- flightDeckRootDir: z.string(),
293
+ flightdeckRootDir: z.string(),
269
294
  downloadPackageToUpdatesCmd: z.array(z.string())
270
295
  }),
271
296
  options: {
@@ -29,7 +29,7 @@
29
29
  "additionalProperties": false
30
30
  }
31
31
  },
32
- "flightDeckRootDir": {
32
+ "flightdeckRootDir": {
33
33
  "type": "string"
34
34
  },
35
35
  "downloadPackageToUpdatesCmd": {
@@ -43,7 +43,7 @@
43
43
  "secret",
44
44
  "packageName",
45
45
  "services",
46
- "flightDeckRootDir",
46
+ "flightdeckRootDir",
47
47
  "downloadPackageToUpdatesCmd"
48
48
  ],
49
49
  "additionalProperties": false,
package/dist/lib.d.ts CHANGED
@@ -36,11 +36,15 @@ declare class FlightDeck<S extends string = string> {
36
36
  [service in S]: boolean;
37
37
  };
38
38
  servicesShouldRestart: boolean;
39
- protected restartTimes: number[];
39
+ protected logger: Pick<Console, `error` | `info` | `warn`>;
40
+ protected serviceLoggers: {
41
+ readonly [service in S]: Pick<Console, `error` | `info` | `warn`>;
42
+ };
40
43
  servicesLive: Future<void>[];
41
44
  servicesDead: Future<void>[];
42
45
  live: Future<unknown>;
43
46
  dead: Future<unknown>;
47
+ protected restartTimes: number[];
44
48
  readonly currentServiceDir: string;
45
49
  readonly updateServiceDir: string;
46
50
  readonly backupServiceDir: string;
@@ -48,7 +52,7 @@ declare class FlightDeck<S extends string = string> {
48
52
  protected startAllServices(): void;
49
53
  protected startService(serviceName: S): void;
50
54
  protected applyUpdate(): void;
51
- protected fetchLatestRelease(): void;
55
+ protected getLatestRelease(): void;
52
56
  stopAllServices(): void;
53
57
  stopService(serviceName: S): void;
54
58
  shutdown(): void;
package/dist/lib.js CHANGED
@@ -30,13 +30,15 @@ class FlightDeck {
30
30
  defaultServicesReadyToUpdate;
31
31
  servicesReadyToUpdate;
32
32
  servicesShouldRestart;
33
- restartTimes = [];
33
+ logger;
34
+ serviceLoggers;
34
35
  servicesLive;
35
36
  servicesDead;
36
37
  live = new Future(() => {
37
38
  });
38
39
  dead = new Future(() => {
39
40
  });
41
+ restartTimes = [];
40
42
  currentServiceDir;
41
43
  updateServiceDir;
42
44
  backupServiceDir;
@@ -52,6 +54,31 @@ class FlightDeck {
52
54
  ]));
53
55
  this.servicesReadyToUpdate = { ...this.defaultServicesReadyToUpdate };
54
56
  this.servicesShouldRestart = true;
57
+ this.logger = {
58
+ info: (...args) => {
59
+ console.log(`${this.options.packageName}:`, ...args);
60
+ },
61
+ warn: (...args) => {
62
+ console.warn(`${this.options.packageName}:`, ...args);
63
+ },
64
+ error: (...args) => {
65
+ console.error(`${this.options.packageName}:`, ...args);
66
+ }
67
+ };
68
+ this.serviceLoggers = fromEntries(servicesEntries.map(([serviceName]) => [
69
+ serviceName,
70
+ {
71
+ info: (...args) => {
72
+ console.log(`${this.options.packageName}::${serviceName}:`, ...args);
73
+ },
74
+ warn: (...args) => {
75
+ console.warn(`${this.options.packageName}::${serviceName}:`, ...args);
76
+ },
77
+ error: (...args) => {
78
+ console.error(`${this.options.packageName}::${serviceName}:`, ...args);
79
+ }
80
+ }
81
+ ]));
55
82
  this.servicesLive = servicesEntries.map(() => new Future(() => {
56
83
  }));
57
84
  this.servicesDead = servicesEntries.map(() => new Future(() => {
@@ -66,7 +93,6 @@ class FlightDeck {
66
93
  req.on(`data`, (chunk) => {
67
94
  data.push(chunk instanceof Buffer ? chunk : Buffer.from(chunk));
68
95
  }).on(`end`, () => {
69
- console.log(req.headers);
70
96
  const authHeader = req.headers.authorization;
71
97
  try {
72
98
  if (typeof req.url === `undefined`)
@@ -74,19 +100,18 @@ class FlightDeck {
74
100
  if (authHeader !== `Bearer ${secret}`)
75
101
  throw 401;
76
102
  const url = new URL(req.url, ORIGIN);
77
- console.log(req.method, url.pathname);
103
+ this.logger.info(req.method, url.pathname);
78
104
  switch (req.method) {
79
105
  case `POST`:
80
106
  {
81
- console.log(`received post, url is ${url.pathname}`);
82
107
  switch (url.pathname) {
83
108
  case `/`:
84
109
  {
85
110
  res.writeHead(200);
86
111
  res.end();
87
- this.fetchLatestRelease();
112
+ this.getLatestRelease();
88
113
  if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
89
- console.log(`All services are ready to update!`);
114
+ this.logger.info(`All services are ready to update!`);
90
115
  this.stopAllServices();
91
116
  return;
92
117
  }
@@ -111,7 +136,7 @@ class FlightDeck {
111
136
  throw 405;
112
137
  }
113
138
  } catch (thrown) {
114
- console.error(thrown, req.url);
139
+ this.logger.error(thrown, req.url);
115
140
  if (typeof thrown === `number`) {
116
141
  res.writeHead(thrown);
117
142
  res.end();
@@ -121,25 +146,25 @@ class FlightDeck {
121
146
  }
122
147
  });
123
148
  }).listen(PORT, () => {
124
- console.log(`Server started on port ${PORT}`);
149
+ this.logger.info(`Server started on port ${PORT}`);
125
150
  });
126
151
  this.startAllServices();
127
152
  }
128
153
  startAllServices() {
129
- console.log(`Starting all services...`);
154
+ this.logger.info(`Starting all services...`);
130
155
  for (const [serviceName] of toEntries(this.services)) {
131
156
  this.startService(serviceName);
132
157
  }
133
158
  }
134
159
  startService(serviceName) {
135
- console.log(`Starting service ${this.options.packageName}::${serviceName}, try ${this.safety}/2...`);
136
- if (this.safety > 2) {
160
+ this.logger.info(`Starting service ${this.options.packageName}::${serviceName}, try ${this.safety}/2...`);
161
+ if (this.safety >= 2) {
137
162
  throw new Error(`Out of tries...`);
138
163
  }
139
164
  this.safety++;
140
165
  if (!existsSync(this.currentServiceDir)) {
141
- console.log(`Tried to start service but failed: Service ${this.options.packageName} is not yet installed.`);
142
- this.fetchLatestRelease();
166
+ this.logger.info(`Tried to start service but failed: could not find ${this.currentServiceDir}`);
167
+ this.getLatestRelease();
143
168
  this.applyUpdate();
144
169
  this.startService(serviceName);
145
170
  return;
@@ -152,13 +177,13 @@ class FlightDeck {
152
177
  });
153
178
  this.services[serviceName] = new ChildSocket(serviceProcess, `${this.options.packageName}::${serviceName}`, console);
154
179
  this.services[serviceName].onAny((...messages) => {
155
- console.log(`${this.options.packageName}::${serviceName} \uD83D\uDCAC`, ...messages);
180
+ this.logger.info(`\uD83D\uDCAC`, ...messages);
156
181
  });
157
182
  this.services[serviceName].on(`readyToUpdate`, () => {
158
- console.log(`Service ${this.options.packageName}::${serviceName} is ready to update.`);
183
+ this.serviceLoggers[serviceName].info(`Ready to update.`);
159
184
  this.servicesReadyToUpdate[serviceName] = true;
160
185
  if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
161
- console.log(`All services are ready to update!`);
186
+ this.logger.info(`All services are ready to update.`);
162
187
  this.stopAllServices();
163
188
  }
164
189
  });
@@ -173,15 +198,15 @@ class FlightDeck {
173
198
  this.dead.use(Promise.all(this.servicesDead));
174
199
  });
175
200
  this.services[serviceName].process.on(`close`, (exitCode) => {
176
- console.log(`${this.options.packageName}::${serviceName} exited with code ${exitCode}`);
201
+ this.serviceLoggers[serviceName].info(`Exited with code ${exitCode}`);
177
202
  this.services[serviceName] = null;
178
203
  if (!this.servicesShouldRestart) {
179
- console.log(`Service ${this.options.packageName}::${serviceName} will not be restarted.`);
204
+ this.serviceLoggers[serviceName].info(`Will not be restarted.`);
180
205
  return;
181
206
  }
182
207
  const updatesAreReady = existsSync(this.updateServiceDir);
183
208
  if (updatesAreReady) {
184
- console.log(`${this.options.packageName}::${serviceName} will be updated before startup...`);
209
+ this.serviceLoggers[serviceName].info(`Updating before startup...`);
185
210
  this.restartTimes = [];
186
211
  this.applyUpdate();
187
212
  this.startService(serviceName);
@@ -191,21 +216,21 @@ class FlightDeck {
191
216
  this.restartTimes = this.restartTimes.filter((time) => time > fiveMinutesAgo);
192
217
  this.restartTimes.push(now);
193
218
  if (this.restartTimes.length < 5) {
194
- console.log(`Service ${this.options.packageName}::${serviceName} crashed. Restarting...`);
219
+ this.serviceLoggers[serviceName].info(`Crashed. Restarting...`);
195
220
  this.startService(serviceName);
196
221
  } else {
197
- console.log(`Service ${this.options.packageName}::${serviceName} crashed too many times. Not restarting.`);
222
+ this.serviceLoggers[serviceName].info(`Crashed 5 times in 5 minutes. Not restarting.`);
198
223
  }
199
224
  }
200
225
  });
201
226
  this.safety = 0;
202
227
  }
203
228
  applyUpdate() {
204
- console.log(`Installing latest version of service ${this.options.packageName}...`);
229
+ this.logger.info(`Applying update...`);
205
230
  if (existsSync(this.updateServiceDir)) {
206
231
  const runningServices = toEntries(this.services).filter(([, service]) => service);
207
232
  if (runningServices.length > 0) {
208
- console.log(`Tried to apply update to ${this.options.packageName} but failed. The following services are currently running: [${runningServices.map(([serviceName]) => serviceName).join(`, `)}]`);
233
+ this.logger.error(`Tried to apply update but failed. The following services are currently running: [${runningServices.map(([serviceName]) => serviceName).join(`, `)}]`);
209
234
  return;
210
235
  }
211
236
  if (existsSync(this.currentServiceDir)) {
@@ -220,29 +245,29 @@ class FlightDeck {
220
245
  this.restartTimes = [];
221
246
  this.servicesReadyToUpdate = { ...this.defaultServicesReadyToUpdate };
222
247
  } else {
223
- console.log(`Service ${this.options.packageName} is already up to date.`);
248
+ this.logger.error(`Tried to apply update but failed: could not find update directory ${this.updateServiceDir}`);
224
249
  }
225
250
  }
226
- fetchLatestRelease() {
227
- console.log(`Downloading latest version of service ${this.options.packageName}...`);
251
+ getLatestRelease() {
252
+ this.logger.info(`Getting latest release...`);
228
253
  try {
229
254
  execSync(this.options.downloadPackageToUpdatesCmd.join(` `));
230
255
  } catch (thrown) {
231
256
  if (thrown instanceof Error) {
232
- console.error(`Failed to fetch the latest release: ${thrown.message}`);
257
+ this.logger.error(`Failed to get the latest release: ${thrown.message}`);
233
258
  }
234
259
  return;
235
260
  }
236
261
  }
237
262
  stopAllServices() {
238
- console.log(`Stopping all services...`);
263
+ this.logger.info(`Stopping all services...`);
239
264
  for (const [serviceName] of toEntries(this.services)) {
240
265
  this.stopService(serviceName);
241
266
  }
242
267
  }
243
268
  stopService(serviceName) {
244
269
  if (this.services[serviceName]) {
245
- console.log(`Stopping service ${this.options.packageName}::${serviceName}...`);
270
+ this.serviceLoggers[serviceName].info(`Stopping service...`);
246
271
  this.services[serviceName].process.kill();
247
272
  this.services[serviceName] = null;
248
273
  this.servicesDead[this.serviceIdx[serviceName]].use(Promise.resolve());
@@ -254,7 +279,7 @@ class FlightDeck {
254
279
  }
255
280
  this.live.use(Promise.all(this.servicesLive));
256
281
  } else {
257
- console.error(`Failed to stop service ${this.options.packageName}::${serviceName}: Service is not running.`);
282
+ this.serviceLoggers[serviceName].error(`Tried to stop service, but it wasn't running.`);
258
283
  }
259
284
  }
260
285
  shutdown() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flightdeck",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "license": "MIT",
5
5
  "author": {
6
6
  "name": "Jeremy Banka",
@@ -16,7 +16,7 @@ const FLIGHTDECK_MANUAL = {
16
16
  services: z.record(
17
17
  z.object({ run: z.array(z.string()), waitFor: z.boolean() }),
18
18
  ),
19
- flightDeckRootDir: z.string(),
19
+ flightdeckRootDir: z.string(),
20
20
  downloadPackageToUpdatesCmd: z.array(z.string()),
21
21
  }),
22
22
  options: {
@@ -34,13 +34,18 @@ export class FlightDeck<S extends string = string> {
34
34
  public servicesReadyToUpdate: { [service in S]: boolean }
35
35
  public servicesShouldRestart: boolean
36
36
 
37
- protected restartTimes: number[] = []
37
+ protected logger: Pick<Console, `error` | `info` | `warn`>
38
+ protected serviceLoggers: {
39
+ readonly [service in S]: Pick<Console, `error` | `info` | `warn`>
40
+ }
38
41
 
39
42
  public servicesLive: Future<void>[]
40
43
  public servicesDead: Future<void>[]
41
44
  public live = new Future(() => {})
42
45
  public dead = new Future(() => {})
43
46
 
47
+ protected restartTimes: number[] = []
48
+
44
49
  public readonly currentServiceDir: string
45
50
  public readonly updateServiceDir: string
46
51
  public readonly backupServiceDir: string
@@ -64,6 +69,38 @@ export class FlightDeck<S extends string = string> {
64
69
  )
65
70
  this.servicesReadyToUpdate = { ...this.defaultServicesReadyToUpdate }
66
71
  this.servicesShouldRestart = true
72
+
73
+ this.logger = {
74
+ info: (...args: any[]) => {
75
+ console.log(`${this.options.packageName}:`, ...args)
76
+ },
77
+ warn: (...args: any[]) => {
78
+ console.warn(`${this.options.packageName}:`, ...args)
79
+ },
80
+ error: (...args: any[]) => {
81
+ console.error(`${this.options.packageName}:`, ...args)
82
+ },
83
+ }
84
+ this.serviceLoggers = fromEntries(
85
+ servicesEntries.map(([serviceName]) => [
86
+ serviceName,
87
+ {
88
+ info: (...args: any[]) => {
89
+ console.log(`${this.options.packageName}::${serviceName}:`, ...args)
90
+ },
91
+ warn: (...args: any[]) => {
92
+ console.warn(`${this.options.packageName}::${serviceName}:`, ...args)
93
+ },
94
+ error: (...args: any[]) => {
95
+ console.error(
96
+ `${this.options.packageName}::${serviceName}:`,
97
+ ...args,
98
+ )
99
+ },
100
+ },
101
+ ]),
102
+ )
103
+
67
104
  this.servicesLive = servicesEntries.map(() => new Future(() => {}))
68
105
  this.servicesDead = servicesEntries.map(() => new Future(() => {}))
69
106
  this.live.use(Promise.all(this.servicesLive))
@@ -92,29 +129,27 @@ export class FlightDeck<S extends string = string> {
92
129
  data.push(chunk instanceof Buffer ? chunk : Buffer.from(chunk))
93
130
  })
94
131
  .on(`end`, () => {
95
- console.log(req.headers)
96
132
  const authHeader = req.headers.authorization
97
133
  try {
98
134
  if (typeof req.url === `undefined`) throw 400
99
135
  if (authHeader !== `Bearer ${secret}`) throw 401
100
136
  const url = new URL(req.url, ORIGIN)
101
- console.log(req.method, url.pathname)
137
+ this.logger.info(req.method, url.pathname)
102
138
  switch (req.method) {
103
139
  case `POST`:
104
140
  {
105
- console.log(`received post, url is ${url.pathname}`)
106
141
  switch (url.pathname) {
107
142
  case `/`:
108
143
  {
109
144
  res.writeHead(200)
110
145
  res.end()
111
- this.fetchLatestRelease()
146
+ this.getLatestRelease()
112
147
  if (
113
148
  toEntries(this.servicesReadyToUpdate).every(
114
149
  ([, isReady]) => isReady,
115
150
  )
116
151
  ) {
117
- console.log(`All services are ready to update!`)
152
+ this.logger.info(`All services are ready to update!`)
118
153
  this.stopAllServices()
119
154
  return
120
155
  }
@@ -141,7 +176,7 @@ export class FlightDeck<S extends string = string> {
141
176
  throw 405
142
177
  }
143
178
  } catch (thrown) {
144
- console.error(thrown, req.url)
179
+ this.logger.error(thrown, req.url)
145
180
  if (typeof thrown === `number`) {
146
181
  res.writeHead(thrown)
147
182
  res.end()
@@ -151,32 +186,32 @@ export class FlightDeck<S extends string = string> {
151
186
  }
152
187
  })
153
188
  }).listen(PORT, () => {
154
- console.log(`Server started on port ${PORT}`)
189
+ this.logger.info(`Server started on port ${PORT}`)
155
190
  })
156
191
 
157
192
  this.startAllServices()
158
193
  }
159
194
 
160
195
  protected startAllServices(): void {
161
- console.log(`Starting all services...`)
196
+ this.logger.info(`Starting all services...`)
162
197
  for (const [serviceName] of toEntries(this.services)) {
163
198
  this.startService(serviceName)
164
199
  }
165
200
  }
166
201
 
167
202
  protected startService(serviceName: S): void {
168
- console.log(
203
+ this.logger.info(
169
204
  `Starting service ${this.options.packageName}::${serviceName}, try ${this.safety}/2...`,
170
205
  )
171
- if (this.safety > 2) {
206
+ if (this.safety >= 2) {
172
207
  throw new Error(`Out of tries...`)
173
208
  }
174
209
  this.safety++
175
210
  if (!existsSync(this.currentServiceDir)) {
176
- console.log(
177
- `Tried to start service but failed: Service ${this.options.packageName} is not yet installed.`,
211
+ this.logger.info(
212
+ `Tried to start service but failed: could not find ${this.currentServiceDir}`,
178
213
  )
179
- this.fetchLatestRelease()
214
+ this.getLatestRelease()
180
215
  this.applyUpdate()
181
216
  this.startService(serviceName)
182
217
 
@@ -197,17 +232,15 @@ export class FlightDeck<S extends string = string> {
197
232
  console,
198
233
  )
199
234
  this.services[serviceName].onAny((...messages) => {
200
- console.log(`${this.options.packageName}::${serviceName} 💬`, ...messages)
235
+ this.logger.info(`💬`, ...messages)
201
236
  })
202
237
  this.services[serviceName].on(`readyToUpdate`, () => {
203
- console.log(
204
- `Service ${this.options.packageName}::${serviceName} is ready to update.`,
205
- )
238
+ this.serviceLoggers[serviceName].info(`Ready to update.`)
206
239
  this.servicesReadyToUpdate[serviceName] = true
207
240
  if (
208
241
  toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)
209
242
  ) {
210
- console.log(`All services are ready to update!`)
243
+ this.logger.info(`All services are ready to update.`)
211
244
  this.stopAllServices()
212
245
  }
213
246
  })
@@ -220,21 +253,15 @@ export class FlightDeck<S extends string = string> {
220
253
  this.dead.use(Promise.all(this.servicesDead))
221
254
  })
222
255
  this.services[serviceName].process.on(`close`, (exitCode) => {
223
- console.log(
224
- `${this.options.packageName}::${serviceName} exited with code ${exitCode}`,
225
- )
256
+ this.serviceLoggers[serviceName].info(`Exited with code ${exitCode}`)
226
257
  this.services[serviceName] = null
227
258
  if (!this.servicesShouldRestart) {
228
- console.log(
229
- `Service ${this.options.packageName}::${serviceName} will not be restarted.`,
230
- )
259
+ this.serviceLoggers[serviceName].info(`Will not be restarted.`)
231
260
  return
232
261
  }
233
262
  const updatesAreReady = existsSync(this.updateServiceDir)
234
263
  if (updatesAreReady) {
235
- console.log(
236
- `${this.options.packageName}::${serviceName} will be updated before startup...`,
237
- )
264
+ this.serviceLoggers[serviceName].info(`Updating before startup...`)
238
265
  this.restartTimes = []
239
266
  this.applyUpdate()
240
267
  this.startService(serviceName)
@@ -247,13 +274,11 @@ export class FlightDeck<S extends string = string> {
247
274
  this.restartTimes.push(now)
248
275
 
249
276
  if (this.restartTimes.length < 5) {
250
- console.log(
251
- `Service ${this.options.packageName}::${serviceName} crashed. Restarting...`,
252
- )
277
+ this.serviceLoggers[serviceName].info(`Crashed. Restarting...`)
253
278
  this.startService(serviceName)
254
279
  } else {
255
- console.log(
256
- `Service ${this.options.packageName}::${serviceName} crashed too many times. Not restarting.`,
280
+ this.serviceLoggers[serviceName].info(
281
+ `Crashed 5 times in 5 minutes. Not restarting.`,
257
282
  )
258
283
  }
259
284
  }
@@ -262,17 +287,14 @@ export class FlightDeck<S extends string = string> {
262
287
  }
263
288
 
264
289
  protected applyUpdate(): void {
265
- console.log(
266
- `Installing latest version of service ${this.options.packageName}...`,
267
- )
268
-
290
+ this.logger.info(`Applying update...`)
269
291
  if (existsSync(this.updateServiceDir)) {
270
292
  const runningServices = toEntries(this.services).filter(
271
293
  ([, service]) => service,
272
294
  )
273
295
  if (runningServices.length > 0) {
274
- console.log(
275
- `Tried to apply update to ${this.options.packageName} but failed. The following services are currently running: [${runningServices.map(([serviceName]) => serviceName).join(`, `)}]`,
296
+ this.logger.error(
297
+ `Tried to apply update but failed. The following services are currently running: [${runningServices.map(([serviceName]) => serviceName).join(`, `)}]`,
276
298
  )
277
299
  return
278
300
  }
@@ -290,27 +312,27 @@ export class FlightDeck<S extends string = string> {
290
312
  this.restartTimes = []
291
313
  this.servicesReadyToUpdate = { ...this.defaultServicesReadyToUpdate }
292
314
  } else {
293
- console.log(`Service ${this.options.packageName} is already up to date.`)
315
+ this.logger.error(
316
+ `Tried to apply update but failed: could not find update directory ${this.updateServiceDir}`,
317
+ )
294
318
  }
295
319
  }
296
320
 
297
- protected fetchLatestRelease(): void {
298
- console.log(
299
- `Downloading latest version of service ${this.options.packageName}...`,
300
- )
321
+ protected getLatestRelease(): void {
322
+ this.logger.info(`Getting latest release...`)
301
323
 
302
324
  try {
303
325
  execSync(this.options.downloadPackageToUpdatesCmd.join(` `))
304
326
  } catch (thrown) {
305
327
  if (thrown instanceof Error) {
306
- console.error(`Failed to fetch the latest release: ${thrown.message}`)
328
+ this.logger.error(`Failed to get the latest release: ${thrown.message}`)
307
329
  }
308
330
  return
309
331
  }
310
332
  }
311
333
 
312
334
  public stopAllServices(): void {
313
- console.log(`Stopping all services...`)
335
+ this.logger.info(`Stopping all services...`)
314
336
  for (const [serviceName] of toEntries(this.services)) {
315
337
  this.stopService(serviceName)
316
338
  }
@@ -318,9 +340,7 @@ export class FlightDeck<S extends string = string> {
318
340
 
319
341
  public stopService(serviceName: S): void {
320
342
  if (this.services[serviceName]) {
321
- console.log(
322
- `Stopping service ${this.options.packageName}::${serviceName}...`,
323
- )
343
+ this.serviceLoggers[serviceName].info(`Stopping service...`)
324
344
  this.services[serviceName].process.kill()
325
345
  this.services[serviceName] = null
326
346
  this.servicesDead[this.serviceIdx[serviceName]].use(Promise.resolve())
@@ -330,8 +350,8 @@ export class FlightDeck<S extends string = string> {
330
350
  }
331
351
  this.live.use(Promise.all(this.servicesLive))
332
352
  } else {
333
- console.error(
334
- `Failed to stop service ${this.options.packageName}::${serviceName}: Service is not running.`,
353
+ this.serviceLoggers[serviceName].error(
354
+ `Tried to stop service, but it wasn't running.`,
335
355
  )
336
356
  }
337
357
  }