This commit is contained in:
2025-08-16 05:13:18 +02:00
parent 97d506ecbf
commit 1cd8e8c299
10 changed files with 315 additions and 184 deletions

View File

@@ -3,16 +3,26 @@
import freeflowuniverse.herolib.core.playcmds import freeflowuniverse.herolib.core.playcmds
import freeflowuniverse.herolib.clients.giteaclient import freeflowuniverse.herolib.clients.giteaclient
// Configure PostgreSQL client heroscript := "
// heroscript := " !!giteaclient.configure
// !!giteaclient.configure name: 'default'
// url: 'git.ourworld.tf' url: 'git.ourworld.tf'
// user: 'despiegk' user: 'despiegk'
// secret: '' secret: '1'
// "
// // Process the heroscript configuration !!giteaclient.configure
// playcmds.play(heroscript: heroscript, emptycheck: false)! name: 'two'
url: 'git.ourworld.tf'
user: 'despiegk2'
secret: '2'
"
// Process the heroscript configuration
playcmds.play(heroscript: heroscript, emptycheck: false)!
println(giteaclient.list()!)
$dbg;
// Get the configured client // Get the configured client
mut client := giteaclient.get()! mut client := giteaclient.get()!

View File

@@ -14,61 +14,60 @@ __global (
@[params] @[params]
pub struct ArgsGet { pub struct ArgsGet {
pub mut: pub mut:
name string name string = "default"
fromdb bool //will load from filesystem
create bool //default will not create if not exist
} }
fn args_get(args_ ArgsGet) ArgsGet { pub fn new(args ArgsGet) !&GiteaClient {
mut args := args_
if args.name == '' {
args.name = 'default'
}
return args
}
pub fn get(args_ ArgsGet) !&GiteaClient {
mut context := base.context()!
mut args := args_get(args_)
mut obj := GiteaClient{ mut obj := GiteaClient{
name: args.name name: args.name
} }
if args.name !in giteaclient_global { set(obj)!
if !exists(args)! { return &obj
set(obj)! }
} else {
pub fn get(args ArgsGet) !&GiteaClient {
mut context := base.context()!
giteaclient_default = args.name
if args.fromdb || args.name !in giteaclient_global {
if context.hero_config_exists('giteaclient', args.name) {
heroscript := context.hero_config_get('giteaclient', args.name)! heroscript := context.hero_config_get('giteaclient', args.name)!
mut obj_ := heroscript_loads(heroscript)! mut obj_ := heroscript_loads(heroscript)!
set_in_mem(obj_)! set_in_mem(obj_)!
}else{
if args.create {
new(args)!
}else{
return error("GiteaClient with name '${args.name}' does not exist")
}
} }
return get(name: args.name)! //no longer from db nor create
} }
return giteaclient_global[args.name] or { return giteaclient_global[args.name] or {
println(giteaclient_global) return error('could not get config for giteaclient with name:${args.name}')
// bug if we get here because should be in globals
panic('could not get config for giteaclient with name, is bug:${args.name}')
} }
} }
// register the config for the future // register the config for the future
pub fn set(o GiteaClient) ! { pub fn set(o GiteaClient) ! {
set_in_mem(o)! set_in_mem(o)!
giteaclient_default = o.name
mut context := base.context()! mut context := base.context()!
heroscript := heroscript_dumps(o)! heroscript := heroscript_dumps(o)!
context.hero_config_set('giteaclient', o.name, heroscript)! context.hero_config_set('giteaclient', o.name, heroscript)!
} }
// does the config exists? // does the config exists?
pub fn exists(args_ ArgsGet) !bool { pub fn exists(args ArgsGet) !bool {
mut context := base.context()! mut context := base.context()!
mut args := args_get(args_)
return context.hero_config_exists('giteaclient', args.name) return context.hero_config_exists('giteaclient', args.name)
} }
pub fn delete(args_ ArgsGet) ! { pub fn delete(args ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()! mut context := base.context()!
context.hero_config_delete('giteaclient', args.name)! giteaclient_global.delete(args.name)
if args.name in giteaclient_global { context.hero_config_delete('giteaclient', args.name)!
// del giteaclient_global[args.name]
}
} }
@[params] @[params]
@@ -77,12 +76,28 @@ pub mut:
fromdb bool //will load from filesystem fromdb bool //will load from filesystem
} }
// if fromdb set: load from filesystem, and not from mem, will also reset what is in mem
pub fn list(args ArgsList) ![]&GiteaClient { pub fn list(args ArgsList) ![]&GiteaClient {
mut res := []&GiteaClient{} mut res := []&GiteaClient{}
if args.name in giteaclient_global { mut context := base.context()!
res << giteaclient_global[o.name] if args.fromdb {
// reset what is in mem
giteaclient_global = map[string]&GiteaClient{}
giteaclient_default = ''
}
if args.fromdb {
for name in context.hero_config_list('giteaclient')!{
mut hscript := context.hero_config_get('giteaclient', name)!
mut obj := heroscript_loads(hscript)!
set_in_mem(obj)!
res << &obj
}
return res
} else {
// load from memory
for _, client in giteaclient_global {
res << client
}
} }
return res return res
} }

