import chalk from 'chalk' ; import { closeSync , openSync , promises , readSync } from 'fs' ; import { resolve } from 'path' ; import { Project } from '../../types' ; import Client from '../../util/client' ; import exposeSystemEnvs from '../../util/dev/expose-system-envs' ; import { emoji , prependEmoji } from '../../util/emoji' ; import getSystemEnvValues from '../../util/env/get-system-env-values' ; import getDecryptedEnvRecords from '../../util/get-decrypted-env-records' ; import confirm from '../../util/input/confirm' ; import { Output } from '../../util/output' ; import param from '../../util/output/param' ; import stamp from '../../util/output/stamp' ; import { getCommandName } from '../../util/pkg-name' ; const { writeFile } = promises ; const CONTENTS_PREFIX = '# Created by Vercel CLI\n' ; type Options = { '--debug' : boolean ; '--yes' : boolean ; } ; function readHeadSync ( path , length ) { const buffer = Buffer . alloc ( length ) ; const fd = openSync ( path , 'r' ) ; try { readSync ( fd , buffer , 0 , buffer . length , null ) ; } finally { closeSync ( fd ) ; } return buffer . toString ( ) ; } function tryReadHeadSync ( path , length ) { try { return readHeadSync ( path , length ) ; } catch ( err ) { if ( err . code !== 'ENOENT' ) { throw err ; } } } export default async function pull ( client , project , opts , args , output ) { if ( args . length > 1 ) { output . error ( `Invalid number of arguments. Usage: ${ getCommandName ( `env pull <file>` ) }` ) ; return 1 ; } const [ filename = '.env' ] = args ; const fullPath = resolve ( filename ) ; const skipConfirmation = opts [ '--yes' ] ; const head = tryReadHeadSync ( fullPath , Buffer . byteLength ( CONTENTS_PREFIX ) ) ; const exists = typeof head !== 'undefined' ; if ( head === CONTENTS_PREFIX ) { output . print ( `Overwriting existing ${ chalk . bold ( filename ) } file\n` ) ; } else if ( exists && ! skipConfirmation && ! ( await confirm ( `Found existing file ${ param ( filename ) }. Do you want to overwrite?` , false ) ) ) { output . log ( 'Aborted' ) ; return 0 ; } output . print ( `Downloading Development Environment Variables for Project ${ chalk . bold ( project . name ) }\n` ) ; const pullStamp = stamp ( ) ; output . spinner ( 'Downloading' ) ; const [ { envs : projectEnvs } , { systemEnvValues } ] = await Promise . all ( [ getDecryptedEnvRecords ( output , client , project . id ) , project . autoExposeSystemEnvs ? getSystemEnvValues ( output , client , project . id ) : { systemEnvValues : [ ] } , ] ) ; const records = exposeSystemEnvs ( projectEnvs , systemEnvValues , project . autoExposeSystemEnvs ) ; const contents = CONTENTS_PREFIX + Object . entries ( records ) . map ( ( [ key , value ] ) => `${ key }="${ escapeValue ( value ) }"` ) . join ( '\n' ) + '\n' ; await writeFile ( fullPath , contents , 'utf8' ) ; output . print ( `${ prependEmoji ( `${ exists ? 'Updated' : 'Created' } ${ chalk . bold ( filename ) } file ${ chalk . gray ( pullStamp ( ) ) }` , emoji ( 'success' ) ) }\n` ) ; return 0 ; } function escapeValue ( value ) { return value ? value . replace ( new RegExp ( '\n' , 'g' ) , '\\n' ) . replace ( new RegExp ( '\r' , 'g' ) , '\\r' ) : '' ; }