fix(release): ship NSIS installer artifact
This commit is contained in:
@@ -382,6 +382,48 @@ jobs:
|
||||
path: bundles/*
|
||||
if-no-files-found: error
|
||||
|
||||
windows-installer:
|
||||
needs: [build, resolve]
|
||||
if: ${{ !cancelled() && needs.build.result == 'success' }}
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.resolve.outputs.source_ref }}
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
pattern: 'codewhale*-windows-x64.exe'
|
||||
- name: Install NSIS
|
||||
shell: pwsh
|
||||
run: choco install nsis -y --no-progress
|
||||
- name: Build NSIS installer
|
||||
shell: pwsh
|
||||
run: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
$version = "${{ needs.resolve.outputs.tag }}".TrimStart("v")
|
||||
Copy-Item "artifacts\codewhale-windows-x64.exe\codewhale-windows-x64.exe" "scripts\installer\codewhale.exe"
|
||||
Copy-Item "artifacts\codewhale-tui-windows-x64.exe\codewhale-tui-windows-x64.exe" "scripts\installer\codewhale-tui.exe"
|
||||
$makensis = "${env:ProgramFiles(x86)}\NSIS\makensis.exe"
|
||||
if (!(Test-Path $makensis)) {
|
||||
$makensis = "${env:ProgramFiles}\NSIS\makensis.exe"
|
||||
}
|
||||
if (!(Test-Path $makensis)) {
|
||||
throw "makensis.exe not found after NSIS install"
|
||||
}
|
||||
Push-Location scripts\installer
|
||||
& $makensis "/DVERSION=$version" "codewhale.nsi"
|
||||
Pop-Location
|
||||
if (!(Test-Path "scripts\installer\CodeWhaleSetup.exe")) {
|
||||
throw "CodeWhaleSetup.exe was not produced"
|
||||
}
|
||||
- name: Upload installer artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CodeWhaleSetup.exe
|
||||
path: scripts/installer/CodeWhaleSetup.exe
|
||||
if-no-files-found: error
|
||||
|
||||
docker:
|
||||
needs: [build, resolve]
|
||||
if: ${{ !cancelled() && needs.build.result == 'success' }}
|
||||
@@ -451,8 +493,8 @@ jobs:
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
release:
|
||||
needs: [build, bundle, docker, resolve]
|
||||
if: ${{ !cancelled() && needs.build.result == 'success' && needs.bundle.result == 'success' && needs.docker.result == 'success' }}
|
||||
needs: [build, bundle, windows-installer, docker, resolve]
|
||||
if: ${{ !cancelled() && needs.build.result == 'success' && needs.bundle.result == 'success' && needs.windows-installer.result == 'success' && needs.docker.result == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -552,6 +594,7 @@ jobs:
|
||||
| Linux RISC-V | `codewhale-linux-riscv64.tar.gz` | `install.sh` |
|
||||
| macOS x64 | `codewhale-macos-x64.tar.gz` | `install.sh` |
|
||||
| macOS ARM | `codewhale-macos-arm64.tar.gz` | `install.sh` |
|
||||
| Windows x64 (installer) | `CodeWhaleSetup.exe` | NSIS setup |
|
||||
| Windows x64 | `codewhale-windows-x64.zip` | `install.bat` |
|
||||
| Windows x64 (portable) | `codewhale-windows-x64-portable.zip` | — |
|
||||
|
||||
@@ -563,11 +606,12 @@ jobs:
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
- For the installer path, run `CodeWhaleSetup.exe`; it installs both binaries under `%LOCALAPPDATA%\Programs\CodeWhale\bin` and adds that directory to the current-user PATH.
|
||||
- Extract `codewhale-windows-x64.zip`
|
||||
- Run `install.bat` (copies to `%USERPROFILE%\bin`)
|
||||
- Add `%USERPROFILE%\bin` to your PATH
|
||||
|
||||
The **portable** Windows archive skips the install script — extract and run from any directory.
|
||||
The **portable** Windows archive skips the install script — extract and run from any directory. The NSIS installer is currently unsigned and may trigger Windows SmartScreen until a signing certificate is wired into the release pipeline.
|
||||
|
||||
Individual binaries are also attached below for scripting and the npm wrapper. Legacy `deepseek-*` and `deepseek-tui-*` assets are compatibility-only deprecation shims for v0.8.x so that existing `deepseek update` invocations on v0.8.40 keep working; they forward to the canonical binaries. The legacy npm package `deepseek-tui` is deprecated and is not republished.
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@ machines running Windows.
|
||||
| 1 | Confirm Windows version: `winver` → 10 build 17763+ or 11 | ☐ |
|
||||
| 2 | Ensure the user account is a **standard user** (not a local admin). The installer does not require elevation. | ☐ |
|
||||
| 3 | Verify outbound HTTPS (port 443) is open to `api.openai.com` (or whichever LLM provider the course uses). | ☐ |
|
||||
| 4 | Obtain the installer: download `CodeWhaleSetup.exe` from the [latest release](https://github.com/Hmbown/CodeWhale/releases/latest) or from your department mirror. | ☐ |
|
||||
| 5 | (Optional) Verify SHA-256 hash matches the published manifest. | ☐ |
|
||||
| 4 | Obtain the installer: download `CodeWhaleSetup.exe` from a v0.8.50+ [release](https://github.com/Hmbown/CodeWhale/releases/latest) or from your department mirror. | ☐ |
|
||||
| 5 | Verify SHA-256 hash against `codewhale-artifacts-sha256.txt` before deploying. | ☐ |
|
||||
| 6 | Note that the public installer is currently unsigned and may trigger Windows SmartScreen unless your organization signs it before deployment. | ☐ |
|
||||
|
||||
---
|
||||
|
||||
@@ -25,7 +26,7 @@ machines running Windows.
|
||||
### Option A — Silent install (recommended for imaging / SCCM / Intune)
|
||||
|
||||
```powershell
|
||||
# Run as admin or via deployment tool
|
||||
# Run as the target user or via a per-user deployment tool
|
||||
CodeWhaleSetup.exe /S
|
||||
```
|
||||
|
||||
@@ -52,13 +53,15 @@ New-Item -ItemType Directory -Force -Path $binDir
|
||||
|
||||
# 2. Download binaries (adjust URL to your mirror or release tag)
|
||||
$tag = (Invoke-RestMethod -Uri "https://api.github.com/repos/Hmbown/CodeWhale/releases/latest").tag_name
|
||||
Invoke-WebRequest -Uri "https://github.com/Hmbown/CodeWhale/releases/download/$tag/codewhale-x64.exe" -OutFile "$binDir\codewhale.exe"
|
||||
Invoke-WebRequest -Uri "https://github.com/Hmbown/CodeWhale/releases/download/$tag/codewhale-tui-x64.exe" -OutFile "$binDir\codewhale-tui.exe"
|
||||
Invoke-WebRequest -Uri "https://github.com/Hmbown/CodeWhale/releases/download/$tag/codewhale-windows-x64.exe" -OutFile "$binDir\codewhale.exe"
|
||||
Invoke-WebRequest -Uri "https://github.com/Hmbown/CodeWhale/releases/download/$tag/codewhale-tui-windows-x64.exe" -OutFile "$binDir\codewhale-tui.exe"
|
||||
|
||||
# 3. Add to user PATH (persistent)
|
||||
$currentPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
||||
if ($currentPath -notlike "*$binDir*") {
|
||||
[Environment]::SetEnvironmentVariable("Path", "$currentPath;$binDir", "User")
|
||||
$pathParts = @($currentPath -split ";" | Where-Object { $_ })
|
||||
if ($pathParts -notcontains $binDir) {
|
||||
$newPath = (@($pathParts) + $binDir) -join ";"
|
||||
[Environment]::SetEnvironmentVariable("Path", $newPath, "User")
|
||||
}
|
||||
|
||||
# 4. Refresh current session PATH
|
||||
@@ -79,6 +82,19 @@ Run these on **each machine** (or spot-check a sample):
|
||||
|
||||
If `codewhale` is not found, the user may need to open a **new** terminal window for PATH changes to take effect.
|
||||
|
||||
## Lab validation checklist
|
||||
|
||||
Run this once on a clean lab machine, and again on a machine that already has a
|
||||
previous CodeWhale install:
|
||||
|
||||
| # | Scenario | Expected result | Done? |
|
||||
|---|----------|-----------------|-------|
|
||||
| 1 | Install with no existing CodeWhale PATH entry | Adds exactly `%LOCALAPPDATA%\Programs\CodeWhale\bin` | ☐ |
|
||||
| 2 | Install twice | PATH is not duplicated | ☐ |
|
||||
| 3 | Install with a neighboring PATH entry such as `C:\Tools\CodeWhale\bin-extra` | Neighboring entry is preserved | ☐ |
|
||||
| 4 | Upgrade by installing a newer `CodeWhaleSetup.exe` over an older one | Apps & Features version and both `--version` outputs match the new build | ☐ |
|
||||
| 5 | Silent uninstall with `Uninstall.exe /S` | Files, uninstall registry entry, and only the exact installer PATH entry are removed | ☐ |
|
||||
|
||||
---
|
||||
|
||||
## API key provisioning
|
||||
@@ -129,7 +145,7 @@ Remove-Item -Recurse -Force (Split-Path $binDir)
|
||||
|
||||
# Remove from PATH
|
||||
$currentPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
||||
$newPath = ($currentPath -split ";" | Where-Object { $_ -ne $binDir }) -join ";"
|
||||
$newPath = ($currentPath -split ";" | Where-Object { $_ -and ($_ -ne $binDir) }) -join ";"
|
||||
[Environment]::SetEnvironmentVariable("Path", $newPath, "User")
|
||||
```
|
||||
|
||||
@@ -175,4 +191,4 @@ If building a golden image (WIM/FFU):
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2026-05-25*
|
||||
*Last updated: 2026-06-02*
|
||||
|
||||
+13
-3
@@ -301,8 +301,9 @@ when you need the newest version immediately.
|
||||
|
||||
### Windows NSIS Installer
|
||||
|
||||
A standalone NSIS-based installer is available for Windows users who prefer a
|
||||
traditional double-click setup (no npm, no Scoop, no Cargo required).
|
||||
A standalone NSIS-based installer is available starting with v0.8.50 for
|
||||
Windows users who prefer a traditional double-click setup (no npm, no Scoop, no
|
||||
Cargo required).
|
||||
|
||||
**Download** `CodeWhaleSetup.exe` from the
|
||||
[Releases page](https://github.com/Hmbown/CodeWhale/releases/latest).
|
||||
@@ -320,12 +321,21 @@ traditional double-click setup (no npm, no Scoop, no Cargo required).
|
||||
CodeWhaleSetup.exe /S
|
||||
```
|
||||
|
||||
The installer is per-user and does not request elevation. Run silent installs in
|
||||
the target user's context, or use a deployment tool that can run the installer
|
||||
for each user profile that needs CodeWhale.
|
||||
|
||||
The release-built installer is currently unsigned and may trigger Windows
|
||||
SmartScreen. Verify the SHA-256 checksum from `codewhale-artifacts-sha256.txt`
|
||||
before deploying, and sign the installer in your internal deployment pipeline if
|
||||
your environment requires signed application packages.
|
||||
|
||||
**Build the installer yourself** (requires [NSIS](https://nsis.sourceforge.io)):
|
||||
|
||||
```powershell
|
||||
cd scripts\installer
|
||||
# Place codewhale.exe and codewhale-tui.exe here, then:
|
||||
makensis /DVERSION=0.9.0 codewhale.nsi
|
||||
makensis /DVERSION=<version> codewhale.nsi
|
||||
```
|
||||
|
||||
**Manual fallback** — if the installer is blocked by group policy, see the
|
||||
|
||||
@@ -11,11 +11,8 @@
|
||||
; codewhale.exe
|
||||
; codewhale-tui.exe
|
||||
; 2. Build:
|
||||
; makensis codewhale.nsi
|
||||
; makensis /DVERSION=1.2.3 codewhale.nsi
|
||||
; 3. Output: CodeWhaleSetup.exe (in current directory)
|
||||
;
|
||||
; You can override version at build time:
|
||||
; makensis /DVERSION=1.2.3 codewhale.nsi
|
||||
|
||||
;--------------------------------
|
||||
; Includes
|
||||
@@ -83,11 +80,12 @@ Section "Install" SecInstall
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
|
||||
; Add to current-user PATH
|
||||
; Read existing PATH, append if not already present
|
||||
; Read existing PATH, append only when the exact entry is absent.
|
||||
ReadRegStr $0 HKCU "Environment" "Path"
|
||||
${StrStr} $1 $0 "$INSTDIR\bin"
|
||||
StrCpy $2 ";$0;"
|
||||
StrCpy $3 ";$INSTDIR\bin;"
|
||||
${StrStr} $1 $2 $3
|
||||
StrCmp $1 "" 0 path_already_set
|
||||
; Not found — append
|
||||
StrCmp $0 "" empty_path
|
||||
WriteRegExpandStr HKCU "Environment" "Path" "$0;$INSTDIR\bin"
|
||||
Goto path_done
|
||||
@@ -128,9 +126,10 @@ Section "Uninstall"
|
||||
|
||||
; Remove from current-user PATH
|
||||
ReadRegStr $0 HKCU "Environment" "Path"
|
||||
${un.StrStr} $1 $0 "$INSTDIR\bin"
|
||||
StrCpy $2 ";$0;"
|
||||
StrCpy $3 ";$INSTDIR\bin;"
|
||||
${UnStrStr} $1 $2 $3
|
||||
StrCmp $1 "" path_clean_done
|
||||
; Remove the entry
|
||||
Push "$0"
|
||||
Push "$INSTDIR\bin"
|
||||
Call un.RemoveFromPath
|
||||
@@ -145,7 +144,7 @@ Section "Uninstall"
|
||||
SectionEnd
|
||||
|
||||
;--------------------------------
|
||||
; Helper: Remove a directory from PATH (uninstaller version)
|
||||
; Helper: Remove exact directory entries from PATH (uninstaller version)
|
||||
; Input: PATH string (on stack), directory to remove (on stack)
|
||||
; Output: cleaned PATH (on stack)
|
||||
;--------------------------------
|
||||
@@ -153,48 +152,80 @@ Function un.RemoveFromPath
|
||||
Exch $R0 ; directory to remove
|
||||
Exch
|
||||
Exch $R1 ; original PATH
|
||||
Push $R2 ; prefix
|
||||
Push $R3 ; suffix
|
||||
Push $R2 ; padded path
|
||||
Push $R3 ; padded needle
|
||||
Push $R4 ; match result
|
||||
Push $R5 ; prefix
|
||||
Push $R6 ; suffix
|
||||
Push $R7 ; offset/length
|
||||
|
||||
${un.StrStr} $R4 $R1 $R0
|
||||
StrCmp $R4 "" done
|
||||
loop:
|
||||
StrCmp $R1 "" done
|
||||
StrCpy $R2 ";$R1;"
|
||||
StrCpy $R3 ";$R0;"
|
||||
${UnStrStr} $R4 $R2 $R3
|
||||
StrCmp $R4 "" done
|
||||
|
||||
; Calculate prefix
|
||||
StrLen $R2 $R1
|
||||
StrLen $R3 $R4
|
||||
IntOp $R3 $R2 - $R3 ; Match offset
|
||||
StrCpy $R2 $R1 $R3 ; Prefix string
|
||||
; Prefix before the exact `;dir;` match in the padded PATH.
|
||||
StrLen $R5 $R2
|
||||
StrLen $R6 $R4
|
||||
IntOp $R6 $R5 - $R6
|
||||
StrCpy $R5 $R2 $R6
|
||||
|
||||
; Calculate suffix
|
||||
StrLen $R4 $R0
|
||||
IntOp $R4 $R3 + $R4 ; Suffix offset = Match offset + Dir length
|
||||
StrCpy $R3 $R1 "" $R4 ; Suffix string
|
||||
; Suffix after the exact `;dir;` match in the padded PATH.
|
||||
StrLen $R7 $R3
|
||||
IntOp $R7 $R6 + $R7
|
||||
StrCpy $R6 $R2 "" $R7
|
||||
|
||||
; Clean up semicolons
|
||||
StrCpy $R4 $R3 1
|
||||
StrCmp $R4 ";" 0 +2
|
||||
StrCpy $R3 $R3 "" 1 ; Strip leading semicolon from suffix
|
||||
Push $R5
|
||||
Call un.TrimPathEdgeSemicolons
|
||||
Pop $R5
|
||||
Push $R6
|
||||
Call un.TrimPathEdgeSemicolons
|
||||
Pop $R6
|
||||
|
||||
StrLen $R4 $R2
|
||||
IntOp $R4 $R4 - 1
|
||||
StrCpy $R0 $R2 1 $R4
|
||||
StrCmp $R0 ";" 0 +2
|
||||
StrCpy $R2 $R2 $R4 ; Strip trailing semicolon from prefix
|
||||
|
||||
; Concatenate
|
||||
StrCmp $R2 "" 0 +3
|
||||
StrCpy $R1 $R3
|
||||
Goto done
|
||||
StrCmp $R3 "" 0 +3
|
||||
StrCpy $R1 $R2
|
||||
Goto done
|
||||
StrCpy $R1 "$R2;$R3"
|
||||
StrCmp $R5 "" 0 +3
|
||||
StrCpy $R1 $R6
|
||||
Goto loop
|
||||
StrCmp $R6 "" 0 +3
|
||||
StrCpy $R1 $R5
|
||||
Goto loop
|
||||
StrCpy $R1 "$R5;$R6"
|
||||
Goto loop
|
||||
|
||||
done:
|
||||
Pop $R7
|
||||
Pop $R6
|
||||
Pop $R5
|
||||
Pop $R4
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Pop $R0
|
||||
Exch $R1
|
||||
FunctionEnd
|
||||
|
||||
Function un.TrimPathEdgeSemicolons
|
||||
Exch $R9
|
||||
Push $R8
|
||||
|
||||
trim_leading:
|
||||
StrCpy $R8 $R9 1
|
||||
StrCmp $R8 ";" 0 trim_trailing
|
||||
StrCpy $R9 $R9 "" 1
|
||||
Goto trim_leading
|
||||
|
||||
trim_trailing:
|
||||
StrLen $R8 $R9
|
||||
IntCmp $R8 0 trim_done
|
||||
IntOp $R8 $R8 - 1
|
||||
StrCpy $R8 $R9 1 $R8
|
||||
StrCmp $R8 ";" 0 trim_done
|
||||
StrLen $R8 $R9
|
||||
IntOp $R8 $R8 - 1
|
||||
StrCpy $R9 $R9 $R8
|
||||
Goto trim_trailing
|
||||
|
||||
trim_done:
|
||||
Pop $R8
|
||||
Exch $R9
|
||||
FunctionEnd
|
||||
|
||||
Reference in New Issue
Block a user