Skip to content

OCPEDGE-2577: Add code-quality plugin with commit convention hooks#75

Closed
jeff-roche wants to merge 4 commits intoopenshift-eng:mainfrom
jeff-roche:feat/code-quality-plugin
Closed

OCPEDGE-2577: Add code-quality plugin with commit convention hooks#75
jeff-roche wants to merge 4 commits intoopenshift-eng:mainfrom
jeff-roche:feat/code-quality-plugin

Conversation

@jeff-roche
Copy link
Copy Markdown
Contributor

@jeff-roche jeff-roche commented Apr 22, 2026

Summary

  • Adds code-quality Claude Code plugin with automatic hooks for code conventions
  • PreToolUse hook validates commit messages against conventional commits format (type(scope): description)
  • PostToolUse hook warns (non-blocking) when AI attribution trailers are missing after commits
  • SessionStart hook checks for .coderabbit.yaml presence and validates its configuration
  • All hooks use fast-path exit for non-matching commands

Test plan

  • Install plugin via marketplace
  • Verify echo '{"toolInput":{"command":"git commit -m \"bad\""}}' | bash plugins/code-quality/scripts/check-commit-message.sh exits 1 with guidance
  • Verify echo '{"toolInput":{"command":"git commit -m \"feat(api): add endpoint\""}}' | bash plugins/code-quality/scripts/check-commit-message.sh exits 0
  • Verify echo '{"cwd":"/tmp"}' | bash plugins/code-quality/scripts/check-coderabbit-config.sh outputs missing config warning
  • Verify attribution hook exits 0 for non-commit commands

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a code-quality plugin that enforces conventional commit messages, warns when AI-attribution trailers are missing from commits, and checks CodeRabbit configuration at session start.
  • Documentation

    • Added plugin documentation with installation instructions, behavior summary, enforced rules, examples, and usage guidance.

Adds PreToolUse hook for conventional commit message validation,
PostToolUse hook for AI attribution trailer checking, and
SessionStart hook for Coderabbit config validation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented Apr 22, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jeff-roche

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 22, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2195a55d-3658-4a56-a61e-37142ab37078

📥 Commits

Reviewing files that changed from the base of the PR and between bbd85e1 and 50fbabb.

📒 Files selected for processing (1)
  • .claude-plugin/marketplace.json
✅ Files skipped from review due to trivial changes (1)
  • .claude-plugin/marketplace.json

Walkthrough

Adds a new code-quality plugin with manifest and marketplace entry, registers PreToolUse/PostToolUse/SessionStart hooks, and introduces three Bash hook scripts to validate commit subjects, check AI attribution trailers, and verify .coderabbit.yaml presence and required fields.

Changes

Cohort / File(s) Summary
Plugin manifest & marketplace
plugins/code-quality/.claude-plugin/plugin.json, .claude-plugin/marketplace.json
Added plugin metadata (name, version, author, homepage, license) and registered the plugin in marketplace.json.
Documentation
plugins/code-quality/README.md
Added README describing hook behaviors, enforced commit convention, allowed attribution trailers, required .coderabbit.yaml fields, install command, and license.
Hook registrations
plugins/code-quality/hooks/hooks.json
Registered three hooks: PreToolUse (runs commit-message check), PostToolUse (runs attribution check), and SessionStart (runs coderabbit config check) with matchers, timeouts, and status messages.
Hook scripts
plugins/code-quality/scripts/check-commit-message.sh, plugins/code-quality/scripts/check-attribution.sh, plugins/code-quality/scripts/check-coderabbit-config.sh
Added executable Bash scripts: commit subject validation (conventional-commit regex, exits 1 on invalid), post-tool attribution scanner (emits advisory JSON when trailers missing), and session-start config validator (emits JSON when .coderabbit.yaml is missing or lacks required keys).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a code-quality plugin with commit convention hooks, which is the primary purpose of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
plugins/code-quality/scripts/check-coderabbit-config.sh (1)

34-43: Tighten YAML key checks to avoid comment/substring false positives.

Current grep patterns can pass when keys appear only in comments or unrelated values.

Suggested refactor
-if ! grep -q 'auto_review' "$CONFIG"; then
+if ! grep -qE '^[[:space:]]*auto_review:' "$CONFIG"; then
     MISSING+=("auto_review")
 fi

-if ! grep -qE 'path_filters|path_instructions' "$CONFIG"; then
+if ! grep -qE '^[[:space:]]*(path_filters|path_instructions):' "$CONFIG"; then
     MISSING+=("path_filters or path_instructions")
 fi

