9.8 KiB
Building and testing
Build, run, and test SubMiner from source. Architecture and workflow rules live in the repo's internal docs, starting at docs/README.md. Module boundaries and layering rules are in docs/architecture/README.md. The lane-by-lane test guide is docs/workflow/verification.md.
Prerequisites
- Bun, at the version pinned in
package.json - A system
luainterpreter for the mpv plugin tests (bun run test:launcher,bun run test:env) - macOS only:
bun run buildcompiles a Swift window helper. SetSUBMINER_SKIP_MACOS_HELPER_BUILD=1to skip it.
Setup
git clone --recurse-submodules https://github.com/ksyasuda/SubMiner.git
cd SubMiner
make deps
make deps initializes submodules and installs dependencies for the root, stats/, and vendor/texthooker-ui. The Yomitan submodule installs its own dependencies during bun run build.
Build
bun run build # app build, including bundled Yomitan from vendor/subminer-yomitan
bun run build:appimage # Linux AppImage
bun run build:mac # macOS DMG + ZIP (signed)
bun run build:mac:unsigned # macOS DMG + ZIP (unsigned)
bun run build:win # Windows NSIS installer + ZIP
make build-launcher # launcher only, output: dist/launcher/subminer
The launcher source is launcher/*.ts. dist/launcher/subminer is generated, so never edit it by hand. The repo-root ./subminer is a stale path and verification rejects it. make install-linux and make install-macos copy from dist/launcher/subminer. To check the launcher build:
make build-launcher
dist/launcher/subminer --help >/dev/null
bash scripts/verify-generated-launcher.sh
Run locally
bun run dev # build, then launch with --start --dev
make dev-watch # watch TS + renderer and relaunch Electron
make dev-watch-macos # same, forcing --backend macos
electron . --start --dev --log-level debug # verbose launch of an existing build
electron . --background # tray/background mode
To test through the mpv plugin without exporting SUBMINER_BINARY_PATH each time, point mpv.subminerBinaryPath in your config at the dev script. The launcher passes it to the plugin at runtime:
{
"mpv": {
"subminerBinaryPath": "/absolute/path/to/SubMiner/scripts/subminer-dev.sh"
}
}
Test
Run the handoff gate before submitting substantial changes:
bun run typecheck
bun run test:fast
bun run test:env
bun run build
bun run test:smoke:dist
For smaller changes, start with the cheapest lane that covers what you touched:
| Command | Covers |
|---|---|
bun run test / test:fast |
All src/** tests, launcher unit tests, and scripts/** tests |
bun run test:config |
Config schema, defaults, and config.example.jsonc generation |
bun run test:launcher |
Launcher tests plus the Lua plugin tests |
bun run test:env |
Launcher e2e smoke, Lua plugin tests, SQLite immersion tests from source |
bun run test:scripts |
Build and release scripts under scripts/** |
bun run test:stats |
Stats dashboard UI under stats/src/** |
bun run test:runtime:compat |
Compiled-runtime smoke against dist/ (run bun run build first) |
bun run test:immersion:sqlite |
Compiles, then runs the SQLite-backed immersion tracker tests |
bun run test:subtitle |
alass/ffsubsync subtitle sync |
bun run test:docs:kb |
Internal docs, AGENTS.md, and repo skills |
Lane membership is defined in scripts/test-lanes.ts and discovered by directory, so a new test file joins its lane automatically. Do not hand-list test files in package.json. scripts/run-test-lane.mjs runs each file in its own bun test process, so a hanging test cannot take down the rest of the lane. Pass --jobs N to parallelize or --single-process to share one process while debugging.
Launcher smoke artifacts go to .tmp/launcher-smoke. CI uploads them when the smoke step fails.
Format
make pretty # format the maintained source and stats files
bun run format:check:src # check the same set without writing
bun run format runs Prettier over the whole repo. Use it only when you mean to.
Config generation
bun run electron . --generate-config # write a default config to ~/.config/SubMiner/config.jsonc (%APPDATA%\SubMiner\config.jsonc on Windows)
bun run generate:config-example # regenerate config.example.jsonc from the defaults
make generate-config and make generate-example-config wrap the same commands.
Config definitions are split by domain under src/config/definitions/:
- defaults:
defaults-*.ts - option metadata:
options-*.ts - generated template sections and comments:
template-sections.ts
src/config/definitions.ts composes them into the public API (DEFAULT_CONFIG, registries, template export). A new key also needs a resolver entry under src/config/resolve/, or the resolved config keeps the default.
Documentation site
The user docs live in docs-site/ (VitePress).
bun --cwd docs-site install
bun run docs:dev # dev server at http://localhost:5173
bun run docs:test # docs regression tests (links, pinned strings)
bun run docs:build # production build into docs-site/.vitepress/dist
bun run docs:preview # preview the build at http://localhost:4173
Run bun run docs:test and bun run docs:build whenever you change docs-site/.
Production uses the versioned build:
bun run docs:build:versioned
It writes .tmp/docs-versioned-site: the latest stable docs at / with a generated /versions page, and development docs at /main/. Prerelease tags are skipped. Stable archives under /v/<version>/ are built once and stored in R2. Without R2 credentials the build skips archive sync, so a local run only produces / and /main/.
The docs-pages GitHub Actions workflow uploads that output to Cloudflare Pages with Wrangler. Cloudflare's Git-integration builds are disabled on purpose, so do not re-enable them in the dashboard. docs-site/README.md has the full deployment setup.
Makefile targets
Run make help for the full list.
| Target | Description |
|---|---|
make deps |
Init submodules and install root, stats, and texthooker deps |
make build |
Build the platform package for the current OS |
make build-linux |
Build the Linux package |
make build-macos |
Build the signed macOS package |
make build-macos-unsigned |
Build the unsigned macOS package |
make build-launcher |
Generate the launcher in dist/launcher/ |
make install |
Install platform artifacts (wrapper, theme, AppImage or app) |
make pretty |
Run scoped Prettier formatting |
make generate-config |
Generate a default config |
Contributor notes
- See Architecture for module boundaries and IPC + runtime contracts before adding IPC channels.
src/core/services/overlay-manager.tsowns overlay window and visibility state.- In
src/main/modules, pass simple dependencies as inline objects. Add a helper or adapter only when it adapts, validates, or gets reused. - Packaged Linux desktop launches pass
--backgroundthroughbuild.linux.executableArgsinpackage.json.
Environment variables
| Variable | Description |
|---|---|
SUBMINER_APPIMAGE_PATH |
SubMiner app binary the launcher uses for playback |
SUBMINER_BINARY_PATH |
Alias for SUBMINER_APPIMAGE_PATH |
SUBMINER_ROFI_THEME |
rofi theme for the launcher picker |
SUBMINER_MPV_PLUGIN_PATH |
mpv plugin directory the launcher injects |
SUBMINER_LOG_LEVEL |
App log level (debug, info, warn, error) |
SUBMINER_MPV_LOG |
Shared mpv/app log file path |
SUBMINER_JIMAKU_API_KEY |
Jimaku API key for launcher subtitle downloads |
SUBMINER_JIMAKU_API_KEY_COMMAND |
Command that prints the Jimaku API key |
SUBMINER_JIMAKU_API_BASE_URL |
Jimaku API base URL |
SUBMINER_JELLYFIN_ACCESS_TOKEN |
Jellyfin access token, used before the stored encrypted session |
SUBMINER_JELLYFIN_USER_ID |
Jellyfin user ID |
SUBMINER_SKIP_MACOS_HELPER_BUILD |
Set to 1 to skip the macOS helper build during bun run build |