Skip to content

Commit 31cf118

Browse files
committed
refactor: project structure and dependencies
- Created index.html for the web interface with a placeholder message. - Added base requirements for the project including FastAPI and Uvicorn. - Included development dependencies such as pytest and black. - Added PyInstaller specifications for packaging the application on Linux, macOS, and Windows.
1 parent 6c9838e commit 31cf118

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1225
-155
lines changed

.dockerignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ readme.md
2323
render.yaml
2424
test.json
2525
test.md
26-
test.py
26+
test.py

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ body:
77
- type: markdown
88
attributes:
99
value: |
10-
Thank you for reporting an issue!
11-
Please complete the form below to help us reproduce and fix the problem quickly.
10+
Thank you for reporting an issue!
11+
Please complete the form below to help us reproduce and fix the problem quickly.
1212
Incomplete reports may be harder to debug.
1313
1414
- type: dropdown
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env python3
2+
import re
3+
import sys
4+
from pathlib import Path
5+
6+
if len(sys.argv) != 2:
7+
print("Usage: update_readme_version.py <tag>", file=sys.stderr)
8+
sys.exit(2)
9+
10+
tag = sys.argv[1].strip() # expected like v1.2.0 or 1.2.0
11+
if not tag:
12+
print("Tag is empty", file=sys.stderr)
13+
sys.exit(2)
14+
15+
# Normalize: ensure tag has single leading 'v'
16+
tag = "v" + tag.lstrip("v")
17+
version = tag.lstrip("v") # version without leading 'v' for filenames
18+
19+
readme_path = Path("README.md")
20+
content = readme_path.read_text(encoding="utf-8")
21+
22+
# Update URLs to the latest tag and filenames to exactly 'mydylms-client-v<version>-...'
23+
# Windows
24+
win_pattern = re.compile(
25+
r"(releases/download/)[^/]+(/mydylms-client-)v+[^/]+(-win-x64\.exe)"
26+
)
27+
28+
29+
def win_repl(m: re.Match) -> str:
30+
return f"{m.group(1)}{tag}{m.group(2)}v{version}{m.group(3)}"
31+
32+
33+
# Linux (download or tag)
34+
linux_pattern = re.compile(
35+
r"(releases/(?:download|tag)/)[^/]+(/mydylms-client-)v+[^/]+(-linux-x86__64(?:\.tar\.gz)?)"
36+
)
37+
38+
39+
def linux_repl(m: re.Match) -> str:
40+
return f"{m.group(1)}{tag}{m.group(2)}v{version}{m.group(3)}"
41+
42+
43+
# macOS
44+
mac_pattern = re.compile(
45+
r"(releases/download/)[^/]+(/mydylms-client-)v+[^/]+(-macos-arm64(?:\.zip)?)"
46+
)
47+
48+
49+
def mac_repl(m: re.Match) -> str:
50+
return f"{m.group(1)}{tag}{m.group(2)}v{version}{m.group(3)}"
51+
52+
53+
updated = content
54+
updated = win_pattern.sub(win_repl, updated)
55+
updated = linux_pattern.sub(linux_repl, updated)
56+
updated = mac_pattern.sub(mac_repl, updated)
57+
58+
if updated != content:
59+
readme_path.write_text(updated, encoding="utf-8")
60+
print(f"README.md updated to {tag}")
61+
else:
62+
print("README.md already up-to-date")
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
name: Build and Test Executables
2+
3+
on:
4+
push:
5+
branches:
6+
- "**"
7+
tags:
8+
- "v*.*.*"
9+
10+
permissions:
11+
contents: write
12+
13+
jobs:
14+
build:
15+
name: Build ${{ matrix.os }}
16+
runs-on: ${{ matrix.os }}
17+
strategy:
18+
matrix:
19+
include:
20+
- os: windows-latest
21+
spec: specs/win_x64.spec
22+
ext: win-x64.exe
23+
- os: macos-14
24+
spec: specs/mac_arm64.spec
25+
ext: macos-arm64
26+
- os: ubuntu-24.04
27+
spec: specs/linux_x86_64.spec
28+
ext: linux-x86_64
29+
30+
steps:
31+
- name: Checkout code
32+
uses: actions/checkout@v4
33+
34+
- name: Set up Python
35+
uses: actions/setup-python@v5
36+
with:
37+
python-version: "3.11"
38+
39+
- name: Install dependencies
40+
run: |
41+
python -m pip install --upgrade pip
42+
pip install -r requirements/base.txt
43+
pip install pyinstaller
44+
45+
- name: Hash JS/CSS Assets
46+
run: |
47+
cd app/static
48+
python hash.py hash
49+
50+
- name: Build executable
51+
shell: bash
52+
run: |
53+
echo "Building for OS: ${{ matrix.os }}"
54+
pyinstaller "${{ matrix.spec }}" --noconfirm --clean
55+
mkdir -p build-output
56+
57+
# Get version from tag or commit
58+
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
59+
version="${GITHUB_REF#refs/tags/}"
60+
else
61+
version="commit-$(git rev-parse --short HEAD)"
62+
fi
63+
64+
exe_name="pw-client-${version}-${{ matrix.ext }}"
65+
66+
# Copy built binary to output folder
67+
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
68+
cp "dist/pw-client.exe" "build-output/${exe_name}"
69+
else
70+
cp "dist/pw-client" "build-output/${exe_name}"
71+
fi
72+
73+
echo "EXE_NAME=${exe_name}" >> $GITHUB_ENV
74+
75+
- name: Upload artifact
76+
uses: actions/upload-artifact@v4
77+
with:
78+
name: ${{ env.EXE_NAME }}
79+
path: build-output/${{ env.EXE_NAME }}
80+
81+
test-windows:
82+
name: Test Windows Executable
83+
needs: build
84+
runs-on: windows-latest
85+
86+
steps:
87+
- name: Download build artifacts
88+
uses: actions/download-artifact@v4
89+
with:
90+
path: ./artifacts
91+
92+
- name: Locate Windows binary
93+
id: find
94+
shell: pwsh
95+
run: |
96+
$file = Get-ChildItem -Path ./artifacts -Recurse -File -Filter "pw-client-*-win-x64.exe" | Select-Object -First 1
97+
if (-Not $file) { Write-Error "Windows binary not found" }
98+
echo "exe_path=$($file.FullName)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
99+
100+
- name: Start server
101+
shell: pwsh
102+
run: |
103+
Start-Process -FilePath "${{ steps.find.outputs.exe_path }}" -WindowStyle Hidden
104+
Start-Sleep -Seconds 30
105+
106+
- name: Test /api/v1/health
107+
shell: pwsh
108+
run: |
109+
$maxRetries = 10
110+
$success = $false
111+
112+
for ($i = 0; $i -lt $maxRetries; $i++) {
113+
try {
114+
$response = Invoke-WebRequest -Uri "http://127.0.0.1:8000/api/v1/health" -UseBasicParsing -TimeoutSec 5
115+
if ($response.StatusCode -eq 200) {
116+
Write-Host "Health check passed"
117+
$success = $true
118+
break
119+
} else {
120+
Write-Host "Health check returned $($response.StatusCode), retrying..."
121+
}
122+
} catch {
123+
Write-Host "Waiting for server to start... ($($i+1)/$maxRetries)"
124+
}
125+
Start-Sleep -Seconds 1
126+
}
127+
128+
if (-not $success) {
129+
Write-Error "Health check failed after $maxRetries attempts"
130+
exit 1
131+
}
132+
133+
- name: Stop server
134+
shell: pwsh
135+
continue-on-error: true
136+
run: |
137+
Get-Process | Where-Object { $_.ProcessName -like "pw*" } | Stop-Process -Force
138+
139+
test-macos:
140+
name: Test macOS Executable
141+
needs: build
142+
runs-on: macos-14
143+
144+
steps:
145+
- name: Download build artifacts
146+
uses: actions/download-artifact@v4
147+
with:
148+
path: ./artifacts
149+
150+
- name: Locate macOS binary
151+
id: find
152+
run: |
153+
file=$(find ./artifacts -type f -name "pw-client-*-macos-arm64" | head -n 1)
154+
if [ -z "$file" ]; then
155+
echo "macOS binary not found" >&2
156+
exit 1
157+
fi
158+
echo "exe_path=$file" >> $GITHUB_OUTPUT
159+
160+
- name: Make executable
161+
run: chmod +x "${{ steps.find.outputs.exe_path }}"
162+
163+
- name: Start server
164+
run: |
165+
"${{ steps.find.outputs.exe_path }}" &
166+
SERVER_PID=$!
167+
echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV
168+
sleep 30
169+
170+
- name: Test /api/v1/health
171+
run: |
172+
for i in {1..10}; do
173+
curl -sf "http://127.0.0.1:8000/api/v1/health" && exit 0
174+
echo "Waiting for server..."
175+
sleep 1
176+
done
177+
echo "Health check failed" >&2
178+
exit 1
179+
180+
- name: Stop server
181+
if: always()
182+
run: |
183+
kill -9 $SERVER_PID || true
184+
185+
test-linux:
186+
name: Test Linux Executable
187+
needs: build
188+
runs-on: ubuntu-24.04
189+
190+
steps:
191+
- name: Download build artifacts
192+
uses: actions/download-artifact@v4
193+
with:
194+
path: ./artifacts
195+
196+
- name: Locate Linux binary
197+
id: find
198+
run: |
199+
file=$(find ./artifacts -type f -name "pw-client-*-linux-x86_64" | head -n 1)
200+
if [ -z "$file" ]; then
201+
echo "Linux binary not found" >&2
202+
exit 1
203+
fi
204+
echo "exe_path=$file" >> $GITHUB_OUTPUT
205+
206+
- name: Make executable
207+
run: chmod +x "${{ steps.find.outputs.exe_path }}"
208+
209+
- name: Start server
210+
run: |
211+
"${{ steps.find.outputs.exe_path }}" &
212+
SERVER_PID=$!
213+
echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV
214+
sleep 30
215+
216+
- name: Test /api/v1/health
217+
run: |
218+
for i in {1..10}; do
219+
curl -sf "http://127.0.0.1:8000/api/v1/health" && exit 0
220+
echo "Waiting for server..."
221+
sleep 1
222+
done
223+
echo "Health check failed" >&2
224+
exit 1
225+
226+
- name: Stop server
227+
if: always()
228+
run: |
229+
kill -9 $SERVER_PID || true
230+
231+
release:
232+
name: Create Pre-Release
233+
needs: build
234+
if: startsWith(github.ref, 'refs/tags/')
235+
runs-on: ubuntu-latest
236+
237+
steps:
238+
- name: Download all build artifacts
239+
uses: actions/download-artifact@v4
240+
with:
241+
path: build-output
242+
243+
- name: Create GitHub Pre-Release
244+
uses: softprops/action-gh-release@v2
245+
with:
246+
files: build-output/**/*
247+
prerelease: true
248+
name: pw-client ${{ github.ref_name }}
249+
tag_name: ${{ github.ref_name }}
250+
env:
251+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

0 commit comments

Comments
 (0)