Skip to content

Commit

Permalink
feat(schematics): ng deploy schematic (#2046)
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesdaniels committed May 22, 2019
1 parent c34c0f3 commit be0a1fb
Show file tree
Hide file tree
Showing 25 changed files with 3,156 additions and 177 deletions.
11 changes: 6 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ install:
script:
- yarn run build
- ./node_modules/.bin/karma start --single-run --browsers ChromeHeadlessTravis --reporters mocha
# Run integration test to make sure our typings are correct for user-land.
- node tools/run-typings-test.js
# Run the ng6 build test
- cd test/ng-build/ng6 && yarn && yarn build:prod
- |
./node_modules/.bin/karma start --single-run --browsers ChromeHeadlessTravis --reporters mocha &&
yarn test:node &&
node tools/run-typings-test.js &&
cd test/ng-build/ng6 &&
yarn && yarn build:prod
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Firebase offers two cloud-based, client-accessible database solutions that suppo

> 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.
- [Deploy your Angular application on Firebase Hosting](docs/deploying-angularfire-to-firebase.md)
- [Deploy your application on Firebase Hosting](docs/deploy/getting-started.md)

#### Server-side rendering

Expand Down
66 changes: 66 additions & 0 deletions docs/deploy/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Deploy your application on Firebase Hosting

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.

## Step 1: add `@angular/fire` to your project

First, you need to add the `@angular/fire` package to your project. In your Angular CLI project run:

```shell
ng add @angular/fire
```

*Note that the command above assumes you have global Angular CLI installed. To install Angular CLI globally run `npm i -g @angular/cli`.*

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.

The schematics will do the following:

- Add `@angular/fire` to your list of dependencies
- 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)
- Update your workspace file (`angular.json`) by inserting the `deploy` builder

In the end, your `angular.json` project will look like below:

```json
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"sample-app": {
// ...
"deploy": {
"builder": "@angular/fire:deploy",
"options": {}
}
}
}
// ...
},
"defaultProject": "sample-app"
}
```

If you want to add deployment capabilities to a different project in your workspace, you can run:

```
ng add @angular/fire --project=[PROJECT_NAME]
```

## Step 2: deploying the project

As the second step, to deploy your project run:

```
ng run [ANGULAR_PROJECT_NAME]:deploy
```

The command above will trigger:

1. Production build of your application
2. Deployment of the produced assets to the firebase hosting project you selected during `ng add`

## Step 3: customization

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).
31 changes: 0 additions & 31 deletions docs/deploying-angularfire-to-firebase.md

This file was deleted.

2 changes: 1 addition & 1 deletion docs/universal/cloud-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ After [setting up your application with Angular Universal as outlined in Getting
If you don't already have the Firebase CLI installed, do so:

```bash
npm i -g @firebase-tools
npm i -g firebase-tools
firebase login
```

Expand Down
32 changes: 22 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"description": "The official library of Firebase and Angular.",
"private": true,
"scripts": {
"test": "npm run build && karma start --single-run",
"test": "npm run build && karma start --single-run && npm run test:node",
"test:node": "jasmine 'dist/packages-dist/schematics/**/*[sS]pec.js'",
"test:watch": "concurrently \"npm run build:watch\" \"npm run delayed_karma\"",
"test:debug": "npm run build && karma start",
"karma": "karma start",
Expand All @@ -14,6 +15,8 @@
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1",
"build:wrapper": "npm i --prefix wrapper && npm run --prefix wrapper build"
},
"schematics": "./dist/packages-dist/collection.json",
"builders": "./dist/packages-dist/builders.json",
"keywords": [
"angular",
"firebase",
Expand All @@ -30,12 +33,19 @@
},
"homepage": "https://github.com/angular/angularfire2#readme",
"dependencies": {
"@angular/common": ">=6.0.0 <8",
"@angular/compiler": ">=6.0.0 <8",
"@angular/core": ">=6.0.0 <8",
"@angular/platform-browser": ">=6.0.0 <8",
"@angular/platform-browser-dynamic": ">=6.0.0 <8",
"@angular-devkit/architect": "^0.800.0-rc.4",
"@angular-devkit/core": ">=6.0.0 <9 || 9.0.0-0",
"@angular-devkit/schematics": ">=6.0.0 <9 || 9.0.0-0",
"@angular/common": ">=6.0.0 <9 || 9.0.0-0",
"@angular/compiler": ">=6.0.0 <9 || 9.0.0-0",
"@angular/core": ">=6.0.0 <9 || 9.0.0-0",
"@angular/platform-browser": ">=6.0.0 <9 || 9.0.0-0",
"@angular/platform-browser-dynamic": ">=6.0.0 <9 || 9.0.0-0",
"firebase": ">= 5.5.7 <7",
"firebase-tools": "^6.10.0",
"fuzzy": "^0.1.3",
"inquirer": "^6.2.2",
"inquirer-autocomplete-prompt": "^1.0.1",
"rxjs": "^6.0.0",
"ws": "^3.3.2",
"xhr2": "^0.1.4",
Expand All @@ -46,9 +56,10 @@
"utf-8-validate": "~4.0.0"
},
"devDependencies": {
"@angular/animations": ">=6.0.0 <8",
"@angular/compiler-cli": ">=6.0.0 <8",
"@angular/platform-server": ">=6.0.0 <8",
"@angular/animations": ">=6.0.0 <9 || 9.0.0-0",
"@angular/compiler-cli": ">=6.0.0 <9 || 9.0.0-0",
"@angular/platform-server": ">=6.0.0 <9 || 9.0.0-0",
"@types/inquirer": "^0.0.44",
"@types/jasmine": "^2.5.36",
"@types/request": "0.0.30",
"concurrently": "^2.2.0",
Expand Down Expand Up @@ -79,11 +90,12 @@
"rollup": "^0.64.1",
"rollup-plugin-node-resolve": "^3.3.0",
"rollup-watch": "^4.3.1",
"schematics-utilities": "^1.1.1",
"shelljs": "^0.8.0",
"systemjs": "^0.19.16",
"systemjs-builder": "^0.15.7",
"traceur": "0.0.96",
"typescript": ">=3.1.1 <3.2"
"typescript": "^3.1.6 <3.2"
},
"typings": "index.d.ts"
}
10 changes: 10 additions & 0 deletions src/core/builders.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$schema": "@angular-devkit/architect/src/builders-schema.json",
"builders": {
"deploy": {
"implementation": "./schematics/deploy/builder",
"schema": "./schematics/deploy/schema.json",
"description": "Deploy builder"
}
}
}
9 changes: 9 additions & 0 deletions src/core/collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"$schema": "@angular-devkit/schematics/collection-schema.json",
"schematics": {
"ng-add": {
"description": "Add firebase deploy schematic",
"factory": "./schematics/index#ngDeploy"
}
}
}
13 changes: 12 additions & 1 deletion src/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"main": "./bundles/core.umd.js",
"module": "index.js",
"es2015": "es2015/index.js",
"schematics": "./collection.json",
"builders": "./builders.json",
"keywords": [
"angular",
"firebase",
Expand All @@ -27,5 +29,14 @@
"rxjs": "RXJS_VERSION",
"zone.js": "ZONEJS_VERSION"
},
"dependencies": {
"@angular-devkit/architect": "ANGULAR_DEVKIT_ARCH_VERSION",
"@angular-devkit/core": "ANGULAR_VERSION",
"@angular-devkit/schematics": "ANGULAR_VERSION",
"firebase-tools": "FIREBASE_TOOLS_VERSION",
"fuzzy": "FUZZY_VERSION",
"inquirer": "INQUIRER_VERSION",
"inquirer-autocomplete-prompt": "INQUIRER_AUTOCOMPLETE_VERSION"
},
"typings": "index.d.ts"
}
}
106 changes: 106 additions & 0 deletions src/schematics/deploy/actions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { JsonObject, logging } from '@angular-devkit/core';
import { BuilderContext, BuilderRun, ScheduleOptions, Target, } from '@angular-devkit/architect/src/index2';
import { FirebaseTools, FirebaseDeployConfig } from 'schematics/interfaces';
import deploy from './actions';


