Skip to content

Release

Release #1

name: Release
on:
workflow_dispatch:
inputs:
release_ticket_key:
description: 'Release Ticket Key (e.g., UIKIT-1234)'
required: true
type: string
is_test:
description: 'Sent by the SDK Release automation; declared so the dispatch is accepted'
required: false
default: false
type: boolean
jobs:
release:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
env:
BOT_GH_TOKEN: ${{ secrets.SDK_GH_BOT1_TOKEN }}
JIRA_AUTH_USER: ${{ secrets.JIRA_AUTH_USER }}
JIRA_AUTH_API_TOKEN: ${{ secrets.JIRA_AUTH_API_TOKEN }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
RELEASE_VERSION: '' # Set by "Extract release version" step
RELEASE_TICKET_KEY: ${{ inputs.release_ticket_key }}
SLACK_RELEASE_CHANNEL_ID: ${{ vars.SLACK_RELEASE_CHANNEL_ID }}
BRANCH_NAME: ${{ github.ref_name }}
REPO_NAME: ${{ github.event.repository.name }}
steps:
- name: Validate branch is a release branch
run: |
if ! printf '%s' "$BRANCH_NAME" | grep -qE '^release/v[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "This workflow must be dispatched from a release/vX.Y.Z branch. Got: '$BRANCH_NAME'"
exit 1
fi
- name: Checkout code
uses: actions/checkout@v6
with:
token: ${{ env.BOT_GH_TOKEN }}
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'yarn'
- name: Install dependencies and build
run: |
yarn install --immutable
yarn build
- name: Extract release version
run: |
pip3 install requests
git config --global user.email "sha.sdk_deployment@sendbird.com"
git config --global user.name "sendbird-sdk-deployment"
git clone --depth 1 --branch main https://${{ env.BOT_GH_TOKEN }}@github.com/sendbird/sdk-deployment.git ~/sdk-deployment
release_version=$(python3 ~/sdk-deployment/scripts/v1.2/extract_version.py --branch_name "$BRANCH_NAME")
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
- name: Validate CHANGELOG_DRAFT.md is fresh
run: |
export VERSION="$RELEASE_VERSION"
node scripts/check-changelog-draft.js
- name: Finalize CHANGELOG.md date on release branch
run: |
DATE=$(date -u '+%b %d %Y' | tr '[:lower:]' '[:upper:]')
export VERSION="$RELEASE_VERSION"
export DATE
node scripts/update-changelog.js
if git diff --quiet CHANGELOG.md; then
echo "CHANGELOG.md date already up to date; skipping commit."
exit 0
fi
git config user.email "sha.sdk_deployment@sendbird.com"
git config user.name "sendbird-sdk-deployment"
git add CHANGELOG.md
git commit -m "chore: finalize CHANGELOG.md date for v$RELEASE_VERSION"
git push origin "$BRANCH_NAME"
- name: Wait for required checks on release branch
timeout-minutes: 30
env:
GH_TOKEN: ${{ env.BOT_GH_TOKEN }}
run: |
# Give GitHub a moment to register the workflow run for the finalize commit
# before watching, otherwise --watch may see no checks and exit 0 immediately.
sleep 15
gh pr checks "$BRANCH_NAME" --watch --required
- name: Merge release branch to main
run: |
python3 ~/sdk-deployment/scripts/v1.2/github_pr_merge.py \
--github_api_token "$BOT_GH_TOKEN" \
--repo_name "$REPO_NAME" \
--branch_name "$BRANCH_NAME"
git fetch origin main
git switch main
git pull origin main
- name: Rebuild after merge
run: |
yarn install --immutable
yarn build
- name: Publish to npm
run: |
cd ./dist
npm publish --access=public --provenance
- name: Verify npm package publication
run: |
cd ./dist
PACKAGE_NAME=$(node -p "require('./package.json').name")
PACKAGE_VERSION=$(node -p "require('./package.json').version")
max_attempts=6
attempt=1
while [ $attempt -le $max_attempts ]; do
if npm view --registry=https://registry.npmjs.org/ "$PACKAGE_NAME@$PACKAGE_VERSION" version > /dev/null 2>&1; then
echo "Package $PACKAGE_NAME@$PACKAGE_VERSION successfully published to npm"
exit 0
fi
echo "Attempt $attempt/$max_attempts: Package not visible yet. Waiting 20s..."
sleep 20
attempt=$((attempt + 1))
done
echo "Package $PACKAGE_NAME@$PACKAGE_VERSION not found on npm registry after $max_attempts attempts"
exit 1
- name: Create git tag
run: |
git tag "v${{ env.RELEASE_VERSION }}"
git push origin "v${{ env.RELEASE_VERSION }}"
- name: Create GitHub Release
run: |
python3 ~/sdk-deployment/scripts/v1.2/create_github_release.py \
--github_api_token "$BOT_GH_TOKEN" \
--repo_name "$REPO_NAME" \
--tag_name "v${{ env.RELEASE_VERSION }}" \
--target_commitish main \
--body_path "$GITHUB_WORKSPACE/CHANGELOG_DRAFT.md"
- name: Slack notification
if: ${{ success() }}
continue-on-error: true
uses: slackapi/slack-github-action@v1.24.0
env:
SLACK_BOT_TOKEN: ${{ env.SLACK_BOT_TOKEN }}
with:
channel-id: ${{ env.SLACK_RELEASE_CHANNEL_ID }}
payload: |
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":tada: <https://github.com/${{ github.repository }}/releases/tag/v${{ env.RELEASE_VERSION }}|@sendbird/uikit-react ${{ env.RELEASE_VERSION }}> has been released!"
}
}
]
}
- name: Jira - Transition to RELEASED
if: ${{ success() }}
run: |
python3 ~/sdk-deployment/scripts/v1.2/transit_ticket.py \
--jira_auth_user "$JIRA_AUTH_USER" \
--jira_auth_api_token "$JIRA_AUTH_API_TOKEN" \
--issue_key "$RELEASE_TICKET_KEY" \
--from_issue_state RELEASING \
--to_issue_state RELEASED
- name: Jira - Rollback on failure
if: ${{ failure() }}
run: |
if ! python3 -c "import requests" 2>/dev/null; then
echo "Installing requests (not yet available)..."
pip3 install requests
fi
if [ ! -d ~/sdk-deployment ]; then
echo "Cloning sdk-deployment (not yet available)..."
git clone --depth 1 --branch main https://${{ env.BOT_GH_TOKEN }}@github.com/sendbird/sdk-deployment.git ~/sdk-deployment
fi
python3 ~/sdk-deployment/scripts/v1.2/transit_ticket.py \
--jira_auth_user "$JIRA_AUTH_USER" \
--jira_auth_api_token "$JIRA_AUTH_API_TOKEN" \
--issue_key "$RELEASE_TICKET_KEY" \
--from_issue_state RELEASING \
--to_issue_state CONDITIONAL_RELEASE_APPROVED