@elastic.io/component-build-helper
Advanced tools
Comparing version 1.1.0 to 2.0.0-alpha1
@@ -6,2 +6,3 @@ "use strict"; | ||
const DockerfileGenerator_1 = require("../DockerfileGenerator"); | ||
const utils_1 = require("../utils"); | ||
; | ||
@@ -15,3 +16,5 @@ exports.command = 'detectComponentLanguage <componentDirectoryPath>'; | ||
const { componentDirectoryPath } = argv; | ||
const contextLoader = await ComponentContextLoader_1.ComponentContextLoader.getInstance(componentDirectoryPath); | ||
const getFileFunction = (0, utils_1.buildGetFileFunction)(componentDirectoryPath); | ||
const contextLoader = new ComponentContextLoader_1.ComponentContextLoader(getFileFunction); | ||
await contextLoader.loadFilesToContext(); | ||
const generator = await DockerfileGenerator_1.DockerfileGenerator.getInstance(contextLoader); | ||
@@ -18,0 +21,0 @@ process.stdout.write(generator.componentLanguage); |
@@ -6,2 +6,3 @@ "use strict"; | ||
const ComponentContextLoader_1 = require("../ComponentContextLoader"); | ||
const utils_1 = require("../utils"); | ||
; | ||
@@ -15,14 +16,12 @@ exports.command = 'generateDockerfile <componentDirectoryPath>'; | ||
demandOption: true, | ||
desc: 'Use "-" to read sources from stdin' | ||
desc: 'Component root directory' | ||
}); | ||
exports.builder = builder; | ||
async function handler(argv) { | ||
// Make sure that nobody print something to the stdout by console.log function | ||
console.log = () => { }; | ||
const { componentDirectoryPath } = argv; | ||
let contextLoader; | ||
if (componentDirectoryPath === '-') { | ||
contextLoader = await ComponentContextLoader_1.ComponentContextLoader.getInstance(process.stdin); | ||
} | ||
else { | ||
contextLoader = await ComponentContextLoader_1.ComponentContextLoader.getInstance(componentDirectoryPath); | ||
} | ||
const getFileFunction = (0, utils_1.buildGetFileFunction)(componentDirectoryPath); | ||
const contextLoader = new ComponentContextLoader_1.ComponentContextLoader(getFileFunction); | ||
await contextLoader.loadFilesToContext(); | ||
const generator = await DockerfileGenerator_1.DockerfileGenerator.getInstance(contextLoader); | ||
@@ -29,0 +28,0 @@ const dockerfile = await generator.genDockerfile(); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.handler = exports.builder = exports.desc = exports.command = void 0; | ||
const fs_1 = require("fs"); | ||
const metaloader_1 = require("../metaloader"); | ||
const utils_1 = require("../utils"); | ||
; | ||
@@ -14,7 +14,3 @@ exports.command = 'validate <componentDirectoryPath>'; | ||
const { componentDirectoryPath } = argv; | ||
const getFileFunction = async (fileRelativePath) => { | ||
const filePath = fileRelativePath.replace(/^\.\/|^\//g, ''); | ||
const buffer = await fs_1.promises.readFile(`${componentDirectoryPath.replace(/\/$/g, '')}/${filePath}`); | ||
return buffer.toString('utf-8'); | ||
}; | ||
const getFileFunction = (0, utils_1.buildGetFileFunction)(componentDirectoryPath); | ||
await (0, metaloader_1.validateComponentJSON)(getFileFunction); | ||
@@ -21,0 +17,0 @@ process.exit(0); |
/// <reference types="node" /> | ||
import { Readable } from 'stream'; | ||
export declare enum SOURCE_INPUT_TYPE { | ||
STREAM = 0, | ||
DIR = 1 | ||
} | ||
export declare abstract class ComponentContextLoader { | ||
export declare class ComponentContextLoader { | ||
private getFileFunction; | ||
constructor(getFileFunction: (path: string) => Promise<Buffer>); | ||
protected _context: { | ||
@@ -12,6 +9,5 @@ [key: string]: string; | ||
protected listOfFilesToLoad: string[]; | ||
abstract loadFilesToContext(): Promise<{ | ||
loadFilesToContext(): Promise<{ | ||
[key: string]: string; | ||
}>; | ||
static getInstance(source: string | Readable): Promise<StreamContextLoader | DirectoryContextLoader>; | ||
get context(): { | ||
@@ -21,15 +17,1 @@ [p: string]: string; | ||
} | ||
export declare class StreamContextLoader extends ComponentContextLoader { | ||
private readonly readStream; | ||
constructor(readStream: Readable); | ||
loadFilesToContext(): Promise<{ | ||
[p: string]: string; | ||
}>; | ||
} | ||
export declare class DirectoryContextLoader extends ComponentContextLoader { | ||
private readonly componentDirPath; | ||
constructor(componentDirPath: string); | ||
loadFilesToContext(): Promise<{ | ||
[key: string]: string; | ||
}>; | ||
} |
@@ -6,15 +6,7 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.DirectoryContextLoader = exports.StreamContextLoader = exports.ComponentContextLoader = exports.SOURCE_INPUT_TYPE = void 0; | ||
/* eslint-disable @typescript-eslint/no-use-before-define */ | ||
const stream_1 = require("stream"); | ||
const tar_1 = __importDefault(require("tar")); | ||
const fs_1 = require("fs"); | ||
exports.ComponentContextLoader = void 0; | ||
const logger_1 = __importDefault(require("./logger")); | ||
var SOURCE_INPUT_TYPE; | ||
(function (SOURCE_INPUT_TYPE) { | ||
SOURCE_INPUT_TYPE[SOURCE_INPUT_TYPE["STREAM"] = 0] = "STREAM"; | ||
SOURCE_INPUT_TYPE[SOURCE_INPUT_TYPE["DIR"] = 1] = "DIR"; | ||
})(SOURCE_INPUT_TYPE = exports.SOURCE_INPUT_TYPE || (exports.SOURCE_INPUT_TYPE = {})); | ||
const metaloader_1 = require("./metaloader"); | ||
class ComponentContextLoader { | ||
constructor() { | ||
constructor(getFileFunction) { | ||
this._context = {}; | ||
@@ -36,67 +28,21 @@ // List of files needed to be downloaded | ||
]; | ||
this.getFileFunction = getFileFunction; | ||
} | ||
static async getInstance(source) { | ||
let contextLoader; | ||
if (source instanceof stream_1.Readable) { | ||
contextLoader = new StreamContextLoader(source); | ||
} | ||
else { | ||
contextLoader = new DirectoryContextLoader(source); | ||
} | ||
await contextLoader.loadFilesToContext(); | ||
return contextLoader; | ||
} | ||
get context() { | ||
return this._context; | ||
} | ||
} | ||
exports.ComponentContextLoader = ComponentContextLoader; | ||
class StreamContextLoader extends ComponentContextLoader { | ||
constructor(readStream) { | ||
super(); | ||
this.readStream = readStream; | ||
} | ||
loadFilesToContext() { | ||
return new Promise((resolve, reject) => { | ||
this.readStream | ||
.pipe(tar_1.default.list()) | ||
.on('entry', async (entry) => { | ||
try { | ||
// Logo must be base64 encoded | ||
if (entry.type === 'File' && this.listOfFilesToLoad.includes(entry.path)) { | ||
if (entry.path === 'logo.png') { | ||
this.context[entry.path] = (await entry.concat()).toString('base64'); | ||
} | ||
else { | ||
this.context[entry.path] = (await entry.concat()).toString(); | ||
} | ||
} | ||
} | ||
catch (e) { | ||
reject(e); | ||
} | ||
}) | ||
.on('finish', () => resolve(this._context)) | ||
.on('error', reject); | ||
}); | ||
} | ||
} | ||
exports.StreamContextLoader = StreamContextLoader; | ||
class DirectoryContextLoader extends ComponentContextLoader { | ||
constructor(componentDirPath) { | ||
super(); | ||
this.componentDirPath = componentDirPath.replace(/\/$/, ''); | ||
} | ||
async loadFilesToContext() { | ||
// check that dir exists | ||
await fs_1.promises.access(`${this.componentDirPath}/`); | ||
await Promise.all(this.listOfFilesToLoad.map(async (filePath) => { | ||
try { | ||
const buffer = await fs_1.promises.readFile(`${this.componentDirPath}/${filePath}`); | ||
if (filePath === 'logo.png') { | ||
this._context[filePath] = buffer.toString('base64'); | ||
const buffer = await this.getFileFunction(filePath); | ||
if (buffer) { | ||
if (filePath === 'component.json') { | ||
const component = JSON.parse(buffer.toString()); | ||
await (0, metaloader_1.resolveMetadataRefs)(component, this.getFileFunction); | ||
this._context[filePath] = JSON.stringify(component); | ||
} | ||
else if (filePath === 'logo.png') { | ||
this._context[filePath] = buffer.toString('base64'); | ||
} | ||
else { | ||
this._context[filePath] = buffer.toString(); | ||
} | ||
} | ||
else { | ||
this._context[filePath] = buffer.toString(); | ||
} | ||
} | ||
@@ -115,3 +61,6 @@ catch (e) { | ||
} | ||
get context() { | ||
return this._context; | ||
} | ||
} | ||
exports.DirectoryContextLoader = DirectoryContextLoader; | ||
exports.ComponentContextLoader = ComponentContextLoader; |
@@ -0,1 +1,5 @@ | ||
/// <reference types="node" /> | ||
import { Component } from './interfaces'; | ||
declare type getFileType = (path: string) => Promise<Buffer>; | ||
export declare function resolveMetadataRefs(component: Component, getFile: getFileType): Promise<[any[], any[]]>; | ||
/** | ||
@@ -6,3 +10,4 @@ * | ||
*/ | ||
export declare function validateComponentJSON(getFile: (path: string) => Promise<string>): Promise<any>; | ||
export declare function validateComponentJSON(getFile: getFileType): Promise<any>; | ||
export declare function getComponentLogo(pathToComponent: string): Promise<string>; | ||
export {}; |
@@ -6,3 +6,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getComponentLogo = exports.validateComponentJSON = void 0; | ||
exports.getComponentLogo = exports.validateComponentJSON = exports.resolveMetadataRefs = void 0; | ||
const index_1 = require("./index"); | ||
@@ -61,2 +61,33 @@ const fs_1 = __importDefault(require("fs")); | ||
} | ||
async function resolveMetadataFromPath(metadata, direction, getFile) { | ||
const fileName = metadata[direction]; | ||
if (typeof fileName === 'string') { | ||
console.log(`Reading incoming ${fileName}`); | ||
const data = await getFile(fileName); | ||
console.log(`Parsing incoming ${fileName}`); | ||
metadata[direction] = JSON.parse(data.toString()); | ||
} | ||
} | ||
function resolveMetadataForObjects(actions, getFile) { | ||
const promises = []; | ||
Object.entries(actions).forEach(([key, action]) => { | ||
const metadata = action.metadata; | ||
if (metadata) { | ||
console.log('Found metadata for', key); | ||
promises.push(resolveMetadataFromPath(metadata, 'in', getFile)); | ||
promises.push(resolveMetadataFromPath(metadata, 'out', getFile)); | ||
} | ||
}); | ||
return Promise.all(promises); | ||
} | ||
function resolveMetadataRefs(component, getFile) { | ||
const triggers = component.triggers || {}; | ||
const actions = component.actions || {}; | ||
console.log('Found %s triggers and %s actions', Object.keys(triggers).length, Object.keys(actions).length); | ||
return Promise.all([ | ||
resolveMetadataForObjects(triggers, getFile), | ||
resolveMetadataForObjects(actions, getFile) | ||
]); | ||
} | ||
exports.resolveMetadataRefs = resolveMetadataRefs; | ||
/** | ||
@@ -71,33 +102,3 @@ * | ||
console.log('Parsing incoming component.json'); | ||
const component = JSON.parse(data); | ||
async function resolveMetadataFromPath(metadata, direction) { | ||
const fileName = metadata[direction]; | ||
if (typeof fileName === 'string') { | ||
console.log(`Reading incoming ${fileName}`); | ||
const data = await getFile(fileName); | ||
console.log(`Parsing incoming ${fileName}`); | ||
metadata[direction] = JSON.parse(data); | ||
} | ||
} | ||
function resolveMetadataForObjects(actions) { | ||
const promises = []; | ||
Object.entries(actions).forEach(([key, action]) => { | ||
const metadata = action.metadata; | ||
if (metadata) { | ||
console.log('Found metadata for', key); | ||
promises.push(resolveMetadataFromPath(metadata, 'in')); | ||
promises.push(resolveMetadataFromPath(metadata, 'out')); | ||
} | ||
}); | ||
return Promise.all(promises); | ||
} | ||
function resolveMetadataRefs(component) { | ||
const triggers = component.triggers || {}; | ||
const actions = component.actions || {}; | ||
console.log('Found %s triggers and %s actions', Object.keys(triggers).length, Object.keys(actions).length); | ||
return Promise.all([ | ||
resolveMetadataForObjects(triggers), | ||
resolveMetadataForObjects(actions) | ||
]); | ||
} | ||
const component = JSON.parse(data.toString()); | ||
validateComponentVersion(component.version); | ||
@@ -107,3 +108,3 @@ validateFunctionsNames(component); | ||
validateCredentials(component); | ||
await resolveMetadataRefs(component); | ||
await resolveMetadataRefs(component, getFile); | ||
return component; | ||
@@ -110,0 +111,0 @@ } |
{ | ||
"name": "@elastic.io/component-build-helper", | ||
"version": "1.1.0", | ||
"version": "2.0.0-alpha1", | ||
"description": "Helpers for the component build process", | ||
@@ -5,0 +5,0 @@ "main": "dist/src/index.js", |
@@ -10,8 +10,4 @@ # component-build-helper | ||
``` | ||
2. get base64 representation of component logo. | ||
```shell | ||
component_cli getComponentLogo /path/to/component/folder | ||
``` | ||
3. generate dockerfile for component. | ||
2. generate dockerfile for component. | ||
@@ -24,3 +20,3 @@ ```shell | ||
``` | ||
4. detect component language. | ||
3. detect component language. | ||
@@ -27,0 +23,0 @@ ```shell |
@@ -7,2 +7,4 @@ import sinon, { SinonSandbox } from 'sinon'; | ||
import { ComponentContextLoader } from '../../src/ComponentContextLoader'; | ||
import { buildGetFileFunction } from '../../src/utils'; | ||
import { nodejsComponentJson } from '../fixtures/DockerfileGenerator.data'; | ||
const { expect } = chai; | ||
@@ -22,14 +24,9 @@ chai.use(require('sinon-chai')); | ||
describe('newInstance', () => { | ||
it('should throw an error in case directory of component is not exists', async () => { | ||
await expect(ComponentContextLoader.getInstance('./spec/fixtures/unexisted_dir/')) | ||
.to.be.rejectedWith('ENOENT: no such file or directory, access \'./spec/fixtures/unexisted_dir/\''); | ||
}); | ||
it('should load all files for nodejs component', async () => { | ||
const contextLoader = await ComponentContextLoader.getInstance('./spec/fixtures/component/nodejs'); | ||
const componentJson = (await fsPromise.readFile('./spec/fixtures/component/nodejs/component.json')).toString(); | ||
const contextLoader = new ComponentContextLoader(buildGetFileFunction('./spec/fixtures/component/nodejs')); | ||
await contextLoader.loadFilesToContext(); | ||
const packageJson = (await fsPromise.readFile('./spec/fixtures/component/nodejs/package.json')).toString(); | ||
const logo = (await fsPromise.readFile('./spec/fixtures/component/nodejs/logo.png')).toString('base64'); | ||
expect(contextLoader.context).to.deep.equal({ | ||
'component.json': componentJson, | ||
'component.json': nodejsComponentJson, | ||
'logo.png': logo, | ||
@@ -40,3 +37,4 @@ 'package.json': packageJson | ||
it('should load all files for gradle component', async () => { | ||
const contextLoader = await ComponentContextLoader.getInstance('./spec/fixtures/component/gradle'); | ||
const contextLoader = new ComponentContextLoader(buildGetFileFunction('./spec/fixtures/component/gradle')); | ||
await contextLoader.loadFilesToContext(); | ||
const componentJson = (await fsPromise.readFile('./spec/fixtures/component/gradle/component.json')).toString(); | ||
@@ -43,0 +41,0 @@ const buildGralde = (await fsPromise.readFile('./spec/fixtures/component/gradle/build.gradle')).toString(); |
@@ -6,13 +6,15 @@ import sinon, { SinonSandbox } from 'sinon'; | ||
import { DockerfileGenerator } from '../../src/DockerfileGenerator'; | ||
import fs, { promises as fsPromise, readFileSync } from 'fs'; | ||
import { promises as fsPromise, readFileSync } from 'fs'; | ||
import { ComponentContextLoader } from '../../src/ComponentContextLoader'; | ||
import { Readable } from 'stream'; | ||
import { | ||
componentJsonGradleLabel, | ||
componentJsonLabel, | ||
componentJsonTarLabel, | ||
gradleDockerfile, | ||
gradleWrapperDockerfile, java11Dockerfile, java11PredefinedJdepsInfo, labelsWithSplittedLogo, | ||
gradleWrapperDockerfile, | ||
java11Dockerfile, | ||
java11PredefinedJdepsInfo, | ||
labelsWithSplittedLogo, | ||
predefinedDockerfile | ||
} from '../fixtures/DockerfileGenerator.data'; | ||
import { buildGetFileFunction } from '../../src/utils'; | ||
@@ -41,3 +43,4 @@ const { expect } = chai; | ||
it('should throw an error in case unknown component type', async () => { | ||
componentContextLoader = await ComponentContextLoader.getInstance('./spec/fixtures/component/'); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction('./spec/fixtures/component/')); | ||
await componentContextLoader.loadFilesToContext(); | ||
await expect(DockerfileGenerator.getInstance(componentContextLoader)) | ||
@@ -56,3 +59,4 @@ .to.be.rejectedWith('Unknown sources type. Please make sure that you provided correct component sources'); | ||
]); | ||
componentContextLoader = await ComponentContextLoader.getInstance('./spec/fixtures/component/nodejs/'); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction('./spec/fixtures/component/nodejs/')); | ||
await componentContextLoader.loadFilesToContext(); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
@@ -63,3 +67,4 @@ expect(dockerfileGenerator['getComponentBuildImage']()).to.be.equal('node:16-alpine'); | ||
sandbox.stub(fsPromise, 'readFile').resolves(Buffer.from('{ "dependencies": { "elasticio-sailor-nodejs": "2.2.2"} }')); | ||
componentContextLoader = await ComponentContextLoader.getInstance('./spec/fixtures/component/nodejs'); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction('./spec/fixtures/component/nodejs')); | ||
await componentContextLoader.loadFilesToContext(); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
@@ -86,3 +91,4 @@ nock('https://nodejs.org:443', { 'encodedQueryParams': true }) | ||
it('genDockerfile should return dockerfile structure', async () => { | ||
componentContextLoader = await ComponentContextLoader.getInstance('./spec/fixtures/component/nodejs'); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction('./spec/fixtures/component/nodejs')); | ||
await componentContextLoader.loadFilesToContext(); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
@@ -116,3 +122,4 @@ await expect(dockerfileGenerator.genDockerfile()).to.eventually.equal( | ||
readFileStub.callThrough(); | ||
componentContextLoader = await ComponentContextLoader.getInstance(path); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction(path)); | ||
await componentContextLoader.loadFilesToContext(); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
@@ -124,53 +131,2 @@ await expect(dockerfileGenerator.genDockerfile()).to.eventually.equal(predefinedDockerfile + | ||
}); | ||
describe('genDockerfile from tar stream', () => { | ||
let tarStream: Readable; | ||
beforeEach(() => { | ||
tarStream = fs.createReadStream('./spec/fixtures/component/nodejs/tarStream'); | ||
}); | ||
it('genDockerfile should return dockerfile structure', async () => { | ||
nock('https://nodejs.org:443', { 'encodedQueryParams': true }) | ||
.get('/dist/') | ||
.reply(200, nodeVersions, [ | ||
'Content-Type', | ||
'text/html' | ||
]); | ||
componentContextLoader = await ComponentContextLoader.getInstance(tarStream); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
await expect(dockerfileGenerator.genDockerfile()).to.eventually.equal( | ||
'FROM node:15-alpine AS base\n' + | ||
'RUN apk add --update git tini python3 make g++ && rm -rf /var/cache/apk/*\n' + | ||
'WORKDIR /home/node\n' + | ||
'COPY --chown=node:node . ./\n' + | ||
'FROM base AS dependencies\n' + | ||
'ENV LOG_OUTPUT_MODE=short\n' + | ||
'USER node\n' + | ||
'RUN npm config set update-notifier false\n' + | ||
'RUN npm install --no-audit\n' + | ||
'RUN npm test\n' + | ||
'RUN npm prune --production\n' + | ||
'RUN rm -rf spec* .circleci README.md LICENSE .idea\n' + | ||
'FROM node:15-alpine AS release\n' + | ||
`LABEL elastic.io.component=${JSON.stringify(JSON.stringify(componentJsonTarLabel))}\n` + | ||
`LABEL elastic.io.logo="${logo}"\n` + | ||
'WORKDIR /home/node\n' + | ||
'COPY --from=base --chown=node:node /sbin/tini /sbin/tini\n' + | ||
'COPY --from=dependencies --chown=node:node /home/node /home/node\n' + | ||
'USER node\n' + | ||
'ENTRYPOINT ["/sbin/tini", "-v", "-e", "143", "--"]'); | ||
}); | ||
it('genDockerfile should return predefined dockerfile with labels', async () => { | ||
tarStream = fs.createReadStream('./spec/fixtures/component/nodejs-custom-dockerfile/tarStream'); | ||
nock('https://nodejs.org:443', { 'encodedQueryParams': true }) | ||
.get('/dist/') | ||
.reply(200, nodeVersions, [ | ||
'Content-Type', | ||
'text/html' | ||
]); | ||
componentContextLoader = await ComponentContextLoader.getInstance(tarStream); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
await expect(dockerfileGenerator.genDockerfile()).to.eventually.equal(predefinedDockerfile + | ||
`\nLABEL elastic.io.component=${JSON.stringify(JSON.stringify(componentJsonTarLabel))}\n` + | ||
`LABEL elastic.io.logo="${logo}"`); | ||
}); | ||
}); | ||
@@ -181,3 +137,4 @@ it('should split long lines in the dockerfile labels', async () => { | ||
readFileStub.callThrough(); | ||
componentContextLoader = await ComponentContextLoader.getInstance('./spec/fixtures/component/nodejs'); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction('./spec/fixtures/component/nodejs')); | ||
await componentContextLoader.loadFilesToContext(); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
@@ -201,3 +158,4 @@ expect(dockerfileGenerator['getLabels']()).to.deep.eq(labelsWithSplittedLogo); | ||
const path = './spec/fixtures/component/nodejs'; | ||
componentContextLoader = await ComponentContextLoader.getInstance(path); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction(path)); | ||
await componentContextLoader.loadFilesToContext(); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
@@ -228,3 +186,4 @@ }); | ||
it('should install gradle in the dockerfile in case wrapper is not exists', async () => { | ||
componentContextLoader = await ComponentContextLoader.getInstance('./spec/fixtures/component/gradle'); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction('./spec/fixtures/component/gradle')); | ||
await componentContextLoader.loadFilesToContext(); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
@@ -234,3 +193,4 @@ expect(dockerfileGenerator.genDockerfile()).to.be.equal(gradleDockerfile); | ||
it('should use gradlew in the dockerfile', async () => { | ||
componentContextLoader = await ComponentContextLoader.getInstance('./spec/fixtures/component/gradle-wrapper'); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction('./spec/fixtures/component/gradle-wrapper')); | ||
await componentContextLoader.loadFilesToContext(); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
@@ -240,3 +200,4 @@ expect(dockerfileGenerator.genDockerfile()).to.be.equal(gradleWrapperDockerfile); | ||
it('genDockerfile should return dockerfile structure', async () => { | ||
componentContextLoader = await ComponentContextLoader.getInstance('./spec/fixtures/component/gradle'); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction('./spec/fixtures/component/gradle')); | ||
await componentContextLoader.loadFilesToContext(); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
@@ -250,3 +211,4 @@ expect(dockerfileGenerator.genDockerfile()).to.be.equal(gradleDockerfile); | ||
readFileStub.callThrough(); | ||
componentContextLoader = await ComponentContextLoader.getInstance(path); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction(path)); | ||
await componentContextLoader.loadFilesToContext(); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
@@ -263,3 +225,4 @@ await expect(dockerfileGenerator.genDockerfile()).to.be.equal(predefinedDockerfile + | ||
readFileStub.callThrough(); | ||
componentContextLoader = await ComponentContextLoader.getInstance(path); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction(path)); | ||
await componentContextLoader.loadFilesToContext(); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
@@ -276,3 +239,4 @@ await expect(dockerfileGenerator.genDockerfile()).to.be.equal(java11Dockerfile); | ||
readFileStub.callThrough(); | ||
componentContextLoader = await ComponentContextLoader.getInstance(path); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction(path)); | ||
await componentContextLoader.loadFilesToContext(); | ||
dockerfileGenerator = await DockerfileGenerator.getInstance(componentContextLoader); | ||
@@ -287,3 +251,4 @@ await expect(dockerfileGenerator.genDockerfile()).to.be.equal(java11PredefinedJdepsInfo); | ||
readFileStub.callThrough(); | ||
componentContextLoader = await ComponentContextLoader.getInstance(path); | ||
componentContextLoader = new ComponentContextLoader(buildGetFileFunction(path)); | ||
await componentContextLoader.loadFilesToContext(); | ||
await expect(DockerfileGenerator.getInstance(componentContextLoader)).to.be.rejectedWith('Unsupported Java version. Allowed values: ["1.8","11","17","18"]'); | ||
@@ -290,0 +255,0 @@ }); |
@@ -58,7 +58,7 @@ /* eslint-disable @typescript-eslint/camelcase */ | ||
function getFileFunction(repositoryPath: string): (path: string) => Promise<string> { | ||
function getFileFunction(repositoryPath: string): (path: string) => Promise<Buffer> { | ||
return (filePath: string) => { | ||
return new Promise((resolve, reject) => { | ||
if (components[repositoryPath + filePath]) { | ||
return resolve(JSON.stringify(components[repositoryPath + filePath])); | ||
return resolve(Buffer.from(JSON.stringify(components[repositoryPath + filePath]))); | ||
} | ||
@@ -71,3 +71,3 @@ reject(new Error('File ' + filePath + ' is missing')); | ||
it('should return promise', function () { | ||
expect(validateComponentJSON(async () => ('')).then).to.be.instanceof(Function); | ||
expect(validateComponentJSON(async () => (Buffer.from(''))).then).to.be.instanceof(Function); | ||
}); | ||
@@ -74,0 +74,0 @@ |
import type { Arguments, CommandBuilder } from 'yargs'; | ||
import { ComponentContextLoader } from '../ComponentContextLoader'; | ||
import { DockerfileGenerator } from '../DockerfileGenerator'; | ||
import { buildGetFileFunction } from '../utils'; | ||
@@ -19,3 +20,5 @@ interface Options { | ||
const { componentDirectoryPath } = argv; | ||
const contextLoader = await ComponentContextLoader.getInstance(componentDirectoryPath as string); | ||
const getFileFunction = buildGetFileFunction(componentDirectoryPath as string); | ||
const contextLoader = new ComponentContextLoader(getFileFunction); | ||
await contextLoader.loadFilesToContext(); | ||
const generator = await DockerfileGenerator.getInstance(contextLoader); | ||
@@ -22,0 +25,0 @@ process.stdout.write(generator.componentLanguage); |
import type { Arguments, CommandBuilder } from 'yargs'; | ||
import { DockerfileGenerator } from '../DockerfileGenerator'; | ||
import { ComponentContextLoader } from '../ComponentContextLoader'; | ||
import { buildGetFileFunction } from '../utils'; | ||
@@ -20,12 +21,11 @@ interface Options { | ||
demandOption: true, | ||
desc: 'Use "-" to read sources from stdin' }); | ||
desc: 'Component root directory' }); | ||
export async function handler(argv: Arguments<Options>) { | ||
// Make sure that nobody print something to the stdout by console.log function | ||
console.log = () => {}; | ||
const { componentDirectoryPath } = argv; | ||
let contextLoader: ComponentContextLoader; | ||
if (componentDirectoryPath === '-') { | ||
contextLoader = await ComponentContextLoader.getInstance(process.stdin); | ||
} else { | ||
contextLoader = await ComponentContextLoader.getInstance(componentDirectoryPath as string); | ||
} | ||
const getFileFunction = buildGetFileFunction(componentDirectoryPath as string); | ||
const contextLoader = new ComponentContextLoader(getFileFunction); | ||
await contextLoader.loadFilesToContext(); | ||
const generator = await DockerfileGenerator.getInstance(contextLoader); | ||
@@ -32,0 +32,0 @@ const dockerfile = await generator.genDockerfile(); |
import type { Arguments, CommandBuilder } from 'yargs'; | ||
import { promises } from 'fs'; | ||
import { validateComponentJSON } from '../metaloader'; | ||
import { buildGetFileFunction } from '../utils'; | ||
@@ -19,9 +19,5 @@ interface Options { | ||
const { componentDirectoryPath } = argv; | ||
const getFileFunction = async (fileRelativePath: string) => { | ||
const filePath = fileRelativePath.replace(/^\.\/|^\//g, ''); | ||
const buffer = await promises.readFile(`${(componentDirectoryPath as string).replace(/\/$/g, '')}/${filePath}`); | ||
return buffer.toString('utf-8'); | ||
}; | ||
const getFileFunction = buildGetFileFunction(componentDirectoryPath as string); | ||
await validateComponentJSON(getFileFunction); | ||
process.exit(0); | ||
}; |
@@ -1,9 +0,10 @@ | ||
/* eslint-disable @typescript-eslint/no-use-before-define */ | ||
import { Readable } from 'stream'; | ||
import tar, { ReadEntry } from 'tar'; | ||
import { promises as fsPromise } from 'fs'; | ||
import logger from './logger'; | ||
export enum SOURCE_INPUT_TYPE { STREAM, DIR } | ||
import { resolveMetadataRefs } from './metaloader'; | ||
export abstract class ComponentContextLoader { | ||
export class ComponentContextLoader { | ||
private getFileFunction: (path: string) => Promise<Buffer>; | ||
constructor(getFileFunction: (path: string) => Promise<Buffer>) { | ||
this.getFileFunction = getFileFunction; | ||
} | ||
protected _context: { [key: string]: string } = {} | ||
@@ -26,70 +27,16 @@ | ||
abstract loadFilesToContext(): Promise<{ [key: string]: string }>; | ||
static async getInstance(source: string | Readable) { | ||
let contextLoader; | ||
if (source instanceof Readable) { | ||
contextLoader = new StreamContextLoader(source); | ||
} else { | ||
contextLoader = new DirectoryContextLoader(source); | ||
} | ||
await contextLoader.loadFilesToContext(); | ||
return contextLoader; | ||
} | ||
get context(): { [p: string]: string } { | ||
return this._context; | ||
} | ||
} | ||
export class StreamContextLoader extends ComponentContextLoader { | ||
private readonly readStream: Readable; | ||
constructor(readStream: Readable) { | ||
super(); | ||
this.readStream = readStream; | ||
} | ||
loadFilesToContext(): Promise<{ [p: string]: string }> { | ||
return new Promise((resolve, reject) => { | ||
this.readStream | ||
.pipe(tar.list()) | ||
.on('entry', async (entry: ReadEntry) => { | ||
try { | ||
// Logo must be base64 encoded | ||
if (entry.type === 'File' && this.listOfFilesToLoad.includes(entry.path)) { | ||
if (entry.path === 'logo.png') { | ||
this.context[entry.path] = (await entry.concat()).toString('base64'); | ||
} else { | ||
this.context[entry.path] = (await entry.concat()).toString(); | ||
} | ||
} | ||
} catch (e) { | ||
reject(e); | ||
} | ||
}) | ||
.on('finish', () => resolve(this._context)) | ||
.on('error', reject); | ||
}); | ||
} | ||
} | ||
export class DirectoryContextLoader extends ComponentContextLoader { | ||
private readonly componentDirPath: string; | ||
constructor(componentDirPath: string) { | ||
super(); | ||
this.componentDirPath = componentDirPath.replace(/\/$/, ''); | ||
} | ||
async loadFilesToContext(): Promise<{ [key: string]: string }> { | ||
// check that dir exists | ||
await fsPromise.access(`${this.componentDirPath}/`); | ||
async loadFilesToContext() { | ||
await Promise.all(this.listOfFilesToLoad.map(async (filePath) => { | ||
try { | ||
const buffer = await fsPromise.readFile(`${this.componentDirPath}/${filePath}`); | ||
if (filePath === 'logo.png') { | ||
this._context[filePath] = buffer.toString('base64'); | ||
} else { | ||
this._context[filePath] = buffer.toString(); | ||
const buffer = await this.getFileFunction(filePath); | ||
if (buffer) { | ||
if (filePath === 'component.json') { | ||
const component = JSON.parse(buffer.toString()); | ||
await resolveMetadataRefs(component, this.getFileFunction); | ||
this._context[filePath] = JSON.stringify(component); | ||
} else if (filePath === 'logo.png') { | ||
this._context[filePath] = buffer.toString('base64'); | ||
} else { | ||
this._context[filePath] = buffer.toString(); | ||
} | ||
} | ||
@@ -107,2 +54,6 @@ } catch (e) { | ||
} | ||
get context(): { [p: string]: string } { | ||
return this._context; | ||
} | ||
} |
@@ -67,2 +67,41 @@ import { Action, Component, Metadata } from './interfaces'; | ||
declare type getFileType = (path: string) => Promise<Buffer>; | ||
async function resolveMetadataFromPath(metadata: Metadata, direction: 'in'|'out', getFile: getFileType) { | ||
const fileName = metadata[direction]; | ||
if (typeof fileName === 'string') { | ||
console.log(`Reading incoming ${fileName}`); | ||
const data = await getFile(fileName); | ||
console.log(`Parsing incoming ${fileName}`); | ||
metadata[direction] = JSON.parse(data.toString()); | ||
} | ||
} | ||
function resolveMetadataForObjects(actions: { [action: string]: Action }, getFile: getFileType) { | ||
const promises: Promise<any>[] = []; | ||
Object.entries(actions).forEach(([key, action]) => { | ||
const metadata = action.metadata; | ||
if (metadata) { | ||
console.log('Found metadata for', key); | ||
promises.push(resolveMetadataFromPath(metadata, 'in', getFile)); | ||
promises.push(resolveMetadataFromPath(metadata, 'out', getFile)); | ||
} | ||
}); | ||
return Promise.all(promises); | ||
} | ||
export function resolveMetadataRefs(component: Component, getFile: getFileType) { | ||
const triggers = component.triggers || {}; | ||
const actions = component.actions || {}; | ||
console.log('Found %s triggers and %s actions', | ||
Object.keys(triggers).length, | ||
Object.keys(actions).length); | ||
return Promise.all([ | ||
resolveMetadataForObjects(triggers, getFile), | ||
resolveMetadataForObjects(actions, getFile) | ||
]); | ||
} | ||
/** | ||
@@ -73,45 +112,8 @@ * | ||
*/ | ||
export async function validateComponentJSON(getFile: (path: string) => Promise<string>) { | ||
export async function validateComponentJSON(getFile: getFileType) { | ||
console.log('Reading incoming component.json'); | ||
const data = await getFile('./component.json'); | ||
console.log('Parsing incoming component.json'); | ||
const component = JSON.parse(data); | ||
const component = JSON.parse(data.toString()); | ||
async function resolveMetadataFromPath(metadata: Metadata, direction: 'in'|'out') { | ||
const fileName = metadata[direction]; | ||
if (typeof fileName === 'string') { | ||
console.log(`Reading incoming ${fileName}`); | ||
const data = await getFile(fileName); | ||
console.log(`Parsing incoming ${fileName}`); | ||
metadata[direction] = JSON.parse(data); | ||
} | ||
} | ||
function resolveMetadataForObjects(actions: { [action: string]: Action }) { | ||
const promises: Promise<any>[] = []; | ||
Object.entries(actions).forEach(([key, action]) => { | ||
const metadata = action.metadata; | ||
if (metadata) { | ||
console.log('Found metadata for', key); | ||
promises.push(resolveMetadataFromPath(metadata, 'in')); | ||
promises.push(resolveMetadataFromPath(metadata, 'out')); | ||
} | ||
}); | ||
return Promise.all(promises); | ||
} | ||
function resolveMetadataRefs(component: Component) { | ||
const triggers = component.triggers || {}; | ||
const actions = component.actions || {}; | ||
console.log('Found %s triggers and %s actions', | ||
Object.keys(triggers).length, | ||
Object.keys(actions).length); | ||
return Promise.all([ | ||
resolveMetadataForObjects(triggers), | ||
resolveMetadataForObjects(actions) | ||
]); | ||
} | ||
validateComponentVersion(component.version); | ||
@@ -121,3 +123,3 @@ validateFunctionsNames(component); | ||
validateCredentials(component); | ||
await resolveMetadataRefs(component); | ||
await resolveMetadataRefs(component, getFile); | ||
return component; | ||
@@ -124,0 +126,0 @@ } |
Sorry, the diff of this file is too big to display
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
5
401952
3296
2
24