GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Backport ten fixes to 20.1.x for 20.1.1 by armando-navarro · Pull Request #3798 · angular/angularfire

Conversation

@armando-navarro

@armando-navarro armando-navarro commented Oct 9, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

This brings ten fixes from main to 20.1.x for the 20.1.1 release. Each commit is a git cherry-pick -x of the squash commit on main, in the order they merged there, plus one config line. Please merge with Rebase and merge, so each fix stays its own commit on 20.1.x.

Fixes for v20 users

  • fix(auth): stop beforeAuthStateChanged from holding the app unstable #3770: registering beforeAuthStateChanged from @angular/fire/auth keeps the app from ever becoming stable for a visitor who never signs in or out (beforeAuthStateChanged imported from @angular/fire/auth makes ng build fail during route extraction #3748).
  • fix(schematics): read the signed-in Firebase account on firebase-tools 15.26+ #3769: ng add stops with Invalid project id: myApp. for a project named with capital letters, and crashes on firebase-tools 15.26+ when it is not attached to a terminal.
  • fix(storage): give the compat fromTask an explicit return type #3796: the @angular/fire/compat/storage typings import firebase/compat, which fails with TS2307 under moduleResolution: "bundler" (@angular/build: TS2307: Cannot find module 'firebase/compat' #3677).
  • fix(core): drop the unused optional @angular/platform-server peer #3792: npm install fails with ERESOLVE on an app one Angular patch behind, because of the unused optional @angular/platform-server peer (@angular/cli v20. NPM cannot resolve @angular/fire #3667).
  • fix(deploy): prevent command injection from angular.json in ng deploy #3738, fix(deploy): pass gcloud arguments as an array instead of a joined string #3726, fix(deploy): prevent code-generation injection from angular.json values #3739 and fix(schematics): use a POSIX path for the Cloud Run main.js entry #3274, in the SSR ng deploy builder:
    • A workspace path containing a space breaks the Cloud Functions and Cloud Run deploys.
    • A failed gcloud step is ignored, and the hosting deploy still runs.
    • angular.json values reach a shell and the generated function code without escaping.
    • Deploying to Cloud Run from Windows writes the entry path with backslashes.

Release tooling

  • fix(ci): choose a release's dist-tag when it publishes, and move next after it #3794: a stable 20.x tag publishes to latest only while it ranks above every other release, and to v20-lts otherwise, so a 20.x release tagged after 21.0.0 cannot move latest back. fix(ci): keep the canary dist-tag from moving back to an older build #3784 is picked first so fix(ci): choose a release's dist-tag when it publishes, and move next after it #3794 applies cleanly.
  • tsconfig.jasmine.json gets the tools/**/*.jasmine.ts include from fix(build): name canaries above every published release of their major #3780, so fix(ci): choose a release's dist-tag when it publishes, and move next after it #3794's specs run on this branch.

Conflicts

