Skip to content

Comments

Docfx/context7 chat#8

Merged
gimlichael merged 2 commits intomainfrom
docfx/context7-chat
Feb 20, 2026
Merged

Docfx/context7 chat#8
gimlichael merged 2 commits intomainfrom
docfx/context7-chat

Conversation

@gimlichael
Copy link
Member

@gimlichael gimlichael commented Feb 20, 2026

This pull request introduces new automation workflows for service updates and downstream repository triggers, as well as minor configuration changes. The main focus is on improving dependency management and automating updates across related repositories.

GitHub Actions automation:

  • Added .github/workflows/service-update.yml to automate service updates, including package version bumps, changelog updates, and PR creation, triggered by repository dispatch or manual workflow dispatch.
  • Added .github/workflows/trigger-downstream.yml to automatically dispatch service update events to downstream repositories after a release, based on .github/dispatch-targets.json.
  • Added an empty .github/dispatch-targets.json file as a placeholder for downstream repository targets.

Documentation configuration:

  • Updated .docfx/docfx.json to include an external widget script in the documentation footer for enhanced functionality.

Summary by CodeRabbit

  • Chores
    • Enhanced documentation footer to support external widget integration
    • Added automation for service dependency updates and release note management
    • Implemented automatic downstream service update notifications triggered on release publication

@gimlichael gimlichael self-assigned this Feb 20, 2026
Copilot AI review requested due to automatic review settings February 20, 2026 14:25
@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2026

📝 Walkthrough

Walkthrough

Added external widget script to DocFX footer metadata. Introduced two new GitHub Actions workflows for automated service version management and downstream repository triggering, alongside a dispatch configuration file listing target repositories.

Changes

Cohort / File(s) Summary
DocFX Metadata
.docfx/docfx.json
Extended global footer metadata by appending an external widget script tag while preserving existing footer HTML.
GitHub Dispatch Configuration
.github/dispatch-targets.json
New configuration file containing an empty JSON array for specifying downstream repositories to trigger.
Service Update Workflow
.github/workflows/service-update.yml
New workflow that automates service updates: derives version from CHANGELOG, updates NuGet packages via Python script, modifies release notes and CHANGELOG entries, and creates PRs with automated change summaries; includes dry-run mode and conditional branch/PR operations.
Downstream Trigger Workflow
.github/workflows/trigger-downstream.yml
New workflow triggered on release publications that reads dispatch targets, extracts version info, generates GitHub App token, and dispatches repository_dispatch events to downstream repositories via GitHub API.

Sequence Diagram(s)

sequenceDiagram
    participant GitHub as GitHub Release Event
    participant Workflow as Trigger Downstream Workflow
    participant API as GitHub API
    participant Downstream as Downstream Repositories
    
    GitHub->>Workflow: release published (non-prerelease)
    Workflow->>Workflow: check dispatch-targets.json exists
    Workflow->>Workflow: extract version from tag (strip v prefix)
    Workflow->>Workflow: generate GitHub App token
    Workflow->>API: POST repository_dispatch events
    API->>Downstream: trigger codebelt-service-update with source_version & source_repo
    Downstream-->>API: dispatch acknowledged
    API-->>Workflow: success response per repository
Loading
sequenceDiagram
    participant Event as Repository Dispatch Event
    participant Workflow as Service Update Workflow
    participant Python as Python Script
    participant Files as File System
    participant GitAPI as GitHub API
    
    Event->>Workflow: repository_dispatch (codebelt-service-update)
    Workflow->>Workflow: resolve trigger inputs (source_repo, source_version)
    Workflow->>Workflow: derive new version from CHANGELOG
    Workflow->>Python: bump NuGet packages with env vars
    Python->>Files: update packages.config
    Workflow->>Files: prepend versioned header to PackageReleaseNotes.txt
    Workflow->>Files: insert new section in CHANGELOG.md
    alt dry_run mode
        Workflow->>Workflow: show git diff
    else real mode
        Workflow->>GitAPI: create branch & commit changes
        GitAPI->>Workflow: branch created
        Workflow->>GitAPI: push changes & create PR
        GitAPI-->>Workflow: PR created with assignment
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • V1.2.2/service update #7: Modifies the same .docfx/docfx.json global footer metadata (_appFooter) to update displayed year in footer content.

Poem

🐰 Two workflows hop into action now,
Service updates flow, I'll show you how!
Versions bump, changelogs glow bright,
Downstream repos triggered with all their might! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The PR title 'Docfx/context7 chat' is vague and does not clearly convey the main changes. It references 'context7' and 'chat' but the PR primarily adds GitHub Actions workflows for service updates and downstream triggers, with only a minor documentation footer update. Consider a more descriptive title that captures the main changes, such as 'Add service update and downstream trigger workflows' or 'Add GitHub Actions workflows for automated service updates and releases'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch docfx/context7-chat

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.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request introduces automated workflows for managing service updates and downstream repository notifications in the Codebelt.Extensions.BenchmarkDotNet project. The automation aims to streamline dependency updates across the Codebelt ecosystem by triggering cascading updates when releases are published, and adds external documentation enhancements.

Changes:

  • Added workflow automation for service updates including package bumping, changelog updates, and automated PR creation via repository dispatch or manual triggers
  • Added workflow to automatically notify downstream repositories of new releases through GitHub API dispatch events
  • Integrated context7.com widget into DocFX documentation footer for enhanced functionality

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
.github/workflows/service-update.yml Automates service updates with version bumping, changelog/release notes updates, and PR creation when triggered by upstream releases or manually
.github/workflows/trigger-downstream.yml Dispatches service update events to downstream repositories after non-prerelease publications using repository dispatch API
.github/dispatch-targets.json Empty JSON array placeholder for defining downstream repository targets
.docfx/docfx.json Added external widget script from context7.com to documentation footer
Comments suppressed due to low confidence (5)

.github/dispatch-targets.json:1

  • The JSON array has a space between the brackets "[ ]" instead of being empty "[]". While this is valid JSON (it's an array containing nothing), it's unconventional and could be misleading. For clarity and consistency, use "[]" for an empty array.
[ ]

.github/workflows/service-update.yml:46

  • The version extraction pattern uses a Perl-compatible regex with grep -oP, which may not be available on all systems (macOS uses BSD grep by default, which doesn't support -P). Since the workflow runs on ubuntu-24.04, this is not immediately problematic, but if portability is a concern, consider using a more portable solution like awk or sed, or explicitly document the ubuntu dependency.
          CURRENT=$(grep -oP '(?<=## \[)[\d.]+(?=\])' CHANGELOG.md | head -1)

.github/workflows/service-update.yml:119

  • The step generates and commits changes without verifying whether any NuGet package bumps were actually performed. If the missing bump-nuget.py script is fixed but doesn't make any changes (e.g., all packages are already up-to-date), the workflow will still create a PR with only CHANGELOG and PackageReleaseNotes updates. Consider checking whether bump-nuget.py actually modified any files before proceeding with PR creation, or document this as intended behavior.
      - 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"

.github/workflows/trigger-downstream.yml:50

  • The inline Python script has an unused import 'sys'. While this doesn't cause functional issues, it's unnecessary and should be removed for code cleanliness.
          import json, urllib.request, os, sys

.github/workflows/service-update.yml:92

  • The CHANGELOG.md update script uses 'from datetime import date' but this import is placed inside the inline script's code block. While functional, the import statement should come before any usage. The current structure is correct for inline Python in shell scripts, but consider documenting this pattern or moving to a separate script file for better maintainability and testability.
      - 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

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

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

🧹 Nitpick comments (2)
.docfx/docfx.json (1)

50-50: Add async (or defer) to prevent render-blocking.

The <script> tag has neither async nor defer, making it a synchronous/render-blocking load. Since the widget is non-critical to page content, async is appropriate here.

⚡ Proposed fix
-"_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><script src=\"https://context7.com/widget.js\" data-library=\"/codebeltnet/benchmarkdotnet\" async></script>",
🤖 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 footer HTML string stored in the
"_appFooter" JSON value contains a blocking <script> tag for the widget; update
that string to include the async attribute on the script element (or defer if
you prefer) so the widget loads non-blocking. Locate the "_appFooter" entry and
modify the embedded "<script src=...>" tag to include async (ensuring proper
JSON string escaping) so the final string includes ...widget.js\" async ...> to
avoid render-blocking.
.github/workflows/service-update.yml (1)

73-74: printf "$ENTRY" treats % in TFM as format specifiers.

If the Availability: line in any PackageReleaseNotes.txt ever contains a % character, printf will misinterpret it. Use printf '%b' to interpret backslash escapes without treating % as a format directive.

Proposed fix
-            { printf "$ENTRY"; cat "$f"; } > "$f.tmp" && mv "$f.tmp" "$f"
+            { printf '%b' "$ENTRY"; cat "$f"; } > "$f.tmp" && mv "$f.tmp" "$f"
🤖 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 73 - 74, The printf call
that writes ENTRY to the temp file treats percent signs in the ENTRY/TFM value
as format directives; in the block that builds ENTRY and runs { printf "$ENTRY";
cat "$f"; } > "$f.tmp" && mv "$f.tmp" "$f", change the printf invocation to use
an explicit format string and pass ENTRY as the argument (for example using the
%b format) so percent signs in TFM are not interpreted as format specifiers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.docfx/docfx.json:
- 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".

In @.github/workflows/service-update.yml:
- Around line 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.
- Around line 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.
- Around line 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.

In @.github/workflows/trigger-downstream.yml:
- Around line 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.
- Around line 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.

---

Nitpick comments:
In @.docfx/docfx.json:
- Line 50: The footer HTML string stored in the "_appFooter" JSON value contains
a blocking <script> tag for the widget; update that string to include the async
attribute on the script element (or defer if you prefer) so the widget loads
non-blocking. Locate the "_appFooter" entry and modify the embedded "<script
src=...>" tag to include async (ensuring proper JSON string escaping) so the
final string includes ...widget.js\" async ...> to avoid render-blocking.

In @.github/workflows/service-update.yml:
- Around line 73-74: The printf call that writes ENTRY to the temp file treats
percent signs in the ENTRY/TFM value as format directives; in the block that
builds ENTRY and runs { printf "$ENTRY"; cat "$f"; } > "$f.tmp" && mv "$f.tmp"
"$f", change the printf invocation to use an explicit format string and pass
ENTRY as the argument (for example using the %b format) so percent signs in TFM
are not interpreted as format specifiers.

"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

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".

Comment on lines +35 to +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 }}"
echo "source=$SOURCE" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
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.

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

Comment on lines +61 to +65
- name: Bump NuGet packages
run: python3 .github/scripts/bump-nuget.py
env:
TRIGGER_SOURCE: ${{ steps.trigger.outputs.source }}
TRIGGER_VERSION: ${{ steps.trigger.outputs.version }}
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.

Comment on lines +32 to +35
run: |
VERSION="${{ github.event.release.tag_name }}"
VERSION="${VERSION#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
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.

Comment on lines +57 to +73
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}')
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.

@codecov
Copy link

codecov bot commented Feb 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.95%. Comparing base (b479df4) to head (0b17bf1).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main       #8   +/-   ##
=======================================
  Coverage   66.95%   66.95%           
=======================================
  Files           6        6           
  Lines         345      345           
  Branches       42       42           
=======================================
  Hits          231      231           
  Misses        114      114           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sonarqubecloud
Copy link

@gimlichael gimlichael merged commit 7340a0f into main Feb 20, 2026
27 checks passed
@gimlichael gimlichael deleted the docfx/context7-chat branch February 20, 2026 14:38
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