mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
5fe0843eb2
* feat: add routable browser URLs for stacks and shell views Sync in-memory navigation to the address bar via a History API hook so deep links, refresh, Back/Forward, and bookmarks work across nodes, views, stack editor tabs, and mobile surfaces. Gate role/tier URL normalization on permissions and license readiness, preserve URLs on metadata fetch failure, and surface retryable stack-list errors without rewriting pending stack paths. * fix: preserve deep-link views on cold load and refresh Stop the node-switch effect from resetting to dashboard on initial mount. Defer URL writer settlement until hydrated activeView matches the route. Adds E2E coverage for shell cold loads, stack refresh, and compose env tab. * fix: keep mobile dashboard on list surface so sidebar renders On mobile, the URL sync hook was routing /nodes/<slug>/dashboard to the content surface, hiding the stack list sidebar. This prevented the data-stacks-loaded sentinel from appearing, causing sidebar truncation E2E tests to time out after reload on a mobile viewport. Mobile dashboard now stays on the list surface; other non-editor views still render on the content surface. * fix: complete mobile URL routing follow-ups for stack deep links Restore mobile /dashboard vs /stacks, list surface always writes /stacks. Hydrate pendingDetailStack, freeze compose failures with routeDetailError, and add unit plus E2E coverage. * fix: hydrate shell views from URL and sync in-app navigation Bootstrap activeView and tab state from the pathname on cold load. Settle route phase when state already matches, normalize unknown segments, and open Monaco editor tabs from stack deep links via applyEditorRouteState. * fix: prevent mobile stack deep links from hanging on cold load The resolvePendingStack effect did not re-fire when the pending stack ref was populated during URL hydration, because the urlHydratingStack state set in the same callback was not listed in the effect's dependency array. Adding it causes the effect to retry once hydration has committed. A resolvingRef mutex prevents concurrent invocations. When the target file is already loaded, route state is applied directly without calling loadFileForRoute, which avoids unmounting the editor (and hiding the recovery chip) if a background refresh triggers route resolution during a deploy operation. * test: adapt stack, deploy, and sidebar e2e specs to routable stack URLs * ci: raise E2E Playwright job timeout to 20 minutes
249 lines
9.0 KiB
YAML
249 lines
9.0 KiB
YAML
name: Sencho CI
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
# Cancel previous runs on the same branch/PR
|
|
concurrency:
|
|
group: ci-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
# Least-privilege default for every job.
|
|
permissions:
|
|
contents: read
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Build & Validation (PRs only, skipped for release-please PRs)
|
|
# ---------------------------------------------------------------------------
|
|
jobs:
|
|
backend:
|
|
name: Backend (Build, Test, Lint)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
# Skip on the release-please bot PR. It only touches version metadata,
|
|
# so the full build+test+lint+audit stack is pure action-minute waste.
|
|
if: >-
|
|
github.event_name == 'pull_request'
|
|
&& github.head_ref != 'release-please--branches--main--components--sencho'
|
|
steps:
|
|
- name: Checkout Code
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
|
with:
|
|
node-version-file: '.node-version'
|
|
cache: 'npm'
|
|
cache-dependency-path: backend/package-lock.json
|
|
|
|
- name: Install Dependencies
|
|
working-directory: ./backend
|
|
run: npm ci
|
|
|
|
- name: Build (TypeScript)
|
|
working-directory: ./backend
|
|
run: npm run build
|
|
|
|
- name: Unit Tests (Vitest)
|
|
working-directory: ./backend
|
|
run: npm test
|
|
|
|
- name: Lint (ESLint)
|
|
working-directory: ./backend
|
|
run: npm run lint
|
|
|
|
- name: Audit Dependencies
|
|
working-directory: ./backend
|
|
run: npm audit --audit-level=high
|
|
|
|
# Hand the freshly-verified dist/ to the E2E job so it does not have to
|
|
# re-run `tsc` on the same source. retention-days is the minimum; this
|
|
# artifact is only needed for the downstream e2e job in the same run.
|
|
- name: Upload backend dist
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: backend-dist
|
|
path: backend/dist
|
|
retention-days: 1
|
|
if-no-files-found: error
|
|
|
|
frontend:
|
|
name: Frontend (Build, Lint)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
# See backend job rationale: skip bot PRs that cannot affect the frontend.
|
|
if: >-
|
|
github.event_name == 'pull_request'
|
|
&& github.head_ref != 'release-please--branches--main--components--sencho'
|
|
steps:
|
|
- name: Checkout Code
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
|
with:
|
|
node-version-file: '.node-version'
|
|
cache: 'npm'
|
|
cache-dependency-path: frontend/package-lock.json
|
|
|
|
- name: Install Dependencies
|
|
working-directory: ./frontend
|
|
run: npm ci
|
|
|
|
- name: Build (Vite/React)
|
|
working-directory: ./frontend
|
|
run: npm run build
|
|
|
|
- name: Unit Tests (Vitest)
|
|
working-directory: ./frontend
|
|
run: npm test
|
|
|
|
- name: Lint (ESLint)
|
|
working-directory: ./frontend
|
|
run: npm run lint
|
|
|
|
- name: Audit Dependencies
|
|
working-directory: ./frontend
|
|
run: npm audit --audit-level=high
|
|
|
|
docker-validate:
|
|
name: Docker Build & Scan
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
# See backend job rationale.
|
|
if: >-
|
|
github.event_name == 'pull_request'
|
|
&& github.head_ref != 'release-please--branches--main--components--sencho'
|
|
steps:
|
|
- name: Checkout Code
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
|
|
|
# Computed once per job run. Feeds Dockerfile's APK_CACHE_BUST arg so
|
|
# the `apk upgrade` layer rebuilds at least once per calendar day, even
|
|
# when every other input to the layer is cached. See Dockerfile:115-122
|
|
# for the rationale.
|
|
- name: Compute daily apk cache bust value
|
|
id: apk-bust
|
|
run: echo "date=$(date -u +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Build Docker image (validation only)
|
|
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
|
|
with:
|
|
context: .
|
|
push: false
|
|
load: true
|
|
tags: sencho:pr-test
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
build-args: |
|
|
APK_CACHE_BUST=${{ steps.apk-bust.outputs.date }}
|
|
|
|
- name: Scan image for vulnerabilities (Trivy)
|
|
# Hard-fails the PR on any HIGH or CRITICAL finding not suppressed by
|
|
# the OpenVEX document at security/vex/sencho.openvex.json (loaded via
|
|
# trivy.yaml). The VEX document is the canonical triage record and is
|
|
# also attached as a cosign attestation on the published image.
|
|
# If a new CVE must be triaged, add a VEX statement with a
|
|
# justification — do NOT recreate .trivyignore.
|
|
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
|
with:
|
|
image-ref: sencho:pr-test
|
|
exit-code: '1'
|
|
severity: 'CRITICAL,HIGH'
|
|
format: 'table'
|
|
trivy-config: trivy.yaml
|
|
|
|
# Mirror of the release-time pre-publish smoke gate in docker-publish.yml.
|
|
# Catches the class of failure where the source-built Docker CLI or
|
|
# Compose plugin compiles cleanly but does not exec at runtime (e.g. -o
|
|
# pointed at a non-main Go package, ABI breakage in a base image bump).
|
|
# The release workflow additionally polls /api/health; PR CI keeps just
|
|
# the binary check to bound job duration without losing the gate.
|
|
- name: Smoke test built image (binaries)
|
|
run: |
|
|
set -euo pipefail
|
|
docker run --rm --entrypoint sh sencho:pr-test -c \
|
|
'docker --version && docker compose version'
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# E2E Tests (PRs only, skipped for release-please PRs)
|
|
# ---------------------------------------------------------------------------
|
|
e2e:
|
|
name: E2E Tests (Playwright)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
needs: [backend, frontend]
|
|
# See backend job rationale.
|
|
if: >-
|
|
github.event_name == 'pull_request'
|
|
&& github.head_ref != 'release-please--branches--main--components--sencho'
|
|
steps:
|
|
- name: Checkout Code
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
# Reuse the dist/ that the `backend` job produced and verified, instead
|
|
# of running `tsc` a second time against the same source tree. The
|
|
# composite action's `skip-backend-build` input short-circuits its build
|
|
# step when this artifact is already in place.
|
|
- name: Download backend dist
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
|
with:
|
|
name: backend-dist
|
|
path: backend/dist
|
|
|
|
- name: Start app & install Playwright
|
|
uses: ./.github/actions/start-app
|
|
with:
|
|
skip-backend-build: 'true'
|
|
|
|
- name: Run E2E tests
|
|
# `--project=chromium` is explicit because playwright.config.ts also
|
|
# defines a `screenshots` project that captures docs/images/ for
|
|
# manual review. That project must not run in CI: its output would
|
|
# be discarded after the run, wasting minutes per PR. Keep this
|
|
# flag; do not "simplify" back to `npx playwright test`.
|
|
run: npx playwright test --project=chromium
|
|
|
|
- name: Upload E2E report and service logs
|
|
if: failure()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: playwright-report
|
|
path: |
|
|
e2e/report/
|
|
test-results/
|
|
ci-logs/
|
|
retention-days: 7
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Changelog Credit Script (PRs only, skipped for release-please PRs)
|
|
# ---------------------------------------------------------------------------
|
|
changelog-script:
|
|
name: Changelog credit script
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
if: >-
|
|
github.event_name == 'pull_request'
|
|
&& github.head_ref != 'release-please--branches--main--components--sencho'
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
|
with:
|
|
node-version-file: '.node-version'
|
|
|
|
- name: Validate workflows
|
|
uses: docker://rhysd/actionlint@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667 # v1.7.12
|
|
with:
|
|
args: .github/workflows/release-please.yml .github/workflows/ci.yml
|
|
|
|
- name: Syntax check
|
|
run: node --check scripts/credit-changelog-contributors.mjs
|
|
|
|
- name: Unit tests
|
|
run: node --test scripts/credit-changelog-contributors.test.mjs
|