|
| 1 | +name: Upstream Sync Check |
| 2 | + |
| 3 | +on: |
| 4 | + schedule: |
| 5 | + # Check every Monday at 9:00 UTC |
| 6 | + - cron: '0 9 * * 1' |
| 7 | + workflow_dispatch: # Allow manual trigger |
| 8 | + |
| 9 | +jobs: |
| 10 | + check-upstream: |
| 11 | + runs-on: ubuntu-latest |
| 12 | + |
| 13 | + steps: |
| 14 | + - name: Checkout |
| 15 | + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 |
| 16 | + with: |
| 17 | + fetch-depth: 0 |
| 18 | + |
| 19 | + - name: Setup Bun |
| 20 | + uses: oven-sh/setup-bun@b7a1c7ccf290d58743029c4f6903da283811b979 # v2.1.0 |
| 21 | + with: |
| 22 | + bun-version: latest |
| 23 | + |
| 24 | + - name: Install dependencies |
| 25 | + run: bun install |
| 26 | + |
| 27 | + - name: Add upstream remote |
| 28 | + run: | |
| 29 | + git remote add upstream https://github.com/hunvreus/basecoat.git |
| 30 | + git fetch upstream --tags |
| 31 | + |
| 32 | + # Create tracking branch if not exists |
| 33 | + git branch upstream/tracking upstream/main 2>/dev/null || \ |
| 34 | + git branch -f upstream/tracking upstream/main |
| 35 | + |
| 36 | + - name: Check for updates |
| 37 | + id: check |
| 38 | + run: | |
| 39 | + # Get current tracking commit (from last sync) |
| 40 | + TRACKING_COMMIT=$(git rev-parse upstream/tracking 2>/dev/null || echo "") |
| 41 | + UPSTREAM_COMMIT=$(git rev-parse upstream/main) |
| 42 | + |
| 43 | + if [ -z "$TRACKING_COMMIT" ]; then |
| 44 | + echo "First run, setting baseline" |
| 45 | + NEW_COMMITS=0 |
| 46 | + else |
| 47 | + NEW_COMMITS=$(git rev-list --count ${TRACKING_COMMIT}..${UPSTREAM_COMMIT} 2>/dev/null || echo "0") |
| 48 | + fi |
| 49 | + |
| 50 | + echo "new_commits=$NEW_COMMITS" >> $GITHUB_OUTPUT |
| 51 | + echo "upstream_commit=$UPSTREAM_COMMIT" >> $GITHUB_OUTPUT |
| 52 | + |
| 53 | + # Get upstream version tag |
| 54 | + UPSTREAM_TAG=$(git describe --tags --exact-match upstream/main 2>/dev/null || \ |
| 55 | + git describe --tags --always upstream/main) |
| 56 | + echo "upstream_tag=$UPSTREAM_TAG" >> $GITHUB_OUTPUT |
| 57 | + |
| 58 | + if [ "$NEW_COMMITS" -gt "0" ]; then |
| 59 | + echo "Found $NEW_COMMITS new commit(s)" |
| 60 | + git log --oneline ${TRACKING_COMMIT}..${UPSTREAM_COMMIT} > upstream-changes.txt |
| 61 | + |
| 62 | + # Run diff analysis from packages/ultra |
| 63 | + cd packages/ultra |
| 64 | + bun run upstream:diff || true |
| 65 | + fi |
| 66 | + |
| 67 | + - name: Upload analysis artifacts |
| 68 | + if: steps.check.outputs.new_commits > 0 |
| 69 | + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 |
| 70 | + with: |
| 71 | + name: upstream-analysis |
| 72 | + path: | |
| 73 | + packages/ultra/upstream-analysis/UPSTREAM_DIFF.md |
| 74 | + packages/ultra/upstream-analysis/patches/ |
| 75 | + packages/ultra/upstream-analysis/latest-analysis.json |
| 76 | + retention-days: 30 |
| 77 | + |
| 78 | + - name: Create Issue if updates found |
| 79 | + if: steps.check.outputs.new_commits > 0 |
| 80 | + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 |
| 81 | + with: |
| 82 | + script: | |
| 83 | + const fs = require('fs'); |
| 84 | + |
| 85 | + let changes = ''; |
| 86 | + try { |
| 87 | + changes = fs.readFileSync('upstream-changes.txt', 'utf8'); |
| 88 | + } catch (e) { |
| 89 | + changes = 'Could not read changes'; |
| 90 | + } |
| 91 | + |
| 92 | + let diffReport = ''; |
| 93 | + try { |
| 94 | + diffReport = fs.readFileSync('packages/ultra/upstream-analysis/UPSTREAM_DIFF.md', 'utf8'); |
| 95 | + // Truncate if too long |
| 96 | + if (diffReport.length > 50000) { |
| 97 | + diffReport = diffReport.substring(0, 50000) + '\n\n... (truncated)'; |
| 98 | + } |
| 99 | + } catch (e) { |
| 100 | + diffReport = 'Diff report not available'; |
| 101 | + } |
| 102 | + |
| 103 | + const newCommits = '${{ steps.check.outputs.new_commits }}'; |
| 104 | + const upstreamTag = '${{ steps.check.outputs.upstream_tag }}'; |
| 105 | + |
| 106 | + // Check for existing open issue |
| 107 | + const existingIssues = await github.rest.issues.listForRepo({ |
| 108 | + owner: context.repo.owner, |
| 109 | + repo: context.repo.repo, |
| 110 | + labels: 'upstream-sync', |
| 111 | + state: 'open' |
| 112 | + }); |
| 113 | + |
| 114 | + if (existingIssues.data.length > 0) { |
| 115 | + // Update existing issue |
| 116 | + await github.rest.issues.createComment({ |
| 117 | + owner: context.repo.owner, |
| 118 | + repo: context.repo.repo, |
| 119 | + issue_number: existingIssues.data[0].number, |
| 120 | + body: `## Update: ${new Date().toISOString()}\n\nUpstream now at **${upstreamTag}** with ${newCommits} new commit(s).\n\n### New Commits:\n\`\`\`\n${changes}\n\`\`\`\n\n<details>\n<summary>Full Diff Report</summary>\n\n${diffReport}\n\n</details>` |
| 121 | + }); |
| 122 | + } else { |
| 123 | + // Create new issue |
| 124 | + await github.rest.issues.create({ |
| 125 | + owner: context.repo.owner, |
| 126 | + repo: context.repo.repo, |
| 127 | + title: `🔄 Upstream Basecoat updated to ${upstreamTag}`, |
| 128 | + body: `## Upstream Repository Update\n\nThe upstream [basecoat](https://github.com/hunvreus/basecoat) repository has **${newCommits}** new commit(s).\n\n### Version\n- **Tag**: ${upstreamTag}\n- **Commit**: ${{ steps.check.outputs.upstream_commit }}\n\n### New Commits\n\`\`\`\n${changes}\n\`\`\`\n\n### Diff Analysis\n\n<details>\n<summary>Click to expand full diff report</summary>\n\n${diffReport}\n\n</details>\n\n### Next Steps\n\n1. Download the analysis artifacts from this workflow run\n2. Review the patches in \`upstream-analysis/patches/\`\n3. Run locally:\n \`\`\`bash\n bun run upstream:sync\n bun run upstream:diff\n \`\`\`\n4. Cherry-pick or manually apply relevant changes\n5. Update \`UPSTREAM.md\` with sync status\n6. Close this issue when done`, |
| 129 | + labels: ['upstream-sync'] |
| 130 | + }); |
| 131 | + } |
0 commit comments