Skip to content

pkg: add release build script (#534)#555

Merged
Chris0Jeky merged 5 commits intomainfrom
pkg/534-build-script
Mar 29, 2026
Merged

pkg: add release build script (#534)#555
Chris0Jeky merged 5 commits intomainfrom
pkg/534-build-script

Conversation

@Chris0Jeky
Copy link
Copy Markdown
Owner

Summary

  • Adds scripts/build-release.sh — cross-platform bash script that automates: npm run build → copy dist/ to backend/.../wwwroot/dotnet publish --self-contained -p:PublishSingleFile=true
  • Adds scripts/build-release.ps1 — PowerShell equivalent for Windows-native users (no Git Bash required)
  • Gitignores backend/src/Taskdeck.Api/wwwroot/ since it is generated content

Both scripts:

  • Accept an optional RID argument (bash: positional; PS1: -Rid), defaulting to the current platform
  • Support win-x64, linux-x64, osx-x64, osx-arm64
  • Check for required tools (node, npm, dotnet) and warn on wrong Node.js version
  • Run npm install automatically if node_modules is missing
  • Clear stale wwwroot/ contents before copying fresh dist/
  • Exit non-zero on any step failure (set -euo pipefail / $ErrorActionPreference = "Stop")
  • Emit a size warning if the produced executable exceeds 100 MB
  • Publish to artifacts/publish/<RID>/ (already gitignored)

Closes #534

Test plan

  • bash -n scripts/build-release.sh passes (syntax valid)
  • PowerShell syntax check passes
  • Frontend dist output path (frontend/taskdeck-web/dist) matches copy source
  • Copy target (backend/src/Taskdeck.Api/wwwroot/) matches what PKG-01 (PKG-01: Add SPA static file serving to ASP.NET Core for self-contained deployment #533) will configure for UseStaticFiles
  • Script exits non-zero when npm or dotnet step fails
  • artifacts/publish/ and wwwroot/ are gitignored and not tracked

Automates: npm run build → copy dist/ → dotnet publish --self-contained.
Accepts optional RID argument (defaults to current platform). Supports
win-x64, linux-x64, osx-x64, osx-arm64. Publishes a single-file executable
to artifacts/publish/<RID>/.
Windows-native equivalent of build-release.sh for users who prefer
PowerShell over Git Bash. Accepts -Rid parameter, defaults to win-x64.
The wwwroot/ directory is populated by build-release.sh/.ps1 from the
frontend dist/ output and should not be tracked in source control.
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Self-review findings:

Error handling

  • bash: set -euo pipefail is present — any failing command aborts the script. Good.
  • PS1: $ErrorActionPreference = "Stop" plus explicit $LASTEXITCODE checks after each & npm/& dotnet call. Good.

Path safety

  • rm -rf "${WWWROOT:?}"/* uses the :? guard to prevent expansion if $WWWROOT is unset/empty. Since the value is computed from the script's own directory, accidental root deletion is not a realistic risk, but the guard is defence-in-depth.
  • artifacts/publish/<RID>/ is already covered by the existing .gitignore artifacts/ entry. The new wwwroot/ entry is also added.

npm run build --prefix

  • The --prefix DIR npm run build form is supported in npm v7+ and works on Git Bash / Linux / macOS. The frontend package.json uses the standard build script (npm run typecheck && vite build). No issue.

PublishTrimmed=true risk

  • Trim is enabled per the issue's explicit guidance ("Consider PublishTrimmed=true for size reduction"). This could break reflection-heavy libraries (EF Core migrations, OpenTelemetry, Swashbuckle). Added TrimmerRootAssembly=Taskdeck.Api to preserve the entry-point assembly, but trim compatibility must be tested end-to-end once PKG-01 (PKG-01: Add SPA static file serving to ASP.NET Core for self-contained deployment #533) lands. A follow-up issue may be needed if trim causes runtime failures.

wwwroot/ does not exist yet (PKG-01 is open)

  • The script correctly creates wwwroot/ via mkdir -p (bash) / New-Item -Force (PS1), so the build script is safe to run even before PKG-01 lands. The copied files will be there, ready to be served once UseStaticFiles is added.

--self-contained flag

  • Using --self-contained true (long form) for clarity. Accepted by dotnet publish in all .NET 8 versions.

No issues found that require blocking changes. The trim risk is a known trade-off documented in the issue and should be validated in an integration test once PKG-01 is done.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces unified release build scripts for both Windows (PowerShell) and Linux/macOS (Bash) environments, automating the frontend build, static file deployment, and .NET backend publishing. The .gitignore file is updated to exclude the generated wwwroot directory. Review feedback suggests enhancing the PowerShell script's cross-platform compatibility by using forward slashes or Join-Path for path construction and using wildcard matching for Windows Runtime Identifiers to support future architectures.

Comment thread scripts/build-release.ps1 Outdated
Comment on lines +39 to +43
$FrontendDir = Join-Path $RepoRoot "frontend\taskdeck-web"
$FrontendDist = Join-Path $FrontendDir "dist"

$ApiProject = Join-Path $RepoRoot "backend\src\Taskdeck.Api\Taskdeck.Api.csproj"
$Wwwroot = Join-Path $RepoRoot "backend\src\Taskdeck.Api\wwwroot"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

For better cross-platform compatibility with PowerShell Core on Linux and macOS, it's recommended to use forward slashes as path separators in your Join-Path calls. PowerShell on Windows handles forward slashes correctly, and this change will make the script more portable without affecting its functionality on Windows.

$FrontendDir  = Join-Path $RepoRoot "frontend/taskdeck-web"
$FrontendDist = Join-Path $FrontendDir "dist"

$ApiProject  = Join-Path $RepoRoot "backend/src/Taskdeck.Api/Taskdeck.Api.csproj"
$Wwwroot     = Join-Path $RepoRoot "backend/src/Taskdeck.Api/wwwroot"

Comment thread scripts/build-release.ps1 Outdated
Get-ChildItem -Path $Wwwroot | Remove-Item -Recurse -Force
}

Copy-Item -Path "$FrontendDist\*" -Destination $Wwwroot -Recurse -Force
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The hardcoded backslash in "$FrontendDist\*" can cause issues on non-Windows platforms where this script might be run using PowerShell Core. The backslash could be interpreted as an escape character. To make this more robust and platform-agnostic, construct the path using Join-Path.

    Copy-Item -Path (Join-Path $FrontendDist '*') -Destination $Wwwroot -Recurse -Force

Comment thread scripts/build-release.ps1 Outdated
Write-Step " RID : $Rid"
Write-Step " Artifact : $OutputDir"

$exeName = if ($Rid -eq "win-x64") { "Taskdeck.Api.exe" } else { "Taskdeck.Api" }
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The check $Rid -eq "win-x64" is very specific. To make the script more future-proof for other Windows-based Runtime Identifiers (like win-arm64), it would be better to use a wildcard match. This makes the logic for determining the executable name more robust.

    $exeName = if ($Rid -like "win-*") { "Taskdeck.Api.exe" } else { "Taskdeck.Api" }

Three genuine issues fixed across both build scripts:

1. Add blocking comment at WWWROOT constant: UseStaticFiles is not yet
   configured in PipelineConfiguration.cs (PKG-01 / #533 is the
   dependency). Without it the published binary silently returns 404 for
   all SPA routes. Comment makes the unshippable state explicit.

2. Replace rm -rf dir/* glob with rm -rf dir + mkdir -p to avoid bash
   glob-expansion failure on empty directories under set -euo pipefail
   (Git Bash on Windows passes literal /* to rm when nullglob is unset).
   PowerShell equivalent updated to Remove-Item + New-Item pattern.

3. Add TRIM WARNING comment before dotnet publish -p:PublishTrimmed=true
   in both scripts. EF Core, ASP.NET DI, System.Text.Json and SignalR
   are all reflection-heavy and can break silently under IL trimming.
@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Adversarial review findings — 3 genuine issues fixed

All three issues have been fixed directly on the branch (commit 49b75909).


Finding 1 — CRITICAL: UseStaticFiles not configured; published binary silently returns 404 for SPA

Both scripts copy dist/ to wwwroot/, but PipelineConfiguration.cs / Program.cs contain no UseStaticFiles(), UseDefaultFiles(), or app.UseSpaStaticFiles() call. A developer who runs this script and ships the resulting artifact today gets a binary where every frontend route returns 404.

The test plan acknowledges PKG-01 (#533) as the dependency, but the scripts themselves said nothing — a quiet footgun. A blocking comment has been added at the WWWROOT constant declaration in both scripts making the pre-condition explicit.


Finding 2 — rm -rf "${WWWROOT:?}"/* glob failure on empty directories (bash + Git Bash on Windows)

With set -euo pipefail and nullglob unset (the bash default), if $WWWROOT is empty the shell passes the literal /* string to rm. On Linux/macOS rm -f suppresses the "no such file" error and returns 0, so it's benign. On Git Bash on Windows the behavior differs and the script can abort mid-run leaving the build in a half-cleaned state.

Fixed by replacing rm -rf "${WWWROOT:?}"/* + mkdir with rm -rf "${WWWROOT:?}" && mkdir -p "$WWWROOT" (wipe-and-recreate). PowerShell script updated to Remove-Item + New-Item for the same reason.


Finding 3 — PublishTrimmed=true with no trim-compatibility warning

Both scripts publish with -p:PublishTrimmed=true and -p:TrimmerRootAssembly=Taskdeck.Api but had no comment about the known risk: EF Core migrations, ASP.NET DI convention scanning, System.Text.Json source gen, and SignalR are all reflection-heavy and can break silently under IL trimming without producing a build error.

A # TRIM WARNING comment has been added before the dotnet publish call in both scripts pointing to the need for a smoke test of the trimmed artifact before shipping.


Items that passed review

  • set -euo pipefail is present and correct; sub-shells use || fallback safely.
  • Node version guard correctly extracts the major version via process.versions.node.split(".")[0] — handles v24.x.x format correctly (it uses the Node API, not node --version string parsing).
  • PowerShell $LASTEXITCODE is checked after every dotnet/npm external call — the $ErrorActionPreference = "Stop" caveat (only covers cmdlets, not external exes) is handled by the explicit checks.
  • .gitignore entry backend/src/Taskdeck.Api/wwwroot/ is correctly scoped with the full path prefix; it will not accidentally match other wwwroot/ directories.
  • Artifact output path artifacts/publish/<RID>/ is consistent and predictable.
  • [ValidateSet] RID values (win-x64, linux-x64, osx-x64, osx-arm64) are current and match the bash case validation.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Addressed all three Gemini review comments:

  1. Backslashes in Join-Path literals — changed to forward slashes throughout for PowerShell Core cross-platform compatibility.
  2. Hardcoded backslash in Copy-Item — replaced "$FrontendDist\*" with (Join-Path $FrontendDist '*') to avoid escape character issues on non-Windows.
  3. RID check — changed $Rid -eq "win-x64" to $Rid -like "win-*" to correctly handle win-arm64 and any future Windows RIDs.

@Chris0Jeky Chris0Jeky merged commit f38ba4b into main Mar 29, 2026
18 checks passed
@Chris0Jeky Chris0Jeky deleted the pkg/534-build-script branch March 29, 2026 18:17
@github-project-automation github-project-automation bot moved this from Pending to Done in Taskdeck Execution Mar 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

PKG-02: Create unified build script for self-contained single-file publish

1 participant