Publish latest builds #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Publish latest builds | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version to publish (e.g., 1.2.3)' | |
| required: true | |
| type: string | |
| permissions: | |
| id-token: write | |
| contents: write | |
| jobs: | |
| publish: | |
| name: Publish latest builds | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: '10.30.3' | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| registry-url: https://registry.npmjs.org | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Validate version input | |
| run: | | |
| if ! [[ "${{ github.event.inputs.version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "❌ Version must be in semantic version format (e.g., 1.2.3)" | |
| exit 1 | |
| fi | |
| - name: Update package versions | |
| run: | | |
| target_version="${{ github.event.inputs.version }}" | |
| echo "Setting version to: $target_version" | |
| for dir in packages/*; do | |
| [ -f "$dir/package.json" ] || continue | |
| echo "Updating $dir/package.json..." | |
| node -e " | |
| const fs = require('fs'); | |
| const path = './$dir/package.json'; | |
| const pkg = require(path); | |
| pkg.version = '$target_version'; | |
| fs.writeFileSync(path, JSON.stringify(pkg, null, 2)); | |
| console.log('Updated ' + pkg.name + ' to version ' + pkg.version); | |
| " | |
| done | |
| - name: Build packages | |
| run: pnpm dlx turbo build --filter='./packages/*' | |
| - name: Publish packages | |
| run: | | |
| PACKAGES=( | |
| "commandkit:packages/commandkit" | |
| "create-commandkit:packages/create-commandkit" | |
| "@commandkit/redis:packages/redis" | |
| "@commandkit/i18n:packages/i18n" | |
| "@commandkit/devtools:packages/devtools" | |
| "@commandkit/cache:packages/cache" | |
| "@commandkit/analytics:packages/analytics" | |
| "@commandkit/ai:packages/ai" | |
| "@commandkit/queue:packages/queue" | |
| "@commandkit/tasks:packages/tasks" | |
| "@commandkit/workflow:packages/workflow" | |
| ) | |
| for entry in "${PACKAGES[@]}"; do | |
| IFS=":" read -r name path <<< "$entry" | |
| echo "Publishing $name..." | |
| VERSION=$(node -p "require('./$path/package.json').version") | |
| if npm view "$name@$VERSION" version >/dev/null 2>&1; then | |
| echo "📦 $name@$VERSION already exists on npm, skipping..." | |
| else | |
| if pnpm --filter="$name" publish --no-git-checks --access public; then | |
| echo "✅ Published $name@$VERSION" | |
| else | |
| if npm view "$name@$VERSION" version >/dev/null 2>&1; then | |
| echo "📦 $name@$VERSION was published by another process, skipping..." | |
| else | |
| echo "❌ Failed to publish $name@$VERSION" | |
| exit 1 | |
| fi | |
| fi | |
| fi | |
| done | |
| - name: Generate changelog | |
| id: changelog | |
| run: | | |
| VERSION="v${{ github.event.inputs.version }}" | |
| # Find the previous version tag | |
| PREV_TAG=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1) | |
| if [ -z "$PREV_TAG" ]; then | |
| COMMIT_RANGE="HEAD" | |
| echo "No previous tag found, using all commits" | |
| else | |
| COMMIT_RANGE="${PREV_TAG}..HEAD" | |
| echo "Generating changelog from $PREV_TAG to HEAD" | |
| fi | |
| PACKAGES=( | |
| "commandkit:packages/commandkit" | |
| "create-commandkit:packages/create-commandkit" | |
| "@commandkit/redis:packages/redis" | |
| "@commandkit/i18n:packages/i18n" | |
| "@commandkit/devtools:packages/devtools" | |
| "@commandkit/cache:packages/cache" | |
| "@commandkit/analytics:packages/analytics" | |
| "@commandkit/ai:packages/ai" | |
| "@commandkit/queue:packages/queue" | |
| "@commandkit/tasks:packages/tasks" | |
| "@commandkit/workflow:packages/workflow" | |
| ) | |
| CHANGELOG="" | |
| for entry in "${PACKAGES[@]}"; do | |
| IFS=":" read -r name path <<< "$entry" | |
| COMMITS=$(git log "$COMMIT_RANGE" --pretty=format:"%s|%h" -- "$path" 2>/dev/null || true) | |
| [ -z "$COMMITS" ] && continue | |
| FEATS="" | |
| FIXES="" | |
| CHORES="" | |
| DOCS="" | |
| REFACTORS="" | |
| PERFS="" | |
| TESTS="" | |
| OTHERS="" | |
| while IFS='|' read -r msg hash; do | |
| line="- ${msg} (\`${hash}\`)" | |
| case "$msg" in | |
| feat:*|feat\(*) FEATS+=$'\n'"$line" ;; | |
| fix:*|fix\(*) FIXES+=$'\n'"$line" ;; | |
| chore:*|chore\(*) CHORES+=$'\n'"$line" ;; | |
| docs:*|docs\(*) DOCS+=$'\n'"$line" ;; | |
| refactor:*|refactor\(*) REFACTORS+=$'\n'"$line" ;; | |
| perf:*|perf\(*) PERFS+=$'\n'"$line" ;; | |
| test:*|test\(*|tests:*|tests\(*) TESTS+=$'\n'"$line" ;; | |
| *) OTHERS+=$'\n'"$line" ;; | |
| esac | |
| done <<< "$COMMITS" | |
| PKG_SECTION="## ${name}"$'\n' | |
| HAS_CONTENT=false | |
| for category_label in \ | |
| "🚀 Features|$FEATS" \ | |
| "🐛 Bug Fixes|$FIXES" \ | |
| "🧹 Chores|$CHORES" \ | |
| "📖 Documentation|$DOCS" \ | |
| "♻️ Refactors|$REFACTORS" \ | |
| "⚡ Performance|$PERFS" \ | |
| "🧪 Tests|$TESTS" \ | |
| "📦 Other Changes|$OTHERS"; do | |
| IFS='|' read -r label content <<< "$category_label" | |
| if [ -n "$content" ]; then | |
| PKG_SECTION+=$'\n'"### ${label}"$'\n'"${content}"$'\n' | |
| HAS_CONTENT=true | |
| fi | |
| done | |
| if [ "$HAS_CONTENT" = true ]; then | |
| CHANGELOG+=$'\n'"${PKG_SECTION}"$'\n' | |
| fi | |
| done | |
| if [ -z "$CHANGELOG" ]; then | |
| CHANGELOG="No package changes detected since the last release." | |
| fi | |
| # Write to file to avoid shell escaping issues | |
| echo "$CHANGELOG" > /tmp/changelog.md | |
| - name: Create GitHub draft release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: v${{ github.event.inputs.version }} | |
| name: v${{ github.event.inputs.version }} | |
| body_path: /tmp/changelog.md | |
| draft: true | |
| generate_release_notes: false |