Skip to content

Commit 9d1e0fe

Browse files
committed
chore: 🤖 add monorepo package release (initial, copied from a concurrent branch now deprecated)
1 parent 04de869 commit 9d1e0fe

1 file changed

Lines changed: 236 additions & 0 deletions

File tree

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
name: 🎯 Monorepo Package Publisher (NPM)
2+
3+
# TODO: This is a concurrent NPM publisher to
4+
# the original changeset based workflow for Click UI
5+
# which is much stricter with a very particular
6+
# release cycle: test or release-candidate -> stable -> latest
7+
# The "Monorepo Package Publisher" initial version is not strict
8+
# and relies on the user managing the changeset state
9+
# e.g.
10+
11+
on:
12+
workflow_dispatch:
13+
inputs:
14+
package:
15+
description: 'Package to release'
16+
required: true
17+
type: choice
18+
options:
19+
# NOTE: Declare the package names
20+
# that can be published to NPM registry
21+
- design-tokens
22+
release_type:
23+
description: 'Release type'
24+
required: true
25+
type: choice
26+
options:
27+
- test
28+
- rc
29+
- stable
30+
- latest
31+
default: 'test'
32+
confirm_package:
33+
description: 'Type the package name to confirm (e.g., "design-tokens")'
34+
required: true
35+
type: string
36+
confirm_branch:
37+
description: 'For stable releases: type the branch name (e.g., "main")'
38+
required: false
39+
type: string
40+
dry_run:
41+
description: 'Dry run (e.g., skip publish, github release, and slack notification)'
42+
required: true
43+
type: boolean
44+
default: true
45+
46+
concurrency: ${{ github.workflow }}-${{ github.ref }}
47+
48+
env:
49+
HUSKY: 0
50+
51+
jobs:
52+
monorepo-release-package:
53+
name: Monorepo Release Package
54+
runs-on: ubuntu-latest
55+
permissions:
56+
contents: write
57+
id-token: write
58+
steps:
59+
- name: Validate package confirmation
60+
run: |
61+
if [[ "${{ inputs.package }}" != "${{ inputs.confirm_package }}" ]]; then
62+
echo "👹 Oops! Package confirmation mismatch!"
63+
echo " Selected: '${{ inputs.package }}'"
64+
echo " Typed: '${{ inputs.confirm_package }}'"
65+
echo ""
66+
echo "đź’ˇ Please type the exact package name to confirm."
67+
exit 1
68+
fi
69+
echo "âś… Package confirmed: ${{ inputs.package }}"
70+
71+
- name: Validate branch for stable release
72+
if: inputs.release_type == 'stable'
73+
run: |
74+
CURRENT_BRANCH="${{ github.ref_name }}"
75+
CONFIRM_BRANCH="${{ inputs.confirm_branch }}"
76+
77+
if [[ -z "$CONFIRM_BRANCH" ]]; then
78+
echo "👹 Oops! Branch confirmation required for stable releases!"
79+
echo " Current branch: '$CURRENT_BRANCH'"
80+
echo ""
81+
echo "đź’ˇ Please type the branch name in 'confirm_branch' to proceed."
82+
exit 1
83+
fi
84+
85+
if [[ "$CURRENT_BRANCH" != "$CONFIRM_BRANCH" ]]; then
86+
echo "👹 Oops! Branch confirmation mismatch!"
87+
echo " Current branch: '$CURRENT_BRANCH'"
88+
echo " Typed: '$CONFIRM_BRANCH'"
89+
echo ""
90+
echo "đź’ˇ Please type the exact branch name to confirm."
91+
exit 1
92+
fi
93+
94+
if [[ "$CURRENT_BRANCH" != "main" ]]; then
95+
echo "👹 Oops! Stable releases must be from 'main' branch!"
96+
echo " Current branch: '$CURRENT_BRANCH'"
97+
exit 1
98+
fi
99+
100+
echo "âś… Branch confirmed: $CURRENT_BRANCH"
101+
102+
- name: Generate GitHub workflow token
103+
if: ${{ !inputs.dry_run }}
104+
id: gh-workflow-token
105+
uses: actions/create-github-app-token@v2
106+
with:
107+
app-id: ${{ secrets.WORKFLOW_AUTH_PUBLIC_APP_ID }}
108+
private-key: ${{ secrets.WORKFLOW_AUTH_PUBLIC_PRIVATE_KEY }}
109+
110+
- name: Checkout repository
111+
uses: actions/checkout@v6
112+
with:
113+
fetch-depth: 0
114+
token: ${{ secrets.GITHUB_TOKEN }}
115+
116+
- name: Setup Node.js
117+
uses: actions/setup-node@v6
118+
with:
119+
node-version: '23.x'
120+
registry-url: 'https://registry.npmjs.org'
121+
122+
- name: Upgrade npm for OIDC support
123+
run: |
124+
npm install -g npm@latest
125+
echo "npm version: $(npm --version)"
126+
127+
- name: Enable Corepack
128+
run: corepack enable
129+
130+
- name: Install dependencies
131+
run: yarn install --frozen-lockfile
132+
133+
- name: Load package configuration
134+
id: package-config
135+
run: |
136+
PKG_DIR="packages/${{ inputs.package }}"
137+
138+
if [[ ! -f "$PKG_DIR/package.json" ]]; then
139+
echo "👹 Oops! No package.json found at $PKG_DIR"
140+
exit 1
141+
fi
142+
143+
PKG_NAME=$(node -p "require('./$PKG_DIR/package.json').name")
144+
echo "package_name=$PKG_NAME" >> $GITHUB_OUTPUT
145+
echo "package_path=$PKG_DIR" >> $GITHUB_OUTPUT
146+
echo "changelog_file=CHANGELOG.md" >> $GITHUB_OUTPUT
147+
148+
echo "âś… Loaded config for $PKG_NAME ($PKG_DIR)"
149+
150+
- name: Build package
151+
working-directory: ${{ steps.package-config.outputs.package_path }}
152+
run: yarn build
153+
154+
- name: Get version from package.json
155+
id: package-version
156+
working-directory: ${{ steps.package-config.outputs.package_path }}
157+
run: |
158+
VERSION=$(node -p "require('./package.json').version")
159+
echo "version=$VERSION" >> $GITHUB_OUTPUT
160+
echo "📦 Package version: $VERSION"
161+
162+
- name: Check version on npm
163+
if: ${{ !inputs.dry_run }}
164+
run: |
165+
PKG_NAME="${{ steps.package-config.outputs.package_name }}"
166+
VERSION="${{ steps.package-version.outputs.version }}"
167+
168+
echo "🔍 Checking if $PKG_NAME@$VERSION already exists on NPM..."
169+
170+
if npm view "$PKG_NAME@$VERSION" version 2>/dev/null; then
171+
echo "👹 Oops! Version $VERSION of $PKG_NAME is already published on NPM!"
172+
echo "đź’ˇ Please bump the version via changesets and try again."
173+
exit 1
174+
fi
175+
176+
echo "âś… Version $VERSION is not yet published, safe to proceed."
177+
178+
- name: Publish to npm over OpenID Connect (OIDC)
179+
working-directory: ${{ steps.package-config.outputs.package_path }}
180+
run: |
181+
if [[ "${{ inputs.dry_run }}" == "true" ]]; then
182+
echo "🧪 Dry run mode — publishing with --dry-run"
183+
npm publish \
184+
--access public \
185+
--provenance \
186+
--tag ${{ inputs.release_type }} \
187+
--dry-run
188+
else
189+
npm publish \
190+
--access public \
191+
--provenance \
192+
--tag ${{ inputs.release_type }}
193+
fi
194+
195+
- name: Extract changelog
196+
if: ${{ !inputs.dry_run }}
197+
id: changelog
198+
run: |
199+
VERSION="${{ steps.package-version.outputs.version }}"
200+
PKG_PATH="${{ steps.package-config.outputs.package_path }}"
201+
CHANGELOG_FILE="${{ steps.package-config.outputs.changelog_file }}"
202+
CHANGELOG_PATH="${PKG_PATH}/${CHANGELOG_FILE}"
203+
204+
if [[ -f "$CHANGELOG_PATH" ]]; then
205+
CHANGELOG=$(awk "/## $VERSION/,/## [0-9]/" "$CHANGELOG_PATH" | sed '1d;$d' | sed '/^$/d')
206+
207+
if [[ -z "$CHANGELOG" ]]; then
208+
CHANGELOG="📝 See [CHANGELOG.md](./$CHANGELOG_FILE) for details."
209+
fi
210+
else
211+
CHANGELOG="No changelog available."
212+
fi
213+
214+
echo "$CHANGELOG" > /tmp/changelog.txt
215+
echo "đź“„ Extracted changelog"
216+
217+
- name: Create GitHub release
218+
if: ${{ !inputs.dry_run }}
219+
uses: softprops/action-gh-release@v1
220+
with:
221+
tag_name: ${{ steps.package-config.outputs.package_name }}@v${{ steps.package-version.outputs.version }}
222+
name: "${{ inputs.package }} v${{ steps.package-version.outputs.version }}"
223+
body_path: /tmp/changelog.txt
224+
prerelease: ${{ inputs.release_type != 'stable' }}
225+
env:
226+
GITHUB_TOKEN: ${{ steps.gh-workflow-token.outputs.token }}
227+
228+
- name: Notify Slack about new release
229+
if: ${{ !inputs.dry_run && secrets.SLACK_BOT_TOKEN != '' }}
230+
uses: slackapi/slack-github-action@v2.1.1
231+
with:
232+
method: chat.postMessage
233+
token: ${{ secrets.SLACK_BOT_TOKEN }}
234+
payload: |
235+
channel: "${{ secrets.SLACK_CHANNEL_FOR_GENERAL }}"
236+
text: "===\n🚀 *${{ steps.package-config.outputs.package_name }}* v${{ steps.package-version.outputs.version }} released to npm!\n\n📦 <https://www.npmjs.com/package/${{ steps.package-config.outputs.package_name }}/v/${{ steps.package-version.outputs.version }}|View on npm>\n🏷️ Tag: `${{ inputs.release_type }}`\n==="

0 commit comments

Comments
 (0)