flightdeck 0.0.4 → 0.0.5

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.
@@ -32,11 +32,21 @@
32
32
  "flightdeckRootDir": {
33
33
  "type": "string"
34
34
  },
35
- "downloadPackageToUpdatesCmd": {
36
- "type": "array",
37
- "items": {
38
- "type": "string"
39
- }
35
+ "scripts": {
36
+ "type": "object",
37
+ "properties": {
38
+ "download": {
39
+ "type": "string"
40
+ },
41
+ "install": {
42
+ "type": "string"
43
+ }
44
+ },
45
+ "required": [
46
+ "download",
47
+ "install"
48
+ ],
49
+ "additionalProperties": false
40
50
  }
41
51
  },
42
52
  "required": [
@@ -44,7 +54,7 @@
44
54
  "packageName",
45
55
  "services",
46
56
  "flightdeckRootDir",
47
- "downloadPackageToUpdatesCmd"
57
+ "scripts"
48
58
  ],
49
59
  "additionalProperties": false,
50
60
  "$schema": "http://json-schema.org/draft-07/schema#"
@@ -2,12 +2,17 @@
2
2
 
3
3
  // src/flightdeck.bin.ts
4
4
  import * as path from "node:path";
5
- import {cli, optional, parseArrayOption} from "comline";
5
+ import {cli, optional} from "comline";
6
6
  import {z} from "zod";
7
7
 
8
8
  // src/flightdeck.lib.ts
9
9
  import {execSync, spawn} from "node:child_process";
10
- import {existsSync, mkdirSync, renameSync, rmSync} from "node:fs";
10
+ import {
11
+ existsSync,
12
+ mkdirSync,
13
+ rmSync,
14
+ writeFileSync
15
+ } from "node:fs";
11
16
  import {createServer} from "node:http";
12
17
  import {homedir} from "node:os";
13
18
  import {resolve} from "node:path";
@@ -35,9 +40,7 @@ class FlightDeck {
35
40
  dead = new Future(() => {
36
41
  });
37
42
  restartTimes = [];
38
- currentServiceDir;
39
- updateServiceDir;
40
- backupServiceDir;
43
+ persistentStateDir;
41
44
  constructor(options) {
42
45
  this.options = options;
43
46
  const { secret, flightdeckRootDir = resolve(homedir(), `services`) } = options;
@@ -81,9 +84,10 @@ class FlightDeck {
81
84
  }));
82
85
  this.live.use(Promise.all(this.servicesLive));
83
86
  this.dead.use(Promise.all(this.servicesDead));
