How the GitHub Docs Content Linter Validates Documentation Quality
The GitHub Docs content linter is an opinionated wrapper around markdownlint that validates Markdown files in the content/ and data/ directories (plus markdown strings inside YAML) through a seven-stage pipeline involving file discovery, custom rule application, severity-based reporting, and optional auto-fixing.
The content linter ensures every page in the GitHub Docs repository meets strict editorial and technical standards before publication. Operating as a specialized validation layer atop markdownlint, it executes a curated blend of native rules and custom GitHub-specific checks against documentation source files, categorizing findings by severity and providing actionable fixes.
The Validation Pipeline Architecture
The linter orchestrates validation through a precise sequence of operations defined in src/content-linter/scripts/lint-content.ts. Each stage handles specific aspects of documentation quality assurance.
1. File Discovery and Categorization
The process begins in getFilesToLint (lines 80-108 of lint-content.ts), which traverses the repository and resolves caller-supplied paths (defaulting to changed files). The implementation separates targets into three distinct groups:
- content – Standard Markdown pages requiring full validation
- data – Partial templates stored under
data/with specialized rules - yml – Markdown strings extracted from lintable YAML files via
get-lintable-yml.ts
This categorization enables context-specific rule application, ensuring data partials and YAML front matter receive appropriate scrutiny without false positives.
2. Rule Configuration and Severity Levels
All rule metadata resides in src/content-linter/style/github-docs.ts. The configuration architecture uses three distinct objects:
githubDocsConfig– Primary custom rules for content validationgithubDocsFrontmatterConfig– Rules requiring only front-matter line numberssearchReplaceConfig– Simple string and regex pattern checks
Each rule specifies a default severity (error or warning) and an optional precommitSeverity that overrides the default during pre-commit hooks, allowing teams to block commits on critical violations while permitting warnings in CI.
3. Custom Rule Registration
Custom rules live as separate TypeScript modules in src/content-linter/lib/linting-rules/, such as liquid-data-tags.ts which validates Liquid template syntax. The index file at src/content-linter/lib/linting-rules/index.ts aggregates all rule modules into a rules array imported by the main configuration.
This modular architecture allows developers to implement domain-specific validation logic—such as checking for internal link consistency or validating reusable content tags—while maintaining clean separation of concerns.
4. Multi-Pass Linting Execution
lint-content.ts invokes markdownlint four times sequentially (lines 71-89), once per file group, with specialized configurations:
- Content rules – Full validation for standard documentation pages
- Data-partial rules – Checks specific to reusable content fragments
- Front-matter-only rules – Metadata validation using line number constraints
- YML-string rules – Markdown content extracted from YAML structures
The raw results from each pass merge into a single results object for unified processing, ensuring comprehensive coverage across all documentation sources.
5. Severity Handling and Result Formatting
After markdownlint execution, the pipeline enriches each violation with contextual metadata. The formatResult helper constructs a FormattedResult structure containing:
- The rule's configured severity level
- Contextual information about the violation
- A "fixable" flag indicating presence of
fixInfoobjects
This enrichment enables precise CLI output, GitHub Actions annotations via print-annotation-results.ts, and JSON report generation for downstream tooling.
6. Auto-Fix and Reporting
When invoked with the --fix flag, the linter calls applyFixes from markdownlint-rule-helpers (lines 31-44 of lint-content.ts) to rewrite files containing automatically correctable violations.
The reporting layer utilizes pretty-print-results.ts to generate colorized CLI summaries. During pre-commit execution, the process aborts if any error-level violations remain, while CI pipelines receive structured annotations for inline PR feedback.
Working with the Content Linter
Running the Linter via CLI
The command-line interface supports targeted validation and automatic correction:
# Lint all changed files (default behavior)
npm run lint-content
# Lint specific files with automatic fixing
npm run lint-content -- --paths content/github/using-actions.md --fix
CLI options are defined in the program setup within lint-content.ts (lines 95-125), supporting path specifications, fix modes, and output format selection.
Creating Custom Rules
New rules follow a standard interface exposing metadata and validation logic. Here is a template enforcing no trailing whitespace:
// src/content-linter/lib/linting-rules/no-trailing-whitespace.ts
import { addError } from 'markdownlint-rule-helpers'
export const noTrailingWhitespace = {
names: ['GHD999', 'no-trailing-whitespace'],
description: 'Lines must not end with trailing whitespace',
tags: ['format'],
parser: 'markdownit',
function: (params, onError) => {
params.lines.forEach((line, index) => {
if (/\s+$/.test(line)) {
addError(onError, index + 1, 'Trailing whitespace detected', line, [line.length - 1, 1])
}
})
},
}
After implementation, register the rule in src/content-linter/lib/linting-rules/index.ts and add a severity configuration entry in src/content-linter/style/github-docs.ts.
Programmatic Integration
The linter exposes its configuration and execution logic for integration with external tools:
import { markdownlint } from 'markdownlint'
import { getMarkdownLintConfig } from '@/content-linter/scripts/lint-content'
const files = ['content/github/markdown.md']
const { config, configuredRules } = getMarkdownLintConfig(false, undefined)
const results = await markdownlint.promises.markdownlint({
files,
config: config.content,
customRules: configuredRules.content,
})
console.log(results)
The getMarkdownLintConfig function (lines 135-147 of lint-content.ts) assembles merged configurations appropriate for each file category, enabling consistent validation outside the standard CLI workflow.
Key Source Files
Understanding the codebase requires familiarity with these specific modules:
| Purpose | File Path |
|---|---|
| High-level overview and contribution guide | src/content-linter/README.md |
| CLI entry point and orchestration logic | src/content-linter/scripts/lint-content.ts |
| Rule registration and severity configuration | src/content-linter/style/github-docs.ts |
| Custom rule implementation example (Liquid validation) | src/content-linter/lib/linting-rules/liquid-data-tags.ts |
| YAML markdown extraction utilities | src/content-linter/lib/helpers/get-lintable-yml.ts |
| Results formatting and display logic | src/content-linter/scripts/pretty-print-results.ts |
| Default markdownlint engine options | src/content-linter/lib/default-markdownlint-options.ts |
| Unit test template for custom rules | src/content-linter/tests/unit/liquid-data-tags.ts |
Summary
- The content linter wraps markdownlint to enforce GitHub-specific documentation standards across the
content/anddata/directories plus YAML files. - Validation occurs through four specialized passes: content rules, data-partial rules, front-matter-only rules, and YML-string rules.
- Severity configuration in
github-docs.tssupports distinct levels for CI and pre-commit contexts, enablingerrorblocking andwarningtolerance strategies. - Custom rules reside in
src/content-linter/lib/linting-rules/and integrate via a centralized index, supporting domain-specific validation like Liquid tag verification. - The auto-fix capability leverages
markdownlint-rule-helpersto correct violations automatically when invoked with the--fixflag.
Frequently Asked Questions
What is the difference between error and warning severity in the content linter?
Errors block commits during pre-commit hooks and fail CI builds, while warnings alert contributors without stopping the workflow. Each rule in src/content-linter/style/github-docs.ts defines both a default severity and an optional precommitSeverity, allowing teams to enforce stricter standards at commit time than in continuous integration.
How does the linter handle markdown content inside YAML files?
The YML file group uses get-lintable-yml.ts to extract markdown strings from YAML structures before running them through markdownlint. This occurs as a separate execution pass with distinct rules from standard content files, ensuring front matter and data files receive appropriate validation without syntax conflicts.
Can I run the content linter on only specific files during development?
Yes. The CLI accepts a --paths argument to target specific files or directories rather than scanning all changed files. For example: npm run lint-content -- --paths content/actions/index.md data/reusables/actions/*.md. This targeting accelerates local validation cycles when working on isolated documentation sections.
Where should I add a new validation rule for GitHub Docs content?
Create the rule implementation in src/content-linter/lib/linting-rules/your-rule-name.ts, following the template established by existing rules like liquid-data-tags.ts. Export the rule from src/content-linter/lib/linting-rules/index.ts, then register its severity and metadata in src/content-linter/style/github-docs.ts. Include unit tests in src/content-linter/tests/unit/ to verify behavior against edge cases.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →