Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .docfx/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
],
"globalMetadata": {
"_appTitle": "Extensions for BenchmarkDotNet by Codebelt",
"_appFooter": "<span>Generated by <strong>DocFX</strong>. Copyright 2025-2026 Geekle. All rights reserved.</span>",
"_appFooter": "<span>Generated by <strong>DocFX</strong>. Copyright 2025-2026 Geekle. All rights reserved.</span><script src=\"https://context7.com/widget.js\" data-library=\"/codebeltnet/benchmarkdotnet\"></script>",
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

Loading an external JavaScript file from context7.com in the documentation footer introduces a third-party dependency that could have security and availability implications. If the external domain is compromised or becomes unavailable, it could affect documentation functionality or expose users to malicious scripts. Consider: 1) adding Subresource Integrity (SRI) hashes if supported by DocFX, 2) documenting the purpose and trust level of this widget, or 3) hosting the script locally if feasible.

Suggested change
"_appFooter": "<span>Generated by <strong>DocFX</strong>. Copyright 2025-2026 Geekle. All rights reserved.</span><script src=\"https://context7.com/widget.js\" data-library=\"/codebeltnet/benchmarkdotnet\"></script>",
"_appFooter": "<span>Generated by <strong>DocFX</strong>. Copyright 2025-2026 Geekle. All rights reserved.</span>",

Copilot uses AI. Check for mistakes.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Third-party script loaded without SRI or a compensating CSP — supply-chain risk.

Subresource Integrity (SRI) is a security feature that enables browsers to verify that resources they fetch are delivered without unexpected manipulation; it works by providing a cryptographic hash that the fetched resource must match. However, SRI works well when importing an external script that's versioned and won't change, but when using JS as an external software service that isn't hosted locally and shouldn't be pinned to one version, SRI cannot be relied upon. The Context7 widget is exactly such a service.

Without SRI, websites that load JavaScript from external sources are vulnerable to supply chain attacks — if the third-party resource is compromised — and CDN hijacking, where attackers may inject malicious code into hosted libraries.

For a continuously-updating service widget like this, a Content Security Policy (CSP) — a whitelist of sites allowed to provide content — is the appropriate alternative: if pulling in a service from a given domain, you can restrict scripts to just that domain. DocFX-generated sites are typically static, so a CSP can be enforced via a hosting-layer response header (e.g., GitHub Pages _headers, Nginx add_header, etc.):

Content-Security-Policy: script-src 'self' https://context7.com;

Until a CSP is in place, every documentation visitor's browser is entirely reliant on context7.com's security posture.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.docfx/docfx.json at line 50, The _appFooter entry currently injects an
external script tag for the Context7 widget ("<script
src=\"https://context7.com/widget.js\"
data-library=\"/codebeltnet/benchmarkdotnet\"></script>") which lacks SRI and
therefore exposes supply-chain risk; remove the inline external script from the
"_appFooter" value in .docfx/docfx.json and instead enforce a hosting-layer
Content-Security-Policy that whitelists the widget domain (e.g., add a response
header like Content-Security-Policy: script-src 'self' https://context7.com;) or
serve the widget via a vetted proxy, and update any deployment docs to include
the CSP configuration so the widget can run without embedding third-party script
tags directly in "_appFooter".

"_appLogoPath": "images/50x50.png",
"_appFaviconPath": "images/favicon.ico",
"_googleAnalyticsTagId": "G-K2NG2TXDWQ",
Expand Down
1 change: 1 addition & 0 deletions .github/dispatch-targets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[ ]
139 changes: 139 additions & 0 deletions .github/workflows/service-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
name: Service Update

on:
repository_dispatch:
types: [codebelt-service-update]
workflow_dispatch:
inputs:
source_repo:
description: 'Triggering source repo name (e.g. cuemon)'
required: false
default: ''
source_version:
description: 'Version released by source (e.g. 10.3.0)'
required: false
default: ''
dry_run:
type: boolean
description: 'Dry run — show changes but do not commit or open PR'
default: false

permissions:
contents: write
pull-requests: write

jobs:
service-update:
runs-on: ubuntu-24.04

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Resolve trigger inputs
id: trigger
run: |
SOURCE="${{ github.event.client_payload.source_repo || github.event.inputs.source_repo }}"
VERSION="${{ github.event.client_payload.source_version || github.event.inputs.source_version }}"
echo "source=$SOURCE" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
Comment on lines +35 to +41
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Script injection via ${{ }} interpolation of untrusted client_payload.

github.event.client_payload.* is attacker-controlled (anyone with repo scope can send a repository_dispatch). Interpolating it directly into a run: block via ${{ }} allows arbitrary command execution. The same tainted values are re-interpolated at lines 109–112 via step outputs.

Pass all untrusted values through environment variables.

Proposed fix for lines 35–41
      - name: Resolve trigger inputs
        id: trigger
        run: |
-          SOURCE="${{ github.event.client_payload.source_repo || github.event.inputs.source_repo }}"
-          VERSION="${{ github.event.client_payload.source_version || github.event.inputs.source_version }}"
+          SOURCE="${PAYLOAD_SOURCE:-$INPUT_SOURCE}"
+          VERSION="${PAYLOAD_VERSION:-$INPUT_VERSION}"
           echo "source=$SOURCE"   >> $GITHUB_OUTPUT
           echo "version=$VERSION" >> $GITHUB_OUTPUT
+        env:
+          PAYLOAD_SOURCE:  ${{ github.event.client_payload.source_repo }}
+          PAYLOAD_VERSION: ${{ github.event.client_payload.source_version }}
+          INPUT_SOURCE:    ${{ github.event.inputs.source_repo }}
+          INPUT_VERSION:   ${{ github.event.inputs.source_version }}

The same pattern must be applied at lines 109–112 where step outputs are interpolated back into a run: block:

Proposed fix for lines 108–112
         env:
           GH_TOKEN: ${{ steps.app-token.outputs.token }}
+          NEW:      ${{ steps.newver.outputs.new }}
+          BRANCH:   ${{ steps.newver.outputs.branch }}
+          SOURCE:   ${{ steps.trigger.outputs.source }}
+          SRC_VER:  ${{ steps.trigger.outputs.version }}
         run: |
-          NEW="${{ steps.newver.outputs.new }}"
-          BRANCH="${{ steps.newver.outputs.branch }}"
-          SOURCE="${{ steps.trigger.outputs.source }}"
-          SRC_VER="${{ steps.trigger.outputs.version }}"
📝 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
- name: Resolve trigger inputs
id: trigger
run: |
SOURCE="${{ github.event.client_payload.source_repo || github.event.inputs.source_repo }}"
VERSION="${{ github.event.client_payload.source_version || github.event.inputs.source_version }}"
echo "source=$SOURCE" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Resolve trigger inputs
id: trigger
run: |
SOURCE="${PAYLOAD_SOURCE:-$INPUT_SOURCE}"
VERSION="${PAYLOAD_VERSION:-$INPUT_VERSION}"
echo "source=$SOURCE" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
env:
PAYLOAD_SOURCE: ${{ github.event.client_payload.source_repo }}
PAYLOAD_VERSION: ${{ github.event.client_payload.source_version }}
INPUT_SOURCE: ${{ github.event.inputs.source_repo }}
INPUT_VERSION: ${{ github.event.inputs.source_version }}
🤖 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 35 - 41, The run block in
the "Resolve trigger inputs" step (id: trigger) is interpolating untrusted
github.event.client_payload values directly into the run script via ${{ }},
enabling script injection; change it to assign those values to environment
variables (use env: SOURCE: ${{ github.event.client_payload.source_repo ||
github.event.inputs.source_repo }} and VERSION: ...) and then reference the safe
shell variables ($SOURCE, $VERSION) inside the run to write to GITHUB_OUTPUT;
apply the same pattern where you later invoke step outputs (the consumer run
blocks that use steps.trigger.outputs.* around lines 109–112) by passing step
outputs into the run via env: entries and reading them from shell env variables
rather than re-interpolating with ${{ }} inside the run.


- 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
Comment on lines +43 to +51
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Version extraction fails silently with a confusing error if CHANGELOG.md has no version entries.

With pipefail (GitHub Actions default), grep returning no match causes the step to fail with a non-obvious exit code. Consider adding a guard:

Proposed improvement
       - name: Determine new version for this repo
         id: newver
         run: |
           CURRENT=$(grep -oP '(?<=## \[)[\d.]+(?=\])' CHANGELOG.md | head -1)
+          if [ -z "$CURRENT" ]; then
+            echo "::error::No version entry found in CHANGELOG.md"
+            exit 1
+          fi
           NEW=$(echo "$CURRENT" | awk -F. '{printf "%s.%s.%d", $1, $2, $3+1}')
🤖 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 with id
newver uses grep into CURRENT and will fail silently when CHANGELOG.md has no
version entries; update the newver step to guard the grep result (the CURRENT
variable) by checking if it is empty after the grep command, and if so either
set a sensible default (e.g., "0.0.0") or print a clear error and exit non-zero
with a helpful message; ensure the subsequent NEW and BRANCH calculations use
the validated CURRENT value and keep the same variable names (CURRENT, NEW,
BRANCH) so downstream echoing to GITHUB_OUTPUT remains unchanged.


- name: Generate codebelt-aicia token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ vars.CODEBELT_AICIA_APP_ID }}
private-key: ${{ secrets.CODEBELT_AICIA_PRIVATE_KEY }}
owner: codebeltnet

- name: Bump NuGet packages
run: python3 .github/scripts/bump-nuget.py
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The script .github/scripts/bump-nuget.py is referenced but does not exist in the repository. This will cause the workflow to fail when the "Bump NuGet packages" step executes. The script needs to be created and committed, or this step should be removed/modified if the functionality is not yet implemented.

Suggested change
run: python3 .github/scripts/bump-nuget.py
run: |
echo "Skipping NuGet package bump: bump-nuget.py script is not available in this repository."
echo "TRIGGER_SOURCE=${TRIGGER_SOURCE}"
echo "TRIGGER_VERSION=${TRIGGER_VERSION}"

Copilot uses AI. Check for mistakes.
env:
TRIGGER_SOURCE: ${{ steps.trigger.outputs.source }}
TRIGGER_VERSION: ${{ steps.trigger.outputs.version }}
Comment on lines +61 to +65
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

fd -t f 'bump-nuget.py'

Repository: codebeltnet/benchmarkdotnet

Length of output: 53


.github/scripts/bump-nuget.py does not exist in the repository.

The workflow step at line 62 references this script, but it is missing. This step will fail at runtime. Either add the missing script or remove the step if it's not needed.

🤖 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 61 - 65, The workflow step
named "Bump NuGet packages" references a missing script
".github/scripts/bump-nuget.py"; either add that script to the repository
(implement the bump logic, make it executable, and commit it to
.github/scripts/) or remove/update the workflow step to call an existing
script/action instead; ensure the workflow's run value points to the correct
script name and path (bump-nuget.py) or replace it with the intended action so
the step no longer fails at runtime.


- name: Update PackageReleaseNotes.txt
run: |
NEW="${{ steps.newver.outputs.new }}"
for f in .nuget/*/PackageReleaseNotes.txt; do
[ -f "$f" ] || continue
TFM=$(grep -m1 "^Availability:" "$f" | sed 's/Availability: //' || echo ".NET 10, .NET 9 and .NET Standard 2.0")
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The PackageReleaseNotes.txt update logic extracts the TFM (Target Framework Moniker) from the existing file or defaults to ".NET 10, .NET 9 and .NET Standard 2.0". However, the actual projects in this repository target only ".NET 10 and .NET 9" according to the existing PackageReleaseNotes.txt files. The fallback default includes ".NET Standard 2.0" which is incorrect for this project. Update the default to match the actual target frameworks: ".NET 10 and .NET 9".

Suggested change
TFM=$(grep -m1 "^Availability:" "$f" | sed 's/Availability: //' || echo ".NET 10, .NET 9 and .NET Standard 2.0")
TFM=$(grep -m1 "^Availability:" "$f" | sed 's/Availability: //' || echo ".NET 10 and .NET 9")

Copilot uses AI. Check for mistakes.
ENTRY="Version ${NEW}\nAvailability: ${TFM}\n \n# ALM\n- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)\n \n"
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The version entry format in PackageReleaseNotes.txt uses "Version ${NEW}" (Version 1.2.3) but the existing file format shows "Version: 1.2.2" with a colon. This inconsistency will break the established format. The ENTRY variable should use "Version: ${NEW}\n" to match the existing convention.

Suggested change
ENTRY="Version ${NEW}\nAvailability: ${TFM}\n \n# ALM\n- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)\n \n"
ENTRY="Version: ${NEW}\nAvailability: ${TFM}\n \n# ALM\n- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)\n \n"

Copilot uses AI. Check for mistakes.
{ printf "$ENTRY"; cat "$f"; } > "$f.tmp" && mv "$f.tmp" "$f"
done

- name: Update CHANGELOG.md
run: |
python3 - <<'EOF'
import os, re
from datetime import date
new_ver = os.environ['NEW_VERSION']
today = date.today().isoformat()
entry = f"## [{new_ver}] - {today}\n\nThis is a service update that focuses on package dependencies.\n\n"
with open("CHANGELOG.md") as f:
content = f.read()
idx = content.find("## [")
content = (content[:idx] + entry + content[idx:]) if idx != -1 else (content + entry)
with open("CHANGELOG.md", "w") as f:
f.write(content)
print(f"CHANGELOG updated for v{new_ver}")
EOF
env:
NEW_VERSION: ${{ steps.newver.outputs.new }}

# Note: Docker image bumps removed in favor of manual updates
# The automated selection was picking wrong variants (e.g., mono-* instead of standard)
# TODO: Move to hosted service for smarter image selection

- name: Show diff (dry run)
if: ${{ github.event.inputs.dry_run == 'true' }}
run: git diff

- name: Create branch and open PR
if: ${{ github.event.inputs.dry_run != 'true' }}
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
NEW="${{ steps.newver.outputs.new }}"
BRANCH="${{ steps.newver.outputs.branch }}"
SOURCE="${{ steps.trigger.outputs.source }}"
SRC_VER="${{ steps.trigger.outputs.version }}"

git config user.name "codebelt-aicia[bot]"
git config user.email "codebelt-aicia[bot]@users.noreply.github.com"
git checkout -b "$BRANCH"
git add -A
git diff --cached --quiet && echo "Nothing changed - skipping PR." && exit 0
git commit -m "V${NEW}/service update"
git push origin "$BRANCH"

echo "This is a service update that focuses on package dependencies." > pr_body.txt
echo "" >> pr_body.txt
echo "Automated changes:" >> pr_body.txt
echo "- Codebelt/Cuemon package versions bumped to latest compatible" >> pr_body.txt
echo "- PackageReleaseNotes.txt updated for v${NEW}" >> pr_body.txt
echo "- CHANGELOG.md entry added for v${NEW}" >> pr_body.txt
echo "" >> pr_body.txt
echo "Note: Third-party packages (Microsoft.Extensions.*, BenchmarkDotNet, etc.) are not auto-updated." >> pr_body.txt
echo "Use Dependabot or manual updates for those." >> pr_body.txt
echo "" >> pr_body.txt
echo "Generated by codebelt-aicia" >> pr_body.txt
if [ -n "$SOURCE" ] && [ -n "$SRC_VER" ]; then
echo "Triggered by: ${SOURCE} @ ${SRC_VER}" >> pr_body.txt
else
echo "Triggered by: manual workflow dispatch" >> pr_body.txt
fi

gh pr create --title "V${NEW}/service update" --body-file pr_body.txt --base main --head "$BRANCH" --assignee gimlichael
78 changes: 78 additions & 0 deletions .github/workflows/trigger-downstream.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Trigger Downstream Service Updates

on:
release:
types: [published]

jobs:
dispatch:
if: github.event.release.prerelease == false
runs-on: ubuntu-24.04
permissions:
contents: read

steps:
- name: Checkout (to read dispatch-targets.json)
uses: actions/checkout@v4

- name: Check for dispatch targets
id: check
run: |
if [ ! -f .github/dispatch-targets.json ]; then
echo "No dispatch-targets.json found, skipping."
echo "has_targets=false" >> $GITHUB_OUTPUT
exit 0
fi
COUNT=$(python3 -c "import json; print(len(json.load(open('.github/dispatch-targets.json'))))")
echo "has_targets=$([ $COUNT -gt 0 ] && echo true || echo false)" >> $GITHUB_OUTPUT

- name: Extract version from release tag
if: steps.check.outputs.has_targets == 'true'
id: version
run: |
VERSION="${{ github.event.release.tag_name }}"
VERSION="${VERSION#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
Comment on lines +32 to +35
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Potential script injection via ${{ }} interpolation in run block.

github.event.release.tag_name is interpolated directly into the shell script. Although exploitability requires repo write access, this is a recognized GitHub Actions security anti-pattern — a specially crafted tag name could break out of the string and execute arbitrary commands.

Use an environment variable instead to avoid shell interpretation of the expression value.

Proposed fix
      - name: Extract version from release tag
        if: steps.check.outputs.has_targets == 'true'
        id: version
        run: |
-          VERSION="${{ github.event.release.tag_name }}"
+          VERSION="$TAG_NAME"
           VERSION="${VERSION#v}"
           echo "version=$VERSION" >> $GITHUB_OUTPUT
+        env:
+          TAG_NAME: ${{ github.event.release.tag_name }}
📝 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
run: |
VERSION="${{ github.event.release.tag_name }}"
VERSION="${VERSION#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Extract version from release tag
if: steps.check.outputs.has_targets == 'true'
id: version
run: |
VERSION="$TAG_NAME"
VERSION="${VERSION#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
env:
TAG_NAME: ${{ github.event.release.tag_name }}
🤖 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 32 - 35, The run block
currently interpolates ${{ github.event.release.tag_name }} directly into the
shell (assigning to VERSION), which risks script injection; instead expose the
tag via an environment variable and reference that env var inside the shell to
avoid GitHub expression expansion in the script. Change the step to set an env
(e.g., TAG: ${{ github.event.release.tag_name }}) and inside the run use the
shell variable (e.g., VERSION="${TAG#v}") and then echo "version=$VERSION" >>
$GITHUB_OUTPUT; also ensure the assignment and echo are quoted to avoid
word-splitting.


- name: Generate codebelt-aicia token
if: steps.check.outputs.has_targets == 'true'
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ vars.CODEBELT_AICIA_APP_ID }}
private-key: ${{ secrets.CODEBELT_AICIA_PRIVATE_KEY }}
owner: codebeltnet
Comment on lines +37 to +44
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The workflow only requests 'contents: read' permissions, but the generated GitHub App token is used to trigger repository_dispatch events on downstream repositories. The GitHub App itself needs to have the appropriate permissions (contents and/or metadata write access) on the target repositories. Ensure the codebelt-aicia app is installed with sufficient permissions on all downstream repositories, or this workflow will fail silently or with authentication errors.

Copilot uses AI. Check for mistakes.

- name: Dispatch to downstream repos
if: steps.check.outputs.has_targets == 'true'
run: |
python3 - <<'EOF'
import json, urllib.request, os, sys

targets = json.load(open('.github/dispatch-targets.json'))
token = os.environ['GH_TOKEN']
version = os.environ['VERSION']
source = os.environ['SOURCE_REPO']

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}')
Comment on lines +49 to +73
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The inline Python script lacks error handling. If any HTTP request to a downstream repository fails (due to network issues, authentication problems, or invalid repository names), the entire workflow will fail with an unclear error. Consider wrapping the urlopen call in a try-except block to catch and log errors gracefully, and decide whether to continue with remaining repositories or fail the workflow.

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +73
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Unhandled HTTP errors will abort dispatch to remaining repos.

urllib.request.urlopen raises urllib.error.HTTPError on non-2xx responses. If any single repo dispatch fails (e.g., repo deleted, permissions issue), the exception propagates and all subsequent repos in the list are skipped.

Wrap the request in a try/except so one failure doesn't block the rest, and surface failures at the end.

Proposed fix
+          errors = []
           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}')
+              try:
+                  with urllib.request.urlopen(req) as r:
+                      print(f'✓ Dispatched to {repo}: HTTP {r.status}')
+              except Exception as e:
+                  print(f'✗ Failed to dispatch to {repo}: {e}', file=sys.stderr)
+                  errors.append(repo)
+
+          if errors:
+              sys.exit(f'Failed to dispatch to: {", ".join(errors)}')
🤖 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 57 - 73, The loop that
calls urllib.request.urlopen inside "for repo in targets" can raise
urllib.error.HTTPError and abort remaining dispatches; update the block around
the Request/urllib.request.urlopen call to wrap it in a try/except (catch
urllib.error.HTTPError and generic Exception), log or record the failure
(including repo, status/code and error message) and continue the loop so
remaining repos are dispatched, and after the loop surface a summary of failures
(e.g., raise a combined exception or print a failure summary) so callers know
which repos failed; reference the existing variables and symbols url, req,
urllib.request.urlopen, and the loop "for repo in targets" when making the
change.

EOF
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
VERSION: ${{ steps.version.outputs.version }}
SOURCE_REPO: ${{ github.event.repository.name }}
Loading