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
66 changes: 40 additions & 26 deletions .codex/noteconnection_release_runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ Provide a stable, repeatable release procedure for NoteConnection so that GitHub

### Preconditions

- Local branch is `main` and synced:
- `rtk git pull --rebase origin main`
- Work on the reviewed release branch. Keep the source PR open until candidate validation and the remaining release work are complete.
- Working tree is clean except intentionally ignored build artifacts.
- Version files are aligned:
- `package.json`
Expand All @@ -30,22 +29,30 @@ Provide a stable, repeatable release procedure for NoteConnection so that GitHub
- `npm run docs:diataxis:check`
- `npm run docs:site:build`
- `npx jest --runInBand` (recommended for CI parity on Windows).
4. Commit and push to main:
4. Commit and push the release branch for review:
- `rtk git add <changed files>`
- `rtk git commit -m "docs(release): ..."` or `release: vX.Y.Z`
- `rtk git push origin main`
- `rtk git push origin <release-branch>`
- Keep the source PR unmerged while the candidate is being validated.
5. Tag and push tag:
- `rtk git tag vX.Y.Z`
- `rtk git push origin refs/tags/vX.Y.Z`
6. Create GitHub Release with explicit notes:
- `rtk gh release create vX.Y.Z --title "vX.Y.Z" --notes-file docs/release_notes_vX.Y.Z.md`
7. Monitor CI:
- `Release Desktop Multi-OS` should pass and upload assets.
- `Publish to npm` should pass. npm is expected to auto-publish when CI succeeds.
8. Verify outputs:
- GitHub release assets include Windows/macOS/Linux and APK (if workflow enables Android).
- npm registry:
- `npm view noteconnection version dist-tags --json`
6. Keep the GitHub Release in draft with explicit notes:
- CI creates the draft from the checked-out release source and rejects an already public release.
- If creating it manually: `rtk gh release create vX.Y.Z --draft --target <release-commit> --title "vX.Y.Z" --notes-file docs/release_notes_vX.Y.Z.md`.
7. Monitor candidate CI:
- `Release Desktop Multi-OS` should pass and upload assets to the draft.
- Android builds run only when native-device evidence is explicitly requested; signed-device gates remain required.
- npm publication does not run for the tag or draft release.
8. Verify the exact candidate artifacts:
- Download the CI AppImage and its separate original-server workflow artifact, run `npm run verify:appimage -- <image.AppImage> <original-server-sidecar>`, and complete baseline KVM/runtime acceptance.
- Record the tested hashes and actual platform coverage in the bilingual release notes before publishing.
9. After validation and all remaining work are complete, merge the source PR into `main` and publish the existing draft:
- `rtk gh release edit vX.Y.Z --draft=false --latest --notes-file docs/release_notes_vX.Y.Z.md`
10. Verify published outputs:
- GitHub release assets match the accepted bytes and the documented platform coverage.
- `Publish to npm` runs only for published product `v*` releases; Godot mirror releases are excluded.
- Verify the npm registry: `npm view noteconnection version dist-tags --json`.

### EdgeOne Docs Deploy (Optional but Recommended for docs releases)

Expand Down Expand Up @@ -77,8 +84,7 @@ Provide a stable, repeatable release procedure for NoteConnection so that GitHub

### 前置条件

