-
Notifications
You must be signed in to change notification settings - Fork 206
212 lines (191 loc) · 8.7 KB
/
Copy pathdocs_sync.yml
File metadata and controls
212 lines (191 loc) · 8.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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/cpp
# This repo's docs file or folder (the source of truth).
SOURCE_DOCS_PATH: docs
# Stable branch on TARGET_REPO, force-pushed each run.
SYNC_BRANCH: robot/docs-sync-clickhouse-cpp
# 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 || '' }}
- 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
)
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