name: Sync upstream & merge-forward skill branches on: # Triggered by upstream repo via repository_dispatch repository_dispatch: types: [upstream-main-updated] # Fallback: run on schedule in case dispatch isn't configured schedule: - cron: '0 */6 * * *' # every 6 hours # Also run when fork's main is pushed directly push: branches: [main] workflow_dispatch: permissions: contents: write issues: write jobs: sync-and-merge: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - uses: actions/setup-node@v4 with: node-version: 20 cache: npm - name: Configure git run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - name: Sync with upstream main id: sync run: | # Add upstream remote git remote add upstream https://github.com/qwibitai/nanoclaw.git git fetch upstream main # Check if upstream has new commits if git merge-base --is-ancestor upstream/main HEAD; then echo "Already up to date with upstream main." echo "synced=false" >> "$GITHUB_OUTPUT" exit 0 fi # Merge upstream main into fork's main if ! git merge upstream/main --no-edit; then echo "::error::Failed to merge upstream/main into fork main — conflicts detected" git merge --abort echo "synced=false" >> "$GITHUB_OUTPUT" echo "sync_failed=true" >> "$GITHUB_OUTPUT" exit 0 fi # Validate build npm ci if ! npm run build; then echo "::error::Build failed after merging upstream/main" git reset --hard "origin/main" echo "synced=false" >> "$GITHUB_OUTPUT" echo "sync_failed=true" >> "$GITHUB_OUTPUT" exit 0 fi if ! npm test 2>/dev/null; then echo "::error::Tests failed after merging upstream/main" git reset --hard "origin/main" echo "synced=false" >> "$GITHUB_OUTPUT" echo "sync_failed=true" >> "$GITHUB_OUTPUT" exit 0 fi git push origin main echo "synced=true" >> "$GITHUB_OUTPUT" - name: Merge main into skill branches id: merge run: | FAILED="" SUCCEEDED="" # List all remote skill branches SKILL_BRANCHES=$(git branch -r --list 'origin/skill/*' | sed 's|origin/||' | xargs) if [ -z "$SKILL_BRANCHES" ]; then echo "No skill branches found." exit 0 fi for BRANCH in $SKILL_BRANCHES; do SKILL_NAME=$(echo "$BRANCH" | sed 's|skill/||') echo "" echo "=== Processing $BRANCH ===" git checkout -B "$BRANCH" "origin/$BRANCH" if ! git merge main --no-edit; then echo "::warning::Merge conflict in $BRANCH" git merge --abort FAILED="$FAILED $SKILL_NAME" continue fi # Check if there's anything new to push if git diff --quiet "origin/$BRANCH"; then echo "$BRANCH is already up to date with main." SUCCEEDED="$SUCCEEDED $SKILL_NAME" continue fi npm ci if ! npm run build; then echo "::warning::Build failed for $BRANCH" git reset --hard "origin/$BRANCH" FAILED="$FAILED $SKILL_NAME" continue fi if ! npm test 2>/dev/null; then echo "::warning::Tests failed for $BRANCH" git reset --hard "origin/$BRANCH" FAILED="$FAILED $SKILL_NAME" continue fi git push origin "$BRANCH" SUCCEEDED="$SUCCEEDED $SKILL_NAME" echo "$BRANCH merged and pushed successfully." done echo "" echo "=== Results ===" echo "Succeeded: $SUCCEEDED" echo "Failed: $FAILED" echo "failed=$FAILED" >> "$GITHUB_OUTPUT" echo "succeeded=$SUCCEEDED" >> "$GITHUB_OUTPUT" - name: Open issue for upstream sync failure if: steps.sync.outputs.sync_failed == 'true' uses: actions/github-script@v7 with: script: | await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: `Upstream sync failed — merge conflict or build failure`, body: [ 'The automated sync with `qwibitai/nanoclaw` main failed.', '', 'This usually means upstream made changes that conflict with this fork\'s channel code.', '', 'To resolve manually:', '```bash', 'git fetch upstream main', 'git merge upstream/main', '# resolve conflicts', 'npm run build && npm test', 'git push', '```', ].join('\n'), labels: ['upstream-sync'] }); - name: Open issue for failed skill merges if: steps.merge.outputs.failed != '' uses: actions/github-script@v7 with: script: | const failed = '${{ steps.merge.outputs.failed }}'.trim().split(/\s+/); const body = [ `The merge-forward workflow failed to merge \`main\` into the following skill branches:`, '', ...failed.map(s => `- \`skill/${s}\`: merge conflict, build failure, or test failure`), '', 'Please resolve manually:', '```bash', ...failed.map(s => [ `git checkout skill/${s}`, `git merge main`, `# resolve conflicts, then: git push`, '' ]).flat(), '```', ].join('\n'); await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: `Merge-forward failed for ${failed.length} skill branch(es)`, body, labels: ['skill-maintenance'] });