name: Publish on: push: tags: - 'v*' workflow_dispatch: concurrency: group: publish-${{ github.ref }} cancel-in-progress: false # `id-token: write` is what lets npm exchange a GitHub OIDC token for a # short-lived publish credential (npm trusted publishing, no long-lived token). permissions: contents: read packages: write id-token: write jobs: publish: name: Test and publish runs-on: ubuntu-latest env: NPM_CONFIG_FUND: 'false' NPM_CONFIG_AUDIT: 'false' steps: - name: Checkout uses: actions/checkout@v6 - name: Setup pnpm uses: pnpm/action-setup@v4 with: version: 10 - name: Setup Node uses: actions/setup-node@v6 with: node-version: '24' registry-url: 'https://registry.npmjs.org' # Release builds must not reuse a package-manager cache. package-manager-cache: false - name: Verify npm CLI supports trusted publishing shell: bash run: | set -euo pipefail NPM_VERSION="$(npm --version)" echo "npm ${NPM_VERSION}" node -e ' const [major, minor] = process.argv[1].split(".").map(Number); if (major < 11 || (major === 11 && minor < 5)) { console.error(`npm >= 11.5.1 is required for trusted publishing, found ${process.argv[1]}`); process.exit(1); } ' "${NPM_VERSION}" - name: Install dependencies run: pnpm install --frozen-lockfile - name: Run tests run: pnpm test - name: Resolve version and owner shell: bash run: | set -euo pipefail VERSION="$(node -p "require('./package.json').version")" OWNER="$(echo "${GITHUB_REPOSITORY_OWNER}" | tr '[:upper:]' '[:lower:]')" if [ "${GITHUB_REF_TYPE}" = "tag" ]; then TAG="${GITHUB_REF_NAME#v}" if [ "${VERSION}" != "${TAG}" ]; then echo "::error::package.json version ${VERSION} does not match tag ${GITHUB_REF_NAME}" exit 1 fi fi echo "VERSION=${VERSION}" >> "${GITHUB_ENV}" echo "OWNER=${OWNER}" >> "${GITHUB_ENV}" - name: Pack npm tarball shell: bash run: | set -euo pipefail mkdir -p dist npm pack --pack-destination dist --json > dist/pack.json TGZ="$(node -p "const j=require('./dist/pack.json'); (Array.isArray(j)?j[0]:j[Object.keys(j)[0]]).filename")" echo "NPM_TGZ=dist/${TGZ}" >> "${GITHUB_ENV}" echo "Packed ${TGZ}" - name: Publish to npmjs (trusted publishing) shell: bash env: # setup-node injects NODE_AUTH_TOKEN; clear it so npm must use OIDC. NODE_AUTH_TOKEN: '' NPM_CONFIG_USERCONFIG: ${{ github.workspace }}/.npmrc-oidc run: | set -euo pipefail # Give npm a clean user config with no credentials at all. : > "${NPM_CONFIG_USERCONFIG}" echo "registry=https://registry.npmjs.org/" >> "${NPM_CONFIG_USERCONFIG}" if [ -n "${NODE_AUTH_TOKEN:-}" ]; then echo "::error::NODE_AUTH_TOKEN must not be set; npm trusted publishing uses OIDC." exit 1 fi if [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then echo "::error::No OIDC token endpoint. Grant 'id-token: write' to this workflow." exit 1 fi echo "Publishing dsh-adapter-qq@${VERSION} with npm $(npm --version) via OIDC" if npm view "dsh-adapter-qq@${VERSION}" version --registry=https://registry.npmjs.org/ >/dev/null 2>&1; then echo "dsh-adapter-qq@${VERSION} already exists on npmjs; skipping." echo "### npmjs: version already exists" >> "${GITHUB_STEP_SUMMARY}" exit 0 fi # A bare path is parsed as a git shorthand by npm; use ./ npm publish "./${NPM_TGZ}" \ --access public \ --registry=https://registry.npmjs.org/ echo "### ✅ Published dsh-adapter-qq@${VERSION} to npmjs" >> "${GITHUB_STEP_SUMMARY}" - name: Prepare GitHub Packages copy run: node .github/scripts/prepare-gh-package.mjs - name: Pack GitHub Packages tarball shell: bash run: | set -euo pipefail mkdir -p dist-gh npm pack --pack-destination dist-gh --json > dist-gh/pack.json TGZ="$(node -p "const j=require('./dist-gh/pack.json'); (Array.isArray(j)?j[0]:j[Object.keys(j)[0]]).filename")" echo "GH_TGZ=dist-gh/${TGZ}" >> "${GITHUB_ENV}" echo "Packed ${TGZ}" - name: Configure GitHub Packages auth shell: bash env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail # The npmjs step pointed NPM_CONFIG_USERCONFIG at a scratch file; give # GitHub Packages its own config so OIDC and token auth never mix. { echo "registry=https://npm.pkg.github.com/" echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" echo "@${OWNER}:registry=https://npm.pkg.github.com/" } > "${GITHUB_WORKSPACE}/.npmrc-ghpkg" - name: Publish to GitHub Packages shell: bash env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_CONFIG_USERCONFIG: ${{ github.workspace }}/.npmrc-ghpkg run: | set -euo pipefail PKG="@${OWNER}/dsh-adapter-qq" if [ -z "${NODE_AUTH_TOKEN:-}" ]; then echo "::error::GITHUB_TOKEN is required for GitHub Packages." exit 1 fi TGZ="./${GH_TGZ}" if [ ! -f "${TGZ}" ]; then echo "::error::tarball not found at ${TGZ}" exit 1 fi if npm view "${PKG}@${VERSION}" version --registry=https://npm.pkg.github.com/ >/dev/null 2>&1; then echo "${PKG}@${VERSION} already exists on GitHub Packages; skipping." exit 0 fi npm publish "${TGZ}" \ --registry=https://npm.pkg.github.com/ echo "### ✅ Published ${PKG}@${VERSION} to GitHub Packages" >> "${GITHUB_STEP_SUMMARY}"