Skip to content

⚡ Bolt: Optimize starter icons resolution to avoid large map allocation #813

⚡ Bolt: Optimize starter icons resolution to avoid large map allocation

⚡ Bolt: Optimize starter icons resolution to avoid large map allocation #813

Workflow file for this run

name: Release - Publish
on:
# Triggers when Version Packages PR is merged
pull_request:
branches: [main]
types: [closed]
# Manual trigger for retrying failed publishes
workflow_dispatch:
permissions:
contents: write
id-token: write
env:
NODE_VERSION: "22"
NPM_VERSION: "11.18.0"
jobs:
publish:
name: Publish to npm
runs-on: ubuntu-latest
environment: npm-publish
# Only run when a Version PR is merged, or on manual trigger
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event.pull_request.merged == true &&
startsWith(github.event.pull_request.title, 'chore(release)'))
steps:
- uses: actions/checkout@v7
- name: Install pnpm
uses: pnpm/action-setup@v6
with:
version: 9
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: 'https://registry.npmjs.org'
cache: pnpm
- name: Upgrade npm for OIDC
run: npm install -g npm@${{ env.NPM_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build all packages
run: |
cd packages/icons && npm run build && cd ../..
cd packages/thesvg && node scripts/build-reexports.mjs && cd ../..
cd packages/cli && npm run build && cd ../..
cd packages/mcp && npm run build && cd ../..
cd packages/react && npm run build && cd ../..
cd packages/vue && npm run build && cd ../..
cd packages/svelte && npm run build && cd ../..
- name: Publish to npm
# pnpm changeset publish ships README.md inside the tarball but does
# not populate the per-version `readme` field on the npm registry
# manifest, which is what npmjs.com renders. To get both correct
# readme rendering AND pnpm's workspace-protocol rewriting, we
# `pnpm pack` each package (which rewrites `workspace:*` -> real
# versions) and then `npm publish <tarball>` (which populates the
# readme field on the registry manifest).
run: |
for PKG_DIR in packages/*/; do
PKG_DIR="${PKG_DIR%/}"
[ -f "$PKG_DIR/package.json" ] || continue
PKG_NAME=$(node -p "require('./$PKG_DIR/package.json').name")
PKG_VERSION=$(node -p "require('./$PKG_DIR/package.json').version")
PKG_PRIVATE=$(node -p "require('./$PKG_DIR/package.json').private || false")
if [ "$PKG_PRIVATE" = "true" ]; then
echo "Skipping $PKG_NAME (private)"
continue
fi
if npm view "$PKG_NAME@$PKG_VERSION" version >/dev/null 2>&1; then
echo "Skipping $PKG_NAME@$PKG_VERSION (already published)"
continue
fi
echo "Packing and publishing $PKG_NAME@$PKG_VERSION"
(
cd "$PKG_DIR"
# pnpm pack rewrites workspace:* deps to concrete versions
TARBALL=$(pnpm pack 2>/dev/null | tail -1)
# npm publish populates the readme field on the registry manifest
npm publish "$TARBALL" --access public --provenance
rm -f "$TARBALL"
)
done
- name: Create git tags
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Tag each package with its current version if not already tagged.
#
# The runner's checkout doesn't always fetch every historical
# tag, so a local-only `git rev-parse` check would create a
# duplicate tag and the subsequent push would be rejected by
# the remote. Check both local and remote, and pull the tag
# down if it already lives upstream - the downstream
# "Create GitHub releases per package" step uses local
# `git rev-parse` and needs the tag to exist in this clone.
for PKG_DIR in packages/*/; do
PKG_DIR="${PKG_DIR%/}"
[ -f "$PKG_DIR/package.json" ] || continue
PKG_NAME=$(node -p "require('./$PKG_DIR/package.json').name")
PKG_VERSION=$(node -p "require('./$PKG_DIR/package.json').version")
TAG="$PKG_NAME@$PKG_VERSION"
if git rev-parse "refs/tags/$TAG" >/dev/null 2>&1; then
echo "Tag already present locally: $TAG"
elif git ls-remote --tags --exit-code origin "refs/tags/$TAG" >/dev/null 2>&1; then
echo "Tag already present on remote: $TAG (fetching)"
# Use `tag` form rather than a refspec - scoped names contain
# `@` which trips the `<src>:<dst>` parser.
git fetch origin tag "$TAG"
else
echo "Creating tag: $TAG"
git tag "$TAG"
git push origin "$TAG"
fi
done
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create combined GitHub release
# One GitHub release per version cycle, tagged as thesvg@{version}.
# All packages are listed in a single release body rather than
# flooding the releases page with per-package noise.
# The @thesvg/cli release is ALSO created separately so the
# homebrew-tap-update workflow (which listens for @thesvg/cli@* releases)
# still fires correctly.
run: |
extract_section() {
local file="$1" version="$2"
awk -v ver="## ${version}" '
$0 == ver { capture = 1; next }
capture && /^## / { exit }
capture { print }
' "$file"
}
CANONICAL_VERSION=$(node -p "require('./packages/thesvg/package.json').version")
CANONICAL_TAG="thesvg@${CANONICAL_VERSION}"
if gh release view "$CANONICAL_TAG" >/dev/null 2>&1; then
echo "Combined release $CANONICAL_TAG already exists, skipping"
else
BODY_FILE="$(mktemp)"
{
cat "${GITHUB_WORKSPACE}/.github/release-body-header.md"
echo ""
echo "## Packages in this release"
echo ""
for PKG_DIR in packages/*/; do
PKG_DIR="${PKG_DIR%/}"
[ -f "$PKG_DIR/package.json" ] || continue
PKG_NAME=$(node -p "require('./$PKG_DIR/package.json').name")
PKG_VERSION=$(node -p "require('./$PKG_DIR/package.json').version")
PKG_PRIVATE=$(node -p "require('./$PKG_DIR/package.json').private || false")
[ "$PKG_PRIVATE" = "true" ] && continue
echo "- \`$PKG_NAME@$PKG_VERSION\`"
done
echo ""
echo "## What changed"
echo ""
for PKG_DIR in packages/*/; do
PKG_DIR="${PKG_DIR%/}"
[ -f "$PKG_DIR/package.json" ] || continue
PKG_NAME=$(node -p "require('./$PKG_DIR/package.json').name")
PKG_VERSION=$(node -p "require('./$PKG_DIR/package.json').version")
PKG_PRIVATE=$(node -p "require('./$PKG_DIR/package.json').private || false")
[ "$PKG_PRIVATE" = "true" ] && continue
CHANGELOG="$PKG_DIR/CHANGELOG.md"
SECTION=""
[ -f "$CHANGELOG" ] && SECTION=$(extract_section "$CHANGELOG" "$PKG_VERSION")
[ -z "$SECTION" ] && continue
echo "### $PKG_NAME"
echo ""
echo "$SECTION"
echo ""
done
echo "---"
echo ""
echo "## Install"
echo ""
echo '```sh'
for PKG_DIR in packages/*/; do
PKG_DIR="${PKG_DIR%/}"
[ -f "$PKG_DIR/package.json" ] || continue
PKG_NAME=$(node -p "require('./$PKG_DIR/package.json').name")
PKG_VERSION=$(node -p "require('./$PKG_DIR/package.json').version")
PKG_PRIVATE=$(node -p "require('./$PKG_DIR/package.json').private || false")
[ "$PKG_PRIVATE" = "true" ] && continue
[ "$PKG_NAME" = "@thesvg/cli" ] && continue
echo "npm i $PKG_NAME@$PKG_VERSION"
done
echo '```'
} > "$BODY_FILE"
# Use the canonical thesvg tag if it exists, else fall back to any valid tag
RELEASE_TAG="$CANONICAL_TAG"
if ! git rev-parse "refs/tags/$RELEASE_TAG" >/dev/null 2>&1; then
# Tag not created yet in this run - use first available package tag
for PKG_DIR in packages/*/; do
PKG_DIR="${PKG_DIR%/}"
[ -f "$PKG_DIR/package.json" ] || continue
PKG_NAME=$(node -p "require('./$PKG_DIR/package.json').name")
PKG_VERSION=$(node -p "require('./$PKG_DIR/package.json').version")
CANDIDATE="$PKG_NAME@$PKG_VERSION"
if git rev-parse "refs/tags/$CANDIDATE" >/dev/null 2>&1; then
RELEASE_TAG="$CANDIDATE"
break
fi
done
fi
if gh release create "$CANONICAL_TAG" \
--title "thesvg v${CANONICAL_VERSION}" \
--notes-file "$BODY_FILE" \
--verify-tag 2>/dev/null || \
gh release create "$CANONICAL_TAG" \
--title "thesvg v${CANONICAL_VERSION}" \
--notes-file "$BODY_FILE"; then
echo "Created combined release $CANONICAL_TAG"
else
echo "::error::Failed to create combined release $CANONICAL_TAG"
fi
rm -f "$BODY_FILE"
fi
# Also create the @thesvg/cli release separately so homebrew-tap-update fires.
CLI_VERSION=$(node -p "require('./packages/cli/package.json').version")
CLI_TAG="@thesvg/cli@${CLI_VERSION}"
if git rev-parse "refs/tags/$CLI_TAG" >/dev/null 2>&1 && \
! gh release view "$CLI_TAG" >/dev/null 2>&1; then
gh release create "$CLI_TAG" \
--title "@thesvg/cli $CLI_VERSION" \
--notes "See the combined release at https://github.com/glincker/thesvg/releases/tag/thesvg%40${CANONICAL_VERSION} for full changelog." \
--verify-tag || true
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}