Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a4795f1
Convert Kotlin website Writerside content to JSON, add it to database…
alexmmiller Jul 22, 2026
3b128c0
Kotlin docs sync and website processing end-to-end pipeline test
alexmmiller Jul 22, 2026
e5ff684
Config/assets for pipeline test
alexmmiller Jul 22, 2026
cd59353
Address PR review feedback and add blacklist-pruning verification
alexmmiller Jul 23, 2026
ae44822
Merge branch 'fix/ADFA-4514' into fix/ADFA-4737
alexmmiller Jul 27, 2026
5984357
Automate stdlib JSON doc generation in the e2e pipeline script
alexmmiller Jul 29, 2026
1f7edd4
Add repository-root CLAUDE.md
alexmmiller Aug 5, 2026
9b4ba07
Add Build Kotlin Docs workflow
alexmmiller Aug 5, 2026
7f67b2f
Add md_to_json.py: convert kotlin-web-site docs to JSON (ADFA-5039)
alexmmiller Aug 6, 2026
421e529
Add Kotlin docs DB pipeline + Build Kotlin Docs GitHub Action (ADFA-4…
alexmmiller Aug 6, 2026
6b25e26
Address PR review feedback and add regression test suite
alexmmiller Aug 7, 2026
5ec1f60
Fix 5 regressions from round-2 PR review
Aug 10, 2026
66da59d
Address Hal's PR #24 review feedback
Aug 10, 2026
5336ee5
Fix 3 issues from Hal's latest code review
Aug 12, 2026
1cf41d2
Merge remote-tracking branch 'origin/fix/ADFA-5039' into fix/ADFA-4739
Aug 13, 2026
26cb831
Fix find_include_warnings crashing on a non-UTF-8 file
Aug 13, 2026
26c6250
Compress Kotlin-website Content rows against a shared Brotli dictionary
davidschachterADFA Aug 15, 2026
09ca170
Add whole-database migration to shared-dictionary Brotli
davidschachterADFA Aug 15, 2026
97755b1
Make docdb-studio's Content reads/writes dictionary-aware
davidschachterADFA Aug 15, 2026
2827bfb
Parallelize the whole-database migration's read+compress phase
davidschachterADFA Aug 15, 2026
b203500
ADFA-5141: Pin page_size in populate_db.py's own VACUUM
davidschachterADFA Aug 16, 2026
b09331f
ADFA-5141: Fix the same WAL deadlock in populate_db.py's own VACUUM
davidschachterADFA Aug 17, 2026
b5084b5
ADFA-5141: Restore file permissions after the VACUUM INTO swap
davidschachterADFA Aug 17, 2026
7970cdd
ADFA-5141: Use a bound parameter for VACUUM INTO's target, close jour…
davidschachterADFA Aug 17, 2026
dda6410
ADFA-5141: Fix chmod ordering and unclosed connections, matching PR #25
davidschachterADFA Aug 18, 2026
801f5eb
Revert ADFA-5141 page_size pinning: declined, keeping this PR scoped …
davidschachterADFA Aug 18, 2026
358276d
ADFA-5171: Add a repair script for chunked rows misnumbered from -2
davidschachterADFA Aug 18, 2026
0599a37
Merge pull request #27 from appdevforall/fix/ADFA-5171-fragment-renum…
davidschachterADFA Aug 18, 2026
838ac44
ADFA-5153: Address review findings on the dictionary migration
davidschachterADFA Aug 21, 2026
4d4f37d
ADFA-5153: Route the last LIKE delete through fragment_chain, declare…
davidschachterADFA Aug 22, 2026
4b19f14
ADFA-5153: Add the dictionary re-mint tooling used on the 21-Aug data…
davidschachterADFA Aug 22, 2026
25d284f
ADFA-5153: Tell Windows users how to install the brotli CLI, and mean it
davidschachterADFA Aug 22, 2026
5a00e22
Merge pull request #26 from appdevforall/ADFA-5153-content-brotli-dic…
davidschachterADFA Aug 22, 2026
4fddb34
Fix 10 issues from Hal's automated corpus review
Aug 24, 2026
7499935
Fix 9 issues from the PR #24 database-insertion review
Aug 24, 2026
8ace4f4
Merge fix/ADFA-4737 into fix/ADFA-4739 (shared-Brotli-dictionary pipe…
Aug 24, 2026
1d7f6a7
Fix 6 latent defects from the md_to_json.py code review
Aug 25, 2026
80e234a
Merge fix/ADFA-5039 into fix/ADFA-4739 (md_to_json.py review fixes)
Aug 25, 2026
06a43f5
Make the Build Kotlin Docs pipeline actually runnable end-to-end
alexmmiller Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
405 changes: 405 additions & 0 deletions .github/workflows/build-kotlin-docs-local.yaml

Large diffs are not rendered by default.

438 changes: 438 additions & 0 deletions .github/workflows/build-kotlin-docs.yaml

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion .github/workflows/docdb-regression-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ jobs:
echo "Extracting database from zip file..."

# Install unzip if not available
sudo apt-get update -qq && sudo apt-get install -y unzip
# brotli: docdb_studio reads dictionary-compressed Content rows through the
# CLI (ADFA-5153); the downloaded production database is one of those.
sudo apt-get update -qq && sudo apt-get install -y unzip brotli

# Extract the zip file
if ! unzip -o documentation.zip; then
Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,11 @@ __pycache__/
*$py.class
*.db
*.sqlite
run_e2e_pipeline_test.local.sh
grep_content_blobs.local.py

# Timestamped safety backups written by populate_db.py /
# insert_optimized_media.py / sync_kdoc_json_to_db.py before they modify a
# database ("*.db" above does not match these - the timestamp comes last).
*.db.backup-*
*.db.bak.*
219 changes: 219 additions & 0 deletions CLAUDE.md

Large diffs are not rendered by default.

147 changes: 147 additions & 0 deletions Dokka-plugin-kdoc2json/scripts/kotlin/build-stdlib-json-docs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#!/usr/bin/env bash
# Builds the kotlin-stdlib/kotlin-test/kotlin-reflect API docs as JSON via the
# kdoc-to-json Dokka plugin, against a full kotlin/ (https://github.com/JetBrains/kotlin)
# repo checkout - freshly compiling and publishing the plugin from source
# first, so every run picks up whatever's currently in
# Dokka-plugin-kdoc2json/kdoc-to-json/src, not a jar left over from an
# earlier run.
#
# Only generates the JSON output (dokkaGenerateModuleJson), not the default
# HTML - JSON/latest/all-libs is the only thing this project's pipeline
# (sync_kdoc_json_to_db.py) consumes. Use build-kotlin-stdlib.sh directly,
# against libraries/tools/kotlin-stdlib-docs, if you also want the HTML
# comparison output that test_kotlin_stdlib.sh checks against.
#
# The target kotlin-stdlib-docs project's build.gradle.kts is swapped out
# for this directory's own (JSON-plugin-enabled) copy for the duration of
# the build, then restored automatically on exit - the kotlin checkout is
# left exactly as it was found, whether the build succeeds or fails.
#
# kotlin_big (a subproject of kotlin-stdlib-docs) extracts the actual
# kotlin-stdlib/-reflect/-test binaries it documents from a Maven repo. Left to
# its own devices it looks for "<kotlin-repo-root>/build/repo" at the checkout's
# own defaultSnapshotVersion - i.e. artifacts that only exist if you have built
# the entire kotlin repo locally first, and that are published nowhere public.
# Against a plain `git clone --depth 1` that resolves to nothing and the build
# fails before generating any docs. --kotlin-libs-version / --kotlin-libs-repo
# point it at already-published artifacts instead, which is hours of CI cheaper
# than building Kotlin just to document it.
#
# Only the final output path is written to stdout; every other message goes
# to stderr, so this composes as:
# STDLIB_ALL_LIBS="$(build-stdlib-json-docs.sh <kotlin-repo-root>)"
#
# Usage:
# build-stdlib-json-docs.sh [options] <kotlin-repo-root> [output-dir]
#
# Options:
# --kotlin-libs-version V Version of the kotlin-stdlib/-reflect/-test
# artifacts to document (Gradle -PdeployVersion).
# Should match <kotlin-repo-root>'s checked-out ref.
# Default: unset, i.e. the checkout's own
# defaultSnapshotVersion, which needs a local build
# of the kotlin repo to exist.
# --kotlin-libs-repo URL Maven repo to resolve them from (Gradle
# -PkotlinLibsRepo). Default: unset. kotlin_big
# already declares mavenCentral(), so a released
# --kotlin-libs-version needs no repo override; this
# is for a private or snapshot repo.
set -euo pipefail

log() { echo "$@" >&2; }

usage() { log "Usage: $0 [--kotlin-libs-version V] [--kotlin-libs-repo URL] <path-to-kotlin-repo-root> [output-dir]"; }

KOTLIN_LIBS_VERSION=""
KOTLIN_LIBS_REPO=""
POSITIONAL=()
while [ $# -gt 0 ]; do
case "$1" in
--kotlin-libs-version) KOTLIN_LIBS_VERSION="$2"; shift 2 ;;
--kotlin-libs-repo) KOTLIN_LIBS_REPO="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
--) shift; POSITIONAL+=("$@"); break ;;
-*) log "error: unrecognized option '$1'"; usage; exit 1 ;;
*) POSITIONAL+=("$1"); shift ;;
esac
done
set -- ${POSITIONAL[@]+"${POSITIONAL[@]}"}

