diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 06b2d980..00000000 --- a/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -coverage/ -node_modules/ - -.DS_Store -.idea -*.iml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f663f407..00000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: node_js -node_js: -- '0.10' -- '0.12' -- stable -sudo: false -install: -- npm install -script: -- npm run travis -after_script: -- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js diff --git a/LICENSE b/LICENSE deleted file mode 100644 index b9b77e9b..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Firebase - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 517daa0d..00000000 --- a/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# firebase-tools - -[![Build Status](https://travis-ci.org/firebase/firebase-tools.svg?branch=master)](https://travis-ci.org/firebase/firebase-tools) -[![Coverage Status](https://img.shields.io/coveralls/firebase/firebase-tools.svg?branch=master&style=flat)](https://coveralls.io/r/firebase/firebase-tools) -[![NPM version](https://badge.fury.io/js/firebase-tools.svg)](http://badge.fury.io/js/firebase-tools) - -These are the Firebase Command Line (CLI) Tools. They can be used to: - -* Administer your Firebase account -* Interact with [Firebase Hosting](https://www.firebase.com/hosting.html), our product to host your -static HTML, JS, CSS, images, etc. - -To get started with the Firebase CLI, [read through our hosting quickstart guide](https://www.firebase.com/docs/hosting.html). - - -## Installation - -To install the Firebase CLI, you first need to [sign up for a Firebase account](https://www.firebase.com/signup/). - -Then you need to install [Node.js](http://nodejs.org/) and [npm](https://npmjs.org/). Note that -installing Node.js should install npm as well. - -Once npm is installed, get the Firebase CLI by running the following command: - -```bash -npm install -g firebase-tools -``` - -This will provide you with the globally accessible `firebase` command. - - -## Commands - -The command `firebase --help` lists the available commands and `firebase --help` shows -more details for an individual command. - -You can get more information about the available commands in our -[command line documentation](https://www.firebase.com/docs/hosting/command-line-tool.html). - - -## Credit - -Inspired by [Luke Vivier](https://github.com/lvivier/)'s Firebase command line tools. diff --git a/bin/firebase b/bin/firebase deleted file mode 100755 index 4ceeb6b2..00000000 --- a/bin/firebase +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env node - -var argv = require('optimist') - .alias('f', 'firebase') - .alias('h', 'help') - .alias('m', 'message') - .alias('p', 'public') - .alias('r', 'rules') - .alias('s', 'silent') - .alias('t', 'template') - .alias('v', 'version') - .boolean('d') - .boolean('s') - .argv, - prompt = require('prompt'), - firebase = require('../lib/firebase'), - app = require('../lib/app'), - help = require('../lib/help'); - -prompt.override = argv; -prompt.message = ''; -prompt.delimiter = ''; -prompt.colors = false; - -if (argv._.length === 0) { - - if (argv.version) { - help.showVersion(); - } else { - help.showHelp(); - } - -} else { - if ( - argv.h || - argv.hel || - argv.hep || - argv.hepl || - argv.help - ) { - - help.showHelp(argv._[0]); - - } else { - // Top-level router - switch (argv._[0]) { - - // Top-level functionality - - case 'login': - firebase.login(argv); - break; - case 'logout': - firebase.logout(argv); - break; - case 'ls': - case 'list': - firebase.list(argv); - break; - - // Submodules - - case 'init': - case 'initialise': - case 'initialize': - app.init(argv); - break; - case 'bootstrap': - app.bootstrap(argv); - break; - case 'deploy': - app.deploy(argv); - break; - case 'delete-site': - app.deleteSite(argv); - break; - case 'open': - app.open(argv); - break; - - case 'h': - case 'hel': - case 'hep': - case 'hepl': - case 'help': - help.showHelp(); - break; - - default: - console.log('\n' + - 'Command not found. Use `firebase --help` for a list of commands or\n' + - '`firebase --help` for more details\n'); - } - } -} diff --git a/changelog.txt b/changelog.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 03c741c4..00000000 --- a/gulpfile.js +++ /dev/null @@ -1,65 +0,0 @@ -/**************/ -/* REQUIRES */ -/**************/ -var gulp = require('gulp'); - -// File I/O -var exit = require('gulp-exit'); -var jshint = require('gulp-jshint'); - -// Testing -var mocha = require('gulp-mocha'); -var istanbul = require('gulp-istanbul'); - - -/****************/ -/* FILE PATHS */ -/****************/ -var paths = { - js: [ - 'lib/*.js' - ], - - tests: [ - 'test/*.spec.js' - ] -}; - - -/***********/ -/* TASKS */ -/***********/ -// Lints the JavaScript files -gulp.task('lint', function() { - return gulp.src(paths.js) - .pipe(jshint()) - .pipe(jshint.reporter('jshint-stylish')) - .pipe(jshint.reporter('fail')) - .on('error', function(error) { - throw error; - }); -}); - -// Runs the Mocha test suite -gulp.task('test', function() { - return gulp.src(paths.js) - .pipe(istanbul()) - .pipe(istanbul.hookRequire()) - .on('finish', function () { - gulp.src(paths.tests) - .pipe(mocha({ - reporter: 'spec', - timeout: 5000 - })) - .pipe(istanbul.writeReports()) - .pipe(exit()); - }); -}); - -// Reruns the linter every time a JavaScript file changes -gulp.task('watch', function() { - gulp.watch(paths.js, ['lint']); -}); - -// Default task -gulp.task('default', ['lint', 'test']); diff --git a/lib/api.js b/lib/api.js deleted file mode 100644 index 5e8d3bd8..00000000 --- a/lib/api.js +++ /dev/null @@ -1,92 +0,0 @@ -var request = require('request'), - url = require('url'), - querystring = require('querystring'); - -var api = { - realtimeUrl: 'https://firebaseio.com', - adminUrl: 'https://admin.firebase.com', - uploadUrl: 'https://hosting.firebase.com', - hostingUrl: 'https://firebaseapp.com', - request: function(method, resource, data, authenticate, callback) { - - // Runtime fetch of Auth singleton to prevent circular module dependencies - var auth = require('./auth'); - - if ((typeof(data) === 'undefined') || !data) { - data = {}; - } - - if ((typeof(authenticate) !== 'undefined') && (authenticate)) { - data.token = auth.token; - } - - var validMethods = ['GET', 'PUT', 'POST', 'DELETE']; - - if (validMethods.indexOf(method) < 0) { - method = 'GET'; - } - - var options = { - method: method, - json: true - }; - - var dataString = ''; - - if (method === 'GET') { - dataString = querystring.stringify(data); - var separator = resource.match(/\?/) ? '&' : '?'; - resource += separator + dataString; - } else if (Object.keys(data).length > 0) { - options.body = JSON.stringify(data, null, 2); - } - - options.url = this.adminUrl + resource; - - request(options, function(err, response, body) { - if (err) { - console.log('SERVER ERROR'.red + ' - Please try again'); - process.exit(1); - } - setTimeout(callback, 0, response.statusCode, body); - }); - }, - setRules: function(firebase, rules, authToken, callback) { - request({ - url: api.realtimeUrl.replace(/\/\//, '//' + firebase + '.') + '/.settings/rules.json?auth=' + authToken, - method: 'PUT', - headers: { - 'Content-Type': 'application/json' - }, - body: rules - }, function(err, response, body) { - if (err) { - console.log('SERVER ERROR'.red + ' - Please try again'); - process.exit(1); - } - setTimeout(callback, 0, response.statusCode, body && JSON.parse(body)); - }); - } -}; - -function possiblyOverride(apiVariable, envVariable) { - if (typeof(process.env[envVariable]) !== 'undefined') { - var urlParts = null; - try { - urlParts = url.parse(process.env[envVariable]); - } catch (err) { - urlParts = null; - } - - if (urlParts) { - api[apiVariable] = process.env[envVariable]; - } - } -} - -possiblyOverride('adminUrl', 'FIREBASE_ADMIN_URL'); -possiblyOverride('hostingUrl', 'FIREBASE_HOSTING_URL'); -possiblyOverride('realtimeUrl', 'FIREBASE_REALTIME_URL'); -possiblyOverride('uploadUrl', 'FIREBASE_UPLOAD_URL'); - -module.exports = api; diff --git a/lib/app.js b/lib/app.js deleted file mode 100644 index 07637df4..00000000 --- a/lib/app.js +++ /dev/null @@ -1,836 +0,0 @@ -var request = require('request'), - fs = require('fs'), - path = require('path'), - zlib = require('zlib'), - tar = require('tar'), - url = require('url'), - prompt = require('prompt'), - open = require('open'), - Firebase = require('firebase'), - _firebase = require('./firebase'), - ProgressBar = require('progress'), - upload = require('./upload'), - auth = require('./auth'), - api = require('./api'), - util = require('util'), - chalk = require('chalk'), - _when = require('when'), - Json = require('source-processor'); - -var defaultSettings = { - 'public': '.', - 'ignore': ['firebase.json', '**/.*', '**/node_modules/**'] -}; - -function getPrompt(argv, schema, onComplete, index, results) { - if (!Array.isArray(schema)) { - console.log(chalk.red('getPrompt schema must be an Array.')); - process.exit(1); - } - onComplete = typeof onComplete !== 'function' ? function() {} : onComplete; - index = typeof index !== 'number' ? 0 : index; - results = typeof results !== 'object' ? {} : results; - var item = schema[index]; - if (argv.silent) { - if (!prompt.override[item.name] || (item.pattern && !item.pattern.test(prompt.override[item.name]))) { - console.log(chalk.red('Input Error') + ' - Not enough or invalid parameters specified while in silent mode'); - console.log('Required ' + chalk.bold(item.name) + ' parameter missing or invalid'); - process.exit(1); - } - } - if (typeof item.beforeValue === 'function') { - item.beforeValue(results); - } - prompt.get(schema[index], function(error, result) { - if (error) { - console.log(chalk.red('Input Error')); - process.exit(1); - } - results[item.name] = result[item.name]; - if (++index < schema.length) { - getPrompt(argv, schema, onComplete, index, results); - } else { - onComplete(results); - } - }); -} - -function updateRules(settings, tokens) { - return _when.promise(function(resolve, reject, notify) { - auth.updateRules(settings.firebase, - tokens.personalToken, - settings.rules, - function(statusCode, response) { - if (statusCode !== 200 || response.error) { - console.log(chalk.red('Security Rules Error') + ' - ' + - response.error.replace(/\n$/, '')); - process.exit(1); - } - resolve(); - }); - }); -} - -function updateRedirects(firebaseRef, settings) { - return _when.promise(function(resolve, reject, notify) { - firebaseRef.child('hosting/path-redirects').child(settings.firebase).set(settings.redirects, function(err) { - if (err) { - console.log(chalk.red('Settings Error') + ' - Incorrectly formatted "redirects" entry in the firebase.json'); - process.exit(1); - } - resolve(); - }); - }); -} - -function updateRewrites(firebaseRef, settings) { - return _when.promise(function(resolve, reject, notify) { - firebaseRef.child('hosting/rewrites').child(settings.firebase).set(settings.rewrites, function(err) { - if (err) { - console.log(chalk.red('Settings Error') + ' - Incorrectly formatted "rewrites" entry in the firebase.json'); - process.exit(1); - } - resolve(); - }); - }); -} - -function updateHeaders(firebaseRef, settings) { - return _when.promise(function(resolve, reject, notify) { - firebaseRef.child('hosting/headers').child(settings.firebase).set(settings.headers, function(err) { - if (err) { - console.log(chalk.red('Settings Error') + ' - Incorrectly formatted "headers" entry in the firebase.json'); - process.exit(1); - } - resolve(); - }); - }); -} - -function validateRedirects(redirects) { - var error = null; - if (redirects) { - if (!Array.isArray(redirects)) { - error = 'Redirects entry in the firebase.json must be an Array.'; - } else { - for (var i = 0; i < redirects.length; i++) { - var redirect = redirects[i]; - if (typeof redirect !== 'object') { - error = 'Redirect rule: ' + JSON.stringify(redirect) + ' Must be an object.'; - } else if (!redirect.source || typeof redirect.source !== 'string' || redirect.source.length === 0) { - error = 'Redirect rule: ' + JSON.stringify(redirect) + ' Must contain a "source" attribute that\'s a non-empty string.'; - } else if (!redirect.destination || typeof redirect.destination !== 'string' || redirect.destination.length === 0) { - error = 'Redirect rule: ' + JSON.stringify(redirect) + ' Must contain a "destination" attribute that\'s a non-empty string.'; - } else if (!/^(\/[^\s]*|https?:\/\/[^\s]+)$/.test(redirect.destination)) { - error = 'Redirect destination: "' + redirect.destination + '" Must be a remote or absolute url and start with "http", "https", or a "/".'; - } else if (redirect.type !== 301 && redirect.type !== 302) { - error = 'Redirect rule: ' + JSON.stringify(redirect) + ' Must have a redirect "type" that\'s either 301 for a permanent redirect or 302 for a temporary redirect.'; - } else if (Object.keys(redirect).length > 3) { - error = 'Redirect rule: ' + JSON.stringify(redirect) + ' Must not contain any keys other than "source", "destination", or "type".'; - } - if (error) { - break; - } - } - } - } - if (error) { - console.log(chalk.red('Settings Error') + ' - ' + error); - process.exit(1); - } -} - -function validateRewrites(rewrites) { - var error = null; - if (rewrites) { - if (!Array.isArray(rewrites)) { - error = 'Rewrites entry in the firebase.json must be an Array.'; - } else { - for (var i = 0; i < rewrites.length; i++) { - var rewrite = rewrites[i]; - if (typeof rewrite !== 'object') { - error = 'Rewrite rule: ' + JSON.stringify(rewrite) + ' Must be an object.'; - } else if (!rewrite.source || typeof rewrite.source !== 'string' || rewrite.source.length === 0) { - error = 'Rewrite rule: ' + JSON.stringify(rewrite) + ' Must contain a "source" attribute that\'s a non-empty string.'; - } else if (!rewrite.destination || typeof rewrite.destination !== 'string' || rewrite.destination.length === 0) { - error = 'Rewrite rule: ' + JSON.stringify(rewrite) + ' Must contain a "destination" attribute that\'s a non-empty string.'; - } else if (!/^\/[^\s]*[^\/\s]$/.test(rewrite.destination)) { - error = 'Rewrite destination: "' + rewrite.destination + '" Must be an absolute path to a file that starts with a "/" and does not end in a "/".'; - } else if (Object.keys(rewrite).length > 2) { - error = 'Rewrite rule: ' + JSON.stringify(rewrite) + ' Must not contain any keys other than "source" or "destination".'; - } - if (error) { - break; - } - } - } - } - if (error) { - console.log(chalk.red('Settings Error') + ' - ' + error); - process.exit(1); - } -} - -function validateHeaders(headers) { - var error = null, - supportedHeaders = [ - 'Access-Control-Allow-Origin', - 'Cache-Control', - 'X-UA-Compatible', - 'X-Content-Type-Options', - 'X-Frame-Options', - 'X-XSS-Protection' - ]; - if (headers) { - if (!Array.isArray(headers)) { - error = 'Headers entry in the firebase.json must be an Array.'; - } else { - for (var i = 0; i < headers.length; i++) { - var header = headers[i]; - if (typeof header !== 'object') { - error = 'Header rule: ' + JSON.stringify(header) + ' Must be an object.'; - } else if (!header.source || typeof header.source !== 'string' || header.source.length === 0) { - error = 'Header rule: ' + JSON.stringify(header) + ' Must contain a "source" attribute that\'s a non-empty string.'; - } else if (!header.headers || !Array.isArray(header.headers)) { - error = 'Header rule: ' + JSON.stringify(header) + ' Must contain a "headers" attribute that\'s an array.'; - } else if (Object.keys(header).length > 2) { - error = 'Header rule: ' + JSON.stringify(header) + ' Must not contain any keys other than "source" or "headers".'; - } - if (!error) { - for (var j = 0; j < header.headers.length; j++) { - var individualHeader = header.headers[j]; - if (typeof individualHeader !== 'object') { - error = 'Header: ' + JSON.stringify(individualHeader) + ' Must be an object'; - } else if (!individualHeader.key || typeof individualHeader.key !== 'string' || individualHeader.key.length === 0) { - error = 'Header: ' + JSON.stringify(individualHeader) + ' Must contain a "key" field that\'s one of: "' + supportedHeaders.join('", "') + '"'; - } else if (!individualHeader.value || typeof individualHeader.value !== 'string' || individualHeader.value.length === 0) { - error = 'Header: ' + JSON.stringify(individualHeader) + ' Must contain a "value" field that\'s a non-empty string.'; - } else if (supportedHeaders.indexOf(individualHeader.key) < 0) { - error = 'Header key: "' + individualHeader.key + '" is not supported. Supported keys are: "' + supportedHeaders.join('", "') + '"'; - } else if (Object.keys(individualHeader).length > 2) { - error = 'Header: ' + JSON.stringify(individualHeader) + ' Must not contain any keys other than "key" or "value".'; - } - if (error) { - break; - } - } - } - if (error) { - break; - } - } - } - } - if (error) { - console.log(chalk.red('Settings Error') + ' - ' + error); - process.exit(1); - } -} - -function handleFailedDeploy(defaultError) { - return function(err) { - if (err) { - var detailedMessage = chalk.red('Deploy Error') + ' - '; - if (typeof(err.message) !== 'undefined') { - detailedMessage += err.message + ' '; - } - if (typeof(err.details) !== 'undefined') { - detailedMessage += err.details; - } - if (detailedMessage.length === 0) { - detailedMessage = defaultError; - } - console.log(detailedMessage); - process.exit(1); - } - }; -} - -function uploadSite(settings, directoryRef, argv) { - return function() { - var bar = null; - var total = 0; - directoryRef.on('value', function(snapshot) { - var status = snapshot.child('status').val(); - if (status === 'deployed') { - var url = api.hostingUrl.replace(/\/\//, util.format('//%s.', settings.firebase)); - console.log(chalk.green('Successfully deployed')); - console.log('Site URL: %s, or use %s', chalk.cyan(url), chalk.bold('firebase open')); - console.log('Hosting Dashboard: %s then view the hosting section of your app', chalk.cyan('https://firebase.com/account')); - process.exit(0); - } else if (status === 'deploying') { - if (!bar && snapshot.hasChild('fileCount')) { - total = snapshot.child('fileCount').val(); - bar = new ProgressBar(chalk.yellow('progress: :percent'), { - total: total - }); - } - if (bar) { - var uploadedCount = snapshot.hasChild('uploadedCount') ? snapshot.child('uploadedCount').val() : 0; - bar.update(Math.floor(100 * uploadedCount / total) / 100); - } - } else if (status === 'removed') { - console.log(chalk.green('Successfully removed')); - process.exit(0); - } else if (status === 'failed') { - if (bar) { - bar.terminate(); - } - var message = chalk.red('Deploy Failed'); - if (snapshot.hasChild('statusMessage')) { - message += ' - ' + snapshot.child('statusMessage').val(); - } - console.log(message); - process.exit(1); - } - }); - - var message = null; - if (argv.message && (typeof(argv.message) === 'string')) { - message = argv.message; - } - - upload.send(settings.firebase, settings.public, settings.ignore, directoryRef.key(), message, handleFailedDeploy('Couldn\'t upload site')); - }; -} - -function getProjectRootDir() { - var projectRootDir = process.cwd(); - while (!fs.existsSync(path.resolve(projectRootDir, './firebase.json'))) { - var parentDir = path.dirname(projectRootDir); - if (parentDir === projectRootDir) { - return null; - } - projectRootDir = parentDir; - } - return projectRootDir; -} - -function getSettings(argv, projectRootDir) { - if (typeof projectRootDir === 'undefined') { - projectRootDir = getProjectRootDir(); - if (projectRootDir === null) { - console.log(chalk.red('Initialization Error') + ' - Directory not ' + - 'initialized'); - process.exit(1); - } - } - defaultSettings.public = projectRootDir; - var settingsFile = path.resolve(projectRootDir, './firebase.json'); - var settingsJSON, settings; - try { - settingsJSON = fs.readFileSync(settingsFile, {encoding: 'utf8'}).toString(); - settings = Json.parse(settingsJSON).toJSON(); - } catch (err) { - var location = new Json.TextLocation(err.at); - console.log(chalk.red('Initialization Error') + ' - Failed to parse ' + - 'firebase.json settings file at row = ' + location.row() + - ', column = ' + location.col() + ' : ' + err.message); - process.exit(1); - } - util._extend(settings, argv); - - return settings; -} - -function mkDirs(filePath) { - var filePathParts = filePath.split(path.sep); - for (var i = 1; i < filePathParts.length; i++) { - var folderPath = filePathParts.slice(0, i).join(path.sep); - if (!fs.existsSync(folderPath)) { - fs.mkdirSync(folderPath); - } - } -} - -module.exports = { - init: function(argv) { - var curDir = process.cwd(); - var homeDir = process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME']; - var isCurDirEmpty = fs.readdirSync(curDir).length === 0; - if (path.relative(homeDir, curDir).match(/^\.\./)) { - console.log(chalk.yellow('Initialization Warning') + ' - Creating new firebase app ' + - 'outside your home directory'); - } - if (homeDir === curDir) { - console.log(chalk.yellow('Initialization Warning') + ' - Creating new firebase app ' + - 'directly at your home directory'); - } - if (!isCurDirEmpty) { - console.log(chalk.yellow('Initialization Warning') + ' - Creating new firebase app ' + - 'inside a non-empty directory.'); - } - - var projectRootDir = getProjectRootDir(); - if (projectRootDir !== null) { - console.log(chalk.red('Initialization Error') + ' - Can\'t create new firebase app ' + - 'inside an existing firebase project'); - process.exit(1); - } - auth.listFirebases(argv).then(function(res) { - - var settingsFile = path.resolve('./firebase.json'); - if (fs.existsSync(settingsFile)) { - console.log(chalk.yellow('Directory already initialized')); - console.log('You can edit your settings in firebase.json or delete this file to start over'); - process.exit(1); - } - if (res.firebases.length === 0) { - console.log(chalk.yellow('You have no apps in your Firebase account')); - console.log('Sign in to %s and create an app', chalk.cyan('https://firebase.com')); - console.log('then initialize a directory for hosting'); - process.exit(1); - } - - // Firebase names always a subset of ^[0-9a-z-]*$ so safe to regex - var validFirebasePattern = new RegExp('^(' + res.firebases.join('|') + ')$'); - if (!argv.firebase || (typeof(argv.firebase) !== 'string') || !argv.firebase.match(validFirebasePattern)) { - res.showFirebases(); - } - - var schemaFirebase = { - name: 'firebase', - required: true, - pattern: validFirebasePattern, - beforeValue: function() { - if (!argv.firebase || (typeof(argv.firebase) !== 'string') || !argv.firebase.match(validFirebasePattern)) { - console.log('Enter the name of the Firebase app you would like to use for hosting'); - } - }, - message: 'Please choose an app from the list above', - description: chalk.bold('Firebase app'), - type: 'string' - }; - var schemaPublic = { - name: 'public', - required: true, - beforeValue: function(results) { - console.log(chalk.yellow('----------------------------------------------------')); - console.log(chalk.yellow('Site URL: %s'), api.hostingUrl.replace(/\/\//, util.format('//%s.', results.firebase))); - console.log(chalk.yellow('----------------------------------------------------')); - if (!argv.public || (typeof(argv.public) !== 'string')) { - console.log('Enter the name of your app\'s public directory.'); - console.log('(usually where you store your index.html file)'); - } - }, - description: chalk.bold('Public Directory'), - default: 'current directory', - type: 'string', - before: function(value) { - if (value === 'current directory') { - return '.'; - } - else { - return value; - } - }, - conform: function(value) { - value = schemaPublic.before(value); - if (path.relative('.', value).match(/^\./)) { - schemaPublic.message = 'Please use a directory within the current working directory'; - return false; - } - if (!fs.existsSync(value)) { - schemaPublic.message = 'The specified directory must exist'; - return false; - } - return true; - } - }; - - getPrompt(argv, [schemaFirebase, schemaPublic], function(results) { - var settings = { - 'firebase': results.firebase, - 'public': results.public, - 'ignore': defaultSettings.ignore - }; - console.log('Initializing app into current directory...'); - auth.checkCanAccess(results.firebase, function(err) { - if (err) { - console.log(chalk.red('Permission Error') + ' - you do not have permission to use this Firebase'); - process.exit(1); - } - console.log('Writing firebase.json settings file...'); - var settingsJSON = JSON.stringify(settings, null, 2) + '\n'; - try { - fs.writeFileSync(settingsFile, settingsJSON); - console.log(chalk.green('Successfully initialized app')); - console.log('To deploy: %s', chalk.bold('firebase deploy')); - } catch (error) { - console.log(chalk.red('init failed - firebase.json could not be created')); - console.log(chalk.yellow('You may want to review the permissions for this directory')); - console.log(chalk.yellow('For help, please contact support@firebase.com')); - process.exit(1); - } - }); - }); - }, function(error) { - console.log(chalk.yellow('We\'re really sorry, but we are currently experiencing issues connecting to Firebase.')); - console.log(chalk.yellow('Please try again later. If this problem continues, please contact support@firebase.com.')); - process.exit(1); - }); - }, - bootstrap: function(argv) { - var projectRootDir = getProjectRootDir(); - if (projectRootDir !== null) { - console.log(chalk.red('Initialization Error') + ' - Can\'t create new firebase app ' + - 'inside an existing firebase project'); - process.exit(1); - } - _when.join(this.getTemplates(), auth.listFirebases(argv)).done(function(resultSet) { - var supportedTemplates = resultSet[0], - res = resultSet[1]; - - if (res.firebases.length === 0) { - console.log(chalk.yellow('You have no apps in your Firebase account')); - console.log('Sign in to %s and create an app', chalk.cyan('https://firebase.com')); - console.log('then initialize a directory for hosting'); - process.exit(1); - } - - // Firebase names always a subset of ^[0-9a-z-]*$ so safe to regex - var templateList = Object.keys(supportedTemplates).sort(); - var firebasePattern = new RegExp('^(' + res.firebases.join('|') + ')$'); - var templatePattern = new RegExp('^(' + templateList.join('|') + ')$'); - if (!argv.firebase || (typeof(argv.firebase) !== 'string') || !argv.firebase.match(firebasePattern)) { - res.showFirebases(); - } - - var schema = [{ - name: 'firebase', - required: true, - pattern: firebasePattern, - beforeValue: function() { - if (!argv.firebase || (typeof(argv.firebase) !== 'string') || !argv.firebase.match(firebasePattern)) { - console.log('Enter the name of the Firebase app you would like to use for hosting'); - } - }, - description: chalk.bold('Firebase app:'), - type: 'string' - },{ - name: 'template', - required: true, - pattern: templatePattern, - beforeValue: function(results) { - if (!argv.template || (typeof(argv.template) !== 'string') || !argv.template.match(templatePattern)) { - console.log(chalk.yellow('----------------------------------------------------')); - console.log(chalk.yellow('Available Templates')); - console.log(chalk.yellow('----------------------------------------------------')); - - var longestTemplateLength = 0; - for (var i = 0; i < templateList.length; i++) { - if (templateList[i].length > longestTemplateLength) { - longestTemplateLength = templateList[i].length; - } - } - for (var j = 0; j < templateList.length; j++) { - var key = templateList[j], - template = supportedTemplates[key]; - var output = chalk.bold(key); - if (template.description) { - var spacingString = ''; - for (var k = longestTemplateLength; k > key.length; k--) { - spacingString += ' '; - } - output += spacingString + ' - ' + template.description; - } - console.log(output); - } - - console.log(chalk.yellow('----------------------------------------------------')); - console.log('Choose a template to help you get started with your app'); - } - }, - description: chalk.bold('Template:'), - type: 'string' - }]; - - getPrompt(argv, schema, function(results) { - var firebase = results.firebase; - var dir = firebase; - var projectDir = path.resolve(dir); - if (fs.existsSync(projectDir)) { - var i = 1; - do { - dir = firebase + '_' + i++; - projectDir = path.resolve(dir); - } while (fs.existsSync(projectDir)); - } - - results.directory = dir; - console.log('Bootstrapping into directory \'' + dir + '\'...'); - - // Load the project root if defined, and gracefully handle missing '/' - var templateRoot = (supportedTemplates[results.template].templateRoot || '/').replace(/\//g, path.sep); - if (templateRoot.length > 0 && templateRoot[0] === path.sep) { - templateRoot = templateRoot.slice(1); - } - if (templateRoot.length > 1 && templateRoot.slice(-1) !== path.sep) { - templateRoot += path.sep; - } - - console.log('Downloading and unpacking template...'); - var gunzip = zlib.createGunzip(); - var untar = tar.Parse(); - - var outStandingFiles = 1; - - function fileFinished() { - outStandingFiles -= 1; - if (outStandingFiles <= 0) { - var configFiles = supportedTemplates[results.template].configFiles || []; - for (var i = 0; i < configFiles.length; i++) { - var config = path.join( - projectDir, - configFiles[i] - ); - try { - var data = fs.readFileSync(config, 'utf8'), - realtimeHost = api.realtimeUrl.replace(/\/\//, '//' + firebase + '.'); - var replaced = data.replace( - new RegExp(supportedTemplates[results.template].configRegex, 'g'), - realtimeHost - ); - fs.writeFileSync(config, replaced); - } catch (err) { - console.log(chalk.red('Initialization Error') + ' - Couldn\'t update template with project settings'); - process.exit(1); - } - } - - console.log('Writing firebase.json settings file...'); - var settings = { - 'firebase': firebase, - 'public': defaultSettings.public, - 'ignore': defaultSettings.ignore - }; - - if (supportedTemplates[results.template].settings) { - if (supportedTemplates[results.template].settings.public) { - settings.public = supportedTemplates[results.template].settings.public; - } - if (supportedTemplates[results.template].settings.rules) { - settings.rules = supportedTemplates[results.template].settings.rules; - } - if (supportedTemplates[results.template].settings.ignore) { - settings.ignore = supportedTemplates[results.template].settings.ignore; - } - } - - var settingsJSON = JSON.stringify(settings, null, 2) + '\n'; - var settingsFile = path.join(projectDir, 'firebase.json'); - try { - fs.writeFileSync(settingsFile, settingsJSON); - } catch (err) { - console.log(chalk.red('Filesystem Error') + ' - Could not save settings file'); - process.exit(1); - } - - console.log(chalk.green('Successfully added template')); - console.log('To deploy: %s then %s', chalk.bold(util.format('cd %s', results.directory + path.sep)), chalk.bold('firebase deploy')); - } - } - - var extraction = request(supportedTemplates[results.template].tarball).pipe(gunzip).pipe(untar); - - extraction.on('error', function(err) { - console.log(chalk.red('Download Error') + ' - Could not download ' + - 'template'); - process.exit(1); - }); - - extraction.on('entry', function(entry) { - if (entry.type === 'File') { - var key = path.normalize(entry.path).split(path.sep).slice(1).join(path.sep); - var pattern = new RegExp('^' + templateRoot.replace(/\\/g, '\\\\') + '(.+)$'); - var match = key.match(pattern); - if (match && match.length > 1) { - outStandingFiles += 1; - var outputPath = dir + path.sep + match[1]; - mkDirs(outputPath); - entry.pipe(fs.createWriteStream(outputPath)).on('finish', fileFinished); - } - } - }); - - extraction.on('end', fileFinished); - }); - }, function(error) { - switch (error.type) { - case 'LOGIN': - console.log(chalk.red('Login Error')); - process.exit(1); - break; - case 'GET-TEMPLATES': - console.log(chalk.red('Bootstrapping Error: ') + 'Could not retrieve available templates.'); - process.exit(1); - break; - case 'PARSE-TEMPLATES': - console.log(chalk.red('Bootstrapping Error: ') + 'Could not parse available templates.'); - process.exit(1); - break; - default: - console.log(chalk.red('Bootstrapping Error')); - process.exit(1); - } - }); - }, - deploy: function(argv) { - auth.requireLogin(argv, function(err) { - if (err) { - console.log(chalk.red('Login Error')); - process.exit(1); - } - var projectRootDir = getProjectRootDir(); - if (projectRootDir === null) { - console.log(chalk.red('Initialization Error') + ' - Directory not ' + - 'initialized'); - process.exit(1); - } - var settings = getSettings(argv, projectRootDir); - if (typeof(settings.firebase) !== 'string') { - console.log(chalk.red('Initialization Error') + ' - Could not read ' + - 'firebase.json settings file'); - process.exit(1); - } - auth.checkCanAccess(settings.firebase, function(err, tokens) { - if (err) { - console.log(chalk.red('Permission Error') + ' - You do not have ' + - 'permission to use this Firebase'); - process.exit(1); - } - for (var i in defaultSettings) { - if (defaultSettings.hasOwnProperty(i)) { - if (!settings.hasOwnProperty(i)) { - settings[i] = defaultSettings[i]; - } else if (i === 'public') { - settings.public = path.resolve(projectRootDir, settings.public); - } - } - } - - console.log(chalk.cyan('Public Directory: ') + chalk.bold(settings.public)); - if (path.relative(projectRootDir, settings.public).match(/^\.\./)) { - console.log(chalk.red('Public Directory Error') + ' - public directory' + - ' must be within directory containing firebase.json'); - process.exit(1); - } - if (!fs.existsSync(settings.public)) { - console.log(chalk.red('Public Directory Error') + ' - Public directory ' + - 'does not exist'); - process.exit(1); - } - - settings.rewrites = settings.rewrites || null; - validateRewrites(settings.rewrites); - settings.redirects = settings.redirects || null; - validateRedirects(settings.redirects); - settings.headers = settings.headers || null; - validateHeaders(settings.headers); - - var firebaseRef = new Firebase(api.realtimeUrl.replace(/\/\//, '//firebase.')); - firebaseRef.authWithCustomToken(tokens.firebaseToken, function(error, result) { - if (error) { - console.log('Firebase authentication failed!'); - process.exit(1); - } - }); - var directoryRef = firebaseRef - .child('hosting/versions') - .child(settings.firebase) - .push(); - - _when.join(updateRules(settings, tokens), - updateRedirects(firebaseRef, settings), - updateRewrites(firebaseRef, settings), - updateHeaders(firebaseRef, settings)) - .done(uploadSite(settings, directoryRef, argv)); - }); - }); - }, - deleteSite: function(argv) { - auth.requireLogin(argv, function(err) { - if (err) { - console.log(chalk.red('Login Error')); - process.exit(1); - } - var settings = getSettings(argv); - if (typeof(settings.firebase) !== 'string') { - console.log(chalk.red('Initialization Error') + ' - Could not read ' + - 'firebase.json settings file'); - process.exit(1); - } - auth.checkCanAccess(settings.firebase, function(err, tokens) { - if (err) { - console.log(chalk.red('Permission Error') + ' - You do not have ' + - 'permission to use this Firebase'); - process.exit(1); - } - var firebaseRef = new Firebase(api.realtimeUrl.replace(/\/\//, '//firebase.')); - firebaseRef.authWithCustomToken(tokens.firebaseToken, function(error, result) { - if (error) { - console.log('Firebase authentication failed!'); - process.exit(1); - } - }); - var directoryRef = firebaseRef - .child('hosting/versions') - .child(settings.firebase) - .push(); - directoryRef.on('value', function(snapshot) { - var status = snapshot.child('status').val(); - if (status === 'removed') { - console.log(chalk.green('Sucessfully removed')); - process.exit(0); - } else if (status === 'failed') { - var message = chalk.red('Deploy Failed'); - if (snapshot.hasChild('statusMessage')) { - message += ' - ' + snapshot.child('statusMessage').val(); - } - console.log(message); - process.exit(1); - } - }); - - var message = null; - if (argv.message && (typeof(argv.message) === 'string')) { - message = argv.message; - } - - upload.deleteSite(settings.firebase, directoryRef.key(), message, handleFailedDeploy('Couldn\'t delete site')); - }); - }); - }, - open: function(argv) { - var settings = getSettings(argv); - if (typeof(settings.firebase) !== 'string') { - console.log(chalk.red('Initialization Error') + ' - Could not read ' + - 'firebase.json settings file'); - process.exit(1); - } - open(api.hostingUrl.replace(/\/\//, util.format('//%s.', settings.firebase))); - }, - getTemplates: function() { - return _when.promise(function(resolve, reject, notify) { - request('https://firebase-public.firebaseio.com/cli-templates.json', function(error, response, body) { - if (error) { - error.type = 'GET-TEMPLATES'; - return reject(error); - } - - try { - var templates = JSON.parse(body); - for (var key in templates) { - if (!templates[key].enabled) { - delete templates[key]; - } - } - resolve(templates); - } catch (e) { - error.type = 'PARSE-TEMPLATES'; - return reject(error); - } - }); - }); - } -}; diff --git a/lib/auth.js b/lib/auth.js deleted file mode 100644 index f9f62d0a..00000000 --- a/lib/auth.js +++ /dev/null @@ -1,329 +0,0 @@ -var prompt = require('prompt'), - fs = require('fs'), - path = require('path'), - api = require('./api'), - util = require('util'), - chalk = require('chalk'), - _when = require('when'); - -var auth = { - configFile: path.join( - (process.env.HOME || process.env.USERPROFILE), - '.firebaserc' - ), - maxRetries: 3, - requireLogin: function(argv, callback) { - var that = this; - - if (argv.email && argv.password) { - this._attemptLogin(argv, this.maxRetries, callback); - } else if ((this.email.length === 0) || (this.token.length === 0)) { - console.log('Please sign into your Firebase account to continue...'); - this._attemptLogin(argv, this.maxRetries, callback); - } else { - this._validate(function(err) { - if (err) { - console.log('Please sign into your Firebase account to continue...'); - that._attemptLogin(argv, that.maxRetries, callback); - } else { - setTimeout(callback, 0, null, that.email, that.token); - } - }); - } - }, - _validate: function(callback) { - api.request( - 'GET', - '/token/validate', - { - cli: require('./firebase').version - }, - true, - function(statusCode, response) { - if (typeof(callback) === 'function') { - if (typeof(response.error) !== 'undefined') { - console.log(chalk.red(response.error.message) + ' - Please update to at ' + - 'least v' + response.minCLI + ' by running ' + - chalk.cyan('npm update -g ' + require('./firebase').name)); - process.exit(1); - } else if (response.valid) { - setTimeout(callback, 0, null); - } else { - setTimeout(callback, 0, new Error('Invalid Access Token')); - } - } - } - ); - }, - login: function(argv, callback) { - this._attemptLogin(argv, this.maxRetries, callback); - }, - _attemptLogin: function(argv, tries, callback) { - var that = this; - if (tries > 0) { - if (tries !== this.maxRetries) { - if (argv.silent) { - console.log(chalk.red('Input Error') + ' - Email or password incorrect'); - process.exit(1); - } - console.log('Email or password incorrect, please try again'); - delete prompt.override.email; - delete prompt.override.password; - } - this._loginRequest(argv, function(err, email, token) { - if (err) { - that._attemptLogin(argv, tries - 1, callback); - } else { - setTimeout(callback, 0, null, email, token); - } - }); - } else { - console.log(chalk.red('Login Unsuccessful')); - process.exit(1); - } - }, - _loginRequest: function(argv, callback) { - var that = this, - schema = [ - { - name: 'email', - description: 'Email:', - pattern: /@/, - message: 'Must be a valid email address', - required: true, - type: 'string' - },{ - name: 'password', - description: 'Password:', - hidden: true, - required: true, - type: 'string' - } - ]; - - if (this.email.length > 0) { - schema[0].default = this.email; - } - - if (argv.silent) { - for (var i in schema) { - var item = schema[i]; - if (!prompt.override[item.name] || (item.pattern && !item.pattern.test(prompt.override[item.name]))) { - console.log(chalk.red('Input Error') + ' - Not enough or invalid parameters specified while in silent mode'); - console.log('Required ' + chalk.bold(item.name) + ' parameter missing or invalid'); - process.exit(1); - } - } - } - - prompt.get(schema, function(err, result) { - if (err) { - if (typeof(callback) === 'function') { - setTimeout(callback, 0, new Error('Error Getting User Input')); - } - return; - } - that.email = result.email; - that._authenticate.bind(that)(result.email, result.password, callback); - }); - }, - _authenticate: function(email, password, callback) { - var data = { - email: email, - password: password, - rememberMe: true, - cli: require('./firebase').version - }; - api.request( - 'GET', - '/account/login', - data, - false, - this._handleLogin.bind(this, callback) - ); - }, - _handleLogin: function(callback, statusCode, response) { - var token = response.adminToken; - if (token) { - this.token = token; - this.saveConfig(callback); - } else if (typeof(response.error) !== 'undefined') { - if (typeof(response.minCLI) === 'undefined') { - if (typeof(callback) === 'function') { - setTimeout(callback, 0, new Error('Email or Password Invalid')); - } - } else { - console.log(chalk.red(response.error.message) + ' - Please update to at ' + - 'least v' + response.minCLI + ' by running ' + - chalk.cyan('npm update -g ' + require('./firebase').name)); - process.exit(1); - } - } - }, - saveConfig: function(callback) { - var data = { - email: this.email, - token: this.token - }; - var dataString = JSON.stringify(data, null, 2) + '\n'; - try { - fs.writeFileSync(this.configFile, dataString); - } catch (err) { - if (typeof(callback) === 'function') { - setTimeout(callback, 0, new Error('Could Not Save Settings')); - } - return; - } - if (typeof(callback) === 'function') { - setTimeout(callback, 0, null, this.email, this.token); - } - }, - logout: function(deleteSettings, callback) { - var that = this; - this._invalidateToken(function(err) { - if (err) { - setTimeout(callback, 0, err); - } - if (deleteSettings) { - try { - fs.unlinkSync(that.configFile); - } catch (e) { - setTimeout(callback, 0, e); - return; - } - setTimeout(callback, 0, null); - } else { - that.token = ''; - that.saveConfig(callback); - } - }); - }, - _invalidateToken: function(callback) { - if (this.token.length > 0) { - var url = '/account/token'; - api.request('DELETE', url, {}, true, function(statusCode, response) { - setTimeout(callback, 0, null); - }); - } else { - if (typeof(callback) === 'function') { - setTimeout(callback, 0, null); - } - } - }, - checkCanAccess: function(firebase, callback) { - var url = '/firebase/' + firebase + '/token'; - api.request('GET', url, {}, true, function(statusCode, response) { - if (!response.error) { - setTimeout(callback, 0, null, response); - } else { - setTimeout(callback, 0, new Error('Permission Denied')); - } - }); - }, - updateRules: function(firebase, authToken, rules, callback) { - var rulesString; - if (rules) { - if (!fs.existsSync(rules)) { - console.log(chalk.red('Security Rules Error') + ' - specified security' + - ' rules file does not exist'); - process.exit(1); - } - try { - rulesString = fs.readFileSync(rules, 'utf8'); - } catch (err) { - console.log(chalk.red('Security Rules Error') + ' - couldn\'t read security ' + - 'rules'); - process.exit(1); - } - if (rulesString.length === 0) { - console.log(chalk.red('Security Rules Error') + ' - couldn\'t read security ' + - 'rules'); - process.exit(1); - } - console.log('Updating security rules...'); - api.setRules(firebase, rulesString, authToken, callback); - } else { - setTimeout(callback, 0, 200, {}); - } - }, - getFirebases: function(callback) { - api.request('GET', '/account', {}, true, function(statusCode, response) { - if (typeof(response.firebases) !== 'undefined') { - var firebases = []; - for (var firebase in response.firebases) { - if (response.firebases.hasOwnProperty(firebase)) { - firebases.push(firebase); - } - } - if (typeof(callback) !== 'undefined') { - setTimeout(callback, 0, null, firebases); - } - } else { - if (typeof(callback) !== 'undefined') { - setTimeout(callback, 0, new Error('Could not get list of Firebases')); - } - } - }); - }, - getConfig: function() { - var config = {}; - var data = fs.readFileSync(this.configFile, 'utf8'); - try { - config = JSON.parse(data); - } catch (e) {} - return config; - }, - listFirebases: function(argv) { - return _when.promise(function(resolve, reject, notify) { - auth.requireLogin(argv, function(err) { - if (err) { - var error = new Error('Login Unsuccessful'); - error.type = 'LOGIN'; - reject(error); - } else { - auth.getFirebases(function(err, firebases) { - if (err) { - var error = new Error('Could Not List Firebases'); - error.type = 'ACCOUNT'; - reject(error); - } else { - resolve({ - firebases: firebases, - showFirebases: function() { - console.log(chalk.yellow('----------------------------------------------------')); - console.log(chalk.yellow(util.format('Your Firebase Apps %s', auth.email))); - console.log(chalk.yellow('----------------------------------------------------')); - console.log(firebases.join('\n')); - console.log(chalk.yellow('----------------------------------------------------')); - } - }); - } - }); - } - }); - }); - } -}; - -function initAuth() { - try { - var config = auth.getConfig(); - if (typeof(config.token) === 'string') { - auth.token = config.token; - } else { - auth.token = ''; - } - if (typeof(config.email) === 'string') { - auth.email = config.email; - } else { - auth.email = ''; - } - } catch (err) { - auth.token = ''; - auth.email = ''; - } -} - -initAuth(); - -module.exports = auth; diff --git a/lib/firebase.js b/lib/firebase.js deleted file mode 100644 index e6ef8dbb..00000000 --- a/lib/firebase.js +++ /dev/null @@ -1,41 +0,0 @@ -var auth = require('./auth'), - packageInfo = require('../package.json'), - chalk = require('chalk'); - -module.exports = { - login: function(argv) { - auth.login(argv, function(err) { - if (err) { - console.log(chalk.red('Login Unsuccessful')); - process.exit(1); - } else { - console.log(chalk.green('Login Successful')); - } - }); - }, - logout: function(argv) { - auth.logout(argv.d, function(err) { - if (err) { - console.log(chalk.red('Log Out Unsuccessful')); - process.exit(1); - } else { - console.log(chalk.green('Log Out Successful')); - } - }); - }, - list: function(argv) { - auth.listFirebases(argv).then(function(res) { - res.showFirebases(); - }, function(error) { - switch (error.type) { - case 'LOGIN': - console.log(chalk.red('Login Unsuccessful')); - break; - default: - console.log(chalk.red('Could Not List Firebases')); - } - }); - }, - version: packageInfo.version || 0, - name: packageInfo.name || 'firebase-tools' -}; diff --git a/lib/help.js b/lib/help.js deleted file mode 100644 index b96cbafe..00000000 --- a/lib/help.js +++ /dev/null @@ -1,218 +0,0 @@ -var app = require('./app'), - firebase = require('./firebase'), - chalk = require('chalk'); - -module.exports = { - showHelp: function(command) { - switch (command) { - - // Top-level functionality - - case 'login': - console.log('\n' + - ' firebase login\n' + - ' Logs the user into Firebase. All commands that require login will prompt\n' + - ' you if you\'re not currently logged in.\n' + - '\n' + - ' Optional command line parameters:\n' + - '\n' + - ' --email The email address of the account\n' + - ' --password The password of the account\n' + - '\n' + - ' Stores the email address and access token granted from Firebase in a\n' + - ' .firebaserc settings file in the user\'s home directory. Access tokens\n' + - ' are valid for up to 30 days.\n'); - break; - case 'logout': - console.log('\n' + - ' firebase logout\n' + - ' Logs the user out of Firebase.\n' + - '\n' + - ' Optional command line parameters:\n' + - '\n' + - ' -d Flag to delete the .firebaserc settings file entirely\n' + - '\n' + - ' Invalidates and removes the access token from the .firebaserc settings file\n' + - ' in the user\'s home directory. Optionally deletes the entire settings file.\n'); - break; - case 'ls': - case 'list': - console.log('\n' + - ' firebase list\n' + - ' Lists the Firebases available to the currently logged in user.\n' + - '\n' + - ' If the user is not currently logged in, they are prompted to do so - see\n' + - ' `firebase login --help` for more details.\n'); - break; - - // Submodules - - case 'init': - case 'initialise': - case 'initialize': - console.log('\n' + - ' firebase init\n' + - ' Initializes an existing Firebase app in the current directory and prompts\n' + - ' you through configuring it for firebaseapp.com.\n' + - '\n' + - ' Optional command line parameters:\n' + - '\n' + - ' -f, --firebase The Firebase to initalize the app with. The current user\n' + - ' must have access to this Firebase. This is used for the\n' + - ' subdomain of firebaseapp.com\n' + - ' -p, --public A directory containing all of the app\'s static files that\n' + - ' should uploaded when deploying. Defaults to the current\n' + - ' directory.\n' + - '\n' + - ' Generates a firebase.json file in the current directory for all the settings\n' + - ' required for deploy. If the user is not currently logged in, they are\n' + - ' prompted to do so - see `firebase login --help` for more details.\n'); - break; - case 'bootstrap': - var helpOverview = - '\n' + - ' firebase bootstrap\n' + - ' Creates a new Firebase powered app from a prebuilt template to quickly\n' + - ' get a project up and running. This creates a new folder and prompts\n' + - ' you through all the required settings.\n' + - '\n' + - ' Optional command line parameters:\n' + - '\n' + - ' -f, --firebase The Firebase to initalize the app with. The current user\n' + - ' must have access to this Firebase. This is used for the\n' + - ' subdomain of firebaseapp.com\n' + - ' -t, --template The name of the template to bootstrap the app with.\n'; - - var helpFooter = '\n' + - ' Downloads and unpacks the template into a folder named after the Firebase\n' + - ' with which it has been initialized. Creates a firebase.json file in the\n' + - ' directory with some pre-configured settings appropriate for the template.\n' + - ' If the user is not currently logged in, they are prompted to do so - see\n' + - ' `firebase login --help` for more details.\n'; - - app.getTemplates() - .done(function(templates) { - var helpTemplates = '\n The currently available templates are:\n'; - var templateList = Object.keys(templates).sort(); - for (var i = 0; i < templateList.length; i++) { - var key = templateList[i], - template = templates[key]; - helpTemplates += '\n ' + chalk.yellow(key); - helpTemplates += (template.repository) ? ' - ' + template.repository + '\n' : '\n'; - if (template.description) { - helpTemplates += ' ' + template.description; - } - helpTemplates += '\n'; - } - - console.log(helpOverview + helpTemplates + helpFooter); - }, function(error) { - var errTemplates = '\n '; - errTemplates += chalk.red('Error: ') + 'Could not retrieve available templates.\n'; - - console.log(helpOverview + errTemplates + helpFooter); - }); - break; - case 'deploy': - console.log('\n' + - ' firebase deploy\n' + - ' Deploys the current app to Firebase Hosting and creates your subdomain on\n' + - ' firebaseapp.com if it doesn\'t already exist.\n' + - '\n' + - ' Optional command line parameters:\n' + - '\n' + - ' -m, --message An optional version message\n' + - '\n' + - ' -f, --firebase Overrides the Firebase setting in the firebase.json\n' + - ' -p, --public Overrides the public directory setting in the\n' + - ' firebase.json\n' + - '\n' + - ' Uploads the directory detailed in the firebase.json settings file and\n' + - ' updates the security rules of the Firebase if specified. The current user\n' + - ' must have access to the Firebase in firebase.json, and if the user is not\n' + - ' currently logged in, they are prompted to do so - see\n' + - ' `firebase login --help` for more details.\n'); - break; - case 'delete-site': - console.log('\n' + - ' firebase delete-site\n' + - ' Deletes the current app from Firebase Hosting and displays a \n' + - ' \'Site not Found\' page as if the site had never been deployed to.\n' + - '\n' + - ' Optional command line parameters:\n' + - '\n' + - ' -m, --message An optional version message\n' + - '\n' + - ' -f, --firebase Overrides the Firebase setting in the firebase.json\n' + - '\n' + - ' Deletes the site associated with the Firebase detailed in the firebase.json\n' + - ' settings file. The current user must have access to the Firebase, and if\n' + - ' the user is not currently logged in, they are prompted to do so - see\n' + - ' `firebase login --help` for more details.\n'); - break; - case 'open': - console.log('\n' + - ' firebase open\n' + - ' Opens the current Firebase app\'s firebaseapp.com subdomain in a browser.\n' + - '\n' + - ' The current directory must have been initialized using firebase init or\n' + - ' firebase bootstrap\n'); - break; - - default: - this.showVersion(); - console.log('Usage: firebase \n' + - '\n' + - ' Available commands are:\n' + - '\n' + - ' bootstrap\n' + - ' Creates a new Firebase powered app from a prebuilt template to quickly\n' + - ' get a project up and running. This creates a new folder and prompts\n' + - ' you through all the required settings.\n' + - '\n' + - ' deploy\n' + - ' Deploys the current app to Firebase Hosting and creates your subdomain on\n' + - ' firebaseapp.com if it doesn\'t already exist.\n' + - '\n' + - ' init\n' + - ' Initializes an existing Firebase app in the current directory and prompts\n' + - ' you through configuring it for firebaseapp.com.\n' + - '\n' + - ' open\n' + - ' Opens the URL of the current Firebase app in a browser.\n' + - '\n' + - ' list\n' + - ' Lists the Firebases available to the currently logged in user.\n' + - '\n' + - ' delete-site\n' + - ' Deletes the current app from Firebase Hosting and displays a \n' + - ' \'Site not Found\' page as if the site had never been deployed to.\n' + - '\n' + - ' login\n' + - ' Logs you into Firebase. All commands that require login will prompt\n' + - ' you if you\'re not currently logged in.\n' + - '\n' + - ' logout\n' + - ' Logs you out of Firebase.\n' + - '\n' + - ' -h, --help\n' + - ' Shows this help screen. Use `firebase --help` for more detailed\n' + - ' help instructions.\n' + - '\n' + - ' -v, --version\n' + - ' Displays the current version.\n' + - '\n' + - ' -s, --silent\n' + - ' Silent mode for scripting - commands will error with non-zero status code\n' + - ' instead of waiting for prompt if not enough information supplied.\n' + - '\n' + - 'For a quick start guide, see https://www.firebase.com/docs/hosting.html\n'); - } - }, - showVersion: function() { - console.log('\n' + - 'Firebase Command Line Tools\n' + - 'Version ' + firebase.version + '\n' + - 'https://www.firebase.com\n'); - } -}; diff --git a/lib/upload.js b/lib/upload.js deleted file mode 100644 index b346e5cd..00000000 --- a/lib/upload.js +++ /dev/null @@ -1,149 +0,0 @@ -var request = require('request'), - auth = require('./auth'), - api = require('./api'), - fstreamIgnore = require('fstream-ignore'), - tar = require('tar'), - zlib = require('zlib'), - fs = require('fs'), - path = require('path'), - chalk = require('chalk'), - util = require('util'), - stream = require('stream'), - filesize = require('filesize'); - -function escapePathString(str) { - return str - .replace(/[\n]/g, '\\n') - .replace(/[\r]/g, '\\r'); -} - -module.exports = { - send: function(firebase, publicDir, ignoreRules, pushId, message, callback) { - var fileCount = 0, - buffers = [], - tarballSize = 0, - foundIndex = false, - zipStream = zlib.createGzip(), - indexPath = path.resolve(path.join(publicDir, 'index.html')); - - console.log('Preparing to deploy Public Directory...'); - - var reader = fstreamIgnore({ - path: publicDir, - type: 'Directory', - follow: true, - filter: function() { - if (this.path.match(/\r|\n/g)) { - console.log(chalk.yellow('Warning: skipping file with newline in path: ') + - '"' + escapePathString(this.path) + '"'); - return false; - } - if (this.type !== 'Directory') { - fileCount += 1; - } - if (this.path === indexPath) { - foundIndex = true; - } - return true; - } - }); - - reader.addIgnoreRules(ignoreRules); - - reader.on('error', function(err) { - console.log(chalk.red('READ ERROR') + ' - Could not read directory. Remove' + - ' symbolic links / shortcuts and try again.'); - process.exit(1); - }); - - reader.pipe(tar.Pack()) - .pipe(zipStream); - - zipStream.on('data', function(chunk) { - buffers.push(chunk); - tarballSize += chunk.length; - }); - - zipStream.on('end', function() { - var BufferStream = function(buffers, options) { - this.buffers = buffers.reverse(); - - stream.Readable.call(this, options); - }; - - util.inherits(BufferStream, stream.Readable); - - BufferStream.prototype._read = function() { - // Populating this stream requires no I/O, given that all of the required data to stream is - // already in memory. Due to a bug affecting Node v0.10 through v0.11.0, this synchronous - // streaming has the potential to block the event loop and lead to a recursive nextTick() - // call resulting in a crash. As a workaround, temporarily pause streaming periodically - // so as to not plug the event queue. - // See https://github.com/joyent/node/issues/6065 - if (this.buffers.length > 0) { - this.push(this.buffers.pop()); - } else { - this.push(null); - } - var self = this; - self.pause(); - setTimeout(function() { - self.resume(); - }, 0); - }; - - var bufferStream = new BufferStream(buffers); - - if (fileCount === 0) { - console.log(chalk.yellow('Public Directory Warning') + ' - Public ' + - 'directory is empty, removing site'); - } else if (!foundIndex) { - console.log(chalk.yellow('Public Directory Warning') + ' - Public ' + - 'directory does not contain an index.html\n' + - 'Make sure you\'re deploying the right public directory: ' + - chalk.bold(path.resolve(publicDir))); - } else if (fileCount > 500 || tarballSize > 100*1024*1024) { - console.log(chalk.yellow('Public Directory Warning') + ' - Uploading ' + - fileCount + ' files with total compressed size = ' + filesize(tarballSize)); - } else { - console.log('Uploading ' + fileCount + ' files with total compressed size = ' + filesize(tarballSize)); - } - - var params = ['id=' + encodeURIComponent(pushId), 'fileCount=' + fileCount, 'token=' + auth.token]; - if (message) { - params.push('message=' + encodeURIComponent(message)); - } - var url = api.uploadUrl + '/upload/' + firebase + '?' + params.join('&'); - - var r = request.put({ - url: url, - json: true - }, function(err, response, body) { - var failed = (err || !body || body.error); - setTimeout(callback, 0, failed, body && body.directory); - }); - var form = r.form(); - form.append('site', bufferStream, { - filename: 'site.tar.gz', - contentType: 'application/x-gzip', - knownLength: tarballSize - }); - }); - }, - deleteSite: function(firebase, pushId, message, callback) { - var params = ['id=' + encodeURIComponent(pushId), 'token=' + auth.token]; - if (message) { - params.push('message=' + encodeURIComponent(message)); - } - var url = api.uploadUrl + '/upload/' + firebase + '?' + params.join('&'); - - var r = request({ - url: url, - method: 'DELETE', - json: true - }, function(err, response, body) { - var failed = (err || !body || body.error); - setTimeout(callback, 0, failed, body && body.directory); - }); - } -}; diff --git a/package.json b/package.json deleted file mode 100644 index dcd649d9..00000000 --- a/package.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "firebase-tools", - "description": "Firebase command line tools", - "version": "0.0.0", - "author": "Firebase (https://www.firebase.com/)", - "homepage": "https://github.com/firebase/firebase-tools/", - "repository": { - "type": "git", - "url": "https://github.com/firebase/firebase-tools.git" - }, - "bugs": { - "url": "https://github.com/firebase/firebase-tools/issues" - }, - "licenses": [ - { - "type": "MIT", - "url": "http://firebase.mit-license.org/" - } - ], - "keywords": [ - "cdn", - "cli", - "ssl", - "cloud", - "hosting", - "firebase", - "realtime", - "websockets", - "synchronization" - ], - "preferGlobal": true, - "bin": { - "firebase": "./bin/firebase" - }, - "engines": { - "node": ">=0.10.0" - }, - "engineStrict": true, - "files": [ - "bin/**", - "lib/**", - "LICENSE", - "README.md", - "package.json" - ], - "dependencies": { - "chalk": "0.5.x", - "filesize": "3.1.2", - "firebase": "2.2.x", - "fstream-ignore": "1.0.x", - "open": "0.0.x", - "optimist": "0.6.x", - "progress": "1.1.x", - "prompt": "0.2.x", - "request": "2.51.x", - "source-map-support": "0.2.7", - "source-processor": "0.0.7", - "tar": "1.0.x", - "when": "3.6.x" - }, - "devDependencies": { - "chai": "^1.10.0", - "coveralls": "2.11.2", - "gulp": "3.8.10", - "gulp-exit": "0.0.2", - "gulp-istanbul": "0.5.0", - "gulp-jshint": "1.9.0", - "gulp-mocha": "2.0.0", - "jshint-stylish": "1.0.0" - }, - "scripts": { - "test": "gulp test", - "travis": "gulp" - } -} diff --git a/test/api.spec.js b/test/api.spec.js deleted file mode 100644 index 2a612da4..00000000 --- a/test/api.spec.js +++ /dev/null @@ -1,10 +0,0 @@ -var chai = require('chai'), - expect = chai.expect; - -var api = require('../lib/api'); - -describe('api', function() { - it('should have a test suite', function() { - expect(true).to.be.true; - }); -}); diff --git a/test/app.spec.js b/test/app.spec.js deleted file mode 100644 index a01f4873..00000000 --- a/test/app.spec.js +++ /dev/null @@ -1,10 +0,0 @@ -var chai = require('chai'), - expect = chai.expect; - -var app = require('../lib/app'); - -describe('app', function() { - it('should have a test suite', function() { - expect(true).to.be.true; - }); -}); diff --git a/test/auth.spec.js b/test/auth.spec.js deleted file mode 100644 index 973c015d..00000000 --- a/test/auth.spec.js +++ /dev/null @@ -1,10 +0,0 @@ -var chai = require('chai'), - expect = chai.expect; - -var auth = require('../lib/auth'); - -describe('auth', function() { - it('should have a test suite', function() { - expect(true).to.be.true; - }); -}); diff --git a/test/firebase.spec.js b/test/firebase.spec.js deleted file mode 100644 index 99b3f61c..00000000 --- a/test/firebase.spec.js +++ /dev/null @@ -1,10 +0,0 @@ -var chai = require('chai'), - expect = chai.expect; - -var firebase = require('../lib/firebase'); - -describe('firebase', function() { - it('should have a test suite', function() { - expect(true).to.be.true; - }); -}); diff --git a/test/help.spec.js b/test/help.spec.js deleted file mode 100644 index af40309f..00000000 --- a/test/help.spec.js +++ /dev/null @@ -1,10 +0,0 @@ -var chai = require('chai'), - expect = chai.expect; - -var help = require('../lib/help'); - -describe('help', function() { - it('should have a test suite', function() { - expect(true).to.be.true; - }); -}); diff --git a/test/upload.spec.js b/test/upload.spec.js deleted file mode 100644 index 70c2d4b6..00000000 --- a/test/upload.spec.js +++ /dev/null @@ -1,10 +0,0 @@ -var chai = require('chai'), - expect = chai.expect; - -var upload = require('../lib/upload'); - -describe('upload', function() { - it('should have a test suite', function() { - expect(true).to.be.true; - }); -});