name: Publish Package to npmjs

on:
  push:
    branches:
      - main # 当推送到 main 分支时触发

concurrency:
  group: npm-publish-${{ github.ref }}
  cancel-in-progress: true

permissions:
  contents: read
  id-token: write

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20.17.0
        
      - name: Determine publish version
        id: version
        run: |
          node <<'NODE'
          const fs = require('fs');
          const { execSync } = require('child_process');

          const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
          const name = pkg.name;
          const current = pkg.version;

          const parse = (v) => v.split('.').map((n) => parseInt(n, 10));
          const cmp = (a, b) => {
            for (let i = 0; i < 3; i += 1) {
              if (a[i] > b[i]) return 1;
              if (a[i] < b[i]) return -1;
            }
            return 0;
          };

          let currentPublished = false;
          try {
            execSync(`npm view ${name}@${current} version`, { stdio: 'pipe' });
            currentPublished = true;
          } catch {
            currentPublished = false;
          }

          let next = current;
          if (currentPublished) {
            let latest = current;
            try {
              latest = execSync(`npm view ${name} version`, { encoding: 'utf8' }).trim();
            } catch {
              latest = current;
            }

            const base = cmp(parse(latest), parse(current)) > 0 ? latest : current;
            const [major, minor, patch] = parse(base);
            next = [major, minor, patch + 1].join('.');
          }
          fs.appendFileSync(process.env.GITHUB_OUTPUT, `next=${next}\n`);
          NODE

      - name: Set publish version
        run: npm version ${{ steps.version.outputs.next }} --no-git-tag-version

      - name: Ensure npm supports OIDC
        run: npm install -g npm@latest

      - name: OIDC diagnostics
        run: |
          if [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ]; then
            echo "OIDC env vars missing; ensure permissions include id-token: write." >&2
            exit 1
          fi
          token_json="$(curl -sSf -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
            "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=npm")"
          token="$(node -e "console.log(JSON.parse(process.argv[1]).value)" "$token_json")"
          OIDC_TOKEN="$token" node <<'NODE'
          const token = process.env.OIDC_TOKEN || '';
          const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64url').toString('utf8'));
          const keys = [
            'iss',
            'aud',
            'sub',
            'repository',
            'repository_owner',
            'job_workflow_ref',
            'ref',
            'workflow',
          ];
          console.log('OIDC claims:');
          for (const key of keys) {
            if (payload[key]) console.log(`${key}: ${payload[key]}`);
          }
          NODE

      - name: Ensure no npm token env vars
        run: |
          if [ -n "${NPM_TOKEN:-}" ] || [ -n "${NODE_AUTH_TOKEN:-}" ]; then
            echo "NPM_TOKEN/NODE_AUTH_TOKEN detected; remove tokens for OIDC publishing." >&2
            exit 1
          fi

      - name: Prepare clean npm config for OIDC
        run: |
          printf '%s\n' "registry=https://registry.npmjs.org/" > "${RUNNER_TEMP}/npmrc"

      - name: Remove npm token config
        run: npm config delete //registry.npmjs.org/:_authToken || true

      - name: Publish to npm (OIDC)
        env:
          NPM_CONFIG_USERCONFIG: ${{ runner.temp }}/npmrc
        run: npm publish --provenance
