diff --git a/integrations/validate-data-type-anchors.ts b/integrations/validate-data-type-anchors.ts index 0de789aa11..e61dd0d6ad 100644 --- a/integrations/validate-data-type-anchors.ts +++ b/integrations/validate-data-type-anchors.ts @@ -1,9 +1,19 @@ import type { AstroIntegration } from 'astro'; +import rawApiSchema from '../public/api-schemas.json'; import rawConfigSchema from '../public/mergify-configuration-schema.json'; import { missingDataTypeAnchors } from '../src/util/dataTypeAnchors'; +// Both synced schemas can carry the engine's `x-has-data-type` marker: the +// configuration schema for types you write in `.mergify.yml`, the OpenAPI spec +// for types the API only reports (a batch status, say). Both arrive by the same +// bot sync, so both need the same gate. +const SCHEMAS: { file: string; schema: unknown }[] = [ + { file: 'public/mergify-configuration-schema.json', schema: rawConfigSchema }, + { file: 'public/api-schemas.json', schema: rawApiSchema }, +]; + /** - * Fail the build when the config schema flags a documented data type whose + * Fail the build when a synced schema flags a documented data type whose * derived anchor (slugified `title`) has no matching heading on the * data-types page. This is the enforcement point that actually guards the * drift path: schema syncs land as direct bot pushes to main (no PR, so no @@ -16,14 +26,18 @@ export function validateDataTypeAnchors(): AstroIntegration { name: 'validate-data-type-anchors', hooks: { 'astro:build:start': () => { - const missing = missingDataTypeAnchors(rawConfigSchema); - if (missing.length > 0) { + const problems = SCHEMAS.flatMap(({ file, schema }) => { + const missing = missingDataTypeAnchors(schema); + return missing.length > 0 ? [`${file}: ${missing.join(', ')}`] : []; + }); + + if (problems.length > 0) { throw new Error( - `Documented data type(s) in public/mergify-configuration-schema.json have no ` + - `matching heading anchor on src/content/docs/configuration/data-types.mdx: ` + - `${missing.join(', ')}. A marked node's slugified title must equal the anchor ` + - `of its section heading (add the missing section, or fix the title next to the ` + - `engine's DocsDataType annotation).` + `Documented data type(s) in a synced schema have no matching heading anchor on ` + + `src/content/docs/configuration/data-types.mdx — ${problems.join('; ')}. ` + + `A marked node's slugified title must equal the anchor of its section heading ` + + `(add the missing section, or fix the title next to the engine's DocsDataType ` + + `annotation).` ); } }, diff --git a/public/api-schemas.json b/public/api-schemas.json index a28b89baa7..39ab7ef174 100644 --- a/public/api-schemas.json +++ b/public/api-schemas.json @@ -1329,53 +1329,7 @@ { "type": "array", "items": { - "enum": [ - "action.assign", - "action.backport", - "action.close", - "action.comment", - "action.copy", - "action.delete_head_branch", - "action.dequeue", - "action.dismiss_reviews", - "action.edit", - "action.github_actions", - "action.label", - "action.merge", - "action.post_check", - "action.queue.batch_bisection_end", - "action.queue.batch_bisection_start", - "action.queue.change", - "action.queue.checks.change", - "action.queue.checks_end", - "action.queue.checks_not_started", - "action.queue.checks_start", - "action.queue.conflict_deferred", - "action.queue.enter", - "action.queue.leave", - "action.queue.merged", - "action.rebase", - "action.refresh", - "action.request_reviews", - "action.review", - "action.squash", - "action.update", - "ci_insights.auto_quarantine.disabled", - "ci_insights.auto_quarantine.enabled", - "ci_insights.auto_quarantine.modified", - "ci_insights.job_retried", - "command.dequeue", - "command.queue", - "queue.pause.create", - "queue.pause.delete", - "queue.pause.update", - "scheduled_freeze.create", - "scheduled_freeze.delete", - "scheduled_freeze.update", - "test.dequarantined", - "test.quarantined" - ], - "type": "string" + "$ref": "#/components/schemas/EventType" } }, { @@ -1396,53 +1350,7 @@ { "type": "array", "items": { - "enum": [ - "action.assign", - "action.backport", - "action.close", - "action.comment", - "action.copy", - "action.delete_head_branch", - "action.dequeue", - "action.dismiss_reviews", - "action.edit", - "action.github_actions", - "action.label", - "action.merge", - "action.post_check", - "action.queue.batch_bisection_end", - "action.queue.batch_bisection_start", - "action.queue.change", - "action.queue.checks.change", - "action.queue.checks_end", - "action.queue.checks_not_started", - "action.queue.checks_start", - "action.queue.conflict_deferred", - "action.queue.enter", - "action.queue.leave", - "action.queue.merged", - "action.rebase", - "action.refresh", - "action.request_reviews", - "action.review", - "action.squash", - "action.update", - "ci_insights.auto_quarantine.disabled", - "ci_insights.auto_quarantine.enabled", - "ci_insights.auto_quarantine.modified", - "ci_insights.job_retried", - "command.dequeue", - "command.queue", - "queue.pause.create", - "queue.pause.delete", - "queue.pause.update", - "scheduled_freeze.create", - "scheduled_freeze.delete", - "scheduled_freeze.update", - "test.dequarantined", - "test.quarantined" - ], - "type": "string" + "$ref": "#/components/schemas/EventType" } }, { @@ -1463,13 +1371,7 @@ { "type": "array", "items": { - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "type": "string" + "$ref": "#/components/schemas/EventOutcome" } }, { @@ -1490,13 +1392,7 @@ { "type": "array", "items": { - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "type": "string" + "$ref": "#/components/schemas/EventOutcome" } }, { @@ -1828,53 +1724,7 @@ { "type": "array", "items": { - "enum": [ - "action.assign", - "action.backport", - "action.close", - "action.comment", - "action.copy", - "action.delete_head_branch", - "action.dequeue", - "action.dismiss_reviews", - "action.edit", - "action.github_actions", - "action.label", - "action.merge", - "action.post_check", - "action.queue.batch_bisection_end", - "action.queue.batch_bisection_start", - "action.queue.change", - "action.queue.checks.change", - "action.queue.checks_end", - "action.queue.checks_not_started", - "action.queue.checks_start", - "action.queue.conflict_deferred", - "action.queue.enter", - "action.queue.leave", - "action.queue.merged", - "action.rebase", - "action.refresh", - "action.request_reviews", - "action.review", - "action.squash", - "action.update", - "ci_insights.auto_quarantine.disabled", - "ci_insights.auto_quarantine.enabled", - "ci_insights.auto_quarantine.modified", - "ci_insights.job_retried", - "command.dequeue", - "command.queue", - "queue.pause.create", - "queue.pause.delete", - "queue.pause.update", - "scheduled_freeze.create", - "scheduled_freeze.delete", - "scheduled_freeze.update", - "test.dequarantined", - "test.quarantined" - ], - "type": "string" + "$ref": "#/components/schemas/EventType" } }, { @@ -1895,53 +1745,7 @@ { "type": "array", "items": { - "enum": [ - "action.assign", - "action.backport", - "action.close", - "action.comment", - "action.copy", - "action.delete_head_branch", - "action.dequeue", - "action.dismiss_reviews", - "action.edit", - "action.github_actions", - "action.label", - "action.merge", - "action.post_check", - "action.queue.batch_bisection_end", - "action.queue.batch_bisection_start", - "action.queue.change", - "action.queue.checks.change", - "action.queue.checks_end", - "action.queue.checks_not_started", - "action.queue.checks_start", - "action.queue.conflict_deferred", - "action.queue.enter", - "action.queue.leave", - "action.queue.merged", - "action.rebase", - "action.refresh", - "action.request_reviews", - "action.review", - "action.squash", - "action.update", - "ci_insights.auto_quarantine.disabled", - "ci_insights.auto_quarantine.enabled", - "ci_insights.auto_quarantine.modified", - "ci_insights.job_retried", - "command.dequeue", - "command.queue", - "queue.pause.create", - "queue.pause.delete", - "queue.pause.update", - "scheduled_freeze.create", - "scheduled_freeze.delete", - "scheduled_freeze.update", - "test.dequarantined", - "test.quarantined" - ], - "type": "string" + "$ref": "#/components/schemas/EventType" } }, { @@ -1962,13 +1766,7 @@ { "type": "array", "items": { - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "type": "string" + "$ref": "#/components/schemas/EventOutcome" } }, { @@ -1989,13 +1787,7 @@ { "type": "array", "items": { - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "type": "string" + "$ref": "#/components/schemas/EventOutcome" } }, { @@ -5567,12 +5359,7 @@ { "type": "array", "items": { - "enum": [ - "healthy", - "flaky", - "broken" - ], - "type": "string" + "$ref": "#/components/schemas/HealthStatus" } }, { @@ -5593,12 +5380,7 @@ { "type": "array", "items": { - "enum": [ - "healthy", - "flaky", - "broken" - ], - "type": "string" + "$ref": "#/components/schemas/HealthStatus" } }, { @@ -5619,12 +5401,7 @@ { "type": "array", "items": { - "enum": [ - "low", - "medium", - "high" - ], - "type": "string" + "$ref": "#/components/schemas/HealthConfidence" } }, { @@ -5645,12 +5422,7 @@ { "type": "array", "items": { - "enum": [ - "low", - "medium", - "high" - ], - "type": "string" + "$ref": "#/components/schemas/HealthConfidence" } }, { @@ -6587,11 +6359,7 @@ "schema": { "anyOf": [ { - "enum": [ - "manual", - "auto" - ], - "type": "string" + "$ref": "#/components/schemas/QuarantineSource" }, { "type": "null" @@ -7536,12 +7304,7 @@ { "type": "array", "items": { - "enum": [ - "low", - "medium", - "high" - ], - "type": "string" + "$ref": "#/components/schemas/HealthConfidence" } }, { @@ -7562,12 +7325,7 @@ { "type": "array", "items": { - "enum": [ - "low", - "medium", - "high" - ], - "type": "string" + "$ref": "#/components/schemas/HealthConfidence" } }, { @@ -9497,7 +9255,58 @@ "waiting_for_checks_slot", "frozen" ], - "title": "Code" + "title": "Batch Status", + "x-has-data-type": true, + "x-mergify-enum": [ + { + "description": "The batch is set up and its CI checks are running.", + "title": "CI Running" + }, + { + "description": "The batch failed, so Mergify is splitting it into smaller sub-batches and testing those to identify the pull request responsible and merge the others.", + "title": "Bisecting Batch" + }, + { + "description": "Mergify is setting the batch up so it can be tested.", + "title": "Preparing" + }, + { + "description": "The batch failed, either because Mergify could not prepare it for testing or because its checks did not pass. It is removed from the queue and its pull requests have to be fixed.", + "title": "Failed" + }, + { + "description": "The batch was merged.", + "title": "Merged" + }, + { + "description": "The batch's checks passed. It merges once the batches ahead of it have merged.", + "title": "Waiting to Merge" + }, + { + "description": "A batch ahead in the queue failed, so this batch is held while Mergify identifies the cause. It has either not started its own checks yet, or already ran and failed and is parked until the cause is known.", + "title": "Waiting on Previous Batch" + }, + { + "description": "A batch ahead in the queue failed, so this batch is requeued and retested against the updated queue.", + "title": "Requeuing" + }, + { + "description": "The batch's merge conditions are met, but a schedule condition is not. It continues once the schedule allows it.", + "title": "Waiting for Schedule" + }, + { + "description": "Mergify is waiting for more pull requests to fill the batch, or for the maximum wait time to elapse, before starting it.", + "title": "Filling Batch" + }, + { + "description": "The batch is ready to be tested, but every slot for running checks in parallel is taken. It starts as soon as one frees up.", + "title": "Waiting for CI Slot" + }, + { + "description": "The batch's checks passed, but merges are frozen. It merges once the freeze is lifted.", + "title": "Frozen" + } + ] }, "batch_filled_slots": { "anyOf": [ @@ -9785,20 +9594,7 @@ "description": "The timestamp when the checks have ended for this pull request" }, "state": { - "type": "string", - "enum": [ - "failure", - "error", - "cancelled", - "action_required", - "timed_out", - "pending", - "neutral", - "skipped", - "stale", - "success" - ], - "title": "State", + "$ref": "#/components/schemas/QueueCheckState", "description": "The global state of the checks" } }, @@ -10450,14 +10246,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -10548,14 +10337,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -10622,14 +10404,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -10723,14 +10498,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -10824,14 +10592,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -10929,14 +10690,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -11092,14 +10846,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -11179,14 +10926,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -11311,14 +11051,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -11385,14 +11118,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -11472,14 +11198,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -11569,14 +11288,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -11656,14 +11368,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "metadata": { "$ref": "#/components/schemas/EventNoMetadata" @@ -11730,14 +11435,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -11820,14 +11518,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -11907,14 +11598,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -12012,14 +11696,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -12110,14 +11787,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -12160,6 +11830,15 @@ "type": "object", "title": "EventNoMetadata" }, + "EventOutcome": { + "type": "string", + "enum": [ + "success", + "failure", + "pending", + "neutral" + ] + }, "EventPostCheck": { "properties": { "id": { @@ -12202,14 +11881,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -12317,14 +11989,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -12538,14 +12203,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -12715,14 +12373,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -12819,14 +12470,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -12911,14 +12555,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -13219,14 +12856,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -13340,14 +12970,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -13579,14 +13202,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -13711,14 +13327,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -13827,14 +13436,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -14103,14 +13705,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -14232,14 +13827,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -14328,14 +13916,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -14419,14 +14000,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -14515,14 +14089,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "metadata": { "$ref": "#/components/schemas/EventNoMetadata" @@ -14589,14 +14156,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "metadata": { "$ref": "#/components/schemas/EventNoMetadata" @@ -14663,14 +14223,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -14761,14 +14314,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -14877,14 +14423,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -14978,14 +14517,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -15084,14 +14616,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -15189,14 +14714,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "metadata": { "$ref": "#/components/schemas/EventNoMetadata" @@ -15263,14 +14781,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -15312,24 +14823,13 @@ "title": "Test Name" }, "source": { - "type": "string", - "enum": [ - "manual", - "auto" - ], - "title": "Source" + "$ref": "#/components/schemas/QuarantineSource" }, "original_quarantine": { "$ref": "#/components/schemas/TestQuarantineSnapshot" }, "health_status_at_event": { - "type": "string", - "enum": [ - "healthy", - "flaky", - "broken" - ], - "title": "Health Status At Event" + "$ref": "#/components/schemas/HealthStatus" }, "dequarantined_at": { "type": "string", @@ -15406,14 +14906,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "type": { "type": "string", @@ -15455,12 +14948,7 @@ "title": "Test Name" }, "source": { - "type": "string", - "enum": [ - "manual", - "auto" - ], - "title": "Source" + "$ref": "#/components/schemas/QuarantineSource" }, "reason": { "type": "string", @@ -15483,13 +14971,7 @@ "title": "Quarantined At" }, "health_status_at_event": { - "type": "string", - "enum": [ - "healthy", - "flaky", - "broken" - ], - "title": "Health Status At Event" + "$ref": "#/components/schemas/HealthStatus" }, "quarantined_by": { "anyOf": [ @@ -15520,6 +15002,55 @@ ], "title": "EventTestQuarantinedMetadata" }, + "EventType": { + "type": "string", + "enum": [ + "action.assign", + "action.backport", + "action.close", + "action.comment", + "action.copy", + "action.delete_head_branch", + "action.dequeue", + "action.dismiss_reviews", + "action.edit", + "action.github_actions", + "action.label", + "action.merge", + "action.post_check", + "action.queue.batch_bisection_end", + "action.queue.batch_bisection_start", + "action.queue.change", + "action.queue.checks.change", + "action.queue.checks_end", + "action.queue.checks_not_started", + "action.queue.checks_start", + "action.queue.conflict_deferred", + "action.queue.enter", + "action.queue.leave", + "action.queue.merged", + "action.rebase", + "action.refresh", + "action.request_reviews", + "action.review", + "action.squash", + "action.update", + "ci_insights.auto_quarantine.disabled", + "ci_insights.auto_quarantine.enabled", + "ci_insights.auto_quarantine.modified", + "ci_insights.job_retried", + "command.dequeue", + "command.queue", + "queue.pause.create", + "queue.pause.delete", + "queue.pause.update", + "scheduled_freeze.create", + "scheduled_freeze.delete", + "scheduled_freeze.update", + "test.dequarantined", + "test.quarantined" + ] + }, "EventUpdate": { "properties": { "id": { @@ -15562,14 +15093,7 @@ "title": "Base Ref" }, "outcome": { - "type": "string", - "enum": [ - "success", - "failure", - "pending", - "neutral" - ], - "title": "Outcome" + "$ref": "#/components/schemas/EventOutcome" }, "metadata": { "$ref": "#/components/schemas/EventNoMetadata" @@ -16329,6 +15853,22 @@ ], "title": "GitHubAccount" }, + "HealthConfidence": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ] + }, + "HealthStatus": { + "type": "string", + "enum": [ + "healthy", + "flaky", + "broken" + ] + }, "HealthTransition": { "properties": { "at": { @@ -16338,13 +15878,7 @@ "description": "Bucket boundary at which the test's health changed to `health_status`." }, "health_status": { - "type": "string", - "enum": [ - "healthy", - "flaky", - "broken" - ], - "title": "Health Status", + "$ref": "#/components/schemas/HealthStatus", "description": "Health the test held from `at` until the next transition." }, "exception_type": { @@ -17777,6 +17311,13 @@ ], "title": "PullRequestQueued" }, + "QuarantineSource": { + "type": "string", + "enum": [ + "manual", + "auto" + ] + }, "QuarantinedTest": { "properties": { "id": { @@ -17820,12 +17361,7 @@ "description": "UTC timestamp when the test was added to quarantine." }, "source": { - "type": "string", - "enum": [ - "manual", - "auto" - ], - "title": "Source", + "$ref": "#/components/schemas/QuarantineSource", "description": "How the test got quarantined: `manual` (user action) or `auto` (auto-quarantined).", "default": "manual" }, @@ -17957,20 +17493,7 @@ "description": "External integrator's direct URL (e.g. Jenkins build URL)" }, "state": { - "type": "string", - "enum": [ - "failure", - "error", - "cancelled", - "action_required", - "timed_out", - "pending", - "neutral", - "skipped", - "stale", - "success" - ], - "title": "State", + "$ref": "#/components/schemas/QueueCheckState", "description": "Check state" }, "avatar_url": { @@ -17996,6 +17519,21 @@ ], "title": "QueueCheck" }, + "QueueCheckState": { + "type": "string", + "enum": [ + "failure", + "error", + "cancelled", + "action_required", + "timed_out", + "pending", + "neutral", + "skipped", + "stale", + "success" + ] + }, "QueueChecksOutcome": { "properties": { "BASE_BRANCH_CHANGED": { @@ -19491,12 +19029,7 @@ "TestQuarantineSnapshot": { "properties": { "source": { - "type": "string", - "enum": [ - "manual", - "auto" - ], - "title": "Source" + "$ref": "#/components/schemas/QuarantineSource" }, "reason": { "type": "string", @@ -19569,23 +19102,11 @@ "TestSearchResultMetrics": { "properties": { "health_status": { - "type": "string", - "enum": [ - "healthy", - "flaky", - "broken" - ], - "title": "Health Status", + "$ref": "#/components/schemas/HealthStatus", "description": "Current health classification of the test: healthy, flaky, or broken." }, "health_confidence": { - "type": "string", - "enum": [ - "low", - "medium", - "high" - ], - "title": "Health Confidence", + "$ref": "#/components/schemas/HealthConfidence", "description": "Confidence in the health classification, derived from how many times the test has been rerun." }, "failed_executions_impact": { @@ -20021,20 +19542,7 @@ "title": "Details Url" }, "state": { - "type": "string", - "enum": [ - "failure", - "error", - "cancelled", - "action_required", - "timed_out", - "pending", - "neutral", - "skipped", - "stale", - "success" - ], - "title": "State" + "$ref": "#/components/schemas/QueueCheckState" }, "avatar_url": { "anyOf": [ @@ -20337,20 +19845,7 @@ "description": "The queue rule conditions evaluation report" }, "state": { - "type": "string", - "enum": [ - "failure", - "error", - "cancelled", - "action_required", - "timed_out", - "pending", - "neutral", - "skipped", - "stale", - "success" - ], - "title": "State", + "$ref": "#/components/schemas/QueueCheckState", "description": "The global state of the checks" } }, diff --git a/public/mergify-configuration-schema.json b/public/mergify-configuration-schema.json index 15febed1a5..7451a64aca 100644 --- a/public/mergify-configuration-schema.json +++ b/public/mergify-configuration-schema.json @@ -2552,19 +2552,11 @@ }, "queue-dequeue-reason": { "anyOf": [ - { - "const": "NONE", - "type": "string" - }, { "enum": [ + "NONE", "PR_MERGED", - "PR_MERGED_INTERMEDIATE_RESULTS_SKIPPED" - ], - "type": "string" - }, - { - "enum": [ + "PR_MERGED_INTERMEDIATE_RESULTS_SKIPPED", "PR_DEQUEUED", "PR_DEQUEUED_FROM_PARTITION", "PR_AHEAD_DEQUEUED", @@ -2616,49 +2608,129 @@ "!=", "~=" ], - "x-enum-descriptions": { - "BASE_BRANCH_CHANGED": "The pull request's base branch changed.", - "BASE_BRANCH_MISSING": "The pull request's base branch no longer exists.", - "BASE_REF_ALIGNMENT_TIMEOUT": "The pull request could not be retargeted onto the queue base branch, for example a stacked pull request GitHub never retargeted.", - "BATCH_AHEAD_FAILED": "A batch ahead in the queue failed, so this pull request's speculative checks are restarted. It stays in the queue.", - "BATCH_MAX_FAILURE_RESOLUTION_ATTEMPTS": "The maximum number of `batch_max_failure_resolution_attempts` was reached while isolating a batch failure.", - "BATCH_PULL_REQUEST_CLOSED": "The merge queue batch pull request was closed.", - "BATCH_SCOPES_CHANGED": "A pull request's scopes changed after it was embarked, so its batch was recomposed. It stays in the queue.", - "BRANCH_UPDATE_FAILED": "Updating the pull request's head branch failed.", - "CHECKS_FAILED": "The queue conditions cannot be satisfied because required checks failed.", - "CHECKS_RETRIED": "The pull request's failed checks are being retried. It stays in the queue.", - "CHECKS_TIMEOUT": "The configured `checks_timeout` was reached before the queue conditions were satisfied.", - "CONFIGURATION_CHANGED": "The Mergify configuration changed while the pull request was queued.", - "CONFLICT_WITH_BASE_BRANCH": "The pull request conflicts with its base branch.", - "CONFLICT_WITH_PULL_AHEAD": "The pull request conflicts with a pull request ahead of it in the queue.", - "DRAFT_PULL_REQUEST_CHANGED": "The merge queue draft pull request received commits that Mergify did not create.", - "DRAFT_PULL_REQUEST_CREATION_BRANCH_NOT_INDEXED": "Mergify could not create the merge queue draft pull request because GitHub had not yet indexed the new branch.", - "DRAFT_PULL_REQUEST_CREATION_FAILED": "Mergify could not create the merge queue draft pull request.", - "DROPPED_BY_BISECTION_ELIMINATION": "Batch bisection isolated the failure to other pull requests and dropped this one without testing it individually.", - "GITHUB_STACKED_PULL_REQUEST_MERGE_UNSUPPORTED": "The pull request is a GitHub stacked pull request, which GitHub does not support merging through its API.", - "INCOMPATIBILITY_WITH_BRANCH_PROTECTIONS": "The pull request cannot be checked because of an incompatibility between the queue and the repository's branch protections.", - "INTERMEDIATE_RESULTS_SKIPPED": "The pull request's intermediate speculative results were skipped. It stays in the queue.", - "MERGE_QUEUE_RESET": "The merge queue was reset.", - "NONE": "The pull request is not, and never was, in a merge queue.", - "PR_AHEAD_DEQUEUED": "A pull request ahead in the queue was removed, so this pull request's speculative checks are restarted. It stays in the queue.", - "PR_CHECKS_STOPPED_BECAUSE_MERGE_QUEUE_PAUSE": "The pull request's checks were interrupted because the merge queue is paused on the repository. It stays in the queue.", - "PR_DEQUEUED": "The pull request was removed from the queue, for example because its queue conditions no longer match.", - "PR_DEQUEUED_FROM_PARTITION": "The pull request stopped matching a partition's rules and was removed from that partition.", - "PR_MANUALLY_DEQUEUED": "The pull request was manually removed from the queue.", - "PR_MANUALLY_MERGED": "The pull request was merged manually, outside of the merge queue.", - "PR_MERGED": "The pull request was merged by the merge queue.", - "PR_MERGED_INTERMEDIATE_RESULTS_SKIPPED": "The pull request was merged based on the successful result of a later pull request in the same speculative batch, skipping its own intermediate checks.", - "PR_UNEXPECTEDLY_FAILED_TO_MERGE": "An unexpected error happened while merging the pull request.", - "PR_WITH_HIGHER_PRIORITY_QUEUED": "A higher-priority pull request was queued ahead of this one. It stays in the queue.", - "PULL_REQUEST_UPDATED": "The pull request was manually updated.", - "QUEUE_RULE_MISSING": "The queue rule referenced by the pull request no longer exists in the configuration.", - "SCHEDULED_FREEZE_STATUS_CHANGED": "A scheduled queue freeze changed the freeze status of the batch's pull requests, so the batch was split. It stays in the queue.", - "SCHEDULE_BLOCKED_AHEAD_YIELDED": "A pull request ahead that was waiting for its merge schedule yielded its position so ready pull requests behind it can merge first. It stays in the queue.", - "SPECULATIVE_CHECK_NUMBER_REDUCED": "The number of speculative checks was reduced. The pull request stays in the queue.", - "STACK_PREDECESSOR_DEQUEUED": "A predecessor pull request in the same stack was dequeued, so this one was removed as well.", - "UNPROCESSABLE_PULL_REQUEST": "Mergify cannot process the pull request, for example because it has too many check runs, comments, or files." - }, "x-has-data-type": true, + "x-mergify-enum": [ + { + "description": "The pull request is not, and never was, in a merge queue." + }, + { + "description": "The pull request was merged by the merge queue." + }, + { + "description": "The pull request was merged based on the successful result of a later pull request in the same speculative batch, skipping its own intermediate checks." + }, + { + "description": "The pull request was removed from the queue, for example because its queue conditions no longer match." + }, + { + "description": "The pull request stopped matching a partition's rules and was removed from that partition." + }, + { + "description": "A pull request ahead in the queue was removed, so this pull request's speculative checks are restarted. It stays in the queue." + }, + { + "description": "A batch ahead in the queue failed, so this pull request's speculative checks are restarted. It stays in the queue." + }, + { + "description": "A higher-priority pull request was queued ahead of this one. It stays in the queue." + }, + { + "description": "A scheduled queue freeze changed the freeze status of the batch's pull requests, so the batch was split. It stays in the queue." + }, + { + "description": "The number of speculative checks was reduced. The pull request stays in the queue." + }, + { + "description": "The configured `checks_timeout` was reached before the queue conditions were satisfied." + }, + { + "description": "The queue conditions cannot be satisfied because required checks failed." + }, + { + "description": "Batch bisection isolated the failure to other pull requests and dropped this one without testing it individually." + }, + { + "description": "The queue rule referenced by the pull request no longer exists in the configuration." + }, + { + "description": "The pull request's base branch no longer exists." + }, + { + "description": "The pull request's base branch changed." + }, + { + "description": "An unexpected error happened while merging the pull request." + }, + { + "description": "The maximum number of `batch_max_failure_resolution_attempts` was reached while isolating a batch failure." + }, + { + "description": "The pull request's checks were interrupted because the merge queue is paused on the repository. It stays in the queue." + }, + { + "description": "The pull request conflicts with its base branch." + }, + { + "description": "The pull request conflicts with a pull request ahead of it in the queue." + }, + { + "description": "Updating the pull request's head branch failed." + }, + { + "description": "The merge queue draft pull request received commits that Mergify did not create." + }, + { + "description": "The merge queue batch pull request was closed." + }, + { + "description": "The pull request was manually updated." + }, + { + "description": "The merge queue was reset." + }, + { + "description": "The pull request cannot be checked because of an incompatibility between the queue and the repository's branch protections." + }, + { + "description": "The pull request was merged manually, outside of the merge queue." + }, + { + "description": "Mergify could not create the merge queue draft pull request." + }, + { + "description": "Mergify could not create the merge queue draft pull request because GitHub had not yet indexed the new branch." + }, + { + "description": "The Mergify configuration changed while the pull request was queued." + }, + { + "description": "Mergify cannot process the pull request, for example because it has too many check runs, comments, or files." + }, + { + "description": "The pull request was manually removed from the queue." + }, + { + "description": "A predecessor pull request in the same stack was dequeued, so this one was removed as well." + }, + { + "description": "The pull request's intermediate speculative results were skipped. It stays in the queue." + }, + { + "description": "The pull request's failed checks are being retried. It stays in the queue." + }, + { + "description": "A pull request ahead that was waiting for its merge schedule yielded its position so ready pull requests behind it can merge first. It stays in the queue." + }, + { + "description": "The pull request could not be retargeted onto the queue base branch, for example a stacked pull request GitHub never retargeted." + }, + { + "description": "The pull request is a GitHub stacked pull request, which GitHub does not support merging through its API." + }, + { + "description": "A pull request's scopes changed after it was embarked, so its batch was recomposed. It stays in the queue." + } + ], "x-modifiers": [ { "type": "negate" diff --git a/src/components/Tables/BatchStatusCodes.tsx b/src/components/Tables/BatchStatusCodes.tsx new file mode 100644 index 0000000000..72b4b861d8 --- /dev/null +++ b/src/components/Tables/BatchStatusCodes.tsx @@ -0,0 +1,47 @@ +import apiSchema from '../../../public/api-schemas.json'; +import { readEnumChoices } from '../../util/enumChoices'; + +import { renderMarkdown } from './utils'; + +// A batch's `status.code` in the merge queue API. The engine is the single +// source of truth: the codes and their one-line descriptions are published +// alongside the schema, so this table can't drift from what the API returns. +// Sourced from the OpenAPI spec rather than the configuration schema because a +// batch status is something the API reports, never something you write in +// `.mergify.yml`. +// +// `readEnumChoices` resolves a `$ref` and understands every documentation +// shape the engine currently publishes, so this keeps rendering whether the +// property is inline or hoisted into a shared component. +const codeProp: unknown = ( + apiSchema as { + components?: { schemas?: Record }> }; + } +).components?.schemas?.BatchStatus?.properties?.code; + +export default function BatchStatusCodes() { + const choices = readEnumChoices(apiSchema, codeProp); + + return ( +
+ + + + + + + + + {choices.map((choice) => ( + + + + ))} + +
StatusDescription
+ {choice.value} + +
+
+ ); +} diff --git a/src/content/docs/configuration/data-types.mdx b/src/content/docs/configuration/data-types.mdx index 27fc2d1564..267a28bc1c 100644 --- a/src/content/docs/configuration/data-types.mdx +++ b/src/content/docs/configuration/data-types.mdx @@ -3,6 +3,7 @@ title: Configuration Data Types description: The different data types you can find in Mergify configuration file --- +import BatchStatusCodes from '../../../components/Tables/BatchStatusCodes'; import OptionsTable from '../../../components/Tables/OptionsTable'; import QueueDequeueReasons from '../../../components/Tables/QueueDequeueReasons'; import TemplateVariablesTable from '../../../components/Tables/TemplateVariablesTable'; @@ -494,6 +495,20 @@ The following reasons can be reported: +## Batch Status + +This describes what a [batch](/merge-queue/batches) is currently doing in the +merge queue. It is reported as the `status.code` field of each batch returned by +the [merge queue status API](/api/merge-queue), and the dashboard shows it on +each batch. + +A status describes the batch as a whole, not an individual pull request: a batch +that is running its checks reports `running` for every pull request it carries. + +The following statuses can be reported: + + + ## Report Mode Report modes allow you to choose the type of report you want for your actions. diff --git a/src/content/docs/merge-queue/batches.mdx b/src/content/docs/merge-queue/batches.mdx index 75df5e21cf..cfa087d6bb 100644 --- a/src/content/docs/merge-queue/batches.mdx +++ b/src/content/docs/merge-queue/batches.mdx @@ -379,6 +379,11 @@ Note that this system is completely automatic and there is no need to intervene. The number of maximum splits can be controlled by [`batch_max_failure_resolution_attempts`](/configuration/file-format#queue-rules). +While this runs, the batches involved report a +[batch status](/configuration/data-types#batch-status) such as `bisecting`, +`waiting_for_previous_batches`, or `waiting_for_requeue`, so you can follow the +resolution from [the dashboard or the CLI](/merge-queue/monitoring). + :::tip Each split carries metadata about the batches it came from. You can use it to [re-run only the tests that failed in the parent diff --git a/src/content/docs/merge-queue/monitoring.mdx b/src/content/docs/merge-queue/monitoring.mdx index 67e3600084..05a3f02312 100644 --- a/src/content/docs/merge-queue/monitoring.mdx +++ b/src/content/docs/merge-queue/monitoring.mdx @@ -126,6 +126,10 @@ This displays: - **Waiting PRs**: queued pull requests with priority, queue time, and estimated merge time +Each batch reports a [batch status](/configuration/data-types#batch-status) +describing what it is doing, such as running its checks or waiting for a +schedule. + To filter results to a specific branch: ```bash diff --git a/src/util/dataType.test.ts b/src/util/dataType.test.ts index 9f7bef6358..068650da88 100644 --- a/src/util/dataType.test.ts +++ b/src/util/dataType.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; +import apiSchema from '../../public/api-schemas.json'; import configSchema from '../../public/mergify-configuration-schema.json'; import { collectDataTypeTitles, getDataTypeHref, isDataType } from './dataType'; import { dataTypesHeadingAnchors, missingDataTypeAnchors } from './dataTypeAnchors'; @@ -68,6 +69,8 @@ describe('data-types page anchors', () => { 'priority', 'report-mode', 'schedule', + // marked in the OpenAPI spec rather than the configuration schema + 'batch-status', // anchors hardcoded in ConfigOptions.tsx link maps 'commit', 'commit-author', @@ -94,4 +97,12 @@ describe('data-types page anchors', () => { it('covers every documented data type flagged in the config schema', () => { expect(missingDataTypeAnchors(configSchema)).toEqual([]); }); + + // The same convention, for types the engine marks in the OpenAPI spec + // instead — a data type the API reports but you never write in + // `.mergify.yml`. Both schemas arrive by the same bot sync, so both are + // gated in integrations/validate-data-type-anchors.ts. + it('covers every documented data type flagged in the API schema', () => { + expect(missingDataTypeAnchors(apiSchema)).toEqual([]); + }); }); diff --git a/src/util/enumChoices.test.ts b/src/util/enumChoices.test.ts index 97c04f1774..6df7862747 100644 --- a/src/util/enumChoices.test.ts +++ b/src/util/enumChoices.test.ts @@ -1,4 +1,6 @@ import { describe, expect, it } from 'vitest'; +import apiSchema from '../../public/api-schemas.json'; +import configSchema from '../../public/mergify-configuration-schema.json'; import { readEnumChoices, resolveRef } from './enumChoices'; // This reader spans an engine-side migration, so each shape it has to survive @@ -162,3 +164,36 @@ describe('resolveRef', () => { expect(resolveRef({}, { enum: ['x'] })).toEqual({ enum: ['x'] }); }); }); + +// The cases above are synthetic. These bind the reader to the two schemas the +// site actually renders, so a sync that changes shape — or reverts one — fails +// here instead of silently publishing a table of blank cells. +function at(root: unknown, ...path: string[]): unknown { + let current = root; + for (const key of path) { + current = (current as Record | undefined)?.[key]; + } + return current; +} + +describe('the real synced schemas', () => { + it('documents every batch status code in the API schema', () => { + const code = at(apiSchema, 'components', 'schemas', 'BatchStatus', 'properties', 'code'); + const choices = readEnumChoices(apiSchema, code); + expect(choices.length).toBeGreaterThan(0); + expect(choices.filter((c) => c.description.trim() === '')).toEqual([]); + }); + + it('documents every dequeue reason in the configuration schema', () => { + const reason = at( + configSchema, + '$defs', + 'PullRequestAttributes', + 'properties', + 'queue-dequeue-reason' + ); + const choices = readEnumChoices(configSchema, reason); + expect(choices.length).toBeGreaterThan(0); + expect(choices.filter((c) => c.description.trim() === '')).toEqual([]); + }); +});