Skip to content

Commit f95d598

Browse files
feat(deploy): deprecate deploying server-side rendering to Cloud Functions (#3803)
ng deploy with ssr set to true or 'cloud-functions' wraps the server bundle in a generated Cloud Function. Firebase has closed its Angular frameworks path to new users and recommends App Hosting for server-rendered apps, and the generated function has broken repeatedly as firebase-functions and the Node runtimes moved on. AngularFire 22 deprecates that mode and AngularFire 23 removes it. Hosting-only and Cloud Run deploys are not deprecated and now print nothing. The old message printed for every target without version: 2, hosting-only and Cloud Run included, and pointed at the closed frameworks page. The new one prints only on the Cloud Functions route, before the build so a failed build still shows it, and states the removal version. CF3v2 and functionsRuntimeOptions, read only by that route, carry x-deprecated so the CLI warns about them before the builder runs and marks CF3v2 in ng deploy --help.
1 parent 7f13f64 commit f95d598

6 files changed

Lines changed: 89 additions & 31 deletions

File tree

‎docs/deploy/getting-started.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Deploy your application on Firebase Hosting & Functions
22

3-
> **This page is out of date.** It describes a setup flow that `ng add @angular/fire` no longer performs, and wiring the deploy target by hand from the samples below puts you on a deprecated path that warns on every deploy. For current guidance see [the Quickstart](../install-and-setup.md), step 6, and for server-side rendered apps see the [App Hosting guide](../app-hosting.md).
3+
> **This page is out of date.** It describes a setup flow that `ng add @angular/fire` no longer performs. Deploying server-side rendering to Cloud Functions with `ng deploy` (a deploy target with `ssr: true`, and the `functionsRuntimeOptions` and `CF3v2` options only it reads) is deprecated in AngularFire 22 and will be removed in AngularFire 23. Hosting-only deploys (`ssr` absent or `false`) are not deprecated. For current guidance see [the Quickstart](../install-and-setup.md), step 6, and for server-side rendered apps see the [App Hosting guide](../app-hosting.md).
44
55
In this guide, we'll look at how to use `@angular/fire` to automatically deploy an Angular application to Firebase hosting or functions by using the Angular CLI.
66

‎docs/universal/cloud-functions.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Deploying your Universal application on Cloud Functions for Firebase
22

3+
> **This page is out of date.** It describes the Angular Universal and `firebase-tools` setup of 2019. Deploy server-rendered apps with Firebase App Hosting instead, see the [App Hosting guide](../app-hosting.md).
4+
35
After [setting up your application with Angular Universal as outlined in Getting Started](getting-started.md), you're now ready to build your application for Firebase Hosting & Cloud Functions.
46

57
> Cloud Functions for Firebase lets you automatically run backend code in response to events triggered by Firebase features and HTTPS requests. Your code is stored in Google's cloud and runs in a managed environment. There's no need to manage and scale your own servers. [Learn more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/).

‎src/schematics/deploy/actions.jasmine.ts‎

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Script } from 'vm';
44
import { BuilderContext, BuilderRun, ScheduleOptions, Target } from '@angular-devkit/architect';
55
import { JsonObject, logging } from '@angular-devkit/core';
66
import { BuildTarget, DeployBuilderSchema, FSHost, FirebaseDeployConfig, FirebaseTools } from '../interfaces';
7-
import deploy, { assertSafeDependencyName, assertSafeFunctionName, assertSafeNodeVersion, assertSafeOutputPath, assertSupportedPackageManager, buildCloudRunBuildsSubmitArgs, buildCloudRunDeployArgs, deployToCloudRun, deployToFunction, findPackageVersion, processHost } from './actions.js'
7+
import deploy, { CLOUD_FUNCTIONS_DEPRECATION, assertSafeDependencyName, assertSafeFunctionName, assertSafeNodeVersion, assertSafeOutputPath, assertSupportedPackageManager, buildCloudRunBuildsSubmitArgs, buildCloudRunDeployArgs, deployToCloudRun, deployToFunction, findPackageVersion, processHost } from './actions.js'
88
import 'jasmine';
99

1010
let context: BuilderContext;
@@ -333,6 +333,55 @@ describe('universal deployment', () => {
333333
});*/
334334
});
335335

336+
describe('Cloud Functions deprecation', () => {
337+
beforeEach(() => initMocks());
338+
339+
/* deploy() also warns about a Node version mismatch, so every assertion is about the
340+
* deprecation text, never about the number of warn calls. */
341+
const deprecationWarnings = (warnSpy: jasmine.Spy) =>
342+
warnSpy.calls.allArgs().filter(([message]) => message === CLOUD_FUNCTIONS_DEPRECATION);
343+
344+
/* Makes the SSR routes throw at their first statement (the output path check), after the
345+
* warning has or has not printed and before anything touches the file system or gcloud. */
346+
const withUnreadableOutputPaths = () => {
347+
context.getTargetOptions = () => Promise.resolve({});
348+
};
349+
350+
it('warns before the build when deploying server-side rendering to Cloud Functions', async () => {
351+
withUnreadableOutputPaths();
352+
const warnSpy = spyOn(context.logger, 'warn');
353+
const scheduleSpy = spyOn(context, 'scheduleTarget').and.callThrough();
354+
await expectAsync(
355+
deploy(firebaseMock, context, STATIC_BUILD_TARGET, SERVER_BUILD_TARGET, undefined, FIREBASE_PROJECT, { preview: false, ssr: true }, FIREBASE_TOKEN)
356+
).toBeRejectedWithError(/Cannot read the output path option/);
357+
expect(deprecationWarnings(warnSpy).length).toBe(1);
358+
expect(warnSpy).toHaveBeenCalledBefore(scheduleSpy);
359+
});
360+
361+
it('does not warn on a hosting-only deploy', async () => {
362+
const warnSpy = spyOn(context.logger, 'warn');
363+
await deploy(firebaseMock, context, STATIC_BUILD_TARGET, undefined, undefined, FIREBASE_PROJECT, { preview: false }, FIREBASE_TOKEN);
364+
expect(deprecationWarnings(warnSpy)).toEqual([]);
365+
});
366+
367+
it('does not warn when version 2 routes an ssr target to hosting', async () => {
368+
const warnSpy = spyOn(context.logger, 'warn');
369+
const deploySpy = spyOn(firebaseMock, 'deploy').and.callThrough();
370+
await deploy(firebaseMock, context, STATIC_BUILD_TARGET, SERVER_BUILD_TARGET, undefined, FIREBASE_PROJECT, { preview: false, version: 2, ssr: true }, FIREBASE_TOKEN);
371+
expect(deploySpy).toHaveBeenCalledWith(jasmine.objectContaining({ only: 'hosting:' + PROJECT }));
372+
expect(deprecationWarnings(warnSpy)).toEqual([]);
373+
});
374+
375+
it('does not warn on a Cloud Run deploy', async () => {
376+
withUnreadableOutputPaths();
377+
const warnSpy = spyOn(context.logger, 'warn');
378+
await expectAsync(
379+
deploy(firebaseMock, context, STATIC_BUILD_TARGET, SERVER_BUILD_TARGET, undefined, FIREBASE_PROJECT, { preview: false, ssr: 'cloud-run' }, FIREBASE_TOKEN)
380+
).toBeRejectedWithError(/Cannot read the output path option/);
381+
expect(deprecationWarnings(warnSpy)).toEqual([]);
382+
});
383+
});
384+
336385
describe('Cloud Run gcloud argv construction', () => {
337386
// Regression coverage for the argv-injection fix: these options used to be interpolated
338387
// into a single command string and split on whitespace, so a value containing a space

‎src/schematics/deploy/actions.ts‎

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,10 @@ const getPackageJson = (context: BuilderContext, workspaceRoot: string, options:
273273
return defaultPackage(dependencies, devDependencies, options, main);
274274
};
275275

276+
export const CLOUD_FUNCTIONS_DEPRECATION = `⚠️ Deploying server-side rendering to Cloud Functions with ng deploy is deprecated in AngularFire 22 and will be removed in AngularFire 23.
277+
Deploy server-rendered apps with Firebase App Hosting instead: https://firebase.google.com/docs/app-hosting
278+
AngularFire's guide: https://github.com/angular/angularfire/blob/main/docs/app-hosting.md`;
279+
276280
export const deployToFunction = async (
277281
firebaseTools: FirebaseTools,
278282
context: BuilderContext,
@@ -560,6 +564,8 @@ export default async function deploy(
560564
firebaseToken?: string,
561565
) {
562566
const legacyNgDeploy = !options.version || options.version < 2;
567+
const deploysToCloudRun = legacyNgDeploy && !!serverBuildTarget && options.ssr === 'cloud-run';
568+
const deploysToCloudFunctions = legacyNgDeploy && !!serverBuildTarget && !deploysToCloudRun;
563569

564570
if (!firebaseToken && !process.env.GOOGLE_APPLICATION_CREDENTIALS) {
565571
if (!await isSignedIn(firebaseTools)) {
@@ -579,10 +585,9 @@ export default async function deploy(
579585
console.log(`Using Google Application Credentials.`);
580586
}
581587

582-
if (legacyNgDeploy) {
583-
console.error(`Legacy ng-deploy Firebase is deprecated.
584-
Please migrate to Firebase Hosting's integration with Angular https://firebase.google.com/docs/hosting/frameworks/angular
585-
or the new Firebase App Hosting product https://firebase.google.com/docs/app-hosting`);
588+
// Before the build, so a failed or interrupted build still displays the warning.
589+
if (deploysToCloudFunctions) {
590+
context.logger.warn(CLOUD_FUNCTIONS_DEPRECATION);
586591
}
587592

588593
if (prerenderBuildTarget) {
@@ -649,28 +654,26 @@ or the new Firebase App Hosting product https://firebase.google.com/docs/app-hos
649654

650655
firebaseTools.logger.logger.add(logger);
651656

652-
if (legacyNgDeploy && serverBuildTarget) {
653-
if (options.ssr === 'cloud-run') {
654-
await deployToCloudRun(
655-
firebaseTools,
656-
context,
657-
context.workspaceRoot,
658-
staticBuildTarget,
659-
serverBuildTarget,
660-
options,
661-
firebaseToken,
662-
);
663-
} else {
664-
await deployToFunction(
665-
firebaseTools,
666-
context,
667-
context.workspaceRoot,
668-
staticBuildTarget,
669-
serverBuildTarget,
670-
options,
671-
firebaseToken,
672-
);
673-
}
657+
if (deploysToCloudRun) {
658+
await deployToCloudRun(
659+
firebaseTools,
660+
context,
661+
context.workspaceRoot,
662+
staticBuildTarget,
663+
serverBuildTarget,
664+
options,
665+
firebaseToken,
666+
);
667+
} else if (deploysToCloudFunctions) {
668+
await deployToFunction(
669+
firebaseTools,
670+
context,
671+
context.workspaceRoot,
672+
staticBuildTarget,
673+
serverBuildTarget,
674+
options,
675+
firebaseToken,
676+
);
674677
} else {
675678
await deployToHosting(
676679
firebaseTools,

‎src/schematics/deploy/schema.json‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
},
3232
"ssr": {
3333
"oneOf": [{ "type": "boolean" }, { "type": "string" }],
34-
"description": "Should we attempt to deploy the function to Cloud Functions (true or 'cloud-functions') / Cloud Run ('cloud-run') or just Hosting (false)"
34+
"description": "Deploy server-side rendering to Cloud Run ('cloud-run'), or just Hosting (false). Deploying to Cloud Functions (true or 'cloud-functions') is deprecated in AngularFire 22 and will be removed in AngularFire 23. Use Firebase App Hosting for server-rendered apps."
3535
},
3636
"prerender": {
3737
"type": "boolean",
@@ -61,7 +61,8 @@
6161
},
6262
"CF3v2": {
6363
"type": "boolean",
64-
"description": "Generation of the Functions product to run on"
64+
"description": "Generation of the Functions product to run on. Read only by the Cloud Functions mode, which AngularFire 22 deprecates and AngularFire 23 removes.",
65+
"x-deprecated": "read only by the Cloud Functions mode of ng deploy, which AngularFire 22 deprecates and AngularFire 23 removes. Delete it unless ssr is true or 'cloud-functions'. Use Firebase App Hosting for server-rendered apps."
6566
},
6667
"region": {
6768
"type": "string",
@@ -74,7 +75,8 @@
7475
},
7576
"functionsRuntimeOptions": {
7677
"type": "object",
77-
"description": "Runtime options for Cloud Functions, if deploying to Cloud Functions"
78+
"description": "Runtime options for Cloud Functions. Read only by the Cloud Functions mode, which AngularFire 22 deprecates and AngularFire 23 removes.",
79+
"x-deprecated": "read only by the Cloud Functions mode of ng deploy, which AngularFire 22 deprecates and AngularFire 23 removes. Delete it unless ssr is true or 'cloud-functions'. Use Firebase App Hosting for server-rendered apps."
7880
},
7981
"preview": {
8082
"type": "boolean",

‎src/schematics/interfaces.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,11 @@ export interface DeployBuilderSchema {
191191
prerender?: boolean;
192192
functionName?: string;
193193
functionsNodeVersion?: number|string;
194+
/** @deprecated Read only by the Cloud Functions mode, which will be removed in AngularFire 23. */
194195
functionsRuntimeOptions?: HttpsOptions;
195196
cloudRunOptions?: Partial<CloudRunOptions>;
196197
outputPath?: string;
198+
/** @deprecated Read only by the Cloud Functions mode, which will be removed in AngularFire 23. */
197199
CF3v2?: boolean;
198200
version?: number;
199201
}

0 commit comments

Comments
 (0)