@agondata/blobpack
Version:
Packaging Benthos configs for AWS Lambda was too exciting, now it's acceptably boring.
143 lines (119 loc) • 3.8 kB
JavaScript
import fs from 'fs'
import path from 'path'
import mkdirp from 'mkdirp'
import jsYaml from 'js-yaml'
import jszip from 'jszip'
import { loadConfig } from './config.js'
import { zipName } from './zip-name.js'
import { mergeArrayProps, mergeConfig } from './merge.js'
const { load: yamlLoad, dump: yamlDump } = jsYaml
const { loadAsync: zipLoadAsync } = jszip
const staticConfigName = 'benthos.yaml'
export const build = async ({
configPath = 'package.json',
tmpRoot = 'tmp',
configRoot = 'config',
resourcesRoot = '',
distRoot = 'dist',
logger = console
} = {}) => {
try {
const config = await loadConfig(configPath)
const artifacts = await createArtifacts(config, {
tmpRoot,
configRoot,
resourcesRoot,
distRoot
})
for (const artifact of artifacts) {
logger.log(`Built artifact: ${artifact}`)
}
return artifacts
} catch (err) {
logger.error(err)
throw err
}
}
const createArtifacts = async (
{ name, version, platform, include = {}, artifacts = [] },
{ tmpRoot, configRoot, resourcesRoot, distRoot }
) => {
if (!tmpRoot) throw new Error('Missing tmpRoot')
if (!configRoot) throw new Error('Missing configRoot')
if (!distRoot) throw new Error('Missing distRoot')
const srcZipPath = path.resolve(tmpRoot, zipName({ name, version, platform }))
const srcZipBuffer = await fs.promises.readFile(srcZipPath)
const createArtifact = createArtifactFrom(
srcZipBuffer,
configRoot,
resourcesRoot,
distRoot
)
await mkdirp(path.resolve(distRoot))
return Promise.all(artifacts.map(normalizeInput(include)).map(createArtifact))
}
const normalizeInput = (include) => (input) => {
if (typeof input === 'string') return { name: input, ...include }
const { name, ...rest } = input
return {
name,
...mergeArrayProps(rest, include)
}
}
const createArtifactFrom =
(srcZipBuffer, configRoot, resourcesRoot, distRoot) => async (artifact) => {
const data = await loadArtifactConfig(configRoot, resourcesRoot, artifact)
const artifactBuffer = await generateZipFrom(srcZipBuffer, [
{ name: staticConfigName, data }
])
const outputName = `${artifact.name}.zip`
const outputPath = path.resolve(distRoot, outputName)
await fs.promises.writeFile(outputPath, artifactBuffer)
return outputPath
}
const loadArtifactConfig = async (
configRoot,
resourcesRoot,
{ name, ...rest }
) => {
const resources = Object.entries(rest).reduce(reduceResources, [])
const [config, resourceConfig] = await Promise.all([
loadYaml(path.resolve(configRoot, `${name}.yaml`)),
loadResources(resourcesRoot, resources)
])
const data = mergeConfig(resourceConfig, config)
return yamlDump(data)
}
const reduceResources = (accumulator, [root, names]) => {
const paths = names
.map((name) => path.join(...name.split('/')))
.map((name) => path.join(root, name))
return [...accumulator, ...paths]
}
const loadResources = async (resourcesRoot, resources) => {
const loadResource = (name) =>
loadYaml(path.resolve(resourcesRoot, `${name}.yaml`))
const data = await Promise.all(resources.map(loadResource))
return data.reduce(mergeConfig, {
input_resources: [],
cache_resources: [],
rate_limit_resources: [],
processor_resources: [],
output_resources: []
})
}
const loadYaml = async (name) => {
const buf = await fs.promises.readFile(path.resolve(name))
return yamlLoad(buf)
}
const generateZipFrom = async (srcZipBuffer, files = []) => {
const zip = await zipLoadAsync(srcZipBuffer)
for (const { name, data } of files) {
zip.file(name, data, { unixPermissions: '644' })
}
return zip.generateAsync({
type: 'nodebuffer',
compression: 'DEFLATE',
platform: process.platform
})
}