Created
March 17, 2025 18:42
-
-
Save eighty4/fec7ae5ece08bf56e78bfc000ebf2ff6 to your computer and use it in GitHub Desktop.
Bun.sh generating multipart cloud-init user data for devops
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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