-if ! grep -q 'instructions' "$CONFIG"; then
+if ! grep -qE '^[[:space:]]*instructions:' "$CONFIG"; then
     MISSING+=("instructions")
 fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/code-quality/scripts/check-coderabbit-config.sh` around lines 34 -
43, The current grep checks on CONFIG use substring matches and can be fooled by
comments; update the three grep invocations to match YAML keys explicitly by
looking for lines that start with optional whitespace followed by the key name
and a colon (e.g., use patterns like '^[[:space:]]*auto_review[[:space:]]*:'),
and for the combined check match either 'path_filters' or 'path_instructions'
with the same anchored pattern; keep using the CONFIG variable and append to the
MISSING array exactly as before when a key is not found.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@plugins/code-quality/README.md`:
- Around line 36-38: The fenced code block containing the command "/plugin
marketplace add openshift-eng/edge-tooling code-quality" is missing a language
identifier and triggers markdownlint MD040; update that fenced block in
plugins/code-quality/README.md to include a language tag (e.g., "bash") after
the opening backticks so the block reads as a bash code fence to satisfy the
linter.

In `@plugins/code-quality/scripts/check-attribution.sh`:
- Line 1: Change the script shebang from the current /bin/bash to the
repository-standard /usr/bin/bash per CONTRIBUTING.md; update the top of
plugins/code-quality/scripts/check-attribution.sh so the first line uses
#!/usr/bin/bash to ensure the correct shell is invoked.

In `@plugins/code-quality/scripts/check-coderabbit-config.sh`:
- Line 1: Replace the non-standard shebang in the script
check-coderabbit-config.sh: change the first line from #!/bin/bash to the
repository-standard interpreter path #!/usr/bin/bash so the script uses the
required shell as specified in CONTRIBUTING.md.

