Files
herolib/lib/virt/podman/builder.v
Mahmoud-Emad 1228441fd6 feat: Add Buildah builder API and refactor module
- Introduce `Builder` struct for image creation
- Implement `PodmanFactory` methods for builder lifecycle
- Rename `herocontainers` module to `podman`
- Update `PodmanFactory.new` with platform checks
- Revise documentation for `podman` and Buildah usage
2025-08-26 20:28:38 +03:00

218 lines
5.3 KiB
V

module podman
import time
import freeflowuniverse.herolib.osal.core as osal { exec }
import freeflowuniverse.herolib.ui.console
import json
@[heap]
pub struct Builder {
pub mut:
id string
containername string
imageid string
imagename string
created time.Time
engine &PodmanFactory @[skip; str: skip]
}
pub struct BuilderInfo {
pub:
id string
containername string
imageid string
imagename string
created string
}
// load all buildah containers/builders
pub fn (mut self PodmanFactory) builders_load() ! {
self.builders = []Builder{}
cmd := 'buildah containers --json'
out := osal.execute_silent(cmd) or {
// If buildah is not installed or no containers, return empty list
console.print_debug('buildah containers command failed: ${err}')
return
}
if out.trim_space() == '' || out.trim_space() == '[]' {
return
}
mut r := json.decode([]BuilderInfo, out) or {
console.print_debug('Failed to decode buildah JSON: ${err}')
return
}
for item in r {
mut builder := Builder{
engine: &self
id: item.id
containername: item.containername
imageid: item.imageid
imagename: item.imagename
}
// Parse created time if needed
builder.created = time.now() // TODO: parse from item.created
self.builders << builder
}
}
// delete all buildah containers/builders
pub fn (mut self PodmanFactory) builders_delete_all() ! {
for mut builder in self.builders.clone() {
builder.delete()!
}
self.builders = []Builder{}
}
@[params]
pub struct BuilderNewArgs {
pub mut:
name string = 'default'
from string = 'docker.io/ubuntu:latest'
delete bool = true
}
pub fn (mut e PodmanFactory) builder_new(args_ BuilderNewArgs) !Builder {
mut args := args_
if args.delete {
e.builder_delete(args.name)!
}
exec(cmd: 'buildah --name ${args.name} from ${args.from}')!
e.builders_load()!
return e.builder_get(args.name)!
}
// get buildah containers
pub fn (mut e PodmanFactory) builders_get() ![]Builder {
if e.builders.len == 0 {
e.builders_load()!
}
return e.builders
}
pub fn (mut e PodmanFactory) builder_exists(name string) !bool {
r := e.builders_get()!
res := r.filter(it.containername == name)
if res.len == 1 {
return true
}
if res.len > 1 {
panic('bug: multiple builders with same name')
}
return false
}
pub fn (mut e PodmanFactory) builder_get(name string) !Builder {
r := e.builders_get()!
res := r.filter(it.containername == name)
if res.len == 0 {
return error('builder with name ${name} not found')
}
if res.len > 1 {
return error('multiple builders found with name ${name}')
}
return res[0]
}
pub fn (mut e PodmanFactory) builder_delete(name string) ! {
if e.builder_exists(name)! {
exec(cmd: 'buildah rm ${name}', stdout: false) or {
console.print_debug('Failed to delete builder ${name}: ${err}')
}
}
e.builders_load()!
}
// Builder methods
pub fn (mut self Builder) delete() ! {
self.engine.builder_delete(self.containername)!
}
pub fn (mut self Builder) run(cmd string) ! {
cmd_str := 'buildah run ${self.id} ${cmd}'
exec(cmd: cmd_str)!
}
pub fn (mut self Builder) copy(src string, dest string) ! {
cmd := 'buildah copy ${self.id} ${src} ${dest}'
exec(cmd: cmd, stdout: false)!
}
pub fn (mut self Builder) shell() ! {
cmd := 'buildah run --terminal --env TERM=xterm ${self.id} /bin/bash'
osal.execute_interactive(cmd)!
}
pub fn (mut self Builder) commit(image_name string) ! {
cmd := 'buildah commit ${self.containername} ${image_name}'
exec(cmd: cmd)!
}
pub fn (self Builder) set_entrypoint(entrypoint string) ! {
cmd := 'buildah config --entrypoint \'${entrypoint}\' ${self.containername}'
exec(cmd: cmd)!
}
pub fn (self Builder) set_workingdir(workdir string) ! {
cmd := 'buildah config --workingdir ${workdir} ${self.containername}'
exec(cmd: cmd)!
}
pub fn (self Builder) set_cmd(command string) ! {
cmd := 'buildah config --cmd ${command} ${self.containername}'
exec(cmd: cmd)!
}
// mount the build container to a path and return the path where its mounted
pub fn (mut self Builder) mount_to_path() !string {
cmd := 'buildah mount ${self.containername}'
out := osal.execute_silent(cmd)!
return out.trim_space()
}
// Builder solution methods for common setups
@[params]
pub struct GetArgs {
pub mut:
reset bool
}
// builder machine based on arch and install vlang
pub fn (mut e PodmanFactory) builder_base(args GetArgs) !Builder {
name := 'base'
if !args.reset && e.builder_exists(name)! {
return e.builder_get(name)!
}
console.print_header('buildah base build')
mut builder := e.builder_new(name: name, from: 'scratch', delete: true)!
mount_path := builder.mount_to_path()!
if mount_path.len < 4 {
return error('mount_path needs to be +4 chars')
}
// TODO: Add base system setup here
console.print_header('buildah base build done')
return builder
}
// builder with hero tools
pub fn (mut e PodmanFactory) builder_hero(args GetArgs) !Builder {
name := 'hero'
if !args.reset && e.builder_exists(name)! {
return e.builder_get(name)!
}
console.print_header('buildah hero build')
mut builder := e.builder_new(name: name, from: 'docker.io/ubuntu:latest', delete: true)!
// Install basic tools and hero
builder.run('apt-get update && apt-get install -y curl git build-essential')!
// TODO: Add hero installation steps
console.print_header('buildah hero build done')
return builder
}