UNPKG

netlify-cli

Version:

Netlify command line tool

299 lines (263 loc) • 8.36 kB
const process = require('process') const { URL } = require('url') const { format, inspect } = require('util') const resolveConfig = require('@netlify/config') const { Command, flags: flagsLib } = require('@oclif/command') const oclifParser = require('@oclif/parser') const merge = require('lodash/merge') const argv = require('minimist')(process.argv.slice(2)) const API = require('netlify') const { getAgent } = require('../lib/http-agent') const chalkInstance = require('./chalk') const globalConfig = require('./global-config') const openBrowser = require('./open-browser') const StateConfig = require('./state-config') const { track, identify } = require('./telemetry') const { NETLIFY_AUTH_TOKEN, NETLIFY_API_URL } = process.env // Netlify CLI client id. Lives in bot@netlify.com // Todo setup client for multiple environments const CLIENT_ID = 'd6f37de6614df7ae58664cfca524744d73807a377f5ee71f1a254f78412e3750' const getToken = (tokenFromFlag) => { // 1. First honor command flag --auth if (tokenFromFlag) { return [tokenFromFlag, 'flag'] } // 2. then Check ENV var if (NETLIFY_AUTH_TOKEN && NETLIFY_AUTH_TOKEN !== 'null') { return [NETLIFY_AUTH_TOKEN, 'env'] } // 3. If no env var use global user setting const userId = globalConfig.get('userId') const tokenFromConfig = globalConfig.get(`users.${userId}.auth.token`) if (tokenFromConfig) { return [tokenFromConfig, 'config'] } return [null, 'not found'] } class BaseCommand extends Command { // Initialize context async init() { const cwd = argv.cwd || process.cwd() // Grab netlify API token const authViaFlag = getAuthArg(argv) const [token] = this.getConfigToken(authViaFlag) // Get site id & build state const state = new StateConfig(cwd) const cachedConfig = await this.getConfig(cwd, state, token) const { configPath, config, buildDir, siteInfo } = cachedConfig const { flags } = this.parse(BaseCommand) const agent = await getAgent({ log: this.log, exit: this.exit, httpProxy: flags.httpProxy, certificateFile: flags.httpProxyCertificateFilename, }) const apiOpts = { agent } if (NETLIFY_API_URL) { const apiUrl = new URL(NETLIFY_API_URL) apiOpts.scheme = apiUrl.protocol.slice(0, -1) apiOpts.host = apiUrl.host apiOpts.pathPrefix = NETLIFY_API_URL === `${apiUrl.protocol}//${apiUrl.host}` ? '/api/v1' : apiUrl.pathname } this.netlify = { // api methods api: new API(token || '', apiOpts), // current site context site: { root: buildDir, configPath, get id() { return state.get('siteId') }, set id(id) { state.set('siteId', id) }, }, // Site information retrieved using the API siteInfo, // Configuration from netlify.[toml/yml] config, // Used to avoid calling @neltify/config again cachedConfig, // global cli config globalConfig, // state of current site dir state, } } // Find and resolve the Netlify configuration async getConfig(cwd, state, token) { try { return await resolveConfig({ config: argv.config, cwd, context: argv.context, debug: argv.debug, siteId: argv.siteId || (typeof argv.site === 'string' && argv.site) || state.get('siteId'), token, mode: 'cli', }) } catch (error) { const message = error.type === 'userError' ? error.message : error.stack console.error(message) this.exit(1) } } async isLoggedIn() { try { await this.netlify.api.getCurrentUser() return true } catch (_) { return false } } logJson(message = '') { /* Only run json logger when --json flag present */ if (!argv.json) { return } process.stdout.write(JSON.stringify(message, null, 2)) } log(message = '', ...args) { /* If --silent or --json flag passed disable logger */ if (argv.silent || argv.json) { return } message = typeof message === 'string' ? message : inspect(message) process.stdout.write(`${format(message, ...args)}\n`) } /* Modified flag parser to support global --auth, --json, & --silent flags */ parse(opts, args = this.argv) { /* Set flags object for commands without flags */ if (!opts.flags) { opts.flags = {} } /* enrich parse with global flags */ const globalFlags = {} if (!opts.flags.silent) { globalFlags.silent = { parse: (value) => value, description: 'Silence CLI output', allowNo: false, type: 'boolean', } } if (!opts.flags.json) { globalFlags.json = { parse: (value) => value, description: 'Output return values as JSON', allowNo: false, type: 'boolean', } } if (!opts.flags.auth) { globalFlags.auth = { parse: (value) => value, description: 'Netlify auth token', input: [], multiple: false, type: 'option', } } // enrich with flags here opts.flags = { ...opts.flags, ...globalFlags } return oclifParser.parse(args, { context: this, ...opts, }) } get chalk() { // If --json flag disable chalk colors return chalkInstance(argv.json) } /** * Get user netlify API token * @param {string} - [tokenFromFlag] - value passed in by CLI flag * @return {[string, string]} - tokenValue & location of resolved Netlify API token */ getConfigToken(tokenFromFlag) { return getToken(tokenFromFlag) } authenticate(tokenFromFlag) { const [token] = this.getConfigToken(tokenFromFlag) if (token) { return token } return this.expensivelyAuthenticate() } async expensivelyAuthenticate() { const webUI = process.env.NETLIFY_WEB_UI || 'https://app.netlify.com' this.log(`Logging into your Netlify account...`) // Create ticket for auth const ticket = await this.netlify.api.createTicket({ clientId: CLIENT_ID, }) // Open browser for authentication const authLink = `${webUI}/authorize?response_type=ticket&ticket=${ticket.id}` this.log(`Opening ${authLink}`) await openBrowser({ url: authLink, log: this.log }) const accessToken = await this.netlify.api.getAccessToken(ticket) if (!accessToken) { this.error('Could not retrieve access token') } const user = await this.netlify.api.getCurrentUser() const userID = user.id const userData = merge(this.netlify.globalConfig.get(`users.${userID}`), { id: userID, name: user.full_name, email: user.email, auth: { token: accessToken, github: { user: undefined, token: undefined, }, }, }) // Set current userId this.netlify.globalConfig.set('userId', userID) // Set user data this.netlify.globalConfig.set(`users.${userID}`, userData) const { email } = user await identify({ name: user.full_name, email, }).then(() => { return track('user_login', { email, }) }) // Log success this.log() this.log(`${this.chalk.greenBright('You are now logged into your Netlify account!')}`) this.log() this.log(`Run ${this.chalk.cyanBright('netlify status')} for account details`) this.log() this.log(`To see all available commands run: ${this.chalk.cyanBright('netlify help')}`) this.log() return accessToken } } const getAuthArg = function (cliArgs) { // If deploy command. Support shorthand 'a' flag if (cliArgs && cliArgs._ && cliArgs._[0] === 'deploy') { return cliArgs.auth || cliArgs.a } return cliArgs.auth } BaseCommand.strict = false BaseCommand.flags = { debug: flagsLib.boolean({ description: 'Print debugging information', }), httpProxy: flagsLib.string({ description: 'Proxy server address to route requests through.', default: process.env.HTTP_PROXY || process.env.HTTPS_PROXY, }), httpProxyCertificateFilename: flagsLib.string({ description: 'Certificate file to use when connecting using a proxy server', default: process.env.NETLIFY_PROXY_CERTIFICATE_FILENAME, }), } BaseCommand.getToken = getToken module.exports = BaseCommand