WIP: Building hero

- The work is still in progress

Co-authored-by: supermario <mariobassem12@gmail.com>
This commit is contained in:
Mahmoud Emad
2025-01-02 19:01:37 +02:00
parent b29a57ea34
commit 4c01c88b85
103 changed files with 7064 additions and 506 deletions

View File

@@ -9,6 +9,7 @@ import freeflowuniverse.herolib.installers.base
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.ui
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.core.playcmds
@@ -48,7 +49,7 @@ fn do() ! {
toinstall = true
}
if osal.is_osx() {
if core.is_osx()! {
if !osal.cmd_exists('brew') {
console.clear()
mut myui := ui.new()!

View File

@@ -0,0 +1,210 @@
module generic
import freeflowuniverse.herolib.ui.console
import os
import freeflowuniverse.herolib.core.pathlib
// will ask questions when not in force mode
// & generate the module
pub fn generate(args_ GeneratorArgs) ! {
mut myconsole := console.new()
mut args := args_
console.print_header('Generate code for path: ${args.path} (reset:${args.force}, force:${args.force})')
console.print_debug(args)
if args.path == '' {
args.path = os.getwd()
}
if args.name == '' {
args.name = os.base(args.path)
}
if args.force {
mut config_path0 := pathlib.get_file(path: '${args.path}/.heroscript', create: false)!
if !config_path0.exists() {
return error("can't generate in force mode (non interactive) if ${config_path0.path} not found.")
}
generate_exec(args.path, args.reset)!
return
}
console.clear()
console.print_header('Configure generation of code for a module on path:')
console.print_green('Path: ${args.path}')
console.lf()
mut config_path := pathlib.get_file(path: '${args.path}/.heroscript', create: false)!
mut pathok := false
if config_path.exists() {
console.print_stdout(config_path.read()!)
console.lf()
myyes := myconsole.ask_yesno(
description: 'We found this heroscript, do you want to make a new one?'
)!
if myyes {
config_path.delete()!
pathok = true
} else {
myyes2 := myconsole.ask_yesno(description: 'Do you want to run it?')!
if myyes2 {
generate_exec(args.path, args.reset)!
} else {
console.print_stderr('Generation aborted.')
}
return
}
}
if pathok == false {
yesno := myconsole.ask_yesno(description: 'Is this path ok?')!
if !yesno {
return error("can't continue without a valid path")
}
}
mycat := myconsole.ask_dropdown(
description: 'Category of the generator'
question: 'What is the category of the generator?'
items: [
'installer',
'client',
]
warning: 'Please select a category'
)!
if mycat == 'installer' {
args.cat = .installer
} else {
args.cat = .client
}
// if args.name==""{
// yesno := myconsole.ask_yesno(description: 'Are you happy with name ${args.name}?')!
// if !yesno {
// return error("can't continue without a valid name, rename the directory you operate in.")
// }
// }
args.classname = myconsole.ask_question(
description: 'Class name of the ${mycat}'
question: 'What is the class name of the generator e.g. MyClass ?'
warning: 'Please provide a valid class name for the generator'
minlen: 4
)!
args.title = myconsole.ask_question(
description: 'Title of the ${mycat} (optional)'
)!
if args.cat == .installer {
args.hasconfig = myconsole.ask_yesno(
description: 'Does your installer have a config (normally yes)?'
)!
}
if args.hasconfig {
args.default = myconsole.ask_yesno(
description: 'Is it ok when doing new() that a default is created (normally yes)?'
)!
args.singleton = !myconsole.ask_yesno(
description: 'Can there be multiple instances (normally yes)?'
)!
}
// args.supported_platforms = myconsole.ask_dropdown_multiple(
// description: 'Supported platforms'
// question: 'Which platforms are supported?'
// items: [
// 'osx',
// 'ubuntu',
// 'arch',
// ]
// warning: 'Please select one or more platforms'
// )!
if args.cat == .installer {
args.templates = myconsole.ask_yesno(
description: 'Will there be templates available for your installer?'
)!
args.startupmanager = myconsole.ask_yesno(
description: 'Is this an installer which will be managed by a startup mananger?'
)!
args.build = myconsole.ask_yesno(
description: 'Are there builders for the installers (compilation)'
)!
}
// args.reset = myconsole.ask_yesno(
// description: 'Reset, overwrite code.'
// question: 'This will overwrite all files in your existing dir, be carefule?'
// )!
create_heroscript(args)!
generate_exec(args.path, true)!
}
pub fn create_heroscript(args GeneratorArgs) ! {
mut script := ''
if args.cat == .installer {
script = "
!!hero_code.generate_installer
name:'${args.name}'
classname:'${args.classname}'
singleton:${if args.singleton {
'1'
} else {
'0'
}}
templates:${if args.templates { '1' } else { '0' }}
default:${if args.default {
'1'
} else {
'0'
}}
title:'${args.title}'
supported_platforms:''
reset:${if args.reset {
'1'
} else {
'0'
}}
startupmanager:${if args.startupmanager { '1' } else { '0' }}
hasconfig:${if args.hasconfig {
'1'
} else {
'0'
}}
build:${if args.build {
'1'
} else {
'0'
}}"
} else {
script = "
!!hero_code.generate_client
name:'${args.name}'
classname:'${args.classname}'
singleton:${if args.singleton {
'1'
} else {
'0'
}}
default:${if args.default { '1' } else { '0' }}
hasconfig:${if args.hasconfig {
'1'
} else {
'0'
}}
reset:${if args.reset {
'1'
} else {
'0'
}}"
}
if !os.exists(args.path) {
os.mkdir(args.path)!
}
os.write_file('${args.path}/.heroscript', script)!
}

View File

@@ -0,0 +1,78 @@
module generic
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.pathlib
fn generate_exec(path string, reset bool) ! {
mut args := args_get(path)!
console.print_debug('generate code for path: ${path}')
if reset {
args.reset = true
}
mut path_actions := pathlib.get(args.path + '/${args.name}_actions.v')
if args.reset {
path_actions.delete()!
}
if !path_actions.exists() && args.cat == .installer {
console.print_debug('write installer actions')
mut templ_1 := $tmpl('templates/objname_actions.vtemplate')
pathlib.template_write(templ_1, '${args.path}/${args.name}_actions.v', true)!
}
mut templ_2 := $tmpl('templates/objname_factory_.vtemplate')
pathlib.template_write(templ_2, '${args.path}/${args.name}_factory_.v', true)!
mut path_model := pathlib.get(args.path + '/${args.name}_model.v')
if args.reset || !path_model.exists() {
console.print_debug('write model.')
mut templ_3 := $tmpl('templates/objname_model.vtemplate')
pathlib.template_write(templ_3, '${args.path}/${args.name}_model.v', true)!
}
// TODO: check case sensistivity for delete
mut path_readme := pathlib.get(args.path + '/readme.md')
if args.reset || !path_readme.exists() {
mut templ_readme := $tmpl('templates/readme.md')
pathlib.template_write(templ_readme, '${args.path}/readme.md', true)!
}
mut path_templ_dir := pathlib.get_dir(path: args.path + '/templates', create: false)!
if args.reset {
path_templ_dir.delete()!
}
if args.templates {
if !path_templ_dir.exists() {
mut templ_6 := $tmpl('templates/atemplate.yaml')
pathlib.template_write(templ_6, '${args.path}/templates/atemplate.yaml', true)!
}
}
}
fn platform_check(args GeneratorArgs) ! {
ok := 'osx,ubuntu,arch'
ok2 := ok.split(',')
for i in args.supported_platforms {
if i !in ok2 {
return error('cannot find ${i} in choices for supported_platforms. Valid ones are ${ok}')
}
}
}
pub fn (args GeneratorArgs) platform_check_str() string {
mut out := ''
if 'osx' in args.supported_platforms {
out += 'myplatform == .osx || '
}
if 'ubuntu' in args.supported_platforms {
out += 'myplatform == .ubuntu ||'
}
if 'arch' in args.supported_platforms {
out += 'myplatform == .arch ||'
}
out = out.trim_right('|')
return out
}

View File

@@ -0,0 +1,84 @@
module generic
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
pub struct GeneratorArgs {
pub mut:
name string
classname string
default bool = true // means user can just get the object and a default will be created
title string
supported_platforms []string // only relevant for installers for now
singleton bool // means there can only be one
templates bool // means we will use templates in the installer, client doesn't do this'
reset bool // regenerate all, dangerous !!!
startupmanager bool = true
build bool
cat Cat
path string
force bool
hasconfig bool = true
}
pub enum Cat {
installer
client
}
fn args_get(path string) !GeneratorArgs {
console.print_debug('play installer code for path: ${path}')
mut config_path := pathlib.get_file(path: '${path}/.heroscript', create: false)!
if !config_path.exists() {
return error("can't find path with .heroscript in ${path}")
}
mut plbook := playbook.new(text: config_path.read()!)!
mut install_actions := plbook.find(filter: 'hero_code.generate_installer')!
if install_actions.len > 0 {
for install_action in install_actions {
mut p := install_action.params
mut args := GeneratorArgs{
name: p.get('name')!
classname: p.get('classname')!
title: p.get_default('title', '')!
default: p.get_default_true('default')
supported_platforms: p.get_list('supported_platforms')!
singleton: p.get_default_false('singleton')
templates: p.get_default_false('templates')
reset: p.get_default_false('reset')
startupmanager: p.get_default_true('startupmanager')
hasconfig: p.get_default_true('hasconfig')
build: p.get_default_false('build')
force: p.get_default_false('force')
cat: .installer
path: path
}
return args
}
}
mut client_actions := plbook.find(filter: 'hero_code.generate_client')!
if client_actions.len > 0 {
for client_action in client_actions {
mut p := client_action.params
args := GeneratorArgs{
name: p.get('name')!
classname: p.get('classname')!
title: p.get_default('title', '')!
default: p.get_default_true('default')
singleton: p.get_default_false('singleton')
reset: p.get_default_false('reset')
cat: .client
path: path
}
return args
}
}
return error("can't find hero_code.generate_client or hero_code.generate_installer in ${path}")
// return GeneratorArgs{}
}

View File

@@ -0,0 +1,86 @@
# generation framework
```bash
#will ask questions if .heroscript is not there yet
hero generate -p thepath_is_optional
# to generate without questions
hero generate -p thepath_is_optional -t client
#if installer, default is a client
hero generate -p thepath_is_optional -t installer
#when you want to scan over multiple directories
hero generate -p thepath_is_optional -t installer -s
```
there will be a ```.heroscript``` in the director you want to generate for, the format is as follows:
```hero
//for a server
!!hero_code.generate_installer
name:'daguserver'
classname:'DaguServer'
singleton:1 //there can only be 1 object in the globals, is called 'default'
templates:1 //are there templates for the installer
default:1 //can we create a default when the factory is used
title:''
supported_platforms:'' //osx, ... (empty means all)
reset:0 // regenerate all, dangerous !!!
startupmanager:1 //managed by a startup manager, default true
build:1 //will we also build the component
//or for a client
!!hero_code.generate_client
name:'mail'
classname:'MailClient'
singleton:0 //default is 0
default:1 //can we create a default when the factory is used
reset:0 // regenerate all, dangerous !!!
//or for a play
!!hero_code.generate_play
path:'' //if not used then is path where this action is found
reset:0 //if set will overwrite the play_$actor_$action_.v
actor:'mail'
action:''
model:"""
"""
```
needs to be put as .heroscript in the directories which we want to generate
## templates remarks
in templates:
- ^^ or @@ > gets replaced to @
- ?? > gets replaced to $
this is to make distinction between processing at compile time (pre-compile) or at runtime.
## call by code
to call in code
```v
#!/usr/bin/env -S v -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.core.generator.generic
generic.scan(path:"~/code/github/freeflowuniverse/crystallib/crystallib/installers",force:true)!
```
to run from bash
```bash
~/code/github/freeflowuniverse/crystallib/scripts/fix_installers.vsh
```

View File

@@ -0,0 +1,30 @@
module generic
import os
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.ui.console
// scan over a set of directories call the play where
pub fn scan(args_ GeneratorArgs) ! {
mut args := args_
console.print_header('Scan for generation of code for path: ${args.path} (reset:${args.force}, force:${args.force})')
if args.path.len == 0 {
args.path = os.getwd()
}
// now walk over all directories, find .heroscript
mut pathroot := pathlib.get_dir(path: args.path, create: false)!
mut plist := pathroot.list(
recursive: true
ignoredefault: false
regex: ['.heroscript']
)!
for mut p in plist.paths {
pparent := p.parent()!
args.path = pparent.path
// println("-- ${pparent}")
generate(args)!
}
}

View File

@@ -0,0 +1,5 @@
name: ??{cfg.configpath}

View File

@@ -0,0 +1,13 @@
!!hero_code.generate_installer
name:'daguserver'
classname:'DaguServer'
singleton:1 //there can only be 1 object in the globals, is called 'default'
default:1 //can we create a default when the factory is used
title:''
supported_platforms:'' //osx, ... (empty means all)
reset:0 // regenerate all, dangerous !!!
//next only relevant for installer
startupmanager:1 //managed by a startup manager, default true
templates:1 //are there templates for the installer
build:1 //will we also build the component

View File

@@ -0,0 +1,212 @@
module ${args.name}
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.pathlib
@if args.startupmanager
import freeflowuniverse.herolib.osal.systemd
import freeflowuniverse.herolib.osal.zinit
@end
@if args.build
import freeflowuniverse.herolib.installers.ulist
import freeflowuniverse.herolib.installers.lang.golang
import freeflowuniverse.herolib.installers.lang.rust
import freeflowuniverse.herolib.installers.lang.python
@end
import os
@if args.startupmanager
fn startupcmd () ![]zinit.ZProcessNewArgs{
mut installer := get()!
mut res := []zinit.ZProcessNewArgs{}
//THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
// res << zinit.ZProcessNewArgs{
// name: '${args.name}'
// cmd: '${args.name} server'
// env: {
// 'HOME': '/root'
// }
// }
return res
}
fn running() !bool {
mut installer := get()!
//THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
// this checks health of ${args.name}
// curl http://localhost:3333/api/v1/s --oauth2-bearer 1234 works
// url:='http://127.0.0.1:??{cfg.port}/api/v1'
// mut conn := httpconnection.new(name: '${args.name}', url: url)!
// if cfg.secret.len > 0 {
// conn.default_header.add(.authorization, 'Bearer ??{cfg.secret}')
// }
// conn.default_header.add(.content_type, 'application/json')
// console.print_debug("curl -X 'GET' '??{url}'/tags --oauth2-bearer ??{cfg.secret}")
// r := conn.get_json_dict(prefix: 'tags', debug: false) or {return false}
// println(r)
// if true{panic("ssss")}
// tags := r['Tags'] or { return false }
// console.print_debug(tags)
// console.print_debug('${args.name} is answering.')
return false
}
fn start_pre()!{
}
fn start_post()!{
}
fn stop_pre()!{
}
fn stop_post()!{
}
@end
//////////////////// following actions are not specific to instance of the object
@if args.cat == .installer
// checks if a certain version or above is installed
fn installed() !bool {
//THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
// res := os.execute('??{osal.profile_path_source_and()} ${args.name} version')
// if res.exit_code != 0 {
// return false
// }
// r := res.output.split_into_lines().filter(it.trim_space().len > 0)
// if r.len != 1 {
// return error("couldn't parse ${args.name} version.\n??{res.output}")
// }
// if texttools.version(version) == texttools.version(r[0]) {
// return true
// }
return false
}
//get the Upload List of the files
fn ulist_get() !ulist.UList {
//optionally build a UList which is all paths which are result of building, is then used e.g. in upload
return ulist.UList{}
}
//uploads to S3 server if configured
fn upload() ! {
// installers.upload(
// cmdname: '${args.name}'
// source: '??{gitpath}/target/x86_64-unknown-linux-musl/release/${args.name}'
// )!
}
fn install() ! {
console.print_header('install ${args.name}')
//THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
// mut url := ''
// if osal.is_linux_arm() {
// url = 'https://github.com/${args.name}-dev/${args.name}/releases/download/v??{version}/${args.name}_??{version}_linux_arm64.tar.gz'
// } else if osal.is_linux_intel() {
// url = 'https://github.com/${args.name}-dev/${args.name}/releases/download/v??{version}/${args.name}_??{version}_linux_amd64.tar.gz'
// } else if osal.is_osx_arm() {
// url = 'https://github.com/${args.name}-dev/${args.name}/releases/download/v??{version}/${args.name}_??{version}_darwin_arm64.tar.gz'
// } else if osal.is_osx_intel() {
// url = 'https://github.com/${args.name}-dev/${args.name}/releases/download/v??{version}/${args.name}_??{version}_darwin_amd64.tar.gz'
// } else {
// return error('unsported platform')
// }
// mut dest := osal.download(
// url: url
// minsize_kb: 9000
// expand_dir: '/tmp/${args.name}'
// )!
// //dest.moveup_single_subdir()!
// mut binpath := dest.file_get('${args.name}')!
// osal.cmd_add(
// cmdname: '${args.name}'
// source: binpath.path
// )!
}
@if args.build
fn build() ! {
//url := 'https://github.com/threefoldtech/${args.name}'
// make sure we install base on the node
// if osal.platform() != .ubuntu {
// return error('only support ubuntu for now')
// }
// golang.install()!
// console.print_header('build ${args.name}')
// gitpath := gittools.get_repo(coderoot: '/tmp/builder', url: url, reset: true, pull: true)!
// cmd := '
// cd ??{gitpath}
// source ~/.cargo/env
// exit 1 #todo
// '
// osal.execute_stdout(cmd)!
//
// //now copy to the default bin path
// mut binpath := dest.file_get('...')!
// adds it to path
// osal.cmd_add(
// cmdname: 'griddriver2'
// source: binpath.path
// )!
}
@end
fn destroy() ! {
// mut systemdfactory := systemd.new()!
// systemdfactory.destroy("zinit")!
// osal.process_kill_recursive(name:'zinit')!
// osal.cmd_delete('zinit')!
// osal.package_remove('
// podman
// conmon
// buildah
// skopeo
// runc
// ')!
// //will remove all paths where go/bin is found
// osal.profile_path_add_remove(paths2delete:"go/bin")!
// osal.rm("
// podman
// conmon
// buildah
// skopeo
// runc
// /var/lib/containers
// /var/lib/podman
// /var/lib/buildah
// /tmp/podman
// /tmp/conmon
// ")!
}
@end

View File

@@ -0,0 +1,313 @@
module ${args.name}
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
@if args.cat == .installer
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.osal.zinit
import time
@end
__global (
${args.name}_global map[string]&${args.classname}
${args.name}_default string
)
/////////FACTORY
^^[params]
pub struct ArgsGet{
pub mut:
name string
}
@if args.hasconfig
fn args_get (args_ ArgsGet) ArgsGet {
mut args:=args_
if args.name == ""{
args.name = ${args.name}_default
}
if args.name == ""{
args.name = "default"
}
return args
}
pub fn get(args_ ArgsGet) !&${args.classname} {
mut args := args_get(args_)
if !(args.name in ${args.name}_global) {
if args.name=="default"{
if ! config_exists(args){
if default{
config_save(args)!
}
}
config_load(args)!
}
}
return ${args.name}_global[args.name] or {
println(${args.name}_global)
panic("could not get config for ${args.name} with name:??{args.name}")
}
}
@else
pub fn get(args_ ArgsGet) !&${args.classname} {
return &${args.classname}{}
}
@end
@if args.hasconfig
fn config_exists(args_ ArgsGet) bool {
mut args := args_get(args_)
mut context:=base.context() or { panic("bug") }
return context.hero_config_exists("${args.name}",args.name)
}
fn config_load(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context:=base.context()!
mut heroscript := context.hero_config_get("${args.name}",args.name)!
play(heroscript:heroscript)!
}
fn config_save(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context:=base.context()!
context.hero_config_set("${args.name}",args.name,heroscript_default()!)!
}
fn set(o ${args.classname})! {
mut o2:=obj_init(o)!
${args.name}_global[o.name] = &o2
${args.name}_default = o.name
}
^^[params]
pub struct PlayArgs {
pub mut:
heroscript string //if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
}
pub fn play(args_ PlayArgs) ! {
mut args:=args_
@if args.hasconfig
if args.heroscript == "" {
args.heroscript = heroscript_default()!
}
@end
mut plbook := args.plbook or {
playbook.new(text: args.heroscript)!
}
@if args.hasconfig
mut install_actions := plbook.find(filter: '${args.name}.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
mut p := install_action.params
mycfg:=cfg_play(p)!
console.print_debug("install action ${args.name}.configure\n??{mycfg}")
set(mycfg)!
}
}
@end
@if args.cat == .installer
mut other_actions := plbook.find(filter: '${args.name}.')!
for other_action in other_actions {
if other_action.name in ["destroy","install","build"]{
mut p := other_action.params
reset:=p.get_default_false("reset")
if other_action.name == "destroy" || reset{
console.print_debug("install action ${args.name}.destroy")
destroy()!
}
if other_action.name == "install"{
console.print_debug("install action ${args.name}.install")
install()!
}
}
@if args.startupmanager
if other_action.name in ["start","stop","restart"]{
mut p := other_action.params
name := p.get('name')!
mut ${args.name}_obj:=get(name:name)!
console.print_debug("action object:\n??{${args.name}_obj}")
if other_action.name == "start"{
console.print_debug("install action ${args.name}.??{other_action.name}")
${args.name}_obj.start()!
}
if other_action.name == "stop"{
console.print_debug("install action ${args.name}.??{other_action.name}")
${args.name}_obj.stop()!
}
if other_action.name == "restart"{
console.print_debug("install action ${args.name}.??{other_action.name}")
${args.name}_obj.restart()!
}
}
@end
}
@end
}
@end
@if args.cat == .installer
////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager {
// unknown
// screen
// zinit
// tmux
// systemd
match cat{
.zinit{
console.print_debug("startupmanager: zinit")
return startupmanager.get(cat:.zinit)!
}
.systemd{
console.print_debug("startupmanager: systemd")
return startupmanager.get(cat:.systemd)!
}else{
console.print_debug("startupmanager: auto")
return startupmanager.get()!
}
}
}
@if args.hasconfig
//load from disk and make sure is properly intialized
pub fn (mut self ${args.classname}) reload() ! {
switch(self.name)
self=obj_init(self)!
}
@end
@if args.startupmanager
pub fn (mut self ${args.classname}) start() ! {
switch(self.name)
if self.running()!{
return
}
console.print_header('${args.name} start')
if ! installed()!{
install()!
}
configure()!
start_pre()!
for zprocess in startupcmd()!{
mut sm:=startupmanager_get(zprocess.startuptype)!
console.print_debug('starting ${args.name} with ??{zprocess.startuptype}...')
sm.new(zprocess)!
sm.start(zprocess.name)!
}
start_post()!
for _ in 0 .. 50 {
if self.running()! {
return
}
time.sleep(100 * time.millisecond)
}
return error('${args.name} did not install properly.')
}
pub fn (mut self ${args.classname}) install_start(args InstallArgs) ! {
switch(self.name)
self.install(args)!
self.start()!
}
pub fn (mut self ${args.classname}) stop() ! {
switch(self.name)
stop_pre()!
for zprocess in startupcmd()!{
mut sm:=startupmanager_get(zprocess.startuptype)!
sm.stop(zprocess.name)!
}
stop_post()!
}
pub fn (mut self ${args.classname}) restart() ! {
switch(self.name)
self.stop()!
self.start()!
}
pub fn (mut self ${args.classname}) running() !bool {
switch(self.name)
//walk over the generic processes, if not running return
for zprocess in startupcmd()!{
mut sm:=startupmanager_get(zprocess.startuptype)!
r:=sm.running(zprocess.name)!
if r==false{
return false
}
}
return running()!
}
@end
@@[params]
pub struct InstallArgs{
pub mut:
reset bool
}
pub fn (mut self ${args.classname}) install(args InstallArgs) ! {
switch(self.name)
if args.reset || (!installed()!) {
install()!
}
}
@if args.build
pub fn (mut self ${args.classname}) build() ! {
switch(self.name)
build()!
}
@end
pub fn (mut self ${args.classname}) destroy() ! {
switch(self.name)
@if args.startupmanager
self.stop() or {}
@end
destroy()!
}
@end
//switch instance to be used for ${args.name}
pub fn switch(name string) {
${args.name}_default = name
}

View File

@@ -0,0 +1,137 @@
module ${args.name}
import freeflowuniverse.herolib.data.paramsparser
import os
pub const version = '1.14.3'
const singleton = ${args.singleton}
const default = ${args.default}
@if args.hasconfig
//TODO: THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE TO STRUCT BELOW, IS STRUCTURED AS HEROSCRIPT
pub fn heroscript_default() !string {
@if args.cat == .installer
heroscript:="
!!${args.name}.configure
name:'${args.name}'
homedir: '{HOME}/hero/var/${args.name}'
configpath: '{HOME}/.config/${args.name}/admin.yaml'
username: 'admin'
password: 'secretpassword'
secret: ''
title: 'My Hero DAG'
host: 'localhost'
port: 8888
"
@else
heroscript:="
!!${args.name}.configure
name:'${args.name}'
mail_from: 'info@@example.com'
mail_password: 'secretpassword'
mail_port: 587
mail_server: 'smtp-relay.brevo.com'
mail_username: 'kristof@@incubaid.com'
"
@end
return heroscript
}
@end
//THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
@if args.cat == .installer
@[heap]
pub struct ${args.classname} {
pub mut:
name string = 'default'
@if args.hasconfig
homedir string
configpath string
username string
password string @@[secret]
secret string @@[secret]
title string
host string
port int
@end
}
@if args.hasconfig
fn cfg_play(p paramsparser.Params) !${args.classname} {
//THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above
mut mycfg := ${args.classname}{
name: p.get_default('name', 'default')!
homedir: p.get_default('homedir', '{HOME}/hero/var/${args.name}')!
configpath: p.get_default('configpath', '{HOME}/hero/var/${args.name}/admin.yaml')!
username: p.get_default('username', 'admin')!
password: p.get_default('password', '')!
secret: p.get_default('secret', '')!
title: p.get_default('title', 'HERO DAG')!
host: p.get_default('host', 'localhost')!
port: p.get_int_default('port', 8888)!
}
if mycfg.password == '' && mycfg.secret == '' {
return error('password or secret needs to be filled in for ${args.name}')
}
return mycfg
}
@end
@else
@[heap]
pub struct ${args.classname} {
pub mut:
name string = 'default'
mail_from string
mail_password string @@[secret]
mail_port int
mail_server string
mail_username string
}
@if args.hasconfig
fn cfg_play(p paramsparser.Params) ! {
//THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above
mut mycfg := ${args.classname}{
name: p.get_default('name', 'default')!
mail_from: p.get('mail_from')!
mail_password: p.get('mail_password')!
mail_port: p.get_int_default('mail_port', 8888)!
mail_server: p.get('mail_server')!
mail_username: p.get('mail_username')!
}
set(mycfg)!
}
@end
@end
fn obj_init(obj_ ${args.classname})!${args.classname}{
//never call get here, only thing we can do here is work on object itself
mut obj:=obj_
return obj
}
@if args.cat == .installer
//called before start if done
fn configure() ! {
@if args.cat == .installer
//mut installer := get()!
@else
//mut client := get()!
@end
@if args.templates
// mut mycode := ??tmpl('templates/atemplate.yaml')
// mut path := pathlib.get_file(path: cfg.configpath, create: true)!
// path.write(mycode)!
// console.print_debug(mycode)
@end
}
@end

View File

@@ -0,0 +1,63 @@
# ${args.name}
${args.title}
To get started
```vlang
@if args.cat == .installer
import freeflowuniverse.herolib.installers.something.${args.name} as ${args.name}_installer
heroscript:="
!!${args.name}.configure name:'test'
password: '1234'
port: 7701
!!${args.name}.start name:'test' reset:1
"
${args.name}_installer.play(heroscript=heroscript)!
//or we can call the default and do a start with reset
//mut installer:= ${args.name}_installer.get()!
//installer.start(reset:true)!
@else
import freeflowuniverse.herolib.clients. ${args.name}
mut client:= ${args.name}.get()!
client...
@end
```
## example heroscript
@if args.cat == .installer
```hero
!!${args.name}.configure
homedir: '/home/user/${args.name}'
username: 'admin'
password: 'secretpassword'
title: 'Some Title'
host: 'localhost'
port: 8888
```
@else
```hero
!!${args.name}.configure
secret: '...'
host: 'localhost'
port: 8888
```
@end

View File

@@ -0,0 +1,119 @@
module herocmds
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.installers.base
import freeflowuniverse.herolib.installers.lang.herolib
import freeflowuniverse.herolib.builder
import cli { Command, Flag }
pub fn cmd_bootstrap(mut cmdroot Command) {
mut cmd_run := Command{
name: 'bootstrap'
description: 'bootstrap hero'
required_args: 0
execute: cmd_bootstrap_execute
}
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'reset'
abbrev: 'r'
description: 'will reset.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'develop'
abbrev: 'd'
description: 'will put system in development mode.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'compileupload'
abbrev: 'c'
description: 'Compile and upload hero.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'update'
abbrev: 'u'
description: 'Update/install hero.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'hero'
abbrev: 'u'
description: 'Update hero.'
})
// cmd_run.add_flag(Flag{
// flag: .bool
// required: false
// name: 'crystal'
// abbrev: 'cr'
// description: 'install crystal lib + vlang.'
// })
cmd_run.add_flag(Flag{
flag: .string
name: 'address'
abbrev: 'a'
description: 'address in form root@212.3.4.5:2222 or root@212.3.4.5 or root@info.three.com'
})
cmdroot.add_command(cmd_run)
}
fn cmd_bootstrap_execute(cmd Command) ! {
mut develop := cmd.flags.get_bool('develop') or { false }
mut reset := cmd.flags.get_bool('reset') or { false }
mut compileupload := cmd.flags.get_bool('compileupload') or { false }
mut update := cmd.flags.get_bool('update') or { false }
// mut hero := cmd.flags.get_bool('hero') or { false }
mut address := cmd.flags.get_string('address') or { '' }
if address == '' {
osal.profile_path_add_hero()!
if develop {
herolib.install(reset: reset)!
} else {
base.install(reset: reset)!
}
base.bash_installers_package()!
} else {
mut b := builder.new()!
mut n := b.node_new(ipaddr: address)!
if develop {
n.crystal_install(reset: reset)!
n.hero_install()!
n.dagu_install()!
} else {
panic('implement, need to download here and install')
}
// return error(cmd.help_message())
}
if compileupload {
// mycmd:='
// \${HOME}/code/github/freeflowuniverse/crystallib/scripts/package.vsh
// '
// osal.exec(cmd: mycmd)!
println('please execute:\n~/code/github/freeflowuniverse/crystallib/scripts/githubactions.sh')
}
if update {
// mycmd:='
// \${HOME}/code/github/freeflowuniverse/crystallib/scripts/package.vsh
// '
// osal.exec(cmd: mycmd)!
println('please execute:\n~/code/github/freeflowuniverse/crystallib/scripts/install_hero.sh')
}
}

