Repository navigation
⚡ Bolt: Optimize starter icons resolution to avoid large map allocation #813
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: 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 }} |