if [ $# -lt 1 ]; then
usage
exit 1
fi

KOTLIN_ROOT="$(cd "$1" && pwd)"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PLUGIN_DIR="$(cd "$SCRIPT_DIR/../../kdoc-to-json" && pwd)"
STDLIB_DOCS_DIR="$KOTLIN_ROOT/libraries/tools/kotlin-stdlib-docs"
OUTPUT_ROOT="$(mkdir -p "${2:-$SCRIPT_DIR/build-output}" && cd "${2:-$SCRIPT_DIR/build-output}" && pwd)"
JSON_OUTPUT_DIR="$OUTPUT_ROOT/json"

# Passed through only when set, so an unset value falls through to the
# build's own default rather than overriding it with an empty string.
ARTIFACT_ARGS=()
[ -n "$KOTLIN_LIBS_VERSION" ] && ARTIFACT_ARGS+=("-PdeployVersion=$KOTLIN_LIBS_VERSION")
[ -n "$KOTLIN_LIBS_REPO" ] && ARTIFACT_ARGS+=("-PkotlinLibsRepo=$KOTLIN_LIBS_REPO")

if [ ! -f "$KOTLIN_ROOT/gradle.properties" ]; then
log "error: '$KOTLIN_ROOT' doesn't look like a kotlin repo checkout (missing gradle.properties)."
exit 1
fi
if [ ! -f "$STDLIB_DOCS_DIR/settings.gradle.kts" ] || [ ! -x "$STDLIB_DOCS_DIR/gradlew" ]; then
log "error: '$STDLIB_DOCS_DIR' doesn't look like a kotlin-stdlib-docs project (missing settings.gradle.kts or gradlew)."
exit 1
fi
if [ ! -x "$PLUGIN_DIR/gradlew" ]; then
log "error: kdoc-to-json plugin project not found at '$PLUGIN_DIR' (missing gradlew)."
exit 1
fi

# The JSON-plugin-enabled build.gradle.kts we're about to install reads
# dokka_version as a plain Gradle project property (-Pdokka_version=...)
# rather than through this repo's own version catalog, so it has to be
# supplied explicitly - pulled from the same catalog entry the rest of the
# kotlin repo's Dokka usage is pinned to, so it never drifts out of sync.
DOKKA_VERSION="$(grep -m1 '^dokka[[:space:]]*=' "$KOTLIN_ROOT/gradle/libs.versions.toml" | sed -E 's/^dokka[[:space:]]*=[[:space:]]*"([^"]*)".*/\1/')"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

F11 · medium

Under set -euo pipefail (L49), this command substitution kills the script when grep matches nothing — so the friendly if [ -z "$DOKKA_VERSION" ] message at L107-110 is dead code and can never print.

A kotlin ref that renames the dokka catalog key fails "Step 4/5" with exit 1 and zero diagnostic output. Appending || true, or splitting the grep and sed into separate steps, restores the intended message.

if [ -z "$DOKKA_VERSION" ]; then
log "error: couldn't find a 'dokka = \"...\"' entry in $KOTLIN_ROOT/gradle/libs.versions.toml"
exit 1
fi

log "==> [1/2] Building and publishing a fresh copy of the kdoc-to-json plugin..."
# Sent to stderr (fd 2), not left on stdout - a caller doing
# STDLIB_ALL_LIBS="$(build-stdlib-json-docs.sh ...)" must only capture the
# final path this script echoes, not gradlew's own build console output.
( cd "$PLUGIN_DIR" && ./gradlew clean publishToMavenLocal ) >&2

log "==> Installing kdoc-to-json-enabled build.gradle.kts into $STDLIB_DOCS_DIR"
ORIGINAL_BUILD_GRADLE="$(mktemp)"
cp "$STDLIB_DOCS_DIR/build.gradle.kts" "$ORIGINAL_BUILD_GRADLE"
restore_build_gradle() {
cp "$ORIGINAL_BUILD_GRADLE" "$STDLIB_DOCS_DIR/build.gradle.kts"
rm -f "$ORIGINAL_BUILD_GRADLE"
}
trap restore_build_gradle EXIT

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

F12 · medium

trap ... EXIT doesn't fire on an untrapped fatal signal, which contradicts the header's promise at L15-18 that "the kotlin checkout is left exactly as it was found."

Ctrl-C during the multi-hour Gradle build leaves the swapped-in build.gradle.kts sitting in the developer's kotlin clone and orphans the mktemp original. trap restore_build_gradle EXIT INT TERM HUP covers it.

cp "$SCRIPT_DIR/build.gradle.kts" "$STDLIB_DOCS_DIR/build.gradle.kts"

log "==> [2/2] Generating JSON documentation via kdoc-to-json (dokka $DOKKA_VERSION)..."
log " stdlib artifacts: ${KOTLIN_LIBS_VERSION:-(checkout default: needs a local kotlin build)}" \
"from ${KOTLIN_LIBS_REPO:-(mavenCentral + checkout default repo)}"
# --refresh-dependencies forces Gradle to re-resolve the just-published
# SNAPSHOT jar from mavenLocal() rather than serving a same-GAV copy it
# cached from an earlier run of this same script.
( cd "$STDLIB_DOCS_DIR" && ./gradlew dokkaGenerateModuleJson \
"-PdocsBuildDir=$JSON_OUTPUT_DIR" \
"-Pdokka_version=$DOKKA_VERSION" \
${ARTIFACT_ARGS[@]+"${ARTIFACT_ARGS[@]}"} \
--refresh-dependencies ) >&2

ALL_LIBS_DIR="$JSON_OUTPUT_DIR/latest/all-libs"
if [ ! -d "$ALL_LIBS_DIR" ]; then
log "error: expected output at '$ALL_LIBS_DIR' but it wasn't created."
exit 1
fi

log "==> Done."
echo "$ALL_LIBS_DIR"
76 changes: 73 additions & 3 deletions Dokka-plugin-kdoc2json/scripts/kotlin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,54 @@ allprojects {
// 3. Maven Central (Keep this for standard standard stable libraries like Gson/Coroutines)
mavenCentral()

// ALL REMOTE JETBRAINS SNAPSHOT SERVERS HAVE BEEN REMOVED!
// 4. Dokka's own dev-snapshot server - required by plugins:dokka-samples-transformer-plugin
// and plugins:dokka-version-filter-plugin (both included by kotlin-stdlib-docs'
// settings.gradle.kts and pulled onto the build graph by its dokka-convention plugin),
// which pin to a Dokka dev build rather than a Maven Central release. Same property +
// default kotlin-stdlib-docs' own settings.gradle.kts uses, so this only ever points
// wherever that project already expects it to.
maven(url = providers.gradleProperty("dokka_repository")
.getOrElse("https://redirector.kotlinlang.org/maven/dokka-dev"))
}

// --- ADDED THIS EXCLUSION BLOCK ---
// Globally ignore the remote playground plugin since it is trapped on the dead 503 server.
configurations.all {
exclude(group = "org.jetbrains.dokka", module = "kotlin-playground-samples-plugin")
}

// kotlin-stdlib-docs' own two Dokka plugin subprojects
// (plugins:dokka-samples-transformer-plugin, plugins:dokka-version-filter-plugin)
// each hardcode `kotlin { jvmToolchain(8) }`. Both are pulled onto the build
// graph by the dokka-convention plugin, and dokkaGenerateModuleJson below
// depends on dokkaGeneratePublicationHtml, so their dependencies have to
// resolve even though this build only ever wants JSON out. Gradle then needs a
// JDK 8 toolchain, and with no toolchain download repository configured it
// fails during task-graph resolution before a single doc is generated:
// > Failed to calculate the value of task
// ':plugins:dokka-samples-transformer-plugin:compileJava' property 'javaCompiler'.
// > Cannot find a Java installation on your machine ... matching:
// {languageVersion=8, ...}. Toolchain download repositories have not been configured.
// That only survives on a runner that happens to have an EOL JDK 8 lying
// around for auto-detection to find; it fails on any that doesn't, which
// includes the act container build-kotlin-docs-local.yaml is written for.
// These two plugins are only ever loaded in-process by Dokka, under the same
// JVM this build is already running on, so compiling them for that JVM instead
// is sufficient - and it means the pipeline needs exactly one JDK (the 17 that
// .github/workflows/build-kotlin-docs*.yaml installs), not two.
//
// Deliberately keyed off the running JVM rather than a hardcoded 17: whatever
// JDK Gradle is on is guaranteed to be present, so this can never ask for a
// toolchain that isn't installed, even if the workflow's java-version moves.
// Registered via plugins.withId + afterEvaluate so it runs after each
// subproject's own jvmToolchain(8) call rather than being overwritten by it.
plugins.withId("org.jetbrains.kotlin.jvm") {
afterEvaluate {
extensions.findByType(JavaPluginExtension::class.java)?.toolchain {
languageVersion.set(JavaLanguageVersion.of(JavaVersion.current().majorVersion))
}
}
}
}

val isTeamcityBuild = project.hasProperty("teamcity.version") ||
Expand All @@ -70,8 +110,38 @@ val defaultSnapshotVersion: String by rootProperties
val kotlinLanguageVersion: String by rootProperties

val githubRevision = if (isTeamcityBuild) project.property("githubRevision") else "master"
val artifactsVersion by extra(if (isTeamcityBuild) project.property("deployVersion") as String else defaultSnapshotVersion)
val artifactsRepo by extra(if (isTeamcityBuild) project.property("kotlinLibsRepo") as String else "$kotlin_root/build/repo")

// Where kotlin_big pulls the kotlin-stdlib/-reflect/-test binaries it extracts
// and documents from, and at what version.
//
// Upstream only honours -PkotlinLibsRepo/-PdeployVersion under TeamCity, and
// otherwise hardcodes "$kotlin_root/build/repo" at defaultSnapshotVersion
// (2.5.255-SNAPSHOT here) - i.e. the artifacts a *local build of the kotlin
// repo itself* would have published. A plain `git clone --depth 1` has no such
// build output, and that snapshot version is published nowhere public, so
// :kotlin_big:extractStdlibCommonMain fails to resolve and the whole docs build
// dies before generating anything:
// > Could not find org.jetbrains.kotlin:kotlin-stdlib:2.5.255-SNAPSHOT
// Building the kotlin repo just to get them is hours of CI for artifacts that
// already exist on Maven Central, so accept both as ordinary Gradle properties
// regardless of TeamCity. kotlin_big already declares mavenCentral() alongside
// artifactsRepo, so a released version resolves with no repo override at all -
// -PkotlinLibsRepo is there for a private/snapshot repo (and to keep the pair
// symmetric with upstream's own TeamCity path).
//
// Blank is treated as unset so a workflow input that defaults to '' falls
// through to the upstream behaviour rather than resolving against an empty URL.
fun overrideOrNull(name: String): String? =
(findProperty(name) as String?)?.takeIf { it.isNotBlank() }

val artifactsVersion by extra(
overrideOrNull("deployVersion")
?: if (isTeamcityBuild) project.property("deployVersion") as String else defaultSnapshotVersion
)
val artifactsRepo by extra(
overrideOrNull("kotlinLibsRepo")
?: if (isTeamcityBuild) project.property("kotlinLibsRepo") as String else "$kotlin_root/build/repo"
)
val dokka_version: String by project

println("# Parameters summary:")
Expand Down
Loading