From 4aee73ecca0252033988f37f6c9bd56100617f71 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Sun, 31 May 2026 14:12:59 +0000 Subject: [PATCH 01/10] 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 02/10] 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 03/10] 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 04/10] 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." ;; From 9e28f9b77dbf064e7e1c140d58df7386f1efe1b7 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Sun, 21 Jun 2026 10:02:35 +0000 Subject: [PATCH 05/10] RDKE-1065: Semi Automated Release workflow Signed-off-by: Yogeswaran K --- .../component-release-finish-on-approval.yml | 2 +- .github/workflows/component-release.yml | 84 +++++++++++++++++-- 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/.github/workflows/component-release-finish-on-approval.yml b/.github/workflows/component-release-finish-on-approval.yml index 0b34fcca..cae745dd 100755 --- a/.github/workflows/component-release-finish-on-approval.yml +++ b/.github/workflows/component-release-finish-on-approval.yml @@ -60,7 +60,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - token: ${{ secrets.RDKCM_RDKE }} + ssh-key: ${{ secrets.RDKCM_DEPLOY_KEY }} - 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 8fd30f31..bcb874d1 100755 --- a/.github/workflows/component-release.yml +++ b/.github/workflows/component-release.yml @@ -7,13 +7,24 @@ on: description: "Release version (example: 5.2.0)" required: true type: string + release_type: + description: "Release type" + required: true + type: choice + options: + - main + - hotfix release_mode: - description: "Release mode" + description: "Release mode (ignored for hotfix)" required: true type: choice options: - approvable - auto-complete + source_branch: + description: "Support branch for hotfix (e.g. support/5.0). Required for hotfix, ignored for main release." + required: false + type: string permissions: contents: write @@ -24,7 +35,9 @@ jobs: runs-on: comcast-ubuntu-latest env: RELEASE_VERSION: ${{ github.event.inputs.release_version }} + RELEASE_TYPE: ${{ github.event.inputs.release_type }} RELEASE_MODE: ${{ github.event.inputs.release_mode }} + SOURCE_BRANCH: ${{ github.event.inputs.source_branch }} REPO: ${{ github.repository }} ACTOR: ${{ github.actor }} GH_TOKEN: ${{ secrets.RDKCM_RDKE }} @@ -40,7 +53,7 @@ jobs: fi - name: Authorize auto-complete (maintainers only) - if: ${{ github.event.inputs.release_mode == 'auto-complete' }} + if: ${{ github.event.inputs.release_type == 'main' && github.event.inputs.release_mode == 'auto-complete' }} run: | set -euo pipefail role=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" -q '.role_name' | tr '[:upper:]' '[:lower:]') @@ -59,7 +72,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - token: ${{ secrets.RDKCM_RDKE }} + ssh-key: ${{ secrets.RDKCM_DEPLOY_KEY }} - name: Install release tools run: | @@ -75,6 +88,7 @@ jobs: git config user.email "${ACTOR}@users.noreply.github.com" - name: Initialize git-flow + if: ${{ github.event.inputs.release_type == 'main' }} run: | set -euo pipefail git fetch --prune origin @@ -87,7 +101,7 @@ jobs: # ── AUTO-COMPLETE RELEASE ── # - name: "Auto-complete: full release" - if: ${{ github.event.inputs.release_mode == 'auto-complete' }} + if: ${{ github.event.inputs.release_type == 'main' && github.event.inputs.release_mode == 'auto-complete' }} run: | set -euo pipefail @@ -112,7 +126,7 @@ jobs: # ── APPROVABLE RELEASE ── # - name: "Approvable: start release and create PR" - if: ${{ github.event.inputs.release_mode == 'approvable' }} + if: ${{ github.event.inputs.release_type == 'main' && github.event.inputs.release_mode == 'approvable' }} run: | set -euo pipefail @@ -146,6 +160,59 @@ jobs: echo "PR from ${release_branch} to develop already exists (#${existing_pr})." fi + # + # ── HOTFIX RELEASE ── + # + - name: "Hotfix: validate source branch" + if: ${{ github.event.inputs.release_type == 'hotfix' }} + run: | + set -euo pipefail + if [ -z "${SOURCE_BRANCH}" ]; then + echo "ERROR: source_branch is required for hotfix release." + exit 1 + fi + + - name: "Hotfix: update support branch and push tag" + if: ${{ github.event.inputs.release_type == 'hotfix' }} + 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 fetch --prune origin + if ! git ls-remote --exit-code --heads origin "${SOURCE_BRANCH}" >/dev/null 2>&1; then + echo "ERROR: source branch '${SOURCE_BRANCH}' does not exist on origin." + exit 1 + fi + + git checkout -B "${SOURCE_BRANCH}" "origin/${SOURCE_BRANCH}" + + hotfix_branch="hotfix/${RELEASE_VERSION}" + if git show-ref --verify --quiet "refs/heads/${hotfix_branch}"; then + git checkout "${hotfix_branch}" + else + git checkout -b "${hotfix_branch}" "${SOURCE_BRANCH}" + fi + + auto-changelog -v "${RELEASE_VERSION}" + git add CHANGELOG.md + if ! git diff --cached --quiet; then + git commit -m "${RELEASE_VERSION} hotfix release" + else + echo "No CHANGELOG.md changes to commit." + fi + + git checkout "${SOURCE_BRANCH}" + git merge --no-ff "${hotfix_branch}" -m "Merge hotfix ${RELEASE_VERSION} into ${SOURCE_BRANCH}" + + git tag -a "${RELEASE_VERSION}" -m "Hotfix ${RELEASE_VERSION}" + git push origin "${SOURCE_BRANCH}" + git push origin "${RELEASE_VERSION}" + echo "Hotfix complete: pushed ${SOURCE_BRANCH} and tag ${RELEASE_VERSION}." + # # ── CLEANUP ON FAILURE ── # @@ -154,7 +221,11 @@ jobs: 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 + if [[ "${RELEASE_TYPE}" == "main" ]]; then + git push origin --delete "release/${RELEASE_VERSION}" 2>/dev/null || true + else + git push origin --delete "hotfix/${RELEASE_VERSION}" 2>/dev/null || true + fi - name: Workflow summary if: always() @@ -164,5 +235,6 @@ jobs: echo "- Repository: ${REPO}" echo "- Actor: ${ACTOR}" echo "- Version: ${RELEASE_VERSION}" + echo "- Type: ${RELEASE_TYPE}" echo "- Mode: ${RELEASE_MODE}" } >> "$GITHUB_STEP_SUMMARY" From 5927ed187652f69e67c037eda7db2a76248bca0a Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Sun, 28 Jun 2026 13:22:28 +0000 Subject: [PATCH 06/10] RDKE-1065: Separated release workflow for Maintainer approval Signed-off-by: Yogeswaran K --- .github/workflows/component-release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/component-release.yml b/.github/workflows/component-release.yml index bcb874d1..88a18f76 100755 --- a/.github/workflows/component-release.yml +++ b/.github/workflows/component-release.yml @@ -30,6 +30,10 @@ permissions: contents: write pull-requests: write +concurrency: + group: component-release-${{ github.event.inputs.release_type }}-${{ github.event.inputs.release_version }} + cancel-in-progress: false + jobs: release: runs-on: comcast-ubuntu-latest From a80d01763bb64dfa34d306bb9ba7b9db7754be04 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Sun, 28 Jun 2026 13:36:03 +0000 Subject: [PATCH 07/10] RDKE-1065: Separated release workflow for Maintainer approval Signed-off-by: Yogeswaran K --- .github/workflows/component-release.yml | 38 ++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/.github/workflows/component-release.yml b/.github/workflows/component-release.yml index 88a18f76..c6bd1096 100755 --- a/.github/workflows/component-release.yml +++ b/.github/workflows/component-release.yml @@ -78,6 +78,14 @@ jobs: fetch-depth: 0 ssh-key: ${{ secrets.RDKCM_DEPLOY_KEY }} + - name: Initialize cleanup flags + run: | + { + echo "CREATED_RELEASE_BRANCH=false" + echo "CREATED_HOTFIX_BRANCH=false" + echo "CREATED_TAG=false" + } >> "$GITHUB_ENV" + - name: Install release tools run: | set -euo pipefail @@ -115,6 +123,7 @@ jobs: fi git flow release start "${RELEASE_VERSION}" + echo "CREATED_RELEASE_BRANCH=true" >> "$GITHUB_ENV" auto-changelog -v "${RELEASE_VERSION}" git add CHANGELOG.md if ! git diff --cached --quiet; then @@ -122,6 +131,7 @@ jobs: fi git flow release publish "${RELEASE_VERSION}" git flow release finish -m "${RELEASE_VERSION} release" "${RELEASE_VERSION}" + echo "CREATED_TAG=true" >> "$GITHUB_ENV" git push origin main git push origin --tags git push origin develop @@ -144,6 +154,7 @@ jobs: echo "${release_branch} already exists on remote. Skipping release start." else git flow release start "${RELEASE_VERSION}" + echo "CREATED_RELEASE_BRANCH=true" >> "$GITHUB_ENV" auto-changelog -v "${RELEASE_VERSION}" git add CHANGELOG.md if ! git diff --cached --quiet; then @@ -175,6 +186,11 @@ jobs: echo "ERROR: source_branch is required for hotfix release." exit 1 fi + if ! [[ "${SOURCE_BRANCH}" =~ ^[A-Za-z0-9._/-]+$ ]]; then + echo "ERROR: source_branch contains invalid characters." + echo "Only alphanumeric characters, dots, underscores, hyphens, and slashes are allowed." + exit 1 + fi - name: "Hotfix: update support branch and push tag" if: ${{ github.event.inputs.release_type == 'hotfix' }} @@ -199,6 +215,7 @@ jobs: git checkout "${hotfix_branch}" else git checkout -b "${hotfix_branch}" "${SOURCE_BRANCH}" + echo "CREATED_HOTFIX_BRANCH=true" >> "$GITHUB_ENV" fi auto-changelog -v "${RELEASE_VERSION}" @@ -213,6 +230,7 @@ jobs: git merge --no-ff "${hotfix_branch}" -m "Merge hotfix ${RELEASE_VERSION} into ${SOURCE_BRANCH}" git tag -a "${RELEASE_VERSION}" -m "Hotfix ${RELEASE_VERSION}" + echo "CREATED_TAG=true" >> "$GITHUB_ENV" git push origin "${SOURCE_BRANCH}" git push origin "${RELEASE_VERSION}" echo "Hotfix complete: pushed ${SOURCE_BRANCH} and tag ${RELEASE_VERSION}." @@ -223,12 +241,24 @@ jobs: - 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 + if [ ! -d .git ]; then + echo "No repository checkout available. Skipping cleanup." + exit 0 + fi + + if [[ "${CREATED_TAG:-false}" == "true" ]] && git rev-parse -q --verify "refs/tags/${RELEASE_VERSION}" >/dev/null 2>&1; then + git tag -d "${RELEASE_VERSION}" 2>/dev/null || true + git push origin ":refs/tags/${RELEASE_VERSION}" 2>/dev/null || true + fi + if [[ "${RELEASE_TYPE}" == "main" ]]; then - git push origin --delete "release/${RELEASE_VERSION}" 2>/dev/null || true + if [[ "${CREATED_RELEASE_BRANCH:-false}" == "true" ]] && git show-ref --verify --quiet "refs/heads/release/${RELEASE_VERSION}"; then + git push origin --delete "release/${RELEASE_VERSION}" 2>/dev/null || true + fi else - git push origin --delete "hotfix/${RELEASE_VERSION}" 2>/dev/null || true + if [[ "${CREATED_HOTFIX_BRANCH:-false}" == "true" ]] && git show-ref --verify --quiet "refs/heads/hotfix/${RELEASE_VERSION}"; then + git push origin --delete "hotfix/${RELEASE_VERSION}" 2>/dev/null || true + fi fi - name: Workflow summary From f6a551ef0d39f7d3f3562e287008bce638f1dea8 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Sun, 28 Jun 2026 14:53:20 +0000 Subject: [PATCH 08/10] RDKE-1065: Separated release workflow for Maintainer approval Signed-off-by: Yogeswaran K --- .github/workflows/README.md | 97 +++++++++++++++++++ .../component-release-finish-on-approval.yml | 24 +---- .github/workflows/component-release.yml | 16 --- 3 files changed, 102 insertions(+), 35 deletions(-) create mode 100755 .github/workflows/README.md diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100755 index 00000000..2c62d0e3 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,97 @@ +# Automated Release Workflow + +Automates main release and hotfix flows via GitHub Actions. + +## Workflows + +### 1. Component Release (`component-release.yml`) + +Triggered manually from the **Actions** tab via `workflow_dispatch`. + +#### Inputs + +| Input | Required | Description | +|-------|----------|-------------| +| `release_version` | Yes | Release version (for example: `5.2.0`, `5.2.0-rc1`) | +| `release_type` | Yes | `main` or `hotfix` | +| `release_mode` | Yes | `approvable` or `auto-complete` (ignored for hotfix) | +| `source_branch` | Hotfix only | Support branch for hotfix (for example: `support/5.0`) | + +#### Concurrency + +Runs are serialized per release key: + +- `component-release--` + +This prevents two runs for the same version/type from racing on branches/tags. + +#### Release Modes + +**Main release - auto-complete** +1. Validates version format. +2. Runs `git flow release start` -> changelog -> `release publish` -> `release finish`. +3. Pushes `main`, `develop`, and tags. + +**Main release - approvable** +1. Validates version format. +2. Runs `git flow release start` -> changelog -> `release publish`. +3. Creates a PR: `release/` -> `develop`. +4. Stops and waits for PR approval. +5. On approval, the second workflow finishes the release. + +**Hotfix (always auto-complete)** +1. Requires `source_branch` input. +2. Validates `source_branch` against `^[A-Za-z0-9._/-]+$`. +3. Creates/uses `hotfix/` from `source_branch`, updates changelog, merges back into `source_branch` only. +4. Creates tag ``. +5. Pushes only `source_branch` and tag (`main`/`develop` are not pushed). + +### 2. Component Release Finish On Approval (`component-release-finish-on-approval.yml`) + +Triggered on approved review for `release/*` PRs targeting `develop`. + +#### What it does +1. Verifies PR review decision is `APPROVED`. +2. Checks out release branch. +3. Runs `git flow release finish` (merge to `main` + `develop`, create tag). +4. Pushes `main`, `develop`, and tags. +5. Closes the release PR. + +## Authentication and Secrets + +Both workflows use: + +- `RDKCM_DEPLOY_KEY`: SSH private key used by checkout and git push operations. +- `RDKCM_RDKE`: token used by `gh` API/CLI calls. + +## Failure Cleanup Safety + +On failure, cleanup only removes refs created by the current run: + +- Deletes tag only if the run created that tag. +- Deletes `release/` only if the run created that local release branch. +- Deletes `hotfix/` only if the run created that local hotfix branch. +- Skips cleanup if repository checkout is unavailable. + +## Prerequisites + +### Repository Settings + +- **Settings -> Actions -> General -> Workflow permissions**: set to **Read and write permissions**. +- **Settings -> Actions -> General**: enable **Allow GitHub Actions to create and approve pull requests**. + +### Branch Protection + +- For approvable flow: enforce PR review rules on `develop`. +- For auto-complete flow pushes to `develop`: ensure the automation actor has appropriate bypass rights in repository rules. + +## Version Format + +Accepted examples: + +- `5.2.0` +- `5.2.0-rc1` +- `5.2.0v1` +- `5.2.0.beta2` + +Rule: `major.minor.patch` digits with optional suffix. diff --git a/.github/workflows/component-release-finish-on-approval.yml b/.github/workflows/component-release-finish-on-approval.yml index cae745dd..ff33b314 100755 --- a/.github/workflows/component-release-finish-on-approval.yml +++ b/.github/workflows/component-release-finish-on-approval.yml @@ -27,21 +27,7 @@ jobs: RELEASE_BRANCH: ${{ github.event.pull_request.head.ref }} steps: - - name: Verify approver is a maintainer - id: auth - run: | - set -euo pipefail - 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." - echo "is_maintainer=false" >> "$GITHUB_OUTPUT" - fi - - name: Check approval status - if: steps.auth.outputs.is_maintainer == 'true' id: approvals run: | set -euo pipefail @@ -56,28 +42,28 @@ jobs: fi - name: Checkout repository - if: steps.auth.outputs.is_maintainer == 'true' && steps.approvals.outputs.should_finish == 'true' + if: steps.approvals.outputs.should_finish == 'true' uses: actions/checkout@v4 with: fetch-depth: 0 ssh-key: ${{ secrets.RDKCM_DEPLOY_KEY }} - name: Install release tools - if: steps.auth.outputs.is_maintainer == 'true' && steps.approvals.outputs.should_finish == 'true' + 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.auth.outputs.is_maintainer == 'true' && steps.approvals.outputs.should_finish == 'true' + if: steps.approvals.outputs.should_finish == 'true' run: | set -euo pipefail git config user.name "${ACTOR}" git config user.email "${ACTOR}@users.noreply.github.com" - name: Finish release and push - if: steps.auth.outputs.is_maintainer == 'true' && steps.approvals.outputs.should_finish == 'true' + if: steps.approvals.outputs.should_finish == 'true' run: | set -euo pipefail release_version="${RELEASE_BRANCH#release/}" @@ -115,7 +101,7 @@ jobs: git push origin develop - name: Close the release PR - if: steps.auth.outputs.is_maintainer == 'true' && steps.approvals.outputs.should_finish == 'true' + if: steps.approvals.outputs.should_finish == 'true' run: | set -euo pipefail pr_number="${{ github.event.pull_request.number }}" diff --git a/.github/workflows/component-release.yml b/.github/workflows/component-release.yml index c6bd1096..77824ffc 100755 --- a/.github/workflows/component-release.yml +++ b/.github/workflows/component-release.yml @@ -56,22 +56,6 @@ jobs: exit 1 fi - - name: Authorize auto-complete (maintainers only) - if: ${{ github.event.inputs.release_type == 'main' && github.event.inputs.release_mode == 'auto-complete' }} - run: | - set -euo pipefail - 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." - ;; - *) - echo "ERROR: Only maintainers/owners can run auto-complete releases." - exit 1 - ;; - esac - - name: Checkout repository uses: actions/checkout@v4 with: From 111ba266a4bdde775779606a73680fdd81e925e9 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Mon, 29 Jun 2026 03:16:24 +0000 Subject: [PATCH 09/10] RDKE-1065: Separated release workflow for Maintainer approval Signed-off-by: Yogeswaran K --- .github/workflows/component-release-finish-on-approval.yml | 1 + .github/workflows/component-release.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/component-release-finish-on-approval.yml b/.github/workflows/component-release-finish-on-approval.yml index ff33b314..2050a947 100755 --- a/.github/workflows/component-release-finish-on-approval.yml +++ b/.github/workflows/component-release-finish-on-approval.yml @@ -99,6 +99,7 @@ jobs: git push origin main git push origin --tags git push origin develop + git push origin --delete "${RELEASE_BRANCH}" || true - name: Close the release PR if: steps.approvals.outputs.should_finish == 'true' diff --git a/.github/workflows/component-release.yml b/.github/workflows/component-release.yml index 77824ffc..54cada20 100755 --- a/.github/workflows/component-release.yml +++ b/.github/workflows/component-release.yml @@ -119,6 +119,7 @@ jobs: git push origin main git push origin --tags git push origin develop + git push origin --delete "release/${RELEASE_VERSION}" || true # # ── APPROVABLE RELEASE ── From 50e01bb47a80af05e69b14a0e3a34baca7ecca7c Mon Sep 17 00:00:00 2001 From: Deva Date: Mon, 29 Jun 2026 11:44:35 +0530 Subject: [PATCH 10/10] Rename README.md to component-release-workflow-usage.md --- .../workflows/{README.md => component-release-workflow-usage.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{README.md => component-release-workflow-usage.md} (100%) diff --git a/.github/workflows/README.md b/.github/workflows/component-release-workflow-usage.md similarity index 100% rename from .github/workflows/README.md rename to .github/workflows/component-release-workflow-usage.md