Files
remark42/CLAUDE.md
T
Dmitry VerkhoturovandUmputun 5c0798fe10 docs(claude): document milestone + issue-label conventions
Milestones: one vX.Y.Z per release; decide a PR's release by whether its merge
commit is contained in a release tag (git tag --contains), not by dates; issues
get a milestone only when closed by a code change. Plus the issue-label taxonomy
(type / area / priority / contribution / resolution).
2026-07-11 02:08:40 -05:00

5.2 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
  • Dependency Updates:
    • When updating Go modules in backend/, also run go mod tidy (and go mod vendor) in backend/_example/memory_store to keep indirect deps in sync. The example module replaces github.com/umputun/remark42/backend with ../../ so stale indirect deps there will break the example build.

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