Skip to content

Commit

Permalink
Merge pull request #308 from vtex/feature/install-feedback
Browse files Browse the repository at this point in the history
Add feedback to apps commands
  • Loading branch information
tamorim authored May 18, 2017
2 parents 3f87217 + a6dd693 commit 02e7ca9
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 302 deletions.
4 changes: 3 additions & 1 deletion src/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import envTimeout from './timeout'
import {version} from '../package.json'
import {getAccount, getWorkspace, getToken} from './conf'

export const userAgent = `Toolbelt/${version}`

const DEFAULT_TIMEOUT = 15000
const options = {
authToken: getToken(),
account: getAccount(),
region: 'aws-us-east-1',
userAgent: `Toolbelt/${version}`,
userAgent,
workspace: getWorkspace() || 'master',
timeout: envTimeout || DEFAULT_TIMEOUT,
}
Expand Down
61 changes: 0 additions & 61 deletions src/courier.ts

This file was deleted.

12 changes: 12 additions & 0 deletions src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as path from 'path'
import {readFileSync, accessSync, constants} from 'fs-promise'

import log from './logger'
import {CommandError} from './errors'

const readFileSyncUtf = (file: string): string =>
readFileSync(file, 'utf8')
Expand Down Expand Up @@ -48,6 +49,17 @@ export const validateAppManifest = (manifest: Manifest): Manifest => {
return manifest
}

const appName = new RegExp(`^${vendorPattern}\\.${namePattern}$`)
const appLocator = new RegExp(`^${vendorPattern}\\.${namePattern}@.+$`)

export const validateApp = (app: string, skipVersion: boolean = false) => {
const regex = skipVersion ? appName : appLocator
if (!regex.test(app)) {
throw new CommandError(`Invalid app format, please use <vendor>.<name>${skipVersion ? '' : '[@<version>]'}`)
}
return app
}

export const manifest = isManifestReadable()
? pipe<string, string, Manifest, Manifest>(readFileSyncUtf, JSON.parse, validateAppManifest)(manifestPath)
: {name: '', vendor: '', version: ''}
69 changes: 24 additions & 45 deletions src/modules/apps/install.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,32 @@
import {head, tail} from 'ramda'
import * as Bluebird from 'bluebird'

import {CommandError} from '../../errors'
import log from '../../logger'
import {apps} from '../../clients'
import {getWorkspace} from '../../conf'
import {workspaceMasterMessage} from './utils'
import {manifest, namePattern, vendorPattern, isManifestReadable} from '../../manifest'
import {listenUntilBuildSuccess, validateAppAction} from './utils'
import {manifest, validateApp} from '../../manifest'

const {installApp} = apps
const ARGS_START_INDEX = 2

const appIdValidator = (app: string): Bluebird<void | never> => {
const appRegex = new RegExp(`^${vendorPattern}\\.${namePattern}@.+$`)
return Promise.resolve(appRegex.test(app))
.then((isAppValid: boolean) => {
if (isAppValid) {
return
}
throw new CommandError('Invalid app format, please use <vendor>.<name>[@<version>]')
})
}

