From bb736825c1d25384a58401e03d975b616e2b25e7 Mon Sep 17 00:00:00 2001 From: DarkWindman Date: Mon, 9 Mar 2026 18:36:11 +0200 Subject: [PATCH] sync-upstream: Add automatic GitHub Actions sync script --- .github/workflows/sync.yml | 98 ++++++++++++++++++++++++++++++++++++++ contrib/sync-upstream.sh | 75 +++++++++-------------------- 2 files changed, 121 insertions(+), 52 deletions(-) create mode 100644 .github/workflows/sync.yml diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 00000000..ea16d068 --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,98 @@ +name: Upstream Sync + +on: + schedule: + - cron: '0 0 1 * *' + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + sync-upstream: + runs-on: ubuntu-latest + env: + UPSTREAM: "https://github.com/bitcoin-core/secp256k1.git" + UPSTREAM_BRANCH: "master" + ORIGIN_BRANCH: "master" + MIN_UPSTREAM_MERGES: 1 + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - 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 & generate branch name & check for new commits + id: check_commits + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh repo set-default ${{ github.repository }} # Set the default repo to the origin repository + git remote add upstream ${{ env.UPSTREAM }} + git fetch upstream + + MERGES=$(git rev-list --count --merges HEAD..upstream/${{ env.UPSTREAM_BRANCH }}) + echo "Found $MERGES new merge commits in upstream." + + if [ "$MERGES" -lt ${{ env.MIN_UPSTREAM_MERGES }} ]; then + echo "Exiting." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + # Generate a sync branch name "sync-UPSTREAM_HEAD", where UPSTREAM_HEAD is a commit ID. + UPSTREAM_HEAD=$(git rev-parse --short upstream/${{ env.UPSTREAM_BRANCH }}) + SYNC_BRANCH="sync-$UPSTREAM_HEAD" + echo "Sync branch name: $SYNC_BRANCH" + echo "SYNC_BRANCH=$SYNC_BRANCH" >> "$GITHUB_ENV" + + # Check if the sync branch already exists in the origin repository + if git ls-remote --heads origin "$SYNC_BRANCH" | grep -q "$SYNC_BRANCH"; then + echo "Branch $SYNC_BRANCH already exists. Skipping the sync." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Generate PR metadata + id: branch_pr_metadata + if: steps.check_commits.outputs.skip == 'false' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Call the sync script to generate PR title and body + ./contrib/sync-upstream.sh -b ${{ env.ORIGIN_BRANCH }} "$SYNC_BRANCH" + + - name: Push a sync branch + id: push_branch + if: steps.check_commits.outputs.skip == 'false' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "Creating sync branch: $SYNC_BRANCH" + git checkout upstream/${{ env.UPSTREAM_BRANCH }} + git checkout -b "$SYNC_BRANCH" + git push -u origin "$SYNC_BRANCH" + + - name: Create pull request + id: create_pr + if: steps.check_commits.outputs.skip == 'false' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Execute the generated PR creation script + ./contrib/gh-pr-create.sh + + - name: Cleanup sync branch on failure + if: steps.push_branch.outcome == 'success' && steps.create_pr.outcome == 'failure' + run: | + echo "PR creation failed but branch was pushed. Deleting: $SYNC_BRANCH" + git push origin --delete "$SYNC_BRANCH" \ No newline at end of file diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index 1cb285bb..b3274040 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -6,53 +6,35 @@ help() { echo "Sync merge commits from bitcoin-core/secp256k1 into secp256k1-zkp." echo echo "Usage:" - echo " $0 [-b ] [end]" - echo " Merges every merge commit present in upstream/master and missing in " - echo " (default: master). If the optional [end] commit is provided, only merges" - echo " up to and including [end]." + echo " $0 [-b ] " + echo " Find every merge commit present in upstream/master and missing in (default: master)." echo - echo "This tool creates a temporary branch and attempts to merge the upstream commits." - echo "If there are merge conflicts, resolve them and run tests, then use the generated" - echo "script contrib/gh-pr-create.sh to create the PR (requires the gh tool)." + echo "This tool prepares the title and body for a sync PR" + echo "and generates a helper script contrib/gh-pr-create.sh." echo echo "Setup:" echo " Requires a remote named 'upstream' pointing to bitcoin-core/secp256k1." - echo " The script will fetch it automatically, and offer to create it if missing." - echo " To add manually: git remote add upstream git@github.com:bitcoin-core/secp256k1.git" echo echo "Listing upstream merge commits:" echo " To list merge commits in upstream/master that are missing from (oldest first):" echo " git log --oneline --topo-order --reverse --merges \$(git merge-base upstream/master )..upstream/master" - echo " These are candidates for [end]." exit 1 } REMOTE=upstream REMOTE_BRANCH="$REMOTE/master" LOCAL_BRANCH="master" -# Makes sure you have a remote "upstream" that is up-to-date -setup() { - ret=0 - git fetch "$REMOTE" &> /dev/null || ret="$?" - if [ ${ret} == 0 ]; then - return - fi - echo "Adding remote \"$REMOTE\" with URL git@github.com:bitcoin-core/secp256k1.git. Continue with y" - read -r yn - case $yn in - [Yy]* ) ;; - * ) exit 1;; - esac - git remote add "$REMOTE" git@github.com:bitcoin-core/secp256k1.git &> /dev/null - git fetch "$REMOTE" &> /dev/null -} + +if ! git remote get-url "$REMOTE" &> /dev/null; then +echo "Error: Remote '$REMOTE' not found." +echo "Add it with: git remote add upstream git@github.com:bitcoin-core/secp256k1.git" +echo "Then run: git fetch upstream" +exit 1 +fi range() { RANGESTART_COMMIT=$(git merge-base "$REMOTE_BRANCH" "$LOCAL_BRANCH") RANGEEND_COMMIT=$(git rev-parse "$REMOTE_BRANCH") - if [ "$#" = 1 ]; then - RANGEEND_COMMIT=$1 - fi COMMITS=$(git --no-pager log --pretty=format:%H --topo-order --reverse --merges "$RANGESTART_COMMIT".."$RANGEEND_COMMIT") } @@ -74,12 +56,19 @@ done # Shift off the processed options shift $((OPTIND -1)) +if [ "$#" -lt 1 ]; then + echo "Error: argument is required." >&2 + echo + help + exit 1 +fi -setup -range "$@" +# Extract the PR branch argument +PR_BRANCH=$1 + +range TITLE="Upstream PRs" -REPRODUCE_COMMAND="$0 -b $LOCAL_BRANCH $RANGEEND_COMMIT" BODY="" for COMMIT in $COMMITS do @@ -93,8 +82,6 @@ TITLE=${TITLE%?} BODY+=$(cat <\` to show the conflict resolution in the merge commit. * Use \`git read-tree --reset -u \` to replay these resolutions during the conflict resolution stage when recreating the PR branch locally. @@ -102,22 +89,11 @@ Tips: EOF ) -echo "Merging $TITLE. Continue with y" -read -r yn -case $yn in - [Yy]* ) ;; - * ) exit 1;; -esac - echo "-----------------------------------" echo "$TITLE" echo "-----------------------------------" echo "$BODY" echo "-----------------------------------" -# Create branch from PR commit and create PR -git checkout "$LOCAL_BRANCH" -git pull --autostash -git checkout -b temp-merge-"$PRNUM" # Escape single quote # ' -> '\'' @@ -132,12 +108,7 @@ BASEDIR=$(dirname "$0") FNAME="$BASEDIR/gh-pr-create.sh" cat < "$FNAME" #!/bin/sh -gh pr create -t '$TITLE' -b '$BODY' --web -# Remove temporary branch -git checkout "$LOCAL_BRANCH" -git branch -D temp-merge-"$PRNUM" +gh pr create -t '$TITLE' -b '$BODY' --base '$LOCAL_BRANCH' --head '$PR_BRANCH' EOT chmod +x "$FNAME" -echo Run "$FNAME" after solving the merge conflicts - -git merge --no-edit -m "Merge upstream '${LAST_COMMIT:0:7}' into temp-merge-$PRNUM" "$LAST_COMMIT" +echo "Generated $FNAME for creating a pull request with the above title and body." \ No newline at end of file