Skip to content

Harden upstream pin freshness reporting - #39

Open
ANcpLua wants to merge 1 commit into
mainfrom
agent/pin-freshness-hardening
Open

Harden upstream pin freshness reporting#39
ANcpLua wants to merge 1 commit into
mainfrom
agent/pin-freshness-hardening

Conversation

@ANcpLua

@ANcpLua ANcpLua commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • distinguish current, unknown, and differs outcomes with stable exit codes
  • harden GitHub release, branch comparison, and XML failure handling
  • reconcile the freshness tracking issue through one workflow path
  • run focused pin-checker tests in CI

Validation

  • Python checker tests: 14/14
  • actionlint on both modified workflows
  • release build: 0 warnings, 0 errors
  • attribute hash, deprecated catalog, and generated-tree verification
  • pipeline tests: 58/58
  • source-generation tests: 58/58
  • live checker returned the expected stale status 10

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • Bug Fixes

    • Improved detection of stale, current, unknown, malformed, and inconsistent pin states.
    • Added clearer handling for divergent or invalid upstream data.
    • Tracking issues are now created, updated, or closed based on verified pin status.
  • Tests

    • Added comprehensive automated coverage for release pins, branch pins, command-line behavior, and status reporting.
    • CI now runs the pin freshness test suite automatically.

Walkthrough

The freshness checker now validates upstream release and branch data, reports explicit current, stale, and unknown statuses, and integrates with workflow issue reconciliation. Unit tests cover checker and CLI behavior, and CI runs the test suite.

Changes

Pin freshness lifecycle

Layer / File(s) Summary
Checker validation and status handling
src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/check_pin_freshness.py
The checker validates GitHub responses and branch comparisons. It reports unknown freshness for malformed data and returns status codes 0, 2, and 10.
Checker behavior tests
tests/scripts/test_check_pin_freshness.py
Tests cover release checks, branch comparison states, malformed responses, and CLI output and exit codes.
Workflow status reconciliation
.github/workflows/pin-freshness.yml, .github/workflows/ci.yml
The workflow validates checker statuses before publishing outputs. It creates, updates, closes, or fails the tracking issue based on the status. CI runs the checker tests with unittest discovery.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested labels: area:infra


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Title check ❌ Error The title describes the changes, but it does not use the required Conventional Commit prefix and area scope. Use a title such as "fix(infra): harden upstream pin freshness reporting".
✅ Passed checks (7 passed)
Check name Status Explanation
Description check ✅ Passed The description directly covers the checker, workflow, testing, and validation changes in the pull request.
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.
Otel Instrumentation Required ✅ Passed The PR adds only workflow, freshness-checker, and Python test changes; no DI registration or injectable service class was added, so ActivitySource/Meter instrumentation is not required.
No Unbounded Mcp Responses ✅ Passed The PR changes only CI, pin-freshness workflow, checker code, and tests; no files under src/qyl.mcp or MCP tool definitions changed.
Duckdb Backpressure On Write Paths ✅ Passed The PR changes only workflows, a Python freshness checker, and tests; the PR diff and tracked executable sources contain no DuckDB reference or DuckDB write path.
Cancellationtoken Threading ✅ Passed The pull request changes no C# files under src; therefore it adds no public async C# methods requiring CancellationToken review.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 @.github/workflows/ci.yml:
- Around line 15-16: Add a push-specific concurrency configuration to the CI
workflow, using a group keyed to the relevant push workflow or ref and
configuring newer runs to cancel in-progress older runs. Keep the existing test
step and other workflow behavior unchanged.

In
`@src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/check_pin_freshness.py`:
- Around line 75-88: Update the lookup exception handling around
urllib.request.urlopen in the freshness check to map transport failures,
including http.client.RemoteDisconnected and IncompleteRead, to FreshnessUnknown
so callers produce EXIT_UNKNOWN. Catch OSError and http.client.HTTPException
without overriding the existing HTTPError-specific details or malformed-JSON
handling, and add fixtures covering these failure cases with the expected
unknown exit status.
🪄 Autofix

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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1122cef2-c8cc-499f-8a37-a306daf84fd9

📥 Commits

