name: Propagate Changes Between Branches on: push: branches: - main - development permissions: contents: write pull-requests: write jobs: propagate: name: Create PR to synchronize branches runs-on: ubuntu-latest if: github.actor != 'github-actions[bot]' && github.event.pusher != null steps: - name: Set up Node (for github-script) uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6 with: node-version: '24.11.1' - name: Propagate Changes uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: script: | const currentBranch = context.ref.replace('refs/heads/', ''); async function createPR(src, base) { if (src === base) return; core.info(`Checking propagation from ${src} to ${base}...`); // Check for existing open PRs const { data: pulls } = await github.rest.pulls.list({ owner: context.repo.owner, repo: context.repo.repo, state: 'open', head: `${context.repo.owner}:${src}`, base: base, }); if (pulls.length > 0) { core.info(`Existing PR found for ${src} -> ${base}. Skipping.`); return; } // Compare commits to see if src is ahead of base try { const compare = await github.rest.repos.compareCommits({ owner: context.repo.owner, repo: context.repo.repo, base: base, head: src, }); // If src is not ahead, nothing to merge if (compare.data.ahead_by === 0) { core.info(`${src} is not ahead of ${base}. No propagation needed.`); return; } } catch (error) { // If base branch doesn't exist, etc. core.warning(`Error comparing ${src} to ${base}: ${error.message}`); return; } // Create PR try { const pr = await github.rest.pulls.create({ owner: context.repo.owner, repo: context.repo.repo, title: `Propagate changes from ${src} into ${base}`, head: src, base: base, body: `Automated PR to propagate changes from ${src} into ${base}.\n\nTriggered by push to ${currentBranch}.`, }); core.info(`Created PR #${pr.data.number} to merge ${src} into ${base}`); } catch (error) { core.warning(`Failed to create PR from ${src} to ${base}: ${error.message}`); } } if (currentBranch === 'main') { // Main -> Development await createPR('main', 'development'); } else if (currentBranch === 'development') { // Development -> Feature branches const branches = await github.paginate(github.rest.repos.listBranches, { owner: context.repo.owner, repo: context.repo.repo, }); const featureBranches = branches .map(b => b.name) .filter(name => name.startsWith('feature/')); core.info(`Found ${featureBranches.length} feature branches: ${featureBranches.join(', ')}`); for (const featureBranch of featureBranches) { await createPR('development', featureBranch); } } env: CHARON_TOKEN: ${{ secrets.CHARON_TOKEN }} CPMP_TOKEN: ${{ secrets.CPMP_TOKEN }}