Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Derived from the existing prometheus-cpp source style.
# Base: Google, with 4-space indent / 100-col / aligned-assignment overrides.
---
Language: Cpp
BasedOnStyle: Google

# Indentation
IndentWidth: 4
ContinuationIndentWidth: 4
ConstructorInitializerIndentWidth: 4
AccessModifierOffset: -4
NamespaceIndentation: None
TabWidth: 4
UseTab: Never

# Line width
ColumnLimit: 100

# Pointers / references bind to the type (int64_t& x, std::ostream& out)
PointerAlignment: Left
DerivePointerAlignment: false

# Braces stay attached (K&R)
BreakBeforeBraces: Attach

# Constructor initializer lists: colon/comma lead the line
BreakConstructorInitializers: BeforeComma
PackConstructorInitializers: Never

# Functions are written out across multiple lines, even when short.
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: true
AllowShortLambdasOnASingleLine: All

# case X: return Y; rows are kept on one line and aligned.
AlignConsecutiveShortCaseStatements:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCaseColons: false

# Do not collapse hand-broken parameter/argument lists.
BinPackParameters: false
BinPackArguments: false

# Aligned consecutive assignments are used intentionally throughout.
AlignConsecutiveAssignments:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true

# Includes are grouped by hand and not alphabetised.
SortIncludes: Never
IncludeBlocks: Preserve

# Misc
Standard: c++20
SpacesBeforeTrailingComments: 1
AlignAfterOpenBracket: Align
AlwaysBreakTemplateDeclarations: Yes
EmptyLineBeforeAccessModifier: LogicalBlock
65 changes: 65 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# clang-tidy configuration for prometheus-cpp.
#
# Strategy: enable broad, high-value check families and disable the specific
# checks that the existing code does not currently satisfy, so the tree is
# GREEN today and any *new* violation of a still-enabled check fails the build
# (WarningsAsErrors below). The disabled-because-firing checks are grouped
# separately from the disabled-by-design ones; the former are candidates to
# fix-and-re-enable later, the latter conflict with deliberate design choices
# (preprocessor label macros, #pragma once, int64_t atomics, terse hot-path
# names) and should stay off.
#
# Run it:
# cmake -B build -DPROMETHEUS_BUILD_TESTS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
# clang-tidy -p build <file.cpp>
# On macOS, clang-tidy needs the SDK; the repo's lint wrappers add it for you
# (scripts/run-clang-tidy-precommit.sh and .claude/hooks/lint-check.sh).
---
Checks: >
bugprone-*,
performance-*,
modernize-*,
readability-*,
portability-*,
clang-analyzer-*,

-bugprone-easily-swappable-parameters,
-modernize-use-trailing-return-type,
-modernize-macro-to-enum,
-modernize-use-designated-initializers,
-readability-identifier-length,
-readability-magic-numbers,
-readability-braces-around-statements,
-readability-implicit-bool-conversion,
-readability-function-cognitive-complexity,
-readability-math-missing-parentheses,
-misc-include-cleaner,
-misc-non-private-member-variables-in-classes,
-misc-use-anonymous-namespace,
-portability-avoid-pragma-once,

-bugprone-exception-escape,
-bugprone-implicit-widening-of-multiplication-result,
-readability-uppercase-literal-suffix,
-clang-diagnostic-unused-lambda-capture,
-clang-diagnostic-comment,
-bugprone-suspicious-include,
-bugprone-unused-local-non-trivial-variable,
-clang-analyzer-optin.core.EnumCastOutOfRange,
-clang-analyzer-optin.performance.Padding,
-modernize-avoid-c-arrays,
-modernize-return-braced-init-list,
-modernize-use-default-member-init,
-modernize-use-nodiscard,
-modernize-use-ranges,
-modernize-use-std-print,
-performance-enum-size,
-performance-faster-string-find,
-readability-container-contains,
-readability-inconsistent-ifelse-braces,
-readability-redundant-member-init,
-readability-redundant-typename,

