Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 22 additions & 17 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,10 @@ jobs:
permissions:
contents: read

# angular/.npmrc maps the @dignite and @dignite-projects scopes to GitHub Packages and
# references ${PACKAGES_READ_TOKEN} for it. Yarn Classic substitutes every env-var placeholder
# in its resolved config on every invocation and throws when one is unset - including the
# `yarn cache dir` probe setup-node runs for `cache: yarn`, which happens before any run: step
# of this job. So it has to be job-level, not set on the install step - exactly as release.yml
# already does.
#
# @dignite/ng.flex-fields (and its -ckeditor/-file-explorer adapters, and @dignite/ng.file-
# explorer) started resolving through those scopes as of 10.0.0-rc.16 - release.yml's copy of
# this comment carries the full account (why, the alias mechanism, and what to revert once it's
# no longer needed); angular/.npmrc carries the reason BOTH scopes have to be mapped, which is
# the non-obvious half and the one this job actually died on twice.
env:
PACKAGES_READ_TOKEN: ${{ secrets.PACKAGES_READ_TOKEN }}
# No job-level token env here on purpose: angular/.npmrc deliberately carries no ${...}
# placeholder, because one would make every yarn invocation in that directory fail hard when the
# variable is unset (see that file's own comment). The credential is written to ~/.npmrc by the
# step below instead.

steps:
- name: Checkout site
Expand Down Expand Up @@ -116,6 +106,20 @@ jobs:
cache: yarn
cache-dependency-path: site/angular/yarn.lock

# @dignite/* resolves from abp-modules' private GitHub Packages as of 10.0.0-rc.16, so the
# install below needs a credential for it. It goes in ~/.npmrc rather than angular/.npmrc, and
# as its own step rather than a job-level env, for the reason that file's comment gives: a
# ${...} placeholder in the committed .npmrc breaks every yarn command run in that directory
# whenever the variable is missing, which is most of the time on a developer's machine.
#
# PACKAGES_READ_TOKEN (a read:packages PAT) rather than secrets.GITHUB_TOKEN, matching what
# release.yml's NuGet verification already does - GITHUB_TOKEN's package access is documented
# as scoped to the workflow's own repository, and these packages belong to abp-modules.
- name: Authenticate to GitHub Packages
env:
PACKAGES_READ_TOKEN: ${{ secrets.PACKAGES_READ_TOKEN }}
run: printf '//npm.pkg.github.com/:_authToken=%s\n' "$PACKAGES_READ_TOKEN" >> ~/.npmrc

# Yarn with the committed lockfile, matching release.yml and every developer machine, so the
# tree verified here is the one the lockfile actually describes rather than a fresh resolution
# that ignores it. --frozen-lockfile additionally fails if a package.json change was committed
Expand All @@ -125,9 +129,10 @@ jobs:
# lockfile entry carried a GitHub Packages "resolved" URL through a code path that never
# attached registry auth. That was fixed and both workflows ran the same yarn command against
# public-npmjs-only @dignite/* packages for a while - but as of 10.0.0-rc.16, @dignite/*
# aliases back onto GitHub Packages (see the job-level env: block's comment above), so this
# repeats the original gotcha's shape if that auth ever regresses. release.yml's step carries
# the longer account.
# aliases back onto GitHub Packages, and the alias reopened that same wound from a new angle:
# yarn picks a tarball's registry (hence its token) by the ALIAS's scope, so angular/.npmrc has
# to map @dignite as well as @dignite-projects or yarn sends no credential at all. That one
# cost two red CI runs; angular/.npmrc carries the full account.
#
# It matters for the duplicate check below specifically: npm dedupes graphs that Yarn Classic
# splits into nested copies, so running that check after an npm install would pass vacuously.
Expand Down
57 changes: 29 additions & 28 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,35 +40,30 @@ jobs:
contents: write # Required by softprops/action-gh-release to create a draft Release
packages: write # Required to push pre-release NuGet/npm packages to GitHub Packages