let context: BuilderContext;
let firebaseMock: FirebaseTools;

const FIREBASE_PROJECT = 'ikachu-aa3ef';
const PROJECT = 'pirojok-project';

describe('Deploy Angular apps', () => {
beforeEach(() => initMocks());

it('should check if the user is authenticated by invoking list', async () => {
const spy = spyOn(firebaseMock, 'list');
const spyLogin = spyOn(firebaseMock, 'login');
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
expect(spy).toHaveBeenCalled();
expect(spyLogin).not.toHaveBeenCalled();
});

it('should invoke login if list rejects', async () => {
firebaseMock.list = () => Promise.reject();
const spy = spyOn(firebaseMock, 'list').and.callThrough();
const spyLogin = spyOn(firebaseMock, 'login');
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
expect(spy).toHaveBeenCalled();
expect(spyLogin).toHaveBeenCalled();
});

it('should invoke the builder', async () => {
const spy = spyOn(context, 'scheduleTarget').and.callThrough();
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
expect(spy).toHaveBeenCalled();
expect(spy).toHaveBeenCalledWith({
target: 'build',
configuration: 'production',
project: PROJECT
});
});

it('should invoke firebase.deploy', async () => {
const spy = spyOn(firebaseMock, 'deploy').and.callThrough();
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
expect(spy).toHaveBeenCalled();
expect(spy).toHaveBeenCalledWith({
cwd: 'host', only: 'hosting:' + PROJECT
});
});

describe('error handling', () => {
it('throws if there is no firebase project', async () => {
try {
await deploy(firebaseMock, context, 'host')
fail();
} catch (e) {
expect(e.message).toMatch(/Cannot find firebase project/);
}
});

it('throws if there is no target project', async () => {
context.target = undefined;
try {
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT)
fail();
} catch (e) {
expect(e.message).toMatch(/Cannot execute the build target/);
}
});
});
});

const initMocks = () => {
firebaseMock = {
login: () => Promise.resolve(),
list: () => Promise.resolve([]),
deploy: (_: FirebaseDeployConfig) => Promise.resolve(),
use: () => Promise.resolve()
};

context = {
target: {
configuration: 'production',
project: PROJECT,
target: 'foo'
},
builder: {
builderName: 'mock',
description: 'mock',
optionSchema: false
},
currentDirectory: 'cwd',
id: 1,
logger: new logging.NullLogger() as any,
workspaceRoot: 'cwd',
getTargetOptions: (_: Target) => Promise.resolve({}),
reportProgress: (_: number, __?: number, ___?: string) => {
},
reportStatus: (_: string) => {},
reportRunning: () => {},
scheduleBuilder: (_: string, __?: JsonObject, ___?: ScheduleOptions) => Promise.resolve({} as BuilderRun),
scheduleTarget: (_: Target, __?: JsonObject, ___?: ScheduleOptions) => Promise.resolve({} as BuilderRun)
};
};
Loading

0 comments on commit be0a1fb

Please sign in to comment.