View File

@@ -0,0 +1,83 @@
module herocmds
import freeflowuniverse.herolib.conversiontools.docsorter
import cli { Command, Flag }
import os
import freeflowuniverse.herolib.ui.console
// const wikipath = os.dir(@FILE) + '/wiki'
pub fn cmd_docsorter(mut cmdroot Command) {
mut cmd_run := Command{
name: 'docsorter'
description: 'can sort, export, ... pdfs and other docs.'
required_args: 0
execute: cmd_docsorter_execute
}
cmd_run.add_flag(Flag{
flag: .string
required: false
name: 'path'
abbrev: 'p'
description: 'If not in current directory.'
})
cmd_run.add_flag(Flag{
flag: .string
required: false
name: 'instructions'
abbrev: 'i'
description: 'Location to the instructions file, format is text file with per line:\naaa:ourworld:kristof_bio\naab:phoenix:phoenix_digital_nation_litepaper:Litepaper of how a Digital nation can use the Hero Phone'
})
cmd_run.add_flag(Flag{
flag: .string
required: false
name: 'dest'
abbrev: 'd'
description: 'if not given will bet /tmp/export.'
})
cmd_run.add_flag(Flag{
flag: .bool
name: 'reset'
abbrev: 'r'
description: 'reset the export dir.'
})
cmd_run.add_flag(Flag{
flag: .bool
name: 'slides'
abbrev: 's'
description: 'extract slides out of the pdfs.'
})
cmdroot.add_command(cmd_run)
}
fn cmd_docsorter_execute(cmd Command) ! {
mut dest := cmd.flags.get_string('dest') or { '' }
if dest == '' {
dest = '/tmp/export'
}
mut path := cmd.flags.get_string('path') or { '' }
if path == '' {
path = os.getwd()
}
mut instructions := cmd.flags.get_string('instructions') or { '' }
mut reset := cmd.flags.get_bool('reset') or { false }
mut slides := cmd.flags.get_bool('slides') or { false }
docsorter.sort(
path: path
export_path: dest
instructions: instructions
reset: reset
slides: slides
) or {
print_backtrace()
console.print_stderr(err.str())
exit(1)
}
}

View File

@@ -0,0 +1,81 @@
module herocmds
import freeflowuniverse.herolib.core.generator.generic
import os
import cli { Command, Flag }
pub fn cmd_generator(mut cmdroot Command) {
mut cmd_run := Command{
name: 'generate'
description: 'generator for vlang code in hero context.'
required_args: 0
execute: cmd_generator_execute
}
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'reset'
abbrev: 'r'
description: 'will reset.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'path'
abbrev: 'p'
description: 'path where to generate the code or scan over multiple directories.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'force'
abbrev: 'f'
description: 'will work non interactive if possible.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'scan'
abbrev: 's'
description: 'force scanning operation.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'installer'
abbrev: 'i'
description: 'Make sure its installer.'
})
cmdroot.add_command(cmd_run)
}
fn cmd_generator_execute(cmd Command) ! {
mut force := cmd.flags.get_bool('force') or { false }
mut reset := cmd.flags.get_bool('reset') or { false }
mut scan := cmd.flags.get_bool('scan') or { false }
mut installer := cmd.flags.get_bool('installer') or { false }
mut path := cmd.flags.get_string('path') or { '' }
if path == '' {
path = os.getwd()
}
path = path.replace('~', os.home_dir())
mut cat := generic.Cat.client
if installer {
cat = generic.Cat.installer
}
if scan {
generic.scan(path: path, reset: reset, force: force, cat: cat)!
} else {
generic.generate(path: path, reset: reset, force: force, cat: cat)!
}
}

284
lib/core/herocmds/git.v Normal file
View File

@@ -0,0 +1,284 @@
module herocmds
import freeflowuniverse.herolib.develop.gittools
import freeflowuniverse.herolib.ui.console
import cli { Command, Flag }
import os
pub fn cmd_git(mut cmdroot Command) {
mut cmd_run := Command{
name: 'git'
description: 'Work with your repos, list, commit, pull, reload, ...'
// required_args: 1
usage: 'sub commands of git are '
execute: cmd_git_execute
sort_commands: true
}
mut clone_command := Command{
sort_flags: true
name: 'clone'
execute: cmd_git_execute
description: 'will clone the repo based on a given url, e.g. https://github.com/freeflowuniverse/webcomponents/tree/main'
}
mut pull_command := Command{
sort_flags: true
name: 'pull'
execute: cmd_git_execute
description: 'will pull the content, if it exists for each found repo.'
}
mut push_command := Command{
sort_flags: true
name: 'push'
execute: cmd_git_execute
description: 'will push the content, if it exists for each found repo.'
}
mut commit_command := Command{
sort_flags: true
name: 'commit'
execute: cmd_git_execute
description: 'will commit newly found content, specify the message.'
}
mut reload_command := Command{
sort_flags: true
name: 'reload'
execute: cmd_git_execute
description: 'reset the cache of the repos, they are kept for 24h in local redis, this will reload all info.'
}
mut delete_command := Command{
sort_flags: true
name: 'delete'
execute: cmd_git_execute
description: 'delete the repo.'
}
mut list_command := Command{
sort_flags: true
name: 'list'
execute: cmd_git_execute
description: 'list all repos.'
}
mut sourcetree_command := Command{
sort_flags: true
name: 'sourcetree'
execute: cmd_git_execute
description: 'Open sourcetree on found repos, will do for max 5.'
}
mut editor_command := Command{
sort_flags: true
name: 'edit'
execute: cmd_git_execute
description: 'Open visual studio code on found repos, will do for max 5.'
}
mut cmd_cd := Command{
sort_flags: true
name: 'cd'
execute: cmd_git_execute
description: 'cd to a git repo, use e.g. eval $(git cd -u https://github.com/threefoldfoundation/www_threefold_io)'
}
cmd_cd.add_flag(Flag{
flag: .string
required: false
name: 'url'
abbrev: 'u'
description: 'url for git cd operation, so we know where to cd to'
})
mut allcmdsref := [&list_command, &clone_command, &push_command, &pull_command, &commit_command,
&reload_command, &delete_command, &sourcetree_command, &editor_command]
for mut c in allcmdsref {
c.add_flag(Flag{
flag: .bool
required: false
name: 'silent'
abbrev: 's'
description: 'be silent.'
})
}
mut allcmdscommit := [&push_command, &pull_command, &commit_command]
for mut c in allcmdscommit {
c.add_flag(Flag{
flag: .string
required: false
name: 'message'
abbrev: 'm'
description: 'which message to use for commit.'
})
}
mut urlcmds := [&clone_command, &pull_command, &push_command, &editor_command, &sourcetree_command]
for mut c in urlcmds {
c.add_flag(Flag{
flag: .string
required: false
name: 'url'
abbrev: 'u'
description: 'url for clone operation.'
})
c.add_flag(Flag{
flag: .bool
required: false
name: 'reset'
description: 'force a pull and reset changes.'
})
c.add_flag(Flag{
flag: .bool
required: false
name: 'pull'
description: 'force a pull.'
})
c.add_flag(Flag{
flag: .bool
required: false
name: 'pullreset'
abbrev: 'pr'
description: 'force a pull and do a reset.'
})
c.add_flag(Flag{
flag: .bool
required: false
name: 'recursive'
description: 'if we do a clone or a pull we also get the git submodules.'
})
}
for mut c in allcmdsref {
c.add_flag(Flag{
flag: .string
required: false
name: 'filter'
abbrev: 'f'
description: 'Filter is part of path of repo e.g. threefoldtech/info_'
})
c.add_flag(Flag{
flag: .string
required: false
name: 'repo'
abbrev: 'r'
description: 'name of repo'
})
c.add_flag(Flag{
flag: .string
required: false
name: 'branch'
abbrev: 'b'
description: 'branch of repo (optional)'
})
c.add_flag(Flag{
flag: .string
required: false
name: 'account'
abbrev: 'a'
description: 'name of account e.g. threefoldtech'
})
c.add_flag(Flag{
flag: .string
required: false
name: 'provider'
abbrev: 'p'
description: 'name of provider e.g. github'
})
}
for mut c_ in allcmdsref {
mut c := *c_
c.add_flag(Flag{
flag: .string
required: false
name: 'coderoot'
abbrev: 'cr'
description: 'If you want to use another directory for your code root.'
})
c.add_flag(Flag{
flag: .bool
required: false
name: 'script'
abbrev: 'z'
description: 'to use in scripts, will not run interative and ask questions.'
})
cmd_run.add_command(c)
}
cmd_run.add_command(cmd_cd)
cmdroot.add_command(cmd_run)
}
fn cmd_git_execute(cmd Command) ! {
mut silent := cmd.flags.get_bool('silent') or { false }
if silent || cmd.name == 'cd' {
console.silent_set()
}
mut coderoot := cmd.flags.get_string('coderoot') or { '' }
if 'CODEROOT' in os.environ() && coderoot == '' {
coderoot = os.environ()['CODEROOT']
}
mut gs := gittools.get()!
if coderoot.len > 0 {
// is a hack for now
gs = gittools.new(coderoot: coderoot)!
}
// create the filter for doing group actions, or action on 1 repo
mut filter := cmd.flags.get_string('filter') or { '' }
mut branch := cmd.flags.get_string('branch') or { '' }
mut repo := cmd.flags.get_string('repo') or { '' }
mut account := cmd.flags.get_string('account') or { '' }
mut provider := cmd.flags.get_string('provider') or { '' }
if cmd.name != 'cd' {
// check if we are in a git repo
if repo == '' && account == '' && provider == '' && filter == '' {
if r0 := gs.get_working_repo() {
repo = r0.name
account = r0.account
provider = r0.provider
}
}
}
if cmd.name in gittools.gitcmds.split(',') {
mut pull := cmd.flags.get_bool('pull') or { false }
mut reset := cmd.flags.get_bool('reset') or { false }
mut recursive := cmd.flags.get_bool('recursive') or { false }
if cmd.flags.get_bool('pullreset') or { false } {
pull = true
reset = true
}
mypath := gs.do(
filter: filter
repo: repo
account: account
provider: provider
branch: branch
recursive: recursive
cmd: cmd.name
script: cmd.flags.get_bool('script') or { false }
pull: pull
reset: reset
msg: cmd.flags.get_string('message') or { '' }
url: cmd.flags.get_string('url') or { '' }
)!
if cmd.name == 'cd' {
print('cd ${mypath}\n')
}
return
} else {
// console.print_debug(" Supported commands are: ${gittools.gitcmds}")
return error(cmd.help_message())
}
}

View File

@@ -0,0 +1,68 @@
module herocmds
import freeflowuniverse.herolib.conversiontools.imagemagick
import cli { Command, Flag }
import os
import freeflowuniverse.herolib.ui.console
// const wikipath = os.dir(@FILE) + '/wiki'
pub fn cmd_imagedownsize(mut cmdroot Command) {
mut cmd_run := Command{
name: 'image_downsize'
description: 'walk over current director or specified one and downsize all images'
required_args: 0
execute: cmd_imagedownsize_execute
}
cmd_run.add_flag(Flag{
flag: .string
required: false
name: 'path'
abbrev: 'p'
description: 'If not in current directory.'
})
cmd_run.add_flag(Flag{
flag: .string
required: false
name: 'backupdir'
abbrev: 'b'
description: 'What is the backup dir if any.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'convertpng'
description: 'will convert png to jpg.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'redo'
abbrev: 'r'
description: 'will do the checks again.'
})
cmdroot.add_command(cmd_run)
}
fn cmd_imagedownsize_execute(cmd Command) ! {
mut backupdir := cmd.flags.get_string('backupdir') or { '' }
mut path := cmd.flags.get_string('path') or { '' }
if path == '' {
path = os.getwd()
}
imagemagick.downsize(
path: path
backupdir: backupdir
redo: cmd.flags.get_bool('redo') or { false }
convertpng: cmd.flags.get_bool('convertpng') or { false }
) or {
print_backtrace()
console.print_stderr(err.str())
panic(err)
}
}

103
lib/core/herocmds/init.v Normal file
View File

@@ -0,0 +1,103 @@
module herocmds
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.installers.base
import freeflowuniverse.herolib.installers.lang.herolib
import freeflowuniverse.herolib.ui.console
import cli { Command, Flag }
pub fn cmd_init(mut cmdroot Command) {
mut cmd_run := Command{
name: 'init'
usage: '
Initialization Helpers for Hero
-r will reset everything e.g. done states (when installing something)
-d will put the platform in development mode, get V, crystallib, hero...
-c will compile hero on local platform (requires local crystallib)
'
description: 'initialize hero environment (reset, development mode, )'
required_args: 0
execute: cmd_init_execute
}
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'reset'
abbrev: 'r'
description: 'will reset.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'develop'
abbrev: 'd'
description: 'will put system in development mode.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'compile'
abbrev: 'c'
description: 'will compile hero.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'redis'
description: 'will make sure redis is in system and is running.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'gitpull'
abbrev: 'gp'
description: 'will try to pull git repos for crystallib.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'gitreset'
abbrev: 'gr'
description: 'will reset the git repo if there are changes inside, will also pull, CAREFUL.'
})
cmdroot.add_command(cmd_run)
}
fn cmd_init_execute(cmd Command) ! {
mut develop := cmd.flags.get_bool('develop') or { false }
mut reset := cmd.flags.get_bool('reset') or { false }
mut hero := cmd.flags.get_bool('compile') or { false }
mut redis := cmd.flags.get_bool('redis') or { false }
mut git_reset := cmd.flags.get_bool('gitreset') or { false }
mut git_pull := cmd.flags.get_bool('gitpull') or { false }
if redis {
base.redis_install(reset: true)!
}
if develop {
console.print_header('init in development mode: reset:${reset}')
base.install(reset: reset, develop: true)!
return
}
if hero {
base.install(reset: reset, develop: true)!
herolib.install(reset: reset, git_pull: git_pull, git_reset: git_reset)!
base.redis_install()!
herolib.hero_compile(reset: reset)!
r := osal.profile_path_add_hero()!
console.print_header(' add path ${r} to profile.')
return
}
return error(cmd.help_message())
}

View File

@@ -0,0 +1,83 @@
module herocmds
import freeflowuniverse.herolib.installers
import cli { Command, Flag }
import freeflowuniverse.herolib.ui.console
pub fn cmd_installers(mut cmdroot Command) {
mut cmd_run := Command{
name: 'installers'
description: 'a set of installers'
required_args: 0
execute: cmd_installers_execute
}
cmd_run.add_flag(Flag{
flag: .string
required: false
name: 'names'
abbrev: 'n'
description: 'Comma separated list of installers to call.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'reset'
abbrev: 'r'
description: 'will reset.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'uninstall'
abbrev: 'u'
description: 'will uninstall in stead of install.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'gitpull'
abbrev: 'gp'
description: 'e.g. in crystallib or other git repo pull changes.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'gitreset'
abbrev: 'gr'
description: 'e.g. in crystallib or other git repo pull & reset changes.'
})
cmdroot.add_command(cmd_run)
}
fn cmd_installers_execute(cmd Command) ! {
mut reset := cmd.flags.get_bool('reset') or { false }
mut uninstall := cmd.flags.get_bool('uninstall') or { false }
mut gitpull := cmd.flags.get_bool('gitpull') or { false }
mut gitreset := cmd.flags.get_bool('gitreset') or { false }
mut names := cmd.flags.get_string('names') or { '' }
if names == '' {
console.clear()
console.print_header('the following installers are known:')
console.print_lf(2)
for x in installers.names() {
console.print_item(x)
}
console.print_lf(1)
console.print_stdout(cmd.help_message())
console.print_lf(5)
exit(1)
}
installers.install_multi(
reset: reset
names: names
uninstall: uninstall
gitpull: gitpull
gitreset: gitreset
)!
}

View File

