Sync upstream #33
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: Sync upstream | |
| on: | |
| schedule: | |
| - cron: "0 3 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| target_commit: | |
| description: 'Commit hash (leave blank to use latest commit)' | |
| required: false | |
| type: string | |
| permissions: | |
| contents: write | |
| jobs: | |
| sync: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Configure git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| - name: Add upstream | |
| run: | | |
| git remote add upstream https://github.com/shuaiplus/NodeWarden.git || true | |
| git fetch upstream --tags | |
| - name: Resolve target commit | |
| id: resolve | |
| run: | | |
| TRIGGER="${{ github.event_name }}" | |
| MANUAL_INPUT="${{ github.event.inputs.target_commit }}" | |
| if [ "$TRIGGER" = "schedule" ]; then | |
| # Auto mode: resolve latest upstream release tag | |
| LATEST_TAG=$(curl -s https://api.github.com/repos/shuaiplus/NodeWarden/releases/latest | jq -r .tag_name) | |
| if [ "$LATEST_TAG" = "null" ] || [ -z "$LATEST_TAG" ]; then | |
| echo "No release found in upstream." | |
| exit 1 | |
| fi | |
| TARGET_SHA=$(git rev-list -n 1 "$LATEST_TAG" 2>/dev/null) | |
| if [ -z "$TARGET_SHA" ]; then | |
| echo "Tag '$LATEST_TAG' not found after fetch." | |
| exit 1 | |
| fi | |
| echo "mode=auto" >> $GITHUB_OUTPUT | |
| echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT | |
| echo "target_sha=$TARGET_SHA" >> $GITHUB_OUTPUT | |
| echo "Auto mode — latest release: $LATEST_TAG ($TARGET_SHA)" | |
| elif [ -n "$MANUAL_INPUT" ]; then | |
| # Manual mode: use provided commit hash or tag | |
| TARGET_SHA=$(git rev-parse "$MANUAL_INPUT" 2>/dev/null) | |
| if [ -z "$TARGET_SHA" ]; then | |
| echo "Cannot resolve '$MANUAL_INPUT' to a commit." | |
| exit 1 | |
| fi | |
| echo "mode=manual" >> $GITHUB_OUTPUT | |
| echo "target_sha=$TARGET_SHA" >> $GITHUB_OUTPUT | |
| echo "Manual mode — target: $MANUAL_INPUT ($TARGET_SHA)" | |
| else | |
| # Manual mode, blank input: use latest commit on upstream/main | |
| TARGET_SHA=$(git rev-parse upstream/main) | |
| echo "mode=manual" >> $GITHUB_OUTPUT | |
| echo "target_sha=$TARGET_SHA" >> $GITHUB_OUTPUT | |
| echo "Manual mode — latest commit: $TARGET_SHA" | |
| fi | |
| - name: Check if update is needed | |
| id: check | |
| run: | | |
| TARGET_SHA="${{ steps.resolve.outputs.target_sha }}" | |
| MODE="${{ steps.resolve.outputs.mode }}" | |
| if [ "$MODE" = "manual" ]; then | |
| # Manual: skip only if HEAD is exactly this commit | |
| CURRENT_SHA=$(git rev-parse HEAD) | |
| if [ "$CURRENT_SHA" = "$TARGET_SHA" ]; then | |
| echo "Already at $TARGET_SHA — skipping." | |
| echo "needs_update=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "Switching to $TARGET_SHA" | |
| echo "needs_update=true" >> $GITHUB_OUTPUT | |
| fi | |
| else | |
| # Auto: skip if target is already in ancestry | |
| if git merge-base --is-ancestor "$TARGET_SHA" HEAD 2>/dev/null; then | |
| echo "Already up to date with $TARGET_SHA — skipping." | |
| echo "needs_update=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "Update needed — target: $TARGET_SHA" | |
| echo "needs_update=true" >> $GITHUB_OUTPUT | |
| fi | |
| fi | |
| - name: Apply update | |
| if: steps.check.outputs.needs_update == 'true' | |
| run: | | |
| TARGET_SHA="${{ steps.resolve.outputs.target_sha }}" | |
| MODE="${{ steps.resolve.outputs.mode }}" | |
| git checkout main | |
| if [ "$MODE" = "manual" ]; then | |
| # Hard reset allows both upgrade and rollback | |
| git reset --hard "$TARGET_SHA" | |
| else | |
| git merge "$TARGET_SHA" --no-edit | |
| fi | |
| - name: Restore workflow file | |
| if: steps.check.outputs.needs_update == 'true' | |
| run: | | |
| # Always keep our own workflow file, never let upstream overwrite it | |
| git checkout HEAD@{1} -- .github/workflows/sync-upstream.yml 2>/dev/null || true | |
| if ! git diff --cached --quiet; then | |
| git commit -m "chore: restore sync-upstream workflow after sync" | |
| fi | |
| - name: Push | |
| if: steps.check.outputs.needs_update == 'true' | |
| run: | | |
| if [ "${{ steps.resolve.outputs.mode }}" = "manual" ]; then | |
| git push origin main --force | |
| else | |
| git push origin main | |
| fi | |
| - name: Summary | |
| run: | | |
| if [ "${{ steps.check.outputs.needs_update }}" = "true" ]; then | |
| echo "### Synced successfully" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Mode:** ${{ steps.resolve.outputs.mode }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Tag:** ${{ steps.resolve.outputs.latest_tag || 'N/A (manual)' }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Commit:** \`${{ steps.resolve.outputs.target_sha }}\`" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "### Nothing to update" >> $GITHUB_STEP_SUMMARY | |
| fi |