Files
remark42/CLAUDE.md
T
Dmitry VerkhoturovandUmputun bf67c251c5 docs(claude): require an example tidy on any go.mod change
Previously the rule was scoped to "updating Go modules", which reads as
version bumps only. Adding or removing a dependency, or changing the `go`
directive, puts the example module out of step in exactly the same way, and
the failure is the same `test examples` step reporting "updates to go.mod
needed".

Also records that Dependabot Go module PRs need this, since the bot updates
`backend/` alone.
2026-08-18 20:27:21 -05:00

5.8 KiB

Remark42 Development Guidelines

Build/Test/Lint Commands

  • Backend:
    • Run server: make rundev
    • Build: make backend
    • Race test: make race_test
  • Backend Testing:
    • Run all tests: cd backend/app && go test -timeout=60s -count 1 ./...
    • Run single test: cd backend/app && go test -run TestName ./path/to/package
    • IMPORTANT: Run example tests: cd backend/_example/memory_store && go test -race ./... && go build -race ./...
  • Frontend:
    • Development: cd frontend && pnpm dev:app
    • Tests: cd frontend && pnpm test
  • Lint:
    • Backend: cd backend && golangci-lint run
    • IMPORTANT: Example lint: cd backend/_example/memory_store && golangci-lint run --config ../../.golangci.yml
    • Frontend: cd frontend && pnpm lint
    • Before committing: Always run tests and linter on both main backend AND examples
  • Go module changes:
    • Any change to backend/go.mod or backend/go.sum requires go mod tidy in backend/_example/memory_store in the same commit. That covers dependency bumps, adding or removing a dependency, and changing the go directive, not only version updates.
    • Only go mod tidy there, not go mod vendor: the example's vendor directory is gitignored (.gitignore:26), so its output is never committed, while a stale local copy silently becomes what the example resolves against.
    • The example module replaces github.com/umputun/remark42/backend with ../../, so it carries the backend's dependencies as indirect entries. Leaving them stale fails the test examples CI step with go: updates to go.mod needed; to update it: go mod tidy.
    • This applies to Dependabot pull requests too: the bot updates backend/ only, so its Go module PRs need the example tidied before they can go green.

Release Procedure

Remark42 uses two tags for each release:

  • vX.Y.Z - product release tag used by GitHub releases, GoReleaser binary artifacts, and Docker image publishing.
  • backend/vX.Y.Z - nested Go module tag for github.com/umputun/remark42/backend.

Release flow:

  1. Create the GitHub release for vX.Y.Z with title Version X.Y.Z. The GitHub release must exist before the vX.Y.Z tag reaches the remote; gh release create vX.Y.Z satisfies this because it creates and pushes the tag.
  2. The vX.Y.Z tag triggers GoReleaser, which builds and uploads binary artifacts to the existing release.
  3. Create and push the matching backend module tag pointing at the same commit:
git fetch origin --tags
git tag backend/vX.Y.Z vX.Y.Z
git push origin backend/vX.Y.Z

GoReleaser must ignore backend/* tags in .goreleaser.yml so release notes and current-tag detection use only product tags. Docker image publishing stays separate and is handled by the existing Docker workflow.

For local artifact runs, install GoReleaser, Go 1.25, Node 16+, PNPM 8, and Perl, then use make release. The target runs a snapshot/no-publish GoReleaser build, leaves local artifacts and metadata in dist/, and cleans generated frontend embed files after GoReleaser exits. Do not run raw goreleaser release for local artifacts unless you also run ./scripts/cleanup-release-assets.sh afterward.

Milestones and Issue Labels

Milestones — one vX.Y.Z milestone per release. Assign every merged PR, and every issue closed by a code change, to the milestone of the release it shipped in.

  • Decide which release a PR belongs to by whether its merge commit is contained in a release tag — not by comparing dates (a tag can be cut from an earlier commit, or moved). git fetch --tags, then git tag --contains <merge_sha> | grep '^v' | sort -V | head -1 is its release. If no release tag contains it yet, it belongs to the next (unreleased) version's milestone — create it if missing (gh api repos/umputun/remark42/milestones -f title="vX.Y.Z").
  • An issue gets a milestone only when it was closed by a code change (a linked closing PR/commit); take the milestone from that PR/commit (via the commit-in-tag rule). Issues closed as duplicate/invalid/wontfix/answered get no milestone.
  • Find unassigned: gh pr list --state merged --search "no:milestone", gh issue list --state closed --search "no:milestone". Assign with gh pr edit N --milestone "vX.Y.Z" / gh issue edit N --milestone "vX.Y.Z".

Issue labels — classify each issue with a type and an area (add priority when relevant):

  • Type: bug, enhancement, question, documentation, discussion
  • Area: backend, frontend, site, CI, design, localization
  • Priority: important, minor, some day
  • Contribution: help wanted, good-first-issue
  • Resolution (on close, when applicable): duplicate, invalid, wontfix, no-action-needed
  • PR auto-labels (applied by Dependabot/Actions, not manual PRs): dependencies, go, javascript, github_actions

Code Style

  • Backend: Formatting with golangci-lint, strict error handling
  • Frontend: TypeScript with ESLint, Stylelint and Prettier
  • Imports: Group stdlib, external packages, then internal packages
  • CSS: All components use CSS Modules (component.module.css). Class naming: BEM block = .root, elements = camelCase, modifiers = camelCase. Use clsx for conditional class composition. raw-content.css is the only global CSS file (syntax highlighting utility). Root wrapper keeps bare .dark/.light theme class — 8+ module CSS files depend on :global(.dark) ancestor. comment_highlighting uses :global() for imperative classList usage in root.tsx

Key Backend Packages

  • Web/API: github.com/go-pkgz/routegroup, github.com/go-pkgz/rest
  • Auth: github.com/go-pkgz/auth/v2
  • Logging: github.com/go-pkgz/lgr
  • Testing: github.com/stretchr/testify
  • Notifications: github.com/go-pkgz/notify

Repository Structure

  • Backend: Go server using BoltDB for storage
  • Frontend: Preact/Redux-based UI with iframe embedding