feat(build): embed web UI in backend binary + simplify Docker variants (#620)

- Add internal/webui/ package with //go:build embedui tag for optional
  SPA embedding (handler.go serves static files with SPA fallback)
- Add internal/version/ shared semver comparison (DRY: extracted from
  gateway/update_check.go and updater/updater.go)
- Enhance UpdateChecker: release notes, ETag caching, filter lite-v* tags
- Add web UI build stage to Dockerfile with ENABLE_EMBEDUI build arg
- Simplify CI: 7 Docker variants → 4 (base, latest, full, otel)
- Add SHA256 checksums job to release workflow
- Add Makefile build-full target (embeds web UI in Go binary)
- Default make up now embeds web UI (no separate nginx needed)
- Add WITH_WEB_NGINX=1 flag for optional nginx reverse proxy
- Update README + 30 translated READMEs: make up, port 18790
- Update docker-compose comments and prepare-env.sh
- About dialog: show release notes with markdown rendering
- Health card: amber badge for available updates

BREAKING: Default Docker setup no longer requires selfservice overlay.
Web dashboard served at :18790 (same port as API).
This commit is contained in:
Viet Tran
2026-04-01 15:25:59 +07:00
committed by GitHub
parent 39bb90bd60
commit 52c67d6d92
56 changed files with 531 additions and 381 deletions
+15 -47
View File
@@ -19,63 +19,33 @@ jobs:
strategy:
matrix:
include:
# ── Runtime variants ──
# Base: backend API-only, no web UI, no runtimes
- variant: base
suffix: "-base"
enable_otel: "false"
enable_embedui: "false"
enable_python: "false"
enable_full_skills: "false"
# Latest: backend + embedded web UI + Python
- variant: latest
suffix: ""
enable_otel: "false"
enable_tsnet: "false"
enable_redis: "false"
enable_node: "false"
enable_python: "false"
enable_full_skills: "false"
- variant: node
suffix: "-node"
enable_otel: "false"
enable_tsnet: "false"
enable_redis: "false"
enable_node: "true"
enable_python: "false"
enable_full_skills: "false"
- variant: python
suffix: "-python"
enable_otel: "false"
enable_tsnet: "false"
enable_redis: "false"
enable_node: "false"
enable_embedui: "true"
enable_python: "true"
enable_full_skills: "false"
# Full: all runtimes + skills pre-installed
- variant: full
suffix: "-full"
enable_otel: "false"
enable_tsnet: "false"
enable_redis: "false"
enable_node: "true"
enable_embedui: "true"
enable_python: "true"
enable_full_skills: "true"
# ── Build-tag variants ──
# OTel: latest + OpenTelemetry tracing
- variant: otel
suffix: "-otel"
enable_otel: "true"
enable_tsnet: "false"
enable_redis: "false"
enable_node: "false"
enable_python: "false"
enable_full_skills: "false"
- variant: tsnet
suffix: "-tsnet"
enable_otel: "false"
enable_tsnet: "true"
enable_redis: "false"
enable_node: "false"
enable_python: "false"
enable_full_skills: "false"
- variant: redis
suffix: "-redis"
enable_otel: "false"
enable_tsnet: "false"
enable_redis: "true"
enable_node: "false"
enable_python: "false"
enable_embedui: "true"
enable_python: "true"
enable_full_skills: "false"
steps:
@@ -124,9 +94,7 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
build-args: |
ENABLE_OTEL=${{ matrix.enable_otel }}
ENABLE_TSNET=${{ matrix.enable_tsnet }}
ENABLE_REDIS=${{ matrix.enable_redis }}
ENABLE_NODE=${{ matrix.enable_node }}
ENABLE_EMBEDUI=${{ matrix.enable_embedui }}
ENABLE_PYTHON=${{ matrix.enable_python }}
ENABLE_FULL_SKILLS=${{ matrix.enable_full_skills }}
VERSION=${{ github.ref_name }}
+59 -46
View File
@@ -53,13 +53,24 @@ jobs:
go-version-file: go.mod
cache-dependency-path: go.sum
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Build web UI
run: |
corepack enable && corepack prepare pnpm@10.28.2 --activate
cd ui/web && pnpm install --frozen-lockfile && pnpm build && cd ../..
mkdir -p internal/webui/dist
cp -r ui/web/dist/* internal/webui/dist/
- name: Build binary
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
VERSION: v${{ needs.release.outputs.version }}
run: |
CGO_ENABLED=0 go build \
CGO_ENABLED=0 go build -tags embedui \
-ldflags="-s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=${VERSION}" \
-o goclaw .
tar -czf "goclaw-${{ needs.release.outputs.version }}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz" goclaw migrations/
@@ -72,6 +83,38 @@ jobs:
"goclaw-${{ needs.release.outputs.version }}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz" \
--clobber
# Generate SHA256 checksums for all binary assets
checksums:
needs: [release, build-binaries]
if: needs.release.outputs.released == 'true'
runs-on: ubuntu-latest
steps:
- name: Download all binary assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mkdir -p assets
gh release download "v${{ needs.release.outputs.version }}" \
--repo "${{ github.repository }}" \
--pattern "goclaw-*.tar.gz" \
--dir assets
- name: Generate checksums
run: |
cd assets
sha256sum goclaw-*.tar.gz > CHECKSUMS.sha256
echo "Generated checksums:"
cat CHECKSUMS.sha256
- name: Upload checksums to release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "v${{ needs.release.outputs.version }}" \
assets/CHECKSUMS.sha256 \
--repo "${{ github.repository }}" \
--clobber
# Build and push Docker images to GHCR + Docker Hub
docker-images:
needs: release
@@ -80,61 +123,33 @@ jobs:
strategy:
matrix:
include:
# Base: backend API-only, no web UI, no runtimes
- variant: base
suffix: "-base"
enable_otel: "false"
enable_embedui: "false"
enable_python: "false"
enable_full_skills: "false"
# Latest: backend + embedded web UI + Python
- variant: latest
suffix: ""
enable_otel: "false"
enable_tsnet: "false"
enable_redis: "false"
enable_node: "false"
enable_python: "false"
enable_full_skills: "false"
- variant: node
suffix: "-node"
enable_otel: "false"
enable_tsnet: "false"
enable_redis: "false"
enable_node: "true"
enable_python: "false"
enable_full_skills: "false"
- variant: python
suffix: "-python"
enable_otel: "false"
enable_tsnet: "false"
enable_redis: "false"
enable_node: "false"
enable_embedui: "true"
enable_python: "true"
enable_full_skills: "false"
# Full: all runtimes + skills pre-installed
- variant: full
suffix: "-full"
enable_otel: "false"
enable_tsnet: "false"
enable_redis: "false"
enable_node: "true"
enable_embedui: "true"
enable_python: "true"
enable_full_skills: "true"
# OTel: latest + OpenTelemetry tracing
- variant: otel
suffix: "-otel"
enable_otel: "true"
enable_tsnet: "false"
enable_redis: "false"
enable_node: "false"
enable_python: "false"
enable_full_skills: "false"
- variant: tsnet
suffix: "-tsnet"
enable_otel: "false"
enable_tsnet: "true"
enable_redis: "false"
enable_node: "false"
enable_python: "false"
enable_full_skills: "false"
- variant: redis
suffix: "-redis"
enable_otel: "false"
enable_tsnet: "false"
enable_redis: "true"
enable_node: "false"
enable_python: "false"
enable_embedui: "true"
enable_python: "true"
enable_full_skills: "false"
steps:
- uses: actions/checkout@v4
@@ -178,9 +193,7 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
build-args: |
ENABLE_OTEL=${{ matrix.enable_otel }}
ENABLE_TSNET=${{ matrix.enable_tsnet }}
ENABLE_REDIS=${{ matrix.enable_redis }}
ENABLE_NODE=${{ matrix.enable_node }}
ENABLE_EMBEDUI=${{ matrix.enable_embedui }}
ENABLE_PYTHON=${{ matrix.enable_python }}
ENABLE_FULL_SKILLS=${{ matrix.enable_full_skills }}
VERSION=v${{ needs.release.outputs.version }}