View File

@@ -133,28 +133,37 @@ pub fn (mut self Context) db_config_get() !dbfs.DB {
pub fn (mut self Context) hero_config_set(cat string, name string, content_ string) ! { pub fn (mut self Context) hero_config_set(cat string, name string, content_ string) ! {
mut content := texttools.dedent(content_) mut content := texttools.dedent(content_)
content = rootpath.shell_expansion(content) content = rootpath.shell_expansion(content)
path := '${self.path()!.path}/${cat}__${name}.yaml' path := '${self.path()!.path}/${cat}/${name}.hero'
mut config_file := pathlib.get_file(path: path)! mut config_file := pathlib.get_file(path: path,create: true)!
config_file.write(content)! config_file.write(content)!
} }
pub fn (mut self Context) hero_config_delete(cat string, name string) ! { pub fn (mut self Context) hero_config_delete(cat string, name string) ! {
path := '${self.path()!.path}/${cat}__${name}.yaml' path := '${self.path()!.path}/${cat}/${name}.hero'
mut config_file := pathlib.get_file(path: path)! mut config_file := pathlib.get_file(path: path)!
config_file.delete()! config_file.delete()!
} }
pub fn (mut self Context) hero_config_exists(cat string, name string) bool { pub fn (mut self Context) hero_config_exists(cat string, name string) bool {
path := '${os.home_dir()}/hero/context/${self.config.name}/${cat}__${name}.yaml' path := '${os.home_dir()}/hero/context/${self.config.name}/${cat}/${name}.hero'
return os.exists(path) return os.exists(path)
} }
pub fn (mut self Context) hero_config_get(cat string, name string) !string { pub fn (mut self Context) hero_config_get(cat string, name string) !string {
path := '${self.path()!.path}/${cat}__${name}.yaml' path := '${self.path()!.path}/${cat}/${name}.hero'
mut config_file := pathlib.get_file(path: path, create: false)! mut config_file := pathlib.get_file(path: path, create: false)!
return config_file.read()! return config_file.read()!
} }
pub fn (mut self Context) hero_config_list(cat string) ![]string {
path := '${self.path()!.path}/${cat}/*.hero'
mut config_files := os.ls(path)!
println(config_files)
$dbg;
return config_files
}
pub fn (mut self Context) secret_encrypt(txt string) !string { pub fn (mut self Context) secret_encrypt(txt string) !string {
return aes_symmetric.encrypt_str(txt, self.secret_get()!) return aes_symmetric.encrypt_str(txt, self.secret_get()!)
} }

View File

@@ -21,103 +21,131 @@ __global (
/////////FACTORY /////////FACTORY
^^[params] @[params]
pub struct ArgsGet{ pub struct ArgsGet {
pub mut: pub mut:
name string name string = "default"
fromdb bool //will load from filesystem
create bool //default will not create if not exist
} }
@if args.hasconfig @if args.hasconfig
fn args_get (args_ ArgsGet) ArgsGet { pub fn new(args ArgsGet) !&${args.classname} {
mut args:=args_ mut obj := ${args.classname}{
if args.name == ""{ name: args.name
args.name = "default" }
} set(obj)!
return args return &obj
} }
pub fn get(args_ ArgsGet) !&${args.classname} { pub fn get(args ArgsGet) !&${args.classname} {
mut context:=base.context()! mut context := base.context()!
mut args := args_get(args_) ${args.name}_default = args.name
mut obj := ${args.classname}{name:args.name} if args.fromdb || args.name !in ${args.name}_global {
if !(args.name in ${args.name}_global) { if context.hero_config_exists('${args.name}', args.name) {
if ! exists(args)!{ heroscript := context.hero_config_get('${args.name}', args.name)!
set(obj)! mut obj_ := heroscript_loads(heroscript)!
}else{ set_in_mem(obj_)!
heroscript := context.hero_config_get("${args.name}",args.name)! }else{
mut obj_:=heroscript_loads(heroscript)! if args.create {
set_in_mem(obj_)! new(args)!
} }else{
return error("${args.classname} with name '\${args.name}' does not exist")
}
}
return get(name: args.name)! //no longer from db nor create
} }
return ${args.name}_global[args.name] or { return ${args.name}_global[args.name] or {
println(${args.name}_global) return error('could not get config for ${args.name} with name:\${args.name}')
//bug if we get here because should be in globals }
panic("could not get config for ${args.name} with name, is bug:??{args.name}")
}
} }
//register the config for the future // register the config for the future
pub fn set(o ${args.classname})! { pub fn set(o ${args.classname}) ! {
set_in_mem(o)! set_in_mem(o)!
mut context := base.context()! ${args.name}_default = o.name
heroscript := heroscript_dumps(o)! mut context := base.context()!
context.hero_config_set("${args.name}", o.name, heroscript)! heroscript := heroscript_dumps(o)!
context.hero_config_set('${args.name}', o.name, heroscript)!
} }
//does the config exists? // does the config exists?
pub fn exists(args_ ArgsGet)! bool { pub fn exists(args ArgsGet) !bool {
mut context := base.context()! mut context := base.context()!
mut args := args_get(args_) return context.hero_config_exists('${args.name}', args.name)
return context.hero_config_exists("${args.name}", args.name)
} }
pub fn delete(args_ ArgsGet)! { pub fn delete(args ArgsGet) ! {
mut args := args_get(args_) mut context := base.context()!
mut context:=base.context()! ${args.name}_global.delete(args.name)
context.hero_config_delete("${args.name}",args.name)! context.hero_config_delete('${args.name}', args.name)!
if args.name in ${args.name}_global {
//del ${args.name}_global[args.name]
}
} }
pub fn list()![]&${args.classname} { @[params]
mut args := args_get(args_) pub struct ArgsList {
mut res:=[]&${args.classname} pub mut:
if args.name in ${args.name}_global { fromdb bool //will load from filesystem
res << ${args.name}_global[o.name] }
}
return res // if fromdb set: load from filesystem, and not from mem, will also reset what is in mem
pub fn list(args ArgsList) ![]&${args.classname} {
mut res := []&${args.classname}{}
mut context := base.context()!
if args.fromdb {
// reset what is in mem
${args.name}_global = map[string]&${args.classname}{}
${args.name}_default = ''
}
if args.fromdb {
for name in context.hero_config_list('${args.name}')!{
mut hscript := context.hero_config_get('${args.name}', name)!
mut obj := heroscript_loads(hscript)!
set_in_mem(obj)!
res << &obj
}
return res
} else {
// load from memory
for _, client in ${args.name}_global {
res << client
}
}
return res
} }
//only sets in mem, does not set as config // only sets in mem, does not set as config
fn set_in_mem(o ${args.classname})! { fn set_in_mem(o ${args.classname}) ! {
mut o2:=obj_init(o)! mut o2 := obj_init(o)!
${args.name}_global[o.name] = &o2 ${args.name}_global[o.name] = &o2
${args.name}_default = o.name ${args.name}_default = o.name
}
// switch instance to be used for ${args.name}
pub fn switch(name string) {
${args.name}_default = name
} }
@else @else
pub fn get(args_ ArgsGet) !&${args.classname} { pub fn new(args ArgsGet) !&${args.classname} {
return &${args.classname}{} return &${args.classname}{}
} }
@end @end
pub fn play(mut plbook PlayBook) ! { pub fn play(mut plbook PlayBook) ! {
mut install_actions := plbook.find(filter: '${args.name}.configure')!
if install_actions.len > 0 {
@if args.hasconfig @if args.hasconfig
mut install_actions := plbook.find(filter: '${args.name}.configure')! for install_action in install_actions {
if install_actions.len > 0 { heroscript := install_action.heroscript()
for install_action in install_actions { mut obj2 := heroscript_loads(heroscript)!
heroscript:=install_action.heroscript() set(obj2)!
mut obj2:=heroscript_loads(heroscript)! }
set(obj2)! @else
} panic("can't configure ??{${args.name}_obj}, because no config in this class.")
} @end
@end }
@if args.cat == .installer @if args.cat == .installer
mut other_actions := plbook.find(filter: '${args.name}.')! mut other_actions := plbook.find(filter: '${args.name}.')!
for other_action in other_actions { for other_action in other_actions {
@@ -155,16 +183,17 @@ pub fn play(mut plbook PlayBook) ! {
} }
@end @end
} }
@end @end
} }
@if args.cat == .installer @if args.cat == .installer
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS /////////////////////////////////// //////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
@if args.startupmanager
fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager { fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager {
// unknown // unknown
// screen // screen
@@ -185,6 +214,7 @@ fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManag
} }
} }
} }
@end
@if args.hasconfig @if args.hasconfig
//load from disk and make sure is properly intialized //load from disk and make sure is properly intialized
@@ -305,11 +335,3 @@ pub fn (mut self ${args.classname}) destroy() ! {
pub fn switch(name string) { pub fn switch(name string) {
${args.name}_default = name ${args.name}_default = name
} }
//helpers
^^[params]
pub struct DefaultConfigArgs{
instance string = 'default'
}

View File

@@ -206,11 +206,11 @@ fn (mut repo GitRepo) update_submodules() ! {
fn (repo GitRepo) exec(cmd_ string) !string { fn (repo GitRepo) exec(cmd_ string) !string {
repo_path := repo.path() repo_path := repo.path()
if cmd_.starts_with("pull ") || cmd_.starts_with("push ") || cmd_.starts_with("fetch "){ // if cmd_.starts_with("pull ") || cmd_.starts_with("push ") || cmd_.starts_with("fetch "){
//means we need to be able to fetch from remote repo, check we have a key registered e.g. for gitea // //means we need to be able to fetch from remote repo, check we have a key registered e.g. for gitea
$dbg; // $dbg;
} // }
cmd := 'cd ${repo_path} && git ${cmd_}' cmd := 'cd ${repo_path} && git ${cmd_}'
// console.print_debug(cmd) // console.print_debug(cmd)
r := os.execute(cmd) r := os.execute(cmd)

View File

@@ -1,12 +1,13 @@
!!hero_code.generate_installer
name: "python"
classname: "Python"
hasconfig: false
singleton: true
default: true
title: ""
templates: false
build: false
startupmanager: false
supported_platforms: ""
!!hero_code.generate_installer
name:'python'
classname:'PythonInstaller'
singleton:1
templates:0
default:1
title:''
supported_platforms:''
reset:0
startupmanager:0
hasconfig:0
build:0

View File

@@ -1,31 +1,28 @@
module python module python
import freeflowuniverse.herolib.osal.core as osal import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.installers.base import freeflowuniverse.herolib.installers.base
import freeflowuniverse.herolib.core.texttools import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.installers.ulist import freeflowuniverse.herolib.installers.ulist
import freeflowuniverse.herolib.core.texttools
import os import os
//////////////////// following actions are not specific to instance of the object //////////////////// following actions are not specific to instance of the object
// checks if a certain version or above is installed
fn installed() !bool { fn installed() !bool {
res := os.execute('python3 --version') res := os.execute('${osal.profile_path_source_and()!} uv self version')
if res.exit_code != 0 { if res.exit_code != 0 {
return false return false
} }
r := res.output.split_into_lines().filter(it.trim_space().len > 0) r := res.output.split_into_lines().filter(it.trim_space().len > 0)
if r.len != 1 { if r.len != 1 {
return error("couldn't parse pnpm version.\n${res.output}") return error("couldn't parse python version.\n${res.output}")
} }
r2 := r[0].split(' ')[1] or { return error("couldn't parse python version.\n${res.output}") }
if texttools.version(r[0].all_after_first('ython')) >= texttools.version(version) { if texttools.version(version) <= texttools.version(r2) {
return true return true
} }
return false return false
} }
@@ -49,29 +46,44 @@ fn install() ! {
osal.package_install('python3')! osal.package_install('python3')!
pl := core.platform()! pl := core.platform()!
if pl == .arch { if pl == .ubuntu {
osal.package_install('python-pipx,sqlite')! osal.package_install('python3')!
} else if pl == .ubuntu {
osal.package_install('pipx,sqlite')!
} else if pl == .osx { } else if pl == .osx {
osal.package_install('pipx,sqlite')! osal.package_install('python@3.12')!
} else { } else {
return error('only support osx, arch & ubuntu.') return error('only support osx & ubuntu.')
}
osal.execute_silent('curl -LsSf https://astral.sh/uv/install.sh | sh')!
if pl == .ubuntu {
osal.execute_silent('echo \'eval "$(uvx --generate-shell-completion bash)"\' >> ~/.bashrc')!
} else if pl == .osx {
osal.execute_silent('echo \'eval "$(uvx --generate-shell-completion bash)"\' >> ~/.bashrc')!
osal.execute_silent('echo \'eval "$(uvx --generate-shell-completion bash)"\' >> ~/.zshrc')!
} else {
return error('only support osx & ubuntu.')
} }
osal.execute_silent('pipx install uv')!
} }
fn destroy() ! { fn destroy() ! {
console.print_header('destroy python') console.print_header('remove python uv')
osal.package_remove('python3')!
pl := core.platform()! // //will remove all paths where go/bin is found
if pl == .arch { // osal.profile_path_add_remove(paths2delete:"go/bin")!
osal.package_remove('pipx,sqlite')!
} else if pl == .ubuntu { dir1 := osal.exec_fast(cmd: 'uv python dir', notempty: true)!
osal.package_remove('pipx,sqlite')! dir2 := osal.exec_fast(cmd: 'uv tool dir', notempty: true)!
} else if pl == .osx { dir3 := osal.exec_fast(cmd: 'uv cache dir', notempty: true)!
osal.package_remove('pipx,sqlite')!
} else { osal.execute_silent('
return error('only support osx, arch & ubuntu.')
} uv cache clean
rm -rf "${dir1}"
rm -rf "${dir2}"
rm -rf "${dir3}"
rm ~/.local/bin/uv ~/.local/bin/uvx
')!
osal.rm('
uv
uvx
')!
} }

View File

@@ -6,7 +6,7 @@ import freeflowuniverse.herolib.osal.startupmanager
import freeflowuniverse.herolib.osal.zinit import freeflowuniverse.herolib.osal.zinit
__global ( __global (
python_global map[string]&Python python_global map[string]&PythonInstaller
python_default string python_default string
) )
@@ -18,8 +18,8 @@ pub mut:
name string name string
} }
pub fn get(args_ ArgsGet) !&Python { pub fn get(args_ ArgsGet) !&PythonInstaller {
return &Python{} return &PythonInstaller{}
} }
pub fn play(mut plbook PlayBook) ! { pub fn play(mut plbook PlayBook) ! {
@@ -72,14 +72,14 @@ pub mut:
reset bool reset bool
} }
pub fn (mut self Python) install(args InstallArgs) ! { pub fn (mut self PythonInstaller) install(args InstallArgs) ! {
switch(self.name) switch(self.name)
if args.reset || (!installed()!) { if args.reset || (!installed()!) {
install()! install()!
} }
} }
pub fn (mut self Python) destroy() ! { pub fn (mut self PythonInstaller) destroy() ! {
switch(self.name) switch(self.name)
destroy()! destroy()!
} }

View File

@@ -1,20 +1,21 @@
module python module python
import freeflowuniverse.herolib.data.encoderhero import freeflowuniverse.herolib.data.encoderhero
import os
pub const version = '3.12.0' pub const version = '0.8.11'
const singleton = true const singleton = false
const default = true const default = true
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED // THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
@[heap] @[heap]
pub struct Python { pub struct PythonInstaller {
pub mut: pub mut:
name string = 'default' name string = 'default'
} }
// your checking & initialization code if needed // your checking & initialization code if needed
fn obj_init(mycfg_ Python) !Python { fn obj_init(mycfg_ PythonInstaller) !PythonInstaller {
mut mycfg := mycfg_ mut mycfg := mycfg_
return mycfg return mycfg
} }
@@ -26,11 +27,11 @@ fn configure() ! {
/////////////NORMALLY NO NEED TO TOUCH /////////////NORMALLY NO NEED TO TOUCH
pub fn heroscript_dumps(obj Python) !string { pub fn heroscript_dumps(obj PythonInstaller) !string {
return encoderhero.encode[Python](obj)! return encoderhero.encode[PythonInstaller](obj)!
} }
pub fn heroscript_loads(heroscript string) !Python { pub fn heroscript_loads(heroscript string) !PythonInstaller {
mut obj := encoderhero.decode[Python](heroscript)! mut obj := encoderhero.decode[PythonInstaller](heroscript)!
return obj return obj
} }

View File

@@ -1,6 +1,6 @@
module core module core
// import freeflowuniverse.herolib.core.texttools import freeflowuniverse.herolib.core.texttools
// import freeflowuniverse.herolib.core.pathlib // import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.ui.console import freeflowuniverse.herolib.ui.console
import json import json
@@ -481,3 +481,64 @@ pub fn exec_string(cmd Command) !string {
job.cmd.scriptpath = cmd_to_script_path(job.cmd)! job.cmd.scriptpath = cmd_to_script_path(job.cmd)!
return job.cmd.scriptpath return job.cmd.scriptpath
} }
@[params]
pub struct CommandFast {
pub mut:
cmd string
ignore_error bool // means if error will just exit and not raise, there will be no error reporting
work_folder string // location where cmd will be executed
environment map[string]string // env variables
ignore_error_codes []int
debug bool // if debug will put +ex in the script which is being executed and will make sure script stays
includeprofile bool
notempty bool
}
//execute something fast, make sure it returns an error if the result is empty
//source the
pub fn exec_fast(cmd_ CommandFast) !string {
mut cmd_str := texttools.dedent(cmd_.cmd)
mut toexecute:=[]string{}
if cmd_.debug {
// Add +ex for debug mode if it's a bash script
// This is a simplification, ideally we'd check if it's a bash script
// For now, assume it's a bash script if debug is true and prepend
toexecute << 'set -ex'
}
if cmd_.includeprofile {
toexecute << profile_path_source_and()!
}
for key,val in cmd_.environment {
toexecute << 'export ${key}=${val}'
}
if cmd_.work_folder.len > 0 {
toexecute << "cd ${cmd_.work_folder}"
}
toexecute << cmd_str
mut cmd_str2 := toexecute.join('\n')
result := os.execute(cmd_str2)
if result.exit_code != 0 {
if !(cmd_.ignore_error || result.exit_code in cmd_.ignore_error_codes) {
return error("couldn't execute '${cmd_.cmd}', result code: ${result.exit_code}\n${result.output}")
}
}
if cmd_.notempty && result.output.len == 0 {
return error("couldn't execute '${cmd_.cmd}', the result is empty but notempty is true")
}
return result.output
}