Skip to content

Sync packages from monorepo (42d5bd0) #94

Sync packages from monorepo (42d5bd0)

Sync packages from monorepo (42d5bd0) #94

Workflow file for this run

name: Publish packages to npm
on:
push:
branches: [main]
workflow_dispatch:
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: actions/setup-node@v4
with:
node-version: "24"
- name: Install dependencies
run: corepack enable && YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn install
- name: Build publishable packages
run: |
set -e
for dir in packages/*/; do
if [ -f "$dir/package.json" ]; then
is_private=$(node -p "try { require('./$dir/package.json').private || false } catch(e) { false }")
has_build=$(node -p "try { !!require('./$dir/package.json').scripts?.build } catch(e) { false }")
if [ "$is_private" != "true" ] && [ "$has_build" = "true" ]; then
echo "🔨 Building $dir"
cd "$dir"
yarn build
cd -
fi
fi
done
- name: Publish changed packages
id: publish
run: |
failed=""
published=""
skipped=""
for dir in packages/*/; do
if [ ! -f "$dir/package.json" ]; then
continue
fi
name=$(node -p "require('./$dir/package.json').name")
# Skip private packages
is_private=$(node -p "try { require('./$dir/package.json').private || false } catch(e) { false }")
if [ "$is_private" = "true" ]; then
skipped="$skipped $name"
continue
fi
# Check if any files changed in this package (skip on workflow_dispatch — publish all)
if [ "${{ github.event_name }}" = "push" ]; then
changed=$(git diff --name-only HEAD~1 -- "$dir" | head -1)
if [ -z "$changed" ]; then
skipped="$skipped $name"
continue
fi
fi
# Get the current version from npm, default to 0.0.0 if unpublished
current=$(npm view "$name" version 2>/dev/null || echo "0.0.0")
# Increment patch version
IFS='.' read -r major minor patch <<< "$current"
next_version="$major.$minor.$((patch + 1))"
echo "📦 Publishing $name@$next_version (was $current)"
cd "$dir"
# Clean package.json: set version, strip devDeps, resolve workspace:/catalog: refs
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
pkg.version = '$next_version';
delete pkg.devDependencies;
for (const depType of ['dependencies', 'peerDependencies']) {
if (!pkg[depType]) continue;
for (const [k, v] of Object.entries(pkg[depType])) {
if (v.startsWith('workspace:') || v.startsWith('catalog:')) {
pkg[depType][k] = '*';
}
}
}
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
"
if npm publish --provenance --access public; then
published="$published $name@$next_version"
else
echo "⚠️ Failed to publish $name@$next_version"
failed="$failed $name@$next_version"
fi
cd -
done
# Write job summary
echo "## Publish summary" >> "$GITHUB_STEP_SUMMARY"
if [ -n "$published" ]; then
echo "### Published" >> "$GITHUB_STEP_SUMMARY"
for pkg in $published; do
echo "- $pkg" >> "$GITHUB_STEP_SUMMARY"
done
fi
if [ -n "$failed" ]; then
echo "### Failed" >> "$GITHUB_STEP_SUMMARY"
for pkg in $failed; do
echo "- $pkg" >> "$GITHUB_STEP_SUMMARY"
done
fi
if [ -n "$skipped" ]; then
echo "### Skipped" >> "$GITHUB_STEP_SUMMARY"
for pkg in $skipped; do
echo "- $pkg" >> "$GITHUB_STEP_SUMMARY"
done
fi
if [ -z "$published" ] && [ -z "$failed" ]; then
echo "No packages to publish." >> "$GITHUB_STEP_SUMMARY"
fi
if [ -n "$failed" ]; then
echo "publish_failed=true" >> "$GITHUB_OUTPUT"
echo "failed_packages=$failed" >> "$GITHUB_OUTPUT"
fi
- name: Notify Slack on publish failure
if: steps.publish.outputs.publish_failed == 'true'
uses: slackapi/slack-github-action@v2.1.0
with:
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
webhook-type: incoming-webhook
payload: |
{
"text": "npm publish failed for:${{ steps.publish.outputs.failed_packages }}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View run>"
}