Skip to content

feat(mcp-ui): next-step UX after register / install / on /connections… #334

feat(mcp-ui): next-step UX after register / install / on /connections…

feat(mcp-ui): next-step UX after register / install / on /connections… #334

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
rust:
name: Rust Check & Test
runs-on: ubuntu-latest
services:
postgres:
image: postgres:18-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: think_watch_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 5
redis:
image: redis:8-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 5s
--health-retries 5
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/think_watch_test
REDIS_URL: redis://localhost:6379
JWT_SECRET: ci-test-jwt-secret-not-for-production-use-00
ENCRYPTION_KEY: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: cargo-nextest
# No standalone `cargo check` — clippy runs the borrow-checker
# as a strict superset, so a separate check pass would just
# rebuild the same artifacts twice.
- name: Test
run: cargo nextest run --workspace --lib --bins --tests
- name: Clippy
run: cargo clippy --workspace -- -D warnings
- name: Format check
run: cargo fmt --all -- --check
frontend:
name: Frontend Build
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: latest
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: i18n parity
run: pnpm check:i18n
- name: Test
run: pnpm test
# `pnpm build` runs `tsc -b && vite build`. `tsc -b` (project
# references) is stricter than `tsc --noEmit` — it catches
# unused imports (TS6133) that --noEmit silently accepts — so
# we rely on the build step for type checking.
- name: Build
run: pnpm build
# ---------------------------------------------------------------------------
# Docker Server: build each arch natively in parallel, then merge manifest
# ---------------------------------------------------------------------------
docker-server-build:
name: Server Build (${{ matrix.platform }})
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
needs: [rust, frontend]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
platform: [linux/amd64, linux/arm64]
steps:
- uses: actions/checkout@v4
- name: Set image prefix
run: echo "IMAGE_PREFIX=ghcr.io/$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_PREFIX }}/think-watch-server
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
file: deploy/docker/Dockerfile.server
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,"name=${{ env.IMAGE_PREFIX }}/think-watch-server",push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=server-${{ matrix.platform }}
cache-to: type=gha,scope=server-${{ matrix.platform }},mode=max
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: server-digest-${{ strategy.job-index }}
path: ${{ runner.temp }}/digests
if-no-files-found: error
retention-days: 1
docker-server-merge:
name: Server Merge Manifest
runs-on: ubuntu-latest
needs: docker-server-build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
steps:
- name: Set image prefix
run: echo "IMAGE_PREFIX=ghcr.io/$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
- name: Download digests
uses: actions/download-artifact@v4
with:
pattern: server-digest-*
merge-multiple: true
path: ${{ runner.temp }}/digests
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Create and push multi-arch manifest
working-directory: ${{ runner.temp }}/digests
run: |
IMAGE="${IMAGE_PREFIX}/think-watch-server"
docker buildx imagetools create \
-t "${IMAGE}:latest" \
-t "${IMAGE}:${{ github.sha }}" \
$(printf "${IMAGE}@sha256:%s " *)
# ---------------------------------------------------------------------------
# Docker Web: static files are arch-independent, single buildx with QEMU
# ---------------------------------------------------------------------------
docker-web:
name: Web Build & Push
runs-on: ubuntu-latest
needs: [rust, frontend]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set image prefix
run: echo "IMAGE_PREFIX=ghcr.io/$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push web image
uses: docker/build-push-action@v6
with:
context: .
file: deploy/docker/Dockerfile.web
push: true
platforms: linux/amd64,linux/arm64
tags: |
${{ env.IMAGE_PREFIX }}/think-watch-web:latest
${{ env.IMAGE_PREFIX }}/think-watch-web:${{ github.sha }}
cache-from: type=gha,scope=web
cache-to: type=gha,scope=web,mode=max