Skip to content

Commit cc57099

Browse files
authored
Merge pull request #2945 from Blargian/agent/own-clickhouse-docs
Docs: own and sync ClickHouse documentation
2 parents dd09060 + 592e8d1 commit cc57099

8 files changed

Lines changed: 3546 additions & 0 deletions

File tree

.github/workflows/docs_sync.yml

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
name: 'Sync docs to ClickHouse/ClickHouse'
2+
3+
# Opens (or refreshes) a pull request on the aggregator docs repo
4+
#
5+
# It fires on three events:
6+
# * a merged PR carrying the `sync-docs` label -> ship docs immediately,
7+
# without waiting for a release
8+
# * a published release -> ship docs on release, gated
9+
# by RELEASE_SCOPE below (major-only by default);
10+
# * manual dispatch -> force a sync.
11+
#
12+
# A single stable branch is force-pushed each run, so the bot keeps exactly one
13+
# open PR that is always one commit off the target branch. The PR is labelled
14+
# `pr-autogenerated-docs`, which the aggregator's docs check recognizes for the
15+
# autogenerated-region edit guard. The generated body includes the changelog
16+
# metadata required for the target repository's CI to initialize.
17+
18+
on:
19+
pull_request_target:
20+
types: [closed]
21+
release:
22+
types: [published]
23+
workflow_dispatch:
24+
25+
# ---------------------------------------------------------------------------
26+
# Configuration -- edit these to reuse this workflow in another client repo.
27+
# ---------------------------------------------------------------------------
28+
env:
29+
# Repo that receives the docs PR, and the branch that PR targets.
30+
TARGET_REPO: ClickHouse/ClickHouse
31+
TARGET_BRANCH: master
32+
# Path inside TARGET_REPO that mirrors this repo's docs. Wiped and replaced on
33+
# every sync so deletions and renames propagate.
34+
TARGET_DOCS_PATH: docs/integrations/language-clients/java
35+
# This repo's docs file or folder (the source of truth).
36+
SOURCE_DOCS_PATH: docs/clickhouse-docs
37+
# Stable branch on TARGET_REPO, force-pushed each run.
38+
SYNC_BRANCH: robot/docs-sync-clickhouse-java
39+
# Label the sync PR gets on TARGET_REPO.
40+
PR_LABEL: pr-autogenerated-docs
41+
# Label on a merged PR in THIS repo that triggers the expedited path.
42+
SYNC_LABEL: sync-docs
43+
# Which releases trigger a sync: major (X.0.0), minor (X.Y.0), or all.
44+
RELEASE_SCOPE: all
45+
46+
# The cross-repo work is done with a GitHub App token (see the sync job); this
47+
# workflow only needs to read its own repository.
48+
permissions:
49+
contents: read
50+
51+
jobs:
52+
# Gate: decide whether this event should produce a sync, and why. Kept in its
53+
# own job so the (large) checkout/clone in `sync` only runs when needed.
54+
decide:
55+
runs-on: ubuntu-latest
56+
outputs:
57+
run: ${{ steps.gate.outputs.run }}
58+
reason: ${{ steps.gate.outputs.reason }}
59+
steps:
60+
- name: Decide whether to sync
61+
id: gate
62+
env:
63+
EVENT_NAME: ${{ github.event_name }}
64+
PR_MERGED: ${{ github.event.pull_request.merged }}
65+
PR_NUMBER: ${{ github.event.pull_request.number }}
66+
PR_LABELS: ${{ toJSON(github.event.pull_request.labels.*.name) }}
67+
RELEASE_TAG: ${{ github.event.release.tag_name }}
68+
RELEASE_PRERELEASE: ${{ github.event.release.prerelease }}
69+
run: |
70+
set -euo pipefail
71+
run=false
72+
reason=""
73+
case "$EVENT_NAME" in
74+
workflow_dispatch)
75+
# Manual runs always sync -- the operator explicitly asked for it.
76+
run=true
77+
reason="manual dispatch"
78+
;;
79+
pull_request_target)
80+
# Expedited path: a merged PR that carries the sync label.
81+
if [[ "$PR_MERGED" == "true" ]] \
82+
&& printf '%s' "$PR_LABELS" | jq -e --arg l "$SYNC_LABEL" 'index($l) != null' >/dev/null; then
83+
run=true
84+
reason="merged PR #${PR_NUMBER} labeled '${SYNC_LABEL}'"
85+
fi
86+
;;
87+
release)
88+
if [[ "$RELEASE_PRERELEASE" == "true" ]]; then
89+
echo "Release ${RELEASE_TAG} is a prerelease; skipping."
90+
elif [[ "${RELEASE_TAG#v}" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+) ]]; then
91+
minor="${BASH_REMATCH[2]}"
92+
patch="${BASH_REMATCH[3]}"
93+
case "$RELEASE_SCOPE" in
94+
all) run=true ;;
95+
minor) [[ "$patch" == "0" ]] && run=true ;;
96+
major|*) [[ "$minor" == "0" && "$patch" == "0" ]] && run=true ;;
97+
esac
98+
[[ "$run" == "true" ]] && reason="release ${RELEASE_TAG} (scope=${RELEASE_SCOPE})"
99+
else
100+
echo "Could not parse release tag '${RELEASE_TAG}'; skipping."
101+
fi
102+
;;
103+
esac
104+
echo "Decision: run=${run} reason='${reason}'"
105+
{
106+
echo "run=${run}"
107+
echo "reason=${reason}"
108+
} >> "$GITHUB_OUTPUT"
109+
110+
sync:
111+
needs: decide
112+
if: needs.decide.outputs.run == 'true'
113+
runs-on: ubuntu-latest
114+
steps:
115+
- name: Checkout docs source
116+
uses: actions/checkout@v5
117+
with:
118+
# On a release, take the docs as of the released tag; otherwise take
119+
# the default checkout (post-merge default branch / dispatched ref).
120+
ref: ${{ github.event_name == 'release' && github.event.release.tag_name || '' }}
121+
122+
- name: Parse target repo
123+
id: parse
124+
run: |
125+
set -euo pipefail
126+
echo "owner=${TARGET_REPO%%/*}" >> "$GITHUB_OUTPUT"
127+
echo "name=${TARGET_REPO##*/}" >> "$GITHUB_OUTPUT"
128+
129+
- name: Generate token for target repo
130+
id: app-token
131+
uses: actions/create-github-app-token@v3
132+
with:
133+
app-id: ${{ secrets.WORKFLOW_AUTH_PUBLIC_APP_ID }}
134+
private-key: ${{ secrets.WORKFLOW_AUTH_PUBLIC_PRIVATE_KEY }}
135+
owner: ${{ steps.parse.outputs.owner }}
136+
repositories: ${{ steps.parse.outputs.name }}
137+
138+
- name: Sync docs and open/refresh PR
139+
env:
140+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
141+
REASON: ${{ needs.decide.outputs.reason }}
142+
SOURCE_REPO: ${{ github.repository }}
143+
SOURCE_REF: ${{ github.event_name == 'release' && github.event.release.tag_name || github.ref_name }}
144+
SOURCE_SHA: ${{ github.sha }}
145+
run: |
146+
set -euo pipefail
147+
148+
# Resolve the absolute source path before we cd elsewhere.
149+
src="$(realpath "$SOURCE_DOCS_PATH")"
150+
151+
workdir="$(mktemp -d)"
152+
git clone --depth 1 --branch "$TARGET_BRANCH" \
153+
"https://x-access-token:${GH_TOKEN}@github.com/${TARGET_REPO}.git" "$workdir"
154+
cd "$workdir"
155+
156+
# Replace the target file or folder with this repo's docs. Wipe first
157+
# so deletions and renames on our side propagate.
158+
target="${workdir:?}/${TARGET_DOCS_PATH}"
159+
rm -rf "$target"
160+
mkdir -p "$(dirname "$target")"
161+
if [[ -d "$src" ]]; then
162+
mkdir -p "$target"
163+
cp -R "${src}/." "${target}/"
164+
else
165+
cp "$src" "$target"
166+
fi
167+
168+
git config user.name "clickhouse-docs-bot"
169+
git config user.email "clickhouse-docs-bot@users.noreply.github.com"
170+
171+
# Build the single-commit-off-target branch fresh from the target
172+
# branch so the PR is always exactly one commit ahead.
173+
git checkout -B "$SYNC_BRANCH"
174+
git add -A -- "$TARGET_DOCS_PATH"
175+
if git diff --cached --quiet; then
176+
echo "No docs changes to sync; nothing to do."
177+
exit 0
178+
fi
179+
180+
git commit -m "Sync ${SOURCE_REPO} docs (${REASON})"
181+
git push --force origin "$SYNC_BRANCH"
182+
183+
title="Docs: Sync ${SOURCE_REPO} docs"
184+
body=$(cat <<EOF
185+
Automated one-way docs sync from [\`${SOURCE_REPO}\`](https://github.com/${SOURCE_REPO}).
186+
187+
- **Source ref:** \`${SOURCE_REF}\` (\`${SOURCE_SHA}\`)
188+
- **Trigger:** ${REASON}
189+
- **Replaces:** \`${TARGET_DOCS_PATH}\`
190+
191+
This PR is generated by the \`Sync docs to ClickHouse/ClickHouse\` workflow in \`${SOURCE_REPO}\`.
192+
The source repo is the source of truth for these docs; edit them there, not here.
193+
194+
### Changelog category (leave one):
195+
- Documentation (changelog entry is not required)
196+
197+
### 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):
198+
Sync language client documentation from \`${SOURCE_REPO}\`.
199+
EOF
200+
)
201+
202+
existing="$(gh pr list --repo "$TARGET_REPO" --head "$SYNC_BRANCH" \
203+
--state open --json number --jq '.[0].number // empty')"
204+
if [[ -n "$existing" ]]; then
205+
echo "PR #${existing} already open; force-push refreshed it."
206+
gh pr edit "$existing" --repo "$TARGET_REPO" \
207+
--title "$title" --body "$body" --add-label "$PR_LABEL"
208+
else
209+
gh pr create --repo "$TARGET_REPO" \
210+
--base "$TARGET_BRANCH" --head "$SYNC_BRANCH" \
211+
--title "$title" --body "$body" --label "$PR_LABEL"
212+
fi

.github/workflows/docs_verify.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: 'Verify Docs'
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
paths:
8+
- 'docs/clickhouse-docs/**'
9+
- '.github/workflows/docs_verify.yml'
10+
push:
11+
branches:
12+
- main
13+
paths:
14+
- 'docs/clickhouse-docs/**'
15+
- '.github/workflows/docs_verify.yml'
16+
17+
jobs:
18+
verify:
19+
runs-on: ubuntu-latest
20+
name: Mintlify checks
21+
# Run inside the docs image so every dependency (git, python3, mint, node)
22+
# is present -- nothing is installed, and this repo vendors no scripts.
23+
container:
24+
image: clickhouse/docs-builder
25+
steps:
26+
- name: Checkout docs source
27+
uses: actions/checkout@v5
28+
29+
# Fetch the check driver from ClickHouse/ClickHouse rather than vendoring
30+
# it. The driver shallow/sparse-clones the aggregator docs plus its CI
31+
# scripts (ci/jobs/scripts/docs), so any check scripts come along for free.
32+
- name: Fetch the docs check driver
33+
run: |
34+
curl -fsSL -o /tmp/mintlify_docs_check.py \
35+
https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/ci/jobs/scripts/docs/mintlify_docs_check.py
36+
37+
# The driver clones the aggregator, replaces the Java client folder with this
38+
# repo's canonical docs (wiping it first), and
39+
# runs the checks from the docs root. --scoped swaps the full site-wide
40+
# `mint validate` (~13 min) for a scoped validate of just the replaced
41+
# slice (docs.json schema, navigation, MDX parse, snippet imports);
42+
# link integrity is still checked site-wide by the lychee check.
43+
# It exits non-zero if any check fails, so a green step means all passed.
44+
- name: Run Mintlify docs checks
45+
run: |
46+
python3 /tmp/mintlify_docs_check.py \
47+
--repo https://github.com/ClickHouse/ClickHouse.git \
48+
--ref master \
49+
--replace docs/clickhouse-docs:integrations/language-clients/java \
50+
--scoped

0 commit comments

Comments
 (0)