Reviewing files that changed from the base of the PR and between 960498f and fc3b3f4.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • .github/workflows/pin-freshness.yml
  • src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/check_pin_freshness.py
  • tests/scripts/test_check_pin_freshness.py
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: build
🧰 Additional context used
📓 Path-based instructions (1)
.github/**

⚙️ CodeRabbit configuration file

GitHub Actions workflows. Review for: action version pinning (use SHA not tags for third-party actions), proper secret handling (no secrets in logs, use GITHUB_TOKEN where possible), unnecessary workflow triggers, and job dependency correctness. Flag missing concurrency groups on push-triggered workflows. Ensure matrix strategies cover the supported .NET TFMs.

Files:

  • .github/workflows/ci.yml
  • .github/workflows/pin-freshness.yml
🔇 Additional comments (4)
src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/check_pin_freshness.py (1)

2-17: LGTM!

Also applies to: 39-63, 90-222

tests/scripts/test_check_pin_freshness.py (1)

1-178: LGTM!

.github/workflows/pin-freshness.yml (2)

5-7: LGTM!

Also applies to: 44-102


65-69: 🗄️ Data Integrity & Integration

No change needed.

The workflow header sets permissions.issues: write, so the reconciliation step has the required issue permissions.

Comment thread .github/workflows/ci.yml
Comment on lines +15 to +16
- name: Test pin freshness checker
run: python3 -m unittest discover --start-directory tests/scripts --pattern 'test_*.py'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,18p' .github/workflows/ci.yml
rg -n '^\s*concurrency:' .github/workflows/ci.yml

Repository: ANcpLua/Qyl.OpenTelemetry.SemanticConventions

Length of output: 558


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== top section with concurrency search =="
awk '
/^[^ ].*[^ ]/ { line=$0; lineno++; print lineno"\t"line }
/^  concurrency:/ { print "FOUND CONCUURRENCY LINE", NR, $0 }
' .github/workflows/ci.yml | sed -n '1,80p'

echo "== relevant workflow header =="
sed -n '1,25p' .github/workflows/ci.yml

echo "== full concurrency occurrences =="
rg -n 'concurrency|push:|pull_request|permissions:|jobs:' .github/workflows/ci.yml || true

Repository: ANcpLua/Qyl.OpenTelemetry.SemanticConventions

Length of output: 953


Add push concurrency to .github/workflows/ci.yml.

This push-triggered workflow has no concurrency group, so add a push-specific concurrency group so later push runs do not keep running in parallel.

🤖 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 @.github/workflows/ci.yml around lines 15 - 16, Add a push-specific
concurrency configuration to the CI workflow, using a group keyed to the
relevant push workflow or ref and configuring newer runs to cancel in-progress
older runs. Keep the existing test step and other workflow behavior unchanged.

Source: Path instructions

Comment on lines 75 to +88
try:
with urllib.request.urlopen(request, timeout=30) as response:
return json.load(response)
payload = json.load(response)
except urllib.error.HTTPError as error:
detail = f"HTTP {error.code}"
if error.code in (403, 429):
detail += " (rate limited; set GITHUB_TOKEN)"
elif error.code == 404:
detail += " (renamed, deleted, or unknown ref)"
raise LookupFailed(f"{path}: {detail}") from error
raise FreshnessUnknown(f"{path}: {detail}") from error
except (urllib.error.URLError, TimeoutError) as error:
raise LookupFailed(f"{path}: {error}") from error
raise FreshnessUnknown(f"{path}: {error}") from error
except json.JSONDecodeError as error:
raise LookupFailed(f"{path}: upstream returned malformed JSON: {error}") from error
raise FreshnessUnknown(f"{path}: upstream returned malformed JSON: {error}") from error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import http.client
import urllib.error

assert not issubclass(http.client.RemoteDisconnected, urllib.error.URLError)
assert not issubclass(http.client.RemoteDisconnected, TimeoutError)
assert issubclass(http.client.RemoteDisconnected, OSError)
assert issubclass(http.client.IncompleteRead, http.client.HTTPException)
print("These transport exceptions are outside the current handler.")
PY

Repository: ANcpLua/Qyl.OpenTelemetry.SemanticConventions

Length of output: 243


🏁 Script executed:

#!/bin/bash
set -euo pipefail

wc -l src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/check_pin_freshness.py
ast-grep outline src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/check_pin_freshness.py || true
sed -n '1,180p' src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/check_pin_freshness.py

printf '\n--- workflow references ---\n'
if [ -f .github/workflows/pin-freshness.yml ]; then
  sed -n '1,220p' .github/workflows/pin-freshness.yml
else
  rg -n "pin-freshness|EXIT_UNKNOWN|unknown status|exit" .github src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts || true
fi

Repository: ANcpLua/Qyl.OpenTelemetry.SemanticConventions

Length of output: 12892


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '189,226p' src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/check_pin_freshness.py

printf '\n--- fixture references ---\n'
rg -n "FreshnessUnknown|fixtures|fixtures|RemoteDisconnected|IncompleteRead|check_pin_freshness|pin-freshness" tests src -g '!bin' -g '!node_modules' -g '!dist' || true

printf '\n--- direct exception-path probes ---\n'
python3 - <<'PY'
from pathlib import Path
import subprocess
import sys
import tempfile

script = Path("src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/check_pin_freshness.py")
if not script.exists():
    raise SystemExit("script not found")

# Static imports and current handler set.
text = script.read_text()
print("imports http.client:", "import http.client" in text or "from http import client" in text)
print("catches JSONDecodeError:", "json.JSONDecodeError" in text)
print("catches TimeoutError:", "TimeoutError" in text)
print("catches URLError:", "urllib.error.URLError" in text)
print("catches OSError:", "OSError" in text)
print("catches HTTPException:", "http.client.HTTPException" in text)
print("catches RemoteDisconnected:", "RemoteDisconnected" in text)
print("catches IncompleteRead:", "IncompleteRead" in text)
print("main handles FreshnessUnknown:", "FreshnessUnknown" in text[text.find("def main"):])
PY

Repository: ANcpLua/Qyl.OpenTelemetry.SemanticConventions

Length of output: 6274


Map transport failures from urllib calls into EXIT_UNKNOWN.

http.client.RemoteDisconnected escapes the current URLError/TimeoutError handler, while http.client.IncompleteRead is not covered by JSONDecodeError when no JSON is read. These cases become unexpected non-0/non-10/non-2 exits in .github/workflows/pin-freshness.yml; catch OSError from the lookup, handle http.client.HTTPException, and add missing unknown-status fixtures for these failures.

Proposed fix
+import http.client
+
-    except (urllib.error.URLError, TimeoutError) as error:
+    except (
+        urllib.error.URLError,
+        OSError,
+        http.client.HTTPException,
+        UnicodeDecodeError,
+    ) as error:
         raise FreshnessUnknown(f"{path}: {error}") from error
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try:
with urllib.request.urlopen(request, timeout=30) as response:
return json.load(response)
payload = json.load(response)
except urllib.error.HTTPError as error:
detail = f"HTTP {error.code}"
if error.code in (403, 429):
detail += " (rate limited; set GITHUB_TOKEN)"
elif error.code == 404:
detail += " (renamed, deleted, or unknown ref)"
raise LookupFailed(f"{path}: {detail}") from error
raise FreshnessUnknown(f"{path}: {detail}") from error
except (urllib.error.URLError, TimeoutError) as error:
raise LookupFailed(f"{path}: {error}") from error
raise FreshnessUnknown(f"{path}: {error}") from error
except json.JSONDecodeError as error:
raise LookupFailed(f"{path}: upstream returned malformed JSON: {error}") from error
raise FreshnessUnknown(f"{path}: upstream returned malformed JSON: {error}") from error
import http.client
try:
with urllib.request.urlopen(request, timeout=30) as response:
payload = json.load(response)
except urllib.error.HTTPError as error:
detail = f"HTTP {error.code}"
if error.code in (403, 429):
detail += " (rate limited; set GITHUB_TOKEN)"
elif error.code == 404:
detail += " (renamed, deleted, or unknown ref)"
raise FreshnessUnknown(f"{path}: {detail}") from error
except (
urllib.error.URLError,
OSError,
http.client.HTTPException,
UnicodeDecodeError,
) as error:
raise FreshnessUnknown(f"{path}: {error}") from error
except json.JSONDecodeError as error:
raise FreshnessUnknown(f"{path}: upstream returned malformed JSON: {error}") from error
🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 75-75: Request-controlled URL passed to urlopen; validate against an allowlist to prevent SSRF.
Context: urllib.request.urlopen(request, timeout=30)
Note: [CWE-918] Server-Side Request Forgery (SSRF).

(urlopen-unsanitized-data)

🤖 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/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/check_pin_freshness.py`
around lines 75 - 88, Update the lookup exception handling around
urllib.request.urlopen in the freshness check to map transport failures,
including http.client.RemoteDisconnected and IncompleteRead, to FreshnessUnknown
so callers produce EXIT_UNKNOWN. Catch OSError and http.client.HTTPException
without overriding the existing HTTPError-specific details or malformed-JSON
handling, and add fixtures covering these failure cases with the expected
unknown exit status.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant