Skip to content

Commit be0a1fb

Browse files
authored
feat(schematics): ng deploy schematic (#2046)
1 parent c34c0f3 commit be0a1fb

25 files changed

+3156
-177
lines changed

.travis.yml

+6-5
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ install:
4040
4141
script:
4242
- yarn run build
43-
- ./node_modules/.bin/karma start --single-run --browsers ChromeHeadlessTravis --reporters mocha
44-
# Run integration test to make sure our typings are correct for user-land.
45-
- node tools/run-typings-test.js
46-
# Run the ng6 build test
47-
- cd test/ng-build/ng6 && yarn && yarn build:prod
43+
- |
44+
./node_modules/.bin/karma start --single-run --browsers ChromeHeadlessTravis --reporters mocha &&
45+
yarn test:node &&
46+
node tools/run-typings-test.js &&
47+
cd test/ng-build/ng6 &&
48+
yarn && yarn build:prod

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ Firebase offers two cloud-based, client-accessible database solutions that suppo
102102

103103
> Firebase Hosting is production-grade web content hosting for developers. With Hosting, you can quickly and easily deploy web apps and static content to a global content delivery network (CDN) with a single command.
104104
105-
- [Deploy your Angular application on Firebase Hosting](docs/deploying-angularfire-to-firebase.md)
105+
- [Deploy your application on Firebase Hosting](docs/deploy/getting-started.md)
106106

107107
#### Server-side rendering
108108

docs/deploy/getting-started.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Deploy your application on Firebase Hosting
2+
3+
In this guide, we'll look at how to use `@angular/fire` to automatically deploy an Angular application to Firebase hosting by using the Angular CLI.
4+
5+
## Step 1: add `@angular/fire` to your project
6+
7+
First, you need to add the `@angular/fire` package to your project. In your Angular CLI project run:
8+
9+
```shell
10+
ng add @angular/fire
11+
```
12+
13+
*Note that the command above assumes you have global Angular CLI installed. To install Angular CLI globally run `npm i -g @angular/cli`.*
14+
15+
The command above will trigger the `@angular/fire` `ng-add` schematics. The schematics will open a web browser and guide you through the Firebase authentication flow (if you're not signed in already). After you authenticate, you'll see a prompt to select a Firebase hosting project.
16+
17+
The schematics will do the following:
18+
19+
- Add `@angular/fire` to your list of dependencies
20+
- Create `firebase.json`, `.firebaserc` files in the root of your workspace. You can use them to configure your firebase hosting deployment. Find more about them [here](https://firebase.google.com/docs/hosting/full-config)
21+
- Update your workspace file (`angular.json`) by inserting the `deploy` builder
22+
23+
In the end, your `angular.json` project will look like below:
24+
25+
```json
26+
{
27+
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
28+
"version": 1,
29+
"newProjectRoot": "projects",
30+
"projects": {
31+
"sample-app": {
32+
// ...
33+
"deploy": {
34+
"builder": "@angular/fire:deploy",
35+
"options": {}
36+
}
37+
}
38+
}
39+
// ...
40+
},
41+
"defaultProject": "sample-app"
42+
}
43+
```
44+
45+
If you want to add deployment capabilities to a different project in your workspace, you can run:
46+
47+
```
48+
ng add @angular/fire --project=[PROJECT_NAME]
49+
```
50+
51+
## Step 2: deploying the project
52+
53+
As the second step, to deploy your project run:
54+
55+
```
56+
ng run [ANGULAR_PROJECT_NAME]:deploy
57+
```
58+
59+
The command above will trigger:
60+
61+
1. Production build of your application
62+
2. Deployment of the produced assets to the firebase hosting project you selected during `ng add`
63+
64+
## Step 3: customization
65+
66+
To customize the deployment flow, you can use the configuration files you're already familiar with from `firebase-tools`. You can find more in the [firebase documentation](https://firebase.google.com/docs/hosting/full-config).

docs/deploying-angularfire-to-firebase.md

-31
This file was deleted.

docs/universal/cloud-functions.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ After [setting up your application with Angular Universal as outlined in Getting
77
If you don't already have the Firebase CLI installed, do so:
88

99
```bash
10-
npm i -g @firebase-tools
10+
npm i -g firebase-tools
1111
firebase login
1212
```
1313

package.json

+22-10
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"description": "The official library of Firebase and Angular.",
55
"private": true,
66
"scripts": {
7-
"test": "npm run build && karma start --single-run",
7+
"test": "npm run build && karma start --single-run && npm run test:node",
8+
"test:node": "jasmine 'dist/packages-dist/schematics/**/*[sS]pec.js'",
89
"test:watch": "concurrently \"npm run build:watch\" \"npm run delayed_karma\"",
910
"test:debug": "npm run build && karma start",
1011
"karma": "karma start",
@@ -14,6 +15,8 @@
1415
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1",
1516
"build:wrapper": "npm i --prefix wrapper && npm run --prefix wrapper build"
1617
},
18+
"schematics": "./dist/packages-dist/collection.json",
19+
"builders": "./dist/packages-dist/builders.json",
1720
"keywords": [
1821
"angular",
1922
"firebase",
@@ -30,12 +33,19 @@
3033
},
3134
"homepage": "https://github.com/angular/angularfire2#readme",
3235
"dependencies": {
33-
"@angular/common": ">=6.0.0 <8",
34-
"@angular/compiler": ">=6.0.0 <8",
35-
"@angular/core": ">=6.0.0 <8",
36-
"@angular/platform-browser": ">=6.0.0 <8",
37-
"@angular/platform-browser-dynamic": ">=6.0.0 <8",
36+
"@angular-devkit/architect": "^0.800.0-rc.4",
37+
"@angular-devkit/core": ">=6.0.0 <9 || 9.0.0-0",
38+
"@angular-devkit/schematics": ">=6.0.0 <9 || 9.0.0-0",
39+
"@angular/common": ">=6.0.0 <9 || 9.0.0-0",
40+
"@angular/compiler": ">=6.0.0 <9 || 9.0.0-0",
41+
"@angular/core": ">=6.0.0 <9 || 9.0.0-0",
42+
"@angular/platform-browser": ">=6.0.0 <9 || 9.0.0-0",
43+
"@angular/platform-browser-dynamic": ">=6.0.0 <9 || 9.0.0-0",
3844
"firebase": ">= 5.5.7 <7",
45+
"firebase-tools": "^6.10.0",
46+
"fuzzy": "^0.1.3",
47+
"inquirer": "^6.2.2",
48+
"inquirer-autocomplete-prompt": "^1.0.1",
3949
"rxjs": "^6.0.0",
4050
"ws": "^3.3.2",
4151
"xhr2": "^0.1.4",
@@ -46,9 +56,10 @@
4656
"utf-8-validate": "~4.0.0"
4757
},
4858
"devDependencies": {
49-
"@angular/animations": ">=6.0.0 <8",
50-
"@angular/compiler-cli": ">=6.0.0 <8",
51-
"@angular/platform-server": ">=6.0.0 <8",
59+
"@angular/animations": ">=6.0.0 <9 || 9.0.0-0",
60+
"@angular/compiler-cli": ">=6.0.0 <9 || 9.0.0-0",
61+
"@angular/platform-server": ">=6.0.0 <9 || 9.0.0-0",
62+
"@types/inquirer": "^0.0.44",
5263
"@types/jasmine": "^2.5.36",
5364
"@types/request": "0.0.30",
5465
"concurrently": "^2.2.0",
@@ -79,11 +90,12 @@
7990
"rollup": "^0.64.1",
8091
"rollup-plugin-node-resolve": "^3.3.0",
8192
"rollup-watch": "^4.3.1",
93+
"schematics-utilities": "^1.1.1",
8294
"shelljs": "^0.8.0",
8395
"systemjs": "^0.19.16",
8496
"systemjs-builder": "^0.15.7",
8597
"traceur": "0.0.96",
86-
"typescript": ">=3.1.1 <3.2"
98+
"typescript": "^3.1.6 <3.2"
8799
},
88100
"typings": "index.d.ts"
89101
}

src/core/builders.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"$schema": "@angular-devkit/architect/src/builders-schema.json",
3+
"builders": {
4+
"deploy": {
5+
"implementation": "./schematics/deploy/builder",
6+
"schema": "./schematics/deploy/schema.json",
7+
"description": "Deploy builder"
8+
}
9+
}
10+
}

src/core/collection.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"$schema": "@angular-devkit/schematics/collection-schema.json",
3+
"schematics": {
4+
"ng-add": {
5+
"description": "Add firebase deploy schematic",
6+
"factory": "./schematics/index#ngDeploy"
7+
}
8+
}
9+
}

src/core/package.json

+12-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
"main": "./bundles/core.umd.js",
66
"module": "index.js",
77
"es2015": "es2015/index.js",
8+
"schematics": "./collection.json",
9+
"builders": "./builders.json",
810
"keywords": [
911
"angular",
1012
"firebase",
@@ -27,5 +29,14 @@
2729
"rxjs": "RXJS_VERSION",
2830
"zone.js": "ZONEJS_VERSION"
2931
},
32+
"dependencies": {
33+
"@angular-devkit/architect": "ANGULAR_DEVKIT_ARCH_VERSION",
34+
"@angular-devkit/core": "ANGULAR_VERSION",
35+
"@angular-devkit/schematics": "ANGULAR_VERSION",
36+
"firebase-tools": "FIREBASE_TOOLS_VERSION",
37+
"fuzzy": "FUZZY_VERSION",
38+
"inquirer": "INQUIRER_VERSION",
39+
"inquirer-autocomplete-prompt": "INQUIRER_AUTOCOMPLETE_VERSION"
40+
},
3041
"typings": "index.d.ts"
31-
}
42+
}

