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
31 changes: 14 additions & 17 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,18 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: '24'

- name: Install pnpm
uses: pnpm/action-setup@v4
# No `version` input on purpose: the action then uses the exact version
# from the `packageManager` field in package.json, which corepack also
# honours for local development. One source of truth.
#
# Why exact and why 10.x: the contents of app.asar depend on how the
# installed pnpm resolves and reports modules. A floating major already
# produced two different app.asar contents from one identical lockfile.
# pnpm 11+ also stops reading `pnpm.onlyBuiltDependencies` from
# package.json, which would silently skip the better-sqlite3 build.

# Caches the npm download cache (not node_modules). Requires the
# committed package-lock.json, which `npm ci` verifies against.
cache: 'npm'

# npm, not pnpm, is deliberate: electron-builder's node-module collector is
# built around npm's node_modules layout and silently dropped transitive
# production dependencies under pnpm's virtual store. When the collector
# does miss something, `allowMissingDependencies: false` in
# electron-builder.yml now fails the build rather than shipping a broken
# app. See .npmrc and the `//dependencies` note in package.json.
- name: Install dependencies
run: pnpm install --frozen-lockfile
run: npm ci

# Fail BEFORE anything is published if the entitlement that lets us load
# third-party native modules is missing from the plist.
Expand Down Expand Up @@ -75,7 +72,7 @@ jobs:
PY

- name: Build application
run: pnpm build
run: npm run build

# macOS 代码签名(可选)
- name: Import Code Signing Certificate (macOS)
Expand Down Expand Up @@ -117,7 +114,7 @@ jobs:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: pnpm build:mac --arm64 --publish always
run: npm run build:mac -- --arm64 --publish always

# Inspect the artifact we just shipped. This is the check that was missing
# when v1.2.0 went out ad-hoc signed, hardened-runtime, and unable to load
Expand Down Expand Up @@ -201,7 +198,7 @@ jobs:
timeout-minutes: 30
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: pnpm build:win --publish always
run: npm run build:win -- --publish always

# electron-builder only creates a DRAFT release (see publish.releaseType in
# electron-builder.yml). It is promoted to public here, which happens only if
Expand Down
70 changes: 70 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Verify

# Pull-request gate. `release.yml` is tag-triggered and only proves that
# installers can be produced, which is not the same as proving the app can start:
# three separate startup crashes in this repo (a hoisted `script-loader!sql.js`
# require, a module-scope `new DOMMatrix()` in bundled pdfjs, and
# `AnkiExport is not a constructor` from a lost CJS interop) all packaged cleanly.
#
# `npm run smoke:packaged` launches the real packaged executable and self-checks
# it, so it is the step that actually catches those. It runs on all three
# platforms because the native addons are arch-specific:
#
# better_sqlite3.node, vec0.so/.dylib/.dll, skia.*.node
#
# `lint` is deliberately not part of this gate: the repository currently has 6
# pre-existing renderer react-hooks errors, unrelated to build correctness.

on:
pull_request:
workflow_dispatch:

# Verification only needs to read the repository.
permissions:
contents: read

