Merge branch 'development_ds' of github.com:freeflowuniverse/herolib into development_ds
* 'development_ds' of github.com:freeflowuniverse/herolib: refactor: improve config path handling and clean up code refactor: dynamically load site config from heroscript feat: add multi-site support and playbook enhancements
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -51,3 +51,6 @@ compile_summary.log
|
||||
server
|
||||
HTTP_REST_MCP_DEMO.md
|
||||
MCP_HTTP_REST_IMPLEMENTATION_PLAN.md
|
||||
.roo
|
||||
.kilocode
|
||||
.continue
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"chat.mcp.discovery.enabled": true,
|
||||
"mcpServers": {
|
||||
"VSCode": {
|
||||
"type": "internal",
|
||||
"tools": ["chat_send", "apply_edits", "read_file", "write_file", "get_file_tree"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"mcpServers": {}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
!!docusaurus.config
|
||||
name:"my-documentation"
|
||||
title:"My Documentation Site"
|
||||
tagline:"Documentation made simple with V and Docusaurus"
|
||||
url:"https://docs.example.com"
|
||||
url_home:"docs/"
|
||||
base_url:"/"
|
||||
favicon:"img/favicon.png"
|
||||
image:"img/hero.png"
|
||||
copyright:"© 2025 Example Organization"
|
||||
|
||||
!!docusaurus.config_meta
|
||||
description:"Comprehensive documentation for our amazing project"
|
||||
image:"https://docs.example.com/img/social-card.png"
|
||||
title:"My Documentation | Official Docs"
|
||||
|
||||
!!docusaurus.ssh_connection
|
||||
name:"production"
|
||||
host:"example.com"
|
||||
login:"deploy"
|
||||
port:22
|
||||
key_path:"~/.ssh/id_rsa"
|
||||
|
||||
!!docusaurus.build_dest
|
||||
ssh_name:"production"
|
||||
path:"/var/www/docs"
|
||||
|
||||
!!docusaurus.navbar
|
||||
title:"My Project"
|
||||
|
||||
!!docusaurus.navbar_item
|
||||
label:"Documentation"
|
||||
href:"/docs"
|
||||
position:"left"
|
||||
|
||||
!!docusaurus.navbar_item
|
||||
label:"API"
|
||||
href:"/api"
|
||||
position:"left"
|
||||
|
||||
!!docusaurus.navbar_item
|
||||
label:"GitHub"
|
||||
href:"https://github.com/example/repo"
|
||||
position:"right"
|
||||
|
||||
!!docusaurus.footer
|
||||
style:"dark"
|
||||
|
||||
!!docusaurus.footer_item
|
||||
title:"Documentation"
|
||||
label:"Introduction"
|
||||
to:"/docs"
|
||||
|
||||
!!docusaurus.footer_item
|
||||
title:"Documentation"
|
||||
label:"API Reference"
|
||||
to:"/api"
|
||||
|
||||
!!docusaurus.footer_item
|
||||
title:"Community"
|
||||
label:"GitHub"
|
||||
href:"https://github.com/example/repo"
|
||||
|
||||
!!docusaurus.footer_item
|
||||
title:"Community"
|
||||
label:"Discord"
|
||||
href:"https://discord.gg/example"
|
||||
|
||||
!!docusaurus.footer_item
|
||||
title:"More"
|
||||
label:"Blog"
|
||||
href:"https://blog.example.com"
|
||||
|
||||
!!docusaurus.import_source
|
||||
url:"https://github.com/example/external-docs"
|
||||
dest:"external"
|
||||
replace:"PROJECT_NAME:My Project, VERSION:1.0.0"
|
||||
@@ -2,7 +2,8 @@ module herocmds
|
||||
|
||||
import freeflowuniverse.herolib.web.docusaurus
|
||||
import freeflowuniverse.herolib.web.site
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.core.playcmds
|
||||
import os
|
||||
import cli { Command, Flag }
|
||||
|
||||
@@ -123,7 +124,6 @@ fn cmd_docusaurus_execute(cmd Command) ! {
|
||||
mut open := cmd.flags.get_bool('open') 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 { '' }
|
||||
|
||||
// --- Build Path Logic ---
|
||||
mut build_path := cmd.flags.get_string('buildpath') or { '' }
|
||||
@@ -141,7 +141,15 @@ fn cmd_docusaurus_execute(cmd Command) ! {
|
||||
if !os.exists(provided_path) || !os.is_dir(provided_path) {
|
||||
return error('Provided path "${provided_path}" does not exist or is not a directory.')
|
||||
}
|
||||
heroscript_config_dir = provided_path
|
||||
|
||||
// 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) {
|
||||
heroscript_config_dir = cfg_subdir
|
||||
} else {
|
||||
// Assume the provided path is already the cfg directory
|
||||
heroscript_config_dir = provided_path
|
||||
}
|
||||
} else {
|
||||
mut cwd := os.getwd()
|
||||
cfg_dir := os.join_path(cwd, 'cfg')
|
||||
@@ -155,8 +163,45 @@ fn cmd_docusaurus_execute(cmd Command) ! {
|
||||
mut builddevpublish := cmd.flags.get_bool('builddevpublish') or { false }
|
||||
mut dev := cmd.flags.get_bool('dev') or { false }
|
||||
|
||||
// Create a site first using the new API
|
||||
mut generic_site := site.new(name: 'cli_site')!
|
||||
// Process heroscript files from the ebook directory to get proper site configuration
|
||||
mut plbook := playbook.new(path: heroscript_config_dir)!
|
||||
|
||||
// Dynamically add ebook configuration files
|
||||
config_file := '${heroscript_config_dir}/config.heroscript'
|
||||
if os.exists(config_file) {
|
||||
plbook.add(path: config_file)!
|
||||
}
|
||||
|
||||
menus_file := '${heroscript_config_dir}/menus.heroscript'
|
||||
if os.exists(menus_file) {
|
||||
plbook.add(path: menus_file)!
|
||||
}
|
||||
|
||||
pages_dir := '${heroscript_config_dir}/pages'
|
||||
if os.exists(pages_dir) && os.is_dir(pages_dir) {
|
||||
plbook.add(path: pages_dir)!
|
||||
}
|
||||
|
||||
playcmds.run(plbook: plbook)!
|
||||
|
||||
// Process site pages to generate the actual documentation files
|
||||
site.play(mut plbook)!
|
||||
|
||||
// Get the site configuration that was processed from the heroscript files
|
||||
// The site.play() function processes the heroscript and creates sites in the global websites map
|
||||
// We need to get the site by name from the processed configuration
|
||||
config_actions := plbook.find(filter: 'site.config')!
|
||||
if config_actions.len == 0 {
|
||||
return error('No site.config found in heroscript files. Make sure config.heroscript contains !!site.config.')
|
||||
}
|
||||
|
||||
// Get the site name from the first site.config action
|
||||
site_name := config_actions[0].params.get('name') or {
|
||||
return error('site.config must specify a name parameter')
|
||||
}
|
||||
|
||||
// Get the processed site configuration
|
||||
mut generic_site := site.get(name: site_name)!
|
||||
|
||||
// Add docusaurus site
|
||||
mut dsite := docusaurus.add(
|
||||
|
||||
@@ -4,6 +4,7 @@ module playcmds
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.data.doctree
|
||||
import freeflowuniverse.herolib.biz.bizmodel
|
||||
import freeflowuniverse.herolib.web.docusaurus
|
||||
// import freeflowuniverse.herolib.hero.publishing
|
||||
// import freeflowuniverse.herolib.threefold.grid4.gridsimulator
|
||||
// import freeflowuniverse.herolib.installers.sysadmintools.daguserver
|
||||
@@ -19,17 +20,17 @@ pub struct PlayArgs {
|
||||
pub mut:
|
||||
heroscript string
|
||||
heroscript_path string
|
||||
plbook ?playbook.PlayBook
|
||||
plbook ?PlayBook
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn run(args_ PlayArgs) ! {
|
||||
|
||||
mut args := args_
|
||||
|
||||
mut plbook := args.plbook or {
|
||||
playbook.new(text: args.heroscript, path: args.heroscript_path)!
|
||||
}
|
||||
|
||||
play_core(mut plbook)!
|
||||
play_git(mut plbook)!
|
||||
|
||||
@@ -46,13 +47,11 @@ pub fn run(args_ PlayArgs) ! {
|
||||
// plbook = gridsimulator.play(mut plbook)!
|
||||
bizmodel.play(mut plbook)!
|
||||
doctree.play(mut plbook)!
|
||||
docusaurus.play(mut plbook)!
|
||||
|
||||
// slides.play(mut plbook)!
|
||||
// base_install(play(mut plbook)!
|
||||
// coredns.play(mut plbook)!
|
||||
|
||||
// plbook.empty_check()!
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ module playcmds
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
// !!context.configure
|
||||
// name:'test'
|
||||
@@ -76,35 +77,51 @@ fn play_core(mut plbook PlayBook) ! {
|
||||
|
||||
sitename := session.env_get('SITENAME') or { '' }
|
||||
|
||||
// for mut action in plbook.find(filter: 'core.coderoot_set')! {
|
||||
// mut p := action.params
|
||||
// if p.exists('coderoot') {
|
||||
// coderoot := p.get_path_create('coderoot')!
|
||||
// mut gs := session.context.gitstructure()!
|
||||
// if gs.rootpath.path != coderoot {
|
||||
// mut db := session.context.contextdb.db_get(dbname: 'context')!
|
||||
// db.set('coderoot', coderoot)!
|
||||
// session.context.gitstructure_reload()!
|
||||
// }
|
||||
// } else {
|
||||
// return error('coderoot needs to be specified')
|
||||
// }
|
||||
// action.done = true
|
||||
// }
|
||||
// Apply template replacement from session environment variables
|
||||
if session.env.len > 0 {
|
||||
// Create a map with name_fix applied to keys for template replacement
|
||||
mut env_fixed := map[string]string{}
|
||||
for key, value in session.env {
|
||||
env_fixed[texttools.name_fix(key)] = value
|
||||
}
|
||||
|
||||
// for mut action in plbook.find(filter: 'core.params_context_set')! {
|
||||
// mut p := action.params
|
||||
// for param in p.params {
|
||||
// session.context.params.set(param.key, param.value)
|
||||
// }
|
||||
// action.done = true
|
||||
// }
|
||||
for mut action in plbook.actions {
|
||||
if !action.done {
|
||||
action.params.replace(env_fixed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for mut action in plbook.find(filter: 'core.params_session_set')! {
|
||||
// mut p := action.params
|
||||
// for param in p.params {
|
||||
// session.params.set(param.key, param.value)
|
||||
// }
|
||||
// action.done = true
|
||||
// }
|
||||
for mut action in plbook.find(filter: 'core.coderoot_set')! {
|
||||
mut p := action.params
|
||||
if p.exists('coderoot') {
|
||||
coderoot := p.get_path_create('coderoot')!
|
||||
if session.context.config.coderoot != coderoot {
|
||||
session.context.config.coderoot = coderoot
|
||||
session.context.save()!
|
||||
}
|
||||
} else {
|
||||
return error('coderoot needs to be specified')
|
||||
}
|
||||
action.done = true
|
||||
}
|
||||
|
||||
for mut action in plbook.find(filter: 'core.params_context_set')! {
|
||||
mut p := action.params
|
||||
mut context_params := session.context.params()!
|
||||
for param in p.params {
|
||||
context_params.set(param.key, param.value)
|
||||
}
|
||||
session.context.save()!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
for mut action in plbook.find(filter: 'core.params_session_set')! {
|
||||
mut p := action.params
|
||||
for param in p.params {
|
||||
session.params.set(param.key, param.value)
|
||||
}
|
||||
session.save()!
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@ module doctree
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
// import freeflowuniverse.herolib.ui.console
|
||||
|
||||
|
||||
pub fn play(mut plbook playbook.PlayBook) ! {
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
mut doctrees := map[string]&Tree{}
|
||||
|
||||
collection_actions := plbook.find(filter: 'doctree.scan')!
|
||||
@@ -27,7 +25,8 @@ pub fn play(mut plbook playbook.PlayBook) ! {
|
||||
}
|
||||
|
||||
export_actions := plbook.find(filter: 'doctree.export')!
|
||||
if export_actions.len == 0 {
|
||||
if export_actions.len == 0 && collection_actions.len > 0 {
|
||||
// Only auto-export if we have collections to export
|
||||
name0 := 'main'
|
||||
mut doctree0 := doctrees[name0] or { panic("can't find doctree with name ${name0}") }
|
||||
doctree0.export()!
|
||||
@@ -56,5 +55,4 @@ pub fn play(mut plbook playbook.PlayBook) ! {
|
||||
// println(tree_list())
|
||||
// println(tree_get("main")!)
|
||||
// panic("sd")
|
||||
|
||||
}
|
||||
|
||||
@@ -76,8 +76,10 @@ pub fn rm(todelete_ string) ! {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if item.contains('/') {
|
||||
return error('there should be no / in to remove list')
|
||||
// Handle relative paths - they can contain '/' as long as they're valid relative paths
|
||||
if item.contains('/') && !item.starts_with('./') && !item.starts_with('../')
|
||||
&& item.contains('..') {
|
||||
return error('relative paths with .. are not allowed for security: ${item}')
|
||||
}
|
||||
cmd_delete(item)! // look for the command, if will be removed if found
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ module docusaurus
|
||||
|
||||
import freeflowuniverse.herolib.web.site
|
||||
|
||||
|
||||
pub struct Configuration {
|
||||
pub mut:
|
||||
main Main
|
||||
@@ -73,6 +72,7 @@ pub mut:
|
||||
href string @[omitempty]
|
||||
to string @[omitempty]
|
||||
}
|
||||
|
||||
// ... (struct definitions remain the same) ...
|
||||
|
||||
// This function is now a pure transformer: site.SiteConfig -> docusaurus.Configuration
|
||||
@@ -105,7 +105,7 @@ pub fn new_configuration(site_cfg site.SiteConfig) !Configuration {
|
||||
}
|
||||
|
||||
cfg := Configuration{
|
||||
main: Main{
|
||||
main: Main{
|
||||
title: site_cfg.title
|
||||
tagline: site_cfg.tagline
|
||||
favicon: site_cfg.favicon
|
||||
@@ -114,9 +114,21 @@ pub fn new_configuration(site_cfg site.SiteConfig) !Configuration {
|
||||
url_home: site_cfg.url_home
|
||||
image: site_cfg.image
|
||||
metadata: Metadata{
|
||||
title: site_cfg.meta_title
|
||||
description: site_cfg.description
|
||||
image: site_cfg.meta_image
|
||||
title: if site_cfg.meta_title == '' {
|
||||
site_cfg.title
|
||||
} else {
|
||||
site_cfg.meta_title
|
||||
}
|
||||
description: if site_cfg.description == '' {
|
||||
site_cfg.tagline
|
||||
} else {
|
||||
site_cfg.description
|
||||
}
|
||||
image: if site_cfg.meta_image == '' {
|
||||
site_cfg.image
|
||||
} else {
|
||||
site_cfg.meta_image
|
||||
}
|
||||
}
|
||||
build_dest: site_cfg.build_dest.map(it.path)
|
||||
build_dest_dev: site_cfg.build_dest_dev.map(it.path)
|
||||
@@ -141,14 +153,27 @@ pub fn new_configuration(site_cfg site.SiteConfig) !Configuration {
|
||||
}
|
||||
|
||||
fn config_fix(config Configuration) !Configuration {
|
||||
// Fix empty logo sources by removing logo entirely if all fields are empty
|
||||
mut navbar_fixed := config.navbar
|
||||
if config.navbar.logo.src == '' && config.navbar.logo.src_dark == ''
|
||||
&& config.navbar.logo.alt == '' {
|
||||
// Create navbar without logo if all logo fields are empty
|
||||
navbar_fixed = Navbar{
|
||||
title: config.navbar.title
|
||||
items: config.navbar.items
|
||||
// logo field omitted entirely
|
||||
}
|
||||
}
|
||||
|
||||
return Configuration{
|
||||
...config
|
||||
main: Main{
|
||||
main: Main{
|
||||
...config.main
|
||||
title: if config.main.title == '' { 'Docusaurus' } else { config.main.title }
|
||||
favicon: if config.main.favicon == '' { 'img/favicon.ico' } else { config.main.favicon }
|
||||
url: if config.main.url == '' { 'https://example.com' } else { config.main.url }
|
||||
base_url: if config.main.base_url == '' { '/' } else { config.main.base_url }
|
||||
}
|
||||
} // ... (no changes needed here) ...
|
||||
navbar: navbar_fixed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,64 @@
|
||||
module site
|
||||
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.core.playbook { Action, PlayBook }
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
import time
|
||||
|
||||
pub fn play(mut plbook PlayBook)! {
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
// Handle multiple site configurations
|
||||
config_actions := plbook.find(filter: 'site.config')!
|
||||
|
||||
mut website := play_config(mut plbook)!
|
||||
if config_actions.len == 0 {
|
||||
return error('No site.config actions found')
|
||||
}
|
||||
|
||||
// Process each site configuration separately
|
||||
for config_action in config_actions {
|
||||
mut website := play_config_single(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)!
|
||||
}
|
||||
}
|
||||
|
||||
fn play_config_single(action Action) !&Site {
|
||||
mut p := action.params
|
||||
name := p.get('name') or {
|
||||
// If name is not specified, try to derive it from title or use a default
|
||||
title := p.get_default('title', 'default-site')!
|
||||
texttools.name_fix(title)
|
||||
}
|
||||
|
||||
mut website := new(name: name)!
|
||||
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)!
|
||||
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)!
|
||||
|
||||
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 := action.params
|
||||
name := p.get('name') or { return error('need to specify name in site.config') }
|
||||
|
||||
mut website := new(name: name)!
|
||||
mut config:= &website.siteconfig
|
||||
mut config := &website.siteconfig
|
||||
|
||||
config.name = texttools.name_fix(name)
|
||||
config.title = p.get_default('title', 'Documentation Site')!
|
||||
@@ -49,8 +80,10 @@ fn play_config(mut plbook PlayBook) !&Site {
|
||||
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.
|
||||
config.meta_image = p_meta.get_default('image', config.image)!
|
||||
// 'description' from site.config_meta can also be parsed here if a separate meta_description field is added to
|
||||
// For now, config.description (from site.config) is used as the primary source or fallback.
|
||||
// If 'description' is present in site.config_meta, it overrides the main description
|
||||
if p_meta.exists('description') {
|
||||
config.description = p_meta.get('description')!
|
||||
}
|
||||
|
||||
return website
|
||||
}
|
||||
@@ -123,7 +156,7 @@ fn play_menu(mut plbook PlayBook, mut config SiteConfig) ! {
|
||||
}
|
||||
}
|
||||
|
||||
fn play_footer(mut plbook PlayBook , mut config SiteConfig) ! {
|
||||
fn play_footer(mut plbook PlayBook, mut config SiteConfig) ! {
|
||||
footer_actions := plbook.find(filter: 'site.footer')!
|
||||
for action in footer_actions {
|
||||
mut p := action.params
|
||||
@@ -180,4 +213,3 @@ fn play_build_dest_dev(mut plbook PlayBook, mut config SiteConfig) ! {
|
||||
config.build_dest_dev << dest_dev
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
7
logfile
7
logfile
@@ -1,7 +0,0 @@
|
||||
2025-07-31 02:25:13.191 CEST [63643] LOG: starting PostgreSQL 17.5 (Homebrew) on aarch64-apple-darwin24.4.0, compiled by Apple clang version 17.0.0 (clang-1700.0.13.3), 64-bit
|
||||
2025-07-31 02:25:13.192 CEST [63643] LOG: listening on IPv6 address "::1", port 5432
|
||||
2025-07-31 02:25:13.192 CEST [63643] LOG: listening on IPv4 address "127.0.0.1", port 5432
|
||||
2025-07-31 02:25:13.192 CEST [63643] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"
|
||||
2025-07-31 02:25:13.194 CEST [63646] LOG: database system was shut down at 2025-07-31 02:24:30 CEST
|
||||
2025-07-31 02:25:13.196 CEST [63643] LOG: database system is ready to accept connections
|
||||
2025-07-31 02:25:18.216 CEST [63775] FATAL: database "despiegk" does not exist
|
||||
Reference in New Issue
Block a user