without autoescape

This commit is contained in:
Kasper Juul Hermansen 2022-08-14 20:19:29 +02:00
parent 49089b3074
commit 319360968b
Signed by: kjuulh
GPG Key ID: 0F95C140730F2F23
17 changed files with 1083 additions and 332 deletions

557
Cargo.lock generated
View File

@ -2,6 +2,24 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "android_system_properties"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e"
dependencies = [
"libc",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
@ -44,6 +62,30 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
"generic-array",
]
[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"memchr",
]
[[package]]
name = "bumpalo"
version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
[[package]]
name = "cc"
version = "1.0.73"
@ -59,6 +101,40 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
dependencies = [
"iana-time-zone",
"num-integer",
"num-traits",
"winapi",
]
[[package]]
name = "chrono-tz"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c39203181991a7dd4343b8005bd804e7a9a37afb8ac070e43771e8c820bbde"
dependencies = [
"chrono",
"chrono-tz-build",
"phf",
]
[[package]]
name = "chrono-tz-build"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f509c3a87b33437b05e2458750a0700e5bdd6956176773e6c7d6dd15a283a0c"
dependencies = [
"parse-zoneinfo",
"phf",
"phf_codegen",
]
[[package]]
name = "clap"
version = "3.2.16"
@ -83,6 +159,41 @@ dependencies = [
"os_str_bytes",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cpufeatures"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
dependencies = [
"libc",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "ctor"
version = "0.1.23"
@ -106,11 +217,28 @@ dependencies = [
"openssl",
"serde",
"serde_yaml",
"tera",
"tracing",
"tracing-subscriber",
"walkdir",
]
[[package]]
name = "deunicode"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690"
[[package]]
name = "digest"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "dirs"
version = "4.0.0"
@ -151,6 +279,12 @@ dependencies = [
"syn",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
@ -176,6 +310,16 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "generic-array"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.7"
@ -202,6 +346,30 @@ dependencies = [
"url",
]
[[package]]
name = "globset"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
dependencies = [
"aho-corasick",
"bstr",
"fnv",
"log",
"regex",
]
[[package]]
name = "globwalk"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
dependencies = [
"bitflags",
"ignore",
"walkdir",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -217,6 +385,25 @@ dependencies = [
"libc",
]
[[package]]
name = "humansize"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026"
[[package]]
name = "iana-time-zone"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf7d67cf4a22adc5be66e75ebdf769b3f2ea032041437a7061f97a63dad4b"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"js-sys",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "idna"
version = "0.2.3"
@ -228,6 +415,24 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "ignore"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
dependencies = [
"crossbeam-utils",
"globset",
"lazy_static",
"log",
"memchr",
"regex",
"same-file",
"thread_local",
"walkdir",
"winapi-util",
]
[[package]]
name = "indexmap"
version = "1.9.1"
@ -253,6 +458,15 @@ dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -321,6 +535,31 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.13.0"
@ -388,12 +627,104 @@ version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4"
[[package]]
name = "parse-zoneinfo"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41"
dependencies = [
"regex",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pest"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69486e2b8c2d2aeb9762db7b4e00b0331156393555cff467f4163ff06821eef8"
dependencies = [
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b13570633aff33c6d22ce47dd566b10a3b9122c2fe9d8e7501895905be532b91"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3c567e5702efdc79fb18859ea74c3eb36e14c43da7b8c1f098a4ed6514ec7a0"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eb32be5ee3bbdafa8c7a18b0a8a8d962b66cfa2ceee4037f49267a50ee821fe"
dependencies = [
"once_cell",
"pest",
"sha-1",
]
[[package]]
name = "phf"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770"
dependencies = [
"phf_generator",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_shared"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
dependencies = [
"siphasher",
"uncased",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
@ -406,6 +737,12 @@ version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro2"
version = "1.0.43"
@ -424,6 +761,36 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
@ -444,6 +811,23 @@ dependencies = [
"thiserror",
]
[[package]]
name = "regex"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
[[package]]
name = "ryu"
version = "1.0.11"
@ -503,6 +887,17 @@ dependencies = [
"unsafe-libyaml",
]
[[package]]
name = "sha-1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.4"
@ -512,6 +907,21 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "siphasher"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]]
name = "slug"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373"
dependencies = [
"deunicode",
]
[[package]]
name = "smallvec"
version = "1.9.0"
@ -535,6 +945,28 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tera"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d4685e72cb35f0eb74319c8fe2d3b61e93da5609841cde2cb87fcc3bea56d20"
dependencies = [
"chrono",
"chrono-tz",
"globwalk",
"humansize",
"lazy_static",
"percent-encoding",
"pest",
"pest_derive",
"rand",
"regex",
"serde",
"serde_json",
"slug",
"unic-segment",
]
[[package]]
name = "termcolor"
version = "1.1.3"
@ -665,6 +1097,77 @@ dependencies = [
"tracing-serde",
]
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "ucd-trie"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c"
[[package]]
name = "uncased"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09b01702b0fd0b3fadcf98e098780badda8742d4f4a7676615cad90e8ac73622"
dependencies = [
"version_check",
]
[[package]]
name = "unic-char-property"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
dependencies = [
"unic-char-range",
]
[[package]]
name = "unic-char-range"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
[[package]]
name = "unic-common"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
[[package]]
name = "unic-segment"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23"
dependencies = [
"unic-ucd-segment",
]
[[package]]
name = "unic-ucd-segment"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700"
dependencies = [
"unic-char-property",
"unic-char-range",
"unic-ucd-version",
]
[[package]]
name = "unic-ucd-version"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
dependencies = [
"unic-common",
]
[[package]]
name = "unicode-bidi"
version = "0.3.8"
@ -749,6 +1252,60 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
[[package]]
name = "winapi"
version = "0.3.9"

View File

@ -18,3 +18,4 @@ tracing = "0.1.36"
tracing-subscriber = { version = "0.3.15", features = ["json"] }
log = { version = "0.4.17", features = ["std", "kv_unstable"] }
openssl = {version = "0.10", features = ["vendored"]}
tera = "1.17.0"

View File

@ -1 +1,78 @@
use std::path::PathBuf;
use crate::{
actions::shell::ShellAction,
model::{CuddleScript, CuddleShellScriptArg, CuddleVariable},
};
pub mod shell;
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct CuddleAction {
pub script: CuddleScript,
pub path: PathBuf,
pub name: String,
}
#[allow(dead_code)]
impl CuddleAction {
pub fn new(script: CuddleScript, path: PathBuf, name: String) -> Self {
Self { script, path, name }
}
pub fn execute(self, variables: Vec<CuddleVariable>) -> anyhow::Result<()> {
match self.script {
CuddleScript::Shell(s) => {
let mut arg_variables: Vec<CuddleVariable> = vec![];
if let Some(args) = s.args {
for (k, v) in args {
let var = match v {
CuddleShellScriptArg::Env(e) => {
let env_var = match std::env::var(e.key.clone()) {
Ok(var) => var,
Err(e) => {
log::error!("env_variable not found: {}", k);
return Err(anyhow::anyhow!(e));
}
};
CuddleVariable::new(k.clone(), env_var)
}
};
arg_variables.push(var);
}
} else {
arg_variables = vec![]
};
let mut vars = variables.clone();
vars.append(&mut arg_variables);
log::trace!("preparing to run action");
match ShellAction::new(
self.name.clone(),
format!(
"{}/scripts/{}.sh",
self.path
.to_str()
.expect("action doesn't have a name, this should never happen"),
self.name
),
)
.execute(vars)
{
Ok(()) => {
log::trace!("finished running action");
Ok(())
}
Err(e) => {
log::error!("{}", e);
Err(e)
}
}
}
CuddleScript::Dagger(_d) => Err(anyhow::anyhow!("not implemented yet!")),
}
}
}

View File

@ -1,6 +1,6 @@
use std::{env::current_dir, path::PathBuf, process::Command};
use crate::cli::CuddleVariable;
use crate::model::CuddleVariable;
#[allow(dead_code)]
pub struct ShellAction {

View File

@ -1,314 +0,0 @@
use std::{
env::{self, current_dir},
path::PathBuf,
sync::{Arc, Mutex},
};
use anyhow::anyhow;
use clap::Command;
use git2::Repository;
use crate::{
actions,
context::{CuddleContext, CuddleTreeType},
model::{CuddleScript, CuddleShellScriptArg},
};
#[derive(Debug, Clone)]
#[allow(dead_code)]
struct CuddleAction {
script: CuddleScript,
path: PathBuf,
name: String,
}
#[allow(dead_code)]
impl CuddleAction {
pub fn new(script: CuddleScript, path: PathBuf, name: String) -> Self {
Self { script, path, name }
}
pub fn execute(self, variables: Vec<CuddleVariable>) -> anyhow::Result<()> {
match self.script {
CuddleScript::Shell(s) => {
let mut arg_variables: Vec<CuddleVariable> = vec![];
if let Some(args) = s.args {
for (k, v) in args {
let var = match v {
CuddleShellScriptArg::Env(e) => {
let env_var = match env::var(e.key.clone()) {
Ok(var) => var,
Err(e) => {
log::error!("env_variable not found: {}", k);
return Err(anyhow::anyhow!(e));
}
};
CuddleVariable::new(k.clone(), env_var)
}
};
arg_variables.push(var);
}
} else {
arg_variables = vec![]
};
let mut vars = variables.clone();
vars.append(&mut arg_variables);
log::trace!("preparing to run action");
match actions::shell::ShellAction::new(
self.name.clone(),
format!(
"{}/scripts/{}.sh",
self.path
.to_str()
.expect("action doesn't have a name, this should never happen"),
self.name
),
)
.execute(vars)
{
Ok(()) => {
log::trace!("finished running action");
Ok(())
}
Err(e) => {
log::error!("{}", e);
Err(e)
}
}
}
CuddleScript::Dagger(_d) => Err(anyhow::anyhow!("not implemented yet!")),
}
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct CuddleVariable {
pub name: String,
pub value: String,
}
impl CuddleVariable {
pub fn new(name: String, value: String) -> Self {
Self { name, value }
}
}
#[derive(Debug)]
struct GitCommit {
commit_sha: String,
}
impl GitCommit {
fn new() -> anyhow::Result<GitCommit> {
let repo = Repository::open(current_dir().expect("having current_dir available")).map_err(
|e| {
log::debug!("{}", e);
anyhow::anyhow!("could not open repository")
},
)?;
let head_ref = repo
.head()
.map_err(|e| {
log::warn!("{}", e);
anyhow::anyhow!("could not get HEAD")
})?
.target()
.ok_or(anyhow::anyhow!(
"could not extract head -> target to commit_sha"
))?;
Ok(Self {
commit_sha: head_ref.to_string(),
})
}
}
#[derive(Debug, Clone)]
pub struct CuddleCli<'a> {
scripts: Vec<CuddleAction>,
variables: Vec<CuddleVariable>,
context: Arc<Mutex<Vec<CuddleContext>>>,
command: Option<Command<'a>>,
tmp_dir: Option<PathBuf>,
}
impl<'a> CuddleCli<'a> {
pub fn new(context: Arc<Mutex<Vec<CuddleContext>>>) -> anyhow::Result<CuddleCli<'a>> {
let mut cli = CuddleCli {
scripts: vec![],
variables: vec![],
context: context.clone(),
command: None,
tmp_dir: None,
};
cli = cli
.process_variables()
.process_scripts()
.process_templates()?
.build_cli();
Ok(cli)
}
fn process_variables(mut self) -> Self {
if let Ok(context_iter) = self.context.clone().lock() {
for ctx in context_iter.iter() {
if let Some(variables) = ctx.plan.vars.clone() {
for (name, var) in variables {
self.variables.push(CuddleVariable::new(name, var))
}
}
if let CuddleTreeType::Root = ctx.node_type {
let mut temp_path = ctx.path.clone();
temp_path.push(".cuddle/tmp/");
self.variables.push(CuddleVariable::new(
"tmp".into(),
temp_path.clone().to_string_lossy().to_string(),
));
self.tmp_dir = Some(temp_path);
}
}
}
match GitCommit::new() {
Ok(commit) => self.variables.push(CuddleVariable::new(
"commit_sha".into(),
commit.commit_sha.clone(),
)),
Err(e) => {
log::debug!("{}", e);
}
}
self
}
fn process_scripts(mut self) -> Self {
if let Ok(context_iter) = self.context.clone().lock() {
for ctx in context_iter.iter() {
if let Some(scripts) = ctx.plan.scripts.clone() {
for (name, script) in scripts {
self.scripts
.push(CuddleAction::new(script.clone(), ctx.path.clone(), name))
}
}
}
}
self
}
fn process_templates(self) -> anyhow::Result<Self> {
if let None = self.tmp_dir {
log::debug!("cannot process template as bare bones cli");
return Ok(self);
}
// Make sure tmp_dir exists and clean it up first
let tmp_dir = self
.tmp_dir
.clone()
.ok_or(anyhow::anyhow!("tmp_dir does not exist aborting"))?;
if tmp_dir.exists() && tmp_dir.ends_with("tmp") {
std::fs::remove_dir_all(tmp_dir.clone())?;
}
std::fs::create_dir_all(tmp_dir.clone())?;
// Handle all templating with variables and such.
// TODO: use actual templating engine, for new we just copy templates to the final folder
if let Ok(context_iter) = self.context.clone().lock() {
for ctx in context_iter.iter() {
let mut template_path = ctx.path.clone();
template_path.push("templates");
log::trace!("template path: {}", template_path.clone().to_string_lossy());
if !template_path.exists() {
continue;
}
for file in std::fs::read_dir(template_path)?.into_iter() {
let f = file?;
let mut dest_file = tmp_dir.clone();
dest_file.push(f.file_name());
std::fs::copy(f.path(), dest_file)?;
}
}
}
Ok(self)
}
fn build_cli(mut self) -> Self {
let mut root_cmd = Command::new("cuddle")
.version("1.0")
.author("kjuulh <contact@kasperhermansen.com>")
.about("cuddle is your domain specific organization tool. It enabled widespread sharing through repositories, as well as collaborating while maintaining speed and integrity")
.subcommand_required(true)
.propagate_version(true);
if self.scripts.len() > 0 {
let mut execute_cmd = Command::new("x").about("x is your entry into your domains scripts, scripts inherited from parents will also be present here").subcommand_required(true);
for script in self.scripts.iter() {
let action_cmd = Command::new(script.name.clone());
// TODO: Some way to add an about for clap, requires conversion from String -> &str
execute_cmd = execute_cmd.subcommand(action_cmd);
}
root_cmd = root_cmd.subcommand(execute_cmd);
}
self.command = Some(root_cmd);
self
}
pub fn execute(self) -> anyhow::Result<Self> {
if let Some(mut cli) = self.command.clone() {
let matches = cli.clone().get_matches();
let res = match matches.subcommand() {
Some(("x", exe_submatch)) => {
log::trace!("executing x");
match exe_submatch.subcommand() {
Some((name, _action_matches)) => {
log::trace!(action=name; "running action; name={}", name);
match self.scripts.iter().find(|ele| ele.name == name) {
Some(script) => {
script.clone().execute(self.variables.clone())?;
Ok(())
}
_ => Err(anyhow!("could not find a match")),
}
}
_ => Err(anyhow!("could not find a match")),
}
}
_ => Err(anyhow!("could not find a match")),
};
match res {
Ok(()) => {}
Err(e) => {
let _ = cli.print_long_help();
return Err(e);
}
}
}
Ok(self)
}
}

182
cuddle_cli/src/cli/mod.rs Normal file
View File

@ -0,0 +1,182 @@
mod subcommands;
use std::{
path::PathBuf,
sync::{Arc, Mutex},
};
use clap::Command;
use crate::{
actions::CuddleAction,
context::{CuddleContext, CuddleTreeType},
model::*,
util::git::GitCommit,
};
use self::subcommands::render_template::RenderTemplateCommand;
#[derive(Debug, Clone)]
pub struct CuddleCli<'a> {
scripts: Vec<CuddleAction>,
variables: Vec<CuddleVariable>,
context: Arc<Mutex<Vec<CuddleContext>>>,
command: Option<Command<'a>>,
tmp_dir: Option<PathBuf>,
}
impl<'a> CuddleCli<'a> {
pub fn new(context: Arc<Mutex<Vec<CuddleContext>>>) -> anyhow::Result<CuddleCli<'a>> {
let mut cli = CuddleCli {
scripts: vec![],
variables: vec![],
context: context.clone(),
command: None,
tmp_dir: None,
};
cli = cli
.process_variables()
.process_scripts()
.process_templates()?
.build_cli();
Ok(cli)
}
fn process_variables(mut self) -> Self {
if let Ok(context_iter) = self.context.clone().lock() {
for ctx in context_iter.iter() {
if let Some(variables) = ctx.plan.vars.clone() {
for (name, var) in variables {
self.variables.push(CuddleVariable::new(name, var))
}
}
if let CuddleTreeType::Root = ctx.node_type {
let mut temp_path = ctx.path.clone();
temp_path.push(".cuddle/tmp");
self.variables.push(CuddleVariable::new(
"tmp".into(),
temp_path.clone().to_string_lossy().to_string(),
));
self.tmp_dir = Some(temp_path);
}
}
}
match GitCommit::new() {
Ok(commit) => self.variables.push(CuddleVariable::new(
"commit_sha".into(),
commit.commit_sha.clone(),
)),
Err(e) => {
log::debug!("{}", e);
}
}
self
}
fn process_scripts(mut self) -> Self {
if let Ok(context_iter) = self.context.clone().lock() {
for ctx in context_iter.iter() {
if let Some(scripts) = ctx.plan.scripts.clone() {
for (name, script) in scripts {
self.scripts
.push(CuddleAction::new(script.clone(), ctx.path.clone(), name))
}
}
}
}
self
}
fn process_templates(self) -> anyhow::Result<Self> {
if let None = self.tmp_dir {
log::debug!("cannot process template as bare bones cli");
return Ok(self);
}
// Make sure tmp_dir exists and clean it up first
let tmp_dir = self
.tmp_dir
.clone()
.ok_or(anyhow::anyhow!("tmp_dir does not exist aborting"))?;
if tmp_dir.exists() && tmp_dir.ends_with("tmp") {
std::fs::remove_dir_all(tmp_dir.clone())?;
}
std::fs::create_dir_all(tmp_dir.clone())?;
// Handle all templating with variables and such.
// TODO: use actual templating engine, for new we just copy templates to the final folder
if let Ok(context_iter) = self.context.clone().lock() {
for ctx in context_iter.iter() {
let mut template_path = ctx.path.clone();
template_path.push("templates");
log::trace!("template path: {}", template_path.clone().to_string_lossy());
if !template_path.exists() {
continue;
}
for file in std::fs::read_dir(template_path)?.into_iter() {
let f = file?;
let mut dest_file = tmp_dir.clone();
dest_file.push(f.file_name());
std::fs::copy(f.path(), dest_file)?;
}
}
}
Ok(self)
}
fn build_cli(mut self) -> Self {
let mut root_cmd = Command::new("cuddle")
.version("1.0")
.author("kjuulh <contact@kasperhermansen.com>")
.about("cuddle is your domain specific organization tool. It enabled widespread sharing through repositories, as well as collaborating while maintaining speed and integrity")
.subcommand_required(true)
.arg_required_else_help(true)
.propagate_version(true);
root_cmd = subcommands::x::build_command(root_cmd, self.clone());
root_cmd = subcommands::render_template::build_command(root_cmd);
self.command = Some(root_cmd);
self
}
pub fn execute(self) -> anyhow::Result<Self> {
if let Some(mut cli) = self.command.clone() {
let matches = cli.clone().get_matches();
let res = match matches.subcommand() {
Some(("x", exe_submatch)) => subcommands::x::execute_x(exe_submatch, self.clone()),
Some(("render_template", sub_matches)) => {
RenderTemplateCommand::from_matches(sub_matches, self.clone())
.and_then(|cmd| cmd.execute())?;
Ok(())
}
_ => Err(anyhow::anyhow!("could not find a match")),
};
match res {
Ok(()) => {}
Err(e) => {
let _ = cli.print_long_help();
return Err(e);
}
}
}
Ok(self)
}
}

View File

@ -0,0 +1,2 @@
pub mod render_template;
pub mod x;

View File

@ -0,0 +1,150 @@
use std::{path::PathBuf, str::FromStr};
use clap::{Arg, ArgMatches, Command};
use crate::{cli::CuddleCli, model::CuddleVariable};
pub fn build_command<'a>(root_cmd: Command<'a>) -> Command<'a> {
root_cmd.subcommand(
Command::new("render_template")
.about("renders a jinja compatible template")
.args(&[
Arg::new("template-file")
.alias("template")
.short('t')
.long("template-file")
.required(true)
.action(clap::ArgAction::Set).long_help("template-file is the input file path of the .tmpl file (or inferred) that you would like to render"),
Arg::new("destination")
.alias("dest")
.short('d')
.long("destination")
.required(true)
.action(clap::ArgAction::Set)
.long_help("destination is the output path of the template once done, but default .tmpl is stripped and the normal file extension is used. this can be overwritten if a file path is entered instead. I.e. (/some/file/name.txt)"),
Arg::new("extra-var")
.long("extra-var")
.required(false)
.action(clap::ArgAction::Set),
]))
}
pub struct RenderTemplateCommand {
variables: Vec<CuddleVariable>,
template_file: PathBuf,
destination: PathBuf,
}
impl RenderTemplateCommand {
pub fn from_matches(matches: &ArgMatches, cli: CuddleCli) -> anyhow::Result<Self> {
let template_file = matches
.get_one::<String>("template-file")
.ok_or(anyhow::anyhow!("template-file was not found"))
.and_then(get_path_buf_and_check_exists)?;
let destination = matches
.get_one::<String>("destination")
.ok_or(anyhow::anyhow!("destination was not found"))
.and_then(get_path_buf_and_check_dir_exists)
.and_then(RenderTemplateCommand::transform_extension)?;
let mut extra_vars: Vec<CuddleVariable> =
if let Some(extra_vars) = matches.get_many::<String>("extra-var") {
let mut vars = Vec::with_capacity(extra_vars.len());
for var in extra_vars.into_iter() {
let parts: Vec<&str> = var.split("=").collect();
if parts.len() != 2 {
return Err(anyhow::anyhow!("extra-var: is not set correctly: {}", var));
}
vars.push(CuddleVariable::new(parts[0].into(), parts[1].into()));
}
vars
} else {
vec![]
};
extra_vars.append(&mut cli.variables.clone());
Ok(Self {
variables: extra_vars,
template_file,
destination,
})
}
pub fn execute(self) -> anyhow::Result<()> {
// Prepare context
let mut context = tera::Context::new();
for var in self.variables {
context.insert(
var.name.to_lowercase().replace(" ", "_").replace("-", "_"),
&var.value,
)
}
// Load source template
let source = std::fs::read_to_string(self.template_file)?;
let output = tera::Tera::one_off(source.as_str(), &context, false)?;
// Put template in final destination
std::fs::write(&self.destination, output)?;
log::info!(
"finished writing template to: {}",
&self.destination.to_string_lossy()
);
Ok(())
}
fn transform_extension(template_path: PathBuf) -> anyhow::Result<PathBuf> {
if template_path.is_file() {
let ext = template_path.extension().ok_or(anyhow::anyhow!(
"destination path does not have an extension"
))?;
if ext.to_string_lossy().ends_with("tmpl") {
let template_dest = template_path
.to_str()
.and_then(|s| s.strip_suffix(".tmpl"))
.ok_or(anyhow::anyhow!("string does not end in .tmpl"))?;
return PathBuf::from_str(template_dest).map_err(|e| anyhow::anyhow!(e));
}
}
Ok(template_path)
}
}
fn get_path_buf_and_check_exists(raw_path: &String) -> anyhow::Result<PathBuf> {
match PathBuf::from_str(&raw_path) {
Ok(pb) => {
if pb.exists() {
Ok(pb)
} else {
Err(anyhow::anyhow!(
"path: {}, could not be found",
pb.to_string_lossy()
))
}
}
Err(e) => Err(anyhow::anyhow!(e)),
}
}
fn get_path_buf_and_check_dir_exists(raw_path: &String) -> anyhow::Result<PathBuf> {
match PathBuf::from_str(&raw_path) {
Ok(pb) => {
if pb.is_dir() && pb.exists() {
Ok(pb)
} else if pb.is_file() {
Ok(pb)
} else {
Ok(pb)
}
}
Err(e) => Err(anyhow::anyhow!(e)),
}
}

View File

@ -0,0 +1,37 @@
use clap::{ArgMatches, Command};
use crate::cli::CuddleCli;
pub fn build_command<'a>(root_cmd: Command<'a>, cli: CuddleCli<'a>) -> Command<'a> {
if cli.scripts.len() > 0 {
let mut execute_cmd = Command::new("x").about("x is your entry into your domains scripts, scripts inherited from parents will also be present here").subcommand_required(true);
for script in cli.scripts.iter() {
let action_cmd = Command::new(script.name.clone());
// TODO: Some way to add an about for clap, requires conversion from String -> &str
execute_cmd = execute_cmd.subcommand(action_cmd);
}
root_cmd.subcommand(execute_cmd)
} else {
root_cmd
}
}
pub fn execute_x(exe_submatch: &ArgMatches, cli: CuddleCli) -> anyhow::Result<()> {
match exe_submatch.subcommand() {
Some((name, _action_matches)) => {
log::trace!(action=name; "running action; name={}", name);
match cli.scripts.iter().find(|ele| ele.name == name) {
Some(script) => {
script.clone().execute(cli.variables.clone())?;
Ok(())
}
_ => Err(anyhow::anyhow!("could not find a match")),
}
}
_ => Err(anyhow::anyhow!("could not find a match")),
}
}

View File

@ -3,6 +3,7 @@ use envconfig::Envconfig;
pub enum CuddleFetchPolicy {
Always,
Once,
Never,
}
#[derive(Envconfig, Clone)]
@ -20,6 +21,7 @@ impl CuddleConfig {
match self.fetch_policy.clone().to_lowercase().as_str() {
"always" => Ok(CuddleFetchPolicy::Always),
"once" => Ok(CuddleFetchPolicy::Once),
"never" => Ok(CuddleFetchPolicy::Never),
_ => Err(anyhow::anyhow!("could not parse fetch policy")),
}
}

View File

@ -1,8 +1,3 @@
use std::{
env::current_dir,
sync::{Arc, Mutex},
};
use config::CuddleConfig;
use tracing::Level;
@ -11,23 +6,15 @@ mod cli;
mod config;
mod context;
mod model;
mod util;
fn main() -> anyhow::Result<()> {
init_logging()?;
let config = CuddleConfig::from_env()?;
match git2::Repository::open(current_dir()?) {
Ok(_) => {
let context = context::extract_cuddle(config.clone())?;
_ = cli::CuddleCli::new(context)?.execute()?;
}
Err(_) => {
// Only build bare bones cli
log::info!("was not opened in a repo with git, only showing bare-bones options");
_ = cli::CuddleCli::new(Arc::new(Mutex::new(vec![])))?.execute()?;
}
}
let context = context::extract_cuddle(config.clone())?;
_ = cli::CuddleCli::new(context)?.execute()?;
Ok(())
}
@ -35,7 +22,7 @@ fn main() -> anyhow::Result<()> {
fn init_logging() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.pretty()
.with_max_level(Level::DEBUG)
.with_max_level(Level::INFO)
.init();
Ok(())

View File

@ -46,3 +46,16 @@ pub struct CuddlePlan {
pub vars: Option<HashMap<String, String>>,
pub scripts: Option<HashMap<String, CuddleScript>>,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct CuddleVariable {
pub name: String,
pub value: String,
}
impl CuddleVariable {
pub fn new(name: String, value: String) -> Self {
Self { name, value }
}
}

View File

@ -0,0 +1,33 @@
use std::env::current_dir;
use git2::Repository;
#[derive(Debug)]
pub struct GitCommit {
pub commit_sha: String,
}
impl GitCommit {
pub fn new() -> anyhow::Result<GitCommit> {
let repo = Repository::open(current_dir().expect("having current_dir available")).map_err(
|e| {
log::debug!("{}", e);
anyhow::anyhow!("could not open repository")
},
)?;
let head_ref = repo
.head()
.map_err(|e| {
log::warn!("{}", e);
anyhow::anyhow!("could not get HEAD")
})?
.target()
.ok_or(anyhow::anyhow!(
"could not extract head -> target to commit_sha"
))?;
Ok(Self {
commit_sha: head_ref.to_string(),
})
}
}

View File

@ -0,0 +1 @@
pub mod git;

View File

@ -0,0 +1,14 @@
# yaml-language-server: $schema=../../schemas/base.json
base: false
vars:
service: "some-service"
scripts:
test_render_template:
type: shell
args:
extravar:
type: env
key: "HOME"

View File

@ -0,0 +1,6 @@
#!/bin/bash
CUDDLE_FETCH_POLICY=never cuddle_cli render_template \
--template-file "$TMP/input.txt.tmpl" \
--destination "$TMP/input.txt" \
--extra-var "extravar=someextravar"

View File

@ -0,0 +1,3 @@
some {{ service }} name
- {{ extravar }}