- 当前分支为 `main` 且已同步:
- `rtk git pull --rebase origin main`
- 在经过审查的发布分支上工作;候选制品验收和其余发布工作完成前,源码 PR 保持未合并。
- 工作区干净(仅允许明确忽略的构建产物残留)。
- 版本文件保持一致:
- `package.json`
Expand All @@ -99,22 +105,30 @@ Provide a stable, repeatable release procedure for NoteConnection so that GitHub
- `npm run docs:diataxis:check`
- `npm run docs:site:build`
- `npx jest --runInBand`(Windows 上更接近 CI,稳定性更高)。
4. 提交并推送主干:
4. 提交并推送发布分支供审查:
- `rtk git add <变更文件>`
- `rtk git commit -m "docs(release): ..."` 或 `release: vX.Y.Z`
- `rtk git push origin main`
- `rtk git push origin <release-branch>`
- 候选制品验收期间保持源码 PR 未合并。
5. 打标签并推送:
- `rtk git tag vX.Y.Z`
- `rtk git push origin refs/tags/vX.Y.Z`
6. 创建 GitHub Release(必须带完整更新日志):
- `rtk gh release create vX.Y.Z --title "vX.Y.Z" --notes-file docs/release_notes_vX.Y.Z.md`
7. 观察 CI:
- `Release Desktop Multi-OS` 通过并上传平台资产。
- `Publish to npm` 通过并自动发布 npm。
8. 发布后验收:
- GitHub Release 资产包含 Windows/macOS/Linux(以及启用时的 APK)。
- npm 版本核验:
- `npm view noteconnection version dist-tags --json`
6. 保持 GitHub Release 为草稿并提供完整更新日志:
- CI 从检出的发布源码创建草稿,并拒绝复用已公开的发布。
- 如需手动创建:`rtk gh release create vX.Y.Z --draft --target <release-commit> --title "vX.Y.Z" --notes-file docs/release_notes_vX.Y.Z.md`。
7. 观察候选制品 CI:
- `Release Desktop Multi-OS` 通过并将平台资产上传至草稿。
- 仅在明确请求原生真机证据时执行 Android 构建,仍保留签名和真机门禁。
- tag 或草稿发布不会触发 npm 发布。
8. 验收候选制品的精确字节:
- 下载 CI AppImage 及单独保存原始 server 的 workflow artifact,执行 `npm run verify:appimage -- <image.AppImage> <original-server-sidecar>`,完成基线 KVM/运行时验收。
- 正式公开前,在双语发布说明中记录实际测试散列和平台覆盖范围。
9. 验收和其余工作全部完成后,将源码 PR 合并到 `main`,再公开已有草稿:
- `rtk gh release edit vX.Y.Z --draft=false --latest --notes-file docs/release_notes_vX.Y.Z.md`
10. 核验已发布产物:
- GitHub Release 资产与已验收字节及文档中的平台覆盖一致。
- `Publish to npm` 仅由正式公开的产品 `v*` release 触发,Godot 镜像发布除外。
- npm 版本核验:`npm view noteconnection version dist-tags --json`。

### EdgeOne 文档发布(文档类版本建议执行)

Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ name: Publish to npm
on:
release:
types: [published]
push:
tags:
- "v*"

concurrency:
group: npm-publish-${{ github.event.release.tag_name || github.ref_name }}
Expand All @@ -28,6 +25,7 @@ env:

jobs:
publish:
if: ${{ startsWith(github.event.release.tag_name, 'v') }}
runs-on: ubuntu-latest

steps:
Expand Down
47 changes: 43 additions & 4 deletions .github/workflows/release-desktop-multi-os.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.source_ref != '' && github.event.inputs.source_ref || github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }}
fetch-depth: 0

- name: Resolve tag name
Expand All @@ -70,10 +71,14 @@ jobs:
shell: bash
run: |
TAG_NAME="${{ steps.tag.outputs.tag_name }}"
if gh release view "$TAG_NAME" >/dev/null 2>&1; then
echo "Release already exists for $TAG_NAME."
if RELEASE_IS_DRAFT="$(gh release view "$TAG_NAME" --json isDraft --jq '.isDraft' 2>/dev/null)"; then
if [ "$RELEASE_IS_DRAFT" != "true" ]; then
echo "::error::Release $TAG_NAME is already public; refusing to replace published assets."
exit 1
fi
echo "Draft release already exists for $TAG_NAME."
else
gh release create "$TAG_NAME" --title "$TAG_NAME" --notes "Automated desktop multi-OS release assets."
gh release create "$TAG_NAME" --draft --target "$(git rev-parse HEAD)" --title "$TAG_NAME" --notes-file "docs/release_notes_${TAG_NAME}.md"
fi

ensure-godot-mirror-assets:
Expand Down Expand Up @@ -102,6 +107,7 @@ jobs:
gh release create "$GODOT_MIRROR_TAG" \
--repo "$GITHUB_REPOSITORY" \
--target "$GITHUB_SHA" \
--latest=false \
--title "$GODOT_MIRROR_TAG" \
--notes "Project-controlled mirror for Godot desktop bootstrap archives."
fi
Expand Down Expand Up @@ -178,7 +184,8 @@ jobs:
files: |
src-tauri/target/release/bundle/**/*.exe
src-tauri/target/release/bundle/**/*.msi
- runner: ubuntu-latest
# The AppImage catalog runs Ubuntu 22.04 (glibc 2.35); build against that baseline.
- runner: ubuntu-22.04
platform_label: linux
files: |
src-tauri/target/release/bundle/**/*.AppImage
Expand Down Expand Up @@ -226,6 +233,9 @@ jobs:
libgtk-3-dev \
librsvg2-dev \
patchelf \
desktop-file-utils \
libgdk-pixbuf2.0-bin \
squashfs-tools \
"${APPINDICATOR_PKG}"
echo "Installed Linux native dependencies with ${WEBKIT_PKG} and ${APPINDICATOR_PKG}."

Expand Down Expand Up @@ -336,10 +346,36 @@ jobs:
fi
cp "${GODOT_BIN}" "${TARGET_BIN}"
chmod +x "${TARGET_BIN}"
# The upstream signature binds Godot.app's Info.plist; the standalone sidecar needs its own signature.
codesign --force --sign - "${TARGET_BIN}"
codesign --verify --strict --verbose=4 "${TARGET_BIN}"
"${TARGET_BIN}" --version

