Add a small, tested base for all three clients, and Gitea CI/release workflows
internal/client (new): a typed Go HTTP client for the API - login, register, logout, me, and full CRUD for games/platforms - shared by the TUI and the Fyne GUI so request-building, auth and error handling (an APIError with the HTTP status) live in one tested place instead of being duplicated. 8 tests against an httptest server covering success, 401, 403 and that the token is stored/cleared at the right times. cmd/tui (rewrite): drops the old unauthenticated read-only viewer. Now a login screen (bubbles/textinput) -> a scrollable games table (bubbles/table) with 'r' to refresh, built as an explicit state machine (screenLogin/screenLoading/screenGames) so Update's transitions are testable without a real terminal. 9 tests driving Update() directly. cmd/gui (rewrite): drops raylib for Fyne. Same shape as the TUI - a login form, then a games table, with networking on a goroutine and fyne.Do for the UI updates. The table-cell and login-validation logic are plain functions with no Fyne/display dependency, so they're tested directly (7 tests) rather than needing Fyne's headless test driver. internal/api: tagged every operation (Auth/Platforms/Games/Misc) so the generated OpenAPI spec groups into named services instead of one bucket; api/openapi.json regenerated. Added CORS middleware (wildcard origin, no credentials needed since clients use a bearer token, not the cookie) - needed for the new web client's browser requests to the API's a different origin. web/ (new): an Angular 22 + Material app, API client generated from api/openapi.json via ng-openapi-gen (services: true for per-resource classes - AuthService/GamesService/PlatformsService - instead of one generic Api facade). A Session service holds the token/user as signals, persists the token to localStorage, and is the only thing that calls the generated AuthService; an HttpInterceptorFn attaches the bearer token to every request; a CanActivateFn guard protects /games. Login and Games are standalone, lazy-loaded route components. 21 tests (Vitest, Angular's new default - no browser/Karma needed) covering the service, interceptor, guard and both components, run via `npm test` exactly as CI will call it. Justfile: fixed run-tui/run-gui, which used `go run .../main.go` - broken now that both packages span multiple files, since that only compiles the one named file. Added web-install/web-dev/web-build/web-test. .gitea/workflows/ci.yml (moved from .github, which Gitea doesn't read): on push to main/develop - gofmt, go vet, go build, go test, go mod tidy diff (with the Fyne build's system deps installed), sqlc diff, and the web app's build+test. .gitea/workflows/release.yml (new): on a vX.Y.Z tag, cross-compiles api+tui for linux/darwin/windows (amd64+arm64, pure Go, no cgo), builds the Fyne GUI for linux/amd64 only (cgo + its OpenGL/X11 link means it can't cross-compile from one Linux runner the way api/tui do, and this repo has no registered macOS/Windows runner to build it on natively - documented in the workflow's comments), builds the web app, and creates a Gitea release with all of it attached via the Gitea API. Written to Gitea's documented GitHub-Actions-compatible syntax but not executable from here - there's no Gitea server in this environment to run it against, unlike everything else in this commit, which was. Verified: every Go package builds/vets/tests clean; sqlc diff and go mod tidy -diff are clean; the Angular app builds for production and its test suite passes via `npm test`/`npm run build` (the exact commands CI runs), including after a clean `npm ci` from the committed lockfile; the new CORS config was checked with real preflight and GET requests against a running server, not just read. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
384c4a7217
commit
9972537b63
97 files changed
+11810
-336
No files matched your search
@@ -0,0 +1,81 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
|
||||
jobs:
|
||||
go:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
# cmd/gui (Fyne) uses cgo and links against the system's OpenGL/X11
|
||||
# libraries, so building or testing ./... needs these present even
|
||||
# though the GUI itself never opens a window in CI.
|
||||
- name: Install system dependencies for the Fyne GUI build
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgl1-mesa-dev xorg-dev
|
||||
|
||||
- name: gofmt
|
||||
run: |
|
||||
fmt_out="$(gofmt -l .)"
|
||||
if [ -n "$fmt_out" ]; then
|
||||
echo "The following files are not gofmt'd:"
|
||||
echo "$fmt_out"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: go vet
|
||||
run: go vet ./...
|
||||
|
||||
- name: go build
|
||||
run: go build ./...
|
||||
|
||||
- name: go test
|
||||
run: go test ./...
|
||||
|
||||
- name: go mod tidy (no diff expected)
|
||||
run: |
|
||||
go mod tidy
|
||||
git diff --exit-code -- go.mod go.sum
|
||||
|
||||
sqlc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: sqlc-dev/setup-sqlc@v4
|
||||
with:
|
||||
sqlc-version: "1.31.1"
|
||||
|
||||
- name: sqlc diff (generated code must match the schema/queries)
|
||||
run: sqlc diff
|
||||
|
||||
web:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
cache-dependency-path: web/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
@@ -0,0 +1,168 @@
|
||||
name: Release
|
||||
|
||||
# A pushed tag like v1.2.3 builds every client and attaches the result to a
|
||||
# Gitea release of the same name. Tag a commit that has already passed CI
|
||||
# on main; this workflow doesn't re-run those checks.
|
||||
on:
|
||||
push:
|
||||
tags: ["v*.*.*"]
|
||||
|
||||
jobs:
|
||||
# cmd/api and cmd/tui are pure Go (no cgo), so they cross-compile cleanly
|
||||
# from one Linux runner for every OS/arch below.
|
||||
build-go:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: amd64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Build api and tui
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
CGO_ENABLED: "0"
|
||||
run: |
|
||||
mkdir -p dist
|
||||
ext=""
|
||||
if [ "${{ matrix.goos }}" = "windows" ]; then ext=".exe"; fi
|
||||
go build -o "dist/completed-api${ext}" ./cmd/api
|
||||
go build -o "dist/completed-tui${ext}" ./cmd/tui
|
||||
|
||||
- name: Archive
|
||||
run: |
|
||||
cd dist
|
||||
base="completed_${{ matrix.goos }}_${{ matrix.goarch }}"
|
||||
if [ "${{ matrix.goos }}" = "windows" ]; then
|
||||
zip "../${base}.zip" completed-api.exe completed-tui.exe
|
||||
else
|
||||
tar czf "../${base}.tar.gz" completed-api completed-tui
|
||||
fi
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: completed_${{ matrix.goos }}_${{ matrix.goarch }}
|
||||
path: completed_${{ matrix.goos }}_${{ matrix.goarch }}.*
|
||||
retention-days: 7
|
||||
|
||||
# cmd/gui (Fyne) uses cgo and links the system's native GL/windowing
|
||||
# libraries, so - unlike build-go above - it can't be cross-compiled for
|
||||
# other operating systems from this one Linux runner. This job only
|
||||
# produces a linux/amd64 build. A macOS or Windows build of the GUI would
|
||||
# need this workflow extended with a job running on a matching
|
||||
# self-hosted runner (there isn't one registered for this repo today).
|
||||
build-gui-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgl1-mesa-dev xorg-dev
|
||||
|
||||
- name: Build gui
|
||||
run: |
|
||||
mkdir -p dist
|
||||
go build -o dist/completed-gui ./cmd/gui
|
||||
|
||||
- name: Archive
|
||||
run: tar czf completed-gui_linux_amd64.tar.gz -C dist completed-gui
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: completed-gui_linux_amd64
|
||||
path: completed-gui_linux_amd64.tar.gz
|
||||
retention-days: 7
|
||||
|
||||
build-web:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
cache-dependency-path: web/package-lock.json
|
||||
|
||||
- name: Build
|
||||
working-directory: web
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
|
||||
- name: Archive
|
||||
run: tar czf completed-web_dist.tar.gz -C web/dist/web .
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: completed-web_dist
|
||||
path: completed-web_dist.tar.gz
|
||||
retention-days: 7
|
||||
|
||||
release:
|
||||
needs: [build-go, build-gui-linux, build-web]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
merge-multiple: true
|
||||
|
||||
- name: Create the Gitea release and upload each artifact
|
||||
env:
|
||||
# Gitea Actions auto-provides this for the compatibility GITHUB_*
|
||||
# context; it needs the repo's Settings > Actions permissions set
|
||||
# to allow writing contents. If your instance doesn't populate it,
|
||||
# replace with a secret holding a personal/repo access token that
|
||||
# has repo write access instead.
|
||||
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SERVER_URL: ${{ github.server_url }}
|
||||
REPO: ${{ github.repository }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update && sudo apt-get install -y jq
|
||||
|
||||
release_json=$(curl -sf -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\":\"${TAG}\",\"name\":\"${TAG}\",\"draft\":false,\"prerelease\":false}" \
|
||||
"${SERVER_URL}/api/v1/repos/${REPO}/releases")
|
||||
release_id=$(echo "$release_json" | jq -r '.id')
|
||||
|
||||
if [ -z "$release_id" ] || [ "$release_id" = "null" ]; then
|
||||
echo "Failed to create release: $release_json" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for f in artifacts/*; do
|
||||
name=$(basename "$f")
|
||||
echo "Uploading $name"
|
||||
curl -sf -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-F "attachment=@${f}" \
|
||||
"${SERVER_URL}/api/v1/repos/${REPO}/releases/${release_id}/assets?name=${name}"
|
||||
done
|
||||
Reference in new issue
Block a user