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