# angular/.npmrc references ${PACKAGES_READ_TOKEN} to authenticate the @dignite-projects scope
# against GitHub Packages. As of 10.0.0-rc.16, angular/package.json aliases @dignite/ng.flex-fields
# (and its -ckeditor/-file-explorer adapters, and @dignite/ng.file-explorer) to
# "npm:@dignite-projects/<name>@<range>" (see the "Install Angular dependencies" step below for
# why), which is what actually routes those four through this scope/token - GitHub Packages'
# npm registry requires the scope to equal the owning org login, so @dignite itself has no
# registry entry there at all. setup-node's `cache: yarn` reads angular/.npmrc (to prime/verify
# the cache) before any later step gets a chance to export the token itself, so it has to be a
# job-level env, not just set on that step. (ci.yml carries the same env: block for the same
# reason; this comment is the one canonical copy of the reasoning - keep them in sync.)
# As of 10.0.0-rc.16, angular/package.json aliases @dignite/ng.flex-fields (and its
# -ckeditor/-file-explorer adapters, and @dignite/ng.file-explorer) to
# "npm:@dignite-projects/<name>@<range>" - GitHub Packages' npm registry requires the scope to
# equal the owning org login, so @dignite has no registry entry there at all. angular/.npmrc maps
# both scopes (the @dignite one is needed because yarn picks a tarball's registry, and hence its
# auth token, by the ALIAS's scope) and deliberately holds no token placeholder; the credential
# is written to ~/.npmrc by the "Authenticate to GitHub Packages" step below, in this workflow
# and in ci.yml alike. angular/.npmrc carries the full account of both decisions.
#
# PACKAGES_READ_TOKEN (a PAT scoped to read:packages only), not secrets.GITHUB_TOKEN - the same
# choice "Verify packed NuGet packages restore cleanly" below already made, for the same
# documented reason: GITHUB_TOKEN's package access is scoped to the workflow's own repository,
# and these packages are published by a different one (abp-modules). Whether GITHUB_TOKEN would
# in fact be refused here has never been isolated - the CI 401s that led to this token were a
# different bug entirely (yarn was sending no token at all; see angular/.npmrc's own comment) -
# so this is the documented-behavior choice plus the NuGet side's precedent, not a measurement.
# If someone wants the ephemeral token back, that experiment is one commit and one CI run.
# in fact be refused has never been isolated - the CI 401s that led here were a different bug
# entirely (yarn was sending no token at all) - so this is the documented-behavior choice plus
# the NuGet side's precedent, not a measurement.
#
# To revert once abp-modules tags a release that mirrors flex-fields (rc.16 or a successor) to
# public npmjs again: drop the four npm: aliases in angular/package.json's dependencies and
# resolutions blocks back to plain semver ranges; change check-angular-package-duplicates.mjs's
# target list in both this file and ci.yml from @dignite-projects back to @dignite. Leave
# PACKAGES_READ_TOKEN itself and angular/.npmrc's @dignite-projects mapping in place either way -
# they're what "Verify packed NuGet packages restore cleanly" already depends on permanently, and
# cost nothing unused.
env:
PACKAGES_READ_TOKEN: ${{ secrets.PACKAGES_READ_TOKEN }}
# resolutions blocks back to plain semver ranges; drop the @dignite mapping from angular/.npmrc
# (the @dignite-projects one stays - this workflow publishes under it); change
# check-angular-package-duplicates.mjs's target list in both this file and ci.yml from
# @dignite-projects back to @dignite. That also gives developers their tokenless local
# `yarn install` back, which is the part with the ongoing cost.

steps:
- name: Checkout site
Expand Down Expand Up @@ -240,18 +235,24 @@ jobs:
cache: yarn
cache-dependency-path: site/angular/yarn.lock

# angular/.npmrc maps the @dignite-projects scope to GitHub Packages; ${GITHUB_TOKEN} in that
# file is a literal env-var placeholder npm/yarn substitute at read time, so it has to be
# present in the environment here (it is, job-level - see the env: block above).
#
# Same credential handling as ci.yml, for the reason angular/.npmrc explains: no token
# placeholder in the committed file, so it goes to ~/.npmrc here instead. This has to run
# before the install below, and before "Configure npm registry for publishing" further down
# redirects npm's user config elsewhere.
- name: Authenticate to GitHub Packages
env:
PACKAGES_READ_TOKEN: ${{ secrets.PACKAGES_READ_TOKEN }}
run: printf '//npm.pkg.github.com/:_authToken=%s\n' "$PACKAGES_READ_TOKEN" >> ~/.npmrc

