Files
wp-prometheus/CLAUDE.md
magdev f1748727ce docs: Update CLAUDE.md with v0.0.2 session learnings
- Document composer path repository configuration with version aliases
- Document Gitea CI/CD fix for handling re-releases
- Add key learnings about submodule handling in CI environments

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 15:52:40 +01:00

12 KiB

WP Prometheus

Author: Marco Graetsch Author URL: https://src.bundespruefstelle.ch/magdev Author Email: magdev3.0@gmail.com Repository URL: https://src.bundespruefstelle.ch/magdev/wp-prometheus Issues URL: https://src.bundespruefstelle.ch/magdev/wp-prometheus/issues

Project Overview

This plugin provides a Prometheus /metrics endpoint and an extensible way to add your own metrics in third-party plugins using hooks. It adds some default metrics like number of active accounts, number of articles, comments, and plugin status. The default metrics can be activated/deactivated in the plugin settings.

Features

  • Prometheus compatible authenticated /metrics endpoint
  • Optional default metrics (users, posts, comments, plugins)
  • Dedicated plugin settings under 'Settings/Metrics' menu
  • Extensible by other plugins using wp_prometheus_collect_metrics action hook
  • License management integration

Key Fact: 100% AI-Generated

This project is proudly "vibe-coded" using Claude.AI - the entire codebase was created through AI assistance.

Temporary Roadmap

Note for AI Assistants: Clean this section after the specific features are done or new releases are made. Effective changes are tracked in CHANGELOG.md. Do not add completed versions here - document them in the Session History section at the end of this file.

Version 0.1.0 (Planned)

  • Add request/response timing metrics
  • Add HTTP status code counters
  • Add database query metrics

Technical Stack

Security Best Practices

  • All user inputs are sanitized (integers for quantities/prices)
  • Nonce verification on form submissions
  • Output escaping in templates (esc_attr, esc_html, esc_js)
  • Direct file access prevention via ABSPATH check
  • XSS-safe DOM construction in JavaScript (no innerHTML with user data)
  • SQL injection prevention using $wpdb->prepare() throughout

Translation Ready

All user-facing strings use:

__('Text to translate', 'wp-prometheus')
_e('Text to translate', 'wp-prometheus')

Text domain: wp-prometheus

Translation Template

  • Base .pot file created: languages/wp-prometheus.pot
  • Ready for translation to any locale
  • All translatable strings properly marked with text domain

Available Translations

  • en_US - English (United States) [base language - .pot template]
  • de_CH - German (Switzerland, formal)

To compile translations to .mo files for production:

for po in languages/*.po; do msgfmt -o "${po%.po}.mo" "$po"; done

Create releases

  • The vendor/ directory MUST be included in releases (Dependencies required for runtime)
  • CRITICAL: Build vendor/ for the MINIMUM supported PHP version, not the development version
    • Use composer config platform.php 8.3.0 before building release packages
    • Run composer update --no-dev --optimize-autoloader to rebuild dependencies
  • CRITICAL: WordPress requires plugins in a subdirectory structure
    • Run zip from the plugins/ parent directory, NOT from within the plugin directory
    • Package must extract to wp-prometheus/ subdirectory with main file at wp-prometheus/wp-prometheus.php
    • Correct command: cd /wp-content/plugins/ && zip -r wp-prometheus/releases/wp-prometheus-x.x.x.zip wp-prometheus ...
    • Wrong: Running zip from inside the plugin directory creates files at root level
  • CRITICAL: Exclude symlinks explicitly - zip follows symlinks by default
    • Always use -x "wp-prometheus/wp-core" -x "wp-prometheus/wp-core/*" -x "wp-prometheus/wp-plugins" -x "wp-prometheus/wp-plugins/*" to exclude development symlinks
    • Otherwise the entire linked directory contents will be included in the package
  • Exclusion patterns must match the relative path structure used in zip command
  • Always verify the package structure with unzip -l before distribution
    • Check all files are prefixed with wp-prometheus/
    • Verify main file is at wp-prometheus/wp-prometheus.php
    • Check for duplicate entries (indicates multiple builds in same archive)
  • Test installation on the minimum supported PHP version before final deployment
  • Releases are stored in releases/ including checksums
  • Track release changes in a single CHANGELOG.md file
  • Bump the version number to either bugfix release versions or on new features minor release versions
  • CRITICAL: WordPress reads version from TWO places - BOTH must be updated:
    1. Plugin header comment Version: x.x.x - WordPress uses THIS for admin display
    2. PHP constant WP_PROMETHEUS_VERSION (line ~28) - Used internally by the plugin
    • If only the constant is updated, WordPress will show the old version in Plugins list

Important Git Notes:

  • Default branch while development is dev
  • Create releases from branch main after merging branch dev
  • Tags should use format vX.X.X (e.g., v1.1.22), start with v0.0.1
  • Use annotated tags (-a) not lightweight tags
  • Commit messages should follow the established format with Claude Code attribution
  • .claude/settings.local.json changes are typically local-only (stash before rebasing)

CRITICAL - Release Workflow:

On every new version, ALWAYS execute this complete workflow:

# 1. Commit changes to dev branch
git add <files>
git commit -m "Description of changes (vX.X.X)"

# 2. Merge dev to main
git checkout main
git merge dev --no-edit

# 3. Create annotated tag
git tag -a vX.X.X -m "Version X.X.X - Brief description"

# 4. Push everything to origin
git push origin dev main vX.X.X

# 5. Switch back to dev for continued development
git checkout dev

Never skip any of these steps. The release is not complete until all branches and the tag are pushed to origin.

What Gets Released

  • All plugin source files
  • Compiled vendor dependencies
  • Translation files (.mo compiled from .po)
  • Assets (CSS, JS)
  • Documentation (README, CHANGELOG, etc.)

What's Excluded

  • Git metadata (.git/)
  • Development files (.vscode/, .claude/, CLAUDE.md, wp-core, wp-plugins)
  • Logs and cache files
  • Previous releases
  • composer.lock (but vendor/ is included)

For AI Assistants:

When starting a new session on this project:

  1. Read this CLAUDE.md file first
  2. Semantic versioning follows the MAJOR.MINOR.BUGFIX pattern
  3. Check git log for recent changes
  4. Verify you're on the dev branch before making changes
  5. Run composer install if vendor/ is missing
  6. Test changes before committing
  7. Follow commit message format with Claude Code attribution
  8. Update this session history section with learnings
  9. Never commit backup files (*.po~, *.bak, etc.) - check git status before committing
  10. Follow markdown linting rules (see below)

Always refer to this document when starting work on this project.

Markdown Linting Rules

When editing CLAUDE.md or other markdown files, follow these rules to avoid linting errors:

  1. MD031 - Blank lines around fenced code blocks: Always add a blank line before and after fenced code blocks, even when they follow list items. Example of correct format:

    • Item label:

      (blank line here) ```php code example ``` (blank line here)

  2. MD056 - Table column count: Table separators must have matching column counts and proper spacing. Use consistent dash lengths that match column header widths.

  3. MD009 - No trailing spaces: Remove trailing whitespace from lines

  4. MD012 - No multiple consecutive blank lines: Use only single blank lines between sections

  5. MD040 - Fenced code blocks should have a language specified: Always add a language identifier to code blocks (e.g., txt, bash, php). For shortcode examples, use txt.

  6. MD032 - Lists should be surrounded by blank lines: Add a blank line before AND after list blocks, including after bold labels like **Attributes:**.

  7. MD034 - Bare URLs: Wrap URLs in angle brackets (e.g., <https://example.com>) or use markdown link syntax [text](url).

  8. Author section formatting: Use a heading (### Name) instead of bold (**Name**) for the author name to maintain consistent document structure.

Project Architecture

Directory Structure

wp-prometheus/
├── .gitea/workflows/
│   └── release.yml          # CI/CD pipeline
├── assets/
│   ├── css/                  # Admin/Frontend styles
│   └── js/
│       └── admin.js          # Admin JavaScript
├── languages/                # Translation files
├── lib/
│   └── wc-licensed-product-client/  # Git submodule
├── releases/                 # Release packages
├── src/
│   ├── Admin/
│   │   └── Settings.php      # Settings page
│   ├── Endpoint/
│   │   └── MetricsEndpoint.php  # /metrics endpoint
│   ├── License/
│   │   └── Manager.php       # License management
│   ├── Metrics/
│   │   └── Collector.php     # Prometheus metrics collector
│   ├── Installer.php         # Activation/Deactivation
│   ├── Plugin.php            # Main plugin class
│   └── index.php
├── CHANGELOG.md
├── CLAUDE.md
├── composer.json
├── index.php
├── PLAN.md
├── README.md
├── uninstall.php
└── wp-prometheus.php         # Plugin bootstrap

Implementation Details

License Manager (src/License/Manager.php)

  • Integration with SecureLicenseClient or LicenseClient
  • Option storage for license key, server URL, server secret
  • License validation with domain binding
  • License activation with domain
  • Status caching (24-hour transient)
  • AJAX handlers for admin operations
  • Exception handling for all license states

Metrics Endpoint Restriction Logic

// In Plugin::init_components()
if ( LicenseManager::is_license_valid() ) {
    $this->collector = new Collector();
    new MetricsEndpoint( $this->collector );
}

Admin settings always work; metrics endpoint requires valid license.

Custom Metrics Extension

// Third-party plugins can add custom metrics
add_action( 'wp_prometheus_collect_metrics', function( $collector ) {
    $gauge = $collector->register_gauge(
        'my_custom_metric',
        'Description of my metric',
        array( 'label1', 'label2' )
    );
    $gauge->set( 42, array( 'value1', 'value2' ) );
} );

Session History

2026-02-01 - CI/CD Fixes (v0.0.2)

  • Fixed composer.json dependency configuration for CI compatibility

  • Key Learning: Git submodules with path repositories need explicit version aliases for CI:

    "repositories": [
        {
            "type": "path",
            "url": "lib/wc-licensed-product-client",
            "options": {
                "symlink": false,
                "versions": {
                    "magdev/wc-licensed-product-client": "0.2.2"
                }
            }
        }
    ]
    
  • Using dev-main constraints with minimum-stability: dev causes issues in CI

  • Path repository with symlink: false and explicit versions mapping works reliably

  • Key Learning: Gitea API returns "Release has no Tag" error when re-releasing existing tags

  • Fixed release.yml to check for and delete existing releases before creating new ones

  • Changed minimum-stability back to stable

2026-02-01 - Initial Setup (v0.0.1)

  • Created initial plugin structure based on wp-fedistream blueprint
  • Set up composer.json with promphp/prometheus_client_php and wc-licensed-product-client
  • Implemented core classes: Plugin, Installer, License/Manager, Metrics/Collector, Endpoint/MetricsEndpoint, Admin/Settings
  • Created authenticated /metrics endpoint with Bearer token support
  • Added default metrics: wordpress_info, users_total, posts_total, comments_total, plugins_total
  • Created extensibility via wp_prometheus_collect_metrics action hook
  • Set up Gitea CI/CD pipeline for automated releases
  • Created documentation: README.md, PLAN.md, CHANGELOG.md