84
- this.currentServiceDir = resolve(flightdeckRootDir, options.packageName, `current`);
85
- this.backupServiceDir = resolve(flightdeckRootDir, options.packageName, `backup`);
86
- this.updateServiceDir = resolve(flightdeckRootDir, options.packageName, `update`);
87
+ this.persistentStateDir = resolve(flightdeckRootDir, `.state`, options.packageName);
88
+ if (!existsSync(this.persistentStateDir)) {
89
+ mkdirSync(this.persistentStateDir, { recursive: true });
90
+ }
87
91
  createServer((req, res) => {
88
92
  let data = [];
89
93
  req.on(`data`, (chunk) => {
@@ -105,6 +109,16 @@ class FlightDeck {
105
109
  {
106
110
  res.writeHead(200);
107
111
  res.end();
112
+ const installFile = resolve(this.persistentStateDir, `install`);
113
+ const readyFile = resolve(this.persistentStateDir, `ready`);
114
+ if (!existsSync(installFile)) {
115
+ this.logger.info(`Install file does not exist yet. Creating...`);
116
+ writeFileSync(installFile, ``);
117
+ }
118
+ if (existsSync(readyFile)) {
119
+ this.logger.info(`Ready file exists. Removing...`);
120
+ rmSync(readyFile);
121
+ }
108
122
  this.getLatestRelease();
109
123
  if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
110
124
  this.logger.info(`All services are ready to update!`);
@@ -158,17 +172,17 @@ class FlightDeck {
158
172
  throw new Error(`Out of tries...`);
159
173
  }
160
174
  this.safety++;
161
- if (!existsSync(this.currentServiceDir)) {
162
- this.logger.info(`Tried to start service but failed: could not find ${this.currentServiceDir}`);
175
+ const readyFile = resolve(this.persistentStateDir, `ready`);
176
+ if (!existsSync(readyFile)) {
177
+ this.logger.info(`Tried to start service but failed: could not find readyFile: ${readyFile}`);
163
178
  this.getLatestRelease();
164
179
  this.applyUpdate();
165
180
  this.startService(serviceName);
166
181
  return;
167
182
  }
168
183
  const [executable, ...args] = this.options.services[serviceName].run;
169
- const program = executable.startsWith(`./`) ? resolve(this.currentServiceDir, executable) : executable;
170
- const serviceProcess = spawn(program, args, {
171
- cwd: this.currentServiceDir,
184
+ const serviceProcess = spawn(executable, args, {
185
+ cwd: this.options.flightdeckRootDir,
172
186
  env: import.meta.env
173
187
  });
174
188
  this.services[serviceName] = new ChildSocket(serviceProcess, `${this.options.packageName}::${serviceName}`, console);
@@ -200,7 +214,8 @@ class FlightDeck {
200
214
  this.serviceLoggers[serviceName].info(`Will not be restarted.`);
201
215
  return;
202
216
  }
203
- const updatesAreReady = existsSync(this.updateServiceDir);
217
+ const installFile = resolve(this.persistentStateDir, `install`);
218
+ const updatesAreReady = existsSync(installFile);
204
219
  if (updatesAreReady) {
205
220
  this.serviceLoggers[serviceName].info(`Updating before startup...`);
206
221
  this.restartTimes = [];
@@ -222,32 +237,28 @@ class FlightDeck {
222
237
  this.safety = 0;
223
238
  }
224
239
  applyUpdate() {
225
- this.logger.info(`Applying update...`);
226
- if (existsSync(this.updateServiceDir)) {
227
- const runningServices = toEntries(this.services).filter(([, service]) => service);
228
- if (runningServices.length > 0) {
229
- this.logger.error(`Tried to apply update but failed. The following services are currently running: [${runningServices.map(([serviceName]) => serviceName).join(`, `)}]`);
230
- return;
240
+ this.logger.info(`Installing...`);
241
+ try {
242
+ execSync(this.options.scripts.install);
243
+ const installFile = resolve(this.persistentStateDir, `install`);
244
+ if (existsSync(installFile)) {
245
+ rmSync(installFile);
231
246
  }
232
- if (existsSync(this.currentServiceDir)) {
233
- if (!existsSync(this.backupServiceDir)) {
234
- mkdirSync(this.backupServiceDir, { recursive: true });
235
- } else {
236
- rmSync(this.backupServiceDir, { recursive: true });
237
- }
238
- renameSync(this.currentServiceDir, this.backupServiceDir);
247
+ const readyFile = resolve(this.persistentStateDir, `ready`);
248
+ writeFileSync(readyFile, ``);
249
+ this.logger.info(`Installed!`);
250
+ } catch (thrown) {
251
+ if (thrown instanceof Error) {
252
+ this.logger.error(`Failed to get the latest release: ${thrown.message}`);
239
253
  }
240
- renameSync(this.updateServiceDir, this.currentServiceDir);
241
- this.restartTimes = [];
242
- this.servicesReadyToUpdate = { ...this.defaultServicesReadyToUpdate };
243
- } else {
244
- this.logger.error(`Tried to apply update but failed: could not find update directory ${this.updateServiceDir}`);
254
+ return;
245
255
  }
246
256
  }
247
257
  getLatestRelease() {
248
- this.logger.info(`Getting latest release...`);
258
+ this.logger.info(`Downloading...`);
249
259
  try {
250
- execSync(this.options.downloadPackageToUpdatesCmd.join(` `));
260
+ execSync(this.options.scripts.download);
261
+ this.logger.info(`Downloaded!`);
251
262
  } catch (thrown) {
252
263
  if (thrown instanceof Error) {
253
264
  this.logger.error(`Failed to get the latest release: ${thrown.message}`);
@@ -279,6 +290,7 @@ class FlightDeck {
279
290
  }
280
291
  }
281
292
  shutdown() {
293
+ this.logger.info(`Shutting down...`);
282
294
  this.servicesShouldRestart = false;
283
295
  this.stopAllServices();
284
296
  }
@@ -291,7 +303,10 @@ var FLIGHTDECK_MANUAL = {
291
303
  packageName: z.string(),
292
304
  services: z.record(z.object({ run: z.array(z.string()), waitFor: z.boolean() })),
293
305
  flightdeckRootDir: z.string(),
294
- downloadPackageToUpdatesCmd: z.array(z.string())
306
+ scripts: z.object({
307
+ download: z.string(),
308
+ install: z.string()
309
+ })
295
310
  }),
296
311
  options: {
297
312
  secret: {
@@ -319,12 +334,12 @@ var FLIGHTDECK_MANUAL = {
319
334
  description: `Directory where the service is stored.`,
320
335
  example: `--flightdeckRootDir=\"./services/sample/repo/my-app/current\"`
321
336
  },
322
- downloadPackageToUpdatesCmd: {
323
- flag: `u`,
337
+ scripts: {
338
+ flag: `r`,
324
339
  required: true,
325
- description: `Command to update the service.`,
326
- example: `--downloadPackageToUpdatesCmd=\"./app\"`,
327
- parse: parseArrayOption
340
+ description: `Map of scripts to run.`,
341
+ example: `--scripts="{\\"download\\":\\"npm i",\\"install\\":\\"npm run build\\"}"`,
342
+ parse: JSON.parse
328
343
  }
329
344
  }
330
345
  };
@@ -32,11 +32,21 @@
32
32
  "flightdeckRootDir": {
33
33
  "type": "string"
34
34
  },
35
- "downloadPackageToUpdatesCmd": {
36
- "type": "array",
37
- "items": {
38
- "type": "string"
39
- }
35
+ "scripts": {
36
+ "type": "object",
37
+ "properties": {
38
+ "download": {
39
+ "type": "string"
40
+ },
41
+ "install": {
42
+ "type": "string"
43
+ }
44
+ },
45
+ "required": [
46
+ "download",
47
+ "install"
48
+ ],
49
+ "additionalProperties": false
40
50
  }
41
51
  },
42
52
  "required": [
@@ -44,7 +54,7 @@
44
54
  "packageName",
45
55
  "services",
46
56
  "flightdeckRootDir",
47
- "downloadPackageToUpdatesCmd"
57
+ "scripts"
48
58
  ],
49
59
  "additionalProperties": false,
50
60
  "$schema": "http://json-schema.org/draft-07/schema#"
package/dist/lib.d.ts CHANGED
@@ -11,7 +11,10 @@ type FlightDeckOptions<S extends string = string> = {
11
11
  waitFor: boolean;
12
12
  };
13
13
  };
14
- downloadPackageToUpdatesCmd: string[];
14
+ scripts: {
15
+ download: string;
16
+ install: string;
17
+ };
15
18
  flightdeckRootDir?: string | undefined;
16
19
  };
17
20
  declare class FlightDeck<S extends string = string> {
@@ -45,9 +48,7 @@ declare class FlightDeck<S extends string = string> {
45
48
  live: Future<unknown>;
46
49
  dead: Future<unknown>;
47
50
  protected restartTimes: number[];
48
- readonly currentServiceDir: string;
49
- readonly updateServiceDir: string;
50
- readonly backupServiceDir: string;
51
+ protected persistentStateDir: string;
51
52
  constructor(options: FlightDeckOptions<S>);
52
53
  protected startAllServices(): void;
53
54
  protected startService(serviceName: S): void;
package/dist/lib.js CHANGED
@@ -11,7 +11,12 @@ var __export = (target, all) => {
11
11
 
12
12
  // src/flightdeck.lib.ts
13
13
  import {execSync, spawn} from "node:child_process";
14
- import {existsSync, mkdirSync, renameSync, rmSync} from "node:fs";
14
+ import {
15
+ existsSync,
16
+ mkdirSync,
17
+ rmSync,
18
+ writeFileSync
19
+ } from "node:fs";
15
20
  import {createServer} from "node:http";
16
21
  import {homedir} from "node:os";
17
22
  import {resolve} from "node:path";
@@ -39,9 +44,7 @@ class FlightDeck {
39
44
  dead = new Future(() => {
40
45
  });
41
46
  restartTimes = [];
42
- currentServiceDir;
43
- updateServiceDir;
44
- backupServiceDir;
47
+ persistentStateDir;
45
48
  constructor(options) {
46
49
  this.options = options;
47
50
  const { secret, flightdeckRootDir = resolve(homedir(), `services`) } = options;
@@ -85,9 +88,10 @@ class FlightDeck {
85
88
  }));
86
89
  this.live.use(Promise.all(this.servicesLive));
87
90
  this.dead.use(Promise.all(this.servicesDead));
88
- this.currentServiceDir = resolve(flightdeckRootDir, options.packageName, `current`);
89
- this.backupServiceDir = resolve(flightdeckRootDir, options.packageName, `backup`);
90
- this.updateServiceDir = resolve(flightdeckRootDir, options.packageName, `update`);
91
+ this.persistentStateDir = resolve(flightdeckRootDir, `.state`, options.packageName);
92
+ if (!existsSync(this.persistentStateDir)) {
93
+ mkdirSync(this.persistentStateDir, { recursive: true });
94
+ }
91
95
  createServer((req, res) => {
92
96
  let data = [];
93
97
  req.on(`data`, (chunk) => {
@@ -109,6 +113,16 @@ class FlightDeck {
109
113
  {
110
114
  res.writeHead(200);
111
115
  res.end();
116
+ const installFile = resolve(this.persistentStateDir, `install`);
117
+ const readyFile = resolve(this.persistentStateDir, `ready`);
118
+ if (!existsSync(installFile)) {
119
+ this.logger.info(`Install file does not exist yet. Creating...`);
120
+ writeFileSync(installFile, ``);
121
+ }
122
+ if (existsSync(readyFile)) {
123
+ this.logger.info(`Ready file exists. Removing...`);
124
+ rmSync(readyFile);
125
+ }
112
126
  this.getLatestRelease();
113
127
  if (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {
114
128
  this.logger.info(`All services are ready to update!`);
@@ -162,17 +176,17 @@ class FlightDeck {
162
176
  throw new Error(`Out of tries...`);
163
177
  }
164
178
  this.safety++;
165
- if (!existsSync(this.currentServiceDir)) {
166
- this.logger.info(`Tried to start service but failed: could not find ${this.currentServiceDir}`);
179
+ const readyFile = resolve(this.persistentStateDir, `ready`);
180
+ if (!existsSync(readyFile)) {
181
+ this.logger.info(`Tried to start service but failed: could not find readyFile: ${readyFile}`);
167
182
  this.getLatestRelease();
168
183
  this.applyUpdate();
169
184
  this.startService(serviceName);
170
185
  return;
171
186
  }
172
187
  const [executable, ...args] = this.options.services[serviceName].run;
173
- const program = executable.startsWith(`./`) ? resolve(this.currentServiceDir, executable) : executable;
174
- const serviceProcess = spawn(program, args, {
175
- cwd: this.currentServiceDir,
188
+ const serviceProcess = spawn(executable, args, {
189
+ cwd: this.options.flightdeckRootDir,
176
190
  env: import.meta.env
177
191
  });
178
192
  this.services[serviceName] = new ChildSocket(serviceProcess, `${this.options.packageName}::${serviceName}`, console);
@@ -204,7 +218,8 @@ class FlightDeck {
204
218
  this.serviceLoggers[serviceName].info(`Will not be restarted.`);
205
219
  return;
206
220
  }
207
- const updatesAreReady = existsSync(this.updateServiceDir);
221
+ const installFile = resolve(this.persistentStateDir, `install`);
222
+ const updatesAreReady = existsSync(installFile);
208
223
  if (updatesAreReady) {
209
224
  this.serviceLoggers[serviceName].info(`Updating before startup...`);
210
225
  this.restartTimes = [];
@@ -226,32 +241,28 @@ class FlightDeck {
226
241
  this.safety = 0;
227
242
  }
228
243
  applyUpdate() {
229
- this.logger.info(`Applying update...`);
230
- if (existsSync(this.updateServiceDir)) {
231
- const runningServices = toEntries(this.services).filter(([, service]) => service);
232
- if (runningServices.length > 0) {
233
- this.logger.error(`Tried to apply update but failed. The following services are currently running: [${runningServices.map(([serviceName]) => serviceName).join(`, `)}]`);
234
- return;
244
+ this.logger.info(`Installing...`);
245
+ try {
246
+ execSync(this.options.scripts.install);
247
+ const installFile = resolve(this.persistentStateDir, `install`);
248
+ if (existsSync(installFile)) {
249
+ rmSync(installFile);
235
250
  }
236
- if (existsSync(this.currentServiceDir)) {
237
- if (!existsSync(this.backupServiceDir)) {
238
- mkdirSync(this.backupServiceDir, { recursive: true });
239
- } else {
240
- rmSync(this.backupServiceDir, { recursive: true });
241
- }
242
- renameSync(this.currentServiceDir, this.backupServiceDir);
251
+ const readyFile = resolve(this.persistentStateDir, `ready`);
252
+ writeFileSync(readyFile, ``);
253
+ this.logger.info(`Installed!`);
254
+ } catch (thrown) {
255
+ if (thrown instanceof Error) {
256
+ this.logger.error(`Failed to get the latest release: ${thrown.message}`);
243
257
  }
244
- renameSync(this.updateServiceDir, this.currentServiceDir);
245
- this.restartTimes = [];
246
- this.servicesReadyToUpdate = { ...this.defaultServicesReadyToUpdate };
247
- } else {
248
- this.logger.error(`Tried to apply update but failed: could not find update directory ${this.updateServiceDir}`);
258
+ return;
249
259
  }
250
260
  }
251
261
  getLatestRelease() {
252
- this.logger.info(`Getting latest release...`);
262
+ this.logger.info(`Downloading...`);
253
263
  try {
254
- execSync(this.options.downloadPackageToUpdatesCmd.join(` `));
264
+ execSync(this.options.scripts.download);
265
+ this.logger.info(`Downloaded!`);
255
266
  } catch (thrown) {
256
267
  if (thrown instanceof Error) {
257
268
  this.logger.error(`Failed to get the latest release: ${thrown.message}`);
@@ -283,6 +294,7 @@ class FlightDeck {
283
294
  }
284
295
  }
285
296
  shutdown() {
297
+ this.logger.info(`Shutting down...`);
286
298
  this.servicesShouldRestart = false;
287
299
  this.stopAllServices();
288
300
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flightdeck",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "license": "MIT",
5
5
  "author": {
6
6
  "name": "Jeremy Banka",
@@ -31,7 +31,7 @@
31
31
  "@types/tmp": "0.2.6",
32
32
  "concurrently": "9.0.1",
33
33
  "tmp": "0.2.3",
34
- "tsup": "8.2.4",
34
+ "tsup": "8.3.0",
35
35
  "rimraf": "6.0.1",
36
36
  "vitest": "2.1.1"
37
37
  },
@@ -6,8 +6,8 @@ import type { OptionsGroup } from "comline"
6
6
  import { cli, optional, parseArrayOption } from "comline"
7
7
  import { z } from "zod"
8
8
 
9
- import type { FlightDeckOptions } from "~/packages/flightdeck/src/flightdeck.lib"
10
- import { FlightDeck } from "~/packages/flightdeck/src/flightdeck.lib"
9
+ import type { FlightDeckOptions } from "./flightdeck.lib"
10
+ import { FlightDeck } from "./flightdeck.lib"
11
11
 
12
12
  const FLIGHTDECK_MANUAL = {
13
13
  optionsSchema: z.object({
@@ -17,7 +17,10 @@ const FLIGHTDECK_MANUAL = {
17
17
  z.object({ run: z.array(z.string()), waitFor: z.boolean() }),
18
18
  ),
19
19
  flightdeckRootDir: z.string(),
20
- downloadPackageToUpdatesCmd: z.array(z.string()),
20
+ scripts: z.object({
21
+ download: z.string(),
22
+ install: z.string(),
23
+ }),
21
24
  }),
22
25
  options: {
23
26
  secret: {
@@ -45,12 +48,12 @@ const FLIGHTDECK_MANUAL = {
45
48
  description: `Directory where the service is stored.`,
46
49
  example: `--flightdeckRootDir=\"./services/sample/repo/my-app/current\"`,
47
50
  },
48
- downloadPackageToUpdatesCmd: {
49
- flag: `u`,
51
+ scripts: {
52
+ flag: `r`,
50
53
  required: true,
51
- description: `Command to update the service.`,
52
- example: `--downloadPackageToUpdatesCmd=\"./app\"`,
53
- parse: parseArrayOption,
54
+ description: `Map of scripts to run.`,
55
+ example: `--scripts="{\\"download\\":\\"npm i",\\"install\\":\\"npm run build\\"}"`,
56
+ parse: JSON.parse,
54
57
  },
55
58
  },
56
59
  } satisfies OptionsGroup<FlightDeckOptions>
@@ -1,5 +1,11 @@
1
1
  import { execSync, spawn } from "node:child_process"
2
- import { existsSync, mkdirSync, renameSync, rmSync } from "node:fs"
2
+ import {
3
+ existsSync,
4
+ mkdirSync,
5
+ renameSync,
6
+ rmSync,
7
+ writeFileSync,
8
+ } from "node:fs"
3
9
  import type { Server } from "node:http"
4
10
  import { createServer } from "node:http"
5
11
  import { homedir } from "node:os"
@@ -13,7 +19,10 @@ export type FlightDeckOptions<S extends string = string> = {
13
19
  secret: string
14
20
  packageName: string
15
21
  services: { [service in S]: { run: string[]; waitFor: boolean } }
16
- downloadPackageToUpdatesCmd: string[]
22
+ scripts: {
23
+ download: string
24
+ install: string
25
+ }
17
26
  flightdeckRootDir?: string | undefined
18
27
  }
19
28
 
@@ -46,9 +55,7 @@ export class FlightDeck<S extends string = string> {
46
55
 
47
56
  protected restartTimes: number[] = []
48
57
 
49
- public readonly currentServiceDir: string
50
- public readonly updateServiceDir: string
51
- public readonly backupServiceDir: string
58
+ protected persistentStateDir: string
52
59
 
53
60
  public constructor(public readonly options: FlightDeckOptions<S>) {
54
61
  const { secret, flightdeckRootDir = resolve(homedir(), `services`) } =
@@ -106,21 +113,14 @@ export class FlightDeck<S extends string = string> {
106
113
  this.live.use(Promise.all(this.servicesLive))
107
114
  this.dead.use(Promise.all(this.servicesDead))
108
115
 
109
- this.currentServiceDir = resolve(
110
- flightdeckRootDir,
111
- options.packageName,
112
- `current`,
113
- )
114
- this.backupServiceDir = resolve(
115
- flightdeckRootDir,
116
- options.packageName,
117
- `backup`,
118
- )
119
- this.updateServiceDir = resolve(
116
+ this.persistentStateDir = resolve(
120
117
  flightdeckRootDir,
118
+ `.state`,
121
119
  options.packageName,
122
- `update`,
123
120
  )
121
+ if (!existsSync(this.persistentStateDir)) {
122
+ mkdirSync(this.persistentStateDir, { recursive: true })
123
+ }
124
124
 
125
125
  createServer((req, res) => {
126
126
  let data: Uint8Array[] = []
@@ -143,6 +143,24 @@ export class FlightDeck<S extends string = string> {
143
143
  {
144
144
  res.writeHead(200)
145
145
  res.end()
146
+ const installFile = resolve(
147
+ this.persistentStateDir,
148
+ `install`,
149
+ )
150
+ const readyFile = resolve(
151
+ this.persistentStateDir,
152
+ `ready`,
153
+ )
154
+ if (!existsSync(installFile)) {
155
+ this.logger.info(
156
+ `Install file does not exist yet. Creating...`,
157
+ )
158
+ writeFileSync(installFile, ``)
159
+ }
160
+ if (existsSync(readyFile)) {
161
+ this.logger.info(`Ready file exists. Removing...`)
162
+ rmSync(readyFile)
163
+ }
146
164
  this.getLatestRelease()
147
165
  if (
148
166
  toEntries(this.servicesReadyToUpdate).every(
@@ -207,9 +225,10 @@ export class FlightDeck<S extends string = string> {
207
225
  throw new Error(`Out of tries...`)
208
226
  }
209
227
  this.safety++
210
- if (!existsSync(this.currentServiceDir)) {
228
+ const readyFile = resolve(this.persistentStateDir, `ready`)
229
+ if (!existsSync(readyFile)) {
211
230
  this.logger.info(
212
- `Tried to start service but failed: could not find ${this.currentServiceDir}`,
231
+ `Tried to start service but failed: could not find readyFile: ${readyFile}`,
213
232
  )
214
233
  this.getLatestRelease()
215
234
  this.applyUpdate()
@@ -219,11 +238,8 @@ export class FlightDeck<S extends string = string> {
219
238
  }
220
239
 
221
240
  const [executable, ...args] = this.options.services[serviceName].run
222
- const program = executable.startsWith(`./`)
223
- ? resolve(this.currentServiceDir, executable)
224
- : executable
225
- const serviceProcess = spawn(program, args, {
226
- cwd: this.currentServiceDir,
241
+ const serviceProcess = spawn(executable, args, {
242
+ cwd: this.options.flightdeckRootDir,
227
243
  env: import.meta.env,
228
244
  })
229
245
  this.services[serviceName] = new ChildSocket(
@@ -259,7 +275,8 @@ export class FlightDeck<S extends string = string> {
259
275
  this.serviceLoggers[serviceName].info(`Will not be restarted.`)
260
276
  return
261
277
  }
262
- const updatesAreReady = existsSync(this.updateServiceDir)
278
+ const installFile = resolve(this.persistentStateDir, `install`)
279
+ const updatesAreReady = existsSync(installFile)
263
280
  if (updatesAreReady) {
264
281
  this.serviceLoggers[serviceName].info(`Updating before startup...`)
265
282
  this.restartTimes = []
@@ -287,42 +304,31 @@ export class FlightDeck<S extends string = string> {
287
304
  }
288
305
 
289
306
  protected applyUpdate(): void {
290
- this.logger.info(`Applying update...`)
291
- if (existsSync(this.updateServiceDir)) {
292
- const runningServices = toEntries(this.services).filter(
293
- ([, service]) => service,
294
- )
295
- if (runningServices.length > 0) {
296
- this.logger.error(
297
- `Tried to apply update but failed. The following services are currently running: [${runningServices.map(([serviceName]) => serviceName).join(`, `)}]`,
298
- )
299
- return
300
- }
307
+ this.logger.info(`Installing...`)
301
308
 
302
- if (existsSync(this.currentServiceDir)) {
303
- if (!existsSync(this.backupServiceDir)) {
304
- mkdirSync(this.backupServiceDir, { recursive: true })
305
- } else {
306
- rmSync(this.backupServiceDir, { recursive: true })
307
- }
308
- renameSync(this.currentServiceDir, this.backupServiceDir)
309
+ try {
310
+ execSync(this.options.scripts.install)
311
+ const installFile = resolve(this.persistentStateDir, `install`)
312
+ if (existsSync(installFile)) {
313
+ rmSync(installFile)
309
314
  }
310
-
311
- renameSync(this.updateServiceDir, this.currentServiceDir)
312
- this.restartTimes = []
313
- this.servicesReadyToUpdate = { ...this.defaultServicesReadyToUpdate }
314
- } else {
315
- this.logger.error(
316
- `Tried to apply update but failed: could not find update directory ${this.updateServiceDir}`,
317
- )
315
+ const readyFile = resolve(this.persistentStateDir, `ready`)
316
+ writeFileSync(readyFile, ``)
317
+ this.logger.info(`Installed!`)
318
+ } catch (thrown) {
319
+ if (thrown instanceof Error) {
320
+ this.logger.error(`Failed to get the latest release: ${thrown.message}`)
321
+ }
322
+ return
318
323
  }
319
324
  }
320
325
 
321
326
  protected getLatestRelease(): void {
322
- this.logger.info(`Getting latest release...`)
327
+ this.logger.info(`Downloading...`)
323
328
 
324
329
  try {
325
- execSync(this.options.downloadPackageToUpdatesCmd.join(` `))
330
+ execSync(this.options.scripts.download)
331
+ this.logger.info(`Downloaded!`)
326
332
  } catch (thrown) {
327
333
  if (thrown instanceof Error) {
328
334
  this.logger.error(`Failed to get the latest release: ${thrown.message}`)
@@ -357,6 +363,7 @@ export class FlightDeck<S extends string = string> {
357
363
  }
358
364
 
359
365
  public shutdown(): void {
366
+ this.logger.info(`Shutting down...`)
360
367
  this.servicesShouldRestart = false
361
368
  this.stopAllServices()
362
369
  }