Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 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
421e529
Add Kotlin docs DB pipeline + Build Kotlin Docs GitHub Action (ADFA-4…
alexmmiller Aug 6, 2026
66da59d
Address Hal's PR #24 review feedback
Aug 10, 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
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
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
d9df0f0
Fix the 15 findings from Hal's PR #24 review, with regression tests
alexmmiller Sep 1, 2026
e6786a8
Merge origin/main into fix/ADFA-4739
alexmmiller Sep 2, 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
422 changes: 422 additions & 0 deletions .github/workflows/build-kotlin-docs-local.yaml

Large diffs are not rendered by default.

110 changes: 92 additions & 18 deletions .github/workflows/build-kotlin-docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,39 @@ on:
Ignored if skip_website_docs is true.
required: false
default: ''
kotlin_libs_version:
description: >-
Version of the published kotlin-stdlib/-reflect/-test artifacts to
document (Gradle -PdeployVersion). kotlin_big extracts the real
binaries at this version rather than requiring a local build of the
whole kotlin repo, which is what the checkout's own
defaultSnapshotVersion would otherwise demand. Keep this in step with
kotlin_ref. Set empty to fall back to that snapshot default, which
only resolves if you have built the kotlin repo yourself.
required: false
default: '2.4.10'
kotlin_libs_repo:
description: >-
Maven repository to resolve those artifacts from (Gradle
-PkotlinLibsRepo). kotlin_big already declares mavenCentral(), so a
released kotlin_libs_version needs nothing here; set it to point at a
private or snapshot repository instead.
required: false
default: ''
skip_website_docs:
description: 'Skip the kotlin-web-site steps and only refresh kotlin-stdlib/-reflect/-test JSON content.'
required: false
default: false
type: boolean
skip_stdlib_docs:
description: >-
Skip the kotlin-stdlib/-reflect/-test steps (cloning JetBrains/kotlin,
the Dokka JSON build, and the sync into the database) and only refresh
the kotlin-web-site content. The mirror image of skip_website_docs -
setting both leaves nothing for the run to do and is rejected.
required: false
default: false
type: boolean
dry_run:
description: >-
If true, build and verify everything but do NOT upload the result
Expand Down Expand Up @@ -108,12 +136,34 @@ jobs:
# Drive). Leave both empty ('') for normal operation.
TEST_DB_FILE_ID: ''
TEST_IMAGES_ZIP_FILE_ID: ''
SKIP_WEBSITE_DOCS: ${{ inputs.skip_website_docs }}
KOTLIN_LIBS_VERSION: ${{ inputs.kotlin_libs_version }}
KOTLIN_LIBS_REPO: ${{ inputs.kotlin_libs_repo }}
SKIP_STDLIB_DOCS: ${{ inputs.skip_stdlib_docs }}
# The ADFA-4737 blacklist, defined once and consumed by both the
# populate_db.py step and the verification step that checks its effect.
# Previously spelled out separately in each, which let the verification
# drift onto a different list than the one actually applied and still
# report PASS. run_e2e_pipeline_test.sh already had it right (a single
# BLACKLIST array expanded at both call sites); this matches that.
# One entry per line, read back with `mapfile -t`. Re-derive these from
# kotlin-web-site/docs/kr.tree if its nav structure has changed.
# "|-" (not "|") so there's no trailing blank line to become a 4th,
# empty array element.
BLACKLISTED_ELEMENT_TITLES: |-
Development\/Web development
Interoperability\/Swift/Objective-C and C interop
Interoperability\/JavaScript interop
steps:
- name: Checkout OfflineDocumentationTools
uses: actions/checkout@v4

- name: Resolve Google Drive file IDs
run: |
if [ "$SKIP_WEBSITE_DOCS" = "true" ] && [ "$SKIP_STDLIB_DOCS" = "true" ]; then
echo "Error: skip_website_docs and skip_stdlib_docs are both true - that skips every step that changes the database, leaving nothing for this run to do" >&2
exit 1
fi
DB_FILE_ID="${TEST_DB_FILE_ID:-$DB_FILE_ID_SECRET}"
IMG_FILE_ID="${TEST_IMAGES_ZIP_FILE_ID:-${IMAGES_ZIP_FILE_ID_INPUT:-$IMAGES_ZIP_FILE_ID_SECRET}}"
if [ -z "$DB_FILE_ID" ]; then
Expand Down Expand Up @@ -151,11 +201,11 @@ jobs:
- name: Install Python dependencies
run: |
pip install -r requirements.txt
# markdown-it-py/scour/cairosvg: ProcessKotlinWebsiteJSON's own
# requirements (see its README), not in the root requirements.txt.
# markdown-it-py: ProcessKotlinWebsiteJSON's own requirement (see
# its README); scour/cairosvg are in requirements.txt already.
# google-api-python-client & friends: Drive download/upload, same
# libraries check-tools/download_database.py already depends on.
pip install markdown-it-py scour cairosvg \
pip install markdown-it-py \

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 — pip in CI against the repo's uv convention, plus a dead install

Two things:

  1. pip usage. The convention for this project is uv for Python dependency management — never pip, pip3, or the uv pip shim. This PR touches three such steps and leaves them all on pip: build-kotlin-docs.yaml:203/208, build-kotlin-docs-local.yaml:218/221, and python-tests.yaml:56. uv is already wired up in this repo — python-tests.yaml installs it for the docdb-studio job — and the ProcessKotlinWebsiteJSON README documents uv run --with-requirements ... as the supported invocation.

  2. Dead install + wrong comment. The edited comment says "markdown-it-py: ProcessKotlinWebsiteJSON's own requirement (see its README); scour/cairosvg are in requirements.txt already" — but markdown-it-py>=2.0 is in requirements.txt on main and was installed by the preceding line all along. The extra pip install markdown-it-py is dead work and the comment's premise is wrong.

google-api-python-client google-auth-httplib2 google-auth-oauthlib

- name: Authenticate to Google Cloud using Workload Identity Federation
Expand Down Expand Up @@ -226,24 +276,22 @@ jobs:
- name: 'Step 2/5: populate_db.py (convert docs, prune blacklist, insert into db)'
if: ${{ !inputs.skip_website_docs }}
run: |
# Same three blacklist entries as run_e2e_pipeline_test.sh
# (ADFA-4737) - re-derive these from kotlin-web-site/docs/kr.tree
# if its nav structure has changed since this was written.
# BLACKLISTED_ELEMENT_TITLES is defined once in this job's env: block
# (ADFA-4737); the verification step below reads the same variable.
mapfile -t BLACKLIST <<< "$BLACKLISTED_ELEMENT_TITLES"
python3 ProcessDocs/ProcessKotlinDocs/ProcessKotlinWebsiteJSON/populate_db.py \
kotlin-web-site/docs \
ProcessDocs/ProcessKotlinDocs/ProcessKotlinWebsiteJSON/config.json \
webHelpImages.zip \
documentation.db \
--blacklisted-element-titles \
'Development\/Web development' \
'Interoperability\/Swift/Objective-C and C interop' \
'Interoperability\/JavaScript interop'
--blacklisted-element-titles "${BLACKLIST[@]}"

- name: 'Step 3/5: insert_optimized_media.py (re-optimize + reinsert images)'
if: ${{ !inputs.skip_website_docs }}
run: |
# --webp requires an "image/webp" ContentTypes row, which this
# database doesn't ship with by default (idempotent).
# --webp requires an "image/webp" ContentTypes row. The current
# production database already has one, so this is normally a no-op;
# it stays for older copies that predate it (idempotent either way).
sqlite3 documentation.db \
"INSERT OR IGNORE INTO ContentTypes (value, compression) VALUES ('image/webp', 'brotli');"
mkdir -p media
Expand All @@ -253,19 +301,26 @@ jobs:
--jpeg-quality 85 --webp --webp-quality 90 --verbose

- name: Clone kotlin (for kotlin-stdlib-docs)
if: ${{ !inputs.skip_stdlib_docs }}
run: |
ARGS=(--depth 1)
[ -n "$KOTLIN_REF" ] && ARGS+=(--branch "$KOTLIN_REF")
git clone "${ARGS[@]}" https://github.com/JetBrains/kotlin.git kotlin-repo

- name: 'Step 4/5: build-stdlib-json-docs.sh (fresh plugin build -> kotlin-stdlib/-reflect/-test JSON)'
if: ${{ !inputs.skip_stdlib_docs }}
id: stdlib_docs
run: |
OUTPUT="$(Dokka-plugin-kdoc2json/scripts/kotlin/build-stdlib-json-docs.sh kotlin-repo stdlib-json-build)"
ARGS=()
[ -n "$KOTLIN_LIBS_VERSION" ] && ARGS+=(--kotlin-libs-version "$KOTLIN_LIBS_VERSION")
[ -n "$KOTLIN_LIBS_REPO" ] && ARGS+=(--kotlin-libs-repo "$KOTLIN_LIBS_REPO")
OUTPUT="$(Dokka-plugin-kdoc2json/scripts/kotlin/build-stdlib-json-docs.sh \
"${ARGS[@]}" kotlin-repo stdlib-json-build)"
echo "Generated JSON docs at $OUTPUT"
echo "all_libs_dir=$OUTPUT" >> "$GITHUB_OUTPUT"

- name: 'Step 5/5: sync_kdoc_json_to_db.py (overwrite kotlin-stdlib/-reflect/-test content)'
if: ${{ !inputs.skip_stdlib_docs }}
run: |
python3 scripts/sync_kotlin_stdlib_docs/sync_kdoc_json_to_db.py \
"${{ steps.stdlib_docs.outputs.all_libs_dir }}" --db documentation.db
Expand Down Expand Up @@ -294,10 +349,12 @@ jobs:
- name: Blacklist pruning verification
if: ${{ !inputs.skip_website_docs }}
run: |
# Same BLACKLISTED_ELEMENT_TITLES the populate_db.py step above
# applied - read from the job env rather than restated here, so this
# check can't silently verify a different list than the one used.
mapfile -t BLACKLIST <<< "$BLACKLISTED_ELEMENT_TITLES"
python3 - ProcessDocs/ProcessKotlinDocs/ProcessKotlinWebsiteJSON kotlin-web-site/docs documentation.db \
'Development\/Web development' \
'Interoperability\/Swift/Objective-C and C interop' \
'Interoperability\/JavaScript interop' <<'PYEOF'
"${BLACKLIST[@]}" <<'PYEOF'
import sqlite3
import sys
import xml.etree.ElementTree as ET
Expand Down Expand Up @@ -367,15 +424,32 @@ jobs:
print(f"Uploaded new revision of {file_id}: {updated}")
PYEOF

# if: always() - the baton must be dropped even when the build fails,
# otherwise the channel shows it held forever by a dead run, which is the
# exact failure this convention exists to prevent. Deliberately NOT gated
# on dry_run: "build started" above is ungated, and since dry_run defaults
# to true an asymmetric gate meant every ordinary run grabbed the baton
# and never dropped it. The message reports the outcome instead.
- name: 'Notify Slack: build complete'
if: ${{ !inputs.dry_run }}
if: always()
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
JOB_STATUS: ${{ job.status }}
DRY_RUN: ${{ inputs.dry_run }}
run: |
# The baton is always dropped, so the text has to say what actually
# happened rather than always claiming an update.
if [ "$JOB_STATUS" != "success" ]; then
TEXT="Kotlin documentation build FAILED ($JOB_STATUS) - database unchanged. Dropping baton"
elif [ "$DRY_RUN" = "true" ]; then
TEXT="Kotlin documentation dry run complete - database unchanged. Dropping baton"
else
TEXT="Updated Kotlin documentation. Dropping baton"
fi
if [ -z "$SLACK_WEBHOOK_URL" ]; then
echo "SLACK_WEBHOOK_URL not set - skipping Slack notification" >&2
else
curl -sS -X POST -H 'Content-type: application/json' \
--data '{"text": "Updated Kotlin documentation. Dropping baton"}' \
--data "$(printf '{"text": "%s"}' "$TEXT")" \
"$SLACK_WEBHOOK_URL" || echo "warning: Slack notification failed" >&2
fi
70 changes: 70 additions & 0 deletions .github/workflows/python-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Python tests

# Runs the repo's pytest suites on every push and PR.
#
# Added because nothing here executed them: the Kotlin-docs pipeline ships
# ~1,300 lines of regression tests covering permanent, silent data-loss paths
# (a migration deleting an unrelated page, an image optimizer collapsing two
# sources onto one output, a chunk chain reassembling truncated), and until now
# they only ran when someone remembered to run them locally. Tests that never
# run in CI rot, and these are exactly the ones whose failure is invisible
# without them.
#
# The suites are separate because their dependencies are: docdb-studio and
# check-tools each own a pyproject.toml + uv.lock, while ProcessKotlinWebsiteJSON

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.

F14 · Medium — this header explains a three-way split, but only two suites actually run

The comment reads "docdb-studio and check-tools each own a pyproject.toml + uv.lock, while ProcessKotlinWebsiteJSON runs against the root requirements.txt", and the workflow's stated purpose is "Runs the repo's pytest suites on every push and PR". There are three suites' worth of tests in the repo and only two are executed.

check-tools/tests/test_db_health_checker.py — covering the health checker wired into the daily docdb-regression-test.yaml cron — continues to run nowhere. Add a uv run --frozen -- python -m pytest -q step with working-directory: check-tools, matching the docdb-studio step.

# runs against the root requirements.txt.

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: python-tests-${{ github.ref }}
cancel-in-progress: true

jobs:
pytest:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install system dependencies
run: |
sudo apt-get update -y
# pngquant: optimize_media.py shells out to it, and its tests exercise
# the real binary rather than mocking it.
# brotli: the CLI, not the Python package - DictionaryCompressor uses
# it because no Python binding exposes a custom dictionary.
# zstd: train_dictionary uses `zstd --train-fastcover` to build the
# shared dictionary the migration tests need.
sudo apt-get install -y pngquant brotli zstd

- name: Install Python dependencies
run: |
pip install -r requirements.txt pytest

- name: 'ProcessKotlinWebsiteJSON + sync_kotlin_stdlib_docs'
run: |
python -m pytest \
ProcessDocs/ProcessKotlinDocs/ProcessKotlinWebsiteJSON \
scripts/sync_kotlin_stdlib_docs \
-q

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: docdb-studio
working-directory: docdb-studio
run: uv run --frozen -- python -m pytest -q
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.*
Loading
Loading