Skip to content

Commit 578d17f

Browse files
Apply fork changes
Signed-off-by: Roman Nikitenko <rnikiten@redhat.com>
1 parent f219059 commit 578d17f

9 files changed

Lines changed: 830 additions & 120 deletions
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
#!/bin/bash
2+
# This file was generated using AI assistance (Cursor AI) and reviewed by the maintainers.
3+
#
4+
# Detect files under code/ changed in a PR whose modifications are NOT fully
5+
# protected by rebase rules. Two categories:
6+
# - "Missing rule" — no rebase rule exists for the file at all
7+
# - "Incomplete rule" — a rule exists but doesn't reproduce the current content
8+
#
9+
# For "incomplete" detection the script runs the actual rebase handler against
10+
# upstream content and diffs the result against the working tree, reusing the
11+
# test infrastructure from .claude/skills/test-rebase-rules/.
12+
#
13+
# Usage:
14+
# bash check-unprotected-changes.sh --pr-comment <base>..<head>
15+
#
16+
# Prerequisites (for incomplete-rule detection):
17+
# - upstream-code remote must be fetched for the current upstream version
18+
#
19+
# Exit codes:
20+
# 0 — All modifications are fully covered
21+
# 1 — Problems found
22+
# 2 — Error
23+
set -u
24+
25+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26+
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
27+
cd "$REPO_ROOT"
28+
29+
OUTPUT_MODE="text"
30+
COMMIT_RANGE=""
31+
for arg in "$@"; do
32+
case "$arg" in
33+
--pr-comment) OUTPUT_MODE="pr-comment" ;;
34+
*..*) COMMIT_RANGE="$arg" ;;
35+
esac
36+
done
37+
38+
if [ -z "$COMMIT_RANGE" ]; then
39+
REBASE_COMMIT=$(git log --format='%H' --grep="^Rebase against the upstream" -1)
40+
if [ -z "$REBASE_COMMIT" ]; then
41+
echo "ERROR: Could not find a rebase commit and no commit range provided." >&2
42+
exit 2
43+
fi
44+
COMMIT_RANGE="${REBASE_COMMIT}..HEAD"
45+
fi
46+
47+
UPSTREAM_VERSION=$(grep '^CURRENT_UPSTREAM_VERSION=' rebase.sh | head -1 | sed 's/.*="\(.*\)"/\1/')
48+
49+
HANDLER_SCRIPT="$REPO_ROOT/.claude/skills/test-rebase-rules/test-rebase-handler.sh"
50+
RUN_ALL_TESTS="$REPO_ROOT/.claude/skills/test-rebase-rules/run-all-tests.sh"
51+
52+
# --- Build the set of files covered by rebase rules ---
53+
COVERED_LIST=$(mktemp)
54+
trap 'rm -f "$COVERED_LIST"' EXIT
55+
56+
while IFS= read -r line; do
57+
if [[ "$line" =~ \[\[.*==.*\"([^\"]+)\".*\]\] ]]; then
58+
echo "${BASH_REMATCH[1]}"
59+
fi
60+
done < <(sed -n '/^resolve_conflicts()/,/^}/p' rebase.sh) >> "$COVERED_LIST"
61+
62+
find .rebase/replace -name '*.json' -type f 2>/dev/null | while IFS= read -r rule_file; do
63+
code_path="${rule_file#.rebase/replace/}"
64+
code_path="${code_path%.json}"
65+
if [[ "$code_path" != code/* ]]; then
66+
code_path="code/$code_path"
67+
fi
68+
echo "$code_path"
69+
done >> "$COVERED_LIST"
70+
71+
find .rebase/add -type f 2>/dev/null | while IFS= read -r f; do echo "${f#.rebase/add/}"; done >> "$COVERED_LIST"
72+
find .rebase/override -type f 2>/dev/null | while IFS= read -r f; do echo "${f#.rebase/override/}"; done >> "$COVERED_LIST"
73+
74+
sort -u -o "$COVERED_LIST" "$COVERED_LIST"
75+
76+
# --- Collect changed files and classify ---
77+
MISSING_RULE=()
78+
TO_TEST=()
79+
80+
while IFS= read -r file_path; do
81+
case "$file_path" in
82+
*/package-lock.json) continue ;;
83+
code/extensions/che-*) continue ;;
84+
*/che/*.ts|*/che/*.js) continue ;;
85+
esac
86+
87+
if grep -qxF "$file_path" "$COVERED_LIST"; then
88+
TO_TEST+=("$file_path")
89+
else
90+
MISSING_RULE+=("$file_path")
91+
fi
92+
done < <(git diff --name-only "$COMMIT_RANGE" -- code/)
93+
94+
# --- Test files that have rules: are rules complete? ---
95+
INCOMPLETE_RULE=()
96+
HAS_UPSTREAM=false
97+
if [ -n "$UPSTREAM_VERSION" ]; then
98+
git rev-parse "upstream-code/$UPSTREAM_VERSION" > /dev/null 2>&1 && HAS_UPSTREAM=true
99+
fi
100+
101+
if [ "$HAS_UPSTREAM" = true ] && [ ${#TO_TEST[@]} -gt 0 ] && [ -f "$RUN_ALL_TESTS" ]; then
102+
TEST_OUTPUT=$(bash "$RUN_ALL_TESTS" "${TO_TEST[@]}" 2>&1) || true
103+
104+
while IFS= read -r line; do
105+
if [[ "$line" =~ ^\|\ \`([^\`]+)\`\ \| ]]; then
106+
failed_file="${BASH_REMATCH[1]}"
107+
INCOMPLETE_RULE+=("$failed_file")
108+
fi
109+
done < <(echo "$TEST_OUTPUT" | sed -n '/^### Failures/,/^### /p' | grep '^| `')
110+
fi
111+
112+
# --- Output ---
113+
TOTAL_ISSUES=$(( ${#MISSING_RULE[@]} + ${#INCOMPLETE_RULE[@]} ))
114+
115+
if [ "$OUTPUT_MODE" = "pr-comment" ]; then
116+
if [ $TOTAL_ISSUES -eq 0 ]; then
117+
exit 0
118+
fi
119+
120+
echo "<!-- rebase-rules-check -->"
121+
echo "### Rebase Rules Check"
122+
echo ""
123+
echo "The following files were modified in this PR but their changes are **not fully protected** by rebase rules."
124+
echo "Without proper rules, these changes **will be lost** during the next upstream rebase."
125+
echo ""
126+
127+
if [ ${#MISSING_RULE[@]} -gt 0 ]; then
128+
echo "#### Missing rules (no rebase rule exists)"
129+
echo ""
130+
echo "| # | File |"
131+
echo "|---|------|"
132+
i=1
133+
for f in "${MISSING_RULE[@]}"; do
134+
echo "| $i | \`$f\` |"
135+
((i++))
136+
done
137+
echo ""
138+
fi
139+
140+
if [ ${#INCOMPLETE_RULE[@]} -gt 0 ]; then
141+
echo "#### Incomplete rules (rule exists but doesn't cover new changes)"
142+
echo ""
143+
echo "| # | File |"
144+
echo "|---|------|"
145+
i=1
146+
for f in "${INCOMPLETE_RULE[@]}"; do
147+
echo "| $i | \`$f\` |"
148+
((i++))
149+
done
150+
echo ""
151+
fi
152+
153+
echo "Comment \`/add-rebase-rules\` on this PR to add or update the rules automatically."
154+
exit 1
155+
fi
156+
157+
# Default text mode
158+
if [ $TOTAL_ISSUES -eq 0 ]; then
159+
echo "All modified files in code/ are fully covered by rebase rules."
160+
exit 0
161+
fi
162+
163+
echo "Found $TOTAL_ISSUES file(s) with rebase rule problems:"
164+
echo ""
165+
if [ ${#MISSING_RULE[@]} -gt 0 ]; then
166+
echo "Missing rules:"
167+
for f in "${MISSING_RULE[@]}"; do echo " - $f"; done
168+
echo ""
169+
fi
170+
if [ ${#INCOMPLETE_RULE[@]} -gt 0 ]; then
171+
echo "Incomplete rules:"
172+
for f in "${INCOMPLETE_RULE[@]}"; do echo " - $f"; done
173+
echo ""
174+
fi
175+
echo "Comment /add-rebase-rules on this PR to fix automatically."
176+
exit 1
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#
2+
# Copyright (c) 2026 Red Hat, Inc.
3+
# This program and the accompanying materials are made
4+
# available under the terms of the Eclipse Public License 2.0
5+
# which is available at https://www.eclipse.org/legal/epl-2.0/
6+
#
7+
# SPDX-License-Identifier: EPL-2.0
8+
#
9+
10+
#
11+
# This file was generated using AI assistance (Cursor AI)
12+
# and reviewed by the maintainers.
13+
#
14+
15+
name: Add Rebase Rules via dw-claude-runner
16+
17+
on:
18+
issue_comment:
19+
types: [created]
20+
21+
jobs:
22+
add-rebase-rules:
23+
if: >-
24+
github.event.issue.pull_request
25+
&& contains(github.event.comment.body, '/add-rebase-rules')
26+
runs-on: ubuntu-22.04
27+
permissions:
28+
pull-requests: write
29+
issues: write
30+
31+
steps:
32+
- name: Check author permission
33+
env:
34+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35+
run: |
36+
PERM=$(gh api "repos/${{ github.repository }}/collaborators/${{ github.event.comment.user.login }}/permission" \
37+
--jq '.permission')
38+
if [[ "$PERM" != "admin" && "$PERM" != "write" ]]; then
39+
echo "User ${{ github.event.comment.user.login }} does not have write access (permission: $PERM)"
40+
exit 1
41+
fi
42+
43+
- name: Add reaction to command comment
44+
env:
45+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46+
run: |
47+
gh api "repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \
48+
-f content='rocket' --silent
49+
50+
- name: Get PR details
51+
id: pr
52+
env:
53+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54+
run: |
55+
PR_JSON=$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.issue.number }}")
56+
HEAD_SHA=$(echo "$PR_JSON" | jq -r '.head.sha')
57+
HEAD_REF=$(echo "$PR_JSON" | jq -r '.head.ref')
58+
PR_URL=$(echo "$PR_JSON" | jq -r '.html_url')
59+
echo "head_sha=${HEAD_SHA}" >> "$GITHUB_OUTPUT"
60+
echo "head_ref=${HEAD_REF}" >> "$GITHUB_OUTPUT"
61+
echo "pr_url=${PR_URL}" >> "$GITHUB_OUTPUT"
62+
63+
- name: Find and update bot comment to in-progress
64+
id: bot-comment
65+
env:
66+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
67+
RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
68+
run: |
69+
COMMENT_ID=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments" \
70+
--paginate --jq '.[] | select(.body | contains("<!-- rebase-rules-check -->")) | .id' | head -1)
71+
72+
BODY="<!-- rebase-rules-check -->"$'\n'"### Missing Rebase Rules"$'\n\n'"Adding rebase rules — **in progress**... [View run](${RUN_URL})"
73+
74+
if [ -n "$COMMENT_ID" ]; then
75+
gh api "repos/${{ github.repository }}/issues/comments/${COMMENT_ID}" \
76+
-X PATCH -f body="$BODY"
77+
echo "comment_id=${COMMENT_ID}" >> "$GITHUB_OUTPUT"
78+
else
79+
COMMENT_URL=$(gh pr comment "${{ github.event.issue.number }}" \
80+
--repo "${{ github.repository }}" \
81+
--body "$BODY")
82+
NEW_ID=$(echo "$COMMENT_URL" | grep -oE '[0-9]+$')
83+
echo "comment_id=${NEW_ID}" >> "$GITHUB_OUTPUT"
84+
fi
85+
86+
- name: Checkout dw-claude-runner
87+
uses: actions/checkout@v4
88+
with:
89+
repository: RomanNikitenko/dw-claude-runner
90+
path: dw-claude-runner
91+
92+
- name: Install oc CLI
93+
uses: redhat-actions/openshift-tools-installer@v1
94+
with:
95+
oc: latest
96+
97+
- name: Login to OpenShift
98+
run: |
99+
echo "::add-mask::${{ secrets.OC_SERVER }}"
100+
echo "::add-mask::${{ secrets.OC_PROJECT }}"
101+
oc login --token=${{ secrets.OC_TOKEN }} \
102+
--server=${{ secrets.OC_SERVER }} > /dev/null
103+
104+
- name: Select OpenShift project
105+
run: oc project ${{ secrets.OC_PROJECT }} > /dev/null
106+
107+
- name: Run dw-claude-runner
108+
working-directory: dw-claude-runner
109+
env:
110+
PROJECT_URL: '"https://github.com/${{ github.repository }}.git"'
111+
TARGET_REPO: ${{ github.repository }}
112+
PROMPT: >-
113+
Checkout branch '${{ steps.pr.outputs.head_ref }}' first:
114+
git fetch origin && git checkout ${{ steps.pr.outputs.head_ref }}
115+
116+
Then run: add-rebase-rules ${{ steps.pr.outputs.head_sha }}
117+
118+
After committing, push the changes to the PR branch:
119+
git push origin ${{ steps.pr.outputs.head_ref }}
120+
TIMEOUT: "600"
121+
CLAUDE_TIMEOUT: "600"
122+
run: |
123+
set -o pipefail
124+
./run.sh -v 2>&1 | tee /tmp/runner-output.txt
125+
126+
- name: Update comment with result
127+
if: always() && steps.bot-comment.outputs.comment_id
128+
env:
129+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
130+
COMMENT_ID: ${{ steps.bot-comment.outputs.comment_id }}
131+
RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
132+
run: |
133+
if [ "${{ job.status }}" = "success" ]; then
134+
BODY="<!-- rebase-rules-check -->"$'\n'"### Missing Rebase Rules"$'\n\n'"Rebase rules have been **added successfully**. [View run](${RUN_URL})"$'\n\n'"Please review the new commit pushed to this PR."
135+
else
136+
BODY="<!-- rebase-rules-check -->"$'\n'"### Missing Rebase Rules"$'\n\n'"Failed to add rebase rules. [View run](${RUN_URL})"
137+
fi
138+
gh api "repos/${{ github.repository }}/issues/comments/${COMMENT_ID}" \
139+
-X PATCH -f body="$BODY"

0 commit comments

Comments
 (0)