diff --git a/.github/workflows/preview-pages.yml b/.github/workflows/preview-pages.yml new file mode 100644 index 0000000..f152b7e --- /dev/null +++ b/.github/workflows/preview-pages.yml @@ -0,0 +1,49 @@ +name: Preview Pages +on: + workflow_dispatch: +permissions: {} +concurrency: + group: site-data-${{ github.repository }} + cancel-in-progress: false +jobs: + build: + if: github.repository == 'shendeguize/AgentOrganon' && github.ref == 'refs/heads/main' + runs-on: ubuntu-24.04 + timeout-minutes: 30 + env: + ORGANON_SITE_PREVIEW: 'true' + permissions: + contents: read + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + with: + path: product + submodules: recursive + persist-credentials: false + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 + with: + node-version: '22.23.2' + - name: Install locked site build dependencies + working-directory: product/OrganonCore/tools/site + run: npm ci --ignore-scripts + - name: Build and check current site preview + working-directory: product + env: + ORGANON_CORE_ROOT: ${{ github.workspace }}/product/OrganonCore + run: npm run check:site + - uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa + with: + path: product/dist/site/ + deploy: + needs: build + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e + id: deployment \ No newline at end of file diff --git a/AdvisedOrganons b/AdvisedOrganons index 6b32ede..bfa4992 160000 --- a/AdvisedOrganons +++ b/AdvisedOrganons @@ -1 +1 @@ -Subproject commit 6b32edea1619e740e229864da757c25d36096bc1 +Subproject commit bfa4992f492d431aec3d056cc98ab378b22033f9 diff --git a/OrganonCore b/OrganonCore index 77c1ef0..28f5a01 160000 --- a/OrganonCore +++ b/OrganonCore @@ -1 +1 @@ -Subproject commit 77c1ef086cc2eb55d192059e97658abc5ca7f95f +Subproject commit 28f5a01274489ae6795e6b76b34c74426b16d550 diff --git a/maintenance/pages-and-stars.md b/maintenance/pages-and-stars.md index 2c5ccd1..1946548 100644 --- a/maintenance/pages-and-stars.md +++ b/maintenance/pages-and-stars.md @@ -1,6 +1,6 @@ # Pages and observed GitHub stars -This repository publishes its site from the independent `site-data` branch. `main` CI produces development artifacts. It does not deploy them. +Verified releases use the independent `site-data` branch. `main` CI produces development artifacts. **Preview Pages** (`preview-pages.yml`) may be dispatched manually from `main` to build, check and deploy the current source without a release receipt. A preview is not a verified release, does not update `site-data` or star history, and may be replaced by a later preview or verified release. Deploying it replaces the site currently served at the public Pages URL until another deployment succeeds. ## Publish a released site diff --git a/tests/release-site-data.test.mjs b/tests/release-site-data.test.mjs index 23712b9..4c55fb4 100644 --- a/tests/release-site-data.test.mjs +++ b/tests/release-site-data.test.mjs @@ -3,9 +3,11 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import { PRODUCTS, digest, writeJSON } from '../scripts/release/lib.mjs'; import { sourceIdentity } from '../scripts/release/manifest.mjs'; import { assertSiteRunner, inventory, validateState, sampleStars, installPublic, buildIdentity, verifyForSite, checkoutState } from '../scripts/release/site-data.mjs'; +const workspace = fileURLToPath(new URL('../', import.meta.url)); const repository = 'shendeguize/AgentOrganon'; function temporary(t) { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'organon-site-state-')); t.after(() => fs.rmSync(root, { recursive: true, force: true })); return root; } function manifest(version = '1.0.0-rc.1') { @@ -83,3 +85,39 @@ test('data branch distinguishes first creation from existing branch and network assert(calls.find(args=>args.includes('ls-remote')).includes('refs/heads/site-data')); } }); + +test('preview Pages deploy current main without creating verified release state', () => { + const workflows = [ + ['', 'shendeguize/AgentOrganon', 'product/dist/site/'], + ['OrganonCore', 'shendeguize/OrganonCore', 'product/dist/site/'], + ['AdvisedOrganons', 'shendeguize/AdvisedOrganons', 'product/dist/site/'], + ]; + for (const [relative, expectedRepository, output] of workflows) { + const workflow = fs.readFileSync(path.join(workspace, relative, '.github/workflows/preview-pages.yml'), 'utf8'); + assert.match(workflow, /^name: Preview Pages\non:\n workflow_dispatch:\npermissions: \{\}\n/); + assert.equal((workflow.match(/^ workflow_dispatch:$/gm) || []).length, 1); + assert.doesNotMatch(workflow, /^ (push|pull_request|schedule|workflow_call):/m); + assert.match(workflow, new RegExp(` build:\n if: github\\.repository == '${expectedRepository}' && github\\.ref == 'refs/heads/main'`)); + assert.match(workflow, /group: site-data-\$\{\{ github\.repository \}\}/); + assert.match(workflow, /ORGANON_SITE_PREVIEW: 'true'/); + assert.equal((workflow.match(/permissions:/g) || []).length, 3); + assert.match(workflow, /build:[\s\S]*?permissions:\n contents: read/); + assert.match(workflow, /deploy:[\s\S]*?permissions:\n pages: write\n id-token: write/); + const actions = [...workflow.matchAll(/uses: ([^\s]+)/g)].map(match => match[1]); + assert(actions.every(action => /@[a-f0-9]{40}$/.test(action))); + assert(actions.includes('actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa')); + assert(actions.includes('actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e')); + assert.match(workflow, /actions\/checkout@[a-f0-9]{40}[\s\S]*?persist-credentials: false/); + assert.match(workflow, /deploy:\n needs: build/); + assert.match(workflow, new RegExp(`path: ${output.replaceAll('/', '\\/')}`)); + assert(workflow.indexOf('run: npm run check:site') < workflow.indexOf('actions/upload-pages-artifact@')); + assert.doesNotMatch(workflow, /contents: write|site-data\.mjs|GOVERNANCE_AUDIT_TOKEN|release-manifest/); + } + const builder = fs.readFileSync(path.join(workspace, 'OrganonCore/tools/site/build.mjs'), 'utf8'); + const layout = fs.readFileSync(path.join(workspace, 'OrganonCore/tools/site/theme/Layout.vue'), 'utf8'); + assert.match(builder, /preview: process\.env\.ORGANON_SITE_PREVIEW === 'true'/); + assert.match(layout, /Source preview · not a release/); + assert.match(layout, /源码预览 · 非发行版/); + assert.match(layout, /Release candidate · awaiting review/); + assert.match(layout, /Stable release/); +}); diff --git a/zh/maintenance/pages-and-stars.md b/zh/maintenance/pages-and-stars.md index 195ca77..9585a16 100644 --- a/zh/maintenance/pages-and-stars.md +++ b/zh/maintenance/pages-and-stars.md @@ -1,6 +1,6 @@ # Pages 与 GitHub 星标实测 -本仓库从独立的 `site-data` 分支发布站点。`main` CI 生成开发产物,不部署这些产物。 +已验证发行版使用独立的 `site-data` 分支。`main` CI 生成开发产物。可以从 `main` 手动运行 **Preview Pages**(`preview-pages.yml`),构建并检查当前源码后直接部署,但不生成发行收据。预览站点不是已验证发行版,不更新 `site-data` 或星标历史,并可被后续预览或已验证发行版替换。部署预览会替换 Pages 公开地址当前提供的站点,直至另一次部署成功。 ## 发布已发行站点