Implement most of the spec reusing scribe & nefthera code
This commit is contained in:
parent
e8b489dd6f
commit
612a98cfde
16 changed files with 225 additions and 25 deletions
13
ocaml/TODO.md
Normal file
13
ocaml/TODO.md
Normal 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
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
(executable
|
||||
(public_name tori)
|
||||
(name main)
|
||||
(libraries tori))
|
||||
(public_name tori)
|
||||
(name main)
|
||||
(libraries tori)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
4
ocaml/dune-workspace
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
(lang dune 3.17)
|
||||
(context default)
|
||||
|
||||
(env (dev (flags (:standard -w +A-40-42))))
|
||||
|
|
@ -1,2 +1,6 @@
|
|||
(library
|
||||
(name tori))
|
||||
(name tori)
|
||||
(libraries unix)
|
||||
)
|
||||
|
||||
(include_subdirs qualified)
|
||||
|
|
|
|||
23
ocaml/lib/parsers/argument.ml
Normal file
23
ocaml/lib/parsers/argument.ml
Normal 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
2
ocaml/lib/qol.ml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
let str_int = string_of_int
|
||||
let print = print_endline
|
||||
38
ocaml/lib/schema/schema.ml
Normal file
38
ocaml/lib/schema/schema.ml
Normal 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
14
ocaml/lib/system/file.ml
Normal 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
|
||||
15
ocaml/lib/system/package.ml
Normal file
15
ocaml/lib/system/package.ml
Normal 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)
|
||||
}}
|
||||
11
ocaml/lib/system/process/fork.ml
Normal file
11
ocaml/lib/system/process/fork.ml
Normal 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)
|
||||
45
ocaml/lib/system/process/reader.ml
Normal file
45
ocaml/lib/system/process/reader.ml
Normal 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
|
||||
|
|
@ -1,2 +1,4 @@
|
|||
(test
|
||||
(name test_tori))
|
||||
(name test_tori)
|
||||
(libraries tori)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 ()
|
||||
|
|
@ -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: [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue