Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55f0621983 | |||
| 75363d7aeb | |||
| 1f9bc11a2e | |||
| aab018925d | |||
| 5fa361256a | |||
| 42fe7b0a0d | |||
| 011e5b039e | |||
|
|
e9bcf6ef69 | ||
| f885563982 | |||
| ffff44f347 | |||
|
|
0e1450b5db | ||
| f8734a7e9f | |||
| c05ec6be7f | |||
| 8677d177cb | |||
| dd37eeaa29 | |||
| d5753ee794 | |||
| 6b46b3dbaa | |||
| 4cd5b51085 | |||
| 0a7851b920 | |||
| a0fdaf395e | |||
| 2c5a2ace17 | |||
| 965a2bebb7 | |||
| 2c08ee8687 | |||
| e105dd73b5 | |||
| f5dfe8c0af | |||
| ac97e9e7bc | |||
|
|
beae2cef82 | ||
| 2b23771056 | |||
| 19632b4b4b | |||
| 31c033300a | |||
| ca4127319d | |||
|
|
d3d8f0d0f1 | ||
|
|
2968e4dddc | ||
|
|
30c7951058 | ||
|
|
af63e266d8 | ||
|
|
5d1e3d416e | ||
|
|
dd9dc59485 | ||
| b473630ceb | |||
| a34b8b70ba | |||
| fd195f0824 | |||
| a727d19281 | |||
| 52c88bccb5 | |||
| 85cb868bff | |||
| 4339220b42 |
13
.github/workflows/hero_build.yml
vendored
13
.github/workflows/hero_build.yml
vendored
@@ -24,9 +24,9 @@ jobs:
|
||||
- target: aarch64-apple-darwin
|
||||
os: macos-latest
|
||||
short-name: macos-arm64
|
||||
- target: x86_64-apple-darwin
|
||||
os: macos-13
|
||||
short-name: macos-i64
|
||||
# - target: x86_64-apple-darwin
|
||||
# os: macos-13
|
||||
# short-name: macos-i64
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
@@ -42,10 +42,9 @@ jobs:
|
||||
run: ./install_v.sh --herolib
|
||||
timeout-minutes: 10
|
||||
|
||||
|
||||
- name: Do all the basic tests
|
||||
timeout-minutes: 25
|
||||
run: ./test_basic.vsh
|
||||
# - name: Do all the basic tests
|
||||
# timeout-minutes: 25
|
||||
# run: ./test_basic.vsh
|
||||
|
||||
- name: Build Hero
|
||||
timeout-minutes: 15
|
||||
|
||||
16
README.md
16
README.md
@@ -14,21 +14,17 @@ Herolib is an opinionated library primarily used by ThreeFold to automate cloud
|
||||
The Hero tool can be installed with a single command:
|
||||
|
||||
```bash
|
||||
curl https://raw.githubusercontent.com/freeflowuniverse/herolib/refs/heads/development/install_hero.sh > /tmp/install_hero.sh
|
||||
bash /tmp/install_hero.sh
|
||||
#do not forget to do the following this makes sure vtest and vrun exists
|
||||
bash install_herolib.vsh
|
||||
curl https://raw.githubusercontent.com/freeflowuniverse/herolib/refs/heads/development/install_hero.sh | bash
|
||||
```
|
||||
|
||||
Hero will be installed in:
|
||||
- `/usr/local/bin` for Linux
|
||||
- `~/hero/bin` for macOS
|
||||
|
||||
After installation on macOS, you may need to:
|
||||
After installation on macOS, you may need to do source see below or restart your terminal to ensure the `hero` command is available:
|
||||
|
||||
```bash
|
||||
source ~/.zprofile
|
||||
# Or copy to system bin directory
|
||||
cp ~/hero/bin/hero /usr/local/bin
|
||||
```
|
||||
|
||||
The Hero tool can be used to work with git, build documentation, interact with Hero AI, and more.
|
||||
@@ -40,7 +36,13 @@ For development purposes, use the automated installation script:
|
||||
```bash
|
||||
curl 'https://raw.githubusercontent.com/freeflowuniverse/herolib/refs/heads/development/install_v.sh' > /tmp/install_v.sh
|
||||
bash /tmp/install_v.sh --analyzer --herolib
|
||||
|
||||
#do not forget to do the following this makes sure vtest and vrun exists
|
||||
cd ~/code/github/freeflowuniverse/herolib
|
||||
bash install_herolib.vsh
|
||||
|
||||
# IMPORTANT: Start a new shell after installation for paths to be set correctly
|
||||
|
||||
```
|
||||
|
||||
#### Installation Options
|
||||
|
||||
19
aiprompts/.openhands/setup.sh
Normal file
19
aiprompts/.openhands/setup.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Herolib Web Server Installation Script
|
||||
# This script sets up the necessary environment for the Flask web server.
|
||||
|
||||
set -e # Exit on any error
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Script directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
/workspace/herolib/install_v.sh
|
||||
@@ -13,12 +13,12 @@ prod_mode := fp.bool('prod', `p`, false, 'Build production version (optimized)')
|
||||
help_requested := fp.bool('help', `h`, false, 'Show help message')
|
||||
|
||||
if help_requested {
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
}
|
||||
|
||||
additional_args := fp.finalize() or {
|
||||
eprintln(err)
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
}
|
||||
eprintln(err)
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env -S v -n -cg -w -parallel-cc -enable-globals run
|
||||
// #!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
#!/usr/bin/env -S v -n -g -cg -w -parallel-cc -showcc -enable-globals run
|
||||
|
||||
// #!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
import os
|
||||
import flag
|
||||
|
||||
@@ -14,20 +14,20 @@ prod_mode := fp.bool('prod', `p`, false, 'Build production version (optimized)')
|
||||
help_requested := fp.bool('help', `h`, false, 'Show help message')
|
||||
|
||||
if help_requested {
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
}
|
||||
|
||||
additional_args := fp.finalize() or {
|
||||
eprintln(err)
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
eprintln(err)
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
}
|
||||
|
||||
if additional_args.len > 0 {
|
||||
eprintln('Unexpected arguments: ${additional_args.join(' ')}')
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
eprintln('Unexpected arguments: ${additional_args.join(' ')}')
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Change to the hero directory
|
||||
@@ -37,35 +37,38 @@ os.chdir(hero_dir) or { panic('Failed to change directory to ${hero_dir}: ${err}
|
||||
// Set HEROPATH based on OS
|
||||
mut heropath := '/usr/local/bin/hero'
|
||||
if os.user_os() == 'macos' {
|
||||
heropath = os.join_path(os.home_dir(), 'hero/bin/hero')
|
||||
heropath = os.join_path(os.home_dir(), 'hero/bin/hero')
|
||||
}
|
||||
|
||||
// Set compilation command based on OS and mode
|
||||
compile_cmd := if os.user_os() == 'macos' {
|
||||
if prod_mode {
|
||||
'v -enable-globals -w -n -prod hero.v'
|
||||
} else {
|
||||
'v -w -cg -gc none -cc tcc -d use_openssl -enable-globals hero.v'
|
||||
}
|
||||
if prod_mode {
|
||||
'v -enable-globals -g -w -n -prod hero.v'
|
||||
} else {
|
||||
'v -n -g -w -cg -gc none -cc tcc -d use_openssl -enable-globals hero.v'
|
||||
}
|
||||
} else {
|
||||
if prod_mode {
|
||||
'v -cg -enable-globals -parallel-cc -w -n hero.v'
|
||||
} else {
|
||||
'v -cg -enable-globals -w -n hero.v'
|
||||
}
|
||||
if prod_mode {
|
||||
'v -cg -enable-globals -parallel-cc -w -n hero.v'
|
||||
} else {
|
||||
'v -cg -enable-globals -w -n hero.v'
|
||||
}
|
||||
}
|
||||
|
||||
println('Building in ${if prod_mode { 'production' } else { 'debug' }} mode...')
|
||||
// eprintln(compile_cmd)
|
||||
|
||||
if os.system(compile_cmd) != 0 {
|
||||
panic('Failed to compile hero.v with command: ${compile_cmd}')
|
||||
panic('Failed to compile hero.v with command: ${compile_cmd}')
|
||||
}
|
||||
|
||||
// Make executable
|
||||
os.chmod('hero', 0o755) or { panic('Failed to make hero binary executable: ${err}') }
|
||||
|
||||
// Ensure destination directory exists
|
||||
os.mkdir_all(os.dir(heropath)) or { panic('Failed to create directory ${os.dir(heropath)}: ${err}') }
|
||||
os.mkdir_all(os.dir(heropath)) or {
|
||||
panic('Failed to create directory ${os.dir(heropath)}: ${err}')
|
||||
}
|
||||
println(heropath)
|
||||
// Copy to destination paths
|
||||
os.cp('hero', heropath) or { panic('Failed to copy hero binary to ${heropath}: ${err}') }
|
||||
|
||||
@@ -64,7 +64,9 @@ account = ${s3keyid}
|
||||
key = ${s3appid}
|
||||
hard_delete = true'
|
||||
|
||||
os.write_file(rclone_conf, config_content) or { return error('Failed to write rclone config: ${err}') }
|
||||
os.write_file(rclone_conf, config_content) or {
|
||||
return error('Failed to write rclone config: ${err}')
|
||||
}
|
||||
|
||||
println('made S3 config on: ${rclone_conf}')
|
||||
content := os.read_file(rclone_conf) or { return error('Failed to read rclone config: ${err}') }
|
||||
@@ -72,8 +74,10 @@ hard_delete = true'
|
||||
}
|
||||
|
||||
fn hero_upload() ! {
|
||||
hero_path := os.find_abs_path_of_executable('hero') or { return error("Error: 'hero' command not found in PATH") }
|
||||
|
||||
hero_path := os.find_abs_path_of_executable('hero') or {
|
||||
return error("Error: 'hero' command not found in PATH")
|
||||
}
|
||||
|
||||
s3_configure()!
|
||||
|
||||
platform_id := get_platform_id()
|
||||
@@ -83,15 +87,18 @@ fn hero_upload() ! {
|
||||
|
||||
// List contents
|
||||
os.execute_or_panic('rclone --config="${rclone_conf}" lsl b2:threefold/${platform_id}/')
|
||||
|
||||
|
||||
// Copy hero binary
|
||||
os.execute_or_panic('rclone --config="${rclone_conf}" copy "${hero_path}" b2:threefold/${platform_id}/')
|
||||
}
|
||||
|
||||
fn main() {
|
||||
//os.execute_or_panic('${os.home_dir()}/code/github/freeflowuniverse/herolib/cli/compile.vsh -p')
|
||||
println("compile hero can take 60 sec+ on osx.")
|
||||
// os.execute_or_panic('${os.home_dir()}/code/github/freeflowuniverse/herolib/cli/compile.vsh -p')
|
||||
println('compile hero can take 60 sec+ on osx.')
|
||||
os.execute_or_panic('${os.home_dir()}/code/github/freeflowuniverse/herolib/cli/compile.vsh -p')
|
||||
println( "upload:")
|
||||
hero_upload() or { eprintln(err) exit(1) }
|
||||
println('upload:')
|
||||
hero_upload() or {
|
||||
eprintln(err)
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env -S v -n -cg -w -parallel-cc -enable-globals run
|
||||
// #!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
// #!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
import os
|
||||
import flag
|
||||
|
||||
@@ -14,20 +14,20 @@ prod_mode := fp.bool('prod', `p`, false, 'Build production version (optimized)')
|
||||
help_requested := fp.bool('help', `h`, false, 'Show help message')
|
||||
|
||||
if help_requested {
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
}
|
||||
|
||||
additional_args := fp.finalize() or {
|
||||
eprintln(err)
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
eprintln(err)
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
}
|
||||
|
||||
if additional_args.len > 0 {
|
||||
eprintln('Unexpected arguments: ${additional_args.join(' ')}')
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
eprintln('Unexpected arguments: ${additional_args.join(' ')}')
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Change to the vdo directory
|
||||
@@ -37,35 +37,37 @@ os.chdir(hero_dir) or { panic('Failed to change directory to ${hero_dir}: ${err}
|
||||
// Set HEROPATH based on OS
|
||||
mut heropath := '/usr/local/bin/vdo'
|
||||
if os.user_os() == 'macos' {
|
||||
heropath = os.join_path(os.home_dir(), 'hero/bin/vdo')
|
||||
heropath = os.join_path(os.home_dir(), 'hero/bin/vdo')
|
||||
}
|
||||
|
||||
// Set compilation command based on OS and mode
|
||||
compile_cmd := if os.user_os() == 'macos' {
|
||||
if prod_mode {
|
||||
'v -enable-globals -w -n -prod vdo.v'
|
||||
} else {
|
||||
'v -w -cg -gc none -cc tcc -d use_openssl -enable-globals vdo.v'
|
||||
}
|
||||
if prod_mode {
|
||||
'v -enable-globals -w -n -prod vdo.v'
|
||||
} else {
|
||||
'v -w -cg -gc none -cc tcc -d use_openssl -enable-globals vdo.v'
|
||||
}
|
||||
} else {
|
||||
if prod_mode {
|
||||
'v -cg -enable-globals -parallel-cc -w -n vdo.v'
|
||||
} else {
|
||||
'v -cg -enable-globals -w -n vdo.v'
|
||||
}
|
||||
if prod_mode {
|
||||
'v -cg -enable-globals -parallel-cc -w -n vdo.v'
|
||||
} else {
|
||||
'v -cg -enable-globals -w -n vdo.v'
|
||||
}
|
||||
}
|
||||
|
||||
println('Building in ${if prod_mode { 'production' } else { 'debug' }} mode...')
|
||||
|
||||
if os.system(compile_cmd) != 0 {
|
||||
panic('Failed to compile vdo.v with command: ${compile_cmd}')
|
||||
panic('Failed to compile vdo.v with command: ${compile_cmd}')
|
||||
}
|
||||
|
||||
// Make executable
|
||||
os.chmod('vdo', 0o755) or { panic('Failed to make vdo binary executable: ${err}') }
|
||||
|
||||
// Ensure destination directory exists
|
||||
os.mkdir_all(os.dir(heropath)) or { panic('Failed to create directory ${os.dir(heropath)}: ${err}') }
|
||||
os.mkdir_all(os.dir(heropath)) or {
|
||||
panic('Failed to create directory ${os.dir(heropath)}: ${err}')
|
||||
}
|
||||
println(heropath)
|
||||
// Copy to destination paths
|
||||
os.cp('vdo', heropath) or { panic('Failed to copy vdo binary to ${heropath}: ${err}') }
|
||||
|
||||
35
cli/hero.v
35
cli/hero.v
@@ -3,8 +3,6 @@ module main
|
||||
import os
|
||||
import cli { Command }
|
||||
import freeflowuniverse.herolib.core.herocmds
|
||||
// import freeflowuniverse.herolib.hero.cmds
|
||||
// import freeflowuniverse.herolib.hero.publishing
|
||||
import freeflowuniverse.herolib.installers.base
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.ui
|
||||
@@ -15,7 +13,7 @@ import freeflowuniverse.herolib.core.playcmds
|
||||
|
||||
fn playcmds_do(path string) ! {
|
||||
mut plbook := playbook.new(path: path)!
|
||||
playcmds.run(plbook:plbook)!
|
||||
playcmds.run(plbook: plbook)!
|
||||
}
|
||||
|
||||
fn do() ! {
|
||||
@@ -50,7 +48,7 @@ fn do() ! {
|
||||
mut cmd := Command{
|
||||
name: 'hero'
|
||||
description: 'Your HERO toolset.'
|
||||
version: '1.0.27'
|
||||
version: '1.0.28'
|
||||
}
|
||||
|
||||
// herocmds.cmd_run_add_flags(mut cmd)
|
||||
@@ -83,9 +81,14 @@ fn do() ! {
|
||||
|
||||
base.redis_install()!
|
||||
|
||||
// herocmds.cmd_bootstrap(mut cmd)
|
||||
|
||||
|
||||
herocmds.cmd_run(mut cmd)
|
||||
herocmds.cmd_git(mut cmd)
|
||||
herocmds.cmd_generator(mut cmd)
|
||||
herocmds.cmd_docusaurus(mut cmd)
|
||||
|
||||
// herocmds.cmd_bootstrap(mut cmd)
|
||||
// herocmds.cmd_init(mut cmd)
|
||||
// herocmds.cmd_imagedownsize(mut cmd)
|
||||
// herocmds.cmd_biztools(mut cmd)
|
||||
@@ -94,13 +97,15 @@ fn do() ! {
|
||||
// herocmds.cmd_installers(mut cmd)
|
||||
// herocmds.cmd_configure(mut cmd)
|
||||
// herocmds.cmd_postgres(mut cmd)
|
||||
herocmds.cmd_mdbook(mut cmd)
|
||||
// Ensure the herocmds module is imported so the remaining commands are visible
|
||||
// `cmd_mdbook` is not part of the current code base – it has been removed.
|
||||
// If you need markdown‑book support, re‑introduce the command implementation
|
||||
// and uncomment the line below.
|
||||
// herocmds.cmd_mdbook(mut cmd)
|
||||
// herocmds.cmd_luadns(mut cmd)
|
||||
// herocmds.cmd_caddy(mut cmd)
|
||||
// herocmds.cmd_zola(mut cmd)
|
||||
// herocmds.cmd_juggler(mut cmd)
|
||||
herocmds.cmd_generator(mut cmd)
|
||||
herocmds.cmd_docusaurus(mut cmd)
|
||||
// herocmds.cmd_starlight(mut cmd)
|
||||
// herocmds.cmd_docsorter(mut cmd)
|
||||
// cmd.add_command(publishing.cmd_publisher(pre_func))
|
||||
@@ -109,9 +114,15 @@ fn do() ! {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
do() or { panic(err) }
|
||||
do() or {
|
||||
$dbg;
|
||||
eprintln('Error: ${err}')
|
||||
print_backtrace()
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_func(cmd Command) ! {
|
||||
herocmds.plbook_run(cmd)!
|
||||
}
|
||||
|
||||
// fn pre_func(cmd Command) ! {
|
||||
// herocmds.plbook_run(cmd)!
|
||||
// }
|
||||
|
||||
@@ -6,7 +6,7 @@ fn main() {
|
||||
// Create and start the MCP server
|
||||
mut server := v_do.new_server()
|
||||
server.start() or {
|
||||
eprintln('Error starting server: $err')
|
||||
eprintln('Error starting server: ${err}')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
35
doc.vsh
35
doc.vsh
@@ -7,13 +7,13 @@ abs_dir_of_script := dir(@FILE)
|
||||
// Format code
|
||||
println('Formatting code...')
|
||||
if os.system('v fmt -w ${abs_dir_of_script}/examples') != 0 {
|
||||
eprintln('Warning: Failed to format examples')
|
||||
exit(1)
|
||||
eprintln('Warning: Failed to format examples')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
if os.system('v fmt -w ${abs_dir_of_script}/lib') != 0 {
|
||||
eprintln('Warning: Failed to format herolib')
|
||||
exit(1)
|
||||
eprintln('Warning: Failed to format herolib')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Clean existing docs
|
||||
@@ -24,9 +24,7 @@ os.rmdir_all('docs') or {}
|
||||
os.rmdir_all('vdocs') or {}
|
||||
|
||||
herolib_path := os.join_path(abs_dir_of_script, 'lib')
|
||||
os.chdir(herolib_path) or {
|
||||
panic('Failed to change directory to herolib: ${err}')
|
||||
}
|
||||
os.chdir(herolib_path) or { panic('Failed to change directory to herolib: ${err}') }
|
||||
|
||||
os.mkdir_all('_docs') or {}
|
||||
os.mkdir_all('docs') or {}
|
||||
@@ -35,17 +33,14 @@ os.mkdir_all('vdocs') or {}
|
||||
// Generate HTML documentation
|
||||
println('Generating HTML documentation...')
|
||||
if os.system('v doc -m -f html . -readme -comments -no-timestamp -o ../docs') != 0 {
|
||||
panic('Failed to generate HTML documentation')
|
||||
panic('Failed to generate HTML documentation')
|
||||
}
|
||||
|
||||
if os.system('v doc -m -f md . -no-color -o ../vdocs/') != 0 {
|
||||
panic('Failed to generate Hero markdown documentation')
|
||||
panic('Failed to generate Hero markdown documentation')
|
||||
}
|
||||
|
||||
|
||||
os.chdir(abs_dir_of_script) or {
|
||||
panic('Failed to change directory to abs_dir_of_script: ${err}')
|
||||
}
|
||||
os.chdir(abs_dir_of_script) or { panic('Failed to change directory to abs_dir_of_script: ${err}') }
|
||||
|
||||
// Generate Markdown documentation
|
||||
println('Generating Markdown documentation...')
|
||||
@@ -60,12 +55,10 @@ println('Generating Markdown documentation...')
|
||||
|
||||
// Open documentation in browser on non-Linux systems
|
||||
$if !linux {
|
||||
os.chdir(abs_dir_of_script) or {
|
||||
panic('Failed to change directory: ${err}')
|
||||
}
|
||||
if os.system('open docs/index.html') != 0 {
|
||||
eprintln('Warning: Failed to open documentation in browser')
|
||||
}
|
||||
os.chdir(abs_dir_of_script) or { panic('Failed to change directory: ${err}') }
|
||||
if os.system('open docs/index.html') != 0 {
|
||||
eprintln('Warning: Failed to open documentation in browser')
|
||||
}
|
||||
}
|
||||
|
||||
// Create Jekyll required files
|
||||
@@ -75,7 +68,7 @@ os.mkdir_all('docs/assets/css') or {}
|
||||
// Create style.scss
|
||||
style_content := '---\n---\n\n@import "{{ site.theme }}";'
|
||||
os.write_file('docs/assets/css/style.scss', style_content) or {
|
||||
panic('Failed to create style.scss: ${err}')
|
||||
panic('Failed to create style.scss: ${err}')
|
||||
}
|
||||
|
||||
// Create _config.yml
|
||||
@@ -94,7 +87,7 @@ exclude:
|
||||
- vendor/ruby/'
|
||||
|
||||
os.write_file('docs/_config.yml', config_content) or {
|
||||
panic('Failed to create _config.yml: ${err}')
|
||||
panic('Failed to create _config.yml: ${err}')
|
||||
}
|
||||
|
||||
println('Documentation generation completed successfully!')
|
||||
|
||||
@@ -4,46 +4,45 @@ import os
|
||||
import flag
|
||||
|
||||
fn addtoscript(tofind string, toadd string) ! {
|
||||
home_dir := os.home_dir()
|
||||
mut rc_file := '${home_dir}/.zshrc'
|
||||
if !os.exists(rc_file) {
|
||||
rc_file = '${home_dir}/.bashrc'
|
||||
if !os.exists(rc_file) {
|
||||
return error('No .zshrc or .bashrc found in home directory')
|
||||
}
|
||||
}
|
||||
home_dir := os.home_dir()
|
||||
mut rc_file := '${home_dir}/.zshrc'
|
||||
if !os.exists(rc_file) {
|
||||
rc_file = '${home_dir}/.bashrc'
|
||||
if !os.exists(rc_file) {
|
||||
return error('No .zshrc or .bashrc found in home directory')
|
||||
}
|
||||
}
|
||||
|
||||
// Read current content
|
||||
mut content := os.read_file(rc_file)!
|
||||
|
||||
// Remove existing alias if present
|
||||
lines := content.split('\n')
|
||||
mut new_lines := []string{}
|
||||
mut prev_is_emtpy := false
|
||||
for line in lines {
|
||||
if prev_is_emtpy {
|
||||
if line.trim_space() == ""{
|
||||
continue
|
||||
}else{
|
||||
prev_is_emtpy = false
|
||||
}
|
||||
}
|
||||
if line.trim_space() == ""{
|
||||
prev_is_emtpy = true
|
||||
}
|
||||
// Read current content
|
||||
mut content := os.read_file(rc_file)!
|
||||
|
||||
if !line.contains(tofind) {
|
||||
new_lines << line
|
||||
}
|
||||
}
|
||||
new_lines << toadd
|
||||
new_lines << ""
|
||||
// Write back to file
|
||||
new_content := new_lines.join('\n')
|
||||
os.write_file(rc_file, new_content)!
|
||||
// Remove existing alias if present
|
||||
lines := content.split('\n')
|
||||
mut new_lines := []string{}
|
||||
mut prev_is_emtpy := false
|
||||
for line in lines {
|
||||
if prev_is_emtpy {
|
||||
if line.trim_space() == '' {
|
||||
continue
|
||||
} else {
|
||||
prev_is_emtpy = false
|
||||
}
|
||||
}
|
||||
if line.trim_space() == '' {
|
||||
prev_is_emtpy = true
|
||||
}
|
||||
|
||||
if !line.contains(tofind) {
|
||||
new_lines << line
|
||||
}
|
||||
}
|
||||
new_lines << toadd
|
||||
new_lines << ''
|
||||
// Write back to file
|
||||
new_content := new_lines.join('\n')
|
||||
os.write_file(rc_file, new_content)!
|
||||
}
|
||||
|
||||
|
||||
vroot := @VROOT
|
||||
abs_dir_of_script := dir(@FILE)
|
||||
|
||||
@@ -52,20 +51,20 @@ println('Resetting all symlinks...')
|
||||
os.rm('${os.home_dir()}/.vmodules/freeflowuniverse/herolib') or {}
|
||||
|
||||
// Create necessary directories
|
||||
os.mkdir_all('${os.home_dir()}/.vmodules/freeflowuniverse') or {
|
||||
panic('Failed to create directory ~/.vmodules/freeflowuniverse: ${err}')
|
||||
os.mkdir_all('${os.home_dir()}/.vmodules/freeflowuniverse') or {
|
||||
panic('Failed to create directory ~/.vmodules/freeflowuniverse: ${err}')
|
||||
}
|
||||
|
||||
// Create new symlinks
|
||||
os.symlink('${abs_dir_of_script}/lib', '${os.home_dir()}/.vmodules/freeflowuniverse/herolib') or {
|
||||
panic('Failed to create herolib symlink: ${err}')
|
||||
panic('Failed to create herolib symlink: ${err}')
|
||||
}
|
||||
|
||||
println('Herolib installation completed successfully!')
|
||||
|
||||
// Add vtest alias
|
||||
addtoscript('alias vtest=', 'alias vtest=\'v -stats -enable-globals -n -w -cg -gc none -cc tcc test\' ') or {
|
||||
eprintln('Failed to add vtest alias: ${err}')
|
||||
addtoscript('alias vtest=', "alias vtest='v -stats -enable-globals -n -w -cg -gc none -cc tcc test' ") or {
|
||||
eprintln('Failed to add vtest alias: ${err}')
|
||||
}
|
||||
|
||||
println('Added vtest alias to shell configuration')
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.biz.bizmodel
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import os
|
||||
|
||||
heroscript := "
|
||||
@@ -21,8 +22,13 @@ This time we have the cogs defined in fixed manner, the default currency is USD
|
||||
cogs: '10:100000,15:1000,20:120000'
|
||||
"
|
||||
|
||||
bizmodel.play(heroscript: heroscript)!
|
||||
// Create a new playbook with the heroscript text
|
||||
mut pb := playbook.new(text: heroscript)!
|
||||
|
||||
// Play the bizmodel actions
|
||||
bizmodel.play(mut pb)!
|
||||
|
||||
// Get the bizmodel and print it
|
||||
mut bm := bizmodel.get('test')!
|
||||
|
||||
bm.sheet.pprint(nr_columns: 30)!
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.biz.bizmodel
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import os
|
||||
|
||||
heroscript := "
|
||||
@@ -16,8 +17,13 @@ heroscript := "
|
||||
//revenue_item_monthly_perc:'3%'
|
||||
"
|
||||
|
||||
bizmodel.play(heroscript: heroscript)!
|
||||
// Create a new playbook with the heroscript text
|
||||
mut pb := playbook.new(text: heroscript)!
|
||||
|
||||
// Play the bizmodel actions
|
||||
bizmodel.play(mut pb)!
|
||||
|
||||
// Get the bizmodel and print it
|
||||
mut bm := bizmodel.get('test')!
|
||||
|
||||
bm.sheet.pprint(nr_columns: 30)!
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.biz.bizmodel
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import os
|
||||
|
||||
heroscript := os.join_path(os.dir(@FILE), 'examples/complete.heroscript')
|
||||
heroscript_path := os.join_path(os.dir(@FILE), 'examples/complete.heroscript')
|
||||
|
||||
// Execute the script and print results
|
||||
bizmodel.play(heroscript_path: heroscript)!
|
||||
// Create a new playbook with the heroscript path
|
||||
mut pb := playbook.new(path: heroscript_path)!
|
||||
|
||||
// Play the bizmodel actions
|
||||
bizmodel.play(mut pb)!
|
||||
|
||||
// Get the bizmodel and print it
|
||||
mut bm := bizmodel.get('threefold')!
|
||||
bm.sheet.pprint(nr_columns: 10)!
|
||||
|
||||
@@ -1,33 +1,23 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
//#!/usr/bin/env -S v -cg -enable-globals run
|
||||
import freeflowuniverse.herolib.biz.bizmodel
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.core.playcmds
|
||||
import os
|
||||
|
||||
// heroscript := os.join_path(os.dir(@FILE), 'examples/full')
|
||||
// // Execute the script and print results
|
||||
// bizmodel.play(heroscript_path:heroscript)!
|
||||
heroscript_path := os.join_path(os.dir(@FILE), 'examples/complete.heroscript')
|
||||
|
||||
heroscript := os.join_path(os.dir(@FILE), 'examples/complete.heroscript')
|
||||
// Execute the script and print results
|
||||
bizmodel.play(heroscript_path: heroscript)!
|
||||
// Create a new playbook with the heroscript path
|
||||
mut pb := playbook.new(path: heroscript_path)!
|
||||
|
||||
// Play the bizmodel actions
|
||||
bizmodel.play(mut pb)!
|
||||
|
||||
// Get the bizmodel and print it
|
||||
mut bm := bizmodel.get('threefold')!
|
||||
bm.sheet.pprint(nr_columns: 10)!
|
||||
|
||||
// buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
|
||||
// println("buildpath: ${buildpath}")
|
||||
|
||||
// model.play(mut playbook.new(path: playbook_path)!)!
|
||||
|
||||
// println(model.sheet)
|
||||
// println(model.sheet.export()!)
|
||||
|
||||
// model.sheet.export(path:"~/Downloads/test.csv")!
|
||||
// model.sheet.export(path:"~/code/github/freeflowuniverse/starlight_template/src/content/test.csv")!
|
||||
|
||||
// Export the business model to a report
|
||||
bm.export(
|
||||
name: 'example_report'
|
||||
title: 'Example Business Model'
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.biz.bizmodel
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import os
|
||||
|
||||
heroscript := os.join_path(os.dir(@FILE), 'examples/full')
|
||||
heroscript_path := os.join_path(os.dir(@FILE), 'examples/full')
|
||||
|
||||
// Execute the script and print results
|
||||
bizmodel.play(heroscript_path: heroscript)!
|
||||
// Create a new playbook with the heroscript path
|
||||
mut pb := playbook.new(path: heroscript_path)!
|
||||
|
||||
// Play the bizmodel actions
|
||||
bizmodel.play(mut pb)!
|
||||
|
||||
// Get the bizmodel and print it
|
||||
mut bm := bizmodel.get('threefold')!
|
||||
bm.sheet.pprint(nr_columns: 25)!
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.biz.bizmodel
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import os
|
||||
|
||||
heroscript := "
|
||||
@@ -45,8 +46,13 @@ heroscript := "
|
||||
|
||||
"
|
||||
|
||||
bizmodel.play(heroscript: heroscript)!
|
||||
// Create a new playbook with the heroscript text
|
||||
mut pb := playbook.new(text: heroscript)!
|
||||
|
||||
// Play the bizmodel actions
|
||||
bizmodel.play(mut pb)!
|
||||
|
||||
// Get the bizmodel and print it
|
||||
mut bm := bizmodel.get('test')!
|
||||
|
||||
bm.sheet.pprint(nr_columns: 20)!
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.biz.bizmodel
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import os
|
||||
|
||||
heroscript := "
|
||||
@@ -17,8 +18,13 @@ heroscript := "
|
||||
|
||||
"
|
||||
|
||||
bizmodel.play(heroscript: heroscript)!
|
||||
// Create a new playbook with the heroscript text
|
||||
mut pb := playbook.new(text: heroscript)!
|
||||
|
||||
// Play the bizmodel actions
|
||||
bizmodel.play(mut pb)!
|
||||
|
||||
// Get the bizmodel and print it
|
||||
mut bm := bizmodel.get('test')!
|
||||
|
||||
bm.sheet.pprint(nr_columns: 20)!
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.biz.bizmodel
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import os
|
||||
|
||||
heroscript := "
|
||||
@@ -36,8 +37,13 @@ heroscript := "
|
||||
|
||||
"
|
||||
|
||||
bizmodel.play(heroscript: heroscript)!
|
||||
// Create a new playbook with the heroscript text
|
||||
mut pb := playbook.new(text: heroscript)!
|
||||
|
||||
// Play the bizmodel actions
|
||||
bizmodel.play(mut pb)!
|
||||
|
||||
// Get the bizmodel and print it
|
||||
mut bm := bizmodel.get('test')!
|
||||
|
||||
bm.sheet.pprint(nr_columns: 20)!
|
||||
|
||||
50
examples/biztools/notworking.md
Normal file
50
examples/biztools/notworking.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# BizTools Examples Test Results
|
||||
|
||||
## Working Examples
|
||||
All examples have been fixed and now work correctly:
|
||||
|
||||
- `bizmodel.vsh` - This example was already working correctly.
|
||||
- `bizmodel1.vsh` - Fixed to use `playbook.new()` with text parameter.
|
||||
- `bizmodel2.vsh` - Fixed to use `playbook.new()` with text parameter.
|
||||
- `bizmodel_complete.vsh` - Fixed to use `playbook.new()` with path parameter.
|
||||
- `bizmodel_export.vsh` - Fixed to use `playbook.new()` with path parameter.
|
||||
- `bizmodel_full.vsh` - Fixed to use `playbook.new()` with path parameter.
|
||||
- `costs.vsh` - Fixed to use `playbook.new()` with text parameter.
|
||||
- `funding.vsh` - Fixed to use `playbook.new()` with text parameter.
|
||||
- `hr.vsh` - Fixed to use `playbook.new()` with text parameter.
|
||||
|
||||
## Previous Issues
|
||||
All examples had issues with the `bizmodel.play()` function:
|
||||
|
||||
1. Unknown field (`heroscript` or `heroscript_path`) in struct literal of type `PlayBook`.
|
||||
2. Reference field `PlayBook.session` must be initialized.
|
||||
3. Function `bizmodel.play` parameter `plbook` is `mut`, so it requires `mut PlayBook{...}` instead.
|
||||
|
||||
## Solution Applied
|
||||
All examples have been fixed by using the `playbook.new()` function to create a properly initialized PlayBook:
|
||||
|
||||
For examples with heroscript text:
|
||||
```v
|
||||
// Create a new playbook with the heroscript text
|
||||
mut pb := playbook.new(text: heroscript)!
|
||||
|
||||
// Play the bizmodel actions
|
||||
bizmodel.play(mut pb)!
|
||||
|
||||
// Get the bizmodel and print it
|
||||
mut bm := bizmodel.get('test')!
|
||||
```
|
||||
|
||||
For examples with heroscript path:
|
||||
```v
|
||||
// Create a new playbook with the heroscript path
|
||||
mut pb := playbook.new(path: heroscript_path)!
|
||||
|
||||
// Play the bizmodel actions
|
||||
bizmodel.play(mut pb)!
|
||||
```
|
||||
|
||||
## Environment Setup
|
||||
- Tests were performed with V language version 0.4.11 a11de72
|
||||
- Redis server was running during tests
|
||||
- All tests were executed from the `/workspace/project/herolib/examples/biztools` directory
|
||||
1
examples/web/.gitignore
vendored
1
examples/web/.gitignore
vendored
@@ -6,3 +6,4 @@ markdown_example0
|
||||
doctree_example
|
||||
tree_scan
|
||||
*.dSYM
|
||||
ui_demo
|
||||
|
||||
@@ -11,15 +11,15 @@ playcmds.run(
|
||||
// install: 1
|
||||
// template_update: 1
|
||||
|
||||
!!docusaurus.add sitename:"tfgrid_tech"
|
||||
git_url:"https://git.threefold.info/tfgrid/docs_tfgrid4/src/branch/main/ebooks/tech"
|
||||
!!docusaurus.add sitename:"owh_intro"
|
||||
git_url:"https://git.ourworld.tf/ourworld_holding/docs_owh/src/branch/main/ebooks/owh_intro"
|
||||
git_root:"/tmp/code"
|
||||
git_reset:1
|
||||
git_pull:1
|
||||
play:true
|
||||
|
||||
!!docusaurus.build
|
||||
// !!docusaurus.build
|
||||
|
||||
// !!docusaurus.dev site:"tfgrid_tech" open:true
|
||||
!!docusaurus.dev site:"owh_intro" open:true
|
||||
'
|
||||
)!
|
||||
|
||||
13
examples/web/ui_demo.vsh
Executable file
13
examples/web/ui_demo.vsh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cg -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.web.ui
|
||||
|
||||
fn main() {
|
||||
println('Starting UI test server on port 8080...')
|
||||
println('Visit http://localhost:8080 to see the admin interface')
|
||||
|
||||
ui.start(
|
||||
title: 'Test Admin Panel'
|
||||
port: 8080
|
||||
)!
|
||||
}
|
||||
30
generate.vsh
30
generate.vsh
@@ -10,41 +10,39 @@ fp.version('v0.1.0')
|
||||
fp.description('Generate code')
|
||||
fp.skip_executable()
|
||||
|
||||
mut path := fp.string('path', `p`, "", 'Path where to generate a module, if not mentioned will scan over all installers & clients.\nif . then will be path we are on.')
|
||||
mut path := fp.string('path', `p`, '', 'Path where to generate a module, if not mentioned will scan over all installers & clients.\nif . then will be path we are on.')
|
||||
reset := fp.bool('reset', `r`, false, 'If we want to reset')
|
||||
interactive := fp.bool('interactive', `i`, false, 'If we want to work interactive')
|
||||
scan := fp.bool('scan', `s`, false, 'If we want to scan')
|
||||
help_requested := fp.bool('help', `h`, false, 'Show help message')
|
||||
|
||||
if help_requested {
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
}
|
||||
|
||||
additional_args := fp.finalize() or {
|
||||
eprintln(err)
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
eprintln(err)
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
}
|
||||
|
||||
if additional_args.len > 0 {
|
||||
eprintln('Unexpected arguments: ${additional_args.join(' ')}')
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
eprintln('Unexpected arguments: ${additional_args.join(' ')}')
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// reset bool // regenerate all, dangerous !!!
|
||||
// interactive bool //if we want to ask
|
||||
// path string
|
||||
|
||||
|
||||
|
||||
if path.trim_space() == "." {
|
||||
if path.trim_space() == '.' {
|
||||
path = os.getwd()
|
||||
}
|
||||
|
||||
if ! scan {
|
||||
generator.do(path:path, reset:reset, interactive:interactive)!
|
||||
}else{
|
||||
generator.scan(path:path, reset:reset, interactive:interactive)!
|
||||
if !scan {
|
||||
generator.do(path: path, reset: reset, interactive: interactive)!
|
||||
} else {
|
||||
generator.scan(path: path, reset: reset, interactive: interactive)!
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ set -e
|
||||
|
||||
os_name="$(uname -s)"
|
||||
arch_name="$(uname -m)"
|
||||
version='1.0.27'
|
||||
version='1.0.28'
|
||||
|
||||
|
||||
# Base URL for GitHub releases
|
||||
|
||||
@@ -4,46 +4,45 @@ import os
|
||||
import flag
|
||||
|
||||
fn addtoscript(tofind string, toadd string) ! {
|
||||
home_dir := os.home_dir()
|
||||
mut rc_file := '${home_dir}/.zshrc'
|
||||
if !os.exists(rc_file) {
|
||||
rc_file = '${home_dir}/.bashrc'
|
||||
if !os.exists(rc_file) {
|
||||
return error('No .zshrc or .bashrc found in home directory')
|
||||
}
|
||||
}
|
||||
home_dir := os.home_dir()
|
||||
mut rc_file := '${home_dir}/.zshrc'
|
||||
if !os.exists(rc_file) {
|
||||
rc_file = '${home_dir}/.bashrc'
|
||||
if !os.exists(rc_file) {
|
||||
return error('No .zshrc or .bashrc found in home directory')
|
||||
}
|
||||
}
|
||||
|
||||
// Read current content
|
||||
mut content := os.read_file(rc_file)!
|
||||
|
||||
// Remove existing alias if present
|
||||
lines := content.split('\n')
|
||||
mut new_lines := []string{}
|
||||
mut prev_is_emtpy := false
|
||||
for line in lines {
|
||||
if prev_is_emtpy {
|
||||
if line.trim_space() == ""{
|
||||
continue
|
||||
}else{
|
||||
prev_is_emtpy = false
|
||||
}
|
||||
}
|
||||
if line.trim_space() == ""{
|
||||
prev_is_emtpy = true
|
||||
}
|
||||
// Read current content
|
||||
mut content := os.read_file(rc_file)!
|
||||
|
||||
if !line.contains(tofind) {
|
||||
new_lines << line
|
||||
}
|
||||
}
|
||||
new_lines << toadd
|
||||
new_lines << ""
|
||||
// Write back to file
|
||||
new_content := new_lines.join('\n')
|
||||
os.write_file(rc_file, new_content)!
|
||||
// Remove existing alias if present
|
||||
lines := content.split('\n')
|
||||
mut new_lines := []string{}
|
||||
mut prev_is_emtpy := false
|
||||
for line in lines {
|
||||
if prev_is_emtpy {
|
||||
if line.trim_space() == '' {
|
||||
continue
|
||||
} else {
|
||||
prev_is_emtpy = false
|
||||
}
|
||||
}
|
||||
if line.trim_space() == '' {
|
||||
prev_is_emtpy = true
|
||||
}
|
||||
|
||||
if !line.contains(tofind) {
|
||||
new_lines << line
|
||||
}
|
||||
}
|
||||
new_lines << toadd
|
||||
new_lines << ''
|
||||
// Write back to file
|
||||
new_content := new_lines.join('\n')
|
||||
os.write_file(rc_file, new_content)!
|
||||
}
|
||||
|
||||
|
||||
vroot := @VROOT
|
||||
abs_dir_of_script := dir(@FILE)
|
||||
|
||||
@@ -52,29 +51,29 @@ println('Resetting all symlinks...')
|
||||
os.rm('${os.home_dir()}/.vmodules/freeflowuniverse/herolib') or {}
|
||||
|
||||
// Create necessary directories
|
||||
os.mkdir_all('${os.home_dir()}/.vmodules/freeflowuniverse') or {
|
||||
panic('Failed to create directory ~/.vmodules/freeflowuniverse: ${err}')
|
||||
os.mkdir_all('${os.home_dir()}/.vmodules/freeflowuniverse') or {
|
||||
panic('Failed to create directory ~/.vmodules/freeflowuniverse: ${err}')
|
||||
}
|
||||
|
||||
// Create new symlinks
|
||||
os.symlink('${abs_dir_of_script}/lib', '${os.home_dir()}/.vmodules/freeflowuniverse/herolib') or {
|
||||
panic('Failed to create herolib symlink: ${err}')
|
||||
panic('Failed to create herolib symlink: ${err}')
|
||||
}
|
||||
|
||||
println('Herolib installation completed successfully!')
|
||||
|
||||
// Add vtest alias
|
||||
addtoscript('alias vtest=', 'alias vtest=\'v -stats -enable-globals -show-c-output -n -w -cg -gc none -cc tcc test\' ') or {
|
||||
eprintln('Failed to add vtest alias: ${err}')
|
||||
addtoscript('alias vtest=', "alias vtest='v -stats -enable-globals -show-c-output -n -w -cg -gc none -cc tcc test' ") or {
|
||||
eprintln('Failed to add vtest alias: ${err}')
|
||||
}
|
||||
|
||||
// Add vrun alias
|
||||
addtoscript('alias vrun=', 'alias vrun=\'v -stats -enable-globals -show-c-output -n -w -cg -gc none -cc tcc run\' ') or {
|
||||
eprintln('Failed to add vrun alias: ${err}')
|
||||
addtoscript('alias vrun=', "alias vrun='v -stats -enable-globals -show-c-output -n -w -cg -gc none -cc tcc run' ") or {
|
||||
eprintln('Failed to add vrun alias: ${err}')
|
||||
}
|
||||
|
||||
addtoscript('HOME/hero/bin', 'export PATH="\$PATH:\$HOME/hero/bin"') or {
|
||||
eprintln('Failed to add path to hero, ${err}')
|
||||
eprintln('Failed to add path to hero, ${err}')
|
||||
}
|
||||
|
||||
// ulimit -n 32000
|
||||
|
||||
@@ -40,7 +40,7 @@ pub fn jina_model_from_string(s string) !JinaModel {
|
||||
'jina-embeddings-v2-base-zh' { JinaModel.jina_embeddings_v2_base_zh }
|
||||
'jina-embeddings-v2-base-code' { JinaModel.jina_embeddings_v2_base_code }
|
||||
'jina-embeddings-v3' { JinaModel.jina_embeddings_v3 }
|
||||
else { error('Invalid Jina model string: ${s}') }
|
||||
else { return error('Invalid Jina model string: ${s}') }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ pub fn truncate_type_from_string(s string) !TruncateType {
|
||||
'NONE' { TruncateType.none_ }
|
||||
'START' { TruncateType.start }
|
||||
'END' { TruncateType.end }
|
||||
else { error('Invalid truncate type string: ${s}') }
|
||||
else { return error('Invalid truncate type string: ${s}') }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ pub fn embedding_type_from_string(s string) !EmbeddingType {
|
||||
'base64' { EmbeddingType.base64 }
|
||||
'binary' { EmbeddingType.binary }
|
||||
'ubinary' { EmbeddingType.ubinary }
|
||||
else { error('Invalid embedding type string: ${s}') }
|
||||
else { return error('Invalid embedding type string: ${s}') }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ pub fn task_type_from_string(s string) !TaskType {
|
||||
'text-matching' { TaskType.text_matching }
|
||||
'classification' { TaskType.classification }
|
||||
'separation' { TaskType.separation }
|
||||
else { error('Invalid task type string: ${s}') }
|
||||
else { return error('Invalid task type string: ${s}') }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ pub fn jina_rerank_model_from_string(s string) !JinaRerankModel {
|
||||
'jina-reranker-v1-tiny-en' { JinaRerankModel.reranker_v1_tiny_en }
|
||||
'jina-reranker-v1-turbo-en' { JinaRerankModel.reranker_v1_turbo_en }
|
||||
'jina-colbert-v1-en' { JinaRerankModel.colbert_v1_en }
|
||||
else { error('Invalid JinaRerankModel string: ${s}') }
|
||||
else { return error('Invalid JinaRerankModel string: ${s}') }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
module installer_client
|
||||
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
|
||||
// will ask questions & create the .heroscript
|
||||
pub fn ask(path string) ! {
|
||||
mut myconsole := console.new()
|
||||
|
||||
mut model := gen_model_get(path, false)!
|
||||
|
||||
console.clear()
|
||||
console.print_header('Configure generation of code for a module on path:')
|
||||
console.print_green('Path: ${path}')
|
||||
console.lf()
|
||||
|
||||
model.classname = myconsole.ask_question(
|
||||
description: 'Class name of the ${model.cat}'
|
||||
question: 'What is the class name of the generator e.g. MyClass ?'
|
||||
warning: 'Please provide a valid class name for the generator'
|
||||
default: model.classname
|
||||
minlen: 4
|
||||
)!
|
||||
|
||||
model.title = myconsole.ask_question(
|
||||
description: 'Title of the ${model.cat} (optional)'
|
||||
default: model.title
|
||||
)!
|
||||
|
||||
model.hasconfig = !myconsole.ask_yesno(
|
||||
description: 'Is there a config (normally yes)?'
|
||||
default: model.hasconfig
|
||||
)!
|
||||
|
||||
if model.hasconfig {
|
||||
model.singleton = !myconsole.ask_yesno(
|
||||
description: 'Can there be multiple instances (normally yes)?'
|
||||
default: !model.singleton
|
||||
)!
|
||||
if model.cat == .installer {
|
||||
model.templates = myconsole.ask_yesno(
|
||||
description: 'Will there be templates available for your installer?'
|
||||
default: model.templates
|
||||
)!
|
||||
}
|
||||
} else {
|
||||
model.singleton = true
|
||||
}
|
||||
|
||||
if model.cat == .installer {
|
||||
model.startupmanager = myconsole.ask_yesno(
|
||||
description: 'Is this an installer which will be managed by a startup mananger?'
|
||||
default: model.startupmanager
|
||||
)!
|
||||
|
||||
model.build = myconsole.ask_yesno(
|
||||
description: 'Are there builders for the installers (compilation)'
|
||||
default: model.build
|
||||
)!
|
||||
}
|
||||
|
||||
// if true{
|
||||
// println(model)
|
||||
// panic("Sdsd")
|
||||
// }
|
||||
|
||||
gen_model_set(GenerateArgs{ model: model, path: path })!
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
module installer_client
|
||||
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import os
|
||||
|
||||
@[params]
|
||||
pub struct GenerateArgs {
|
||||
pub mut:
|
||||
reset bool // regenerate all, dangerous !!!
|
||||
interactive bool // if we want to ask
|
||||
path string
|
||||
playonly bool
|
||||
model ?GenModel
|
||||
cat ?Cat
|
||||
}
|
||||
|
||||
pub struct PlayArgs {
|
||||
pub mut:
|
||||
name string
|
||||
modulepath string
|
||||
}
|
||||
|
||||
// the default to start with
|
||||
//
|
||||
// reset bool // regenerate all, dangerous !!!
|
||||
// interactive bool //if we want to ask
|
||||
// path string
|
||||
// model ?GenModel
|
||||
// cat ?Cat
|
||||
//
|
||||
// will return the module path where we need to execute a play command as well as the name of
|
||||
pub fn do(args_ GenerateArgs) !PlayArgs {
|
||||
mut args := args_
|
||||
|
||||
console.print_header('Generate code for path: ${args.path} (reset:${args.reset}, interactive:${args.interactive})')
|
||||
|
||||
mut create := true // to create .heroscript
|
||||
|
||||
mut model := args.model or {
|
||||
create = false // we cannot create because model not given
|
||||
if args.path == '' {
|
||||
args.path = os.getwd()
|
||||
}
|
||||
mut m := gen_model_get(args.path, false)!
|
||||
m
|
||||
}
|
||||
|
||||
if model.classname == '' {
|
||||
args.interactive = true
|
||||
}
|
||||
|
||||
if create {
|
||||
if args.path == '' {
|
||||
return error('need to specify path fo ${args_} because we asked to create .heroscript ')
|
||||
}
|
||||
gen_model_set(args)! // persist it on disk
|
||||
} else {
|
||||
if args.path == '' {
|
||||
args.path = os.getwd()
|
||||
}
|
||||
}
|
||||
|
||||
// if model.cat == .unknown {
|
||||
// model.cat = args.cat or { return error('cat needs to be specified for generator.') }
|
||||
// }
|
||||
|
||||
if args.interactive {
|
||||
ask(args.path)!
|
||||
args.model = gen_model_get(args.path, false)!
|
||||
} else {
|
||||
args.model = model
|
||||
}
|
||||
|
||||
console.print_debug(args)
|
||||
|
||||
// only generate if playonly is false and there is a classname
|
||||
if !args.playonly && model.classname.len > 0 {
|
||||
generate(args)!
|
||||
}
|
||||
|
||||
return PlayArgs{
|
||||
name: model.play_name
|
||||
modulepath: model.module_path
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
module installer_client
|
||||
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
|
||||
// generate based on filled in args, ask has to be done before
|
||||
fn generate(args GenerateArgs) ! {
|
||||
console.print_debug('generate code for path: ${args.path}')
|
||||
|
||||
// as used in the templates
|
||||
model := args.model or { panic('bug no model specified in generate') }
|
||||
|
||||
mut path_actions := pathlib.get(args.path + '/${model.name}_actions.v')
|
||||
if args.reset {
|
||||
path_actions.delete()!
|
||||
}
|
||||
if !path_actions.exists() && model.cat == .installer {
|
||||
console.print_debug('write installer actions')
|
||||
mut templ_1 := $tmpl('templates/objname_actions.vtemplate')
|
||||
pathlib.template_write(templ_1, '${args.path}/${model.name}_actions.v', true)!
|
||||
}
|
||||
|
||||
mut templ_2 := $tmpl('templates/objname_factory_.vtemplate')
|
||||
|
||||
pathlib.template_write(templ_2, '${args.path}/${model.name}_factory_.v', true)!
|
||||
|
||||
mut path_model := pathlib.get(args.path + '/${model.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}/${model.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.model or { panic('bug') }).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 GenModel) ! {
|
||||
// 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 GenModel) 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
|
||||
// }
|
||||
@@ -1,136 +0,0 @@
|
||||
module installer_client
|
||||
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
pub struct GenModel {
|
||||
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 !!!
|
||||
interactive bool // if we want to ask
|
||||
startupmanager bool = true
|
||||
build bool = true
|
||||
hasconfig bool = true
|
||||
cat Cat // dont' set default
|
||||
play_name string // e.g. docusaurus is what we look for
|
||||
module_path string // e.g.freeflowuniverse.herolib.web.docusaurus
|
||||
}
|
||||
|
||||
pub enum Cat {
|
||||
unknown
|
||||
client
|
||||
installer
|
||||
}
|
||||
|
||||
// creates the heroscript from the GenModel as part of GenerateArgs
|
||||
pub fn gen_model_set(args GenerateArgs) ! {
|
||||
console.print_debug('Code generator set: ${args}')
|
||||
model := args.model or { return error('model is none') }
|
||||
heroscript_templ := match model.cat {
|
||||
.client { $tmpl('templates/heroscript_client') }
|
||||
.installer { $tmpl('templates/heroscript_installer') }
|
||||
else { return error('Invalid category: ${model.cat}') }
|
||||
}
|
||||
pathlib.template_write(heroscript_templ, '${args.path}/.heroscript', true)!
|
||||
}
|
||||
|
||||
// loads the heroscript and return the model
|
||||
pub fn gen_model_get(path string, create bool) !GenModel {
|
||||
console.print_debug('play installer code for path: ${path}')
|
||||
|
||||
mut config_path := pathlib.get_file(path: '${path}/.heroscript', create: create)!
|
||||
|
||||
mut plbook := playbook.new(text: config_path.read()!)!
|
||||
|
||||
mut model := GenModel{}
|
||||
mut found := false
|
||||
|
||||
mut install_actions := plbook.find(filter: 'hero_code.generate_installer')!
|
||||
if install_actions.len > 0 {
|
||||
for install_action in install_actions {
|
||||
if found {
|
||||
return error('cannot find more than one her_code.generate_installer ... in ${path}')
|
||||
}
|
||||
found = true
|
||||
mut p := install_action.params
|
||||
model = GenModel{
|
||||
name: p.get_default('name', '')!
|
||||
classname: p.get_default('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')
|
||||
startupmanager: p.get_default_true('startupmanager')
|
||||
build: p.get_default_true('build')
|
||||
hasconfig: p.get_default_true('hasconfig')
|
||||
cat: .installer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mut client_actions := plbook.find(filter: 'hero_code.generate_client')!
|
||||
if client_actions.len > 0 {
|
||||
for client_action in client_actions {
|
||||
if found {
|
||||
return error('cannot find more than one her_code.generate_client ... in ${path}')
|
||||
}
|
||||
found = true
|
||||
mut p := client_action.params
|
||||
model = GenModel{
|
||||
name: p.get_default('name', '')!
|
||||
classname: p.get_default('classname', '')!
|
||||
title: p.get_default('title', '')!
|
||||
default: p.get_default_true('default')
|
||||
singleton: p.get_default_false('singleton')
|
||||
hasconfig: p.get_default_true('hasconfig')
|
||||
cat: .client
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if model.cat == .unknown {
|
||||
if path.contains('clients') {
|
||||
model.cat = .client
|
||||
} else {
|
||||
model.cat = .installer
|
||||
}
|
||||
}
|
||||
|
||||
if model.name == '' {
|
||||
model.name = os.base(path).to_lower()
|
||||
}
|
||||
|
||||
model.play_name = model.name
|
||||
|
||||
pathsub := path.replace('${os.home_dir()}/code/github/', '')
|
||||
model.module_path = pathsub.replace('/', '.').replace('.lib.', '.')
|
||||
|
||||
// !!hero_code.play
|
||||
// name:'docusaurus'
|
||||
|
||||
mut play_actions := plbook.find(filter: 'hero_code.play')!
|
||||
if play_actions.len > 1 {
|
||||
return error('should have max 1 hero_code.play action in ${config_path.path}')
|
||||
}
|
||||
if play_actions.len == 1 {
|
||||
mut p := play_actions[0].params
|
||||
model.play_name = p.get_default('name', model.name)!
|
||||
}
|
||||
|
||||
if model.module_path.contains('docusaurus') {
|
||||
println(model)
|
||||
println('4567ujhjk')
|
||||
exit(0)
|
||||
}
|
||||
|
||||
return model
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
# generation framework for clients & installers
|
||||
|
||||
```bash
|
||||
#generate all play commands
|
||||
hero generate -playonly
|
||||
#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
|
||||
title:''
|
||||
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
|
||||
|
||||
```
|
||||
|
||||
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 -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
|
||||
```
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
module installer_client
|
||||
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
@[params]
|
||||
pub struct ScannerArgs {
|
||||
pub mut:
|
||||
reset bool // regenerate all, dangerous !!!
|
||||
interactive bool // if we want to ask
|
||||
path string
|
||||
playonly bool
|
||||
}
|
||||
|
||||
// scan over a set of directories call the play where
|
||||
pub fn scan(args ScannerArgs) ! {
|
||||
console.print_debug('Code generator scan: ${args.path}')
|
||||
|
||||
if args.path == '' {
|
||||
scan(path: '${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/installers')!
|
||||
scan(path: '${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/clients')!
|
||||
scan(path: '${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/web')!
|
||||
return
|
||||
}
|
||||
|
||||
console.print_header('Scan for generation of code for ${args.path}')
|
||||
|
||||
// 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()!
|
||||
path_module := pparent.path
|
||||
if os.exists('${path_module}/.heroscript') {
|
||||
do(
|
||||
interactive: args.interactive
|
||||
path: path_module
|
||||
reset: args.reset
|
||||
playonly: args.playonly
|
||||
)!
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
|
||||
name: ??{model.name}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
!!hero_code.generate_client
|
||||
name: "${model.name}"
|
||||
classname: "${model.classname}"
|
||||
hasconfig: ${model.hasconfig}
|
||||
singleton: ${model.singleton}
|
||||
default: ${model.default}
|
||||
title: "${model.title}"
|
||||
@@ -1,11 +0,0 @@
|
||||
!!hero_code.generate_installer
|
||||
name: "${model.name}"
|
||||
classname: "${model.classname}"
|
||||
hasconfig: ${model.hasconfig}
|
||||
singleton: ${model.singleton}
|
||||
default: ${model.default}
|
||||
title: "${model.title}"
|
||||
templates: ${model.templates}
|
||||
build: ${model.build}
|
||||
startupmanager: ${model.startupmanager}
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
module ${model.name}
|
||||
|
||||
import freeflowuniverse.herolib.osal.core as osal
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.core
|
||||
import freeflowuniverse.herolib.installers.ulist
|
||||
import freeflowuniverse.herolib.installers.base
|
||||
|
||||
@if model.startupmanager
|
||||
import freeflowuniverse.herolib.osal.systemd
|
||||
import freeflowuniverse.herolib.osal.zinit
|
||||
@end
|
||||
|
||||
@if model.build
|
||||
import freeflowuniverse.herolib.installers.lang.golang
|
||||
import freeflowuniverse.herolib.installers.lang.rust
|
||||
import freeflowuniverse.herolib.installers.lang.python
|
||||
@end
|
||||
|
||||
import os
|
||||
|
||||
@if model.startupmanager
|
||||
fn startupcmd () ![]zinit.ZProcessNewArgs{
|
||||
mut installer := get()!
|
||||
mut res := []zinit.ZProcessNewArgs{}
|
||||
//THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
|
||||
// res << zinit.ZProcessNewArgs{
|
||||
// name: '${model.name}'
|
||||
// cmd: '${model.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 ${model.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: '${model.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('${model.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 model.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()!} ${model.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 ${model.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: '${model.name}'
|
||||
// source: '??{gitpath}/target/x86_64-unknown-linux-musl/release/${model.name}'
|
||||
// )!
|
||||
|
||||
}
|
||||
|
||||
fn install_() ! {
|
||||
console.print_header('install ${model.name}')
|
||||
//THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
|
||||
// mut url := ''
|
||||
// if core.is_linux_arm()! {
|
||||
// url = 'https://github.com/${model.name}-dev/${model.name}/releases/download/v??{version}/${model.name}_??{version}_linux_arm64.tar.gz'
|
||||
// } else if core.is_linux_intel()! {
|
||||
// url = 'https://github.com/${model.name}-dev/${model.name}/releases/download/v??{version}/${model.name}_??{version}_linux_amd64.tar.gz'
|
||||
// } else if core.is_osx_arm()! {
|
||||
// url = 'https://github.com/${model.name}-dev/${model.name}/releases/download/v??{version}/${model.name}_??{version}_darwin_arm64.tar.gz'
|
||||
// } else if core.is_osx_intel()! {
|
||||
// url = 'https://github.com/${model.name}-dev/${model.name}/releases/download/v??{version}/${model.name}_??{version}_darwin_amd64.tar.gz'
|
||||
// } else {
|
||||
// return error('unsported platform')
|
||||
// }
|
||||
|
||||
// mut dest := osal.download(
|
||||
// url: url
|
||||
// minsize_kb: 9000
|
||||
// expand_dir: '/tmp/${model.name}'
|
||||
// )!
|
||||
|
||||
// //dest.moveup_single_subdir()!
|
||||
|
||||
// mut binpath := dest.file_get('${model.name}')!
|
||||
// osal.cmd_add(
|
||||
// cmdname: '${model.name}'
|
||||
// source: binpath.path
|
||||
// )!
|
||||
}
|
||||
|
||||
@if model.build
|
||||
fn build_() ! {
|
||||
//url := 'https://github.com/threefoldtech/${model.name}'
|
||||
|
||||
// make sure we install base on the node
|
||||
// if core.platform()!= .ubuntu {
|
||||
// return error('only support ubuntu for now')
|
||||
// }
|
||||
|
||||
//mut g:=golang.get()!
|
||||
//g.install()!
|
||||
|
||||
//console.print_header('build coredns')
|
||||
|
||||
//mut gs := gittools.new(coderoot: '~/code')!
|
||||
// console.print_header('build ${model.name}')
|
||||
|
||||
// gitpath := gittools.get_repo(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
|
||||
@@ -1,335 +0,0 @@
|
||||
|
||||
module ${model.name}
|
||||
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core
|
||||
@if model.hasconfig
|
||||
import freeflowuniverse.herolib.data.encoderhero
|
||||
@end
|
||||
|
||||
@if model.cat == .installer
|
||||
import freeflowuniverse.herolib.osal.startupmanager
|
||||
import freeflowuniverse.herolib.osal.zinit
|
||||
import time
|
||||
@end
|
||||
|
||||
__global (
|
||||
${model.name}_global map[string]&${model.classname}
|
||||
${model.name}_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
|
||||
@if model.singleton == false
|
||||
|
||||
^^[params]
|
||||
pub struct ArgsGet{
|
||||
pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
fn args_get (args_ ArgsGet) ArgsGet {
|
||||
mut model:=args_
|
||||
if model.name == ""{
|
||||
model.name = ${model.name}_default
|
||||
}
|
||||
if model.name == ""{
|
||||
model.name = "default"
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
pub fn get(args_ ArgsGet) !&${model.classname} {
|
||||
mut args := args_get(args_)
|
||||
if !(args.name in ${model.name}_global) {
|
||||
if args.name=="default"{
|
||||
if ! exists(args)!{
|
||||
if default{
|
||||
mut context:=base.context() or { panic("bug") }
|
||||
context.hero_config_set("${model.name}",args.name,heroscript_default()!)!
|
||||
}
|
||||
}
|
||||
load(args)!
|
||||
}
|
||||
}
|
||||
return ${model.name}_global[args.name] or {
|
||||
println(${model.name}_global)
|
||||
panic("could not get config for ??{args.name}.")
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@if model.hasconfig
|
||||
|
||||
//set the model in mem and the config on the filesystem
|
||||
pub fn set(o ${model.classname})! {
|
||||
mut o2:=obj_init(o)!
|
||||
${model.name}_global[o.name] = &o2
|
||||
${model.name}_default = o.name
|
||||
}
|
||||
|
||||
//check we find the config on the filesystem
|
||||
pub fn exists(args_ ArgsGet)!bool {
|
||||
mut model := args_get(args_)
|
||||
mut context:=base.context()!
|
||||
return context.hero_config_exists("${model.name}",model.name)
|
||||
}
|
||||
|
||||
//load the config error if it doesn't exist
|
||||
pub fn load(args_ ArgsGet) ! {
|
||||
mut model := args_get(args_)
|
||||
mut context:=base.context()!
|
||||
mut heroscript := context.hero_config_get("${model.name}",model.name)!
|
||||
play(heroscript:heroscript)!
|
||||
}
|
||||
|
||||
//save the config to the filesystem in the context
|
||||
pub fn save(o ${model.classname})! {
|
||||
mut context:=base.context()!
|
||||
heroscript := encoderhero.encode[${model.classname}](o)!
|
||||
context.hero_config_set("${model.name}",o.name,heroscript)!
|
||||
}
|
||||
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
|
||||
|
||||
@if model.hasconfig
|
||||
mut configure_actions := plbook.find(filter: '${model.name}.configure')!
|
||||
if configure_actions.len > 0 {
|
||||
for config_action in configure_actions {
|
||||
mut p := config_action.params
|
||||
mycfg:=cfg_play(p)!
|
||||
console.print_debug("install action ${model.name}.configure\n??{mycfg}")
|
||||
set(mycfg)!
|
||||
save(mycfg)!
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@if model.cat == .installer
|
||||
mut other_actions := plbook.find(filter: '${model.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 ${model.name}.destroy")
|
||||
destroy_()!
|
||||
}
|
||||
if other_action.name == "install"{
|
||||
console.print_debug("install action ${model.name}.install")
|
||||
install_()!
|
||||
}
|
||||
}
|
||||
@if model.startupmanager
|
||||
if other_action.name in ["start","stop","restart"]{
|
||||
mut p := other_action.params
|
||||
name := p.get('name')!
|
||||
mut ${model.name}_obj:=get(name:name)!
|
||||
console.print_debug("action object:\n??{${model.name}_obj}")
|
||||
if other_action.name == "start"{
|
||||
console.print_debug("install action ${model.name}.??{other_action.name}")
|
||||
${model.name}_obj.start()!
|
||||
}
|
||||
|
||||
if other_action.name == "stop"{
|
||||
console.print_debug("install action ${model.name}.??{other_action.name}")
|
||||
${model.name}_obj.stop()!
|
||||
}
|
||||
if other_action.name == "restart"{
|
||||
console.print_debug("install action ${model.name}.??{other_action.name}")
|
||||
${model.name}_obj.restart()!
|
||||
}
|
||||
}
|
||||
@end
|
||||
}
|
||||
@end
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@if model.cat == .installer
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@if model.hasconfig
|
||||
//load from disk and make sure is properly intialized
|
||||
pub fn (mut self ${model.classname}) reload() ! {
|
||||
switch(self.name)
|
||||
self=obj_init(self)!
|
||||
}
|
||||
@end
|
||||
|
||||
@if model.startupmanager
|
||||
|
||||
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()!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self ${model.classname}) start() ! {
|
||||
switch(self.name)
|
||||
if self.running()!{
|
||||
return
|
||||
}
|
||||
|
||||
console.print_header('${model.name} start')
|
||||
|
||||
if ! installed_()!{
|
||||
install_()!
|
||||
}
|
||||
|
||||
configure()!
|
||||
|
||||
start_pre()!
|
||||
|
||||
for zprocess in startupcmd()!{
|
||||
mut sm:=startupmanager_get(zprocess.startuptype)!
|
||||
|
||||
console.print_debug('starting ${model.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('${model.name} did not install properly.')
|
||||
|
||||
}
|
||||
|
||||
pub fn (mut self ${model.classname}) install_start(model InstallArgs) ! {
|
||||
switch(self.name)
|
||||
self.install(model)!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self ${model.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 ${model.classname}) restart() ! {
|
||||
switch(self.name)
|
||||
self.stop()!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self ${model.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
|
||||
}
|
||||
|
||||
@if model.singleton
|
||||
|
||||
pub fn install(args InstallArgs) ! {
|
||||
if args.reset {
|
||||
destroy()!
|
||||
}
|
||||
if ! (installed_()!){
|
||||
install_()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy() ! {
|
||||
destroy_()!
|
||||
}
|
||||
|
||||
@if model.build
|
||||
pub fn build() ! {
|
||||
build_()!
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@else
|
||||
|
||||
//switch instance to be used for ${model.name}
|
||||
pub fn switch(name string) {
|
||||
${model.name}_default = name
|
||||
}
|
||||
|
||||
|
||||
pub fn (mut self ${model.classname}) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset {
|
||||
destroy_()!
|
||||
}
|
||||
if ! (installed_()!){
|
||||
install_()!
|
||||
}
|
||||
}
|
||||
|
||||
@if model.build
|
||||
pub fn (mut self ${model.classname}) build() ! {
|
||||
switch(self.name)
|
||||
build_()!
|
||||
}
|
||||
@end
|
||||
|
||||
pub fn (mut self ${model.classname}) destroy() ! {
|
||||
switch(self.name)
|
||||
@if model.startupmanager
|
||||
self.stop() or {}
|
||||
@end
|
||||
destroy_()!
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
module ${model.name}
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import os
|
||||
|
||||
pub const version = '0.0.0'
|
||||
const singleton = ${model.singleton}
|
||||
const default = ${model.default}
|
||||
|
||||
@if model.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 model.cat == .installer
|
||||
heroscript:="
|
||||
!!${model.name}.configure
|
||||
name:'${model.name}'
|
||||
homedir: '{HOME}/hero/var/${model.name}'
|
||||
configpath: '{HOME}/.config/${model.name}/admin.yaml'
|
||||
username: 'admin'
|
||||
password: 'secretpassword'
|
||||
secret: ''
|
||||
title: 'My Hero DAG'
|
||||
host: 'localhost'
|
||||
port: 8888
|
||||
|
||||
"
|
||||
@else
|
||||
heroscript:="
|
||||
!!${model.name}.configure
|
||||
name:'${model.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 model.cat == .installer
|
||||
^^[heap]
|
||||
pub struct ${model.classname} {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
@if model.hasconfig
|
||||
homedir string
|
||||
configpath string
|
||||
username string
|
||||
password string @@[secret]
|
||||
secret string @@[secret]
|
||||
title string
|
||||
host string
|
||||
port int
|
||||
@end
|
||||
}
|
||||
@if model.hasconfig
|
||||
fn cfg_play(p paramsparser.Params) !${model.classname} {
|
||||
//THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above
|
||||
mut mycfg := ${model.classname}{
|
||||
name: p.get_default('name', 'default')!
|
||||
homedir: p.get_default('homedir', '{HOME}/hero/var/${model.name}')!
|
||||
configpath: p.get_default('configpath', '{HOME}/hero/var/${model.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 ${model.name}')
|
||||
}
|
||||
return mycfg
|
||||
}
|
||||
@end
|
||||
|
||||
@else
|
||||
|
||||
^^[heap]
|
||||
pub struct ${model.classname} {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
mail_from string
|
||||
mail_password string @@[secret]
|
||||
mail_port int
|
||||
mail_server string
|
||||
mail_username string
|
||||
}
|
||||
|
||||
@if model.hasconfig
|
||||
fn cfg_play(p paramsparser.Params) !${model.classname} {
|
||||
//THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above
|
||||
mut mycfg := ${model.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)!
|
||||
return mycfg
|
||||
}
|
||||
@end
|
||||
|
||||
@end
|
||||
|
||||
fn obj_init(obj_ ${model.classname})!${model.classname}{
|
||||
//never call get here, only thing we can do here is work on object itself
|
||||
mut obj:=obj_
|
||||
return obj
|
||||
}
|
||||
|
||||
@if model.cat == .installer
|
||||
//called before start if done
|
||||
fn configure() ! {
|
||||
@if model.cat == .installer
|
||||
//mut installer := get()!
|
||||
@else
|
||||
//mut client := get()!
|
||||
@end
|
||||
@if model.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
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
# ${model.name}
|
||||
|
||||
${model.title}
|
||||
|
||||
To get started
|
||||
|
||||
```vlang
|
||||
|
||||
@if model.cat == .installer
|
||||
|
||||
import freeflowuniverse.herolib.installers.something.${model.name} as ${model.name}_installer
|
||||
|
||||
heroscript:="
|
||||
!!${model.name}.configure name:'test'
|
||||
password: '1234'
|
||||
port: 7701
|
||||
|
||||
!!${model.name}.start name:'test' reset:1
|
||||
"
|
||||
|
||||
${model.name}_installer.play(heroscript=heroscript)!
|
||||
|
||||
//or we can call the default and do a start with reset
|
||||
//mut installer:= ${model.name}_installer.get()!
|
||||
//installer.start(reset:true)!
|
||||
|
||||
@else
|
||||
|
||||
import freeflowuniverse.herolib.clients. ${model.name}
|
||||
|
||||
mut client:= ${model.name}.get()!
|
||||
|
||||
client...
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
## example heroscript
|
||||
|
||||
@if model.cat == .installer
|
||||
```hero
|
||||
!!${model.name}.configure
|
||||
homedir: '/home/user/${model.name}'
|
||||
username: 'admin'
|
||||
password: 'secretpassword'
|
||||
title: 'Some Title'
|
||||
host: 'localhost'
|
||||
port: 8888
|
||||
|
||||
```
|
||||
@else
|
||||
```hero
|
||||
!!${model.name}.configure
|
||||
secret: '...'
|
||||
host: 'localhost'
|
||||
port: 8888
|
||||
```
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
module herocmds
|
||||
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.web.docusaurus
|
||||
import freeflowuniverse.herolib.core.playcmds
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import os
|
||||
import cli { Command, Flag }
|
||||
|
||||
@@ -14,13 +15,13 @@ pub fn cmd_docusaurus(mut cmdroot Command) Command {
|
||||
execute: cmd_docusaurus_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: 'reset'
|
||||
abbrev: 'r'
|
||||
description: 'will reset.'
|
||||
})
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .string
|
||||
@@ -40,31 +41,31 @@ pub fn cmd_docusaurus(mut cmdroot Command) Command {
|
||||
description: 'Path where docusaurus configuration is.'
|
||||
})
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .string
|
||||
required: false
|
||||
name: 'buildpath'
|
||||
abbrev: 'b'
|
||||
// default: ''
|
||||
description: 'Path where docusaurus build is.'
|
||||
})
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .string
|
||||
// required: false
|
||||
// name: 'buildpath'
|
||||
// abbrev: 'b'
|
||||
// // default: ''
|
||||
// description: 'Path where docusaurus build is.'
|
||||
// })
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .string
|
||||
required: false
|
||||
name: 'deploykey'
|
||||
abbrev: 'dk'
|
||||
// default: ''
|
||||
description: 'Path of SSH Key used to deploy.'
|
||||
})
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .string
|
||||
// required: false
|
||||
// name: 'deploykey'
|
||||
// abbrev: 'dk'
|
||||
// // default: ''
|
||||
// description: 'Path of SSH Key used to deploy.'
|
||||
// })
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .string
|
||||
required: false
|
||||
name: 'publish'
|
||||
// default: ''
|
||||
description: 'Path where to publish.'
|
||||
})
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .string
|
||||
// required: false
|
||||
// name: 'publish'
|
||||
// // default: ''
|
||||
// description: 'Path where to publish.'
|
||||
// })
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .bool
|
||||
@@ -74,13 +75,13 @@ pub fn cmd_docusaurus(mut cmdroot Command) Command {
|
||||
description: 'build and publish.'
|
||||
})
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .bool
|
||||
required: false
|
||||
name: 'builddevpublish'
|
||||
abbrev: 'bpd'
|
||||
description: 'build dev version and publish.'
|
||||
})
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .bool
|
||||
// required: false
|
||||
// name: 'builddevpublish'
|
||||
// abbrev: 'bpd'
|
||||
// description: 'build dev version and publish.'
|
||||
// })
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .bool
|
||||
@@ -105,102 +106,57 @@ pub fn cmd_docusaurus(mut cmdroot Command) Command {
|
||||
description: 'Run your dev environment on local browser.'
|
||||
})
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .bool
|
||||
required: false
|
||||
name: 'new'
|
||||
abbrev: 'n'
|
||||
description: 'create a new docusaurus site.'
|
||||
})
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .bool
|
||||
required: false
|
||||
name: 'reset'
|
||||
abbrev: 'r'
|
||||
description: 'reset the docusaurus building process, reinstall all.'
|
||||
})
|
||||
|
||||
cmdroot.add_command(cmd_run)
|
||||
return cmdroot
|
||||
}
|
||||
|
||||
fn cmd_docusaurus_execute(cmd Command) ! {
|
||||
mut open := cmd.flags.get_bool('open') or { false }
|
||||
// ---------- FLAGS ----------
|
||||
mut open_ := cmd.flags.get_bool('open') or { false }
|
||||
mut buildpublish := cmd.flags.get_bool('buildpublish') or { false }
|
||||
mut builddevpublish := cmd.flags.get_bool('builddevpublish') or { false }
|
||||
mut dev := cmd.flags.get_bool('dev') or { false }
|
||||
mut new := cmd.flags.get_bool('new') or { false }
|
||||
mut reset := cmd.flags.get_bool('reset') or { false }
|
||||
mut update := cmd.flags.get_bool('update') or { false }
|
||||
|
||||
// --- Build Path Logic ---
|
||||
mut build_path := cmd.flags.get_string('buildpath') or { '' }
|
||||
if build_path == '' {
|
||||
build_path = '${os.home_dir()}/hero/var/docusaurus'
|
||||
// ---------- PATH LOGIC ----------
|
||||
// Resolve the source directory that contains a “cfg” sub‑directory.
|
||||
mut path := cmd.flags.get_string('path') or { '' }
|
||||
mut url := cmd.flags.get_string('url') or { '' }
|
||||
|
||||
if path == '' && url == '' {
|
||||
path = os.getwd()
|
||||
}
|
||||
|
||||
// --- Path Logic ---
|
||||
mut provided_path := cmd.flags.get_string('path') or { '' }
|
||||
mut source_path := ''
|
||||
|
||||
if provided_path != '' {
|
||||
if !os.exists(provided_path) || !os.is_dir(provided_path) {
|
||||
return error('Provided path "${provided_path}" does not exist or is not a directory.')
|
||||
}
|
||||
|
||||
// Check if the provided path contains a cfg subdirectory (ebook directory structure)
|
||||
cfg_subdir := os.join_path(provided_path, 'cfg')
|
||||
if os.exists(cfg_subdir) && os.is_dir(cfg_subdir) {
|
||||
source_path = provided_path
|
||||
} else {
|
||||
if provided_path.ends_with('cfg') {
|
||||
// If path ends with cfg, use parent directory as source
|
||||
source_path = os.dir(provided_path)
|
||||
} else {
|
||||
return error('Provided path "${provided_path}" does not contain a "cfg" subdirectory.')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mut cwd := os.getwd()
|
||||
cfg_dir := os.join_path(cwd, 'cfg')
|
||||
if !os.exists(cfg_dir) || !os.is_dir(cfg_dir) {
|
||||
return error('Flag -path not provided and directory "./cfg" not found in the current working directory.')
|
||||
}
|
||||
source_path = cwd
|
||||
}
|
||||
|
||||
console.print_header('Running Docusaurus for: ${source_path}')
|
||||
|
||||
// Use the centralized site processing function from docusaurus module
|
||||
mysite := docusaurus.process_site_from_path(source_path, '')!
|
||||
site_name := mysite.siteconfig.name
|
||||
|
||||
// Set up the docusaurus factory
|
||||
docusaurus.factory_set(
|
||||
path_build: build_path
|
||||
reset: reset
|
||||
install: reset
|
||||
template_update: reset
|
||||
docusaurus_path := gittools.path(
|
||||
git_url: url
|
||||
path: path
|
||||
git_reset: reset
|
||||
git_pull: update
|
||||
)!
|
||||
|
||||
// Add the docusaurus site
|
||||
mut dsite := docusaurus.dsite_add(
|
||||
sitename: site_name
|
||||
path: source_path
|
||||
play: false // Site already processed
|
||||
console.print_header('Running Docusaurus for: ${docusaurus_path.path}')
|
||||
|
||||
playcmds.run(
|
||||
heroscript_path: docusaurus_path.path
|
||||
reset: false
|
||||
)!
|
||||
|
||||
// Execute the requested action directly
|
||||
// TODO: We need to load the sitename instead, or maybe remove it
|
||||
mut dsite := docusaurus.dsite_get("")!
|
||||
|
||||
|
||||
if buildpublish {
|
||||
// Build and publish production-ready artifacts
|
||||
dsite.build_publish()!
|
||||
} else if builddevpublish {
|
||||
dsite.build()!
|
||||
} else if dev {
|
||||
// Run local development server
|
||||
dsite.dev(
|
||||
open: open
|
||||
open: open_
|
||||
watch_changes: false
|
||||
)!
|
||||
} else {
|
||||
// Default: just build the static site
|
||||
dsite.build()!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,38 +170,8 @@ pub fn cmd_git(mut cmdroot Command) {
|
||||
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{
|
||||
@@ -245,21 +215,6 @@ fn cmd_git_execute(cmd Command) ! {
|
||||
|
||||
// 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 }
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
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.threefold.info/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 plbook
|
||||
mut plbook, _ := plbook_run(cmd)!
|
||||
// get name from the book.generate action
|
||||
if name == '' {
|
||||
mut a := plbook.get(filter: 'book.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)
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
module herocmds
|
||||
|
||||
// import freeflowuniverse.herolib.web.starlight
|
||||
import os
|
||||
import cli
|
||||
|
||||
// pub fn cmd_starlight(mut cmdroot Command) {
|
||||
// mut cmd_run := Command{
|
||||
// name: 'starlight'
|
||||
// description: 'Generate, build, run starlight sites.'
|
||||
// required_args: 0
|
||||
// execute: cmd_starlight_execute
|
||||
// }
|
||||
|
||||
// // cmd_run.add_flag(Flag{
|
||||
// // flag: .bool
|
||||
// // required: false
|
||||
// // name: 'reset'
|
||||
// // abbrev: 'r'
|
||||
// // description: 'will reset.'
|
||||
// // })
|
||||
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .string
|
||||
// required: false
|
||||
// name: 'url'
|
||||
// abbrev: 'u'
|
||||
// // default: ''
|
||||
// description: 'Url where starlight source is.'
|
||||
// })
|
||||
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .string
|
||||
// required: false
|
||||
// name: 'path'
|
||||
// abbrev: 'p'
|
||||
// // default: ''
|
||||
// description: 'Path where starlight source is.'
|
||||
// })
|
||||
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .string
|
||||
// required: false
|
||||
// name: 'deploykey'
|
||||
// abbrev: 'dk'
|
||||
// // default: ''
|
||||
// description: 'Path of SSH Key used to deploy.'
|
||||
// })
|
||||
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .string
|
||||
// required: false
|
||||
// name: 'publish'
|
||||
// // default: ''
|
||||
// description: 'Path where to publish.'
|
||||
// })
|
||||
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .bool
|
||||
// required: false
|
||||
// name: 'buildpublish'
|
||||
// abbrev: 'bp'
|
||||
// description: 'build and publish.'
|
||||
// })
|
||||
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .bool
|
||||
// required: false
|
||||
// name: 'builddevpublish'
|
||||
// abbrev: 'bpd'
|
||||
// description: 'build dev version and publish.'
|
||||
// })
|
||||
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .bool
|
||||
// required: false
|
||||
// name: 'update'
|
||||
// description: 'update your environment the template and the repo you are working on (git pull).'
|
||||
// })
|
||||
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .bool
|
||||
// required: false
|
||||
// name: 'dev'
|
||||
// abbrev: 'd'
|
||||
// description: 'Run your dev environment on local browser.'
|
||||
// })
|
||||
|
||||
// cmd_run.add_flag(Flag{
|
||||
// flag: .bool
|
||||
// required: false
|
||||
// name: 'new'
|
||||
// abbrev: 'n'
|
||||
// description: 'create a new starlight site.'
|
||||
// })
|
||||
|
||||
// cmdroot.add_command(cmd_run)
|
||||
// }
|
||||
|
||||
// fn cmd_starlight_execute(cmd Command) ! {
|
||||
// mut update := cmd.flags.get_bool('update') or { false }
|
||||
// mut init := cmd.flags.get_bool('new') or { false }
|
||||
// mut url := cmd.flags.get_string('url') or { '' }
|
||||
// mut publish_path := cmd.flags.get_string('publish') or { '' }
|
||||
// mut deploykey := cmd.flags.get_string('deploykey') or { '' }
|
||||
|
||||
// mut path := cmd.flags.get_string('path') or { '' }
|
||||
|
||||
// mut buildpublish := cmd.flags.get_bool('buildpublish') or { false }
|
||||
// mut builddevpublish := cmd.flags.get_bool('builddevpublish') or { false }
|
||||
// mut dev := cmd.flags.get_bool('dev') or { false }
|
||||
|
||||
// // if build== false && build== false && build== false {
|
||||
// // eprintln("specify build, builddev or dev")
|
||||
// // exit(1)
|
||||
// // }
|
||||
|
||||
// mut docs := starlight.new(update: update)!
|
||||
// mut site := docs.get(
|
||||
// url: url
|
||||
// path: path
|
||||
// update: update
|
||||
// publish_path: publish_path
|
||||
// deploykey: deploykey
|
||||
// init: init
|
||||
// )!
|
||||
|
||||
// if publish_path.len > 0 {
|
||||
// site.build()!
|
||||
// }
|
||||
|
||||
// if buildpublish {
|
||||
// site.build_publish()!
|
||||
// }
|
||||
|
||||
// if builddevpublish {
|
||||
// site.build_dev_publish()!
|
||||
// }
|
||||
|
||||
// if dev {
|
||||
// site.dev(host: 'localhost', port: 3000)!
|
||||
// }
|
||||
// }
|
||||
326
lib/core/herocmds/tofix.md
Normal file
326
lib/core/herocmds/tofix.md
Normal file
@@ -0,0 +1,326 @@
|
||||
## Overview
|
||||
|
||||
The **`lib/core/playcmds`** directory contains the core “play‑commands” that are used by the `playcmds.run()` entry‑point. The bulk of the code works, but there are several **logic, naming and dead‑code problems** that make the package harder to maintain, cause potential compile‑time collisions, and generate confusing TODO comments in the source.
|
||||
|
||||
Below you’ll find a **concise, actionable “TODO list”** grouped by file, together with a **short description of the problem** and **exact code changes that should be applied**. The aim is to get the module to compile cleanly, make the public API consistent, and remove dead / misleading code.
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ `lib/core/playcmds/play_docusaurus.v`
|
||||
|
||||
| Problem | What to do |
|
||||
|---|---|
|
||||
| **Name clash** – the file defines a **`fn play(mut plbook PlayBook) !`** in the `playcmds` module. This collides with other possible `play` functions (e.g. `play_core`, `play_git`), and the function is never used by the factory. | 1. **Rename** the function to `pub fn play_docusaurus(mut plbook PlayBook) !`. <br>2. Update the `factory.v` to call the renamed function (or simply remove this file and call `docusaurus.play` directly). <br>3. Remove the import of `PlayBook` from the file header if it is no longer needed. |
|
||||
| **Unused import** – `import freeflowuniverse.herolib.core.playbook { PlayBook }` is only needed for the renamed function. If we decide to keep the wrapper, keep it; otherwise, delete the whole file. | Delete or comment out the file if you prefer to call `docusaurus.play` directly from `factory.v`. |
|
||||
| **Missing documentation** – The file has no comment describing why the wrapper exists. | Add a short comment: `// Wrapper used by legacy scripts – forwards to the docusaurus module.` |
|
||||
|
||||
### Concrete Patch (rename function, update factory)
|
||||
|
||||
```v
|
||||
// lib/core/playcmds/play_docusaurus.v
|
||||
module playcmds
|
||||
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.web.docusaurus
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Legacy wrapper: older scripts expected a “play” function in the
|
||||
// playcmds package. We keep it for backwards‑compatibility and
|
||||
// forward the call to the real docusaurus implementation.
|
||||
// -------------------------------------------------------------------
|
||||
pub fn play_docusaurus(mut plbook PlayBook) ! {
|
||||
docusaurus.play(mut plbook)!
|
||||
}
|
||||
```
|
||||
|
||||
**Update in `lib/core/playcmds/factory.v`**
|
||||
|
||||
```v
|
||||
// Replace the old call (if any) with the new wrapper:
|
||||
play_docusaurus.play(mut plbook)! // <-- new line, optional
|
||||
// OR simply delete the import of the wrapper if you decide to
|
||||
// use the docusaurus module directly.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ `lib/core/playcmds/play_git.v`
|
||||
|
||||
| Problem | What to do |
|
||||
|---|---|
|
||||
| **Wrong API name** – the code uses **`gittools.get(gittools.GitStructureArgGet{})`** – there is no `GitStructureArgGet` struct in the git‑tools package. The correct type is **`gittools.GitStructureArgs`** (or the default `gittools.GitStructure` argument). | Replace `GitStructureArgGet` with the correct type (`gittools.GitStructureArgs`). |
|
||||
| **Missing import alias** – the file uses `gittools.get` and `gittools.new` but the import is just `import freeflowuniverse.herolib.develop.gittools`. That is fine, but for clarity rename the import to **`gittools`** (it already is) and use the same alias everywhere. |
|
||||
| **Potential nil `gs`** – after a `git.clone` we do `gs = gittools.new(coderoot: coderoot)!`. This shadows the previous `gs` and loses the original configuration (e.g. `light`, `log`). The intent is to **re‑initialise** the `GitStructure` **only** when a `coderoot` is explicitly given. Keep the current flow but **document** the intention. |
|
||||
| **Unused variable `action_`** – the variable `action_` is used only for iteration. No problem. |
|
||||
| **Missing `gittools.GitCloneArgs`** – check that the struct is actually named `GitCloneArgs` in the git‑tools package. If not, change to the proper name. | Verify and, if needed, replace with the correct struct name (`gittools.GitCloneArgs`). |
|
||||
| **Missing error handling for unknown actions** – the code already prints an error and continues when `error_ignore` is true. That part is OK. |
|
||||
| **Redundant import** – the file imports `freeflowuniverse.herolib.ui.console` but only uses `console.print_stderr`. Keep it, but add a comment that it is for verbose error reporting. |
|
||||
| **Formatting** – add a header comment explaining what this file does (process git actions). | Add a comment block at the top of the file. |
|
||||
|
||||
### Concrete Patch (partial)
|
||||
|
||||
```v
|
||||
// lib/core/playcmds/play_git.v
|
||||
module playcmds
|
||||
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Git actions interpreter for HeroScript. This file
|
||||
// parses `!!git.*` actions and forwards them to the
|
||||
// gittools package.
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
fn play_git(mut plbook PlayBook) ! {
|
||||
// -----------------------------------------------------------
|
||||
// !!git.define – configure the GitStructure
|
||||
// -----------------------------------------------------------
|
||||
define_actions := plbook.find(filter: 'git.define')!
|
||||
mut gs := if define_actions.len > 0 {
|
||||
// ... (same as before)
|
||||
} else {
|
||||
// Default GitStructure (no args)
|
||||
gittools.get(gittools.GitStructureArgs{})!
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// !!git.clone – clone repositories
|
||||
// -----------------------------------------------------------
|
||||
// (unchanged)
|
||||
// -----------------------------------------------------------
|
||||
// !!git.repo_action – pull, commit, push, …
|
||||
// -----------------------------------------------------------
|
||||
// (unchanged)
|
||||
// -----------------------------------------------------------
|
||||
// !!git.list – print repo status
|
||||
// -----------------------------------------------------------
|
||||
// (unchanged)
|
||||
// -----------------------------------------------------------
|
||||
// !!git.reload_cache – reload git cache
|
||||
// -----------------------------------------------------------
|
||||
// (unchanged)
|
||||
}
|
||||
```
|
||||
|
||||
**Fix the `GitStructureArgs`** (if the actual struct name differs, adjust accordingly).
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ `lib/core/playcmds/play_core.v`
|
||||
|
||||
| Problem | What to do |
|
||||
|---|---|
|
||||
| **`env_set_once`** – the code uses `session.env_set(key, val)` for both `env_set` and `env_set_once`. The **`env_set_once`** method exists on `Session` and prevents overwriting a previously set key. If the intention is to set a variable *only* when it has not been set, use `session.env_set_once(key, val)`. | Change the `env_set_once` case to call `session.env_set_once`. |
|
||||
| **Unused comment** – the comment “// Use env_set instead of env_set_once to avoid duplicate errors” is contradictory. Replace the comment with a clear explanation. |
|
||||
| **Missing import** – `console` is already imported. No changes needed. |
|
||||
| **Potential missing `session.env` nil check** – if `plbook.session` is optional, a nil check should be added. This is defensive but not strictly required because a PlayBook always creates a Session. Still, add a guard. | Add a guard: `if plbook.session == none { return error('No session attached to PlayBook') }` before first use of `session`. |
|
||||
| **Unused variable** – `sitename := session.env_get('SITENAME') or { '' }` is never used. Remove it (or use it for templating if needed). |
|
||||
| **Formatting** – add a comment block at the top explaining purpose of the module. |
|
||||
|
||||
### Patch
|
||||
|
||||
```v
|
||||
// lib/core/playcmds/play_core.v
|
||||
module playcmds
|
||||
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Core play‑command processing (context, session, env‑subst, etc)
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
fn play_core(mut plbook PlayBook) ! {
|
||||
// ----------------------------------------------------------------
|
||||
// 1. Include handling (play include / echo)
|
||||
// ----------------------------------------------------------------
|
||||
// ... (unchanged)
|
||||
// ----------------------------------------------------------------
|
||||
// 2. Session environment handling
|
||||
// ----------------------------------------------------------------
|
||||
// Guard – make sure a session exists
|
||||
mut session := plbook.session or {
|
||||
return error('PlayBook has no attached Session')
|
||||
}
|
||||
|
||||
// !!session.env_set / env_set_once
|
||||
for mut action in plbook.find(filter: 'session.')! {
|
||||
mut p := action.params
|
||||
match action.name {
|
||||
'env_set' {
|
||||
key := p.get('key')!
|
||||
val := p.get('val') or { p.get('value')! }
|
||||
session.env_set(key, val)!
|
||||
}
|
||||
'env_set_once' {
|
||||
key := p.get('key')!
|
||||
val := p.get('val') or { p.get('value')! }
|
||||
// Use the dedicated “set‑once” method
|
||||
session.env_set_once(key, val)!
|
||||
}
|
||||
else { /* ignore unknown sub‑action */ }
|
||||
}
|
||||
action.done = true
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 3. Template replacement in action parameters
|
||||
// ----------------------------------------------------------------
|
||||
// (unchanged)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4️⃣ `lib/core/playcmds/factory.v`
|
||||
|
||||
| Problem | What to do |
|
||||
|---|---|
|
||||
| **Unused imports** – several imports are commented out. Keep them commented or delete if not needed. |
|
||||
| **Missing call to the renamed `play_docusaurus`** – after renaming, the factory should either call `play_docusaurus` or rely on `docusaurus.play` directly. | Either **(a)** remove the wrapper import entirely (since `docusaurus.play` is already called) **or** add a call to `play_docusaurus` if you want to keep the wrapper. |
|
||||
| **Potential dead‑code** – the commented-out sections for `play_ssh`, `play_publisher`, etc., are dead and can be removed to keep the file concise. |
|
||||
| **Consistency** – rename the `run` function to `run_playcmds` (optional) for clarity; not mandatory, but improves readability. |
|
||||
| **Add documentation** – a short header comment describing the purpose of the factory function. | Add comment at top of file. |
|
||||
|
||||
### Patch (cleanup)
|
||||
|
||||
```v
|
||||
module playcmds
|
||||
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook, PlayArgs }
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.core.playcmds
|
||||
import freeflowuniverse.herolib.core.playcmds.{play_core, play_git, play_docusaurus}
|
||||
import freeflowuniverse.herolib.web.docusaurus as docusaurus_mod // optional alias
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// run – entry point for all HeroScript play‑commands
|
||||
// -------------------------------------------------------------------
|
||||
pub fn run(args_ PlayArgs) ! {
|
||||
mut args := args_
|
||||
mut plbook := args.plbook or {
|
||||
playbook.new(text: args.heroscript, path: args.heroscript_path)!
|
||||
}
|
||||
|
||||
// Core actions
|
||||
play_core(mut plbook)!
|
||||
// Git actions
|
||||
play_git(mut plbook)!
|
||||
|
||||
// Business model (e.g. currency, bizmodel)
|
||||
bizmodel.play(mut plbook)! // <-- ensure that bizmodel.play exists
|
||||
|
||||
// OpenAI client
|
||||
openai.play(mut plbook)!
|
||||
|
||||
// Website / docs
|
||||
site.play(mut plbook)!
|
||||
doctree.play(mut plbook)!
|
||||
// Docusaurus – either call the wrapper or the module directly
|
||||
// play_docusaurus(mut plbook)! // <‑‑ optional wrapper
|
||||
docusaurus.play(mut plbook)! // direct call (preferred)
|
||||
|
||||
// (optional) other play‑commands can be added here
|
||||
|
||||
// Ensure we did not leave any actions un‑processed
|
||||
plbook.empty_check()!
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5️⃣ `_archive` Directory (All Files)
|
||||
|
||||
The `_archive` folder contains **dead, commented‑out, or placeholder code** (e.g., `bizmodel.v`, `currency.v`, `dagu.v`, etc.). They are compiled into the `playcmds` module but serve no purpose. Keeping them clutters the module namespace and may hide future compile errors.
|
||||
|
||||
**Action Items**
|
||||
|
||||
| File | Action |
|
||||
|------|-------|
|
||||
| `*_archive/*.v` | **Delete the whole `_archive` directory** (or move it outside of `lib/core/playcmds` to a non‑compiled location). |
|
||||
| If any of the archived files contain code that you still need (e.g., `play_juggler`), **move them** to a dedicated `playcmds/juggler.v` file or an appropriate sub‑module. |
|
||||
| After removal, run `v .` to verify the package builds without “duplicate module” errors. |
|
||||
|
||||
---
|
||||
|
||||
## 6️⃣ `lib/core/playcmds/play_ssh.v` – Minor Clean‑up
|
||||
|
||||
| Problem | What to do |
|
||||
|---|---|
|
||||
| **Unused import** – `import freeflowuniverse.herolib.osal.sshagent` is used, fine. |
|
||||
| **No `pub` on `play_ssh`** – the function is private but is referenced from `factory.v`. It is already called as `play_ssh(mut plbook)`. The function is **public** (no `pub` needed because it's called inside the same module). No change needed. |
|
||||
| **Comment about missing actions** – Keep a short comment stating “Currently only `key_add` is supported”. |
|
||||
| **Add error handling** – The `else` branch returns an error. This is fine. No changes needed. |
|
||||
|
||||
---
|
||||
|
||||
## 7️⃣ `lib/core/playcmds/play_luadns.v`
|
||||
|
||||
| Problem | What to do |
|
||||
|---|---|
|
||||
| **Unused import** – `os` is commented out (good). |
|
||||
| **Unused variable `mut buildroot` etc.** – These variables are **commented out** already. No change needed. |
|
||||
| **Naming** – function `play_luadns` is `pub` (good). |
|
||||
| **Potential missing `luadns` import** – The import is correct. No changes. |
|
||||
|
||||
---
|
||||
|
||||
## 8️⃣ `lib/core/playcmds/play_zola.v` – **All code is commented out**
|
||||
|
||||
No current code. Keep the file as a placeholder for future implementation. No action needed unless you want to **delete** it to keep the repo tidy.
|
||||
|
||||
---
|
||||
|
||||
## 9️⃣ `lib/core/playcmds/readme.md`
|
||||
|
||||
| Problem | What to do |
|
||||
|---|---|
|
||||
| **Out‑of‑date example** – It still refers to `playcmds.run(..., heroscript_path:'')`. The `run` signature now uses `PlayArgs`. Update the README to reflect the new `PlayArgs` struct (heroscript_path is now `heroscript_path` but `heroscript` is a string argument, not a map). |
|
||||
| **Add example** – Provide a tiny example that shows how to run from a `.hero` file using `run(PlayArgs{...})`. | Update README accordingly. |
|
||||
|
||||
### Example README update
|
||||
|
||||
```md
|
||||
# Using the playcmds package
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.core.playcmds
|
||||
|
||||
mut args := playcmds.PlayArgs{
|
||||
heroscript: my_hero_script,
|
||||
heroscript_path: '/tmp/hero',
|
||||
reset: false,
|
||||
}
|
||||
playcmds.run(args)!
|
||||
```
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Summary of Files to Modify
|
||||
|
||||
| File | Change(s) |
|
||||
|------|----------|
|
||||
| `lib/core/playcmds/play_docusaurus.v` | Rename exported function to `play_docusaurus`, update wrapper or remove. |
|
||||
| `lib/core/playcmds/play_git.v` | Correct `GitStructure` API, add header comment, ensure correct struct names (`GitStructureArgs`, `GitCloneArgs`). |
|
||||
| `lib/playcmds/play_core.v` | Use `env_set_once`, add session‑nil guard, remove unused `sitename`, add documentation comment. |
|
||||
| `lib/playcmds/factory.v` | Clean up imports, call `docusaurus.play` directly (or use renamed wrapper), remove dead/ commented-out sections, add file header comment. |
|
||||
| `lib/core/playcmds/_archive/*` | **Delete** entire `_archive` folder (or move needed code elsewhere). |
|
||||
| `lib/core/playcmds/readme.md` | Update example to match current `PlayArgs` struct. |
|
||||
| (optional) `lib/playcmds/play_docusaurus.v` (if wrapper kept) – add wrapper comment. |
|
||||
| (optional) `lib/playcmds/_archive/...` – move any needed code to proper modules. |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Next Steps
|
||||
|
||||
1. **Apply the patches** listed above (or copy‑paste the suggested code changes).
|
||||
2. **Run** `v .` from the repository root to verify that the package builds.
|
||||
3. **Run** the test suite (`./test_runner.vsh` or the GitHub actions) to ensure no regressions.
|
||||
4. **Commit** the changes with a clear commit message, e.g. `"fix: rename play_docusaurus, fix gittools API, cleanup playcmds module"`.
|
||||
|
||||
After these changes the **`lib/core/playcmds`** module will compile cleanly, the public API will be consistent, and the dead code will no longer pollute the package namespace. Happy coding! 🚀
|
||||
@@ -30,7 +30,7 @@ pub mut:
|
||||
// if overwrite this means will overwrite the last one in the directory
|
||||
pub fn (mut path Path) backup_path(args BackupArgs) !Path {
|
||||
if !path.exists() && args.restore == false {
|
||||
error('cannot find path, so cannot create backup for ${path}')
|
||||
return error('cannot find path, so cannot create backup for ${path}')
|
||||
}
|
||||
mut dest := ''
|
||||
mut rel := ''
|
||||
|
||||
@@ -13,6 +13,7 @@ pub mut:
|
||||
git_reset bool
|
||||
prio int = 50
|
||||
priorities map[int]string // filter and give priority, see filtersort method to know how to use
|
||||
replace map[string]string
|
||||
// session ?&base.Session
|
||||
}
|
||||
|
||||
@@ -25,10 +26,12 @@ pub mut:
|
||||
// git_branch string
|
||||
// git_reset bool
|
||||
// session &base.Session
|
||||
// replace map[string]string
|
||||
// ```
|
||||
pub fn new(args_ PlayBookNewArgs) !PlayBook {
|
||||
mut args := args_
|
||||
|
||||
|
||||
mut c := base.context() or { return error('failed to get context: ${err}') }
|
||||
|
||||
mut s := c.session_new()!
|
||||
|
||||
@@ -92,6 +92,23 @@ pub fn (mut plbook PlayBook) find(args FindArgs) ![]&Action {
|
||||
return res
|
||||
}
|
||||
|
||||
// use this in play function to make sure we only have one of those actions, the one action is then returned
|
||||
pub fn (mut plbook PlayBook) ensure_once(args FindArgs) !&Action {
|
||||
// println('DEBUG: In the error')
|
||||
|
||||
mut res := plbook.find(args) or { [] }
|
||||
// println('res: ${res}')
|
||||
if res.len == 0 {
|
||||
return error('No actions found based on filter: ${args.filter}')
|
||||
}
|
||||
|
||||
if res.len > 1 {
|
||||
return error('More than 1 action found based on filter: ${args.filter}, this Playbook only supports 1.')
|
||||
}
|
||||
return res[0] or { panic('bug') }
|
||||
}
|
||||
|
||||
// check its once, if not say that playbook only can have one action
|
||||
pub fn (mut plbook PlayBook) exists_once(args FindArgs) bool {
|
||||
mut res := plbook.find(args) or { [] }
|
||||
return res.len == 1
|
||||
@@ -101,6 +118,9 @@ pub fn (mut plbook PlayBook) exists_once(args FindArgs) bool {
|
||||
pub fn (mut plbook PlayBook) max_once(args FindArgs) !bool {
|
||||
mut res := plbook.find(args) or { [] }
|
||||
if res.len > 1 {
|
||||
$if debug {
|
||||
print_backtrace()
|
||||
}
|
||||
return error("found more than one action: '${args.filter}'")
|
||||
}
|
||||
return res.len == 1
|
||||
@@ -126,6 +146,9 @@ pub fn (mut plbook PlayBook) get(args FindArgs) !&Action {
|
||||
if res.len == 0 {
|
||||
return error("can't find action: '${args.filter}'")
|
||||
} else if res.len > 1 {
|
||||
$if debug {
|
||||
print_backtrace()
|
||||
}
|
||||
return error("found more than one action: '${args.filter}'")
|
||||
}
|
||||
return res[0] or { panic('bug') }
|
||||
|
||||
@@ -14,6 +14,7 @@ pub mut:
|
||||
result string // if any result
|
||||
nractions int
|
||||
done []int // which actions did we already find/run?
|
||||
path string
|
||||
session &base.Session @[skip; str: skip]
|
||||
}
|
||||
|
||||
@@ -83,7 +84,7 @@ pub fn (mut plbook PlayBook) actions_sorted(args SortArgs) ![]&Action {
|
||||
@[params]
|
||||
pub struct HeroScriptArgs {
|
||||
pub mut:
|
||||
show_done bool = true
|
||||
show_done bool
|
||||
}
|
||||
|
||||
// serialize to heroscript
|
||||
|
||||
@@ -12,6 +12,18 @@ enum State {
|
||||
othertext
|
||||
}
|
||||
|
||||
|
||||
// pub struct PlayBookNewArgs {
|
||||
// path string
|
||||
// text string
|
||||
// git_url string
|
||||
// git_pull bool
|
||||
// git_branch string
|
||||
// git_reset bool
|
||||
// prio int = 50
|
||||
// priorities map[int]string // filter and give priority, see filtersort method to know how to use
|
||||
// replace map[string]string
|
||||
// }
|
||||
pub fn (mut plbook PlayBook) add(args_ PlayBookNewArgs) ! {
|
||||
mut args := args_
|
||||
|
||||
@@ -21,9 +33,27 @@ pub fn (mut plbook PlayBook) add(args_ PlayBookNewArgs) ! {
|
||||
git_pull: args.git_pull
|
||||
git_reset: args.git_reset
|
||||
}
|
||||
args.path = gittools.path(git_path_args)!.path
|
||||
newpath := gittools.path(git_path_args)!
|
||||
args.path = newpath.path
|
||||
}
|
||||
|
||||
if plbook.path=="" && args.path!="" {
|
||||
plbook.path = args.path
|
||||
}
|
||||
|
||||
if args.text.len>0 && args.replace.len>0{
|
||||
//now we need to replace any placeholders in the text
|
||||
for key, value in args.replace {
|
||||
if key.starts_with('@') || key.starts_with('$') || key.starts_with('[') || key.starts_with('{') {
|
||||
args.text = args.text.replace(key, value)
|
||||
}else{
|
||||
args.text = args.text.replace("@${key}", value)
|
||||
args.text = args.text.replace("$\{${key}\}", value)
|
||||
args.text = args.text.replace("\{${key}\}", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// walk over directory
|
||||
if args.path.len > 0 {
|
||||
// console.print_header("PLBOOK add path:'${args.path}'")
|
||||
@@ -33,7 +63,7 @@ pub fn (mut plbook PlayBook) add(args_ PlayBookNewArgs) ! {
|
||||
}
|
||||
if p.is_file() {
|
||||
c := p.read()!
|
||||
plbook.add(text: c, prio: args.prio)!
|
||||
plbook.add(text: c, prio: args.prio, replace: args.replace)!
|
||||
return
|
||||
} else if p.is_dir() {
|
||||
// get .md and .hero files from dir
|
||||
@@ -45,7 +75,7 @@ pub fn (mut plbook PlayBook) add(args_ PlayBookNewArgs) ! {
|
||||
paths << ol2.paths
|
||||
for mut p2 in paths {
|
||||
c2 := p2.read()!
|
||||
plbook.add(text: c2, prio: args.prio)!
|
||||
plbook.add(text: c2, prio: args.prio, replace: args.replace)!
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -122,7 +152,7 @@ pub fn (mut plbook PlayBook) add(args_ PlayBookNewArgs) ! {
|
||||
paramsdata << line_strip.all_after_first(' ').trim_space()
|
||||
}
|
||||
if actionname.starts_with('!!!!!') {
|
||||
error('there is no action starting with 5 x !')
|
||||
return error('there is no action starting with 5 x !')
|
||||
} else if actionname.starts_with('!!!!') {
|
||||
action.actiontype = .wal
|
||||
} else if actionname.starts_with('!!!') {
|
||||
|
||||
60
lib/core/playbook/playbook_include.v
Normal file
60
lib/core/playbook/playbook_include.v
Normal file
@@ -0,0 +1,60 @@
|
||||
module playbook
|
||||
|
||||
import freeflowuniverse.herolib.develop.gittools // Added import for gittools
|
||||
|
||||
//REMARK: include is done in play_core
|
||||
|
||||
// // Include external playbook actions (from git repo or local path)
|
||||
// // based on actions defined as `!!play.include`.
|
||||
// // Parameters:
|
||||
// // git_url – git repository URL (optional)
|
||||
// // git_pull – pull latest changes (bool, default false)
|
||||
// // git_reset – reset local copy (bool, default false)
|
||||
// // path – local path to include (optional)
|
||||
// pub fn (mut plbook PlayBook) include() ! {
|
||||
// // Find all include actions in the playbook
|
||||
|
||||
// println(plbook)
|
||||
// if true{panic("568")}
|
||||
|
||||
// mut inc_actions := plbook.find(filter: 'play.include')!
|
||||
// if inc_actions.len == 0 {
|
||||
// return
|
||||
// }
|
||||
|
||||
// println(plbook)
|
||||
// if true{panic("56")}
|
||||
|
||||
// for mut inc in inc_actions {
|
||||
// mut p := inc.params
|
||||
|
||||
// // Extract parameters with sensible defaults
|
||||
// git_url := p.get_default('git_url', '')!
|
||||
// git_pull := p.get_default_false('git_pull')
|
||||
// git_reset := p.get_default_false('git_reset')
|
||||
// path := p.get_default('path', '')!
|
||||
|
||||
// // Resolve the path to include
|
||||
// mut includepath := ''
|
||||
// if git_url != '' {
|
||||
// // Resolve a git repository path (may clone / pull)
|
||||
// includepath = gittools.path(
|
||||
// git_url: git_url
|
||||
// path: path
|
||||
// git_pull: git_pull
|
||||
// git_reset: git_reset
|
||||
// )!.path
|
||||
// } else {
|
||||
// includepath = path
|
||||
// }
|
||||
|
||||
// // Add the found content (files / directories) to the current playbook.
|
||||
// // `add` will handle reading files, recursing into directories, etc.
|
||||
// if includepath != '' {
|
||||
// plbook.add(path: includepath)!
|
||||
// }
|
||||
|
||||
// // Mark this include action as processed
|
||||
// inc.done = true
|
||||
// }
|
||||
// }
|
||||
@@ -1,19 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
// import freeflowuniverse.herolib.core.playbook
|
||||
|
||||
// fn git(mut actions playbook.Actions, action playbook.Action) ! {
|
||||
// if action.name == 'init' {
|
||||
// // means we support initialization afterwards
|
||||
// c.bizmodel_init(mut actions, action)!
|
||||
// }
|
||||
|
||||
// // if action.name == 'get' {
|
||||
// // mut gs := gittools.get()!
|
||||
// // url := action.params.get('url')!
|
||||
// // branch := action.params.get_default('branch', '')!
|
||||
// // reset := action.params.get_default_false('reset')!
|
||||
// // pull := action.params.get_default_false('pull')!
|
||||
// // mut gr := gs.repo_get_from_url(url: url, branch: branch, pull: pull, reset: reset)!
|
||||
// // }
|
||||
// }
|
||||
@@ -1,21 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
// fn currency_actions(actions_ []playbook.Action) ! {
|
||||
// mut actions2 := actions.filtersort(actions: actions_, actor: 'currency', book: '*')!
|
||||
// if actions2.len == 0 {
|
||||
// return
|
||||
// }
|
||||
|
||||
// mut cs := currency.new()!
|
||||
|
||||
// for action in actions2 {
|
||||
// // TODO: set the currencies
|
||||
// if action.name == 'default_set' {
|
||||
// cur := action.params.get('cur')!
|
||||
// usdval := action.params.get_int('usdval')!
|
||||
// cs.default_set(cur, usdval)!
|
||||
// }
|
||||
// }
|
||||
|
||||
// // TODO: add the currency metainfo, do a test
|
||||
// }
|
||||
@@ -1,9 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
// import freeflowuniverse.herolib.installers.sysadmintools.daguserver
|
||||
|
||||
// pub fn scheduler(heroscript string) ! {
|
||||
// daguserver.play(
|
||||
// heroscript: heroscript
|
||||
// )!
|
||||
// }
|
||||
@@ -1,59 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
// import freeflowuniverse.herolib.core.playbook
|
||||
// import freeflowuniverse.herolib.sysadmin.downloader
|
||||
|
||||
// can start with sal, dal, ... the 2nd name is typicall the actor (or topic)
|
||||
// do this function public and then it breaches out to detail functionality
|
||||
|
||||
// pub fn sal_downloader(action playbook.Action) ! {
|
||||
// match action.actor {
|
||||
// 'downloader' {
|
||||
// match action.name {
|
||||
// 'get' {
|
||||
// downloader_get(action: action)!
|
||||
// }
|
||||
// else {
|
||||
// return error('actions not supported yet')
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// return error('actor not supported yet')
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn downloader_get(args ActionExecArgs) ! {
|
||||
// action := args.action
|
||||
// // session:=args.action or {panic("no context")} //if we need it here
|
||||
// mut name := action.params.get_default('name', '')!
|
||||
// mut downloadpath := action.params.get_default('downloadpath', '')!
|
||||
// mut url := action.params.get_default('url', '')!
|
||||
// mut reset := action.params.get_default_false('reset')
|
||||
// mut gitpull := action.params.get_default_false('gitpull')
|
||||
|
||||
// mut minsize_kb := action.params.get_u32_default('minsize_kb', 0)!
|
||||
// mut maxsize_kb := action.params.get_u32_default('maxsize_kb', 0)!
|
||||
|
||||
// mut destlink := action.params.get_default_false('destlink')
|
||||
|
||||
// mut dest := action.params.get_default('dest', '')!
|
||||
// mut hash := action.params.get_default('hash', '')!
|
||||
// mut metapath := action.params.get_default('metapath', '')!
|
||||
|
||||
// mut meta := downloader.download(
|
||||
// name: name
|
||||
// downloadpath: downloadpath
|
||||
// url: url
|
||||
// reset: reset
|
||||
// gitpull: gitpull
|
||||
// minsize_kb: minsize_kb
|
||||
// maxsize_kb: maxsize_kb
|
||||
// destlink: destlink
|
||||
// dest: dest
|
||||
// hash: hash
|
||||
// metapath: metapath
|
||||
// // session:session // TODO IMPLEMENT (also optional)
|
||||
// )!
|
||||
// }
|
||||
@@ -1,143 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
// import freeflowuniverse.herolib.installers.web.caddy as caddy_installer
|
||||
// import freeflowuniverse.herolib.servers.caddy { CaddyFile }
|
||||
// import freeflowuniverse.herolib.core.playbook
|
||||
// import os
|
||||
// // import net.urllib
|
||||
|
||||
// pub fn play_caddy(mut plbook playbook.PlayBook) ! {
|
||||
// play_caddy_basic(mut plbook)!
|
||||
// play_caddy_configure(mut plbook)!
|
||||
// }
|
||||
|
||||
// pub fn play_caddy_configure(mut plbook playbook.PlayBook) ! {
|
||||
// mut caddy_actions := plbook.find(filter: 'caddy_configure')!
|
||||
// if caddy_actions.len == 0 {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub fn play_caddy_basic(mut plbook playbook.PlayBook) ! {
|
||||
// caddy_actions := plbook.find(filter: 'caddy.')!
|
||||
// if caddy_actions.len == 0 {
|
||||
// return
|
||||
// }
|
||||
|
||||
// mut install_actions := plbook.find(filter: 'caddy.install')!
|
||||
|
||||
// if install_actions.len > 0 {
|
||||
// for install_action in install_actions {
|
||||
// mut p := install_action.params
|
||||
// xcaddy := p.get_default_false('xcaddy')
|
||||
// file_path := p.get_default('file_path', '/etc/caddy')!
|
||||
// file_url := p.get_default('file_url', '')!
|
||||
// reset := p.get_default_false('reset')
|
||||
// start := p.get_default_false('start')
|
||||
// restart := p.get_default_false('restart')
|
||||
// stop := p.get_default_false('stop')
|
||||
// homedir := p.get_default('file_url', '')!
|
||||
// plugins := p.get_list_default('plugins', []string{})!
|
||||
|
||||
// caddy_installer.install(
|
||||
// xcaddy: xcaddy
|
||||
// file_path: file_path
|
||||
// file_url: file_url
|
||||
// reset: reset
|
||||
// start: start
|
||||
// restart: restart
|
||||
// stop: stop
|
||||
// homedir: homedir
|
||||
// plugins: plugins
|
||||
// )!
|
||||
// }
|
||||
// }
|
||||
|
||||
// mut config_actions := plbook.find(filter: 'caddy.configure')!
|
||||
// if config_actions.len > 0 {
|
||||
// mut coderoot := ''
|
||||
// mut reset := false
|
||||
// mut pull := false
|
||||
|
||||
// mut public_ip := ''
|
||||
|
||||
// mut c := caddy.get('')!
|
||||
// // that to me seems to be wrong, not generic enough
|
||||
// if config_actions.len > 1 {
|
||||
// return error('can only have 1 config action for books')
|
||||
// } else if config_actions.len == 1 {
|
||||
// mut p := config_actions[0].params
|
||||
// path := p.get_default('path', '/etc/caddy')!
|
||||
// url := p.get_default('url', '')!
|
||||
// public_ip = p.get_default('public_ip', '')!
|
||||
// c = caddy.configure('', homedir: path)!
|
||||
// config_actions[0].done = true
|
||||
// }
|
||||
|
||||
// mut caddyfile := CaddyFile{}
|
||||
// for mut action in plbook.find(filter: 'caddy.add_reverse_proxy')! {
|
||||
// mut p := action.params
|
||||
// mut from := p.get_default('from', '')!
|
||||
// mut to := p.get_default('to', '')!
|
||||
|
||||
// if from == '' || to == '' {
|
||||
// return error('from & to cannot be empty')
|
||||
// }
|
||||
|
||||
// caddyfile.add_reverse_proxy(
|
||||
// from: from
|
||||
// to: to
|
||||
// )!
|
||||
// action.done = true
|
||||
// }
|
||||
|
||||
// for mut action in plbook.find(filter: 'caddy.add_file_server')! {
|
||||
// mut p := action.params
|
||||
// mut domain := p.get_default('domain', '')!
|
||||
// mut root := p.get_default('root', '')!
|
||||
|
||||
// if root.starts_with('~') {
|
||||
// root = '${os.home_dir()}${root.trim_string_left('~')}'
|
||||
// }
|
||||
|
||||
// if domain == '' || root == '' {
|
||||
// return error('domain & root cannot be empty')
|
||||
// }
|
||||
|
||||
// caddyfile.add_file_server(
|
||||
// domain: domain
|
||||
// root: root
|
||||
// )!
|
||||
// action.done = true
|
||||
// }
|
||||
|
||||
// for mut action in plbook.find(filter: 'caddy.add_basic_auth')! {
|
||||
// mut p := action.params
|
||||
// mut domain := p.get_default('domain', '')!
|
||||
// mut username := p.get_default('username', '')!
|
||||
// mut password := p.get_default('password', '')!
|
||||
|
||||
// if domain == '' || username == '' || password == '' {
|
||||
// return error('domain & root cannot be empty')
|
||||
// }
|
||||
|
||||
// caddyfile.add_basic_auth(
|
||||
// domain: domain
|
||||
// username: username
|
||||
// password: password
|
||||
// )!
|
||||
// action.done = true
|
||||
// }
|
||||
|
||||
// for mut action in plbook.find(filter: 'caddy.generate')! {
|
||||
// c.set_caddyfile(caddyfile)!
|
||||
// action.done = true
|
||||
// }
|
||||
|
||||
// for mut action in plbook.find(filter: 'caddy.start')! {
|
||||
// c.start()!
|
||||
// action.done = true
|
||||
// }
|
||||
// c.reload()!
|
||||
// }
|
||||
// }
|
||||
@@ -1,95 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
// import freeflowuniverse.herolib.clients.daguclient
|
||||
// import freeflowuniverse.herolib.installers.sysadmintools.daguserver
|
||||
// import freeflowuniverse.herolib.installers.sysadmintools.daguserver
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import os
|
||||
|
||||
// pub fn play_dagu(mut plbook playbook.PlayBook) ! {
|
||||
// // dagu_actions := plbook.find(filter: 'dagu.')!
|
||||
// // if dagu_actions.len == 0 {
|
||||
// // return
|
||||
// // }
|
||||
|
||||
// // play_dagu_basic(mut plbook)!
|
||||
// // play_dagu_configure(mut plbook)!
|
||||
// }
|
||||
|
||||
// // play_dagu plays the dagu play commands
|
||||
// pub fn play_dagu_basic(mut plbook playbook.PlayBook) ! {
|
||||
// // mut install_actions := plbook.find(filter: 'daguserver.configure')!
|
||||
|
||||
// // if install_actions.len > 0 {
|
||||
// // for install_action in install_actions {
|
||||
// // mut p := install_action.params
|
||||
// // panic("daguinstall play")
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // dagu_actions := plbook.find(filter: 'daguserver.install')!
|
||||
// // if dagu_actions.len > 0 {
|
||||
// // panic("daguinstall play")
|
||||
// // return
|
||||
// // }
|
||||
|
||||
// // mut config_actions := plbook.find(filter: 'dagu.configure')!
|
||||
// // mut d := if config_actions.len > 1 {
|
||||
// // return error('can only have 1 config action for dagu')
|
||||
// // } else if config_actions.len == 1 {
|
||||
// // mut p := config_actions[0].params
|
||||
// // instance := p.get_default('instance', 'default')!
|
||||
// // port := p.get_int_default('port', 8888)!
|
||||
// // username := p.get_default('username', '')!
|
||||
// // password := p.get_default('password', '')!
|
||||
// // config_actions[0].done = true
|
||||
// // mut server := daguserver.configure(instance,
|
||||
// // port: port
|
||||
// // username: username
|
||||
// // password: password
|
||||
// // )!
|
||||
// // server.start()!
|
||||
// // console.print_debug('Dagu server is running at http://localhost:${port}')
|
||||
// // console.print_debug('Username: ${username} password: ${password}')
|
||||
|
||||
// // // configure dagu client with server url and api secret
|
||||
// // server_cfg := server.config()!
|
||||
// // daguclient.get(instance,
|
||||
// // url: 'http://localhost:${port}'
|
||||
// // apisecret: server_cfg.secret
|
||||
// // )!
|
||||
// // } else {
|
||||
// // mut server := daguserver.get('')!
|
||||
// // server.start()!
|
||||
// // daguclient.get('')!
|
||||
// // }
|
||||
|
||||
// // mut dags := map[string]DAG{}
|
||||
|
||||
// // for mut action in plbook.find(filter: 'dagu.new_dag')! {
|
||||
// // mut p := action.params
|
||||
// // name := p.get_default('name', '')!
|
||||
// // dags[name] = DAG{}
|
||||
// // action.done = true
|
||||
// // }
|
||||
|
||||
// // for mut action in plbook.find(filter: 'dagu.add_step')! {
|
||||
// // mut p := action.params
|
||||
// // dag := p.get_default('dag', 'default')!
|
||||
// // name := p.get_default('name', 'default')!
|
||||
// // command := p.get_default('command', '')!
|
||||
// // dags[dag].step_add(
|
||||
// // nr: dags.len
|
||||
// // name: name
|
||||
// // command: command
|
||||
// // )!
|
||||
// // }
|
||||
|
||||
// // for mut action in plbook.find(filter: 'dagu.run')! {
|
||||
// // mut p := action.params
|
||||
// // dag := p.get_default('dag', 'default')!
|
||||
// // // d.new_dag(dags[dag])!
|
||||
// // panic('to implement')
|
||||
// // }
|
||||
// }
|
||||
@@ -1,31 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
|
||||
const dagu_script = "
|
||||
!!dagu.configure
|
||||
instance: 'test'
|
||||
username: 'admin'
|
||||
password: 'testpassword'
|
||||
|
||||
!!dagu.new_dag
|
||||
name: 'test_dag'
|
||||
|
||||
!!dagu.add_step
|
||||
dag: 'test_dag'
|
||||
name: 'hello_world'
|
||||
command: 'echo hello world'
|
||||
|
||||
!!dagu.add_step
|
||||
dag: 'test_dag'
|
||||
name: 'last_step'
|
||||
command: 'echo last step'
|
||||
|
||||
|
||||
"
|
||||
|
||||
fn test_play_dagu() ! {
|
||||
mut plbook := playbook.new(text: dagu_script)!
|
||||
play_dagu(mut plbook)!
|
||||
// panic('s')
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
import freeflowuniverse.herolib.data.doctree
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.develop.juggler
|
||||
import os
|
||||
|
||||
pub fn play_juggler(mut plbook playbook.PlayBook) ! {
|
||||
mut coderoot := ''
|
||||
// mut install := false
|
||||
mut reset := false
|
||||
mut pull := false
|
||||
|
||||
mut config_actions := plbook.find(filter: 'juggler.configure')!
|
||||
|
||||
mut j := juggler.Juggler{}
|
||||
|
||||
if config_actions.len > 1 {
|
||||
return error('can only have 1 config action for juggler')
|
||||
} else if config_actions.len == 1 {
|
||||
mut p := config_actions[0].params
|
||||
path := p.get_default('path', '/etc/juggler')!
|
||||
url := p.get_default('url', '')!
|
||||
username := p.get_default('username', '')!
|
||||
password := p.get_default('password', '')!
|
||||
port := p.get_int_default('port', 8000)!
|
||||
|
||||
j = juggler.configure(
|
||||
url: 'https://git.threefold.info/projectmycelium/itenv'
|
||||
username: username
|
||||
password: password
|
||||
reset: true
|
||||
)!
|
||||
config_actions[0].done = true
|
||||
}
|
||||
|
||||
for mut action in plbook.find(filter: 'juggler.start')! {
|
||||
j.start()!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
for mut action in plbook.find(filter: 'juggler.restart')! {
|
||||
j.restart()!
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
// import freeflowuniverse.herolib.hero.publishing
|
||||
|
||||
// pub fn play_publisher(mut plbook playbook.PlayBook) ! {
|
||||
// publishing.play(mut plbook)!
|
||||
// }
|
||||
@@ -1,34 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.core.playcmds
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import os
|
||||
|
||||
fn test_play_publisher() {
|
||||
mut p := pathlib.get_file(path: '/tmp/heroscript/do.hero', create: true)!
|
||||
|
||||
s2 := "
|
||||
|
||||
!!publisher.new_collection
|
||||
url:'https://git.threefold.info/tfgrid/info_tfgrid/src/branch/main/collections'
|
||||
reset: false
|
||||
pull: true
|
||||
|
||||
|
||||
!!book.define
|
||||
name:'info_tfgrid'
|
||||
summary_url:'https://git.threefold.info/tfgrid/info_tfgrid/src/branch/development/books/tech/SUMMARY.md'
|
||||
title:'ThreeFold Technology'
|
||||
collections: 'about,dashboard,farmers,library,partners_utilization,tech,p2p'
|
||||
|
||||
|
||||
!!book.publish
|
||||
name:'tech'
|
||||
production: false
|
||||
"
|
||||
p.write(s2)!
|
||||
|
||||
mut plbook := playbook.new(path: '/tmp/heroscript')!
|
||||
playcmds.play_publisher(mut plbook)!
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
// import freeflowuniverse.herolib.threefold.grid
|
||||
// import freeflowuniverse.herolib.threefold.tfrobot
|
||||
// import os
|
||||
|
||||
// pub fn play_threefold(mut plbook playbook.PlayBook) ! {
|
||||
// panic('fix tfrobot module')
|
||||
// // mut config_actions := plbook.find(filter: 'threefold.configure')!
|
||||
|
||||
// // mnemonics_ := os.getenv_opt('TFGRID_MNEMONIC') or { '' }
|
||||
// // mut ssh_key := os.getenv_opt('SSH_KEY') or { '' }
|
||||
|
||||
// // tfrobot.configure('play', network: 'main', mnemonics: mnemonics_)!
|
||||
|
||||
// // mut robot := tfrobot.get('play')!
|
||||
|
||||
// // if config_actions.len > 1 {
|
||||
// // return error('can only have 1 config action for threefold')
|
||||
// // } else if config_actions.len == 1 {
|
||||
// // mut a := config_actions[0]
|
||||
// // mut p := a.params
|
||||
// // mut network := p.get_default('network', 'main')!
|
||||
// // mnemonics := p.get_default('mnemonics', '')!
|
||||
// // ssh_key = p.get_default('ssh_key', '')!
|
||||
|
||||
// // network = network.to_lower()
|
||||
|
||||
// // // mnemonics string
|
||||
// // // network string = 'main'
|
||||
// // tfrobot.configure('play', network: network, mnemonics: mnemonics)!
|
||||
|
||||
// // robot = tfrobot.get('play')!
|
||||
|
||||
// // config_actions[0].done = true
|
||||
// // }
|
||||
// // cfg := robot.config()!
|
||||
// // if cfg.mnemonics == '' {
|
||||
// // return error('TFGRID_MNEMONIC should be specified as env variable')
|
||||
// // }
|
||||
|
||||
// // if ssh_key == '' {
|
||||
// // return error('SSHKey should be specified as env variable')
|
||||
// // }
|
||||
|
||||
// // panic('implement')
|
||||
|
||||
// // for mut action in plbook.find(filter: 'threefold.deploy_vm')! {
|
||||
// // mut p := action.params
|
||||
// // deployment_name := p.get_default('deployment_name', 'deployment')!
|
||||
// // name := p.get_default('name', 'vm')!
|
||||
// // ssh_key := p.get_default('ssh_key', '')!
|
||||
// // cores := p.get_int_default('cores', 1)!
|
||||
// // memory := p.get_int_default('memory', 20)!
|
||||
// // panic("implement")
|
||||
// // action.done = true
|
||||
// // }
|
||||
|
||||
// // for mut action in plbook.find(filter: 'threefold.deploy_zdb')! {
|
||||
// // panic("implement")
|
||||
// // action.done = true
|
||||
// // }
|
||||
// }
|
||||
@@ -1,246 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
// import freeflowuniverse.herolib.ui.console
|
||||
// import freeflowuniverse.herolib.web.zola
|
||||
// import freeflowuniverse.herolib.core.playbook
|
||||
|
||||
// struct WebsiteItem {
|
||||
// mut:
|
||||
// name string
|
||||
// site ?&zola.ZolaSite
|
||||
// }
|
||||
|
||||
// pub fn play_zola(mut plbook playbook.PlayBook) ! {
|
||||
// // mut coderoot := ''
|
||||
// mut buildroot := ''
|
||||
// mut publishroot := ''
|
||||
// mut install := true
|
||||
// mut reset := false
|
||||
|
||||
// wsactions := plbook.find(filter: 'website.')!
|
||||
// if wsactions.len == 0 {
|
||||
// return
|
||||
// }
|
||||
|
||||
// mut config_actions := plbook.find(filter: 'websites:configure')!
|
||||
// if config_actions.len > 1 {
|
||||
// return error('can only have 1 config action for websites')
|
||||
// } else if config_actions.len == 1 {
|
||||
// mut p := config_actions[0].params
|
||||
// buildroot = p.get_default('buildroot', '')!
|
||||
// publishroot = p.get_default('publishroot', '')!
|
||||
// // coderoot = p.get_default('coderoot', '')!
|
||||
// install = p.get_default_true('install')
|
||||
// reset = p.get_default_false('reset')
|
||||
// config_actions[0].done = true
|
||||
// }
|
||||
// mut websites := zola.new(
|
||||
// path_build: buildroot
|
||||
// path_publish: publishroot
|
||||
// install: install
|
||||
// reset: reset
|
||||
// )!
|
||||
|
||||
// mut ws := WebsiteItem{}
|
||||
|
||||
// for mut action in plbook.find(filter: 'website.')! {
|
||||
// if action.name == 'define' {
|
||||
// console.print_debug('website.define')
|
||||
// mut p := action.params
|
||||
// ws.name = p.get('name')!
|
||||
// title := p.get_default('title', '')!
|
||||
// description := p.get_default('description', '')!
|
||||
// ws.site = websites.new(name: ws.name, title: title, description: description)!
|
||||
// } else if action.name == 'template_add' {
|
||||
// console.print_debug('website.template_add')
|
||||
// mut p := action.params
|
||||
// url := p.get_default('url', '')!
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for template_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// site_.template_add(url: url)!
|
||||
// } else if action.name == 'content_add' {
|
||||
// console.print_debug('website.content_add')
|
||||
// mut p := action.params
|
||||
// url := p.get_default('url', '')!
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for content_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// site_.content_add(url: url)!
|
||||
// } else if action.name == 'doctree_add' {
|
||||
// console.print_debug('website.doctree_add')
|
||||
// mut p := action.params
|
||||
// url := p.get_default('url', '')!
|
||||
// pull := p.get_default_false('pull')
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for doctree_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// site_.doctree_add(url: url, pull: pull)!
|
||||
// } else if action.name == 'post_add' {
|
||||
// console.print_debug('website.post_add')
|
||||
// mut p := action.params
|
||||
// name := p.get_default('name', '')!
|
||||
// collection := p.get_default('collection', '')!
|
||||
// file := p.get_default('file', '')!
|
||||
// page := p.get_default('page', '')!
|
||||
// pointer := p.get_default('pointer', '')!
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for doctree_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// site_.post_add(name: name, collection: collection, file: file, pointer: pointer)!
|
||||
// } else if action.name == 'blog_add' {
|
||||
// console.print_debug('website.blog_add')
|
||||
// mut p := action.params
|
||||
// name := p.get_default('name', '')!
|
||||
// collection := p.get_default('collection', '')!
|
||||
// file := p.get_default('file', '')!
|
||||
// page := p.get_default('page', '')!
|
||||
// pointer := p.get_default('pointer', '')!
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for doctree_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// site_.blog_add(name: name)!
|
||||
// } else if action.name == 'person_add' {
|
||||
// console.print_debug('website.person_add')
|
||||
// mut p := action.params
|
||||
// name := p.get_default('name', '')!
|
||||
// page := p.get_default('page', '')!
|
||||
// collection := p.get_default('collection', '')!
|
||||
// file := p.get_default('file', '')!
|
||||
// pointer := p.get_default('pointer', '')!
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for doctree_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// site_.person_add(
|
||||
// name: name
|
||||
// collection: collection
|
||||
// file: file
|
||||
// page: page
|
||||
// pointer: pointer
|
||||
// )!
|
||||
// } else if action.name == 'people_add' {
|
||||
// console.print_debug('website.people_add')
|
||||
// mut p := action.params
|
||||
// name := p.get_default('name', '')!
|
||||
// description := p.get_default('description', '')!
|
||||
// sort_by_ := p.get_default('sort_by', '')!
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for people_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// sort_by := zola.SortBy.from(sort_by_)!
|
||||
// site_.people_add(
|
||||
// name: name
|
||||
// title: p.get_default('title', '')!
|
||||
// sort_by: sort_by
|
||||
// description: description
|
||||
// )!
|
||||
// } else if action.name == 'blog_add' {
|
||||
// console.print_debug('website.blog_add')
|
||||
// mut p := action.params
|
||||
// name := p.get_default('name', '')!
|
||||
// description := p.get_default('description', '')!
|
||||
// sort_by_ := p.get_default('sort_by', '')!
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for people_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// sort_by := zola.SortBy.from(sort_by_)!
|
||||
// site_.blog_add(
|
||||
// name: name
|
||||
// title: p.get_default('title', '')!
|
||||
// sort_by: sort_by
|
||||
// description: description
|
||||
// )!
|
||||
// } else if action.name == 'news_add' {
|
||||
// console.print_debug('website.news_add')
|
||||
// mut p := action.params
|
||||
// name := p.get_default('name', '')!
|
||||
// collection := p.get_default('collection', '')!
|
||||
// pointer := p.get_default('pointer', '')!
|
||||
// file := p.get_default('file', '')!
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for news_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// site_.article_add(name: name, collection: collection, file: file, pointer: pointer)!
|
||||
// } else if action.name == 'header_add' {
|
||||
// console.print_debug('website.header_add')
|
||||
// mut p := action.params
|
||||
// template := p.get_default('template', '')!
|
||||
// logo := p.get_default('logo', '')!
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for doctree_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// site_.header_add(template: template, logo: logo)!
|
||||
// } else if action.name == 'header_link_add' {
|
||||
// console.print_debug('website.header_link_add')
|
||||
// mut p := action.params
|
||||
// page := p.get_default('page', '')!
|
||||
// label := p.get_default('label', '')!
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for header_link_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// site_.header_link_add(page: page, label: label)!
|
||||
// } else if action.name == 'footer_add' {
|
||||
// console.print_debug('website.footer_add')
|
||||
// mut p := action.params
|
||||
// template := p.get_default('template', '')!
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for doctree_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// site_.footer_add(template: template)!
|
||||
// } else if action.name == 'page_add' {
|
||||
// console.print_debug('website.page_add')
|
||||
// mut p := action.params
|
||||
// name := p.get_default('name', '')!
|
||||
// collection := p.get_default('collection', '')!
|
||||
// file := p.get_default('file', '')!
|
||||
// homepage := p.get_default_false('homepage')
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for doctree_add, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// site_.page_add(name: name, collection: collection, file: file, homepage: homepage)!
|
||||
|
||||
// // }else if action.name=="pull"{
|
||||
// // mut site_:=ws.site or { return error("can't find website for pull, should have been defined before with !!website.define")}
|
||||
// // site_.pull()!
|
||||
// } else if action.name == 'section_add' {
|
||||
// console.print_debug('website.section_add')
|
||||
// // mut p := action.params
|
||||
// // name := p.get_default('name', '')!
|
||||
// // // collection := p.get_default('collection', '')!
|
||||
// // // file := p.get_default('file', '')!
|
||||
// // // homepage := p.get_default_false('homepage')
|
||||
// // mut site_ := ws.site or {
|
||||
// // return error("can't find website for doctree_add, should have been defined before with !!website.define")
|
||||
// // }
|
||||
|
||||
// // site_.add_section(name: name)!
|
||||
|
||||
// // }else if action.name=="pull"{
|
||||
// // mut site_:=ws.site or { return error("can't find website for pull, should have been defined before with !!website.define")}
|
||||
// // site_.pull()!
|
||||
// } else if action.name == 'generate' {
|
||||
// mut site_ := ws.site or {
|
||||
// return error("can't find website for generate, should have been defined before with !!website.define")
|
||||
// }
|
||||
|
||||
// site_.generate()!
|
||||
// // site_.serve()!
|
||||
// } else {
|
||||
// return error("Cannot find right action for website. Found '${action.name}' which is a non understood action for !!website.")
|
||||
// }
|
||||
// action.done = true
|
||||
// }
|
||||
// }
|
||||
@@ -1,21 +1,15 @@
|
||||
module playcmds
|
||||
|
||||
// import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.data.doctree
|
||||
import freeflowuniverse.herolib.biz.bizmodel
|
||||
import freeflowuniverse.herolib.web.site
|
||||
import freeflowuniverse.herolib.web.docusaurus
|
||||
import freeflowuniverse.herolib.clients.openai
|
||||
|
||||
// import freeflowuniverse.herolib.hero.publishing
|
||||
// import freeflowuniverse.herolib.threefold.grid4.gridsimulator
|
||||
// import freeflowuniverse.herolib.installers.sysadmintools.daguserver
|
||||
// import freeflowuniverse.herolib.threefold.grid4.farmingsimulator
|
||||
// import freeflowuniverse.herolib.web.components.slides
|
||||
// import freeflowuniverse.herolib.installers.base as base_install
|
||||
// import freeflowuniverse.herolib.installers.infra.coredns
|
||||
// import freeflowuniverse.herolib.virt.hetzner
|
||||
// import freeflowuniverse.herolib.clients.b2
|
||||
// -------------------------------------------------------------------
|
||||
// run – entry point for all HeroScript play‑commands
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
@[params]
|
||||
pub struct PlayArgs {
|
||||
@@ -28,33 +22,29 @@ pub mut:
|
||||
|
||||
pub fn run(args_ PlayArgs) ! {
|
||||
mut args := args_
|
||||
|
||||
// println('DEBUG: the args is: ${args}')
|
||||
mut plbook := args.plbook or {
|
||||
playbook.new(text: args.heroscript, path: args.heroscript_path)!
|
||||
}
|
||||
|
||||
// Core actions
|
||||
play_core(mut plbook)!
|
||||
|
||||
// Git actions
|
||||
play_git(mut plbook)!
|
||||
|
||||
// play_ssh(mut plbook)!
|
||||
// play_publisher(mut plbook)!
|
||||
// play_zola(mut plbook)!
|
||||
// play_caddy(mut plbook)!
|
||||
// play_juggler(mut plbook)!
|
||||
// play_luadns(mut plbook)!
|
||||
// hetzner.heroplay(mut plbook)!
|
||||
// b2.heroplay(mut plbook)!
|
||||
|
||||
// plbook = farmingsimulator.play(mut plbook)!
|
||||
// plbook = gridsimulator.play(mut plbook)!
|
||||
// Business model (e.g. currency, bizmodel)
|
||||
bizmodel.play(mut plbook)!
|
||||
doctree.play(mut plbook)!
|
||||
docusaurus.play(mut plbook)!
|
||||
|
||||
// OpenAI client
|
||||
openai.play(mut plbook)!
|
||||
|
||||
// slides.play(mut plbook)!
|
||||
// base_install(play(mut plbook)!
|
||||
// coredns.play(mut plbook)!
|
||||
// Website / docs
|
||||
site.play(mut plbook)!
|
||||
doctree.play(mut plbook)!
|
||||
|
||||
// plbook.empty_check()!
|
||||
docusaurus.play(mut plbook)!
|
||||
|
||||
// Ensure we did not leave any actions un‑processed
|
||||
plbook.empty_check()!
|
||||
}
|
||||
|
||||
@@ -4,41 +4,31 @@ import freeflowuniverse.herolib.develop.gittools
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import os
|
||||
|
||||
// !!context.configure
|
||||
// name:'test'
|
||||
// coderoot:...
|
||||
// interactive:true
|
||||
// -------------------------------------------------------------------
|
||||
// Core play‑command processing (context, session, env‑subst, etc)
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
fn play_core(mut plbook PlayBook) ! {
|
||||
// for mut action in plbook.find(filter: 'context.configure')! {
|
||||
// mut p := action.params
|
||||
// mut session := plbook.session
|
||||
|
||||
// if p.exists('interactive') {
|
||||
// session.interactive = p.get_default_false('interactive')
|
||||
// }
|
||||
|
||||
// if p.exists('coderoot') {
|
||||
// panic('implement')
|
||||
// // mut coderoot := p.get_path_create('coderoot')!
|
||||
// // mut gs := gittools.get()!
|
||||
// }
|
||||
// action.done = true
|
||||
// }
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 1. Include handling (play include / echo)
|
||||
// ----------------------------------------------------------------
|
||||
// Track included paths to prevent infinite recursion
|
||||
mut included_paths := map[string]bool{}
|
||||
|
||||
for action_ in plbook.find(filter: 'play.*')! {
|
||||
|
||||
|
||||
for mut action_ in plbook.find(filter: 'play.*')! {
|
||||
|
||||
if action_.name == 'include' {
|
||||
console.print_debug('play run:${action_}')
|
||||
mut action := *action_
|
||||
mut toreplace := action.params.get_default('replace', '')!
|
||||
mut playrunpath := action.params.get_default('path', '')!
|
||||
if playrunpath.len == 0 {
|
||||
action.name = 'pull'
|
||||
playrunpath = gittools.get_repo_path(
|
||||
path: action.params.get_default('path', '')!
|
||||
path: playrunpath
|
||||
git_url: action.params.get_default('git_url', '')!
|
||||
git_reset: action.params.get_default_false('git_reset')
|
||||
git_pull: action.params.get_default_false('git_pull')
|
||||
@@ -48,15 +38,24 @@ fn play_core(mut plbook PlayBook) ! {
|
||||
return error("can't run a heroscript didn't find url or path.")
|
||||
}
|
||||
|
||||
// console.print_debug('play run:\n${action_}')
|
||||
if ! playrunpath.starts_with('/') {
|
||||
playrunpath=os.abs_path("${plbook.path}/${playrunpath}")
|
||||
}
|
||||
|
||||
console.print_debug('play run include path:${playrunpath}')
|
||||
|
||||
// Check for cycle detection
|
||||
if playrunpath in included_paths {
|
||||
console.print_debug('Skipping already included path: ${playrunpath}')
|
||||
continue
|
||||
}
|
||||
|
||||
console.print_debug('play run path:${playrunpath}')
|
||||
toreplacedict:=texttools.to_map(toreplace)
|
||||
included_paths[playrunpath] = true
|
||||
plbook.add(path: playrunpath)!
|
||||
plbook.add(path: playrunpath,replace:toreplacedict)!
|
||||
|
||||
action.done = true
|
||||
|
||||
}
|
||||
if action_.name == 'echo' {
|
||||
content := action_.params.get_default('content', "didn't find content")!
|
||||
@@ -64,30 +63,38 @@ fn play_core(mut plbook PlayBook) ! {
|
||||
}
|
||||
}
|
||||
|
||||
for mut action in plbook.find(filter: 'session.')! {
|
||||
mut p := action.params
|
||||
mut session := plbook.session
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 2. Session environment handling
|
||||
// ----------------------------------------------------------------
|
||||
// Guard – make sure a session exists
|
||||
mut session := plbook.session
|
||||
|
||||
// !!session.env_set / env_set_once
|
||||
for mut action in plbook.find(filter: 'session.')! {
|
||||
|
||||
//!!session.env_set key:'JWT_SHARED_KEY' val:'...'
|
||||
if action.name == 'env_set' {
|
||||
mut key := p.get('key')!
|
||||
mut val := p.get('val') or { p.get('value')! }
|
||||
session.env_set(key, val)!
|
||||
}
|
||||
|
||||
if action.name == 'env_set_once' {
|
||||
mut key := p.get('key')!
|
||||
mut val := p.get('val') or { p.get('value')! }
|
||||
// Use env_set instead of env_set_once to avoid duplicate errors
|
||||
session.env_set(key, val)!
|
||||
}
|
||||
|
||||
action.done = true
|
||||
}
|
||||
mut session := plbook.session
|
||||
|
||||
sitename := session.env_get('SITENAME') or { '' }
|
||||
mut p := action.params
|
||||
match action.name {
|
||||
'env_set' {
|
||||
key := p.get('key')!
|
||||
val := p.get('val') or { p.get('value')! }
|
||||
session.env_set(key, val)!
|
||||
}
|
||||
'env_set_once' {
|
||||
key := p.get('key')!
|
||||
val := p.get('val') or { p.get('value')! }
|
||||
// Use the dedicated “set‑once” method
|
||||
session.env_set_once(key, val)!
|
||||
}
|
||||
else { /* ignore unknown sub‑action */ }
|
||||
}
|
||||
action.done = true
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 3. Template replacement in action parameters
|
||||
// ----------------------------------------------------------------
|
||||
// Apply template replacement from session environment variables
|
||||
if session.env.len > 0 {
|
||||
// Create a map with name_fix applied to keys for template replacement
|
||||
@@ -135,4 +142,5 @@ fn play_core(mut plbook PlayBook) ! {
|
||||
session.save()!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
module playcmds
|
||||
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
// import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.web.docusaurus
|
||||
|
||||
fn play(mut plbook PlayBook) ! {
|
||||
// Use the new docusaurus.play() function which handles the new API structure
|
||||
docusaurus.play(mut plbook)!
|
||||
}
|
||||
@@ -2,10 +2,18 @@ module playcmds
|
||||
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.ui.console // For verbose error reporting
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Git actions interpreter for HeroScript. This file
|
||||
// parses `!!git.*` actions and forwards them to the
|
||||
// gittools package.
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
fn play_git(mut plbook PlayBook) ! {
|
||||
// Handle !!git.define action first to configure GitStructure
|
||||
// -----------------------------------------------------------
|
||||
// !!git.define – configure the GitStructure
|
||||
// -----------------------------------------------------------
|
||||
define_actions := plbook.find(filter: 'git.define')!
|
||||
mut gs := if define_actions.len > 0 {
|
||||
mut p := define_actions[0].params
|
||||
@@ -17,7 +25,7 @@ fn play_git(mut plbook PlayBook) ! {
|
||||
ssh_key_path := p.get_default('ssh_key_path', '')!
|
||||
reload := p.get_default_false('reload')
|
||||
|
||||
gittools.new( // Changed to gittools.new
|
||||
gittools.new(
|
||||
coderoot: coderoot
|
||||
light: light
|
||||
log: log
|
||||
@@ -27,11 +35,13 @@ fn play_git(mut plbook PlayBook) ! {
|
||||
reload: reload
|
||||
)!
|
||||
} else {
|
||||
// Initialize GitStructure with defaults
|
||||
gittools.get(gittools.GitStructureArgGet{})! // Changed to gittools.get with default args
|
||||
// Default GitStructure (no args)
|
||||
gittools.get()!
|
||||
}
|
||||
|
||||
// Handle !!git.clone action
|
||||
// -----------------------------------------------------------
|
||||
// !!git.clone – clone repositories
|
||||
// -----------------------------------------------------------
|
||||
clone_actions := plbook.find(filter: 'git.clone')!
|
||||
for action in clone_actions {
|
||||
mut p := action.params
|
||||
@@ -41,14 +51,16 @@ fn play_git(mut plbook PlayBook) ! {
|
||||
light := p.get_default_true('light')
|
||||
recursive := p.get_default_false('recursive')
|
||||
|
||||
mut clone_args := gittools.GitCloneArgs{ // Changed to gittools.GitCloneArgs
|
||||
mut clone_args := gittools.GitCloneArgs{
|
||||
url: url
|
||||
sshkey: sshkey
|
||||
recursive: recursive
|
||||
light: light
|
||||
}
|
||||
if coderoot.len > 0 {
|
||||
gs = gittools.new(coderoot: coderoot)! // Changed to gittools.new
|
||||
// Re-initialize GitStructure only when a coderoot is explicitly given.
|
||||
// This shadows the previous `gs` and loses the original configuration (e.g. `light`, `log`).
|
||||
gs = gittools.new(coderoot: coderoot)!
|
||||
}
|
||||
gs.clone(clone_args)!
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ fn play_ssh(mut plbook PlayBook) ! {
|
||||
agent.add(name, privkey)!
|
||||
}
|
||||
else {
|
||||
// Currently only `key_add` is supported
|
||||
return error('action name ${action.name} not supported')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,26 @@ pub fn to_array_int(r string) []int {
|
||||
return r2
|
||||
}
|
||||
|
||||
//convert a:b ,c:d,e:f to dict with keys a,c,e and corresponding values b,d,f
|
||||
pub fn to_map(mapstring string) map[string]string {
|
||||
mut result := map[string]string{}
|
||||
mut mapstring_array := to_array(mapstring)
|
||||
for item in mapstring_array {
|
||||
if item.contains(':') {
|
||||
parts := item.split(':')
|
||||
if parts.len == 2 {
|
||||
result[parts[0].trim_space()] = parts[1].trim_space().trim("'\"").trim_space()
|
||||
} else {
|
||||
panic('to_map: expected key:value pairs, got: ${item}')
|
||||
}
|
||||
} else {
|
||||
panic('to_map: expected key:value pairs, got: ${item}')
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
// intelligent way how to map a line to a map
|
||||
//```
|
||||
// r:=texttools.to_map("name,-,-,-,-,pid,-,-,-,-,path",
|
||||
@@ -37,7 +57,7 @@ pub fn to_array_int(r string) []int {
|
||||
// "root 304 0.0 0.0 408185328 1360 ?? S 16Dec23 0:34.06 \n \n")
|
||||
// assert {'name': 'root', 'pid': '1360', 'path': ''} == r3
|
||||
//```
|
||||
pub fn to_map(mapstring string, line string, delimiter_ string) map[string]string {
|
||||
pub fn to_map_special(mapstring string, line string, delimiter_ string) map[string]string {
|
||||
mapstring_array := split_smart(mapstring, '')
|
||||
mut line_array := split_smart(line, '')
|
||||
mut result := map[string]string{}
|
||||
@@ -71,7 +91,7 @@ pub fn to_list_map(mapstring string, txt_ string, delimiter_ string) []map[strin
|
||||
mut txt := remove_empty_lines(txt_)
|
||||
txt = dedent(txt)
|
||||
for line in txt.split_into_lines() {
|
||||
result << to_map(mapstring, line, delimiter_)
|
||||
result << to_map_special(mapstring, line, delimiter_)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
mut doctrees := map[string]&Tree{}
|
||||
|
||||
collection_actions := plbook.find(filter: 'doctree.scan')!
|
||||
for action in collection_actions {
|
||||
mut collection_actions := plbook.find(filter: 'doctree.scan')!
|
||||
for mut action in collection_actions {
|
||||
mut p := action.params
|
||||
name := p.get_default('name', 'main')!
|
||||
mut doctree := doctrees[name] or {
|
||||
@@ -20,11 +20,11 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
git_reset := p.get_default_false('git_reset')
|
||||
git_pull := p.get_default_false('git_pull')
|
||||
doctree.scan(path: path, git_url: git_url, git_reset: git_reset, git_pull: git_pull)!
|
||||
|
||||
action.done = true
|
||||
tree_set(doctree)
|
||||
}
|
||||
|
||||
export_actions := plbook.find(filter: 'doctree.export')!
|
||||
mut export_actions := plbook.find(filter: 'doctree.export')!
|
||||
if export_actions.len == 0 && collection_actions.len > 0 {
|
||||
// Only auto-export if we have collections to export
|
||||
name0 := 'main'
|
||||
@@ -38,7 +38,7 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
}
|
||||
}
|
||||
|
||||
for action in export_actions {
|
||||
for mut action in export_actions {
|
||||
mut p := action.params
|
||||
name := p.get_default('name', 'main')!
|
||||
destination := p.get('destination')!
|
||||
@@ -50,6 +50,7 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
reset: reset
|
||||
exclude_errors: exclude_errors
|
||||
)!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
// println(tree_list())
|
||||
|
||||
@@ -12,7 +12,9 @@ pub fn (params &Params) get(key_ string) !string {
|
||||
return p.value.trim(' ')
|
||||
}
|
||||
}
|
||||
// print_backtrace()
|
||||
$if debug {
|
||||
print_backtrace()
|
||||
}
|
||||
return error('Did not find key:${key} in ${params}')
|
||||
}
|
||||
|
||||
@@ -156,7 +158,10 @@ pub fn (params &Params) get_int_default(key string, defval int) !int {
|
||||
}
|
||||
|
||||
pub fn (params &Params) get_default_true(key string) bool {
|
||||
mut r := params.get(key) or { '' }
|
||||
mut r := ""
|
||||
if params.exists(key) {
|
||||
r = params.get(key) or { panic("bug") }
|
||||
}
|
||||
r = texttools.name_fix_no_underscore(r)
|
||||
if r == '' || r == '1' || r == 'true' || r == 'y' || r == 'yes' {
|
||||
return true
|
||||
@@ -165,8 +170,10 @@ pub fn (params &Params) get_default_true(key string) bool {
|
||||
}
|
||||
|
||||
pub fn (params &Params) get_default_false(key string) bool {
|
||||
mut r := params.get(key) or { '' }
|
||||
r = texttools.name_fix_no_underscore(r)
|
||||
mut r := ""
|
||||
if params.exists(key) {
|
||||
r = params.get(key) or { panic("bug") }
|
||||
} r = texttools.name_fix_no_underscore(r)
|
||||
if r == '' || r == '0' || r == 'false' || r == 'n' || r == 'no' {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -15,14 +15,22 @@ pub mut:
|
||||
git_url string
|
||||
git_pull bool
|
||||
git_reset bool
|
||||
git_root string
|
||||
}
|
||||
|
||||
// get_repo_path implements the GitUrlResolver interface
|
||||
pub fn get_repo_path(args GetRepoArgs) !string {
|
||||
if os.exists(args.path) {
|
||||
return args.path
|
||||
if args.path!=""{
|
||||
if os.exists(args.path) {
|
||||
return args.path
|
||||
}else{
|
||||
if args.git_url == "" {
|
||||
return error("can't resolve git repo path without url or existing path, ${args.path} does not exist.")
|
||||
}
|
||||
}
|
||||
}
|
||||
mut gs := get()!
|
||||
|
||||
mut gs := get(coderoot:args.git_root)!
|
||||
mut repo := gs.get_repo(
|
||||
url: args.git_url
|
||||
pull: args.git_pull
|
||||
|
||||
@@ -116,8 +116,11 @@ pub fn (mut gitstructure GitStructure) get_repo(args_ ReposGetArgs) !&GitRepo {
|
||||
}
|
||||
|
||||
if repositories.len > 1 {
|
||||
repos := repositories.map('- ${it} ${it.account}.${it.name}').join_lines()
|
||||
return error('Found more than one repository for \n${args}\n${repos}')
|
||||
// repos := repositories.map('- ${it.account}.${it.name}').join_lines()
|
||||
$if debug {
|
||||
print_backtrace()
|
||||
}
|
||||
return error('Found more than one repository for \n${args}')
|
||||
}
|
||||
|
||||
// the pull & reset was not used, now re-inserted
|
||||
|
||||
8
lib/develop/reprompt/.heroscript
Normal file
8
lib/develop/reprompt/.heroscript
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
!!hero_code.generate_client
|
||||
name:'reprompt'
|
||||
classname:'RepromptWorkspace'
|
||||
singleton:0
|
||||
default:1
|
||||
hasconfig:1
|
||||
reset:0
|
||||
30
lib/develop/reprompt/readme.md
Normal file
30
lib/develop/reprompt/readme.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# reprompt
|
||||
|
||||
|
||||
|
||||
To get started
|
||||
|
||||
```vlang
|
||||
|
||||
|
||||
import freeflowuniverse.herolib.clients. reprompt
|
||||
|
||||
mut client:= reprompt.get()!
|
||||
|
||||
client...
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
## example heroscript
|
||||
|
||||
```hero
|
||||
!!reprompt.configure
|
||||
secret: '...'
|
||||
host: 'localhost'
|
||||
port: 8888
|
||||
```
|
||||
|
||||
|
||||
12
lib/develop/reprompt/reprompt_do.v
Normal file
12
lib/develop/reprompt/reprompt_do.v
Normal file
@@ -0,0 +1,12 @@
|
||||
module reprompt
|
||||
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import freeflowuniverse.herolib.data.encoderhero
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import os
|
||||
|
||||
// your checking & initialization code if needed
|
||||
fn (mut ws RepromptWorkspace) reprompt() !string {
|
||||
// TODO: fill in template based on selection
|
||||
return ''
|
||||
}
|
||||
102
lib/develop/reprompt/reprompt_factory_.v
Normal file
102
lib/develop/reprompt/reprompt_factory_.v
Normal file
@@ -0,0 +1,102 @@
|
||||
module reprompt
|
||||
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
__global (
|
||||
reprompt_global map[string]&RepromptWorkspace
|
||||
reprompt_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 = 'default'
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
pub fn get(args_ ArgsGet) !&RepromptWorkspace {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
mut obj := RepromptWorkspace{
|
||||
name: args.name
|
||||
}
|
||||
if args.name !in reprompt_global {
|
||||
if !exists(args)! {
|
||||
set(obj)!
|
||||
} else {
|
||||
heroscript := context.hero_config_get('reprompt', args.name)!
|
||||
mut obj_ := heroscript_loads(heroscript)!
|
||||
set_in_mem(obj_)!
|
||||
}
|
||||
}
|
||||
return reprompt_global[args.name] or {
|
||||
println(reprompt_global)
|
||||
// bug if we get here because should be in globals
|
||||
panic('could not get config for reprompt with name, is bug:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
// register the config for the future
|
||||
pub fn set(o RepromptWorkspace) ! {
|
||||
set_in_mem(o)!
|
||||
mut context := base.context()!
|
||||
heroscript := heroscript_dumps(o)!
|
||||
context.hero_config_set('reprompt', o.name, heroscript)!
|
||||
}
|
||||
|
||||
// does the config exists?
|
||||
pub fn exists(args_ ArgsGet) !bool {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
return context.hero_config_exists('reprompt', args.name)
|
||||
}
|
||||
|
||||
pub fn delete(args_ ArgsGet) ! {
|
||||
mut args := args_get(args_)
|
||||
mut context := base.context()!
|
||||
context.hero_config_delete('reprompt', args.name)!
|
||||
if args.name in reprompt_global {
|
||||
// del reprompt_global[args.name]
|
||||
}
|
||||
}
|
||||
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o RepromptWorkspace) ! {
|
||||
mut o2 := obj_init(o)!
|
||||
reprompt_global[o.name] = &o2
|
||||
reprompt_default = o.name
|
||||
}
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
mut install_actions := plbook.find(filter: 'reprompt.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for install_action in install_actions {
|
||||
heroscript := install_action.heroscript()
|
||||
mut obj2 := heroscript_loads(heroscript)!
|
||||
set(obj2)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// switch instance to be used for reprompt
|
||||
pub fn switch(name string) {
|
||||
reprompt_default = name
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
@[params]
|
||||
pub struct DefaultConfigArgs {
|
||||
instance string = 'default'
|
||||
}
|
||||
48
lib/develop/reprompt/reprompt_model.v
Normal file
48
lib/develop/reprompt/reprompt_model.v
Normal file
@@ -0,0 +1,48 @@
|
||||
module reprompt
|
||||
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import freeflowuniverse.herolib.data.encoderhero
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import os
|
||||
|
||||
pub const version = '0.0.0'
|
||||
const singleton = false
|
||||
const default = true
|
||||
|
||||
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
||||
|
||||
@[heap]
|
||||
pub struct RepromptWorkspace {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
dirs []RepromptDir
|
||||
}
|
||||
|
||||
pub struct RepromptDir {
|
||||
pub mut:
|
||||
path pathlib.Path
|
||||
selections []string // paths selected in the RepromptDir
|
||||
}
|
||||
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ RepromptWorkspace) !RepromptWorkspace {
|
||||
mut mycfg := mycfg_
|
||||
if mycfg.password == '' && mycfg.secret == '' {
|
||||
return error('password or secret needs to be filled in for ${mycfg.name}')
|
||||
}
|
||||
return mycfg
|
||||
}
|
||||
|
||||
/////////////NORMALLY NO NEED TO TOUCH
|
||||
|
||||
pub fn heroscript_dumps(obj RepromptWorkspace) !string {
|
||||
// create heroscript following template
|
||||
// check for our homedir on our machine and replace in the heroscript to @HOME in path
|
||||
return encoderhero.encode[RepromptWorkspace](obj)!
|
||||
}
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !RepromptWorkspace {
|
||||
// TODO: parse heroscript populate RepromptWorkspace
|
||||
mut obj := encoderhero.decode[RepromptWorkspace](heroscript)!
|
||||
return obj
|
||||
}
|
||||
10
lib/develop/reprompt/templates/heroscript_template.hero
Normal file
10
lib/develop/reprompt/templates/heroscript_template.hero
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
!!reprompt.configure name:"default"
|
||||
|
||||
!!reprompt.workspace_dir name:"default"
|
||||
path:"@HOME/code/github/freeflowuniverse/herolib/lib/builder"
|
||||
selection:"path1,path2" //paths are relative in the path of workspace
|
||||
filter_exclude:","
|
||||
filter_include:","
|
||||
|
||||
|
||||
1
lib/develop/reprompt/templates/prompt.md
Normal file
1
lib/develop/reprompt/templates/prompt.md
Normal file
@@ -0,0 +1 @@
|
||||
TODO:...
|
||||
1607
lib/develop/reprompt/templates/prompt_example.md
Normal file
1607
lib/develop/reprompt/templates/prompt_example.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -53,7 +53,7 @@ pub fn ping(args PingArgs) !PingResult {
|
||||
}
|
||||
else {
|
||||
// println("${err} ${err.code()}")
|
||||
error("can't ping on osx (${err.code()})\n${err}")
|
||||
return error("can't ping on osx (${err.code()})\n${err}")
|
||||
}
|
||||
}
|
||||
} else if platform_ == .ubuntu {
|
||||
|
||||
@@ -388,11 +388,11 @@ fn (mut self TFDeployment) save() ! {
|
||||
}
|
||||
|
||||
fn (self TFDeployment) compress(data []u8) ![]u8 {
|
||||
return zlib.compress(data) or { error('Cannot compress the data due to: ${err}') }
|
||||
return zlib.compress(data) or { return error('Cannot compress the data due to: ${err}') }
|
||||
}
|
||||
|
||||
fn (self TFDeployment) decompress(data []u8) ![]u8 {
|
||||
return zlib.decompress(data) or { error('Cannot decompress the data due to: ${err}') }
|
||||
return zlib.decompress(data) or { return error('Cannot decompress the data due to: ${err}') }
|
||||
}
|
||||
|
||||
fn (self TFDeployment) encrypt(compressed []u8) ![]u8 {
|
||||
|
||||
@@ -24,12 +24,6 @@ hero docusaurus -b -path /path/to/your/site
|
||||
hero docusaurus -bp -path /path/to/your/site
|
||||
```
|
||||
|
||||
The hero command automatically:
|
||||
|
||||
- Processes all heroscript files in `cfg/` directory and subdirectories
|
||||
- Filters out global includes (`!!play.include`) while preserving page definitions
|
||||
- Creates and configures the docusaurus site
|
||||
- Handles site name resolution from configuration
|
||||
|
||||
### Example HeroScript
|
||||
|
||||
|
||||
66
lib/web/docusaurus/config.v
Normal file
66
lib/web/docusaurus/config.v
Normal file
@@ -0,0 +1,66 @@
|
||||
module docusaurus
|
||||
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
|
||||
__global (
|
||||
docusaurus_sites map[string]&DocSite
|
||||
docusaurus_config []DocusaurusConfigParams
|
||||
docusaurus_last string //the last one we worked with
|
||||
)
|
||||
|
||||
pub struct DocusaurusConfig {
|
||||
pub mut:
|
||||
path_build pathlib.Path
|
||||
path_publish pathlib.Path
|
||||
install bool
|
||||
reset bool
|
||||
template_update bool
|
||||
coderoot string
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct DocusaurusConfigParams {
|
||||
pub mut:
|
||||
path_build string
|
||||
path_publish string
|
||||
install bool
|
||||
reset bool
|
||||
template_update bool
|
||||
coderoot string
|
||||
}
|
||||
|
||||
//return the last know config
|
||||
pub fn config() !DocusaurusConfig {
|
||||
if docusaurus_config.len == 0 {
|
||||
docusaurus_config << DocusaurusConfigParams{}
|
||||
}
|
||||
mut args:= docusaurus_config[0] or { panic("bug in docusaurus config") }
|
||||
if args.path_build == '' {
|
||||
args.path_build = '${os.home_dir()}/hero/var/docusaurus/build'
|
||||
}
|
||||
if args.path_publish == '' {
|
||||
args.path_publish = '${os.home_dir()}/hero/var/docusaurus/publish'
|
||||
}
|
||||
if !os.exists('${args.path_build}/node_modules') {
|
||||
args.install = true
|
||||
}
|
||||
|
||||
mut c := DocusaurusConfig{
|
||||
path_publish: pathlib.get_dir(path: args.path_publish, create: true)!
|
||||
path_build: pathlib.get_dir(path: args.path_build, create: true)!
|
||||
coderoot: args.coderoot
|
||||
install: args.install
|
||||
reset: args.reset
|
||||
template_update: args.template_update
|
||||
}
|
||||
if c.install {
|
||||
install(c)!
|
||||
c.install=true
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
pub fn config_set(args_ DocusaurusConfigParams) ! {
|
||||
docusaurus_config = [args_]
|
||||
}
|
||||
@@ -1,25 +1,23 @@
|
||||
module docusaurus
|
||||
|
||||
import freeflowuniverse.herolib.osal.screen
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.web.site as sitemodule
|
||||
import freeflowuniverse.herolib.osal.core as osal
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import time
|
||||
|
||||
@[heap]
|
||||
pub struct DocSite {
|
||||
pub mut:
|
||||
name string
|
||||
url string
|
||||
path_src pathlib.Path
|
||||
// path_src pathlib.Path
|
||||
path_publish pathlib.Path
|
||||
path_build pathlib.Path
|
||||
errors []SiteError
|
||||
config Configuration
|
||||
website sitemodule.Site
|
||||
}
|
||||
generated bool
|
||||
}
|
||||
|
||||
pub fn (mut s DocSite) build() ! {
|
||||
s.generate()!
|
||||
@@ -52,6 +50,21 @@ pub fn (mut s DocSite) build_publish() ! {
|
||||
'
|
||||
retry: 0
|
||||
)!
|
||||
for item in s.website.siteconfig.build_dest {
|
||||
if item.path.trim_space().trim("/ ") == "" {
|
||||
$if debug{
|
||||
print_backtrace()
|
||||
}
|
||||
return error("build destination path is empty for docusaurus.")
|
||||
}
|
||||
osal.exec(
|
||||
cmd: '
|
||||
cd ${s.path_build.path}
|
||||
rsync -avz --delete -e "ssh -p 22 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" build/ ${item.path}
|
||||
'
|
||||
)!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@[params]
|
||||
@@ -81,104 +94,6 @@ pub fn (mut s DocSite) dev(args DevArgs) ! {
|
||||
s.open()!
|
||||
}
|
||||
|
||||
pub fn (mut s DocSite) dev_watch(args DevArgs) ! {
|
||||
s.generate()!
|
||||
|
||||
// Create screen session for docusaurus development server
|
||||
mut screen_name := 'docusaurus'
|
||||
mut sf := screen.new()!
|
||||
|
||||
// Add and start a new screen session
|
||||
mut scr := sf.add(
|
||||
name: screen_name
|
||||
cmd: '/bin/bash'
|
||||
start: true
|
||||
attach: false
|
||||
reset: true
|
||||
)!
|
||||
|
||||
// Send commands to the screen session
|
||||
console.print_item('To view the server output:: cd ${s.path_build.path}')
|
||||
scr.cmd_send('cd ${s.path_build.path}')!
|
||||
|
||||
// Start script recording in the screen session for log streaming
|
||||
log_file := '/tmp/docusaurus_${screen_name}.log'
|
||||
script_cmd := 'script -f ${log_file}'
|
||||
scr.cmd_send(script_cmd)!
|
||||
|
||||
// Small delay to ensure script is ready
|
||||
time.sleep(500 * time.millisecond)
|
||||
|
||||
// Start bun in the scripted session
|
||||
bun_cmd := 'bun start -p ${args.port} -h ${args.host}'
|
||||
scr.cmd_send(bun_cmd)!
|
||||
|
||||
// Stream the log output to current terminal
|
||||
console.print_header(' Docusaurus Development Server')
|
||||
console.print_item('Streaming server output... Press Ctrl+C to detach and leave server running')
|
||||
console.print_item('Server will be available at: http://${args.host}:${args.port}')
|
||||
console.print_item('To reattach later: screen -r ${screen_name}')
|
||||
println('')
|
||||
|
||||
// Stream logs until user interrupts
|
||||
s.stream_logs(log_file, screen_name)!
|
||||
|
||||
// After user interrupts, show final instructions
|
||||
console.print_header(' Server Running in Background')
|
||||
console.print_item('✓ Development server is running in background')
|
||||
console.print_item('Server URL: http://${args.host}:${args.port}')
|
||||
console.print_item('To reattach: screen -r ${screen_name}')
|
||||
console.print_item('To stop server: screen -S ${screen_name} -X kill')
|
||||
console.print_item('The site content is on: ${s.path_src.path}/docs')
|
||||
|
||||
// Start the watcher in a separate thread
|
||||
// mut tf:=spawn watch_docs(docs_path, s.path_src.path, s.path_build.path)
|
||||
// tf.wait()!
|
||||
println('\n')
|
||||
|
||||
if args.open {
|
||||
s.open()!
|
||||
}
|
||||
|
||||
if args.watch_changes {
|
||||
docs_path := '${s.path_src.path}/docs'
|
||||
watch_docs(docs_path, s.path_src.path, s.path_build.path)!
|
||||
}
|
||||
}
|
||||
|
||||
// Stream logs from script file to current terminal until user interrupts
|
||||
fn (mut s DocSite) stream_logs(log_file string, screen_name string) ! {
|
||||
// Wait a moment for the log file to be created
|
||||
mut attempts := 0
|
||||
for !os.exists(log_file) && attempts < 10 {
|
||||
time.sleep(200 * time.millisecond)
|
||||
attempts++
|
||||
}
|
||||
|
||||
if !os.exists(log_file) {
|
||||
console.print_stderr('Warning: Log file not created, falling back to screen attach')
|
||||
console.print_item('Attaching to screen session... Press Ctrl+A then D to detach')
|
||||
// Fallback to direct screen attach
|
||||
osal.execute_interactive('screen -r ${screen_name}')!
|
||||
return
|
||||
}
|
||||
|
||||
// Use tail -f to stream the log file
|
||||
// The -f flag follows the file as it grows
|
||||
tail_cmd := 'tail -f ${log_file}'
|
||||
|
||||
// Execute tail in interactive mode - this will stream until Ctrl+C
|
||||
osal.execute_interactive(tail_cmd) or {
|
||||
// If tail fails, try alternative approach
|
||||
console.print_stderr('Log streaming failed, attaching to screen session...')
|
||||
osal.execute_interactive('screen -r ${screen_name}')!
|
||||
return
|
||||
}
|
||||
|
||||
// Clean up the log file after streaming
|
||||
os.rm(log_file) or {}
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct ErrorArgs {
|
||||
pub mut:
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
module docusaurus
|
||||
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import freeflowuniverse.herolib.web.site
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.osal.core as osal
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
// import freeflowuniverse.herolib.data.doctree
|
||||
|
||||
// Recursively process heroscript files in a directory
|
||||
fn process_heroscript_files_recursive(dir_path string) !string {
|
||||
mut combined_heroscript := ''
|
||||
files := os.ls(dir_path) or { return combined_heroscript }
|
||||
|
||||
for file in files {
|
||||
file_path := os.join_path(dir_path, file)
|
||||
|
||||
if os.is_dir(file_path) {
|
||||
// Recursively process subdirectories
|
||||
subdir_content := process_heroscript_files_recursive(file_path)!
|
||||
combined_heroscript += subdir_content
|
||||
} else if file.ends_with('.heroscript') {
|
||||
content := os.read_file(file_path) or { continue }
|
||||
|
||||
// Filter out only the play.include lines while keeping all other content
|
||||
lines := content.split('\n')
|
||||
mut filtered_lines := []string{}
|
||||
for line in lines {
|
||||
trimmed := line.trim_space()
|
||||
if !trimmed.starts_with('!!play.include') {
|
||||
filtered_lines << line
|
||||
}
|
||||
}
|
||||
filtered_content := filtered_lines.join('\n')
|
||||
|
||||
// Only add if there's meaningful content after filtering
|
||||
if filtered_content.trim_space().len > 0 {
|
||||
combined_heroscript += filtered_content + '\n\n'
|
||||
}
|
||||
}
|
||||
}
|
||||
return combined_heroscript
|
||||
}
|
||||
|
||||
// Central function to process site configuration from a path
|
||||
// This is the single point of use for all site processing logic
|
||||
// If sitename is empty, it will return the first available site
|
||||
pub fn process_site_from_path(path string, sitename string) !&site.Site {
|
||||
console.print_debug('Processing site configuration from: ${path}')
|
||||
|
||||
// Process the site configuration recursively (excluding global includes)
|
||||
combined_heroscript := process_heroscript_files_recursive('${path}/cfg')!
|
||||
console.print_debug('Combined heroscript length: ${combined_heroscript.len} characters')
|
||||
|
||||
if combined_heroscript.trim_space().len == 0 {
|
||||
return error('No valid heroscript files found in ${path}/cfg')
|
||||
}
|
||||
|
||||
// Create playbook and process site configuration
|
||||
mut plbook := playbook.new(text: combined_heroscript)!
|
||||
console.print_debug('Created playbook with ${plbook.actions.len} actions')
|
||||
site.play(mut plbook)!
|
||||
|
||||
// Check what sites were created
|
||||
available_sites := site.list()
|
||||
console.print_debug('Available sites after site.play(): ${available_sites}')
|
||||
|
||||
if available_sites.len == 0 {
|
||||
return error('No sites were created from the configuration')
|
||||
}
|
||||
|
||||
// Determine which site to return
|
||||
target_sitename := if sitename.len == 0 {
|
||||
console.print_debug('No specific site requested, using first available: ${available_sites[0]}')
|
||||
available_sites[0] // Use the first (and likely only) site
|
||||
} else {
|
||||
console.print_debug('Looking for specific site: ${sitename}')
|
||||
sitename
|
||||
}
|
||||
|
||||
mysite := site.get(name: target_sitename) or {
|
||||
return error('Failed to get site after playing playbook: ${target_sitename}. Available sites: ${available_sites}')
|
||||
}
|
||||
|
||||
console.print_debug('Site processed successfully: ${mysite.siteconfig.name} with ${mysite.pages.len} pages')
|
||||
return mysite
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct AddArgs {
|
||||
pub mut:
|
||||
sitename string // needs to exist in web.site module
|
||||
path string // site of the docusaurus site with the config as is needed to populate the docusaurus site
|
||||
git_url string
|
||||
git_reset bool
|
||||
git_root string
|
||||
git_pull bool
|
||||
path_publish string
|
||||
play bool = true
|
||||
}
|
||||
|
||||
pub fn dsite_add(args_ AddArgs) !&DocSite {
|
||||
mut args := args_
|
||||
args.sitename = texttools.name_fix(args_.sitename)
|
||||
|
||||
console.print_header('Add Docusaurus Site: ${args.sitename}')
|
||||
|
||||
if args.sitename in docusaurus_sites {
|
||||
return error('Docusaurus site ${args.sitename} already exists, returning existing.')
|
||||
}
|
||||
|
||||
mut path := gittools.path(
|
||||
path: args.path
|
||||
git_url: args.git_url
|
||||
git_reset: args.git_reset
|
||||
git_root: args.git_root
|
||||
git_pull: args.git_pull
|
||||
currentdir: false
|
||||
)!
|
||||
args.path = path.path
|
||||
if !path.is_dir() {
|
||||
return error('path is not a directory')
|
||||
}
|
||||
|
||||
if !os.exists('${args.path}/cfg') {
|
||||
return error('config directory for docusaurus does not exist in ${args.path}/cfg.\n${args}')
|
||||
}
|
||||
|
||||
configpath := '${args.path}/cfg'
|
||||
if !os.exists(configpath) {
|
||||
return error("can't find config file for docusaurus in ${configpath}")
|
||||
}
|
||||
|
||||
osal.rm('${args.path}/cfg/main.json')!
|
||||
osal.rm('${args.path}/cfg/footer.json')!
|
||||
osal.rm('${args.path}/cfg/navbar.json')!
|
||||
osal.rm('${args.path}/build.sh')!
|
||||
osal.rm('${args.path}/develop.sh')!
|
||||
osal.rm('${args.path}/sync.sh')!
|
||||
osal.rm('${args.path}/.DS_Store')!
|
||||
|
||||
mut f := factory_get()!
|
||||
|
||||
if args.path_publish == '' {
|
||||
args.path_publish = '${f.path_publish.path}/${args.sitename}'
|
||||
}
|
||||
|
||||
path_build_ := '${f.path_build.path}/${args.sitename}'
|
||||
|
||||
// get our website
|
||||
mut mysite := &site.Site{}
|
||||
if site.exists(name: args.sitename) {
|
||||
// Site already exists (likely processed by hero command), use existing site
|
||||
mysite = site.get(name: args.sitename)!
|
||||
} else {
|
||||
if !args.play {
|
||||
return error('Docusaurus site ${args.sitename} does not exist, please set play to true to create it.')
|
||||
}
|
||||
// Use the centralized site processing function
|
||||
mysite = process_site_from_path(args.path, args.sitename)!
|
||||
}
|
||||
|
||||
// Create the DocSite instance
|
||||
mut dsite := &DocSite{
|
||||
name: args.sitename
|
||||
path_src: pathlib.get_dir(path: args.path, create: false)!
|
||||
path_publish: pathlib.get_dir(path: args.path_publish, create: true)!
|
||||
path_build: pathlib.get_dir(path: path_build_, create: true)!
|
||||
config: new_configuration(mysite.siteconfig)!
|
||||
website: mysite
|
||||
}
|
||||
|
||||
docusaurus_sites[args.sitename] = dsite
|
||||
return dsite
|
||||
}
|
||||
@@ -1,199 +1,36 @@
|
||||
module docusaurus
|
||||
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import json
|
||||
import os
|
||||
import freeflowuniverse.herolib.osal.core as osal
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.data.doctree
|
||||
import freeflowuniverse.herolib.web.site as sitegen
|
||||
import freeflowuniverse.herolib.core.texttools.regext
|
||||
|
||||
pub fn (mut site DocSite) generate() ! {
|
||||
mut f := factory_get()!
|
||||
pub fn (mut docsite DocSite) generate() ! {
|
||||
if docsite.generated {
|
||||
return
|
||||
}
|
||||
mut c := config()!
|
||||
|
||||
console.print_header(' site generate: ${site.name} on ${f.path_build.path}')
|
||||
console.print_header(' site source on ${site.path_src.path}')
|
||||
console.print_header(' docsite generate: ${docsite.name} on ${c.path_build.path}')
|
||||
|
||||
// lets make sure we remove the cfg dir so we rebuild
|
||||
cfg_path := os.join_path(f.path_build.path, 'cfg')
|
||||
osal.rm('${c.path_build.path}/docs')!
|
||||
|
||||
cfg_path:="${c.path_build.path}/cfg"
|
||||
osal.rm(cfg_path)!
|
||||
|
||||
mut gs := gittools.new()!
|
||||
|
||||
template_path := gs.get_path(
|
||||
pull: false
|
||||
reset: false
|
||||
url: 'https://github.com/freeflowuniverse/docusaurus_template/src/branch/main/template/'
|
||||
)!
|
||||
|
||||
// we need to copy the template each time for these 2 items, otherwise there can be leftovers from other run
|
||||
for item in ['src', 'static'] {
|
||||
mut template_src_path := pathlib.get_dir(path: '${template_path}/${item}', create: true)!
|
||||
template_src_path.copy(dest: '${f.path_build.path}/${item}', delete: true)!
|
||||
// now copy the info which can be overruled from source in relation to the template
|
||||
if os.exists('${site.path_src.path}/${item}') {
|
||||
mut src_path := pathlib.get_dir(path: '${site.path_src.path}/${item}', create: false)!
|
||||
src_path.copy(dest: '${f.path_build.path}/${item}', delete: false)!
|
||||
}
|
||||
}
|
||||
|
||||
// We'll generate the configuration files after processing the site
|
||||
// This is moved to after sitegen.play() so we can use the processed site configuration
|
||||
|
||||
osal.rm('${f.path_build.path}/docs')!
|
||||
|
||||
if os.exists('${site.path_src.path}/docs') {
|
||||
mut aa := site.path_src.dir_get('docs')!
|
||||
aa.copy(dest: '${f.path_build.path}/docs', delete: true)!
|
||||
}
|
||||
|
||||
// now we need to process the pages, call the sitegen module, which will look for statements like
|
||||
// !!site.page sitename:'atest'
|
||||
// path:"crazy/sub.md" position:1
|
||||
// src:"marketplace_specs:tft_tfp_marketplace"
|
||||
// title:"Just a Page"
|
||||
// description:"A description not filled in"
|
||||
// draft:1 hide_title:1
|
||||
|
||||
configpath := '${site.path_src.path}/cfg'
|
||||
|
||||
// Create a playbook from the config path and run site processing
|
||||
mut plbook := playbook.new(path: configpath)!
|
||||
sitegen.play(mut plbook)!
|
||||
|
||||
// Get the updated site object after processing
|
||||
// The site name in the config might be different from the docusaurus site name
|
||||
// Find the site with the most pages (should contain the processed page definitions)
|
||||
available_sites := sitegen.list()
|
||||
mut best_site := &sitegen.Site(unsafe { nil })
|
||||
mut max_pages := 0
|
||||
|
||||
for site_name in available_sites {
|
||||
mut test_site := sitegen.get(name: site_name) or { continue }
|
||||
if test_site.pages.len > max_pages {
|
||||
max_pages = test_site.pages.len
|
||||
best_site = test_site
|
||||
}
|
||||
}
|
||||
|
||||
if best_site == unsafe { nil } || max_pages == 0 {
|
||||
return error('No sites with pages found after processing playbook. Available sites: ${available_sites}')
|
||||
}
|
||||
|
||||
mut updated_site := best_site
|
||||
|
||||
// Generate the configuration files using the processed site configuration
|
||||
mut updated_config := new_configuration(updated_site.siteconfig)!
|
||||
|
||||
mut main_file := pathlib.get_file(path: '${cfg_path}/main.json', create: true)!
|
||||
main_file.write(json.encode_pretty(updated_config.main))!
|
||||
main_file.write(json.encode_pretty(docsite.config.main))!
|
||||
|
||||
mut navbar_file := pathlib.get_file(path: '${cfg_path}/navbar.json', create: true)!
|
||||
navbar_file.write(json.encode_pretty(updated_config.navbar))!
|
||||
navbar_file.write(json.encode_pretty(docsite.config.navbar))!
|
||||
|
||||
mut footer_file := pathlib.get_file(path: '${cfg_path}/footer.json', create: true)!
|
||||
footer_file.write(json.encode_pretty(updated_config.footer))!
|
||||
footer_file.write(json.encode_pretty(docsite.config.footer))!
|
||||
|
||||
// Fix the index.tsx redirect to handle baseUrl properly
|
||||
// When baseUrl is not '/', we need to use an absolute redirect path
|
||||
if updated_config.main.base_url != '/' {
|
||||
index_tsx_path := '${f.path_build.path}/src/pages/index.tsx'
|
||||
if os.exists(index_tsx_path) {
|
||||
// Create the corrected index.tsx content
|
||||
fixed_index_content := "import React from 'react';
|
||||
import { Redirect } from '@docusaurus/router';
|
||||
import main from '../../cfg/main.json';
|
||||
docsite.generate_docs()!
|
||||
|
||||
export default function Home() {
|
||||
// Use absolute redirect path when baseUrl is not root
|
||||
const redirectPath = main.baseUrl + main.url_home;
|
||||
return <Redirect to={redirectPath} />;
|
||||
}"
|
||||
docsite.import()!
|
||||
|
||||
mut index_file := pathlib.get_file(path: index_tsx_path, create: false)!
|
||||
index_file.write(fixed_index_content)!
|
||||
}
|
||||
}
|
||||
|
||||
// Scan and export doctree collections to Redis before generating docs
|
||||
// This ensures the doctreeclient can access the collections when generating pages
|
||||
console.print_header(' scanning doctree collections for site: ${site.name}')
|
||||
|
||||
// Find the collections directory relative to the source path
|
||||
// The collections should be in the parent directory of the ebooks
|
||||
mut collections_path := ''
|
||||
|
||||
// Try to find collections directory by going up from the source path
|
||||
mut current_path := pathlib.get_dir(path: site.path_src.path)!
|
||||
for _ in 0 .. 5 { // Search up to 5 levels up
|
||||
collections_candidate := '${current_path.path}/collections'
|
||||
if os.exists(collections_candidate) {
|
||||
collections_path = collections_candidate
|
||||
break
|
||||
}
|
||||
parent := current_path.parent() or { break } // reached root or error
|
||||
if parent.path == current_path.path {
|
||||
break // reached root
|
||||
}
|
||||
current_path = parent
|
||||
}
|
||||
|
||||
if collections_path != '' {
|
||||
// Create a doctree and scan the collections
|
||||
mut tree := doctree.new(name: site.name)!
|
||||
tree.scan(path: collections_path)!
|
||||
|
||||
// Export to Redis and temporary location for doctreeclient access
|
||||
tree.export(
|
||||
destination: '/tmp/doctree_export_${site.name}'
|
||||
reset: true
|
||||
exclude_errors: false
|
||||
)!
|
||||
}
|
||||
|
||||
// Generate the actual docs content from the processed site configuration
|
||||
docs_path := '${f.path_build.path}/docs'
|
||||
console.print_header(' generating docs from site pages to: ${docs_path}')
|
||||
|
||||
generate_docs(
|
||||
path: docs_path
|
||||
site: updated_site
|
||||
)!
|
||||
|
||||
site.process_imports()!
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn (mut site DocSite) process_imports() ! {
|
||||
mut gs := gittools.new()!
|
||||
mut f:=factory_get()!
|
||||
|
||||
for item in site.website.siteconfig.imports {
|
||||
mypath := gs.get_path(
|
||||
pull: false
|
||||
reset: false
|
||||
url: item.url
|
||||
)!
|
||||
mut mypatho := pathlib.get(mypath)
|
||||
|
||||
mypatho.copy(dest: '${f.path_build.path}/docs/${item.dest}', delete: true)!
|
||||
|
||||
// println(item)
|
||||
// replace: {'NAME': 'MyName', 'URGENCY': 'red'}
|
||||
mut ri := regext.regex_instructions_new()
|
||||
for key, val in item.replace {
|
||||
ri.add_item('\{${key}\}', val)!
|
||||
}
|
||||
mypatho.copy(dest: '${f.path_build.path}/docs/${item.dest}', delete: true)!
|
||||
ri.replace_in_dir(
|
||||
path: '${f.path_build.path}/docs/${item.dest}'
|
||||
extensions: [
|
||||
'md',
|
||||
]
|
||||
)!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,29 +15,22 @@ mut:
|
||||
client &doctreeclient.DocTreeClient
|
||||
flat bool // if flat then won't use sitenames as subdir's
|
||||
site Site
|
||||
errors []string // collect errors here
|
||||
}
|
||||
|
||||
@[params]
|
||||
struct SiteGeneratorArgs {
|
||||
mut:
|
||||
path string
|
||||
flat bool // if flat then won't use sitenames as subdir's
|
||||
site Site
|
||||
errors []string // collect errors here
|
||||
}
|
||||
|
||||
// Generate docs from site configuration
|
||||
pub fn generate_docs(args SiteGeneratorArgs) ! {
|
||||
mut path := args.path
|
||||
if args.path == '' {
|
||||
return error('Path must be provided to generate site')
|
||||
}
|
||||
pub fn (mut docsite DocSite) generate_docs() ! {
|
||||
|
||||
c := config()!
|
||||
|
||||
//we generate the docs in the build path
|
||||
docs_path := '${c.path_build.path}/docs'
|
||||
|
||||
mut gen := SiteGenerator{
|
||||
path: pathlib.get_dir(path: path, create: true)!
|
||||
path: pathlib.get_dir(path: docs_path, create: true)!
|
||||
client: doctreeclient.new()!
|
||||
flat: args.flat
|
||||
site: args.site
|
||||
flat: true
|
||||
site: docsite.website
|
||||
}
|
||||
|
||||
for section in gen.site.sections {
|
||||
@@ -51,31 +44,28 @@ pub fn generate_docs(args SiteGeneratorArgs) ! {
|
||||
if gen.errors.len > 0 {
|
||||
return error('Errors occurred during site generation:\n${gen.errors.join('\n\n')}\nPlease fix the errors and try again.\nPage List: is header collection and page name per collection.\nAvailable pages:\n${gen.client.list_markdown()!}')
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
fn (mut mysite SiteGenerator) error( msg string) ! {
|
||||
fn (mut generator SiteGenerator) error(msg string) ! {
|
||||
console.print_stderr('Error: ${msg}')
|
||||
mysite.errors << msg
|
||||
generator.errors << msg
|
||||
}
|
||||
|
||||
|
||||
fn (mut mysite SiteGenerator) page_generate(args_ Page) ! {
|
||||
fn (mut generator SiteGenerator) page_generate(args_ Page) ! {
|
||||
mut args := args_
|
||||
|
||||
mut content := ['---']
|
||||
|
||||
mut parts := args.src.split(':')
|
||||
if parts.len != 2 {
|
||||
mysite.error("Invalid src format for page '${args.src}', expected format: collection:page_name, TODO: fix in ${args.path}, check the collection & page_name exists in the pagelist")!
|
||||
return
|
||||
generator.error("Invalid src format for page '${args.src}', expected format: collection:page_name, TODO: fix in ${args.path}, check the collection & page_name exists in the pagelist")!
|
||||
return
|
||||
}
|
||||
collection_name := parts[0]
|
||||
page_name := parts[1]
|
||||
|
||||
mut page_content := mysite.client.get_page_content(collection_name, page_name) or {
|
||||
mysite.error("Couldn't find page '${collection_name}:${page_name}' is formatted as collectionname:pagename. TODO: fix in ${args.path}, check the collection & page_name exists in the pagelist. ")!
|
||||
mut page_content := generator.client.get_page_content(collection_name, page_name) or {
|
||||
generator.error("Couldn't find page '${collection_name}:${page_name}' is formatted as collectionname:pagename. TODO: fix in ${args.path}, check the collection & page_name exists in the pagelist. ")!
|
||||
return
|
||||
}
|
||||
|
||||
@@ -129,7 +119,7 @@ fn (mut mysite SiteGenerator) page_generate(args_ Page) ! {
|
||||
|
||||
c += '\n${page_content}\n'
|
||||
|
||||
if args.path.ends_with('/') {
|
||||
if args.path.ends_with('/') || args.path.trim_space() == '' {
|
||||
// means is dir
|
||||
args.path += page_name
|
||||
}
|
||||
@@ -138,18 +128,18 @@ fn (mut mysite SiteGenerator) page_generate(args_ Page) ! {
|
||||
args.path += '.md'
|
||||
}
|
||||
|
||||
mut pagepath := '${mysite.path.path}/${args.path}'
|
||||
mut pagepath := '${generator.path.path}/${args.path}'
|
||||
mut pagefile := pathlib.get_file(path: pagepath, create: true)!
|
||||
|
||||
pagefile.write(c)!
|
||||
|
||||
mysite.client.copy_images(collection_name, page_name, pagefile.path_dir()) or {
|
||||
mysite.error("Couldn't copy image ${pagefile} for '${page_name}' in collection '${collection_name}', try to find the image and fix the path is in ${args.path}.}\nError: ${err}")!
|
||||
generator.client.copy_images(collection_name, page_name, pagefile.path_dir()) or {
|
||||
generator.error("Couldn't copy image ${pagefile} for '${page_name}' in collection '${collection_name}', try to find the image and fix the path is in ${args.path}.}\nError: ${err}")!
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut mysite SiteGenerator) section_generate(args_ Section) ! {
|
||||
fn (mut generator SiteGenerator) section_generate(args_ Section) ! {
|
||||
mut args := args_
|
||||
|
||||
mut c := '{
|
||||
@@ -160,7 +150,7 @@ fn (mut mysite SiteGenerator) section_generate(args_ Section) ! {
|
||||
}
|
||||
}'
|
||||
|
||||
mut category_path := '${mysite.path.path}/${args.path}/_category_.json'
|
||||
mut category_path := '${generator.path.path}/${args.path}/_category_.json'
|
||||
mut catfile := pathlib.get_file(path: category_path, create: true)!
|
||||
|
||||
catfile.write(c)!
|
||||
61
lib/web/docusaurus/dsite_import.v
Normal file
61
lib/web/docusaurus/dsite_import.v
Normal file
@@ -0,0 +1,61 @@
|
||||
module docusaurus
|
||||
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools.regext
|
||||
|
||||
|
||||
|
||||
pub fn (mut docsite DocSite) import() ! {
|
||||
for importparams in docsite.website.siteconfig.imports {
|
||||
|
||||
console.print_header('Importing: path:${importparams.path} or url:${importparams.url}')
|
||||
|
||||
// pub struct ImportItem {
|
||||
// name string // will normally be empty
|
||||
// url string // http git url can be to specific path
|
||||
// path string
|
||||
// dest string // location in the docs folder of the place where we will build the documentation site e.g. docusaurus
|
||||
// replace map[string]string // will replace ${NAME} in the imported content
|
||||
// visible bool = true
|
||||
// }
|
||||
|
||||
c:=config()!
|
||||
|
||||
if importparams.path == "" && importparams.url != "" {
|
||||
return error("in import for docusaurus need to specify url or path")
|
||||
}
|
||||
|
||||
// Use gittools to get path of what we want to import
|
||||
import_path := gittools.get_repo_path(
|
||||
git_pull: c.reset
|
||||
git_reset: c.reset
|
||||
git_url: importparams.url
|
||||
git_root: c.coderoot
|
||||
path: importparams.path
|
||||
)!
|
||||
|
||||
mut import_patho := pathlib.get(import_path)
|
||||
|
||||
if importparams.dest.starts_with("/") {
|
||||
return error("Import path ${importparams.dest} must be relative, will be relative in relation to the build dir.")
|
||||
}
|
||||
|
||||
import_patho.copy(dest: '${c.path_build.path}/${importparams.dest}', delete: false)!
|
||||
|
||||
// println(importparams)
|
||||
// replace: {'NAME': 'MyName', 'URGENCY': 'red'}
|
||||
mut ri := regext.regex_instructions_new()
|
||||
for key, val in importparams.replace {
|
||||
ri.add_item('\{${key}\}', val)!
|
||||
}
|
||||
ri.replace_in_dir(
|
||||
path: '${c.path_build.path}/docs/${importparams.dest}'
|
||||
extensions: [
|
||||
'md',
|
||||
]
|
||||
)!
|
||||
}
|
||||
}
|
||||
@@ -1,79 +1,63 @@
|
||||
module docusaurus
|
||||
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import freeflowuniverse.herolib.osal.core as osal
|
||||
import freeflowuniverse.herolib.installers.web.bun
|
||||
|
||||
__global (
|
||||
docusaurus_sites map[string]&DocSite
|
||||
docusaurus_factory []DocSiteFactory
|
||||
)
|
||||
|
||||
pub struct DocSiteFactory {
|
||||
pub mut:
|
||||
path_publish pathlib.Path
|
||||
path_build pathlib.Path
|
||||
}
|
||||
import freeflowuniverse.herolib.web.site
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
@[params]
|
||||
pub struct DocSiteFactoryArgs {
|
||||
pub struct AddArgs {
|
||||
pub mut:
|
||||
path_build string
|
||||
path_publish string
|
||||
install bool
|
||||
reset bool
|
||||
template_update bool
|
||||
sitename string // needs to exist in web.site module
|
||||
}
|
||||
|
||||
pub fn factory_get(args_ DocSiteFactoryArgs) !DocSiteFactory {
|
||||
mut args := args_
|
||||
if docusaurus_factory.len > 1 {
|
||||
panic('multiple docusaurus factories found, please specify which one to use')
|
||||
}
|
||||
if docusaurus_factory.len > 0 {
|
||||
return docusaurus_factory[0]
|
||||
}
|
||||
return factory_set(args)!
|
||||
}
|
||||
pub fn dsite_define(sitename string) ! {
|
||||
console.print_header('Add Docusaurus Site: ${sitename}')
|
||||
mut c := config()!
|
||||
|
||||
pub fn factory_set(args_ DocSiteFactoryArgs) !DocSiteFactory {
|
||||
mut args := args_
|
||||
if args.path_build == '' {
|
||||
args.path_build = '${os.home_dir()}/hero/var/docusaurus/build'
|
||||
}
|
||||
if args.path_publish == '' {
|
||||
args.path_publish = '${os.home_dir()}/hero/var/docusaurus/publish'
|
||||
}
|
||||
mut factory := DocSiteFactory{
|
||||
path_publish: pathlib.get_dir(path: args.path_publish, create: true)!
|
||||
path_build: pathlib.get_dir(path: args.path_build, create: true)!
|
||||
path_publish := '${c.path_publish.path}/${sitename}'
|
||||
path_build_ := '${c.path_build.path}'
|
||||
|
||||
// Get the site object after processing, this is the website which is a generic definition of a site
|
||||
mut website := site.get(name: sitename)!
|
||||
|
||||
// Create the DocSite instance
|
||||
mut dsite := &DocSite{
|
||||
name: sitename
|
||||
path_publish: pathlib.get_dir(path: "${path_build_}/build", create: true)!
|
||||
path_build: pathlib.get_dir(path: path_build_, create: true)!
|
||||
config: new_configuration(website.siteconfig)!
|
||||
website: website
|
||||
}
|
||||
|
||||
if !os.exists('${args.path_build}/node_modules') {
|
||||
args.install = true
|
||||
}
|
||||
|
||||
if args.install {
|
||||
factory.install(args.reset, args.template_update)!
|
||||
}
|
||||
|
||||
docusaurus_factory << factory
|
||||
|
||||
return factory
|
||||
docusaurus_sites[sitename] = dsite
|
||||
docusaurus_last = sitename
|
||||
}
|
||||
|
||||
pub fn dsite_get(name_ string) !&DocSite {
|
||||
name := texttools.name_fix(name_)
|
||||
mut name := texttools.name_fix(name_)
|
||||
if name=="" {
|
||||
name = docusaurus_last
|
||||
}
|
||||
return docusaurus_sites[name] or {
|
||||
return error('docusaurus site with name "${name}" does not exist')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dsite_exists(name_ string) !bool {
|
||||
name := texttools.name_fix(name_)
|
||||
d := docusaurus_sites[name] or { return false }
|
||||
mut name := texttools.name_fix(name_)
|
||||
if name=="" {
|
||||
name = docusaurus_last
|
||||
}
|
||||
_ := docusaurus_sites[name] or { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
// dsite_names returns the list of defined docusaurus site names.
|
||||
pub fn dsite_names() []string {
|
||||
mut names := []string{}
|
||||
for k, _ in docusaurus_sites {
|
||||
names << k
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
@@ -6,23 +6,23 @@ import freeflowuniverse.herolib.develop.gittools
|
||||
import freeflowuniverse.herolib.osal.core as osal
|
||||
import freeflowuniverse.herolib.installers.web.bun
|
||||
|
||||
fn (mut f DocSiteFactory) install(reset bool, template_update bool) ! {
|
||||
fn install( c DocusaurusConfig) ! {
|
||||
mut gs := gittools.new()!
|
||||
|
||||
if reset {
|
||||
osal.rm(f.path_build.path)!
|
||||
osal.dir_ensure(f.path_build.path)!
|
||||
if c.reset {
|
||||
osal.rm(c.path_build.path)!
|
||||
osal.dir_ensure(c.path_build.path)!
|
||||
}
|
||||
|
||||
template_path := gs.get_path(
|
||||
pull: template_update
|
||||
reset: reset // Changed args.delete to args.reset
|
||||
pull: c.template_update
|
||||
reset: c.reset
|
||||
url: 'https://github.com/freeflowuniverse/docusaurus_template/src/branch/main/template'
|
||||
)!
|
||||
|
||||
mut template_path0 := pathlib.get_dir(path: template_path, create: false)!
|
||||
|
||||
template_path0.copy(dest: f.path_build.path, delete: reset)! // Changed args.delete to args.reset
|
||||
template_path0.copy(dest: c.path_build.path, delete: false)! //the dir has already been deleted so no point to delete again
|
||||
|
||||
// install bun
|
||||
mut installer := bun.get()!
|
||||
@@ -30,10 +30,11 @@ fn (mut f DocSiteFactory) install(reset bool, template_update bool) ! {
|
||||
osal.exec(
|
||||
// always stay in the context of the build directory
|
||||
cmd: '
|
||||
${osal.profile_path_source_and()!}
|
||||
export PATH=${f.path_build.path}/node_modules/.bin::${os.home_dir()}/.bun/bin/:\$PATH
|
||||
cd ${f.path_build.path}
|
||||
bun install
|
||||
'
|
||||
${osal.profile_path_source_and()!}
|
||||
export PATH=${c.path_build.path}/node_modules/.bin::${os.home_dir()}/.bun/bin/:\$PATH
|
||||
cd ${c.path_build.path}
|
||||
bun install
|
||||
'
|
||||
)!
|
||||
|
||||
}
|
||||
|
||||
@@ -2,91 +2,69 @@ module docusaurus
|
||||
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.web.site
|
||||
import os
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
if !plbook.exists(filter: 'docusaurus.') {
|
||||
return
|
||||
}
|
||||
|
||||
// 1. Process generic site configuration first.
|
||||
// This populates the global `site.websites` map.
|
||||
site.play(mut plbook)!
|
||||
//there should be 1 define section
|
||||
mut action_define := plbook.ensure_once(filter: 'docusaurus.define')!
|
||||
mut param_define := action_define.params
|
||||
|
||||
// check if docusaurus.define exists, if not, we create a default factory
|
||||
mut f := DocSiteFactory{}
|
||||
if plbook.max_once(filter: 'docusaurus.define')! {
|
||||
mut a := plbook.get(filter: 'docusaurus.define') or {
|
||||
panic('docusaurus.define action not found, this should not happen.')
|
||||
}
|
||||
mut p := a.params
|
||||
f = factory_set(
|
||||
path_build: p.get_default('path_build', '')!
|
||||
path_publish: p.get_default('path_publish', '')!
|
||||
reset: p.get_default_false('reset')
|
||||
template_update: p.get_default_false('template_update')
|
||||
install: p.get_default_false('install')
|
||||
)!
|
||||
a.done = true
|
||||
} else {
|
||||
f = factory_get()!
|
||||
config_set(
|
||||
path_build: param_define.get_default('path_build', '')!
|
||||
path_publish: param_define.get_default('path_publish', '')!
|
||||
reset: param_define.get_default_false('reset')
|
||||
template_update: param_define.get_default_false('template_update')
|
||||
install: param_define.get_default_false('install')
|
||||
)!
|
||||
|
||||
site_name := param_define.get('name') or {
|
||||
return error('In docusaurus.define, param "name" is required.')
|
||||
}
|
||||
|
||||
dsite_define(site_name)!
|
||||
|
||||
// 3. Process `docusaurus.add` actions to create sites.
|
||||
for mut action in plbook.find(filter: 'docusaurus.add')! {
|
||||
mut p := action.params
|
||||
site_name := p.get('sitename') or {
|
||||
return error('In docusaurus.add, param "sitename" is required.')
|
||||
}
|
||||
|
||||
action_define.done = true
|
||||
mut dsite := dsite_get(site_name)!
|
||||
|
||||
dsite_add(
|
||||
sitename: site_name
|
||||
path: p.get_default('path', '')! // Make path optional
|
||||
git_url: p.get_default('git_url', '')! // Make git_url optional too
|
||||
git_reset: p.get_default_false('git_reset')
|
||||
git_root: p.get_default('git_root', '')! // Make git_root optional
|
||||
git_pull: p.get_default_false('git_pull')
|
||||
path_publish: p.get_default('path_publish', f.path_publish.path)!
|
||||
play: p.get_default_false('play') // Respect the play parameter from heroscript
|
||||
)!
|
||||
action.done = true
|
||||
}
|
||||
dsite.generate()!
|
||||
|
||||
mut actions_dev := plbook.find(filter: 'docusaurus.dev')!
|
||||
if actions_dev.len > 1 {
|
||||
return error('Multiple "docusaurus.dev" actions found. Only one is allowed.')
|
||||
}
|
||||
|
||||
for mut action in actions_dev {
|
||||
mut p := action.params
|
||||
site_name := p.get('site')!
|
||||
mut dsite := dsite_get(site_name)!
|
||||
dsite.dev(
|
||||
host: p.get_default('host', 'localhost')!
|
||||
port: p.get_int_default('port', 3000)!
|
||||
open: p.get_default_false('open')
|
||||
watch_changes: p.get_default_false('watch_changes')
|
||||
host: p.get_default('host', 'localhost')!
|
||||
port: p.get_int_default('port', 3000)!
|
||||
open: p.get_default_false('open')
|
||||
)!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
|
||||
mut actions_build := plbook.find(filter: 'docusaurus.build')!
|
||||
if actions_build.len > 1 {
|
||||
return error('Multiple "docusaurus.build" actions found. Only one is allowed.')
|
||||
}
|
||||
for mut action in actions_build {
|
||||
mut p := action.params
|
||||
site_name := p.get('site') or {
|
||||
// If no site specified, use the first available site
|
||||
if docusaurus_sites.len == 0 {
|
||||
return error('No docusaurus sites available to build. Use docusaurus.add to create a site first.')
|
||||
}
|
||||
// Get the first site name
|
||||
docusaurus_sites.keys()[0]
|
||||
}
|
||||
mut dsite := dsite_get(site_name)!
|
||||
dsite.build()!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
mut actions_export := plbook.find(filter: 'docusaurus.publish')!
|
||||
if actions_export.len > 1 {
|
||||
return error('Multiple "docusaurus.publish" actions found. Only one is allowed.')
|
||||
}
|
||||
for mut action in actions_export {
|
||||
dsite.build_publish()!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
plbook.ensure_processed(filter: 'docusaurus.')!
|
||||
}
|
||||
|
||||
@@ -1,96 +1,195 @@
|
||||
module docusaurus
|
||||
|
||||
import freeflowuniverse.herolib.osal.notifier
|
||||
import os
|
||||
//not longer working because is coming from doctree
|
||||
|
||||
fn watch_docs(docs_path string, path_src string, path_build string) ! {
|
||||
mut n := notifier.new('docsite_watcher') or {
|
||||
eprintln('Failed to create watcher: ${err}')
|
||||
return
|
||||
}
|
||||
// import freeflowuniverse.herolib.osal.notifier
|
||||
// import os
|
||||
|
||||
n.args['path_src'] = path_src
|
||||
n.args['path_build'] = path_build
|
||||
// fn watch_docs(docs_path string, path_src string, path_build string) ! {
|
||||
// mut n := notifier.new('docsite_watcher') or {
|
||||
// eprintln('Failed to create watcher: ${err}')
|
||||
// return
|
||||
// }
|
||||
|
||||
// Add watch with captured args
|
||||
n.add_watch(docs_path, fn (event notifier.NotifyEvent, path string, args map[string]string) {
|
||||
handle_file_change(event, path, args) or { eprintln('Error handling file change: ${err}') }
|
||||
})!
|
||||
// n.args['path_src'] = path_src
|
||||
// n.args['path_build'] = path_build
|
||||
|
||||
n.start()!
|
||||
}
|
||||
// // Add watch with captured args
|
||||
// n.add_watch(docs_path, fn (event notifier.NotifyEvent, path string, args map[string]string) {
|
||||
// handle_file_change(event, path, args) or { eprintln('Error handling file change: ${err}') }
|
||||
// })!
|
||||
|
||||
// handle_file_change processes file system events
|
||||
fn handle_file_change(event notifier.NotifyEvent, path string, args map[string]string) ! {
|
||||
file_base := os.base(path)
|
||||
is_dir := os.is_dir(path)
|
||||
// n.start()!
|
||||
// }
|
||||
|
||||
// Skip files starting with #
|
||||
if file_base.starts_with('#') {
|
||||
return
|
||||
}
|
||||
// // handle_file_change processes file system events
|
||||
// fn handle_file_change(event notifier.NotifyEvent, path string, args map[string]string) ! {
|
||||
// file_base := os.base(path)
|
||||
// is_dir := os.is_dir(path)
|
||||
|
||||
// For files (not directories), check extensions
|
||||
if !is_dir {
|
||||
ext := os.file_ext(path).to_lower()
|
||||
if ext !in ['.md', '.png', '.jpeg', '.jpg'] {
|
||||
return
|
||||
}
|
||||
}
|
||||
// // Skip files starting with #
|
||||
// if file_base.starts_with('#') {
|
||||
// return
|
||||
// }
|
||||
|
||||
// Get relative path from docs directory
|
||||
rel_path := path.replace('${args['path_src']}/docs/', '')
|
||||
dest_path := '${args['path_build']}/docs/${rel_path}'
|
||||
// // For files (not directories), check extensions
|
||||
// if !is_dir {
|
||||
// ext := os.file_ext(path).to_lower()
|
||||
// if ext !in ['.md', '.png', '.jpeg', '.jpg'] {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
match event {
|
||||
.create, .modify {
|
||||
if is_dir {
|
||||
// For directories, just ensure they exist
|
||||
os.mkdir_all(dest_path) or {
|
||||
return error('Failed to create directory ${dest_path}: ${err}')
|
||||
}
|
||||
println('Created directory: ${rel_path}')
|
||||
} else {
|
||||
// For files, ensure parent directory exists and copy
|
||||
os.mkdir_all(os.dir(dest_path)) or {
|
||||
return error('Failed to create directory ${os.dir(dest_path)}: ${err}')
|
||||
}
|
||||
os.cp(path, dest_path) or {
|
||||
return error('Failed to copy ${path} to ${dest_path}: ${err}')
|
||||
}
|
||||
println('Updated: ${rel_path}')
|
||||
}
|
||||
}
|
||||
.delete {
|
||||
if os.exists(dest_path) {
|
||||
if is_dir {
|
||||
os.rmdir_all(dest_path) or {
|
||||
return error('Failed to delete directory ${dest_path}: ${err}')
|
||||
}
|
||||
println('Deleted directory: ${rel_path}')
|
||||
} else {
|
||||
os.rm(dest_path) or { return error('Failed to delete ${dest_path}: ${err}') }
|
||||
println('Deleted: ${rel_path}')
|
||||
}
|
||||
}
|
||||
}
|
||||
.rename {
|
||||
// For rename events, fswatch provides the new path in the event
|
||||
// The old path is already removed, so we just need to handle the new path
|
||||
if is_dir {
|
||||
os.mkdir_all(dest_path) or {
|
||||
return error('Failed to create directory ${dest_path}: ${err}')
|
||||
}
|
||||
println('Renamed directory to: ${rel_path}')
|
||||
} else {
|
||||
os.mkdir_all(os.dir(dest_path)) or {
|
||||
return error('Failed to create directory ${os.dir(dest_path)}: ${err}')
|
||||
}
|
||||
os.cp(path, dest_path) or {
|
||||
return error('Failed to copy ${path} to ${dest_path}: ${err}')
|
||||
}
|
||||
println('Renamed to: ${rel_path}')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// // Get relative path from docs directory
|
||||
// rel_path := path.replace('${args['path_src']}/docs/', '')
|
||||
// dest_path := '${args['path_build']}/docs/${rel_path}'
|
||||
|
||||
// match event {
|
||||
// .create, .modify {
|
||||
// if is_dir {
|
||||
// // For directories, just ensure they exist
|
||||
// os.mkdir_all(dest_path) or {
|
||||
// return error('Failed to create directory ${dest_path}: ${err}')
|
||||
// }
|
||||
// println('Created directory: ${rel_path}')
|
||||
// } else {
|
||||
// // For files, ensure parent directory exists and copy
|
||||
// os.mkdir_all(os.dir(dest_path)) or {
|
||||
// return error('Failed to create directory ${os.dir(dest_path)}: ${err}')
|
||||
// }
|
||||
// os.cp(path, dest_path) or {
|
||||
// return error('Failed to copy ${path} to ${dest_path}: ${err}')
|
||||
// }
|
||||
// println('Updated: ${rel_path}')
|
||||
// }
|
||||
// }
|
||||
// .delete {
|
||||
// if os.exists(dest_path) {
|
||||
// if is_dir {
|
||||
// os.rmdir_all(dest_path) or {
|
||||
// return error('Failed to delete directory ${dest_path}: ${err}')
|
||||
// }
|
||||
// println('Deleted directory: ${rel_path}')
|
||||
// } else {
|
||||
// os.rm(dest_path) or { return error('Failed to delete ${dest_path}: ${err}') }
|
||||
// println('Deleted: ${rel_path}')
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// .rename {
|
||||
// // For rename events, fswatch provides the new path in the event
|
||||
// // The old path is already removed, so we just need to handle the new path
|
||||
// if is_dir {
|
||||
// os.mkdir_all(dest_path) or {
|
||||
// return error('Failed to create directory ${dest_path}: ${err}')
|
||||
// }
|
||||
// println('Renamed directory to: ${rel_path}')
|
||||
// } else {
|
||||
// os.mkdir_all(os.dir(dest_path)) or {
|
||||
// return error('Failed to create directory ${os.dir(dest_path)}: ${err}')
|
||||
// }
|
||||
// os.cp(path, dest_path) or {
|
||||
// return error('Failed to copy ${path} to ${dest_path}: ${err}')
|
||||
// }
|
||||
// println('Renamed to: ${rel_path}')
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
// pub fn (mut s DocSite) dev_watch(args DevArgs) ! {
|
||||
// s.generate()!
|
||||
|
||||
// // Create screen session for docusaurus development server
|
||||
// mut screen_name := 'docusaurus'
|
||||
// mut sf := screen.new()!
|
||||
|
||||
// // Add and start a new screen session
|
||||
// mut scr := sf.add(
|
||||
// name: screen_name
|
||||
// cmd: '/bin/bash'
|
||||
// start: true
|
||||
// attach: false
|
||||
// reset: true
|
||||
// )!
|
||||
|
||||
// // Send commands to the screen session
|
||||
// console.print_item('To view the server output:: cd ${s.path_build.path}')
|
||||
// scr.cmd_send('cd ${s.path_build.path}')!
|
||||
|
||||
// // Start script recording in the screen session for log streaming
|
||||
// log_file := '/tmp/docusaurus_${screen_name}.log'
|
||||
// script_cmd := 'script -f ${log_file}'
|
||||
// scr.cmd_send(script_cmd)!
|
||||
|
||||
// // Small delay to ensure script is ready
|
||||
// time.sleep(500 * time.millisecond)
|
||||
|
||||
// // Start bun in the scripted session
|
||||
// bun_cmd := 'bun start -p ${args.port} -h ${args.host}'
|
||||
// scr.cmd_send(bun_cmd)!
|
||||
|
||||
// // Stream the log output to current terminal
|
||||
// console.print_header(' Docusaurus Development Server')
|
||||
// console.print_item('Streaming server output... Press Ctrl+C to detach and leave server running')
|
||||
// console.print_item('Server will be available at: http://${args.host}:${args.port}')
|
||||
// console.print_item('To reattach later: screen -r ${screen_name}')
|
||||
// println('')
|
||||
|
||||
// // Stream logs until user interrupts
|
||||
// s.stream_logs(log_file, screen_name)!
|
||||
|
||||
// // After user interrupts, show final instructions
|
||||
// console.print_header(' Server Running in Background')
|
||||
// console.print_item('✓ Development server is running in background')
|
||||
// console.print_item('Server URL: http://${args.host}:${args.port}')
|
||||
// console.print_item('To reattach: screen -r ${screen_name}')
|
||||
// console.print_item('To stop server: screen -S ${screen_name} -X kill')
|
||||
// // console.print_item('The site content is on: ${s.path_src.path}/docs')
|
||||
|
||||
// // Start the watcher in a separate thread
|
||||
// // mut tf:=spawn watch_docs(docs_path, s.path_src.path, s.path_build.path)
|
||||
// // tf.wait()!
|
||||
// println('\n')
|
||||
|
||||
// if args.open {
|
||||
// s.open()!
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// // Stream logs from script file to current terminal until user interrupts
|
||||
// fn (mut s DocSite) stream_logs(log_file string, screen_name string) ! {
|
||||
// // Wait a moment for the log file to be created
|
||||
// mut attempts := 0
|
||||
// for !os.exists(log_file) && attempts < 10 {
|
||||
// time.sleep(200 * time.millisecond)
|
||||
// attempts++
|
||||
// }
|
||||
|
||||
// if !os.exists(log_file) {
|
||||
// console.print_stderr('Warning: Log file not created, falling back to screen attach')
|
||||
// console.print_item('Attaching to screen session... Press Ctrl+A then D to detach')
|
||||
// // Fallback to direct screen attach
|
||||
// osal.execute_interactive('screen -r ${screen_name}')!
|
||||
// return
|
||||
// }
|
||||
|
||||
// // Use tail -f to stream the log file
|
||||
// // The -f flag follows the file as it grows
|
||||
// tail_cmd := 'tail -f ${log_file}'
|
||||
|
||||
// // Execute tail in interactive mode - this will stream until Ctrl+C
|
||||
// osal.execute_interactive(tail_cmd) or {
|
||||
// // If tail fails, try alternative approach
|
||||
// console.print_stderr('Log streaming failed, attaching to screen session...')
|
||||
// osal.execute_interactive('screen -r ${screen_name}')!
|
||||
// return
|
||||
// }
|
||||
|
||||
// // Clean up the log file after streaming
|
||||
// os.rm(log_file) or {}
|
||||
// }
|
||||
|
||||
@@ -1,77 +1,20 @@
|
||||
module site
|
||||
|
||||
import freeflowuniverse.herolib.core.playbook { Action, PlayBook }
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import time
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
if !plbook.exists(filter: 'site.') && !plbook.exists(filter: 'docusaurus.config') {
|
||||
if !plbook.exists(filter: 'site.') {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle multiple site configurations - look for both site.config and docusaurus.config
|
||||
mut config_actions := plbook.find(filter: 'site.config')!
|
||||
if config_actions.len == 0 {
|
||||
// Fallback to docusaurus.config for backward compatibility
|
||||
config_actions = plbook.find(filter: 'docusaurus.config')!
|
||||
}
|
||||
mut config_action := plbook.ensure_once(filter: 'site.config')!
|
||||
|
||||
if config_actions.len == 0 {
|
||||
return error('No site.config or docusaurus.config actions found')
|
||||
}
|
||||
|
||||
// For now, just process the first site configuration to avoid memory issues
|
||||
// TODO: Fix the underlying memory corruption issue with multiple site configs
|
||||
if config_actions.len > 0 {
|
||||
mut config_action := config_actions[0]
|
||||
// Work around memory corruption by accessing params directly here
|
||||
mut p := config_action.params
|
||||
name := p.get_default('name', 'default')! // Use 'default' as fallback name
|
||||
|
||||
mut website := play_config_single_safe(name, mut config_action)!
|
||||
|
||||
mut config := &website.siteconfig
|
||||
|
||||
play_import(mut plbook, mut config)!
|
||||
play_menu(mut plbook, mut config)!
|
||||
play_footer(mut plbook, mut config)!
|
||||
play_build_dest(mut plbook, mut config)!
|
||||
play_build_dest_dev(mut plbook, mut config)!
|
||||
|
||||
play_pages(mut plbook, mut website)!
|
||||
|
||||
// Mark all other config actions as done to avoid processing them
|
||||
for i in 1 .. config_actions.len {
|
||||
config_actions[i].done = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn play_config_single_safe(name string, mut action Action) !&Site {
|
||||
mut p := action.params
|
||||
|
||||
mut website := new(name: name)!
|
||||
mut config := &website.siteconfig
|
||||
|
||||
config.title = p.get_default('title', config.title)!
|
||||
config.description = p.get_default('description', config.description)!
|
||||
config.tagline = p.get_default('tagline', config.tagline)!
|
||||
config.favicon = p.get_default('favicon', config.favicon)!
|
||||
config.image = p.get_default('image', config.image)!
|
||||
config.copyright = p.get_default('copyright', config.copyright)!
|
||||
config.url = p.get_default('url', config.url)!
|
||||
config.base_url = p.get_default('base_url', config.base_url)!
|
||||
config.url_home = p.get_default('url_home', config.url_home)!
|
||||
config.name = name
|
||||
return website
|
||||
}
|
||||
|
||||
fn play_config(mut plbook PlayBook) !&Site {
|
||||
mut action := plbook.get(filter: 'site.config')!
|
||||
|
||||
mut p := action.params
|
||||
name := p.get('name') or { return error('need to specify name in site.config') }
|
||||
mut p := config_action.params
|
||||
name := p.get_default('name', 'default')! // Use 'default' as fallback name
|
||||
|
||||
// configure the website
|
||||
mut website := new(name: name)!
|
||||
mut config := &website.siteconfig
|
||||
|
||||
@@ -88,9 +31,9 @@ fn play_config(mut plbook PlayBook) !&Site {
|
||||
config.url_home = p.get_default('url_home', '')!
|
||||
|
||||
// Process !!site.config_meta for specific metadata overrides
|
||||
mut meta_action := plbook.get(filter: 'site.config_meta')!
|
||||
|
||||
mut meta_action := plbook.ensure_once(filter: 'site.config_meta')!
|
||||
mut p_meta := meta_action.params
|
||||
|
||||
// If 'title' is present in site.config_meta, it overrides. Otherwise, meta_title remains empty or uses site.config.title logic in docusaurus model.
|
||||
config.meta_title = p_meta.get_default('title', config.title)!
|
||||
// If 'image' is present in site.config_meta, it overrides. Otherwise, meta_image remains empty or uses site.config.image logic.
|
||||
@@ -100,8 +43,15 @@ fn play_config(mut plbook PlayBook) !&Site {
|
||||
config.description = p_meta.get('description')!
|
||||
}
|
||||
|
||||
action.done = true // Mark the action as done
|
||||
return website
|
||||
config_action.done = true // Mark the action as done
|
||||
meta_action.done = true
|
||||
|
||||
play_import(mut plbook, mut config)!
|
||||
play_menu(mut plbook, mut config)!
|
||||
play_footer(mut plbook, mut config)!
|
||||
play_publish(mut plbook, mut config)!
|
||||
play_publish_dev(mut plbook, mut config)!
|
||||
play_pages(mut plbook, mut website)!
|
||||
}
|
||||
|
||||
fn play_import(mut plbook PlayBook, mut config SiteConfig) ! {
|
||||
@@ -120,10 +70,18 @@ fn play_import(mut plbook PlayBook, mut config SiteConfig) ! {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mut importpath := p.get_default('path', '')!
|
||||
if importpath != '' {
|
||||
if ! importpath.starts_with('/') {
|
||||
importpath = os.abs_path('${plbook.path}/${importpath}')
|
||||
}
|
||||
}
|
||||
|
||||
mut import_ := ImportItem{
|
||||
name: p.get_default('name', '')!
|
||||
url: p.get('url')!
|
||||
path: p.get_default('path', '')!
|
||||
url: p.get_default('url', '')!
|
||||
path: importpath
|
||||
dest: p.get_default('dest', '')!
|
||||
replace: replace_map
|
||||
visible: p.get_default_false('visible')
|
||||
@@ -219,12 +177,12 @@ fn play_footer(mut plbook PlayBook, mut config SiteConfig) ! {
|
||||
}
|
||||
}
|
||||
|
||||
fn play_build_dest(mut plbook PlayBook, mut config SiteConfig) ! {
|
||||
mut build_dest_actions := plbook.find(filter: 'site.build_dest')!
|
||||
fn play_publish(mut plbook PlayBook, mut config SiteConfig) ! {
|
||||
mut build_dest_actions := plbook.find(filter: 'site.publish')!
|
||||
for mut action in build_dest_actions {
|
||||
mut p := action.params
|
||||
mut dest := BuildDest{
|
||||
path: p.get('path')!
|
||||
path: p.get_default('path', '')! //can be url
|
||||
ssh_name: p.get_default('ssh_name', '')!
|
||||
}
|
||||
config.build_dest << dest
|
||||
@@ -232,15 +190,16 @@ fn play_build_dest(mut plbook PlayBook, mut config SiteConfig) ! {
|
||||
}
|
||||
}
|
||||
|
||||
fn play_build_dest_dev(mut plbook PlayBook, mut config SiteConfig) ! {
|
||||
mut build_dest_dev_actions := plbook.find(filter: 'site.build_dest_dev')!
|
||||
for mut action in build_dest_dev_actions {
|
||||
|
||||
fn play_publish_dev(mut plbook PlayBook, mut config SiteConfig) ! {
|
||||
mut build_dest_actions := plbook.find(filter: 'site.publish_dev')!
|
||||
for mut action in build_dest_actions {
|
||||
mut p := action.params
|
||||
mut dest_dev := BuildDest{
|
||||
path: p.get('path')!
|
||||
mut dest := BuildDest{
|
||||
path: p.get_default('path', '')! //can be url
|
||||
ssh_name: p.get_default('ssh_name', '')!
|
||||
}
|
||||
config.build_dest_dev << dest_dev
|
||||
config.build_dest_dev << dest
|
||||
action.done = true // Mark the action as done
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ fn play_pages(mut plbook PlayBook, mut site Site) ! {
|
||||
}
|
||||
|
||||
// LETS FIRST DO THE CATEGORIES
|
||||
category_actions := plbook.find(filter: 'site.page_category')!
|
||||
mut category_actions := plbook.find(filter: 'site.page_category')!
|
||||
mut section := Section{}
|
||||
for action in category_actions {
|
||||
for mut action in category_actions {
|
||||
// println(action)
|
||||
mut p := action.params
|
||||
section.position = p.get_int_default('position', 20)!
|
||||
@@ -32,9 +32,10 @@ fn play_pages(mut plbook PlayBook, mut site Site) ! {
|
||||
return error('need to specify path in site.page_category')
|
||||
}
|
||||
site.sections << section
|
||||
action.done = true // Mark the action as done
|
||||
}
|
||||
|
||||
page_actions := plbook.find(filter: 'site.page')!
|
||||
mut page_actions := plbook.find(filter: 'site.page')!
|
||||
mut mypage := Page{
|
||||
src: ''
|
||||
path: ''
|
||||
@@ -42,7 +43,7 @@ fn play_pages(mut plbook PlayBook, mut site Site) ! {
|
||||
mut position_next := 1
|
||||
mut position := 0
|
||||
mut path := ''
|
||||
for action in page_actions {
|
||||
for mut action in page_actions {
|
||||
// println(action)
|
||||
mut p := action.params
|
||||
pathnew := p.get_default('path', '')!
|
||||
@@ -82,5 +83,7 @@ fn play_pages(mut plbook PlayBook, mut site Site) ! {
|
||||
mypage.title_nr = p.get_int_default('title_nr', 0)!
|
||||
|
||||
site.pages << mypage
|
||||
|
||||
action.done = true // Mark the action as done
|
||||
}
|
||||
}
|
||||
|
||||
502
lib/web/ui/factory.v
Normal file
502
lib/web/ui/factory.v
Normal file
@@ -0,0 +1,502 @@
|
||||
module ui
|
||||
|
||||
import veb
|
||||
import os
|
||||
import net.http
|
||||
|
||||
// Public Context type for veb
|
||||
pub struct Context {
|
||||
veb.Context
|
||||
}
|
||||
|
||||
// Simple tree menu structure
|
||||
pub struct MenuItem {
|
||||
pub:
|
||||
title string
|
||||
href string
|
||||
children []MenuItem
|
||||
}
|
||||
|
||||
// Factory args
|
||||
@[params]
|
||||
pub struct FactoryArgs {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
port int = 8080
|
||||
title string = 'Admin'
|
||||
menu []MenuItem
|
||||
}
|
||||
|
||||
// The App holds server state and config
|
||||
pub struct App {
|
||||
veb.StaticHandler
|
||||
pub mut:
|
||||
title string
|
||||
menu []MenuItem
|
||||
port int
|
||||
}
|
||||
|
||||
// Global registry (multi-instance support by name)
|
||||
__global (
|
||||
uireg map[string]&App
|
||||
)
|
||||
|
||||
// Create a new app (does not start the server)
|
||||
pub fn new(args FactoryArgs) !&App {
|
||||
name := if args.name.len == 0 { 'default' } else { args.name }
|
||||
if app := uireg[name] {
|
||||
return app
|
||||
}
|
||||
mut app := &App{
|
||||
title: args.title
|
||||
menu: if args.menu.len > 0 { args.menu } else { default_menu() }
|
||||
port: args.port
|
||||
}
|
||||
uireg[name] = app
|
||||
return app
|
||||
}
|
||||
|
||||
// Get a named app
|
||||
pub fn get(name string) !&App {
|
||||
mut app := uireg[name] or {
|
||||
return error('ui: app "${name}" not found, call ui.new(...) first')
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
// Get default app (creates if not existing)
|
||||
pub fn default() !&App {
|
||||
if uireg.len == 0 {
|
||||
return new(port: 8080)!
|
||||
}
|
||||
return get('default')!
|
||||
}
|
||||
|
||||
// Start the webserver (blocking)
|
||||
pub fn start(args FactoryArgs) ! {
|
||||
mut app := new(args)!
|
||||
veb.run[App, Context](mut app, app.port)
|
||||
}
|
||||
|
||||
// Routes
|
||||
|
||||
// Redirect root to /admin
|
||||
@['/'; get]
|
||||
pub fn (app &App) root(mut ctx Context) veb.Result {
|
||||
return ctx.redirect('/admin')
|
||||
}
|
||||
|
||||
// Admin home page
|
||||
@['/admin'; get]
|
||||
pub fn (app &App) admin_index(mut ctx Context) veb.Result {
|
||||
return ctx.html(app.render_admin('/', 'Welcome'))
|
||||
}
|
||||
|
||||
// HeroScript editor page
|
||||
@['/admin/heroscript'; get]
|
||||
pub fn (app &App) admin_heroscript(mut ctx Context) veb.Result {
|
||||
return ctx.html(app.render_heroscript())
|
||||
}
|
||||
|
||||
// Chat page
|
||||
@['/admin/chat'; get]
|
||||
pub fn (app &App) admin_chat(mut ctx Context) veb.Result {
|
||||
return ctx.html(app.render_chat())
|
||||
}
|
||||
|
||||
// Static CSS files
|
||||
@['/static/css/colors.css'; get]
|
||||
pub fn (app &App) serve_colors_css(mut ctx Context) veb.Result {
|
||||
css_path := os.join_path(os.dir(@FILE), 'templates', 'css', 'colors.css')
|
||||
css_content := os.read_file(css_path) or { return ctx.text('/* CSS file not found */') }
|
||||
ctx.set_content_type('text/css')
|
||||
return ctx.text(css_content)
|
||||
}
|
||||
|
||||
@['/static/css/main.css'; get]
|
||||
pub fn (app &App) serve_main_css(mut ctx Context) veb.Result {
|
||||
css_path := os.join_path(os.dir(@FILE), 'templates', 'css', 'main.css')
|
||||
css_content := os.read_file(css_path) or { return ctx.text('/* CSS file not found */') }
|
||||
ctx.set_content_type('text/css')
|
||||
return ctx.text(css_content)
|
||||
}
|
||||
|
||||
// Static JS files
|
||||
@['/static/js/theme.js'; get]
|
||||
pub fn (app &App) serve_theme_js(mut ctx Context) veb.Result {
|
||||
js_path := os.join_path(os.dir(@FILE), 'templates', 'js', 'theme.js')
|
||||
js_content := os.read_file(js_path) or { return ctx.text('/* JS file not found */') }
|
||||
ctx.set_content_type('application/javascript')
|
||||
return ctx.text(js_content)
|
||||
}
|
||||
|
||||
@['/static/js/heroscript.js'; get]
|
||||
pub fn (app &App) serve_heroscript_js(mut ctx Context) veb.Result {
|
||||
js_path := os.join_path(os.dir(@FILE), 'templates', 'js', 'heroscript.js')
|
||||
js_content := os.read_file(js_path) or { return ctx.text('/* JS file not found */') }
|
||||
ctx.set_content_type('application/javascript')
|
||||
return ctx.text(js_content)
|
||||
}
|
||||
|
||||
@['/static/js/chat.js'; get]
|
||||
pub fn (app &App) serve_chat_js(mut ctx Context) veb.Result {
|
||||
js_path := os.join_path(os.dir(@FILE), 'templates', 'js', 'chat.js')
|
||||
js_content := os.read_file(js_path) or { return ctx.text('/* JS file not found */') }
|
||||
ctx.set_content_type('application/javascript')
|
||||
return ctx.text(js_content)
|
||||
}
|
||||
|
||||
@['/static/css/heroscript.css'; get]
|
||||
pub fn (app &App) serve_heroscript_css(mut ctx Context) veb.Result {
|
||||
css_path := os.join_path(os.dir(@FILE), 'templates', 'css', 'heroscript.css')
|
||||
css_content := os.read_file(css_path) or { return ctx.text('/* CSS file not found */') }
|
||||
ctx.set_content_type('text/css')
|
||||
return ctx.text(css_content)
|
||||
}
|
||||
|
||||
@['/static/css/chat.css'; get]
|
||||
pub fn (app &App) serve_chat_css(mut ctx Context) veb.Result {
|
||||
css_path := os.join_path(os.dir(@FILE), 'templates', 'css', 'chat.css')
|
||||
css_content := os.read_file(css_path) or { return ctx.text('/* CSS file not found */') }
|
||||
ctx.set_content_type('text/css')
|
||||
return ctx.text(css_content)
|
||||
}
|
||||
|
||||
// Catch-all content under /admin/*
|
||||
@['/admin/:path...'; get]
|
||||
pub fn (app &App) admin_section(mut ctx Context, path string) veb.Result {
|
||||
// Render current path in the main content
|
||||
return ctx.html(app.render_admin(path, 'Content'))
|
||||
}
|
||||
|
||||
// View rendering using external template
|
||||
|
||||
fn (app &App) render_admin(path string, heading string) string {
|
||||
// Get the template file path relative to the module
|
||||
template_path := os.join_path(os.dir(@FILE), 'templates', 'admin_layout.html')
|
||||
|
||||
// Read the template file
|
||||
template_content := os.read_file(template_path) or {
|
||||
// Fallback to inline template if file not found
|
||||
return app.render_admin_fallback(path, heading)
|
||||
}
|
||||
|
||||
// Generate menu HTML
|
||||
menu_content := menu_html(app.menu, 0, 'm')
|
||||
|
||||
// Simple template variable replacement
|
||||
mut result := template_content
|
||||
result = result.replace('{{.title}}', app.title)
|
||||
result = result.replace('{{.heading}}', heading)
|
||||
result = result.replace('{{.path}}', path)
|
||||
result = result.replace('{{.menu_html}}', menu_content)
|
||||
result = result.replace('{{.css_colors_url}}', '/static/css/colors.css')
|
||||
result = result.replace('{{.css_main_url}}', '/static/css/main.css')
|
||||
result = result.replace('{{.js_theme_url}}', '/static/js/theme.js')
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// HeroScript editor rendering using external template
|
||||
fn (app &App) render_heroscript() string {
|
||||
// Get the template file path relative to the module
|
||||
template_path := os.join_path(os.dir(@FILE), 'templates', 'heroscript_editor.html')
|
||||
|
||||
// Read the template file
|
||||
template_content := os.read_file(template_path) or {
|
||||
// Fallback to basic template if file not found
|
||||
return app.render_heroscript_fallback()
|
||||
}
|
||||
|
||||
// Generate menu HTML
|
||||
menu_content := menu_html(app.menu, 0, 'm')
|
||||
|
||||
// Simple template variable replacement
|
||||
mut result := template_content
|
||||
result = result.replace('{{.title}}', app.title)
|
||||
result = result.replace('{{.menu_html}}', menu_content)
|
||||
result = result.replace('{{.css_colors_url}}', '/static/css/colors.css')
|
||||
result = result.replace('{{.css_main_url}}', '/static/css/main.css')
|
||||
result = result.replace('{{.css_heroscript_url}}', '/static/css/heroscript.css')
|
||||
result = result.replace('{{.js_theme_url}}', '/static/js/theme.js')
|
||||
result = result.replace('{{.js_heroscript_url}}', '/static/js/heroscript.js')
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Chat rendering using external template
|
||||
fn (app &App) render_chat() string {
|
||||
// Get the template file path relative to the module
|
||||
template_path := os.join_path(os.dir(@FILE), 'templates', 'chat.html')
|
||||
|
||||
// Read the template file
|
||||
template_content := os.read_file(template_path) or {
|
||||
// Fallback to basic template if file not found
|
||||
return app.render_chat_fallback()
|
||||
}
|
||||
|
||||
// Generate menu HTML
|
||||
menu_content := menu_html(app.menu, 0, 'm')
|
||||
|
||||
// Simple template variable replacement
|
||||
mut result := template_content
|
||||
result = result.replace('{{.title}}', app.title)
|
||||
result = result.replace('{{.menu_html}}', menu_content)
|
||||
result = result.replace('{{.css_colors_url}}', '/static/css/colors.css')
|
||||
result = result.replace('{{.css_main_url}}', '/static/css/main.css')
|
||||
result = result.replace('{{.css_chat_url}}', '/static/css/chat.css')
|
||||
result = result.replace('{{.js_theme_url}}', '/static/js/theme.js')
|
||||
result = result.replace('{{.js_chat_url}}', '/static/js/chat.js')
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Fallback HeroScript rendering method
|
||||
fn (app &App) render_heroscript_fallback() string {
|
||||
return '
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>${app.title} - HeroScript Editor</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h1>HeroScript Editor</h1>
|
||||
<p>HeroScript editor template not found. Please check the template files.</p>
|
||||
<a href="/admin" class="btn btn-primary">Back to Admin</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
'
|
||||
}
|
||||
|
||||
// Fallback Chat rendering method
|
||||
fn (app &App) render_chat_fallback() string {
|
||||
return '
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>${app.title} - Chat</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h1>Chat Assistant</h1>
|
||||
<p>Chat template not found. Please check the template files.</p>
|
||||
<a href="/admin" class="btn btn-primary">Back to Admin</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
'
|
||||
}
|
||||
|
||||
// Fallback rendering method (inline template)
|
||||
fn (app &App) render_admin_fallback(path string, heading string) string {
|
||||
return '
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>${app.title}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<style>
|
||||
body { padding-top: 44px; }
|
||||
.header {
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 44px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 260px;
|
||||
overflow-y: auto;
|
||||
background: #f8f9fa;
|
||||
border-right: 1px solid #e0e0e0;
|
||||
}
|
||||
.main {
|
||||
margin-left: 260px;
|
||||
padding: 16px;
|
||||
}
|
||||
.list-group-item {
|
||||
border: 0;
|
||||
padding: .35rem .75rem;
|
||||
background: transparent;
|
||||
}
|
||||
.menu-leaf a {
|
||||
color: #212529;
|
||||
text-decoration: none;
|
||||
}
|
||||
.menu-toggle {
|
||||
text-decoration: none;
|
||||
color: #212529;
|
||||
}
|
||||
.menu-toggle .chev {
|
||||
font-size: 10px;
|
||||
opacity: .6;
|
||||
}
|
||||
.menu-section {
|
||||
font-weight: 600;
|
||||
color: #6c757d;
|
||||
padding: .5rem .75rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-dark bg-dark fixed-top header px-2">
|
||||
<div class="d-flex w-100 align-items-center justify-content-between">
|
||||
<div class="text-white fw-bold">${app.title}</div>
|
||||
<div class="text-white-50">Admin</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="p-2">
|
||||
<div class="menu-section">Navigation</div>
|
||||
<div class="list-group list-group-flush">
|
||||
${menu_html(app.menu,
|
||||
0, 'm')}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<div class="container-fluid">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<h5 class="mb-0">${heading}</h5>
|
||||
<span class="ms-2 text-muted small">/admin/${path}</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="text-muted">This is a placeholder admin content area for: <code>/admin/${path}</code>.</p>
|
||||
<p class="mb-0">Use the treeview on the left to navigate.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
'
|
||||
}
|
||||
|
||||
// Recursive menu renderer
|
||||
|
||||
fn menu_html(items []MenuItem, depth int, prefix string) string {
|
||||
mut out := []string{}
|
||||
for i, it in items {
|
||||
id := '${prefix}_${depth}_${i}'
|
||||
if it.children.len > 0 {
|
||||
// expandable group
|
||||
out << '<div class="list-group-item">'
|
||||
out << '<a class="menu-toggle d-flex align-items-center justify-content-between" data-bs-toggle="collapse" href="#${id}" role="button" aria-expanded="${if depth == 0 {
|
||||
'true'
|
||||
} else {
|
||||
'false'
|
||||
}}" aria-controls="${id}">'
|
||||
out << '<span>${it.title}</span><span class="chev">›</span>'
|
||||
out << '</a>'
|
||||
out << '<div class="collapse ${if depth == 0 { 'show' } else { '' }}" id="${id}">'
|
||||
out << '<div class="ms-2 mt-1">'
|
||||
out << menu_html(it.children, depth + 1, id)
|
||||
out << '</div>'
|
||||
out << '</div>'
|
||||
out << '</div>'
|
||||
} else {
|
||||
// leaf
|
||||
out << '<div class="list-group-item menu-leaf"><a href="${if it.href.len > 0 {
|
||||
it.href
|
||||
} else {
|
||||
'/admin'
|
||||
}}">${it.title}</a></div>'
|
||||
}
|
||||
}
|
||||
return out.join('\n')
|
||||
}
|
||||
|
||||
// Default sample menu
|
||||
fn default_menu() []MenuItem {
|
||||
return [
|
||||
MenuItem{
|
||||
title: 'Dashboard'
|
||||
href: '/admin'
|
||||
},
|
||||
MenuItem{
|
||||
title: 'HeroScript'
|
||||
href: '/admin/heroscript'
|
||||
},
|
||||
MenuItem{
|
||||
title: 'Chat'
|
||||
href: '/admin/chat'
|
||||
},
|
||||
MenuItem{
|
||||
title: 'Users'
|
||||
children: [
|
||||
MenuItem{
|
||||
title: 'Overview'
|
||||
href: '/admin/users/overview'
|
||||
},
|
||||
MenuItem{
|
||||
title: 'Create'
|
||||
href: '/admin/users/create'
|
||||
},
|
||||
MenuItem{
|
||||
title: 'Roles'
|
||||
href: '/admin/users/roles'
|
||||
},
|
||||
]
|
||||
},
|
||||
MenuItem{
|
||||
title: 'Content'
|
||||
children: [
|
||||
MenuItem{
|
||||
title: 'Pages'
|
||||
href: '/admin/content/pages'
|
||||
},
|
||||
MenuItem{
|
||||
title: 'Media'
|
||||
href: '/admin/content/media'
|
||||
},
|
||||
MenuItem{
|
||||
title: 'Settings'
|
||||
children: [
|
||||
MenuItem{
|
||||
title: 'SEO'
|
||||
href: '/admin/content/settings/seo'
|
||||
},
|
||||
MenuItem{
|
||||
title: 'Themes'
|
||||
href: '/admin/content/settings/themes'
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
MenuItem{
|
||||
title: 'System'
|
||||
children: [
|
||||
MenuItem{
|
||||
title: 'Status'
|
||||
href: '/admin/system/status'
|
||||
},
|
||||
MenuItem{
|
||||
title: 'Logs'
|
||||
href: '/admin/system/logs'
|
||||
},
|
||||
MenuItem{
|
||||
title: 'Backups'
|
||||
href: '/admin/system/backups'
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
47
lib/web/ui/templates/admin_layout.html
Normal file
47
lib/web/ui/templates/admin_layout.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{.title}}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{{.css_colors_url}}">
|
||||
<link rel="stylesheet" href="{{.css_main_url}}">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-dark bg-dark fixed-top header px-2">
|
||||
<div class="d-flex w-100 align-items-center justify-content-between">
|
||||
<div class="text-white fw-bold">{{.title}}</div>
|
||||
<div class="text-white-50">Admin</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="p-2">
|
||||
<div class="menu-section">Navigation</div>
|
||||
<div class="list-group list-group-flush">
|
||||
{{.menu_html}}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<div class="container-fluid">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<h5 class="mb-0">{{.heading}}</h5>
|
||||
<span class="ms-2 text-muted small">/admin/{{.path}}</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="text-muted">This is a placeholder admin content area for: <code>/admin/{{.path}}</code>.</p>
|
||||
<p class="mb-0">Use the treeview on the left to navigate.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
<script src="{{.js_theme_url}}"></script>
|
||||
</body>
|
||||
</html>
|
||||
200
lib/web/ui/templates/chat.html
Normal file
200
lib/web/ui/templates/chat.html
Normal file
@@ -0,0 +1,200 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{.title}} - Chat</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{{.css_colors_url}}">
|
||||
<link rel="stylesheet" href="{{.css_main_url}}">
|
||||
<link rel="stylesheet" href="{{.css_chat_url}}">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-dark bg-dark fixed-top header px-2">
|
||||
<div class="d-flex w-100 align-items-center justify-content-between">
|
||||
<div class="text-white fw-bold">{{.title}}</div>
|
||||
<div class="text-white-50">Chat</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="p-2">
|
||||
<div class="menu-section">Navigation</div>
|
||||
<div class="list-group list-group-flush">
|
||||
{{.menu_html}}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<div class="container-fluid h-100">
|
||||
<!-- Chat Section -->
|
||||
<div class="chat-container">
|
||||
<div class="chat-header">
|
||||
<h5 class="mb-0">AI Chat Assistant</h5>
|
||||
<div class="chat-controls">
|
||||
<button class="btn btn-sm btn-outline-secondary" id="clearChat">
|
||||
<i class="bi bi-trash"></i> Clear
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-secondary" id="voiceToggle">
|
||||
<i class="bi bi-mic"></i> Voice
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-messages" id="chatMessages">
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar">
|
||||
<i class="bi bi-robot"></i>
|
||||
</div>
|
||||
<div class="message-content">
|
||||
<div class="message-text">Hello! I'm your AI assistant. How can I help you today?</div>
|
||||
<div class="message-time">Just now</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-input-container">
|
||||
<div class="chat-input-wrapper">
|
||||
<textarea class="form-control chat-input" id="chatInput" placeholder="Type your message here..." rows="1"></textarea>
|
||||
<div class="chat-input-actions">
|
||||
<button class="btn btn-outline-secondary btn-sm" id="attachFile" title="Attach file">
|
||||
<i class="bi bi-paperclip"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" id="voiceInput" title="Voice input">
|
||||
<i class="bi bi-mic"></i>
|
||||
</button>
|
||||
<button class="btn btn-primary btn-sm" id="sendMessage" title="Send message">
|
||||
<i class="bi bi-send"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-status" id="chatStatus"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recorder Section -->
|
||||
<div class="recorder-container">
|
||||
<div class="recorder-header">
|
||||
<h6 class="mb-0">Voice Recorder</h6>
|
||||
<div class="recorder-controls">
|
||||
<button class="btn btn-sm btn-danger" id="recordBtn">
|
||||
<i class="bi bi-record-circle"></i> Record
|
||||
</button>
|
||||
<button class="btn btn-sm btn-secondary" id="stopBtn" disabled>
|
||||
<i class="bi bi-stop-circle"></i> Stop
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-secondary" id="playBtn" disabled>
|
||||
<i class="bi bi-play-circle"></i> Play
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="recording-status" id="recordingStatus">
|
||||
<div class="recording-indicator">
|
||||
<span class="recording-dot"></span>
|
||||
<span class="recording-time">00:00</span>
|
||||
</div>
|
||||
<div class="recording-level">
|
||||
<div class="level-bar" id="levelBar"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="recordings-explorer">
|
||||
<div class="explorer-header">
|
||||
<h6 class="mb-2">Recordings</h6>
|
||||
<div class="explorer-actions">
|
||||
<button class="btn btn-sm btn-outline-secondary" id="newFolderBtn">
|
||||
<i class="bi bi-folder-plus"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-secondary" id="refreshBtn">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="explorer-tree" id="explorerTree">
|
||||
<div class="tree-item folder expanded" data-path="/">
|
||||
<div class="tree-item-content">
|
||||
<i class="bi bi-folder-open"></i>
|
||||
<span class="tree-item-name">Recordings</span>
|
||||
</div>
|
||||
<div class="tree-item-children">
|
||||
<div class="tree-item file" data-path="/sample1.mp3">
|
||||
<div class="tree-item-content">
|
||||
<i class="bi bi-file-earmark-music"></i>
|
||||
<span class="tree-item-name">sample1.mp3</span>
|
||||
<span class="tree-item-size">2.1 MB</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tree-item file" data-path="/sample2.wav">
|
||||
<div class="tree-item-content">
|
||||
<i class="bi bi-file-earmark-music"></i>
|
||||
<span class="tree-item-name">sample2.wav</span>
|
||||
<span class="tree-item-size">5.3 MB</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Context Menu -->
|
||||
<div class="context-menu" id="contextMenu">
|
||||
<div class="context-menu-item" data-action="transcribe">
|
||||
<i class="bi bi-file-text"></i> Transcribe
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="translate">
|
||||
<i class="bi bi-translate"></i> Translate
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="open">
|
||||
<i class="bi bi-folder-open"></i> Open
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="move">
|
||||
<i class="bi bi-arrow-right"></i> Move
|
||||
</div>
|
||||
<div class="context-menu-item" data-action="rename">
|
||||
<i class="bi bi-pencil"></i> Rename
|
||||
</div>
|
||||
<div class="context-menu-divider"></div>
|
||||
<div class="context-menu-item" data-action="export">
|
||||
<i class="bi bi-download"></i> Export
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File Upload Modal -->
|
||||
<div class="modal fade" id="fileUploadModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Upload File</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="fileInput" class="form-label">Choose file</label>
|
||||
<input class="form-control" type="file" id="fileInput" accept=".txt,.pdf,.doc,.docx,.md">
|
||||
</div>
|
||||
<div class="upload-progress" id="uploadProgress" style="display: none;">
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" id="uploadBtn">Upload</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
<script src="{{.js_theme_url}}"></script>
|
||||
<script src="{{.js_chat_url}}"></script>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user