Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions .github/workflows/component-release-finish-on-approval.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
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
Comment thread
yogeswaransky marked this conversation as resolved.

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 }}
ACTOR: ${{ github.event.review.user.login }}
GH_TOKEN: ${{ secrets.RDKCM_RDKE }}
RELEASE_BRANCH: ${{ github.event.pull_request.head.ref }}
Comment on lines +22 to +27

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"
Comment on lines +33 to +37
else
echo "Approver is not a maintainer. Skipping release finish."
echo "is_maintainer=false" >> "$GITHUB_OUTPUT"
fi
Comment on lines +30 to +41

- name: Check approval status
if: steps.auth.outputs.is_maintainer == 'true'
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
Comment thread
yogeswaransky marked this conversation as resolved.

- name: Checkout repository
if: steps.auth.outputs.is_maintainer == 'true' && 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'
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'
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'
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}"
Comment on lines +103 to +106

# 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.auth.outputs.is_maintainer == 'true' && 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"
240 changes: 240 additions & 0 deletions .github/workflows/component-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
name: Component Release

on:
workflow_dispatch:
inputs:
release_version:
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 (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
pull-requests: write

Comment on lines +29 to +32
jobs:
release:
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 }}

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_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
Comment on lines +55 to +69

- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ssh-key: ${{ secrets.RDKCM_DEPLOY_KEY }}

- 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
if: ${{ github.event.inputs.release_type == 'main' }}
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_type == 'main' && 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_type == 'main' && 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
Comment on lines +151 to +152
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."
Comment on lines +153 to +158
else
echo "PR from ${release_branch} to develop already exists (#${existing_pr})."
fi
Comment on lines +151 to +161

#
# ── 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
Comment on lines +169 to +173

- 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 ──
#
- 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 [[ "${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
Comment on lines +219 to +228

- name: Workflow summary
if: always()
run: |
{
echo "## Component Release Summary"
echo "- Repository: ${REPO}"
echo "- Actor: ${ACTOR}"
echo "- Version: ${RELEASE_VERSION}"
echo "- Type: ${RELEASE_TYPE}"
echo "- Mode: ${RELEASE_MODE}"
} >> "$GITHUB_STEP_SUMMARY"