@@ -0,0 +1,92 @@
module herocmds
import cli { Command, Flag }
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.develop.luadns
// Main function to set up CLI commands
pub fn cmd_luadns(mut cmdroot Command) {
mut cmd_luadns := Command{
name: 'luadns'
usage: '
## Manage your LuaDNS
example:
hero luadns load -u https://github.com/example/dns-repo
hero luadns set-domain -d example.com -i 51.129.54.234
'
description: 'Manage LuaDNS configurations'
required_args: 0
execute: cmd_luadns_execute
}
cmd_luadns.add_command(cmd_luadns_set_domain())
cmdroot.add_command(cmd_luadns)
}
fn cmd_luadns_set_domain() Command {
mut cmd := Command{
name: 'set-domain'
usage: 'Set a domain in the DNS configurations'
required_args: 0
execute: cmd_luadns_set_domain_execute
}
cmd.add_flag(Flag{
flag: .string
required: true
name: 'domain'
abbrev: 'd'
description: 'Domain to set in the DNS configurations'
})
cmd.add_flag(Flag{
flag: .string
required: true
name: 'ip'
abbrev: 'i'
description: 'IP address for the domain'
})
return cmd
}
fn cmd_luadns_execute(cmd Command) ! {
console.print_stdout(cmd.help_message())
}
fn cmd_luadns_set_domain_execute(cmd Command) ! {
url := cmd.flags.get_string('url') or {
console.print_debug('URL flag is required')
cmd_luadns_help(cmd)
exit(1)
}
domain := cmd.flags.get_string('domain') or {
console.print_debug('Domain flag is required')
cmd_luadns_help(cmd)
exit(1)
}
ip := cmd.flags.get_string('ip') or {
console.print_debug('IP flag is required')
cmd_luadns_help(cmd)
exit(1)
}
mut lua_dns := luadns.load(url)!
lua_dns.set_domain(domain, ip) or {
eprintln('Failed to set domain: ${err}')
exit(1)
}
}
fn cmd_luadns_help(cmd Command) {
console.clear()
console.print_header('Instructions for LuaDNS:')
console.print_lf(1)
console.print_stdout(cmd.help_message())
console.print_lf(5)
}

121
lib/core/herocmds/mdbook.v Normal file
View File

@@ -0,0 +1,121 @@
module herocmds
import freeflowuniverse.herolib.web.mdbook
import freeflowuniverse.herolib.core.pathlib
import cli { Command, Flag }
import os
import freeflowuniverse.herolib.ui.console
// path string //if location on filessytem, if exists, this has prio on git_url
// git_url string // location of where the hero scripts are
// git_pull bool // means when getting new repo will pull even when repo is already there
// git_pullreset bool // means we will force a pull and reset old content
// coderoot string //the location of coderoot if its another one
pub fn cmd_mdbook(mut cmdroot Command) {
mut cmd_mdbook := Command{
name: 'mdbook'
usage: '
## Manage your MDBooks
example:
hero mdbook -u https://git.ourworld.tf/tfgrid/info_tfgrid/src/branch/main/heroscript
If you do -gp it will pull newest book content from git and give error if there are local changes.
If you do -gr it will pull newest book content from git and overwrite local changes (careful).
'
description: 'create, edit, show mdbooks'
required_args: 0
execute: cmd_mdbook_execute
}
cmd_run_add_flags(mut cmd_mdbook)
cmd_mdbook.add_flag(Flag{
flag: .string
name: 'name'
abbrev: 'n'
description: 'name of the mdbook.'
})
// cmd_mdbook.add_flag(Flag{
// flag: .bool
// required: false
// name: 'edit'
// description: 'will open vscode for collections & summary.'
// })
cmd_mdbook.add_flag(Flag{
flag: .bool
required: false
name: 'open'
abbrev: 'o'
description: 'will open the generated book.'
})
mut cmd_list := Command{
sort_flags: true
name: 'list'
execute: cmd_mdbook_list
description: 'will list existing mdbooks'
}
cmd_mdbook.add_command(cmd_list)
cmdroot.add_command(cmd_mdbook)
}
fn cmd_mdbook_list(cmd Command) ! {
console.print_header('MDBooks:')
build_path := os.join_path(os.home_dir(), 'hero/var/mdbuild')
mut build_dir := pathlib.get_dir(path: build_path)!
list := build_dir.list(
recursive: false
dirs_only: true
)!
for path in list.paths {
console.print_stdout(path.name())
}
}
fn cmd_mdbook_execute(cmd Command) ! {
mut name := cmd.flags.get_string('name') or { '' }
mut url := cmd.flags.get_string('url') or { '' }
mut path := cmd.flags.get_string('path') or { '' }
if path.len > 0 || url.len > 0 {
// execute the attached playbook
mut plbook, _ := plbook_run(cmd)!
// get name from the book.generate action
if name == '' {
mut a := plbook.action_get(actor: 'mdbook', name: 'define')!
name = a.params.get('name') or { '' }
}
} else {
mdbook_help(cmd)
}
if name == '' {
console.print_debug('did not find name of book to generate, check in heroscript or specify with --name')
mdbook_help(cmd)
exit(1)
}
edit := cmd.flags.get_bool('edit') or { false }
open := cmd.flags.get_bool('open') or { false }
if edit || open {
mdbook.book_open(name)!
}
if edit {
mdbook.book_edit(name)!
}
}
fn mdbook_help(cmd Command) {
console.clear()
console.print_header('Instructions for mdbook:')
console.print_lf(1)
console.print_stdout(cmd.help_message())
console.print_lf(5)
}

View File

@@ -0,0 +1,183 @@
module herocmds
import freeflowuniverse.herolib.develop.gittools
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playcmds
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.develop.vscode
import freeflowuniverse.herolib.develop.sourcetree
import cli { Command, Flag }
import os
pub fn cmd_run_add_flags(mut cmd_run Command) {
cmd_run.add_flag(Flag{
flag: .string
required: false
name: 'path'
abbrev: 'p'
description: 'path where heroscripts can be found.'
})
// cmd_run.add_flag(Flag{
// flag: .string
// required: false
// name: 'sessionname'
// abbrev: 'sn'
// description: 'name for the session (optional).'
// })
// cmd_run.add_flag(Flag{
// flag: .string
// required: false
// name: 'contextname'
// abbrev: 'cn'
// description: 'name for the context (optional).'
// })
cmd_run.add_flag(Flag{
flag: .string
required: false
name: 'url'
abbrev: 'u'
description: 'url where heroscript can be found.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'gitpull'
abbrev: 'gp'
description: 'will try to pull.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'gitreset'
abbrev: 'gr'
description: 'will reset the git repo if there are changes inside, will also pull, CAREFUL.'
})
cmd_run.add_flag(Flag{
flag: .string
required: false
name: 'coderoot'
abbrev: 'cr'
description: 'Set code root for gittools.'
})
cmd_run.add_flag(Flag{
flag: .bool
name: 'script'
abbrev: 's'
description: 'runs non interactive!'
})
cmd_run.add_flag(Flag{
flag: .bool
name: 'reset'
abbrev: 'r'
description: 'reset, means lose changes of your repos, BE CAREFUL.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'edit'
abbrev: 'e'
description: 'Open visual studio code for where we found the content.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'sourcetree'
abbrev: 'st'
description: 'Open sourcetree (git mgmt) for the repo where we found the content.'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
name: 'dagu'
abbrev: 'da'
description: 'Schedule the heroscripts through DAGU.'
})
}
// returns the path of the fetched repo url
pub fn plbook_code_get(cmd Command) !string {
mut path := cmd.flags.get_string('path') or { '' }
mut url := cmd.flags.get_string('url') or { '' }
// mut sessionname := cmd.flags.get_string('sessionname') or { '' }
// mut contextname := cmd.flags.get_string('contextname') or { '' }
mut coderoot := cmd.flags.get_string('coderoot') or { '' }
if 'CODEROOT' in os.environ() && coderoot == '' {
coderoot = os.environ()['CODEROOT']
}
if coderoot.len > 0 {
base.context_new(coderoot: coderoot)!
}
reset := cmd.flags.get_bool('gitreset') or { false }
pull := cmd.flags.get_bool('gitpull') or { false }
// interactive := !cmd.flags.get_bool('script') or { false }
mut gs := gittools.get(coderoot: coderoot)!
if url.len > 0 {
mut repo := gs.get_repo(
pull: pull
reset: reset
url: url
// QUESTION: why should reload be default true?
// reload: true
)!
path = repo.get_path_of_url(url)!
}
return path
}
// same as session_run_get but will also run the playbook
pub fn plbook_run(cmd Command) !(&playbook.PlayBook, string) {
path := plbook_code_get(cmd)!
if path.len == 0 {
return error(cmd.help_message())
}
// add all actions inside to the playbook
mut plbook := playbook.new(path: path)!
dagu := cmd.flags.get_bool('dagu') or { false }
playcmds.run(mut plbook, dagu)!
// TODO: below gives Segmentation fault (core dumped)
// console.print_stdout(plbook.str())
return &plbook, path
}
fn plbook_edit_sourcetree(cmd Command) !(&playbook.PlayBook, string) {
edit := cmd.flags.get_bool('edit') or { false }
treedo := cmd.flags.get_bool('sourcetree') or { false }
mut plbook, path := plbook_run(cmd)!
if path.len == 0 {
return error('path or url needs to be specified')
}
if treedo {
// mut repo := gittools.git_repo_get(coderoot: coderoot, path: path)!
// repo.sourcetree()!
sourcetree.open(path: path)!
} else if edit {
mut vscode_ := vscode.new(path)
vscode_.open()!
}
return plbook, path
}

40
lib/core/herocmds/run.v Normal file
View File

@@ -0,0 +1,40 @@
module herocmds
import cli { Command }
// path string //if location on filessytem, if exists, this has prio on git_url
// git_url string // location of where the hero scripts are
// git_pull bool // means when getting new repo will pull even when repo is already there
// git_pullreset bool // means we will force a pull and reset old content
// coderoot string //the location of coderoot if its another one
pub fn cmd_run(mut cmdroot Command) {
mut cmd_run := Command{
name: 'run'
usage: '
## Powerfull command to run heroscript
heroscript has numerous ways to execute actions using your hero tool.
example:
hero run -u https://git.ourworld.tf/threefold_coop/info_asimov/src/branch/main/heroscript
Can also do -e or -st to see sourcetree
If you do -gp it will pull newest heroscripts from git and give error if there are local changes.
If you do -gr it will pull newest heroscripts from git and overwrite local changes (careful).
'
required_args: 0
description: 'run heroscript commands'
execute: cmd_heroscript_execute
}
cmd_run_add_flags(mut cmd_run)
cmdroot.add_command(cmd_run)
}
fn cmd_heroscript_execute(cmd Command) ! {
plbook_edit_sourcetree(cmd)!
}

View File

@@ -0,0 +1,138 @@
module herocmds
import freeflowuniverse.herolib.osal.sshagent
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.ui
import cli { Command, Flag }
pub fn cmd_sshagent(mut cmdroot Command) {
mut cmd_run := Command{
name: 'sshagent'
description: 'Work with SSHAgent'
// required_args: 1
usage: 'sub commands of generate are list, generate, unload, load'
execute: cmd_sshagent_execute
sort_commands: true
}
mut sshagent_command_list := Command{
sort_flags: true
name: 'list'
execute: cmd_sshagent_execute
description: 'list ssh-keys.'
}
mut sshagent_command_generate := Command{
sort_flags: true
name: 'generate'
execute: cmd_sshagent_execute
description: 'generate ssh-key.'
}
mut sshagent_command_add := Command{
sort_flags: true
name: 'add'
execute: cmd_sshagent_execute
description: 'add a key starting from private key, only works interactive for nows.'
}
sshagent_command_generate.add_flag(Flag{
flag: .bool
name: 'load'
abbrev: 'l'
description: 'should key be loaded'
})
mut sshagent_command_load := Command{
sort_flags: true
name: 'load'
execute: cmd_sshagent_execute
description: 'load ssh-key in agent.'
}
mut sshagent_command_unload := Command{
sort_flags: true
name: 'forget'
execute: cmd_sshagent_execute
description: 'Unload ssh-key from agent.'
}
mut sshagent_command_reset := Command{
sort_flags: true
name: 'reset'
execute: cmd_sshagent_execute
description: 'Reset all keys, means unload them all.'
}
mut allcmdsref_gen0 := [&sshagent_command_generate, &sshagent_command_load, &sshagent_command_unload,
&sshagent_command_reset, &sshagent_command_add]
for mut d in allcmdsref_gen0 {
d.add_flag(Flag{
flag: .string
name: 'name'
abbrev: 'n'
required: true
description: 'name of the key'
})
}
mut allcmdsref_gen := [&sshagent_command_list, &sshagent_command_generate, &sshagent_command_load,
&sshagent_command_unload, &sshagent_command_reset]
for mut c in allcmdsref_gen {
// c.add_flag(Flag{
// flag: .bool
// name: 'reset'
// abbrev: 'r'
// description: 'do you want to reset all? Dangerous!'
// })
c.add_flag(Flag{
flag: .bool
name: 'script'
abbrev: 's'
description: 'runs non interactive!'
})
cmd_run.add_command(*c)
}
cmdroot.add_command(cmd_run)
}
fn cmd_sshagent_execute(cmd Command) ! {
// mut reset := cmd.flags.get_bool('reset') or {false }
mut isscript := cmd.flags.get_bool('script') or { false }
mut load := cmd.flags.get_bool('load') or { false }
mut name := cmd.flags.get_string('name') or { '' }
mut agent := sshagent.new()!
if cmd.name == 'list' {
if !isscript {
console.clear()
}
console.print_debug(agent.str())
} else if cmd.name == 'generate' {
agent.generate(name, '')!
if load {
agent.load(name)!
}
} else if cmd.name == 'load' {
agent.load(name)!
} else if cmd.name == 'forget' {
agent.forget(name)!
} else if cmd.name == 'reset' {
agent.reset()!
} else if cmd.name == 'add' {
panic("can't work, no support for multiline yet")
mut myui := ui.new()!
privkey := myui.ask_question(
question: 'private key of your ssh key'
)!
agent.add(name, privkey)!
} else {
// console.print_debug(1)
return error(cmd.help_message())
// console.print_debug(" Supported commands are: ${gentools.gencmds}")
// return error('unknown subcmd')
}
}

View File

