This commit is contained in:
2025-02-07 11:59:52 +03:00
parent cd6c899661
commit e34d804dda
26 changed files with 600 additions and 668 deletions

View File

@@ -1,11 +1,9 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.clients.mailclient
import freeflowuniverse.herolib.clients. mailclient
//remove the previous one, otherwise the env variables are not read
mailclient.config_delete(name:"test")!
// remove the previous one, otherwise the env variables are not read
mailclient.config_delete(name: 'test')!
// env variables which need to be set are:
// - MAIL_FROM=...
@@ -14,11 +12,14 @@ mailclient.config_delete(name:"test")!
// - MAIL_SERVER=...
// - MAIL_USERNAME=...
mut client:= mailclient.get(name:"test")!
mut client := mailclient.get(name: 'test')!
println(client)
client.send(subject:'this is a test',to:'kristof@incubaid.com',body:'
client.send(
subject: 'this is a test'
to: 'kristof@incubaid.com'
body: '
this is my email content
')!
'
)!

View File

@@ -3,7 +3,6 @@
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.clients.postgresql_client
// Configure PostgreSQL client
heroscript := "
!!postgresql_client.configure
@@ -19,7 +18,7 @@ heroscript := "
postgresql_client.play(heroscript: heroscript)!
// Get the configured client
mut db_client := postgresql_client.get(name: "test")!
mut db_client := postgresql_client.get(name: 'test')!
// Check if test database exists, create if not
if !db_client.db_exists('test')! {
@@ -31,15 +30,14 @@ if !db_client.db_exists('test')! {
db_client.dbname = 'test'
// Create table if not exists
create_table_sql := "CREATE TABLE IF NOT EXISTS users (
create_table_sql := 'CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)"
)'
println('Creating table users if not exists...')
db_client.exec(create_table_sql)!
println('Database and table setup completed successfully!')

View File

@@ -12,8 +12,8 @@ mut:
}
mut person := Person{
name: 'Bob'
birthday: time.now()
name: 'Bob'
birthday: time.now()
}
heroscript := encoderhero.encode[Person](person)!
@@ -22,9 +22,8 @@ println(heroscript)
person2 := encoderhero.decode[Person](heroscript)!
println(person2)
//show that it doesn't matter which action & method is used
heroscript2:="!!a.b name:Bob age:20 birthday:'2025-02-06 09:57:30'"
// show that it doesn't matter which action & method is used
heroscript2 := "!!a.b name:Bob age:20 birthday:'2025-02-06 09:57:30'"
person3 := encoderhero.decode[Person](heroscript)!
println(person3)

View File

@@ -1,23 +1,22 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import json
enum JobTitle {
manager
executive
worker
manager
executive
worker
}
struct Employee {
mut:
name string
family string @[json: '-'] // this field will be skipped
age int
salary f32
title JobTitle @[json: 'ETitle'] // the key for this field will be 'ETitle', not 'title'
notes string @[omitempty] // the JSON property is not created if the string is equal to '' (an empty string).
// TODO: document @[raw]
name string
family string @[json: '-'] // this field will be skipped
age int
salary f32
title JobTitle @[json: 'ETitle'] // the key for this field will be 'ETitle', not 'title'
notes string @[omitempty] // the JSON property is not created if the string is equal to '' (an empty string).
// TODO: document @[raw]
}
x := Employee{'Peter', 'Begins', 28, 95000.5, .worker, ''}
@@ -34,4 +33,3 @@ println(y)
ss := json.encode(y)
println('JSON encoding of employee y: ${ss}')
assert ss == s

View File

@@ -18,8 +18,7 @@ heroscript := "
postgresql_client.play(heroscript: heroscript)!
// Get the configured client
mut db_client := postgresql_client.get(name: "test")!
mut db_client := postgresql_client.get(name: 'test')!
// Create a new location instance
mut loc := location.new(mut db_client, false) or { panic(err) }

View File

@@ -18,8 +18,7 @@ heroscript := "
postgresql_client.play(heroscript: heroscript)!
// Get the configured client
mut db_client := postgresql_client.get(name: "test")!
mut db_client := postgresql_client.get(name: 'test')!
// Create a new location instance
mut loc := location.new(mut db_client, false) or { panic(err) }

View File

@@ -2,14 +2,15 @@
import freeflowuniverse.herolib.installers.infra.gitea as gitea_installer
mut installer := gitea_installer.get(name: 'test')!
mut installer:= gitea_installer.get(name:'test')!
//if you want to configure using heroscript
gitea_installer.play(heroscript:"
// if you want to configure using heroscript
gitea_installer.play(
heroscript: "
!!gitea.configure name:test
passwd:'something'
domain: 'docs.info.com'
")!
"
)!
installer.start()!

View File

@@ -1,127 +1,112 @@
module mailclient
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
__global (
mailclient_global map[string]&MailClient
mailclient_default string
mailclient_global map[string]&MailClient
mailclient_default string
)
/////////FACTORY
@[params]
pub struct ArgsGet{
pub struct ArgsGet {
pub mut:
name string
name string
}
fn args_get (args_ ArgsGet) ArgsGet {
mut args:=args_
if args.name == ""{
args.name = mailclient_default
}
if args.name == ""{
args.name = "default"
}
return args
fn args_get(args_ ArgsGet) ArgsGet {
mut args := args_
if args.name == '' {
args.name = mailclient_default
}
if args.name == '' {
args.name = 'default'
}
return args
}
pub fn get(args_ ArgsGet) !&MailClient {
mut args := args_get(args_)
if !(args.name in mailclient_global) {
if ! config_exists(args){
config_save(args)!
}
config_load(args)!
}
return mailclient_global[args.name] or {
println(mailclient_global)
//bug if we get here because should be in globals
panic("could not get config for mailclient with name, is bug:${args.name}")
}
pub fn get(args_ ArgsGet) !&MailClient {
mut args := args_get(args_)
if args.name !in mailclient_global {
if !config_exists(args) {
config_save(args)!
}
config_load(args)!
}
return mailclient_global[args.name] or {
println(mailclient_global)
// bug if we get here because should be in globals
panic('could not get config for mailclient with name, is bug:${args.name}')
}
}
pub fn config_exists(args_ ArgsGet) bool {
mut args := args_get(args_)
mut context:=base.context() or { panic("bug") }
return context.hero_config_exists("mailclient",args.name)
mut args := args_get(args_)
mut context := base.context() or { panic('bug') }
return context.hero_config_exists('mailclient', args.name)
}
pub fn config_load(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context:=base.context()!
mut heroscript := context.hero_config_get("mailclient",args.name)!
play(heroscript:heroscript)!
mut args := args_get(args_)
mut context := base.context()!
mut heroscript := context.hero_config_get('mailclient', args.name)!
play(heroscript: heroscript)!
}
pub fn config_save(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context:=base.context()!
context.hero_config_set("mailclient",args.name,heroscript_default(instance:args.name)!)!
mut args := args_get(args_)
mut context := base.context()!
context.hero_config_set('mailclient', args.name, heroscript_default(instance: args.name)!)!
}
pub fn config_delete(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context:=base.context()!
context.hero_config_delete("mailclient",args.name)!
mut args := args_get(args_)
mut context := base.context()!
context.hero_config_delete('mailclient', args.name)!
}
fn set(o MailClient)! {
mut o2:=obj_init(o)!
mailclient_global[o.name] = &o2
mailclient_default = o.name
fn set(o MailClient) ! {
mut o2 := obj_init(o)!
mailclient_global[o.name] = &o2
mailclient_default = o.name
}
@[params]
pub struct PlayArgs {
pub mut:
heroscript string //if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
heroscript string // if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
}
pub fn play(args_ PlayArgs) ! {
mut args:=args_
if args.heroscript == "" {
args.heroscript = heroscript_default()!
}
mut plbook := args.plbook or {
playbook.new(text: args.heroscript)!
}
mut install_actions := plbook.find(filter: 'mailclient.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
mut p := install_action.params
cfg_play(p)!
}
}
mut args := args_
if args.heroscript == '' {
args.heroscript = heroscript_default()!
}
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
mut install_actions := plbook.find(filter: 'mailclient.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
mut p := install_action.params
cfg_play(p)!
}
}
}
//switch instance to be used for mailclient
// switch instance to be used for mailclient
pub fn switch(name string) {
mailclient_default = name
mailclient_default = name
}
//helpers
// helpers
@[params]
pub struct DefaultConfigArgs{
instance string = 'default'
pub struct DefaultConfigArgs {
instance string = 'default'
}

View File

@@ -1,4 +1,5 @@
module mailclient
import freeflowuniverse.herolib.data.paramsparser
import os
@@ -6,7 +7,6 @@ pub const version = '0.0.0'
const singleton = false
const default = true
pub fn heroscript_default(args DefaultConfigArgs) !string {
mail_from := os.getenv_opt('MAIL_FROM') or { 'info@example.com' }
mail_password := os.getenv_opt('MAIL_PASSWORD') or { 'secretpassword' }
@@ -23,7 +23,7 @@ pub fn heroscript_default(args DefaultConfigArgs) !string {
mail_username: '${mail_username}'
"
return heroscript
return heroscript
}
@[heap]
@@ -40,22 +40,18 @@ pub mut:
}
fn cfg_play(p paramsparser.Params) ! {
mut mycfg := MailClient{
mut mycfg := MailClient{
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', 465)!
mail_server: p.get('mail_server')!
mail_username: p.get('mail_username')!
}
set(mycfg)!
}
fn obj_init(obj_ MailClient)!MailClient{
mut obj:=obj_
return obj
}
set(mycfg)!
}
fn obj_init(obj_ MailClient) !MailClient {
mut obj := obj_
return obj
}

View File

@@ -144,7 +144,6 @@ pub fn (mut self Context) hero_config_delete(cat string, name string) ! {
config_file.delete()!
}
pub fn (mut self Context) hero_config_exists(cat string, name string) bool {
path := '${os.home_dir()}/hero/context/${self.config.name}/${cat}__${name}.yaml'
return os.exists(path)

View File

@@ -53,7 +53,6 @@ pub fn cmd_docusaurus(mut cmdroot Command) {
description: 'update your environment the template and the repo you are working on (git pull).'
})
cmd_run.add_flag(Flag{
flag: .bool
required: false
@@ -84,29 +83,29 @@ fn cmd_docusaurus_execute(cmd Command) ! {
// exit(1)
// }
mut docs := docusaurus.new(update:update)!
mut docs := docusaurus.new(update: update)!
if build {
// Create a new docusaurus site
_ := docs.build(
url: url
update:update
url: url
update: update
)!
}
if builddev {
// Create a new docusaurus site
_ := docs.build_dev(
url: url
update:update
url: url
update: update
)!
}
if dev {
// Create a new docusaurus site
_ := docs.dev(
url: url
update:update
url: url
update: update
)!
}
}

View File

@@ -10,16 +10,16 @@ import freeflowuniverse.herolib.clients.postgresql_client
// LocationDB handles all database operations for locations
pub struct LocationDB {
pub mut:
db pg.DB
db pg.DB
db_client postgresql_client.PostgresClient
tmp_dir pathlib.Path
db_dir pathlib.Path
tmp_dir pathlib.Path
db_dir pathlib.Path
}
// new_location_db creates a new LocationDB instance
pub fn new_location_db(mut db_client postgresql_client.PostgresClient, reset bool) !LocationDB {
mut db_dir := pathlib.get_dir(path:'${os.home_dir()}/hero/var/db/location.db',create: true)!
mut db_dir := pathlib.get_dir(path: '${os.home_dir()}/hero/var/db/location.db', create: true)!
// Create locations database if it doesn't exist
if !db_client.db_exists('locations')! {
db_client.db_create('locations')!
@@ -27,15 +27,15 @@ pub fn new_location_db(mut db_client postgresql_client.PostgresClient, reset boo
// Switch to locations database
db_client.dbname = 'locations'
// Get the underlying pg.DB connection
db := db_client.db()!
mut loc_db := LocationDB{
db: db
db: db
db_client: db_client
tmp_dir: pathlib.get_dir(path: '/tmp/location/',create: true)!
db_dir: db_dir
tmp_dir: pathlib.get_dir(path: '/tmp/location/', create: true)!
db_dir: db_dir
}
loc_db.init_tables(reset)!
return loc_db
@@ -50,7 +50,7 @@ fn (mut l LocationDB) init_tables(reset bool) ! {
drop table Country
}!
}
sql l.db {
create table Country
create table City

View File

@@ -5,7 +5,7 @@ import freeflowuniverse.herolib.clients.postgresql_client
// Location represents the main API for location operations
pub struct Location {
mut:
db LocationDB
db LocationDB
db_client postgresql_client.PostgresClient
}
@@ -13,7 +13,7 @@ mut:
pub fn new(mut db_client postgresql_client.PostgresClient, reset bool) !Location {
db := new_location_db(mut db_client, reset)!
return Location{
db: db
db: db
db_client: db_client
}
}
@@ -28,7 +28,7 @@ pub fn (mut l Location) download_and_import(redownload bool) ! {
fn main() ! {
// Configure and get PostgreSQL client
heroscript := "
!!postgresql_client.configure
!!postgresql_client.configure
name:'test'
user: 'postgres'
port: 5432

View File

@@ -1,4 +1,3 @@
module location
//https://www.geonames.org/export/codes.html
// https://www.geonames.org/export/codes.html

View File

@@ -7,27 +7,24 @@ import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
const (
geonames_url = 'https://download.geonames.org/export/dump'
)
const geonames_url = 'https://download.geonames.org/export/dump'
// download_and_import_data downloads and imports GeoNames data
pub fn (mut l LocationDB) download_and_import_data(redownload bool) ! {
// Download country info
if redownload{
if redownload {
l.reset_import_dates()!
}
country_file := osal.download(
url: '${geonames_url}/countryInfo.txt'
dest: '${l.tmp_dir.path}/country.txt'
url: '${geonames_url}/countryInfo.txt'
dest: '${l.tmp_dir.path}/country.txt'
minsize_kb: 10
)!
l.import_country_data(country_file.path)!
l.import_cities()!
}
// reset_import_dates sets all country import_dates to 0
@@ -41,9 +38,9 @@ pub fn (mut l LocationDB) reset_import_dates() ! {
// should_import_cities checks if a city should be imported based on its last import date on country level
fn (mut l LocationDB) should_import_cities(iso2 string) !bool {
console.print_debug('Checking if should import country: ${iso2}')
country := sql l.db {
select from Country where iso2 == "${iso2}" limit 1
select from Country where iso2 == '${iso2}' limit 1
} or { []Country{} }
console.print_debug('SQL query result: ${country.len} records found')
@@ -58,11 +55,11 @@ fn (mut l LocationDB) should_import_cities(iso2 string) !bool {
one_month := i64(30 * 24 * 60 * 60) // 30 days in seconds
last_import := country[0].import_date
time_since_import := now - last_import
console.print_debug('Last import: ${last_import}, Time since import: ${time_since_import} seconds (${time_since_import/86400} days)')
should_import := (time_since_import > one_month) || (last_import == 0)
console.print_debug('Last import: ${last_import}, Time since import: ${time_since_import} seconds (${time_since_import / 86400} days)')
should_import := time_since_import > one_month || last_import == 0
console.print_debug('Should import ${iso2}: ${should_import}')
return should_import
}
@@ -70,10 +67,10 @@ fn (mut l LocationDB) should_import_cities(iso2 string) !bool {
fn (mut l LocationDB) import_country_data(filepath string) ! {
console.print_header('Starting import from: ${filepath}')
l.db.exec('BEGIN TRANSACTION')!
mut file := os.open(filepath) or {
mut file := os.open(filepath) or {
console.print_stderr('Failed to open country file: ${err}')
return err
return err
}
defer { file.close() }
@@ -98,39 +95,34 @@ fn (mut l LocationDB) import_country_data(filepath string) ! {
} or { []Country{} }
country := Country{
iso2: iso2
iso3: fields[1]
name: fields[4]
continent: fields[8]
iso2: iso2
iso3: fields[1]
name: fields[4]
continent: fields[8]
population: fields[7].i64()
timezone: fields[17]
timezone: fields[17]
}
if existing_country.len > 0 {
// Update existing country
sql l.db {
update Country set
iso3 = country.iso3,
name = country.name,
continent = country.continent,
population = country.population,
timezone = country.timezone
where iso2 == iso2
update Country set iso3 = country.iso3, name = country.name, continent = country.continent,
population = country.population, timezone = country.timezone where iso2 == iso2
}!
//console.print_debug("Updated country: ${country}")
// console.print_debug("Updated country: ${country}")
} else {
// Insert new country
sql l.db {
insert country into Country
}!
//console.print_debug("Inserted country: ${country}")
// console.print_debug("Inserted country: ${country}")
}
count++
if count % 10 == 0 {
console.print_header('Processed ${count} countries')
}
}
l.db.exec('COMMIT')!
console.print_header('Finished importing countries. Total records: ${count}')
}
@@ -158,13 +150,13 @@ fn (mut l LocationDB) import_cities() ! {
// Download and process cities for this country
cities_file := osal.download(
url: '${geonames_url}/${iso2}.zip'
dest: '${l.tmp_dir.path}/${iso2}.zip'
url: '${geonames_url}/${iso2}.zip'
dest: '${l.tmp_dir.path}/${iso2}.zip'
expand_file: '${l.tmp_dir.path}/${iso2}'
minsize_kb: 2
minsize_kb: 2
)!
l.import_city_data("${l.tmp_dir.path}/${iso2}/${iso2}.txt")!
l.import_city_data('${l.tmp_dir.path}/${iso2}/${iso2}.txt')!
// Update the country's import date after successful city import
now := time.now().unix()
@@ -193,36 +185,34 @@ fn (mut l LocationDB) import_city_data(filepath string) ! {
// country code : ISO-3166 2-letter country code, 2 characters
// cc2 : alternate country codes, comma separated, ISO-3166 2-letter country code, 200 characters
// admin1 code : fipscode (subject to change to iso code), see exceptions below, see file admin1Codes.txt for display names of this code; varchar(20)
// admin2 code : code for the second administrative division, a county in the US, see file admin2Codes.txt; varchar(80)
// admin2 code : code for the second administrative division, a county in the US, see file admin2Codes.txt; varchar(80)
// admin3 code : code for third level administrative division, varchar(20)
// admin4 code : code for fourth level administrative division, varchar(20)
// population : bigint (8 byte int)
// population : bigint (8 byte int)
// elevation : in meters, integer
// dem : digital elevation model, srtm3 or gtopo30, average elevation of 3''x3'' (ca 90mx90m) or 30''x30'' (ca 900mx900m) area in meters, integer. srtm processed by cgiar/ciat.
// timezone : the iana timezone id (see file timeZone.txt) varchar(40)
// modification date : date of last modification in yyyy-MM-dd format
l.db.exec('BEGIN TRANSACTION')!
mut file := os.open(filepath) or {
mut file := os.open(filepath) or {
console.print_stderr('Failed to open city file: ${err}')
return err
return err
}
defer { file.close() }
mut reader := io.new_buffered_reader(reader:file)
mut reader := io.new_buffered_reader(reader: file)
defer { reader.free() }
mut count := 0
console.print_header('Start import ${filepath}')
for {
line := reader.read_line() or {
//console.print_debug('End of file reached')
break
line := reader.read_line() or {
// console.print_debug('End of file reached')
break
}
//console.print_debug(line)
// console.print_debug(line)
fields := line.split('\t')
if fields.len < 12 { // Need at least 12 fields for required data
console.print_stderr('fields < 12: ${line}')
@@ -234,65 +224,52 @@ fn (mut l LocationDB) import_city_data(filepath string) ! {
name := fields[1]
ascii_name := texttools.name_fix(fields[2])
country_iso2 := fields[8].to_upper()
// Check if city exists
existing_city := sql l.db {
select from City where id == geoname_id
} or { []City{} }
city := City{
id: geoname_id
name: name
ascii_name: ascii_name
country_iso2: country_iso2
postal_code: '' // Not provided in this format
state_name: '' // Will need separate admin codes file
state_code: fields[10]
county_name: ''
county_code: fields[11]
community_name: ''
community_code: ''
latitude: fields[4].f64()
longitude: fields[5].f64()
accuracy: 4 // Using geonameid, so accuracy is 4
population: fields[14].i64()
timezone: fields[17]
feature_class: fields[6]
feature_code: fields[7]
id: geoname_id
name: name
ascii_name: ascii_name
country_iso2: country_iso2
postal_code: '' // Not provided in this format
state_name: '' // Will need separate admin codes file
state_code: fields[10]
county_name: ''
county_code: fields[11]
community_name: ''
community_code: ''
latitude: fields[4].f64()
longitude: fields[5].f64()
accuracy: 4 // Using geonameid, so accuracy is 4
population: fields[14].i64()
timezone: fields[17]
feature_class: fields[6]
feature_code: fields[7]
search_priority: 0 // Default priority
}
if existing_city.len > 0 {
// Update existing city
sql l.db {
update City set
name = city.name,
ascii_name = city.ascii_name,
country_iso2 = city.country_iso2,
postal_code = city.postal_code,
state_name = city.state_name,
state_code = city.state_code,
county_name = city.county_name,
county_code = city.county_code,
community_name = city.community_name,
community_code = city.community_code,
latitude = city.latitude,
longitude = city.longitude,
accuracy = city.accuracy,
population = city.population,
timezone = city.timezone,
feature_class = city.feature_class,
feature_code = city.feature_code,
search_priority = city.search_priority
where id == geoname_id
update City set name = city.name, ascii_name = city.ascii_name, country_iso2 = city.country_iso2,
postal_code = city.postal_code, state_name = city.state_name, state_code = city.state_code,
county_name = city.county_name, county_code = city.county_code, community_name = city.community_name,
community_code = city.community_code, latitude = city.latitude, longitude = city.longitude,
accuracy = city.accuracy, population = city.population, timezone = city.timezone,
feature_class = city.feature_class, feature_code = city.feature_code,
search_priority = city.search_priority where id == geoname_id
}!
//console.print_debug("Updated city: ${city}")
// console.print_debug("Updated city: ${city}")
} else {
// Insert new city
sql l.db {
insert city into City
}!
//console.print_debug("Inserted city: ${city}")
// console.print_debug("Inserted city: ${city}")
}
count++
// if count % 1000 == 0 {
@@ -300,8 +277,8 @@ fn (mut l LocationDB) import_city_data(filepath string) ! {
// }
}
console.print_debug( 'Processed ${count} cities')
console.print_debug('Processed ${count} cities')
l.db.exec('COMMIT')!
console.print_header('Finished importing cities for ${filepath}. Total records: ${count}')
}

View File

@@ -2,44 +2,44 @@ module location
pub struct Country {
pub:
iso2 string @[primary; sql: 'iso2'; max_len: 2; unique; index]
name string @[required; unique; index]
iso3 string @[required; sql: 'iso3'; max_len: 3; unique; index]
iso2 string @[index; max_len: 2; primary; sql: 'iso2'; unique]
name string @[index; required; unique]
iso3 string @[index; max_len: 3; required; sql: 'iso3'; unique]
continent string @[max_len: 2]
population i64
timezone string @[max_len: 40]
import_date i64 // Epoch timestamp of last import
import_date i64 // Epoch timestamp of last import
}
pub struct City {
pub:
id int @[unique; index]
name string @[required; max_len: 200; index]
ascii_name string @[required; max_len: 200; index] // Normalized name without special characters
country_iso2 string @[required; fkey: 'Country.iso2']
postal_code string @[max_len: 20; index ] //postal code
state_name string @[max_len: 100] // State/Province name
state_code string @[max_len: 20] // State/Province code
county_name string @[max_len: 100]
county_code string @[max_len: 20]
community_name string @[max_len: 100]
community_code string @[max_len: 20]
latitude f64 @[index: 'idx_coords']
longitude f64 @[index: 'idx_coords']
population i64
timezone string @[max_len: 40]
feature_class string @[max_len: 1] // For filtering (P for populated places)
feature_code string @[max_len: 10] // Detailed type (PPL, PPLA, etc.)
id int @[index; unique]
name string @[index; max_len: 200; required]
ascii_name string @[index; max_len: 200; required] // Normalized name without special characters
country_iso2 string @[fkey: 'Country.iso2'; required]
postal_code string @[index; max_len: 20] // postal code
state_name string @[max_len: 100] // State/Province name
state_code string @[max_len: 20] // State/Province code
county_name string @[max_len: 100]
county_code string @[max_len: 20]
community_name string @[max_len: 100]
community_code string @[max_len: 20]
latitude f64 @[index: 'idx_coords']
longitude f64 @[index: 'idx_coords']
population i64
timezone string @[max_len: 40]
feature_class string @[max_len: 1] // For filtering (P for populated places)
feature_code string @[max_len: 10] // Detailed type (PPL, PPLA, etc.)
search_priority int
accuracy i16 = 1 //1=estimated, 4=geonameid, 6=centroid of addresses or shape
accuracy i16 = 1 // 1=estimated, 4=geonameid, 6=centroid of addresses or shape
}
pub struct AlternateName {
pub:
id int @[primary; sql: serial]
city_id int @[required; fkey: 'City.id']
name string @[required; max_len: 200; index]
language_code string @[max_len: 2]
id int @[primary; sql: serial]
city_id int @[fkey: 'City.id'; required]
name string @[index; max_len: 200; required]
language_code string @[max_len: 2]
is_preferred bool
is_short bool
}
@@ -47,9 +47,9 @@ pub:
// SearchResult represents a location search result with combined city and country info
pub struct SearchResult {
pub:
city City
country Country
similarity f64 // Search similarity score
city City
country Country
similarity f64 // Search similarity score
}
// Coordinates represents a geographic point

View File

@@ -51,11 +51,11 @@ import db.pg
// where_clause := if query_conditions.len > 0 { 'WHERE ' + query_conditions.join(' AND ') } else { '' }
// query := '
// SELECT c.*, co.*
// SELECT c.*, co.*
// FROM City c
// JOIN Country co ON c.country_iso2 = co.iso2
// ${where_clause}
// ORDER BY c.search_priority DESC, c.population DESC
// ORDER BY c.search_priority DESC, c.population DESC
// LIMIT ${opts.limit}
// '
@@ -111,8 +111,8 @@ import db.pg
// query := "
// WITH distances AS (
// SELECT c.*, co.*,
// (6371 * acos(cos(radians($1)) * cos(radians(latitude)) *
// cos(radians(longitude) - radians($2)) + sin(radians($1)) *
// (6371 * acos(cos(radians($1)) * cos(radians(latitude)) *
// cos(radians(longitude) - radians($2)) + sin(radians($1)) *
// sin(radians(latitude)))) AS distance
// FROM City c
// JOIN Country co ON c.country_iso2 = co.iso2
@@ -122,7 +122,7 @@ import db.pg
// ORDER BY distance
// LIMIT $4
// "
// params := [
// opts.coordinates.latitude.str(),
// opts.coordinates.longitude.str(),

View File

@@ -97,13 +97,13 @@ fn (mut repo GitRepo) load_tags() ! {
tags_result := repo.exec('git tag --list') or {
return error('Failed to list tags: ${err}. Please ensure git is installed and repository is accessible.')
}
//println(tags_result)
// println(tags_result)
for line in tags_result.split('\n') {
line_trimmed := line.trim_space()
if line_trimmed != '' {
parts := line_trimmed.split(' ')
if parts.len < 2 {
//console.print_debug('Skipping malformed tag line: ${line_trimmed}')
// console.print_debug('Skipping malformed tag line: ${line_trimmed}')
continue
}
commit_hash := parts[0].trim_space()

View File

@@ -25,22 +25,21 @@ fn installed() !bool {
}
fn install() ! {
console.print_header('install gitea')
baseurl:="https://github.com/go-gitea/gitea/releases/download/v${version}/gitea-${version}"
baseurl := 'https://github.com/go-gitea/gitea/releases/download/v${version}/gitea-${version}'
mut url := ''
if core.is_linux_arm()! {
//https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-linux-arm64.xz
if core.is_linux_arm()! {
// https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-linux-arm64.xz
url = '${baseurl}-linux-arm64.xz'
} else if core.is_linux_intel()! {
// https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-linux-amd64.xz
url = '${baseurl}-linux-amd64.xz'
} else if core.is_osx_arm()! {
//https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-darwin-10.12-arm64.xz
// https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-darwin-10.12-arm64.xz
url = '${baseurl}-darwin-10.12-arm64.xz'
} else if core.is_osx_intel()! {
//https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-darwin-10.12-amd64.xz
// https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-darwin-10.12-amd64.xz
url = '${baseurl}-darwin-10.12-amd64.xz'
} else {
return error('unsported platform')
@@ -104,68 +103,66 @@ fn startupcmd() ![]zinit.ZProcessNewArgs {
}
return res
// mut res := []zinit.ZProcessNewArgs{}
// cfg := get()!
// res << zinit.ZProcessNewArgs{
// name: 'gitea'
// // cmd: 'GITEA_WORK_DIR=${cfg.path} sudo -u git /var/lib/git/gitea web -c /etc/gitea_app.ini'
// cmd: '
// # Variables
// GITEA_USER="${cfg.run_user}"
// GITEA_HOME="${cfg.path}"
// GITEA_BINARY="/usr/local/bin/gitea"
// GITEA_CONFIG="/etc/gitea_app.ini"
// GITEA_DATA_PATH="\$GITEA_HOME/data"
// GITEA_CUSTOM_PATH="\$GITEA_HOME/custom"
// GITEA_LOG_PATH="\$GITEA_HOME/log"
// mut res := []zinit.ZProcessNewArgs{}
// cfg := get()!
// res << zinit.ZProcessNewArgs{
// name: 'gitea'
// // cmd: 'GITEA_WORK_DIR=${cfg.path} sudo -u git /var/lib/git/gitea web -c /etc/gitea_app.ini'
// cmd: '
// # Ensure the script is run as root
// if [[ \$EUID -ne 0 ]]; then
// echo "This script must be run as root."
// exit 1
// fi
// # Variables
// GITEA_USER="${cfg.run_user}"
// GITEA_HOME="${cfg.path}"
// GITEA_BINARY="/usr/local/bin/gitea"
// GITEA_CONFIG="/etc/gitea_app.ini"
// GITEA_DATA_PATH="\$GITEA_HOME/data"
// GITEA_CUSTOM_PATH="\$GITEA_HOME/custom"
// GITEA_LOG_PATH="\$GITEA_HOME/log"
// echo "Setting up Gitea..."
// # Ensure the script is run as root
// if [[ \$EUID -ne 0 ]]; then
// echo "This script must be run as root."
// exit 1
// fi
// # Create Gitea user if it doesn\'t exist
// if id -u "\$GITEA_USER" &>/dev/null; then
// echo "User \$GITEA_USER already exists."
// else
// echo "Creating Gitea user..."
// if ! sudo adduser --system --shell /bin/bash --group --disabled-password --home "/var/lib/\$GITEA_USER" "\$GITEA_USER"; then
// echo "Failed to create user \$GITEA_USER."
// exit 1
// fi
// fi
// echo "Setting up Gitea..."
// # Create necessary directories
// echo "Creating directories..."
// mkdir -p "\$GITEA_DATA_PATH" "\$GITEA_CUSTOM_PATH" "\$GITEA_LOG_PATH"
// chown -R "\$GITEA_USER:\$GITEA_USER" "\$GITEA_HOME"
// chmod -R 750 "\$GITEA_HOME"
// # Create Gitea user if it doesn\'t exist
// if id -u "\$GITEA_USER" &>/dev/null; then
// echo "User \$GITEA_USER already exists."
// else
// echo "Creating Gitea user..."
// if ! sudo adduser --system --shell /bin/bash --group --disabled-password --home "/var/lib/\$GITEA_USER" "\$GITEA_USER"; then
// echo "Failed to create user \$GITEA_USER."
// exit 1
// fi
// fi
// chown "\$GITEA_USER:\$GITEA_USER" "\$GITEA_CONFIG"
// chmod 640 "\$GITEA_CONFIG"
// # Create necessary directories
// echo "Creating directories..."
// mkdir -p "\$GITEA_DATA_PATH" "\$GITEA_CUSTOM_PATH" "\$GITEA_LOG_PATH"
// chown -R "\$GITEA_USER:\$GITEA_USER" "\$GITEA_HOME"
// chmod -R 750 "\$GITEA_HOME"
// chown "\$GITEA_USER:\$GITEA_USER" "\$GITEA_CONFIG"
// chmod 640 "\$GITEA_CONFIG"
// GITEA_WORK_DIR=\$GITEA_HOME sudo -u git gitea web -c \$GITEA_CONFIG
// '
// workdir: cfg.path
// }
// res << zinit.ZProcessNewArgs{
// name: 'restart_gitea'
// cmd: 'sleep 30 && zinit restart gitea && exit 1'
// after: ['gitea']
// oneshot: true
// workdir: cfg.path
// }
// return res
// GITEA_WORK_DIR=\$GITEA_HOME sudo -u git gitea web -c \$GITEA_CONFIG
// '
// workdir: cfg.path
// }
// res << zinit.ZProcessNewArgs{
// name: 'restart_gitea'
// cmd: 'sleep 30 && zinit restart gitea && exit 1'
// after: ['gitea']
// oneshot: true
// workdir: cfg.path
// }
// return res
}
fn running() !bool {
//TODO: extend with proper gitea client
// TODO: extend with proper gitea client
res := os.execute('curl -fsSL http://localhost:3000 || exit 1')
return res.exit_code == 0
}

View File

@@ -4,287 +4,277 @@ import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.osal.zinit
import time
__global (
gitea_global map[string]&GiteaServer
gitea_default string
gitea_global map[string]&GiteaServer
gitea_default string
)
/////////FACTORY
@[params]
pub struct ArgsGet{
pub struct ArgsGet {
pub mut:
name string
name string
}
fn args_get (args_ ArgsGet) ArgsGet {
mut args:=args_
if args.name == ""{
args.name = "default"
}
return args
fn args_get(args_ ArgsGet) ArgsGet {
mut args := args_
if args.name == '' {
args.name = 'default'
}
return args
}
pub fn get(args_ ArgsGet) !&GiteaServer {
mut context:=base.context()!
mut args := args_get(args_)
mut obj := GiteaServer{}
if !(args.name in gitea_global) {
if ! exists(args)!{
set(obj)!
}else{
heroscript := context.hero_config_get("gitea",args.name)!
mut obj2:=heroscript_loads(heroscript)!
set_in_mem(obj2)!
}
}
return gitea_global[args.name] or {
println(gitea_global)
//bug if we get here because should be in globals
panic("could not get config for gitea with name, is bug:${args.name}")
}
pub fn get(args_ ArgsGet) !&GiteaServer {
mut context := base.context()!
mut args := args_get(args_)
mut obj := GiteaServer{}
if args.name !in gitea_global {
if !exists(args)! {
set(obj)!
} else {
heroscript := context.hero_config_get('gitea', args.name)!
mut obj2 := heroscript_loads(heroscript)!
set_in_mem(obj2)!
}
}
return gitea_global[args.name] or {
println(gitea_global)
// bug if we get here because should be in globals
panic('could not get config for gitea with name, is bug:${args.name}')
}
}
//register the config for the future
pub fn set(o GiteaServer)! {
set_in_mem(o)!
mut context := base.context()!
heroscript := heroscript_dumps(o)!
context.hero_config_set("gitea", o.name, heroscript)!
// register the config for the future
pub fn set(o GiteaServer) ! {
set_in_mem(o)!
mut context := base.context()!
heroscript := heroscript_dumps(o)!
context.hero_config_set('gitea', o.name, heroscript)!
}
//does the config exists?
// does the config exists?
pub fn exists(args_ ArgsGet) !bool {
mut context := base.context()!
mut args := args_get(args_)
return context.hero_config_exists("gitea", args.name)
mut context := base.context()!
mut args := args_get(args_)
return context.hero_config_exists('gitea', args.name)
}
pub fn delete(args_ ArgsGet)! {
mut args := args_get(args_)
mut context:=base.context()!
context.hero_config_delete("gitea",args.name)!
if args.name in gitea_global {
//del gitea_global[args.name]
}
pub fn delete(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
context.hero_config_delete('gitea', args.name)!
if args.name in gitea_global {
// del gitea_global[args.name]
}
}
//only sets in mem, does not set as config
fn set_in_mem(o GiteaServer)! {
mut o2:=obj_init(o)!
gitea_global[o.name] = &o2
gitea_default = o.name
// only sets in mem, does not set as config
fn set_in_mem(o GiteaServer) ! {
mut o2 := obj_init(o)!
gitea_global[o.name] = &o2
gitea_default = o.name
}
@[params]
pub struct PlayArgs {
pub mut:
heroscript string //if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
heroscript string // if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
}
pub fn play(args_ PlayArgs) ! {
mut args:=args_
mut args := args_
mut plbook := args.plbook or {
playbook.new(text: args.heroscript)!
}
mut install_actions := plbook.find(filter: 'gitea.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
heroscript:=install_action.heroscript()
mut obj:=heroscript_loads(heroscript)!
set(obj)!
}
}
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
mut other_actions := plbook.find(filter: 'gitea.')!
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 gitea.destroy")
destroy()!
}
if other_action.name == "install"{
console.print_debug("install action gitea.install")
install()!
}
}
if other_action.name in ["start","stop","restart"]{
mut p := other_action.params
name := p.get('name')!
mut gitea_obj:=get(name:name)!
console.print_debug("action object:\n${gitea_obj}")
if other_action.name == "start"{
console.print_debug("install action gitea.${other_action.name}")
gitea_obj.start()!
}
mut install_actions := plbook.find(filter: 'gitea.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
heroscript := install_action.heroscript()
mut obj := heroscript_loads(heroscript)!
set(obj)!
}
}
if other_action.name == "stop"{
console.print_debug("install action gitea.${other_action.name}")
gitea_obj.stop()!
}
if other_action.name == "restart"{
console.print_debug("install action gitea.${other_action.name}")
gitea_obj.restart()!
}
}
}
mut other_actions := plbook.find(filter: 'gitea.')!
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 gitea.destroy')
destroy()!
}
if other_action.name == 'install' {
console.print_debug('install action gitea.install')
install()!
}
}
if other_action.name in ['start', 'stop', 'restart'] {
mut p := other_action.params
name := p.get('name')!
mut gitea_obj := get(name: name)!
console.print_debug('action object:\n${gitea_obj}')
if other_action.name == 'start' {
console.print_debug('install action gitea.${other_action.name}')
gitea_obj.start()!
}
if other_action.name == 'stop' {
console.print_debug('install action gitea.${other_action.name}')
gitea_obj.stop()!
}
if other_action.name == 'restart' {
console.print_debug('install action gitea.${other_action.name}')
gitea_obj.restart()!
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager {
// unknown
// screen
// zinit
// tmux
// systemd
match cat{
.zinit{
console.print_debug("startupmanager: zinit")
return startupmanager.get(cat:.zinit)!
}
.systemd{
console.print_debug("startupmanager: systemd")
return startupmanager.get(cat:.systemd)!
}else{
console.print_debug("startupmanager: auto")
return startupmanager.get()!
}
}
// unknown
// screen
// zinit
// tmux
// systemd
match cat {
.zinit {
console.print_debug('startupmanager: zinit')
return startupmanager.get(cat: .zinit)!
}
.systemd {
console.print_debug('startupmanager: systemd')
return startupmanager.get(cat: .systemd)!
}
else {
console.print_debug('startupmanager: auto')
return startupmanager.get()!
}
}
}
//load from disk and make sure is properly intialized
// load from disk and make sure is properly intialized
pub fn (mut self GiteaServer) reload() ! {
switch(self.name)
self=obj_init(self)!
switch(self.name)
self = obj_init(self)!
}
pub fn (mut self GiteaServer) start() ! {
switch(self.name)
if self.running()!{
return
}
switch(self.name)
if self.running()! {
return
}
console.print_header('gitea start')
console.print_header('gitea start')
if ! installed()!{
install()!
}
if !installed()! {
install()!
}
configure()!
configure()!
start_pre()!
start_pre()!
for zprocess in startupcmd()!{
mut sm:=startupmanager_get(zprocess.startuptype)!
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
console.print_debug('starting gitea with ${zprocess.startuptype}...')
console.print_debug('starting gitea with ${zprocess.startuptype}...')
sm.new(zprocess)!
sm.new(zprocess)!
sm.start(zprocess.name)!
}
sm.start(zprocess.name)!
}
start_post()!
for _ in 0 .. 50 {
if self.running()! {
return
}
time.sleep(100 * time.millisecond)
}
return error('gitea did not install properly.')
start_post()!
for _ in 0 .. 50 {
if self.running()! {
return
}
time.sleep(100 * time.millisecond)
}
return error('gitea did not install properly.')
}
pub fn (mut self GiteaServer) install_start(args InstallArgs) ! {
switch(self.name)
self.install(args)!
self.start()!
switch(self.name)
self.install(args)!
self.start()!
}
pub fn (mut self GiteaServer) stop() ! {
switch(self.name)
stop_pre()!
for zprocess in startupcmd()!{
mut sm:=startupmanager_get(zprocess.startuptype)!
sm.stop(zprocess.name)!
}
stop_post()!
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 GiteaServer) restart() ! {
switch(self.name)
self.stop()!
self.start()!
switch(self.name)
self.stop()!
self.start()!
}
pub fn (mut self GiteaServer) running() !bool {
switch(self.name)
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()!
// walk over the generic processes, if not running return
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
r := sm.running(zprocess.name)!
if r == false {
return false
}
}
return running()!
}
@[params]
pub struct InstallArgs{
pub struct InstallArgs {
pub mut:
reset bool
reset bool
}
pub fn (mut self GiteaServer) install(args InstallArgs) ! {
switch(self.name)
if args.reset || (!installed()!) {
install()!
}
switch(self.name)
if args.reset || (!installed()!) {
install()!
}
}
pub fn (mut self GiteaServer) build() ! {
switch(self.name)
build()!
switch(self.name)
build()!
}
pub fn (mut self GiteaServer) destroy() ! {
switch(self.name)
self.stop() or {}
destroy()!
switch(self.name)
self.stop() or {}
destroy()!
}
//switch instance to be used for gitea
// switch instance to be used for gitea
pub fn switch(name string) {
gitea_default = name
gitea_default = name
}
//helpers
// helpers
@[params]
pub struct DefaultConfigArgs{
instance string = 'default'
pub struct DefaultConfigArgs {
instance string = 'default'
}

View File

@@ -1,4 +1,5 @@
module gitea
import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.herolib.data.encoderhero
import freeflowuniverse.herolib.core.pathlib
@@ -16,30 +17,29 @@ const default = false
@[heap]
pub struct GiteaServer {
pub mut:
name string = 'default'
path string = '${os.home_dir()}/hero/var/gitea'
passwd string
domain string = "git.test.com"
jwt_secret string = rand.hex(12)
lfs_jwt_secret string
internal_token string
secret_key string
postgresql_client_name string = "default"
mail_client_name string = "default"
name string = 'default'
path string = '${os.home_dir()}/hero/var/gitea'
passwd string
domain string = 'git.test.com'
jwt_secret string = rand.hex(12)
lfs_jwt_secret string
internal_token string
secret_key string
postgresql_client_name string = 'default'
mail_client_name string = 'default'
}
pub fn (obj GiteaServer) config_path() string {
return '${obj.path}/config.ini'
return '${obj.path}/config.ini'
}
//your checking & initialization code if needed
fn obj_init(mycfg_ GiteaServer)!GiteaServer{
mut mycfg:=mycfg_
return mycfg
// your checking & initialization code if needed
fn obj_init(mycfg_ GiteaServer) !GiteaServer {
mut mycfg := mycfg_
return mycfg
}
//called before start if done
// called before start if done
fn configure() ! {
mut server := get()!
@@ -68,7 +68,7 @@ fn configure() ! {
return error('Failed to initialize mail client "${server.mail_client_name}": ${err}')
}
//TODO: check database exists
// TODO: check database exists
if !db_client.db_exists('gitea_${server.name}')! {
console.print_header('Creating database gitea_${server.name} for gitea.')
db_client.db_create('gitea_${server.name}')!
@@ -85,10 +85,10 @@ fn configure() ! {
/////////////NORMALLY NO NEED TO TOUCH
pub fn heroscript_dumps(obj GiteaServer) !string {
return encoderhero.encode[GiteaServer ](obj)!
return encoderhero.encode[GiteaServer](obj)!
}
pub fn heroscript_loads(heroscript string) !GiteaServer {
mut obj := encoderhero.decode[GiteaServer](heroscript)!
return obj
mut obj := encoderhero.decode[GiteaServer](heroscript)!
return obj
}

View File

@@ -10,9 +10,9 @@ fn testsuite_begin() {
muttmux := new() or { panic('Cannot create tmux: ${err}') }
// reset tmux for tests
is_running := tmux.is_running() or { panic('cannot check if tmux is running: ${err}') }
is_running := is_running() or { panic('cannot check if tmux is running: ${err}') }
if is_running {
tmux.stop() or { panic('Cannot stop tmux: ${err}') }
stop() or { panic('Cannot stop tmux: ${err}') }
}
}
@@ -41,27 +41,21 @@ fn test_window_new() ! {
// tests creating duplicate windows
fn test_window_new0() {
installer := get_install()!
installer := tmux.get_install(
mut tmux := Tmux {
mut tmux := Tmux{
node: node_ssh
}
window_args := WindowArgs {
window_args := WindowArgs{
name: 'TestWindow0'
}
// console.print_debug(tmux)
mut window := tmux.window_new(window_args) or {
panic("Can't create new window: $err")
}
mut window := tmux.window_new(window_args) or { panic("Can't create new window: ${err}") }
assert tmux.sessions.keys().contains('main')
mut window_dup := tmux.window_new(window_args) or {
panic("Can't create new window: $err")
}
console.print_debug(node_ssh.exec('tmux ls') or { panic("fail:$err")})
window.delete() or { panic("Cant delete window") }
mut window_dup := tmux.window_new(window_args) or { panic("Can't create new window: ${err}") }
console.print_debug(node_ssh.exec('tmux ls') or { panic('fail:${err}') })
window.delete() or { panic('Cant delete window') }
// console.print_debug(tmux)
}

View File

@@ -13,58 +13,58 @@ import freeflowuniverse.herolib.ui.console
@[heap]
pub struct DocSite {
pub mut:
name string
url string
path_src pathlib.Path
path_build pathlib.Path
name string
url string
path_src pathlib.Path
path_build pathlib.Path
// path_publish pathlib.Path
args DSiteNewArgs
errors []SiteError
args DSiteNewArgs
errors []SiteError
config Config
}
@[params]
pub struct DSiteNewArgs {
pub mut:
name string
nameshort string
path string
url string
name string
nameshort string
path string
url string
// publish_path string
build_path string
production bool
build_path string
production bool
watch_changes bool = true
update bool
update bool
}
pub fn (mut f DocusaurusFactory) build_dev(args_ DSiteNewArgs) !&DocSite {
mut s:=f.add(args_)!
mut s := f.add(args_)!
s.generate()!
osal.exec(
cmd: '
cmd: '
cd ${s.path_build.path}
bash build_dev.sh
'
retry: 0
)!
)!
return s
}
pub fn (mut f DocusaurusFactory) build(args_ DSiteNewArgs) !&DocSite {
mut s:=f.add(args_)!
mut s := f.add(args_)!
s.generate()!
osal.exec(
cmd: '
cmd: '
cd ${s.path_build.path}
bash build.sh
'
retry: 0
)!
)!
return s
}
pub fn (mut f DocusaurusFactory) dev(args_ DSiteNewArgs) !&DocSite {
mut s:=f.add(args_)!
mut s := f.add(args_)!
s.clean()!
s.generate()!
@@ -72,14 +72,14 @@ pub fn (mut f DocusaurusFactory) dev(args_ DSiteNewArgs) !&DocSite {
// 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
name: screen_name
cmd: '/bin/bash'
start: true
attach: false
reset: true
reset: true
)!
// Send commands to the screen session
@@ -93,33 +93,29 @@ pub fn (mut f DocusaurusFactory) dev(args_ DSiteNewArgs) !&DocSite {
console.print_item(' 1. Attach to screen: screen -r ${screen_name}')
console.print_item(' 2. To detach from screen: Press Ctrl+A then D')
console.print_item(' 3. To list all screens: screen -ls')
console.print_item('The site content is on::')
console.print_item('The site content is on::')
console.print_item(' 1. location of documents: ${s.path_src.path}/docs')
if osal.cmd_exists("code"){
if osal.cmd_exists('code') {
console.print_item(' 2. We opened above dir in vscode.')
osal.exec(cmd:'code ${s.path_src.path}/docs')!
osal.exec(cmd: 'code ${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")
// mut tf:=spawn watch_docs(docs_path, s.path_src.path, s.path_build.path)
// tf.wait()!
println('\n')
if args_.watch_changes {
docs_path := '${s.path_src.path}/docs'
watch_docs(docs_path, s.path_src.path, s.path_build.path)!
}
}
return s
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
pub fn (mut f DocusaurusFactory) add(args_ DSiteNewArgs) !&DocSite {
console.print_header(' Docusaurus: ${args_.name}')
mut args := args_
@@ -129,62 +125,57 @@ pub fn (mut f DocusaurusFactory) add(args_ DSiteNewArgs) !&DocSite {
}
// if args.publish_path.len == 0 {
// args.publish_path = '${f.path_publish.path}/${args.name}'
if args.url.len>0{
if args.url.len > 0 {
mut gs := gittools.new()!
args.path = gs.get_path(url: args.url)!
}
if args.path.len==0{
if args.path.len == 0 {
return error("Can't get path from docusaurus site, its not specified.")
}
mut gs := gittools.new()!
mut r := gs.get_repo(url: 'https://github.com/freeflowuniverse/docusaurus_template.git',pull:args.update)!
mut r := gs.get_repo(
url: 'https://github.com/freeflowuniverse/docusaurus_template.git'
pull: args.update
)!
mut template_path := r.patho()!
// First ensure cfg directory exists in src, if not copy from template
if !os.exists("${args.path}/cfg") {
mut template_cfg := template_path.dir_get("cfg")!
template_cfg.copy(dest:"${args.path}/cfg")!
if !os.exists('${args.path}/cfg') {
mut template_cfg := template_path.dir_get('cfg')!
template_cfg.copy(dest: '${args.path}/cfg')!
}
if !os.exists("${args.path}/docs") {
mut template_cfg := template_path.dir_get("docs")!
template_cfg.copy(dest:"${args.path}/docs")!
if !os.exists('${args.path}/docs') {
mut template_cfg := template_path.dir_get('docs')!
template_cfg.copy(dest: '${args.path}/docs')!
}
mut myconfig := load_config('${args.path}/cfg')!
mut myconfig:=load_config("${args.path}/cfg")!
if myconfig.main.name.len==0{
myconfig.main.name = myconfig.main.base_url.trim_space().trim("/").trim_space()
if myconfig.main.name.len == 0 {
myconfig.main.name = myconfig.main.base_url.trim_space().trim('/').trim_space()
}
if args.name == '' {
args.name = myconfig.main.name
}
}
if args.nameshort.len == 0 {
args.nameshort = args.name
}
}
args.nameshort = texttools.name_fix(args.nameshort)
mut ds := DocSite{
name: args.name
url: args.url
path_src: pathlib.get_dir(path: args.path, create: false)!
name: args.name
url: args.url
path_src: pathlib.get_dir(path: args.path, create: false)!
path_build: f.path_build
// path_publish: pathlib.get_dir(path: args.publish_path, create: true)!
args: args
config:myconfig
args: args
config: myconfig
}
f.sites << &ds
@@ -201,9 +192,9 @@ pub mut:
}
pub fn (mut site DocSite) error(args ErrorArgs) {
// path2 := pathlib.get(args.path)
e := SiteError{
path: args.path
// path2 := pathlib.get(args.path)
e := SiteError{
path: args.path
msg: args.msg
cat: args.cat
}
@@ -222,21 +213,19 @@ pub fn (mut site DocSite) generate() ! {
// retry: 0
// )!
// Now copy all directories that exist in src to build
for item in ["src","static","cfg"]{
if os.exists("${site.path_src.path}/${item}"){
mut aa:= site.path_src.dir_get(item)!
aa.copy(dest:"${site.path_build.path}/${item}")!
for item in ['src', 'static', 'cfg'] {
if os.exists('${site.path_src.path}/${item}') {
mut aa := site.path_src.dir_get(item)!
aa.copy(dest: '${site.path_build.path}/${item}')!
}
}
for item in ["docs"]{
if os.exists("${site.path_src.path}/${item}"){
mut aa:= site.path_src.dir_get(item)!
aa.copy(dest:"${site.path_build.path}/${item}",delete:true)!
for item in ['docs'] {
if os.exists('${site.path_src.path}/${item}') {
mut aa := site.path_src.dir_get(item)!
aa.copy(dest: '${site.path_build.path}/${item}', delete: true)!
}
}
}
fn (mut site DocSite) template_install() ! {
@@ -245,22 +234,26 @@ fn (mut site DocSite) template_install() ! {
mut r := gs.get_repo(url: 'https://github.com/freeflowuniverse/docusaurus_template.git')!
mut template_path := r.patho()!
//always start from template first
for item in ["src","static","cfg"]{
mut aa:= template_path.dir_get(item)!
aa.copy(dest:"${site.path_build.path}/${item}",delete:true)!
// always start from template first
for item in ['src', 'static', 'cfg'] {
mut aa := template_path.dir_get(item)!
aa.copy(dest: '${site.path_build.path}/${item}', delete: true)!
}
for item in ['package.json', 'sidebars.ts', 'tsconfig.json','docusaurus.config.ts'] {
for item in ['package.json', 'sidebars.ts', 'tsconfig.json', 'docusaurus.config.ts'] {
src_path := os.join_path(template_path.path, item)
dest_path := os.join_path(site.path_build.path, item)
os.cp(src_path, dest_path) or { return error('Failed to copy ${item} to build path: ${err}') }
os.cp(src_path, dest_path) or {
return error('Failed to copy ${item} to build path: ${err}')
}
}
for item in ['.gitignore'] {
src_path := os.join_path(template_path.path, item)
dest_path := os.join_path(site.path_src.path, item)
os.cp(src_path, dest_path) or { return error('Failed to copy ${item} to source path: ${err}') }
os.cp(src_path, dest_path) or {
return error('Failed to copy ${item} to source path: ${err}')
}
}
cfg := site.config
@@ -269,30 +262,27 @@ fn (mut site DocSite) template_install() ! {
build := $tmpl('templates/build.sh')
build_dev := $tmpl('templates/build_dev.sh')
mut develop_ := site.path_build.file_get_new("develop.sh")!
develop_.template_write(develop,true)!
mut develop_ := site.path_build.file_get_new('develop.sh')!
develop_.template_write(develop, true)!
develop_.chmod(0o700)!
mut build_ := site.path_build.file_get_new("build.sh")!
build_.template_write(build,true)!
mut build_ := site.path_build.file_get_new('build.sh')!
build_.template_write(build, true)!
build_.chmod(0o700)!
mut build_dev_ := site.path_build.file_get_new("build_dev.sh")!
build_dev_.template_write(build_dev,true)!
mut build_dev_ := site.path_build.file_get_new('build_dev.sh')!
build_dev_.template_write(build_dev, true)!
build_dev_.chmod(0o700)!
mut develop2_ := site.path_src.file_get_new("develop.sh")!
develop2_.template_write(develop,true)!
mut develop2_ := site.path_src.file_get_new('develop.sh')!
develop2_.template_write(develop, true)!
develop2_.chmod(0o700)!
mut build2_ := site.path_src.file_get_new("build.sh")!
build2_.template_write(build,true)!
mut build2_ := site.path_src.file_get_new('build.sh')!
build2_.template_write(build, true)!
build2_.chmod(0o700)!
mut build_dev2_ := site.path_src.file_get_new("build_dev.sh")!
build_dev2_.template_write(build_dev,true)!
build_dev2_.chmod(0o700)!
mut build_dev2_ := site.path_src.file_get_new('build_dev.sh')!
build_dev2_.template_write(build_dev, true)!
build_dev2_.chmod(0o700)!
}

View File

@@ -22,7 +22,7 @@ pub mut:
// publish_path string
build_path string
production bool
update bool
update bool
}
pub fn new(args_ DocusaurusArgs) !&DocusaurusFactory {

View File

@@ -7,7 +7,10 @@ import freeflowuniverse.herolib.installers.web.bun
fn (mut site DocusaurusFactory) template_install(update bool) ! {
mut gs := gittools.new()!
mut r := gs.get_repo(url: 'https://github.com/freeflowuniverse/docusaurus_template.git',pull:update)!
mut r := gs.get_repo(
url: 'https://github.com/freeflowuniverse/docusaurus_template.git'
pull: update
)!
mut template_path := r.patho()!
for item in ['package.json', 'sidebars.ts', 'tsconfig.json'] {

8
manual/create_tag.md Normal file
View File

@@ -0,0 +1,8 @@
## how to tag a version and push
```bash
cd ~/Users/despiegk~/code/github/freeflowuniverse/herolib/README.md
git tag -a v1.0.2 -m "some message"
git add . -A ; git commit -m ... ; git pull ; git push origin v1.0.2
```