turtle
turtle copied to clipboard
Full practical example
Nothing like a full example to pull all the concepts you just learned together. I give you this code so you can see review the code (and see what mistakes a new user of your library made) and to possibly include for others to learn from.
I know some repositories have a examples subdirectory. This could even fit on a Tutorials.DatabaseBackup example documentation page or something?
code:
{-# LANGUAGE OverloadedStrings #-}
import Control.Category ((<<<), (>>>))
import qualified Control.Foldl as Fold
import Data.Maybe
import Turtle
run sink f = sink (f empty)
main = do
-- backup old db
output "revert.sql" (inproc "mysqldump" ["-uroot", "myproject"] empty)
-- newest filename from server
newestFileName <- fmap fromText . listToMaybe <$>
fold ((inproc "ssh" ["lalala", "ls -Art /backups/db/myproject | tail -n 1"]) empty) Fold.list
let serverName = "lalala"
dbName = "myproject-copy"
backupDir = "/backups/db/myproject/"
newestFileName' = fromMaybe (error "couldn't get backup path") newestFileName
backupFilePath = backupDir </> newestFileName'
localFilePath = "/tmp/" </> newestFileName'
backupFilePath' = either (\e -> error $ "Couldn't parse backupFilePath: " <> show e) id (toText backupFilePath)
localFilePath' = either (\e -> error $ "Couldn't parse localFilePath: " <> show e) id (toText localFilePath)
-- download backup
sh $ inproc "rsync" ["-avz", serverName, ":", backupFilePath', localFilePath'] empty
-- drop old db (should have backup)
run sh (inshell ("mysqladmin -uroot drop -f " <> dbName))
-- restore database from just downloaded backup
run sh (inshell ("zcat " <> localFilePath') >>> inshell ("mysql -uroot" <> dbName))
print "done"
Not bad! A few comments:
- Use
Control.Foldl.head
instead oflistToMaybe
like I mentioned in the other thread - Maybe use
die
instead oferror
(Maybe we need a combinator for this) - Use
run
for thersync
command, too, for consistency - Use
echo
instead ofprint
- Use
inproc
instead ofinshell
Other than that, though, this is a really useful example. I will add this to an examples
directory
Thanks for the feedback! I was going to try and use turtle alone to rewrite this:
https://github.com/andreafabrizi/Dropbox-Uploader/blob/master/dropShell.sh
But making a repl in Turtle wrapping bash seems weird and potentially difficult. The reason was I wanted to test replacing pieces of a shell library and seeing how it worked out.
I'm struggling to figure out what the Turtle equivalent of this shell code would be:
(source: https://github.com/andreafabrizi/Dropbox-Uploader/blob/master/dropShell.sh#L335)
while (true); do
#Reading command from shell
read -e -p "$username@Dropbox:$CWD$ " input
#Tokenizing command
eval tokens=($input)
cmd=${tokens[0]}
arg1=${tokens[1]}
arg2=${tokens[2]}
#Saving command in the history file
history -s "$input"
history -w "$SHELL_HISTORY"
case $cmd in
ls)
sh_ls "$arg1"
;;
Mainly I'm not sure what input is or where it's coming from, though I'm guessing somewhere else in the script.
I'd like to (and think it would be fun to) convert the linux steam install script that famously deleted someone’s entire root directory because of an uninitialized environmental variable.
It would also be good PR for Turtle and even the "fiercest' pragmatists would acknowledge it solved a real world task. link if you're curious:
https://github.com/indrora/steam_latest/blob/master/scripts/steam.sh
I'm very familiar with that Steam bug :)
I can translate what the script is doing. read -e -p "$username@Dropbox:CWD$ " input
is basically saying:
- Prompt the user with
"$username@Dropbox:$CWD$ "
- Read a line from standard input into the variable named
input
The equivalent of this in turtle
would roughly be:
example userName shellHistory = forever (do
Right dirTxt <- fmap toText pwd
echo (format (s%"@Dropbox:"%s%"$ ") userName dirTxt)
Just (input@[cmd, arg1, arg2]) <- readline
proc "history" ["-s", input]
proc "history" ["-w", shellHistory]
case cmd of
"ls" -> ... )
I skipped over proper error handling. A more robust script would handle errors with more descriptive error messages.
Also, it just occurred to me that it might be worth providing a Format
specifier for FilePath
s. In other words, something like this:
fp :: Format r (FilePath -> r)
So then you could just write:
dir <- pwd
echo (format (s%"@Dropbox:"%fp%"$ ") userName dir)
@Gabriel439 Just to make sure we aren't duplicating work, I wanted to let you know I'm working on a PR with examples with the same style as bos/wreq.
Alright
Just wanted to +1 this. It would be great to have even more small stand-alone examples, possibly just a link to different gists on the wiki, or however people like to share their scripts.
+1 from me too: I am finding the learning curve bit steep (thinking in terms of shell).
It could also just be on the wiki or a separate git repo but having lots of different real examples would be most helpful to pick up the idioms.
@juhp: You can simplify your code a little bit like this:
checkPkg top p = do
True <- testdir $ branchDir "epel7"
True <- testfile $ specfile "epel7"
arch <- rpmspecSrc "%{arch}" $ specfile_ "epel7"
guard (arch == "noarch")
el7 <- rpmspecSrc nvr (specfile_ "epel7")
(_, out) <- procStrict "koji" ["latest-pkg", "--quiet", "epel7", p] empty
guard (out /= "")
cur:_ <- return (Data.Text.words out)
guard (cur == el7 <> ".el7")
True <- testfile $ specfile "f20"
f20 <- rpmspecSrc nvr (specfile_ "f20")
if (el7 < f20)
then echo (el7 <> " < F20 " <> f20)
else do
f21 <- rpmspecSrc nvr (specfile_ "f21")
guard (el7 < f21)
echo (el7 <> "< F21 " <> f21)
Also, instead of:
main = sh $ do
top <- pwd
arguments >>= mapM_ (checkPkg top)
... you can do:
main = sh $ do
top <- pwd
args <- arguments
arg <- select args
checkPkg top arg
In fact, you could just inline the definition of checkPkg
into your main after this change
Alright, so I polished up @codygman's original script and added it to a newly formed examples/
directory in this commit: 43a0f2371c5145ae8341cf5a32018f1e69230d46
I'll update this thread as I go
Thanks! :-)
Update: I put your suggestions into https://pagure.io/haskell-sig/blob/master/f/scripts/el7merge.hs