azubi 0.1.0.1 → 0.2.0.0
raw patch · 24 files changed
+989/−889 lines, 24 filesdep +Diffdep +directorydep +hspecPVP ok
version bump matches the API change (PVP)
Dependencies added: Diff, directory, hspec, process, unix
API changes (from Hackage documentation)
- Azubi: (!) :: (Revertable a) => [(a -> [Command])] -> (a -> [Command]) -> [(a -> [Command])]
- Azubi: (!?!) :: (Revertable a) => [(a -> [Command])] -> (a -> [Command]) -> [(a -> [Command])]
- Azubi: (!?&) :: (Revertable a) => [(a -> [Command])] -> (a -> [Command]) -> [(a -> [Command])]
- Azubi: (&?!) :: (Revertable a) => [(a -> [Command])] -> (a -> [Command]) -> [(a -> [Command])]
- Azubi: (&?&) :: (Revertable a) => [(a -> [Command])] -> (a -> [Command]) -> [(a -> [Command])]
- Azubi: Branch :: String -> GitOptions
- Azubi: Directory :: String -> File
- Azubi: File :: String -> File
- Azubi: Gentoo :: Gentoo
- Azubi: GentooRevert :: Gentoo
- Azubi: Recursive :: GitOptions
- Azubi: Symlink :: String -> String -> File
- Azubi: [folder] :: Repository -> String
- Azubi: [options] :: Repository -> [GitOptions]
- Azubi: [path] :: File -> String
- Azubi: [repo] :: Repository -> String
- Azubi: [target] :: File -> String
- Azubi: azubiConfig :: (Context a) => a -> [(a -> [Command])] -> [Command]
- Azubi: azubiLogo :: [String]
- Azubi: contains :: (Revertable a) => File -> [String] -> a -> [Command]
- Azubi: data File
- Azubi: data Gentoo
- Azubi: data GitOptions
- Azubi: data Repository
- Azubi: exists :: (Existance a, Revertable b) => a -> b -> [Command]
- Azubi: instance GHC.Enum.Bounded Azubi.UserConfiguration
- Azubi: instance GHC.Enum.Enum Azubi.UserConfiguration
- Azubi: instance GHC.Show.Show Azubi.UserConfiguration
- Azubi: instance Options.Options Azubi.AzubiOptions
- Azubi: type AzubiConfig = [Command]
+ Azubi: Always :: String -> [Argument] -> RunCommand
+ Azubi: Ebuild :: String -> Ebuild
+ Azubi: Once :: String -> [Argument] -> Path -> RunCommand
+ Azubi: State :: [Check] -> [Command] -> (Maybe Comment) -> State
+ Azubi: States :: [Check] -> [State] -> (Maybe Comment) -> State
+ Azubi: class Installable a
+ Azubi: class Updatable a
+ Azubi: content :: Path -> [String] -> State
+ Azubi: data Ebuild
+ Azubi: data Git
+ Azubi: data RunCommand
+ Azubi: data State
+ Azubi: folderExists :: Path -> State
+ Azubi: link :: Path -> Path -> State
+ Azubi: uptodate :: Updatable a => a -> State
- Azubi: (&) :: (Context a) => [(a -> [Command])] -> (a -> [Command]) -> [(a -> [Command])]
+ Azubi: (&) :: [State] -> State -> [State]
- Azubi: Git :: String -> String -> [GitOptions] -> Repository
+ Azubi: Git :: RepoUrl -> Path -> Git
- Azubi: azubiMain :: [Command] -> IO ()
+ Azubi: azubiMain :: [State] -> IO ()
- Azubi: installed :: Installed a => Package -> a -> [Command]
+ Azubi: installed :: Installable a => a -> State
- Azubi: requires :: (Context a, Revertable a) => (a -> [Command]) -> (a -> [Command]) -> a -> [Command]
+ Azubi: requires :: State -> State -> State
- Azubi: run :: (Context a) => String -> Maybe File -> a -> [Command]
+ Azubi: run :: RunCommand -> State
- Azubi: submodule :: (Context a) => [(a -> [Command])] -> a -> [Command]
+ Azubi: submodule :: [State] -> State
Files
- README.md +13/−186
- azubi.cabal +57/−16
- src/Azubi.hs +42/−116
- src/Azubi/Commands/Existance.hs +0/−18
- src/Azubi/Commands/File.hs +0/−92
- src/Azubi/Commands/Git.hs +0/−47
- src/Azubi/Commands/Install.hs +0/−40
- src/Azubi/Commands/Run.hs +0/−33
- src/Azubi/Core/Boot.hs +92/−0
- src/Azubi/Core/Command.hs +0/−23
- src/Azubi/Core/Context.hs +0/−16
- src/Azubi/Core/Model.hs +164/−0
- src/Azubi/Core/Provision.hs +0/−25
- src/Azubi/Core/Revertable.hs +0/−36
- src/Azubi/Core/StateExecutor.hs +85/−0
- src/Azubi/Core/StateExecutors/LocalUnixStateExecutor.hs +226/−0
- src/Azubi/Core/Syntax.hs +0/−78
- src/Azubi/Module/Installable.hs +130/−0
- src/Azubi/Module/Runable.hs +53/−0
- src/Azubi/Render/BashScript.hs +0/−123
- src/Azubi/Render/Warez.hs +0/−12
- src/Azubi/Syntax.hs +109/−0
- src/Azubi/Systems/Gentoo.hs +0/−28
- test/Test.hs +18/−0
README.md view
@@ -1,6 +1,9 @@ # Azubi [](https://travis-ci.org/mrVanDalo/azubi)+[](https://github.com/mrVanDalo/azubi/releases/tag/0.1.0.1)+[](https://www.gnu.org/licenses/gpl-3.0.en.html)+[](https://gitter.im/azubi-configuration/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Is a very simple DevOps tool, which will never "reach" enterprise level. @@ -8,23 +11,11 @@ * Readable -> Haskell * Check your rule set before changing your system -> Strong Type-system of Haskell-* Adaptive, -> can run on all kinds of Linux and it is also planed to run on osx and Windows+* Adaptive -> can run on all kinds of Linux and it is also planed to run on osx and Windows * Lightweight -> No installation (except some basic shell tools) needed on the target host. -## Features -### Different types of Execution--You can --* enforce everything by command line (not yet)-* create a bash script which you can run for system setup-* use ssh to setup a target host (not yet)-* create different configurations for different situations (not yet)-* export to a Dockerfile (not yet)-* export to a Bat file (not yet)--### How to start+## How to start Install `azubi` via cabal. @@ -37,8 +28,12 @@ import Azubi main :: IO ()- main = azubiMain $ azubiConfig Gentoo $ []- & installed "vim"+ main = azubiMain $ []+ & installed (Ebuild "vim")+ & uptodate (Git "git@github.com:mrVanDalo\/azubi.git" "\/dev\/shm\/azubi")+ & installed (Git "git@github.com:mrVanDalo\/azubi-config.git" "\/dev\/shm\/azubi-config")+ & run (Always "touch" ["/dev/shm/run.test"])+ & link "\/dev\/shm\/azubi.link" "\/dev\/shm\/azubi" call the script to get a help@@ -46,174 +41,6 @@ ./config.hs --help -call the script to get a bashscript-- ./config.hs --output "my-first-azubi-script.sh"---# Syntax--## Commands--Every Command should be revertable.--### installed--#### Package --install vim if not already done:-- & installed "vim"--uninstall vim if vim is installed:-- ! installed "vim"--### exists--#### Files--create files, directories and symlinks : -- & exists (File "~/.vimrc")- & exists (Directory "~/.vim")- & exists (Symlink ".bashrc" "~/.bashrc.d/bashrc")- -delete files files, directories and symlinks : -- & exists (File "~/.vimrc")- & exists (Directory "~/.vim")- & exists (Symlink ".bashrc" "~/.bashrc.d/bashrc")--#### Git projects--pull git repository if not pressent:-- & exists (Git "git@github.com:mrVanDalo/azubi.git" "~/develop/azubi" [Branch "develop"])--you can give it options--* *Branch "branchname"*-* *Recursive*-* more to follow ... ---## Logic Components---### Combiner--#### &, !--Almost every command should be revertable. So you have 2 states--* do it -> `&`-* undo it -> `!`--this ensures the file `/tmp/foo` exist-- & exists (File "/tmp/foo")--and this ensures the file does not exist-- ! exists (File "/tmp/foo")--In most cases it happens what you expect, but sometimes it's not so obvious so we it is written right next to the command.-(e.g.:) `! exists (File "/tmp/foo")` will delete a file `/tmp/foo` but won't delete a directory `/tmp/foo`--#### !?&,!?!, &?&, &?!--They are special cases of `!` and `&` and should be read like `X if in context Y` -> `X?Y` and start only to make -sense in combination of submodules (see later).--* `&?&` is `&` if you are in a `do it` context.-* `!?&` is `!` if you are in a `do it` context.-* `&?!` is `&` if you are in a `undo it` context.-* `!?!` is `!` if you are in a `undo it` context.--for example -- & (submodule $ []- &?& contains (File "/dev/shm/test") ["text"]- & exists (Symlink "~/.vimrc" "/dev/shm/test")- )--would be reverted like this-- ! (submodule $ []- &?& contains (File "/dev/shm/test") ["text"]- & exists (Symlink "~/.vimrc" "/dev/shm/test")- )--which is similar to -- ! exists (Symlink "~/.vimrc" "/dev/shm/test")- --### `submodule`--to group a bunch of command together to on command you can negate all at once if you want.-But you can create a much more sophisticated combination of commands using-`!?&`,`!?!`,`&?&` and `&?!`.--for example -- & (submodule $ []- & contains (File "/dev/shm/test") ["text"]- & exists (Symlink "~/.vimrc" "/dev/shm/test")- )--is equivalent to-- & contains (File "/dev/shm/test") ["text"]- & exists (Symlink "~/.vimrc" "/dev/shm/test")--but could be reverted like this-- ! (submodule $ []- & contains (File "/dev/shm/test") ["text"]- & exists (Symlink "~/.vimrc" "/dev/shm/test")- )--which is equivalent to-- ! contains (File "/dev/shm/test") ["text"]- ! exists (Symlink "~/.vimrc" "/dev/shm/test")--### `requires`--is used to create dependencies like "first do *this*, and when everything is fine do *this*".-They make most sense with submodules-- & ((submodule $ []- & exists (Symlink "~/.vim" "~/.dot_vim")- & exists (Symlink "~/.vimrc" "~/.vim/vimrc")- ) - `requires` - (submodule $ []- & exists (Git "git@github.com/myrepo/dot_vim.git" "~/.dot_vim" [Recursive])- ))- -If `requires` is called in a reverting context (e.g. using `!`) it will also create a dependency -but twisted and the body will be reverted as well.-- ! ((submodule $ []- & exists (Symlink "~/.vim" "~/.dot_vim")- & exists (Symlink "~/.vimrc" "~/.vim/vimrc")- ) - `requires` - (submodule $ []- & exists (Git "git@github.com/myrepo/dot_vim.git" "~/.dot_vim" [Recursive])- ))- -is equivalent to-- & ((submodule $ []- ! exists (Git "git@github.com/myrepo/dot_vim.git" "~/.dot_vim" [Recursive])- )- `requires` - (submodule $ []- ! exists (Symlink "~/.vim" "~/.dot_vim")- ! exists (Symlink "~/.vimrc" "~/.vim/vimrc")- ))+## Links +* [Hackage Documentation](http://hackage.haskell.org/package/azubi)
azubi.cabal view
@@ -10,7 +10,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 0.1.0.1+version: 0.2.0.0 -- A short (one-line) description of the package. synopsis: A simple DevOps tool which will never "reach" enterprice level.@@ -38,8 +38,10 @@ maintainer: azubi@ingolf-wagner.de -- A copyright notice.--- copyright: +-- copyright: +stability: Unstable+ category: System build-type: Simple@@ -56,26 +58,65 @@ , GHC == 7.10.1 , GHC == 8.0.1 ++test-suite test+ build-depends: base >= 4.7 && < 5+ , filepath >=1 && < 2+ , hspec >= 2.3++ type: exitcode-stdio-1.0+ main-is: Test.hs+ hs-source-dirs: src test+ default-language: Haskell2010+ ghc-options: -Wall+ buildable: True+++-- executable azubi+-- main-is: main.hs library- exposed-modules: Azubi- other-modules: Azubi.Commands.File- , Azubi.Commands.Install- , Azubi.Commands.Git- , Azubi.Commands.Run- , Azubi.Commands.Existance- , Azubi.Core.Command- , Azubi.Core.Context- , Azubi.Core.Provision- , Azubi.Core.Revertable- , Azubi.Core.Syntax- , Azubi.Render.BashScript- , Azubi.Render.Warez- , Azubi.Systems.Gentoo hs-source-dirs: src++ exposed-modules: Azubi+ other-modules: Azubi.Core.Model+ Azubi.Core.Boot+ Azubi.Core.StateExecutor+ Azubi.Core.StateExecutors.LocalUnixStateExecutor+ Azubi.Syntax+ Azubi.Module.Installable+ Azubi.Module.Runable+ build-depends: base >=4.7 && < 5 , filepath >=1 && < 2 , options >= 1.2 && < 2+ , process >=1.4 && < 2+ , directory >= 1.3 && < 2+ , Diff >= 0.3 + , unix >= 2.7 default-language: Haskell2010+ ghc-options: -fwarn-incomplete-patterns++-- library+-- exposed-modules: Azubi+-- other-modules: Azubi.Commands.File+-- , Azubi.Commands.Install+-- , Azubi.Commands.Git+-- , Azubi.Commands.Run+-- , Azubi.Commands.Existance+-- , Azubi.Core.Command+-- , Azubi.Core.Context+-- , Azubi.Core.Provision+-- , Azubi.Core.Revertable+-- , Azubi.Core.Syntax+-- , Azubi.Render.BashScript+-- , Azubi.Render.Warez+-- , Azubi.Systems.Gentoo+-- hs-source-dirs: oldsrc+-- build-depends: base >=4.7 && < 5+-- , filepath >=1 && < 2+-- , options >= 1.2 && < 2+-- default-language: Haskell2010+ source-repository head type: git
src/Azubi.hs view
@@ -1,123 +1,49 @@--module Azubi( azubiMain- , AzubiConfig- , azubiConfig- , (&)- , (!)- , (&?&)- , (!?&)- , (&?!)- , (!?!)- , submodule- , requires- , Gentoo(..)- , installed- , exists- , contains- , File(..)- , Repository(..)- , GitOptions(..)- , azubiLogo- , run- ) where--import Options--import Azubi.Core.Syntax-import Azubi.Core.Command-import Azubi.Core.Provision--import Azubi.Systems.Gentoo--import Azubi.Render.BashScript-import Azubi.Render.Warez--import Azubi.Commands.Install-import Azubi.Commands.Existance-import Azubi.Commands.File-import Azubi.Commands.Git-import Azubi.Commands.Run--type AzubiConfig = [Command]---- | main class called by the user--azubiMain :: [Command] -> IO ()-azubiMain config =- do- renderContext <- runCommand extractArguments- azubiMainExecute renderContext config--azubiMainExecute :: RenderContext -> [Command] -> IO()-azubiMainExecute (BashScript user output) commands = do - putStr $ unlines bashScriptHeader- writeFile output $ bashScriptExecuter user commands- where- bashScriptHeader = [ "Generating Bashscript"- , "---------------------"- , "output : " ++ output- , "user mode : " ++ (userMode user)- ]- userMode (User None) = "login user with no root command strategy"- userMode (User Su) = "login user with su as root command strategy"- userMode (User Sudo) = "login user with sudo as root command strategy"- userMode Root = "root"---azubiMainExecute render _ = putStrLn $ (show render) ++ "not supported yet"----- | option parsing--extractArguments :: AzubiOptions -> [String] -> IO RenderContext-extractArguments opts args =- return (BashScript (userMode $ optUser opts) (optOutput opts))- where- userMode RootConfiguration = Root- userMode UserNoneConfiguration = User None- userMode UserSuConfiguration = User Su- userMode UserSudoConfiguration = User Sudo-+{-| +Module : Azubi+Description : Azubi main class is all you need.+Copyright : (c) Ingolf Wagner, 2017+License : GPL-3+Maintainer : azubi@ingolf-wagner.de+Stability : experimental+Portability : POSIX +Example: -data AzubiOptions = AzubiOptions { optOutput :: String- , optUser :: UserConfiguration- }+@+import Azubi -instance Options AzubiOptions where- defineOptions = pure AzubiOptions- <*> simpleOption "output" "./azubi.sh"- "Outfile of the scritpt"- <*> defineOption (optionType_enum "User Type") userConfiguration+main :: IO ()+main = azubiMain $ []+ & installed (Ebuild "vim")+ & uptodate (Git "git@github.com:mrVanDalo\/azubi.git" "\/dev\/shm\/azubi")+ & installed (Git "git@github.com:mrVanDalo\/azubi-config.git" "\/dev\/shm\/azubi-config")+ & run (Always "touch" ["/dev/shm/run.test"])+ & link "\/dev\/shm\/azubi.link" "\/dev\/shm\/azubi"+@ +-}+module Azubi ( State(..)+ , Ebuild(..)+ , Git(..)+ , RunCommand(..)+ , installed+ , Installable+ , uptodate+ , Updatable+ , run+ , link+ , folderExists+ , content+ , requires+ , submodule+ , (&)+ , azubiMain+ ) where --- | UserContext parsing (see "User Type" parameter)--- | ------------------------------------------------data UserConfiguration = RootConfiguration- | UserNoneConfiguration- | UserSuConfiguration- | UserSudoConfiguration- deriving (Bounded, Enum)--instance Show UserConfiguration where- show RootConfiguration = "root"- show UserNoneConfiguration = "user"- show UserSudoConfiguration = "sudoUser"- show UserSuConfiguration = "suUser"--userConfiguration :: Option UserConfiguration -> Option UserConfiguration-userConfiguration opt =- opt{ optionLongFlags = ["user"]- , optionDefault = UserNoneConfiguration- , optionDescription =- unwords $ [ "User type the commands should be run in."- , "For example 'UserSu' will run every command"- , "as normal login user, but the superuser commands via"- , "su -c 'command' so you have to enter the"- , "superuser password for every install command for example."- , "possible options are:"- ]- ++ (map show [RootConfiguration ..])- }+import Azubi.Core.Model+import Azubi.Core.Boot+import Azubi.Syntax+import Azubi.Module.Runable+import Azubi.Module.Installable
− src/Azubi/Commands/Existance.hs
@@ -1,18 +0,0 @@--module Azubi.Commands.Existance where---import Azubi.Core.Revertable-import Azubi.Core.Command---class Existance a where- exists :: (Revertable b) => a -> b -> [ Command ]- exists c revertable =- if (isRevert revertable) then- notExists c revertable- else- doExists c revertable-- notExists :: (Revertable b) => a -> b -> [ Command ]- doExists :: (Revertable b) => a -> b -> [ Command ]
− src/Azubi/Commands/File.hs
@@ -1,92 +0,0 @@--module Azubi.Commands.File where--import Azubi.Core.Revertable-import Azubi.Core.Command--import Azubi.Commands.Existance--import System.FilePath--data File = File { path :: String }- | Symlink { path :: String- , target :: String- }- | Directory { path :: String }------- ------------------------------------------------------------------ exists / not exists------ -------------------------------------------------------------instance Existance File where- doExists (File path) con =- [ IfCommand (BoolCommand $ "-e " ++ path)- [ InfoMsg $ path ++ " already exists" ]- ((doExists ( Directory $ takeDirectory path ) con)- ++ [ InfoMsg $ "check existence of " ++ path- , ShellCommand $ "touch " ++ path ])- ]-- doExists (Symlink path target) con =- ( doExists ( Directory $ takeDirectory path ) con)- ++ [ InfoMsg $ "create symlink " ++ path ++ " -> " ++ target- , ShellCommand $ "ln -s -f --no-target-directory " ++ target ++ " " ++ path ]-- doExists (Directory path) con = [- IfCommand {- testCommand = BoolCommand $ "-d " ++ path- , thenCommand = [ LogMsg $ "directory " ++ path ++ " exists"]- , elseCommand = [ InfoMsg $ "create direcotory " ++ path- , ShellCommand $ "mkdir -p " ++ path ]- }- ]--- notExists (File path) con =- [ IfCommand (BoolCommand $ "-e " ++ path)- [ShellCommand $ "rm -f " ++ path ]- [InfoMsg $ path ++ " does not exists"]- ]-- notExists (Symlink path target) con = notExists (File path) con-- notExists (Directory path) con =- [ IfCommand (BoolCommand $ "-d " ++ path)- [ ShellCommand $ "rm -fr " ++ path ]- [ LogMsg $ path ++ " does not exists" ]- ]------ ------------------------------------------------------------------ File Content------ ---------------------------------------------------------------- | todo : rename consists--- | contain should mean that a string inside exists like this. (sed magic than happens)-contains :: (Revertable a ) => File -> [ String ] -> a -> [ Command ]-contains file content revertable =- if (isRevert revertable) then- notExists file revertable- else- doContent file- where- doContent (File path) =- exists file revertable- ++ [ InfoMsg $ "write content to " ++ path- , FileContent path content- ]- doContent (Symlink _ target) =- exists file revertable- ++ [ InfoMsg $ "write content to " ++ target- , FileContent target content- ]- doContent (Directory path ) =- exists file revertable- ++ [ ErrorMsg $ "can not put content in a directory " ++ path ]
− src/Azubi/Commands/Git.hs
@@ -1,47 +0,0 @@----module Azubi.Commands.Git where---import Azubi.Core.Command--import Azubi.Commands.Existance-import Azubi.Commands.File--import System.FilePath--data Repository = Git { repo :: String- , folder :: String- , options :: [GitOptions]}--data GitOptions = Branch String- | Recursive--instance Existance Repository where-- doExists (Git repo folder options) con =- [Dependency{- body = bodyCommands- , dependency = [IfCommand{- testCommand = BoolCommand $ "-d " ++ folder- , thenCommand = []- , elseCommand = (exists (Directory parentFolder) con)- ++ [ InfoMsg $ "clone git repository " ++ repo ++ " to folder " ++ folder- , ShellCommand $ unwords $ ["git", "clone"] ++ gitOptions ++ [repo, folder] ]- }]- }]- where- parentFolder = takeDirectory folder- gitOptions = concat $ map (\opt -> case opt of- Recursive -> ["--recursive"]- _ -> []- ) options- bodyCommands = concat $ map (\opt -> case opt of- Branch branch -> [ ShellCommand $ "cd " ++ folder- , ShellCommand $ "git checkout " ++ branch]- _ -> []- ) options-- notExists (Git _ folder _) con = notExists (Directory folder) con-
− src/Azubi/Commands/Install.hs
@@ -1,40 +0,0 @@-module Azubi.Commands.Install where--import Azubi.Core.Context-import Azubi.Core.Revertable-import Azubi.Core.Command---type Package = String---- check if installed or not-class (Context a, Revertable a) => Installed a where-- installed :: Package -> a -> [ Command ]- installed package context =- if (isRevert context) then- uninstallCommands- else- installCommands- where- uninstallCommands =- [ IfCommand (isInstalled context package)- ([ InfoMsg $ "uninstalling " ++ package ] ++ (doUnInstall context package))- [ InfoMsg $ package ++ " is not installed" ]- ]- installCommands =- [ IfCommand (isInstalled context package)- [ InfoMsg $ package ++ " is already installed" ]- $ [ InfoMsg $ "installing " ++ package ] ++ doInstall context package- ]--- --- -- exit code of the command decides if it is True or False- --- isInstalled :: a -> Package -> BoolCommand-- doInstall :: a -> Package -> [ Command ]-- doUnInstall :: a -> Package -> [ Command ]-
− src/Azubi/Commands/Run.hs
@@ -1,33 +0,0 @@--module Azubi.Commands.Run where--import Azubi.Core.Command-import Azubi.Core.Context--import Azubi.Commands.File------ | run a command which creates Nothing or a File.-run:: (Context a) => String -> Maybe File -> a -> [Command]-run command (Just(File path)) _ = [ IfCommand {- testCommand = BoolCommand $ "-e " ++ path- , thenCommand = []- , elseCommand = [ShellCommand command]- }]--run command (Just(Directory path)) _ = [ IfCommand {- testCommand = BoolCommand $ "-d " ++ path- , thenCommand = []- , elseCommand = [ShellCommand command]- }]--run command (Just (Symlink path target)) _ = [ IfCommand {- testCommand = BoolCommand $ "-L " ++ path- , thenCommand = [] -- todo : check if link points to path- , elseCommand = [ShellCommand command]- }]--run command Nothing _ = [ InfoMsg $ "run " ++ command- , ShellCommand command- ]
+ src/Azubi/Core/Boot.hs view
@@ -0,0 +1,92 @@+{-|++Module : Azubi.Core.Boot+Description : Functions to create an Azubi Programm+Copyright : (c) Ingolf Wagner, 2017+License : GPL-3+Maintainer : azubi@ingolf-wagner.de+Stability : experimental+Portability : POSIX++delivers functions you need for starting the+whole azubi process and parsing options from+the command line.++-}+module Azubi.Core.Boot where++import Options+import Azubi.Core.Model+import Azubi.Core.StateExecutor+import Azubi.Core.StateExecutors.LocalUnixStateExecutor+++{-|+ The function you should use to get you commands running.+-}+azubiMain :: [State] -> IO ()+azubiMain states = do+ context <- runCommand extractConfiguration+ execute (config context) states+ where+ config context = check (optSystem context)+ where+ check Gentoo = (LocalContext $ UnixSystem verbosity)+ check Debian = (LocalContext $ UnixSystem verbosity)+ check Ubuntu = (LocalContext $ UnixSystem verbosity)+ verbosity = if (optVerbose context)+ then Verbose+ else Silent++++extractConfiguration :: AzubiOptions -> [String] -> IO AzubiOptions+extractConfiguration opts _ = return opts++data AzubiOptions = AzubiOptions { optVerbose :: Bool+ , optSystem :: System+ }++instance Options AzubiOptions where+ defineOptions = pure AzubiOptions+ <*> simpleOption "verbose" False "shows the commands output"+ <*> defineOption (optionType_enum "Target System") systemConfig++descriptionHelper :: (Show a ) => [String] -> [ a ] -> String+descriptionHelper text (x:xs)= unwords $ text ++ ["[" ++ foldl (\a b -> a ++ ", " ++ (show b)) (show x) xs ++ "]" ]+descriptionHelper text [] = unwords text+++data Execution = Command+ | Dummy+ deriving (Show, Eq, Bounded, Enum)++execConfig :: Option Execution -> Option Execution+execConfig opt =+ opt{ optionLongFlags = ["exec"]+ , optionDefault = Command+ , optionDescription =+ descriptionHelper+ [ "Type of Execution"+ , "which is more or less the way the rules should"+ , "System States should be enforced."]+ [Command ..]+ }+++data System = Gentoo+ | Debian+ | Ubuntu+ deriving (Show, Eq, Bounded, Enum)++systemConfig :: Option System -> Option System+systemConfig opt =+ opt{ optionLongFlags = ["system"]+ , optionDefault = Gentoo+ , optionDescription =+ descriptionHelper+ [ "The System type the States should"+ , "be enforced on. Different Systems have"+ , "different commands"]+ [Gentoo ..]+ }
− src/Azubi/Core/Command.hs
@@ -1,23 +0,0 @@--module Azubi.Core.Command where--data Command = ShellCommand String- | SuperUserShellCommand String- | IfCommand { testCommand :: BoolCommand- , thenCommand :: [ Command ]- , elseCommand :: [ Command ]- }- | FileContent { path :: String- , content :: [ String ]- }- | InfoMsg String- | ErrorMsg String- | Dependency { body :: [Command]- , dependency :: [Command] }- | LogMsg String- deriving (Show, Eq)---- exit code will decide if if is True or False-data BoolCommand = BoolCommand String- deriving (Show, Eq)-
− src/Azubi/Core/Context.hs
@@ -1,16 +0,0 @@---module Azubi.Core.Context where---- System Context,------ like Gentoo, Debian, (Docker Gentoo), (Docker Debian)-----class Context a where-- label :: a -> String----
+ src/Azubi/Core/Model.hs view
@@ -0,0 +1,164 @@++{-|++Module : Azubi.Core.Model+Description : Core low level Model of Azubi+Copyright : (c) Ingolf Wagner, 2017+License : GPL-3+Maintainer : azubi@ingolf-wagner.de+Stability : experimental+Portability : POSIX++This is the lowest level of Azubi.+It should deliver only the bare+minimum to create 'State' evaluation+and 'State' enforcement.++The 'State' object is the element+a user should use to formulate more complex+situations.++The idea here is to create a simple AST which+can be run on different setups.++* export to Script+* run on the Machine (Linux, OSX and (maybe) Windows)+* run over SSH on another computer++-}+module Azubi.Core.Model where+++type Path = String+type Target = String+type Argument = String+type Comment = String++{-|++A command is something that will be run.+In a normal case you put a 'Check' before+using a 'State'++When a Command get executed,+it will create a 'CommandResult'.++@ command -> exit code -> 'CommandResult' @++-}+data Command = Run String [Argument] (Maybe Comment)+ | FileContent Path [String]+ | CreateSymlink Path Target+ | CreateFolder Path+ | Remove Path+ deriving (Show, Eq)+++{-|++The Result of a 'Command' will be matched+to a 'CommandResult'. The basic rule is++@+0 -> 'Success'+_ -> 'Failure'+@++-}+data CommandResult = Success+ | Failure+ deriving (Show, Enum, Eq)+++{-|++Gather information about a situation.++@+check -> success/failure+check -> exit code -> success/failure+@++-}+data Check =+ {-|++Check if command returns exit status++@+0 -> Yes+_ -> No+@++-}+ Check String [Argument] (Maybe Comment)+ | AlwaysYes+ -- | Opposite result of a 'Check'+ | Not Check+ -- | Check if 'Path' has content+ | HasFileContent Path [String]+ -- | Check if a Symbolic link exists to a specific target+ | SymlinkExists Path Target+ -- | Check if a folder exists+ | FolderExists Path+ -- | Check if something exists at path+ | DoesExist Path+ deriving (Show, Eq)++{-|++The result of a 'Check'.++-}+data CheckResult = Yes+ | No+ deriving (Show, Enum, Eq)++{-|++The low level element to formulate a state on a machine.+If the check returns 'No' the 'Command's will run.+So the author should make sure the 'Command' will result+in a 'Yes' the next time this state will run.++All 'Check's have to return 'Yes' to avoid the 'Command's+to be run.++If a 'Command' returns a 'Failure' the following commands+will not be called.+Same holdes for 'States' if one of the states fail,+the following 'State's will not be /run/.++-}+data State =++ -- | State contains checks and commands+ -- if one command failed+ -- the following commands will not be+ -- executed.+ State [Check] [Command] (Maybe Comment)++ -- | To create depended states+ -- which should stop being executed+ -- when a previous state fails+ | States [Check] [State] (Maybe Comment)+ deriving (Show, Eq)+++{-|++When a 'State' is /run/ it returns one of the 'StateResult's.++* positive 'CheckResult's will result in 'Fulfilled'.+* negative 'CheckResult's with positive 'CommandResult's will result in 'Fulfilled'.+* negative 'CheckResult's with negative 'CommandResult's will result in 'Unfulfilled'.++-}+data StateResult = Fulfilled+ | Unfulfilled+ deriving (Show, Eq, Enum)+++++
− src/Azubi/Core/Provision.hs
@@ -1,25 +0,0 @@---module Azubi.Core.Provision where---- | what type of user is running the script-data UserContext = User SuperUserMethod- | Root- deriving(Show)---- | the way SuperUserCommands should be called-data SuperUserMethod = Sudo- | Su- | None- deriving(Show)--type OutputFile = String---- | different output which can be produced-data RenderContext = BashScript UserContext OutputFile- | Dockerfile- deriving(Show)----
− src/Azubi/Core/Revertable.hs
@@ -1,36 +0,0 @@----- | to manage reverting of commands-module Azubi.Core.Revertable where--import Azubi.Core.Context----- | Revertable context is for to implement--- | revertable Actions-class Context a => Revertable a where-- -- | toggle reverting- toggleRevert :: a -> a-- -- | should revert an action- -- | eg: uninstall- isRevert :: a -> Bool-- setRevert :: a -> a- setRevert context =- if (isRevert context)- then context- else toggleRevert context-- -- | should execute an action- -- | eg: install- isExecute :: a -> Bool- isExecute context = not $ isRevert context-- setExectue :: a -> a- setExectue context =- if (isExecute context)- then context- else toggleRevert context-
+ src/Azubi/Core/StateExecutor.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE FlexibleInstances #-}++{-|++Module : Azubi.Core.StateExecutor+Description : Core low level State evaluation and enforcement+Copyright : (c) Ingolf Wagner, 2017+License : GPL-3+Maintainer : azubi@ingolf-wagner.de+Stability : experimental+Portability : POSIX++'State' must be evaluated and enforced.+This is done by a 'StateExecutor'.++-}+module Azubi.Core.StateExecutor where++import Azubi.Core.Model++{-|++should evaluate and enforce given 'State's++-}+class StateExecutor a where+ execute :: a -> [State] -> IO ()++{-|++should create a script.++todo : write one++-}+class ScriptStateExecuter a where+ header :: a -> IO ()+ footer :: a -> IO ()+ body :: a -> [State] -> IO ()++-- | wrapper type to prove Haskell+-- there will be now looping.+newtype ScriptExecute a = ScriptExecute a++instance ScriptStateExecuter a => StateExecutor (ScriptExecute a) where+ execute (ScriptExecute context) states = do+ header context+ body context states+ footer context++++++{-|++should /run/ the states on the local machine.++-}+class LocalStateExecute a where+ setup :: a -> IO ()+ executeState :: a -> State -> IO StateResult+ tearDown :: a -> [StateResult] -> IO ()++-- | wrapper type to prove Haskell+-- there will be now looping.+newtype LocalContext a = LocalContext a++instance LocalStateExecute a => StateExecutor (LocalContext a) where++ execute (LocalContext context) states = do+ setup context+ results <- collectStateResults states+ tearDown context results+ where+ collectStateResults :: [State] -> IO [StateResult]+ collectStateResults [] = return []+ collectStateResults (x:xs) = do+ result <- executeState context x+ restResults <- collectStateResults xs+ return $ result:restResults++++
+ src/Azubi/Core/StateExecutors/LocalUnixStateExecutor.hs view
@@ -0,0 +1,226 @@++{-|++Module : Azubi.Core.StateExecutors.LocalUnixStateExecutor+Description : 'StateExecutor' for Unix machines+Copyright : (c) Ingolf Wagner, 2017+License : GPL-3+Maintainer : azubi@ingolf-wagner.de+Stability : experimental+Portability : POSIX++Run 'State's on a Unix machine.++-}++module Azubi.Core.StateExecutors.LocalUnixStateExecutor ( UnixSystem(..)+ , Verbosity(..) ) where++import Azubi.Core.Model+import Azubi.Core.StateExecutor++import System.Directory++import System.Process+import System.Exit++import System.Posix.Files (createSymbolicLink, readSymbolicLink)++import Data.Algorithm.Diff+import Data.Algorithm.DiffOutput++++data Verbosity = Verbose | Silent++{-|++Unix System like Linux, AIX or OSX++<https://en.wikipedia.org/wiki/Unix>++-}+data UnixSystem = UnixSystem { verbose :: Verbosity }++instance LocalStateExecute UnixSystem where+ setup _ = return ()+ tearDown _ _ = return ()+++ executeState systemConfig (State checks commands comment) = do+ runComment' comment+ checkResult <- collectCheckResults systemConfig checks+ case checkResult of+ Yes -> return Fulfilled+ No -> do+ commandResult <- collectRunResults systemConfig commands+ case commandResult of+ Success -> return Fulfilled+ Failure -> return Unfulfilled++ executeState systemConfig (States check states comment) = do+ runComment' comment+ result <- collectCheckResults systemConfig check+ case result of+ Yes -> return Fulfilled+ No -> collectStateResults states+ where+ collectStateResults :: [State] -> IO StateResult+ collectStateResults [] = return Fulfilled+ collectStateResults (x:xs) = do+ result <- executeState systemConfig x+ case result of+ Unfulfilled -> return Unfulfilled+ Fulfilled -> collectStateResults xs++-- | unroll a number of Check(s)+-- | If one fail, they all fail+collectCheckResults :: UnixSystem -> [Check] -> IO CheckResult+collectCheckResults _ [] = return Yes+collectCheckResults systemConfig (check:rest) = do+ result <- runCheck' systemConfig check+ case result of+ Yes -> collectCheckResults systemConfig rest+ No -> return No++-- | unroll a number of Run Commands+-- | if one fail, they all fail+collectRunResults :: UnixSystem -> [Command] -> IO CommandResult+collectRunResults _ [] = return Success+collectRunResults systemConfig (command:rest) = do+ result <- runCommand' systemConfig command+ case result of+ Success -> collectRunResults systemConfig rest+ Failure -> return Failure++-- | Run a command+runCommand' :: UnixSystem -> Command -> IO CommandResult+runCommand' _ (CreateFolder path) = do+ runComment' $ Just $ "create directory " ++ path+ createDirectoryIfMissing True path+ return Success++runCommand' _ (FileContent path content) = do+ runComment' $ Just $ "write content to " ++ path+ writeFile path $ unlines content+ return Success++runCommand' _ (CreateSymlink path target) = do+ runComment' $ Just $ "create link " ++ path ++ " to " ++ target+ createSymbolicLink target path+ return Success++runCommand' systemConfig (Run command arguments comment) = do+ runComment' comment+ result <- runProcess' systemConfig [command] arguments+ case result of+ ExitSuccess -> return Success+ _ -> return Failure++runCommand' _ (Remove path) = do+ runComment' (Just $ "remove " ++ path)+ removePathForcibly path+ return Success+++-- | show a comment+runComment' :: Maybe Comment -> IO ()+runComment' (Just comment) = echo' [comment]+runComment' Nothing = return ()++++-- | Run a Check+runCheck' :: UnixSystem -> Check -> IO CheckResult+runCheck' _ (FolderExists path) = do+ behind <- whatIsBehind' path+ case behind of+ IsFolder -> return Yes+ _ -> return No++runCheck' _ (SymlinkExists path target) = do+ behind <- whatIsBehind' path+ case behind of+ IsSymlink -> do+ behindTarget <- readSymbolicLink path+ if behindTarget == target+ then return Yes+ else return No+ _ -> return No++runCheck' _ (HasFileContent path content) = do+ behind <- whatIsBehind' path+ case behind of+ IsFile -> checkContent+ _ -> return No+ where+ checkContent = do+ file <- readFile path+ currentContent <- return $ lines file+ diff <- return $ getGroupedDiff currentContent content+ case diff of+ (Both _ _):[] -> return Yes+ _ -> do+ echo' [ppDiff diff]+ return No+runCheck' systemConfig (Check command args comment) = do+ runComment' comment+ result <- runProcess' systemConfig [command] args+ case result of+ ExitSuccess -> return Yes+ _ -> return No++runCheck' systemConfig (Not check ) = do+ result <- runCheck' systemConfig check+ case result of+ No -> return Yes+ Yes -> return No++runCheck' _ AlwaysYes = return Yes++runCheck' _ (DoesExist path) = do+ behind <- whatIsBehind' path+ case behind of+ DoesNotExist -> return No+ _ -> return Yes+++data FileType = IsFile+ | DoesNotExist+ | IsSymlink+ | IsFolder+ deriving (Show, Eq, Enum)++-- | helper function to check whats behind a path+whatIsBehind' :: String -> IO FileType+whatIsBehind' path = do+ checkFile <- doesFileExist path+ checkFolder <- doesDirectoryExist path+ case (checkFolder, checkFile) of+ (True, _) -> return IsFolder+ (False, True) -> do+ checkSymlink <- pathIsSymbolicLink path+ if checkSymlink+ then return IsSymlink+ else return IsFile+ (False, False) -> return DoesNotExist+++-- | simple print function+echo' :: [String] -> IO ()+echo' text = putStrLn $ unwords $ "[Azubi]":text++-- | run a process and wait until it's finished+-- | return the exit code+runProcess' :: UnixSystem -> [String] -> [String] -> IO ExitCode+runProcess' systemConfig command args = do+ (_, _ , _ , checkHandle ) <- createProcess (shell $ unwords $ command ++ args ){ std_out = stdOutHandle }+ waitForProcess checkHandle+ where+ stdOutHandle :: StdStream+ stdOutHandle =+ case (verbose systemConfig) of+ Verbose -> Inherit+ Silent -> NoStream++
− src/Azubi/Core/Syntax.hs
@@ -1,78 +0,0 @@---module Azubi.Core.Syntax where--import Azubi.Core.Command-import Azubi.Core.Context-import Azubi.Core.Revertable---- | wrap up everything-azubiConfig :: (Context a) => a -> [(a -> [Command])] -> [Command]-azubiConfig con commands =- concat $ map injectContext commands- where- injectContext f = f con---- | create a submodule-submodule :: (Context a) => [(a -> [Command])] -> a -> [Command]-submodule commands context =- concat $ map injectContext commands- where- injectContext f = f context--requires :: (Context a, Revertable a) => (a -> [Command]) -> (a -> [Command]) -> a -> [Command]-first `requires` sec = \context ->- if (isRevert context)- then- [ Dependency { body=(sec context)- , dependency=(first context)}]- else- [ Dependency { body=(first context)- , dependency=(sec context)}]----- | add a command-(&) :: (Context a) => [ (a -> [Command]) ] -> (a -> [Command] ) -> [ (a -> [Command])]-first & second = first ++ [ second ]----- | add the opposite of the command-(!) :: (Revertable a) => [ (a -> [Command]) ] -> (a -> [Command] ) -> [ (a -> [Command])]-first ! second = first ++ [( second . toggleRevert ) ]----- | add a command under condition of being in--- | a positive context-(&?&) :: (Revertable a) => [ (a -> [Command]) ] -> (a -> [Command] ) -> [ (a -> [Command])]-first &?& second = first ++ [ check ]- where check con =- if (isRevert con)- then []- else second con---- | add the opposite command under condition of being in--- | a positive context-(!?&) :: (Revertable a) => [ (a -> [Command]) ] -> (a -> [Command] ) -> [ (a -> [Command])]-first !?& second = first ++ [ check ]- where check con =- if (isRevert con)- then []- else second (setRevert con)---- | add a command under condition of being in--- | a negative context-(&?!) :: (Revertable a) => [ (a -> [Command]) ] -> (a -> [Command] ) -> [ (a -> [Command])]-first &?! second = first ++ [ check ]- where check con =- if (isRevert con)- then second (setExectue con)- else []---- | add the opposite command under condition of being in--- | a negative context-(!?!) :: (Revertable a) => [ (a -> [Command]) ] -> (a -> [Command] ) -> [ (a -> [Command])]-first !?! second = first ++ [ check ]- where check con =- if (isRevert con)- then second con- else []
+ src/Azubi/Module/Installable.hs view
@@ -0,0 +1,130 @@+{-|++Module : Azubi.Module.Installable+Description : provide install function+Copyright : (c) Ingolf Wagner, 2017+License : GPL-3+Maintainer : azubi@ingolf-wagner.de+Stability : experimental+Portability : POSIX++To install software on your computer.++-}+module Azubi.Module.Installable where++import Azubi.Core.Model++import Azubi.Module.Runable++++{-|++To install Software you have to+instance the Installable class.++-}+class Installable a where++ {-|++install a piece of software on the system.+The /piece/ of software should be typed of course.++-}+ installed :: a -> State++++{-|++Same like 'Installable' but will also+make sure there you have the newest+version.++-}+class Updatable a where+++ {-|++make sure something is installed and+up to date.++-}+ uptodate :: a -> State++++{-|++Ebuild is a Portage package (used by Gentoo and Funtoo).++<http://gentoo.org>++See 'installed'.++-}+data Ebuild = Ebuild String++instance Installable Ebuild where++ installed (Ebuild package) = State+ [Check "eix" ["--exact", "--nocolor", "--installed", package] (Just $ "check if package " ++ package ++ " is installed")]+ [Run "emerge" [package] (Just $ "installing " ++ package)]+ Nothing++instance Updatable Ebuild where++ uptodate (Ebuild package) = States [AlwaysYes]+ [ installed (Ebuild package)+ , State+ [Not $ Check "eix" ["--upgrade-", "--nocolor", package] Nothing]+ [Run "emerge" [package] (Just $ "upgrade " ++ package)]+ Nothing+ ]+ Nothing+++++++-- | Url of the repository used by+-- git clone+type RepoUrl = String++{-|++Git is a version control system.++<http://git.scm.com>++See 'installed'.++-}+data Git = Git RepoUrl Path+++instance Installable Git where++ installed (Git repository path) = State+ [FolderExists path]+ [Run "git"+ ["clone"+ , repository+ , path]+ (Just $ "cloning " ++ repository ++ " to " ++ path)]+ Nothing++instance Updatable Git where++ uptodate (Git repo path) =+ States [AlwaysYes]+ [ installed (Git repo path )+ , run (Always "git" ["--work-tree=" ++ path, "pull"])+ ]+ Nothing++
+ src/Azubi/Module/Runable.hs view
@@ -0,0 +1,53 @@+{-|++Module : Azubi.Module.Runable+Description : deploy 'run' command+Copyright : (c) Ingolf Wagner, 2017+License : GPL-3+Maintainer : azubi@ingolf-wagner.de+Stability : experimental+Portability : POSIX++-}+module Azubi.Module.Runable where++import Azubi.Core.Model++{-|++creates a 'State' that will run a command of your choice.++-}+run :: RunCommand -> State+run (Once command arguments result) =+ let+ fullCommand = unwords $ command : arguments+ fileContent = [ "This is a Azubi cache file"+ , "You can delete or edit it to make the following command run again"+ , ""+ , fullCommand ]+ in+ State+ [ HasFileContent result fileContent ]+ [ Run command arguments $ Just $ unwords $ [ "run command" , command ] ++ arguments+ , FileContent result fileContent ]+ Nothing++run (Always command arguments) =+ State+ [ Not AlwaysYes ]+ [ Run command arguments $ Just $ unwords $ [ "run command", command ] ++ arguments ]+ Nothing++{-|++The way a command should be run. See 'run'++-}+data RunCommand =+ -- | run command every time+ Always String [Argument]+ -- | run command and creates a file to prevent to run again+ | Once String [Argument] Path++
− src/Azubi/Render/BashScript.hs
@@ -1,123 +0,0 @@--module Azubi.Render.BashScript( bashScriptExecuter- , bashScriptGenerator) where--import Azubi.Core.Command-import Azubi.Core.Provision-import Azubi.Render.Warez------- This is a small renderer--- which compiles the wishlist into a shell-script----bashScriptExecuter :: UserContext -> [Command] -> String-bashScriptExecuter context commands =- unlines $- scripHeader- ++ printLogo- ++ (bashScriptGenerator context commands)- where- scripHeader = [ "#!/bin/bash"- , "#"- , "# script is automatically generated by "- ]- printLogo = ["cat <<EOF"]- ++ azubiLogo- ++ ["EOF"]--bashScriptGenerator :: UserContext -> [Command] -> [String]-bashScriptGenerator context commands =- (bashScriptInit context)- ++ (concat (map (commandSnippet $ BashScriptContext [] context) commands))---bashScriptInit :: UserContext -> [String]-bashScriptInit Root = [ "if [[ `whoami` != root ]]; then"- , " echo 'you must be root to run this script !'"- , " exit 1"- , "fi"- , "" ]-bashScriptInit _ = []--data BashScriptContext = BashScriptContext { dependencyStack :: [String]- , renderContext :: UserContext- }---commandSnippet :: BashScriptContext -> Command -> [ String ]--commandSnippet context (ShellCommand command) =- [ command ++ " &>> ~/.azubi.log"- , "if [[ $? -ne 0 ]]; then"- , "echo 'Error : running `" ++ command ++ "`' failed | tee -a ~/.azubi.log"- ]- ++ [ d ++ "=false" | d <- (dependencyStack context) ]- ++ ["fi"]--commandSnippet context@(BashScriptContext _ Root)(SuperUserShellCommand command) =- commandSnippet context (ShellCommand command)--commandSnippet context@(BashScriptContext _ (User None)) (SuperUserShellCommand command) =- commandSnippet context (InfoMsg $ "cant run superuser command `" ++ command ++ "` as Normal user")--commandSnippet context@(BashScriptContext _ (User Sudo)) (SuperUserShellCommand command) =- concat $ map (\c -> commandSnippet context c) commands- where- commands = [ InfoMsg $ "sudo \"" ++ command ++ "\""- , ShellCommand $ "sudo \"" ++ command ++"\""]--commandSnippet context@(BashScriptContext _ (User Su)) (SuperUserShellCommand command) =- concat $ map (\c -> commandSnippet context c) commands- where- commands = [ InfoMsg $ "su - -c \"" ++ command ++ "\""- , ShellCommand $ "su - -c \"" ++ command ++"\""]--commandSnippet _ (InfoMsg i) =- ["echo 'INFO : " ++ i ++ "' | tee -a ~/.azubi.log" ]--commandSnippet _ (ErrorMsg i) =- ["echo 'ERROR: " ++ i ++ "' | tee -a ~/.azubi.log" ]--commandSnippet _ (LogMsg i) =- ["echo 'INFO : " ++ i ++ "' &>> ~/.azubi.log"]--commandSnippet context (IfCommand (BoolCommand b) t e) =- ["if [[ " ++ b ++ " ]]; then" ]- ++ thenPart t- ++ elsePart e- ++ ["fi"]- where- thenPart [] = ["echo -n ''"]- thenPart commands = bodyIndent (map (commandSnippet context) commands)- elsePart [] = []- elsePart commands = ["else" ]- ++ bodyIndent (map (commandSnippet context) commands)---commandSnippet _ (FileContent contentPath content) =- [ "cat >" ++ contentPath ++ " <<EOF" ]- ++ content- ++ ["EOF"]--commandSnippet context (Dependency [] []) = []--commandSnippet context (Dependency [] dependency) =- concat (map (commandSnippet context) dependency)--commandSnippet context (Dependency body []) =- concat (map (commandSnippet context) body)--commandSnippet context (Dependency body dependency) =- [ dependencyVariable ++ "=true" ]- ++ concat (map (commandSnippet (BashScriptContext (dependencyVariable : (dependencyStack context)) (renderContext context ) ) ) dependency)- ++ ["if $" ++ dependencyVariable ++ " ; then"]- ++ bodyIndent (map (commandSnippet context) body)- ++ ["fi"]- where- dependencyVariable :: String- dependencyVariable = "dependency" ++ (show $ length $ dependencyStack context )---bodyIndent :: [[String]] -> [ String ]-bodyIndent body =- concat body
− src/Azubi/Render/Warez.hs
@@ -1,12 +0,0 @@--module Azubi.Render.Warez where---azubiLogo ::[ String ]-azubiLogo = [ " ___. .__"- , "_____ __________ _\\_ |__ |__|"- , "\\__ \\ \\___ / | \\ __ \\| |"- , " / __ \\_/ /| | / \\_\\ \\ |"- , "(____ /_____ \\____/|___ /__|"- , " \\/ \\/ \\/"- ]
+ src/Azubi/Syntax.hs view
@@ -0,0 +1,109 @@+{-|++Module : Azubi.Syntax+Description : Basic Azubi syntax to combine 'State's+Copyright : (c) Ingolf Wagner, 2017+License : GPL-3+Maintainer : azubi@ingolf-wagner.de+Stability : experimental+Portability : POSIX++Functions to create a readable and typesafe+language, to describe system states.++-}+module Azubi.Syntax where++import Azubi.Core.Model+++{-|++Create a state that ensures that a file in @Path@ will+have the content of the String List.++Every String in the List will be a Line in the File.++-}+content :: Path -> [String] -> State+content path fileContent = State [HasFileContent path fileContent]+ [FileContent path fileContent]+ (Just $ unwords [ "Content for File" , path ])++{-|++Creates a state out of two states.+When the first State fails the second+State will not be checked nor enforced.++This can also be achieved using 'submodule':++@+stateA `requires` stateB == 'submodule' [stateB, stateA]+@++-}+requires :: State -> State -> State+stateA `requires` stateB = States [AlwaysYes] [stateB, stateA] Nothing++{-|++Create a state containing of sub-states,+which have to be fulfilled in order.++If one state is not fulfilled, the following+will be ignored.++-}+submodule :: [State] -> State+submodule states = States [AlwaysYes] states Nothing+++{-|++make sure a folder exists++-}+folderExists :: Path -> State+folderExists path = State [FolderExists path] [CreateFolder path] Nothing++{-|++ensure there is a link file -> target++example:++@+link "~\/file" "~\/target"+@++will create a link at @~\/file\/@ pointing to @~\/target\/@++-}+link :: Path -> Path -> State+link path target =+ States+ [ SymlinkExists path target ]+ [ State [Not $ DoesExist path] [Remove path] Nothing+ , State [Not AlwaysYes] [CreateSymlink path target] Nothing+ ]+ (Just $ "link " ++ path ++ " to " ++ target)++{-|++'State' combinator.++@+[]+& folderExists "\/tmp\/foo"+& folderExists "\/tmp\/bar"+@++-}+(&) :: [State] -> State -> [State]+states & state = states ++ [state]+++-- (!) :: [State] -> State -> [State]+-- states ! state = states ++ [(revertState state)]+
− src/Azubi/Systems/Gentoo.hs
@@ -1,28 +0,0 @@--module Azubi.Systems.Gentoo where--import Azubi.Core.Context-import Azubi.Core.Revertable-import Azubi.Core.Command--import Azubi.Commands.Install--data Gentoo = Gentoo- | GentooRevert--instance Context Gentoo where- label _ = "Gentoo system"--instance Revertable Gentoo where- isRevert GentooRevert = True- isRevert Gentoo = False-- toggleRevert GentooRevert = Gentoo- toggleRevert Gentoo = GentooRevert--instance Installed Gentoo where- isInstalled _ package = BoolCommand $- "`eix -e " ++ package ++ " | head -n1 | cut -d' ' -f 1` == '[I]'"- doInstall _ package = [ SuperUserShellCommand $ "emerge " ++ package ]- doUnInstall _ package = [ SuperUserShellCommand $ "emerge --unmerge " ++ package ]-
+ test/Test.hs view
@@ -0,0 +1,18 @@++import Test.Hspec++import Azubi.Syntax+import Azubi.Core.Model++main :: IO ()+main = hspec $ do+ describe "requires" $ do+ it "should be the same as submoulde for 2 arguments" $ do+ (stateA `requires` stateB) `shouldBe` (submodule [stateB, stateA])++++stateA :: State+stateA = State [] [Run "echo" ["you"] Nothing] Nothing+stateB :: State+stateB = State [] [Run "echo" ["me"] Nothing] Nothing