Fix/apply migrations in cd - #6
Conversation
No JsonStringEnumConverter is registered, so /api/calls/{id} returns
"status": 2 rather than "Completed". Comparing against the names matched
nothing, so every job would have been counted as never reaching a terminal
state and the run would have reported a false loss of all 50.
Also forces TLS 1.2 (5.1 negotiates 1.0, which Azure refuses) and silences the
per-request progress bar that dominates a multi-megabyte upload loop.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UnusAvj1QtPwNnd28udLm
The deployed database has no schema. Program.cs only calls Database.Migrate() inside if (app.Environment.IsDevelopment()), and cd.yml had no migration step, so InitialCreate and SeedRubrics have never been applied to Azure SQL. Every write to the deployed API returns 500 with "Invalid object name 'Calls'". Applies the schema from cd.yml, after the Bicep deploy that creates the server and before any traffic shift: - sql.bicep grants a new contained user (cognilens-cicd) db_ddladmin, created by SID like the existing ones so no Graph lookup is needed. Azure SQL allows exactly one AAD admin and that is the deploy MI, so the pipeline principal cannot be an admin too. Not db_owner: the pipeline has no business granting permissions or dropping users. The Api and Worker identities keep db_datareader/db_datawriter only and still cannot touch the schema. - cd.yml generates an idempotent script and applies it with go-sqlcmd, opening the SQL firewall for the runner IP and closing it again in an always() step. Rejected running Migrate() at app startup: it needs DDL rights on the runtime identity, races across replicas, and turns a bad migration into a crash-loop rather than a failed deploy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019UnusAvj1QtPwNnd28udLm
📝 WalkthroughWalkthroughThe deployment workflow now provisions a CI/CD SQL identity and runs EF migrations with temporary firewall access. Bicep wiring supports the identity, and the load-shed test normalizes API call status values during polling. ChangesDeployment SQL migration flow
Load-shed status handling
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant GitHubRunner
participant BicepDeployment
participant AzureSQL
GitHubRunner->>BicepDeployment: Deploy infrastructure with cicdPrincipalClientId
BicepDeployment->>AzureSQL: Create cognilens-cicd contained user
GitHubRunner->>AzureSQL: Open runner firewall rule
GitHubRunner->>AzureSQL: Apply idempotent EF migrations with sqlcmd
GitHubRunner->>AzureSQL: Remove runner firewall rule
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Azure what-if (rg-cognilens-dev)Show plan |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/cd.yml:
- Around line 142-182: Add a bounded retry loop between the firewall-rule
creation step and the migration execution in the “Apply EF migrations” workflow,
using the same authenticated SQL connection parameters as the final sqlcmd call
to verify access before running migrate.sql. Wait between attempts, fail after
the timeout with a clear error, and preserve the existing migration command once
connectivity succeeds.
- Around line 129-136: Update the “Resolve SQL connection details” step to
capture the api.ipify.org response, validate that it is exactly one IPv4 address
with no extra lines or content, and only then append RUNNER_IP to GITHUB_ENV;
fail the step on invalid output while preserving the existing SQL_FQDN and
SQL_SERVER_NAME exports.
In `@infra/modules/sql.bicep`:
- Around line 163-178: Update the bootstrap SQL generation around the
CICD_CLIENT_ID handling so cognilens-cicd reflects desired state: when the value
is empty, remove the existing user and its db_ddladmin, db_datareader, and
db_datawriter memberships; when provided, compare the existing principal SID
with the requested client ID and drop/recreate the user if they differ before
assigning the roles. Preserve creation and role assignment when the user is
absent or already has the matching SID.
In `@scripts/loadshed-test.ps1`:
- Around line 46-72: Save scripts/loadshed-test.ps1 as UTF-8 with a BOM,
preserving the existing script content and non-ASCII characters so
PSUseBOMForUnicodeEncodedFile passes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: dd9faef3-31d7-4084-91ad-5664c3a525f8
📒 Files selected for processing (5)
.github/workflows/cd.yml.gitignoreinfra/main.bicepinfra/modules/sql.bicepscripts/loadshed-test.ps1
| - name: Resolve SQL connection details | ||
| run: | | ||
| set -euo pipefail | ||
| SQL_FQDN=$(az sql server list --resource-group "${{ env.RESOURCE_GROUP }}" \ | ||
| --query "[0].fullyQualifiedDomainName" -o tsv) | ||
| echo "SQL_FQDN=$SQL_FQDN" >> "$GITHUB_ENV" | ||
| echo "SQL_SERVER_NAME=${SQL_FQDN%%.*}" >> "$GITHUB_ENV" | ||
| echo "RUNNER_IP=$(curl -sf https://api.ipify.org)" >> "$GITHUB_ENV" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## tracked workflow"
git ls-files .github/workflows/cd.yml || true
echo
echo "## relevant workflow section"
if [ -f .github/workflows/cd.yml ]; then
nl -ba .github/workflows/cd.yml | sed -n '100,160p'
fi
echo
echo "## all GITHUB_ENV writes in workflow"
if [ -f .github/workflows/cd.yml ]; then
rg -n 'GITHUB_ENV|to_json|RUNNER_IP|curl -sf https://api.ipify|ipify' .github/workflows/cd.yml
fi
echo
echo "## behavioral probe: multi-line API response would append multiple lines to env-like assignment"
python3 - <<'PY'
payloads = [
"1.2.3.4\n",
"1.2.3.4\r\n",
"1.2.3.4\nOTHER=value\n",
]
for p in payloads:
lines = p.splitlines()
print(f"payload={payloads.index(p)+1}: lines={lines}")
print("append result if interpreted as echo \"KEY=${lines[0]}\" >> GITHUB_ENV:")
for line in lines:
if '=' in line:
print(f" env file line: {line}")
print()
PYRepository: Affan2900/cognilens
Length of output: 273
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## workflow exists"
[ -f .github/workflows/cd.yml ] && echo yes || echo no
echo
echo "## relevant workflow section"
python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/cd.yml')
lines = p.read_text().splitlines()
for i in range(100, min(len(lines), 165)):
print(f"{i+1:5d}: {lines[i]}")
PY
echo
echo "## GITHUB_ENV / ipify usages"
python3 - <<'PY'
import re
from pathlib import Path
lines = Path('.github/workflows/cd.yml').read_text().splitlines()
for i, line in enumerate(lines, 1):
if 'GITHUB_ENV' in line or 'ipify' in line or 'curl' in line or 'RUNNER_IP' in line:
print(f"{i:5d}: {line}")
PY
echo
echo "## behavioral probe"
python3 - <<'PY'
payloads = [
"1.2.3.4\n",
"1.2.3.4\r\n",
"1.2.3.4\nOTHER=value\n",
]
for payload in payloads:
print("payload repr:", repr(payload))
key = payload.splitlines()[0]
print("current echo assignment:")
print(f'RUNNER_IP={key}')
print("implicit env-file append lines:")
for line in payload.splitlines():
print(repr(line))
print()
PYRepository: Affan2900/cognilens
Length of output: 5583
Validate RUNNER_IP before appending it to GITHUB_ENV.
External output from api.ipify.org is embedded in the echoed assignment, so a multi-line response can append extra lines to the generated environment file and affect later steps. Parse the response as exactly one IPv4 address before writing it.
Proposed fix
- echo "RUNNER_IP=$(curl -sf https://api.ipify.org)" >> "$GITHUB_ENV"
+ RUNNER_IP="$(curl -fsS https://api.ipify.org)"
+ python3 -c 'import ipaddress, sys; ipaddress.IPv4Address(sys.argv[1])' "$RUNNER_IP"
+ printf 'RUNNER_IP=%s\n' "$RUNNER_IP" >> "$GITHUB_ENV"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Resolve SQL connection details | |
| run: | | |
| set -euo pipefail | |
| SQL_FQDN=$(az sql server list --resource-group "${{ env.RESOURCE_GROUP }}" \ | |
| --query "[0].fullyQualifiedDomainName" -o tsv) | |
| echo "SQL_FQDN=$SQL_FQDN" >> "$GITHUB_ENV" | |
| echo "SQL_SERVER_NAME=${SQL_FQDN%%.*}" >> "$GITHUB_ENV" | |
| echo "RUNNER_IP=$(curl -sf https://api.ipify.org)" >> "$GITHUB_ENV" | |
| - name: Resolve SQL connection details | |
| run: | | |
| set -euo pipefail | |
| SQL_FQDN=$(az sql server list --resource-group "${{ env.RESOURCE_GROUP }}" \ | |
| --query "[0].fullyQualifiedDomainName" -o tsv) | |
| echo "SQL_FQDN=$SQL_FQDN" >> "$GITHUB_ENV" | |
| echo "SQL_SERVER_NAME=${SQL_FQDN%%.*}" >> "$GITHUB_ENV" | |
| RUNNER_IP="$(curl -fsS https://api.ipify.org)" | |
| python3 -c 'import ipaddress, sys; ipaddress.IPv4Address(sys.argv[1])' "$RUNNER_IP" | |
| printf 'RUNNER_IP=%s\n' "$RUNNER_IP" >> "$GITHUB_ENV" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/cd.yml around lines 129 - 136, Update the “Resolve SQL
connection details” step to capture the api.ipify.org response, validate that it
is exactly one IPv4 address with no extra lines or content, and only then append
RUNNER_IP to GITHUB_ENV; fail the step on invalid output while preserving the
existing SQL_FQDN and SQL_SERVER_NAME exports.
Source: Linters/SAST tools
| - name: Open the SQL firewall for this runner | ||
| run: | | ||
| set -euo pipefail | ||
| az sql server firewall-rule create \ | ||
| --resource-group "${{ env.RESOURCE_GROUP }}" \ | ||
| --server "${{ env.SQL_SERVER_NAME }}" \ | ||
| --name "gh-runner-${{ github.run_id }}" \ | ||
| --start-ip-address "${{ env.RUNNER_IP }}" \ | ||
| --end-ip-address "${{ env.RUNNER_IP }}" >/dev/null | ||
| echo "Opened SQL firewall for ${{ env.RUNNER_IP }}" | ||
|
|
||
| - name: Apply EF migrations | ||
| run: | | ||
| set -euo pipefail | ||
| # Pinned to the EF Core version in CogniLens.Infrastructure.csproj. A floating version | ||
| # here would let a tooling release change what a deploy does without a commit. | ||
| dotnet tool install --global dotnet-ef --version 10.0.10 >/dev/null | ||
| export PATH="$PATH:$HOME/.dotnet/tools" | ||
|
|
||
| # --idempotent wraps every migration in an "if not already applied" guard, so this is | ||
| # safe to run on every deploy and safe to re-run after a partial failure. | ||
| # Infrastructure is both project and startup project: it declares | ||
| # Microsoft.EntityFrameworkCore.Design with PrivateAssets, so the package does not flow | ||
| # to CogniLens.Api and the tools reject the Api as a startup project. CogniLensDbContextFactory | ||
| # is the design-time factory that makes the class library usable on its own. | ||
| dotnet ef migrations script --idempotent \ | ||
| --project src/CogniLens.Infrastructure \ | ||
| --startup-project src/CogniLens.Infrastructure \ | ||
| --output migrate.sql | ||
|
|
||
| curl -sSL -o sqlcmd.tar.bz2 \ | ||
| https://github.com/microsoft/go-sqlcmd/releases/download/v1.10.0/sqlcmd-linux-amd64.tar.bz2 | ||
| tar -xjf sqlcmd.tar.bz2 | ||
| chmod +x ./sqlcmd | ||
|
|
||
| # ActiveDirectoryAzCli reuses the token from azure/login above — the same principal the | ||
| # bootstrap script granted db_ddladmin to. -b makes a failed batch exit non-zero; | ||
| # without it sqlcmd returns 0 on SQL errors and a broken migration reports success. | ||
| ./sqlcmd -S "tcp:${{ env.SQL_FQDN }},1433" -d CogniLens -l 30 -b \ | ||
| --authentication-method=ActiveDirectoryAzCli \ | ||
| -i migrate.sql |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== candidate workflow =="
if [ -f .github/workflows/cd.yml ]; then
nl -ba .github/workflows/cd.yml | sed -n '120,200p'
else
echo ".github/workflows/cd.yml not found"
fi
echo "== azure/login and sqlcmd / firewall context =="
rg -n "azure/login|Open the SQL firewall|Firewall|sqlcmd|migrations script|WAIT|sleep|az sql server firewall-rule" .github/workflows cd.yml 2>/dev/null || true
echo "== relevant docs/source references in repo =="
rg -n "firewall-rule|Azure SQL firewall|sqlcmd\)|--authentication-method=ActiveDirectoryAzCli|wait .* firewall|propagat" .github src 2>/dev/null || true
echo "== deterministic sequence check from workflow text =="
python3 - <<'PY'
from pathlib import Path
p=Path('.github/workflows/cd.yml')
if not p.exists():
print("missing workflow")
raise SystemExit
text=p.read_text()
terms=["Open the SQL firewall for this runner","az sql server firewall-rule create","dotnet ef migrations script","./sqlcmd"]
pos=[text.find(t) for t in terms]
for t,p_ in zip(terms,pos):
print(f"{t}: {p_}")
print("firewall_before_ef:", pos[:2]<None and pos[1]>pos[0])
print("ef_before_sqlcmd:", 2 < pos[3] or None)
print("firewall_and_sqlcmd_present:", all(p_!=-1 for p_ in pos))
PY
echo "== docs lookup =="
web_search "Azure SQL server firewall rules maximum time to take effect allow traffic after firewall rule created"Repository: Affan2900/cognilens
Length of output: 222
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate workflow candidates =="
find . -path '.git' -prune -o -type f \( -name 'cd.yml' -o -name 'cd.yaml' \) -print | sort
echo "== inspect relevant workflow excerpt =="
if [ -f .github/workflows/cd.yml ]; then
awk 'NR>=120 && NR<=210 {printf "%4d %s\n", NR, $0}' .github/workflows/cd.yml
else
echo ".github/workflows/cd.yml not found"
fi
echo "== search for firewall/login/sqlcmd patterns =="
rg -n "azure/login|Open the SQL firewall|sql server firewall-rule|dotnet ef migrations script|sqlcmd|authentication-method=ActiveDirectoryAzCli|sleep|wait|retry" .github src 2>/dev/null || true
echo "== deterministic sequence check from workflow text =="
python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/cd.yml')
if not p.exists():
print("missing workflow")
raise SystemExit
text = p.read_text()
terms = [
"Step: Build CogniLens solution",
"az sql server firewall-rule create",
"dotnet ef migrations script",
"--authentication-method=ActiveDirectoryAzCli",
"./sqlcmd"
]
for term in terms:
idx = text.find(term)
print(f"{term!r}: {idx}")
PYRepository: Affan2900/cognilens
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== list tracked workflow files =="
git ls-files .github/workflows | sort
echo "== inspect .github/workflows/cd.yml excerpt near firewall/migration path =="
awk 'NR>=135 && NR<=190 {printf "%4d %s\n", NR, $0}' .github/workflows/cd.yml
echo "== exact target markers in workflow =="
python3 - <<'PY'
from pathlib import Path
p=Path('.github/workflows/cd.yml')
text=p.read_text()
for term in [
"AZURE_CREDENTIALS",
"azure/login",
"Open the SQL firewall",
"az sql server firewall-rule create",
"dotnet ef migrations script",
"--authentication-method=ActiveDirectoryAzCli",
"./sqlcmd"
]:
print(f"{term!r}: {text.find(term)}")
PY
echo "== source references to firewall propagation handling =="
rg -n "firewall|firewall-rule|propaga|sqlcmd|dotnet ef migrations script|migrations script|ActiveDirectoryAzCli" .github/workflows src package.json package-lock.json -g '!node_modules' 2>/dev/null | head -200Repository: Affan2900/cognilens
Length of output: 5430
🌐 Web query:
Azure SQL database firewall firewall rules propagation time after creating rule to take effect
💡 Result:
In Azure SQL Database, changes to firewall rules can take up to 5 minutes to propagate and take effect [1][2][3]. This latency occurs because server-level firewall rules are temporarily cached at the database level to improve performance [1][4][5]. The specific behavior and latency depend on the database configuration: - Server Authentication Model: Changes have a 5-minute latency for all databases [1][3]. - Contained Database (with a failover partner): Changes are instantaneous on the primary database, but the secondary database may take up to 5 minutes to reflect the updates [1][3]. - Contained Database (no failover partner): Changes are instantaneous [1][3]. If you need a firewall rule to take effect immediately, you can manually refresh the authentication cache by executing the following command in the target database: DBCC FLUSHAUTHCACHE [1][4][3] This command forces the database to flush its local cache and immediately reload the latest firewall rules [1][5].
Citations:
- 1: https://learn.microsoft.com/en-us/azure/azure-sql/database/firewall-configure?view=azuresql
- 2: https://learn.microsoft.com/en-us/azure/azure-sql/database/network-access-controls-overview?view=azuresql
- 3: https://github.com/MicrosoftDocs/sql-docs/blob/live/azure-sql/database/firewall-configure.md
- 4: https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-set-firewall-rule-azure-sql-database?view=azuresqldb-current
- 5: https://docs.azure.cn/en-us/azure-sql/database/firewall-configure
Add a firewall propagation wait before migrating.
az sql server firewall-rule create returns before the database-level authorization cache is refreshed, and Azure SQL firewall changes can take up to 5 minutes to take effect. Running ./sqlcmd can return immediately after the rule create, causing intermittent migration failures; add a retry/waits for a successful authenticated sqlcmd or another lightweight connection check before applying migrate.sql.
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 147-147: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[warning] 149-149: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[warning] 149-149: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[warning] 150-150: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[warning] 180-180: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/cd.yml around lines 142 - 182, Add a bounded retry loop
between the firewall-rule creation step and the migration execution in the
“Apply EF migrations” workflow, using the same authenticated SQL connection
parameters as the final sqlcmd call to verify access before running migrate.sql.
Wait between attempts, fail after the timeout with a clear error, and preserve
the existing migration command once connectivity succeeds.
| if [ -n "$CICD_CLIENT_ID" ]; then | ||
| cat <<SQL >> bootstrap.sql | ||
| DECLARE @cicdUser sysname = N'cognilens-cicd'; | ||
| DECLARE @cicdSid varbinary(16) = CONVERT(varbinary(16), CAST(N'$CICD_CLIENT_ID' AS uniqueidentifier)); | ||
| DECLARE @cicdCmd nvarchar(max); | ||
|
|
||
| IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = @cicdUser) | ||
| BEGIN | ||
| SET @cicdCmd = N'CREATE USER [' + @cicdUser + N'] WITH SID = 0x' + CONVERT(varchar(100), @cicdSid, 2) + N', TYPE = E;'; | ||
| EXEC(@cicdCmd); | ||
| END | ||
| EXEC('ALTER ROLE db_ddladmin ADD MEMBER [' + @cicdUser + ']'); | ||
| EXEC('ALTER ROLE db_datareader ADD MEMBER [' + @cicdUser + ']'); | ||
| EXEC('ALTER ROLE db_datawriter ADD MEMBER [' + @cicdUser + ']'); | ||
| SQL | ||
| fi |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
rg -n -C4 'CICD_CLIENT_ID|cognilens-cicd|CREATE USER|ALTER USER|DROP USER' infra/modules/sql.bicepRepository: Affan2900/cognilens
Length of output: 3778
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
wc -l infra/modules/sql.bicep
sed -n '1,240p' infra/modules/sql.bicepRepository: Affan2900/cognilens
Length of output: 9150
Treat the CI/CD user as desired state.
When cicdPrincipalClientId is empty, the script skips the existing cognilens-cicd user, leaving it and its db_ddladmin/data roles in place. When it changes, the new SID is assigned to roles by name, but the existing row keeps the old SID. Add targeted cleanup for an empty/disable case and update/recreate the SID when the provided value differs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infra/modules/sql.bicep` around lines 163 - 178, Update the bootstrap SQL
generation around the CICD_CLIENT_ID handling so cognilens-cicd reflects desired
state: when the value is empty, remove the existing user and its db_ddladmin,
db_datareader, and db_datawriter memberships; when provided, compare the
existing principal SID with the requested client ID and drop/recreate the user
if they differ before assigning the roles. Preserve creation and role assignment
when the user is absent or already has the matching SID.
| # Windows PowerShell 5.1 negotiates TLS 1.0 by default, which Azure Storage and Container Apps | ||
| # both refuse — without this the first upload fails with an unhelpful "connection closed". | ||
| [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 | ||
| # Invoke-WebRequest renders a progress bar per call in 5.1 and it dominates the runtime of a | ||
| # multi-megabyte upload loop. | ||
| $ProgressPreference = 'SilentlyContinue' | ||
|
|
||
| if (-not (Test-Path $AudioPath)) { throw "Audio file not found: $AudioPath" } | ||
| $audioBytes = [System.IO.File]::ReadAllBytes($AudioPath) | ||
| $audioName = Split-Path $AudioPath -Leaf | ||
| Write-Host "Audio: $audioName ($([math]::Round($audioBytes.Length / 1MB, 2)) MB), $Count jobs" -ForegroundColor Cyan | ||
|
|
||
| # CallStatus has no JsonStringEnumConverter registered, so the API serialises it as an ordinal: | ||
| # Pending=0, Processing=1, Completed=2, Failed=3. Comparing against the names directly matches | ||
| # nothing and reports every job as lost. Both forms are accepted so this keeps working if a | ||
| # string converter is added later. | ||
| function ConvertTo-CallStatusName($value) { | ||
| if ($value -is [string]) { return $value } | ||
| switch ([int]$value) { | ||
| 0 { 'Pending' } | ||
| 1 { 'Processing' } | ||
| 2 { 'Completed' } | ||
| 3 { 'Failed' } | ||
| default { "Unknown($value)" } | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Save the script with a UTF-8 BOM.
PSScriptAnalyzer reports PSUseBOMForUnicodeEncodedFile. Since this Windows PowerShell script contains non-ASCII characters, add a UTF-8 BOM to ensure consistent parsing and avoid CI analyzer failures.
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)
[warning] Missing BOM encoding for non-ASCII encoded file 'loadshed-test.ps1'
(PSUseBOMForUnicodeEncodedFile)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/loadshed-test.ps1` around lines 46 - 72, Save
scripts/loadshed-test.ps1 as UTF-8 with a BOM, preserving the existing script
content and non-ASCII characters so PSUseBOMForUnicodeEncodedFile passes.
Source: Linters/SAST tools
Summary by CodeRabbit
New Features
Bug Fixes
Chores