Skip to content
Merged
Show file tree
Hide file tree
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
10 changes: 10 additions & 0 deletions .github/fork-specific-files.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.github/dependabot.yml
.github/workflows/codacy.yml
.github/workflows/codeql.yml
.github/workflows/maven.yml
.github/workflows/rebase.yml
.github/workflows/rebase-upstream.yml
.github/workflows/sync-upstream.yml
.github/workflows/create-upstream-pr.yml
.github/fork-specific-files.txt
README.md
126 changes: 126 additions & 0 deletions .github/workflows/create-upstream-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
name: Create Upstream PR Branch

on:
workflow_dispatch:
inputs:
issue_number:
description: 'Issue number (used for branch name: upstream-pr/issue-{number})'
required: true
type: string
source_branch:
description: 'Source branch containing the commits to cherry-pick'
required: true
type: string
commit_shas:
description: 'Comma-separated list of commit SHAs to cherry-pick onto upstream/master'
required: true
type: string
pr_title:
description: 'Title for the upstream PR (informational only)'
required: false
type: string

jobs:
create-upstream-pr-branch:
name: Create Clean Upstream PR Branch
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Fetch upstream master
run: |
git remote add upstream https://github.com/eclipse-jdt/eclipse.jdt.ui.git
git fetch upstream master

- name: Create branch based on upstream/master
run: |
BRANCH_NAME="upstream-pr/issue-${{ inputs.issue_number }}"
git checkout -b "$BRANCH_NAME" upstream/master
echo "Created branch: $BRANCH_NAME"

- name: Cherry-pick specified commits
id: cherry_pick
run: |
BRANCH_NAME="upstream-pr/issue-${{ inputs.issue_number }}"
COMMIT_SHAS="${{ inputs.commit_shas }}"

# Convert comma-separated list to space-separated
SHAS=$(echo "$COMMIT_SHAS" | tr ',' ' ' | tr -s ' ')

echo "Cherry-picking commits: $SHAS"

SUCCESS=true
for SHA in $SHAS; do
SHA=$(echo "$SHA" | xargs) # trim whitespace
if [ -z "$SHA" ]; then
continue
fi
echo "Cherry-picking $SHA..."
if ! git cherry-pick "$SHA"; then
echo "Cherry-pick failed for $SHA"
git cherry-pick --abort 2>/dev/null || true
SUCCESS=false
break
fi
done

if [ "$SUCCESS" = "true" ]; then
echo "success=true" >> $GITHUB_OUTPUT
else
echo "success=false" >> $GITHUB_OUTPUT
fi

- name: Verify branch contents
if: steps.cherry_pick.outputs.success == 'true'
run: |
echo "=== Commits on this branch (not in upstream/master) ==="
git log upstream/master..HEAD --oneline
echo ""
echo "=== Files changed vs upstream/master ==="
git diff upstream/master..HEAD --stat

- name: Force-push branch to fork
if: steps.cherry_pick.outputs.success == 'true'
run: |
BRANCH_NAME="upstream-pr/issue-${{ inputs.issue_number }}"
git push --force origin "$BRANCH_NAME"
echo "✅ Branch '$BRANCH_NAME' pushed to fork"

- name: Output PR creation link
if: steps.cherry_pick.outputs.success == 'true'
run: |
BRANCH_NAME="upstream-pr/issue-${{ inputs.issue_number }}"
REPO_OWNER="${{ github.repository_owner }}"
REPO_NAME="${{ github.event.repository.name }}"
PR_LINK="https://github.com/eclipse-jdt/eclipse.jdt.ui/compare/master...${REPO_OWNER}:${REPO_NAME}:${BRANCH_NAME}"

echo ""
echo "=========================================="
echo "✅ Branch ready for upstream PR!"
echo "=========================================="
echo ""
echo "Branch: $BRANCH_NAME"
if [ -n "${{ inputs.pr_title }}" ]; then
echo "PR Title: ${{ inputs.pr_title }}"
fi
echo ""
echo "Create PR at:"
echo "$PR_LINK"
echo ""

- name: Fail if cherry-pick failed
if: steps.cherry_pick.outputs.success == 'false'
run: |
echo "❌ Cherry-pick failed. Check the logs above for details."
exit 1
30 changes: 11 additions & 19 deletions .github/workflows/rebase-upstream.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,6 @@ jobs:
core.setOutput('head_ref', pr.head.ref);
core.setOutput('head_repo', pr.head.repo.full_name);
core.setOutput('head_clone_url', pr.head.repo.clone_url);

// Get the number of commits in the PR
// Note: per_page is set to 250, which covers most PRs.
// For PRs with >250 commits, manual rebasing would be required.
const { data: commits } = await github.rest.pulls.listCommits({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
per_page: 250
});
core.setOutput('commit_count', commits.length);

- name: Checkout PR branch
uses: actions/checkout@v4
Expand All @@ -145,19 +134,22 @@ jobs:
git remote add upstream https://github.com/eclipse-jdt/eclipse.jdt.ui.git
git fetch upstream master

# Get the number of commits in the PR
PR_COMMIT_COUNT=${{ steps.pr.outputs.commit_count }}
echo "PR has $PR_COMMIT_COUNT commits"
# Find the actual fork point with upstream
MERGE_BASE=$(git merge-base HEAD upstream/master)
echo "Merge base with upstream: $MERGE_BASE"

# Count actual commits to rebase (between merge-base and HEAD)
ACTUAL_COMMITS=$(git rev-list --count $MERGE_BASE..HEAD)
echo "Actual commits to rebase: $ACTUAL_COMMITS"

