Skip to content

Commit d902941

Browse files
hakalbclaude
andcommitted
fix(fly-deployment-action): use machine update to clear MAINTENANCE_MODE
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c8aa7aa commit d902941

2 files changed

Lines changed: 60 additions & 12 deletions

File tree

packages/fly-deployment-action/src/lib/utils/run-deploy-apps.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -223,27 +223,29 @@ export const runDeployApps = async (options: {
223223
}
224224
}
225225

226-
// Fly.io suspends idle machines (auto_stop_machines = 'suspend') by
227-
// snapshotting their memory. The unset triggers a machine replacement for
228-
// running machines but suspended machines may resume from the snapshot
229-
// and still carry the old MAINTENANCE_MODE env. Force-restart all
230-
// machines so they pick up the fresh environment.
226+
// MAINTENANCE_MODE is baked into the machine's per-machine config.env
227+
// from when it was a Fly secret during deployment. fly secrets unset
228+
// removes it from the secrets store but NOT from the machine-level config.
229+
// fly machine update creates a new machine version with the env var cleared,
230+
// forcing a cold restart — not a resume from a suspended memory snapshot.
231231
if (unsetSucceeded) {
232232
const statusAfterUnset = await fly.status({ app: appName });
233233
if (statusAfterUnset?.machines) {
234234
for (const machine of statusAfterUnset.machines) {
235235
try {
236236
core.info(
237-
`Restarting machine '${machine.id}' for '${appName}' to clear stale env`
237+
`Clearing MAINTENANCE_MODE env for machine '${machine.id}' of '${appName}'`
238238
);
239-
await fly.machines.restart(appName, machine.id);
240-
} catch (restartErr) {
239+
await fly.machines.update(appName, machine.id, {
240+
env: { MAINTENANCE_MODE: '' }
241+
});
242+
} catch (updateErr) {
241243
const msg =
242-
restartErr instanceof Error
243-
? restartErr.message
244-
: String(restartErr);
244+
updateErr instanceof Error
245+
? updateErr.message
246+
: String(updateErr);
245247
core.warning(
246-
`Failed to restart machine '${machine.id}' for '${appName}': ${msg}`
248+
`Failed to clear MAINTENANCE_MODE for machine '${machine.id}' of '${appName}': ${msg}`
247249
);
248250
}
249251
}

packages/fly-node/src/lib/fly.class.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,32 @@ export class Fly {
462462
} catch (error) {
463463
throw new Error(`[restart machine] something broke\n${error}`);
464464
}
465+
},
466+
/**
467+
* Update a machine's per-machine configuration (env vars, etc.).
468+
*
469+
* Unlike `restart`, this creates a new machine version from the updated config
470+
* and forces a cold start — it does NOT resume from a suspended memory snapshot.
471+
*
472+
* @param app - The name of the application
473+
* @param machineId - The machine ID to update
474+
* @param options - Fields to update; env values set to `''` clear that var
475+
* @throws An error if the machine cannot be updated
476+
*/
477+
update: async (
478+
app: string,
479+
machineId: string,
480+
options: { env?: Record<string, string> }
481+
): Promise<void> => {
482+
try {
483+
await this.ensureInitialized();
484+
await this.updateMachine(app, machineId, options);
485+
this.logger.info(
486+
`Machine '${machineId}' in app '${app}' was updated`
487+
);
488+
} catch (error) {
489+
throw new Error(`[update machine] something broke\n${error}`);
490+
}
465491
}
466492
};
467493
postgres = {
@@ -1037,6 +1063,26 @@ export class Fly {
10371063
await this.startMachine(app, machineId);
10381064
}
10391065

1066+
/**
1067+
* @private
1068+
* Update a machine's per-machine config (env vars, etc.).
1069+
* Creates a new machine version so the machine cold-starts from the updated config,
1070+
* bypassing any suspended memory snapshot.
1071+
* @throws An error if the machine cannot be updated
1072+
*/
1073+
private async updateMachine(
1074+
app: string,
1075+
machineId: string,
1076+
options: { env?: Record<string, string> }
1077+
): Promise<void> {
1078+
const args = ['machine', 'update', machineId, '--app', app];
1079+
for (const [key, value] of Object.entries(options.env || {})) {
1080+
args.push('--env', `${key}=${this.safeArg(value)}`);
1081+
}
1082+
args.push('--yes');
1083+
await this.execFly(args);
1084+
}
1085+
10401086
/**
10411087
* @private
10421088
* @returns A list of all applications or `null` if listing applications fails and `nullOnError` is used

0 commit comments

Comments
 (0)