Skip to content

feat: support org-level issue templates from .github repo#440

Closed
jwm4 wants to merge 1 commit into
mainfrom
support-org-level-issue-templates
Closed

feat: support org-level issue templates from .github repo#440
jwm4 wants to merge 1 commit into
mainfrom
support-org-level-issue-templates

Conversation

@jwm4
Copy link
Copy Markdown
Contributor

@jwm4 jwm4 commented May 18, 2026

Summary

  • When assessing a repository that belongs to a GitHub organization, issue_pr_templates check now falls back to the organization's .github repo for issue and PR templates when no local templates are found
  • GitHubTemplatesFetcher utility class to query the GitHub API for org-level templates
  • IssuePRTemplatesAssessor updated to check local .github/ first, then use org-level templates as fallback (requires GITHUB_TEMPLATE_TOKEN)
  • 26 unit tests covering the fetcher and the assessor

Problem

Repos like kagenti/kagenti have no .github/ISSUE_TEMPLATE/ directory locally, but the organization kagenti defines templates at kagenti/.github. agentready was scoring these as completely missing templates.

Implementation

New: src/agentready/utils/github_templates.py

  • GitHubTemplatesFetcher — fetches issue/PR templates from an org's .github repo via the GitHub API
  • extract_owner(url) — parses the owner/org from HTTPS (https://github.com/owner/repo) and SSH (git@github.com:owner/repo.git) URLs
  • fetch_pr_templates(owner) — returns list of PR template file paths
  • fetch_issue_templates(owner) — returns list of issue template file paths
  • Requires GITHUB_TOKEN env var; gracefully skips when no token is available

Modified: src/agentready/assessors/structure.py

  • IssuePRTemplatesAssessor.assess() checks local .github/ first, then falls back to org-level templates when no local templates exist and GITHUB_TOKEN is available
  • Evidence messages clearly distinguish local vs inherited templates
  • Local templates always take precedence

New: tests/unit/test_github_templates.py (18 tests)

Tests for owner extraction, token handling, PR/issue fetching, edge cases (None/empty URLs, non-GitHub URLs, missing repos, auth errors).

New: tests/unit/test_issue_template_assessor.py (8 tests)

Tests for basic assessor behavior plus org-level fallback covering scenarios:

  • Org-only templates (PR + issue inherited from .github)
  • No org templates (falls back to no-op)
  • No token (org check skipped)
  • Non-GitHub URL (owner extraction returns None)
  • Local templates alone (no org check when local suffice)
  • Mixed: org PR template + local issue template

Tests

$ python -m pytest tests/unit/test_github_templates.py tests/unit/test_issue_template_assessor.py -v
26 passed in 0.16s

$ python -m pytest tests/ -x -q
1248 passed, 21 skipped

All linters pass: black, isort, ruff.

Summary by CodeRabbit

Release Notes

  • New Features

    • PR and issue templates can now be inherited from organization-level GitHub repositories as a fallback when local templates are unavailable.
    • Organization-level template checks require a configured GitHub token.
  • Tests

    • Added comprehensive test coverage for template inheritance and assessment behavior.

When assessing a repository that belongs to a GitHub organization,
the issue_pr_templates check now falls back to the organization's
.github repo for issue and PR templates when no local templates exist.

Requires GITHUB_TOKEN to be set. Local templates take precedence
over org-level inheritance and the fallback is only triggered when
no local templates are found.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

📝 Walkthrough

Walkthrough

This PR adds organization-level GitHub template fallback to the issue/PR templates assessor. The new GitHubTemplatesFetcher utility queries GitHub's REST API for .github repo templates, while IssuePRTemplatesAssessor now checks local templates first, then fetches org-level templates when local ones are insufficient—conditional on a valid GITHUB_TOKEN.

Changes

Org-level GitHub template support

Layer / File(s) Summary
GitHub templates utility and API integration
src/agentready/utils/github_templates.py, tests/unit/test_github_templates.py
GitHubTemplatesFetcher fetches PR/issue templates from .github org repos via GitHub REST API, extracts owner from HTTPS/SSH URLs, and handles auth/missing-repo errors gracefully. Unit tests cover URL parsing (HTTPS and SSH), token configuration, and template filtering for both PR and issue templates.
IssuePRTemplatesAssessor org-level fallback
src/agentready/assessors/structure.py, tests/unit/test_issue_template_assessor.py
Assessor checks local templates first, then falls back to org-level templates when local coverage is incomplete. Scoring depends on org template counts (≥2 issue templates pass, 1 partial score, 0 fail). Org checks require valid GITHUB_TOKEN and extractable repo owner. Integration tests validate local-only, org-only, combined, and missing-token scenarios.

Possibly related issues

  • ambient-code/agentready#430: This PR implements org-level .github/ISSUE_TEMPLATE support via the GitHubTemplatesFetcher and org-fallback logic, directly addressing the feature request.
🚥 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 Title follows Conventional Commits format (feat: type with clear scope) and accurately describes the main changeset: adding org-level template inheritance support.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch support-org-level-issue-templates
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch support-org-level-issue-templates

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link
Copy Markdown
Contributor

📈 Test Coverage Report

Branch Coverage
This PR 73.6%
Main 73.3%
Diff ✅ +0.3%

Coverage calculated from unit tests only

Copy link
Copy Markdown

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/agentready/assessors/structure.py`:
- Around line 835-847: The assessor's assess() currently lets
GitHubTemplatesFetcher.fetch_pr_templates and fetch_issue_templates raise
GitHubTemplatesError and crash the run; wrap calls to
GitHubTemplatesFetcher.extract_owner + fetch_pr_templates and
fetch_issue_templates in try/except catching GitHubTemplatesError, and on
exception degrade gracefully by leaving score unchanged, setting
pr_template_found/issue_template_found false (or whatever local flag is used),
appending evidence like "Skipped: GitHub API error fetching templates" (and
optionally processLogger.warn the error), and continue; apply the same guard
around both fetch_pr_templates and fetch_issue_templates invocations so
transient GitHub failures do not propagate.

In `@tests/unit/test_github_templates.py`:
- Around line 86-94: The tests test_no_repo_returns_empty and the similar one at
lines 146-153 incorrectly simulate a 404 because they set
mock_get.return_value.status_code = 404 but do not make raise_for_status()
raise, so the code path in GitHubTemplatesFetcher._fetch_contents() that catches
requests.HTTPError for 404 is not exercised; update those tests to have
mock_get.return_value.raise_for_status.side_effect =
requests.exceptions.HTTPError(response=mock_get.return_value) (or otherwise
raise an HTTPError) so that calling fetch_pr_templates on the
GitHubTemplatesFetcher instance triggers the exception branch and returns an
empty list.
🪄 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: ASSERTIVE

Plan: Enterprise

Run ID: 29fb1509-7a55-459a-a01d-4839b2544773

📥 Commits

Reviewing files that changed from the base of the PR and between 7c5c9ef and a20de54.

📒 Files selected for processing (4)
  • src/agentready/assessors/structure.py
  • src/agentready/utils/github_templates.py
  • tests/unit/test_github_templates.py
  • tests/unit/test_issue_template_assessor.py

Comment on lines +835 to +847
fetcher = GitHubTemplatesFetcher()
owner = fetcher.extract_owner(repository.url)
if owner:
org_pr_templates = fetcher.fetch_pr_templates(owner)
if org_pr_templates:
score += 50
pr_template_found = True
evidence.append(
"PR template found (inherited from org-level .github repo)"
)
else:
evidence.append("No PR template found")
else:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle GitHub API failures in the assessor instead of propagating exceptions.

fetch_pr_templates() / fetch_issue_templates() can raise GitHubTemplatesError (e.g., 5xx/network), and assess() currently does not catch it. A transient GitHub outage can therefore fail this assessor run instead of degrading gracefully.

Suggested guard pattern
 from ..utils.github_templates import GitHubTemplatesFetcher
+from ..utils.github_templates import GitHubTemplatesError
...
-            if owner:
-                org_pr_templates = fetcher.fetch_pr_templates(owner)
+            if owner:
+                try:
+                    org_pr_templates = fetcher.fetch_pr_templates(owner)
+                except GitHubTemplatesError:
+                    org_pr_templates = []
+                    evidence.append("Org-level PR template check unavailable (GitHub API error)")
                 if org_pr_templates:
...
-            if owner:
-                org_issue_templates = fetcher.fetch_issue_templates(owner)
+            if owner:
+                try:
+                    org_issue_templates = fetcher.fetch_issue_templates(owner)
+                except GitHubTemplatesError:
+                    org_issue_templates = []
+                    evidence.append("Org-level issue template check unavailable (GitHub API error)")
                 if len(org_issue_templates) >= 2:

As per coding guidelines, "Check for proper error handling (return skipped/error Finding, don't crash)."

Also applies to: 878-896

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agentready/assessors/structure.py` around lines 835 - 847, The assessor's
assess() currently lets GitHubTemplatesFetcher.fetch_pr_templates and
fetch_issue_templates raise GitHubTemplatesError and crash the run; wrap calls
to GitHubTemplatesFetcher.extract_owner + fetch_pr_templates and
fetch_issue_templates in try/except catching GitHubTemplatesError, and on
exception degrade gracefully by leaving score unchanged, setting
pr_template_found/issue_template_found false (or whatever local flag is used),
appending evidence like "Skipped: GitHub API error fetching templates" (and
optionally processLogger.warn the error), and continue; apply the same guard
around both fetch_pr_templates and fetch_issue_templates invocations so
transient GitHub failures do not propagate.

Comment on lines +86 to +94
def test_no_repo_returns_empty(self, mock_get):
"""Test that non-existent .github repo returns empty."""
mock_get.return_value.status_code = 404
mock_get.return_value.raise_for_status.side_effect = None

fetcher = GitHubTemplatesFetcher(token="ghp_test123456789012345678901234567890")
result = fetcher.fetch_pr_templates("kagenti")
assert result == []

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

404 tests are not exercising the HTTPError branch.

Both “no repo” tests set status_code = 404 but keep raise_for_status() non-raising, so _fetch_contents() never enters the 404 exception path these tests intend to validate.

Suggested test fix
+import requests
...
     `@patch`("agentready.utils.github_templates.requests.get")
     def test_no_repo_returns_empty(self, mock_get):
         """Test that non-existent .github repo returns empty."""
-        mock_get.return_value.status_code = 404
-        mock_get.return_value.raise_for_status.side_effect = None
+        response = mock_get.return_value
+        response.status_code = 404
+        response.raise_for_status.side_effect = requests.HTTPError(response=response)
...
     `@patch`("agentready.utils.github_templates.requests.get")
     def test_no_repo_returns_empty(self, mock_get):
         """Test that non-existent .github repo returns empty."""
-        mock_get.return_value.status_code = 404
-        mock_get.return_value.raise_for_status.side_effect = None
+        response = mock_get.return_value
+        response.status_code = 404
+        response.raise_for_status.side_effect = requests.HTTPError(response=response)

Also applies to: 146-153

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_github_templates.py` around lines 86 - 94, The tests
test_no_repo_returns_empty and the similar one at lines 146-153 incorrectly
simulate a 404 because they set mock_get.return_value.status_code = 404 but do
not make raise_for_status() raise, so the code path in
GitHubTemplatesFetcher._fetch_contents() that catches requests.HTTPError for 404
is not exercised; update those tests to have
mock_get.return_value.raise_for_status.side_effect =
requests.exceptions.HTTPError(response=mock_get.return_value) (or otherwise
raise an HTTPError) so that calling fetch_pr_templates on the
GitHubTemplatesFetcher instance triggers the exception branch and returns an
empty list.

@jwm4
Copy link
Copy Markdown
Contributor Author

jwm4 commented May 18, 2026

Tested this against kagenti/kagenti (the example from issue #430). Without GITHUB_TOKEN set, the org-level fallback doesn't fire because GitHubTemplatesFetcher.configured gates all fetch operations. The assessor still reports issue_pr_templates as FAIL with score 0, which is the same behavior as main.

With GITHUB_TOKEN set, it works correctly: finds the PR template and 4 issue templates from the org-level .github repo, scoring 100.

Since most org .github repos are public, requiring a token for this check means the common case described in the issue still fails silently. Consider allowing unauthenticated requests as a fallback (the GitHub REST API supports this for public repos, just with lower rate limits).

Also noting the two CodeRabbit findings are valid: the uncaught GitHubTemplatesError in assess() could crash the run on transient API errors, and the 404 tests don't actually trigger raise_for_status().

This comment is from Bill Murdock with assistance from Claude Code.

@jwm4
Copy link
Copy Markdown
Contributor Author

jwm4 commented May 18, 2026

This was fixed in #441 instead.

@jwm4 jwm4 closed this May 18, 2026
@jwm4 jwm4 deleted the support-org-level-issue-templates branch May 18, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant