ci: disable automatic workflow triggers

This commit is contained in:
gsxdsm
2026-04-12 13:48:13 -07:00
parent 51f31f099a
commit ecfe134591
8 changed files with 77 additions and 31 deletions

View File

@@ -1,14 +1,8 @@
name: Mobile Builds name: Mobile Builds
# Auto-trigger disabled; workflow preserved for manual use via workflow_dispatch.
on: on:
workflow_dispatch: workflow_dispatch:
push:
branches:
- main
paths:
- "packages/mobile/**"
- "packages/dashboard/**"
- ".github/workflows/mobile.yml"
jobs: jobs:
build-web: build-web:

View File

@@ -10,10 +10,9 @@
name: Binary Release name: Binary Release
# Auto-trigger disabled; workflow preserved for manual use via workflow_dispatch.
on: on:
push: workflow_dispatch:
tags:
- "v*"
permissions: permissions:
contents: write contents: write

View File

@@ -5,10 +5,9 @@
name: Version & Release name: Version & Release
# Auto-trigger disabled; workflow preserved for manual use via workflow_dispatch.
on: on:
push: workflow_dispatch:
branches:
- main
permissions: permissions:
contents: write contents: write

View File

@@ -83,8 +83,12 @@ describe("Version & Release workflow (.github/workflows/version.yml)", () => {
expect(typeof workflow).toBe("object"); expect(typeof workflow).toBe("object");
}); });
it("has push trigger on main", () => { it("uses workflow_dispatch trigger (auto release disabled)", () => {
expect(workflow.on.push.branches).toContain("main"); expect(workflow.on).toHaveProperty("workflow_dispatch");
});
it("does not auto-trigger on push", () => {
expect(workflow.on.push).toBeUndefined();
}); });
it("includes pnpm install step", () => { it("includes pnpm install step", () => {
@@ -143,9 +147,12 @@ describe("Binary release workflow (.github/workflows/release.yml)", () => {
expect(typeof workflow).toBe("object"); expect(typeof workflow).toBe("object");
}); });
it("triggers on version tags", () => { it("uses workflow_dispatch trigger (auto binary release disabled)", () => {
expect(workflow.on.push.tags).toBeDefined(); expect(workflow.on).toHaveProperty("workflow_dispatch");
expect(workflow.on.push.tags.some((t: string) => t.includes("v"))).toBe(true); });
it("does not auto-trigger on version tags", () => {
expect(workflow.on.push).toBeUndefined();
}); });
it("has build-binaries job with 4-target matrix", () => { it("has build-binaries job with 4-target matrix", () => {

View File

@@ -40,7 +40,7 @@ describe("Changeset configuration", () => {
const content = readFileSync(workflowPath, "utf-8"); const content = readFileSync(workflowPath, "utf-8");
expect(content).toContain("changesets/action"); expect(content).toContain("changesets/action");
expect(content).toContain("push"); expect(content).toContain("workflow_dispatch");
expect(content).toContain("main"); expect(content).toContain("Auto-trigger disabled");
}); });
}); });

View File

@@ -1,4 +1,4 @@
import { describe, it, expect, vi, beforeEach } from "vitest"; import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { EventEmitter } from "node:events"; import { EventEmitter } from "node:events";
// ── Capture arguments ─────────────────────────────────────────────── // ── Capture arguments ───────────────────────────────────────────────
@@ -6,6 +6,10 @@ import { EventEmitter } from "node:events";
// Minimal mock store backed by EventEmitter so `store.on` works // Minimal mock store backed by EventEmitter so `store.on` works
function makeMockStore() { function makeMockStore() {
const emitter = new EventEmitter(); const emitter = new EventEmitter();
// runDashboard registers several independent settings listeners by design;
// keep the test mock above Node's low default threshold while still checking
// startup wiring behavior.
emitter.setMaxListeners(20);
const mockMissionStore = { const mockMissionStore = {
listMissions: vi.fn().mockReturnValue([]), listMissions: vi.fn().mockReturnValue([]),
getMission: vi.fn(), getMission: vi.fn(),
@@ -214,10 +218,28 @@ vi.mock("@mariozechner/pi-coding-agent", () => ({
// ── Import module under test (after mocks) ────────────────────────── // ── Import module under test (after mocks) ──────────────────────────
const { runDashboard } = await import("../dashboard.js"); const { runDashboard: runDashboardImpl } = await import("../dashboard.js");
const dashboardDisposables: Array<() => void> = [];
function disposeTrackedDashboards(): void {
for (const dispose of dashboardDisposables.splice(0)) {
dispose();
}
}
async function runDashboard(...args: Parameters<typeof runDashboardImpl>): ReturnType<typeof runDashboardImpl> {
disposeTrackedDashboards();
const result = await runDashboardImpl(...args);
dashboardDisposables.push(result.dispose);
return result;
}
// ── Tests ─────────────────────────────────────────────────────────── // ── Tests ───────────────────────────────────────────────────────────
afterEach(() => {
disposeTrackedDashboards();
});
describe("runDashboard — AuthStorage & ModelRegistry wiring", () => { describe("runDashboard — AuthStorage & ModelRegistry wiring", () => {
beforeEach(async () => { beforeEach(async () => {
vi.clearAllMocks(); vi.clearAllMocks();

View File

@@ -35,6 +35,10 @@ const {
// Minimal mock store backed by EventEmitter so `store.on` works // Minimal mock store backed by EventEmitter so `store.on` works
function makeMockStore() { function makeMockStore() {
const emitter = new EventEmitter(); const emitter = new EventEmitter();
// runDashboard registers several independent settings listeners by design;
// keep the test mock above Node's low default threshold while still asserting
// disposal behavior in the lifecycle cleanup tests below.
emitter.setMaxListeners(20);
const mockMissionStore = { const mockMissionStore = {
listMissions: vi.fn().mockReturnValue([]), listMissions: vi.fn().mockReturnValue([]),
getMission: vi.fn(), getMission: vi.fn(),
@@ -164,12 +168,19 @@ const {
mockMergePr, mockMergePr,
} = vi.hoisted(() => ({ } = vi.hoisted(() => ({
mockExec: vi.fn((_command: string, _options?: any, callback?: (err: null, stdout: string, stderr: string) => void) => { mockExec: vi.fn((_command: string, _options?: any, callback?: (err: null, stdout: string, stderr: string) => void) => {
// Handle both callback-style (original exec) and promise-style (promisified execAsync)
if (typeof callback === "function") { if (typeof callback === "function") {
callback(null, "", ""); callback(null, "", "");
} }
// Return resolved promise for promisified usage // Match child_process.exec's callback-style contract. Returning a Promise
return Promise.resolve({ stdout: "", stderr: "" }); // makes util.promisify(exec) emit DEP0174 in tests.
return {
pid: 12345,
stdout: null,
stderr: null,
on: vi.fn(),
once: vi.fn(),
kill: vi.fn(),
};
}), }),
mockExecSync: vi.fn(() => ""), mockExecSync: vi.fn(() => ""),
mockFindPrForBranch: vi.fn(), mockFindPrForBranch: vi.fn(),
@@ -329,8 +340,22 @@ vi.mock("@mariozechner/pi-coding-agent", () => ({
// ── Import module under test (after mocks) ────────────────────────── // ── Import module under test (after mocks) ──────────────────────────
const { runDashboard } = await import("./dashboard.js"); const { runDashboard: runDashboardImpl } = await import("./dashboard.js");
const { processPullRequestMergeTask, getMergeStrategy, getTaskBranchName } = await import("./task-lifecycle.js"); const { processPullRequestMergeTask, getMergeStrategy, getTaskBranchName } = await import("./task-lifecycle.js");
const dashboardDisposables: Array<() => void> = [];
function disposeTrackedDashboards(): void {
for (const dispose of dashboardDisposables.splice(0)) {
dispose();
}
}
async function runDashboard(...args: Parameters<typeof runDashboardImpl>): ReturnType<typeof runDashboardImpl> {
disposeTrackedDashboards();
const result = await runDashboardImpl(...args);
dashboardDisposables.push(result.dispose);
return result;
}
// ── Tests ─────────────────────────────────────────────────────────── // ── Tests ───────────────────────────────────────────────────────────
@@ -385,6 +410,10 @@ beforeEach(() => {
mockStuckCheckNow.mockResolvedValue(undefined); mockStuckCheckNow.mockResolvedValue(undefined);
}); });
afterEach(() => {
disposeTrackedDashboards();
});
describe("PR merge helpers", () => { describe("PR merge helpers", () => {
it("defaults mergeStrategy to direct when unset", () => { it("defaults mergeStrategy to direct when unset", () => {
expect(getMergeStrategy({ mergeStrategy: undefined })).toBe("direct"); expect(getMergeStrategy({ mergeStrategy: undefined })).toBe("direct");

View File

@@ -147,11 +147,7 @@ function cleanupExpiredRateLimits(): void {
// Start cleanup interval // Start cleanup interval
const cleanupInterval = setInterval(cleanupExpiredRateLimits, CLEANUP_INTERVAL_MS); const cleanupInterval = setInterval(cleanupExpiredRateLimits, CLEANUP_INTERVAL_MS);
cleanupInterval.unref?.();
// Handle graceful shutdown
process.on("beforeExit", () => {
clearInterval(cleanupInterval);
});
// ── Custom Errors ─────────────────────────────────────────────────────────── // ── Custom Errors ───────────────────────────────────────────────────────────