Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
06a3a62
Implement
sf-tyler-jeong Apr 1, 2026
dc367f4
chore: migrate npm publish to OIDC trusted publishing
sf-tyler-jeong Apr 2, 2026
bc43850
fix warnings
sf-tyler-jeong Apr 2, 2026
eeae3ce
ci: add test step to release workflow before npm publish
sf-tyler-jeong Apr 2, 2026
79eb576
ci: add workflow for manual Node 24 build and test verification
sf-tyler-jeong Apr 2, 2026
0fd80e7
ci: update build-and-test workflow to Node 24 with latest actions
sf-tyler-jeong Apr 2, 2026
123b89a
fix: replace deprecated import assert with import with in rollup config
sf-tyler-jeong Apr 2, 2026
cd7aa3f
ci: handle unhandled rejection in test step for Node 24 compatibility
sf-tyler-jeong Apr 2, 2026
7edf6bb
test: mock global fetch in jest setup for Node 24 native fetch compat…
sf-tyler-jeong Apr 2, 2026
dd163ea
ci: change coverage workflow to manual trigger and update actions
sf-tyler-jeong Apr 2, 2026
23c388e
ci: add lint step and align test options with build-and-test workflow
sf-tyler-jeong Apr 2, 2026
f6bfeb4
fix: prevent shell injection in package-publish npm_tag input
sf-tyler-jeong Apr 2, 2026
13b9bf8
fix: resolve shell injection and env context warnings in package-publish
sf-tyler-jeong Apr 2, 2026
641124c
chore: fix missing comma and use single quotes in jest config
sf-tyler-jeong Apr 2, 2026
c860537
ci: switch coverage to manual trigger with original config as comments
sf-tyler-jeong Apr 2, 2026
21ee9f4
ci: add JUnit reporter, failed test retry script, and reorder release…
sf-tyler-jeong Apr 6, 2026
7ee3365
fix: use env vars instead of expression interpolation in release work…
sf-tyler-jeong Apr 7, 2026
bfdf72b
fix: correct python package check and add contents write permission i…
sf-tyler-jeong Apr 7, 2026
dbf4b11
fix: rebuild dist after merge and enable file attribute in jest-junit
sf-tyler-jeong Apr 16, 2026
1c5a2b7
ci: automate CHANGELOG.md updates and finalize release date
sf-tyler-jeong Apr 28, 2026
0641a48
ci: remove skip-template check from prepare-changelog
sf-tyler-jeong Apr 28, 2026
90de97a
ci: harden release workflows after code review
sf-tyler-jeong Apr 28, 2026
8734311
ci: add CHANGELOG_DRAFT.md freshness check to release workflow
sf-tyler-jeong Apr 28, 2026
25cd4d9
ci: detect template-only CHANGELOG_DRAFT.md as a release blocker
sf-tyler-jeong Apr 28, 2026
b752694
ci: validate release branch and wait for checks before merge
sf-tyler-jeong Apr 28, 2026
e3ad381
ci: sleep before gh pr checks --watch to avoid race
sf-tyler-jeong Apr 28, 2026
4859cfb
ci: migrate package-publish to OIDC trusted publishing
sf-tyler-jeong Apr 28, 2026
f42aa34
ci: unify permissions.contents to write across publish workflows
sf-tyler-jeong Apr 28, 2026
a1299d3
ci: fix Node version and repository URL for npm OIDC publish
sf-tyler-jeong Apr 28, 2026
5c54bd4
docs(ci): note npm Trusted Publisher swap for manual publish workflow
sf-tyler-jeong Apr 28, 2026
7658017
docs: remove obsolete RELEASE_GUIDE.md from public repo
sf-tyler-jeong Apr 28, 2026
63f0480
ci: enforce dispatch ref matches version input in package-publish
sf-tyler-jeong Apr 28, 2026
9525cdd
ci: use yarn install --immutable in package-publish
sf-tyler-jeong Apr 28, 2026
8c3d75f
ci: prevent prerelease publish to latest and orphan tag_suffix
sf-tyler-jeong Apr 28, 2026
6b93459
ci: tighten input validation to forbid prerelease in version and npm_…
sf-tyler-jeong Apr 28, 2026
a4b9d66
ci: align npm_tag/tag_suffix regex with semver prerelease identifier set
sf-tyler-jeong Apr 28, 2026
852cb3e
ci: enforce dot-separated semver prerelease form for npm_tag/tag_suffix
sf-tyler-jeong Apr 28, 2026
52f39e2
ci: tighten semver validation, empty-draft, and junit parser
sf-tyler-jeong Apr 28, 2026
4c50a76
ci: reject npm_tag starting with digit or v/V
sf-tyler-jeong Apr 28, 2026
01abdad
build: read package.json via createRequire for Node 18.0+ compat
sf-tyler-jeong Apr 28, 2026
90ca78b
docs: add v3.17.12 changelog entry for MessageInput zero-width space fix
sf-tyler-jeong May 13, 2026
ecce64d
Merge branch 'main' into feature/CLNP-8355
sf-tyler-jeong May 13, 2026
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
17 changes: 13 additions & 4 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
push:
branches:
- main
- release/**
concurrency:
group: build-and-test-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true
Expand All @@ -17,12 +18,12 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: '16.19.1'
node-version: '24'
cache: 'yarn'

- name: Install dependencies
Expand All @@ -43,4 +44,12 @@ jobs:
if: ${{ success() }}
timeout-minutes: 15
run: |
yarn test --forceExit --runInBand
yarn test --forceExit --runInBand || {
if [ -f "./test-results/junit-report.xml" ]; then
echo "Tests failed. Retrying failed tests..."
node scripts/failed-test-retry.js
else
echo "Tests failed and no JUnit report found."
exit 1
fi
}
23 changes: 15 additions & 8 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
name: Coverage Report

# Manual trigger only. To restore PR comment trigger, replace workflow_dispatch with:
# on:
# issue_comment:
# types: [created, edited]
# And add this condition to the job:
# if: github.event.issue.pull_request && contains(github.event.comment.body, './coverage')

on:
issue_comment:
types: [created, edited]
workflow_dispatch:

jobs:
coverage:
runs-on: ubuntu-latest
if: github.event.issue.pull_request && contains(github.event.comment.body, './coverage')
steps:
- uses: actions/checkout@v3
- uses: ArtiomTr/jest-coverage-report-action@v2
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: ArtiomTr/jest-coverage-report-action@v2
14 changes: 9 additions & 5 deletions .github/workflows/package-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ on:
jobs:
publish:
runs-on: ubuntu-latest
env:
SKIP_REST: '' # Set to 'true' when npm_tag is provided
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
token: ${{ secrets.SDK_GH_BOT1_TOKEN }}
fetch-depth: 0
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: 18.x
cache: 'yarn'
Expand Down Expand Up @@ -57,13 +59,15 @@ jobs:
yarn install
yarn build
- name: Publish to npm
env:
NPM_TAG: ${{ github.event.inputs.npm_tag }}
run: |
cd ./dist
echo "//registry.npmjs.org/:_authToken=${{ secrets.npm_token }}" > .npmrc
if [ -z "${{ github.event.inputs.npm_tag }}" ]; then
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc
if [ -z "$NPM_TAG" ]; then
npm publish --access=public
else
Comment thread
sf-tyler-jeong marked this conversation as resolved.
Comment thread
sf-tyler-jeong marked this conversation as resolved.
npm publish --tag ${{ github.event.inputs.npm_tag }} --access=public
npm publish --tag "$NPM_TAG" --access=public
echo "npm_tag is provided; Skipping the rest of the steps."
echo "SKIP_REST=true" >> $GITHUB_ENV
fi
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-comment-bot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
if : ${{ github.event.issue.pull_request}}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: sendbird/release-automation-action@latest
with:
gh_token: ${{ secrets.SDK_GH_BOT1_TOKEN }}
Expand Down
156 changes: 156 additions & 0 deletions .github/workflows/release-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
name: Release

on:
workflow_dispatch:
inputs:
release_ticket_key:
description: 'Release Ticket Key (e.g., UIKIT-1234)'
required: true
type: string

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: 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: 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
Comment thread
sf-tyler-jeong marked this conversation as resolved.
Comment on lines +100 to +112
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Rebuild dist after pulling main before npm publish

The workflow builds dist before merging/pulling main and then publishes without rebuilding, so the published tarball can come from the pre-merge release-branch contents rather than the commit that gets tagged/released on main. This mismatch is possible whenever main has moved or merge resolution changes files, and it can ship artifacts that do not match the Git tag/source of record.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge 후 yarn install --immutable && yarn build로 rebuild 스텝을 추가했습니다. 이제 publish되는 tarball이 main merge 후 코드와 일치합니다. (dbf4b11e)


- 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
5 changes: 5 additions & 0 deletions CHANGELOG_DRAFT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### Features
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

github에서 release tag 생성에 필요해서 추가 하신거 같은데, 최종 chagelog.md로 update는 언제 하게 되나요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prepare-changelog 워크플로우를 추가해서 자동화했습니다. release 브랜치 push 시 CHANGELOG_DRAFT.md 내용이 CHANGELOG.md에 자동으로 prepend되고, 실제 릴리즈 시점에 release-workflow가 날짜를 확정합니다. 개발자는 CHANGELOG_DRAFT.md만 작성하면 됩니다.
감사합니다!

- Added ...

### Fixes
- Fixed a bug where ...
8 changes: 8 additions & 0 deletions jest-setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,11 @@ class MockMediaRecorder {
global.MediaRecorder = MockMediaRecorder;

copyProps(window, global);

// Mock global fetch to prevent actual network requests in tests (Node 24+ has native fetch)
global.fetch = jest.fn(() => Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve({}),
text: () => Promise.resolve(''),
}));
25 changes: 16 additions & 9 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ module.exports = {

// An array of regexp pattern strings used to skip coverage collection
coveragePathIgnorePatterns: [
"/node_modules/",
"/_externals/",
"/stories/"
'/node_modules/',
'/_externals/',
'/stories/',
],

// A list of reporter names that Jest uses when writing coverage reports
Expand Down Expand Up @@ -82,8 +82,8 @@ module.exports = {

// A map from regular expressions to module names that allow to stub out resources with a single module
moduleNameMapper: {
"\\.(css|less|sass|scss)$": "<rootDir>/__mocks__/styleMock.js",
"\\.(gif|ttf|eot|svg)$": "<rootDir>/__mocks__/fileMock.js"
'\\.(css|less|sass|scss)$': '<rootDir>/__mocks__/styleMock.js',
'\\.(gif|ttf|eot|svg)$': '<rootDir>/__mocks__/fileMock.js',
},

// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
Expand All @@ -102,7 +102,14 @@ module.exports = {
// projects: undefined,

// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
reporters: [
'default',
['jest-junit', {
outputDirectory: './test-results',
outputName: 'junit-report.xml',
addFileAttribute: 'true',
}],
Comment on lines +107 to +111
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Configure jest-junit to emit file paths for retry logic

scripts/failed-test-retry.js only retries failures when each failed <testsuite> includes a file attribute, but the new reporter config does not enable that output. As a result, on failed test runs the retry step often finds zero failed files and exits, so the flaky-test retry path never actually works even though CI now depends on this report.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addFileAttribute: 'true' 추가하여 JUnit 리포트에 file 속성이 출력되도록 수정했습니다. 이제 failed-test-retry.js가 실패 테스트 파일을 정상적으로 파싱합니다. (dbf4b11e)

],

// Automatically reset mock state between every test
// resetMocks: false,
Expand Down Expand Up @@ -140,7 +147,7 @@ module.exports = {
// snapshotSerializers: [],

// The test environment that will be used for testing
testEnvironment: "node",
testEnvironment: 'node',

// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
Expand Down Expand Up @@ -179,8 +186,8 @@ module.exports = {

// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
transformIgnorePatterns: [
'/node_modules/(?!(?:@sendbird/(react-uikit-message-template-view|uikit-message-template))/)'
]
'/node_modules/(?!(?:@sendbird/(react-uikit-message-template-view|uikit-message-template))/)',
],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
"jest": "29.5.0",
"jest-environment-jsdom": "29.5.0",
"jest-extended": "^3.2.4",
"jest-junit": "^16.0.0",
"jsdom": "^20.0.0",
"plop": "^2.5.3",
"postcss": "^8.3.5",
Expand Down
2 changes: 1 addition & 1 deletion rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {visualizer} from "rollup-plugin-visualizer";
import ts2 from "rollup-plugin-typescript2"

// config from package.json
import pkg from "./package.json" assert {type: "json"};
import pkg from "./package.json" with {type: "json"};
import inputs from "./rollup.module-exports.mjs";
import { readFileSync, writeFileSync } from 'fs';
import postcssRTLOptions from "./postcssRtlOptions.mjs";
Expand Down
Loading