PHPackages                             thetwopct/wporg-plugin-readme-linter - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [CLI &amp; Console](/categories/cli)
4. /
5. thetwopct/wporg-plugin-readme-linter

ActiveLibrary[CLI &amp; Console](/categories/cli)

thetwopct/wporg-plugin-readme-linter
====================================

WordPress.org readme.txt validator

v1.0.2(6mo ago)06[2 PRs](https://github.com/thetwopct/wporg-plugin-readme-linter/pulls)MITPHPPHP ^7.4|^8.0CI passing

Since Oct 18Pushed 5mo agoCompare

[ Source](https://github.com/thetwopct/wporg-plugin-readme-linter)[ Packagist](https://packagist.org/packages/thetwopct/wporg-plugin-readme-linter)[ RSS](/packages/thetwopct-wporg-plugin-readme-linter/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (3)Dependencies (6)Versions (6)Used By (0)

WordPress.org Plugin Readme Linter
==================================

[](#wordpressorg-plugin-readme-linter)

[![Tests](https://github.com/thetwopct/wporg-plugin-readme-linter/actions/workflows/tests.yml/badge.svg)](https://github.com/thetwopct/wporg-plugin-readme-linter/actions/workflows/tests.yml)[![License: MIT](https://camo.githubusercontent.com/08cef40a9105b6526ca22088bc514fbfdbc9aac1ddbf8d4e6c750e3a88a44dca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d626c75652e737667)](LICENSE)

A comprehensive linter for WordPress.org plugin `readme.txt` files. Helps ensure your plugin readme meets WordPress.org Plugin Directory standards before submission. Lint from your development environment via composer, or include in your GitHub Actions.

Features
--------

[](#features)

- ✅ **Comprehensive validation** - Checks headers, metadata, sections, and content quality
- 📝 **GitHub Annotations** - Inline PR comments on readme issues (when using GitHub Actions)
- 🎯 **Configurable rules** - Customize severity levels and required sections
- ⚡ **Fast** - Typically runs in under 2 seconds
- 🐳 **Zero setup** - Container action includes all dependencies
- 📊 **Multiple output formats** - Annotations, JSON, or SARIF

Quick Start
-----------

[](#quick-start)

### Use as a GitHub Action

[](#use-as-a-github-action)

Add this workflow to your repository at `.github/workflows/readme-lint.yml`:

```
name: Lint Readme

on:
  pull_request:
    paths:
      - 'readme.txt'
  push:
    branches:
      - main

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Lint readme.txt
        uses: thetwopct/wporg-plugin-readme-linter@v1
        with:
          path: readme.txt
          fail-on: error
```

or you could add something like the below to your existing workflow action:

```
    - name: Lint readme.txt
      uses: thetwopct/wporg-plugin-readme-linter@v1
      with:
        path: readme.txt
        fail-on: error
```

### Use as a Composer Package (CLI)

[](#use-as-a-composer-package-cli)

Install via Composer:

```
composer require --dev thetwopct/wporg-plugin-readme-linter
```

Run the linter:

```
vendor/bin/wporg-plugin-readme-linter readme.txt
```

By default, the linter uses a human-friendly text format for local development and automatically switches to GitHub Actions annotation format when running in CI.

With options:

```
# Use GitHub Actions annotation format (useful for CI)
vendor/bin/wporg-plugin-readme-linter readme.txt --format=annotations

# Output as JSON
vendor/bin/wporg-plugin-readme-linter readme.txt --format=json

# Generate SARIF file
vendor/bin/wporg-plugin-readme-linter readme.txt --format=sarif --output=report.sarif

# Fail on warnings (not just errors)
vendor/bin/wporg-plugin-readme-linter readme.txt --fail-on=warning

# Use custom config
vendor/bin/wporg-plugin-readme-linter readme.txt --config=.wporg-readme-lint.json
```

Configuration
-------------

[](#configuration)

Create a `.wporg-readme-lint.json` file in your repository root to customize the linter:

```
{
  "readmePath": "readme.txt",
  "failOn": "error",
  "ignoreRules": ["donate-link"],
  "requiredSections": ["description", "installation", "changelog", "faq"],
  "allowTrunk": false
}
```

### Configuration Options

[](#configuration-options)

- **`readmePath`** (string): Path to readme file. Default: `readme.txt`
- **`failOn`** (string): Minimum severity to fail on. Options: `error`, `warning`, `info`. Default: `error`
- **`ignoreRules`** (array): Rule IDs to ignore. Default: `[]`
- **`requiredSections`** (array): Sections that must be present. Default: `["description", "installation", "changelog"]`
- **`allowTrunk`** (boolean): Whether to allow "trunk" as stable tag. Default: `false`

Rules
-----

[](#rules)

### Headers &amp; Metadata

[](#headers--metadata)

Rule IDDescriptionLevel`plugin-name`Plugin name header must be present, properly formatted, and not use trademarks improperlyError/Warning`required-fields`Required metadata fields (Contributors, Tags, Requires at least, Tested up to, Stable tag)Error`short-description`Short description must be ≤150 charactersError/Warning`stable-tag`Stable tag must be valid semantic version (not "trunk" unless configured)Error/Warning`license`License field must be present, valid, and match plugin file headerError`contributors`Contributors must use valid usernames and not be restricted/reservedError/Warning`tested-up-to`WordPress version must be current and validError### Sections &amp; Structure

[](#sections--structure)

Rule IDDescriptionLevel`required-sections`Required sections must be presentError`heading-levels`Proper heading levels (=== for title, == for sections)Error`empty-sections`Sections should have meaningful contentWarning### Content Quality

[](#content-quality)

Rule IDDescriptionLevel`file-size`Readme file size should be reasonable (≤20KB)Warning/Info`default-text`Detects unmodified readme template textError`trademark`Detects improper use of trademarked names (WordPress, etc.)Warning/Info### WordPress.org Compliance

[](#wordpressorg-compliance)

Rule IDDescriptionLevel`donate-link`Donate link should use appropriate domains and be properly formattedError/Warning/Info`upgrade-notice`Upgrade notices should be limited in number and lengthWarningAction Inputs
-------------

[](#action-inputs)

InputDescriptionRequiredDefault`path`Path to readme.txt fileNo`readme.txt``format`Output format: `text`, `annotations`, `sarif`, or `json`NoAuto-detected (`text` for CLI, `annotations` for CI)`fail-on`Fail on level: `error`, `warning`, or `info`No`error``config`Path to configuration fileNo`.wporg-readme-lint.json``output`Output file path for SARIF/JSONNo`''` (stdout)Action Outputs
--------------

[](#action-outputs)

OutputDescription`sarif-file`Path to generated SARIF file (if format=sarif)`violations-count`Total number of violations found`failed`Whether the linter failed based on fail-on levelCLI Exit Codes
--------------

[](#cli-exit-codes)

- 0 - No issues or issues below fail-on threshold
- **1** - Issues found at or above fail-on threshold
- **2** - Usage error (e.g., file not found, invalid arguments)

Output Formats
--------------

[](#output-formats)

The linter automatically selects the appropriate output format:

- **Text format** (default for local CLI usage) - Human-friendly, colored output grouped by severity
- **Annotations format** (default for CI/GitHub Actions) - GitHub Actions workflow commands for inline PR comments

You can explicitly set the format using the `--format` option.

### Text Format (default for CLI)

[](#text-format-default-for-cli)

Human-friendly output with colors and grouping by severity level:

```
Warnings:
  ⚠ Line 10: [short-description] Short description is approaching maximum length (150 characters, recommended maximum 140)

Info:
  ℹ [donate-link] Consider adding a donate link to support the plugin
  ℹ [file-size] Readme file is getting large (8.5KB)

```

### GitHub Annotations (default for CI)

[](#github-annotations-default-for-ci)

Produces inline PR comments in GitHub Actions:

```
::error file=readme.txt,line=5::[short-description] Short description is too long (160 characters, maximum 150)

```

### SARIF

[](#sarif)

Produces SARIF 2.1.0 output for GitHub Code Scanning integration.

### JSON

[](#json)

Produces structured JSON output:

```
{
  "issues": [
    {
      "ruleId": "short-description",
      "level": "error",
      "message": "Short description is too long",
      "file": "readme.txt",
      "line": 5,
      "column": null
    }
  ],
  "summary": {
    "total": 1,
    "errors": 1,
    "warnings": 0,
    "info": 0
  }
}
```

Advanced Usage
--------------

[](#advanced-usage)

### GitHub Code Scanning Integration (SARIF)

[](#github-code-scanning-integration-sarif)

For teams using GitHub's Code Scanning feature, the linter can output results in SARIF format:

```
name: Lint Readme with Code Scanning

on:
  pull_request:
  push:
    branches:
      - main

jobs:
  lint:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    steps:
      - uses: actions/checkout@v4

      - name: Lint readme.txt
        uses: thetwopct/wporg-plugin-readme-linter@v1
        with:
          path: readme.txt
          format: sarif
          output: readme-lint.sarif
        continue-on-error: true

      - name: Upload SARIF
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: readme-lint.sarif
```

This displays linting results in the Security tab alongside other code scanning alerts.

Comparison with Other Tools
---------------------------

[](#comparison-with-other-tools)

### vs. Plugin Check

[](#vs-plugin-check)

[Plugin Check](https://wordpress.org/plugins/plugin-check/) is the official WordPress.org tool and checks the entire plugin including PHP code. This linter focuses exclusively on `readme.txt` validation and is designed for CI integration.

### vs. PHPCS

[](#vs-phpcs)

While PHPCS can check code style, this tool specializes in readme validation with WordPress.org-specific rules and better CI integration.

Development
-----------

[](#development)

### Requirements

[](#requirements)

- PHP 7.4 or higher
- Composer

### Setup

[](#setup)

```
git clone https://github.com/thetwopct/wporg-plugin-readme-linter.git
cd wporg-plugin-readme-linter
composer install
```

### Running Tests

[](#running-tests)

```
# All tests
composer test

# Unit tests only
vendor/bin/phpunit --testsuite=Unit

# Integration tests only
vendor/bin/phpunit --testsuite=Integration
```

### Code Quality

[](#code-quality)

```
# Static analysis
vendor/bin/phpstan analyse

# Code style check
vendor/bin/phpcs

# Fix code style
vendor/bin/phpcbf
```

Contributing
------------

[](#contributing)

Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.

License
-------

[](#license)

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

Credits
-------

[](#credits)

- Built on top of [afragen/wordpress-plugin-readme-parser](https://github.com/afragen/wordpress-plugin-readme-parser)
- Inspired by WordPress.org Plugin Directory requirements

Support &amp; Feedback
----------------------

[](#support--feedback)

- 🐛 [Issue Tracker](https://github.com/thetwopct/wporg-plugin-readme-linter/issues)

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance69

Regular maintenance activity

Popularity5

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 73.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~5 days

Total

3

Last Release

196d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/9106eb37f5fcf352f006287833966f01d3d3926d0da0f43abfa30d340cab75f6?d=identicon)[thetwopct](/maintainers/thetwopct)

---

Top Contributors

[![thetwopct](https://avatars.githubusercontent.com/u/10615884?v=4)](https://github.com/thetwopct "thetwopct (11 commits)")[![Copilot](https://avatars.githubusercontent.com/in/1143301?v=4)](https://github.com/Copilot "Copilot (4 commits)")

---

Tags

cliwordpress-developmentwordpress-pluginwordpress-plugin-developmentwordpress-readmewordpress-standardspluginvalidationwordpresslinterreadmewporg

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/thetwopct-wporg-plugin-readme-linter/health.svg)

```
[![Health](https://phpackages.com/badges/thetwopct-wporg-plugin-readme-linter/health.svg)](https://phpackages.com/packages/thetwopct-wporg-plugin-readme-linter)
```

###  Alternatives

[crunzphp/crunz

Schedule your tasks right from the code.

2292.0M6](/packages/crunzphp-crunz)[crazywhalecc/static-php-cli

Build single static PHP binary, with PHP project together, with popular extensions included.

1.8k13.9k](/packages/crazywhalecc-static-php-cli)[phpcr/phpcr-shell

Shell for PHPCR

721.3M8](/packages/phpcr-phpcr-shell)[afragen/git-updater

A plugin to automatically update GitHub, Bitbucket, GitLab, or Gitea hosted plugins, themes, and language packs.

3.3k1.6k](/packages/afragen-git-updater)[madewithlove/license-checker

CLI tool to verify allowed licenses for composer dependencies

54449.8k21](/packages/madewithlove-license-checker)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
