Implement most of the spec reusing scribe & nefthera code

This commit is contained in:
Juno Takano 2025-04-09 01:59:32 -03:00
commit 612a98cfde
16 changed files with 225 additions and 25 deletions

13
ocaml/TODO.md Normal file
View file

@ -0,0 +1,13 @@
- [ ] Match test coverage with spec requirements
- [ ] Simplify Reader
- [ ] Create interface files
- [ ] Move comment on top of Parsers.Argument.say to the interface doc file
- [ ] Try out doc generation
- [ ] Simplify and analyze System.File
- [ ] Add log function and match spec
- [ ] Output begins with ' [log] '
- [ ] Only prints if DEBUG is set
- [ ] Get su command from $XDG_CONFIG_HOME/tori/tori.conf and use it for pkg
- [ ] Check if working
- [ ] Command 'user': print the output of 'whoami'
- [ ] Unrecognized command: exit code 1

View file

@ -1,4 +1,5 @@
(executable
(public_name tori)
(name main)
(libraries tori))
(public_name tori)
(name main)
(libraries tori)
)

View file

@ -1 +1,6 @@
let () = print_endline "Hello, OCaml!"
let () =
match Array.to_list Sys.argv with
| _ :: tail ->
let future = (Tori.Parsers.Argument.interpret Tori.Schema.seed tail) in
if future.output.message <> "" then print_endline future.output.message
| [] -> assert false

View file

@ -3,25 +3,28 @@
(name tori)
(version 0.8.0)
(generate_opam_files true)
(homepage https://tori.jutty.dev)
(source (uri git+https://brew.bsd.cafe/tori/tori.git))
(authors "Juno Takano <juno@jutty.dev>")
(maintainers "Juno Takano <juno@jutty.dev>")
(license GPL-3.0-only)
(documentation https://tori.jutty.dev/docs)
(package
(name tori)
(synopsis "Track system configurations and replicate them")
(description "tori lets you define several characteristics of a unix system and track how they change along time, tracking how they changed and choosing whether or not to commit them. It aims for portability and declarative configuration practices, meaning you tell it what your system is, not how to reproduce it.")
(depends ocaml)
(tags
("operating systems" "unix" "configuration management")))
(name tori)
(synopsis "Track system configurations and replicate them")
(description
"\> tori lets you define several characteristics of a unix system and
"\> track changes happen along time, allowing you to choose whether or
"\> not to commit. It aims for portability and declarative configuration
"\> practices, meaning you tell it what your system is, not how to
"\> reproduce it.
)
(tags ("operating systems" "unix" "configuration management"))
(depends
(ocaml (>= 5.3.0))
(dune (>= 3.17.2))
)
)
; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html
(generate_opam_files true)

4
ocaml/dune-workspace Normal file
View file

@ -0,0 +1,4 @@
(lang dune 3.17)
(context default)
(env (dev (flags (:standard -w +A-40-42))))

View file

@ -1,2 +1,6 @@
(library
(name tori))
(name tori)
(libraries unix)
)
(include_subdirs qualified)

View file

@ -0,0 +1,23 @@
let interpret (past: Schema.schema) (input: string list): Schema.schema =
let future = { past with output = { message = "" } } in
(* say is useful when the only change to future is the output message *)
let say (message: string) =
{ future with output = { message = message } } in
(*
TODO: return a schema with orders, instead of calling side-effects
directly, making this more of a parser and less of a glorified switch
*)
match input with
| "pkg" :: tail -> System.Package.merge past tail
| "os" :: _ -> say (System.File.read "/etc/os-release")
| "host" :: _ -> say (System.Process.Reader.read [||] "hostname").output
| "echo" :: tail -> say (String.concat " " tail)
| ("version" | "-v" | "--version") :: _ ->
say (Schema.format_version future.meta.version)
| ("help" | "-h" | "--help") :: _ -> say future.meta.help.long
| head :: _ ->
say ("Unrecognized command: " ^ head ^ "\n" ^ future.meta.help.short)
| _ -> future

2
ocaml/lib/qol.ml Normal file
View file

@ -0,0 +1,2 @@
let str_int = string_of_int
let print = print_endline

View file

@ -0,0 +1,38 @@
open Qol
type version = { major: int; minor: int; patch: int }
type help = { short: string; long: string }
type meta = { version: version; help: help }
type output = { message: string; }
type os = Unknown | FreeBSD | Void | Alpine
type host = { os: os; name: string; }
type schema = { meta: meta; output: output; host: host; }
let seed: schema = {
meta = {
version = {
major = 0;
minor = 8;
patch = 0;
};
help = {
short = "Use 'tori help' for usage instructions";
long = "<help message>";
};
};
output = {
message = "Use command 'help' for help";
};
host = {
os = Unknown;
name = "Unknown Host";
};
}
let format_version (version: version): string =
"v" ^ str_int version.major ^
"." ^ str_int version.minor ^
"." ^ str_int version.patch

14
ocaml/lib/system/file.ml Normal file
View file

@ -0,0 +1,14 @@
let read_channel channel =
let buffer = Buffer.create 4096 in
let rec read () =
let line = input_line channel in
Buffer.add_string buffer line;
Buffer.add_char buffer '\n';
read ()
in
try read () with
End_of_file -> Buffer.contents buffer
let read path =
let channel = open_in path in
read_channel channel

View file

@ -0,0 +1,15 @@
let merge (schema: Schema.schema) (packages: string list) =
match packages with
| [] -> { schema with output = { message = "No packages provided" } }
| _ ->
let in_targets = List.flatten [["doas"; "apk"; "-i"; "add"]; packages] and
out_targets = List.flatten [["doas"; "apk"; "-i"; "del"]; packages] in
Process.Fork.run "doas" in_targets;
Process.Fork.run "doas" out_targets;
{ schema with output = {
message = "Done: " ^ (String.concat "\n" packages)
}}

View file

@ -0,0 +1,11 @@
open Qol
let run (command: string) (arguments: string list) =
match Unix.fork () with
| 0 -> Unix.execvp command (Array.of_list arguments)
| pid -> let (_, status) = Unix.waitpid [] pid in
match status with
| Unix.WEXITED 0 -> ()
| Unix.WEXITED n -> print ("Process exited with code " ^ str_int n)
| Unix.WSIGNALED n -> print ("Process terminated by signal " ^ str_int n)
| Unix.WSTOPPED n -> print ("Process stopped by signal " ^ str_int n)

View file

@ -0,0 +1,45 @@
open Qol
type output = { output: string; error: string; status: string; }
let handle_exit_status (status: Unix.process_status): string =
match status with
| Unix.WEXITED n -> "Exit " ^ str_int n
| Unix.WSIGNALED n -> "Kill " ^ str_int n
| Unix.WSTOPPED n -> "Stopped " ^ str_int n
let read (env: string array) (command: string): output =
let stdout, stdin, stderr = Unix.open_process_full command env in
let in_buffer = Buffer.create 4096 in
let err_buffer = Buffer.create 4096 in
let rec read_in () =
let in_line = input_line stdout in
Buffer.add_string in_buffer in_line;
Buffer.add_char in_buffer '\n';
read_in ()
in
try read_in () with End_of_file -> ();
let rec read_err () =
let err_line = input_line stderr in
Buffer.add_string err_buffer err_line;
Buffer.add_char err_buffer '\n';
read_err ()
in
try read_err () with
End_of_file -> let exit_status =
handle_exit_status (Unix.close_process_full (stdout, stdin, stderr))
in
{
output = String.trim (Buffer.contents in_buffer);
error = Buffer.contents err_buffer;
status = exit_status;
}
let format (output: output): string =
match output with
| { output = o; error = _; status = "Exit 0" } -> o
| { output = ""; error = e; status = s } -> "[" ^ s ^ "]" ^ " " ^ e
| { output = o; error = _; status = s } -> "[" ^ s ^ "]" ^ " " ^ o

View file

@ -1,2 +1,4 @@
(test
(name test_tori))
(name test_tori)
(libraries tori)
)

View file

@ -0,0 +1,15 @@
module Reader = Tori.System.Process.Reader
module File = Tori.System.File
let smoke () =
(* Executing echo should return the same string on output *)
let result = Reader.read [||] "echo 0x70121" in
assert (Reader.format result = "0x70121");
(* Reading a file, relying on Dune's directory structure *)
let file_contents = File.read "../tori.opam" in
let contents_list = String.split_on_char '\n' file_contents in
assert (List.mem "depends: [" contents_list)
let () = smoke ()

View file

@ -1,18 +1,23 @@
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
version: "0.8.0"
synopsis: "A tool to track system configurations and replicate them."
description:
"tori lets you define several characteristics of a unix system and track how they change along time, tracking how they changed and choosing whether or not to commit them. It aims for portability and declarative configuration practices, meaning you tell it what your system is, not how to reproduce it."
synopsis: "Track system configurations and replicate them"
description: """
tori lets you define several characteristics of a unix system and
track changes happen along time, allowing you to choose whether or
not to commit. It aims for portability and declarative configuration
practices, meaning you tell it what your system is, not how to
reproduce it.
"""
maintainer: ["Juno Takano <juno@jutty.dev>"]
authors: ["Juno Takano <juno@jutty.dev>"]
license: "GPL-3.5-only"
license: "GPL-3.0-only"
tags: ["operating systems" "unix" "configuration management"]
homepage: "https://tori.jutty.dev"
doc: "https://tori.jutty.dev/docs"
depends: [
"dune" {>= "3.17"}
"ocaml"
"ocaml" {>= "5.3.0"}
"dune" {>= "3.17" & >= "3.17.2"}
"odoc" {with-doc}
]
build: [