-
Notifications
You must be signed in to change notification settings - Fork 629
Docs: own and sync ClickHouse documentation #2945
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,212 @@ | ||
| name: 'Sync docs to ClickHouse/ClickHouse' | ||
|
|
||
| # Opens (or refreshes) a pull request on the aggregator docs repo | ||
| # | ||
| # It fires on three events: | ||
| # * a merged PR carrying the `sync-docs` label -> ship docs immediately, | ||
| # without waiting for a release | ||
| # * a published release -> ship docs on release, gated | ||
| # by RELEASE_SCOPE below (major-only by default); | ||
| # * manual dispatch -> force a sync. | ||
| # | ||
| # A single stable branch is force-pushed each run, so the bot keeps exactly one | ||
| # open PR that is always one commit off the target branch. The PR is labelled | ||
| # `pr-autogenerated-docs`, which the aggregator's docs check recognizes for the | ||
| # autogenerated-region edit guard. The generated body includes the changelog | ||
| # metadata required for the target repository's CI to initialize. | ||
|
|
||
| on: | ||
| pull_request_target: | ||
| types: [closed] | ||
| release: | ||
| types: [published] | ||
| workflow_dispatch: | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Configuration -- edit these to reuse this workflow in another client repo. | ||
| # --------------------------------------------------------------------------- | ||
| env: | ||
| # Repo that receives the docs PR, and the branch that PR targets. | ||
| TARGET_REPO: ClickHouse/ClickHouse | ||
| TARGET_BRANCH: master | ||
| # Path inside TARGET_REPO that mirrors this repo's docs. Wiped and replaced on | ||
| # every sync so deletions and renames propagate. | ||
| TARGET_DOCS_PATH: docs/integrations/language-clients/java | ||
| # This repo's docs file or folder (the source of truth). | ||
| SOURCE_DOCS_PATH: docs/clickhouse-docs | ||
| # Stable branch on TARGET_REPO, force-pushed each run. | ||
| SYNC_BRANCH: robot/docs-sync-clickhouse-java | ||
| # Label the sync PR gets on TARGET_REPO. | ||
| PR_LABEL: pr-autogenerated-docs | ||
| # Label on a merged PR in THIS repo that triggers the expedited path. | ||
| SYNC_LABEL: sync-docs | ||
| # Which releases trigger a sync: major (X.0.0), minor (X.Y.0), or all. | ||
| RELEASE_SCOPE: all | ||
|
|
||
| # The cross-repo work is done with a GitHub App token (see the sync job); this | ||
| # workflow only needs to read its own repository. | ||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| # Gate: decide whether this event should produce a sync, and why. Kept in its | ||
| # own job so the (large) checkout/clone in `sync` only runs when needed. | ||
| decide: | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| run: ${{ steps.gate.outputs.run }} | ||
| reason: ${{ steps.gate.outputs.reason }} | ||
| steps: | ||
| - name: Decide whether to sync | ||
| id: gate | ||
| env: | ||
| EVENT_NAME: ${{ github.event_name }} | ||
| PR_MERGED: ${{ github.event.pull_request.merged }} | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| PR_LABELS: ${{ toJSON(github.event.pull_request.labels.*.name) }} | ||
| RELEASE_TAG: ${{ github.event.release.tag_name }} | ||
| RELEASE_PRERELEASE: ${{ github.event.release.prerelease }} | ||
| run: | | ||
| set -euo pipefail | ||
| run=false | ||
| reason="" | ||
| case "$EVENT_NAME" in | ||
| workflow_dispatch) | ||
| # Manual runs always sync -- the operator explicitly asked for it. | ||
| run=true | ||
| reason="manual dispatch" | ||
| ;; | ||
| pull_request_target) | ||
| # Expedited path: a merged PR that carries the sync label. | ||
| if [[ "$PR_MERGED" == "true" ]] \ | ||
| && printf '%s' "$PR_LABELS" | jq -e --arg l "$SYNC_LABEL" 'index($l) != null' >/dev/null; then | ||
| run=true | ||
| reason="merged PR #${PR_NUMBER} labeled '${SYNC_LABEL}'" | ||
| fi | ||
| ;; | ||
| release) | ||
| if [[ "$RELEASE_PRERELEASE" == "true" ]]; then | ||
| echo "Release ${RELEASE_TAG} is a prerelease; skipping." | ||
| elif [[ "${RELEASE_TAG#v}" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+) ]]; then | ||
| minor="${BASH_REMATCH[2]}" | ||
| patch="${BASH_REMATCH[3]}" | ||
| case "$RELEASE_SCOPE" in | ||
| all) run=true ;; | ||
| minor) [[ "$patch" == "0" ]] && run=true ;; | ||
| major|*) [[ "$minor" == "0" && "$patch" == "0" ]] && run=true ;; | ||
| esac | ||
| [[ "$run" == "true" ]] && reason="release ${RELEASE_TAG} (scope=${RELEASE_SCOPE})" | ||
| else | ||
| echo "Could not parse release tag '${RELEASE_TAG}'; skipping." | ||
| fi | ||
| ;; | ||
| esac | ||
| echo "Decision: run=${run} reason='${reason}'" | ||
| { | ||
| echo "run=${run}" | ||
| echo "reason=${reason}" | ||
| } >> "$GITHUB_OUTPUT" | ||
|
|
||
| sync: | ||
| needs: decide | ||
| if: needs.decide.outputs.run == 'true' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout docs source | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| # On a release, take the docs as of the released tag; otherwise take | ||
| # the default checkout (post-merge default branch / dispatched ref). | ||
| ref: ${{ github.event_name == 'release' && github.event.release.tag_name || '' }} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fork merge checkout failsHigh Severity The sync job leaves Reviewed by Cursor Bugbot for commit 592e8d1. Configure here. |
||
|
|
||
| - name: Parse target repo | ||
| id: parse | ||
| run: | | ||
| set -euo pipefail | ||
| echo "owner=${TARGET_REPO%%/*}" >> "$GITHUB_OUTPUT" | ||
| echo "name=${TARGET_REPO##*/}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Generate token for target repo | ||
| id: app-token | ||
| uses: actions/create-github-app-token@v3 | ||
| with: | ||
| app-id: ${{ secrets.WORKFLOW_AUTH_PUBLIC_APP_ID }} | ||
| private-key: ${{ secrets.WORKFLOW_AUTH_PUBLIC_PRIVATE_KEY }} | ||
| owner: ${{ steps.parse.outputs.owner }} | ||
| repositories: ${{ steps.parse.outputs.name }} | ||
|
|
||
| - name: Sync docs and open/refresh PR | ||
| env: | ||
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | ||
| REASON: ${{ needs.decide.outputs.reason }} | ||
| SOURCE_REPO: ${{ github.repository }} | ||
| SOURCE_REF: ${{ github.event_name == 'release' && github.event.release.tag_name || github.ref_name }} | ||
| SOURCE_SHA: ${{ github.sha }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| # Resolve the absolute source path before we cd elsewhere. | ||
| src="$(realpath "$SOURCE_DOCS_PATH")" | ||
|
|
||
| workdir="$(mktemp -d)" | ||
| git clone --depth 1 --branch "$TARGET_BRANCH" \ | ||
| "https://x-access-token:${GH_TOKEN}@github.com/${TARGET_REPO}.git" "$workdir" | ||
| cd "$workdir" | ||
|
|
||
| # Replace the target file or folder with this repo's docs. Wipe first | ||
| # so deletions and renames on our side propagate. | ||
| target="${workdir:?}/${TARGET_DOCS_PATH}" | ||
| rm -rf "$target" | ||
| mkdir -p "$(dirname "$target")" | ||
| if [[ -d "$src" ]]; then | ||
| mkdir -p "$target" | ||
| cp -R "${src}/." "${target}/" | ||
| else | ||
| cp "$src" "$target" | ||
| fi | ||
|
|
||
| git config user.name "clickhouse-docs-bot" | ||
| git config user.email "clickhouse-docs-bot@users.noreply.github.com" | ||
|
|
||
| # Build the single-commit-off-target branch fresh from the target | ||
| # branch so the PR is always exactly one commit ahead. | ||
| git checkout -B "$SYNC_BRANCH" | ||
| git add -A -- "$TARGET_DOCS_PATH" | ||
| if git diff --cached --quiet; then | ||
| echo "No docs changes to sync; nothing to do." | ||
| exit 0 | ||
| fi | ||
|
|
||
| git commit -m "Sync ${SOURCE_REPO} docs (${REASON})" | ||
| git push --force origin "$SYNC_BRANCH" | ||
|
|
||
| title="Docs: Sync ${SOURCE_REPO} docs" | ||
| body=$(cat <<EOF | ||
| Automated one-way docs sync from [\`${SOURCE_REPO}\`](https://github.com/${SOURCE_REPO}). | ||
|
|
||
| - **Source ref:** \`${SOURCE_REF}\` (\`${SOURCE_SHA}\`) | ||
| - **Trigger:** ${REASON} | ||
| - **Replaces:** \`${TARGET_DOCS_PATH}\` | ||
|
|
||
| This PR is generated by the \`Sync docs to ClickHouse/ClickHouse\` workflow in \`${SOURCE_REPO}\`. | ||
| The source repo is the source of truth for these docs; edit them there, not here. | ||
|
|
||
| ### Changelog category (leave one): | ||
| - Documentation (changelog entry is not required) | ||
|
|
||
| ### Changelog entry (a [user-readable short description](https://github.com/ClickHouse/ClickHouse/blob/master/docs/changelog_entry_guidelines.md) of the changes that goes into CHANGELOG.md): | ||
| Sync language client documentation from \`${SOURCE_REPO}\`. | ||
| EOF | ||
| ) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indented heredoc breaks bodyMedium Severity The aggregator PR Reviewed by Cursor Bugbot for commit 592e8d1. Configure here. |
||
|
|
||
| existing="$(gh pr list --repo "$TARGET_REPO" --head "$SYNC_BRANCH" \ | ||
| --state open --json number --jq '.[0].number // empty')" | ||
| if [[ -n "$existing" ]]; then | ||
| echo "PR #${existing} already open; force-push refreshed it." | ||
| gh pr edit "$existing" --repo "$TARGET_REPO" \ | ||
| --title "$title" --body "$body" --add-label "$PR_LABEL" | ||
| else | ||
| gh pr create --repo "$TARGET_REPO" \ | ||
| --base "$TARGET_BRANCH" --head "$SYNC_BRANCH" \ | ||
| --title "$title" --body "$body" --label "$PR_LABEL" | ||
| fi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| name: 'Verify Docs' | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: | ||
| - main | ||
| paths: | ||
| - 'docs/clickhouse-docs/**' | ||
| - '.github/workflows/docs_verify.yml' | ||
| push: | ||
| branches: | ||
| - main | ||
| paths: | ||
| - 'docs/clickhouse-docs/**' | ||
| - '.github/workflows/docs_verify.yml' | ||
|
|
||
| jobs: | ||
| verify: | ||
| runs-on: ubuntu-latest | ||
| name: Mintlify checks | ||
| # Run inside the docs image so every dependency (git, python3, mint, node) | ||
| # is present -- nothing is installed, and this repo vendors no scripts. | ||
| container: | ||
| image: clickhouse/docs-builder | ||
| steps: | ||
| - name: Checkout docs source | ||
| uses: actions/checkout@v5 | ||
|
|
||
| # Fetch the check driver from ClickHouse/ClickHouse rather than vendoring | ||
| # it. The driver shallow/sparse-clones the aggregator docs plus its CI | ||
| # scripts (ci/jobs/scripts/docs), so any check scripts come along for free. | ||
| - name: Fetch the docs check driver | ||
| run: | | ||
| curl -fsSL -o /tmp/mintlify_docs_check.py \ | ||
| https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/ci/jobs/scripts/docs/mintlify_docs_check.py | ||
|
|
||
| # The driver clones the aggregator, replaces the Java client folder with this | ||
| # repo's canonical docs (wiping it first), and | ||
| # runs the checks from the docs root. --scoped swaps the full site-wide | ||
| # `mint validate` (~13 min) for a scoped validate of just the replaced | ||
| # slice (docs.json schema, navigation, MDX parse, snippet imports); | ||
| # link integrity is still checked site-wide by the lychee check. | ||
| # It exits non-zero if any check fails, so a green step means all passed. | ||
| - name: Run Mintlify docs checks | ||
| run: | | ||
| python3 /tmp/mintlify_docs_check.py \ | ||
| --repo https://github.com/ClickHouse/ClickHouse.git \ | ||
| --ref master \ | ||
| --replace docs/clickhouse-docs:integrations/language-clients/java \ | ||
| --scoped |


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Release sync skips prereleases
Medium Severity
The new sync workflow skips published releases when
github.event.release.prereleaseis true, but the repository’s existing Release workflow always creates GitHub releases with--prerelease. Thereleasetrigger path therefore never runs after a normal release; docs only sync viasync-docsmerges or manual dispatch unless someone later clears the prerelease flag by hand.Reviewed by Cursor Bugbot for commit 592e8d1. Configure here.