packages feed

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 view
@@ -1,6 +1,9 @@ # Azubi  [![Build Status](https://travis-ci.org/mrVanDalo/azubi.svg?branch=master)](https://travis-ci.org/mrVanDalo/azubi)+[![Version](https://img.shields.io/badge/version-0.1.0.1-green.svg)](https://github.com/mrVanDalo/azubi/releases/tag/0.1.0.1)+[![License](https://img.shields.io/badge/license-gpl-green.svg)](https://www.gnu.org/licenses/gpl-3.0.en.html)+[![Join the chat at https://gitter.im/azubi-configuration/Lobby](https://badges.gitter.im/azubi-configuration/Lobby.svg)](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