Skip to content

Commit 488f94a

Browse files
committed
chore: Adds reverse sync workflow
1 parent 072626e commit 488f94a

File tree

1 file changed

+354
-0
lines changed

1 file changed

+354
-0
lines changed
Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
name: Reverse Sync on Push
2+
3+
env:
4+
TARGET_REPO: alaudadevops/connectors-operator
5+
# Ignoring the files or folders in this prefix (uses comma to split)
6+
IGNORE_PATHS: .github/,README.md
7+
# will check these files change to create a new patch
8+
SYNCED_PATHS: "docs/ theme/ .yarn/ doom.config.yml yarn.lock tsconfig.json package.json sites.yaml"
9+
10+
on:
11+
push:
12+
branches:
13+
- main
14+
- release-*
15+
16+
# Limit token capabilities to what the job really needs
17+
permissions:
18+
contents: read # checkout / git diff
19+
pull-requests: write # create PR in target repo
20+
21+
# (Optional) Prevent multiple syncs of the same ref running in parallel
22+
concurrency:
23+
group: reverse-sync-${{ github.ref }}
24+
cancel-in-progress: true
25+
jobs:
26+
reverse-sync:
27+
runs-on: ubuntu-latest
28+
29+
steps:
30+
- name: Checkout devops-connectors-docs repository
31+
uses: actions/checkout@v4
32+
with:
33+
token: ${{ secrets.GH_TOKEN }}
34+
fetch-depth: 0
35+
36+
- name: Check if commit is from merged PR
37+
id: check_pr_commit
38+
run: |
39+
# Get the latest commit
40+
commit_sha="${{ github.sha }}"
41+
echo "commit_sha=$commit_sha" >> $GITHUB_OUTPUT
42+
43+
# Get commit message
44+
commit_message=$(git log -1 --pretty=format:"%s" $commit_sha)
45+
echo "commit_message=$commit_message" >> $GITHUB_OUTPUT
46+
47+
# Get commit author
48+
commit_author=$(git log -1 --pretty=format:"%an" $commit_sha)
49+
commit_author_email=$(git log -1 --pretty=format:"%ae" $commit_sha)
50+
echo "commit_author=$commit_author" >> $GITHUB_OUTPUT
51+
echo "commit_author_email=$commit_author_email" >> $GITHUB_OUTPUT
52+
53+
echo "=> Commit: $commit_sha"
54+
echo "=> Message: $commit_message"
55+
echo "=> Author: $commit_author <$commit_author_email>"
56+
57+
# Check if this is a merge commit from GitHub (squash merge creates a single commit)
58+
# Look for PR number in commit message (GitHub automatically adds this)
59+
if [[ "$commit_message" =~ \(#([0-9]+)\)$ ]]; then
60+
pr_number="${BASH_REMATCH[1]}"
61+
echo "pr_number=$pr_number" >> $GITHUB_OUTPUT
62+
echo "is_pr_commit=true" >> $GITHUB_OUTPUT
63+
echo "✅ Detected commit from PR #$pr_number"
64+
else
65+
echo "is_pr_commit=false" >> $GITHUB_OUTPUT
66+
echo "ℹ️ Not a PR commit - skipping reverse sync"
67+
fi
68+
69+
# Skip if the commit is from our sync bot
70+
if [[ "$commit_author_email" == "[email protected]" ]] && [[ "$commit_message" == *"[reverse-sync]"* ]]; then
71+
echo "skip_sync=true" >> $GITHUB_OUTPUT
72+
echo "🤖 Commit is from sync bot - skipping reverse sync"
73+
elif [[ "$commit_message" == *"Sync documentation"* ]] || [[ "$commit_message" == *"sync-docs"* ]]; then
74+
echo "skip_sync=true" >> $GITHUB_OUTPUT
75+
echo "🤖 Commit appears to be from sync process - skipping reverse sync"
76+
else
77+
echo "skip_sync=false" >> $GITHUB_OUTPUT
78+
echo "👥 Commit is from external contributor - proceeding with reverse sync"
79+
fi
80+
81+
- name: Get PR information
82+
if: steps.check_pr_commit.outputs.is_pr_commit == 'true' && steps.check_pr_commit.outputs.skip_sync == 'false'
83+
id: get_pr_info
84+
run: |
85+
pr_number="${{ steps.check_pr_commit.outputs.pr_number }}"
86+
87+
echo "👀 Looking for PR $pr_number ..."
88+
89+
90+
# Get PR information using GitHub API
91+
pr_info=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
92+
"https://api.github.com/repos/${{ github.repository }}/pulls/$pr_number")
93+
94+
95+
echo "👀 PR data $pr_info ..."
96+
97+
pr_title=$(echo "$pr_info" | jq -r '.title')
98+
pr_author=$(echo "$pr_info" | jq -r '.user.login')
99+
pr_url=$(echo "$pr_info" | jq -r '.html_url')
100+
pr_base_ref=$(echo "$pr_info" | jq -r '.base.ref')
101+
102+
echo "pr_title=$pr_title" >> $GITHUB_OUTPUT
103+
echo "pr_author=$pr_author" >> $GITHUB_OUTPUT
104+
echo "pr_url=$pr_url" >> $GITHUB_OUTPUT
105+
echo "pr_base_ref=$pr_base_ref" >> $GITHUB_OUTPUT
106+
107+
echo "=> PR #$pr_number: $pr_title"
108+
echo "=> Author: $pr_author"
109+
echo "=> URL: $pr_url"
110+
111+
- name: Get commit changes
112+
if: steps.check_pr_commit.outputs.is_pr_commit == 'true' && steps.check_pr_commit.outputs.skip_sync == 'false'
113+
id: get_changes
114+
run: |
115+
commit_sha="${{ steps.check_pr_commit.outputs.commit_sha }}"
116+
117+
# Get the parent commit (previous commit before this one)
118+
parent_commit=$(git rev-parse ${commit_sha}^)
119+
echo "parent_commit=$parent_commit" >> $GITHUB_OUTPUT
120+
121+
# Get list of changed files in the commit, excluding ignored paths
122+
ignore_pattern=$(echo "$IGNORE_PATHS" | sed 's/,/|/g' | sed 's|/$||g')
123+
echo "🙈 Ignored paths: $ignore_pattern"
124+
125+
git diff --name-only $parent_commit $commit_sha | grep -v -E "^($ignore_pattern)" > changed_files.txt || true
126+
127+
echo "📋 Changed files in commit:"
128+
cat changed_files.txt
129+
130+
# Check if any relevant files were changed
131+
if [ -s changed_files.txt ]; then
132+
echo "has_doc_changes=true" >> $GITHUB_OUTPUT
133+
echo "✅ Documentation changes detected"
134+
else
135+
echo "has_doc_changes=false" >> $GITHUB_OUTPUT
136+
echo "ℹ️ No documentation changes detected"
137+
fi
138+
139+
- name: Checkout target repository
140+
if: steps.check_pr_commit.outputs.is_pr_commit == 'true' && steps.check_pr_commit.outputs.skip_sync == 'false' && steps.get_changes.outputs.has_doc_changes == 'true'
141+
uses: actions/checkout@v4
142+
with:
143+
repository: ${{env.TARGET_REPO}}
144+
token: ${{ secrets.GH_TOKEN }}
145+
path: target-docs
146+
fetch-depth: 0
147+
ref: ${{ steps.get_pr_info.outputs.pr_base_ref }}
148+
149+
- name: Create reverse sync branch
150+
if: steps.check_pr_commit.outputs.is_pr_commit == 'true' && steps.check_pr_commit.outputs.skip_sync == 'false' && steps.get_changes.outputs.has_doc_changes == 'true'
151+
id: create_branch
152+
run: |
153+
cd target-docs
154+
155+
# Create a unique branch name
156+
pr_number="${{ steps.check_pr_commit.outputs.pr_number }}"
157+
branch_name="reverse-sync/pr-$pr_number-$(date +%s)"
158+
echo "branch_name=$branch_name" >> $GITHUB_OUTPUT
159+
160+
git checkout -b "$branch_name"
161+
162+
# Configure git
163+
git config user.name "Documentation Sync Bot"
164+
git config user.email "[email protected]"
165+
166+
echo "📝 Created branch: $branch_name"
167+
168+
- name: Apply changes from devops-connectors-docs
169+
if: steps.check_pr_commit.outputs.is_pr_commit == 'true' && steps.check_pr_commit.outputs.skip_sync == 'false' && steps.get_changes.outputs.has_doc_changes == 'true'
170+
run: |
171+
commit_sha="${{ steps.check_pr_commit.outputs.commit_sha }}"
172+
parent_commit="${{ steps.get_changes.outputs.parent_commit }}"
173+
174+
# Create a patch with only the synced paths
175+
echo "📑 Will only sync these paths: $SYNCED_PATHS"
176+
git format-patch $parent_commit..$commit_sha --stdout -- $SYNCED_PATHS > changes.patch
177+
178+
echo "📋 Generated Patch: "
179+
cat changes.patch
180+
181+
cd target-docs
182+
183+
# Apply the patch
184+
if [ -s ../changes.patch ]; then
185+
echo "📦 Applying changes from devops-connectors-docs..."
186+
git apply ../changes.patch || {
187+
echo "⚠️ Patch application failed, trying manual copy..."
188+
189+
# Fallback: manual copy of changed files
190+
while IFS= read -r file; do
191+
if [ -f "../$file" ]; then
192+
mkdir -p "$(dirname "$file")"
193+
cp "../$file" "$file"
194+
echo "✅ Copied: $file"
195+
fi
196+
done < ../changed_files.txt
197+
}
198+
else
199+
echo "⚠️ No patch generated, using manual copy..."
200+
201+
# Manual copy approach
202+
while IFS= read -r file; do
203+
if [ -f "../$file" ]; then
204+
mkdir -p "$(dirname "$file")"
205+
cp "../$file" "$file"
206+
echo "✅ Copied: $file"
207+
fi
208+
done < ../changed_files.txt
209+
fi
210+
211+
- name: Commit changes
212+
if: steps.check_pr_commit.outputs.is_pr_commit == 'true' && steps.check_pr_commit.outputs.skip_sync == 'false' && steps.get_changes.outputs.has_doc_changes == 'true'
213+
id: commit_changes
214+
run: |
215+
cd target-docs
216+
217+
echo "🔍 Checking for changes..."
218+
219+
git status
220+
221+
echo "=> Adding changes..."
222+
223+
git add .
224+
225+
echo "==> Checking if there are staged changes..."
226+
if git diff --staged --quiet; then
227+
echo "has_changes=false" >> $GITHUB_OUTPUT
228+
echo "ℹ️ No changes to commit"
229+
else
230+
echo "=> Changes detected..."
231+
echo "has_changes=true" >> $GITHUB_OUTPUT
232+
233+
pr_number="${{ steps.check_pr_commit.outputs.pr_number }}"
234+
pr_title="${{ steps.get_pr_info.outputs.pr_title }}"
235+
pr_author="${{ steps.get_pr_info.outputs.pr_author }}"
236+
pr_url="${{ steps.get_pr_info.outputs.pr_url }}"
237+
commit_sha="${{ steps.check_pr_commit.outputs.commit_sha }}"
238+
239+
echo "=> Creating commit message..."
240+
echo " - PR Number: $pr_number"
241+
echo " - PR Title: $pr_title"
242+
echo " - PR Author: $pr_author"
243+
echo " - PR URL: $pr_url"
244+
echo " - Commit SHA: $commit_sha"
245+
246+
# Create commit message with reverse sync marker
247+
248+
echo "[reverse-sync] Sync documentation changes from devops-connectors-docs PR #$pr_number" > commit_message.txt
249+
echo "" >> commit_message.txt
250+
echo "- Title: $pr_title" >> commit_message.txt
251+
echo "- Author: $pr_author" >> commit_message.txt
252+
echo "- URL: $pr_url" >> commit_message.txt
253+
echo "- Commit: $commit_sha" >> commit_message.txt
254+
255+
echo "=> Commit message:"
256+
cat commit_message.txt
257+
258+
echo "=> Committing changes..."
259+
260+
git commit -F commit_message.txt
261+
rm commit_message.txt
262+
263+
echo "✅ Changes committed successfully"
264+
fi
265+
266+
- name: Push branch and create PR
267+
if: steps.check_pr_commit.outputs.is_pr_commit == 'true' && steps.check_pr_commit.outputs.skip_sync == 'false' && steps.get_changes.outputs.has_doc_changes == 'true' && steps.commit_changes.outputs.has_changes == 'true'
268+
run: |
269+
cd target-docs
270+
branch_name="${{ steps.create_branch.outputs.branch_name }}"
271+
pr_base_ref="${{ steps.get_pr_info.outputs.pr_base_ref }}"
272+
pr_number="${{ steps.check_pr_commit.outputs.pr_number }}"
273+
pr_title="${{ steps.get_pr_info.outputs.pr_title }}"
274+
pr_author="${{ steps.get_pr_info.outputs.pr_author }}"
275+
pr_url="${{ steps.get_pr_info.outputs.pr_url }}"
276+
277+
# Push the branch
278+
git push origin "$branch_name"
279+
280+
# Create PR body with proper JSON escaping
281+
pr_body=$(cat << 'EOF'
282+
### 🔄 Reverse Sync from devops-connectors-docs
283+
284+
This PR incorporates documentation changes from external contributors to the devops-connectors-docs repository.
285+
286+
#### Original PR Details
287+
- **Repository**: alauda/devops-connectors-docs
288+
- **PR**: #%PR_NUMBER% - %PR_TITLE%
289+
- **Author**: @%PR_AUTHOR%
290+
- **URL**: %PR_URL%
291+
292+
#### Changes
293+
This PR includes changes to documentation files that were contributed by external contributors to the public devops-connectors-docs repository.
294+
295+
#### Important Notes
296+
- ⚠️ This PR contains the `[reverse-sync]` marker to prevent infinite sync loops
297+
- ✅ Once merged, this will NOT trigger a forward sync back to devops-connectors-docs
298+
- 🔍 Please review the changes to ensure they align with internal documentation standards
299+
300+
---
301+
*This PR was automatically created by the reverse sync workflow.*
302+
EOF)
303+
304+
# Replace placeholders in the PR body
305+
pr_body=$(echo "$pr_body" | sed "s/%PR_NUMBER%/$pr_number/g")
306+
pr_body=$(echo "$pr_body" | sed "s/%PR_TITLE%/$pr_title/g")
307+
pr_body=$(echo "$pr_body" | sed "s/%PR_AUTHOR%/$pr_author/g")
308+
pr_body=$(echo "$pr_body" | sed "s|%PR_URL%|$pr_url|g")
309+
310+
# Create JSON payload with proper escaping
311+
json_payload=$(jq -n \
312+
--arg title "[reverse-sync] Documentation changes from devops-connectors-docs PR #$pr_number" \
313+
--arg head "$branch_name" \
314+
--arg base "$pr_base_ref" \
315+
--arg body "$pr_body" \
316+
'{title: $title, head: $head, base: $base, body: $body}')
317+
318+
# Create the PR using GitHub API
319+
curl -X POST \
320+
-H "Authorization: token ${{ secrets.GH_TOKEN }}" \
321+
-H "Accept: application/vnd.github.v3+json" \
322+
-H "Content-Type: application/json" \
323+
https://api.github.com/repos/${{env.TARGET_REPO}}/pulls \
324+
-d "$json_payload"
325+
326+
- name: Create workflow summary
327+
run: |
328+
if [ "${{ steps.check_pr_commit.outputs.is_pr_commit }}" == "false" ]; then
329+
echo "## ℹ️ Not a PR Commit" >> $GITHUB_STEP_SUMMARY
330+
echo "" >> $GITHUB_STEP_SUMMARY
331+
echo "This commit was not created from a merged PR, so no reverse sync was performed." >> $GITHUB_STEP_SUMMARY
332+
elif [ "${{ steps.check_pr_commit.outputs.skip_sync }}" == "true" ]; then
333+
echo "## 🤖 Sync Bot Commit - Reverse Sync Skipped" >> $GITHUB_STEP_SUMMARY
334+
echo "" >> $GITHUB_STEP_SUMMARY
335+
echo "This commit was created by the sync bot, so reverse sync was skipped to prevent loops." >> $GITHUB_STEP_SUMMARY
336+
elif [ "${{ steps.get_changes.outputs.has_doc_changes }}" == "false" ]; then
337+
echo "## ℹ️ No Documentation Changes" >> $GITHUB_STEP_SUMMARY
338+
echo "" >> $GITHUB_STEP_SUMMARY
339+
echo "This commit didn't contain any documentation changes, so no reverse sync was needed." >> $GITHUB_STEP_SUMMARY
340+
elif [ "${{ steps.commit_changes.outputs.has_changes }}" == "false" ]; then
341+
echo "## ℹ️ No Changes to Sync" >> $GITHUB_STEP_SUMMARY
342+
echo "" >> $GITHUB_STEP_SUMMARY
343+
echo "The documentation changes were already present in target-docs." >> $GITHUB_STEP_SUMMARY
344+
else
345+
echo "## 🎉 Reverse Sync Completed" >> $GITHUB_STEP_SUMMARY
346+
echo "" >> $GITHUB_STEP_SUMMARY
347+
echo "Successfully created a PR in target-docs with the documentation changes." >> $GITHUB_STEP_SUMMARY
348+
echo "" >> $GITHUB_STEP_SUMMARY
349+
echo "### Details:" >> $GITHUB_STEP_SUMMARY
350+
echo "- **Source PR**: #${{ steps.check_pr_commit.outputs.pr_number }} by @${{ steps.get_pr_info.outputs.pr_author }}" >> $GITHUB_STEP_SUMMARY
351+
echo "- **Branch**: ${{ steps.create_branch.outputs.branch_name }}" >> $GITHUB_STEP_SUMMARY
352+
echo "- **Base ref**: ${{ steps.get_pr_info.outputs.pr_base_ref }}" >> $GITHUB_STEP_SUMMARY
353+
echo "- **Target repository**: ${{env.TARGET_REPO}}" >> $GITHUB_STEP_SUMMARY
354+
fi

0 commit comments

Comments
 (0)