const installApps = (apps: string[], reg: string): Bluebird<void | never> => {
const app = head(apps)
const decApp = tail(apps)
log.debug('Starting to install app', app)
return appIdValidator(app)
.then(() => installApp(app, reg))
.tap(() => log.info(`Installed app ${app} successfully`))
.then(() => decApp.length > 0 ? installApps(decApp, reg) : Promise.resolve())
.catch(err => {
// A warn message will display the apps not installed.
if (!err.toolbeltWarning) {
log.warn(`The following apps were not installed: ${apps.join(', ')}`)
// the warn message is only displayed the first time the err occurs.
err.toolbeltWarning = true
}
throw err
})
const installApps = async (apps: string[], reg: string): Promise<void> => {
if (apps.length === 0) {
return
}
const app = validateApp(head(apps))
try {
log.debug('Starting to install app', app)
await installApp(app, reg)
} catch (e) {
log.warn(`The following apps were not installed: ${apps.join(', ')}`)
throw e
}
log.info(`Installed app ${app} successfully`)
await installApps(tail(apps), reg)
}

export default {
optionalArgs: 'app',
description: 'Install an app on the current directory or a specified one',
description: 'Install an app (defaults to the app in the current directory)',
options: [
{
short: 'r',
Expand All @@ -52,20 +35,16 @@ export default {
type: 'string',
},
],
handler: (optionalApp: string, options) => {
const workspace = getWorkspace()
if (workspace === 'master') {
log.error(workspaceMasterMessage)
return Promise.resolve()
}
handler: async (optionalApp: string, options) => {
await validateAppAction(optionalApp)
const app = optionalApp || `${manifest.vendor}.${manifest.name}@${manifest.version}`
const apps = [app, ...options._.slice(ARGS_START_INDEX)].map(arg => arg.toString())

// No app arguments and no manifest file.
if (!optionalApp && !isManifestReadable()) {
throw new CommandError('No app was found, please fix the manifest.json or use <vendor>.<name>[@<version>]')
// Only listen for feedback if there's only one app
if (apps.length === 1) {
listenUntilBuildSuccess(app)
}

const app = optionalApp || `${manifest.vendor}.${manifest.name}@${manifest.version}`
const apps = [app, ...options._.slice(ARGS_START_INDEX)].map(arg => arg.toString())
log.debug('Installing app(s)', apps)
return installApps(apps, options.r || options.registry || 'smartcheckout')
},
Expand Down
62 changes: 24 additions & 38 deletions src/modules/apps/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,18 @@ import {uniqBy, prop} from 'ramda'
import * as debounce from 'debounce'
import {createInterface} from 'readline'

import {CommandError} from '../../errors'
import log from '../../logger'
import {apps} from '../../clients'
import {listen} from '../../courier'
import {manifest, isManifestReadable } from '../../manifest'
import {logAll} from '../../sse'
import {manifest} from '../../manifest'
import {changesToString} from '../../apps'
import {toMajorLocator} from '../../locator'
import {id, workspaceMasterMessage} from './utils'
import {getWorkspace, getAccount} from '../../conf'
import {id, validateAppAction} from './utils'
import {watch, listLocalFiles, addChangeContent} from '../../file'

const {link} = apps
const root = process.cwd()
const pathProp = prop('path')
const [account, workspace] = [getAccount(), getWorkspace()]
const cleanAll: Change = {path: '*', action: 'remove'}

const mapFilesToChanges = (files: string[]): Change[] =>
Expand Down Expand Up @@ -47,39 +44,28 @@ const sendChanges = (() => {

export default {
description: 'Send the files to the registry and watch for changes',
handler: () => {
if (workspace === 'master') {
log.error(workspaceMasterMessage)
return Promise.resolve()
}

if (!isManifestReadable()) {
throw new CommandError('No app was found, please fix the manifest.json to update the registry.')
}

handler: async () => {
await validateAppAction()
log.info('Linking app', `${id(manifest)}`)
listen(account, workspace, log.level, `${manifest.vendor}.${manifest.name}`)
const unlisten = logAll(log.level, `${manifest.vendor}.${manifest.name}`)
const majorLocator = toMajorLocator(manifest.vendor, manifest.name, manifest.version)
return listLocalFiles(root)
.tap(() => log.debug('Sending files:'))
.tap((paths: string[]) => paths.forEach(p => log.debug(p)))
.then(mapFilesToChanges)
.then(addChangeContent)
.tap((batch: Batch[]) =>
log.info(`Sending ${batch.length} file` + (batch.length > 1 ? 's' : '')),
)
.tap((batch: Batch[]) => link(majorLocator, batch))
.tap((batch: Batch[]) =>
log.info(`${batch.length} file` + (batch.length > 1 ? 's' : '') + ' sent'),
)
.then(() => watch(root, sendChanges))
.then(() =>
createInterface({input: process.stdin, output: process.stdout})
.on('SIGINT', () => {
log.info('Your app is still in development mode.')
log.info(`You can unlink it with: 'vtex unlink ${majorLocator}'`)
process.exit()
}),
)
const paths = await listLocalFiles(root)
const changes = mapFilesToChanges(paths)
const batch = addChangeContent(changes)

log.debug('Sending files:')
paths.forEach(p => log.debug(p))
log.info(`Sending ${batch.length} file` + (batch.length > 1 ? 's' : ''))
await link(majorLocator, batch)
log.info(`${batch.length} file` + (batch.length > 1 ? 's' : '') + ' sent')
await watch(root, sendChanges)

createInterface({input: process.stdin, output: process.stdout})
.on('SIGINT', () => {
unlisten()
log.info('Your app is still in development mode.')
log.info(`You can unlink it with: 'vtex unlink ${majorLocator}'`)
process.exit()
})
},
}
Loading

0 comments on commit 02e7ca9

Please sign in to comment.