diff --git a/.gitea/workflows/creative-workspace.yml b/.gitea/workflows/creative-workspace.yml index ca7fc48..a5eb2aa 100644 --- a/.gitea/workflows/creative-workspace.yml +++ b/.gitea/workflows/creative-workspace.yml @@ -5,14 +5,15 @@ on: paths: - 'apps/studio/**' - 'apps/waterfall-wiki/**' - - '.gitea/workflows/creative-workspace.yml' + - '.gitea/workflows/**' + - 'scripts/*studio*' concurrency: group: creative-workspace-${{ gitea.event.pull_request.number }} cancel-in-progress: true jobs: verify: runs-on: ces-control - timeout-minutes: 45 + timeout-minutes: 60 steps: - uses: actions/checkout@v4 with: @@ -21,5 +22,13 @@ jobs: run: git diff --check HEAD^ HEAD - name: Verify Studio types, tests, build and smoke run: docker build -f apps/studio/Dockerfile -t "creative-studio-review:${{ gitea.sha }}" . - - name: Verify Wiki types, tests, build and smoke - run: docker build --target build -f apps/waterfall-wiki/Dockerfile -t "creative-wiki-review:${{ gitea.sha }}" . + - name: Build Wiki integration and runtime images + run: | + docker build --target integration -t "creative-wiki-tests:${{ gitea.sha }}" . + docker build --target runtime -t "creative-wiki-review:${{ gitea.sha }}" . + - name: Verify isolated PostgreSQL, S3 and browser integration + env: + WATERFALL_WIKI_IMAGE: creative-wiki-review:${{ gitea.sha }} + WATERFALL_WIKI_TEST_IMAGE: creative-wiki-tests:${{ gitea.sha }} + GITEA_RUN_ID: ${{ gitea.run_id }} + run: bash scripts/integration.sh diff --git a/.gitea/workflows/studio.yml b/.gitea/workflows/studio.yml deleted file mode 100644 index b7bc0a3..0000000 --- a/.gitea/workflows/studio.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: studio CI and deployment -on: - push: - branches: [main] - paths: - - 'apps/studio/**' - - 'scripts/deploy-studio.sh' - - 'scripts/verify-studio-production.sh' - - 'scripts/verify-studio-library.mjs' - - 'scripts/verify-studio-visual.cjs' - - '.gitea/workflows/studio.yml' - - 'scripts/*studio*' - - 'content/studio-examples/**' - - 'content/studio-tools/layouts-api.md' - - 'pnpm-workspace.yaml' - -jobs: - verify-and-deploy: - if: gitea.ref == 'refs/heads/main' && gitea.event_name == 'push' - runs-on: ces-control - timeout-minutes: 45 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Check shared content and documentation contracts - run: | - node scripts/check-studio-contracts.mjs - node --check scripts/export-studio-series.cjs - node --check scripts/save-studio-series.mjs - bash -n scripts/publish-studio-guide.sh scripts/deliver-studio-series.sh - - name: Build and verify studio - run: docker build -f apps/studio/Dockerfile -t "prompt-studio:${{ gitea.sha }}" . - - name: Release studio - if: gitea.ref == 'refs/heads/main' && gitea.event_name == 'push' - run: bash scripts/deploy-studio.sh "${{ gitea.sha }}" - - name: Verify the deployed Studio API and native pages - if: gitea.ref == 'refs/heads/main' && gitea.event_name == 'push' - run: bash scripts/verify-studio-production.sh - - name: Save server acceptance evidence - id: acceptance_evidence - if: always() - uses: actions/upload-artifact@v3 - with: - name: studio-production-acceptance - path: artifacts/studio-acceptance/ - if-no-files-found: ignore - retention-days: 7 - - name: Remove temporary acceptance output after upload - if: always() && steps.acceptance_evidence.outcome == 'success' - run: rm -rf artifacts/studio-acceptance diff --git a/.gitea/workflows/waterfall-wiki.yml b/.gitea/workflows/waterfall-wiki.yml index 945aae9..5172da9 100644 --- a/.gitea/workflows/waterfall-wiki.yml +++ b/.gitea/workflows/waterfall-wiki.yml @@ -1,9 +1,13 @@ -name: waterfall-wiki CI and deployment +name: creative workspace CI and deployment on: push: branches: [main] paths: - 'apps/waterfall-wiki/**' + - 'apps/studio/**' + - 'scripts/*studio*' + - 'content/studio-examples/**' + - 'content/studio-tools/layouts-api.md' - '.gitea/workflows/waterfall-wiki.yml' - 'Dockerfile' - 'compose.yml' @@ -18,11 +22,19 @@ jobs: verify-and-deploy: if: gitea.ref == 'refs/heads/main' && gitea.event_name == 'push' runs-on: ces-control - timeout-minutes: 45 + timeout-minutes: 60 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Check shared content and documentation contracts + run: | + node scripts/check-studio-contracts.mjs + node --check scripts/export-studio-series.cjs + node --check scripts/save-studio-series.mjs + bash -n scripts/publish-studio-guide.sh scripts/deliver-studio-series.sh + - name: Build and verify Studio + run: docker build -f apps/studio/Dockerfile -t "prompt-studio:${{ gitea.sha }}" . - name: Build, verify, deploy and retain wiki images env: RELEASE_SHA: ${{ gitea.sha }} @@ -32,3 +44,19 @@ jobs: else sudo -n python3 scripts/wiki-image-lifecycle.py ci --sha "$RELEASE_SHA" fi + - name: Release Studio after Wiki routes are deployed + run: bash scripts/deploy-studio.sh "${{ gitea.sha }}" + - name: Verify the deployed creative workspace + run: bash scripts/verify-studio-production.sh + - name: Save server acceptance evidence + id: acceptance_evidence + if: always() + uses: actions/upload-artifact@v3 + with: + name: studio-production-acceptance + path: artifacts/studio-acceptance/ + if-no-files-found: ignore + retention-days: 7 + - name: Remove temporary acceptance output after upload + if: always() && steps.acceptance_evidence.outcome == 'success' + run: rm -rf artifacts/studio-acceptance diff --git a/README.md b/README.md index e3e490d..1b6a690 100644 --- a/README.md +++ b/README.md @@ -81,14 +81,14 @@ bash -n scripts/deploy.sh scripts/integration.sh scripts/deploy-studio.sh ## 部署与持久化 -Gitea 工作流按应用路径触发:`waterfall-wiki.yml`、`studio.yml` 和 `python.yml`。waterfall-wiki 完成类型检查、单元测试、生产构建、冒烟和真实 PostgreSQL/S3 集成测试后部署;Studio 独立构建和部署。文档与作者数据本身不触发应用部署。 +Web 应用由 `waterfall-wiki.yml` 统一发布,两个应用的路径都能触发它:先检查并构建 Studio,再完成 Wiki 的类型检查、单元测试、生产构建、冒烟及隔离 PostgreSQL/S3 集成测试并部署 Wiki,随后部署 Studio,最后在指定服务器验收。顺序在同一个 ces-control 作业内保证,避免独立作业互相等待。PR 检查也运行隔离集成测试,但不部署。Python 检查仍由 `python.yml` 承担;文档与作者数据本身不触发应用部署。 | 服务 | 入口/端口 | | --- | --- | | `waterfall-wiki` | 公网 `18131`,也是 Studio 的统一入口 | | PostgreSQL | `127.0.0.1:18132` | | 对象存储 | `127.0.0.1:18133` | -| Studio | `127.0.0.1:18134`;公网通过 `/studio` 代理 | +| Studio | `127.0.0.1:18134`;编辑器公网入口为 `/graphic`、`/infinite_canvas`,资源和 API 保留 `/studio` 代理 | | Python | 无网络端口,按需运行 | 生产 Compose 项目仍为 `prompt`,部署根目录仍为 `/opt/prompt`。既有 `prompt_postgres_data`、`prompt_objects`、`prompt_auth_data` 卷与数据库身份保留;应用更名不会重新创建一套空存储。Compose 应用服务改为 `waterfall-wiki` 和 `waterfall-wiki-worker`,镜像参数为 `WATERFALL_WIKI_IMAGE` / `WATERFALL_WIKI_TEST_IMAGE`。 diff --git a/apps/waterfall-wiki/tests/guidance-integration.ts b/apps/waterfall-wiki/tests/guidance-integration.ts index 373dbe7..ff8f3ea 100644 --- a/apps/waterfall-wiki/tests/guidance-integration.ts +++ b/apps/waterfall-wiki/tests/guidance-integration.ts @@ -30,12 +30,12 @@ export async function verifyGuidancePublishing(base:string){ const own=await fetch(base+'/api/documents?author='+owner).then(r=>r.json());assert.ok(own.documents.some((d:{id:string})=>d.id===httpDoc.id));assert.ok(own.documents.every((d:{author:string})=>d.author===owner));assert.equal((await fetch(base+'/api/documents?author=invalid')).status,400); const xml=await fetch(base+'/api/documents/'+httpDoc.id+'?revision=1&format=xml');assert.equal(xml.status,200);assert.match(await xml.text(),/往返标签<\/tag>/); const page=await fetch(base+'/documents/'+httpDoc.id+'?revision=1');assert.equal(page.status,200);assert.ok((await page.text()).includes('实际数据库文档')); - // The homepage and discovery list hydrate from the real API, so verify them in Chromium on the CI runner. + // The document discovery list hydrates from the real API; the homepage is the product landing page. const browser=await chromium.launch({executablePath:process.env.CHROMIUM_PATH||'/usr/bin/chromium',headless:true,args:['--no-sandbox','--disable-dev-shm-usage']}); try{ const page=await browser.newPage(); const link=`a[href="/documents/${httpDoc.id}?revision=1"]`; - await page.goto(base+'/',{waitUntil:'domcontentloaded'}); + await page.goto(base+'/documents',{waitUntil:'domcontentloaded'}); await page.locator(link).waitFor({state:'visible'});assert.equal(await page.locator(link).getByRole('heading').textContent(),httpDoc.title); // Published matches must not inherit the examples' empty state; submitted filters survive reload. const search=page.getByRole('textbox',{name:'搜索文档'}); diff --git a/scripts/deploy-studio.sh b/scripts/deploy-studio.sh index 7b11d3e..9df86c3 100644 --- a/scripts/deploy-studio.sh +++ b/scripts/deploy-studio.sh @@ -8,7 +8,7 @@ flock -w 300 9 latest=$(git -c safe.directory="$PWD" ls-remote origin refs/heads/main | cut -f1) if [ "$latest" != "$sha" ]; then git -c safe.directory="$PWD" fetch origin main - git -c safe.directory="$PWD" diff --quiet "$sha" origin/main -- apps/studio scripts/deploy-studio.sh .gitea/workflows/studio.yml pnpm-workspace.yaml || exit 0 + git -c safe.directory="$PWD" diff --quiet "$sha" origin/main -- apps/studio scripts/deploy-studio.sh .gitea/workflows/waterfall-wiki.yml pnpm-workspace.yaml || exit 0 fi mkdir -p /opt/prompt-studio previous=$(cat /opt/prompt-studio/image 2>/dev/null || true) diff --git a/scripts/verify-studio-production.sh b/scripts/verify-studio-production.sh index 35ef390..72c934a 100644 --- a/scripts/verify-studio-production.sh +++ b/scripts/verify-studio-production.sh @@ -1,13 +1,12 @@ #!/usr/bin/env bash # Run on ces-control after deployment; never starts a preview web server. set -Eeuo pipefail -# Wiki and Studio deploy independently; wait for Wiki's canonical route proxy. -canonical_ready=false -for attempt in {1..120}; do - if curl -fsS http://8.211.154.134:18131/ | grep -q '想法不该停在对话里'; then canonical_ready=true; break; fi - sleep 5 -done -$canonical_ready || { echo 'Wiki landing and canonical route release is not ready'; exit 1; } +# The shared release job deploys Wiki before Studio; a missing route is an error, +# not a reason to occupy this runner while another queued job tries to deploy it. +if ! curl -fsS http://8.211.154.134:18131/ | grep -q '想法不该停在对话里'; then + echo 'The shared release has not deployed the Wiki landing and canonical routes' + exit 1 +fi REPO_ROOT=$(git rev-parse --show-toplevel) RESULT_DIR="$REPO_ROOT/artifacts/studio-acceptance" MODE=${1:-verify}