Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: aio app pack, aio app install output cleanup, fixes #671

Merged
merged 4 commits into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 50 additions & 16 deletions src/commands/app/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ const { Flags } = require('@oclif/core')
const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:install', { provider: 'debug' })
const path = require('node:path')
const fs = require('fs-extra')
const execa = require('execa')
const unzipper = require('unzipper')
const { validateJsonWithSchema } = require('../../lib/install-helper')
const jsYaml = require('js-yaml')
const { USER_CONFIG_FILE, DEPLOY_CONFIG_FILE } = require('../../lib/defaults')
const ora = require('ora')

class InstallCommand extends BaseCommand {
async run () {
Expand All @@ -41,13 +43,25 @@ class InstallCommand extends BaseCommand {
aioLogger.debug(`changed current working directory to: ${outputPath}`)
}

await this.validateZipDirectoryStructure(args.path)
await this.unzipFile(args.path, outputPath)
await this.validateConfig(outputPath, USER_CONFIG_FILE)
await this.validateConfig(outputPath, DEPLOY_CONFIG_FILE)
await this.runTests()
try {
await this.validateZipDirectoryStructure(args.path)
await this.unzipFile(args.path, outputPath)
await this.validateConfig(outputPath, USER_CONFIG_FILE)
await this.validateConfig(outputPath, DEPLOY_CONFIG_FILE)
await this.npmInstall(flags.verbose)
await this.runTests()
this.spinner.succeed('Install done.')
} catch (e) {
this.spinner.fail(e.message)
this.error(flags.verbose ? e : e.message)
}
}

this.log('Install done.')
get spinner () {
if (!this._spinner) {
this._spinner = ora()
}
return this._spinner
}

diffArray (expected, actual) {
Expand All @@ -62,7 +76,7 @@ class InstallCommand extends BaseCommand {
const expectedFiles = [USER_CONFIG_FILE, DEPLOY_CONFIG_FILE, 'package.json']
const foundFiles = []

this.log(`Validating integrity of app package at ${zipFilePath}...`)
this.spinner.start(`Validating integrity of app package at ${zipFilePath}...`)

const zip = fs.createReadStream(zipFilePath).pipe(unzipper.Parse({ forceStream: true }))
for await (const entry of zip) {
Expand All @@ -76,33 +90,53 @@ class InstallCommand extends BaseCommand {

const diff = this.diffArray(expectedFiles, foundFiles)
if (diff.length > 0) {
this.error(`The app package ${zipFilePath} is missing these files: ${JSON.stringify(diff, null, 2)}`)
throw new Error(`The app package ${zipFilePath} is missing these files: ${JSON.stringify(diff, null, 2)}`)
}
this.spinner.succeed(`Validated integrity of app package at ${zipFilePath}`)
}

async unzipFile (zipFilePath, destFolderPath) {
aioLogger.debug(`unzipFile: ${zipFilePath} to be extracted to ${destFolderPath}`)

this.log(`Extracting app package to ${destFolderPath}...`)
this.spinner.start(`Extracting app package to ${destFolderPath}...`)
return unzipper.Open.file(zipFilePath)
.then(d => d.extract({ path: destFolderPath, concurrency: 5 }))
.then((d) => {
d.extract({ path: destFolderPath, concurrency: 5 })
this.spinner.succeed(`Extracted app package to ${destFolderPath}`)
})
}

async validateConfig (outputPath, configFileName, configFilePath = path.join(outputPath, configFileName)) {
this.log(`Validating ${configFileName}...`)
this.spinner.start(`Validating ${configFileName}...`)
aioLogger.debug(`validateConfig: ${configFileName} at ${configFilePath}`)

const configFileJson = jsYaml.load(fs.readFileSync(configFilePath).toString())
const { valid, errors } = validateJsonWithSchema(configFileJson, configFileName)
if (!valid) {
const message = `Missing or invalid keys in ${configFileName}: ${JSON.stringify(errors, null, 2)}`
this.error(message)
throw new Error(`Missing or invalid keys in ${configFileName}: ${JSON.stringify(errors, null, 2)}`)
} else {
this.spinner.succeed(`Validated ${configFileName}`)
}
}

async runTests () {
this.log('Running tests...')
return this.config.runCommand('app:test')
async npmInstall (isVerbose) {
this.spinner.start('Running npm install...')
const stdio = isVerbose ? 'inherit' : 'ignore'
return execa('npm', ['install'], { stdio })
.then(() => {
this.spinner.succeed('Ran npm install')
})
}

async runTests (isVerbose) {
this.spinner.start('Running app tests...')
return this.config.runCommand('app:test').then((result) => {
if (result === 0) { // success
this.spinner.succeed('App tests passed')
} else {
throw new Error('App tests failed')
}
})
}
}

Expand Down
82 changes: 54 additions & 28 deletions src/commands/app/pack.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const yaml = require('js-yaml')
const execa = require('execa')
const { loadConfigFile, writeFile } = require('../../lib/import-helper')
const { getObjectValue } = require('../../lib/app-helper')
const ora = require('ora')

const DEFAULTS = {
OUTPUT_ZIP_FILE: 'app.zip',
Expand All @@ -36,7 +37,7 @@ class Pack extends BaseCommand {
const appConfig = this.getFullConfig()

// resolve to absolute path before any chdir
flags.output = path.resolve(flags.output)
const outputZipFile = path.resolve(flags.output)

// change the cwd if necessary
if (args.path !== '.') {
Expand All @@ -45,32 +46,52 @@ class Pack extends BaseCommand {
aioLogger.debug(`changed current working directory to: ${resolvedPath}`)
}

// 1. create artifacts phase
this.log('Creating package artifacts...')
await fs.emptyDir(DEFAULTS.ARTIFACTS_FOLDER)

// ACNA-2038
// not artifacts folder should exist before we fire the event
await this.config.runHook('pre-pack', { appConfig, artifactsFolder: DEFAULTS.ARTIFACTS_FOLDER })

// 2. copy files to package phase
this.log('Copying files...')
const fileList = await this.filesToPack()
await this.copyPackageFiles(DEFAULTS.ARTIFACTS_FOLDER, fileList)

// 3. add/modify artifacts phase
this.log('Creating configuration files...')
await this.createDeployYamlFile(appConfig)
await this.addCodeDownloadAnnotation(appConfig)
// doing this before zip so other things can be added to the zip
await this.config.runHook('post-pack', { appConfig, artifactsFolder: DEFAULTS.ARTIFACTS_FOLDER })
try {
// 1. create artifacts phase
this.spinner.start(`Creating package artifacts folder '${DEFAULTS.ARTIFACTS_FOLDER}'...`)
await fs.emptyDir(DEFAULTS.ARTIFACTS_FOLDER)
this.spinner.succeed(`Created package artifacts folder '${DEFAULTS.ARTIFACTS_FOLDER}'`)

// ACNA-2038
// not artifacts folder should exist before we fire the event
await this.config.runHook('pre-pack', { appConfig, artifactsFolder: DEFAULTS.ARTIFACTS_FOLDER })

// 2. copy files to package phase
this.spinner.start('Copying project files...')
const fileList = await this.filesToPack([flags.output])
await this.copyPackageFiles(DEFAULTS.ARTIFACTS_FOLDER, fileList)
this.spinner.succeed('Copied project files')

// 3. add/modify artifacts phase
this.spinner.start('Creating configuration files...')
await this.createDeployYamlFile(appConfig)
this.spinner.succeed('Created configuration files')

this.spinner.start('Adding code-download annotations...')
await this.addCodeDownloadAnnotation(appConfig)
this.spinner.succeed('Added code-download annotations')

// doing this before zip so other things can be added to the zip
await this.config.runHook('post-pack', { appConfig, artifactsFolder: DEFAULTS.ARTIFACTS_FOLDER })

// 4. zip package phase
this.spinner.start(`Zipping package artifacts folder '${DEFAULTS.ARTIFACTS_FOLDER}' to '${outputZipFile}'...`)
await fs.remove(outputZipFile)
await this.zipHelper(DEFAULTS.ARTIFACTS_FOLDER, outputZipFile)
this.spinner.succeed(`Zipped package artifacts folder '${DEFAULTS.ARTIFACTS_FOLDER}' to '${outputZipFile}'`)
} catch (e) {
this.spinner.fail(e.message)
this.error(flags.verbose ? e : e.message)
}

// 4. zip package phase
this.log(`Zipping package artifacts folder '${DEFAULTS.ARTIFACTS_FOLDER}' to '${flags.output}'...`)
await fs.remove(flags.output)
await this.zipHelper(DEFAULTS.ARTIFACTS_FOLDER, flags.output)
this.spinner.succeed('Packaging done.')
}

this.log('Packaging done.')
get spinner () {
if (!this._spinner) {
this._spinner = ora()
}
return this._spinner
}

/**
Expand Down Expand Up @@ -111,11 +132,13 @@ class Pack extends BaseCommand {
// TODO: send a PR to their plugin to have a `--json` flag
const command = await this.config.findCommand('api-mesh:get')
if (command) {
this.log('Getting api-mesh config...')
this.spinner.start('Getting api-mesh config...')
const { stdout } = await execa('aio', ['api-mesh', 'get'], { cwd: process.cwd() })
// until we get the --json flag, we parse the output
const idx = stdout.indexOf('{')
meshConfig = JSON.parse(stdout.substring(idx))
aioLogger.debug(`api-mesh:get - ${JSON.stringify(meshConfig, null, 2)}`)
this.spinner.succeed('Got api-mesh config')
} else {
aioLogger.debug('api-mesh:get command was not found, meshConfig is not available for app:pack')
}
Expand Down Expand Up @@ -195,14 +218,17 @@ class Pack extends BaseCommand {
*
* This runs `npm pack` to get the list.
*
* @param {Array<string>} filesToExclude a list of files to exclude
* @param {string} workingDirectory the working directory to run `npm pack` in
* @returns {Array<string>} a list of files that are to be packed
*/
async filesToPack (workingDirectory = process.cwd()) {
async filesToPack (filesToExclude = [], workingDirectory = process.cwd()) {
const { stdout } = await execa('npm', ['pack', '--dry-run', '--json'], { cwd: workingDirectory })

const { files } = JSON.parse(stdout)[0]
return files.map(file => file.path)
return files
.map(file => file.path)
.filter(file => !filesToExclude.includes(file))
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/commands/app/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class Test extends BaseCommand {

this.printReport(totalResults)
process.exitCode = exitCode
return exitCode
}

printReport (totalResults) {
Expand Down
Loading