# Validate commit count
if [ "$PR_COMMIT_COUNT" -eq 0 ]; then
echo "Error: PR has 0 commits"
if [ "$ACTUAL_COMMITS" -eq 0 ]; then
echo "Error: No commits to rebase"
echo "success=false" >> $GITHUB_OUTPUT
exit 0
fi

# Rebase only the PR commits onto upstream/master
if git rebase --onto upstream/master HEAD~${PR_COMMIT_COUNT}; then
# Rebase using merge-base (correct: only replays commits after the fork point)
if git rebase --onto upstream/master $MERGE_BASE; then
echo "success=true" >> $GITHUB_OUTPUT
else
echo "success=false" >> $GITHUB_OUTPUT
Expand Down
76 changes: 42 additions & 34 deletions .github/workflows/sync-upstream.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,28 @@ jobs:
# Create temporary directory for fork-specific files
mkdir -p /tmp/fork-specific

# Identify fork-specific workflow files (files that don't exist in upstream)
git fetch upstream master

# Get list of workflow files in fork
FORK_WORKFLOWS=$(git ls-tree -r HEAD --name-only .github/workflows/ 2>/dev/null || echo "")
# Read the list of fork-specific files from the manifest
MANIFEST=".github/fork-specific-files.txt"
if [ ! -f "$MANIFEST" ]; then
echo "Warning: $MANIFEST not found, no files to backup"
echo "has_fork_files=false" >> $GITHUB_OUTPUT
exit 0
fi

# For each workflow file in fork, check if it exists in upstream
for file in $FORK_WORKFLOWS; do
if ! git cat-file -e upstream/master:"$file" 2>/dev/null; then
echo "Fork-specific file: $file"
# Create directory structure and copy file
HAS_FILES=false
while IFS= read -r file || [ -n "$file" ]; do
# Skip empty lines and comments
[ -z "$file" ] && continue
case "$file" in \#*) continue ;; esac
if [ -f "$file" ]; then
echo "Backing up fork-specific file: $file"
mkdir -p "/tmp/fork-specific/$(dirname "$file")"
cp "$file" "/tmp/fork-specific/$file"
HAS_FILES=true
fi
done
done < "$MANIFEST"

# Check if any fork-specific files were found
if [ -d "/tmp/fork-specific/.github" ]; then
if [ "$HAS_FILES" = "true" ]; then
echo "has_fork_files=true" >> $GITHUB_OUTPUT
echo "Found fork-specific files:"
find /tmp/fork-specific -type f
Expand All @@ -90,13 +94,13 @@ jobs:
- name: Restore fork-specific files
if: steps.check_sync.outputs.needs_sync == 'true' && steps.backup.outputs.has_fork_files == 'true'
run: |
# Copy fork-specific files back
# Restore all backed-up fork-specific files
if [ -d "/tmp/fork-specific" ]; then
cp -r /tmp/fork-specific/.github ./ 2>/dev/null || true
cp -r /tmp/fork-specific/. ./
fi

# Add all fork-specific changes
git add .github/
# Stage all restored files
git add -A

- name: Commit fork-specific changes
if: steps.check_sync.outputs.needs_sync == 'true' && steps.backup.outputs.has_fork_files == 'true'
Expand Down Expand Up @@ -214,24 +218,28 @@ jobs:
# Create temporary directory for fork-specific files
mkdir -p /tmp/fork-specific

# Identify fork-specific workflow files (files that don't exist in upstream)
git fetch upstream master

# Get list of workflow files in fork
FORK_WORKFLOWS=$(git ls-tree -r HEAD --name-only .github/workflows/ 2>/dev/null || echo "")
# Read the list of fork-specific files from the manifest
MANIFEST=".github/fork-specific-files.txt"
if [ ! -f "$MANIFEST" ]; then
echo "Warning: $MANIFEST not found, no files to backup"
echo "has_fork_files=false" >> $GITHUB_OUTPUT
exit 0
fi

# For each workflow file in fork, check if it exists in upstream
for file in $FORK_WORKFLOWS; do
if ! git cat-file -e upstream/master:"$file" 2>/dev/null; then
echo "Fork-specific file: $file"
# Create directory structure and copy file
HAS_FILES=false
while IFS= read -r file || [ -n "$file" ]; do
# Skip empty lines and comments
[ -z "$file" ] && continue
case "$file" in \#*) continue ;; esac
if [ -f "$file" ]; then
echo "Backing up fork-specific file: $file"
mkdir -p "/tmp/fork-specific/$(dirname "$file")"
cp "$file" "/tmp/fork-specific/$file"
HAS_FILES=true
fi
done
done < "$MANIFEST"

# Check if any fork-specific files were found
if [ -d "/tmp/fork-specific/.github" ]; then
if [ "$HAS_FILES" = "true" ]; then
echo "has_fork_files=true" >> $GITHUB_OUTPUT
echo "Found fork-specific files:"
find /tmp/fork-specific -type f
Expand All @@ -251,13 +259,13 @@ jobs:
- name: Restore fork-specific files
if: steps.check_sync.outputs.needs_sync == 'true' && steps.backup.outputs.has_fork_files == 'true'
run: |
# Copy fork-specific files back
# Restore all backed-up fork-specific files
if [ -d "/tmp/fork-specific" ]; then
cp -r /tmp/fork-specific/.github ./ 2>/dev/null || true
cp -r /tmp/fork-specific/. ./
fi

# Add all fork-specific changes
git add .github/
# Stage all restored files
git add -A

- name: Commit fork-specific changes
if: steps.check_sync.outputs.needs_sync == 'true' && steps.backup.outputs.has_fork_files == 'true'
Expand Down