Skip to content

Instantly share code, notes, and snippets.

@eighty4
Created March 17, 2025 18:42
Show Gist options
  • Save eighty4/fec7ae5ece08bf56e78bfc000ebf2ff6 to your computer and use it in GitHub Desktop.
Save eighty4/fec7ae5ece08bf56e78bfc000ebf2ff6 to your computer and use it in GitHub Desktop.
Bun.sh generating multipart cloud-init user data for devops
import { Glob } from 'bun'
import MagicString from 'magic-string'
type AttachmentType = 'cloud-config'
interface Attachment {
content: string
filename: string
type: AttachmentType
}
function resolveAttachmentType(
filename: string,
content: string,
): AttachmentType {
if (
filename.endsWith('.yml') ||
(filename.endsWith('.yaml') &&
content.trim().startsWith('#cloud-config'))
) {
return 'cloud-config'
} else {
throw new Error()
}
}
async function collectAttachments(dir: string) {
const result: Array<Attachment> = []
for (const filename of new Glob('*').scanSync(dir)) {
const content = await Bun.file(`${dir}/${filename}`).text()
const type = resolveAttachmentType(filename, content)
result.push({ type, content, filename })
}
return result
}
type TemplateExpression = {
index: number
innie: string
outie: string
}
function templateContent(content: string) {
const regex = new RegExp(/{{\s*(.*)\s*}}/g)
let match: RegExpExecArray | null
const expressions: Array<TemplateExpression> = []
while ((match = regex.exec(content)) != null) {
expressions.push({
index: match.index,
innie: match[1],
outie: match[0],
})
}
if (!expressions.length) {
return content
}
const ms = new MagicString(content)
for (const expression of expressions) {
ms.update(
expression.index,
expression.index + expression.outie.length,
evaluateExpression(expression.innie),
)
}
return ms.toString()
}
function evaluateExpression(expression: string): string {
let match: RegExpMatchArray | null
if ((match = expression.match(/env\(\s*'(.*)'\s*\)/)) != null) {
const envVarKey = match[1]
if (!/[A-Z_]+/.test(envVarKey)) {
throw new Error(
`env var expression \`${envVarKey}\` is not valid syntax`,
)
}
const envVarValue = Bun.env[envVarKey]
if (!envVarValue) {
throw new Error(`env var \`${envVarKey}\` does not exist`)
}
return envVarValue
}
throw new Error('unsupported expression: ' + expression)
}
const attachments = await collectAttachments('init')
const boundary = Bun.randomUUIDv7()
let result = `
Content-Type: multipart/form-data; boundary=${boundary}
MIME-Version: 1.0
Number-Attachments: ${attachments.length}
`
function appendAttachment(attachment: Attachment) {
let content: string
try {
content = templateContent(attachment.content)
} catch (e) {
console.error(`error templating ${attachment.filename}: ${e.message}`)
}
}
for (const attachment of attachments) {
let content: string
try {
content = templateContent(attachment.content)
} catch (e) {
console.error(`error templating ${attachment.filename}: ${e.message}`)
process.exit(1)
}
result += `
Content-Type: text/${attachment.type}; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="${attachment.filename}"
${content}
--${boundary}
`
}
console.log(result)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment