Skip to content

Fix self-hosted help-bot deploy reconciliation#16

Merged
frostebite merged 1 commit intomainfrom
codex/fix-help-bot-self-healing-deploy
Apr 2, 2026
Merged

Fix self-hosted help-bot deploy reconciliation#16
frostebite merged 1 commit intomainfrom
codex/fix-help-bot-self-healing-deploy

Conversation

@frostebite
Copy link
Copy Markdown
Member

@frostebite frostebite commented Apr 1, 2026

Summary

  • make the deploy workflow sync the live tree instead of only copying dist and config
  • repair and reconfigure the NSSM service on each deploy so drift is self-healed
  • stop the startup wrapper from doing git-based self-updates and limit it to local runtime repair

Testing

  • yarn build
  • powershell parser check for startup.ps1

Summary by CodeRabbit

  • Chores
    • Improved deployment synchronization strategy to ensure all application files are properly mirrored to the production environment.
    • Enhanced service management with automatic configuration and validation to ensure consistent behavior.
    • Optimized startup repair logic to conditionally rebuild application artifacts, reducing startup time when dependencies are already present.
    • Added robust verification checks to confirm the service is running correctly after deployment.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

Modified deployment workflow and startup script to implement live-tree synchronization during deployment, introduce conditional artifact repair logic during startup, and replace service restart mechanisms with NSSM service reconciliation and validation workflows.

Changes

Cohort / File(s) Summary
Deployment Workflow
.github/workflows/deploy.yml
Replaced simple file copy with robocopy-based live-tree synchronization; added repair install step with yarn install --frozen-lockfile and yarn build; introduced NSSM service reconciliation flow with conditional installation and configuration validation; updated verification to check NSSM configuration and service running state.
Startup Script
startup.ps1
Replaced git-pull and npm-based initialization with conditional "minimal repair" logic; added checks for dist\cli.js and node_modules presence; only executes yarn install --frozen-lockfile and yarn build when artifacts are missing.

Sequence Diagrams

sequenceDiagram
    actor CI as GitHub Actions
    participant Deploy as Deployment Target
    participant NSSM as NSSM Service
    participant Build as Build Process
    participant App as Application

    CI->>Deploy: Create deployment directories
    CI->>Deploy: robocopy src/ and dist/
    CI->>Deploy: Copy config files (package.json, etc.)
    
    CI->>Build: Run yarn install --frozen-lockfile
    CI->>Build: Run yarn build
    Build-->>Deploy: Generate artifacts
    
    alt NSSM service exists
        CI->>NSSM: Stop service
    else
        CI->>NSSM: Install service
    end
    
    CI->>NSSM: Configure (Application, AppParameters, AppDirectory, logs)
    CI->>NSSM: Set auto-start
    CI->>NSSM: Start service
    NSSM->>App: Launch startup.ps1
    App-->>Deploy: Verify artifacts
    App->>App: Start CLI application
Loading
sequenceDiagram
    participant NSSM as NSSM Service
    participant Startup as startup.ps1
    participant Check as Artifact Check
    participant Repair as Repair Install
    participant CLI as CLI Application

    NSSM->>Startup: Execute on service start
    Startup->>Check: Check dist\cli.js exists
    Startup->>Check: Check node_modules exists
    
    alt Artifacts missing
        Check-->>Repair: Trigger repair
        Repair->>Repair: yarn install --frozen-lockfile
        Repair->>Repair: yarn build
        Repair-->>Startup: Repair complete
    else Artifacts present
        Check-->>Startup: Skip install/build
    end
    
    Startup->>CLI: Invoke dist\cli.js
    CLI-->>NSSM: Application running
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A rabbit hops through deployment trees,
With robocopy winds and NSSM keys,
No more git pulls that fail in the night—
Just artifacts checking, repairs done right,
The service springs up, alive and bright! 🚀

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix self-hosted help-bot deploy reconciliation' directly and clearly summarizes the main change: improving the deployment workflow to use live-tree synchronization and NSSM service reconciliation for self-healing deployment.
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
  • Commit unit tests in branch codex/fix-help-bot-self-healing-deploy

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
Copy Markdown

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/deploy.yml:
- Around line 118-124: The Write-Error call is triggering the Stop preference
and preventing the subsequent Get-Content tail and exit from running; update the
failure path in the service check (the block that inspects $svc and its .Status
for the 'gameci-help-bot' service) so it logs the error without terminating
early (for example replace Write-Error with Write-Host/Write-Output or call
Write-Error with -ErrorAction SilentlyContinue), ensure you null-check $svc
before accessing $svc.Status, then run Get-Content
"C:\game-ci\help-bot\logs\service.log" -Tail 30 and finally exit 1 so the logs
are always emitted on failure.
- Around line 60-65: The PowerShell steps run native commands (yarn, nssm) that
don't auto-throw on non-zero exit; after each native invocation—specifically
after the yarn install and yarn build calls and after every nssm install, nssm
stop, nssm set and nssm get invocation—check $LASTEXITCODE and abort/throw
(e.g., throw or exit 1) if non-zero so the try/catch and fallback logic
triggers; update the block around Push-Location and the try/catch to add these
$LASTEXITCODE guards immediately after each command.

In `@startup.ps1`:
- Around line 24-37: The try/catch blocks around the yarn calls log success even
when yarn fails because PowerShell 5.1 doesn't throw on native command non-zero
exits; after each yarn invocation (the yarn install and yarn build calls around
the Log(...) and "Build: done" messages) check $LASTEXITCODE and if it's
non-zero, throw or call exit with a non-zero code so the catch path runs; update
the catch handlers to Log the error and terminate (e.g., exit 1) so failures
don't proceed as successes.
🪄 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

Run ID: 1034ba7d-22b5-4955-a4a4-68aea6a16ec4

📥 Commits

Reviewing files that changed from the base of the PR and between 6b02af8 and 850f0ad.

📒 Files selected for processing (2)
  • .github/workflows/deploy.yml
  • startup.ps1

Comment on lines +60 to +65
$ErrorActionPreference = 'Stop'
$deployRoot = 'C:\game-ci\help-bot'
Push-Location $deployRoot
try {
yarn install --frozen-lockfile
yarn build
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Windows PowerShell 5.1, do native executables like yarnandnssmthrow on non-zero exit codes when$ErrorActionPreference = 'Stop'is set, or must scripts check$LASTEXITCODE explicitly?

💡 Result:

In Windows PowerShell 5.1, native executables like yarn and nssm do not throw terminating errors on non-zero exit codes when $ErrorActionPreference = 'Stop' is set. Scripts must check $LASTEXITCODE explicitly (or use $?) after calling such executables to detect and handle failures. This behavior is consistent across multiple authoritative sources discussing PowerShell 5.1 error handling. $ErrorActionPreference = 'Stop' makes non-terminating errors from PowerShell cmdlets into terminating errors catchable by try/catch, but external native commands (executables) only set $LASTEXITCODE to the process exit code and $? to $false if non-zero; they do not generate PowerShell errors or respect ErrorActionPreference for exit codes. Official docs (about_Preference_Variables) demonstrate the preference working for PowerShell commands like Write-Error, but not external exes. GitHub issue #3996 confirms no terminating error from native non-zero exit even with 'Stop', requiring explicit $LASTEXITCODE checks. Stack Overflow answers (e.g., on call operator, bash set -e equivalent) explicitly state the need to check $LASTEXITCODE -ne 0 and throw manually. This changed in PowerShell 7.4+ with $PSNativeCommandUseErrorActionPreference = $true, but not in 5.1. Example: $ErrorActionPreference = 'Stop' yarn --version # Assume non-zero exit if ($LASTEXITCODE -ne 0) { throw "yarn failed with exit code $LASTEXITCODE" } Write-Host "Continues here only if success" Without the if check, execution continues despite failure.

Citations:


🏁 Script executed:

# Check if file exists and get its structure
ls -la .github/workflows/deploy.yml
wc -l .github/workflows/deploy.yml

Repository: game-ci/help-bot

Length of output: 170


🏁 Script executed:

# Read the specific line ranges mentioned in the review
echo "=== Lines 60-65 (yarn install/build) ==="
sed -n '55,70p' .github/workflows/deploy.yml

echo ""
echo "=== Lines 82-102 (NSSM calls) ==="
sed -n '78,105p' .github/workflows/deploy.yml

echo ""
echo "=== Lines 108-112 (nssm restart) ==="
sed -n '105,115p' .github/workflows/deploy.yml

echo ""
echo "=== Lines 118-124 (error handling) ==="
sed -n '115,130p' .github/workflows/deploy.yml

Repository: game-ci/help-bot

Length of output: 2924


Native command failures won't fail these steps or trigger the fallback.

These yarn and nssm invocations run under shell: powershell, so non-zero exit codes do not throw. That means the live-repair step can continue after a failed install/build, the NSSM reconciliation can partially fail silently, and the nssm restart catch block never runs.

Suggested pattern
       - name: Repair live install
         shell: powershell
         run: |
           $ErrorActionPreference = 'Stop'
           $deployRoot = 'C:\game-ci\help-bot'
           Push-Location $deployRoot
           try {
             yarn install --frozen-lockfile
+            if ($LASTEXITCODE -ne 0) { throw "yarn install failed with exit code $LASTEXITCODE" }
             yarn build
+            if ($LASTEXITCODE -ne 0) { throw "yarn build failed with exit code $LASTEXITCODE" }
           } finally {
             Pop-Location
           }
…
-          try {
-            nssm restart gameci-help-bot
-          } catch {
-            Write-Host "NSSM restart failed, trying service start: $_"
+          nssm restart gameci-help-bot
+          if ($LASTEXITCODE -ne 0) {
+            Write-Host "NSSM restart failed, trying service start"
             Start-Service -Name 'gameci-help-bot'
           }

Apply the same $LASTEXITCODE guard after each nssm install, nssm stop, nssm set, and nssm get call.

📝 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
$ErrorActionPreference = 'Stop'
$deployRoot = 'C:\game-ci\help-bot'
Push-Location $deployRoot
try {
yarn install --frozen-lockfile
yarn build
$ErrorActionPreference = 'Stop'
$deployRoot = 'C:\game-ci\help-bot'
Push-Location $deployRoot
try {
yarn install --frozen-lockfile
if ($LASTEXITCODE -ne 0) { throw "yarn install failed with exit code $LASTEXITCODE" }
yarn build
if ($LASTEXITCODE -ne 0) { throw "yarn build failed with exit code $LASTEXITCODE" }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/deploy.yml around lines 60 - 65, The PowerShell steps run
native commands (yarn, nssm) that don't auto-throw on non-zero exit; after each
native invocation—specifically after the yarn install and yarn build calls and
after every nssm install, nssm stop, nssm set and nssm get invocation—check
$LASTEXITCODE and abort/throw (e.g., throw or exit 1) if non-zero so the
try/catch and fallback logic triggers; update the block around Push-Location and
the try/catch to add these $LASTEXITCODE guards immediately after each command.

Comment on lines +118 to 124
$ErrorActionPreference = 'Stop'
Start-Sleep -Seconds 8
$svc = Get-Service -Name 'gameci-help-bot' -ErrorAction SilentlyContinue
if ($svc.Status -ne 'Running') {
Write-Error "Service is not running: $($svc.Status)"
Get-Content "C:\game-ci\help-bot\logs\service.log" -Tail 30
exit 1
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

With $ErrorActionPreference = 'Stop', does Write-Error terminate a Windows PowerShell script immediately, or do subsequent statements still run?

💡 Result:

With $ErrorActionPreference = 'Stop', Write-Error terminates a Windows PowerShell script immediately, and subsequent statements do not run.

Citations:


The log tail is unreachable on the failure path.

With $ErrorActionPreference = 'Stop', Write-Error on line 122 terminates execution immediately and prevents lines 123-124 from running. This loses the service log output exactly when you need it for troubleshooting.

Proposed fix
-          if ($svc.Status -ne 'Running') {
-            Write-Error "Service is not running: $($svc.Status)"
-            Get-Content "C:\game-ci\help-bot\logs\service.log" -Tail 30
-            exit 1
-          }
+          if (-not $svc -or $svc.Status -ne 'Running') {
+            $status = if ($svc) { $svc.Status } else { 'Missing' }
+            Write-Host "Service is not running: $status"
+            if (Test-Path "C:\game-ci\help-bot\logs\service.log") {
+              Get-Content "C:\game-ci\help-bot\logs\service.log" -Tail 30
+            }
+            throw "gameci-help-bot failed verification"
+          }
📝 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
$ErrorActionPreference = 'Stop'
Start-Sleep -Seconds 8
$svc = Get-Service -Name 'gameci-help-bot' -ErrorAction SilentlyContinue
if ($svc.Status -ne 'Running') {
Write-Error "Service is not running: $($svc.Status)"
Get-Content "C:\game-ci\help-bot\logs\service.log" -Tail 30
exit 1
$ErrorActionPreference = 'Stop'
Start-Sleep -Seconds 8
$svc = Get-Service -Name 'gameci-help-bot' -ErrorAction SilentlyContinue
if (-not $svc -or $svc.Status -ne 'Running') {
$status = if ($svc) { $svc.Status } else { 'Missing' }
Write-Host "Service is not running: $status"
if (Test-Path "C:\game-ci\help-bot\logs\service.log") {
Get-Content "C:\game-ci\help-bot\logs\service.log" -Tail 30
}
throw "gameci-help-bot failed verification"
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/deploy.yml around lines 118 - 124, The Write-Error call is
triggering the Stop preference and preventing the subsequent Get-Content tail
and exit from running; update the failure path in the service check (the block
that inspects $svc and its .Status for the 'gameci-help-bot' service) so it logs
the error without terminating early (for example replace Write-Error with
Write-Host/Write-Output or call Write-Error with -ErrorAction SilentlyContinue),
ensure you null-check $svc before accessing $svc.Status, then run Get-Content
"C:\game-ci\help-bot\logs\service.log" -Tail 30 and finally exit 1 so the logs
are always emitted on failure.

Comment on lines +24 to +37
try {
Log "Installing dependencies..."
yarn install --frozen-lockfile 2>&1 | Out-Null
Log " yarn install: done"
} catch {
Log " yarn install failed: $_"
}

try {
Log "Building..."
yarn build 2>&1 | Out-Null
Log " Build: done"
} catch {
Log " Build failed: $_"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Windows PowerShell 5.1, do non-zero exit codes from native commands like yarntriggertry/catchor obey$ErrorActionPreference? What is the recommended pattern for logging or failing based on $LASTEXITCODE?

💡 Result:

In Windows PowerShell 5.1, non-zero exit codes from native commands like yarn do not trigger try/catch blocks and are not affected by $ErrorActionPreference. These mechanisms handle PowerShell-native errors (terminating or non-terminating with -ErrorAction Stop), but external executables only set $LASTEXITCODE and $? ($false for non-zero). You must explicitly check $LASTEXITCODE after the command: yarn install if ($LASTEXITCODE -ne 0) { Write-Error "yarn failed with exit code $LASTEXITCODE" # Log details, fail script, etc. } Recommended pattern for logging/failing: $output = yarn install 2>&1 if ($LASTEXITCODE -ne 0) { Write-Warning "yarn failed (exit code $LASTEXITCODE): $output" exit $LASTEXITCODE # Propagate exit code } Or wrap in a reusable function: function Invoke-NativeCommand { param([scriptblock] $Command, [string] $ErrorMessage = "Command failed with exit code $LASTEXITCODE") & $Command if ($LASTEXITCODE -ne 0) { throw $ErrorMessage } } Invoke-NativeCommand { yarn install } "yarn install failed" This catches the failure in try/catch if needed, logs via $Error or custom output, and allows explicit failing with exit $LASTEXITCODE. Note: Redirect stderr (2>&1) to capture output; without it, stderr prints but doesn't affect error handling in PS 5.1.

Citations:


yarn failures are treated as success here.

In PowerShell 5.1, non-zero exit codes from native commands like yarn install or yarn build do not trigger catch blocks—they only set $LASTEXITCODE. The script will execute Log " yarn install: done" even when the command fails, allowing a broken build to proceed.

Proposed fix
     try {
         Log "Installing dependencies..."
         yarn install --frozen-lockfile 2>&1 | Out-Null
-        Log "  yarn install: done"
+        if ($LASTEXITCODE -eq 0) {
+            Log "  yarn install: done"
+        } else {
+            Log "  yarn install failed with exit code $LASTEXITCODE"
+        }
     } catch {
         Log "  yarn install failed: $_"
     }

     try {
         Log "Building..."
         yarn build 2>&1 | Out-Null
-        Log "  Build: done"
+        if ($LASTEXITCODE -eq 0) {
+            Log "  Build: done"
+        } else {
+            Log "  Build failed with exit code $LASTEXITCODE"
+        }
     } catch {
         Log "  Build failed: $_"
     }
📝 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
try {
Log "Installing dependencies..."
yarn install --frozen-lockfile 2>&1 | Out-Null
Log " yarn install: done"
} catch {
Log " yarn install failed: $_"
}
try {
Log "Building..."
yarn build 2>&1 | Out-Null
Log " Build: done"
} catch {
Log " Build failed: $_"
try {
Log "Installing dependencies..."
yarn install --frozen-lockfile 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
Log " yarn install: done"
} else {
Log " yarn install failed with exit code $LASTEXITCODE"
}
} catch {
Log " yarn install failed: $_"
}
try {
Log "Building..."
yarn build 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
Log " Build: done"
} else {
Log " Build failed with exit code $LASTEXITCODE"
}
} catch {
Log " Build failed: $_"
}
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] Missing BOM encoding for non-ASCII encoded file 'startup.ps1'

(PSUseBOMForUnicodeEncodedFile)

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

In `@startup.ps1` around lines 24 - 37, The try/catch blocks around the yarn calls
log success even when yarn fails because PowerShell 5.1 doesn't throw on native
command non-zero exits; after each yarn invocation (the yarn install and yarn
build calls around the Log(...) and "Build: done" messages) check $LASTEXITCODE
and if it's non-zero, throw or call exit with a non-zero code so the catch path
runs; update the catch handlers to Log the error and terminate (e.g., exit 1) so
failures don't proceed as successes.

@frostebite frostebite merged commit 1f26349 into main Apr 2, 2026
2 checks passed
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