Skip to content

Development

go-while edited this page Oct 26, 2026 · 5 revisions

Development

NewsCache is about 17,000 lines of C++ in src/ (the programs), NServer-0.9.1/ (upstream connections, groups, spool), NVClasses-0.9.1/ (the memory-mapped databases) and md5lib/. Contributions are welcome as pull requests at https://github.com/go-while/NewsCache.

Building and testing

./autogen
./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var
make -j"$(nproc)"
make check

make check runs:

  • C++ unit tests: cArticle, cNSError, cOverviewFmt, cNewsgroupFilter, cNewsgroupList, cActiveDB, cHistory, cChunk, cCorruptDB, cLogger and cUtil in NServer-0.9.1/checks/, and cConfig in src/. cCorruptDB skips its cases that need a file or address space limit where the system doesn't enforce one (GNU/Hurd doesn't enforce RLIMIT_NOFILE);

  • end-to-end tests in tests/e2e/, written in Go. They start the built newscache against a fake upstream server from nntp-testkit and talk NNTP to it, so they need no network (except to download the Go modules once). They need Go 1.26 or later; with an older Go or none they are skipped. tests/e2e/upstream_test.go makes the fake upstream behave like real servers that don't follow RFC 3977 to the letter (old LISTGROUP, only OVER or XOVER, 420 for empty ranges, last numbers that go down, capped OVER replies, logins that time out, connection limits). tests/e2e/upstreams_test.go adds a generated group of 10^7 articles, overview records whose Message-ID isn't the article's, and two servers that number the same group differently; tests/e2e/spoolfull_test.go runs NewsCache with few file descriptors (ulimit -n), with writes that fail (ulimit -f) and on a spool that fills up (a small tmpfs in a user namespace, skipped where the system doesn't allow one). When a NewsCache change depends on an upstream behaviour the fake server can't produce, add it to nntp-testkit first, tag a release, and bump tests/e2e/go.mod.

Run the e2e tests by hand, for example with the race detector or a single test:

cd tests/e2e
NEWSCACHE_BIN=$PWD/../../src/newscache go test -race -count=1 ./...
NEWSCACHE_BIN=$PWD/../../src/newscache go test -count=1 -run TestXpat -v ./...
  • Some tests need the file-log build (--without-syslog) to read the log; with syslog they are skipped.
  • Tests that show a known, not yet fixed bug are skipped by default; NEWSCACHE_KNOWN_BUGS=fail runs them as failing tests.

make distcheck TEXI2DVI=true builds and tests the release tarball (TEXI2DVI=true skips the PDF manual when TeX isn't installed). New test files must be listed in EXTRA_DIST in the top Makefile.am.

CI

Every pull request runs .github/workflows/ci.yml and CodeQL. 16 checks are required before a pull request can be merged:

Check What
Ubuntu (default, file log/debug, AddressSanitizer, ARM64) build, make check, e2e with -race; the default job also runs make distcheck
Fedora 43, 44, 45 (rpm flags) Fedora's hardened compiler flags, --with-pam
Alpine 3.24 (musl), Debian 13 other libcs and distributions
FreeBSD in a VM, with clang and libc++
macOS (build and test) clang, Homebrew GnuTLS (manual runs only, see below)
Checkout (Windows), Checkout (macOS) the tree checks out on case-insensitive file systems (macOS: manual runs only)
Container image builds the image and checks that nobody is let in by default
Analyze (c-cpp), Analyze (actions) CodeQL

The two macOS jobs run only when CI is started by hand (Actions, CI, "Run workflow", with "Also run the macOS jobs" checked): GitHub's macOS runners often wait in the queue until the job times out. On pushes and pull requests they are skipped, which counts as passed. They are run when someone reports a problem on macOS.

clang/libc++ (FreeBSD, macOS) catches things GCC doesn't, for example names that clash with std:: when a file uses using namespace std.

Branches

Branch Purpose
main The maintained line. Changes arrive only by pull request.
trunk A mirror of Christof Meerwald's Subversion trunk (r4–r81), written only by git svn. Never commit to it.
features Feature work, planned in PLAN-new-features.md. Gets main merged in.
patches-r80 The fixes sent to Christof Meerwald as patches against SVN r80, kept for reference.
master, V1.2rc6 Herbert Straub's original branches.

Tags 1.2rc6, 1.3.ipv6 and 1.3.ipv6-2 … 1.3.ipv6-11 mirror the SVN tags; releases since 2026 are tagged 1.3.17, 1.3.18, … on main.

How changes are made

  • Branches: each change starts on a branch from main named fix/<topic>, with one pull request per finding, so that each one can be reviewed, tested and reverted on its own.
  • Tests: add a test where possible; ideally it fails without the fix.
  • NEWS: add an entry under Unreleased: in the same pull request; NEWS is edited only in pull requests to main.
  • Merging: all required checks must pass, and nobody pushes to main directly (a ruleset enforces it). Force pushes and deleting main or trunk are blocked, and release tags can't be moved.
  • Long-lived branches: changes flow one way, from main into features. Don't cherry-pick from a feature branch to main.

Code review

REVIEW.md tracks a systematic review of the whole code base by area: what has been reviewed, every finding with the pull request that fixed it, open points, and feature ideas from the old TODO list.

Releases

  1. Release branch and pull request:
    • set the version in configure.ac (AC_INIT);
    • add a debian/changelog entry;
    • turn the Unreleased: section of NEWS into the release's section.
  2. Tarball check: after the merge, build and check the tarball (make distcheck).
  3. Tag: tag the merge commit on main with an annotated tag named after the version (1.3.22, no v), and push the tag.
  4. Release: publish the GitHub release with the tarball and its checksum. Releases are immutable once published, so the files must be final.

Syncing Subversion

If Christof Meerwald's Subversion repository ever gets new commits, trunk is updated with git svn rebase and merged into main by pull request, with a merge commit (never squash or rebase, so trunk's commits stay unchanged). The one-time setup is described in .claude/CLAUDE.md.

Clone this wiki locally