The deployable content host for readingbat.com. This
repo is intentionally thin: the web server, routing, persistence, and challenge
runtime all live in readingbat-core,
which is consumed here as a binary dependency. What lives in this repo is the
content composition and the deployment plumbing around it.
- Top-level
Content.kt— composes the full site - Java and Kotlin content
- Python content
- JDK 25 (the Gradle toolchain will resolve one via foojay if you don't already have it)
- Docker +
buildx(only needed for releases)
All other versions — Gradle, Kotlin, Ktor, plugins — are pinned in
gradle/libs.versions.toml.
./gradlew run # run the app locally
./gradlew build -x test # build without tests
./gradlew buildFatJar # produce build/libs/server.jar
java -jar build/libs/server.jarThe app listens on 8080 (HTTP) by default, with additional ports for JMX and
metrics — see the Dockerfile for the full list.
./gradlew check # full verification (tests + lint)
./gradlew test # tests onlyTests use Kotest StringSpec. Test sources live under
src/test/kotlin/com/readingbat/.
.github/workflows/ci.yml runs on every push and pull
request against master. It builds, tests, and lints on JDK 25 (Temurin) with a
single ./gradlew build — detekt and lintKotlin both attach to check, so
that one command is the same verification you get locally.
./gradlew lintKotlin detekt # static analysis + style check
./gradlew formatKotlin # apply ktlint fixesProject formatting rules are in .editorconfig. Line endings are
normalized by .gitattributes: text files are stored and checked
out with LF (*.bat is the sole CRLF exception), so gradlew and the machines/
shell scripts stay runnable on Linux and in Docker regardless of your
core.autocrlf setting.
Most common workflows have a make target. Run make help for the full list:
make build # clean and build (no tests)
make tests # full test suite
make lint # lintKotlin + detekt
make format # apply formatting
make uberjar # build the fat jar
make run-uber # run the fat jar
make release # build + push multi-arch Docker image
make deploy # deploy via secrets/deploy-app.sh
make upgrade-wrapper # bump the Gradle wrapper to the catalog-pinned versionThe image is built from eclipse-temurin:25-jdk-alpine, runs as a non-root
readingbat user, and declares a HEALTHCHECK on /ping. make release
publishes a multi-arch (linux/amd64,linux/arm64) image to
pambrose/readingbat:{latest,$VERSION}.
A multi-instance local composition is provided in
docker-compose.yml.
- Bump
version=ingradle.properties. - Bump the pinned image tags in
docker-compose.ymlandmachines/content/run.shto the new version. - Move
[Unreleased]entries inCHANGELOG.mdunder the new version and add a highlights entry toRELEASE_NOTES.md. make releaseto build and push the Docker image.- Follow the runbook in
docs/release_notes.mdto roll the deployment on Digital Ocean. - Cut a GitHub release: tag
X.Y.Z(novprefix), titlevX.Y.Z.
See CLAUDE.md for repo conventions (version-catalog discipline,
configuration-cache safety, shadowJar duplicate handling, and the gotchas that
the code alone won't teach you) — useful for both humans and AI coding agents.
Apache License 2.0 — see LICENSE.txt.