All conflicts are import lines, version ranges or lines that exist only on one branch:

  • fix(deploy): prevent command injection from angular.json in ng deploy #3738: kept this branch's Angular 20 dev dependency ranges and fs-extra import, and added @types/cross-spawn and the cross-spawn import.
  • fix(deploy): prevent command injection from angular.json in ng deploy #3738, fix(deploy): pass gcloud arguments as an array instead of a joined string #3726 and fix(deploy): prevent code-generation injection from angular.json values #3739: the deploy spec keeps this branch's ./actions import specifier.
  • fix(schematics): read the signed-in Firebase account on firebase-tools 15.26+ #3769: added only the new getActiveAccount helper to firebaseTools.ts, and left the docs as they are on this branch.
  • fix(core): drop the unused optional @angular/platform-server peer #3792: kept the Angular 20 peer ranges and removed only the @angular/platform-server peer.
  • fix(ci): choose a release's dist-tag when it publishes, and move next after it #3794: tools/build.sh keeps this branch's canary version line, since canary-version.js (fix(build): name canaries above every published release of their major #3780) is not here, and drops the NPM_TAG and publish.sh lines the new publish job replaces.
  • fix(auth): stop beforeAuthStateChanged from holding the app unstable #3770: kept this branch's docs/auth.md, since its docs change edits a section of the guide that 20.1.x does not have.

Verification

./tools/build.sh, build:jasmine and test:node pass with 166 specs and 0 failures, up from 29 on 20.1.x. The Chrome suite gives the same result as on 20.1.x (108 passed, 48 skipped). The built schematics bundle cross-spawn, so the published package needs no new dependency, and the only manifest change is the removed peer.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
herdiyana256 and others added 10 commits October 9, 2026 14:07
@herdiyana256 @armando-navarro
fix(deploy): prevent command injection from angular.json in ng deploy (…
9e2bbcd
…angular#3738)

The SSR deploy builder shelled out with values read straight from
angular.json: the package manager and each server externalDependencies
entry reached `execSync` as a command string, as did the functions output
path. A malicious or cloned workspace could run arbitrary commands the
moment a developer ran `ng deploy`.

Both calls now go through a single runner built on cross-spawn, which
passes arguments as argv entries and launches the Windows .cmd shims that
child_process.execFile cannot, so the deploy keeps working cross-platform
without a shell. The package manager is checked against a supported set
and dependency names are rejected unless they are plain specifiers.

Tests cover the validators and assert the call sites route through the
shell-free runner, so reverting to a shell or dropping a validator fails
the suite.

(cherry picked from commit 0e8ed0c)
@herdiyana256 @armando-navarro
fix(deploy): pass gcloud arguments as an array instead of a joined st…
706a712
…ring (angular#3726)

spawnAsync built each gcloud command as one string and split it on
whitespace before calling spawn(), so any deploy option containing a
space was chopped into extra argv entries and a value from angular.json
could add flags of its own. It now takes command and args separately and
the three call sites pass arrays, removing the join and split entirely.

The close handler also rejected only on exit code 1, so a gcloud failure
with any other code, or a process killed by a signal, resolved as success
and a failed deploy reported as done. It now rejects on any non-zero or
null code.

The Cloud Run argument construction moved into two exported functions so
tests can assert the argv shape without mocking spawn, and functionName
and region gained schema patterns, since both also reach the generated
Cloud Functions source.

(cherry picked from commit 778625a)
@fr-esco @armando-navarro
fix(schematics): use a POSIX path for the Cloud Run main.js entry (an…
e9b74ba
…gular#3274)

Running ng deploy from Windows with the Cloud Run SSR option wrote the
generated run/package.json entry paths with backslashes, which the
Linux-based Cloud Run runtime cannot resolve. The manifest path is now
built with forward slashes explicitly, since the target runtime is
Linux regardless of the deploying machine.

Fixes angular#3098

Co-authored-by: Armando Navarro <[email protected]>
(cherry picked from commit 73acea7)
@herdiyana256 @armando-navarro
fix(deploy): prevent code-generation injection from angular.json valu…
9798896
…es (angular#3739)

The SSR deploy builders interpolate several angular.json values into generated
artifacts that are later executed: a server build target's outputPath into the
Cloud Function index.js and the Cloud Run package.json start script, functionName
into the exports assignment, region into the .region() call, and
functionsNodeVersion into the Cloud Run Dockerfile FROM line.

region is escaped structurally with JSON.stringify in the template, and the start
script now quotes its path, so a shell no longer splits or expands it. On top of
that, outputPath, functionName and functionsNodeVersion are screened before code
generation (assertSafeOutputPath, assertSafeFunctionName, assertSafeNodeVersion).

assertSafeOutputPath rejects only what is still live once the start script is
quoted: quotes, a backslash and line terminators, which break out of the
require() string literal, `$` and a backtick, which are still command
substitution inside double quotes, and a leading dash, which node reads as a
flag. functionName is only screened on the Functions path, where it becomes a
JavaScript identifier. functionsNodeVersion is screened against Docker's tag
grammar, since the value is a node image tag, with latest excluded because its
slim variant is published as node:slim.

The functionName and region schema patterns are not repeated here. Stricter
versions of both already landed on main in angular#3726. The functionsNodeVersion
schema pattern stays here and matches the runtime check exactly.

The TODO above the gcloud calls is restored in narrowed form, covering the values
that are still unvalidated: firebaseProject, vpcConnector, and the outputPath
deploy option.

(cherry picked from commit fb6796b)
@armando-navarro
fix(schematics): read the signed-in account on firebase-tools 15.26+ (a…
695045c
…ngular#3769)

firebase-tools 15.26 runs non-interactively when it detects an AI
agent or when stdin is not a terminal, and in that mode login()
returns undefined instead of the signed-in account. `ng add` and
`ng deploy` crashed reading `.email` from it.

getActiveAccount passes `interactive: true`, which returns the account
without a prompt once one is signed in. The `firebase login` and
`firebase login:add` commands setup starts get `--interactive` for the
same reason. Setup no longer passes its options to login(), since they
carry the Angular project name, which firebase-tools rejects as a
project id for names like `myApp`.

The Quickstart stops telling readers to install firebase-tools 14, and
says what a stopped run leaves behind.

Fixes angular#3768

The Quickstart changes are left out, since docs are not backported.
(cherry picked from commit 38dfc3f)
@armando-navarro
fix(ci): keep the canary dist-tag from moving back to an older build (a…
3c37773
…ngular#3784)

Every canary publish moved the `canary` dist-tag, whatever commit it
was built from. Two merges close together could publish out of order,
and a re-run of an older run could publish its build last.

The publish job now clones the repository's commit history and
publishes a canary only when its commit comes after the commit of the
canary on npm, or is the same commit under a new version. A build of
an earlier commit is skipped with a warning. Commits are compared
rather than versions, because a version can be higher for an older
commit.

Canary publishes also run one at a time, queued, so each check reads
what the previous publish left on npm. Release publishes are unchanged.

A scheduled run on an unchanged main now skips with a notice instead
of failing on the duplicate version.

Fixes angular#3783

(cherry picked from commit eee3486)
@armando-navarro
fix(core): drop the unused optional @angular/platform-server peer (an…
ac42ffa
…gular#3792)

npm 10, and npm 11 before 11.20, resolve a missing optional peer anyway
while installing. For an app whose package.json uses caret ranges and
whose Angular is one patch behind the newest, npm picks the newest
@angular/platform-server, which requires that exact newest
@angular/core, and the install stops with ERESOLVE. Every current Node
line bundles one of those npm versions.

Nothing in the published package imports @angular/platform-server, so
the peer is removed. Apps that render on the server still install it
themselves, as they already do.

(cherry picked from commit bbce8f0)
@armando-navarro
fix(ci): choose a release's dist-tag when it publishes, and move next…
72a74eb
… after it (angular#3794)

* fix(ci): choose a release's dist-tag when it publishes, and move next after it

A stable tag published to latest unconditionally, so a 20.x release
tagged after 21.0.0 would move latest back to 20.x. A stable release now
goes to latest only when it ranks above npm's latest and every other
stable git tag, since a higher release can still be in npm's publish-time
malware scan. Otherwise it goes to v<major>-lts, and the job fails if it
does not rank above that tag or another release of its major.

Every publish now waits until npm lists it, in one npm-publish queue, so
the next publish reads the real dist-tags. That stops a queued canary from
reading a stale canary tag. After a release reaches latest, next moves up
to it, and a failure there prints the npm dist-tag command that finishes
it. A re-run of a published release skips npm publish and goes on to the
wait and the next move.

The steps live in tools/publish-job.js and tools/release-tag.js so specs
cover them, and build.sh no longer picks a tag.

* refactor(ci): move the canary check into tools/publish-job.js

The check that skips a canary of an earlier commit keeps the same
decisions and messages, and its specs now cover each branch. It runs
only for pushes and scheduled runs, and a failed read of npm's dist-tags
now says why.

(cherry picked from commit 31fb2ae)
@armando-navarro
build: compile the tools specs in the jasmine build
4e2459a
The publish job's specs (tools/publish-job.jasmine.ts and
tools/release-tag.jasmine.ts) live under tools/. On main, angular#3780 added
this include, and angular#3780 itself stays off 20.1.x, so without it
build:jasmine never compiles those 39 specs and test:node skips them
without failing.
@armando-navarro
fix(storage): give the compat fromTask an explicit return type (angul…
0fa01ea
…ar#3796)

fromTask declared no return type, so the published typings import
whatever path the typings bundler picks for the inferred firebase
type. In 20.x that is 'firebase/compat', which firebase's exports map
does not list. Apps using moduleResolution "bundler", the setting the
@angular/build migration moves apps to, fail with TS2307 in
@angular/fire/compat/storage. 21.0.0-rc.1 happens to emit
'firebase/compat/app' and compiles, but ng-packagr 22.2's new typings
bundler cannot parse the inferred type and fails the library build.

Declaring Observable<UploadTaskSnapshot> removes the inferred import.
The type is unchanged: UploadTaskSnapshot is the compat alias for
firebase.storage.UploadTaskSnapshot.

Also drop a comment explaining a firebase import that angular#3421 removed
in 2023 as unused. The import only existed to steer these typings.

Fixes angular#3677

(cherry picked from commit 4c5ba7e)
@armando-navarro
armando-navarro requested a review from tyler-reitz October 9, 2026 21:08
@armando-navarro armando-navarro added bump: patch Size of the change: a fix or docs change that reaches users comp: build/pipeline Build, bundling, packaging, release pipeline. comp: compat Compatibility layer for the pre-modular API (src/compat). comp: core FirebaseApp DI core (src/app). comp: schematics ng add / deploy schematics (src/schematics). comp: storage Cloud Storage (src/storage). comp: typings TypeScript type correctness across the public API. type: bug Defect: expected behavior doesn't happen. labels Oct 9, 2026
tyler-reitz
tyler-reitz approved these changes Oct 9, 2026
View reviewed changes

@tyler-reitz tyler-reitz left a comment

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved.

I checked each pick against its source on main. Every deviation is one of the conflict resolutions you listed, and two picks are byte-identical. 166 specs, 0 failures reproduces locally, and the specs do gate: neutering releaseTag gives 9 failures, neutering assertSafeOutputPath gives 15. The built package drops only the platform-server peer and emits no publish.sh. Loading the built actions.js resolves cross-spawn zero times externally, so the bundling claim holds. Running release-tag.js against npm's current dist-tags and all 144 git tags puts 20.1.1 on latest and 21.0.0-rc.2 on next.

Two questions, neither blocking:

  • #3793 and #3770 merged to main in the same window and are not in this set. Deliberate, or worth a follow-up backport?
  • The new functionName and functionsNodeVersion schema patterns reject values 20.1.0 accepted, such as a name starting with a digit, or >=18. Fine by me, but it deserves a line in the 20.1.1 notes.

Agreed on Rebase and merge. The squash we normally use would collapse the nine into one commit on 20.1.x.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@armando-navarro armando-navarro added this to the 20.1.1 milestone Oct 10, 2026
@armando-navarro
fix(auth): stop beforeAuthStateChanged from holding the app unstable (a…
69d94e6
…ngular#3770)

AngularFire wrapped beforeAuthStateChanged so that registering the
hook added a pending task, cleared only when the callback first runs.
Firebase runs that callback only on a sign-in or sign-out, so for a
visitor who does neither the app never became stable. Registered on
the server, it failed ng build during route extraction and left
server-rendered requests without a response.

This restores the blockUntilFirst: false override from angular#3590, which
angular#3613 dropped without comment while adding log-level overrides next
to it. The callback still runs inside Angular's zone and injection
context, and its returned promise still reaches Firebase, so a
rejection still cancels the sign-in. A call outside an injection
context now logs its per-call warning only at the verbose level, as
onMessage does.

Fixes angular#3748

docs(auth): scope the beforeAuthStateChanged note to rc.1 and earlier

Merging this change closes angular#3748, so the section's present-tense note
would point at a closed issue. Also removed the false claim that the
@angular/fire/auth import makes ng build hang: the guide registers the
hook only in the browser, so its own build succeeds.

The docs/auth.md change is left out, since it edits a section of the
guide that 20.1.x does not have.

(cherry picked from commit f182972)
@armando-navarro armando-navarro changed the title Backport nine fixes to 20.1.x for 20.1.1 Backport ten fixes to 20.1.x for 20.1.1 Oct 10, 2026
@armando-navarro

armando-navarro commented Oct 10, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for checking every pick against main and for breaking the code to confirm the specs fail.

  • Leaving out fix(schematics): require only exported devkit paths from the shipped schematics #3793 was deliberate. The 20.1.0 schematics require the bare @angular-devkit/schematics/tasks path, which every devkit version allows, so the failure fix(schematics): require only exported devkit paths from the shipped schematics #3793 fixes can't happen on 20.1.x. Its pick would also conflict in four places in tools/build.ts.

  • fix(auth): stop beforeAuthStateChanged from holding the app unstable #3770 I missed. The bug is on 20.1.x too, since beforeAuthStateChanged is still wrapped with true there. I've added its pick as the last commit. Its docs change edits a section of docs/auth.md that this branch doesn't have, so only the code change came over. test:node still passes with 166 specs.

  • Agreed on the schema patterns. region gets a pattern as well, so the 20.1.1 notes will list all three options.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@armando-navarro
armando-navarro merged commit cc1fc15 into angular:20.1.x Oct 10, 2026
18 checks passed

Uh oh!

There was an error while loading. Please reload this page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@tyler-reitz tyler-reitz tyler-reitz approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

bump: patch Size of the change: a fix or docs change that reaches users comp: build/pipeline Build, bundling, packaging, release pipeline. comp: compat Compatibility layer for the pre-modular API (src/compat). comp: core FirebaseApp DI core (src/app). comp: schematics ng add / deploy schematics (src/schematics). comp: storage Cloud Storage (src/storage). comp: typings TypeScript type correctness across the public API. type: bug Defect: expected behavior doesn't happen.

Projects

None yet

Milestone

20.1.1

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@armando-navarro @tyler-reitz @herdiyana256 @fr-esco
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.