the base
This commit is contained in:
46
.github/workflows/documentation.yml
vendored
Normal file
46
.github/workflows/documentation.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Deploy Documentation to Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["development"]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
deploy-documentation:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Vlang dependencies
|
||||
run: sudo apt update && sudo apt install -y libgc-dev
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Vlang
|
||||
run: ./install_v.sh
|
||||
|
||||
- name: Generate documentation
|
||||
run: ./doc.vsh
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v3
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
path: "/home/runner/work/crystallib/crystallib/docs"
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
55
.github/workflows/hero_build_s3.yml
vendored
Normal file
55
.github/workflows/hero_build_s3.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Test Crystal & Release to S3
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["development"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: x86_64-unknown-linux-musl
|
||||
os: ubuntu-latest
|
||||
short-name: linux-i64
|
||||
# - target: aarch64-unknown-linux-musl
|
||||
# os: ubuntu-latest
|
||||
# short-name: linux-arm64
|
||||
# - target: aarch64-apple-darwin
|
||||
# os: macos-latest
|
||||
# short-name: macos-arm64
|
||||
# - target: x86_64-apple-darwin
|
||||
# os: macos-latest
|
||||
# short-name: macos-i64
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
|
||||
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"
|
||||
- run: echo "🔎 The name of your branch is ${{ github.ref_name }} and your repository is ${{ github.repository }}."
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Vlang
|
||||
run: ./install_v.sh
|
||||
|
||||
- name: Setup Herolib
|
||||
run: ./install_herolib.vsh
|
||||
|
||||
- name: Do all the basic tests
|
||||
run: ./test_basic.vsh
|
||||
|
||||
# - name: Upload to S3
|
||||
# run: |
|
||||
# echo 'export S3KEYID=${{ secrets.S3KEYID }}' > ${HOME}/mysecrets.sh
|
||||
# echo 'export S3APPID=${{ secrets.S3APPID }}' >> ${HOME}/mysecrets.sh
|
||||
# set -e && cat ${HOME}/mysecrets.sh
|
||||
# sudo bash +x scripts/githubactions.sh
|
||||
|
||||
- name: Extract tag name
|
||||
run: echo "TAG_NAME=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -27,3 +27,4 @@ output/
|
||||
.stellar
|
||||
vdocs/
|
||||
data.ms/
|
||||
test_basic
|
||||
@@ -1,6 +1,6 @@
|
||||
module codeparser
|
||||
|
||||
// import freeflowuniverse.herolib.core.codemodel {Example}
|
||||
// import freeflowuniverse.herolib.code.codemodel {Example}
|
||||
// import freeflowuniverse.herolib.rpc.openrpc {ExamplePairing}
|
||||
|
||||
// pub fn parse_example_pairing(text_ string) !ExamplePairing {
|
||||
|
||||
@@ -4,7 +4,7 @@ import v.ast
|
||||
import v.parser
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.codemodel { CodeFile, CodeItem, Function, Import, Module, Param, Result, Struct, StructField, Sumtype, Type, parse_consts, parse_import }
|
||||
import freeflowuniverse.herolib.code.codemodel { CodeFile, CodeItem, Function, Import, Module, Param, Result, Struct, StructField, Sumtype, Type, parse_consts, parse_import }
|
||||
import v.pref
|
||||
|
||||
// VParser holds configuration of parsing
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module codeparser
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel { CodeItem, Function, Struct }
|
||||
import freeflowuniverse.herolib.code.codemodel { CodeItem, Function, Struct }
|
||||
import os
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
@@ -20,7 +20,7 @@ const testcode = {
|
||||
symbol: 'void'
|
||||
}
|
||||
}
|
||||
mod: 'core.codeparser.testdata.flatdir'
|
||||
mod: 'code.codeparser.testdata.flatdir'
|
||||
description: 'is the first function of file'
|
||||
params: []
|
||||
body: ''
|
||||
@@ -43,7 +43,7 @@ const testcode = {
|
||||
symbol: 'void'
|
||||
}
|
||||
}
|
||||
mod: 'core.codeparser.testdata.flatdir'
|
||||
mod: 'code.codeparser.testdata.flatdir'
|
||||
description: 'is the second function of file'
|
||||
params: [
|
||||
codemodel.Param{
|
||||
@@ -68,7 +68,7 @@ const testcode = {
|
||||
CodeItem(Struct{
|
||||
name: 'AnotherfileStruct0'
|
||||
description: 'AnotherfileStruct0 defines the configuration params of anotherfile_func2'
|
||||
mod: 'core.codeparser.testdata.flatdir'
|
||||
mod: 'code.codeparser.testdata.flatdir'
|
||||
is_pub: true
|
||||
attrs: [
|
||||
codemodel.Attribute{
|
||||
@@ -118,7 +118,7 @@ const testcode = {
|
||||
symbol: 'void'
|
||||
}
|
||||
}
|
||||
mod: 'core.codeparser.testdata.flatdir'
|
||||
mod: 'code.codeparser.testdata.flatdir'
|
||||
description: 'is the third function of the file'
|
||||
params: [
|
||||
codemodel.Param{
|
||||
@@ -143,7 +143,7 @@ const testcode = {
|
||||
CodeItem(Struct{
|
||||
name: 'AnotherfileStruct1'
|
||||
description: ''
|
||||
mod: 'core.codeparser.testdata.flatdir'
|
||||
mod: 'code.codeparser.testdata.flatdir'
|
||||
is_pub: true
|
||||
fields: [
|
||||
codemodel.StructField{
|
||||
@@ -172,7 +172,7 @@ const testcode = {
|
||||
symbol: 'void'
|
||||
}
|
||||
}
|
||||
mod: 'core.codeparser.testdata.flatdir'
|
||||
mod: 'code.codeparser.testdata.flatdir'
|
||||
description: 'is the fourth function of the file is does something with param1 and param2 and creates AnotherfileStruct1'
|
||||
params: [
|
||||
codemodel.Param{
|
||||
@@ -214,7 +214,7 @@ const testcode = {
|
||||
symbol: 'void'
|
||||
}
|
||||
}
|
||||
mod: 'core.codeparser.testdata.flatdir'
|
||||
mod: 'code.codeparser.testdata.flatdir'
|
||||
description: 'is the first function of file'
|
||||
params: []
|
||||
body: ''
|
||||
@@ -237,7 +237,7 @@ const testcode = {
|
||||
symbol: 'void'
|
||||
}
|
||||
}
|
||||
mod: 'core.codeparser.testdata.flatdir'
|
||||
mod: 'code.codeparser.testdata.flatdir'
|
||||
description: 'is the second function of file'
|
||||
params: [
|
||||
codemodel.Param{
|
||||
@@ -262,7 +262,7 @@ const testcode = {
|
||||
CodeItem(Struct{
|
||||
name: 'SubfileStruct0'
|
||||
description: 'SubfileStruct0 defines the configuration params of subfile_func2'
|
||||
mod: 'core.codeparser.testdata.flatdir'
|
||||
mod: 'code.codeparser.testdata.flatdir'
|
||||
is_pub: true
|
||||
attrs: [
|
||||
codemodel.Attribute{
|
||||
@@ -312,7 +312,7 @@ const testcode = {
|
||||
symbol: 'void'
|
||||
}
|
||||
}
|
||||
mod: 'core.codeparser.testdata.flatdir'
|
||||
mod: 'code.codeparser.testdata.flatdir'
|
||||
description: 'is the third function of the file'
|
||||
params: [
|
||||
codemodel.Param{
|
||||
@@ -337,7 +337,7 @@ const testcode = {
|
||||
CodeItem(Struct{
|
||||
name: 'SubfileStruct1'
|
||||
description: ''
|
||||
mod: 'core.codeparser.testdata.flatdir'
|
||||
mod: 'code.codeparser.testdata.flatdir'
|
||||
is_pub: true
|
||||
fields: [
|
||||
codemodel.StructField{
|
||||
@@ -366,7 +366,7 @@ const testcode = {
|
||||
symbol: 'void'
|
||||
}
|
||||
}
|
||||
mod: 'core.codeparser.testdata.flatdir'
|
||||
mod: 'code.codeparser.testdata.flatdir'
|
||||
description: 'is the fourth function of the file is does something with param1 and param2 and creates SubfileStruct1'
|
||||
params: [
|
||||
codemodel.Param{
|
||||
@@ -408,7 +408,7 @@ const testcode = {
|
||||
symbol: 'void'
|
||||
}
|
||||
}
|
||||
mod: 'core.codeparser.testdata'
|
||||
mod: 'code.codeparser.testdata'
|
||||
description: 'is the first function of file'
|
||||
params: []
|
||||
body: ''
|
||||
@@ -431,7 +431,7 @@ const testcode = {
|
||||
symbol: 'void'
|
||||
}
|
||||
}
|
||||
mod: 'core.codeparser.testdata'
|
||||
mod: 'code.codeparser.testdata'
|
||||
description: 'is the second function of file'
|
||||
params: [
|
||||
codemodel.Param{
|
||||
@@ -456,7 +456,7 @@ const testcode = {
|
||||
CodeItem(Struct{
|
||||
name: 'FileStruct0'
|
||||
description: 'FileStruct0 defines the configuration params of file_func2'
|
||||
mod: 'core.codeparser.testdata'
|
||||
mod: 'code.codeparser.testdata'
|
||||
is_pub: true
|
||||
attrs: [
|
||||
codemodel.Attribute{
|
||||
@@ -506,7 +506,7 @@ const testcode = {
|
||||
symbol: 'void'
|
||||
}
|
||||
}
|
||||
mod: 'core.codeparser.testdata'
|
||||
mod: 'code.codeparser.testdata'
|
||||
description: 'is the third function of the file'
|
||||
params: [
|
||||
codemodel.Param{
|
||||
@@ -532,7 +532,7 @@ const testcode = {
|
||||
name: 'FileStruct1'
|
||||
description: ''
|
||||
fields: []
|
||||
mod: 'core.codeparser.testdata'
|
||||
mod: 'code.codeparser.testdata'
|
||||
is_pub: true
|
||||
}),
|
||||
CodeItem(Function{
|
||||
@@ -545,7 +545,7 @@ const testcode = {
|
||||
symbol: 'void'
|
||||
}
|
||||
}
|
||||
mod: 'core.codeparser.testdata'
|
||||
mod: 'code.codeparser.testdata'
|
||||
description: 'is the fourth function of the file is does something with param1 and param2 and creates FileStruct1'
|
||||
params: [
|
||||
codemodel.Param{
|
||||
|
||||
210
lib/code/generator/generic/factory.v
Normal file
210
lib/code/generator/generic/factory.v
Normal 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)!
|
||||
}
|
||||
78
lib/code/generator/generic/generator.v
Normal file
78
lib/code/generator/generic/generator.v
Normal 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
|
||||
}
|
||||
84
lib/code/generator/generic/model.v
Normal file
84
lib/code/generator/generic/model.v
Normal 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{}
|
||||
}
|
||||
74
lib/code/generator/generic/readme.md
Normal file
74
lib/code/generator/generic/readme.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# 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 !!!
|
||||
|
||||
```
|
||||
|
||||
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.code.generator.generic
|
||||
|
||||
generic.scan(path:"~/code/github/freeflowuniverse/herolib/herolib/installers",force:true)!
|
||||
|
||||
|
||||
```
|
||||
|
||||
to run from bash
|
||||
|
||||
```bash
|
||||
~/code/github/freeflowuniverse/herolib/scripts/fix_installers.vsh
|
||||
```
|
||||
|
||||
30
lib/code/generator/generic/scanner.v
Normal file
30
lib/code/generator/generic/scanner.v
Normal 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)!
|
||||
}
|
||||
}
|
||||
5
lib/code/generator/generic/templates/atemplate.yaml
Normal file
5
lib/code/generator/generic/templates/atemplate.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
name: ??{cfg.configpath}
|
||||
|
||||
|
||||
13
lib/code/generator/generic/templates/heroscript_example
Normal file
13
lib/code/generator/generic/templates/heroscript_example
Normal 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
|
||||
212
lib/code/generator/generic/templates/objname_actions.vtemplate
Normal file
212
lib/code/generator/generic/templates/objname_actions.vtemplate
Normal 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
|
||||
313
lib/code/generator/generic/templates/objname_factory_.vtemplate
Normal file
313
lib/code/generator/generic/templates/objname_factory_.vtemplate
Normal 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
|
||||
}
|
||||
154
lib/code/generator/generic/templates/objname_model.vtemplate
Normal file
154
lib/code/generator/generic/templates/objname_model.vtemplate
Normal file
@@ -0,0 +1,154 @@
|
||||
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'
|
||||
|
||||
"
|
||||
|
||||
// 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}'
|
||||
//
|
||||
// "
|
||||
//
|
||||
|
||||
@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
|
||||
|
||||
|
||||
63
lib/code/generator/generic/templates/readme.md
Normal file
63
lib/code/generator/generic/templates/readme.md
Normal 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
|
||||
|
||||
|
||||
94
lib/core/vexecutor/execute_large.v
Normal file
94
lib/core/vexecutor/execute_large.v
Normal file
@@ -0,0 +1,94 @@
|
||||
module vexecutor
|
||||
|
||||
import strings
|
||||
import os { Result, fileno }
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
fn vpopen(path string) voidptr {
|
||||
// *C.FILE {
|
||||
$if windows {
|
||||
mode := 'rb'
|
||||
wpath := path.to_wide()
|
||||
return C._wpopen(wpath, mode.to_wide())
|
||||
} $else {
|
||||
cpath := path.str
|
||||
return C.popen(&char(cpath), c'r')
|
||||
}
|
||||
}
|
||||
|
||||
fn vpclose(f voidptr) int {
|
||||
$if windows {
|
||||
return C._pclose(f)
|
||||
} $else {
|
||||
ret, _ := posix_wait4_to_exit_status(C.pclose(f))
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
fn posix_wait4_to_exit_status(waitret int) (int, bool) {
|
||||
$if windows {
|
||||
return waitret, false
|
||||
} $else {
|
||||
mut ret := 0
|
||||
mut is_signaled := true
|
||||
// (see man system, man 2 waitpid: C macro WEXITSTATUS section)
|
||||
if C.WIFEXITED(waitret) {
|
||||
ret = C.WEXITSTATUS(waitret)
|
||||
is_signaled = false
|
||||
} else if C.WIFSIGNALED(waitret) {
|
||||
ret = C.WTERMSIG(waitret)
|
||||
is_signaled = true
|
||||
}
|
||||
return ret, is_signaled
|
||||
}
|
||||
}
|
||||
|
||||
@[manualfree]
|
||||
pub fn execute_large(cmd string) Result {
|
||||
// if cmd.contains(';') || cmd.contains('&&') || cmd.contains('||') || cmd.contains('\n') {
|
||||
// return Result{ exit_code: -1, output: ';, &&, || and \\n are not allowed in shell commands' }
|
||||
// }
|
||||
pcmd := if cmd.contains('2>') { cmd.clone() } else { '${cmd} 2>&1' }
|
||||
defer {
|
||||
unsafe { pcmd.free() }
|
||||
}
|
||||
f := vpopen(pcmd)
|
||||
if isnil(f) {
|
||||
return Result{
|
||||
exit_code: -1
|
||||
output: 'exec("${cmd}") failed'
|
||||
}
|
||||
}
|
||||
fd := fileno(f)
|
||||
mut res := strings.new_builder(4096)
|
||||
defer {
|
||||
unsafe { res.free() }
|
||||
}
|
||||
buf := [8192]u8{}
|
||||
unsafe {
|
||||
pbuf := &buf[0]
|
||||
for {
|
||||
len := C.read(fd, pbuf, 8192)
|
||||
if len == 0 {
|
||||
break
|
||||
}
|
||||
res.write_ptr(pbuf, len)
|
||||
}
|
||||
}
|
||||
soutput := res.str()
|
||||
exit_code := vpclose(f)
|
||||
return Result{
|
||||
exit_code: exit_code
|
||||
output: soutput
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_large_or_panic(cmd string) Result {
|
||||
res := execute_large(cmd)
|
||||
if res.exit_code != 0 {
|
||||
console.print_debug('failed cmd: ${cmd}')
|
||||
console.print_debug('failed code: ${res.exit_code}')
|
||||
panic(res.output)
|
||||
}
|
||||
return res
|
||||
}
|
||||
169
lib/core/vexecutor/vexecutor.v
Normal file
169
lib/core/vexecutor/vexecutor.v
Normal file
@@ -0,0 +1,169 @@
|
||||
module vexecutor
|
||||
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import os
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
// TODO make sure each function opens and closes the executor.vsh file, no file should be open across functions
|
||||
|
||||
struct VExecutor {
|
||||
mut:
|
||||
actions []VAction
|
||||
execution_file os.File
|
||||
execution_file_path pathlib.Path
|
||||
final_file_path pathlib.Path
|
||||
}
|
||||
|
||||
struct VAction {
|
||||
path pathlib.Path
|
||||
lines []string
|
||||
}
|
||||
|
||||
// Creates a new executor object
|
||||
// ARGS:
|
||||
// path string - /directory/filename.v
|
||||
// cat Category - unknown, file, dir, linkdir, linkfile (enum)
|
||||
// exist UYN - unknown, yes, no (enum)
|
||||
pub fn new_executor(initial_file_path_ string, execution_file_path_ string, final_file_path_ string) !VExecutor {
|
||||
// create a execution file at execution_file_path and fill out
|
||||
mut execution_file_path := pathlib.get(execution_file_path_)
|
||||
execution_file_path.check()
|
||||
|
||||
mut initial_file_path := pathlib.get(initial_file_path_)
|
||||
initial_file_path.check()
|
||||
|
||||
mut final_file_path := pathlib.get(final_file_path_)
|
||||
final_file_path.check()
|
||||
|
||||
// ensure that the execution file is an empty file
|
||||
if execution_file_path.exist == .yes {
|
||||
os.rm(execution_file_path.path) or {
|
||||
return error('Failed to remove execution file: ${err}')
|
||||
}
|
||||
}
|
||||
execution_file := os.create(execution_file_path.path) or {
|
||||
return error('Failed to create execution file at ' + @FN + ' : ${err}')
|
||||
}
|
||||
|
||||
if initial_file_path.exist == .no {
|
||||
return error('Initial file does not exist at path: ${initial_file_path.path}')
|
||||
}
|
||||
|
||||
// convert the initial file into an action
|
||||
initial_action := scan_file(mut initial_file_path) or {
|
||||
return error('Failed to scan initial file path at ' + @FN + ' : ${err}')
|
||||
}
|
||||
|
||||
// create an VExecutor object
|
||||
mut v_executor := VExecutor{
|
||||
execution_file: execution_file
|
||||
execution_file_path: execution_file_path
|
||||
final_file_path: final_file_path
|
||||
actions: [initial_action]
|
||||
}
|
||||
|
||||
return v_executor
|
||||
}
|
||||
|
||||
// Adds all the top level files in a directory to the VExecutor.actions list
|
||||
// ARGS:
|
||||
// directory_path string
|
||||
pub fn (mut v_executor VExecutor) add_dir_to_end(directory_path_ string) ! {
|
||||
mut directory_path := pathlib.get(directory_path_)
|
||||
directory_path.check()
|
||||
|
||||
mut fl := directory_path.list() or {
|
||||
return error('Failed to get file_paths of directory at ' + @FN + ' : ${err}')
|
||||
}
|
||||
// mut count := 0
|
||||
for mut file_path in fl.paths {
|
||||
// if count <= 10 {
|
||||
v_executor.actions << scan_file(mut file_path)!
|
||||
// count += 1
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// Scans a file and converts it into a VAction
|
||||
// cuts out all content prior to // BEGINNING
|
||||
// ARGS:
|
||||
// path string - path to file
|
||||
fn scan_file(mut path pathlib.Path) !VAction {
|
||||
if !path.exists() {
|
||||
return error('cannot find path: ${path.path} to scan for vlang files.')
|
||||
}
|
||||
|
||||
// read all the lines in the file into an array of lines
|
||||
mut file := os.open(path.path) or { return error('Failed to open file: ${err}') }
|
||||
mut lines := os.read_lines(path.path) or {
|
||||
return error('Failed to readlines of file at ' + @FN + ' : ${err}')
|
||||
}
|
||||
file.close()
|
||||
// remove lines until '// BEGINNING' is reached
|
||||
mut beginning_found := false
|
||||
|
||||
mut count := 0
|
||||
outer: for line in lines {
|
||||
count += 1
|
||||
if line.contains('// BEGINNING') {
|
||||
beginning_found = true
|
||||
break outer
|
||||
}
|
||||
}
|
||||
mut trimmed_lines := lines.clone()
|
||||
if beginning_found == true {
|
||||
trimmed_lines = lines[count..lines.len]
|
||||
}
|
||||
|
||||
return VAction{
|
||||
lines: trimmed_lines
|
||||
path: path
|
||||
}
|
||||
}
|
||||
|
||||
// combine all actions in VExecutor into one file
|
||||
pub fn (mut v_executor VExecutor) compile() ! {
|
||||
v_executor.actions << scan_file(mut v_executor.final_file_path) or {
|
||||
return error('Failed to scan file at ' + @FN + ' : ${err}')
|
||||
}
|
||||
|
||||
mut all_lines := []string{}
|
||||
for action in v_executor.actions {
|
||||
for line in action.lines {
|
||||
all_lines << line
|
||||
}
|
||||
}
|
||||
|
||||
for line in all_lines {
|
||||
v_executor.execution_file.writeln(line) or {
|
||||
return error('Failed to write line to execution file at ' + @FN + ' : ${err}')
|
||||
}
|
||||
}
|
||||
|
||||
v_executor.execution_file.close()
|
||||
}
|
||||
|
||||
// Executes the file
|
||||
pub fn (mut v_executor VExecutor) do() ! {
|
||||
console.print_debug('v run ${v_executor.execution_file_path.path}')
|
||||
result := os.execute('v run ${v_executor.execution_file_path.path}')
|
||||
console.print_debug('Exit Code: ${result.exit_code}')
|
||||
if result.exit_code != 0 {
|
||||
console.print_debug(result)
|
||||
return error('Failed to run executor file! There are issues with the code.')
|
||||
}
|
||||
}
|
||||
|
||||
// prints out all the file paths accessed
|
||||
pub fn (mut v_executor VExecutor) info() {
|
||||
for action in v_executor.actions {
|
||||
console.print_debug('action_path: ${action.path.path}')
|
||||
}
|
||||
}
|
||||
|
||||
// Cleans up VExecutors impact on the file system
|
||||
pub fn (mut v_executor VExecutor) clean() ! {
|
||||
os.rm(v_executor.execution_file_path.path) or {
|
||||
return error('Failed to delete execution file at ' + @FN + ' : ${err}')
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
module jsonschema
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel { Alias, Attribute, CodeItem, Struct, StructField, Type }
|
||||
import freeflowuniverse.herolib.code.codemodel { Alias, Attribute, CodeItem, Struct, StructField, Type }
|
||||
|
||||
const vtypes = {
|
||||
'integer': 'int'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module jsonschema
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel { Param, Result, Struct, Type }
|
||||
import freeflowuniverse.herolib.code.codemodel { Param, Result, Struct, Type }
|
||||
|
||||
// struct_to_schema generates a json schema or reference from a struct model
|
||||
pub fn sumtype_to_schema(sumtype codemodel.Sumtype) SchemaRef {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module jsonschema
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel
|
||||
import freeflowuniverse.herolib.code.codemodel
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
fn test_struct_to_schema() {
|
||||
|
||||
@@ -14,13 +14,15 @@ fn check_redis() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
const redis_key_prefix = 'vtests'
|
||||
|
||||
// Set Redis key with expiration
|
||||
fn redis_set(key string) ! {
|
||||
mut sock := net.dial_tcp('127.0.0.1:6379')!
|
||||
defer { sock.close() or {} }
|
||||
|
||||
// SET key value EX seconds
|
||||
cmd := 'SET vtests.${key} 1 EX 3600\r\n'
|
||||
cmd := 'SET ${redis_key_prefix}.${key} 1 EX 3600\r\n'
|
||||
sock.write_string(cmd)!
|
||||
}
|
||||
|
||||
@@ -30,7 +32,7 @@ fn redis_exists(key string) bool {
|
||||
defer { sock.close() or {} }
|
||||
|
||||
// EXISTS key
|
||||
cmd := 'EXISTS vtests.${key}\r\n'
|
||||
cmd := 'EXISTS ${redis_key_prefix}.${key}\r\n'
|
||||
sock.write_string(cmd) or { return false }
|
||||
|
||||
response := sock.read_line()
|
||||
@@ -38,19 +40,41 @@ fn redis_exists(key string) bool {
|
||||
}
|
||||
|
||||
// Delete Redis key
|
||||
fn redis_del(key string) ! {
|
||||
mut sock := net.dial_tcp('127.0.0.1:6379')!
|
||||
defer { sock.close() or {} }
|
||||
// fn redis_del(key string) ! {
|
||||
// mut sock := net.dial_tcp('127.0.0.1:6379')!
|
||||
// defer { sock.close() or {} }
|
||||
|
||||
// DEL key
|
||||
cmd := 'DEL vtests.${key}\r\n'
|
||||
sock.write_string(cmd)!
|
||||
// // DEL key
|
||||
// cmd := 'DEL ${redis_key_prefix}.${key}\r\n'
|
||||
// sock.write_string(cmd)!
|
||||
// }
|
||||
|
||||
// Normalize a path for consistent handling
|
||||
fn normalize_path(path string) string {
|
||||
mut norm_path := os.abs_path(path)
|
||||
norm_path = norm_path.replace('//', '/') // Remove any double slashes
|
||||
return norm_path
|
||||
}
|
||||
|
||||
// Get normalized and relative path
|
||||
fn get_normalized_paths(path string, base_dir_norm string) (string, string) {
|
||||
// base_dir_norm is already normalized
|
||||
norm_path := normalize_path(path)
|
||||
rel_path := norm_path.replace(base_dir_norm + '/', '')
|
||||
return norm_path, rel_path
|
||||
}
|
||||
|
||||
// Generate a Redis key from a path
|
||||
fn get_redis_key(path string, base_dir string) string {
|
||||
_, rel_path := get_normalized_paths(path, base_dir)
|
||||
// Create consistent key format
|
||||
return rel_path.replace('/', '_').trim('_').to_lower()
|
||||
}
|
||||
|
||||
// Check if a file should be ignored or marked as error based on its path
|
||||
fn process_test_file(path string, test_files_ignore []string, test_files_error []string, redis_available bool, mut tests_in_error []string)! {
|
||||
// Get relative path for more accurate pattern matching
|
||||
rel_path := path.replace(os.abs_path('.') + '/', '')
|
||||
fn process_test_file(path string, base_dir string, test_files_ignore []string, test_files_error []string, redis_available bool, mut tests_in_error []string)! {
|
||||
// Get normalized paths
|
||||
norm_path, rel_path := get_normalized_paths(path, base_dir)
|
||||
|
||||
mut should_ignore := false
|
||||
mut is_error := false
|
||||
@@ -72,7 +96,7 @@ fn process_test_file(path string, test_files_ignore []string, test_files_error [
|
||||
}
|
||||
|
||||
if !should_ignore && !is_error {
|
||||
dotest(path, redis_available)!
|
||||
dotest(norm_path, base_dir, redis_available)!
|
||||
} else {
|
||||
println('Ignoring test: ${rel_path}')
|
||||
if !should_ignore {
|
||||
@@ -81,11 +105,11 @@ fn process_test_file(path string, test_files_ignore []string, test_files_error [
|
||||
}
|
||||
}
|
||||
|
||||
fn dotest(path string, use_redis bool)! {
|
||||
fn dotest(path string, base_dir string, use_redis bool)! {
|
||||
norm_path, _ := get_normalized_paths(path, base_dir)
|
||||
|
||||
if use_redis {
|
||||
// Use absolute path as Redis key
|
||||
abs_path := os.abs_path(path)
|
||||
redis_key := abs_path.replace('/', '_')
|
||||
redis_key := get_redis_key(norm_path, base_dir)
|
||||
|
||||
// Check if test result is cached
|
||||
if redis_exists(redis_key) {
|
||||
@@ -94,7 +118,7 @@ fn dotest(path string, use_redis bool)! {
|
||||
}
|
||||
}
|
||||
|
||||
cmd := 'vtest ${path}'
|
||||
cmd := 'vtest ${norm_path}'
|
||||
println(cmd)
|
||||
result := os.execute(cmd)
|
||||
|
||||
@@ -105,9 +129,7 @@ fn dotest(path string, use_redis bool)! {
|
||||
}
|
||||
|
||||
if use_redis {
|
||||
// Cache successful test result
|
||||
abs_path := os.abs_path(path)
|
||||
redis_key := abs_path.replace('/', '_')
|
||||
redis_key := get_redis_key(norm_path, base_dir)
|
||||
redis_set(redis_key) or {
|
||||
eprintln('Failed to cache test result: ${err}')
|
||||
}
|
||||
@@ -122,12 +144,16 @@ fn dotest(path string, use_redis bool)! {
|
||||
|
||||
|
||||
abs_dir_of_script := dir(@FILE)
|
||||
norm_dir_of_script := normalize_path(abs_dir_of_script)
|
||||
os.chdir(abs_dir_of_script) or { panic(err) }
|
||||
|
||||
|
||||
|
||||
// can use // inside this list as well to ignore temporary certain dirs, useful for testing
|
||||
tests := "
|
||||
lib/data
|
||||
lib/osal
|
||||
lib/lang
|
||||
lib/data
|
||||
lib/code
|
||||
lib/clients
|
||||
"
|
||||
@@ -168,6 +194,9 @@ ourdb/db_test.v
|
||||
ourdb/lookup_location_test.v
|
||||
encoderhero/encoder_test.v
|
||||
encoderhero/decoder_test.v
|
||||
code/codeparser
|
||||
clients/meilisearch
|
||||
clients/zdb
|
||||
"
|
||||
|
||||
|
||||
@@ -189,7 +218,7 @@ if redis_available {
|
||||
|
||||
// Run each test with proper v command flags
|
||||
for test in test_files {
|
||||
if test.trim_space() == '' {
|
||||
if test.trim_space() == '' || test.trim_space().starts_with("//") || test.trim_space().starts_with("#") {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -204,10 +233,10 @@ for test in test_files {
|
||||
// If directory, run tests for each .v file in it recursively
|
||||
files := os.walk_ext(full_path, '.v')
|
||||
for file in files {
|
||||
process_test_file(file, test_files_ignore, test_files_error, redis_available, mut tests_in_error)!
|
||||
process_test_file(file, norm_dir_of_script, test_files_ignore, test_files_error, redis_available, mut tests_in_error)!
|
||||
}
|
||||
} else if os.is_file(full_path) {
|
||||
process_test_file(full_path, test_files_ignore, test_files_error, redis_available, mut tests_in_error)!
|
||||
process_test_file(full_path, norm_dir_of_script, test_files_ignore, test_files_error, redis_available, mut tests_in_error)!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user