From c1d9c1e8cbcf0102dc7b217ee81cf7ef73a2a732 Mon Sep 17 00:00:00 2001 From: Armando Navarro Date: Tue, 22 Sep 2026 17:02:12 -0700 Subject: [PATCH 1/2] fix(build): name canaries after the release, not after a prerelease The root version field held `21.0.0-rc.1`, so every merge to main published `21.0.0-rc.1-canary.`. Semver ranks that above `21.0.0-rc.1`, and the caret range `ng add` writes into a user's package.json then resolves to the canary instead of the release candidate they asked for. Releases through v20 avoided this by keeping the plain release number in the version field and publishing prereleases from their own git tag, which is what the publish job reads. This restores that: the field returns to `21.0.0`, and build.sh drops any prerelease part when naming a canary, so a prerelease written there again cannot produce a canary that outranks it. The schematics comment and its log line no longer say a prerelease range matches canary builds, which stops being true here. --- package-lock.json | 4 ++-- package.json | 2 +- sample/README.md | 4 ++-- sample/package.json | 2 +- src/schematics/common.ts | 8 ++++---- tools/build.sh | 10 +++++++++- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2cf0a922..d3d6f1d45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@angular/fire", - "version": "21.0.0-rc.1", + "version": "21.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@angular/fire", - "version": "21.0.0-rc.1", + "version": "21.0.0", "license": "MIT", "dependencies": { "@angular-devkit/architect": "~0.2102.0", diff --git a/package.json b/package.json index d5aa94e82..85b212f7f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@angular/fire", - "version": "21.0.0-rc.1", + "version": "21.0.0", "description": "Angular + Firebase = ❤️", "publishConfig": { "registry": "https://wombat-dressing-room.appspot.com", diff --git a/sample/README.md b/sample/README.md index 9af4abcb7..f8bf555d2 100644 --- a/sample/README.md +++ b/sample/README.md @@ -4,7 +4,7 @@ This project was generated using [Angular CLI](https://github.com/angular/angula ## AngularFire tarball -The sample consumes the library as `file:../angular-fire-21.0.0-rc.1.tgz`. Produce that file from the repository root before installing here: +The sample consumes the library as `file:../angular-fire-21.0.0.tgz`. Produce that file from the repository root before installing here: ```bash npm ci @@ -16,7 +16,7 @@ The root build ends with `npm pack ./dist/packages-dist`, which writes `angular- Then, from this `sample/` folder, install the dependencies by naming the tarball explicitly: ```bash -npm install ../angular-fire-21.0.0-rc.1.tgz +npm install ../angular-fire-21.0.0.tgz ``` A plain `npm install` is not reliable here. `package-lock.json` records the integrity hash of the tarball built by whoever committed the lockfile, and a tarball you build yourself can hash differently. With a cold npm cache the install then fails with an `EINTEGRITY` error. With a warm cache that holds the recorded hash, npm installs the stale cached copy and exits successfully, so you would be testing a library you did not build. Naming the tarball installs the file on disk and rewrites the recorded hash in your local `package-lock.json`. Leave that lockfile change uncommitted. diff --git a/sample/package.json b/sample/package.json index 7595c3ec7..1c5c1ad29 100644 --- a/sample/package.json +++ b/sample/package.json @@ -14,7 +14,7 @@ "@angular/common": "^21.0.0", "@angular/compiler": "^21.0.0", "@angular/core": "^21.0.0", - "@angular/fire": "file:../angular-fire-21.0.0-rc.1.tgz", + "@angular/fire": "file:../angular-fire-21.0.0.tgz", "@angular/forms": "^21.0.0", "@angular/platform-browser": "^21.0.0", "@angular/platform-server": "^21.0.0", diff --git a/src/schematics/common.ts b/src/schematics/common.ts index 50ffad745..4630bcfe9 100644 --- a/src/schematics/common.ts +++ b/src/schematics/common.ts @@ -146,9 +146,9 @@ const angularFireVersion = 'ANGULARFIRE2_VERSION'; /** * Pins the workspace's `@angular/fire` entry to the exact installed version when `ng add` wrote a - * prerelease range. A prerelease range like `^21.0.0-rc.0` also matches the canary build published - * for every merge to main, so a later fresh install can silently replace the version the user - * chose. Stable ranges are left untouched. + * prerelease range. A prerelease range like `^21.0.0-rc.0` also matches every later prerelease of + * the same release, so a later fresh install can silently replace the version the user chose. + * Stable ranges are left untouched. */ export const pinInstalledPrereleaseVersion = ( host: Tree, @@ -181,7 +181,7 @@ export const pinInstalledPrereleaseVersion = ( overwriteIfExists(host, 'package.json', stringifyFormatted(packageJson)); context.logger.info( `Pinned @angular/fire to the exact version ${installedVersion} — a prerelease range like ` + - `${declaredAngularFireVersion} also matches unreviewed canary builds, so a later install ` + + `${declaredAngularFireVersion} also matches later prereleases, so a later install ` + 'could silently change versions.' ); } diff --git a/tools/build.sh b/tools/build.sh index f92330a27..ad31edac0 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -10,7 +10,15 @@ if [[ $GITHUB_REF =~ $TAG_TEST ]]; then NPM_TAG=next fi; else - OVERRIDE_VERSION=$(node -e "console.log(require('./package.json').version)")-canary.$SHORT_SHA + FULL_VERSION=$(node -e "console.log(require('./package.json').version)") + # Name the canary after the release itself, never after a prerelease of it. A canary built on + # `21.0.0-rc.1` sorts above it, so the caret range `ng add` writes into a user's package.json + # resolves to the canary rather than to the release candidate they asked for. + BASE_VERSION=${FULL_VERSION%%-*} + if [[ $BASE_VERSION != "$FULL_VERSION" ]]; then + echo "package.json version is $FULL_VERSION. Naming this canary after $BASE_VERSION instead, so it does not outrank $FULL_VERSION on npm. Prereleases are published from their own git tag, so this field is meant to hold a plain release number." >&2 + fi + OVERRIDE_VERSION=$BASE_VERSION-canary.$SHORT_SHA NPM_TAG=canary fi; From 433fe9f1c7edfac267d0363c935048fdf5907979 Mon Sep 17 00:00:00 2001 From: Armando Navarro Date: Tue, 22 Sep 2026 17:02:36 -0700 Subject: [PATCH 2/2] chore(sample): regenerate the lockfile against the 21.0.0 tarball The sample's lockfile still named `angular-fire-21.0.0-rc.0.tgz`, two releases behind its own package.json, so `npm ci` there failed on the mismatch. Regenerated by building the library and running `npm install ../angular-fire-21.0.0.tgz` in sample/. It also picks up the `^21.2.0` peer ranges the Angular 21.2 raise introduced. --- sample/package-lock.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sample/package-lock.json b/sample/package-lock.json index ffb64d141..759804ca9 100644 --- a/sample/package-lock.json +++ b/sample/package-lock.json @@ -11,7 +11,7 @@ "@angular/common": "^21.0.0", "@angular/compiler": "^21.0.0", "@angular/core": "^21.0.0", - "@angular/fire": "file:../angular-fire-21.0.0-rc.0.tgz", + "@angular/fire": "file:../angular-fire-21.0.0.tgz", "@angular/forms": "^21.0.0", "@angular/platform-browser": "^21.0.0", "@angular/platform-server": "^21.0.0", @@ -682,9 +682,9 @@ } }, "node_modules/@angular/fire": { - "version": "21.0.0-rc.0", - "resolved": "file:../angular-fire-21.0.0-rc.0.tgz", - "integrity": "sha512-jayr/zD9AM4HbD7z55lXUYByha1UST9PqX6yeNyc7a8GTfrTGofThhjorJylRBh+rIVt07VOepkXZaFkR7MKJA==", + "version": "21.0.0", + "resolved": "file:../angular-fire-21.0.0.tgz", + "integrity": "sha512-+gdJe4QgT3Qe5zkZmFP5NRenk2BbE9HUmpGmk2FaFOyPN/vNaU3LdbNUVKz1m2QXhHKxnwsnPPDKAaDzbkqFGA==", "license": "MIT", "dependencies": { "@angular-devkit/architect": ">= 0.2100.0 < 0.2200.0", @@ -697,10 +697,10 @@ "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/common": "^21.0.0", - "@angular/core": "^21.0.0", - "@angular/platform-browser": "^21.0.0", - "@angular/platform-server": "^21.0.0", + "@angular/common": "^21.2.0", + "@angular/core": "^21.2.0", + "@angular/platform-browser": "^21.2.0", + "@angular/platform-server": "^21.2.0", "firebase-tools": "^14.0.0 || ^15.0.0", "rxjs": "~7.8.0", "typescript": ">=5.8 <6.0"