# Yarn with the committed lockfile, matching ci.yml and every developer machine. This step
# used to be `npm install --no-package-lock` because Yarn Classic fetches a dependency whose
# lockfile entry carries a GitHub Packages "resolved" URL through a code path that never
# attaches registry auth. That was fixed, and for a while every @dignite/* package resolved
# from public npmjs instead - but as of 10.0.0-rc.16, @dignite/* aliases back onto GitHub
# Packages (see the job-level env: block's comment), so this step is exercising that auth
# path again. --legacy-peer-deps is gone with npm: Yarn Classic does not enforce peer ranges,
# which is what that flag was working around (@swimlane/ngx-datatable@22 wants
# Packages, and the alias reopened the same wound from a new angle (yarn keys a tarball's
# registry, hence its token, off the ALIAS's scope), which is why angular/.npmrc now maps
# @dignite too. --legacy-peer-deps is gone with npm: Yarn Classic does not enforce peer
# ranges, which is what that flag was working around (@swimlane/ngx-datatable@22 wants
# @angular/common 18-20.x, workspace is on 21.2.x).
#
# It also matters for the duplicate check below: npm dedupes graphs that Yarn Classic splits
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`ci.yml`'s briefly-added `packages: read` permission is gone again, and no "Manage Actions access"
grant turned out to be required either.

**The credential itself is no longer named in the committed `angular/.npmrc`.** It briefly was, as
`${PACKAGES_READ_TOKEN}`, and that broke local development outright: any `${...}` placeholder in a
committed `.npmrc` makes *every* yarn invocation in that directory fail with `Failed to replace env
in config` when the variable is unset - including `yarn start`, `yarn build` and `yarn test`, none
of which touch this registry. The file now carries only the two scope mappings, and both CI
workflows write the token to `~/.npmrc` in an "Authenticate to GitHub Packages" step instead.
Developers do the same thing once, by hand - the file documents the command. Note a plain
`repo`-scoped token is not sufficient; this needs `read:packages`.

The `//npm.pkg.github.com/:always-auth=true` line is gone too: with both scopes mapped, yarn
already attaches the token to the tarball download without it, and npm 11 warns on every
invocation that the per-registry form is unknown config. Verified by cold-cache installs with and
without it - the fix was always the `@dignite` scope mapping, never `always-auth`.

**`release.yml`'s "Verify packed npm package installs and bundles cleanly" (`packed` mode) step
would otherwise have failed on the next actual release attempt** - a real, verified break, not a
hypothetical one: it installs the *raw* packed `dist/site/package.json`, which still names its
Expand Down
20 changes: 18 additions & 2 deletions angular/.npmrc
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,21 @@
# a package under the @dignite scope, which would 404.)
@dignite:registry=https://npm.pkg.github.com

//npm.pkg.github.com/:_authToken=${PACKAGES_READ_TOKEN}
//npm.pkg.github.com/:always-auth=true
# Deliberately NO _authToken line here. Any ${ENV_VAR} placeholder in a committed .npmrc makes
# EVERY yarn invocation in this directory die with "Failed to replace env in config" the moment that
# variable is unset - including `yarn start`, `yarn build` and `yarn test`, which never touch this
# registry at all. Credentials belong in the user-level ~/.npmrc instead, where they also stay out
# of git.
#
# One-time local setup - needs a token with the read:packages scope (a plain `repo`-scoped one is
# NOT enough; `gh auth token` is usually the easy source):
#
# printf '//npm.pkg.github.com/:_authToken=%s\n' "$(gh auth token)" >> ~/.npmrc
#
# CI writes the same line from the PACKAGES_READ_TOKEN secret - see the "Authenticate to GitHub
# Packages" step in ci.yml and release.yml.
#
# There is deliberately no always-auth line either: with both scopes mapped above, yarn already
# attaches the token to the tarball download, and the per-registry form makes npm 11 warn on every
# invocation that it is unknown config. Verified by a cold-cache `yarn install --frozen-lockfile`
# with and without it - the fix is the @dignite mapping, not always-auth.
Loading