src/schematics/deploy/actions.spec.ts

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { JsonObject, logging } from '@angular-devkit/core';
2+
import { BuilderContext, BuilderRun, ScheduleOptions, Target, } from '@angular-devkit/architect/src/index2';
3+
import { FirebaseTools, FirebaseDeployConfig } from 'schematics/interfaces';
4+
import deploy from './actions';
5+
6+
7+
let context: BuilderContext;
8+
let firebaseMock: FirebaseTools;
9+
10+
const FIREBASE_PROJECT = 'ikachu-aa3ef';
11+
const PROJECT = 'pirojok-project';
12+
13+
describe('Deploy Angular apps', () => {
14+
beforeEach(() => initMocks());
15+
16+
it('should check if the user is authenticated by invoking list', async () => {
17+
const spy = spyOn(firebaseMock, 'list');
18+
const spyLogin = spyOn(firebaseMock, 'login');
19+
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
20+
expect(spy).toHaveBeenCalled();
21+
expect(spyLogin).not.toHaveBeenCalled();
22+
});
23+
24+
it('should invoke login if list rejects', async () => {
25+
firebaseMock.list = () => Promise.reject();
26+
const spy = spyOn(firebaseMock, 'list').and.callThrough();
27+
const spyLogin = spyOn(firebaseMock, 'login');
28+
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
29+
expect(spy).toHaveBeenCalled();
30+
expect(spyLogin).toHaveBeenCalled();
31+
});
32+
33+
it('should invoke the builder', async () => {
34+
const spy = spyOn(context, 'scheduleTarget').and.callThrough();
35+
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
36+
expect(spy).toHaveBeenCalled();
37+
expect(spy).toHaveBeenCalledWith({
38+
target: 'build',
39+
configuration: 'production',
40+
project: PROJECT
41+
});
42+
});
43+
44+
it('should invoke firebase.deploy', async () => {
45+
const spy = spyOn(firebaseMock, 'deploy').and.callThrough();
46+
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
47+
expect(spy).toHaveBeenCalled();
48+
expect(spy).toHaveBeenCalledWith({
49+
cwd: 'host', only: 'hosting:' + PROJECT
50+
});
51+
});
52+
53+
describe('error handling', () => {
54+
it('throws if there is no firebase project', async () => {
55+
try {
56+
await deploy(firebaseMock, context, 'host')
57+
fail();
58+
} catch (e) {
59+
expect(e.message).toMatch(/Cannot find firebase project/);
60+
}
61+
});
62+
63+
it('throws if there is no target project', async () => {
64+
context.target = undefined;
65+
try {
66+
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT)
67+
fail();
68+
} catch (e) {
69+
expect(e.message).toMatch(/Cannot execute the build target/);
70+
}
71+
});
72+
});
73+
});
74+
75+
const initMocks = () => {
76+
firebaseMock = {
77+
login: () => Promise.resolve(),
78+
list: () => Promise.resolve([]),
79+
deploy: (_: FirebaseDeployConfig) => Promise.resolve(),
80+
use: () => Promise.resolve()
81+
};
82+
83+
context = {
84+
target: {
85+
configuration: 'production',
86+
project: PROJECT,
87+
target: 'foo'
88+
},
89+
builder: {
90+
builderName: 'mock',
91+
description: 'mock',
92+
optionSchema: false
93+
},
94+
currentDirectory: 'cwd',
95+
id: 1,
96+
logger: new logging.NullLogger() as any,
97+
workspaceRoot: 'cwd',
98+
getTargetOptions: (_: Target) => Promise.resolve({}),
99+
reportProgress: (_: number, __?: number, ___?: string) => {
100+
},
101+
reportStatus: (_: string) => {},
102+
reportRunning: () => {},
103+
scheduleBuilder: (_: string, __?: JsonObject, ___?: ScheduleOptions) => Promise.resolve({} as BuilderRun),
104+
scheduleTarget: (_: Target, __?: JsonObject, ___?: ScheduleOptions) => Promise.resolve({} as BuilderRun)
105+
};
106+
};

0 commit comments

Comments
 (0)