diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ea0a90d..6abd426 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,11 @@ concurrency: jobs: ci: runs-on: ${{ matrix.os }} + # npm ci reads @plexinc packages, so do not rely on the repository + # default, which an organization can tighten without warning. + permissions: + contents: read + packages: read env: NODE_OPTIONS: "--max-old-space-size=4096" strategy: diff --git a/.github/workflows/xelp_npm_release.yml b/.github/workflows/xelp_npm_release.yml index 6e91c6f..03f8cf9 100644 --- a/.github/workflows/xelp_npm_release.yml +++ b/.github/workflows/xelp_npm_release.yml @@ -4,8 +4,10 @@ name: Xelp npm Release # @plexinc/, then tags the commit and creates a GitHub release. # # Authentication is the workflow's own GITHUB_TOKEN, so there is no secret to -# provision or rotate. Consumers authenticate the way every other Plex client -# repo does, with a personal access token carrying read:packages. +# provision or rotate. Reading a dependency that lives in another repository's +# package needs that repository granted under the package's Manage Actions +# access, not a different token. Consumers authenticate the way every other Plex +# client repo does, with a personal access token carrying read:packages. # # This replaces xelp_shadow_release.yml, which committed dist/ to the xelp/dist # branch and served the package to consumers through a git tag. Run one or the @@ -20,7 +22,7 @@ on: workflow_dispatch: inputs: dryRun: - description: Build and pack, but do not publish, tag, or release. + description: Build and report what would be packed, without publishing, tagging or releasing. type: boolean default: false @@ -107,6 +109,16 @@ jobs: exit 1 fi + # The publish happens before the tag, and a published version cannot be + # replaced, so a tag collision found at the tagging step would leave a + # published package that no re-run can tag. The shadow release used this + # same version scheme, so its tags are real candidates for a collision. + # + if git ls-remote --exit-code --tags origin "refs/tags/$VERSION" >/dev/null 2>&1; then + echo "::error::Tag $VERSION already exists. Land another commit, or wait for tomorrow's date stamp." + exit 1 + fi + # The upstream lockfile is what keeps the transitive dependencies on # working versions, so install from it rather than re-resolving. A clean # re-resolve floats vscode-languageserver-protocol onto an exports only @@ -114,12 +126,12 @@ jobs: # - name: Install dependencies env: - # Dependencies can live in another repo's package, and a repository's - # own GITHUB_TOKEN cannot read those, so prefer the organization token. - # The fallback keeps this working in a fork that has no @plexinc - # dependencies, where the repo token is enough. + # Reading a dependency published from another repository requires that + # repository to be listed under the package's Manage Actions access. + # The grant is per package and is not inherited, so a newly published + # @plexinc package needs its consumers added before this will resolve. # - NODE_AUTH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: npm ci - name: Build @@ -156,7 +168,7 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: npm publish - - name: Pack without publishing + - name: Report what would be packed if: ${{ inputs.dryRun }} run: npm pack --dry-run