From dbd0877bdcbb8fae3cd38fe730dd53c71ee2ef74 Mon Sep 17 00:00:00 2001 From: bobra200 Date: Mon, 29 Jun 2026 16:15:52 -0700 Subject: [PATCH] ci: install sync-branches workflow via automation bundle --- .github/actions/sync-branches/README.md | 280 ++++++++++++++++++ .github/actions/sync-branches/action.yml | 225 ++++++++++++++ .../sync-branches/consumer-template.yml | 44 +++ .../sync-branches/scripts/move-major-tag.sh | 64 ++++ .github/automation-install.lock.json | 25 ++ .github/workflows/sync-develop-to-main.yml | 44 +++ 6 files changed, 682 insertions(+) create mode 100644 .github/actions/sync-branches/README.md create mode 100644 .github/actions/sync-branches/action.yml create mode 100644 .github/actions/sync-branches/consumer-template.yml create mode 100755 .github/actions/sync-branches/scripts/move-major-tag.sh create mode 100644 .github/automation-install.lock.json create mode 100644 .github/workflows/sync-develop-to-main.yml diff --git a/.github/actions/sync-branches/README.md b/.github/actions/sync-branches/README.md new file mode 100644 index 0000000..11ff251 --- /dev/null +++ b/.github/actions/sync-branches/README.md @@ -0,0 +1,280 @@ +# Sync Branches Action + +Automated branch synchronization that keeps a target branch (for example `main`) in sync with a source branch (for example `develop`). + +## Features + +- **Automatic triggers**: Runs on every push to source branch or manual dispatch +- **Clean merges**: Direct push to target branch when no conflicts exist +- **Conflict handling**: Automatically opens a PR for manual resolution if conflicts occur +- **Idempotent**: Skips sync if source is already in target (no unnecessary merges) +- **Safer token policy**: Push-triggered runs use `github.token`; optional PAT is limited to manual dispatch +- **Customizable**: Configure branches, labels, reviewers, and commit identity + +## Quick Start + +### 1. Copy The Consumer Template + +Copy `actions/sync-branches/consumer-template.yml` into your consumer repo as `.github/workflows/sync-develop-to-main.yml`. + +The default template uses: +- `rdk-e/app-gateway-automation/actions/sync-branches@actions-v1` +- `runs-on: comcast-ubuntu-latest` +- `source_branch: develop` +- `target_branch: main` + +### 2. Optional: Add PAT Secret For Manual Dispatch + +If you want manual dispatch runs to use a PAT (for protected branch scenarios), add `SEMANTIC_RELEASE_TOKEN` in the consumer repo. + +Required PAT scopes: +- `contents:write` +- `pull-requests:write` +- `issues:write` + +Push-triggered runs still use `github.token` by design. + +### 3. Example Consumer Workflow + +```yaml +name: Sync develop to main + +on: + push: + branches: [develop] + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + sync: + runs-on: comcast-ubuntu-latest + steps: + - uses: rdk-e/app-gateway-automation/actions/sync-branches@actions-v1 + with: + source_branch: ${{ github.event.inputs.source_branch || 'develop' }} + target_branch: ${{ github.event.inputs.target_branch || 'main' }} + token: ${{ github.event_name == 'workflow_dispatch' && (secrets.SEMANTIC_RELEASE_TOKEN || github.token) || github.token }} +``` + +### 4. Optional: Generate From Installer + +You can generate a repo-specific workflow from `tools/install-workflow.sh`: + +```bash +./tools/install-workflow.sh \ + --workflow sync-develop-to-main \ + --repo-dir ../firebolt-entos-apis \ + --source-branch develop \ + --target-branch main \ + --secret-name SEMANTIC_RELEASE_TOKEN \ + --runner comcast-ubuntu-latest \ + --open-pr +``` + +This writes `.github/workflows/sync-develop-to-main.yml` in the target repo. + +## OSS Bundle Installer (Manifest + Lock) + +For OSS repositories that cannot directly consume private reusable actions, use the bundle installer flow: + +1. Download release assets from `app-gateway-automation`: +- `sync-branches-.tar.gz` +- `sync-branches-.manifest.yaml` +- `sync-branches-.sha256` + +2. Install into the OSS repo: + +```bash +./tools/install-automation-bundle.sh install \ + --repo-dir /path/to/oss-repo \ + --bundle /path/to/sync-branches-.tar.gz +``` + +3. Check current state: + +```bash +./tools/install-automation-bundle.sh status \ + --repo-dir /path/to/oss-repo \ + --bundle /path/to/sync-branches-.tar.gz +``` + +4. Update to a newer bundle: + +```bash +./tools/install-automation-bundle.sh update \ + --repo-dir /path/to/oss-repo \ + --bundle /path/to/sync-branches-.tar.gz +``` + +Managed files: +- `.github/actions/sync-branches/` +- `.github/workflows/sync-develop-to-main.yml` +- `.github/automation-install.lock.json` + +Drift policy: +- Managed file drift is blocked by default. +- Use `--force` to overwrite managed drift. + +## Release Then Onboard Consumers + +For fastest rollout, use this order: + +1. Run `.github/workflows/release-actions.yml` on `main` to publish `actions-vX.Y.Z`. +2. Confirm the floating major tag (`actions-v1`) moved to the new release. +3. Install consumer workflow from template/installer (already pinned to `@actions-v1`). +4. In each consumer repo, ensure permissions include `contents: write`, `pull-requests: write`, and `issues: write`. +5. Trigger `workflow_dispatch` once to validate token and branch settings. + +## Usage Examples + +### Example 1: Simple develop -> main sync + +```yaml +jobs: + sync: + runs-on: comcast-ubuntu-latest + steps: + - uses: rdk-e/app-gateway-automation/actions/sync-branches@actions-v1 + with: + token: ${{ github.token }} +``` + +### Example 2: Custom branches with token + +```yaml +jobs: + sync: + runs-on: comcast-ubuntu-latest + steps: + - uses: rdk-e/app-gateway-automation/actions/sync-branches@actions-v1 + with: + source_branch: staging + target_branch: production + token: ${{ secrets.MY_PAT }} +``` + +### Example 3: Custom PR behavior + +```yaml +jobs: + sync: + runs-on: comcast-ubuntu-latest + steps: + - uses: rdk-e/app-gateway-automation/actions/sync-branches@actions-v1 + with: + assign_reviewers: "@platform-team,@devops-team" + pr_labels: "auto-merge,requires-review" + git_user_name: "Release Bot" + git_user_email: "releases@company.com" + token: ${{ secrets.SEMANTIC_RELEASE_TOKEN }} +``` + +## Inputs + +| Input | Default | Description | +|-------|---------|-------------| +| `source_branch` | `develop` | Branch to sync from | +| `target_branch` | `main` | Branch to sync to | +| `git_user_name` | `github-actions[bot]` | Committer name for merge commits | +| `git_user_email` | `github-actions[bot]@users.noreply.github.com` | Committer email | +| `pr_branch_prefix` | `auto-sync` | Prefix for fallback PR branches | +| `assign_reviewers` | `` | Comma-separated team/user handles for PR assignment | +| `pr_labels` | `auto-sync,needs-review` | Comma-separated labels for fallback PR | + +## Token Input + +| Input | Required | Description | +|--------|----------|-------------| +| `token` | No | Auth token for push/PR operations. Falls back to `github.token` if not provided by caller workflow. | + +## Behavior + +### Success Case: No Conflicts +``` +push to develop + ↓ +workflow runs + ↓ +merge-base check: develop NOT in main ✓ + ↓ +attempt merge: success ✓ + ↓ +direct push to main: success ✓ + ↓ +✅ main updated, no PR created +``` + +### Fallback Case: Conflict or Protected Branch +``` +push to develop + ↓ +workflow runs + ↓ +merge-base check: develop NOT in main ✓ + ↓ +attempt merge: CONFLICT ✗ (or direct push fails due to protection) + ↓ +create branch: auto-sync/develop-to-main + ↓ +open PR with labels & reviewers + ↓ +⚠️ PR #123 created for manual resolution +``` + +### Idempotent Case: Already Synced +``` +push to develop (but develop already in main) + ↓ +workflow runs + ↓ +merge-base check: develop IS in main ✓ + ↓ +✅ Skip sync (nothing to do) +``` + +## Token Scope Reference + +For branch-protected targets, use a fine-grained PAT with: + +``` +Permissions: + - Contents: Read & write + - Pull requests: Read & write + +Repository access: + - All repositories (or specific repo) +``` + +[Create fine-grained PAT](https://github.com/settings/personal-access-tokens/new) + +## Troubleshooting + +**Q: Workflow runs but push fails to protected branch** +- Ensure your token has `contents:write`, `pull-requests:write`, and `issues:write` +- If you are using the template, verify `SEMANTIC_RELEASE_TOKEN` exists when you need PAT-backed dispatch +- Confirm the workflow expression passes `github.token` for push runs and optional PAT only for dispatch runs + +**Q: PR opens but I don't want it** +- If it's just checking feature, use `workflow_dispatch` instead of automatic trigger +- Adjust branch protection rules if conflicts are expected + +**Q: I need to sync multiple branch pairs** +- Create separate workflow files, each calling sync-branches with different `source_branch`/`target_branch` +- Or use a matrix job in your calling workflow + +## File Location + +This integration guide is stored in `actions/sync-branches/` for easy discovery. + +The composite action is at `actions/sync-branches/action.yml`. + +Versioned releases are published from `actions-vX.Y.Z` tags and include the action surface (`action.yml`, `consumer-template.yml`, `README.md`, `CHANGELOG.md`, and `scripts/`). + +--- + +**Integration Branch**: `feat/reusable-sync-automation` + +For the latest stable version, check the main branch or GitHub Releases. diff --git a/.github/actions/sync-branches/action.yml b/.github/actions/sync-branches/action.yml new file mode 100644 index 0000000..194fa6a --- /dev/null +++ b/.github/actions/sync-branches/action.yml @@ -0,0 +1,225 @@ +name: Sync Branches +description: > + Merge source branch into target branch and push directly. + On conflict or push failure, opens a fallback PR instead. + All logic is here — callers only need concurrency + permissions + triggers. + +inputs: + source_branch: + description: "Branch to sync from" + required: false + default: "develop" + target_branch: + description: "Branch to sync to" + required: false + default: "main" + token: + description: "PAT with contents:write + pull-requests:write + issues:write. Falls back to GITHUB_TOKEN then github.token." + required: false + default: "" + git_user_name: + description: "Git committer name" + required: false + default: "github-actions[bot]" + git_user_email: + description: "Git committer email" + required: false + default: "github-actions[bot]@users.noreply.github.com" + pr_branch_prefix: + description: "Prefix for fallback PR branch" + required: false + default: "auto-sync" + assign_reviewers: + description: "Comma-separated reviewers to assign to fallback PR" + required: false + default: "" + pr_labels: + description: "Comma-separated labels to apply to fallback PR" + required: false + default: "auto-sync,needs-manual-merge" + +outputs: + result: + description: "Outcome: merged | skipped | pr-opened" + value: ${{ steps.outcome.outputs.result }} + pr_number: + description: "Fallback PR number (if opened)" + value: ${{ steps.outcome.outputs.pr_number }} + +runs: + using: composite + steps: + - name: Resolve authentication token + id: token + shell: bash + env: + INPUT_TOKEN: ${{ inputs.token }} + run: | + if [ -n "${INPUT_TOKEN}" ]; then + echo "token=${INPUT_TOKEN}" >> "$GITHUB_OUTPUT" + echo "source=input:token" >> "$GITHUB_OUTPUT" + elif [ -n "${GITHUB_TOKEN}" ]; then + echo "token=${GITHUB_TOKEN}" >> "$GITHUB_OUTPUT" + echo "source=env:GITHUB_TOKEN" >> "$GITHUB_OUTPUT" + else + echo "token=${{ github.token }}" >> "$GITHUB_OUTPUT" + echo "source=implicit:github.token" >> "$GITHUB_OUTPUT" + fi + + - name: Checkout full history + uses: actions/checkout@v4 + with: + token: ${{ steps.token.outputs.token }} + fetch-depth: 0 + + - name: Configure git identity + shell: bash + run: | + git config user.name "${{ inputs.git_user_name }}" + git config user.email "${{ inputs.git_user_email }}" + + - name: Check if sync is needed + id: check + shell: bash + run: | + git fetch origin \ + "refs/heads/${{ inputs.source_branch }}:refs/remotes/origin/${{ inputs.source_branch }}" \ + "refs/heads/${{ inputs.target_branch }}:refs/remotes/origin/${{ inputs.target_branch }}" + if git merge-base --is-ancestor "origin/${{ inputs.source_branch }}" "origin/${{ inputs.target_branch }}"; then + echo "already_synced=true" >> "$GITHUB_OUTPUT" + echo "${{ inputs.target_branch }} already contains all commits from ${{ inputs.source_branch }} — nothing to do." + else + echo "already_synced=false" >> "$GITHUB_OUTPUT" + fi + + - name: Attempt merge + if: steps.check.outputs.already_synced == 'false' + id: merge + shell: bash + run: | + git checkout -B "${{ inputs.target_branch }}" "origin/${{ inputs.target_branch }}" + if git merge --no-edit "origin/${{ inputs.source_branch }}"; then + echo "conflict=false" >> "$GITHUB_OUTPUT" + else + echo "conflict=true" >> "$GITHUB_OUTPUT" + git merge --abort || true + fi + + - name: Push merged result + if: steps.check.outputs.already_synced == 'false' && steps.merge.outputs.conflict == 'false' + id: push + shell: bash + run: | + if git push origin "${{ inputs.target_branch }}"; then + echo "push_failed=false" >> "$GITHUB_OUTPUT" + echo "✅ Pushed ${{ inputs.source_branch }} → ${{ inputs.target_branch }} successfully." + else + echo "push_failed=true" >> "$GITHUB_OUTPUT" + echo "Direct push failed (branch protection). Falling back to PR." + fi + + - name: Ensure labels exist + if: steps.check.outputs.already_synced == 'false' && (steps.merge.outputs.conflict == 'true' || steps.push.outputs.push_failed == 'true') + shell: bash + env: + GH_TOKEN: ${{ steps.token.outputs.token }} + run: | + IFS=',' read -ra LABELS <<< "${{ inputs.pr_labels }}" + for label in "${LABELS[@]}"; do + label=$(echo "$label" | xargs) + [ -n "$label" ] && gh label create "$label" --force || true + done + + - name: Open or update fallback PR + if: steps.check.outputs.already_synced == 'false' && (steps.merge.outputs.conflict == 'true' || steps.push.outputs.push_failed == 'true') + id: pr_fallback + shell: bash + env: + GH_TOKEN: ${{ steps.token.outputs.token }} + run: | + PR_BRANCH="${{ inputs.pr_branch_prefix }}/${{ inputs.source_branch }}-to-${{ inputs.target_branch }}" + REASON="merge conflict" + if [ "${{ steps.merge.outputs.conflict }}" != "true" ]; then + REASON="direct push failure (branch protection)" + fi + + EXISTING=$(gh pr list \ + --base "${{ inputs.target_branch }}" \ + --state open \ + --json number,headRefName 2>/dev/null | \ + jq -r --arg prefix "$PR_BRANCH" 'map(select(.headRefName | startswith($prefix))) | .[0].number // empty' || echo "") + + if [ -z "$EXISTING" ]; then + if git ls-remote --exit-code --heads origin "$PR_BRANCH" >/dev/null 2>&1; then + PR_BRANCH="${PR_BRANCH}-${GITHUB_RUN_ID}" + fi + git checkout -B "$PR_BRANCH" "origin/${{ inputs.source_branch }}" + git push origin "$PR_BRANCH" + + PR_CREATE_ARGS=( + "pr" "create" + "--base" "${{ inputs.target_branch }}" + "--head" "$PR_BRANCH" + "--title" "chore: auto-sync ${{ inputs.source_branch }} → ${{ inputs.target_branch }} (${REASON})" + "--body" "Automatic sync of \`${{ inputs.source_branch }}\` → \`${{ inputs.target_branch }}\` could not complete. **Action required:** resolve then merge. **Reason:** ${REASON}. Triggered by: ${{ github.sha }} on ${{ github.ref_name }}" + ) + + if [ -n "${{ inputs.pr_labels }}" ]; then + IFS=',' read -ra LABELS <<< "${{ inputs.pr_labels }}" + for label in "${LABELS[@]}"; do + label=$(echo "$label" | xargs) + [ -n "$label" ] && PR_CREATE_ARGS+=("--label" "$label") + done + fi + + if [ -n "${{ inputs.assign_reviewers }}" ]; then + IFS=',' read -ra REVIEWERS <<< "${{ inputs.assign_reviewers }}" + for reviewer in "${REVIEWERS[@]}"; do + reviewer=$(echo "$reviewer" | xargs) + [ -n "$reviewer" ] && PR_CREATE_ARGS+=("--reviewer" "$reviewer") + done + fi + + PR_URL=$(gh "${PR_CREATE_ARGS[@]}") + PR_NUM=$(gh pr view "$PR_URL" --json number --jq .number 2>/dev/null || echo "") + if [ -z "$PR_NUM" ]; then + PR_NUM=$(gh pr list \ + --base "${{ inputs.target_branch }}" \ + --head "$PR_BRANCH" \ + --state open \ + --json number --jq '.[0].number // empty' 2>/dev/null || echo "") + fi + if [ -z "$PR_NUM" ]; then + echo "Failed to resolve fallback PR number after creation." + exit 1 + fi + echo "pr_number=${PR_NUM}" >> "$GITHUB_OUTPUT" + echo "PR #${PR_NUM} opened for manual resolution." + else + echo "pr_number=${EXISTING}" >> "$GITHUB_OUTPUT" + if [ "${{ steps.merge.outputs.conflict }}" != "true" ]; then + # Push-failure: update branch to latest source (no conflict resolution in progress) + UPDATE_BRANCH=$(gh pr view "$EXISTING" --json headRefName --jq .headRefName 2>/dev/null || echo "$PR_BRANCH") + git checkout -B "$UPDATE_BRANCH" "origin/${{ inputs.source_branch }}" + git push --force-with-lease origin "$UPDATE_BRANCH" + echo "PR #${EXISTING} branch updated to latest ${{ inputs.source_branch }} commits on $UPDATE_BRANCH." + else + echo "PR #${EXISTING} already open with merge conflict — leaving branch untouched." + fi + fi + + - name: Determine outcome + id: outcome + if: always() + shell: bash + run: | + if [ "${{ steps.check.outputs.already_synced }}" = "true" ]; then + echo "result=skipped" >> "$GITHUB_OUTPUT" + elif [ "${{ steps.merge.outputs.conflict }}" = "false" ] && [ "${{ steps.push.outputs.push_failed }}" = "false" ]; then + echo "result=merged" >> "$GITHUB_OUTPUT" + else + echo "result=pr-opened" >> "$GITHUB_OUTPUT" + [ -n "${{ steps.pr_fallback.outputs.pr_number }}" ] && \ + echo "pr_number=${{ steps.pr_fallback.outputs.pr_number }}" >> "$GITHUB_OUTPUT" + exit 1 + fi diff --git a/.github/actions/sync-branches/consumer-template.yml b/.github/actions/sync-branches/consumer-template.yml new file mode 100644 index 0000000..d208790 --- /dev/null +++ b/.github/actions/sync-branches/consumer-template.yml @@ -0,0 +1,44 @@ +# Sync develop -> main -- consumer template +# +# Copy this file to .github/workflows/sync-develop-to-main.yml in any consumer repo. +# The sync logic lives in app-gateway-automation/actions/sync-branches. +# +# Optional secret: SEMANTIC_RELEASE_TOKEN (contents:write + pull-requests:write + issues:write) +# Used only for workflow_dispatch runs (falls back to github.token if absent). +# Push-triggered runs always use github.token to keep PAT scope out of routine automation. + +name: Sync develop to main + +on: + push: + branches: [develop] + workflow_dispatch: + inputs: + source_branch: + description: "Source branch" + required: false + default: "develop" + target_branch: + description: "Target branch" + required: false + default: "main" + +concurrency: + group: sync-${{ github.repository }}-${{ github.event_name == 'workflow_dispatch' && format('{0}-to-{1}', github.event.inputs.source_branch || 'develop', github.event.inputs.target_branch || 'main') || format('{0}-to-{1}', github.ref_name, 'main') }} + cancel-in-progress: ${{ github.event_name != 'workflow_dispatch' }} + +permissions: + contents: write + pull-requests: write + issues: write + +jobs: + sync: + name: Merge ${{ github.event.inputs.source_branch || 'develop' }} → ${{ github.event.inputs.target_branch || 'main' }} + runs-on: comcast-ubuntu-latest # use org runner label; change if repo uses a different label + steps: + - uses: rdk-e/app-gateway-automation/actions/sync-branches@actions-v1 + with: + source_branch: ${{ github.event.inputs.source_branch || 'develop' }} + target_branch: ${{ github.event.inputs.target_branch || 'main' }} + token: ${{ github.event_name == 'workflow_dispatch' && (secrets.SEMANTIC_RELEASE_TOKEN || github.token) || github.token }} diff --git a/.github/actions/sync-branches/scripts/move-major-tag.sh b/.github/actions/sync-branches/scripts/move-major-tag.sh new file mode 100755 index 0000000..3112512 --- /dev/null +++ b/.github/actions/sync-branches/scripts/move-major-tag.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Move floating major tag to point to the new release +# Usage: move-major-tag.sh --version +# Example: move-major-tag.sh --version 1.2.3 +# → creates/updates actions-v1 to point to actions-v1.2.3 + +VERSION="" +while [[ $# -gt 0 ]]; do + case "$1" in + --version) + VERSION="$2" + shift 2 + ;; + *) + shift + ;; + esac +done + +if [ -z "$VERSION" ]; then + echo "ERROR: Usage: move-major-tag.sh --version " + exit 1 +fi + +# Skip prerelease versions (e.g. 1.2.3-rc.1) — don't move the stable major tag +if [[ "$VERSION" == *-* ]]; then + echo "Skipping major tag update for prerelease version: $VERSION" + exit 0 +fi + +# Extract major version from semver +MAJOR=$(echo "$VERSION" | cut -d. -f1) + +# Configure git if needed +if ! git config user.name 2>/dev/null; then + git config user.name "github-actions[bot]" +fi +if ! git config user.email 2>/dev/null; then + git config user.email "github-actions[bot]@users.noreply.github.com" +fi + +# Move floating major tag +MAJOR_TAG="actions-v${MAJOR}" +VERSION_TAG="actions-v${VERSION}" +TARGET_COMMIT=$(git rev-parse "${VERSION_TAG}^{commit}" 2>/dev/null || true) + +if [ -z "$TARGET_COMMIT" ]; then + echo "ERROR: Failed to resolve commit for $VERSION_TAG" + exit 1 +fi + +git tag -f "$MAJOR_TAG" "$TARGET_COMMIT" || { + echo "ERROR: Failed to tag $MAJOR_TAG -> $TARGET_COMMIT" + exit 1 +} + +git push origin "refs/tags/$MAJOR_TAG" --force || { + echo "ERROR: Failed to push $MAJOR_TAG" + exit 1 +} + +echo "Moved $MAJOR_TAG → $VERSION_TAG ($TARGET_COMMIT)" diff --git a/.github/automation-install.lock.json b/.github/automation-install.lock.json new file mode 100644 index 0000000..9e8b56b --- /dev/null +++ b/.github/automation-install.lock.json @@ -0,0 +1,25 @@ +{ + "schema_version": 1, + "installer_version": "1.0.0", + "installed_at": "2026-06-29T23:05:35Z", + "modules": [ + { + "module": "sync-branches", + "version": "1.0.99-test", + "source": "sync-branches-1.0.99-test.tar.gz", + "managed_files": [ + ".github/actions/sync-branches/action.yml", + ".github/actions/sync-branches/consumer-template.yml", + ".github/actions/sync-branches/README.md", + ".github/actions/sync-branches/scripts/move-major-tag.sh", + ".github/workflows/sync-develop-to-main.yml" + ], + "template_values": { + "source_branch": "develop", + "target_branch": "main", + "runner": "comcast-ubuntu-latest", + "secret_name": "SEMANTIC_RELEASE_TOKEN" + } + } + ] +} diff --git a/.github/workflows/sync-develop-to-main.yml b/.github/workflows/sync-develop-to-main.yml new file mode 100644 index 0000000..d208790 --- /dev/null +++ b/.github/workflows/sync-develop-to-main.yml @@ -0,0 +1,44 @@ +# Sync develop -> main -- consumer template +# +# Copy this file to .github/workflows/sync-develop-to-main.yml in any consumer repo. +# The sync logic lives in app-gateway-automation/actions/sync-branches. +# +# Optional secret: SEMANTIC_RELEASE_TOKEN (contents:write + pull-requests:write + issues:write) +# Used only for workflow_dispatch runs (falls back to github.token if absent). +# Push-triggered runs always use github.token to keep PAT scope out of routine automation. + +name: Sync develop to main + +on: + push: + branches: [develop] + workflow_dispatch: + inputs: + source_branch: + description: "Source branch" + required: false + default: "develop" + target_branch: + description: "Target branch" + required: false + default: "main" + +concurrency: + group: sync-${{ github.repository }}-${{ github.event_name == 'workflow_dispatch' && format('{0}-to-{1}', github.event.inputs.source_branch || 'develop', github.event.inputs.target_branch || 'main') || format('{0}-to-{1}', github.ref_name, 'main') }} + cancel-in-progress: ${{ github.event_name != 'workflow_dispatch' }} + +permissions: + contents: write + pull-requests: write + issues: write + +jobs: + sync: + name: Merge ${{ github.event.inputs.source_branch || 'develop' }} → ${{ github.event.inputs.target_branch || 'main' }} + runs-on: comcast-ubuntu-latest # use org runner label; change if repo uses a different label + steps: + - uses: rdk-e/app-gateway-automation/actions/sync-branches@actions-v1 + with: + source_branch: ${{ github.event.inputs.source_branch || 'develop' }} + target_branch: ${{ github.event.inputs.target_branch || 'main' }} + token: ${{ github.event_name == 'workflow_dispatch' && (secrets.SEMANTIC_RELEASE_TOKEN || github.token) || github.token }}