jobs:
verify:
name: verify (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 30

strategy:
# Report every platform's result instead of stopping at the first failure.
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Typecheck
run: npm run typecheck

# Runs `npm run build` first, then electron-builder --dir, so the bundle and
# the package are both covered by this one step.
- name: Build and package (unpacked)
run: npm run build:unpack
env:
# No signing material in CI; don't let electron-builder go looking for
# an identity on macOS or a certificate on Windows.
CSC_IDENTITY_AUTO_DISCOVERY: 'false'

# The packaged app needs a display; GitHub's Linux runners are headless.
- name: Smoke test the packaged app (Linux)
if: runner.os == 'Linux'
run: xvfb-run -a npm run smoke:packaged

- name: Smoke test the packaged app (macOS / Windows)
if: runner.os != 'Linux'
run: npm run smoke:packaged
25 changes: 18 additions & 7 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
# node-linker=isolated is deliberate and must stay: with pnpm's virtual store,
# `pnpm list --json` reports paths that actually exist. Switching to
# node-linker=hoisted was measured to make things WORSE - `pnpm list` then emits
# node_modules/.pnpm/<pkg>@<ver>/... paths that do not exist on disk, so
# electron-builder's collector cannot read any nested package.json.
shamefully-hoist=true
node-linker=isolated
# KnowNote deliberately uses npm, not pnpm.
#
# electron-builder collects production dependencies with its own node-module
# collector, and that collector is built around npm's flat-ish node_modules
# layout. Against pnpm's virtual store it silently dropped transitive
# dependencies (jsdom -> whatwg-url -> tr46, http-proxy-agent -> debug -> ms),
# which is how v1.2.1 shipped an app.asar that crashed with MODULE_NOT_FOUND.
# See https://github.com/electron-userland/electron-builder/issues/10064.
#
# The previous workaround lived in this file (shamefully-hoist=true,
# node-linker=isolated) plus five hand-promoted transitive dependencies in
# package.json. Both are gone: a flat node_modules makes collection
# well-defined, and `allowMissingDependencies: false` in electron-builder.yml
# turns any remaining miss into a hard build failure instead of a runtime crash.
#
# Do not reintroduce pnpm-only options here (shamefully-hoist, node-linker,
# onlyBuiltDependencies, ...). npm warns about unknown project config and they
# only ever papered over the collector.

# Electron / electron-builder binaries are fetched from the official sources so
# release artifacts stay reproducible and verifiable. Developers behind a slow
Expand Down
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
out
dist
pnpm-lock.yaml
package-lock.json
LICENSE.md
tsconfig.json
tsconfig.*.json
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ Get the latest version from GitHub Releases:
```bash
git clone https://github.com/MrSibe/KnowNote.git
cd KnowNote
pnpm install
pnpm dev
npm install
npm run dev
```

---
Expand Down
4 changes: 2 additions & 2 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ KnowNote 是一个早期阶段的项目。
```bash
git clone https://github.com/MrSibe/KnowNote.git
cd KnowNote
pnpm install
pnpm dev
npm install
npm run dev
```

---
Expand Down
18 changes: 16 additions & 2 deletions electron-builder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ files:
- '!src/*'
- '!electron.vite.config.{js,ts,mjs,cjs}'
- '!{.eslintcache,eslint.config.mjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
- '!{.env,.env.*,.npmrc,package-lock.json}'
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
# Dev-only: test fixtures and build scripts are not part of the shipped app.
- '!{test,scripts}/*'
# Native modules must live outside the asar so they can be loaded by dlopen()
# AND so electron-builder signs them individually. Keep these patterns explicit:
# if a native module stays inside app.asar it cannot be signed or loaded.
Expand Down Expand Up @@ -62,7 +64,19 @@ linux:
category: Utility
appImage:
artifactName: ${name}-${version}.${ext}
npmRebuild: false
# Fail the build when the dependency collector cannot resolve a production
# dependency, instead of only logging a warning (which is the v26 default).
# A missed dependency is invisible at build time and only surfaces as
# MODULE_NOT_FOUND on a user's machine - exactly how v1.2.1 broke. Requires
# electron-builder >= 26.16.0. Missing *optional* dependencies (e.g. fsevents on
# Linux/Windows) are still allowed and never fail the build.
allowMissingDependencies: false
# `npmRebuild` is intentionally left at its default (true) so electron-builder
# rebuilds better-sqlite3 against the target Electron ABI and target
# architecture while packaging. Turning it off, as this project previously did,
# makes the shipped binary depend on whatever ABI `postinstall` happened to
# produce on the build host, which breaks cross-arch builds such as
# `build:mac --arm64`. The postinstall hook is kept for `dev`/`preview`.
# Published as a DRAFT: electron-builder uploads the artifacts, but the release
# only becomes public after every platform built and the macOS artifact passed
# the verification step in .github/workflows/release.yml.
Expand Down
30 changes: 17 additions & 13 deletions electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,25 @@ export default defineConfig({
plugins: [copyMigrationsPlugin()],
build: {
rollupOptions: {
// Only things rollup CANNOT inline may stay external.
// Only things rollup CANNOT inline may stay external. Every entry in
// package.json `dependencies` is already externalized automatically
// (electron-vite's build.externalizeDeps defaults to true); the two
// below are restated as documentation of why they can never be bundled.
//
// Native addons: better-sqlite3 (binding) and sqlite-vec (loadable
// extension) are real binaries. They must be resolved from node_modules
// at runtime and must therefore also stay in package.json
// `dependencies` so electron-builder packages them, and in
// `asarUnpack` so they are not trapped inside the archive.
// better-sqlite3 (binding) and sqlite-vec (loadable extension) are real
// binaries: rollup cannot inline .node or .dylib, so they must resolve
// from node_modules at runtime. They therefore also have to stay in
// `dependencies` so electron-builder packages them, and in `asarUnpack`
// so they are not trapped inside the archive.
//
// Optional native peers that jsdom and pdfjs-dist reach for lazily:
// listing them keeps rollup from failing on an unresolvable import.
// They are intentionally NOT in `dependencies`, so they are simply
// absent at runtime - both packages degrade gracefully (jsdom without
// node-canvas, pdfjs without @napi-rs/canvas, which is only used by
// page.render() and this app never renders).
external: ['better-sqlite3', 'sqlite-vec', 'canvas', '@napi-rs/canvas']
// jsdom, pdfjs-dist, anki-apkg-export and @mixmark-io/domino are also
// external, purely because they live in `dependencies` - it is required,
// not optional. See the `//dependencies` note in package.json for what
// breaks when rollup inlines each one. Note that pdfjs-dist in
// particular does NOT degrade gracefully without its optional peer
// @napi-rs/canvas: it polyfills DOMMatrix from it and then evaluates
// `new DOMMatrix()` at module scope, so the two have to ship together.
external: ['better-sqlite3', 'sqlite-vec']
}
}
},
Expand Down
9 changes: 9 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,14 @@ export default defineConfig(
'react-refresh/only-export-components': 'off'
}
},
{
// Build scripts are plain JS (run directly by node/CI), so the TS-only
// explicit-return-type rule cannot be satisfied there. Source files turn it
// off above for the same reason.
files: ['scripts/**/*.mjs'],
rules: {
'@typescript-eslint/explicit-function-return-type': 'off'
}
},
eslintConfigPrettier
)
Loading
Loading