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/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 8120b11e67..4eb7cc7e1d 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'; @@ -69,6 +70,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', @@ -95,4 +98,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 e3a6f57973..28071ffc75 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 @@ -171,3 +173,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([]); + }); +});