diff --git a/README.md b/README.md
index ce53e00..4b0bac7 100644
--- a/README.md
+++ b/README.md
@@ -111,7 +111,8 @@ Advanced configuration of Firebase deployment is often necessary when deploying
* [`copyVersion`](#createversion) - Copy version from `package.json` to `functions/package.json`
* [`createConfig`](#createconfig) - Create a config file based on CI environment variables (defaults to `src/config.js`)
-* [`deploy`](#deploy) - Deploy to Firebase (runs other actions by default)
+* [`deploy`](#deploy) - Deploy to Firebase project matching branch name in `.firebaserc` (runs other `firebase-ci` actions by default unless `-s` is passed)
+* [`serve`](#serve) - Serve a the Firebase project matching branch name in `.firebaserc` using `firebase serve`
* [`mapEnv`](#mapenv) - Map environment variables from CI Environment to Firebase functions environment
* [`project`](#project) - Output project name associated with CI environment (useful for commands that should be run for each environment)
@@ -195,10 +196,12 @@ Options can be passed as flags or within an options object if calling action as
**Options:**
* [Simple mode](#simple-mode)
* [Info](#info-option)
+* [Only](#only-option)
Deploy to Firebase. Following the API of `firebase-tools`, specific targets (i.e. `functions, hosting`) can be specified for deployment.
#### Default
+
* Everything skipped on Pull Requests
* Deployment goes to default project
* If you have a `functions` folder, `npm install` will be run for you within your `functions` folder
@@ -206,18 +209,27 @@ Deploy to Firebase. Following the API of `firebase-tools`, specific targets (i.e
* [`mapEnv`](#mapenv) is called before deployment based on settings in `.firebaserc`, if you don't want this to happen, use simple mode.
#### Simple Mode
+
Option: `--simple`
Flag: `-s`
Skip all `firebase-ci` actions and only run Firebase deployment
#### Info Option
+
Option : `--info`
Flag: `-i`
Provide extra information from internal actions (including npm install of `firebase-tools`).
-#### Skipping Deploying Functions
+#### Only Option
+
+Option : `--only`
+Flag: `-o`
+
+Firebase targets to include (passed directly to firebase-tools)
+
+##### Skipping Deploying Functions
If you have a functions folder, your functions will automatically deploy as part of using `firebase-ci`. For skipping this functionality, you may use the only flag, similar to the API of `firebase-tools`.
@@ -226,6 +238,27 @@ script:
- $(npm bin)/firebase-ci deploy --only hosting
```
+### serve
+
+`firebase-ci serve`
+
+**Options:**
+* [only](#only-option)
+
+Serve using to `firebase serve`. Following the API of `firebase-tools`, specific targets (i.e. `functions, hosting`) can be specified for serving.
+
+#### Default
+
+* Project alias matching branch name is served
+* If there is no matching alias, `default` project is used
+
+#### Only Option
+
+Option : `--only`
+Flag: `-o`
+
+Firebase targets to include (passed directly to firebase-tools)
+
### mapEnv
`firebase-ci mapEnv`
diff --git a/bin/firebase-ci b/bin/firebase-ci
index 4ab4e16..93e7dee 100755
--- a/bin/firebase-ci
+++ b/bin/firebase-ci
@@ -20,7 +20,7 @@ program.on('*', function(name) {
program.parse(process.argv)
-if (program.args.length < 1) {
+if (program.rawArgs.length < 3) {
console.log("No command specified. See 'firebase-ci --help':") // eslint-disable-line no-console
program.outputHelp()
process.exit(1)
diff --git a/cmds/deploy.js b/cmds/deploy.js
index 6451b7f..a601f5b 100644
--- a/cmds/deploy.js
+++ b/cmds/deploy.js
@@ -16,7 +16,7 @@ module.exports = function(program) {
program
.command('deploy')
.description(
- 'Deploy to Firebase only on build branches (master, stage, prod)'
+ 'Deploy to Firebase only on build branches with matching project settings in .firebaserc'
)
.option('-d --debug', 'Enable extra logging') // taken by autocmdr
.option('-i --info', 'Extra Info from installs')
diff --git a/cmds/index.js b/cmds/index.js
index 144e01f..ac8af40 100644
--- a/cmds/index.js
+++ b/cmds/index.js
@@ -12,6 +12,7 @@ module.exports = function(client) {
client.mapEnv = loadCommand('mapEnv')
client.run = loadCommand('run')
client.project = loadCommand('project')
+ client.serve = loadCommand('serve')
return client
}
diff --git a/cmds/serve.js b/cmds/serve.js
new file mode 100644
index 0000000..f756fc2
--- /dev/null
+++ b/cmds/serve.js
@@ -0,0 +1,31 @@
+/* deploy commander component
+ * To use add require('../cmds/deploy.js')(program) to your commander.js based node executable before program.parse
+ */
+'use strict'
+const serve = require('../lib/actions/serve').default
+
+/**
+ * @name serve
+ * Serve project using project matching associated branch
+ * @param {object} program - Commander program object
+ * @example
Basic
+ * # make sure FIREBASE_TOKEN env variable is set
+ * firebase-ci deploy
+ */
+module.exports = function(program) {
+ program
+ .command('serve')
+ .description(
+ 'Use firebase serve to serve a project matching branch name settings in .firebaserc'
+ )
+ .option('-d --debug', 'Enable extra logging') // taken by autocmdr
+ .option(
+ '-o --only ',
+ 'Only serve specified targets, comma-seperated (e.g "hosting, storage")'
+ )
+ .action(opts => {
+ return serve(opts)
+ .then(() => process.exit(0))
+ .catch(() => process.exit(1))
+ })
+}
diff --git a/package.json b/package.json
index 94d585f..f6da58a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "firebase-ci",
- "version": "0.8.0",
+ "version": "0.9.0",
"description": "Simplified Firebase interaction for continuous integration including deploying hosting, functions, and database/storage rules.",
"main": "lib/index.js",
"bin": {
diff --git a/src/actions/serve.js b/src/actions/serve.js
new file mode 100644
index 0000000..75daf38
--- /dev/null
+++ b/src/actions/serve.js
@@ -0,0 +1,82 @@
+import { get } from 'lodash'
+import chalk from 'chalk'
+import { error, info, warn } from '../utils/logger'
+import { getFile } from '../utils/files'
+import { to } from '../utils/async'
+import { runCommand } from '../utils/commands'
+import { getProjectKey, getFallbackProjectKey } from '../utils/ci'
+
+const skipPrefix = 'Skipping firebase-ci serve'
+
+/**
+ * Serve specific project
+ * @param {object} opts - Settings for serving
+ * @returns {Promise} Resolves after serve is called
+ */
+export default async function serve(opts) {
+ // Load settings from .firebaserc
+ const settings = getFile('.firebaserc')
+
+ // Get project from passed options, falling back to branch name
+ const fallbackProjectName = getFallbackProjectKey()
+ const projectKey = getProjectKey(opts)
+
+ // Get project setting from settings file based on branchName falling back
+ // to fallbackProjectName
+ const projectName = get(settings, `projects.${projectKey}`)
+ const fallbackProjectSetting = get(
+ settings,
+ `projects.${fallbackProjectName}`
+ )
+
+ // Handle project option
+ if (!projectName) {
+ const nonProjectBranch = `${skipPrefix} - Project ${chalk.cyan(
+ projectKey
+ )} is not an alias, checking for fallback...`
+ warn(nonProjectBranch)
+ if (!fallbackProjectSetting) {
+ const nonFallbackBranch = `${skipPrefix} - Fallback Project: ${chalk.cyan(
+ fallbackProjectName
+ )} is a not an alias, exiting...`
+ warn(nonFallbackBranch)
+ return nonProjectBranch
+ }
+ return null
+ }
+
+ info(
+ `Calling serve for project ${chalk.cyan(projectName)} (alias ${chalk.cyan(
+ projectKey
+ )})`
+ )
+
+ const serveArgs = ['serve', '-P', projectKey]
+ const onlyString = opts && opts.only ? `--only ${opts.only}` : ''
+ if (onlyString) {
+ serveArgs.push(onlyString)
+ }
+
+ // Run command to set functions config
+ const [serveErr] = await to(
+ runCommand({
+ command: 'firebase',
+ args: serveArgs
+ })
+ )
+
+ // Handle errors running functions config
+ if (serveErr) {
+ const errMsg = `Error calling serve for ${chalk.cyan(
+ projectName
+ )} (alias ${chalk.cyan(projectKey)}) :`
+ error(errMsg, serveErr)
+ throw new Error(errMsg)
+ }
+
+ info(
+ `Successfully called serve for project ${chalk.cyan(
+ projectName
+ )} (alias ${chalk.cyan(projectKey)})`
+ )
+}
diff --git a/test/unit/actions/deploy.spec.js b/test/unit/actions/deploy.spec.js
new file mode 100644
index 0000000..6797205
--- /dev/null
+++ b/test/unit/actions/deploy.spec.js
@@ -0,0 +1,23 @@
+import deploy from 'actions/deploy'
+
+describe('deploy action', () => {
+ let logSpy
+ beforeEach(() => {
+ logSpy = sinon.spy(console, 'log')
+ })
+
+ afterEach(() => {
+ logSpy && logSpy.restore()
+ })
+
+ it('exports a function to be used as a command', () => {
+ expect(deploy).to.be.an('function')
+ })
+
+ it('Logs error saying no config found for provided alias', async () => {
+ await deploy({ project: 'asdf' })
+ // first time with: 'ℹ Skipping Firebase Deploy - Project asdf is not an alias, checking for fallback...'
+ // second time with: 'ℹ Skipping Firebase Deploy - Fallback Project: undefined is a not an alias, exiting...'
+ expect(logSpy).to.have.been.calledTwice
+ })
+})
diff --git a/test/unit/actions/serve.spec.js b/test/unit/actions/serve.spec.js
new file mode 100644
index 0000000..95da5e5
--- /dev/null
+++ b/test/unit/actions/serve.spec.js
@@ -0,0 +1,23 @@
+import serve from 'actions/serve'
+
+describe('serve action', () => {
+ let logSpy
+ beforeEach(() => {
+ logSpy = sinon.spy(console, 'log')
+ })
+
+ afterEach(() => {
+ logSpy && logSpy.restore()
+ })
+
+ it('exports a function to be used as a command', () => {
+ expect(serve).to.be.an('function')
+ })
+
+ it('Logs error saying no config found for provided alias', async () => {
+ await serve({ project: 'asdf' })
+ // first time with: '⚠ Warning: Skipping firebase-ci serve - Project asdf is not an alias, checking for fallback...'
+ // second time with: '⚠ Warning: Skipping firebase-ci serve - Fallback Project: undefined is a not an alias, exiting...'
+ expect(logSpy).to.have.been.calledTwice
+ })
+})