diff --git a/.github/rebase-upstream-prod-conflict.md b/.github/rebase-upstream-prod-conflict.md new file mode 100644 index 00000000000..f7a71d5f0f2 --- /dev/null +++ b/.github/rebase-upstream-prod-conflict.md @@ -0,0 +1,131 @@ +# Manual Rebase Conflict Resolution Guide + +This guide explains how to manually resolve rebase conflicts when the automated rebase workflow fails. + +## Overview + +The automated workflow attempts to rebase the `opensoft-prod` branch onto the upstream `production` branch. When conflicts occur, they must be resolved manually. + +## Prerequisites + +- Git installed locally +- Write access to the opensoft/opencode repository +- Familiarity with Git rebase and conflict resolution + +## Steps to Resolve Conflicts + +### 1. Clone the Repository (if not already done) + +```bash +git clone https://github.com/opensoft/opencode.git +cd opencode +``` + +### 2. Add the Upstream Remote + +```bash +git remote add upstream https://github.com/sst/opencode.git +``` + +If the remote already exists, you can skip this step or update it: + +```bash +git remote set-url upstream https://github.com/sst/opencode.git +``` + +### 3. Fetch All Remotes + +```bash +git fetch origin +git fetch upstream +``` + +### 4. Checkout the opensoft-prod Branch + +```bash +git checkout opensoft-prod +``` + +### 5. Start the Rebase + +```bash +git rebase upstream/production +``` + +### 6. Resolve Conflicts + +When conflicts occur, Git will pause and show you which files have conflicts: + +``` +CONFLICT (content): Merge conflict in +``` + +For each conflicted file: + +1. Open the file in your editor +2. Look for conflict markers: + ``` + <<<<<<< HEAD + (upstream/production changes) + ======= + (your opensoft-prod changes) + >>>>>>> opensoft-prod + ``` +3. Edit the file to resolve the conflict, keeping the appropriate changes +4. Remove the conflict markers +5. Save the file + +### 7. Mark Files as Resolved + +After resolving conflicts in a file: + +```bash +git add +``` + +### 8. Continue the Rebase + +```bash +git rebase --continue +``` + +Repeat steps 6-8 for each conflict that occurs during the rebase. + +### 9. Force Push to Update opensoft-prod + +⚠️ **Warning**: This is a force push operation. Make sure you've resolved all conflicts correctly. + +```bash +git push --force-with-lease origin opensoft-prod +``` + +Using `--force-with-lease` is safer than `--force` as it will fail if someone else has pushed changes since you last fetched. + +## Aborting the Rebase + +If you need to abort the rebase and start over: + +```bash +git rebase --abort +``` + +This will return your branch to the state before the rebase started. + +## Tips + +- **Review changes carefully**: Use `git diff` to review changes before continuing +- **Test after resolution**: If possible, test the code after resolving conflicts +- **Ask for help**: If you're unsure about a conflict, consult with team members +- **Document complex resolutions**: If a conflict required non-obvious resolution, document it in the commit message + +## Verification + +After successfully pushing, verify the rebase: + +1. Check the commit history: `git log --oneline -10` +2. Verify the branch has the expected upstream changes +3. Ensure any custom opensoft changes are preserved + +## Re-running the Automated Workflow + +After manually resolving and pushing, the automated workflow should succeed on its next run (or you can trigger it manually using workflow_dispatch). diff --git a/.github/workflows/rebase-upstream-prod.yml b/.github/workflows/rebase-upstream-prod.yml new file mode 100644 index 00000000000..c5200a2e746 --- /dev/null +++ b/.github/workflows/rebase-upstream-prod.yml @@ -0,0 +1,170 @@ +name: Rebase opensoft-prod onto upstream production + +on: + schedule: + # Run every Sunday at 23:00 UTC (Sunday night) + - cron: "0 23 * * 0" + workflow_dispatch: + +concurrency: ${{ github.workflow }} +jobs: + rebase: + runs-on: blacksmith-4vcpu-ubuntu-2404 + permissions: + contents: write + issues: write + steps: + - name: Checkout opensoft-prod branch + uses: actions/checkout@v4 + with: + ref: opensoft-prod + 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 remote + run: | + if ! git remote | grep -q '^upstream$'; then + git remote add upstream https://github.com/sst/opencode.git + fi + git remote -v + + - name: Fetch upstream + run: | + git fetch upstream production + + - name: Rebase onto upstream/production + id: rebase + run: | + echo "Attempting rebase of opensoft-prod onto upstream/production..." + if git rebase upstream/production; then + echo "result=success" >> $GITHUB_OUTPUT + echo "Rebase successful" + else + echo "result=conflict" >> $GITHUB_OUTPUT + echo "Rebase failed with conflicts" + git rebase --abort + exit 1 + fi + + - name: Force push to origin + if: steps.rebase.outputs.result == 'success' + run: | + echo "Pushing rebased opensoft-prod to origin..." + git push --force-with-lease origin opensoft-prod + echo "Successfully pushed rebased branch" + + - name: Create issue on conflict + if: failure() && steps.rebase.outputs.result == 'conflict' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const path = require('path'); + + // Read the conflict resolution instructions + const instructionsPath = path.join( + process.env.GITHUB_WORKSPACE, + '.github/rebase-upstream-prod-conflict.md' + ); + let instructions = ''; + try { + instructions = fs.readFileSync(instructionsPath, 'utf8'); + } catch (error) { + instructions = [ + 'See .github/rebase-upstream-prod-conflict.md', + 'for manual resolution instructions.' + ].join(' '); + } + + const issueTitle = [ + '🔄 Rebase Conflict: opensoft-prod onto', + 'upstream/production', + `(${new Date().toISOString().split('T')[0]})` + ].join(' '); + const issueBody = `## Automated Rebase Failed + + The automated rebase of \`opensoft-prod\` onto \`upstream/production\` has encountered conflicts that require manual resolution. + + **Workflow Run:** [#${context.runNumber}](${context.payload.repository.html_url}/actions/runs/${context.runId}) + + ## What Happened + + The GitHub Actions workflow attempted to: + 1. Fetch the latest \`production\` branch from upstream (sst/opencode) + 2. Rebase \`opensoft-prod\` onto \`upstream/production\` + 3. The rebase encountered merge conflicts and was aborted + + ## Next Steps + + A maintainer needs to manually resolve the conflicts and push the rebased branch. Please follow the instructions below: + + --- + + ${instructions} + + --- + + ## After Resolution + + Once you've manually resolved and pushed the changes: + - [ ] Verify \`opensoft-prod\` is up to date with \`upstream/production\` + - [ ] Close this issue + - [ ] The next automated run should succeed + `.trim(); + + // Check if an issue already exists + const existingIssues = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + labels: 'rebase-conflict', + per_page: 10 + }); + + // Only create a new issue if one doesn't already exist + if (existingIssues.data.length === 0) { + const issue = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: issueTitle, + body: issueBody, + labels: [ + 'rebase-conflict', + 'maintenance', + 'automated' + ] + }); + + console.log(`Created issue #${issue.data.number}`); + } else { + const existingNum = existingIssues.data[0].number; + console.log(`Issue already exists: #${existingNum}`); + + // Add a comment to the existing issue + const date = new Date().toISOString().split('T')[0]; + const runUrl = [ + context.payload.repository.html_url, + '/actions/runs/', + context.runId + ].join(''); + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: existingIssues.data[0].number, + body: [ + `Another rebase conflict detected on ${date}.\n\n`, + `Workflow Run: [#${context.runNumber}](${runUrl})` + ].join('') + }); + } + + - name: Notify on failure + if: failure() && steps.rebase.outputs.result != 'conflict' + run: | + echo "::error::Rebase workflow failed unexpectedly. Check logs." + exit 1