Skip to content

feat(schematics): ng deploy schematic #2046

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
May 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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