Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,17 @@ jobs:
- name: Check TypeScript
run: npm.cmd run check

- name: Check project site TypeScript
run: npm.cmd run check:site

- name: Run frontend tests
run: npm.cmd run test:frontend

- name: Build frontend
run: npm.cmd run build

- name: Build project site
run: npm.cmd run build:site

- name: Run Rust tests
run: cargo test --locked --manifest-path src-tauri/Cargo.toml
73 changes: 73 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Pages

on:
push:
branches:
- master
paths:
- ".github/workflows/pages.yml"
- "package.json"
- "package-lock.json"
- "site/**"
- "src/assets/eraw-icon.svg"
- "tests/site.test.mjs"
- "vite.pages.config.ts"
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
name: Build project site
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22
cache: npm
cache-dependency-path: package-lock.json

- name: Configure GitHub Pages
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0

- name: Install dependencies
run: npm ci

- name: Check project site TypeScript
run: npm run check:site

- name: Run project site tests
run: npm run test:site

- name: Build project site
run: npm run build:site

- name: Upload GitHub Pages artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: ./dist-site

deploy:
name: Deploy project site
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Deploy GitHub Pages
id: deployment
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
2 changes: 2 additions & 0 deletions docs/PUBLICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ GitHub 免费私有仓库不开放以下配置;可见性切换后应在同一
5. 确认 fork Pull Request 的工作流需要首次贡献者批准,且不会向 fork 暴露 secrets。
6. 检查 About 区域、Topics、Social preview、README 徽章和 Latest Release 的公开显示。
7. 从未登录会话检查 README、Releases、Issues、Security policy 与下载资产。
8. 将 GitHub Pages 的构建来源设为 GitHub Actions,并由 `.github/workflows/pages.yml` 发布 `dist-site/`。

## 发布与供应链

- GitHub Actions 使用允许列表并固定到完整 commit SHA;Dependabot 负责跟踪 Action 更新。
- 默认 `GITHUB_TOKEN` 为只读;只有 Release job 获得 `contents: write`,公开时的 attestation step 另获 `id-token: write` 与 `attestations: write`。
- Pages 工作流仅获得 `contents: read`、`pages: write` 与 `id-token: write`,只部署静态站点产物,不创建 tag 或 Release。
- Release EXE 目前没有商业代码签名证书。SHA-256、SBOM 与构建来源证明用于完整性和来源核验,但不能替代 Windows Authenticode。
- 历史 Release 保留用于追溯;新用户应下载 Latest Release。

Expand Down
5 changes: 3 additions & 2 deletions site/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
```powershell
npm.cmd run dev:site
npm.cmd run check:site
npm.cmd run test:site
npm.cmd run build:site
```

开发服务器固定使用 `http://127.0.0.1:4174/eRAW/`,避免落入 Windows 可能保留的默认 Vite 端口范围
开发服务器默认使用 `http://127.0.0.1:44174/eRAW/`。如果该端口在本机被占用或保留,可以通过 `npm.cmd run dev:site -- --port <端口>` 临时覆盖

- 本地开发入口由 `vite.pages.config.ts` 管理。
- 生产构建输出到被 Git 忽略的 `dist-site/`。
- GitHub Pages 发布暂未启用;等仓库公开且中英文页面通过最终审阅后再增加部署工作流
- GitHub Pages 由 `.github/workflows/pages.yml` 构建并部署;对站点相关文件的 `master` 推送会自动发布,也可以手动触发
2 changes: 1 addition & 1 deletion site/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

