Conversation
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughThis PR introduces automation infrastructure for service updates, including dispatch targets, NuGet version bumping scripts, and GitHub Actions workflows. It also adds DocFX template configuration, editor configuration, Context7 widget integration for documentation, and AI agent guidelines. Changes
Sequence Diagram(s)sequenceDiagram
participant Release as Release Event
participant Trigger as trigger-downstream
participant DispatchAPI as GitHub API
participant ServiceUpdate as Service Update Workflow
participant NuGet as NuGet API
participant Props as Directory.Packages.props
participant CHANGELOG as CHANGELOG.md
participant PR as Pull Request
Release->>Trigger: Triggered on release publication
Trigger->>Trigger: Read dispatch-targets.json
Trigger->>DispatchAPI: POST repository_dispatch to each target
DispatchAPI->>ServiceUpdate: Trigger workflow_dispatch event
ServiceUpdate->>ServiceUpdate: Read trigger inputs (source_repo, version)
ServiceUpdate->>NuGet: Query latest versions for packages
ServiceUpdate->>Props: Bump PackageVersion entries
ServiceUpdate->>CHANGELOG: Update with new version entry
ServiceUpdate->>ServiceUpdate: Update Docker/NGINX versions if applicable
ServiceUpdate->>PR: Create PR with changes and assign reviewer
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Pull request overview
This pull request establishes a comprehensive automation framework for managing dependency updates across the Codebelt repository ecosystem. The changes introduce GitHub Actions workflows that automatically update NuGet packages when upstream dependencies are released, propagate those updates to downstream repositories, and handle related documentation and configuration updates.
Changes:
- Added automated service update workflow that bumps NuGet packages, updates release notes, CHANGELOG, and Docker tags when triggered by upstream releases or manually
- Added downstream notification workflow that dispatches update events to dependent repositories when a new release is published
- Added Python script to intelligently bump NuGet package versions by querying the NuGet API or using triggered version information
- Added comprehensive
.editorconfigfor code style enforcement and analyzer configuration - Added Context7 chat widget integration to documentation pages
- Added
AGENTS.mdguide for AI agents working on the codebase
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/service-update.yml |
Workflow that orchestrates dependency updates, version bumping, and PR creation |
.github/workflows/trigger-downstream.yml |
Workflow that notifies downstream repositories of new releases |
.github/scripts/bump-nuget.py |
Python script that updates package versions in Directory.Packages.props |
.github/dispatch-targets.json |
Configuration defining downstream repositories to notify |
.editorconfig |
Code style rules and analyzer configuration for consistent formatting |
.docfx/docfx.json |
Updated to include custom template for documentation |
.docfx/templates/modern/public/main.js |
JavaScript to inject Context7 support widget |
AGENTS.md |
Comprehensive guide for AI agents with project conventions and workflows |
.editorconfig
Outdated
| # Excluded becuase of inconsistency with other analyzers | ||
| [*.{cs,vb}] | ||
| dotnet_diagnostic.IDE0036.severity = none | ||
|
|
||
| # Order modifiers | ||
| # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0036 | ||
| # Excluded becuase of inconsistency with other analyzers |
There was a problem hiding this comment.
Spelling error: "becuase" should be "because"
| # Excluded becuase of inconsistency with other analyzers | |
| [*.{cs,vb}] | |
| dotnet_diagnostic.IDE0036.severity = none | |
| # Order modifiers | |
| # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0036 | |
| # Excluded becuase of inconsistency with other analyzers | |
| # Excluded because of inconsistency with other analyzers | |
| [*.{cs,vb}] | |
| dotnet_diagnostic.IDE0036.severity = none | |
| # Order modifiers | |
| # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0036 | |
| # Excluded because of inconsistency with other analyzers |
| for repo in targets: | ||
| url = f'https://api.github.com/repos/codebeltnet/{repo}/dispatches' | ||
| payload = json.dumps({ | ||
| 'event_type': 'codebelt-service-update', | ||
| 'client_payload': { | ||
| 'source_repo': source, | ||
| 'source_version': version | ||
| } | ||
| }).encode() | ||
| req = urllib.request.Request(url, data=payload, method='POST', headers={ | ||
| 'Authorization': f'Bearer {token}', | ||
| 'Accept': 'application/vnd.github+json', | ||
| 'Content-Type': 'application/json', | ||
| 'X-GitHub-Api-Version': '2022-11-28' | ||
| }) | ||
| with urllib.request.urlopen(req) as r: | ||
| print(f'✓ Dispatched to {repo}: HTTP {r.status}') |
There was a problem hiding this comment.
Missing error handling for HTTP request failures. If the urlopen call fails (network error, HTTP error, or invalid JSON response), the entire workflow will fail without attempting to dispatch to remaining repositories. Consider wrapping the dispatch loop in a try-except block to handle errors gracefully and continue processing other repositories.
| - name: Determine new version for this repo | ||
| id: newver | ||
| run: | | ||
| CURRENT=$(grep -oP '(?<=## \[)[\d.]+(?=\])' CHANGELOG.md | head -1) | ||
| NEW=$(echo "$CURRENT" | awk -F. '{printf "%s.%s.%d", $1, $2, $3+1}') | ||
| BRANCH="v${NEW}/service-update" | ||
| echo "current=$CURRENT" >> $GITHUB_OUTPUT | ||
| echo "new=$NEW" >> $GITHUB_OUTPUT | ||
| echo "branch=$BRANCH" >> $GITHUB_OUTPUT |
There was a problem hiding this comment.
The version extraction regex may fail if the CHANGELOG.md format deviates. The pattern '(?<=## \[)[\d.]+(?=\])' assumes a specific format with no spaces between brackets and version numbers. If the CHANGELOG.md ever contains entries like ## [ 11.0.5 ] with spaces, the extraction will fail silently and the awk command will receive empty input, potentially causing the version bump to fail with an unclear error. Consider adding validation to check if CURRENT is empty after extraction.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #54 +/- ##
==========================================
- Coverage 94.69% 94.37% -0.32%
==========================================
Files 46 46
Lines 942 942
Branches 124 124
==========================================
- Hits 892 889 -3
- Misses 41 44 +3
Partials 9 9 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (3)
.docfx/templates/modern/public/main.js (1)
2-11:start()is not idempotent — duplicate script injection on repeated callsDocFX may call the
start()hook more than once during hot-reload/navigation. Without a guard, each invocation appends another<script>element, loading the widget multiple times. Add a simple sentinel:♻️ Proposed fix
start: () => { + if (document.querySelector('script[data-library="/codebeltnet/xunit"]')) return; const script = document.createElement('script'); script.src = 'https://context7.com/widget.js';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.docfx/templates/modern/public/main.js around lines 2 - 11, The start() function currently appends the widget <script> every time it's called, causing duplicate injections; modify start() to first check for an existing script element (e.g., using document.querySelector for script[src="https://context7.com/widget.js"] or script[data-library="/codebeltnet/xunit"]) and return early if found, otherwise proceed to create and append the new script with the same attributes (data-library, data-color, data-position, data-placeholder, async) so the widget is only injected once even if start() is invoked multiple times.AGENTS.md (1)
188-193: Duplicate benchmark attribute guidanceLines 188–189 and 191–193 repeat the same
[MemoryDiagnoser],[GroupBenchmarksBy],[GlobalSetup], and[Params]recommendations. The second block addsBenchmarkLogicalGroupRule.ByCategoryand extra guidance, but the duplication is confusing. Consider merging into a single list with the fuller detail.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@AGENTS.md` around lines 188 - 193, The document repeats benchmark attribute guidance twice; merge the two blocks into one consolidated bullet list that includes the fuller details: keep a single entry for [MemoryDiagnoser], a single entry for [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)], and unified guidance for [GlobalSetup] (use for initialization and expensive prep) and [Params] (use for input variations, deterministic data, avoid external systems); remove the duplicate lines so the attributes [MemoryDiagnoser], [GroupBenchmarksBy(...)], [GlobalSetup], and [Params] appear once with the complete guidance..github/scripts/bump-nuget.py (1)
67-69: Broadexcept Exceptionswallows unexpected errors silently
except Exceptioncatches everything includingKeyError(if the NuGet API response has an unexpected schema),json.JSONDecodeError, and network-level errors equally, losing diagnostic information. Be more specific:♻️ Proposed fix
- except Exception as e: - print(f" SKIP {pkg}: {e}", file=sys.stderr) - return None + except (urllib.error.URLError, json.JSONDecodeError, KeyError) as e: + print(f" SKIP {pkg}: {type(e).__name__}: {e}", file=sys.stderr) + return None🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/scripts/bump-nuget.py around lines 67 - 69, The broad "except Exception as e" in bump-nuget.py hides important failures; replace it with specific handlers (e.g., except requests.exceptions.RequestException as e, except json.JSONDecodeError as e, except KeyError as e) that print meaningful error messages for those known cases and only catch expected errors, and add a final bare "except Exception" that re-raises (or logs full traceback using traceback.format_exc()) so truly unexpected errors aren't silently swallowed; target the block handling "pkg" where the current except resides.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.editorconfig:
- Around line 105-108: Update the misleading comment rule IDs to match the
suppressed rules: replace occurrences of "IDE0039" with "IDE0300" in the comment
that precedes the IDE0300 suppression (the block that currently says "Use
collection expression for array") and replace "IDE0031" with "IDE0301" in the
comment that precedes the IDE0301 suppression (the block that currently says
"Use collection expression for empty"); ensure the comment text and IDs align
with the dotnet_diagnostic entries (IDE0300 and IDE0301) so the comments
accurately describe the suppressed rules.
- Around line 183-187: Remove the redundant duplicate suppression block for
IDE0036: find the repeated "[*.{cs,vb}]" section that contains
"dotnet_diagnostic.IDE0036.severity = none" (the second "Order modifiers" block)
and delete it so only a single suppression entry for IDE0036 remains in the
.editorconfig.
In @.github/workflows/service-update.yml:
- Around line 43-51: The step that computes CURRENT/NEW/BRANCH must guard
against an empty or non-matching CURRENT (from grep) to avoid creating malformed
versions like "..1"; update the shell logic in the "Determine new version for
this repo" step to validate CURRENT (e.g., test it against a regex like
'^[0-9]+\.[0-9]+\.[0-9]+$') and if it fails set a safe default such as "0.0.0"
(or directly set NEW="0.0.1"), then compute NEW from that validated CURRENT and
build BRANCH="v${NEW}/service-update" so the branch name is always well-formed;
ensure the variables CURRENT, NEW and BRANCH are the ones used and exported to
GITHUB_OUTPUT as before.
- Around line 96-122: The shell compound that updates files uses the LATEST
variable and a short-circuiting conditional ([ -n "$LATEST" ] && sed ... && echo
...) which returns exit code 1 when LATEST is empty and fails the step under set
-e; update both steps named "Bump test runner Docker tag" and "Bump NGINX Alpine
version" to make the skip explicit by either replacing the short-circuit with an
if ... fi or appending a no-op fallback (e.g., add "|| true" to the end of the
compound) so that when LATEST is empty the step succeeds instead of failing;
locate the lines referencing the LATEST variable and the sed && echo sequence
and apply the change in both places.
- Around line 38-41: This step dangerously interpolates user-controlled
github.event.client_payload.source_repo and source_version directly into the run
block; move those values into the job/step env (e.g., set SOURCE and VERSION via
env: using the ${{ ... }} expressions) and then inside the run block read them
from the environment with properly quoted shell expansion (referencing SOURCE
and VERSION), then write them to GITHUB_OUTPUT; update the lines that currently
set SOURCE and VERSION and the echo "source=..." / echo "version=..." usages to
use the env-provided variables to avoid shell evaluation of client_payload
content.
In @.github/workflows/trigger-downstream.yml:
- Around line 72-73: The current loop calls urllib.request.urlopen(req) which
raises urllib.error.HTTPError on 4xx/5xx and aborts the whole dispatch; wrap
each per-repo dispatch (the block calling urllib.request.urlopen(req) and the
print f'✓ Dispatched to {repo}: HTTP {r.status}') in a try/except that catches
urllib.error.HTTPError and Exception, log the error (including repo and error
details) and continue the loop so remaining repos are attempted, and ensure sys
is imported at the top of the heredoc as requested.
---
Nitpick comments:
In @.docfx/templates/modern/public/main.js:
- Around line 2-11: The start() function currently appends the widget <script>
every time it's called, causing duplicate injections; modify start() to first
check for an existing script element (e.g., using document.querySelector for
script[src="https://context7.com/widget.js"] or
script[data-library="/codebeltnet/xunit"]) and return early if found, otherwise
proceed to create and append the new script with the same attributes
(data-library, data-color, data-position, data-placeholder, async) so the widget
is only injected once even if start() is invoked multiple times.
In @.github/scripts/bump-nuget.py:
- Around line 67-69: The broad "except Exception as e" in bump-nuget.py hides
important failures; replace it with specific handlers (e.g., except
requests.exceptions.RequestException as e, except json.JSONDecodeError as e,
except KeyError as e) that print meaningful error messages for those known cases
and only catch expected errors, and add a final bare "except Exception" that
re-raises (or logs full traceback using traceback.format_exc()) so truly
unexpected errors aren't silently swallowed; target the block handling "pkg"
where the current except resides.
In `@AGENTS.md`:
- Around line 188-193: The document repeats benchmark attribute guidance twice;
merge the two blocks into one consolidated bullet list that includes the fuller
details: keep a single entry for [MemoryDiagnoser], a single entry for
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)], and unified guidance
for [GlobalSetup] (use for initialization and expensive prep) and [Params] (use
for input variations, deterministic data, avoid external systems); remove the
duplicate lines so the attributes [MemoryDiagnoser], [GroupBenchmarksBy(...)],
[GlobalSetup], and [Params] appear once with the complete guidance.
| # IDE0039: Use collection expression for array | ||
| # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0300 | ||
| [*.{cs,vb}] | ||
| dotnet_diagnostic.IDE0300.severity = none |
There was a problem hiding this comment.
Misleading rule IDs in comments — "IDE0039" and "IDE0031" should be "IDE0300" and "IDE0301"
Both comment lines reference old, unrelated rule IDs:
- Line 105:
# IDE0039: Use collection expression for array— IDE0039 is "Use local function"; the suppressed rule and linked URL are for IDE0300. - Line 110:
# IDE0031: Use collection expression for empty— IDE0031 is "Use null propagation"; the suppressed rule and linked URL are for IDE0301.
📝 Proposed fix
-# IDE0039: Use collection expression for array
-# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0300
+# IDE0300: Use collection expression for array
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0300
[*.{cs,vb}]
dotnet_diagnostic.IDE0300.severity = none
-# IDE0031: Use collection expression for empty
-# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0301
+# IDE0301: Use collection expression for empty
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0301
[*.{cs,vb}]
dotnet_diagnostic.IDE0301.severity = noneAlso applies to: 110-113
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.editorconfig around lines 105 - 108, Update the misleading comment rule IDs
to match the suppressed rules: replace occurrences of "IDE0039" with "IDE0300"
in the comment that precedes the IDE0300 suppression (the block that currently
says "Use collection expression for array") and replace "IDE0031" with "IDE0301"
in the comment that precedes the IDE0301 suppression (the block that currently
says "Use collection expression for empty"); ensure the comment text and IDs
align with the dotnet_diagnostic entries (IDE0300 and IDE0301) so the comments
accurately describe the suppressed rules.
| - name: Determine new version for this repo | ||
| id: newver | ||
| run: | | ||
| CURRENT=$(grep -oP '(?<=## \[)[\d.]+(?=\])' CHANGELOG.md | head -1) | ||
| NEW=$(echo "$CURRENT" | awk -F. '{printf "%s.%s.%d", $1, $2, $3+1}') | ||
| BRANCH="v${NEW}/service-update" | ||
| echo "current=$CURRENT" >> $GITHUB_OUTPUT | ||
| echo "new=$NEW" >> $GITHUB_OUTPUT | ||
| echo "branch=$BRANCH" >> $GITHUB_OUTPUT |
There was a problem hiding this comment.
Malformed version and branch name if CHANGELOG has no ## [x.y.z] entry
If CHANGELOG.md is missing, empty, or doesn't yet contain a ## [x.y.z] header (e.g., first run on a fresh repo), CURRENT will be an empty string. The awk pipeline then produces ..1 as the version, and the branch name becomes v..1/service-update. This will likely cause git checkout -b to fail or produce an unusable PR, silently breaking the entire workflow.
Add a guard:
🛡️ Proposed fix
CURRENT=$(grep -oP '(?<=## \[)[\d.]+(?=\])' CHANGELOG.md | head -1)
+ if [ -z "$CURRENT" ]; then
+ echo "::error::Could not determine current version from CHANGELOG.md"
+ exit 1
+ fi
NEW=$(echo "$CURRENT" | awk -F. '{printf "%s.%s.%d", $1, $2, $3+1}')📝 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.
| - name: Determine new version for this repo | |
| id: newver | |
| run: | | |
| CURRENT=$(grep -oP '(?<=## \[)[\d.]+(?=\])' CHANGELOG.md | head -1) | |
| NEW=$(echo "$CURRENT" | awk -F. '{printf "%s.%s.%d", $1, $2, $3+1}') | |
| BRANCH="v${NEW}/service-update" | |
| echo "current=$CURRENT" >> $GITHUB_OUTPUT | |
| echo "new=$NEW" >> $GITHUB_OUTPUT | |
| echo "branch=$BRANCH" >> $GITHUB_OUTPUT | |
| - name: Determine new version for this repo | |
| id: newver | |
| run: | | |
| CURRENT=$(grep -oP '(?<=## \[)[\d.]+(?=\])' CHANGELOG.md | head -1) | |
| if [ -z "$CURRENT" ]; then | |
| echo "::error::Could not determine current version from CHANGELOG.md" | |
| exit 1 | |
| fi | |
| NEW=$(echo "$CURRENT" | awk -F. '{printf "%s.%s.%d", $1, $2, $3+1}') | |
| BRANCH="v${NEW}/service-update" | |
| echo "current=$CURRENT" >> $GITHUB_OUTPUT | |
| echo "new=$NEW" >> $GITHUB_OUTPUT | |
| echo "branch=$BRANCH" >> $GITHUB_OUTPUT |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/service-update.yml around lines 43 - 51, The step that
computes CURRENT/NEW/BRANCH must guard against an empty or non-matching CURRENT
(from grep) to avoid creating malformed versions like "..1"; update the shell
logic in the "Determine new version for this repo" step to validate CURRENT
(e.g., test it against a regex like '^[0-9]+\.[0-9]+\.[0-9]+$') and if it fails
set a safe default such as "0.0.0" (or directly set NEW="0.0.1"), then compute
NEW from that validated CURRENT and build BRANCH="v${NEW}/service-update" so the
branch name is always well-formed; ensure the variables CURRENT, NEW and BRANCH
are the ones used and exported to GITHUB_OUTPUT as before.
| - name: Bump test runner Docker tag | ||
| run: | | ||
| [ -f testenvironments.json ] || exit 0 | ||
| LATEST=$(curl -sf "https://hub.docker.com/v2/repositories/codebeltnet/ubuntu-testrunner/tags/?page_size=10&ordering=last_updated" \ | ||
| | python3 -c " | ||
| import sys, json | ||
| tags = [t['name'] for t in json.load(sys.stdin)['results'] if 'latest' not in t['name']] | ||
| print(tags[0] if tags else '') | ||
| ") | ||
| [ -n "$LATEST" ] && \ | ||
| sed -i "s|codebeltnet/ubuntu-testrunner:[^\"]*|codebeltnet/ubuntu-testrunner:${LATEST}|g" \ | ||
| testenvironments.json && echo "Bumped to $LATEST" | ||
|
|
||
| - name: Bump NGINX Alpine version | ||
| run: | | ||
| [ -f .docfx/Dockerfile.docfx ] || exit 0 | ||
| LATEST=$(curl -sf "https://hub.docker.com/v2/repositories/library/nginx/tags/?page_size=50&name=alpine&ordering=last_updated" \ | ||
| | python3 -c " | ||
| import sys, json, re | ||
| data = json.load(sys.stdin) | ||
| tags = [t['name'] for t in data['results'] if re.match(r'^\d+\.\d+\.\d+-alpine$', t['name'])] | ||
| if tags: | ||
| print(sorted(tags, key=lambda v: list(map(int, v.replace('-alpine','').split('.'))))[-1]) | ||
| ") | ||
| [ -n "$LATEST" ] && \ | ||
| sed -i "s|NGINX_VERSION=.*|NGINX_VERSION=${LATEST}|g" \ | ||
| .docfx/Dockerfile.docfx && echo "Bumped NGINX to $LATEST" |
There was a problem hiding this comment.
Steps fail with exit code 1 when LATEST is empty, under GitHub Actions' default set -eo pipefail shell
GitHub Actions runs run: blocks with /bin/bash --noprofile --norc -eo pipefail. When LATEST is empty (Docker Hub API unavailable, rate-limited, or returns no matching tags), the compound expression [ -n "$LATEST" ] && sed ... && echo "..." short-circuits after [ -n "$LATEST" ] exits 1. The overall compound command exits 1, which under set -e fails the step — even though the intent is clearly "skip if nothing to update."
This affects both the Docker test runner step (lines 105–107) and the NGINX Alpine step (lines 120–122).
🐛 Proposed fix — append `|| true` to make the skip explicit
[ -n "$LATEST" ] && \
sed -i "s|codebeltnet/ubuntu-testrunner:[^\"]*|codebeltnet/ubuntu-testrunner:${LATEST}|g" \
- testenvironments.json && echo "Bumped to $LATEST"
+ testenvironments.json && echo "Bumped to $LATEST" || true [ -n "$LATEST" ] && \
sed -i "s|NGINX_VERSION=.*|NGINX_VERSION=${LATEST}|g" \
- .docfx/Dockerfile.docfx && echo "Bumped NGINX to $LATEST"
+ .docfx/Dockerfile.docfx && echo "Bumped NGINX to $LATEST" || true📝 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.
| - name: Bump test runner Docker tag | |
| run: | | |
| [ -f testenvironments.json ] || exit 0 | |
| LATEST=$(curl -sf "https://hub.docker.com/v2/repositories/codebeltnet/ubuntu-testrunner/tags/?page_size=10&ordering=last_updated" \ | |
| | python3 -c " | |
| import sys, json | |
| tags = [t['name'] for t in json.load(sys.stdin)['results'] if 'latest' not in t['name']] | |
| print(tags[0] if tags else '') | |
| ") | |
| [ -n "$LATEST" ] && \ | |
| sed -i "s|codebeltnet/ubuntu-testrunner:[^\"]*|codebeltnet/ubuntu-testrunner:${LATEST}|g" \ | |
| testenvironments.json && echo "Bumped to $LATEST" | |
| - name: Bump NGINX Alpine version | |
| run: | | |
| [ -f .docfx/Dockerfile.docfx ] || exit 0 | |
| LATEST=$(curl -sf "https://hub.docker.com/v2/repositories/library/nginx/tags/?page_size=50&name=alpine&ordering=last_updated" \ | |
| | python3 -c " | |
| import sys, json, re | |
| data = json.load(sys.stdin) | |
| tags = [t['name'] for t in data['results'] if re.match(r'^\d+\.\d+\.\d+-alpine$', t['name'])] | |
| if tags: | |
| print(sorted(tags, key=lambda v: list(map(int, v.replace('-alpine','').split('.'))))[-1]) | |
| ") | |
| [ -n "$LATEST" ] && \ | |
| sed -i "s|NGINX_VERSION=.*|NGINX_VERSION=${LATEST}|g" \ | |
| .docfx/Dockerfile.docfx && echo "Bumped NGINX to $LATEST" | |
| - name: Bump test runner Docker tag | |
| run: | | |
| [ -f testenvironments.json ] || exit 0 | |
| LATEST=$(curl -sf "https://hub.docker.com/v2/repositories/codebeltnet/ubuntu-testrunner/tags/?page_size=10&ordering=last_updated" \ | |
| | python3 -c " | |
| import sys, json | |
| tags = [t['name'] for t in json.load(sys.stdin)['results'] if 'latest' not in t['name']] | |
| print(tags[0] if tags else '') | |
| ") | |
| [ -n "$LATEST" ] && \ | |
| sed -i "s|codebeltnet/ubuntu-testrunner:[^\"]*|codebeltnet/ubuntu-testrunner:${LATEST}|g" \ | |
| testenvironments.json && echo "Bumped to $LATEST" || true | |
| - name: Bump NGINX Alpine version | |
| run: | | |
| [ -f .docfx/Dockerfile.docfx ] || exit 0 | |
| LATEST=$(curl -sf "https://hub.docker.com/v2/repositories/library/nginx/tags/?page_size=50&name=alpine&ordering=last_updated" \ | |
| | python3 -c " | |
| import sys, json, re | |
| data = json.load(sys.stdin) | |
| tags = [t['name'] for t in data['results'] if re.match(r'^\d+\.\d+\.\d+-alpine$', t['name'])] | |
| if tags: | |
| print(sorted(tags, key=lambda v: list(map(int, v.replace('-alpine','').split('.'))))[-1]) | |
| ") | |
| [ -n "$LATEST" ] && \ | |
| sed -i "s|NGINX_VERSION=.*|NGINX_VERSION=${LATEST}|g" \ | |
| .docfx/Dockerfile.docfx && echo "Bumped NGINX to $LATEST" || true |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/service-update.yml around lines 96 - 122, The shell
compound that updates files uses the LATEST variable and a short-circuiting
conditional ([ -n "$LATEST" ] && sed ... && echo ...) which returns exit code 1
when LATEST is empty and fails the step under set -e; update both steps named
"Bump test runner Docker tag" and "Bump NGINX Alpine version" to make the skip
explicit by either replacing the short-circuit with an if ... fi or appending a
no-op fallback (e.g., add "|| true" to the end of the compound) so that when
LATEST is empty the step succeeds instead of failing; locate the lines
referencing the LATEST variable and the sed && echo sequence and apply the
change in both places.
| with urllib.request.urlopen(req) as r: | ||
| print(f'✓ Dispatched to {repo}: HTTP {r.status}') |
There was a problem hiding this comment.
urlopen raises on HTTP errors — single failure aborts all remaining dispatches
urllib.request.urlopen raises urllib.error.HTTPError for any 4xx/5xx response (e.g., 404 for a repo that doesn't exist yet, 403 if app permissions are missing on a target, 422 for a bad payload). Because the exception propagates out of the for loop, any subsequent repos in the list never receive their dispatch event.
Wrap each dispatch in a try/except so failures are logged but the loop continues:
🛡️ Proposed fix
- with urllib.request.urlopen(req) as r:
- print(f'✓ Dispatched to {repo}: HTTP {r.status}')
+ try:
+ with urllib.request.urlopen(req) as r:
+ print(f'✓ Dispatched to {repo}: HTTP {r.status}')
+ except urllib.error.HTTPError as e:
+ print(f'✗ Failed to dispatch to {repo}: HTTP {e.code} {e.reason}', file=sys.stderr)
+ except urllib.error.URLError as e:
+ print(f'✗ Failed to dispatch to {repo}: {e.reason}', file=sys.stderr)Also import sys at the top of the heredoc:
- import json, urllib.request, os, sys
+ import json, urllib.request, urllib.error, os, sys📝 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.
| with urllib.request.urlopen(req) as r: | |
| print(f'✓ Dispatched to {repo}: HTTP {r.status}') | |
| try: | |
| with urllib.request.urlopen(req) as r: | |
| print(f'✓ Dispatched to {repo}: HTTP {r.status}') | |
| except urllib.error.HTTPError as e: | |
| print(f'✗ Failed to dispatch to {repo}: HTTP {e.code} {e.reason}', file=sys.stderr) | |
| except urllib.error.URLError as e: | |
| print(f'✗ Failed to dispatch to {repo}: {e.reason}', file=sys.stderr) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/trigger-downstream.yml around lines 72 - 73, The current
loop calls urllib.request.urlopen(req) which raises urllib.error.HTTPError on
4xx/5xx and aborts the whole dispatch; wrap each per-repo dispatch (the block
calling urllib.request.urlopen(req) and the print f'✓ Dispatched to {repo}: HTTP
{r.status}') in a try/except that catches urllib.error.HTTPError and Exception,
log the error (including repo and error details) and continue the loop so
remaining repos are attempted, and ensure sys is imported at the top of the
heredoc as requested.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|



This pull request introduces a robust automation system for managing dependency updates and downstream notifications across repositories. The main focus is on adding GitHub Actions workflows and supporting scripts to enable automatic service updates when dependencies are released, as well as propagating those updates to downstream repositories. Additionally, it includes some configuration and documentation improvements.
Key changes:
GitHub Actions Workflows and Automation
.github/workflows/service-update.ymlto automate dependency/service updates, including bumping NuGet packages, updating release notes, changelog, Docker tags, and opening a pull request with all changes. This workflow is triggered by repository dispatch events or manual triggers and supports dry runs..github/workflows/trigger-downstream.ymlto automatically dispatch service update events to downstream repositories when a new release is published, ensuring dependent repos stay up-to-date..github/scripts/bump-nuget.py, a Python script that bumps NuGet package versions inDirectory.Packages.propsby either using a triggered version or querying the NuGet API for the latest stable version..github/dispatch-targets.jsonto define the list of downstream repositories that should receive update notifications.Development and Documentation Configuration
.editorconfigfile to standardize code formatting and analyzer rules across the codebase, with detailed comments and rationale for each rule..docfx/docfx.jsonto include a custom template path, likely to support further documentation customization.main.jsscript under.docfx/templates/modern/public/to inject a support widget into documentation pages, enhancing user support experience.Summary by CodeRabbit
Documentation
Chores