HeaderFilterRegex: 'prometheus/.*\.hpp$'
WarningsAsErrors: '*'
FormatStyle: file
141 changes: 141 additions & 0 deletions .claude/hooks/lint-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#!/usr/bin/env bash
#
# Stop hook: ensure the C/C++ code Claude touched this turn is lint-free before
# the turn is allowed to finish. Runs clang-format (always) and clang-tidy (when
# a compile database is available, or directly on headers) over the files changed
# relative to HEAD. If anything is non-conformant it blocks the stop and feeds the
# diagnostics back to Claude so it fixes them and re-checks — by design this lives
# at "post-prompt" (Stop) rather than post-tool, so Claude can make several edits
# and only needs the tree clean once it is done.
#
# Output contract (Stop hook): print {"decision":"block","reason":...} on stdout
# to keep Claude working; print nothing and exit 0 to let it stop.

set -uo pipefail

repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
cd "$repo_root" || exit 0

# --- locate tools (PATH first, then a Homebrew LLVM install) -----------------
find_tool() {
local name="$1" p
if command -v "$name" >/dev/null 2>&1; then command -v "$name"; return 0; fi
for p in /opt/homebrew/opt/llvm/bin /usr/local/opt/llvm/bin; do
[ -x "$p/$name" ] && { echo "$p/$name"; return 0; }
done
return 1
}

CLANG_FORMAT="$(find_tool clang-format || true)"
CLANG_TIDY="$(find_tool clang-tidy || true)"

# On macOS, clang-tidy (Homebrew LLVM) must be pointed at the SDK or it cannot
# find the C++ standard library headers when reading an AppleClang compile DB.
TIDY_EXTRA=()
if [ "$(uname -s)" = "Darwin" ] && command -v xcrun >/dev/null 2>&1; then
_sdk="$(xcrun --show-sdk-path 2>/dev/null || true)"
[ -n "$_sdk" ] && TIDY_EXTRA=(--extra-arg=-isysroot --extra-arg="$_sdk")
fi

# Nothing to enforce with — don't block the turn over missing local tooling.
[ -z "$CLANG_FORMAT" ] && [ -z "$CLANG_TIDY" ] && exit 0

# --- collect changed C/C++ files (modified, staged, untracked) ---------------
mapfile -t changed < <(
{
git diff --name-only HEAD 2>/dev/null
git diff --name-only --cached 2>/dev/null
git ls-files --others --exclude-standard 2>/dev/null
} | sort -u
)

files=()
for f in "${changed[@]}"; do
[ -f "$f" ] || continue
case "$f" in
build/*|build-*/*|build_*/*) continue ;;
esac
case "$f" in
include/*|tests/*|examples/*|bench/*) ;;
*) continue ;;
esac
case "$f" in
*.hpp|*.cpp|*.h|*.hh|*.cc|*.cxx|*.hxx) files+=("$f") ;;
esac
done

[ "${#files[@]}" -eq 0 ] && exit 0

# --- find a compile database for clang-tidy ----------------------------------
compile_db_dir=""
for d in build-tidy build build-example build_bench; do
[ -f "$d/compile_commands.json" ] && { compile_db_dir="$d"; break; }
done

report=""

# --- clang-format: report any file that is not already formatted -------------
if [ -n "$CLANG_FORMAT" ]; then
unformatted=()
for f in "${files[@]}"; do
if ! "$CLANG_FORMAT" --dry-run --Werror "$f" >/dev/null 2>&1; then
unformatted+=("$f")
fi
done
if [ "${#unformatted[@]}" -gt 0 ]; then
report+="clang-format: the following files are not formatted. Run \`clang-format -i <file>\` (or fix manually):"$'\n'
for f in "${unformatted[@]}"; do report+=" - $f"$'\n'; done
report+=$'\n'
fi
fi

# --- clang-tidy: lint only translation units present in the compile DB --------
# Only enforce when the project ships a tuned .clang-tidy AND a compile database
# exists. Linting a file that is not in the DB makes clang-tidy fall back to a
# guessed command line; with missing external headers the parse breaks and the
# broken AST yields garbage findings. So we lint strictly the TUs the DB knows.
# Headers are covered transitively (HeaderFilterRegex): if a header changed we
# lint every DB TU, since we cannot tell which one exercises it.
if [ -n "$CLANG_TIDY" ] && [ -f "$repo_root/.clang-tidy" ] && [ -n "$compile_db_dir" ]; then
mapfile -t db_files < <(
python3 -c "import json,sys,os
for e in json.load(open(sys.argv[1])):
print(os.path.realpath(e['file']))" "$compile_db_dir/compile_commands.json" 2>/dev/null | sort -u
)

header_changed=0
declare -A want=()
for f in "${files[@]}"; do
case "$f" in include/*.hpp|include/*.h|include/*/*.hpp|include/*/*.h) header_changed=1 ;; esac
abs="$(cd "$repo_root" && python3 -c "import os,sys;print(os.path.realpath(sys.argv[1]))" "$f")"
for t in "${db_files[@]}"; do [ "$t" = "$abs" ] && want["$t"]=1; done
done
if [ "$header_changed" -eq 1 ]; then
for t in "${db_files[@]}"; do want["$t"]=1; done
fi

tidy_out=""
for f in "${!want[@]}"; do
out="$("$CLANG_TIDY" -p "$compile_db_dir" "${TIDY_EXTRA[@]}" "$f" 2>/dev/null)"
if printf '%s' "$out" | grep -Eq 'warning:|error:'; then
tidy_out+="### ${f#"$repo_root"/}"$'\n'"$out"$'\n\n'
fi
done
if [ -n "$tidy_out" ]; then
report+="clang-tidy reported issues:"$'\n'"$tidy_out"
fi
fi

[ -z "$report" ] && exit 0

# Keep the fed-back context bounded.
report="$(printf '%s' "$report" | head -c 6000)"

reason="Lint is not clean. Fix these before finishing (clang-format / clang-tidy, see .clang-format and .clang-tidy):"$'\n\n'"$report"

# Emit the Stop-hook block decision as JSON.
python3 - "$reason" <<'PY' 2>/dev/null || printf '{"decision":"block","reason":%s}\n' "\"lint not clean; run clang-format/clang-tidy on changed files\""
import json, sys
print(json.dumps({"decision": "block", "reason": sys.argv[1]}))
PY
exit 0
17 changes: 17 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/lint-check.sh\"",
"timeout": 120,
"statusMessage": "Checking clang-format / clang-tidy on changed files"
}
]
}
]
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
build/
build_bench/
build-*/
30 changes: 30 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Pre-commit hooks for prometheus-cpp.
#
# pip install pre-commit # or: brew install pre-commit
# pre-commit install # enable on every commit
# pre-commit run --all-files # run across the whole tree
#
# clang-format runs from a pinned, self-contained mirror (no system install
# needed). clang-tidy runs the system binary against the CMake compile
# database; it is skipped gracefully if the project hasn't been configured
# (see scripts/run-clang-tidy-precommit.sh).

minimum_pre_commit_version: "3.0.0"

repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v22.1.8
hooks:
- id: clang-format
files: ^(include|tests|examples|bench)/.*\.(c|cc|cpp|cxx|h|hh|hpp|hxx)$

- repo: local
hooks:
- id: clang-tidy
name: clang-tidy
language: system
entry: bash scripts/run-clang-tidy-precommit.sh
# Only translation units; headers are covered transitively via the
# .clang-tidy HeaderFilterRegex when their including TU is checked.
files: ^(tests|examples|bench)/.*\.(c|cc|cpp|cxx)$
require_serial: true
21 changes: 21 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# prometheus-cpp

High-performance header-only C++23 Prometheus client library (stigsb/prometheus-cpp).

- Values are `int64_t` atomics, not `double` — single `LOCK XADD` on x86, no CAS loops
- Labels are compile-time typed structs via `PROMETHEUS_DEFINE_LABELS` macro
- Zero allocation on the hot path; `get()` handles should be cached

## Building

```bash
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
# Tests:
cmake -B build -DCMAKE_BUILD_TYPE=Debug -DPROMETHEUS_BUILD_TESTS=ON && cmake --build build && ctest --test-dir build --output-on-failure
```

## Available skills

- `/prometheus-metrics` — Add, audit, and maintain Prometheus metrics in apps using this library.
Install in your project: `cp -r .claude/skills/prometheus-metrics/ your-project/.claude/skills/`
21 changes: 0 additions & 21 deletions CLAUDE.md

This file was deleted.

1 change: 1 addition & 0 deletions CLAUDE.md
Loading
Loading