In `@plugins/code-quality/scripts/check-commit-message.sh`:
- Line 1: Update the shebang in the check-commit-message.sh script: replace the
current first line shebang (#!/bin/bash) with the repository standard shebang
(#!/usr/bin/bash) so it conforms to CONTRIBUTING.md; modify only the top-of-file
shebang in check-commit-message.sh and keep the rest of the script (including
set -euo pipefail and quoting) unchanged, preserving the file's executable
permission.

---

Nitpick comments:
In `@plugins/code-quality/scripts/check-coderabbit-config.sh`:
- Around line 34-43: The current grep checks on CONFIG use substring matches and
can be fooled by comments; update the three grep invocations to match YAML keys
explicitly by looking for lines that start with optional whitespace followed by
the key name and a colon (e.g., use patterns like
'^[[:space:]]*auto_review[[:space:]]*:'), and for the combined check match
either 'path_filters' or 'path_instructions' with the same anchored pattern;
keep using the CONFIG variable and append to the MISSING array exactly as before
when a key is not found.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d031845d-20c3-4bdd-824f-c8e3c4ead8f2

📥 Commits

Reviewing files that changed from the base of the PR and between c994b2d and b3cfac3.

📒 Files selected for processing (6)
  • plugins/code-quality/.claude-plugin/plugin.json
  • plugins/code-quality/README.md
  • plugins/code-quality/hooks/hooks.json
  • plugins/code-quality/scripts/check-attribution.sh
  • plugins/code-quality/scripts/check-coderabbit-config.sh
  • plugins/code-quality/scripts/check-commit-message.sh

Comment thread plugins/code-quality/README.md Outdated
Comment thread plugins/code-quality/scripts/check-attribution.sh Outdated
Comment thread plugins/code-quality/scripts/check-coderabbit-config.sh Outdated
Comment thread plugins/code-quality/scripts/check-commit-message.sh Outdated
jeff-roche and others added 3 commits April 22, 2026 13:17
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add bash language tag to fenced code block in README (MD040)
- Use #!/usr/bin/bash shebang across all scripts
- Anchor grep patterns to YAML key positions in coderabbit config check

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@brandisher
Copy link
Copy Markdown
Contributor

/assign

Copy link
Copy Markdown
Contributor

@brandisher brandisher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I get the goal of this PR but the implementation needs refining if I've understood it correctly.

First thing I see is that the plugin is called code-quality but is geared more towards standardizing parts of the git workflow like commit message correctness. For that goal, ai-helpers actually has a good git base already that we should take inspiration from.

Second thing, the use case isn't a good fit for a plugin. If someone doesn't install it, they don't get the hooks. I think the idea is good, it just needs a different implementation approach. Is there any reason these can't be landed in .claude/hooks so just by virture of starting CC in the repo we're doing all the necessary checks?

Third thing, I'm not sure I understand the coderabbit check - that's distinctly separate from the git aspect and once the file exists in the repo, I'm not sure why we'd want to check for it on repeat. That's kind of a set it and forget it thing. What am I missing?

@brandisher
Copy link
Copy Markdown
Contributor

Adding a hold while the comment gets addressed.

/hold

@openshift-ci openshift-ci Bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Apr 22, 2026
openshift-merge-bot Bot pushed a commit that referenced this pull request Apr 23, 2026
…ew process (#77)

* docs: add architectural guidelines from PR #74 and #75 reviews

Codify recurring review feedback into CONTRIBUTING.md so contributors
can self-evaluate before submitting: naming accuracy, hooks vs plugins
for mandatory checks, lifecycle placement, reuse, and test coverage
for validation logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: remove ai-helpers references from CONTRIBUTING.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@jeff-roche
Copy link
Copy Markdown
Contributor Author

@brandisher

I think I get the goal of this PR but the implementation needs refining if I've understood it correctly.

Agreed, I meant to throw a hold on it so we could discuss approaches like this as a team but forgot to do the wip/hold

First thing I see is that the plugin is called code-quality but is geared more towards standardizing parts of the git workflow like commit message correctness. For that goal, ai-helpers actually has a good git base already that we should take inspiration from.

code-quality was just a generic name to throw on it for now, open to moving and/or renaming and/or dropping entirely after discussing with the team. Also not opposed to pulling in more from ai-helpers

Second thing, the use case isn't a good fit for a plugin. If someone doesn't install it, they don't get the hooks. I think the idea is good, it just needs a different implementation approach. Is there any reason these can't be landed in .claude/hooks so just by virture of starting CC in the repo we're doing all the necessary checks?

I actually disagree, I think it's a good usecase for a repo hook and a plugin and here's my reasoning:

  • We want to maintain quality within this repo which fits a git/claude hook perfectly ✔️
  • It's also based around generic industry best practices so it can be applied to any repo we work on which fits it as a good plugin so that it can be cross-repo ✔️

That being said, I'm not married to any of these checks and happy to change them up

Third thing, I'm not sure I understand the coderabbit check - that's distinctly separate from the git aspect and once the file exists in the repo, I'm not sure why we'd want to check for it on repeat. That's kind of a set it and forget it thing. What am I missing?

I actually think the code rabbit check is a better fit for #73 because it was meant to be part of the org-standardization toolkit (which is what #73 covers, will comment there about potentially moving it to a different repo (ai-helpers?))

Copy link
Copy Markdown
Contributor

@brandisher brandisher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I put some more specific comments to narrow the discussion a bit. The spirit of the PR is clear - standardize conventions. But, we need to be clear about what this PR is doing in practice.

  1. Trys to enforce that all commits have an attribution for AI.
  2. Trys to enforce that .coderabbit.yaml exists.
  3. Trys to enforce a commit message style.

We want to maintain quality within this repo which fits a git/claude hook perfectly.

We agree that we want to maintain quality. What this PR is doing is enforcing an opinionated style. See the individual comments for specifics.

It's also based around generic industry best practices so it can be applied to any repo we work on which fits it as a good plugin so that it can be cross-repo ✔️

I hear the drive, but generic practices lead to generic results. We want to generalize where it makes sense and is impactful, not just where its possible. I left a few specific comments around this as well - the tldr is that these things are generally done well enough that they're not worth enforcing. If we are going to try to enforce them, they need to be done at the right level:

  • git related checks need to be .git resident so they're deterministic and work regardless of development model. The Claude hook you have kind of fits the case where you're creating commits with CC so it only needs to be checked there, but its repo-local so for our multi-repo workstreams this would get missed anyway.
  • Checking for optional specific files has little impact. In this particular case, we'd actually be fine with no .coderabbit.yaml in our repo - it just got set up so we could start evolving it.

"version": "1.0.0"
},
{
"name": "code-quality",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This plugin is trying to enforce an opinionated style as opposed to code quality which is something objectively measurable. This needs to be renamed to reflect that.

Comment on lines +2 to +3
"name": "code-quality",
"description": "Code convention enforcement — commit messages, attribution, and Coderabbit configuration",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here about the name update.

Comment on lines +8 to +11
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/check-commit-message.sh",
"timeout": 2,
"statusMessage": "Validating commit message..."
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit message checking needs to be deterministic to be valuable. The only way to do this as deterministically as possible is to have a pre-commit hook at the git level.

The Claude hooks also don't work in a multi-repo development model which is table stakes for many workstreams.

@@ -0,0 +1,65 @@
#!/usr/bin/bash
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be dropped. This isn't worth the tokens to enforce. Especially in the case of openshift and openshift-eng there's a default configuration enabled globally so even without this file you still get coderabbit configuration.

Comment on lines +1 to +54
#!/usr/bin/bash
# PreToolUse(Bash) hook — validates conventional commits format on git commit commands
set -euo pipefail

if ! command -v jq &>/dev/null; then
exit 0
fi

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.toolInput.command // empty')

# Fast path: not a commit command
if [[ "$COMMAND" != *"git commit"* ]]; then
exit 0
fi

# Extract commit message subject line
SUBJECT=""

# Heredoc pattern: git commit -m "$(cat <<'EOF' ... EOF )"
if [[ "$COMMAND" =~ cat\ \<\<[\']?EOF ]]; then
# Extract first non-empty line after the heredoc delimiter
SUBJECT=$(echo "$COMMAND" | sed -n '/<<.*EOF/,/^EOF/{/<<.*EOF/d;/^EOF/d;/^[[:space:]]*$/d;p;}' | head -1 | sed 's/^[[:space:]]*//')
# Standard -m pattern with double or single quotes
elif [[ "$COMMAND" =~ git\ commit.*-m\ \" ]]; then
SUBJECT=$(echo "$COMMAND" | sed -n 's/.*git commit[^"]*-m "\([^"]*\)".*/\1/p' | head -1)
elif [[ "$COMMAND" =~ git\ commit.*-m\ \' ]]; then
SUBJECT=$(echo "$COMMAND" | sed -n "s/.*git commit[^']*-m '\\([^']*\\)'.*/\\1/p" | head -1)
fi

# If we couldn't extract a message, fail open
if [ -z "$SUBJECT" ]; then
exit 0
fi

# For multi-line messages, take only the first line (subject)
SUBJECT=$(echo "$SUBJECT" | head -1)

# Validate against conventional commits pattern
if echo "$SUBJECT" | grep -qE '^(feat|fix|docs|refactor|test|chore|build|ci|perf|style)(\(.+\))?: .+'; then
exit 0
fi

# Invalid format
cat >&2 <<'MSG'
Commit message does not follow conventional commits format.
Expected: type(scope): description
Types: feat, fix, docs, refactor, test, chore, build, ci, perf, style
Examples:
feat(api): add health endpoint
fix(deploy): correct subnet CIDR validation
docs: update contributing guide
MSG
exit 1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit message enforcement needs to happen at the git level to be deterministic. This hook would get skipped in a multi-repo setup.

I'd also say that this style is informal and not something we'd want to enforce firmly. The CONTRIBUTING.md has some words around this style so as long as CC does it best effort, that's all we need.

Comment on lines +1 to +45
#!/usr/bin/bash
# PostToolUse(Bash) hook — warns if AI attribution trailers are missing after commits
set -euo pipefail

if ! command -v jq &>/dev/null; then
exit 0
fi

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.toolInput.command // empty')

# Fast path: not a commit command
if [[ "$COMMAND" != *"git commit"* ]]; then
exit 0
fi

# Get the last commit message
COMMIT_MSG=$(git log -1 --format=%B 2>/dev/null || true)

if [ -z "$COMMIT_MSG" ]; then
exit 0
fi

# Check for attribution trailers (case-insensitive)
if echo "$COMMIT_MSG" | grep -qi 'Co-Authored-By:'; then
exit 0
fi
if echo "$COMMIT_MSG" | grep -qi 'Assisted-by:'; then
exit 0
fi
if echo "$COMMIT_MSG" | grep -qi 'Generated-by:'; then
exit 0
fi

# No attribution found — emit advisory context
cat <<'EOF'
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "AI ATTRIBUTION MISSING: The last commit has no AI attribution trailer. Consider amending with one of:\n Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>\n Assisted-by: Claude Code\n Generated-by: Claude Code"
}
}
EOF

exit 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enforcing at this level is bound to create more noise than value. This is an opinionated style and it creates noise where commits are not AI generated which is still a valid case.

To be frank, the guideline around this is clear so I get the motivation but the impact is very low to the point that I wouldn't want to have enforcement of this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completely fair! I think none of this is 100% required from the perspective of our repo. This one is an easy one to cut

@jeff-roche
Copy link
Copy Markdown
Contributor Author

I'm going to close this one, I don't think any of this is 100% required and if we want to revisit down the road, it's easy enough to recreate any of this.

@jeff-roche jeff-roche closed this Apr 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants