@urateam/core 0.1.16 → 0.1.18
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.
- package/dist/__tests__/agentic-deep-review-provider.test.d.ts +2 -0
- package/dist/__tests__/agentic-deep-review-provider.test.d.ts.map +1 -0
- package/dist/__tests__/agentic-deep-review-provider.test.js +70 -0
- package/dist/__tests__/agentic-deep-review-provider.test.js.map +1 -0
- package/dist/__tests__/audit-immutability.test.js +16 -1
- package/dist/__tests__/audit-immutability.test.js.map +1 -1
- package/dist/__tests__/cost/per-run-multi-model.test.d.ts +2 -0
- package/dist/__tests__/cost/per-run-multi-model.test.d.ts.map +1 -0
- package/dist/__tests__/cost/per-run-multi-model.test.js +102 -0
- package/dist/__tests__/cost/per-run-multi-model.test.js.map +1 -0
- package/dist/__tests__/db-qa-gap-issues.test.d.ts +2 -0
- package/dist/__tests__/db-qa-gap-issues.test.d.ts.map +1 -0
- package/dist/__tests__/db-qa-gap-issues.test.js +113 -0
- package/dist/__tests__/db-qa-gap-issues.test.js.map +1 -0
- package/dist/__tests__/db-release-decisions.test.d.ts +2 -0
- package/dist/__tests__/db-release-decisions.test.d.ts.map +1 -0
- package/dist/__tests__/db-release-decisions.test.js +98 -0
- package/dist/__tests__/db-release-decisions.test.js.map +1 -0
- package/dist/__tests__/db-review-model-runs.test.d.ts +2 -0
- package/dist/__tests__/db-review-model-runs.test.d.ts.map +1 -0
- package/dist/__tests__/db-review-model-runs.test.js +90 -0
- package/dist/__tests__/db-review-model-runs.test.js.map +1 -0
- package/dist/__tests__/e2e-fanout.test.d.ts +17 -0
- package/dist/__tests__/e2e-fanout.test.d.ts.map +1 -0
- package/dist/__tests__/e2e-fanout.test.js +184 -0
- package/dist/__tests__/e2e-fanout.test.js.map +1 -0
- package/dist/__tests__/e2e-pipeline.test.js +1 -0
- package/dist/__tests__/e2e-pipeline.test.js.map +1 -1
- package/dist/__tests__/notifier-discord.test.js +3 -3
- package/dist/__tests__/notifier-discord.test.js.map +1 -1
- package/dist/__tests__/notifier-slack.test.js +2 -2
- package/dist/__tests__/notifier-slack.test.js.map +1 -1
- package/dist/__tests__/notifier.test.js +1 -0
- package/dist/__tests__/notifier.test.js.map +1 -1
- package/dist/__tests__/openrouter-client.test.d.ts +2 -0
- package/dist/__tests__/openrouter-client.test.d.ts.map +1 -0
- package/dist/__tests__/openrouter-client.test.js +56 -0
- package/dist/__tests__/openrouter-client.test.js.map +1 -0
- package/dist/__tests__/openrouter-fanout.test.d.ts +2 -0
- package/dist/__tests__/openrouter-fanout.test.d.ts.map +1 -0
- package/dist/__tests__/openrouter-fanout.test.js +93 -0
- package/dist/__tests__/openrouter-fanout.test.js.map +1 -0
- package/dist/__tests__/post-fanout-comments.test.d.ts +2 -0
- package/dist/__tests__/post-fanout-comments.test.d.ts.map +1 -0
- package/dist/__tests__/post-fanout-comments.test.js +84 -0
- package/dist/__tests__/post-fanout-comments.test.js.map +1 -0
- package/dist/__tests__/qa-audit-events.test.d.ts +2 -0
- package/dist/__tests__/qa-audit-events.test.d.ts.map +1 -0
- package/dist/__tests__/qa-audit-events.test.js +57 -0
- package/dist/__tests__/qa-audit-events.test.js.map +1 -0
- package/dist/__tests__/qa-config.test.d.ts +2 -0
- package/dist/__tests__/qa-config.test.d.ts.map +1 -0
- package/dist/__tests__/qa-config.test.js +80 -0
- package/dist/__tests__/qa-config.test.js.map +1 -0
- package/dist/__tests__/qa-eval.test.d.ts +2 -0
- package/dist/__tests__/qa-eval.test.d.ts.map +1 -0
- package/dist/__tests__/qa-eval.test.js +146 -0
- package/dist/__tests__/qa-eval.test.js.map +1 -0
- package/dist/__tests__/qa-gap.test.d.ts +2 -0
- package/dist/__tests__/qa-gap.test.d.ts.map +1 -0
- package/dist/__tests__/qa-gap.test.js +100 -0
- package/dist/__tests__/qa-gap.test.js.map +1 -0
- package/dist/__tests__/qa-github.test.d.ts +2 -0
- package/dist/__tests__/qa-github.test.d.ts.map +1 -0
- package/dist/__tests__/qa-github.test.js +162 -0
- package/dist/__tests__/qa-github.test.js.map +1 -0
- package/dist/__tests__/ralph-review-fix-regression.test.js +1 -0
- package/dist/__tests__/ralph-review-fix-regression.test.js.map +1 -1
- package/dist/__tests__/release-manager-audit-events.test.d.ts +2 -0
- package/dist/__tests__/release-manager-audit-events.test.d.ts.map +1 -0
- package/dist/__tests__/release-manager-audit-events.test.js +64 -0
- package/dist/__tests__/release-manager-audit-events.test.js.map +1 -0
- package/dist/__tests__/release-manager-config.test.d.ts +2 -0
- package/dist/__tests__/release-manager-config.test.d.ts.map +1 -0
- package/dist/__tests__/release-manager-config.test.js +56 -0
- package/dist/__tests__/release-manager-config.test.js.map +1 -0
- package/dist/__tests__/release-manager-decide.test.d.ts +2 -0
- package/dist/__tests__/release-manager-decide.test.d.ts.map +1 -0
- package/dist/__tests__/release-manager-decide.test.js +102 -0
- package/dist/__tests__/release-manager-decide.test.js.map +1 -0
- package/dist/__tests__/release-manager-github.test.d.ts +2 -0
- package/dist/__tests__/release-manager-github.test.d.ts.map +1 -0
- package/dist/__tests__/release-manager-github.test.js +83 -0
- package/dist/__tests__/release-manager-github.test.js.map +1 -0
- package/dist/__tests__/release-manager-license-gate.test.d.ts +2 -0
- package/dist/__tests__/release-manager-license-gate.test.d.ts.map +1 -0
- package/dist/__tests__/release-manager-license-gate.test.js +25 -0
- package/dist/__tests__/release-manager-license-gate.test.js.map +1 -0
- package/dist/__tests__/release-manager-scheduler.test.d.ts +2 -0
- package/dist/__tests__/release-manager-scheduler.test.d.ts.map +1 -0
- package/dist/__tests__/release-manager-scheduler.test.js +395 -0
- package/dist/__tests__/release-manager-scheduler.test.js.map +1 -0
- package/dist/__tests__/release-manager-slack-handler.test.d.ts +2 -0
- package/dist/__tests__/release-manager-slack-handler.test.d.ts.map +1 -0
- package/dist/__tests__/release-manager-slack-handler.test.js +175 -0
- package/dist/__tests__/release-manager-slack-handler.test.js.map +1 -0
- package/dist/__tests__/release-manager-triggers.test.d.ts +2 -0
- package/dist/__tests__/release-manager-triggers.test.d.ts.map +1 -0
- package/dist/__tests__/release-manager-triggers.test.js +67 -0
- package/dist/__tests__/release-manager-triggers.test.js.map +1 -0
- package/dist/__tests__/release-manager-versioning.test.d.ts +2 -0
- package/dist/__tests__/release-manager-versioning.test.d.ts.map +1 -0
- package/dist/__tests__/release-manager-versioning.test.js +51 -0
- package/dist/__tests__/release-manager-versioning.test.js.map +1 -0
- package/dist/__tests__/review-prompt.test.d.ts +2 -0
- package/dist/__tests__/review-prompt.test.d.ts.map +1 -0
- package/dist/__tests__/review-prompt.test.js +88 -0
- package/dist/__tests__/review-prompt.test.js.map +1 -0
- package/dist/__tests__/review-provider-registry.test.d.ts +2 -0
- package/dist/__tests__/review-provider-registry.test.d.ts.map +1 -0
- package/dist/__tests__/review-provider-registry.test.js +61 -0
- package/dist/__tests__/review-provider-registry.test.js.map +1 -0
- package/dist/__tests__/runner-fanout-integration.test.d.ts +2 -0
- package/dist/__tests__/runner-fanout-integration.test.d.ts.map +1 -0
- package/dist/__tests__/runner-fanout-integration.test.js +107 -0
- package/dist/__tests__/runner-fanout-integration.test.js.map +1 -0
- package/dist/audit/events.d.ts +54 -0
- package/dist/audit/events.d.ts.map +1 -1
- package/dist/audit/events.js +100 -0
- package/dist/audit/events.js.map +1 -1
- package/dist/audit/writer.d.ts +22 -8
- package/dist/audit/writer.d.ts.map +1 -1
- package/dist/audit/writer.js +22 -8
- package/dist/audit/writer.js.map +1 -1
- package/dist/cost/aggregate.d.ts.map +1 -1
- package/dist/cost/aggregate.js +14 -2
- package/dist/cost/aggregate.js.map +1 -1
- package/dist/cost/csv.d.ts.map +1 -1
- package/dist/cost/csv.js +14 -2
- package/dist/cost/csv.js.map +1 -1
- package/dist/cost/per-run.d.ts +11 -0
- package/dist/cost/per-run.d.ts.map +1 -1
- package/dist/cost/per-run.js +21 -8
- package/dist/cost/per-run.js.map +1 -1
- package/dist/cost/rollup.d.ts.map +1 -1
- package/dist/cost/rollup.js +14 -2
- package/dist/cost/rollup.js.map +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +20 -1
- package/dist/db/client.js.map +1 -1
- package/dist/db/migrations/postgres/009_review_model_runs.sql +18 -0
- package/dist/db/migrations/postgres/010_release_manager.sql +38 -0
- package/dist/db/migrations/postgres/011_qa_run_columns.sql +8 -0
- package/dist/db/migrations/postgres/012_qa_gap_issues.sql +18 -0
- package/dist/db/migrations/sqlite/008_review_model_runs.sql +18 -0
- package/dist/db/migrations/sqlite/009_release_manager.sql +43 -0
- package/dist/db/migrations/sqlite/010_qa_run_columns.sql +9 -0
- package/dist/db/migrations/sqlite/011_qa_gap_issues.sql +22 -0
- package/dist/db/review-model-runs.d.ts +4 -0
- package/dist/db/review-model-runs.d.ts.map +1 -0
- package/dist/db/review-model-runs.js +25 -0
- package/dist/db/review-model-runs.js.map +1 -0
- package/dist/db/schema.d.ts +761 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +67 -0
- package/dist/db/schema.js.map +1 -1
- package/dist/executor/executor.d.ts.map +1 -1
- package/dist/executor/executor.js +2 -0
- package/dist/executor/executor.js.map +1 -1
- package/dist/executor/review/agentic-deep-review.d.ts +6 -0
- package/dist/executor/review/agentic-deep-review.d.ts.map +1 -0
- package/dist/executor/review/agentic-deep-review.js +37 -0
- package/dist/executor/review/agentic-deep-review.js.map +1 -0
- package/dist/executor/review/openrouter-client.d.ts +23 -0
- package/dist/executor/review/openrouter-client.d.ts.map +1 -0
- package/dist/executor/review/openrouter-client.js +34 -0
- package/dist/executor/review/openrouter-client.js.map +1 -0
- package/dist/executor/review/openrouter-fanout.d.ts +17 -0
- package/dist/executor/review/openrouter-fanout.d.ts.map +1 -0
- package/dist/executor/review/openrouter-fanout.js +84 -0
- package/dist/executor/review/openrouter-fanout.js.map +1 -0
- package/dist/executor/review/post-fanout-comments.d.ts +4 -0
- package/dist/executor/review/post-fanout-comments.d.ts.map +1 -0
- package/dist/executor/review/post-fanout-comments.js +45 -0
- package/dist/executor/review/post-fanout-comments.js.map +1 -0
- package/dist/executor/review/review-prompt.d.ts +21 -0
- package/dist/executor/review/review-prompt.d.ts.map +1 -0
- package/dist/executor/review/review-prompt.js +130 -0
- package/dist/executor/review/review-prompt.js.map +1 -0
- package/dist/executor/review/review-provider.d.ts +27 -0
- package/dist/executor/review/review-provider.d.ts.map +1 -0
- package/dist/executor/review/review-provider.js +36 -0
- package/dist/executor/review/review-provider.js.map +1 -0
- package/dist/executor/review/workdir-snapshot.d.ts +13 -0
- package/dist/executor/review/workdir-snapshot.d.ts.map +1 -0
- package/dist/executor/review/workdir-snapshot.js +24 -0
- package/dist/executor/review/workdir-snapshot.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/license.d.ts.map +1 -1
- package/dist/license.js +1 -0
- package/dist/license.js.map +1 -1
- package/dist/pipeline/review-providers-runner.d.ts +31 -0
- package/dist/pipeline/review-providers-runner.d.ts.map +1 -0
- package/dist/pipeline/review-providers-runner.js +64 -0
- package/dist/pipeline/review-providers-runner.js.map +1 -0
- package/dist/pipeline/runner.d.ts.map +1 -1
- package/dist/pipeline/runner.js +99 -7
- package/dist/pipeline/runner.js.map +1 -1
- package/dist/pm/actions/promote.js +2 -2
- package/dist/pm/actions/promote.js.map +1 -1
- package/dist/pm/actions/resolve-approvals.js +3 -3
- package/dist/pm/actions/resolve-approvals.js.map +1 -1
- package/dist/pm/actions/triage.js +2 -2
- package/dist/pm/actions/triage.js.map +1 -1
- package/dist/pm/scheduler.js +3 -3
- package/dist/pm/scheduler.js.map +1 -1
- package/dist/pm/slack-interface.d.ts +8 -0
- package/dist/pm/slack-interface.d.ts.map +1 -1
- package/dist/pm/slack-interface.js +20 -3
- package/dist/pm/slack-interface.js.map +1 -1
- package/dist/qa/gap.d.ts +29 -0
- package/dist/qa/gap.d.ts.map +1 -0
- package/dist/qa/gap.js +91 -0
- package/dist/qa/gap.js.map +1 -0
- package/dist/qa/github.d.ts +77 -0
- package/dist/qa/github.d.ts.map +1 -0
- package/dist/qa/github.js +106 -0
- package/dist/qa/github.js.map +1 -0
- package/dist/qa/index.d.ts +4 -0
- package/dist/qa/index.d.ts.map +1 -0
- package/dist/qa/index.js +4 -0
- package/dist/qa/index.js.map +1 -0
- package/dist/qa/types.d.ts +44 -0
- package/dist/qa/types.d.ts.map +1 -0
- package/dist/qa/types.js +12 -0
- package/dist/qa/types.js.map +1 -0
- package/dist/release-manager/decide.d.ts +26 -0
- package/dist/release-manager/decide.d.ts.map +1 -0
- package/dist/release-manager/decide.js +58 -0
- package/dist/release-manager/decide.js.map +1 -0
- package/dist/release-manager/github.d.ts +47 -0
- package/dist/release-manager/github.d.ts.map +1 -0
- package/dist/release-manager/github.js +66 -0
- package/dist/release-manager/github.js.map +1 -0
- package/dist/release-manager/index.d.ts +9 -0
- package/dist/release-manager/index.d.ts.map +1 -0
- package/dist/release-manager/index.js +9 -0
- package/dist/release-manager/index.js.map +1 -0
- package/dist/release-manager/scheduler.d.ts +30 -0
- package/dist/release-manager/scheduler.d.ts.map +1 -0
- package/dist/release-manager/scheduler.js +427 -0
- package/dist/release-manager/scheduler.js.map +1 -0
- package/dist/release-manager/slack-handler.d.ts +39 -0
- package/dist/release-manager/slack-handler.d.ts.map +1 -0
- package/dist/release-manager/slack-handler.js +124 -0
- package/dist/release-manager/slack-handler.js.map +1 -0
- package/dist/release-manager/state.d.ts +18 -0
- package/dist/release-manager/state.d.ts.map +1 -0
- package/dist/release-manager/state.js +154 -0
- package/dist/release-manager/state.js.map +1 -0
- package/dist/release-manager/triggers.d.ts +32 -0
- package/dist/release-manager/triggers.d.ts.map +1 -0
- package/dist/release-manager/triggers.js +82 -0
- package/dist/release-manager/triggers.js.map +1 -0
- package/dist/release-manager/types.d.ts +79 -0
- package/dist/release-manager/types.d.ts.map +1 -0
- package/dist/release-manager/types.js +48 -0
- package/dist/release-manager/types.js.map +1 -0
- package/dist/release-manager/versioning.d.ts +20 -0
- package/dist/release-manager/versioning.d.ts.map +1 -0
- package/dist/release-manager/versioning.js +60 -0
- package/dist/release-manager/versioning.js.map +1 -0
- package/dist/types.d.ts +48 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { eq, isNull, and, desc, max } from "drizzle-orm";
|
|
3
|
+
import { Cron } from "croner";
|
|
4
|
+
import { releaseApprovals, releaseDecisions } from "../db/schema.js";
|
|
5
|
+
import { createLogger } from "../logger.js";
|
|
6
|
+
import { logAuditEventUnchecked } from "../audit/writer.js";
|
|
7
|
+
import { releaseFiredEvent, releaseSkippedEvent, releaseTagConflictEvent, releasePartialEvent, slackPostFailedEvent, qaRunCompletedEvent, } from "../audit/events.js";
|
|
8
|
+
import { collectState } from "./state.js";
|
|
9
|
+
import { decide } from "./decide.js";
|
|
10
|
+
import { bumpFromConfigAndCommits } from "./versioning.js";
|
|
11
|
+
import { createTagAndRelease, parseRepoFromUrl } from "./github.js";
|
|
12
|
+
import { triggerWorkflow, pollWorkflowRun, workflowFileExists } from "../qa/github.js";
|
|
13
|
+
import { fileGapIssue, markGapResolved } from "../qa/gap.js";
|
|
14
|
+
const log = createLogger({ component: "ReleaseManager:scheduler" });
|
|
15
|
+
const SLACK_DEDUP_WINDOW_MS = 24 * 3600 * 1000;
|
|
16
|
+
export function createReleaseManagerScheduler(input) {
|
|
17
|
+
const { config, db, octokit, linear, repoUrl, isLicensed, slack } = input;
|
|
18
|
+
const branch = config.branch;
|
|
19
|
+
const slackChannel = config.slackChannel;
|
|
20
|
+
// Per-(repo, branch) in-memory dedup state.
|
|
21
|
+
let lastSlackSkipReason = null;
|
|
22
|
+
let lastSlackPostAt = 0;
|
|
23
|
+
let pausedUntilTs = 0;
|
|
24
|
+
let cronJob = null;
|
|
25
|
+
let licenseWarnLogged = false;
|
|
26
|
+
const auditedCompletedRunIds = new Set();
|
|
27
|
+
function approvalTtlMs() {
|
|
28
|
+
const hours = config.triggers.timeSinceLastHours;
|
|
29
|
+
if (hours && hours > 0)
|
|
30
|
+
return hours * 3600 * 1000;
|
|
31
|
+
return 24 * 3600 * 1000;
|
|
32
|
+
}
|
|
33
|
+
async function maybePostSlack(text, currentSkipReason) {
|
|
34
|
+
if (!slack || !slackChannel)
|
|
35
|
+
return;
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
// Always post when transitioning to fire / awaiting-approval.
|
|
38
|
+
// Otherwise dedup: same reason + within window → suppress.
|
|
39
|
+
if (currentSkipReason) {
|
|
40
|
+
const sameReason = currentSkipReason === lastSlackSkipReason;
|
|
41
|
+
const withinWindow = now - lastSlackPostAt < SLACK_DEDUP_WINDOW_MS;
|
|
42
|
+
if (sameReason && withinWindow)
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const ok = await slack.postMessage(slackChannel, text).catch(() => false);
|
|
46
|
+
if (!ok) {
|
|
47
|
+
void logAuditEventUnchecked(db, slackPostFailedEvent({ channel: slackChannel, reason: "post_returned_false" }));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
lastSlackPostAt = now;
|
|
51
|
+
lastSlackSkipReason = currentSkipReason;
|
|
52
|
+
}
|
|
53
|
+
async function persistDecision(row) {
|
|
54
|
+
await db.insert(releaseDecisions).values({
|
|
55
|
+
id: row.id,
|
|
56
|
+
repoUrl,
|
|
57
|
+
branch,
|
|
58
|
+
decidedAt: new Date(),
|
|
59
|
+
decision: row.decision,
|
|
60
|
+
reason: row.reason,
|
|
61
|
+
triggerStateJson: row.triggerStateJson,
|
|
62
|
+
proposedVersion: row.proposedVersion,
|
|
63
|
+
firedTag: row.firedTag,
|
|
64
|
+
firedSha: row.firedSha,
|
|
65
|
+
attemptCount: row.attemptCount ?? 0,
|
|
66
|
+
qaRunId: row.qaRunId,
|
|
67
|
+
qaRunSha: row.qaRunSha,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async function consumeApprovalRow(decisionId) {
|
|
71
|
+
// Mark the most-recent fresh approval as consumed by this decision.
|
|
72
|
+
const fresh = await db
|
|
73
|
+
.select({ id: releaseApprovals.id })
|
|
74
|
+
.from(releaseApprovals)
|
|
75
|
+
.where(and(eq(releaseApprovals.repoUrl, repoUrl), eq(releaseApprovals.branch, branch), isNull(releaseApprovals.consumedAt)))
|
|
76
|
+
.orderBy(desc(releaseApprovals.approvedAt))
|
|
77
|
+
.limit(1);
|
|
78
|
+
if (fresh?.[0]?.id) {
|
|
79
|
+
await db
|
|
80
|
+
.update(releaseApprovals)
|
|
81
|
+
.set({ consumedAt: new Date(), consumedByDecisionId: decisionId })
|
|
82
|
+
.where(eq(releaseApprovals.id, fresh[0].id));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function tick() {
|
|
86
|
+
if (!isLicensed()) {
|
|
87
|
+
if (!licenseWarnLogged) {
|
|
88
|
+
log.warn({ repoUrl, branch }, "release-manager unlicensed — skipping ticks");
|
|
89
|
+
licenseWarnLogged = true;
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (Date.now() < pausedUntilTs) {
|
|
94
|
+
log.info({ pausedUntilTs }, "scheduler paused (via /release skip) — skipping tick");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let state;
|
|
98
|
+
try {
|
|
99
|
+
state = await collectState({
|
|
100
|
+
octokit, db, repoUrl, branch, approvalTtlMs: approvalTtlMs(),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
log.error({ err, repoUrl, branch }, "collectState failed — skipping tick");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const triggerStateJson = JSON.stringify({
|
|
108
|
+
mergedCommitsSinceLastTag: state.mergedCommitsSinceLastTag,
|
|
109
|
+
lastTag: state.lastTag,
|
|
110
|
+
lastTagAt: state.lastTagAt?.toISOString() ?? null,
|
|
111
|
+
ciStatus: state.ciStatus,
|
|
112
|
+
hasFreshApproval: state.hasFreshApproval,
|
|
113
|
+
});
|
|
114
|
+
// 1. Manual-tag detection — re-baseline counters.
|
|
115
|
+
if (state.manualTagDetected) {
|
|
116
|
+
const id = `rd_${randomUUID()}`;
|
|
117
|
+
await persistDecision({
|
|
118
|
+
id,
|
|
119
|
+
decision: "skip",
|
|
120
|
+
reason: "manual_tag_detected",
|
|
121
|
+
triggerStateJson,
|
|
122
|
+
});
|
|
123
|
+
void logAuditEventUnchecked(db, releaseSkippedEvent({ repoUrl, branch, reason: "manual_tag_detected" }));
|
|
124
|
+
log.info({ repoUrl, branch }, "manual tag detected — re-baselining");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// 2. BEC-136: Compute QA state when qaCheck is configured.
|
|
128
|
+
let qaState;
|
|
129
|
+
if (config.triggers.qaCheck) {
|
|
130
|
+
const { owner, repo } = parseRepoFromUrl(repoUrl);
|
|
131
|
+
let wfExists = false;
|
|
132
|
+
try {
|
|
133
|
+
wfExists = await workflowFileExists({
|
|
134
|
+
octokit, owner, repo,
|
|
135
|
+
path: config.triggers.qaCheck.workflow,
|
|
136
|
+
ref: state.headSha,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
log.warn({ err }, "qa workflowFileExists check failed — treating as exists");
|
|
141
|
+
wfExists = true; // fail-open so retries hit the dispatch path
|
|
142
|
+
}
|
|
143
|
+
if (wfExists) {
|
|
144
|
+
// BEC-136: workflow file is present; if there was an open gap issue for this
|
|
145
|
+
// (repo, branch, workflow), mark it resolved so a future gap can be re-filed.
|
|
146
|
+
await markGapResolved({
|
|
147
|
+
db,
|
|
148
|
+
repoUrl,
|
|
149
|
+
branch,
|
|
150
|
+
workflowPath: config.triggers.qaCheck.workflow,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
let runConclusion = null;
|
|
154
|
+
if (state.qaRun && state.qaRun.runSha === state.headSha) {
|
|
155
|
+
try {
|
|
156
|
+
const polled = await pollWorkflowRun({ octokit, owner, repo, runId: state.qaRun.runId });
|
|
157
|
+
if (polled.kind === "completed" && !auditedCompletedRunIds.has(state.qaRun.runId)) {
|
|
158
|
+
runConclusion = polled.conclusion;
|
|
159
|
+
auditedCompletedRunIds.add(state.qaRun.runId);
|
|
160
|
+
// Emit qa.run_completed audit on first observation of completion.
|
|
161
|
+
void logAuditEventUnchecked(db, qaRunCompletedEvent({
|
|
162
|
+
repoUrl, branch,
|
|
163
|
+
runId: state.qaRun.runId,
|
|
164
|
+
conclusion: polled.conclusion,
|
|
165
|
+
durationMs: polled.durationMs,
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
else if (polled.kind === "completed") {
|
|
169
|
+
runConclusion = polled.conclusion;
|
|
170
|
+
// Already audited this runId on a prior tick; skip emit.
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
log.warn({ err }, "qa pollWorkflowRun failed — treating as still running");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
qaState = { workflowFileExists: wfExists, runConclusion };
|
|
178
|
+
}
|
|
179
|
+
// 3. Decision.
|
|
180
|
+
const result = decide(state, config.triggers, undefined, qaState);
|
|
181
|
+
const proposedVersion = bumpFromConfigAndCommits(state.lastTag, state.commitsSinceLastTag, config.versionBump);
|
|
182
|
+
if (result.kind === "skip") {
|
|
183
|
+
const id = `rd_${randomUUID()}`;
|
|
184
|
+
// Compute attempt count for retry handling on qa_dispatch_error path.
|
|
185
|
+
let attemptCount = 0;
|
|
186
|
+
let qaRunId;
|
|
187
|
+
let qaRunSha;
|
|
188
|
+
let finalReason = result.reason;
|
|
189
|
+
if (result.qaActionNeeded?.reason === "qa_needs_trigger") {
|
|
190
|
+
// Look up the highest attempt count across all qa_needs_trigger rows for this branch.
|
|
191
|
+
// Using MAX instead of ORDER BY + LIMIT 1 to be stable when multiple rows share the same decidedAt.
|
|
192
|
+
const prevAttempts = await db
|
|
193
|
+
.select({ maxAttempts: max(releaseDecisions.attemptCount) })
|
|
194
|
+
.from(releaseDecisions)
|
|
195
|
+
.where(and(eq(releaseDecisions.repoUrl, repoUrl), eq(releaseDecisions.branch, branch), eq(releaseDecisions.reason, "qa_needs_trigger"), eq(releaseDecisions.qaRunSha, state.headSha)));
|
|
196
|
+
attemptCount = (prevAttempts?.[0]?.maxAttempts ?? 0);
|
|
197
|
+
const { owner, repo } = parseRepoFromUrl(repoUrl);
|
|
198
|
+
const dispatch = await triggerWorkflow({
|
|
199
|
+
octokit, db, owner, repo, repoUrl, branch,
|
|
200
|
+
workflow: config.triggers.qaCheck.workflow,
|
|
201
|
+
ref: state.headSha,
|
|
202
|
+
inputs: config.triggers.qaCheck.workflowInputs,
|
|
203
|
+
});
|
|
204
|
+
if (dispatch.kind === "ok") {
|
|
205
|
+
attemptCount = 0; // reset on successful dispatch
|
|
206
|
+
qaRunId = dispatch.runId;
|
|
207
|
+
qaRunSha = state.headSha;
|
|
208
|
+
}
|
|
209
|
+
else if (dispatch.kind === "dispatch_404") {
|
|
210
|
+
// Workflow disappeared between state cache and dispatch — drop into gap-issue path.
|
|
211
|
+
finalReason = "qa_no_workflow";
|
|
212
|
+
if (linear) {
|
|
213
|
+
const gapResult = await fileGapIssue({
|
|
214
|
+
db,
|
|
215
|
+
linear,
|
|
216
|
+
repoUrl,
|
|
217
|
+
branch,
|
|
218
|
+
workflowPath: config.triggers.qaCheck.workflow,
|
|
219
|
+
linearTeamId: config.triggers.qaCheck.linearTeamId,
|
|
220
|
+
});
|
|
221
|
+
if (gapResult.kind === "linear_error") {
|
|
222
|
+
const prevGapAttempts = await db
|
|
223
|
+
.select({ attemptCount: max(releaseDecisions.attemptCount) })
|
|
224
|
+
.from(releaseDecisions)
|
|
225
|
+
.where(and(eq(releaseDecisions.repoUrl, repoUrl), eq(releaseDecisions.branch, branch), eq(releaseDecisions.reason, "qa_no_workflow")));
|
|
226
|
+
const gapAttemptCount = (prevGapAttempts?.[0]?.attemptCount ?? 0) + 1;
|
|
227
|
+
if (gapAttemptCount >= 3) {
|
|
228
|
+
finalReason = "qa_gap_file_error";
|
|
229
|
+
attemptCount = gapAttemptCount;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
attemptCount = gapAttemptCount;
|
|
233
|
+
}
|
|
234
|
+
log.error({ err: gapResult.message, repoUrl, branch }, "fileGapIssue failed; will retry");
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
log.error({ repoUrl, branch }, "qaCheck requires Linear client but none configured — skipping gap-issue file");
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else if (dispatch.kind === "dispatch_422") {
|
|
242
|
+
finalReason = "qa_dispatch_error";
|
|
243
|
+
attemptCount = 99; // permanent skip — workflow misconfigured, retrying won't help
|
|
244
|
+
}
|
|
245
|
+
else if (dispatch.kind === "dispatch_pending") {
|
|
246
|
+
// GitHub eventual-consistency window. Don't count against retry budget; next tick
|
|
247
|
+
// will re-evaluate and the run should be findable by then.
|
|
248
|
+
// Tag with qaRunSha so the per-SHA retry counter query can find these rows.
|
|
249
|
+
qaRunSha = state.headSha;
|
|
250
|
+
// attemptCount stays as-is; finalReason stays as "qa_needs_trigger" for next tick to retry.
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
// dispatch_error — increment attempt counter.
|
|
254
|
+
// Tag with qaRunSha so the per-SHA retry counter query can find these rows.
|
|
255
|
+
qaRunSha = state.headSha;
|
|
256
|
+
attemptCount += 1;
|
|
257
|
+
if (attemptCount >= 3) {
|
|
258
|
+
finalReason = "qa_dispatch_error";
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else if (result.qaActionNeeded?.reason === "qa_no_workflow") {
|
|
263
|
+
if (linear) {
|
|
264
|
+
const gapResult = await fileGapIssue({
|
|
265
|
+
db,
|
|
266
|
+
linear,
|
|
267
|
+
repoUrl,
|
|
268
|
+
branch,
|
|
269
|
+
workflowPath: config.triggers.qaCheck.workflow,
|
|
270
|
+
linearTeamId: config.triggers.qaCheck.linearTeamId,
|
|
271
|
+
});
|
|
272
|
+
if (gapResult.kind === "linear_error") {
|
|
273
|
+
const prevGapAttempts = await db
|
|
274
|
+
.select({ attemptCount: max(releaseDecisions.attemptCount) })
|
|
275
|
+
.from(releaseDecisions)
|
|
276
|
+
.where(and(eq(releaseDecisions.repoUrl, repoUrl), eq(releaseDecisions.branch, branch), eq(releaseDecisions.reason, "qa_no_workflow")));
|
|
277
|
+
const gapAttemptCount = (prevGapAttempts?.[0]?.attemptCount ?? 0) + 1;
|
|
278
|
+
if (gapAttemptCount >= 3) {
|
|
279
|
+
finalReason = "qa_gap_file_error";
|
|
280
|
+
attemptCount = gapAttemptCount;
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
attemptCount = gapAttemptCount;
|
|
284
|
+
}
|
|
285
|
+
log.error({ err: gapResult.message, repoUrl, branch }, "fileGapIssue failed; will retry");
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
log.error({ repoUrl, branch }, "qaCheck requires Linear client but none configured — skipping gap-issue file");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (result.qaActionNeeded?.reason === "qa_timed_out" && !result.qaActionNeeded.pass && state.qaRun) {
|
|
293
|
+
const elapsedMs = Date.now() - state.qaRun.triggeredAt.getTime();
|
|
294
|
+
void logAuditEventUnchecked(db, qaRunCompletedEvent({
|
|
295
|
+
repoUrl,
|
|
296
|
+
branch,
|
|
297
|
+
runId: result.qaActionNeeded.runId,
|
|
298
|
+
conclusion: "timed_out",
|
|
299
|
+
durationMs: elapsedMs,
|
|
300
|
+
synthetic: true,
|
|
301
|
+
}));
|
|
302
|
+
}
|
|
303
|
+
await persistDecision({
|
|
304
|
+
id,
|
|
305
|
+
decision: "skip",
|
|
306
|
+
reason: finalReason,
|
|
307
|
+
triggerStateJson,
|
|
308
|
+
proposedVersion,
|
|
309
|
+
qaRunId,
|
|
310
|
+
qaRunSha,
|
|
311
|
+
attemptCount,
|
|
312
|
+
});
|
|
313
|
+
void logAuditEventUnchecked(db, releaseSkippedEvent({ repoUrl, branch, reason: finalReason }));
|
|
314
|
+
// Slack notification with dedup
|
|
315
|
+
await maybePostSlack(`:double_vertical_bar: Release skipped for *${repoUrl}* (${branch}): ${finalReason}`, finalReason);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (result.kind === "awaiting-approval") {
|
|
319
|
+
const id = `rd_${randomUUID()}`;
|
|
320
|
+
await persistDecision({
|
|
321
|
+
id,
|
|
322
|
+
decision: "awaiting-approval",
|
|
323
|
+
reason: result.reason,
|
|
324
|
+
triggerStateJson,
|
|
325
|
+
proposedVersion,
|
|
326
|
+
});
|
|
327
|
+
void logAuditEventUnchecked(db, releaseSkippedEvent({ repoUrl, branch, reason: "awaiting-approval" }));
|
|
328
|
+
// Always post on first transition to awaiting-approval (bypass dedup).
|
|
329
|
+
// Reset lastSlackSkipReason so a subsequent regular-skip will re-post.
|
|
330
|
+
lastSlackSkipReason = null;
|
|
331
|
+
await maybePostSlack(`:hourglass_flowing_sand: Release ready for *${repoUrl}* (${branch}): bumping ${proposedVersion} (${state.mergedCommitsSinceLastTag} commits since last tag). Run \`/release approve\` to fire.`, null);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
// 3. Fire — create tag + release.
|
|
335
|
+
const id = `rd_${randomUUID()}`;
|
|
336
|
+
const githubResult = await createTagAndRelease({
|
|
337
|
+
octokit,
|
|
338
|
+
...parseRepoFromUrl(repoUrl),
|
|
339
|
+
tag: proposedVersion,
|
|
340
|
+
sha: state.headSha,
|
|
341
|
+
});
|
|
342
|
+
if (githubResult.kind === "tag_exists") {
|
|
343
|
+
await persistDecision({
|
|
344
|
+
id,
|
|
345
|
+
decision: "skip",
|
|
346
|
+
reason: "tag_exists",
|
|
347
|
+
triggerStateJson,
|
|
348
|
+
proposedVersion,
|
|
349
|
+
});
|
|
350
|
+
void logAuditEventUnchecked(db, releaseTagConflictEvent({ repoUrl, branch, tag: proposedVersion }));
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (githubResult.kind === "release_create_failed") {
|
|
354
|
+
// Tag was created; release-creation failed. Write a single skip row with the
|
|
355
|
+
// partial-fire details so an operator can see what happened and clean up the
|
|
356
|
+
// orphaned tag manually. v1 does NOT retry release-creation across ticks
|
|
357
|
+
// (the tag is now committed, so the next tick would hit `tag_exists` and
|
|
358
|
+
// skip again — see plan §"Known v1 simplifications"). Proper retry is a v2
|
|
359
|
+
// feature requiring a tick-start sweep that calls only `createRelease` for
|
|
360
|
+
// matching fire-pending rows.
|
|
361
|
+
await persistDecision({
|
|
362
|
+
id,
|
|
363
|
+
decision: "skip",
|
|
364
|
+
reason: "release_create_failed",
|
|
365
|
+
triggerStateJson,
|
|
366
|
+
proposedVersion,
|
|
367
|
+
firedTag: proposedVersion,
|
|
368
|
+
firedSha: state.headSha,
|
|
369
|
+
});
|
|
370
|
+
void logAuditEventUnchecked(db, releasePartialEvent({ repoUrl, branch, tag: proposedVersion, attemptCount: 1 }));
|
|
371
|
+
log.error({ repoUrl, branch, tag: proposedVersion, msg: githubResult.message }, "release create failed — tag exists in GitHub but release page not created; manual cleanup required");
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (githubResult.kind === "other_error") {
|
|
375
|
+
await persistDecision({
|
|
376
|
+
id,
|
|
377
|
+
decision: "skip",
|
|
378
|
+
reason: "tag_create_error",
|
|
379
|
+
triggerStateJson,
|
|
380
|
+
proposedVersion,
|
|
381
|
+
});
|
|
382
|
+
void logAuditEventUnchecked(db, releaseSkippedEvent({ repoUrl, branch, reason: "tag_create_error" }));
|
|
383
|
+
log.error({ err: githubResult.message, repoUrl, branch }, "createTagAndRelease unknown error — wrote skip row");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
// ok
|
|
387
|
+
await persistDecision({
|
|
388
|
+
id,
|
|
389
|
+
decision: "fire",
|
|
390
|
+
reason: "all triggers passed",
|
|
391
|
+
triggerStateJson,
|
|
392
|
+
proposedVersion,
|
|
393
|
+
firedTag: proposedVersion,
|
|
394
|
+
firedSha: state.headSha,
|
|
395
|
+
});
|
|
396
|
+
if (state.hasFreshApproval) {
|
|
397
|
+
await consumeApprovalRow(id);
|
|
398
|
+
}
|
|
399
|
+
void logAuditEventUnchecked(db, releaseFiredEvent({
|
|
400
|
+
repoUrl,
|
|
401
|
+
branch,
|
|
402
|
+
tag: proposedVersion,
|
|
403
|
+
sha: state.headSha,
|
|
404
|
+
mergedPrCount: state.mergedCommitsSinceLastTag,
|
|
405
|
+
}));
|
|
406
|
+
await maybePostSlack(`:rocket: Released *${proposedVersion}* for ${repoUrl} (${branch}). ${githubResult.releaseUrl}`, null);
|
|
407
|
+
// Reset Slack dedup so the next skip re-posts.
|
|
408
|
+
lastSlackSkipReason = null;
|
|
409
|
+
}
|
|
410
|
+
function start() {
|
|
411
|
+
if (cronJob)
|
|
412
|
+
return;
|
|
413
|
+
cronJob = new Cron(config.schedule, () => {
|
|
414
|
+
tick().catch((err) => log.error({ err, repoUrl, branch }, "release-manager tick errored"));
|
|
415
|
+
});
|
|
416
|
+
log.info({ schedule: config.schedule, repoUrl, branch }, "release-manager scheduler started");
|
|
417
|
+
}
|
|
418
|
+
function stop() {
|
|
419
|
+
cronJob?.stop();
|
|
420
|
+
cronJob = null;
|
|
421
|
+
}
|
|
422
|
+
function pauseUntil(ts) {
|
|
423
|
+
pausedUntilTs = ts.getTime();
|
|
424
|
+
}
|
|
425
|
+
return { tick, start, stop, pauseUntil };
|
|
426
|
+
}
|
|
427
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/release-manager/scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AnyDb } from "../db/client.js";
|
|
2
|
+
export type ReleaseSubcommand = {
|
|
3
|
+
kind: "approve";
|
|
4
|
+
} | {
|
|
5
|
+
kind: "skip";
|
|
6
|
+
reason: string;
|
|
7
|
+
} | {
|
|
8
|
+
kind: "status";
|
|
9
|
+
} | {
|
|
10
|
+
kind: "unknown";
|
|
11
|
+
original: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Parse the text after "/release" into a structured subcommand.
|
|
15
|
+
* /release approve → { kind: "approve" }
|
|
16
|
+
* /release skip foo bar → { kind: "skip", reason: "foo bar" }
|
|
17
|
+
* /release status → { kind: "status" }
|
|
18
|
+
* anything else / empty → { kind: "unknown", original }
|
|
19
|
+
*
|
|
20
|
+
* "skip" with no reason returns "unknown" so the caller renders the help message.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseReleaseSubcommand(text: string): ReleaseSubcommand;
|
|
23
|
+
export interface HandleReleaseSubcommandInput {
|
|
24
|
+
cmd: ReleaseSubcommand;
|
|
25
|
+
db: AnyDb;
|
|
26
|
+
repoUrl: string;
|
|
27
|
+
branch: string;
|
|
28
|
+
slackUserId: string;
|
|
29
|
+
/** Optional hook so the scheduler can be told to pause after /release skip. */
|
|
30
|
+
onSkip?: (reason: string) => void;
|
|
31
|
+
/** Optional: hours until the next eligible tick after a /release skip. Used in the response text. */
|
|
32
|
+
pauseDurationHours?: number;
|
|
33
|
+
}
|
|
34
|
+
export interface SlackResponse {
|
|
35
|
+
text: string;
|
|
36
|
+
responseType: "ephemeral" | "in_channel";
|
|
37
|
+
}
|
|
38
|
+
export declare function handleReleaseSubcommand(input: HandleReleaseSubcommandInput): Promise<SlackResponse>;
|
|
39
|
+
//# sourceMappingURL=slack-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-handler.d.ts","sourceRoot":"","sources":["../../src/release-manager/slack-handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAQ7C,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1C;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAatE;AAED,MAAM,WAAW,4BAA4B;IAC3C,GAAG,EAAE,iBAAiB,CAAC;IACvB,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,qGAAqG;IACrG,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,WAAW,GAAG,YAAY,CAAC;CAC1C;AAKD,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,aAAa,CAAC,CA+FxB"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { eq, and, desc } from "drizzle-orm";
|
|
3
|
+
import { releaseApprovals, releaseDecisions } from "../db/schema.js";
|
|
4
|
+
import { logAuditEventUnchecked } from "../audit/writer.js";
|
|
5
|
+
import { releaseApprovedEvent, releaseSkippedEvent } from "../audit/events.js";
|
|
6
|
+
import { createLogger } from "../logger.js";
|
|
7
|
+
const log = createLogger({ component: "ReleaseManager:slack-handler" });
|
|
8
|
+
/**
|
|
9
|
+
* Parse the text after "/release" into a structured subcommand.
|
|
10
|
+
* /release approve → { kind: "approve" }
|
|
11
|
+
* /release skip foo bar → { kind: "skip", reason: "foo bar" }
|
|
12
|
+
* /release status → { kind: "status" }
|
|
13
|
+
* anything else / empty → { kind: "unknown", original }
|
|
14
|
+
*
|
|
15
|
+
* "skip" with no reason returns "unknown" so the caller renders the help message.
|
|
16
|
+
*/
|
|
17
|
+
export function parseReleaseSubcommand(text) {
|
|
18
|
+
const trimmed = (text ?? "").trim();
|
|
19
|
+
const lower = trimmed.toLowerCase();
|
|
20
|
+
if (lower === "approve")
|
|
21
|
+
return { kind: "approve" };
|
|
22
|
+
if (lower === "status")
|
|
23
|
+
return { kind: "status" };
|
|
24
|
+
if (/^skip\s+\S/.test(lower)) {
|
|
25
|
+
const reason = trimmed.replace(/^skip\s+/i, "").trim();
|
|
26
|
+
return { kind: "skip", reason };
|
|
27
|
+
}
|
|
28
|
+
return { kind: "unknown", original: trimmed };
|
|
29
|
+
}
|
|
30
|
+
const HELP_TEXT = "Try `/release approve`, `/release skip <reason>`, or `/release status`.";
|
|
31
|
+
export async function handleReleaseSubcommand(input) {
|
|
32
|
+
const { cmd, db, repoUrl, branch, slackUserId, onSkip, pauseDurationHours } = input;
|
|
33
|
+
switch (cmd.kind) {
|
|
34
|
+
case "approve": {
|
|
35
|
+
const id = `ra_${randomUUID()}`;
|
|
36
|
+
try {
|
|
37
|
+
await db.insert(releaseApprovals).values({
|
|
38
|
+
id,
|
|
39
|
+
repoUrl,
|
|
40
|
+
branch,
|
|
41
|
+
approvedAt: new Date(),
|
|
42
|
+
approvedBy: slackUserId,
|
|
43
|
+
});
|
|
44
|
+
void logAuditEventUnchecked(db, releaseApprovedEvent({ repoUrl, branch, approvedBy: slackUserId }));
|
|
45
|
+
return {
|
|
46
|
+
text: `:white_check_mark: Approved by <@${slackUserId}>. Next eligible tick will fire if other rules pass.`,
|
|
47
|
+
responseType: "in_channel",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
// UNIQUE partial index → "already approved" friendly message.
|
|
52
|
+
const msg = String(err?.message ?? err);
|
|
53
|
+
if (/UNIQUE|unique|duplicate/.test(msg)) {
|
|
54
|
+
return {
|
|
55
|
+
text: `:information_source: <@${slackUserId}> has already approved this release. Awaiting other triggers.`,
|
|
56
|
+
responseType: "in_channel",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
log.error({ err, repoUrl, branch }, "release approve write failed");
|
|
60
|
+
return {
|
|
61
|
+
text: `:x: Failed to record approval: ${msg}`,
|
|
62
|
+
responseType: "ephemeral",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
case "skip": {
|
|
67
|
+
const id = `rd_${randomUUID()}`;
|
|
68
|
+
await db.insert(releaseDecisions).values({
|
|
69
|
+
id,
|
|
70
|
+
repoUrl,
|
|
71
|
+
branch,
|
|
72
|
+
decidedAt: new Date(),
|
|
73
|
+
decision: "skip",
|
|
74
|
+
reason: `manual:${cmd.reason}`,
|
|
75
|
+
triggerStateJson: JSON.stringify({ source: "slack", slackUserId }),
|
|
76
|
+
attemptCount: 0,
|
|
77
|
+
});
|
|
78
|
+
void logAuditEventUnchecked(db, releaseSkippedEvent({
|
|
79
|
+
repoUrl,
|
|
80
|
+
branch,
|
|
81
|
+
reason: `manual:${cmd.reason}`,
|
|
82
|
+
}));
|
|
83
|
+
onSkip?.(cmd.reason);
|
|
84
|
+
const durationText = pauseDurationHours
|
|
85
|
+
? `Will re-evaluate after ${pauseDurationHours}h.`
|
|
86
|
+
: "Will re-evaluate on next tick.";
|
|
87
|
+
return {
|
|
88
|
+
text: `:double_vertical_bar: Release skipped: ${cmd.reason}. ${durationText}`,
|
|
89
|
+
responseType: "in_channel",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
case "status": {
|
|
93
|
+
const recent = await db
|
|
94
|
+
.select({
|
|
95
|
+
decidedAt: releaseDecisions.decidedAt,
|
|
96
|
+
decision: releaseDecisions.decision,
|
|
97
|
+
reason: releaseDecisions.reason,
|
|
98
|
+
firedTag: releaseDecisions.firedTag,
|
|
99
|
+
})
|
|
100
|
+
.from(releaseDecisions)
|
|
101
|
+
.where(and(eq(releaseDecisions.repoUrl, repoUrl), eq(releaseDecisions.branch, branch)))
|
|
102
|
+
.orderBy(desc(releaseDecisions.decidedAt))
|
|
103
|
+
.limit(5);
|
|
104
|
+
const lines = [`*Release Manager status — ${repoUrl} (${branch})*`, "Recent decisions:"];
|
|
105
|
+
if (recent.length === 0) {
|
|
106
|
+
lines.push(" _no decisions yet_");
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
for (const r of recent) {
|
|
110
|
+
const ts = r.decidedAt instanceof Date ? r.decidedAt.toISOString() : String(r.decidedAt);
|
|
111
|
+
const tail = r.firedTag ? ` (tag=${r.firedTag})` : "";
|
|
112
|
+
lines.push(` • [${r.decision}] ${r.reason}${tail} — ${ts}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
text: lines.join("\n"),
|
|
117
|
+
responseType: "ephemeral",
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
case "unknown":
|
|
121
|
+
return { text: HELP_TEXT, responseType: "ephemeral" };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=slack-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-handler.js","sourceRoot":"","sources":["../../src/release-manager/slack-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Octokit } from "@octokit/rest";
|
|
2
|
+
import type { AnyDb } from "../db/client.js";
|
|
3
|
+
import type { CollectedState } from "./types.js";
|
|
4
|
+
export interface CollectStateInput {
|
|
5
|
+
octokit: Octokit;
|
|
6
|
+
db: AnyDb;
|
|
7
|
+
repoUrl: string;
|
|
8
|
+
branch: string;
|
|
9
|
+
/** Approval freshness window in ms (computed from triggers.timeSinceLastHours, default 24h). */
|
|
10
|
+
approvalTtlMs: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Single GitHub call surface for one tick. Wrapped in try/catch by the
|
|
14
|
+
* scheduler — failures here surface as "ci_check_unavailable" or are
|
|
15
|
+
* re-thrown depending on which API failed.
|
|
16
|
+
*/
|
|
17
|
+
export declare function collectState(input: CollectStateInput): Promise<CollectedState>;
|
|
18
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/release-manager/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAI7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,gGAAgG;IAChG,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAoKpF"}
|