| ... | ... | @@ -635,17 +635,9 @@ export const reconcileDesiredCloudLink = Effect.fn("environment.cloud.reconcileD |
| 635 | 635 | }, |
| 636 | 636 | ); |
| 637 | 637 | |
| 638 | | // The launcher records an in-flight update in its durable state file. Read |
| 639 | | // straight from disk: the launcher owns that file, and this runs while the |
| 640 | | // server is tearing down. Unreadable or unparseable state means no pending |
| 641 | | // update — the release then proceeds as before. |
| 642 | | // |
| 643 | | // A pending update alone is not proof a replacement server is coming: an |
| 644 | | // explicit launcher stop (`t3 service uninstall`, `systemctl stop`) during |
| 645 | | // the pending window also tears this server down, with nothing starting |
| 646 | | // afterwards. The launcher marks that case with a stop-marker file just |
| 647 | | // before it signals the child, so pending + no marker is the update handoff. |
| 648 | | export const pendingUpdateHandoffExists = Effect.gen(function* () { |
| 638 | // The launcher owns this durable state, so read it directly both when a trial |
| 639 | // decides whether it owns pre-activation cleanup and while a server tears down. |
| 640 | export const pendingServiceUpdateExists = Effect.gen(function* () { |
| 649 | 641 | const config = yield* ServerConfig.ServerConfig; |
| 650 | 642 | const fs = yield* FileSystem.FileSystem; |
| 651 | 643 | const path = yield* Path.Path; |
| ... | ... | @@ -653,9 +645,21 @@ export const pendingUpdateHandoffExists = Effect.gen(function* () { |
| 653 | 645 | const stateText = yield* fs |
| 654 | 646 | .readFileString(path.join(runtimeDir, SERVICE_STATE_FILE)) |
| 655 | 647 | .pipe(Effect.option); |
| 656 | | if (Option.isNone(stateText) || !serviceStateHasPendingUpdate(stateText.value)) { |
| 648 | return Option.isSome(stateText) && serviceStateHasPendingUpdate(stateText.value); |
| 649 | }); |
| 650 | |
| 651 | // A pending update alone is not proof a replacement server is coming: an |
| 652 | // explicit launcher stop (`t3 service uninstall`, `systemctl stop`) during |
| 653 | // the pending window also tears this server down. The launcher marks that case |
| 654 | // just before it signals the child, so pending + no marker is the handoff. |
| 655 | const pendingUpdateHandoffExists = Effect.gen(function* () { |
| 656 | if (!(yield* pendingServiceUpdateExists)) { |
| 657 | 657 | return false; |
| 658 | 658 | } |
| 659 | const config = yield* ServerConfig.ServerConfig; |
| 660 | const fs = yield* FileSystem.FileSystem; |
| 661 | const path = yield* Path.Path; |
| 662 | const runtimeDir = path.join(config.baseDir, "runtime"); |
| 659 | 663 | const stopping = yield* fs |
| 660 | 664 | .exists(path.join(runtimeDir, SERVICE_STOP_MARKER_FILE)) |
| 661 | 665 | .pipe(Effect.orElseSucceed(() => false)); |