@@ -2,10 +2,10 @@ module playcmds
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.virt.hetzner
// import freeflowuniverse.herolib.virt.hetzner
// import freeflowuniverse.herolib.clients.b2
import freeflowuniverse.herolib.biz.bizmodel
import freeflowuniverse.herolib.hero.publishing
// import freeflowuniverse.herolib.hero.publishing
import freeflowuniverse.herolib.threefold.grid4.gridsimulator
// import freeflowuniverse.herolib.installers.sysadmintools.daguserver
import freeflowuniverse.herolib.threefold.grid4.farmingsimulator
@@ -26,7 +26,7 @@ pub fn run(mut plbook playbook.PlayBook, dagu bool) ! {
// play_caddy(mut plbook)!
// play_juggler(mut plbook)!
// play_luadns(mut plbook)!
hetzner.heroplay(mut plbook)!
// hetzner.heroplay(mut plbook)!
// b2.heroplay(mut plbook)!
farmingsimulator.play(mut plbook)!
@@ -36,7 +36,7 @@ pub fn run(mut plbook playbook.PlayBook, dagu bool) ! {
// base_install(play(mut plbook)!
// coredns.play(mut plbook)!
publishing.play(mut plbook)!
// publishing.play(mut plbook)!
// plbook.empty_check()!

View File

@@ -1,9 +1,9 @@
module playcmds
// import freeflowuniverse.herolib.installers.sysadmintools.daguserver
import freeflowuniverse.herolib.installers.sysadmintools.daguserver
// pub fn scheduler(heroscript string) ! {
// daguserver.play(
// heroscript: heroscript
// )!
// }
pub fn scheduler(heroscript string) ! {
daguserver.play(
heroscript: heroscript
)!
}

View File

@@ -0,0 +1,31 @@
# LuaDNS Vlang Module
## Overview
This module provides functionality to parse and manage DNS configurations from Lua scripts. It supports creating, updating, and validating DNS records for multiple domains.
## Features
- Parse Lua DNS configuration files.
- Manage DNS records for multiple domains.
- Validate IP addresses and domain names.
- Automatically add default CNAME and CAA records.
## Usage
### Load DNS Configurations
Load DNS configurations from a git repository.
```v
import luadns
// Load configurations from a git URL
dns := luadns.load('https://git.example.com/repo.git')!
```
### Set Domain
Add or update an A record for a domain or subdomain.
```v
dns.set_domain('example.com', '51.129.54.234')!
dns.set_domain('auth.example.com', '231.29.54.234')!
```
### Validate Inputs
The module ensures that only valid IP addresses and domain names are accepted.

View File

@@ -0,0 +1,25 @@
module luadns
import freeflowuniverse.herolib.develop.gittools
struct LuaDNS {
pub mut:
url string
configs []DNSConfig
}
// returns the path of the fetched repo
pub fn load(url string) !LuaDNS {
mut gs := gittools.new()!
mut repo := gs.get_repo(
url: url
pull: true
)!
repo_path := repo.get_path()!
return LuaDNS{
url: url
configs: parse_dns_configs(repo_path)!
}
}

114
lib/develop/luadns/luadns.v Normal file
View File

@@ -0,0 +1,114 @@
module luadns
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.develop.gittools
// returns the directory of the git repository for the dns
pub fn (dns LuaDNS) directory() !pathlib.Path {
mut gs := gittools.new()!
mut repo := gs.get_repo(url: dns.url, pull: true)!
repo_path := repo.get_path()!
return pathlib.get_dir(path: repo_path)!
}
pub fn (dns LuaDNS) domain_file(domain string) !pathlib.Path {
return pathlib.get_file(path: '${dns.directory()!.path}/${domain}.lua')!
}
// returns the git repository for the dns
pub fn (dns LuaDNS) repository() !&gittools.GitRepo {
mut gs := gittools.new()!
repo := gs.get_repo(url: dns.url, pull: true)!
return repo
}
pub fn (mut dns LuaDNS) set_domain(domain string, ip string) ! {
// Validate the IP address
if !is_valid_ip(ip) {
return error('Invalid IP address: ${ip}')
}
// Validate the domain
if !is_valid_domain(domain) {
return error('Invalid domain: ${domain}')
}
subdomain := if domain.contains('.') { domain.all_before('.') } else { domain }
root_domain := if domain.contains('.') { domain.all_after('.') } else { domain }
mut config := dns.find_or_create_config(root_domain)
mut updated := false
for mut record in config.a_records {
if record.name == subdomain {
record.ip = ip
updated = true
break
}
}
if !updated {
if subdomain == root_domain {
config.a_records << ARecord{'', ip}
} else {
config.a_records << ARecord{subdomain, ip}
}
}
// Add default CNAME and CAA records if they do not exist
if config.cname_records.len == 0 {
config.cname_records << CNAMERecord{'www', '@'}
}
if config.caa_records.len == 0 {
config.caa_records << CAARecord{'', 'letsencrypt.org', 'issue'}
config.caa_records << CAARecord{'', 'mailto:info@threefold.io', 'iodef'}
}
dns.save_config(config)!
}
fn (mut dns LuaDNS) find_or_create_config(domain string) DNSConfig {
for mut config in dns.configs {
if config.domain == domain {
return config
}
}
dns.configs << DNSConfig{
domain: domain
}
return dns.configs[dns.configs.len - 1]
}
fn (dns LuaDNS) save_config(config DNSConfig) ! {
mut repo := dns.repository()!
repo.pull()!
mut file := pathlib.get_file(path: '${dns.directory()!.path}/${config.domain}.lua')!
mut content := ''
for record in config.a_records {
if record.name == '' || record.name == '_a' {
content += 'a(_a, "${record.ip}")\n'
} else {
content += 'a("${record.name}", "${record.ip}")\n'
}
}
for record in config.cname_records {
if record.alias == '_a' {
content += 'cname("${record.name}", _a)\n'
} else {
content += 'cname("${record.name}", "${record.alias}")\n'
}
}
for record in config.caa_records {
content += 'caa("${record.name}", "${record.value}", "${record.tag}")\n'
}
file.write(content)!
repo.commit('Update DNS records')!
repo.pull()!
repo.push()!
}

View File

@@ -0,0 +1,28 @@
module luadns
struct ARecord {
mut:
name string
ip string
}
struct CNAMERecord {
mut:
name string
alias string
}
struct CAARecord {
mut:
name string
value string
tag string
}
struct DNSConfig {
mut:
domain string
a_records []ARecord
cname_records []CNAMERecord
caa_records []CAARecord
}

View File

@@ -0,0 +1,56 @@
module luadns
import os
import freeflowuniverse.herolib.core.pathlib
fn parse_dns_configs(directory_path string) ![]DNSConfig {
mut configs := []DNSConfig{}
mut luadns_dir := pathlib.get_dir(path: directory_path)!
mut list := luadns_dir.list()!
for mut file in list.paths {
if file.extension() == 'lua' {
config := parse_luadns_file(file.path)!
configs << config
}
}
return configs
}
fn parse_luadns_file(file_path string) !DNSConfig {
mut config := DNSConfig{}
mut file := pathlib.get_file(path: file_path)!
content := file.read()!
for line in content.split('\n') {
trimmed_line := line.trim_space()
if trimmed_line.len == 0 || trimmed_line.starts_with('//') {
continue
}
if trimmed_line.starts_with('a(') {
parts := trimmed_line.all_after('a(').all_before(')').split(',')
name := parts[0].trim('" ')
ip := parts[1].trim('" ')
config.a_records << ARecord{name, ip}
} else if trimmed_line.starts_with('cname(') {
parts := trimmed_line.all_after('cname(').all_before(')').split(',')
name := parts[0].trim('" ')
alias := parts[1].trim('" ')
config.cname_records << CNAMERecord{name, alias}
} else if trimmed_line.starts_with('caa(') {
parts := trimmed_line.all_after('caa(').all_before(')').split(',')
name := parts[0].trim('" ')
value := parts[1].trim('" ')
tag := parts[2].trim('" ')
config.caa_records << CAARecord{name, value, tag}
}
}
config.domain = os.base(file_path).all_before('.lua')
return config
}

View File

@@ -0,0 +1,20 @@
module luadns
fn is_valid_ip(ip string) bool {
parts := ip.split('.')
if parts.len != 4 {
return false
}
for part in parts {
num := part.int()
if num < 0 || num > 255 {
return false
}
}
return true
}
fn is_valid_domain(domain string) bool {
// TODO: implement
return true
}

View File

@@ -0,0 +1,120 @@
module chrome
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.ui.console
import os
@[params]
pub struct InstallArgs {
pub mut:
reset bool
uninstall bool
}
pub fn install(args_ InstallArgs) ! {
mut args := args_
// base.install()!
if args.reset || args.uninstall {
console.print_header('uninstall chrome')
uninstall()!
console.print_debug(' - ok')
if args.uninstall {
return
}
}
console.print_header('package_install install chrome')
if !args.reset && osal.done_exists('install_chrome') && exists()! {
console.print_debug(' - already installed')
return
}
mut url := ''
platform := core.platform()!
if platform in [.alpine, .arch, .ubuntu] {
// url = 'https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg'
panic('not implemented yet')
} else if platform == .osx {
url = 'https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg'
}
console.print_debug(' download ${url}')
_ = osal.download(
url: url
minsize_kb: 5000
reset: args.reset
dest: '/tmp/googlechrome.dmg'
)!
cmd := "
hdiutil attach /tmp/googlechrome.dmg
echo ' - copy chrome into app folder'
echo ' - will now copy all files to Application folder, this can take a while'
cp -r /Volumes/Google\\ Chrome/Google\\ Chrome.app /Applications/
sleep 30
echo ' - copy done'
hdiutil detach /Volumes/Google\\ Chrome/
rm -f /tmp/googlechrome.dmg
"
osal.exec(cmd: cmd)!
console.print_debug(' - copy done to Application folder.')
if exists()! {
console.print_debug(' - exists check ok.')
}
osal.done_set('install_chrome', 'OK')!
}
@[params]
pub struct ExtensionsInstallArgs {
pub mut:
extensions string
default bool = true
}
pub fn exists() !bool {
cmd := 'mdfind "kMDItemKind == \'Application\'" | grep "Google Chrome"'
res := os.execute(cmd)
if res.exit_code > 0 {
return false
}
return true
}
pub fn install_path() !string {
cmd := 'mdfind "kMDItemKind == \'Application\'" | grep "Google Chrome"'
res := os.execute(cmd)
if res.exit_code > 0 {
return error("can't find install path")
}
return res.output.trim_space()
}
pub fn uninstall() ! {
cmd := '
# Quit Google Chrome
osascript -e \'quit app "Google Chrome"\'
# Wait a bit to ensure Chrome has completely quit
sleep 2
# Remove the Google Chrome Application
rm -rf "/Applications/Google Chrome.app"
# Remove Chromes Application Support Data
rm -rf ~/Library/Application\\ Support/Google/Chrome
# Remove Chrome\'s Caches
rm -rf ~/Library/Caches/Google/Chrome
# Delete Chrome Preferences
rm -rf ~/Library/Preferences/com.google.Chrome.plist
# Clear Chrome Saved Application State
rm -rf ~/Library/Saved\\ Application\\ State/com.google.Chrome.savedState
'
osal.exec(cmd: cmd)!
}
// # Optional: Empty the Trash
// osascript -e 'tell app "Finder" to empty'

View File

@@ -0,0 +1,132 @@
module vscode
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.ui.console
import os
@[params]
pub struct InstallArgs {
pub mut:
reset bool
}
pub fn install(args_ InstallArgs) ! {
mut args := args_
// base.install()!
console.print_header('install vscode')
if !args.reset && osal.done_exists('install_vscode') && osal.cmd_exists('code') {
console.print_debug(' - already installed')
return
}
mut url := ''
platform := core.platform()!
if platform in [.alpine, .arch, .ubuntu] {
if core.cputype()! == .arm {
url = 'https://code.visualstudio.com/sha/download?build=stable&os=cli-alpine-arm64'
} else {
url = 'https://code.visualstudio.com/sha/download?build=stable&os=cli-alpine-x64'
}
} else if platform == .osx {
if core.cputype()! == .arm {
url = 'https://code.visualstudio.com/sha/download?build=stable&os=cli-darwin-arm64'
} else {
url = 'https://code.visualstudio.com/sha/download?build=stable&os=cli-darwin-x64'
}
}
console.print_debug(' download ${url}')
_ = osal.download(
url: url
minsize_kb: 5000
reset: args.reset
dest: '/tmp/vscode.zip'
expand_file: '/tmp/download/vscode'
)!
osal.cmd_add(
cmdname: 'code'
source: '/tmp/download/vscode/code'
reset: true
)!
extensions_install(default: true)!
osal.done_set('install_vscode', 'OK')!
}
@[params]
pub struct ExtensionsInstallArgs {
pub mut:
extensions string
default bool = true
}
pub fn extensions_install(args_ ExtensionsInstallArgs) ! {
mut args := args_
mut items := []string{}
for item in args.extensions.split(',').map(it.trim_space()) {
if item.trim_space() == '' {
continue
}
if item !in items {
items << item
}
}
default := [
'golang.go',
'ms-azuretools.vscode-docker',
'ms-python.autopep8',
'ms-python.python',
'ms-vscode-remote.remote-ssh',
'ms-vscode-remote.remote-ssh-edit',
'ms-vscode-remote.remote-containers',
'ms-vscode.cmake-tools',
'ms-vscode.makefile-tools',
'ms-vscode.remote-explorer',
'ms-vscode.remote-repositories',
'ms-vsliveshare.vsliveshare',
'redhat.vscode-yaml',
'rust-lang.rust-analyzer',
'sumneko.lua',
'shd101wyy.markdown-preview-enhanced',
'TakumiI.markdowntable',
'telesoho.vscode-markdown-paste-image',
'tamasfe.even-better-toml',
'tomoki1207.pdf',
'VOSCA.vscode-v-analyzer',
'yzhang.markdown-all-in-one',
'zamerick.vscode-caddyfile-syntax',
'zxh404.vscode-proto3',
]
if args.default {
for item in default {
if item !in items {
items << item
}
}
}
for item in items {
cmd := 'code --install-extension ${item}'
console.print_debug(' - extension install: ${item}')
res := os.execute(cmd)
if res.exit_code > 0 {
return error("could not install visual studio code extension:'${item}'.\n${res}")
}
}
}
pub fn extensions_list() ![]string {
cmd := 'code --list-extensions'
res := os.execute(cmd)
if res.exit_code > 0 {
return error('could not list visual studio code extensions.\n${res}')
}
mut res2 := []string{}
for i in res.output.split_into_lines().map(it.trim_space()) {
if i.trim_space() == '' {
continue
}
res2 << i
}
return res2
}

View File

@@ -10,7 +10,7 @@ import os
// checks if a certain version or above is installed
fn installed() !bool {
res := os.execute('${osal.profile_path_source_and()} livekit-server -v')
res := os.execute('${osal.profile_path_source_and()!} livekit-server -v')
if res.exit_code != 0 {
return false
}

View File

@@ -0,0 +1,13 @@
!!hero_code.generate_installer
name:'zinit'
classname:'Zinit'
singleton:1
templates:0
default:1
title:''
supported_platforms:''
reset:0
startupmanager:1
hasconfig:0
build:1

View File

@@ -0,0 +1,34 @@
# zinit
To get started
```vlang
import freeflowuniverse.herolib.installers.something. zinit
mut installer:= zinit.get()!
installer.start()!
```
## example heroscript
```hero
!!zinit.install
homedir: '/home/user/zinit'
username: 'admin'
password: 'secretpassword'
title: 'Some Title'
host: 'localhost'
port: 8888
```

View File

@@ -0,0 +1,132 @@
module zinit
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.osal.zinit
import freeflowuniverse.herolib.installers.ulist
import freeflowuniverse.herolib.installers.lang.rust
import freeflowuniverse.herolib.develop.gittools
import freeflowuniverse.herolib.osal.systemd
import os
// checks if a certain version or above is installed
fn installed() !bool {
cmd := 'zinit --version'
// console.print_debug(cmd)
res := os.execute(cmd)
if res.exit_code == 0 {
r := res.output.split_into_lines().filter(it.trim_space().starts_with('zinit v'))
if r.len != 1 {
return error("couldn't parse zinit version.\n${res.output}")
}
if texttools.version(version) == texttools.version(r[0].all_after_first('zinit v')) {
return true
}
}
console.print_debug(res.str())
return false
}
fn install() ! {
console.print_header('install zinit')
if !osal.is_linux() {
return error('only support linux for now')
}
release_url := 'https://github.com/threefoldtech/zinit/releases/download/v0.2.14/zinit'
mut dest := osal.download(
url: release_url
minsize_kb: 2000
reset: true
)!
osal.cmd_add(
cmdname: 'zinit'
source: dest.path
)!
osal.dir_ensure('/etc/zinit')!
console.print_header('install zinit done')
}
fn build() ! {
if !osal.is_linux() {
return error('only support linux for now')
}
rust.install()!
// install zinit if it was already done will return true
console.print_header('build zinit')
mut gs := gittools.get(coderoot: '/tmp/builder')!
mut repo := gs.get_repo(
url: 'https://github.com/threefoldtech/zinit'
reset: true
pull: true
)!
gitpath := repo.get_path()!
// source ${osal.profile_path()}
cmd := '
source ~/.cargo/env
cd ${gitpath}
make release
'
osal.execute_stdout(cmd)!
osal.cmd_add(
cmdname: 'zinit'
source: '/tmp/builder/github/threefoldtech/zinit/target/x86_64-unknown-linux-musl/release/zinit'
)!
}
// get the Upload List of the files
fn ulist_get() !ulist.UList {
return ulist.UList{}
}
// uploads to S3 server if configured
fn upload() ! {
}
fn startupcmd() ![]ZProcessNewArgs {
mut res := []zinit.ZProcessNewArgs{}
res << ZProcessNewArgs{
name: 'zinit'
cmd: '/usr/local/bin/zinit init'
startuptype: .systemd
start: true
restart: true
}
return res
}
fn running() !bool {
cmd := 'zinit list'
return osal.execute_ok(cmd)
}
fn start_pre() ! {
}
fn start_post() ! {
}
fn stop_pre() ! {
}
fn stop_post() ! {
}
fn destroy() ! {
mut systemdfactory := systemd.new()!
systemdfactory.destroy('zinit')!
osal.process_kill_recursive(name: 'zinit')!
osal.cmd_delete('zinit')!
}

View File

@@ -0,0 +1,153 @@
module zinit
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.osal.zinit
import freeflowuniverse.herolib.ui.console
import time
__global (
zinit_global map[string]&Zinit
zinit_default string
)
/////////FACTORY
@[params]
pub struct ArgsGet {
pub mut:
name string
}
pub fn get(args_ ArgsGet) !&Zinit {
return &Zinit{}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
fn startupmanager_get(cat StartupManagerType) !startupmanager.StartupManager {
// unknown
// screen
// zinit
// tmux
// systemd
match cat {
.zinit {
console.print_debug('startupmanager: zinit')
return startupmanager.get(cat: .zinit)!
}
.systemd {
console.print_debug('startupmanager: systemd')
return startupmanager.get(cat: .systemd)!
}
else {
console.print_debug('startupmanager: auto')
return startupmanager.get()!
}
}
}
pub fn (mut self Zinit) start() ! {
switch(self.name)
if self.running()! {
return
}
console.print_header('zinit start')
if !installed()! {
install()!
}
configure()!
start_pre()!
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
console.print_debug('starting zinit with ${zprocess.startuptype}...')
sm.new(zprocess)!
sm.start(zprocess.name)!
}
start_post()!
for _ in 0 .. 50 {
if self.running()! {
return
}
time.sleep(100 * time.millisecond)
}
return error('zinit did not install properly.')
}
pub fn (mut self Zinit) install_start(args InstallArgs) ! {
switch(self.name)
self.install(args)!
self.start()!
}
pub fn (mut self Zinit) stop() ! {
switch(self.name)
stop_pre()!
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
sm.stop(zprocess.name)!
}
stop_post()!
}
pub fn (mut self Zinit) restart() ! {
switch(self.name)
self.stop()!
self.start()!
}
pub fn (mut self Zinit) running() !bool {
switch(self.name)
// walk over the generic processes, if not running return
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
r := sm.running(zprocess.name)!
if r == false {
return false
}
}
return running()!
}
@[params]
pub struct InstallArgs {
pub mut:
reset bool
}
pub fn (mut self Zinit) install(args InstallArgs) ! {
switch(self.name)
if args.reset || (!installed()!) {
install()!
}
}
pub fn (mut self Zinit) build() ! {
switch(self.name)
build()!
}
pub fn (mut self Zinit) destroy() ! {
switch(self.name)
self.stop() or {}
destroy()!
}
// switch instance to be used for zinit
pub fn switch(name string) {
zinit_default = name
}

View File

@@ -0,0 +1,26 @@
module zinit
import freeflowuniverse.herolib.data.paramsparser
import os
pub const version = '0.2.14'
const singleton = true
const default = true
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
pub struct Zinit {
pub mut:
name string = 'default'
}
fn obj_init(obj_ Zinit) !Zinit {
// never call get here, only thing we can do here is work on object itself
mut obj := obj_
return obj
}
// called before start if done
fn configure() ! {
// mut installer := get()!
}

View File

@@ -16,8 +16,8 @@ import freeflowuniverse.herolib.installers.lang.nodejs
import freeflowuniverse.herolib.installers.lang.python
import freeflowuniverse.herolib.installers.web.zola
import freeflowuniverse.herolib.installers.web.tailwind
import freeflowuniverse.herolib.installers.hero.heroweb
import freeflowuniverse.herolib.installers.hero.herodev
// import freeflowuniverse.herolib.installers.hero.heroweb
// import freeflowuniverse.herolib.installers.hero.herodev
import freeflowuniverse.herolib.installers.sysadmintools.daguserver
import freeflowuniverse.herolib.installers.sysadmintools.rclone
import freeflowuniverse.herolib.installers.sysadmintools.prometheus
@@ -51,7 +51,7 @@ pub fn names(args_ InstallArgs) []string {
grafana
hero
herodev
heroweb
// heroweb
lima
mycelium
nodejs
@@ -108,9 +108,9 @@ pub fn install_multi(args_ InstallArgs) ! {
git_reset: args.gitreset
)!
}
'hero' {
herolib.hero_install(reset: args.reset)!
}
// 'hero' {
// herolib.hero_install(reset: args.reset)!
// }
'caddy' {
// caddy.install(reset: args.reset)!
// caddy.configure_examples()!
@@ -123,30 +123,30 @@ pub fn install_multi(args_ InstallArgs) ! {
mycelium.start()!
}
'garage_s3' {
garage_s3.install(reset: args.reset, config_reset: args.reset, restart: true)!
mut garages3 := garage_s3.get()!
garages3.install(reset: args.reset)!
}
'fungistor' {
fungistor.install(reset: args.reset)!
}
'lima' {
lima.install(reset: args.reset, uninstall: args.uninstall)!
}
'herocontainers' {
mut podman_installer0 := podman_installer.get()!
mut buildah_installer0 := buildah_installer.get()!
if args.reset {
podman_installer0.destroy()! // will remove all
buildah_installer0.destroy()! // will remove all
}
podman_installer0.install()!
buildah_installer0.install()!
lima.install_(reset: args.reset, uninstall: args.uninstall)!
}
// 'herocontainers' {
// mut podman_installer0 := podman_installer.get()!
// mut buildah_installer0 := buildah_installer.get()!
// if args.reset {
// podman_installer0.destroy()! // will remove all
// buildah_installer0.destroy()! // will remove all
// }
// podman_installer0.install()!
// buildah_installer0.install()!
// }
'prometheus' {
prometheus.install(reset: args.reset, uninstall: args.uninstall)!
prometheus.install(reset: args.reset)!
}
'grafana' {
grafana.install(reset: args.reset, uninstall: args.uninstall)!
grafana.install(reset: args.reset)!
}
'vscode' {
vscode.install(reset: args.reset)!
@@ -157,11 +157,11 @@ pub fn install_multi(args_ InstallArgs) ! {
'python' {
python.install()!
}
'herodev' {
herodev.install()!
}
// 'herodev' {
// herodev.install()!
// }
// 'heroweb' {
// heroweb.install()!
// heroweb.install()!
// }
'dagu' {
// will call the installer underneith

View File

@@ -0,0 +1,129 @@
module herolib
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.installers.base
import freeflowuniverse.herolib.installers.lang.vlang
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.develop.gittools
import os
// install herolib will return true if it was already installed
@[params]
pub struct InstallArgs {
pub mut:
git_pull bool
git_reset bool
reset bool // means reinstall
}
pub fn install(args InstallArgs) ! {
// install herolib if it was already done will return true
console.print_header('install herolib (reset: ${args.reset})')
// osal.package_refresh()!
if args.reset {
osal.done_reset()!
}
base.install(develop: true)!
vlang.install(reset: args.reset)!
vlang.v_analyzer_install(reset: args.reset)!
mut gs := gittools.get()!
gs.config.light = true // means we clone depth 1
mut repo := gs.get_repo(
pull: args.git_pull
reset: args.git_reset
url: 'https://github.com/freeflowuniverse/herolib/tree/development/lib'
)!
// mut repo2 := gs.get_repo(
// pull: args.git_pull
// reset: args.git_reset
// url: 'https://github.com/freeflowuniverse/webcomponents/tree/main/webcomponents'
// )!
mut path1 := repo.get_path()!
// mut path2 := repo2.get_path()!
mut path1p := pathlib.get_dir(path: path1, create: false)!
// mut path2p := pathlib.get_dir(path: path2, create: false)!
path1p.link('${os.home_dir()}/.vmodules/freeflowuniverse/herolib', true)!
// path2p.link('${os.home_dir()}/.vmodules/freeflowuniverse/webcomponents', true)!
// hero_compile()!
osal.done_set('install_herolib', 'OK')!
return
}
// check if herolib installed and hero, if not do so
pub fn check() ! {
if osal.done_exists('install_herolib') {
return
}
install()!
}
// remove hero, crystal, ...
pub fn uninstall() ! {
console.print_debug('uninstall hero & herolib')
cmd := '
rm -rf ${os.home_dir()}/hero
rm -rf ${os.home_dir()}/_code
rm -f /usr/local/bin/hero
rm -f /tmp/hero
rm -f /tmp/install*
rm -f /tmp/build_hero*
rm -rf /tmp/execscripts
'
osal.execute_stdout(cmd) or { return error('Cannot uninstall herolib/hero.\n${err}') }
}
pub fn hero_install(args InstallArgs) ! {
if args.reset == false && osal.done_exists('install_hero') {
console.print_debug('hero already installed')
return
}
console.print_header('install hero')
base.install()!
cmd := "
cd /tmp
export TERM=xterm
curl 'https://raw.githubusercontent.com/freeflowuniverse/herolib/refs/heads/main/install_v.sh' > /tmp/install_v.sh
bash /tmp/install_v.sh --analyzer --herolib
"
osal.execute_stdout(cmd) or { return error('Cannot install hero.\n${err}') }
osal.done_set('install_hero', 'OK')!
return
}
pub fn hero_compile(args InstallArgs) ! {
if args.reset == false && osal.done_exists('compile_hero') {
console.print_debug('hero already compiled')
return
}
console.print_header('compile hero')
home_dir := os.home_dir()
cmd_hero := texttools.template_replace($tmpl('templates/hero.sh'))
osal.exec(cmd: cmd_hero, stdout: false)!
osal.execute_stdout(cmd_hero) or { return error('Cannot compile hero.\n${err}') }
osal.done_set('compile_hero', 'OK')!
return
}
// pub fn update() ! {
// console.print_header('package_install update herolib')
// if !(i.state == .reset) && osal.done_exists('install_crystaltools') {
// console.print_debug(' package_install was already done')
// return
// }
// osal.execute_silent('cd /tmp && export TERM=xterm && source /root/env.sh && ct_upgrade') or {
// return error('Cannot update crystal tools.\n${err}')
// }
// osal.done_set('update_crystaltools', 'OK')!
// }

View File

@@ -0,0 +1,21 @@
export PATH=${home_dir}/hero/bin:??PATH
export TERM=xterm
cd ${home_dir}/code/github/freeflowuniverse/crystallib/cli/hero
PRF="${home_dir}/.profile"
[ -f "??PRF" ] && source "??PRF"
if [[ "??OSTYPE" == "linux-gnu"* ]]; then
#v -enable-globals -w -cflags -static -cc gcc hero.v
v -enable-globals -w -n hero.v
export HEROPATH='/usr/local/bin/hero'
elif [[ "??OSTYPE" == "darwin"* ]]; then
v -enable-globals -w -n hero.v
export HEROPATH=${home_dir}/hero/bin/hero
fi
chmod +x hero
cp hero ??HEROPATH
rm hero

View File

@@ -0,0 +1 @@
https://github.com/v-analyzer/v-analyzer

View File

@@ -0,0 +1,98 @@
module vlang
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import os
// import freeflowuniverse.herolib.sysadmin.downloader
pub fn v_analyzer_install(args_ InstallArgs) ! {
mut args := args_
console.print_header('install v-analyzer (reset: ${args.reset})')
version := '0.0.4'
_ := core.platform()!
res := os.execute('${osal.profile_path_source_and()!} v-analyzer version')
if res.exit_code == 0 {
r := res.output.split_into_lines().filter(it.trim_space().starts_with('v-analyzer'))
if r.len != 1 {
return error("couldn't parse v-analyzer version.\n${res.output}")
}
mut myversion := r[0].all_after_first('version').trim_space()
if texttools.version(version) > texttools.version(myversion) {
args.reset = true
}
} else {
args.reset = true
}
if args.reset == false {
console.print_debug('v-analyzer already installed')
return
}
install()!
if args.reset {
console.print_header('install v-analyzer')
cmd := '
export TERM=xterm
mkdir -p ${os.home_dir()}/_code
cd ${os.home_dir()}/_code
rm -rf ${os.home_dir()}/_code/v-analyzer
git clone --filter=blob:none --recursive --shallow-submodules https://github.com/vlang/v-analyzer
cd v-analyzer
v build.vsh debug
'
osal.execute_stdout(cmd) or { return error('Cannot install hero.\n${err}') }
osal.cmd_add(
cmdname: 'v-analyzer'
source: '${os.home_dir()}/_code/v-analyzer/bin/v-analyzer'
)!
}
// if pl == .ubuntu {
// }else{
// mut url := ''
// if osal.is_linux_intel() {
// url = 'https://github.com/vlang/v-analyzer/releases/download/nightly/v-analyzer-linux-x86_64.zip'
// } else if osal.is_osx_arm() {
// url = 'https://github.com/vlang/v-analyzer/releases/download/nightly/v-analyzer-darwin-arm64.zip'
// } else if osal.is_osx_intel() {
// url = 'https://github.com/vlang/v-analyzer/releases/download/nightly/v-analyzer-darwin-x86_64.zip'
// } else {
// return error('unsported platform for installing v-analyzer')
// }
// mut dest := osal.download(
// url: url
// minsize_kb: 1000
// expand_dir: '/tmp/v-analyzer'
// )!
// mut binpath := dest.file_get('v-analyzer')!
// osal.cmd_add(
// cmdname: 'v-analyzer'
// source: binpath.path
// )!
// }
// if args.reset == false && osal.done_exists('install_v_analyzer') {
// console.print_debug(' v analyzer already installed')
// return
// }
// console.print_header('install v analyzer')
// cmd := '
// cd /tmp
// export TERM=xterm
// source ~/.profile
// rm -f install.sh
// curl -fksSL https://raw.githubusercontent.com/v-lang/v-analyzer/master/install.vsh > install.vsh
// v run install.vsh --no-interaction
// '
// osal.execute_stdout(cmd) or { return error('Cannot install hero.\n${err}') }
osal.done_set('install_v_analyzer', 'OK')!
return
}

View File

@@ -0,0 +1,73 @@
module vlang
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.ui.console
import os
import freeflowuniverse.herolib.installers.base
import freeflowuniverse.herolib.develop.gittools
// import freeflowuniverse.herolib.sysadmin.downloader
pub fn install(args_ InstallArgs) ! {
mut args := args_
version := '0.4.8'
console.print_header('install vlang (reset: ${args.reset})')
res := os.execute('${osal.profile_path_source_and()!} v --version')
if res.exit_code == 0 {
r := res.output.split_into_lines().filter(it.trim_space().starts_with('V'))
if r.len != 1 {
return error("couldn't parse v version.\n${res.output}")
}
myversion := r[0].all_after_first('V ').all_before(' ').trim_space()
console.print_debug("V version: '${myversion}'")
if texttools.version(version) > texttools.version(myversion) {
// println(texttools.version(version))
// println(texttools.version(myversion))
// if true{panic("s")}
args.reset = true
}
} else {
args.reset = true
}
// install vlang if it was already done will return true
if args.reset == false {
return
}
base.develop()!
mut gs := gittools.get(coderoot: '${os.home_dir()}/_code')!
mut repo := gs.get_repo(
pull: true
reset: true
url: 'https://github.com/vlang/v/tree/master'
)!
mut path1 := repo.get_path()!
mut extra := ''
if core.is_linux()! {
extra = './v symlink'
} else {
extra = 'cp v ${os.home_dir()}/bin/'
}
cmd := '
cd ${path1}
make
${extra}
'
console.print_header('compile')
osal.exec(cmd: cmd, stdout: true)!
console.print_header('compile done')
osal.done_set('install_vlang', 'OK')!
return
}
@[params]
pub struct InstallArgs {
pub mut:
reset bool
}

View File

@@ -10,7 +10,7 @@ import os
import time
// install yggdrasil will return true if it was already installed
pub fn installll(args_ InstallArgs) ! {
pub fn install(args_ InstallArgs) ! {
peers := '
Peers:
[

View File

@@ -5,7 +5,7 @@ import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import os
pub fn installlll(args_ InstallArgs) ! {
pub fn installl(args_ InstallArgs) ! {
mut args := args_
version := '0.2.10'

View File

@@ -5,7 +5,7 @@ import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.lang.python
// import os
pub fn installll(args_ InstallArgs) ! {
pub fn install(args_ InstallArgs) ! {
mut args := args_
if args.reset == false && osal.done_exists('install_b2') {

View File

@@ -0,0 +1,13 @@
!!hero_code.generate_installer
name:'daguserver'
classname:'DaguInstaller'
singleton:1
templates:1
default:1
title:''
supported_platforms:''
reset:0
startupmanager:1
hasconfig:1
build:0

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,152 @@
module daguserver
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.core.httpconnection
// import freeflowuniverse.herolib.develop.gittools
import freeflowuniverse.herolib.osal.zinit
import freeflowuniverse.herolib.crypt.secrets
import os
// checks if a certain version or above is installed
fn installed() !bool {
res := os.execute('${osal.profile_path_source_and()!} dagu version')
if res.exit_code == 0 {
r := res.output.split_into_lines().filter(it.trim_space().len > 0)
if r.len != 1 {
return error("couldn't parse dagu version.\n${res.output}")
}
if texttools.version(version) > texttools.version(r[0]) {
return false
}
} else {
return false
}
return true
}
fn install() ! {
console.print_header('install daguserver')
mut url := ''
if core.is_linux_arm()! {
url = 'https://github.com/dagu-dev/dagu/releases/download/v${version}/dagu_${version}_linux_arm64.tar.gz'
} else if core.is_linux_intel()! {
url = 'https://github.com/dagu-dev/dagu/releases/download/v${version}/dagu_${version}_linux_amd64.tar.gz'
} else if core.is_osx_arm()! {
url = 'https://github.com/dagu-dev/dagu/releases/download/v${version}/dagu_${version}_darwin_arm64.tar.gz'
} else if core.is_osx_intel()! {
url = 'https://github.com/dagu-dev/dagu/releases/download/v${version}/dagu_${version}_darwin_amd64.tar.gz'
} else {
return error('unsported platform')
}
mut dest := osal.download(
url: url
minsize_kb: 9000
expand_dir: '/tmp/dagu'
)!
mut binpath := dest.file_get('dagu')!
osal.cmd_add(
cmdname: 'dagu'
source: binpath.path
)!
}
fn startupcmd() ![]zinit.ZProcessNewArgs {
mut res := []zinit.ZProcessNewArgs{}
mut cfg := get()!
res << zinit.ZProcessNewArgs{
name: 'dagu'
cmd: 'dagu server'
env: {
'HOME ': os.home_dir()
'DAGU_HOME ': cfg.configpath // config for dagu is called admin.yml and is in this dir
}
}
res << zinit.ZProcessNewArgs{
name: 'dagu_scheduler'
cmd: 'dagu scheduler'
env: {
'HOME ': os.home_dir()
'DAGU_HOME ': cfg.configpath
}
}
return res
}
// user needs to us switch to make sure we get the right object
fn configure() ! {
mut cfg := get()!
if cfg.password == '' {
cfg.password = secrets.hex_secret()!
}
// TODO:use DAGU_SECRET from env variables in os if not set then empty string
if cfg.secret == '' {
cfg.secret = secrets.openssl_hex_secret(input: cfg.password)!
}
mut mycode := $tmpl('templates/dagu.yaml')
mut path := pathlib.get_file(path: '${cfg.configpath}/admin.yaml', create: true)!
path.write(mycode)!
console.print_debug(mycode)
}
fn running() !bool {
mut cfg := get()!
// this checks health of dagu
// curl http://localhost:3333/api/v1/s --oauth2-bearer 1234 works
url := 'http://127.0.0.1:${cfg.port}/api/v1'
mut conn := httpconnection.new(name: 'dagu', url: url)!
if cfg.secret.len > 0 {
conn.default_header.add(.authorization, 'Bearer ${cfg.secret}')
}
conn.default_header.add(.content_type, 'application/json')
console.print_debug("curl -X 'GET' '${url}'/tags --oauth2-bearer ${cfg.secret}")
r := conn.get_json_dict(prefix: 'tags', debug: false) or { return false }
println(r)
// if true{panic("ssss")}
tags := r['Tags'] or { return false }
console.print_debug(tags)
console.print_debug('Dagu is answering.')
return true
}
fn destroy() ! {
cmd := '
systemctl disable daguserver_scheduler.service
systemctl disable daguserver.service
systemctl stop daguserver_scheduler.service
systemctl stop daguserver.service
systemctl list-unit-files | grep daguserver
pkill -9 -f daguserver
ps aux | grep daguserver
'
osal.execute_silent(cmd) or {}
}
fn start_pre() ! {
}
fn start_post() ! {
}
fn stop_pre() ! {
}
fn stop_post() ! {
}

View File

@@ -0,0 +1,266 @@
module daguserver
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.osal.zinit
import freeflowuniverse.herolib.ui.console
import time
__global (
daguserver_global map[string]&DaguInstaller
daguserver_default string
)
/////////FACTORY
@[params]
pub struct ArgsGet {
pub mut:
name string
}
fn args_get(args_ ArgsGet) ArgsGet {
mut args := args_
if args.name == '' {
args.name = daguserver_default
}
if args.name == '' {
args.name = 'default'
}
return args
}
pub fn get(args_ ArgsGet) !&DaguInstaller {
mut args := args_get(args_)
if args.name !in daguserver_global {
if args.name == 'default' {
if !config_exists(args) {
if default {
config_save(args)!
}
}
config_load(args)!
}
}
return daguserver_global[args.name] or {
println(daguserver_global)
panic('could not get config for daguserver with name:${args.name}')
}
}
fn config_exists(args_ ArgsGet) bool {
mut args := args_get(args_)
mut context := base.context() or { panic('bug') }
return context.hero_config_exists('daguserver', args.name)
}
fn config_load(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
mut heroscript := context.hero_config_get('daguserver', args.name)!
play(heroscript: heroscript)!
}
fn config_save(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
context.hero_config_set('daguserver', args.name, heroscript_default()!)!
}
fn set(o DaguInstaller) ! {
mut o2 := obj_init(o)!
daguserver_global[o.name] = &o2
daguserver_default = o.name
}
@[params]
pub struct PlayArgs {
pub mut:
heroscript string // if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
}
pub fn play(args_ PlayArgs) ! {
mut args := args_
if args.heroscript == '' {
args.heroscript = heroscript_default()!
}
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
mut install_actions := plbook.find(filter: 'daguserver.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
mut p := install_action.params
mycfg := cfg_play(p)!
console.print_debug('install action daguserver.configure\n${mycfg}')
set(mycfg)!
}
}
mut other_actions := plbook.find(filter: 'daguserver.')!
for other_action in other_actions {
if other_action.name in ['destroy', 'install', 'build'] {
mut p := other_action.params
reset := p.get_default_false('reset')
if other_action.name == 'destroy' || reset {
console.print_debug('install action daguserver.destroy')
destroy()!
}
if other_action.name == 'install' {
console.print_debug('install action daguserver.install')
install()!
}
}
if other_action.name in ['start', 'stop', 'restart'] {
mut p := other_action.params
name := p.get('name')!
mut daguserver_obj := get(name: name)!
console.print_debug('action object:\n${daguserver_obj}')
if other_action.name == 'start' {
console.print_debug('install action daguserver.${other_action.name}')
daguserver_obj.start()!
}
if other_action.name == 'stop' {
console.print_debug('install action daguserver.${other_action.name}')
daguserver_obj.stop()!
}
if other_action.name == 'restart' {
console.print_debug('install action daguserver.${other_action.name}')
daguserver_obj.restart()!
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager {
// unknown
// screen
// zinit
// tmux
// systemd
match cat {
.zinit {
console.print_debug('startupmanager: zinit')
return startupmanager.get(cat: .zinit)!
}
.systemd {
console.print_debug('startupmanager: systemd')
return startupmanager.get(cat: .systemd)!
}
else {
console.print_debug('startupmanager: auto')
return startupmanager.get()!
}
}
}
// load from disk and make sure is properly intialized
pub fn (mut self DaguInstaller) reload() ! {
switch(self.name)
self = obj_init(self)!
}
pub fn (mut self DaguInstaller) start() ! {
switch(self.name)
if self.running()! {
return
}
console.print_header('daguserver start')
if !installed()! {
install()!
}
configure()!
start_pre()!
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
console.print_debug('starting daguserver with ${zprocess.startuptype}...')
sm.new(zprocess)!
sm.start(zprocess.name)!
}
start_post()!
for _ in 0 .. 50 {
if self.running()! {
return
}
time.sleep(100 * time.millisecond)
}
return error('daguserver did not install properly.')
}
pub fn (mut self DaguInstaller) install_start(args InstallArgs) ! {
switch(self.name)
self.install(args)!
self.start()!
}
pub fn (mut self DaguInstaller) stop() ! {
switch(self.name)
stop_pre()!
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
sm.stop(zprocess.name)!
}
stop_post()!
}
pub fn (mut self DaguInstaller) restart() ! {
switch(self.name)
self.stop()!
self.start()!
}
pub fn (mut self DaguInstaller) running() !bool {
switch(self.name)
// walk over the generic processes, if not running return
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
r := sm.running(zprocess.name)!
if r == false {
return false
}
}
return running()!
}
@[params]
pub struct InstallArgs {
pub mut:
reset bool
}
pub fn (mut self DaguInstaller) install(args InstallArgs) ! {
switch(self.name)
if args.reset || (!installed()!) {
install()!
}
}
pub fn (mut self DaguInstaller) destroy() ! {
switch(self.name)
self.stop() or {}
destroy()!
}
// switch instance to be used for daguserver
pub fn switch(name string) {
daguserver_default = name
}

View File

@@ -0,0 +1,60 @@
module daguserver
import freeflowuniverse.herolib.data.paramsparser
import os
pub const version = '1.14.3'
const singleton = true
const default = true
pub fn heroscript_default() !string {
heroscript := "
!!daguserver.configure
name:'daguserver'
title: 'My Hero DAG'
host: 'localhost'
port: 8888
"
return heroscript
}
pub struct DaguInstaller {
pub mut:
name string = 'default'
dagsdir string
configpath string
username string
password string @[secret]
secret string @[secret]
title string
host string
port int
}
fn cfg_play(p paramsparser.Params) !DaguInstaller {
// THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above
mut mycfg := DaguInstaller{
name: p.get_default('name', 'default')!
dagsdir: p.get_default('homedir', '${os.home_dir()}/hero/var/daguserver')!
configpath: p.get_default('configpath', '${os.home_dir()}/hero/cfg/dagu')!
username: p.get_default('username', 'admin')!
password: p.get_default('password', 'secretpassword')!
secret: p.get_default('secret', '')!
title: p.get_default('title', 'HERO DAG')!
host: p.get_default('host', 'localhost')!
port: p.get_int_default('port', 8888)!
}
if mycfg.password == '' && mycfg.secret == '' {
return error('password or secret needs to be filled in for daguserver')
}
return mycfg
}
fn obj_init(obj_ DaguInstaller) !DaguInstaller {
// never call get here, only thing we can do here is work on object itself
mut obj := obj_
return obj
}

View File

@@ -0,0 +1,48 @@
module daguserver
import os
@[params]
pub struct DaguCommunicationConfig {
pub:
log_dir string // directory path to save logs from standard output
history_retention_days int // history retention days (default: 30)
mail_on MailOn // Email notification settings
smtp SMTP // SMTP server settings
error_mail Mail // Error mail configuration
info_mail Mail // Info mail configuration
}
pub struct SMTP {
pub:
host string
port string
username string
password string
error_mail Mail
}
pub struct Mail {
pub:
from string
to string
prefix string
}
pub struct MailOn {
pub:
failure bool
success bool
}
pub fn (mut self DaguInstaller) comms_configure(config DaguCommunicationConfig) ! {
// mut homedir := self.config()!.homedir
// config_yaml := $tmpl('./templates/communication.yaml')
// os.write_file('${homedir}/communication.yaml', config_yaml)!
// dags_dir := '${homedir}/dags'
// if !os.exists(dags_dir) {
// os.mkdir(dags_dir)!
// }
}

View File

@@ -0,0 +1,36 @@
# daguserver
To get started
```vlang
import freeflowuniverse.herolib.installers.something. daguserver
mut installer:= daguserver.get()!
installer.start()!
```
## example heroscript
```hero
!!daguserver.install
homedir: '/home/user/daguserver'
username: 'admin'
password: 'secretpassword'
title: 'Some Title'
host: 'localhost'
port: 8888
```

View File

@@ -0,0 +1,29 @@
# directory path to save logs from standard output
logDir: @{config.log_dir}
# history retention days (default: 30)
histRetentionDays: @{config.history_retention_days}
# Email notification settings
mailOn:
failure: @{config.mail_on.failure}
success: @{config.mail_on.success}
# SMTP server settings
smtp:
host: @{config.smtp.host}
port: @{config.smtp.port}
username: @{config.smtp.username}
password: @{config.smtp.password}
# Error mail configuration
errorMail:
from: @{config.error_mail.from}
to: @{config.error_mail.to}
prefix: @{config.error_mail.prefix}
# Info mail configuration
infoMail:
from: @{config.info_mail.from}
to: @{config.info_mail.to}
prefix: @{config.info_mail.prefix}

View File

@@ -0,0 +1,27 @@
host: "${cfg.host}" # default: 127.0.0.1
port: ${cfg.port}
# path to the DAGs directory
dags: ${cfg.dagsdir}
# Web UI Color & Title
# navbarColor: <ui header color> # header color for web UI (e.g. "#ff0000")
navbarTitle: ${cfg.title} # header title for web UI (e.g. "PROD")
isBasicAuth: true
basicAuthUsername: ${cfg.username}
basicAuthPassword: ${cfg.password}
isAuthToken: true # enables API token
authToken: ${cfg.secret}
# Base Config
# baseConfig:
# Working Directory
# workDir: # default: DAG location
# SSL Configuration
# tls:
# certFile: <path to SSL certificate file>
# keyFile: <path to SSL key file>

View File

@@ -5,7 +5,7 @@ import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import os
pub fn installlll(args_ InstallArgs) ! {
pub fn installl(args_ InstallArgs) ! {
mut args := args_
version := '2.0.6'

View File

@@ -1,11 +1,13 @@
!!hero_code.generate_installer
name: "garage_s3"
classname: "GarageS3"
hasconfig: false
singleton: true
default: true
title: ""
templates: false
build: true
startupmanager: true
!!hero_code.generate_installer
name:'garage_s3'
classname:'GarageS3'
singleton:0
templates:1
default:1
title:''
supported_platforms:''
reset:0
startupmanager:1
hasconfig:1
build:0

View File

@@ -0,0 +1,56 @@
// module garage_s3
// import freeflowuniverse.herolib.osal
// import freeflowuniverse.herolib.core
// import freeflowuniverse.herolib.ui.console
// import freeflowuniverse.herolib.core.texttools
// import os
// pub fn install(args_ GarageS3) ! {
// mut args := args_
// version := '1.0.0'
// res := os.execute('garage --version')
// if res.exit_code == 0 {
// r := res.output.split(' ')
// if r.len < 2 {
// return error("couldn't parse garage version, expected 'garage v*'.\n${res.output}")
// }
// v := r[1]
// if texttools.version(v) < texttools.version(version) {
// args.reset = true
// }
// } else {
// args.reset = true
// }
// if args.reset {
// console.print_header('install garage')
// mut url := ''
// if core.is_linux_arm()! {
// url = 'https://garagehq.deuxfleurs.fr/_releases/v${version}/aarch64-unknown-linux-musl/garage'
// } else if core.is_linux_intel()! {
// url = 'https://garagehq.deuxfleurs.fr/_releases/v${version}/x86_64-unknown-linux-musl/garage'
// } else {
// return error('unsported platform')
// }
// mut dest := osal.download(
// url: url
// minsize_kb: 15 * 1024
// dest: '/tmp/garage'
// reset: true
// )!
// console.print_debug('download garage done')
// osal.cmd_add(
// cmdname: 'garage'
// source: '${dest.path}'
// )!
// }
// if args.start {
// start(args)!
// }
// }

View File

@@ -1,121 +1,121 @@
module garage_s3
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.crypt.secrets
// import freeflowuniverse.herolib.core.texttools
// import freeflowuniverse.herolib.core.httpconnection
import os
import time
// import freeflowuniverse.herolib.ui.console
// import freeflowuniverse.herolib.core.pathlib
// import freeflowuniverse.herolib.sysadmin.startupmanager
// import freeflowuniverse.herolib.crypt.secrets
// // import freeflowuniverse.herolib.core.texttools
// // import freeflowuniverse.herolib.core.httpconnection
// import os
// import time
@[params]
pub struct S3Config {
pub mut:
replication_mode string = '3'
metadata_dir string = '/var/garage/meta'
data_dir string = '/var/garage/data'
sled_cache_capacity u32 = 128 // in MB
compression_level u8 = 1
// @[params]
// pub struct S3Config {
// pub mut:
// replication_mode string = '3'
// metadata_dir string = '/var/garage/meta'
// data_dir string = '/var/garage/data'
// sled_cache_capacity u32 = 128 // in MB
// compression_level u8 = 1
rpc_secret string //{GARAGE_RPCSECRET}
rpc_bind_addr string = '[::]:3901'
rpc_bind_outgoing bool
rpc_public_addr string = '127.0.0.1:3901'
// rpc_secret string //{GARAGE_RPCSECRET}
// rpc_bind_addr string = '[::]:3901'
// rpc_bind_outgoing bool
// rpc_public_addr string = '127.0.0.1:3901'
bootstrap_peers []string
// bootstrap_peers []string
api_bind_addr string = '[::]:3900'
s3_region string = 'garage'
root_domain string = '.s3.garage'
// api_bind_addr string = '[::]:3900'
// s3_region string = 'garage'
// root_domain string = '.s3.garage'
web_bind_addr string = '[::]:3902'
web_root_domain string = '.web.garage'
// web_bind_addr string = '[::]:3902'
// web_root_domain string = '.web.garage'
admin_api_bind_addr string = '[::]:3903'
admin_metrics_token string //{GARAGE_METRICSTOKEN}
admin_token string //{GARAGE_ADMINTOKEN}
admin_trace_sink string = 'http://localhost:4317'
// admin_api_bind_addr string = '[::]:3903'
// admin_metrics_token string //{GARAGE_METRICSTOKEN}
// admin_token string //{GARAGE_ADMINTOKEN}
// admin_trace_sink string = 'http://localhost:4317'
reset bool
config_reset bool
start bool = true
restart bool = true
}
// reset bool
// config_reset bool
// start bool = true
// restart bool = true
// }
pub fn configure(args_ S3Config) !S3Config {
mut args := args_
// pub fn configure(args_ S3Config) !S3Config {
// mut args := args_
if args.rpc_secret == '' {
args.rpc_secret = secrets.openssl_hex_secret()!
println('export GARAGE_RPCSECRET=${args.rpc_secret}')
}
// if args.rpc_secret == '' {
// args.rpc_secret = secrets.openssl_hex_secret()!
// println('export GARAGE_RPCSECRET=${args.rpc_secret}')
// }
if args.admin_metrics_token == '' {
args.admin_metrics_token = secrets.openssl_base64_secret()!
println('export GARAGE_METRICSTOKEN=${args.admin_metrics_token}')
}
// if args.admin_metrics_token == '' {
// args.admin_metrics_token = secrets.openssl_base64_secret()!
// println('export GARAGE_METRICSTOKEN=${args.admin_metrics_token}')
// }
if args.admin_token == '' {
args.admin_token = secrets.openssl_base64_secret()!
println('export GARAGE_ADMINTOKEN=${args.admin_token}')
}
// if args.admin_token == '' {
// args.admin_token = secrets.openssl_base64_secret()!
// println('export GARAGE_ADMINTOKEN=${args.admin_token}')
// }
mut config_file := $tmpl('templates/garage.toml')
// mut config_file := $tmpl('templates/garage.toml')
myconfigpath_ := '/etc/garage.toml'
mut myconfigpath := pathlib.get_file(path: myconfigpath_, create: true)!
myconfigpath.write(config_file)!
// myconfigpath_ := '/etc/garage.toml'
// mut myconfigpath := pathlib.get_file(path: myconfigpath_, create: true)!
// myconfigpath.write(config_file)!
console.print_header('garage start')
// console.print_header('garage start')
return args
}
// return args
// }
pub fn start(args_ S3Config) !S3Config {
mut args := args_
// pub fn start(args_ S3Config) !S3Config {
// mut args := args_
myconfigpath_ := '/etc/garage.toml'
// myconfigpath_ := '/etc/garage.toml'
if args.config_reset || !os.exists(myconfigpath_) {
args = configure(args)!
}
// if args.config_reset || !os.exists(myconfigpath_) {
// args = configure(args)!
// }
if args.restart {
stop()!
}
// if args.restart {
// stop()!
// }
mut sm := startupmanager.get()!
// mut sm := startupmanager.get()!
sm.new(
name: 'garage'
cmd: 'garage -c ${myconfigpath_} server'
start: true
)!
// sm.new(
// name: 'garage'
// cmd: 'garage -c ${myconfigpath_} server'
// start: true
// )!
console.print_debug('garage -c ${myconfigpath_} server')
// console.print_debug('garage -c ${myconfigpath_} server')
for _ in 0 .. 50 {
if check(args)! {
return args
}
time.sleep(100 * time.millisecond)
}
// for _ in 0 .. 50 {
// if check(args)! {
// return args
// }
// time.sleep(100 * time.millisecond)
// }
return error('garage server did not start properly.')
}
// return error('garage server did not start properly.')
// }
pub fn stop() ! {
console.print_header('garage stop')
mut sm := startupmanager.get()!
sm.stop('garage')!
}
// pub fn stop() ! {
// console.print_header('garage stop')
// mut sm := startupmanager.get()!
// sm.stop('garage')!
// }
fn check(args S3Config) !bool {
_ := 'garage status'
res := os.execute('garage status')
if res.exit_code == 0 {
return true
}
return false
}
// fn check(args S3Config) !bool {
// _ := 'garage status'
// res := os.execute('garage status')
// if res.exit_code == 0 {
// return true
// }
// return false
// }

View File

@@ -3,26 +3,73 @@ module garage_s3
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.osal.systemd
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.osal.zinit
import freeflowuniverse.herolib.installers.ulist
import freeflowuniverse.herolib.installers.lang.golang
import freeflowuniverse.herolib.installers.lang.rust
import freeflowuniverse.herolib.installers.lang.python
// import freeflowuniverse.herolib.osal.systemd
import os
fn startupcmd() ![]zinit.ZProcessNewArgs {
// checks if a certain version or above is installed
fn installed() !bool {
// THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
res := os.execute('${osal.profile_path_source_and()!} garage_s3 version')
if res.exit_code != 0 {
return false
}
r := res.output.split_into_lines().filter(it.trim_space().len > 0)
if r.len != 1 {
return error("couldn't parse garage_s3 version.\n${res.output}")
}
if texttools.version(version) > texttools.version(r[0]) {
return false
}
return true
}
fn install() ! {
console.print_header('install garage_s3')
mut installer := get()!
// THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
mut url := ''
if core.is_linux_arm()! {
url = 'https://github.com/garage_s3-dev/garage_s3/releases/download/v${version}/garage_s3_${version}_linux_arm64.tar.gz'
} else if core.is_linux_intel()! {
url = 'https://github.com/garage_s3-dev/garage_s3/releases/download/v${version}/garage_s3_${version}_linux_amd64.tar.gz'
} else if core.is_osx_arm()! {
url = 'https://github.com/garage_s3-dev/garage_s3/releases/download/v${version}/garage_s3_${version}_darwin_arm64.tar.gz'
} else if core.is_osx_intel()! {
url = 'https://github.com/garage_s3-dev/garage_s3/releases/download/v${version}/garage_s3_${version}_darwin_amd64.tar.gz'
} else {
return error('unsported platform')
}
mut dest := osal.download(
url: url
minsize_kb: 9000
expand_dir: '/tmp/garage_s3'
)!
// dest.moveup_single_subdir()!
mut binpath := dest.file_get('garage_s3')!
osal.cmd_add(
cmdname: 'garage_s3'
source: binpath.path
)!
}
fn startupcmd() ![]zinit.ZProcessNewArgs {
mut res := []zinit.ZProcessNewArgs{}
// THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
// res << zinit.ZProcessNewArgs{
// name: 'garage_s3'
// cmd: 'garage_s3 server'
// env: {
// 'HOME': '/root'
// }
// }
res << zinit.ZProcessNewArgs{
name: 'garage_s3'
cmd: 'garage_s3 server'
env: {
'HOME': '/root'
}
}
return res
}
@@ -61,99 +108,6 @@ fn stop_pre() ! {
fn stop_post() ! {
}
//////////////////// following actions are not specific to instance of the object
// checks if a certain version or above is installed
fn installed_() !bool {
// THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
// res := os.execute('${osal.profile_path_source_and()!} garage_s3 version')
// if res.exit_code != 0 {
// return false
// }
// r := res.output.split_into_lines().filter(it.trim_space().len > 0)
// if r.len != 1 {
// return error("couldn't parse garage_s3 version.\n${res.output}")
// }
// if texttools.version(version) == texttools.version(r[0]) {
// return true
// }
return false
}
// get the Upload List of the files
fn ulist_get() !ulist.UList {
// optionally build a UList which is all paths which are result of building, is then used e.g. in upload
return ulist.UList{}
}
// uploads to S3 server if configured
fn upload_() ! {
// installers.upload(
// cmdname: 'garage_s3'
// source: '${gitpath}/target/x86_64-unknown-linux-musl/release/garage_s3'
// )!
}
fn install_() ! {
console.print_header('install garage_s3')
// THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
// mut url := ''
// if core.is_linux_arm()! {
// url = 'https://github.com/garage_s3-dev/garage_s3/releases/download/v${version}/garage_s3_${version}_linux_arm64.tar.gz'
// } else if core.is_linux_intel()! {
// url = 'https://github.com/garage_s3-dev/garage_s3/releases/download/v${version}/garage_s3_${version}_linux_amd64.tar.gz'
// } else if core.is_osx_arm()! {
// url = 'https://github.com/garage_s3-dev/garage_s3/releases/download/v${version}/garage_s3_${version}_darwin_arm64.tar.gz'
// } else if core.is_osx_intel()! {
// url = 'https://github.com/garage_s3-dev/garage_s3/releases/download/v${version}/garage_s3_${version}_darwin_amd64.tar.gz'
// } else {
// return error('unsported platform')
// }
// mut dest := osal.download(
// url: url
// minsize_kb: 9000
// expand_dir: '/tmp/garage_s3'
// )!
// //dest.moveup_single_subdir()!
// mut binpath := dest.file_get('garage_s3')!
// osal.cmd_add(
// cmdname: 'garage_s3'
// source: binpath.path
// )!
}
fn build_() ! {
// url := 'https://github.com/threefoldtech/garage_s3'
// make sure we install base on the node
// if core.platform()!= .ubuntu {
// return error('only support ubuntu for now')
// }
// golang.install()!
// console.print_header('build garage_s3')
// gitpath := gittools.get_repo(coderoot: '/tmp/builder', url: url, reset: true, pull: true)!
// cmd := '
// cd ${gitpath}
// source ~/.cargo/env
// exit 1 #todo
// '
// osal.execute_stdout(cmd)!
//
// //now copy to the default bin path
// mut binpath := dest.file_get('...')!
// adds it to path
// osal.cmd_add(
// cmdname: 'griddriver2'
// source: binpath.path
// )!
}
fn destroy_() ! {
// mut systemdfactory := systemd.new()!
// systemdfactory.destroy("zinit")!

View File

@@ -2,9 +2,9 @@ module garage_s3
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.osal.zinit
import freeflowuniverse.herolib.ui.console
import time
__global (
@@ -14,6 +14,96 @@ __global (
/////////FACTORY
@[params]
pub struct ArgsGet {
pub mut:
name string = 'default'
}
fn args_get(args_ ArgsGet) ArgsGet {
mut args := args_
if args.name == '' {
args.name = garage_s3_default
}
if args.name == '' {
args.name = 'default'
}
return args
}
pub fn get(args_ ArgsGet) !&GarageS3 {
mut args := args_get(args_)
if args.name !in garage_s3_global {
if !config_exists() {
if default {
config_save()!
}
}
config_load()!
}
return garage_s3_global[args.name] or {
println(garage_s3_global)
panic('bug in get from factory: ')
}
}
fn config_exists(args_ ArgsGet) bool {
mut args := args_get(args_)
mut context := base.context() or { panic('bug') }
return context.hero_config_exists('garage_s3', args.name)
}
fn config_load(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
mut heroscript := context.hero_config_get('garage_s3', args.name)!
play(heroscript: heroscript)!
}
fn config_save(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
context.hero_config_set('garage_s3', args.name, heroscript_default()!)!
}
fn set(o GarageS3) ! {
mut o2 := obj_init(o)!
garage_s3_global['default'] = &o2
}
@[params]
pub struct PlayArgs {
pub mut:
name string = 'default'
heroscript string // if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
start bool
stop bool
restart bool
delete bool
configure bool // make sure there is at least one installed
}
pub fn play(args_ PlayArgs) ! {
mut args := args_
if args.heroscript == '' {
args.heroscript = heroscript_default()!
}
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
mut install_actions := plbook.find(filter: 'garage_s3.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
mut p := install_action.params
mycfg := cfg_play(p)!
set(mycfg)!
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -40,6 +130,12 @@ fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManag
}
}
// load from disk and make sure is properly intialized
pub fn (mut self GarageS3) reload() ! {
switch(self.name)
self = obj_init(self)!
}
pub fn (mut self GarageS3) start() ! {
switch(self.name)
if self.running()! {
@@ -48,8 +144,8 @@ pub fn (mut self GarageS3) start() ! {
console.print_header('garage_s3 start')
if !installed_()! {
install_()!
if !installed()! {
install()!
}
configure()!
@@ -77,9 +173,9 @@ pub fn (mut self GarageS3) start() ! {
return error('garage_s3 did not install properly.')
}
pub fn (mut self GarageS3) install_start(model InstallArgs) ! {
pub fn (mut self GarageS3) install_start(args InstallArgs) ! {
switch(self.name)
self.install(model)!
self.install(args)!
self.start()!
}
@@ -110,7 +206,7 @@ pub fn (mut self GarageS3) running() !bool {
return false
}
}
return running()!
return running_()!
}
@[params]
@@ -119,19 +215,21 @@ pub mut:
reset bool
}
pub fn install(args InstallArgs) ! {
if args.reset {
destroy()!
}
if !(installed_()!) {
install_()!
pub fn (mut self GarageS3) install(args InstallArgs) ! {
switch(self.name)
if args.reset || (!installed()!) {
install()!
}
}
pub fn destroy() ! {
pub fn (mut self GarageS3) destroy() ! {
switch(self.name)
self.stop() or {}
destroy_()!
}
pub fn build() ! {
build_()!
// switch instance to be used for garage_s3
pub fn switch(name string) {
garage_s3_default = name
}

View File

@@ -3,25 +3,109 @@ module garage_s3
import freeflowuniverse.herolib.data.paramsparser
import os
pub const version = '0.0.0'
const singleton = true
pub const version = '1.14.3'
const singleton = false
const default = true
// TODO: THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE TO STRUCT BELOW, IS STRUCTURED AS HEROSCRIPT
pub fn heroscript_default() !string {
heroscript := "
!!garage_s3.configure
name:'garage_s3'
homedir: '{HOME}/hero/var/garage_s3'
configpath: '{HOME}/.config/garage_s3/admin.yaml'
username: 'admin'
password: 'secretpassword'
secret: ''
title: 'My Hero DAG'
host: 'localhost'
port: 8888
"
return heroscript
}
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
@[heap]
pub struct GarageS3 {
pub mut:
name string = 'default'
replication_mode string = '3'
metadata_dir string = '/var/garage/meta'
data_dir string = '/var/garage/data'
sled_cache_capacity u32 = 128 // in MB
compression_level u8 = 1
rpc_secret string //{GARAGE_RPCSECRET}
rpc_bind_addr string = '[::]:3901'
rpc_bind_outgoing bool
rpc_public_addr string = '127.0.0.1:3901'
bootstrap_peers []string
api_bind_addr string = '[::]:3900'
s3_region string = 'garage'
root_domain string = '.s3.garage'
web_bind_addr string = '[::]:3902'
web_root_domain string = '.web.garage'
admin_api_bind_addr string = '[::]:3903'
admin_metrics_token string //{GARAGE_METRICSTOKEN}
admin_token string //{GARAGE_ADMINTOKEN}
admin_trace_sink string = 'http://localhost:4317'
reset bool
config_reset bool
start bool = true
restart bool = true
}
fn cfg_play(p paramsparser.Params) !GarageS3 {
mut mycfg := GarageS3{
name: p.get_default('name', 'default')!
replication_mode: p.get_default('replication_mode', '3')!
metadata_dir: p.get_default('metadata_dir', '/var/garage/meta')!
data_dir: p.get_default('data_dir', '/var/garage/data')!
sled_cache_capacity: p.get_u32_default('sled_cache_capacity', 128)!
compression_level: p.get_u8_default('compression_level', 1)!
rpc_secret: p.get_default('rpc_secret', '')!
rpc_bind_addr: p.get_default('rpc_bind_addr', '[::]:3901')!
rpc_public_addr: p.get_default('rpc_public_addr', '127.0.0.1:3901')!
api_bind_addr: p.get_default('api_bind_addr', '[::]:3900')!
s3_region: p.get_default('s3_region', 'garage')!
root_domain: p.get_default('root_domain', '.s3.garage')!
web_bind_addr: p.get_default('web_bind_addr', '[::]:3902')!
web_root_domain: p.get_default('web_root_domain', '.web.garage')!
admin_api_bind_addr: p.get_default('admin_api_bind_addr', '[::]:3903')!
admin_metrics_token: p.get_default('admin_metrics_token', '')!
admin_token: p.get_default('admin_token', '')!
admin_trace_sink: p.get_default('admin_trace_sink', 'http://localhost:4317')!
bootstrap_peers: p.get_list_default('bootstrap_peers', [])!
rpc_bind_outgoing: p.get_default_false('rpc_bind_outgoing')
reset: p.get_default_false('reset')
config_reset: p.get_default_false('config_reset')
start: p.get_default_true('start')
restart: p.get_default_true('restart')
}
return mycfg
}
fn obj_init(obj_ GarageS3) !GarageS3 {
// never call get here, only thing we can do here is work on object itself
mut obj := obj_
panic('implement')
return obj
}
// called before start if done
fn configure() ! {
// mut installer := get()!
// mut mycode := $tmpl('templates/atemplate.yaml')
// mut path := pathlib.get_file(path: cfg.configpath, create: true)!
// path.write(mycode)!
// console.print_debug(mycode)
}

View File

@@ -1,55 +0,0 @@
module garage_s3
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import os
pub fn installll(args_ S3Config) ! {
mut args := args_
version := '1.0.0'
res := os.execute('garage --version')
if res.exit_code == 0 {
r := res.output.split(' ')
if r.len < 2 {
return error("couldn't parse garage version, expected 'garage v*'.\n${res.output}")
}
v := r[1]
if texttools.version(v) < texttools.version(version) {
args.reset = true
}
} else {
args.reset = true
}
if args.reset {
console.print_header('install garage')
mut url := ''
if core.is_linux_arm()! {
url = 'https://garagehq.deuxfleurs.fr/_releases/v${version}/aarch64-unknown-linux-musl/garage'
} else if core.is_linux_intel()! {
url = 'https://garagehq.deuxfleurs.fr/_releases/v${version}/x86_64-unknown-linux-musl/garage'
} else {
return error('unsported platform')
}
mut dest := osal.download(
url: url
minsize_kb: 15 * 1024
dest: '/tmp/garage'
reset: true
)!
console.print_debug('download garage done')
osal.cmd_add(
cmdname: 'garage'
source: '${dest.path}'
)!
}
if args.start {
start(args)!
}
}

View File

@@ -1,9 +1,36 @@
## garage S3 server
see [https://garagehq.deuxfleurs.fr](https://garagehq.deuxfleurs.fr)
# garage_s3
https://garagehq.deuxfleurs.fr/documentation/quick-start/
To get started
```vlang
import freeflowuniverse.herolib.installers.something. garage_s3
mut installer:= garage_s3.get()!
installer.start()!
```
## example heroscript
```hero
!!garage_s3.install
homedir: '/home/user/garage_s3'
username: 'admin'
password: 'secretpassword'
title: 'Some Title'
host: 'localhost'
port: 8888
```

View File

@@ -0,0 +1,5 @@
name: ${cfg.configpath}

View File

@@ -1,59 +0,0 @@
replication_mode = "${args.replication_mode}"
metadata_dir = "${args.metadata_dir}"
data_dir = "${args.data_dir}"
metadata_fsync = false
data_fsync = false
db_engine = "sqlite"
block_size = "1M"
sled_cache_capacity = "${args.sled_cache_capacity}MiB"
sled_flush_every_ms = 2000
lmdb_map_size = "1T"
compression_level = ${args.compression_level}
rpc_secret = "${args.rpc_secret}"
rpc_bind_addr = "${args.rpc_bind_addr}"
rpc_bind_outgoing = ${args.rpc_bind_outgoing}
rpc_public_addr = "${args.rpc_public_addr}"
bootstrap_peers = ${args.bootstrap_peers}
# [consul_discovery]
# api = "catalog"
# consul_http_addr = "http://127.0.0.1:8500"
# service_name = "garage-daemon"
# ca_cert = "/etc/consul/consul-ca.crt"
# client_cert = "/etc/consul/consul-client.crt"
# client_key = "/etc/consul/consul-key.crt"
# # for `agent` API mode, unset client_cert and client_key, and optionally enable `token`
# # token = "abcdef-01234-56789"
# tls_skip_verify = false
# tags = [ "dns-enabled" ]
# meta = { dns-acl = "allow trusted" }
# [kubernetes_discovery]
# namespace = "garage"
# service_name = "garage-daemon"
# skip_crd = false
[s3_api]
api_bind_addr = "${args.api_bind_addr}"
s3_region = "${args.s3_region}"
root_domain = "${args.root_domain}"
[s3_web]
bind_addr = "${args.web_bind_addr}"
root_domain = "${args.web_root_domain}"
[admin]
api_bind_addr = "${args.admin_api_bind_addr}"
metrics_token = "${args.admin_metrics_token}"
admin_token = "${args.admin_token}"
trace_sink = "${args.admin_trace_sink}"

View File

@@ -9,7 +9,7 @@ import freeflowuniverse.herolib.sysadmin.startupmanager
import os
import time
pub fn installll(args_ InstallArgs) ! {
pub fn install(args_ InstallArgs) ! {
mut args := args_
version := '11.1.4'

View File

@@ -1,9 +1,9 @@
module rclone
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.httpconnection
import os
// checks if a certain version or above is installed

View File

@@ -2,10 +2,9 @@ module rclone
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.data.encoderhero
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.osal.zinit
import freeflowuniverse.herolib.ui.console
import time
__global (
@@ -18,108 +17,86 @@ __global (
@[params]
pub struct ArgsGet {
pub mut:
name string
name string = 'default'
}
fn args_get(args_ ArgsGet) ArgsGet {
mut model := args_
if model.name == '' {
model.name = rclone_default
mut args := args_
if args.name == '' {
args.name = rclone_default
}
if model.name == '' {
model.name = 'default'
if args.name == '' {
args.name = 'default'
}
return model
return args
}
pub fn get(args_ ArgsGet) !&RClone {
mut args := args_get(args_)
if args.name !in rclone_global {
if args.name == 'default' {
if !config_exists(args) {
if default {
mut context := base.context() or { panic('bug') }
context.hero_config_set('rclone', model.name, heroscript_default()!)!
}
if !config_exists() {
if default {
config_save()!
}
load(args)!
}
config_load()!
}
return rclone_global[args.name] or {
println(rclone_global)
panic('could not get config for ${args.name} with name:${model.name}')
panic('bug in get from factory: ')
}
}
// set the model in mem and the config on the filesystem
pub fn set(o RClone) ! {
mut o2 := obj_init(o)!
rclone_global[o.name] = &o2
rclone_default = o.name
}
// check we find the config on the filesystem
pub fn exists(args_ ArgsGet) bool {
mut model := args_get(args_)
fn config_exists(args_ ArgsGet) bool {
mut args := args_get(args_)
mut context := base.context() or { panic('bug') }
return context.hero_config_exists('rclone', model.name)
return context.hero_config_exists('rclone', args.name)
}
// load the config error if it doesn't exist
pub fn load(args_ ArgsGet) ! {
mut model := args_get(args_)
fn config_load(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
mut heroscript := context.hero_config_get('rclone', model.name)!
mut heroscript := context.hero_config_get('rclone', args.name)!
play(heroscript: heroscript)!
}
// save the config to the filesystem in the context
pub fn save(o RClone) ! {
fn config_save(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
heroscript := encoderhero.encode[RClone](o)!
context.hero_config_set('rclone', model.name, heroscript)!
context.hero_config_set('rclone', args.name, heroscript_default()!)!
}
fn set(o RClone) ! {
mut o2 := obj_init(o)!
rclone_global['default'] = &o2
}
@[params]
pub struct PlayArgs {
pub mut:
name string = 'default'
heroscript string // if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
delete bool
configure bool // make sure there is at least one installed
}
pub fn play(args_ PlayArgs) ! {
mut model := args_
mut args := args_
if model.heroscript == '' {
model.heroscript = heroscript_default()!
if args.heroscript == '' {
args.heroscript = heroscript_default()!
}
mut plbook := model.plbook or { playbook.new(text: model.heroscript)! }
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
mut configure_actions := plbook.find(filter: 'rclone.configure')!
if configure_actions.len > 0 {
for config_action in configure_actions {
mut p := config_action.params
mut install_actions := plbook.find(filter: 'rclone.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
mut p := install_action.params
mycfg := cfg_play(p)!
console.print_debug('install action rclone.configure\n${mycfg}')
set(mycfg)!
save(mycfg)!
}
}
mut other_actions := plbook.find(filter: 'rclone.')!
for other_action in other_actions {
if other_action.name in ['destroy', 'install', 'build'] {
mut p := other_action.params
reset := p.get_default_false('reset')
if other_action.name == 'destroy' || reset {
console.print_debug('install action rclone.destroy')
destroy_()!
}
if other_action.name == 'install' {
console.print_debug('install action rclone.install')
install_()!
}
}
}
}
@@ -128,6 +105,28 @@ pub fn play(args_ PlayArgs) ! {
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager {
// unknown
// screen
// zinit
// tmux
// systemd
match cat {
.zinit {
console.print_debug('startupmanager: zinit')
return startupmanager.get(cat: .zinit)!
}
.systemd {
console.print_debug('startupmanager: systemd')
return startupmanager.get(cat: .systemd)!
}
else {
console.print_debug('startupmanager: auto')
return startupmanager.get()!
}
}
}
// load from disk and make sure is properly intialized
pub fn (mut self RClone) reload() ! {
switch(self.name)
@@ -140,22 +139,20 @@ pub mut:
reset bool
}
// switch instance to be used for rclone
pub fn switch(name string) {
rclone_default = name
}
pub fn (mut self RClone) install(args InstallArgs) ! {
switch(self.name)
if args.reset {
destroy_()!
}
if !(installed_()!) {
if args.reset || (!installed_()!) {
install_()!
}
}
pub fn (mut self RClone) destroy() ! {
switch(self.name)
destroy_()!
}
// switch instance to be used for rclone
pub fn switch(name string) {
rclone_default = name
}

View File

@@ -5,7 +5,7 @@ import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import os
pub fn installll(args_ InstallArgs) ! {
pub fn install(args_ InstallArgs) ! {
mut args := args_
version := '0.16.2'

View File

@@ -11,7 +11,7 @@ import freeflowuniverse.herolib.osal.screen
import os
// install lighttpd will return true if it was already installed
pub fn installll(args InstallArgs) ! {
pub fn install(args InstallArgs) ! {
// make sure we install base on the node
base.install()!

View File

@@ -1,9 +1,9 @@
module tailwind
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.pathlib
import os
pub const version = '3.4.12'

View File

@@ -2,9 +2,9 @@ module tailwind
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.osal.zinit
import freeflowuniverse.herolib.ui.console
import time
__global (
@@ -17,65 +17,59 @@ __global (
@[params]
pub struct ArgsGet {
pub mut:
name string
}
fn args_get(args_ ArgsGet) ArgsGet {
mut model := args_
if model.name == '' {
model.name = tailwind_default
}
if model.name == '' {
model.name = 'default'
}
return model
name string = 'default'
}
pub fn get(args_ ArgsGet) !&Tailwind {
mut args := args_get(args_)
if args.name !in tailwind_global {
if args.name == 'default' {
if !config_exists(args) {
if default {
mut context := base.context() or { panic('bug') }
context.hero_config_set('tailwind', model.name, heroscript_default()!)!
}
}
load(args)!
}
}
return tailwind_global[args.name] or {
println(tailwind_global)
panic('could not get config for ${args.name} with name:${model.name}')
}
return &Tailwind{}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager {
// unknown
// screen
// zinit
// tmux
// systemd
match cat {
.zinit {
console.print_debug('startupmanager: zinit')
return startupmanager.get(cat: .zinit)!
}
.systemd {
console.print_debug('startupmanager: systemd')
return startupmanager.get(cat: .systemd)!
}
else {
console.print_debug('startupmanager: auto')
return startupmanager.get()!
}
}
}
@[params]
pub struct InstallArgs {
pub mut:
reset bool
}
// switch instance to be used for tailwind
pub fn switch(name string) {
tailwind_default = name
}
pub fn (mut self Tailwind) install(args InstallArgs) ! {
switch(self.name)
if args.reset {
destroy_()!
}
if !(installed_()!) {
if args.reset || (!installed_()!) {
install_()!
}
}
pub fn (mut self Tailwind) destroy() ! {
switch(self.name)
destroy_()!
}
// switch instance to be used for tailwind
pub fn switch(name string) {
tailwind_default = name
}

33
lib/lang/python/freeze.v Normal file
View File

@@ -0,0 +1,33 @@
module python
// // remember the requirements list for all pips
// pub fn (mut py PythonEnv) freeze(name string) ! {
// console.print_debug('Freezing requirements for environment: ${py.name}')
// cmd := '
// cd ${py.path.path}
// source bin/activate
// python3 -m pip freeze
// '
// res := os.execute(cmd)
// if res.exit_code > 0 {
// console.print_stderr('Failed to freeze requirements: ${res}')
// return error('could not execute freeze.\n${res}\n${cmd}')
// }
// console.print_debug('Successfully froze requirements')
// }
// remember the requirements list for all pips
// pub fn (mut py PythonEnv) unfreeze(name string) ! {
// // requirements := py.db.get('freeze_${name}')!
// mut p := py.path.file_get_new('requirements.txt')!
// p.write(requirements)!
// cmd := '
// cd ${py.path.path}
// source bin/activate
// python3 -m pip install -r requirements.txt
// '
// res := os.execute(cmd)
// if res.exit_code > 0 {
// return error('could not execute unfreeze.\n${res}\n${cmd}')
// }
// }

84
lib/lang/python/pyexec.v Normal file
View File

@@ -0,0 +1,84 @@
module python
import freeflowuniverse.herolib.osal
// import freeflowuniverse.herolib.data.dbfs
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.core.texttools
import os
import freeflowuniverse.herolib.ui.console
@[params]
pub struct PythonExecArgs {
pub mut:
cmd string @[required]
result_delimiter string = '==RESULT=='
ok_delimiter string = '==OK=='
python_script_name string // if used will put it in root of the sandbox under that name
stdout bool = true
}
pub fn (py PythonEnv) exec(args PythonExecArgs) !string {
mut cmd := texttools.dedent(args.cmd)
mut debug := false
if cmd.contains('DEBUG()') {
cmd = cmd.replace('DEBUG()', 'from IPython import embed; embed()')
debug = true
}
cmd += "\n\nprint(\"${args.ok_delimiter}\")\n"
mut scriptpath := ''
if args.python_script_name.len > 0 {
scriptpath = '${py.path.path}/${args.python_script_name}.py'
mut p := pathlib.get_file(path: scriptpath, create: true)!
p.write(cmd)!
} else {
scriptpath = pathlib.temp_write(text: cmd, ext: 'py') or {
return error('error: cannot write script to execute: ${err}')
}
}
console.print_debug(' - python script exec: ${scriptpath}')
os.chmod(scriptpath, 0o777)!
cmd2 := '
cd ${py.path.path}
source bin/activate
python3 ${scriptpath}
'
if args.stdout || debug {
console.print_debug(cmd2)
}
mut job := osal.Job{}
if debug {
osal.execute_interactive(cmd2)!
} else {
job = osal.exec(cmd: cmd2, stdout: args.stdout, raise_error: false)!
}
if job.exit_code > 0 {
// means error
mut msg := ' - error in execution of python script: ${scriptpath}\n'
msg += 'ERROR:\n'
msg += job.error.str()
return error(msg)
}
// console.print_debug(job)
mut o := []string{}
mut start := false
for l in job.output.split_into_lines() {
if l.trim_space().starts_with(args.result_delimiter) {
start = true
continue
}
if l.trim_space().starts_with(args.ok_delimiter) {
break
}
if start {
o << l
}
}
return o.join_lines()
}

161
lib/lang/python/python.v Normal file
View File

@@ -0,0 +1,161 @@
module python
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.installers.lang.python
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.data.dbfs
import freeflowuniverse.herolib.ui.console
import os
pub struct PythonEnv {
pub mut:
name string
path pathlib.Path
db dbfs.DB
}
@[params]
pub struct PythonEnvArgs {
pub mut:
name string = 'default'
reset bool
}
pub fn new(args_ PythonEnvArgs) !PythonEnv {
console.print_debug('Creating new Python environment with name: ${args_.name}')
mut args := args_
name := texttools.name_fix(args.name)
pp := '${os.home_dir()}/hero/python/${name}'
console.print_debug('Python environment path: ${pp}')
mut c := base.context()!
mut py := PythonEnv{
name: name
path: pathlib.get_dir(path: pp, create: true)!
db: c.db_get('python_${args.name}')!
}
key_install := 'pips_${py.name}_install'
key_update := 'pips_${py.name}_update'
if !os.exists('${pp}/bin/activate') {
console.print_debug('Python environment directory does not exist, triggering reset')
args.reset = true
}
if args.reset {
console.print_debug('Resetting Python environment')
py.pips_done_reset()!
py.db.delete(key: key_install)!
py.db.delete(key: key_update)!
}
toinstall := !py.db.exists(key: key_install)!
if toinstall {
console.print_debug('Installing Python environment')
python.install()!
py.init_env()!
py.db.set(key: key_install, value: 'done')!
console.print_debug('Python environment setup complete')
}
toupdate := !py.db.exists(key: key_update)!
if toupdate {
console.print_debug('Updating Python environment')
py.update()!
py.db.set(key: key_update, value: 'done')!
console.print_debug('Python environment update complete')
}
return py
}
// comma separated list of packages to install
pub fn (py PythonEnv) init_env() ! {
console.print_green('Initializing Python virtual environment at: ${py.path.path}')
cmd := '
cd ${py.path.path}
python3 -m venv .
'
osal.exec(cmd: cmd)!
console.print_debug('Virtual environment initialization complete')
}
// comma separated list of packages to install
pub fn (py PythonEnv) update() ! {
console.print_green('Updating pip in Python environment: ${py.name}')
cmd := '
cd ${py.path.path}
source bin/activate
python3 -m pip install --upgrade pip
'
osal.exec(cmd: cmd)!
console.print_debug('Pip update complete')
}
// comma separated list of packages to install
pub fn (mut py PythonEnv) pip(packages string) ! {
mut to_install := []string{}
for i in packages.split(',') {
pip := i.trim_space()
if !py.pips_done_check(pip)! {
to_install << pip
console.print_debug('Package to install: ${pip}')
}
}
if to_install.len == 0 {
return
}
console.print_debug('Installing Python packages: ${packages}')
packages2 := to_install.join(' ')
cmd := '
cd ${py.path.path}
source bin/activate
pip3 install ${packages2} -q
'
osal.exec(cmd: cmd)!
// After successful installation, record the packages as done
for pip in to_install {
py.pips_done_add(pip)!
console.print_debug('Successfully installed package: ${pip}')
}
}
pub fn (mut py PythonEnv) pips_done_reset() ! {
console.print_debug('Resetting installed packages list for environment: ${py.name}')
py.db.delete(key: 'pips_${py.name}')!
}
pub fn (mut py PythonEnv) pips_done() ![]string {
// console.print_debug('Getting list of installed packages for environment: ${py.name}')
mut res := []string{}
pips := py.db.get(key: 'pips_${py.name}') or { '' }
for pip_ in pips.split_into_lines() {
pip := pip_.trim_space()
if pip !in res && pip.len > 0 {
res << pip
}
}
// console.print_debug('Found ${res.len} installed packages')
return res
}
pub fn (mut py PythonEnv) pips_done_add(name string) ! {
console.print_debug('Adding package ${name} to installed packages list')
mut pips := py.pips_done()!
if name in pips {
// console.print_debug('Package ${name} already marked as installed')
return
}
pips << name
out := pips.join_lines()
py.db.set(key: 'pips_${py.name}', value: out)!
console.print_debug('Successfully added package ${name} to installed list')
}
pub fn (mut py PythonEnv) pips_done_check(name string) !bool {
// console.print_debug('Checking if package ${name} is installed')
mut pips := py.pips_done()!
return name in pips
}

View File

@@ -0,0 +1,7 @@
module python
fn test_python() {
py := new() or { panic(err) }
py.update() or { panic(err) }
py.pip('ipython') or { panic(err) }
}

96
lib/lang/python/readme.md Normal file
View File

@@ -0,0 +1,96 @@
## use virtual env
```v
import freeflowuniverse.herolib.lang.python
py:=python.new(name:'default')! //a python env with name default
py.update()!
py.pip("ipython")!
```
### to activate an environment and use the installed python
```bash
source ~/hero/python/default/bin/activate
```
### how to write python scripts to execute
```v
#!/usr/bin/env -S v -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.lang.python
import json
pub struct Person {
name string
age int
is_member bool
skills []string
}
mut py:=python.new(name:'test')! //a python env with name test
//py.update()!
py.pip("ipython")!
nrcount:=5
//this is used in the pythonexample
cmd:=$tmpl("pythonexample.py")
mut res:=""
for i in 0..5{
println(i)
res=py.exec(cmd:cmd)!
}
//res:=py.exec(cmd:cmd)!
person:=json.decode(Person,res)!
println(person)
```
example python script which is in the pythonscripts/ dir
```py
import json
for counter in range(1, @nrcount): # Loop from 1 to the specified param
print(f"done_{counter}")
# Define a simple Python structure (e.g., a dictionary)
example_struct = {
"name": "John Doe",
"age": @nrcount,
"is_member": True,
"skills": ["Python", "Data Analysis", "Machine Learning"]
}
# Convert the structure to a JSON string
json_string = json.dumps(example_struct, indent=4)
# Print the JSON string
print("==RESULT==")
print(json_string)
```
> see `crystallib/examples/lang/python/pythonexample.vsh`
## remark
This is a slow way how to execute python, is about 2 per second on a fast machine, need to implement something where we keep the python in mem and reading from a queue e.g. redis this will go much faster, but ok for now.
see also examples dir, there is a working example

14
lib/lang/python/shell.v Normal file
View File

@@ -0,0 +1,14 @@
module python
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.core.texttools
pub fn (py PythonEnv) shell(name_ string) ! {
_ := texttools.name_fix(name_)
cmd := '
cd ${py.path.path}
source bin/activate
'
osal.exec(cmd: cmd)!
}

View File

@@ -104,7 +104,7 @@ pub fn usr_local_path() !string {
// return the source statement if the profile exists
pub fn profile_path_source() !string {
if hostname() or { '' } == 'rescue' {
if core.hostname() or { '' } == 'rescue' {
return ''
}
pp := profile_path()!
@@ -117,7 +117,7 @@ pub fn profile_path_source() !string {
// return source $path && .
// or empty if it doesn't exist
pub fn profile_path_source_and() !string {
if hostname() or { '' } == 'rescue' {
if core.hostname() or { '' } == 'rescue' {
return ''
}
pp := profile_path()!

View File

@@ -4,7 +4,7 @@ module tfrobot
import freeflowuniverse.herolib.builder
import freeflowuniverse.herolib.osal
// import freeflowuniverse.herolib.servers.daguserver as dagu
import freeflowuniverse.herolib.clients.daguclient as dagu_client
// import freeflowuniverse.herolib.clients.daguclient as dagu_client
import freeflowuniverse.herolib.ui.console
import time

View File

@@ -0,0 +1,8 @@
!!hero_code.generate_client
name:'mdbook'
classname:'MDBooks'
singleton:0
default:1
hasconfig:1
reset:0

View File

@@ -0,0 +1,49 @@
// module mdbook
// import freeflowuniverse.herolib.core.base
// import os
// import crypto.md5
// @[params]
// pub struct Config {
// pub mut:
// path_build string = '${os.home_dir()}/hero/var/mdbuild'
// path_publish string = '${os.home_dir()}/hero/www/info'
// }
// // @[heap]
// // pub struct MDBooks {
// // pub:
// // cfg Config
// // }
// @[params]
// pub struct InitParams{
// action string
// name string
// }
// fn (cfg MDBooks) init(args InitParams) {
// }
// pub fn get(cfg_ Config) !MDBooks {
// mut c := base.context()!
// // lets get a unique name based on the used build and publishpaths
// mut cfg := cfg_
// cfg.path_build = cfg.path_build.replace('~', os.home_dir())
// cfg.path_publish = cfg.path_publish.replace('~', os.home_dir())
// mut name := md5.hexhash('${cfg.path_build}${cfg.path_publish}')
// mut myparams := c.params()!
// mut self := MDBooks{
// cfg: cfg
// }
// if myparams.exists('mdbookname') {
// name = myparams.get('mdbookname')!
// self.init('mdbook', name: name, .get, cfg)!
// } else {
// self.init('mdbook', name, .set, cfg)!
// myparams.set('mdbookname', name)
// }
// return self
// }

View File

@@ -0,0 +1,37 @@
module mdbook
import freeflowuniverse.herolib.develop.vscode
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.core.base
import os
pub fn book_open(name string) ! {
mut c := base.context()!
mut r := c.redis()!
mut path_publish := r.get('mdbook:${name}:publish')!
path_publish = path_publish.replace('~', os.home_dir())
if path_publish.len == 0 {
return error("can't find book: ${name}, was it generated before?")
}
if !os.exists(path_publish) {
return error("can't find generated book in ${path_publish}, was it generated properly.")
}
cmd3 := "open '${path_publish}/index.html'"
// console.print_debug(cmd3)
osal.exec(cmd: cmd3)!
}
pub fn book_edit(name string) ! {
mut c := base.context()!
mut r := c.redis()!
path_build := r.get('mdbook:${name}:build')!
if path_build.len == 0 {
return error("can't find book: ${name}, was it generated before?")
}
edit_path := '${path_build}/edit'.replace('~', os.home_dir())
if !os.exists(edit_path) {
return error("can't find book edit path in ${edit_path}, was it generated properly.")
}
vscode_helper := vscode.new(edit_path)
vscode_helper.open()!
}

101
lib/web/mdbook/load.v Normal file
View File

@@ -0,0 +1,101 @@
module mdbook
import v.embed_file
// import freeflowuniverse.herolib.installers.web.mdbook as mdbook_installer
// import freeflowuniverse.herolib.installers.web.imagemagick
import os
@[heap]
pub struct FileLoader {
pub mut:
embedded_files []embed_file.EmbedFileData @[skip; str: skip]
}
fn (mut loader FileLoader) load() ! {
loader.embedded_files << $embed_file('template/css/print.css')
loader.embedded_files << $embed_file('template/css/variables.css')
loader.embedded_files << $embed_file('template/css/general.css')
loader.embedded_files << $embed_file('template/mermaid-init.js')
loader.embedded_files << $embed_file('template/echarts.min.js')
loader.embedded_files << $embed_file('template/mermaid.min.js')
}
fn loader() !FileLoader {
if 'OFFLINE' !in os.environ() {
// console.print_debug(" - CHECK INSTALLER")
// mdbook_installer.install()!
// imagemagick.install()!
}
mut loader := FileLoader{}
return loader
}
// if true {
// panic('generate')
// }
// now we have to reset the rev keys, so we remember current status
// for key, mut status in self.gitrepos_status {
// osal.done_set('mdbookrev_${key}', status.revnew)!
// status.revlast = status.revnew
// }
// pub struct RepoStatus {
// pub mut:
// revlast string
// revnew string
// }
// make sure all intial states for the revisions are reset
// fn (mut self MDBooksFactory) reset_state() ! {
// for key, mut status in self.gitrepos_status {
// osal.done_set('mdbookrev_${key}', '')!
// status.revlast = ''
// }
// }
// get all content
// pub fn (mut self MDBooksFactory) pull(reset bool) ! {
// console.print_header(' pull mdbooks')
// print_backtrace()
// self.init()!
// for key, repo_ in self.gitrepos {
// mut repo := repo_
// if reset {
// repo.pull_reset(reload: true)! // need to overwrite all changes
// } else {
// repo.pull(reload: true)! // will not overwrite changes
// }
// revnew := repo.rev()!
// lastrev := osal.done_get('mdbookrev_${key}') or { '' }
// self.gitrepos_status[key] = RepoStatus{
// revnew: revnew
// revlast: lastrev
// }
// }
// }
// @[params]
// pub struct WatchArgs {
// pub mut:
// period int = 300 // 5 min default
// reset bool
// }
// pub fn (mut self MDBooksFactory) watch(args WatchArgs) {
// mut t := ourtime.now()
// mut last := i64(0)
// for {
// t.now()
// console.print_stdout('${t} ${t.unix()} period:${args.period}')
// if t.unix() > last + args.period {
// console.print_header(' will try to check the mdbooks')
// self.pull(args.reset) or { panic(" - ERROR: couldn't pull the repo's.\n${err}") }
// self.generate() or { panic(" - ERROR: couldn't generate the repo's.\n${err}") }
// last = t.unix()
// }
// time.sleep(time.second)
// if args.period == 0 {
// return
// }
// }
// }

296
lib/web/mdbook/mdbook.v Normal file
View File

@@ -0,0 +1,296 @@
module mdbook
import freeflowuniverse.herolib.osal
import os
import freeflowuniverse.herolib.data.doctree.collection
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.develop.gittools
@[heap]
pub struct MDBook {
pub mut:
name string
books &MDBooks @[skip; str: skip]
path_build pathlib.Path
path_publish pathlib.Path
args MDBookArgs
errors []collection.CollectionError
}
@[params]
pub struct MDBookArgs {
pub mut:
name string @[required]
title string
foldlevel int
printbook bool
// summary_url string // url of the summary.md file
summary_path string // can also give the path to the summary file (can be the dir or the summary itself)
// doctree_url string
// doctree_path string
publish_path string
build_path string
production bool
collections []string
description string
export bool // whether mdbook should be built
}
pub fn (mut books MDBooks) generate(args_ MDBookArgs) !&MDBook {
console.print_header(' mdbook: ${args_.name}')
mut args := args_
if args.title == '' {
args.title = args.name
}
if args.build_path.len == 0 {
args.build_path = '${books.path_build}/${args.name}'
}
if args.publish_path.len == 0 {
args.publish_path = '${books.path_publish}/${args.name}'
}
mut mycontext := base.context()!
mut r := mycontext.redis()!
r.set('mdbook:${args.name}:build', args.build_path)!
r.set('mdbook:${args.name}:publish', args.publish_path)!
r.expire('mdbook:${args.name}:build', 3600 * 12)! // expire after 12h
r.expire('mdbook:${args.name}:publish', 3600 * 12)!
_ := gittools.get()!
mut src_path := pathlib.get_dir(path: '${args.build_path}/src', create: true)!
_ := pathlib.get_dir(path: '${args.build_path}/.edit', create: true)!
mut collection_set := map[string]bool{}
for col_path in args.collections {
// link collections from col_path to src
mut p := pathlib.get_dir(path: col_path)!
_ := p.list(dirs_only: true, recursive: false)!
if _ := collection_set[p.name()] {
return error('collection with name ${p.name()} already exists')
}
p.link('${src_path.path}/${p.name()}', true)!
// QUESTION: why was this ever implemented per entry?
// for mut entry in entries.paths {
// if _ := collection_set[entry.name()] {
// println('collection with name ${entry.name()} already exists')
// // return error('collection with name ${entry.name()} already exists')
// }
// collection_set[entry.name()] = true
// entry.link('${src_path.path}/${entry.name()}', true)!
// }
}
mut book := MDBook{
args: args
path_build: pathlib.get_dir(path: args.build_path, create: true)!
path_publish: pathlib.get_dir(path: args.publish_path, create: true)!
books: &books
}
mut summary := book.summary(args_.production)!
mut dir_list := src_path.list(dirs_only: true, include_links: true, recursive: false)!
for mut collection_dir_path in dir_list.paths {
collectionname := collection_dir_path.path.all_after_last('/')
// should always work because done in summary
// check if there are errors, if yes add to summary
if os.exists('${collection_dir_path.path}/errors.md') {
summary.add_error_page(collectionname, 'errors.md')
}
// now link the exported collection into the build dir
collection_dirbuild_str := '${book.path_build.path}/src/${collectionname}'.replace('~',
os.home_dir())
if !pathlib.path_equal(collection_dirbuild_str, collection_dir_path.path) {
collection_dir_path.link(collection_dirbuild_str, true)!
}
if !os.exists('${collection_dir_path.path}/.linkedpages') {
continue
}
mut linked_pages := pathlib.get_file(path: '${collection_dir_path.path}/.linkedpages')!
lpagescontent := linked_pages.read()!
for lpagestr in lpagescontent.split_into_lines().filter(it.trim_space() != '') {
// console.print_green('find linked page: ${lpagestr}')
// format $collection:$pagename.md
splitted := lpagestr.split(':')
assert splitted.len == 2
summary.add_page_additional(splitted[0], splitted[1])
}
// if collection_dir_path.file_exists('.linkedpages') {
// mut lpages := collection_dir_path.file_get('.linkedpages')!
// lpagescontent := lpages.read()!
// for lpagestr in lpagescontent.split_into_lines().filter(it.trim_space() != '') {
// // console.print_green('find linked page: ${lpagestr}')
// // format $collection:$pagename.md
// splitted := lpagestr.split(':')
// assert splitted.len == 2
// summary.add_page_additional(splitted[0], splitted[1])
// }
// }
}
// create the additional collection (is a system collection)
addpath := '${book.path_build.path}/src/additional'
mut a := pathlib.get_file(path: '${addpath}/additional.md', create: true)!
mut b := pathlib.get_file(path: '${addpath}/errors.md', create: true)!
mut c := pathlib.get_file(path: '${addpath}/pages.md', create: true)!
a.write('
# Additional pages
A normal user can ignore these pages, they are for the authors to see e.g. errors
')!
b.write('
# Errors
Be the mother for our errors.
')!
c.write('
# Additional pages
You can ignore these pages, they are just to get links to work.
')!
if book.errors.len > 0 {
book.errors_report()!
summary.errors << SummaryItem{
level: 2
description: 'errors mdbook'
pagename: 'errors_mdbook.md'
collection: 'additional'
}
}
path_summary_str := '${book.path_build.path}/src/SUMMARY.md'
mut path_summary := pathlib.get_file(path: path_summary_str, create: true)!
path_summary.write(summary.str())!
book.template_install()!
if args.export {
book.generate()!
}
console.print_header(' mdbook prepared: ${book.path_build.path}')
return &book
}
// write errors.md in the collection, this allows us to see what the errors are
fn (book MDBook) errors_report() ! {
errors_path_str := '${book.path_build.path}/src/additional/errors_mdbook.md'
mut dest := pathlib.get_file(path: errors_path_str, create: true)!
if book.errors.len == 0 {
dest.delete()!
return
}
c := $tmpl('template/errors.md')
dest.write(c)!
}
@[params]
pub struct ErrorArgs {
pub mut:
path string
msg string
cat collection.CollectionErrorCat
}
pub fn (mut book MDBook) error(args ErrorArgs) {
path2 := pathlib.get(args.path)
e := collection.CollectionError{
path: path2
msg: args.msg
cat: args.cat
}
book.errors << e
console.print_stderr(args.msg)
}
pub fn (mut book MDBook) open() ! {
console.print_header('open book: ${book.name}')
cmd := 'open \'${book.path_publish.path}/index.html\''
// console.print_debug(cmd)
// cmd:='bash ${book.path_build.path}/develop.sh'
osal.exec(cmd: cmd)!
}
pub fn (mut book MDBook) generate() ! {
console.print_header(' book generate: ${book.name} on ${book.path_build.path}')
book.summary_image_set()!
osal.exec(
cmd: '
cd ${book.path_build.path}
mdbook build --dest-dir ${book.path_publish.path}
'
retry: 0
)!
}
fn (mut book MDBook) template_install() ! {
// get embedded files to the mdbook dir
// console.print_debug(book.str())
mut l := loader()!
l.load()!
for item in l.embedded_files {
dpath := '${book.path_build.path}/${item.path.all_after_first('/')}'
// console.print_debug(' embed: ${dpath}')
mut dpatho := pathlib.get_file(path: dpath, create: true)!
dpatho.write(item.to_string())!
}
c := $tmpl('template/book.toml')
mut tomlfile := book.path_build.file_get_new('book.toml')!
tomlfile.write(c)!
c1 := $tmpl('template/build.sh')
mut file1 := book.path_build.file_get_new('build.sh')!
file1.write(c1)!
file1.chmod(0o770)!
c2 := $tmpl('template/develop.sh')
mut file2 := book.path_build.file_get_new('develop.sh')!
file2.write(c2)!
file2.chmod(0o770)!
}
fn (mut book MDBook) summary_image_set() ! {
// this is needed to link the first image dir in the summary to the src, otherwise empty home image
summaryfile := '${book.path_build.path}/src/SUMMARY.md'
mut p := pathlib.get_file(path: summaryfile)!
c := p.read()!
mut first := true
for line in c.split_into_lines() {
if !(line.trim_space().starts_with('-')) {
continue
}
if line.contains('](') && first {
folder_first := line.all_after('](').all_before_last(')')
folder_first_dir_img := '${book.path_build.path}/src/${folder_first.all_before_last('/')}/img'
// console.print_debug(folder_first_dir_img)
// if true{panic("s")}
if os.exists(folder_first_dir_img) {
mut image_dir := pathlib.get_dir(path: folder_first_dir_img)!
image_dir.link('${book.path_build.path}/src/img', true)!
}
first = false
}
}
}

View File

@@ -0,0 +1,105 @@
module mdbook
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
__global (
mdbook_global map[string]&MDBooks
mdbook_default string
)
/////////FACTORY
@[params]
pub struct ArgsGet {
pub mut:
name string = 'default'
}
fn args_get(args_ ArgsGet) ArgsGet {
mut args := args_
if args.name == '' {
args.name = mdbook_default
}
if args.name == '' {
args.name = 'default'
}
return args
}
pub fn get(args_ ArgsGet) !&MDBooks {
mut args := args_get(args_)
if args.name !in mdbook_global {
if !config_exists() {
if default {
config_save()!
}
}
config_load()!
}
return mdbook_global[args.name] or {
println(mdbook_global)
panic('bug in get from factory: ')
}
}
fn config_exists(args_ ArgsGet) bool {
mut args := args_get(args_)
mut context := base.context() or { panic('bug') }
return context.hero_config_exists('mdbook', args.name)
}
fn config_load(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
mut heroscript := context.hero_config_get('mdbook', args.name)!
play(heroscript: heroscript)!
}
fn config_save(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
context.hero_config_set('mdbook', args.name, heroscript_default()!)!
}
fn set(o MDBooks) ! {
mut o2 := obj_init(o)!
mdbook_global['default'] = &o2
}
@[params]
pub struct PlayArgs {
pub mut:
name string = 'default'
heroscript string // if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
start bool
stop bool
restart bool
delete bool
configure bool // make sure there is at least one installed
}
pub fn play(args_ PlayArgs) ! {
mut args := args_
if args.heroscript == '' {
args.heroscript = heroscript_default()!
}
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
mut install_actions := plbook.find(filter: 'mdbook.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
mut p := install_action.params
cfg_play(p)!
}
}
}
// switch instance to be used for mdbook
pub fn switch(name string) {
mdbook_default = name
}

View File

@@ -0,0 +1,66 @@
module mdbook
import freeflowuniverse.herolib.data.paramsparser
import os
pub const version = '1.14.3'
const singleton = false
const default = true
// TODO: THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE TO STRUCT BELOW, IS STRUCTURED AS HEROSCRIPT
pub fn heroscript_default() !string {
heroscript := "
!!mdbook.configure
name:'mdbook'
mail_from: 'info@example.com'
mail_password: 'secretpassword'
mail_port: 587
mail_server: 'smtp-relay.brevo.com'
mail_username: 'kristof@incubaid.com'
"
// mail_from := os.getenv_opt('MAIL_FROM') or {'info@example.com'}
// mail_password := os.getenv_opt('MAIL_PASSWORD') or {'secretpassword'}
// mail_port := (os.getenv_opt('MAIL_PORT') or {"587"}).int()
// mail_server := os.getenv_opt('MAIL_SERVER') or {'smtp-relay.brevo.com'}
// mail_username := os.getenv_opt('MAIL_USERNAME') or {'kristof@incubaid.com'}
//
// heroscript:="
// !!mailclient.configure name:'default'
// mail_from: '${mail_from}'
// mail_password: '${mail_password}'
// mail_port: ${mail_port}
// mail_server: '${mail_server}'
// mail_username: '${mail_username}'
//
// "
//
return heroscript
}
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
@[heap]
pub struct MDBooks {
pub mut:
name string
path_build string
path_publish string
}
fn cfg_play(p paramsparser.Params) ! {
// THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above
mut mycfg := MDBooks{
name: p.get_default('name', 'default')!
path_build: p.get_default('path_build', '${os.home_dir()}/hero/var/mdbuild')!
path_publish: p.get_default('path_publish', '${os.home_dir()}/hero/www/info')!
}
set(mycfg)!
}
fn obj_init(obj_ MDBooks) !MDBooks {
// never call get here, only thing we can do here is work on object itself
mut obj := obj_
return obj
}

32
lib/web/mdbook/readme.md Normal file
View File

@@ -0,0 +1,32 @@
# mdbook
To get started
```vlang
import freeflowuniverse.herolib.clients. mdbook
mut client:= mdbook.get()!
client...
```
## example heroscript
```hero
!!mdbook.configure
secret: '...'
host: 'localhost'
port: 8888
```

222
lib/web/mdbook/summary.v Normal file
View File

@@ -0,0 +1,222 @@
module mdbook
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.core.texttools
import os
@[heap]
pub struct Summary {
pub mut:
items []SummaryItem
errors []SummaryItem // means we found errors, so we need to add to summary
addpages []SummaryItem // means we found pages as links, so we need to add them to the summary
collections []string
production bool
}
pub struct SummaryItem {
pub mut:
level int
description string
relpath string // relative path of summary item to source
collection string
pagename string
}
pub fn (mut book MDBook) summary(production bool) !Summary {
if !os.exists(book.args.summary_path) {
panic("summary file ${book.args.summary_path} doesn't exist")
}
mut summary := Summary{
production: production
}
mut summary_path := pathlib.get_file(path: book.args.summary_path, create: false)!
c := summary_path.read()!
summary_path.link('${book.path_build.path}/edit/summary.md', true)!
mut level := 0
mut ident := 2
for mut line in c.split_into_lines() {
if !(line.trim_space().starts_with('-')) {
continue
}
pre := line.all_before('-')
level = int(pre.len / ident)
// console.print_debug("${line} === '${pre}' ${level}")
line = line.trim_left(' -')
// - [Dunia Yetu](dy_intro/dunia_yetu/dunia_yetu.md)
// - [About Us](dy_intro/dunia_yetu/about_us.md)
if !line.starts_with('[') {
book.error(msg: "syntax error in summary: '${line}', needs to start with [")
continue
}
if !line.contains('](') {
book.error(msg: "syntax error in summary: '${line}', needs to have ](")
continue
}
description := line.all_after_first('[').all_before(']').trim_space()
path := line.all_after_last('(').all_before_last(')').trim_space()
if !path.contains('/') {
book.error(
msg: "syntax error in summary: '${line}', doesn't contain collectionname (is first element of path)"
)
continue
}
pagename := texttools.name_fix(path.all_after_last('/'))
collection := texttools.name_fix(path.all_before('/'))
if collection !in summary.collections {
summary.collections << collection
}
mut path_collection_str := '${book.args.build_path}/src/${collection}'.replace('~',
os.home_dir())
mut path_collection := pathlib.get_dir(path: path_collection_str, create: false) or {
book.error(
msg: "collection find error in summary: '${line}', can't find collection: ${path_collection_str} "
)
continue
}
list := path_collection.list()!
file_path_ := list.paths.filter(it.name() == pagename)
if file_path_.len == 0 {
book.error(
msg: "page find error in summary: '${line}', can't find page: ${pagename} in collection: ${path_collection_str}\n${file_path_} doesnt exist"
)
continue
} else if file_path_.len > 1 {
book.error(msg: 'duplicate page in collection: ${pagename}')
continue
}
file_path := file_path_[0].path
if !os.exists(file_path) || !os.is_file(file_path) {
book.error(
msg: "page find error in summary: '${line}', can't find page: ${pagename} in collection: ${path_collection_str}\n${file_path} doesnt exist"
)
continue
}
// NOTE: using this returns false because path_collection is a link
// if !path_collection.file_exists(pagename) {
// book.error(
// msg: "page find error in summary: '${line}', can't find page: ${pagename} in collection: ${path_collection_str}"
// )
// continue
// }
summary.items << SummaryItem{
level: level
description: description
pagename: pagename
relpath: file_path.all_after('${book.args.build_path}/src/') // relative path of page to src dir
collection: collection
}
}
return summary
}
fn (mut self Summary) add_error_page(collectionname string, pagename string) {
description := 'errors ${collectionname}'
self.errors << SummaryItem{
level: 2
description: description
pagename: pagename
collection: collectionname
}
}
fn (mut self Summary) is_in_summary(collection_name_ string, page_name_ string) bool {
mut collection_name := texttools.name_fix(collection_name_).to_lower()
mut page_name := texttools.name_fix(page_name_).to_lower()
if !(page_name.ends_with('.md')) {
page_name += '.md'
}
for i in self.items {
mut pname := texttools.name_fix(i.pagename.to_lower())
if !(pname.ends_with('.md')) {
pname += '.md'
}
if i.collection.to_lower() == collection_name && pname == page_name {
return true
}
}
for i in self.addpages {
mut pname := texttools.name_fix(i.pagename.to_lower())
if !(pname.ends_with('.md')) {
pname += '.md'
}
if i.collection.to_lower() == collection_name && pname == page_name {
return true
}
}
return false
}
fn (mut self Summary) add_page_additional(collectionname string, pagename string) {
if self.is_in_summary(collectionname, pagename) {
return
}
shortname := pagename.all_before_last('.').to_lower()
description := '${shortname}'
self.addpages << SummaryItem{
level: 2
description: description
pagename: pagename
collection: collectionname
}
}
pub fn (mut self Summary) str() string {
mut out := []string{}
for item in self.items {
mut pre := ''
for _ in 0 .. item.level {
pre += ' '
}
out << '${pre}- [${item.description}](${item.relpath})'
}
if self.addpages.len > 0 || (!self.production && self.errors.len > 0) {
out << '- [_](additional/additional.md)'
}
if self.addpages.len > 0 {
out << ' - [unlisted_pages](additional/pages.md)'
for item in self.addpages {
mut pre := ''
for _ in 0 .. item.level {
pre += ' '
}
out << '${pre}- [${item.description}](${item.collection}/${item.pagename})'
}
}
if !self.production && self.errors.len > 0 {
out << ' - [errors](additional/errors.md)'
for item in self.errors {
mut pre := ''
for _ in 0 .. item.level {
pre += ' '
}
out << '${pre}- [${item.description}](${item.collection}/${item.pagename})'
}
}
return out.join_lines()
}

View File

@@ -0,0 +1,70 @@
[book]
authors = []
language = "en"
multilingual = false
src = "src"
title = "@book.args.title"
[output.html.fold]
enable = true
level = @book.args.foldlevel
[output]
[output.html]
no-section-label = true
additional-css = ["css/general.css"]
additional-js = ["mermaid.min.js", "mermaid-init.js", "echarts.min.js"]
default-theme = "light"
mathjax-support = true
preserve-inline-html = true
[build]
create-missing = false
[output.html.print]
enable = @book.args.printbook
[preprocessor]
[preprocessor.mermaid]
command = "mdbook-mermaid"
# [preprocessor.last-changed]
# command = "mdbook-last-changed"
# renderer = ["html"]
#use as mdbook preprocessor
[preprocessor.echarts]
# [preprocessor.embed]
[preprocessor.kroki-preprocessor]
# [output.linkcheck]
# follow-web-links = true
# traverse-parent-directories = true
# warning-policy = "warn"
# [output.pdf]
# landscape = false
# display-header-footer = true
# print-background = true
# theme = "ayu"
# # scale = 0.7
# paper-width = 8.3
# paper-height = 11.7
# margin-top = 0.5
# margin-bottom = 0.5
# margin-left = 0.5
# margin-right = 0.5
# page-ranges = ""
# ignore-invalid-page-ranges = false
# header-template = "<h3 style='font-size:8px; margin-left: 48%' class='title'></h3>"
# footer-template = "<p style='font-size:10px; margin-left: 48%'><span class='pageNumber'></span> / <span class='totalPages'></span></p>"
# prefer-css-page-size = false
# [preprocessor.plantuml]
# plantuml-cmd="http://localhost:8080/plantuml"
# # plantuml-cmd="http://www.plantuml.com/plantuml"
# use-data-uris=true

View File

@@ -0,0 +1,3 @@
set -e
cd ${book.path_build.path}
mdbook build --dest-dir ${book.path_publish.path}

View File

@@ -0,0 +1,208 @@
/* Base styles and content styles */
@import 'variables.css';
:root {
/* Browser default font-size is 16px, this way 1 rem = 10px */
font-size: 62.5%;
color-scheme: var(--color-scheme);
}
html {
font-family: "Open Sans", sans-serif;
color: var(--fg);
background-color: var(--bg);
text-size-adjust: none;
-webkit-text-size-adjust: none;
}
body {
margin: 0;
font-size: 1.6rem;
overflow-x: hidden;
}
code {
font-family: var(--mono-font) !important;
font-size: var(--code-font-size);
}
/* make long words/inline code not x overflow */
main {
overflow-wrap: break-word;
}
/* make wide tables scroll if they overflow */
.table-wrapper {
overflow-x: auto;
text-align: left;
}
/* Don't change font size in headers. */
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
font-size: unset;
}
.left { float: left; }
.right { float: right; }
.boring { opacity: 0.6; }
.hide-boring .boring { display: none; }
.hidden { display: none !important; }
h2, h3 { margin-top: 2.5em; }
h4, h5 { margin-top: 2em; }
.header + .header h3,
.header + .header h4,
.header + .header h5 {
margin-top: 1em;
}
h1:target::before,
h2:target::before,
h3:target::before,
h4:target::before,
h5:target::before,
h6:target::before {
display: inline-block;
content: "»";
margin-left: -30px;
width: 30px;
}
/* This is broken on Safari as of version 14, but is fixed
in Safari Technology Preview 117 which I think will be Safari 14.2.
https://bugs.webkit.org/show_bug.cgi?id=218076
*/
:target {
scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);
}
.page {
outline: 0;
padding: 0 var(--page-padding);
margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */
}
.page-wrapper {
box-sizing: border-box;
}
.js:not(.sidebar-resizing) .page-wrapper {
transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */
}
.content {
overflow-y: auto;
padding: 0 5px 5px 5px;
}
.content main {
margin-left: 20;
margin-right: auto;
max-width: 1000px;
}
.content p { line-height: 1.45em; }
.content ol { line-height: 1.45em; }
.content ul { line-height: 1.45em; }
.content a { text-decoration: none; }
.content a:hover { text-decoration: underline; }
.content img, .content video { max-width: 100%; }
.content .header:link,
.content .header:visited {
color: var(--fg);
}
.content .header:link,
.content .header:visited:hover {
text-decoration: none;
}
table {
margin: 0 auto;
margin-left: 0;
margin-right: 0;
border-collapse: collapse;
width: 100%;
}
table td {
padding: 3px 20px;
border: 1px var(--table-border-color) solid;
}
table thead {
background: var(--table-header-bg);
}
table thead td {
font-weight: 700;
border: none;
}
table thead th {
padding: 3px 20px;
}
table thead tr {
border: 1px var(--table-header-bg) solid;
}
/* Alternate background colors for rows */
table tbody tr:nth-child(2n) {
background: var(--table-alternate-bg);
}
blockquote {
margin: 20px 0;
padding: 0 20px;
color: var(--fg);
background-color: var(--quote-bg);
border-top: .1em solid var(--quote-border);
border-bottom: .1em solid var(--quote-border);
}
kbd {
background-color: var(--table-border-color);
border-radius: 4px;
border: solid 1px var(--theme-popup-border);
box-shadow: inset 0 -1px 0 var(--theme-hover);
display: inline-block;
font-size: var(--code-font-size);
font-family: var(--mono-font);
line-height: 10px;
padding: 4px 5px;
vertical-align: middle;
}
:not(.footnote-definition) + .footnote-definition,
.footnote-definition + :not(.footnote-definition) {
margin-top: 2em;
}
.footnote-definition {
font-size: 0.9em;
margin: 0.5em 0;
}
.footnote-definition p {
display: inline;
}
.tooltiptext {
position: absolute;
visibility: hidden;
color: #fff;
background-color: #333;
transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */
left: -8px; /* Half of the width of the icon */
top: -35px;
font-size: 0.8em;
text-align: center;
border-radius: 6px;
padding: 5px 8px;
margin: 5px;
z-index: 1000;
}
.tooltipped .tooltiptext {
visibility: visible;
}
.chapter li.part-title {
color: var(--sidebar-fg);
margin: 5px 0px;
font-weight: bold;
}
.result-no-output {
font-style: italic;
}

View File

@@ -0,0 +1,71 @@
#sidebar,
#menu-bar,
.nav-collections,
.mobile-nav-collections {
display: none;
}
#page-wrapper.page-wrapper {
transform: none;
margin-left: 0px;
overflow-y: initial;
}
#content {
max-width: none;
margin: 0;
padding: 0;
}
.page {
overflow-y: initial;
}
code {
background-color: #666666;
border-radius: 5px;
/* Force background to be printed in Chrome */
-webkit-print-color-adjust: exact;
}
pre > .buttons {
z-index: 2;
}
a, a:visited, a:active, a:hover {
color: #4183c4;
text-decoration: none;
}
h1, h2 {
page-break-inside: avoid;
page-break-after: avoid;
}
h3, h4, h5, h6 {
page-break-inside: avoid;
/* page-break-before: avoid; */
page-break-after: avoid;
}
pre, code {
page-break-inside: avoid;
white-space: pre-wrap;
}
.fa {
display: none !important;
}
body {
font-size: 1.25rem !important;
}
@@media print {
h1 {
page-break-before: always;
}
}

View File

@@ -0,0 +1,265 @@
/* Globals */
:root {
--sidebar-width: 250px;
--page-padding: 15px;
--content-max-width: 1600px;
--menu-bar-height: 50px;
--mono-font: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace;
--code-font-size: 0.875em /* please adjust the ace font size accordingly in editor.js */
}
/* Themes */
.ayu {
--bg: hsl(210, 25%, 8%);
--fg: #c5c5c5;
--sidebar-bg: #14191f;
--sidebar-fg: #c8c9db;
--sidebar-non-existant: #5c6773;
--sidebar-active: #ffb454;
--sidebar-spacer: #2d334f;
--scrollbar: var(--sidebar-fg);
--icons: #737480;
--icons-hover: #b7b9cc;
--links: #0096cf;
--inline-code-color: #ffb454;
--theme-popup-bg: #14191f;
--theme-popup-border: #5c6773;
--theme-hover: #191f26;
--quote-bg: hsl(226, 15%, 17%);
--quote-border: hsl(226, 15%, 22%);
--table-border-color: hsl(210, 25%, 13%);
--table-header-bg: hsl(210, 25%, 28%);
--table-alternate-bg: hsl(210, 25%, 11%);
--searchbar-border-color: #848484;
--searchbar-bg: #424242;
--searchbar-fg: #fff;
--searchbar-shadow-color: #d4c89f;
--searchresults-header-fg: #666;
--searchresults-border-color: #888;
--searchresults-li-bg: #252932;
--search-mark-bg: #e3b171;
--color-scheme: dark;
}
.coal {
--bg: hsl(200, 7%, 8%);
--fg: #98a3ad;
--sidebar-bg: #292c2f;
--sidebar-fg: #a1adb8;
--sidebar-non-existant: #505254;
--sidebar-active: #3473ad;
--sidebar-spacer: #393939;
--scrollbar: var(--sidebar-fg);
--icons: #43484d;
--icons-hover: #b3c0cc;
--links: #2b79a2;
--inline-code-color: #c5c8c6;
--theme-popup-bg: #141617;
--theme-popup-border: #43484d;
--theme-hover: #1f2124;
--quote-bg: hsl(234, 21%, 18%);
--quote-border: hsl(234, 21%, 23%);
--table-border-color: hsl(200, 7%, 13%);
--table-header-bg: hsl(200, 7%, 28%);
--table-alternate-bg: hsl(200, 7%, 11%);
--searchbar-border-color: #aaa;
--searchbar-bg: #b7b7b7;
--searchbar-fg: #000;
--searchbar-shadow-color: #aaa;
--searchresults-header-fg: #666;
--searchresults-border-color: #98a3ad;
--searchresults-li-bg: #2b2b2f;
--search-mark-bg: #355c7d;
--color-scheme: dark;
}
.light {
--bg: hsl(0, 0%, 100%);
--fg: hsl(0, 0%, 0%);
--sidebar-bg: #fafafa;
--sidebar-fg: hsl(0, 0%, 0%);
--sidebar-non-existant: #aaaaaa;
--sidebar-active: #1f1fff;
--sidebar-spacer: #f4f4f4;
--scrollbar: #8F8F8F;
--icons: #747474;
--icons-hover: #000000;
--links: #20609f;
--inline-code-color: #301900;
--theme-popup-bg: #fafafa;
--theme-popup-border: #cccccc;
--theme-hover: #e6e6e6;
--quote-bg: hsl(197, 37%, 96%);
--quote-border: hsl(197, 37%, 91%);
--table-border-color: hsl(0, 0%, 95%);
--table-header-bg: hsl(0, 0%, 80%);
--table-alternate-bg: hsl(0, 0%, 97%);
--searchbar-border-color: #aaa;
--searchbar-bg: #fafafa;
--searchbar-fg: #000;
--searchbar-shadow-color: #aaa;
--searchresults-header-fg: #666;
--searchresults-border-color: #888;
--searchresults-li-bg: #e4f2fe;
--search-mark-bg: #a2cff5;
--color-scheme: light;
}
.navy {
--bg: hsl(226, 23%, 11%);
--fg: #bcbdd0;
--sidebar-bg: #282d3f;
--sidebar-fg: #c8c9db;
--sidebar-non-existant: #505274;
--sidebar-active: #2b79a2;
--sidebar-spacer: #2d334f;
--scrollbar: var(--sidebar-fg);
--icons: #737480;
--icons-hover: #b7b9cc;
--links: #2b79a2;
--inline-code-color: #c5c8c6;
--theme-popup-bg: #161923;
--theme-popup-border: #737480;
--theme-hover: #282e40;
--quote-bg: hsl(226, 15%, 17%);
--quote-border: hsl(226, 15%, 22%);
--table-border-color: hsl(226, 23%, 16%);
--table-header-bg: hsl(226, 23%, 31%);
--table-alternate-bg: hsl(226, 23%, 14%);
--searchbar-border-color: #aaa;
--searchbar-bg: #aeaec6;
--searchbar-fg: #000;
--searchbar-shadow-color: #aaa;
--searchresults-header-fg: #5f5f71;
--searchresults-border-color: #5c5c68;
--searchresults-li-bg: #242430;
--search-mark-bg: #a2cff5;
--color-scheme: dark;
}
.rust {
--bg: hsl(60, 9%, 87%);
--fg: #262625;
--sidebar-bg: #3b2e2a;
--sidebar-fg: #c8c9db;
--sidebar-non-existant: #505254;
--sidebar-active: #e69f67;
--sidebar-spacer: #45373a;
--scrollbar: var(--sidebar-fg);
--icons: #737480;
--icons-hover: #262625;
--links: #2b79a2;
--inline-code-color: #6e6b5e;
--theme-popup-bg: #e1e1db;
--theme-popup-border: #b38f6b;
--theme-hover: #99908a;
--quote-bg: hsl(60, 5%, 75%);
--quote-border: hsl(60, 5%, 70%);
--table-border-color: hsl(60, 9%, 82%);
--table-header-bg: #b3a497;
--table-alternate-bg: hsl(60, 9%, 84%);
--searchbar-border-color: #aaa;
--searchbar-bg: #fafafa;
--searchbar-fg: #000;
--searchbar-shadow-color: #aaa;
--searchresults-header-fg: #666;
--searchresults-border-color: #888;
--searchresults-li-bg: #dec2a2;
--search-mark-bg: #e69f67;
--color-scheme: light;
}
@media (prefers-color-scheme: dark) {
.light.no-js {
--bg: hsl(200, 7%, 8%);
--fg: #98a3ad;
--sidebar-bg: #292c2f;
--sidebar-fg: #a1adb8;
--sidebar-non-existant: #505254;
--sidebar-active: #3473ad;
--sidebar-spacer: #393939;
--scrollbar: var(--sidebar-fg);
--icons: #43484d;
--icons-hover: #b3c0cc;
--links: #2b79a2;
--inline-code-color: #c5c8c6;
--theme-popup-bg: #141617;
--theme-popup-border: #43484d;
--theme-hover: #1f2124;
--quote-bg: hsl(234, 21%, 18%);
--quote-border: hsl(234, 21%, 23%);
--table-border-color: hsl(200, 7%, 13%);
--table-header-bg: hsl(200, 7%, 28%);
--table-alternate-bg: hsl(200, 7%, 11%);
--searchbar-border-color: #aaa;
--searchbar-bg: #b7b7b7;
--searchbar-fg: #000;
--searchbar-shadow-color: #aaa;
--searchresults-header-fg: #666;
--searchresults-border-color: #98a3ad;
--searchresults-li-bg: #2b2b2f;
--search-mark-bg: #355c7d;
}
}

View File

@@ -0,0 +1,3 @@
set -e
cd ${book.path_build.path}
mdbook serve . -p 8884 -n localhost --open --dest-dir ${book.path_publish.path}

45
lib/web/mdbook/template/echarts.min.js vendored Normal file

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More