Skip to content
Merged
Changes from all commits
Commits
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
127 changes: 127 additions & 0 deletions .github/workflows/check-example-sync-conflict.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
name: Check for example tutorial sync conflicts

# When a PR touches sdk/next/tutorials/example/, check if any open docs-sync
# PRs on cosmos/example modify the same files. If so, post or update a warning
# comment on this PR.

on:
pull_request:
paths:
- "sdk/next/tutorials/example/**"

jobs:
check-conflict:
name: Check for open sync PR conflict
runs-on: ubuntu-latest
permissions:
pull-requests: write

steps:
- name: Check for conflicting sync PR on cosmos/example
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Cross-repo access using GITHUB_TOKEN may fail if cosmos/example is private

GH_TOKEN is set to secrets.GITHUB_TOKEN, which is scoped to cosmos/docs. The docs-sync-to-example.yml workflow uses a separate fine-grained PAT for all operations against cosmos/example. If that repository is private, the gh pr list --repo cosmos/example and gh pr view --repo cosmos/example calls will fail with a 404/403, causing the entire job to fail and potentially surfacing as a broken required check on the PR.

If cosmos/example is public this is fine for read-only operations; but for consistency with the existing sync workflow and to future-proof against a visibility change, consider using the same cross-repo PAT (already available as a repo secret) for the cosmos/example queries.

EXAMPLE_TOKEN: ${{ secrets.EXAMPLE_REPO_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
# Get files changed in this PR that are in the tutorials folder
THIS_PR_FILES=$(gh pr view "$PR_NUMBER" \
--repo cosmos/docs \
--json files \
--jq '[.files[].path | select(startswith("sdk/next/tutorials/example/"))] | sort')

echo "Files changed in this PR: $THIS_PR_FILES"

# Get all open docs-sync PRs on cosmos/example (use cross-repo PAT)
SYNC_PRS=$(GH_TOKEN="$EXAMPLE_TOKEN" gh pr list \
--repo cosmos/example \
--label "docs-sync" \
--state open \
--json number,url)

echo "Open sync PRs: $SYNC_PRS"

if [ "$SYNC_PRS" = "[]" ] || [ -z "$SYNC_PRS" ]; then
echo "No open sync PRs on cosmos/example, all clear."
exit 0
Comment on lines +40 to +45
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Only the first open sync PR is checked

--jq '.[0]' selects only the first docs-sync PR on cosmos/example. If multiple sync PRs happen to be open simultaneously (e.g., one opened by the bot and another manually), conflicts with the second PR will go undetected.

Consider iterating over all open sync PRs:

SYNC_PRS=$(gh pr list \
  --repo cosmos/example \
  --label "docs-sync" \
  --state open \
  --json number,url,headRefName)

Then loop over each entry and union the file sets before computing the overlap.

fi

# Collect all files from all open sync PRs, mapped to docs site paths
ALL_SYNC_FILES="[]"
SYNC_PR_URLS=""

while IFS= read -r pr; do
SYNC_PR_NUMBER=$(echo "$pr" | jq -r '.number')
SYNC_PR_URL=$(echo "$pr" | jq -r '.url')
SYNC_PR_URLS="$SYNC_PR_URLS $SYNC_PR_URL"

SYNC_FILES=$(GH_TOKEN="$EXAMPLE_TOKEN" gh pr view "$SYNC_PR_NUMBER" \
--repo cosmos/example \
--json files \
--jq '[.files[].path
| select(startswith("docs/"))
| sub("^docs/([0-9]+-)?"; "sdk/next/tutorials/example/")
| sub("\\.md$"; ".mdx")
] | sort')

ALL_SYNC_FILES=$(jq -n \
--argjson a "$ALL_SYNC_FILES" \
--argjson b "$SYNC_FILES" \
'$a + $b | unique | sort')
done < <(echo "$SYNC_PRS" | jq -c '.[]')

echo "All sync PR files (mapped): $ALL_SYNC_FILES"

# Find overlapping files
OVERLAP=$(jq -n \
--argjson a "$THIS_PR_FILES" \
--argjson b "$ALL_SYNC_FILES" \
'[$a[], $b[]] | group_by(.) | map(select(length > 1)) | map(.[0])')

echo "Overlapping files: $OVERLAP"

MARKER="<!-- docs-sync-conflict-check -->"

if [ "$OVERLAP" = "[]" ] || [ -z "$OVERLAP" ]; then
echo "No overlapping files, all clear."
# If a previous warning comment exists, update it to say all clear
EXISTING_COMMENT=$(gh api "repos/cosmos/docs/issues/$PR_NUMBER/comments" \
--jq ".[] | select(.body | contains(\"$MARKER\")) | .id" | head -1)
if [ -n "$EXISTING_COMMENT" ]; then
gh api "repos/cosmos/docs/issues/comments/$EXISTING_COMMENT" \
-X PATCH \
-f body="$MARKER
✅ **Sync conflict resolved** — no overlapping files with open sync PRs on \`cosmos/example\`."
Comment on lines +81 to +93
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Duplicate comments on every push

The workflow triggers on every synchronize event (every new commit pushed to the PR). Since there's no guard against posting duplicate comments, a PR author who pushes multiple commits while the conflict exists will receive a new warning comment each time. This can become very noisy.

A simple fix is to check whether a matching comment already exists before posting:

# Check if a conflict comment already exists
EXISTING_COMMENT=$(gh pr view "$PR_NUMBER" \
  --repo cosmos/docs \
  --json comments \
  --jq '[.comments[].body | select(startswith("⚠️ **Potential sync conflict detected**"))] | length')

if [ "$EXISTING_COMMENT" -gt 0 ]; then
  echo "Conflict comment already posted, skipping."
  exit 0
fi

Alternatively, use the --edit-last flag or a match-and-update-or-create pattern to keep only one comment alive.

fi
exit 0
fi

OVERLAP_LIST=$(echo "$OVERLAP" | jq -r '.[] | "- `\(.)`"')
SYNC_PR_LINKS=$(echo "$SYNC_PR_URLS" | tr ' ' '\n' | grep -v '^$' | sed 's/^/- /')

COMMENT_BODY=$(cat <<EOF
${MARKER}
⚠️ **Potential sync conflict detected**

This PR modifies example tutorial files that are also modified in open sync PR(s) on \`cosmos/example\`:
${SYNC_PR_LINKS}

**Overlapping files:**
${OVERLAP_LIST}

These files are kept in sync between the two repos. Merging this PR before the sync PR is resolved may cause conflicts. Please coordinate with the sync PR author or wait until it is merged first.
EOF
)

# Update existing comment or post a new one
EXISTING_COMMENT=$(gh api "repos/cosmos/docs/issues/$PR_NUMBER/comments" \
--jq ".[] | select(.body | contains(\"$MARKER\")) | .id" | head -1)

if [ -n "$EXISTING_COMMENT" ]; then
gh api "repos/cosmos/docs/issues/comments/$EXISTING_COMMENT" \
-X PATCH \
-f body="$COMMENT_BODY"
echo "Updated existing warning comment."
else
gh pr comment "$PR_NUMBER" --repo cosmos/docs --body "$COMMENT_BODY"
echo "Posted new warning comment."
fi