multiavatar 1.0.13

Deterministic multicultural avatar generator — pure Rust port of multiavatar.js with SVG/PNG/JPEG/GIF/favicon/ICO/data-URI outputs; part library embedded in the binary, no runtime files
Documentation
# Release workflow(仅 [publish] 关键字或手动触发才执行):
#   · push 到 main:普通提交不执行任何 release 动作(不 bump、不打 tag、不出 Release);
#     提交信息含 "[publish]" 关键字时:bump 版本 + 打 tag + GitHub Release + 发布 crates.io + npm stage publish。
#   · 手动触发(workflow_dispatch):可指定 minor / major,并可分别勾选是否发布 crates.io / npm。
#   · npm 采用 stage 模式 + Trusted Publishing(OIDC,无需 NPM_TOKEN):
#     需先在 npmjs → Trusted Publishing 配置 GitHub Environment "npm"(owner=w916797724, repo=multiavatar-rust);
#     CI 只把版本推入暂存区(npm stage publish),正式发布需 maintainer(2FA)promote:
#     网页 https://www.npmjs.com/package/@w916797724/multiavatar → Staged releases → Approve
#     命令行 npm stage promote --package @w916797724/multiavatar --version <版本>
# 版本递增提交带 [skip ci],避免自身提交再次触发本 workflow。

name: Release

on:
  push:
    branches: [main]
    paths:
      - "src/**"
      - "Cargo.toml"
      - "Cargo.lock"
      - ".github/workflows/release.yml"
  workflow_dispatch:
    inputs:
      version_bump:
        description: "版本递增类型(手动触发时选择;push [publish] 触发时固定为 patch)"
        type: choice
        default: patch
        options:
          - patch
          - minor
          - major
      publish_crates:
        description: "发布 crates.io(需 secret CARGO_REGISTRY_TOKEN)"
        type: boolean
        default: true
      publish_npm:
        description: "npm stage publish(Trusted Publishing/OIDC,正式发布需手动 promote)"
        type: boolean
        default: true

permissions:
  contents: write
  id-token: write   # Trusted Publishing(OIDC)必需:npm 借此验证 workflow 身份

jobs:
  release:
    runs-on: ubuntu-24.04
    environment: npm   # 与 npmjs Trusted Publishing 配置的 GitHub Environment 同名
    # 门控:只有手动触发,或 push 的最近提交含 "[publish]" 才进入 release 流程
    if: |
      github.event_name == 'workflow_dispatch' ||
      (github.event_name == 'push' && contains(github.event.head_commit.message, '[publish]'))
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      # ---- 1. 递增版本号(纯 bash,无 heredoc/python,避免引号转义坑)----
      - name: Bump version
        id: next_version
        run: |
          KIND="${{ github.event.inputs.version_bump || 'patch' }}"
          [ -z "$KIND" ] && KIND=patch
          VER="$(grep -m1 '^version' Cargo.toml | cut -d '"' -f2)"
          IFS=. read -r MAJ MIN PAT <<< "$VER"
          if [ "$KIND" = "major" ]; then MAJ=$((MAJ+1)); MIN=0; PAT=0
          elif [ "$KIND" = "minor" ]; then MIN=$((MIN+1)); PAT=0
          else PAT=$((PAT+1)); fi
          NV="$MAJ.$MIN.$PAT"
          sed -i "s/^version = .*/version = \"$NV\"/" Cargo.toml
          # 同步 npm 包版本(单一来源:Cargo.toml)
          if [ -f wasm/package.json ]; then
            sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"$NV\"/" wasm/package.json
          fi
          cargo metadata --no-deps --format-version 1 >/dev/null
          echo "version=$NV" >> "$GITHUB_OUTPUT"

      # ---- 2. 打包校验 + 发布 crates.io / npm ----
      # 发布条件(三者缺一不可):
      #   ① 对应 secret(CARGO_REGISTRY_TOKEN)已配置(npm 走 Trusted Publishing/OIDC,无需 token);
      #   ② 手动触发(workflow_dispatch)且勾选对应开关;
      #      或 push 触发且最近提交信息含 "[publish]" 关键字(两个仓库同时发);
      #   ③ cargo package 校验通过(crates.io 步骤天然保证)。
      - name: Package verify
        run: cargo package --allow-dirty

      - name: Publish to crates.io
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        # --allow-dirty:版本号已由 Bump 修改但 Commit and tag 在全部发布成功后才执行(设计使然)
        run: cargo publish --registry crates-io --allow-dirty
        if: |
          env.CARGO_REGISTRY_TOKEN != '' &&
          (
            (github.event_name == 'workflow_dispatch' && github.event.inputs.publish_crates == 'true') ||
            (github.event_name == 'push' && contains(github.event.head_commit.message, '[publish]'))
          )

      # npm 包(stage 模式 + Trusted Publishing):wasm/package.json 版本已由 Bump 步骤同步。
      # 无需 NPM_TOKEN——npm 通过 GitHub OIDC(id-token: write + environment: npm)验证身份;
      # --provenance 附加来源证明。npm stage publish 是 npm 12+ 命令(CI 镜像 npm 10/11 没有),
      # 故用 npx npm@12 显式调用;prepublishOnly(node 同步脚本)幂等无害。
      - name: Stage npm package
        working-directory: wasm
        run: |
          npx -y npm@12 stage publish --registry https://registry.npmjs.org --provenance
        if: |
          (github.event_name == 'workflow_dispatch' && github.event.inputs.publish_npm == 'true') ||
          (github.event_name == 'push' && contains(github.event.head_commit.message, '[publish]'))

      # ---- 4. 构建可运行 demo 二进制并打 Release(附 .crate 源码包)----
      # 注意:不带 --locked——Cargo.lock 被 .gitignore 忽略,CI 检出时不存在
      - name: Build demo binary
        run: cargo build --release --example demo

      # ---- 5. 全部发布成功后:提交版本变更 + 打 tag([skip ci] 防循环)----
      # tag 只代表"已发布的版本":crates.io / npm stage 均成功后才落 tag;
      # 发布失败则工作区无 git 痕迹,重跑自动跳到下一版本号。
      - name: Commit and tag
        if: steps.next_version.outputs.version != ''
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add Cargo.toml
          git add wasm/package.json 2>/dev/null || true
          # Cargo.lock 被 .gitignore 忽略(库 crate 惯例,不随 checkout 存在):
          # 存在才 add(cargo metadata 会在本地生成它),不存在则跳过
          git add Cargo.lock 2>/dev/null || true
          git commit -m "release: v${{ steps.next_version.outputs.version }} [skip ci]"
          git tag "v${{ steps.next_version.outputs.version }}"
          git push origin HEAD:main
          git push origin "v${{ steps.next_version.outputs.version }}"

      # ---- 6. 打 GitHub Release(需 tag 已 push 到远端)----
      - name: Create GitHub Release
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          gh release create "v${{ steps.next_version.outputs.version }}" \
            --repo "${{ github.repository }}" \
            --title "v${{ steps.next_version.outputs.version }}" \
            --generate-notes \
            target/package/multiavatar-*.crate \
            target/release/examples/demo