Skip to content

Release kafka (#381) #16

Release kafka (#381)

Release kafka (#381) #16

Workflow file for this run

name: Publish to npm
on:
push:
branches:
- main
# Prevent overlapping releases if multiple PRs merge close together
concurrency:
group: release-main
cancel-in-progress: false
jobs:
detect-changes:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
outputs:
matrix: ${{ steps.finalize.outputs.matrix }}
has_changes: ${{ steps.finalize.outputs.has_changes }}
bump: ${{ steps.finalize.outputs.bump }}
should_publish: ${{ steps.finalize.outputs.should_publish }}
steps:
- name: Set default outputs
id: defaults
run: |
echo "matrix=[]" >> $GITHUB_OUTPUT
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "bump=patch" >> $GITHUB_OUTPUT
echo "should_publish=false" >> $GITHUB_OUTPUT
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check if should skip
id: skip-check
run: |
COMMIT_MSG=$(git log -1 --pretty=%B)
# Only skip our own version bump commits (exact match)
if echo "$COMMIT_MSG" | grep -qE '^chore: bump versions \[skip ci\]$'; then
echo "Skipping: this is a version bump commit"
echo "should_skip=true" >> $GITHUB_OUTPUT
else
echo "should_skip=false" >> $GITHUB_OUTPUT
fi
- name: Get PR info from merge commit
id: pr-info
if: steps.skip-check.outputs.should_skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
# Use GitHub API to find PR associated with this commit
# This works for both merge commits and squash merges
PR_DATA=$(gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${{ github.repository }}/commits/${{ github.sha }}/pulls" \
--jq '.[0] | {number, labels: [.labels[].name]}' 2>/dev/null || echo '{}')
PR_NUMBER=$(echo "$PR_DATA" | jq -r '.number // empty')
if [ -z "$PR_NUMBER" ]; then
echo "No PR found for this commit"
echo "should_publish=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "Found PR #$PR_NUMBER"
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
# Get labels from the API response
LABELS=$(echo "$PR_DATA" | jq -r '.labels | join(" ")')
echo "PR labels: $LABELS"
echo "labels=$LABELS" >> $GITHUB_OUTPUT
# Check if PR has version bump labels
if echo "$LABELS" | grep -qE '\b(patch|minor|major)\b'; then
echo "should_publish=true" >> $GITHUB_OUTPUT
else
echo "No version bump label found (patch/minor/major)"
echo "should_publish=false" >> $GITHUB_OUTPUT
fi
- name: Determine version bump
id: version
if: steps.skip-check.outputs.should_skip != 'true' && steps.pr-info.outputs.should_publish == 'true'
env:
LABELS: ${{ steps.pr-info.outputs.labels }}
run: |
if echo "$LABELS" | grep -qE '\bmajor\b'; then
echo "bump=major" >> $GITHUB_OUTPUT
echo "Version bump: major"
elif echo "$LABELS" | grep -qE '\bminor\b'; then
echo "bump=minor" >> $GITHUB_OUTPUT
echo "Version bump: minor"
else
echo "bump=patch" >> $GITHUB_OUTPUT
echo "Version bump: patch"
fi
- name: Debug commit info
if: steps.skip-check.outputs.should_skip != 'true' && steps.pr-info.outputs.should_publish == 'true'
run: |
echo "Commit SHA: ${{ github.sha }}"
echo "Previous SHA: ${{ github.event.before }}"
echo "PR Number: ${{ steps.pr-info.outputs.pr_number }}"
echo "Files changed:"
git diff --name-only ${{ github.event.before }}..${{ github.sha }} || echo "git diff failed"
- id: filter
if: steps.skip-check.outputs.should_skip != 'true' && steps.pr-info.outputs.should_publish == 'true'
uses: dorny/paths-filter@v3
with:
base: ${{ github.event.before }}
ref: ${{ github.sha }}
filters: |
pkg_core:
- 'packages/core/lib/**'
- 'packages/core/package.json'
- 'packages/core/README.md'
pkg_amqp:
- 'packages/amqp/lib/**'
- 'packages/amqp/package.json'
- 'packages/amqp/README.md'
pkg_sqs:
- 'packages/sqs/lib/**'
- 'packages/sqs/package.json'
- 'packages/sqs/README.md'
pkg_sns:
- 'packages/sns/lib/**'
- 'packages/sns/package.json'
- 'packages/sns/README.md'
pkg_kafka:
- 'packages/kafka/lib/**'
- 'packages/kafka/package.json'
- 'packages/kafka/README.md'
pkg_gcp_pubsub:
- 'packages/gcp-pubsub/lib/**'
- 'packages/gcp-pubsub/package.json'
- 'packages/gcp-pubsub/README.md'
pkg_gcs_payload_store:
- 'packages/gcs-payload-store/lib/**'
- 'packages/gcs-payload-store/package.json'
- 'packages/gcs-payload-store/README.md'
pkg_s3_payload_store:
- 'packages/s3-payload-store/lib/**'
- 'packages/s3-payload-store/package.json'
- 'packages/s3-payload-store/README.md'
pkg_metrics:
- 'packages/metrics/lib/**'
- 'packages/metrics/package.json'
- 'packages/metrics/README.md'
pkg_outbox_core:
- 'packages/outbox-core/lib/**'
- 'packages/outbox-core/package.json'
- 'packages/outbox-core/README.md'
pkg_redis_message_deduplication_store:
- 'packages/redis-message-deduplication-store/lib/**'
- 'packages/redis-message-deduplication-store/package.json'
- 'packages/redis-message-deduplication-store/README.md'
pkg_schemas:
- 'packages/schemas/lib/**'
- 'packages/schemas/package.json'
- 'packages/schemas/README.md'
- name: Build dynamic matrix
id: build-matrix
if: steps.skip-check.outputs.should_skip != 'true' && steps.pr-info.outputs.should_publish == 'true'
run: |
# Package mapping: filterKey -> name, npmName
declare -A PKG_NAMES=(
["pkg_core"]="core"
["pkg_amqp"]="amqp"
["pkg_sqs"]="sqs"
["pkg_sns"]="sns"
["pkg_kafka"]="kafka"
["pkg_gcp_pubsub"]="gcp-pubsub"
["pkg_gcs_payload_store"]="gcs-payload-store"
["pkg_s3_payload_store"]="s3-payload-store"
["pkg_metrics"]="metrics"
["pkg_outbox_core"]="outbox-core"
["pkg_redis_message_deduplication_store"]="redis-message-deduplication-store"
["pkg_schemas"]="schemas"
)
CHANGED='${{ steps.filter.outputs.changes }}'
echo "Changed filters: $CHANGED"
# Build matrix JSON array
MATRIX="["
FIRST=true
for key in "${!PKG_NAMES[@]}"; do
if echo "$CHANGED" | grep -q "\"$key\""; then
NAME="${PKG_NAMES[$key]}"
NPM_NAME="@message-queue-toolkit/${NAME}"
if [ "$FIRST" = true ]; then
FIRST=false
else
MATRIX+=","
fi
MATRIX+="{\"name\":\"${NAME}\",\"npmName\":\"${NPM_NAME}\"}"
fi
done
MATRIX+="]"
echo "Generated matrix: $MATRIX"
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
if [ "$MATRIX" = "[]" ]; then
echo "has_changes=false" >> $GITHUB_OUTPUT
else
echo "has_changes=true" >> $GITHUB_OUTPUT
fi
# Finalize outputs - consolidates all outputs with proper fallbacks
# Uses environment variables to preserve JSON quoting
- name: Finalize outputs
id: finalize
env:
BUILD_MATRIX: ${{ steps.build-matrix.outputs.matrix }}
BUILD_HAS_CHANGES: ${{ steps.build-matrix.outputs.has_changes }}
DEFAULT_MATRIX: ${{ steps.defaults.outputs.matrix }}
DEFAULT_HAS_CHANGES: ${{ steps.defaults.outputs.has_changes }}
VERSION_BUMP: ${{ steps.version.outputs.bump }}
DEFAULT_BUMP: ${{ steps.defaults.outputs.bump }}
PR_SHOULD_PUBLISH: ${{ steps.pr-info.outputs.should_publish }}
DEFAULT_SHOULD_PUBLISH: ${{ steps.defaults.outputs.should_publish }}
run: |
# Use build-matrix outputs if available, otherwise defaults
if [ -n "$BUILD_MATRIX" ]; then
echo "matrix=$BUILD_MATRIX" >> $GITHUB_OUTPUT
else
echo "matrix=$DEFAULT_MATRIX" >> $GITHUB_OUTPUT
fi
if [ -n "$BUILD_HAS_CHANGES" ]; then
echo "has_changes=$BUILD_HAS_CHANGES" >> $GITHUB_OUTPUT
else
echo "has_changes=$DEFAULT_HAS_CHANGES" >> $GITHUB_OUTPUT
fi
if [ -n "$VERSION_BUMP" ]; then
echo "bump=$VERSION_BUMP" >> $GITHUB_OUTPUT
else
echo "bump=$DEFAULT_BUMP" >> $GITHUB_OUTPUT
fi
if [ -n "$PR_SHOULD_PUBLISH" ]; then
echo "should_publish=$PR_SHOULD_PUBLISH" >> $GITHUB_OUTPUT
else
echo "should_publish=$DEFAULT_SHOULD_PUBLISH" >> $GITHUB_OUTPUT
fi
# Single job that bumps, publishes, and pushes tags/commits at the end
# This avoids the "checkout wrong commit" problem of multi-job workflows
release:
needs: detect-changes
if: needs.detect-changes.outputs.should_publish == 'true' && needs.detect-changes.outputs.has_changes == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- name: Checkout Repository
uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ github.token }}
persist-credentials: true
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24.x
registry-url: 'https://registry.npmjs.org'
package-manager-cache: false
- name: Install dependencies
run: npm install --ignore-scripts
- name: Bump versions for changed packages
id: bump
env:
MATRIX: ${{ needs.detect-changes.outputs.matrix }}
BUMP: ${{ needs.detect-changes.outputs.bump }}
run: |
echo "Packages to bump: $MATRIX"
echo "Version bump type: $BUMP"
# Collect version info for later steps
VERSIONS_JSON="{"
FIRST=true
# Parse matrix JSON and bump each package
for PKG_NAME in $(echo "$MATRIX" | jq -r '.[] | .name'); do
echo "Bumping version for $PKG_NAME..."
cd "packages/$PKG_NAME"
OLD_VERSION=$(node -p "require('./package.json').version")
npm version "$BUMP" --no-git-tag-version
NEW_VERSION=$(node -p "require('./package.json').version")
echo " $OLD_VERSION -> $NEW_VERSION"
if [ "$FIRST" = true ]; then
FIRST=false
else
VERSIONS_JSON+=","
fi
VERSIONS_JSON+="\"$PKG_NAME\":\"$NEW_VERSION\""
cd ../..
done
VERSIONS_JSON+="}"
echo "versions=$VERSIONS_JSON" >> $GITHUB_OUTPUT
- name: Build all packages
env:
MATRIX: ${{ needs.detect-changes.outputs.matrix }}
run: |
for NPM_NAME in $(echo "$MATRIX" | jq -r '.[] | .npmName'); do
echo "Building $NPM_NAME..."
npm run build -- --filter="$NPM_NAME"
done
- name: Publish packages to npm
id: publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
MATRIX: ${{ needs.detect-changes.outputs.matrix }}
run: |
PUBLISHED=""
SKIPPED=""
for PKG_NAME in $(echo "$MATRIX" | jq -r '.[] | .name'); do
cd "packages/$PKG_NAME"
# Read actual name and version from package.json for accuracy
NPM_NAME=$(node -p "require('./package.json').name")
VERSION=$(node -p "require('./package.json').version")
# Check if this version is already published (idempotent)
if npm view "$NPM_NAME@$VERSION" version >/dev/null 2>&1; then
echo "Skipping $NPM_NAME@$VERSION - already published"
SKIPPED+="$NPM_NAME@$VERSION "
else
echo "Publishing $NPM_NAME@$VERSION..."
npm publish --provenance --access public
PUBLISHED+="$NPM_NAME@$VERSION "
fi
cd ../..
done
echo "published=$PUBLISHED" >> $GITHUB_OUTPUT
echo "skipped=$SKIPPED" >> $GITHUB_OUTPUT
- name: Commit version bumps
run: |
git add packages/*/package.json
if git diff --staged --quiet; then
echo "No version changes to commit"
else
git commit -m "chore: bump versions [skip ci]"
fi
- name: Create tags for published packages
env:
MATRIX: ${{ needs.detect-changes.outputs.matrix }}
run: |
for PKG_NAME in $(echo "$MATRIX" | jq -r '.[] | .name'); do
# Read actual name from package.json for accurate tag
NPM_NAME=$(node -p "require('./packages/$PKG_NAME/package.json').name")
VERSION=$(node -p "require('./packages/$PKG_NAME/package.json').version")
TAG="${NPM_NAME}@${VERSION}"
# Check if tag already exists (could indicate re-run or version issue)
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Warning: Tag already exists: $TAG (skipping)"
else
echo "Creating tag: $TAG"
git tag "$TAG"
fi
done
- name: Push commits and tags
run: |
git push
git push --tags
- name: Summary
env:
PUBLISHED: ${{ steps.publish.outputs.published }}
SKIPPED: ${{ steps.publish.outputs.skipped }}
run: |
echo "## Release Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -n "$PUBLISHED" ]; then
echo "### Published packages" >> $GITHUB_STEP_SUMMARY
for PKG in $PUBLISHED; do
# URL-encode the @ symbol for npm links
NPM_URL_NAME=$(echo "$PKG" | sed 's/@/%40/g' | sed 's/@/%40/')
echo "- [$PKG](https://www.npmjs.com/package/${NPM_URL_NAME%@*})" >> $GITHUB_STEP_SUMMARY
done
echo "" >> $GITHUB_STEP_SUMMARY
fi
if [ -n "$SKIPPED" ]; then
echo "### Skipped (already published)" >> $GITHUB_STEP_SUMMARY
for PKG in $SKIPPED; do
echo "- $PKG" >> $GITHUB_STEP_SUMMARY
done
echo "" >> $GITHUB_STEP_SUMMARY
fi
if [ -z "$PUBLISHED" ] && [ -z "$SKIPPED" ]; then
echo "No packages were published or skipped." >> $GITHUB_STEP_SUMMARY
fi