From 4aee73ecca0252033988f37f6c9bd56100617f71 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Sun, 31 May 2026 14:12:59 +0000 Subject: [PATCH 1/4] RDKE-1065: Semi Automated Release workflow Signed-off-by: Yogeswaran K --- .../component-release-finish-on-approval.yml | 117 ++++++++++++ .github/workflows/component-release.yml | 167 ++++++++++++++++++ 2 files changed, 284 insertions(+) create mode 100755 .github/workflows/component-release-finish-on-approval.yml create mode 100755 .github/workflows/component-release.yml diff --git a/.github/workflows/component-release-finish-on-approval.yml b/.github/workflows/component-release-finish-on-approval.yml new file mode 100755 index 00000000..8c548076 --- /dev/null +++ b/.github/workflows/component-release-finish-on-approval.yml @@ -0,0 +1,117 @@ +name: Component Release Finish On Approval + +on: + pull_request_review: + types: [submitted] + +permissions: + contents: write + pull-requests: write + +concurrency: + group: release-finish-${{ github.event.pull_request.head.ref }} + cancel-in-progress: false + +jobs: + finish-release: + name: Finish release when PR is approved + if: > + github.event.review.state == 'approved' && + github.event.pull_request.base.ref == 'develop' && + startsWith(github.event.pull_request.head.ref, 'release/') + runs-on: comcast-ubuntu-latest + env: + REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + RELEASE_BRANCH: ${{ github.event.pull_request.head.ref }} + + steps: + - name: Check approval status + id: approvals + run: | + set -euo pipefail + pr_number="${{ github.event.pull_request.number }}" + decision=$(gh pr view --repo "${REPO}" "${pr_number}" --json reviewDecision -q '.reviewDecision') + echo "PR #${pr_number} review decision: ${decision}" + if [ "${decision}" = "APPROVED" ]; then + echo "should_finish=true" >> "$GITHUB_OUTPUT" + else + echo "PR is not fully approved yet. Waiting." + echo "should_finish=false" >> "$GITHUB_OUTPUT" + fi + + - name: Checkout repository + if: steps.approvals.outputs.should_finish == 'true' + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install release tools + if: steps.approvals.outputs.should_finish == 'true' + run: | + set -euo pipefail + sudo apt-get update + sudo apt-get install -y git-flow + + - name: Configure git identity + if: steps.approvals.outputs.should_finish == 'true' + run: | + set -euo pipefail + git config user.name "release-bot" + git config user.email "release-bot@users.noreply.github.com" + + - name: Finish release and push + if: steps.approvals.outputs.should_finish == 'true' + run: | + set -euo pipefail + release_version="${RELEASE_BRANCH#release/}" + + git fetch --prune origin + + # Skip if tag already exists + if git ls-remote --exit-code --tags origin "refs/tags/${release_version}" >/dev/null 2>&1; then + echo "Tag ${release_version} already exists on origin. Skipping release finish." + exit 0 + fi + + # Checkout the release branch + if git show-ref --verify --quiet "refs/heads/${RELEASE_BRANCH}"; then + git checkout "${RELEASE_BRANCH}" + elif git ls-remote --exit-code --heads origin "${RELEASE_BRANCH}" >/dev/null 2>&1; then + git checkout -b "${RELEASE_BRANCH}" "origin/${RELEASE_BRANCH}" + else + echo "${RELEASE_BRANCH} does not exist locally or on origin." + exit 1 + fi + + # Ensure main and develop exist locally + git checkout main 2>/dev/null || git checkout -b main origin/main + git checkout develop 2>/dev/null || git checkout -b develop origin/develop + git checkout "${RELEASE_BRANCH}" + + # Initialize git-flow + git flow init -d + + # Finish release: merges to main + develop, creates tag + git flow release finish -m "Release ${release_version}" "${release_version}" + git push origin main + git push origin --tags + git push origin develop + + - name: Close the release PR + if: steps.approvals.outputs.should_finish == 'true' + run: | + set -euo pipefail + pr_number="${{ github.event.pull_request.number }}" + gh pr close "${pr_number}" --repo "${REPO}" --comment "Release finished by automation. Merged via git flow release finish." || true + + - name: Workflow summary + if: always() + run: | + { + echo "## Release Finish On Approval" + echo "- Repository: ${REPO}" + echo "- Release branch: ${RELEASE_BRANCH}" + echo "- Triggered by review: ${{ github.event.review.state }}" + echo "- Finished release: ${{ steps.approvals.outputs.should_finish || 'false' }}" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/component-release.yml b/.github/workflows/component-release.yml new file mode 100755 index 00000000..5f8d8927 --- /dev/null +++ b/.github/workflows/component-release.yml @@ -0,0 +1,167 @@ +name: Component Release + +on: + workflow_dispatch: + inputs: + release_version: + description: "Release version (example: 5.2.0)" + required: true + type: string + release_mode: + description: "Release mode" + required: true + type: choice + options: + - approvable + - auto-complete + +permissions: + contents: write + pull-requests: write + +jobs: + release: + runs-on: comcast-ubuntu-latest + env: + RELEASE_VERSION: ${{ github.event.inputs.release_version }} + RELEASE_MODE: ${{ github.event.inputs.release_mode }} + REPO: ${{ github.repository }} + ACTOR: ${{ github.actor }} + GH_TOKEN: ${{ github.token }} + + steps: + - name: Validate version format + run: | + set -euo pipefail + if ! [[ "${RELEASE_VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+[A-Za-z0-9._-]*$ ]]; then + echo "Invalid version format: ${RELEASE_VERSION}" + echo "Expected format: major.minor.patch with optional suffix (e.g. 5.2.0, 5.2.0-rc1)" + exit 1 + fi + + - name: Authorize auto-complete (maintainers only) + if: ${{ github.event.inputs.release_mode == 'auto-complete' }} + run: | + set -euo pipefail + permission=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" -q '.permission' | tr '[:upper:]' '[:lower:]') + echo "Actor '${ACTOR}' permission: ${permission}" + case "${permission}" in + admin|maintain) + echo "Authorization successful." + ;; + *) + echo "ERROR: Only maintainers/owners can run auto-complete releases." + exit 1 + ;; + esac + + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install release tools + run: | + set -euo pipefail + sudo apt-get update + sudo apt-get install -y git-flow + npm install -g auto-changelog + + - name: Configure git identity + run: | + set -euo pipefail + git config user.name "${ACTOR}" + git config user.email "${ACTOR}@users.noreply.github.com" + + - name: Initialize git-flow + run: | + set -euo pipefail + git fetch --prune origin + git checkout develop + git reset --hard origin/develop + git fetch origin main:main + git flow init -d + + # + # ── AUTO-COMPLETE RELEASE ── + # + - name: "Auto-complete: full release" + if: ${{ github.event.inputs.release_mode == 'auto-complete' }} + run: | + set -euo pipefail + + if git ls-remote --exit-code --tags origin "refs/tags/${RELEASE_VERSION}" >/dev/null 2>&1; then + echo "Tag ${RELEASE_VERSION} already exists. Skipping." + exit 0 + fi + + git flow release start "${RELEASE_VERSION}" + auto-changelog -v "${RELEASE_VERSION}" + git add CHANGELOG.md + if ! git diff --cached --quiet; then + git commit -m "${RELEASE_VERSION} release changelog updates" + fi + git flow release publish "${RELEASE_VERSION}" + git flow release finish -m "${RELEASE_VERSION} release" "${RELEASE_VERSION}" + git push origin main + git push origin --tags + git push origin develop + + # + # ── APPROVABLE RELEASE ── + # + - name: "Approvable: start release and create PR" + if: ${{ github.event.inputs.release_mode == 'approvable' }} + run: | + set -euo pipefail + + if git ls-remote --exit-code --tags origin "refs/tags/${RELEASE_VERSION}" >/dev/null 2>&1; then + echo "Tag ${RELEASE_VERSION} already exists. Skipping." + exit 0 + fi + + release_branch="release/${RELEASE_VERSION}" + if git ls-remote --exit-code --heads origin "${release_branch}" >/dev/null 2>&1; then + echo "${release_branch} already exists on remote. Skipping release start." + else + git flow release start "${RELEASE_VERSION}" + auto-changelog -v "${RELEASE_VERSION}" + git add CHANGELOG.md + if ! git diff --cached --quiet; then + git commit -m "${RELEASE_VERSION} release changelog updates" + fi + git flow release publish "${RELEASE_VERSION}" + fi + + existing_pr=$(gh pr list --head "${release_branch}" --base develop --state open --json number -q '.[0].number') + if [ -z "${existing_pr}" ]; then + gh pr create \ + --base develop \ + --head "${release_branch}" \ + --title "Release ${RELEASE_VERSION}" \ + --body "Automated release PR for ${RELEASE_VERSION}. Approve this PR to trigger release finish." + echo "PR created. Waiting for approval to finish release." + else + echo "PR from ${release_branch} to develop already exists (#${existing_pr})." + fi + + # + # ── CLEANUP ON FAILURE ── + # + - name: Cleanup on failure + if: failure() + run: | + git tag -d "${RELEASE_VERSION}" 2>/dev/null || true + git push origin ":refs/tags/${RELEASE_VERSION}" 2>/dev/null || true + git push origin --delete "release/${RELEASE_VERSION}" 2>/dev/null || true + + - name: Workflow summary + if: always() + run: | + { + echo "## Component Release Summary" + echo "- Repository: ${REPO}" + echo "- Actor: ${ACTOR}" + echo "- Version: ${RELEASE_VERSION}" + echo "- Mode: ${RELEASE_MODE}" + } >> "$GITHUB_STEP_SUMMARY" From c21d6eb1b6c146c6e29e62078708925fa032acc8 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Sun, 31 May 2026 15:33:38 +0000 Subject: [PATCH 2/4] RDKE-1065: Semi Automated Release workflow Signed-off-by: Yogeswaran K --- .../component-release-finish-on-approval.yml | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/.github/workflows/component-release-finish-on-approval.yml b/.github/workflows/component-release-finish-on-approval.yml index 8c548076..b4c541fe 100755 --- a/.github/workflows/component-release-finish-on-approval.yml +++ b/.github/workflows/component-release-finish-on-approval.yml @@ -22,11 +22,26 @@ jobs: runs-on: comcast-ubuntu-latest env: REPO: ${{ github.repository }} + ACTOR: ${{ github.event.review.user.login }} GH_TOKEN: ${{ github.token }} RELEASE_BRANCH: ${{ github.event.pull_request.head.ref }} steps: + - name: Verify approver is a maintainer + id: auth + run: | + set -euo pipefail + permission=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" -q '.permission' | tr '[:upper:]' '[:lower:]') + echo "Approver '${ACTOR}' permission: ${permission}" + if [[ "${permission}" == "admin" || "${permission}" == "maintain" ]]; then + echo "is_maintainer=true" >> "$GITHUB_OUTPUT" + else + echo "Approver is not a maintainer. Skipping release finish." + echo "is_maintainer=false" >> "$GITHUB_OUTPUT" + fi + - name: Check approval status + if: steps.auth.outputs.is_maintainer == 'true' id: approvals run: | set -euo pipefail @@ -41,27 +56,27 @@ jobs: fi - name: Checkout repository - if: steps.approvals.outputs.should_finish == 'true' + if: steps.auth.outputs.is_maintainer == 'true' && steps.approvals.outputs.should_finish == 'true' uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install release tools - if: steps.approvals.outputs.should_finish == 'true' + if: steps.auth.outputs.is_maintainer == 'true' && steps.approvals.outputs.should_finish == 'true' run: | set -euo pipefail sudo apt-get update sudo apt-get install -y git-flow - name: Configure git identity - if: steps.approvals.outputs.should_finish == 'true' + if: steps.auth.outputs.is_maintainer == 'true' && steps.approvals.outputs.should_finish == 'true' run: | set -euo pipefail - git config user.name "release-bot" - git config user.email "release-bot@users.noreply.github.com" + git config user.name "${ACTOR}" + git config user.email "${ACTOR}@users.noreply.github.com" - name: Finish release and push - if: steps.approvals.outputs.should_finish == 'true' + if: steps.auth.outputs.is_maintainer == 'true' && steps.approvals.outputs.should_finish == 'true' run: | set -euo pipefail release_version="${RELEASE_BRANCH#release/}" @@ -99,7 +114,7 @@ jobs: git push origin develop - name: Close the release PR - if: steps.approvals.outputs.should_finish == 'true' + if: steps.auth.outputs.is_maintainer == 'true' && steps.approvals.outputs.should_finish == 'true' run: | set -euo pipefail pr_number="${{ github.event.pull_request.number }}" From 862e27f26c59f3a6147d59b4c184108ad373938e Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Sun, 31 May 2026 15:56:29 +0000 Subject: [PATCH 3/4] RDKE-1065: Semi Automated Release workflow Signed-off-by: Yogeswaran K --- .github/workflows/component-release-finish-on-approval.yml | 3 ++- .github/workflows/component-release.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/component-release-finish-on-approval.yml b/.github/workflows/component-release-finish-on-approval.yml index b4c541fe..eda7541b 100755 --- a/.github/workflows/component-release-finish-on-approval.yml +++ b/.github/workflows/component-release-finish-on-approval.yml @@ -23,7 +23,7 @@ jobs: env: REPO: ${{ github.repository }} ACTOR: ${{ github.event.review.user.login }} - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ secrets.RDKCM_RDKE }} RELEASE_BRANCH: ${{ github.event.pull_request.head.ref }} steps: @@ -60,6 +60,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + token: ${{ secrets.RDKCM_RDKE }} - name: Install release tools if: steps.auth.outputs.is_maintainer == 'true' && steps.approvals.outputs.should_finish == 'true' diff --git a/.github/workflows/component-release.yml b/.github/workflows/component-release.yml index 5f8d8927..b4332d3f 100755 --- a/.github/workflows/component-release.yml +++ b/.github/workflows/component-release.yml @@ -27,7 +27,7 @@ jobs: RELEASE_MODE: ${{ github.event.inputs.release_mode }} REPO: ${{ github.repository }} ACTOR: ${{ github.actor }} - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ secrets.RDKCM_RDKE }} steps: - name: Validate version format @@ -59,6 +59,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + token: ${{ secrets.RDKCM_RDKE }} - name: Install release tools run: | From ba8e94d7602045162874b9a3b902fa15f9efd136 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Sun, 31 May 2026 16:05:47 +0000 Subject: [PATCH 4/4] RDKE-1065: Semi Automated Release workflow Signed-off-by: Yogeswaran K --- .github/workflows/component-release-finish-on-approval.yml | 6 +++--- .github/workflows/component-release.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/component-release-finish-on-approval.yml b/.github/workflows/component-release-finish-on-approval.yml index eda7541b..0b34fcca 100755 --- a/.github/workflows/component-release-finish-on-approval.yml +++ b/.github/workflows/component-release-finish-on-approval.yml @@ -31,9 +31,9 @@ jobs: id: auth run: | set -euo pipefail - permission=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" -q '.permission' | tr '[:upper:]' '[:lower:]') - echo "Approver '${ACTOR}' permission: ${permission}" - if [[ "${permission}" == "admin" || "${permission}" == "maintain" ]]; then + role=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" -q '.role_name' | tr '[:upper:]' '[:lower:]') + echo "Approver '${ACTOR}' role: ${role}" + if [[ "${role}" == "admin" || "${role}" == "maintain" ]]; then echo "is_maintainer=true" >> "$GITHUB_OUTPUT" else echo "Approver is not a maintainer. Skipping release finish." diff --git a/.github/workflows/component-release.yml b/.github/workflows/component-release.yml index b4332d3f..8fd30f31 100755 --- a/.github/workflows/component-release.yml +++ b/.github/workflows/component-release.yml @@ -43,9 +43,9 @@ jobs: if: ${{ github.event.inputs.release_mode == 'auto-complete' }} run: | set -euo pipefail - permission=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" -q '.permission' | tr '[:upper:]' '[:lower:]') - echo "Actor '${ACTOR}' permission: ${permission}" - case "${permission}" in + role=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" -q '.role_name' | tr '[:upper:]' '[:lower:]') + echo "Actor '${ACTOR}' role: ${role}" + case "${role}" in admin|maintain) echo "Authorization successful." ;;