* { box-sizing: border-box; }
html { background: var(--page); scroll-behavior: smooth; }
body { min-width: 320px; margin: 0; background: var(--page); color: var(--text); font-size: 14px; line-height: 1.65; }
body { margin: 0; background: var(--page); color: var(--text); font-size: 14px; line-height: 1.65; }
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
.page-width { width: min(1344px, calc(100% - 64px)); margin-inline: auto; }
Expand Down
15 changes: 13 additions & 2 deletions tests/repository-publication.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const [
helpWindowSource,
ciSource,
releaseSource,
pagesSource,
dependabotSource,
contributingSource,
securitySource,
Expand All @@ -30,6 +31,7 @@ const [
read("src/help-window.ts"),
read(".github/workflows/ci.yml"),
read(".github/workflows/release.yml"),
read(".github/workflows/pages.yml"),
read(".github/dependabot.yml"),
read("CONTRIBUTING.md"),
read("SECURITY.md"),
Expand Down Expand Up @@ -94,18 +96,27 @@ test("dependency automation covers npm, Cargo, and GitHub Actions", () => {
assert.match(publicationSource, /RUSTSEC-2026-0195/);
});

test("workflows pin actions and release integrity metadata", () => {
for (const workflow of [ciSource, releaseSource]) {
test("workflows pin actions and protect build and release integrity", () => {
for (const workflow of [ciSource, releaseSource, pagesSource]) {
const uses = [...workflow.matchAll(/^\s*uses:\s*([^\s#]+).*$/gm)].map((match) => match[1]);
assert.ok(uses.length > 0);
for (const action of uses) assert.match(action, /@[0-9a-f]{40}$/);
}
assert.match(ciSource, /Verify third-party dependency notices/);
assert.match(ciSource, /npm\.cmd run check:site/);
assert.match(ciSource, /npm\.cmd run build:site/);
assert.match(releaseSource, /SHA256SUMS/);
assert.match(releaseSource, /dependency-graph\/sbom/);
assert.match(releaseSource, /THIRD_PARTY_LICENSES\.txt/);
assert.match(releaseSource, /attest-build-provenance@[0-9a-f]{40}/);
assert.match(releaseSource, /repository\.visibility == 'public'/);
assert.match(pagesSource, /pages:\s*write/);
assert.match(pagesSource, /id-token:\s*write/);
assert.match(pagesSource, /npm run check:site/);
assert.match(pagesSource, /npm run test:site/);
assert.match(pagesSource, /npm run build:site/);
assert.match(pagesSource, /path:\s*\.\/dist-site/);
assert.match(pagesSource, /name:\s*github-pages/);
assert.match(publicationSource, /Secret scanning/);
assert.match(publicationSource, /enforce_admins=false/);
assert.match(publicationSource, /GHSA-wrw7-89jp-8q8g/);
Expand Down
3 changes: 2 additions & 1 deletion tests/site.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ test("the bilingual project site builds independently of the application version
assert.match(configSource, /root: "site"/);
assert.match(configSource, /base: "\/eRAW\/"/);
assert.match(configSource, /host: "127\.0\.0\.1"/);
assert.match(configSource, /port: 4174/);
assert.match(configSource, /port: 44174/);
assert.match(configSource, /strictPort: true/);
assert.match(configSource, /outDir: "\.\.\/dist-site"/);
assert.match(configSource, /site\/index\.html/);
Expand Down Expand Up @@ -127,5 +127,6 @@ test("both locales use replaceable theme captures and a release-resolved x64 dow
assert.match(styleSource, /@media \(max-width: 980px\)/);
assert.match(styleSource, /@media \(max-width: 620px\)/);
assert.match(styleSource, /@media \(prefers-reduced-motion: reduce\)/);
assert.doesNotMatch(styleSource, /body\s*\{[^}]*min-width:\s*320px/);
assert.doesNotMatch(`${zhHtmlSource}\n${enHtmlSource}`, /google-analytics|googletagmanager|fonts\.googleapis|use\.typekit/i);
});
2 changes: 1 addition & 1 deletion vite.pages.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default defineConfig({
base: "/eRAW/",
server: {
host: "127.0.0.1",
port: 4174,
port: 44174,
strictPort: true,
},
build: {
Expand Down