- name: Build desktop bundle
run: npm run tauri:build:mini

- name: Verify portable AppImage desktop integration
if: runner.os == 'Linux'
shell: bash
run: |
set -euo pipefail
mapfile -d '' artifacts < <(find src-tauri/target/release/bundle -type f -name '*.AppImage' -print0)
if [ "${#artifacts[@]}" -eq 0 ]; then
echo "No AppImage artifacts were produced." >&2
exit 1
fi
for artifact in "${artifacts[@]}"; do
npm run verify:appimage -- "${artifact}" src-tauri/bin/server-x86_64-unknown-linux-gnu
done

- name: Upload Linux AppImage verification input
if: runner.os == 'Linux'
uses: actions/upload-artifact@v4
with:
name: release-${{ needs.ensure-release.outputs.tag_name }}-linux-verification-inputs
if-no-files-found: error
path: src-tauri/bin/server-x86_64-unknown-linux-gnu

- name: Upload workflow artifacts
uses: actions/upload-artifact@v4
with:
Expand All @@ -351,6 +387,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.ensure-release.outputs.tag_name }}
draft: true
files: ${{ matrix.files }}
fail_on_unmatched_files: true
env:
Expand All @@ -359,6 +396,7 @@ jobs:
build-and-upload-android:
name: Build and Upload (android-apk)
needs: ensure-release
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.run_android_device_evidence == 'true' }}
runs-on: ubuntu-latest

steps:
Expand Down Expand Up @@ -589,6 +627,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.ensure-release.outputs.tag_name }}
draft: true
files: build/release/mobile/*
fail_on_unmatched_files: true
env:
Expand Down
34 changes: 34 additions & 0 deletions .trellis/spec/backend/quality-guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,37 @@ Jest with `ts-jest` (configured in `jest.config.js`). Node.js 20 is the CI targe
5. Is database access through the store interface?
6. Does the change avoid `any` in public API signatures?
7. For performance-sensitive code: is `PerformanceLogger` used for timing?

## Linux AppImage Packaging Contract

### Scope

Changes to the Tauri CLI, sidecars, Linux build runner, or release workflow must preserve the final artifact's portability. Build Linux releases on Ubuntu 22.04 (glibc 2.35), the catalog baseline.

### Commands

Use `npm run tauri:build:mini`, then `npm run verify:appimage -- <image.AppImage> <original-server-sidecar>`. Both verifier arguments are required and must come from the same build.

### Boundary and environment

The Linux runner selects `scripts/appimage-patchelf.js` through `PATCHELF`. `NOTE_CONNECTION_APPIMAGE_PATCHELF` identifies the original executable; `NOTE_CONNECTION_APPIMAGE_SERVER_SUFFIX` and `NOTE_CONNECTION_APPIMAGE_SERVER_SHA256` identify the protected pkg sidecar. Only its `--set-rpath` write is suppressed; dependency queries and other ELF operations remain enabled. AppRun supplies the sidecar's library search path. Never rewrite pkg's completed ELF payload offsets.

### Validation and errors

The final verifier rejects inaccessible stored SquashFS modes, invalid integration symlinks, missing desktop executables/icons, invalid image decoding, and any server hash mismatch. A matching path with changed bytes makes the patchelf adapter fail. Metadata success alone does not prove that the ELF loader, WebView, or backend starts.

### Acceptance cases

- Good: the Ubuntu 22.04 artifact launches as a normal user, serves authenticated graph/reader requests, and shuts down its sidecars.
- Base: `.DirIcon` resolves inside the image, `AppRun.wrapped` is executable by other users, and the packaged server equals the original file.
- Bad: testing only on the newer build host, resolving links against the CI workspace, or accepting a visible shell window while its backend has crashed.

### Required tests

Run the portability and patchelf behavioral suites. Verify the final image on the baseline OS and a newer host, retaining its SHA-256, logs, and screenshots. The installed Markdown worker must be discovered under Tauri's suffixless name and report `engine: pulldown` without a missing-worker fallback.

Run the offline simulation worker suite and load a graph with external networking disabled, keeping loopback available for the sidecar. Graph layout dependencies must be bundled; an initial window and successful graph API do not establish that worker-produced node positions render.

### Wrong and correct evidence

An extracted directory with manually repaired links is diagnostic evidence. A fresh image produced by the corrected build, checked outside the build directory and exercised through the catalog worker and native runtime, establishes release acceptance. Use `unsquashfs` for stored-mode checks: runtime `--appimage-extract` can alter directory permissions.
Loading
Loading