Thanks for considering a contribution. This document covers the developer workflow once your environment is set up, the test commands, code style, pull-request process, release procedure, and how to report issues.
See README.md#build-from-source for the canonical dev setup -- this section documents the developer workflow once the environment is up.
| Tool | Version | Source of truth |
|---|---|---|
| Go | 1.25.x | go.mod |
| Node.js | 20.x LTS | .github/workflows/ci.yml |
| Wails v3 CLI | v3.0.0-alpha.74 |
go.mod require block |
| golangci-lint | v2.1.6 | .golangci.yml |
- VSCode with the Go extension (
gopls), ESLint, and Tailwind CSS IntelliSense .nvmrcsets Node 24 fornvm use; CI pins Node 20. Both work locally, but CI runs 20.
task dev is a shortcut for wails3 dev (see Taskfile.yml).
The project has a Go root module plus per-suite acceptance modules under tests/*/go.mod. A naive go test ./... at the repo root does NOT descend into the per-suite modules -- the loop below mirrors the CI pattern (e2e and support skipped).
# Go unit tests (root module)
go test ./...
# Go acceptance test suites (per-module, under tests/*/go.mod;
# e2e, support, and boot-smoke directories are skipped to match CI --
# boot-smoke has its own dedicated step that wraps with xvfb-run on Linux)
for mod in tests/*/go.mod; do
dir="$(dirname "$mod")"
case "$(basename "$dir")" in
e2e|support|boot-smoke) continue ;;
esac
(cd "$dir" && go test ./...)
done
# Frontend (Vitest)
npm run test --prefix frontend
# Frontend lint (ESLint)
npm run lint --prefix frontend
# Frontend type-check (tsc)
npm run typecheck --prefix frontend
# Go lint (golangci-lint v2.1.6)
golangci-lint run ./...Do NOT add new strings.Contains, regexp.Match, or other file-content assertions against main.go, frontend/src/components/MainLayout.tsx, or frontend/src/components/EmptyState.tsx from any test under tests/. If you find yourself reaching for that pattern, write a behavioural test (Vitest, Playwright, or tests/boot-smoke) instead.
Pre-existing structural greps in those files are grandfathered pending a bulk-removal follow-up. Do not extend the pattern. Reviewers must flag new source-greps at PR time.
Generated build artifacts (build/linux/*.desktop, build/darwin/Info.plist, etc.) are exempt -- parsing those is legitimate behavioural testing on a build output, not source.
- Go:
gofmtandgoimportson every file; linted bygolangci-lintwith the project.golangci.yml(version 2 schema;errcheck,govet,ineffassign,staticcheck,unusedenabled). - TypeScript / React: ESLint 9 flat config with
typescript-eslint,eslint-plugin-react, andeslint-plugin-react-hooks.@typescript-eslint/no-explicit-anyand@typescript-eslint/no-unused-varsareerror-- do not relax them. - Commit messages: short single-line messages. No
Co-Authored-Bytrailers. - Docstrings (per CLAUDE.md): Go godoc on every exported symbol; TypeScript JSDoc on every exported component, hook, and utility. See
CLAUDE.mdfor the full rulebook.
- Fork the repository and create a branch from
dev(notmaster). - Write tests first where practical -- the project uses a story-based TDD pattern, but writing tests up-front is optional for contributor PRs.
- Open your PR against the
devbranch. - CI must pass on all three runners:
build-and-test (macos-latest),build-and-test (windows-latest), andbuild-and-test (ubuntu-latest). - At least one maintainer review plus green CI are required before merge.
Maintainers: configure required status checks on the master and dev branches via GitHub Settings -> Branches to require the three build-and-test (*) runs. This replaces the one-line deferred note from story 7-1 Task 7.5.
-
Tag a version. Tag
vX.Y.Zondevand push to the remote. This triggers.github/workflows/release.yml, which builds cross-platform artifacts and publishes a GitHub Release (see story 7-2). -
Apple Developer ID cert rotation (maintainer-only). Apple Developer ID certificates have a 5-year lifetime. When the cert expires:
-
Regenerate the certificate via the Apple Developer portal.
-
Export as
.p12and base64-encode:base64 -i DeveloperID.p12 | pbcopy -
Update the
APPLE_DEVELOPER_ID_CERT_P12_BASE64secret in repo Settings -> Secrets and variables -> Actions. -
If the DUNS changes (rare), also update
APPLE_DEVELOPER_ID.
-
-
Notarization is currently disabled. macOS artifacts are codesigned but not notarized, so first-launch from Finder will trigger a Gatekeeper warning that the user must dismiss via right-click -> Open. The Apple Developer ID cert above is sufficient for codesign. To re-enable notarization later, restore the
Notarize and staplestep inrelease.yml, restore the secret detection requirement forAPPLE_ID,APPLE_ID_APP_PASSWORD, andAPPLE_TEAM_ID, and configure those three secrets.
- File a GitHub Issue with a minimal reproducer: attach a PDF file (redact sensitive content) and the CLI or GUI output.
- For security-sensitive issues, email
security@unidoc.io-- do not open a public issue. - V1 does not define formal triage SLAs; maintainers aim for a 7-day first-response target.