Merge upstream actions/runner commits into sirredbeard/runner (keep one commit ahead) #78
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Merge upstream actions/runner commits into sirredbeard/runner (keep one commit ahead) | |
| on: | |
| workflow_dispatch: | |
| schedule: | |
| # Run daily at 2 AM UTC to check for upstream updates | |
| - cron: '0 2 * * *' | |
| permissions: | |
| contents: write # required to push commits | |
| issues: write # required to create an issue when automatic merge fails | |
| concurrency: | |
| group: merge-upstream-${{ github.repository }} | |
| cancel-in-progress: false | |
| env: | |
| UPSTREAM_REPO: actions/runner | |
| UPSTREAM_BRANCH: main | |
| WORK_BRANCH: main | |
| COMMIT_GREP: Add Windows Containers Support | |
| jobs: | |
| merge: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository (full history) | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: true | |
| - name: Configure git user | |
| run: | | |
| git config user.name "sirredbeard" | |
| git config user.email "hbarnes@herodevs.com" | |
| - name: Add upstream remote and fetch | |
| run: | | |
| git remote add upstream https://github.com/${{ env.UPSTREAM_REPO }}.git || true | |
| git fetch upstream ${{ env.UPSTREAM_BRANCH }} --tags || true | |
| - name: Check for new upstream release tag | |
| id: check_upstream | |
| run: | | |
| # Get the latest upstream release tag | |
| latest_upstream_tag=$(git tag -l --sort=-version:refname "v*" | grep -E "^v[0-9]+\.[0-9]+\.[0-9]+$" | head -n 1 || true) | |
| # Get our current version - releaseVersion may contain the upstream placeholder text | |
| # if it was never set after a rebase; fall back to src/runnerversion in that case | |
| current_version=$(cat releaseVersion) | |
| if ! echo "$current_version" | grep -qE "^[0-9]+\.[0-9]+\.[0-9]+$"; then | |
| echo "releaseVersion contains placeholder, falling back to src/runnerversion" | |
| current_version=$(cat src/runnerversion) | |
| fi | |
| current_tag="v${current_version}" | |
| echo "Current version: $current_tag" | |
| echo "Latest upstream tag: $latest_upstream_tag" | |
| if [ -z "$latest_upstream_tag" ]; then | |
| echo "Could not find any upstream release tags matching pattern v[0-9]+.[0-9]+.[0-9]+" | |
| echo "needs_update=false" >> $GITHUB_OUTPUT | |
| elif [ "$current_tag" = "$latest_upstream_tag" ]; then | |
| echo "Already up to date with upstream release $latest_upstream_tag" | |
| echo "needs_update=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "New upstream release found: $latest_upstream_tag" | |
| echo "needs_update=true" >> $GITHUB_OUTPUT | |
| echo "new_tag=$latest_upstream_tag" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Save current HEAD (original fork state) | |
| id: save | |
| run: echo "old_head=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT | |
| - name: Find the 'Add Windows Containers Support' commit in history | |
| if: steps.check_upstream.outputs.needs_update == 'true' | |
| id: find | |
| run: | | |
| # Find commit hash by commit message (first match) | |
| commit=$(git log --pretty=format:%H --grep="${{ env.COMMIT_GREP }}" -n 1 || true) | |
| if [ -z "$commit" ]; then | |
| echo "No commit found matching '${{ env.COMMIT_GREP }}' in local history." | |
| # Fail early so we can create an issue in the next step | |
| echo "commit_found=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "commit=$commit" >> $GITHUB_OUTPUT | |
| echo "commit_found=true" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Abort if special commit not found (create issue) | |
| if: steps.check_upstream.outputs.needs_update == 'true' && steps.find.outputs.commit_found == 'false' | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: "Automated merge aborted: patch commit not found", | |
| body: `The workflow could not find a commit with message matching "${{ env.COMMIT_GREP }}" in the repository history. Please verify the commit message and rerun the workflow manually.` | |
| }) | |
| # stop the job after issue is created | |
| - name: Reset to upstream/main and cherry-pick patch | |
| if: steps.check_upstream.outputs.needs_update == 'true' && steps.find.outputs.commit_found == 'true' | |
| id: merge | |
| shell: bash | |
| run: | | |
| set -uo pipefail | |
| commit="${{ steps.find.outputs.commit }}" | |
| old_head="${{ steps.save.outputs.old_head }}" | |
| new_tag="${{ steps.check_upstream.outputs.new_tag }}" | |
| # Files where conflicts are expected and can be auto-resolved | |
| # These are always overwritten by our fork and should never block a merge | |
| AUTO_RESOLVE_OURS=(README.md images/Dockerfile AGENTS.md .github/workflows/release.yml .github/workflows/build.yml .github/workflows/docker-publish.yml releaseNote.md) | |
| # Ensure we're on the working branch | |
| git checkout ${{ env.WORK_BRANCH }} || exit 1 | |
| # Reset to upstream/main (NOT the release tag) so we stay exactly one commit | |
| # above upstream/main. The releaseVersion is updated via --amend below. | |
| git fetch upstream ${{ env.UPSTREAM_BRANCH }} --tags --force || true | |
| git reset --hard upstream/${{ env.UPSTREAM_BRANCH }} || exit 1 | |
| # Try to cherry-pick the special commit onto the new base | |
| if git cherry-pick "$commit" 2>/dev/null; then | |
| echo "Cherry-pick applied cleanly" | |
| cherry_pick_ok=true | |
| else | |
| echo "Cherry-pick had conflicts, attempting auto-resolution..." | |
| # Get list of conflicted files | |
| conflicted_files=$(git diff --name-only --diff-filter=U) | |
| echo "Conflicted files:" | |
| echo "$conflicted_files" | |
| # Separate auto-resolvable conflicts from real conflicts | |
| has_real_conflicts=false | |
| for f in $conflicted_files; do | |
| is_auto=false | |
| # Check if this file is in our auto-resolve list | |
| for auto_file in "${AUTO_RESOLVE_OURS[@]}"; do | |
| if [ "$f" = "$auto_file" ]; then | |
| is_auto=true | |
| break | |
| fi | |
| done | |
| if [ "$f" = "releaseVersion" ]; then | |
| # releaseVersion is always overwritten after cherry-pick via --amend; take upstream's for now | |
| echo "Auto-resolving releaseVersion (will be squash-amended after cherry-pick)" | |
| git checkout --ours "$f" | |
| git add "$f" | |
| elif [ "$is_auto" = "true" ]; then | |
| # For fork-owned files, always take our cherry-picked version | |
| echo "Auto-resolving $f (taking ours from patch)" | |
| git checkout --theirs "$f" | |
| git add "$f" | |
| else | |
| echo "REAL CONFLICT in $f — cannot auto-resolve" | |
| has_real_conflicts=true | |
| fi | |
| done | |
| if [ "$has_real_conflicts" = "true" ]; then | |
| echo "Unresolvable conflicts remain, aborting cherry-pick" | |
| git cherry-pick --abort 2>/dev/null || true | |
| short=${old_head:0:7} | |
| manual_branch="manual-merge-${short}" | |
| git branch "$manual_branch" "$old_head" 2>/dev/null || true | |
| git push origin "$manual_branch" 2>/dev/null || true | |
| echo "result=failed" >> $GITHUB_OUTPUT | |
| echo "manual_branch=$manual_branch" >> $GITHUB_OUTPUT | |
| cherry_pick_ok=false | |
| else | |
| echo "All conflicts auto-resolved, continuing cherry-pick" | |
| git cherry-pick --continue --no-edit | |
| cherry_pick_ok=true | |
| fi | |
| fi | |
| if [ "$cherry_pick_ok" = "true" ]; then | |
| echo "result=success" >> $GITHUB_OUTPUT | |
| # Update releaseVersion to match the new upstream tag (strip 'v' prefix). | |
| # This MUST be squashed into the single "Add Windows Containers Support" commit | |
| # via --amend so the repo stays exactly one commit ahead of upstream. | |
| # Never use a plain `git commit` here — two commits above upstream is wrong. | |
| upstream_version="${new_tag#v}" | |
| echo "$upstream_version" > releaseVersion | |
| git add releaseVersion | |
| git commit --amend --no-edit | |
| # Push the updated branch (force-with-lease to be safer) | |
| # This will trigger release.yml via the releaseVersion push trigger | |
| git push origin ${{ env.WORK_BRANCH }} --force-with-lease | |
| fi | |
| - name: Create issue on failure with manual-merge branch info | |
| if: steps.check_upstream.outputs.needs_update == 'true' && steps.merge.outputs.result == 'failed' | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const branch = '${{ steps.merge.outputs.manual_branch }}'; | |
| const commit = '${{ steps.find.outputs.commit }}'; | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: `Automated merge failed — manual resolution required (${branch})`, | |
| body: `The workflow attempted to rebase the fork onto upstream release tag and cherry-pick the patch commit \`${commit}\` ("${{ env.COMMIT_GREP }}"). The cherry-pick failed (likely due to merge conflicts). A branch \`${branch}\` pointing at the original fork state has been pushed for manual resolution.\n\nPlease check the branch, resolve conflicts, and merge as appropriate.` | |
| }) | |
| - name: Success notification | |
| if: steps.check_upstream.outputs.needs_update == 'true' && steps.merge.outputs.result == 'success' | |
| run: | | |
| echo "Upstream release ${{ steps.check_upstream.outputs.new_tag }} merged and patch commit reapplied successfully." | |
| echo "Release workflow will now be triggered by the releaseVersion update." | |
| - name: Already up to date | |
| if: steps.check_upstream.outputs.needs_update == 'false' | |
| run: echo "Fork is already up to date with latest upstream release. No action needed." |