Skip to content

Sync Fork with Upstream Main #287

Sync Fork with Upstream Main

Sync Fork with Upstream Main #287

Workflow file for this run

name: Sync Fork with Upstream Main
on:
schedule:
# Check upstream main every 6 hours
- cron: "0 */6 * * *"
workflow_dispatch:
inputs:
force_build:
description: "Force build even if no upstream changes"
type: boolean
default: false
jobs:
check-upstream:
runs-on: ubuntu-latest
outputs:
has_upstream_changes: ${{ steps.check.outputs.has_upstream_changes }}
latest_upstream_sha: ${{ steps.check.outputs.latest_upstream_sha }}
should_release: ${{ steps.check.outputs.should_release }}
latest_release_tag: ${{ steps.check.outputs.latest_release_tag }}
steps:
- name: Checkout fork
uses: actions/checkout@v4
- name: Check upstream main for new commits
id: check
run: |
git remote add upstream https://github.com/21st-dev/1code.git || true
git fetch --no-tags upstream main
UPSTREAM_SHA=$(git rev-parse upstream/main)
CURRENT_SHA=$(git rev-parse HEAD)
echo "Upstream main SHA: $UPSTREAM_SHA"
echo "Current fork SHA: $CURRENT_SHA"
echo "latest_upstream_sha=$UPSTREAM_SHA" >> $GITHUB_OUTPUT
LATEST_TAG=$(curl -fsSL \
-H "Authorization: Bearer ${{ github.token }}" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/21st-dev/1code/releases/latest | jq -r '.tag_name // empty')
if [ -z "$LATEST_TAG" ]; then
echo "::error::Could not determine latest upstream release tag from GitHub API."
exit 1
fi
CURRENT_TAG=""
if [ -f ".last-released-upstream-tag" ]; then
CURRENT_TAG=$(cat .last-released-upstream-tag)
fi
echo "Upstream latest release tag: ${LATEST_TAG:-<none>}"
echo "Last released upstream tag in fork: ${CURRENT_TAG:-<none>}"
echo "latest_release_tag=$LATEST_TAG" >> $GITHUB_OUTPUT
if [ "${{ github.event.inputs.force_build }}" == "true" ]; then
echo "Force build requested"
echo "has_upstream_changes=true" >> $GITHUB_OUTPUT
echo "should_release=true" >> $GITHUB_OUTPUT
elif git merge-base --is-ancestor "$UPSTREAM_SHA" "$CURRENT_SHA"; then
echo "Already up to date with upstream/main"
echo "has_upstream_changes=false" >> $GITHUB_OUTPUT
if [ -n "$LATEST_TAG" ] && [ "$LATEST_TAG" != "$CURRENT_TAG" ]; then
echo "New upstream release tag detected"
echo "should_release=true" >> $GITHUB_OUTPUT
else
echo "No new upstream release tag"
echo "should_release=false" >> $GITHUB_OUTPUT
fi
else
echo "Upstream has new commits to sync"
echo "has_upstream_changes=true" >> $GITHUB_OUTPUT
if [ -n "$LATEST_TAG" ] && [ "$LATEST_TAG" != "$CURRENT_TAG" ]; then
echo "New upstream release tag detected"
echo "should_release=true" >> $GITHUB_OUTPUT
else
echo "No new upstream release tag"
echo "should_release=false" >> $GITHUB_OUTPUT
fi
fi
sync-and-build:
needs: check-upstream
if: needs.check-upstream.outputs.has_upstream_changes == 'true'
runs-on: ubuntu-latest
outputs:
sync_success: ${{ steps.sync.outputs.sync_success }}
steps:
- name: Checkout fork
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: Add upstream and sync
id: sync
run: |
git remote add upstream https://github.com/21st-dev/1code.git || true
git fetch upstream main
# Try to merge upstream changes
if git merge upstream/main --no-edit; then
echo "Merge successful"
echo "sync_success=true" >> $GITHUB_OUTPUT
else
echo "Merge conflict detected — attempting auto-resolution"
# For conflicted files, prefer our fork's version for fork-specific
# files (README.md) and upstream's version for everything else
CONFLICTED=$(git diff --name-only --diff-filter=U)
RESOLVED=true
for file in $CONFLICTED; do
case "$file" in
README.md)
echo " $file → keeping fork version"
git checkout --ours "$file"
git add "$file"
;;
*)
echo " $file → taking upstream version"
git checkout --theirs "$file"
git add "$file"
;;
esac
done
if $RESOLVED; then
git commit --no-edit
echo "Merge conflicts auto-resolved"
echo "sync_success=true" >> $GITHUB_OUTPUT
else
echo "::warning::Could not auto-resolve merge conflicts."
git merge --abort
echo "sync_success=false" >> $GITHUB_OUTPUT
exit 0
fi
fi
- name: Update last synced version
if: steps.sync.outputs.sync_success == 'true'
run: |
echo "${{ needs.check-upstream.outputs.latest_upstream_sha }}" > .last-synced-version
git add .last-synced-version
git commit -m "Sync with upstream ${{ needs.check-upstream.outputs.latest_upstream_sha }}" || true
- name: Push changes
if: steps.sync.outputs.sync_success == 'true'
run: git push origin main
trigger-build:
needs: [check-upstream, sync-and-build]
if: needs.check-upstream.outputs.should_release == 'true' && (needs['sync-and-build'].result == 'skipped' || needs['sync-and-build'].outputs.sync_success == 'true')
uses: ./.github/workflows/build-release.yml
with:
release_version: ${{ needs.check-upstream.outputs.latest_release_tag }}
secrets: inherit
mark-release-tag:
needs: [check-upstream, trigger-build]
if: needs.trigger-build.result == 'success' && needs.check-upstream.outputs.latest_release_tag != ''
runs-on: ubuntu-latest
steps:
- name: Checkout fork
uses: actions/checkout@v4
with:
ref: main
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: Pull latest (sync-and-build may have pushed)
run: git pull --rebase origin main
- name: Update last released upstream tag marker
run: |
echo "${{ needs.check-upstream.outputs.latest_release_tag }}" > .last-released-upstream-tag
git add .last-released-upstream-tag
git commit -m "Mark upstream release ${{ needs.check-upstream.outputs.latest_release_tag }} as built" || true
- name: Push tag marker
run: git push origin main