Skip to content

Commit

Permalink
v0.9.0
Browse files Browse the repository at this point in the history
* fix(core): fix an issue where "no command specified" error was shown erroneously
* feat(core): add `serve` command for serving project matching branch
* chore(tests): add initial tests for `serve` and deploy actions
  • Loading branch information
prescottprue authored Dec 4, 2019
1 parent 5fd3ca7 commit d1ee3dd
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 5 deletions.
37 changes: 35 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -195,29 +196,40 @@ 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
* [`copyVersion`](#copyversion) is called before deployment based on settings in `.firebaserc`, if you don't want this to happen, use simple mode.
* [`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`.

Expand All @@ -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`
Expand Down
2 changes: 1 addition & 1 deletion bin/firebase-ci
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion cmds/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
1 change: 1 addition & 0 deletions cmds/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
31 changes: 31 additions & 0 deletions cmds/serve.js
Original file line number Diff line number Diff line change
@@ -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 <caption>Basic</caption>
* # 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 <targets>',
'Only serve specified targets, comma-seperated (e.g "hosting, storage")'
)
.action(opts => {
return serve(opts)
.then(() => process.exit(0))
.catch(() => process.exit(1))
})
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
82 changes: 82 additions & 0 deletions src/actions/serve.js
Original file line number Diff line number Diff line change
@@ -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)})`
)
}
23 changes: 23 additions & 0 deletions test/unit/actions/deploy.spec.js
Original file line number Diff line number Diff line change
@@ -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
})
})
23 changes: 23 additions & 0 deletions test/unit/actions/serve.spec.js
Original file line number Diff line number Diff line change
@@ -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
})
})

0 comments on commit d1ee3dd

Please sign in to comment.