merge-steward 0.32.3 → 0.32.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.
@@ -0,0 +1,11 @@
1
+ export interface ShutdownLogger {
2
+ info(bindings: Record<string, unknown>, message: string): void;
3
+ warn(bindings: Record<string, unknown>, message: string): void;
4
+ error(bindings: Record<string, unknown>, message: string): void;
5
+ }
6
+ export declare function createGracefulShutdown(options: {
7
+ service: string;
8
+ logger: ShutdownLogger;
9
+ cleanup: () => Promise<void>;
10
+ terminate?: (code: number) => void;
11
+ }): (trigger: string) => Promise<void>;
@@ -0,0 +1,29 @@
1
+ export function createGracefulShutdown(options) {
2
+ let shutdownPromise;
3
+ let firstTrigger;
4
+ const terminate = options.terminate ?? ((code) => {
5
+ process.exitCode = code;
6
+ setImmediate(() => process.exit(code));
7
+ });
8
+ return (trigger) => {
9
+ if (shutdownPromise) {
10
+ options.logger.warn({ service: options.service, trigger, firstTrigger }, "Shutdown already in progress");
11
+ return shutdownPromise;
12
+ }
13
+ firstTrigger = trigger;
14
+ options.logger.info({ service: options.service, trigger }, "Shutdown requested");
15
+ shutdownPromise = options.cleanup()
16
+ .then(() => {
17
+ options.logger.info({ service: options.service, trigger }, "Shutdown complete");
18
+ })
19
+ .catch((error) => {
20
+ options.logger.error({
21
+ service: options.service,
22
+ trigger,
23
+ error: error instanceof Error ? error.message : String(error),
24
+ }, "Shutdown failed");
25
+ terminate(1);
26
+ });
27
+ return shutdownPromise;
28
+ };
29
+ }
@@ -18,4 +18,4 @@ export declare function buildMultiRepoHttpServer(options: {
18
18
  }): Promise<ServiceGitHubRepoAccessResponse>;
19
19
  };
20
20
  logger: Logger;
21
- }): Promise<fastify.FastifyInstance<import("node:http").Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>, import("node:http").IncomingMessage, import("node:http").ServerResponse<import("node:http").IncomingMessage>, Logger, fastify.FastifyTypeProviderDefault>>;
21
+ }): Promise<fastify.FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, Logger, fastify.FastifyTypeProviderDefault>>;
@@ -1,4 +1,4 @@
1
- import fastify from "fastify";
1
+ import fastify, { LogController } from "fastify";
2
2
  import rawBody from "fastify-raw-body";
3
3
  import { z } from "zod";
4
4
  import { verifySignature, normalizeWebhook, processWebhookEvent } from "./webhook-handler.js";
