Skip to content

Commit 09fa577

Browse files
committed
feat(ci): add version tagging to build artifacts and improve installer
- Add version determination step in CI workflow to tag artifacts with release version or 'dev' - Enhance installer script with better architecture detection, TLS support, and PATH handling - Add uninstall script for clean removal of Tricode-cli - Improve error handling and logging in both installer and uninstaller scripts
1 parent c1483ab commit 09fa577

3 files changed

Lines changed: 180 additions & 36 deletions

File tree

.github/workflows/ci.yml

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,25 @@ jobs:
4242
with:
4343
python-version: '3.x'
4444

45+
- name: Determine version
46+
id: meta
47+
shell: bash
48+
run: |
49+
if [ "${{ github.event_name }}" = "release" ]; then
50+
echo "version=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
51+
else
52+
echo "version=dev" >> "$GITHUB_OUTPUT"
53+
fi
54+
4555
# ==== Linux (native runners, no Docker) ====
4656
- name: Build on Linux (native)
4757
if: startsWith(matrix.os, 'ubuntu')
4858
run: |
4959
pip install -r requirements.txt
5060
pip install pyinstaller
5161
chmod +x build.sh
52-
./build.sh auto
53-
mv dist/tricode dist/tricode-${{ matrix.asset_suffix }}
62+
./build.sh "${{ steps.meta.outputs.version }}"
63+
mv dist/tricode dist/tricode-${{ matrix.asset_suffix }}-${{ steps.meta.outputs.version }}
5464
5565
# ==== MacOS ====
5666
- name: Build on macOS
@@ -59,8 +69,8 @@ jobs:
5969
pip install -r requirements.txt
6070
pip install pyinstaller
6171
chmod +x build.sh
62-
./build.sh auto
63-
mv dist/tricode dist/tricode-${{ matrix.asset_suffix }}
72+
./build.sh "${{ steps.meta.outputs.version }}"
73+
mv dist/tricode dist/tricode-${{ matrix.asset_suffix }}-${{ steps.meta.outputs.version }}
6474
6575
# ==== Windows ====
6676
- name: Build on Windows
@@ -69,14 +79,14 @@ jobs:
6979
run: |
7080
pip install -r requirements.txt
7181
pip install pyinstaller
72-
.\build.bat auto
82+
.\build.bat ${{ steps.meta.outputs.version }}
7383
7484
- name: Rename Windows artifact
7585
if: startsWith(matrix.os, 'windows')
7686
shell: pwsh
7787
run: |
7888
$src = "dist/tricode.exe"
79-
$dst = "dist/tricode-${{ matrix.asset_suffix }}.exe"
89+
$dst = "dist/tricode-${{ matrix.asset_suffix }}-${{ steps.meta.outputs.version }}.exe"
8090
if (!(Test-Path $src)) { Write-Error "Source not found: $src"; exit 1 }
8191
if (Test-Path $dst) { Remove-Item $dst -Force }
8292
try {
@@ -90,28 +100,28 @@ jobs:
90100
if: startsWith(matrix.os, 'windows')
91101
uses: actions/upload-artifact@v4
92102
with:
93-
name: tricode-${{ matrix.asset_suffix }}
103+
name: tricode-${{ matrix.asset_suffix }}-${{ steps.meta.outputs.version }}
94104
path: |
95-
dist/tricode-${{ matrix.asset_suffix }}.exe
105+
dist/tricode-${{ matrix.asset_suffix }}-${{ steps.meta.outputs.version }}.exe
96106
97107
- name: Upload artifact (Unix)
98108
if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos')
99109
uses: actions/upload-artifact@v4
100110
with:
101-
name: tricode-${{ matrix.asset_suffix }}
111+
name: tricode-${{ matrix.asset_suffix }}-${{ steps.meta.outputs.version }}
102112
path: |
103-
dist/tricode-${{ matrix.asset_suffix }}
113+
dist/tricode-${{ matrix.asset_suffix }}-${{ steps.meta.outputs.version }}
104114
105115
- name: Upload to release (Windows)
106116
if: github.event_name == 'release' && startsWith(matrix.os, 'windows')
107117
env:
108118
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
109119
run: |
110-
gh release upload ${{ github.event.release.tag_name }} dist/tricode-${{ matrix.asset_suffix }}.exe --clobber
120+
gh release upload ${{ github.event.release.tag_name }} dist/tricode-${{ matrix.asset_suffix }}-${{ steps.meta.outputs.version }}.exe --clobber
111121
112122
- name: Upload to release (Unix)
113123
if: github.event_name == 'release' && (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos'))
114124
env:
115125
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
116126
run: |
117-
gh release upload ${{ github.event.release.tag_name }} dist/tricode-${{ matrix.asset_suffix }} --clobber
127+
gh release upload ${{ github.event.release.tag_name }} dist/tricode-${{ matrix.asset_suffix }}-${{ steps.meta.outputs.version }} --clobber

install_tricode.ps1

Lines changed: 67 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ $REPO = "Trirrin/Tricode-cli"
99
$INSTALL_DIR = "$env:LOCALAPPDATA\Tricode"
1010
$GITHUB_API = "https://api.github.com/repos/$REPO/releases/latest"
1111

12+
# Try to ensure TLS1.2 for older systems
13+
try {
14+
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
15+
} catch {}
16+
1217
# ==== TEXTS 说明文本(EN/中)====
1318
$TEXT_START = "Installing Tricode-cli..."
1419
$TEXT_DOWNLOAD = "Downloading the latest release..."
@@ -42,37 +47,54 @@ function Write-Error-Exit {
4247
# ==== MAIN 主流程 ====
4348
Write-Info $TEXT_START
4449

45-
# 1. Detect Architecture 检测架构
46-
$ARCH = $env:PROCESSOR_ARCHITECTURE
47-
Write-Host "Detected Architecture: $ARCH"
50+
# 1. Detect Architecture 检测架构(兼容 32 位 PowerShell)
51+
$archRaw = $env:PROCESSOR_ARCHITECTURE
52+
$archWow = $env:PROCESSOR_ARCHITEW6432
53+
$isArm64 = ($archRaw -match 'ARM64') -or ($archWow -match 'ARM64')
54+
$is64OS = [Environment]::Is64BitOperatingSystem
55+
Write-Host "Detected Architecture: raw=$archRaw, wow64=$archWow, is64OS=$is64OS"
4856

49-
if ($ARCH -eq "AMD64" -or $ARCH -eq "x64") {
50-
$ASSET_ARCH = "windows-x86_64"
51-
} elseif ($ARCH -eq "ARM64") {
57+
if ($isArm64) {
5258
$ASSET_ARCH = "windows-arm64"
59+
} elseif ($is64OS) {
60+
$ASSET_ARCH = "windows-x86_64"
5361
} else {
54-
Write-Error-Exit "Unsupported architecture: $ARCH"
62+
Write-Error-Exit "Unsupported architecture or 32-bit OS is not supported."
5563
}
5664

57-
Write-Host "Looking for release: tricode-$ASSET_ARCH.exe"
65+
Write-Host "Looking for release asset matching: $ASSET_ARCH (.exe preferred)"
5866

59-
# 2. Get download URL from GitHub API 通过GitHub API获取下载链接
67+
# 2. Get download URL from GitHub API 通过 GitHub API 获取下载链接
6068
Write-Info $TEXT_DOWNLOAD
6169

6270
try {
63-
$API_RESPONSE = Invoke-RestMethod -Uri $GITHUB_API -ErrorAction Stop
71+
$headers = @{ 'User-Agent' = 'tricode-installer' }
72+
$API_RESPONSE = Invoke-RestMethod -Uri $GITHUB_API -Headers $headers -ErrorAction Stop
6473
} catch {
6574
Write-Error-Exit "Cannot connect to GitHub API. Please check your network.`nError: $_" 2
6675
}
6776

68-
$ASSET_URL = $API_RESPONSE.assets | Where-Object { $_.name -like "*$ASSET_ARCH*" } | Select-Object -First 1 -ExpandProperty browser_download_url
77+
# Prefer .exe; fallback to .zip; only match files containing the arch and with .exe/.zip suffix
78+
$assets = @($API_RESPONSE.assets)
79+
if (-not $assets -or $assets.Count -eq 0) {
80+
Write-Error-Exit "No assets found in latest release." 2
81+
}
82+
83+
$archPattern = [Regex]::Escape($ASSET_ARCH)
84+
$exeCandidates = $assets | Where-Object { $_.name -match "(?i)$archPattern.*\.exe$" }
85+
$zipCandidates = $assets | Where-Object { $_.name -match "(?i)$archPattern.*\.zip$" }
86+
87+
$asset = $null
88+
if ($exeCandidates) { $asset = $exeCandidates | Select-Object -First 1 }
89+
elseif ($zipCandidates) { $asset = $zipCandidates | Select-Object -First 1 }
6990

70-
if (-not $ASSET_URL) {
91+
if (-not $asset) {
7192
Write-Host "Debug: Available assets:" -ForegroundColor Yellow
72-
$API_RESPONSE.assets | ForEach-Object { Write-Host " - $($_.name)" }
73-
Write-Error-Exit "Cannot find matching release binary for tricode-$ASSET_ARCH.exe`nPlease check if the release exists at: https://github.com/$REPO/releases" 2
93+
$assets | ForEach-Object { Write-Host " - $($_.name)" }
94+
Write-Error-Exit "Cannot find matching release binary for $ASSET_ARCH (.exe or .zip).`nPlease check: https://github.com/$REPO/releases" 2
7495
}
7596

97+
$ASSET_URL = $asset.browser_download_url
7698
Write-Host "Downloading from: $ASSET_URL"
7799

78100
# 3. Download binary 下载二进制文件
@@ -81,7 +103,7 @@ New-Item -ItemType Directory -Path $TMP_DIR -Force | Out-Null
81103

82104
try {
83105
$DOWNLOAD_FILE = Join-Path $TMP_DIR (Split-Path $ASSET_URL -Leaf)
84-
Invoke-WebRequest -Uri $ASSET_URL -OutFile $DOWNLOAD_FILE -ErrorAction Stop
106+
Invoke-WebRequest -Uri $ASSET_URL -OutFile $DOWNLOAD_FILE -UseBasicParsing -ErrorAction Stop
85107
} catch {
86108
Write-Error-Exit "Failed to download binary.`nError: $_" 3
87109
}
@@ -115,24 +137,44 @@ try {
115137
Write-Error-Exit "Failed to copy binary to $INSTALL_DIR`nError: $_" 5
116138
}
117139

118-
# 6. Add to PATH 添加到PATH
140+
# 6. Add to PATH 添加到 PATH(去重、规范化、无多余分号)
119141
Write-Info $TEXT_PATH
120142

121143
$USER_PATH = [Environment]::GetEnvironmentVariable("Path", "User")
144+
$normalizedInstall = $INSTALL_DIR.TrimEnd('\\')
122145

123-
if ($USER_PATH -notlike "*$INSTALL_DIR*") {
146+
$parts = @()
147+
if ($null -ne $USER_PATH -and $USER_PATH -ne '') {
148+
$parts = $USER_PATH -split ';' | Where-Object { $_ -and $_ -ne '' }
149+
}
150+
151+
$exists = $false
152+
foreach ($p in $parts) {
153+
if ($p.Trim().TrimEnd('\\') -ieq $normalizedInstall) { $exists = $true; break }
154+
}
155+
156+
if (-not $exists) {
124157
try {
125-
$NEW_PATH = "$USER_PATH;$INSTALL_DIR"
158+
$cleanParts = @()
159+
foreach ($p in $parts) {
160+
$pp = $p.Trim()
161+
if ($pp -ne '') { $cleanParts += $pp }
162+
}
163+
$cleanParts += $normalizedInstall
164+
$NEW_PATH = ($cleanParts) -join ';'
126165
[Environment]::SetEnvironmentVariable("Path", $NEW_PATH, "User")
127-
Write-Host "Added $INSTALL_DIR to User PATH"
128-
129-
# Update current session PATH
130-
$env:Path = "$env:Path;$INSTALL_DIR"
166+
Write-Host "Added $normalizedInstall to User PATH"
167+
168+
# Update current session PATH(若当前会话不存在则追加)
169+
$sessionHas = $false
170+
$sessionParts = ($env:Path -split ';')
171+
foreach ($sp in $sessionParts) { if ($sp.Trim().TrimEnd('\\') -ieq $normalizedInstall) { $sessionHas = $true; break } }
172+
if (-not $sessionHas) { $env:Path = "$env:Path;$normalizedInstall" }
131173
} catch {
132-
Write-Warning "Failed to add to PATH automatically. Please add manually:`n $INSTALL_DIR"
174+
Write-Warning "Failed to add to PATH automatically. Please add manually:`n $normalizedInstall"
133175
}
134176
} else {
135-
Write-Host "$INSTALL_DIR is already in PATH"
177+
Write-Host "$normalizedInstall is already in PATH"
136178
}
137179

138180
# 7. Cleanup 清理
@@ -148,3 +190,4 @@ if (Test-Path "$INSTALL_DIR\tricode.exe") {
148190
} else {
149191
Write-Warning "Installation may have issues. Binary not found."
150192
}
193+

uninstall_tricode.ps1

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<#
2+
Tricode-cli Uninstaller for Windows (PowerShell)
3+
Removes installed binary and PATH modifications made by install_tricode.ps1
4+
#>
5+
6+
$ErrorActionPreference = "Stop"
7+
$REPO = "Trirrin/Tricode-cli"
8+
$INSTALL_DIR = Join-Path $env:LOCALAPPDATA "Tricode"
9+
$TARGET = Join-Path $INSTALL_DIR "tricode.exe"
10+
11+
function Write-Info {
12+
param([string]$Message)
13+
Write-Host $Message -ForegroundColor Green
14+
}
15+
16+
function Write-Error-Exit {
17+
param([string]$Message, [int]$Code = 1)
18+
Write-Host "`n[ERROR] $Message" -ForegroundColor Red
19+
Write-Host "Please report issues to: https://github.com/$REPO/issues" -ForegroundColor Yellow
20+
exit $Code
21+
}
22+
23+
Write-Info "Uninstalling Tricode-cli..."
24+
25+
# 1) Remove binary
26+
try {
27+
if (Test-Path $TARGET) {
28+
Remove-Item -Path $TARGET -Force
29+
Write-Host "Removed binary: $TARGET"
30+
} else {
31+
Write-Host "Binary not found at: $TARGET (already removed)"
32+
}
33+
} catch {
34+
Write-Error-Exit "Failed to remove binary at $TARGET`nError: $_" 1
35+
}
36+
37+
# 2) Remove INSTALL_DIR from User PATH if present (precise match only)
38+
try {
39+
$USER_PATH = [Environment]::GetEnvironmentVariable("Path", "User")
40+
$normalizedInstall = $INSTALL_DIR.TrimEnd('\\')
41+
if ($null -ne $USER_PATH -and $USER_PATH -ne '') {
42+
$segments = $USER_PATH -split ";"
43+
$filtered = foreach ($seg in $segments) {
44+
if ($null -eq $seg -or $seg -eq '') { continue }
45+
$segTrim = $seg.Trim()
46+
$segNorm = $segTrim.TrimEnd('\\')
47+
if ($segNorm -ieq $normalizedInstall) { continue }
48+
$segTrim
49+
}
50+
$NEW_PATH = ($filtered | Where-Object { $_ -and $_ -ne '' }) -join ";"
51+
[Environment]::SetEnvironmentVariable("Path", $NEW_PATH, "User")
52+
Write-Host "Removed $INSTALL_DIR from User PATH"
53+
54+
# Update current session PATH similarly
55+
$sessionPath = $env:Path
56+
if ($null -ne $sessionPath -and $sessionPath -ne '') {
57+
$sSegments = $sessionPath -split ";"
58+
$sFiltered = foreach ($seg in $sSegments) {
59+
if ($null -eq $seg -or $seg -eq '') { continue }
60+
$segTrim = $seg.Trim()
61+
$segNorm = $segTrim.TrimEnd('\\')
62+
if ($segNorm -ieq $normalizedInstall) { continue }
63+
$segTrim
64+
}
65+
$env:Path = ($sFiltered | Where-Object { $_ -and $_ -ne '' }) -join ";"
66+
}
67+
} else {
68+
Write-Host "$INSTALL_DIR was not in User PATH"
69+
}
70+
} catch {
71+
Write-Host "Warning: Failed to update PATH automatically. You may remove it manually from Environment Variables." -ForegroundColor Yellow
72+
}
73+
74+
# 3) Remove install directory if empty
75+
try {
76+
if (Test-Path $INSTALL_DIR) {
77+
$items = Get-ChildItem -Path $INSTALL_DIR -Force -ErrorAction SilentlyContinue
78+
if (-not $items) {
79+
Remove-Item -Path $INSTALL_DIR -Force -Recurse
80+
Write-Host "Removed empty directory: $INSTALL_DIR"
81+
}
82+
}
83+
} catch {
84+
Write-Host "Warning: Failed to remove directory $INSTALL_DIR. You can delete it manually." -ForegroundColor Yellow
85+
}
86+
87+
Write-Host "`n[Success] Tricode-cli uninstalled."
88+
Write-Host "(成功) Tricode-cli 已卸载。"
89+
Write-Host "Note: Please restart your terminal or PowerShell."
90+
Write-Host "提示: 请重启终端或 PowerShell。"
91+

0 commit comments

Comments
 (0)