@@ -33,7 +33,7 @@ export async function buildMultiRepoHttpServer(options) {
33
33
  for (const record of repos.values()) {
34
34
  byRepoId.set(record.config.repoId, record);
35
35
  }
36
- const app = fastify({ loggerInstance: logger, disableRequestLogging: true });
36
+ const app = fastify({ loggerInstance: logger, logController: new LogController({ disableRequestLogging: true }) });
37
37
  await app.register(rawBody, { runFirst: true });
38
38
  // --- Health ---
39
39
  app.get("/health", async () => ({
package/dist/server.js CHANGED
@@ -11,6 +11,7 @@ import { buildMultiRepoHttpServer } from "./http-multi.js";
11
11
  import { loadAllRepoConfigs } from "./install.js";
12
12
  import { parseHomeConfigObject } from "./steward-home.js";
13
13
  import { getMergeStewardPathLayout } from "./runtime-paths.js";
14
+ import { createGracefulShutdown } from "./graceful-shutdown.js";
14
15
  import { createGitHubAppTokenManager, generateJwt, resolveGitHubAuthConfig, resolveAppSlug } from "./github-auth.js";
15
16
  import { discoverRepoSettings } from "./github-repo-discovery.js";
16
17
  import { resolveSecretWithSource } from "./resolve-secret.js";
@@ -193,24 +194,26 @@ export async function startMultiServer() {
193
194
  },
194
195
  logger,
195
196
  });
196
- const shutdown = async () => {
197
- shuttingDown = true;
198
- logger.info("Shutting down...");
199
- githubAppTokenManager?.stop();
200
- setRuntimeGitHubAuthProvider(undefined);
201
- for (const record of repos.values()) {
202
- const inst = record.instance;
203
- if (!inst) {
204
- continue;
197
+ const shutdown = createGracefulShutdown({
198
+ service: "merge-steward",
199
+ logger,
200
+ cleanup: async () => {
201
+ shuttingDown = true;
202
+ githubAppTokenManager?.stop();
203
+ setRuntimeGitHubAuthProvider(undefined);
204
+ for (const record of repos.values()) {
205
+ const inst = record.instance;
206
+ if (!inst) {
207
+ continue;
208
+ }
209
+ await inst.service.stop();
210
+ inst.store.close();
205
211
  }
206
- await inst.service.stop();
207
- inst.store.close();
208
- }
209
- await app.close();
210
- process.exit(0);
211
- };
212
- process.on("SIGTERM", () => void shutdown());
213
- process.on("SIGINT", () => void shutdown());
212
+ await app.close();
213
+ },
214
+ });
215
+ process.once("SIGTERM", () => void shutdown("SIGTERM"));
216
+ process.once("SIGINT", () => void shutdown("SIGINT"));
214
217
  await app.listen({ host: bind, port });
215
218
  logger.info({ bind, port, repos: repos.size }, "merge-steward listening");
216
219
  const initializeRepo = async (record) => {
@@ -21,7 +21,7 @@ LoadCredentialEncrypted=merge-steward-webhook-secret:/etc/credstore.encrypted/me
21
21
  LoadCredentialEncrypted=merge-steward-github-app-pem:/etc/credstore.encrypted/merge-steward-github-app-pem.cred
22
22
 
23
23
  ExecStart=/usr/bin/env merge-steward serve
24
- Restart=on-failure
24
+ Restart=always
25
25
  RestartSec=5
26
26
 
27
27
  NoNewPrivileges=true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "merge-steward",
3
- "version": "0.32.3",
3
+ "version": "0.32.5",
4
4
  "description": "Serial merge queue for GitHub — rebase, CI-gate, and merge PRs one at a time",
5
5
  "type": "module",
6
6
  "repository": {
@@ -35,20 +35,20 @@
35
35
  "merge-steward": "dist/index.js"
36
36
  },
37
37
  "dependencies": {
38
- "fastify": "^5.8.5",
38
+ "fastify": "^5.10.0",
39
39
  "fastify-raw-body": "^5.0.0",
40
- "ink": "^7.0.1",
40
+ "ink": "^7.1.0",
41
41
  "pino": "^10.3.1",
42
- "react": "^19.2.5",
43
- "zod": "^4.3.6"
42
+ "react": "^19.2.7",
43
+ "zod": "^4.4.3"
44
44
  },
45
45
  "devDependencies": {
46
- "@types/node": "^25.6.0",
47
- "@types/react": "^19.2.14",
46
+ "@types/node": "^24.13.3",
47
+ "@types/react": "^19.2.17",
48
48
  "@typescript/native-preview": "7.0.0-dev.20260427.1",
49
- "fast-check": "^4.7.0",
50
- "isomorphic-git": "^1.37.6",
51
- "memfs": "^4.57.2"
49
+ "fast-check": "^4.9.0",
50
+ "isomorphic-git": "^1.38.6",
51
+ "memfs": "^4.64.0"
52
52
  },
53
53
  "scripts": {
54
54
  "dev": "node --watch --experimental-transform-types src/index.ts",