Merge branch 'master' into concurrentprogress

Conflicts:
	Command/Fsck.hs
	Messages.hs
	Remote/Directory.hs
	Remote/Git.hs
	Remote/Helper/Special.hs
	Types/Remote.hs
	debian/changelog
	git-annex.cabal
This commit is contained in:
Joey Hess 2015-05-12 13:23:22 -04:00
commit e27b97d364
378 changed files with 4978 additions and 1158 deletions

View file

@ -1,7 +1,28 @@
Antoine Beaupré <anarcat@koumbit.org> anarcat <anarcat@web>
Antoine Beaupré <anarcat@koumbit.org> https://id.koumbit.net/anarcat <https://id.koumbit.net/anarcat@web>
Greg Grossmeier <greg@grossmeier.net> http://grossmeier.net/ <greg@web>
Jimmy Tang <jtang@tchpc.tcd.ie> jtang <jtang@web>
Joachim Breitner <mail@joachim-breitner.de> http://www.joachim-breitner.de/ <nomeata@web>
Joey Hess <id@joeyh.name> Joey Hess <joey@gnu.kitenet.net>
Joey Hess <id@joeyh.name> Joey Hess <joey@kitenet.net>
Joey Hess <id@joeyh.name> Joey Hess <joeyh@debian.org>
Joey Hess <id@joeyh.name> Joey Hess <joeyh@fischer.debian.org>
Joey Hess <id@joeyh.name> Joey Hess <joeyh@joeyh.name>
Joey Hess <id@joeyh.name> Joey Hess <joeyh@oberon.tam-lin.net>
Joey Hess <id@joeyh.name> Joey Hess <joeyh@oberon.underhill.private>
Joey Hess <id@joeyh.name> http://joey.kitenet.net/ <joey@web> Joey Hess <id@joeyh.name> http://joey.kitenet.net/ <joey@web>
Joey Hess <id@joeyh.name> http://joeyh.name/ <joey@web>
Joey Hess <id@joeyh.name> http://joeyh.name/ <http://joeyh.name/@web> Joey Hess <id@joeyh.name> http://joeyh.name/ <http://joeyh.name/@web>
Joey Hess <id@joeyh.name> http://joeyh.name/ <joey@web>
Joey Hess <id@joeyh.name> https://www.google.com/accounts/o8/id?id=AItOawmJfIszzreLNvCqzqzvTayA9_9L6gb9RtY <Joey@web>
Johan Kiviniemi <devel@johan.kiviniemi.name> http://johan.kiviniemi.name/ <Johan@web>
Johan Kiviniemi <devel@johan.kiviniemi.name> http://johan.kiviniemi.name/ <http://johan.kiviniemi.name/@web>
Nicolas Pouillard <nicolas.pouillard@gmail.com> http://ertai.myopenid.com/ <npouillard@web>
Peter Simons <simons@cryp.to> Peter Simons <simons@ubuntu-12.04>
Peter Simons <simons@cryp.to> http://peter-simons.myopenid.com/ <http://peter-simons.myopenid.com/@web>
Philipp Kern <pkern@debian.org> http://phil.0x539.de/ <Philipp_Kern@web>
Richard Hartmann <richih@debian.org> https://www.google.com/accounts/o8/id?id=AItOawl9sYlePmv1xK-VvjBdN-5doOa_Xw-jH4U <Richard@web>
Yaroslav Halchenko <debian@onerussian.com> Yaroslav Halchenko <debian@onerussian.com>
Yaroslav Halchenko <debian@onerussian.com> http://yarikoptic.myopenid.com/ <site-myopenid@web> Yaroslav Halchenko <debian@onerussian.com> http://yarikoptic.myopenid.com/ <site-myopenid@web>
Yaroslav Halchenko <debian@onerussian.com> https://www.google.com/accounts/o8/id?id=AItOawnx8kHW66N3BqmkVpgtXDlYMvr8TJ5VvfY <Yaroslav@web> Yaroslav Halchenko <debian@onerussian.com> https://www.google.com/accounts/o8/id?id=AItOawnx8kHW66N3BqmkVpgtXDlYMvr8TJ5VvfY <Yaroslav@web>
Richard Hartmann <richih@debian.org> https://www.google.com/accounts/o8/id?id=AItOawl9sYlePmv1xK-VvjBdN-5doOa_Xw-jH4U <Richard@web> Øyvind A. Holm <sunny@sunbase.org> http://sunny256.sunbase.org/ <sunny256@web>
Øyvind A. Holm <sunny@sunbase.org> https://sunny256.wordpress.com/ <sunny256@web>

View file

@ -5,7 +5,7 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, PackageImports #-} {-# LANGUAGE CPP, GeneralizedNewtypeDeriving, PackageImports, BangPatterns #-}
module Annex ( module Annex (
Annex, Annex,
@ -32,6 +32,7 @@ module Annex (
getRemoteGitConfig, getRemoteGitConfig,
withCurrentState, withCurrentState,
changeDirectory, changeDirectory,
incError,
) where ) where
import Common import Common
@ -312,3 +313,9 @@ changeDirectory d = do
liftIO $ setCurrentDirectory d liftIO $ setCurrentDirectory d
r' <- liftIO $ Git.relPath r r' <- liftIO $ Git.relPath r
changeState $ \s -> s { repo = r' } changeState $ \s -> s { repo = r' }
incError :: Annex ()
incError = changeState $ \s ->
let ! c = errcounter s + 1
! s' = s { errcounter = c }
in s'

View file

@ -280,17 +280,19 @@ withTmp key action = do
{- Checks that there is disk space available to store a given key, {- Checks that there is disk space available to store a given key,
- in a destination (or the annex) printing a warning if not. -} - in a destination (or the annex) printing a warning if not. -}
checkDiskSpace :: Maybe FilePath -> Key -> Integer -> Annex Bool checkDiskSpace :: Maybe FilePath -> Key -> Integer -> Annex Bool
checkDiskSpace destination key alreadythere = do checkDiskSpace destination key alreadythere = ifM (Annex.getState Annex.force)
reserve <- annexDiskReserve <$> Annex.getGitConfig ( return True
free <- liftIO . getDiskFree =<< dir , do
force <- Annex.getState Annex.force reserve <- annexDiskReserve <$> Annex.getGitConfig
case (free, keySize key) of free <- liftIO . getDiskFree =<< dir
(Just have, Just need) -> do case (free, fromMaybe 1 (keySize key)) of
let ok = (need + reserve <= have + alreadythere) || force (Just have, need) -> do
unless ok $ let ok = (need + reserve <= have + alreadythere)
needmorespace (need + reserve - have - alreadythere) unless ok $
return ok needmorespace (need + reserve - have - alreadythere)
_ -> return True return ok
_ -> return True
)
where where
dir = maybe (fromRepo gitAnnexDir) return destination dir = maybe (fromRepo gitAnnexDir) return destination
needmorespace n = needmorespace n =
@ -498,9 +500,9 @@ getKeysPresent keyloc = do
direct <- isDirect direct <- isDirect
dir <- fromRepo gitAnnexObjectDir dir <- fromRepo gitAnnexObjectDir
s <- getstate direct s <- getstate direct
liftIO $ traverse s direct (2 :: Int) dir liftIO $ walk s direct (2 :: Int) dir
where where
traverse s direct depth dir = do walk s direct depth dir = do
contents <- catchDefaultIO [] (dirContents dir) contents <- catchDefaultIO [] (dirContents dir)
if depth == 0 if depth == 0
then do then do
@ -508,7 +510,7 @@ getKeysPresent keyloc = do
let keys = mapMaybe (fileKey . takeFileName) contents' let keys = mapMaybe (fileKey . takeFileName) contents'
continue keys [] continue keys []
else do else do
let deeper = traverse s direct (depth - 1) let deeper = walk s direct (depth - 1)
continue [] (map deeper contents) continue [] (map deeper contents)
continue keys [] = return keys continue keys [] = return keys
continue keys (a:as) = do continue keys (a:as) = do

View file

@ -9,7 +9,7 @@ module Annex.Drop where
import Common.Annex import Common.Annex
import Logs.Trust import Logs.Trust
import Config.NumCopies import Annex.NumCopies
import Types.Remote (uuid) import Types.Remote (uuid)
import Types.Key (key2file) import Types.Key (key2file)
import qualified Remote import qualified Remote

View file

@ -57,15 +57,15 @@ genDescription Nothing = do
initialize :: Maybe String -> Annex () initialize :: Maybe String -> Annex ()
initialize mdescription = do initialize mdescription = do
{- This will make the first commit to git, so ensure git is set up
- properly to allow commits when running it. -}
ensureCommit $ Annex.Branch.create
prepUUID prepUUID
initialize' initialize'
u <- getUUID u <- getUUID
{- This will make the first commit to git, so ensure git is set up describeUUID u =<< genDescription mdescription
- properly to allow commits when running it. -}
ensureCommit $ do
Annex.Branch.create
describeUUID u =<< genDescription mdescription
-- Everything except for uuid setup. -- Everything except for uuid setup.
initialize' :: Annex () initialize' :: Annex ()

View file

@ -1,11 +1,11 @@
{- git-annex numcopies configuration {- git-annex numcopies configuration and checking
- -
- Copyright 2014 Joey Hess <id@joeyh.name> - Copyright 2014-2015 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
module Config.NumCopies ( module Annex.NumCopies (
module Types.NumCopies, module Types.NumCopies,
module Logs.NumCopies, module Logs.NumCopies,
getFileNumCopies, getFileNumCopies,
@ -15,6 +15,8 @@ module Config.NumCopies (
defaultNumCopies, defaultNumCopies,
numCopiesCheck, numCopiesCheck,
numCopiesCheck', numCopiesCheck',
verifyEnoughCopies,
knownCopies,
) where ) where
import Common.Annex import Common.Annex
@ -24,6 +26,8 @@ import Logs.NumCopies
import Logs.Trust import Logs.Trust
import Annex.CheckAttr import Annex.CheckAttr
import qualified Remote import qualified Remote
import Annex.UUID
import Annex.Content
defaultNumCopies :: NumCopies defaultNumCopies :: NumCopies
defaultNumCopies = NumCopies 1 defaultNumCopies = NumCopies 1
@ -83,3 +87,61 @@ numCopiesCheck' :: FilePath -> (Int -> Int -> v) -> [UUID] -> Annex v
numCopiesCheck' file vs have = do numCopiesCheck' file vs have = do
NumCopies needed <- getFileNumCopies file NumCopies needed <- getFileNumCopies file
return $ length have `vs` needed return $ length have `vs` needed
{- Verifies that enough copies of a key exist amoung the listed remotes,
- priting an informative message if not.
-}
verifyEnoughCopies
:: String -- message to print when there are no known locations
-> Key
-> NumCopies
-> [UUID] -- repos to skip (generally untrusted remotes)
-> [UUID] -- repos that are trusted or already verified to have it
-> [Remote] -- remotes to check to see if they have it
-> Annex Bool
verifyEnoughCopies nolocmsg key need skip trusted tocheck =
helper [] [] (nub trusted) (nub tocheck)
where
helper bad missing have []
| NumCopies (length have) >= need = return True
| otherwise = do
notEnoughCopies key need have (skip++missing) bad nolocmsg
return False
helper bad missing have (r:rs)
| NumCopies (length have) >= need = return True
| otherwise = do
let u = Remote.uuid r
let duplicate = u `elem` have
haskey <- Remote.hasKey r key
case (duplicate, haskey) of
(False, Right True) -> helper bad missing (u:have) rs
(False, Left _) -> helper (r:bad) missing have rs
(False, Right False) -> helper bad (u:missing) have rs
_ -> helper bad missing have rs
notEnoughCopies :: Key -> NumCopies -> [UUID] -> [UUID] -> [Remote] -> String -> Annex ()
notEnoughCopies key need have skip bad nolocmsg = do
showNote "unsafe"
showLongNote $
"Could only verify the existence of " ++
show (length have) ++ " out of " ++ show (fromNumCopies need) ++
" necessary copies"
Remote.showTriedRemotes bad
Remote.showLocations True key (have++skip) nolocmsg
{- Cost ordered lists of remotes that the location log indicates
- may have a key.
-
- Also returns a list of UUIDs that are trusted to have the key
- (some may not have configured remotes). If the current repository
- currently has the key, and is not untrusted, it is included in this list.
-}
knownCopies :: Key -> Annex ([Remote], [UUID])
knownCopies key = do
(remotes, trusteduuids) <- Remote.keyPossibilitiesTrusted key
u <- getUUID
trusteduuids' <- ifM (inAnnex key <&&> (<= SemiTrusted) <$> lookupTrust u)
( pure (u:trusteduuids)
, pure trusteduuids
)
return (remotes, trusteduuids')

View file

@ -69,14 +69,14 @@ annexFileMode = withShared $ return . go
{- Creates a directory inside the gitAnnexDir, including any parent {- Creates a directory inside the gitAnnexDir, including any parent
- directories. Makes directories with appropriate permissions. -} - directories. Makes directories with appropriate permissions. -}
createAnnexDirectory :: FilePath -> Annex () createAnnexDirectory :: FilePath -> Annex ()
createAnnexDirectory dir = traverse dir [] =<< top createAnnexDirectory dir = walk dir [] =<< top
where where
top = parentDir <$> fromRepo gitAnnexDir top = parentDir <$> fromRepo gitAnnexDir
traverse d below stop walk d below stop
| d `equalFilePath` stop = done | d `equalFilePath` stop = done
| otherwise = ifM (liftIO $ doesDirectoryExist d) | otherwise = ifM (liftIO $ doesDirectoryExist d)
( done ( done
, traverse (parentDir d) (d:below) stop , walk (parentDir d) (d:below) stop
) )
where where
done = forM_ below $ \p -> do done = forM_ below $ \p -> do

View file

@ -57,7 +57,6 @@ import Utility.LogFile
#ifdef mingw32_HOST_OS #ifdef mingw32_HOST_OS
import Utility.Env import Utility.Env
import Annex.Path import Annex.Path
import Config.Files
import System.Environment (getArgs) import System.Environment (getArgs)
#endif #endif

View file

@ -16,6 +16,7 @@ import Assistant.Types.NetMessager
import Utility.NotificationBroadcaster import Utility.NotificationBroadcaster
import Logs.Transfer import Logs.Transfer
import Logs.Trust import Logs.Trust
import Logs.TimeStamp
import qualified Remote import qualified Remote
import qualified Types.Remote as Remote import qualified Types.Remote as Remote
import qualified Git import qualified Git
@ -23,8 +24,6 @@ import qualified Git
import Control.Concurrent.STM import Control.Concurrent.STM
import System.Posix.Types import System.Posix.Types
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Data.Time
import System.Locale
import qualified Data.Map as M import qualified Data.Map as M
import qualified Data.Set as S import qualified Data.Set as S
import qualified Data.Text as T import qualified Data.Text as T
@ -125,21 +124,18 @@ readDaemonStatusFile file = parse <$> newDaemonStatus <*> readFile file
where where
parse status = foldr parseline status . lines parse status = foldr parseline status . lines
parseline line status parseline line status
| key == "lastRunning" = parseval readtime $ \v -> | key == "lastRunning" = parseval parsePOSIXTime $ \v ->
status { lastRunning = Just v } status { lastRunning = Just v }
| key == "scanComplete" = parseval readish $ \v -> | key == "scanComplete" = parseval readish $ \v ->
status { scanComplete = v } status { scanComplete = v }
| key == "sanityCheckRunning" = parseval readish $ \v -> | key == "sanityCheckRunning" = parseval readish $ \v ->
status { sanityCheckRunning = v } status { sanityCheckRunning = v }
| key == "lastSanityCheck" = parseval readtime $ \v -> | key == "lastSanityCheck" = parseval parsePOSIXTime $ \v ->
status { lastSanityCheck = Just v } status { lastSanityCheck = Just v }
| otherwise = status -- unparsable line | otherwise = status -- unparsable line
where where
(key, value) = separate (== ':') line (key, value) = separate (== ':') line
parseval parser a = maybe status a (parser value) parseval parser a = maybe status a (parser value)
readtime s = do
d <- parseTime defaultTimeLocale "%s%Qs" s
Just $ utcTimeToPOSIXSeconds d
{- Checks if a time stamp was made after the daemon was lastRunning. {- Checks if a time stamp was made after the daemon was lastRunning.
- -

View file

@ -145,10 +145,12 @@ installFileManagerHooks program = do
, "Name=" ++ command , "Name=" ++ command
, "Icon=git-annex" , "Icon=git-annex"
, unwords , unwords
[ "Exec=sh -c 'cd \"$(dirname '%U')\" &&" [ "Exec=sh -c 'cd \"$(dirname \"$1\")\" &&"
, program , program
, command , command
, "--notify-start --notify-finish -- %U'" , "--notify-start --notify-finish -- \"$1\"'"
, "false" -- this becomes $0 in sh, so unused
, "%f"
] ]
] ]
#else #else

View file

@ -6,6 +6,7 @@
-} -}
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Assistant.Install.AutoStart where module Assistant.Install.AutoStart where

View file

@ -6,6 +6,7 @@
-} -}
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Assistant.Install.Menu where module Assistant.Install.Menu where

View file

@ -81,6 +81,8 @@ data PairingInProgress = PairingInProgress
} }
deriving (Show) deriving (Show)
data AddrClass = IPv4AddrClass | IPv6AddrClass
data SomeAddr = IPv4Addr HostAddress data SomeAddr = IPv4Addr HostAddress
{- My Android build of the Network library does not currently have IPV6 {- My Android build of the Network library does not currently have IPV6
- support. -} - support. -}

View file

@ -88,8 +88,8 @@ bestHostName msg = case remoteHostName $ pairMsgData msg of
fallback = do fallback = do
let a = pairMsgAddr msg let a = pairMsgAddr msg
let sockaddr = case a of let sockaddr = case a of
IPv4Addr addr -> SockAddrInet (PortNum 0) addr IPv4Addr addr -> SockAddrInet (fromInteger 0) addr
IPv6Addr addr -> SockAddrInet6 (PortNum 0) 0 addr 0 IPv6Addr addr -> SockAddrInet6 (fromInteger 0) 0 addr 0
fromMaybe (showAddr a) fromMaybe (showAddr a)
<$> catchDefaultIO Nothing <$> catchDefaultIO Nothing
(fst <$> getNameInfo [] True False sockaddr) (fst <$> getNameInfo [] True False sockaddr)

View file

@ -33,9 +33,9 @@ pairingPort = 55556
{- Goal: Reach all hosts on the same network segment. {- Goal: Reach all hosts on the same network segment.
- Method: Use same address that avahi uses. Other broadcast addresses seem - Method: Use same address that avahi uses. Other broadcast addresses seem
- to not be let through some routers. -} - to not be let through some routers. -}
multicastAddress :: SomeAddr -> HostName multicastAddress :: AddrClass -> HostName
multicastAddress (IPv4Addr _) = "224.0.0.251" multicastAddress IPv4AddrClass = "224.0.0.251"
multicastAddress (IPv6Addr _) = "ff02::fb" multicastAddress IPv6AddrClass = "ff02::fb"
{- Multicasts a message repeatedly on all interfaces, with a 2 second {- Multicasts a message repeatedly on all interfaces, with a 2 second
- delay between each transmission. The message is repeated forever - delay between each transmission. The message is repeated forever
@ -62,7 +62,7 @@ multicastPairMsg repeats secret pairdata stage = go M.empty repeats
sendinterface cache i = void $ tryIO $ sendinterface cache i = void $ tryIO $
withSocketsDo $ bracket setup cleanup use withSocketsDo $ bracket setup cleanup use
where where
setup = multicastSender (multicastAddress i) pairingPort setup = multicastSender (multicastAddress IPv4AddrClass) pairingPort
cleanup (sock, _) = sClose sock -- FIXME does not work cleanup (sock, _) = sClose sock -- FIXME does not work
use (sock, addr) = do use (sock, addr) = do
setInterface sock (showAddr i) setInterface sock (showAddr i)

View file

@ -196,7 +196,7 @@ maxCommitSize :: Int
maxCommitSize = 5000 maxCommitSize = 5000
{- Decide if now is a good time to make a commit. {- Decide if now is a good time to make a commit.
- Note that the list of changes has an undefined order. - Note that the list of changes has a random order.
- -
- Current strategy: If there have been 10 changes within the past second, - Current strategy: If there have been 10 changes within the past second,
- a batch activity is taking place, so wait for later. - a batch activity is taking place, so wait for later.

View file

@ -63,11 +63,7 @@ dbusThread urlrenderer = do
wasmounted <- liftIO $ swapMVar mvar nowmounted wasmounted <- liftIO $ swapMVar mvar nowmounted
handleMounts urlrenderer wasmounted nowmounted handleMounts urlrenderer wasmounted nowmounted
liftIO $ forM_ mountChanged $ \matcher -> liftIO $ forM_ mountChanged $ \matcher ->
#if MIN_VERSION_dbus(0,10,7)
void $ addMatch client matcher handleevent void $ addMatch client matcher handleevent
#else
listen client matcher handleevent
#endif
, do , do
liftAnnex $ liftAnnex $
warning "No known volume monitor available through dbus; falling back to mtab polling" warning "No known volume monitor available through dbus; falling back to mtab polling"

View file

@ -112,11 +112,7 @@ checkNetMonitor client = do
-} -}
listenNMConnections :: Client -> (Bool -> IO ()) -> IO () listenNMConnections :: Client -> (Bool -> IO ()) -> IO ()
listenNMConnections client setconnected = listenNMConnections client setconnected =
#if MIN_VERSION_dbus(0,10,7)
void $ addMatch client matcher void $ addMatch client matcher
#else
listen client matcher
#endif
$ \event -> mapM_ handleevent $ \event -> mapM_ handleevent
(map dictionaryItems $ mapMaybe fromVariant $ signalBody event) (map dictionaryItems $ mapMaybe fromVariant $ signalBody event)
where where
@ -166,11 +162,7 @@ listenWicdConnections client setconnected = do
| any (== wicd_disconnected) status = setconnected False | any (== wicd_disconnected) status = setconnected False
| otherwise = noop | otherwise = noop
match matcher a = match matcher a =
#if MIN_VERSION_dbus(0,10,7)
void $ addMatch client matcher a void $ addMatch client matcher a
#else
listen client matcher a
#endif
#endif #endif
handleConnection :: Assistant () handleConnection :: Assistant ()

View file

@ -31,7 +31,7 @@ pairListenerThread urlrenderer = namedThread "PairListener" $ do
where where
{- Note this can crash if there's no network interface, {- Note this can crash if there's no network interface,
- or only one like lo that doesn't support multicast. -} - or only one like lo that doesn't support multicast. -}
getsock = multicastReceiver (multicastAddress $ IPv4Addr undefined) pairingPort getsock = multicastReceiver (multicastAddress IPv4AddrClass) pairingPort
go reqs cache sock = liftIO (getmsg sock []) >>= \msg -> case readish msg of go reqs cache sock = liftIO (getmsg sock []) >>= \msg -> case readish msg of
Nothing -> go reqs cache sock Nothing -> go reqs cache sock

View file

@ -78,4 +78,5 @@ selectNextPush lastpushedto l = go [] l
(Pushing clientid _) (Pushing clientid _)
| Just clientid /= lastpushedto -> (m, rejected ++ ms) | Just clientid /= lastpushedto -> (m, rejected ++ ms)
_ -> go (m:rejected) ms _ -> go (m:rejected) ms
go [] [] = undefined go [] [] = error "empty push queue"

View file

@ -8,7 +8,8 @@
module Assistant.Types.BranchChange where module Assistant.Types.BranchChange where
import Control.Concurrent.MSampleVar import Control.Concurrent.MSampleVar
import Common.Annex import Control.Applicative
import Prelude
newtype BranchChangeHandle = BranchChangeHandle (MSampleVar ()) newtype BranchChangeHandle = BranchChangeHandle (MSampleVar ())

View file

@ -1,260 +0,0 @@
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Helper functions for creating forms when using Bootstrap v3.
-- This is a copy of the Yesod.Form.Bootstrap3 module that has been slightly
-- modified to be compatible with Yesod 1.0.1
module Assistant.WebApp.Bootstrap3
( -- * Rendering forms
renderBootstrap3
, BootstrapFormLayout(..)
, BootstrapGridOptions(..)
-- * Field settings
, bfs
, withPlaceholder
, withAutofocus
, withLargeInput
, withSmallInput
-- * Submit button
, bootstrapSubmit
, mbootstrapSubmit
, BootstrapSubmit(..)
) where
import Control.Arrow (second)
import Control.Monad (liftM)
import Data.Text (Text)
import Data.String (IsString(..))
import Yesod.Core
import qualified Data.Text as T
import Yesod.Form.Types
import Yesod.Form.Functions
-- | Create a new 'FieldSettings' with the classes that are
-- required by Bootstrap v3.
--
-- Since: yesod-form 1.3.8
bfs :: RenderMessage site msg => msg -> FieldSettings site
bfs msg =
FieldSettings (SomeMessage msg) Nothing Nothing Nothing [("class", "form-control")]
-- | Add a placeholder attribute to a field. If you need i18n
-- for the placeholder, currently you\'ll need to do a hack and
-- use 'getMessageRender' manually.
--
-- Since: yesod-form 1.3.8
withPlaceholder :: Text -> FieldSettings site -> FieldSettings site
withPlaceholder placeholder fs = fs { fsAttrs = newAttrs }
where newAttrs = ("placeholder", placeholder) : fsAttrs fs
-- | Add an autofocus attribute to a field.
--
-- Since: yesod-form 1.3.8
withAutofocus :: FieldSettings site -> FieldSettings site
withAutofocus fs = fs { fsAttrs = newAttrs }
where newAttrs = ("autofocus", "autofocus") : fsAttrs fs
-- | Add the @input-lg@ CSS class to a field.
--
-- Since: yesod-form 1.3.8
withLargeInput :: FieldSettings site -> FieldSettings site
withLargeInput fs = fs { fsAttrs = newAttrs }
where newAttrs = addClass "input-lg" (fsAttrs fs)
-- | Add the @input-sm@ CSS class to a field.
--
-- Since: yesod-form 1.3.8
withSmallInput :: FieldSettings site -> FieldSettings site
withSmallInput fs = fs { fsAttrs = newAttrs }
where newAttrs = addClass "input-sm" (fsAttrs fs)
addClass :: Text -> [(Text, Text)] -> [(Text, Text)]
addClass klass [] = [("class", klass)]
addClass klass (("class", old):rest) = ("class", T.concat [old, " ", klass]) : rest
addClass klass (other :rest) = other : addClass klass rest
-- | How many bootstrap grid columns should be taken (see
-- 'BootstrapFormLayout').
--
-- Since: yesod-form 1.3.8
data BootstrapGridOptions =
ColXs !Int
| ColSm !Int
| ColMd !Int
| ColLg !Int
deriving (Eq, Ord, Show)
toColumn :: BootstrapGridOptions -> String
toColumn (ColXs 0) = ""
toColumn (ColSm 0) = ""
toColumn (ColMd 0) = ""
toColumn (ColLg 0) = ""
toColumn (ColXs columns) = "col-xs-" ++ show columns
toColumn (ColSm columns) = "col-sm-" ++ show columns
toColumn (ColMd columns) = "col-md-" ++ show columns
toColumn (ColLg columns) = "col-lg-" ++ show columns
toOffset :: BootstrapGridOptions -> String
toOffset (ColXs 0) = ""
toOffset (ColSm 0) = ""
toOffset (ColMd 0) = ""
toOffset (ColLg 0) = ""
toOffset (ColXs columns) = "col-xs-offset-" ++ show columns
toOffset (ColSm columns) = "col-sm-offset-" ++ show columns
toOffset (ColMd columns) = "col-md-offset-" ++ show columns
toOffset (ColLg columns) = "col-lg-offset-" ++ show columns
addGO :: BootstrapGridOptions -> BootstrapGridOptions -> BootstrapGridOptions
addGO (ColXs a) (ColXs b) = ColXs (a+b)
addGO (ColSm a) (ColSm b) = ColSm (a+b)
addGO (ColMd a) (ColMd b) = ColMd (a+b)
addGO (ColLg a) (ColLg b) = ColLg (a+b)
addGO a b | a > b = addGO b a
addGO (ColXs a) other = addGO (ColSm a) other
addGO (ColSm a) other = addGO (ColMd a) other
addGO (ColMd a) other = addGO (ColLg a) other
addGO (ColLg _) _ = error "Yesod.Form.Bootstrap.addGO: never here"
-- | The layout used for the bootstrap form.
--
-- Since: yesod-form 1.3.8
data BootstrapFormLayout =
BootstrapBasicForm
| BootstrapInlineForm
| BootstrapHorizontalForm
{ bflLabelOffset :: !BootstrapGridOptions
, bflLabelSize :: !BootstrapGridOptions
, bflInputOffset :: !BootstrapGridOptions
, bflInputSize :: !BootstrapGridOptions
}
deriving (Show)
-- | Render the given form using Bootstrap v3 conventions.
--
-- Sample Hamlet for 'BootstrapHorizontalForm':
--
-- > <form .form-horizontal role=form method=post action=@{ActionR} enctype=#{formEnctype}>
-- > ^{formWidget}
-- > ^{bootstrapSubmit MsgSubmit}
--
-- Since: yesod-form 1.3.8
renderBootstrap3 :: BootstrapFormLayout -> FormRender sub master a
renderBootstrap3 formLayout aform fragment = do
(res, views') <- aFormToForm aform
let views = views' []
has (Just _) = True
has Nothing = False
widget = [whamlet|
#{fragment}
$forall view <- views
<div .form-group :fvRequired view:.required :not $ fvRequired view:.optional :has $ fvErrors view:.has-error>
$case formLayout
$of BootstrapBasicForm
$if nequals (fvId view) bootstrapSubmitId
<label for=#{fvId view}>#{fvLabel view}
^{fvInput view}
^{helpWidget view}
$of BootstrapInlineForm
$if nequals (fvId view) bootstrapSubmitId
<label .sr-only for=#{fvId view}>#{fvLabel view}
^{fvInput view}
^{helpWidget view}
$of BootstrapHorizontalForm _a _b _c _d
$if nequals (fvId view) bootstrapSubmitId
<label .control-label .#{toOffset (bflLabelOffset formLayout)} .#{toColumn (bflLabelSize formLayout)} for=#{fvId view}>#{fvLabel view}
<div .#{toOffset (bflInputOffset formLayout)} .#{toColumn (bflInputSize formLayout)}>
^{fvInput view}
^{helpWidget view}
$else
<div .#{toOffset (addGO (bflInputOffset formLayout) (addGO (bflLabelOffset formLayout) (bflLabelSize formLayout)))} .#{toColumn (bflInputSize formLayout)}>
^{fvInput view}
^{helpWidget view}
|]
return (res, widget)
where
nequals a b = a /= b -- work around older hamlet versions not liking /=
-- | (Internal) Render a help widget for tooltips and errors.
helpWidget :: FieldView sub master -> GWidget sub master ()
helpWidget view = [whamlet|
$maybe tt <- fvTooltip view
<span .help-block>#{tt}
$maybe err <- fvErrors view
<span .help-block>#{err}
|]
-- | How the 'bootstrapSubmit' button should be rendered.
--
-- Since: yesod-form 1.3.8
data BootstrapSubmit msg =
BootstrapSubmit
{ bsValue :: msg
-- ^ The text of the submit button.
, bsClasses :: Text
-- ^ Classes added to the @<button>@.
, bsAttrs :: [(Text, Text)]
-- ^ Attributes added to the @<button>@.
} deriving (Show)
instance IsString msg => IsString (BootstrapSubmit msg) where
fromString msg = BootstrapSubmit (fromString msg) " btn-default " []
-- | A Bootstrap v3 submit button disguised as a field for
-- convenience. For example, if your form currently is:
--
-- > Person <$> areq textField "Name" Nothing
-- > <*> areq textField "Surname" Nothing
--
-- Then just change it to:
--
-- > Person <$> areq textField "Name" Nothing
-- > <*> areq textField "Surname" Nothing
-- > <* bootstrapSubmit "Register"
--
-- (Note that @<*@ is not a typo.)
--
-- Alternatively, you may also just create the submit button
-- manually as well in order to have more control over its
-- layout.
--
-- Since: yesod-form 1.3.8
bootstrapSubmit :: (RenderMessage master msg) => BootstrapSubmit msg -> AForm sub master ()
bootstrapSubmit = formToAForm . liftM (second return) . mbootstrapSubmit
-- | Same as 'bootstrapSubmit' but for monadic forms. This isn't
-- as useful since you're not going to use 'renderBootstrap3'
-- anyway.
--
-- Since: yesod-form 1.3.8
mbootstrapSubmit :: (RenderMessage master msg) => BootstrapSubmit msg -> MForm sub master (FormResult (), FieldView sub master)
mbootstrapSubmit (BootstrapSubmit msg classes attrs) =
let res = FormSuccess ()
widget = [whamlet|<button class="btn #{classes}" type=submit *{attrs}>_{msg}|]
fv = FieldView { fvLabel = ""
, fvTooltip = Nothing
, fvId = bootstrapSubmitId
, fvInput = widget
, fvErrors = Nothing
, fvRequired = False }
in return (res, fv)
-- | A royal hack. Magic id used to identify whether a field
-- should have no label. A valid HTML4 id which is probably not
-- going to clash with any other id should someone use
-- 'bootstrapSubmit' outside 'renderBootstrap3'.
bootstrapSubmitId :: Text
bootstrapSubmitId = "b:ootstrap___unique__:::::::::::::::::submit-id"

View file

@ -5,8 +5,6 @@
- Licensed under the GNU AGPL version 3 or higher. - Licensed under the GNU AGPL version 3 or higher.
-} -}
{-# LANGUAGE CPP #-}
module Assistant.WebApp.Common (module X) where module Assistant.WebApp.Common (module X) where
import Assistant.Common as X import Assistant.Common as X
@ -15,9 +13,5 @@ import Assistant.WebApp.Page as X
import Assistant.WebApp.Form as X import Assistant.WebApp.Form as X
import Assistant.WebApp.Types as X import Assistant.WebApp.Types as X
import Assistant.WebApp.RepoId as X import Assistant.WebApp.RepoId as X
#if MIN_VERSION_yesod(1,2,0)
import Utility.Yesod as X hiding (textField, passwordField, insertBy, replace, joinPath, deleteBy, delete, insert, Key, Option) import Utility.Yesod as X hiding (textField, passwordField, insertBy, replace, joinPath, deleteBy, delete, insert, Key, Option)
#else
import Utility.Yesod as X hiding (textField, passwordField, selectField, selectFieldList, insertBy, replace, joinPath, deleteBy, delete, insert, Key, Option)
#endif
import Data.Text as X (Text) import Data.Text as X (Text)

View file

@ -6,6 +6,7 @@
-} -}
{-# LANGUAGE CPP, QuasiQuotes, TemplateHaskell, OverloadedStrings #-} {-# LANGUAGE CPP, QuasiQuotes, TemplateHaskell, OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
module Assistant.WebApp.Configurators.Edit where module Assistant.WebApp.Configurators.Edit where

View file

@ -5,7 +5,8 @@
- Licensed under the GNU AGPL version 3 or higher. - Licensed under the GNU AGPL version 3 or higher.
-} -}
{-# LANGUAGE CPP, QuasiQuotes, TemplateHaskell, OverloadedStrings, RankNTypes, KindSignatures, TypeFamilies #-} {-# LANGUAGE CPP, QuasiQuotes, TemplateHaskell, OverloadedStrings #-}
{-# LANGUAGE RankNTypes, KindSignatures, TypeFamilies, FlexibleContexts #-}
module Assistant.WebApp.Configurators.Local where module Assistant.WebApp.Configurators.Local where
@ -50,18 +51,10 @@ data RepositoryPath = RepositoryPath Text
- -
- Validates that the path entered is not empty, and is a safe value - Validates that the path entered is not empty, and is a safe value
- to use as a repository. -} - to use as a repository. -}
#if MIN_VERSION_yesod(1,2,0)
repositoryPathField :: forall (m :: * -> *). (MonadIO m, HandlerSite m ~ WebApp) => Bool -> Field m Text repositoryPathField :: forall (m :: * -> *). (MonadIO m, HandlerSite m ~ WebApp) => Bool -> Field m Text
#else
repositoryPathField :: forall sub. Bool -> Field sub WebApp Text
#endif
repositoryPathField autofocus = Field repositoryPathField autofocus = Field
#if ! MIN_VERSION_yesod_form(1,2,0)
{ fieldParse = parse
#else
{ fieldParse = \l _ -> parse l { fieldParse = \l _ -> parse l
, fieldEnctype = UrlEncoded , fieldEnctype = UrlEncoded
#endif
, fieldView = view , fieldView = view
} }
where where

View file

@ -39,7 +39,7 @@ import Git
import qualified Data.Text as T import qualified Data.Text as T
#ifdef WITH_PAIRING #ifdef WITH_PAIRING
import qualified Data.Text.Encoding as T import qualified Data.Text.Encoding as T
import qualified Data.ByteString.Lazy as B import qualified Data.ByteString as B
import Data.Char import Data.Char
import qualified Control.Exception as E import qualified Control.Exception as E
import Control.Concurrent import Control.Concurrent
@ -304,7 +304,7 @@ secretProblem s
| otherwise = Nothing | otherwise = Nothing
toSecret :: Text -> Secret toSecret :: Text -> Secret
toSecret s = B.fromChunks [T.encodeUtf8 $ T.toLower $ T.filter isAlphaNum s] toSecret s = T.encodeUtf8 $ T.toLower $ T.filter isAlphaNum s
{- From Dickens -} {- From Dickens -}
sampleQuote :: Text sampleQuote :: Text

View file

@ -17,7 +17,7 @@ import qualified Annex
import qualified Git import qualified Git
import Config import Config
import Config.Files import Config.Files
import Config.NumCopies import Annex.NumCopies
import Utility.DataUnits import Utility.DataUnits
import Git.Config import Git.Config
import Types.Distribution import Types.Distribution

View file

@ -6,7 +6,7 @@
-} -}
{-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings #-} {-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings #-}
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP, FlexibleContexts #-}
module Assistant.WebApp.Configurators.Ssh where module Assistant.WebApp.Configurators.Ssh where
@ -86,11 +86,7 @@ mkSshInput s = SshInput
, inputPort = sshPort s , inputPort = sshPort s
} }
#if MIN_VERSION_yesod(1,2,0)
sshInputAForm :: Field Handler Text -> SshInput -> AForm Handler SshInput sshInputAForm :: Field Handler Text -> SshInput -> AForm Handler SshInput
#else
sshInputAForm :: Field WebApp WebApp Text -> SshInput -> AForm WebApp WebApp SshInput
#endif
sshInputAForm hostnamefield d = normalize <$> gen sshInputAForm hostnamefield d = normalize <$> gen
where where
gen = SshInput gen = SshInput
@ -107,7 +103,7 @@ sshInputAForm hostnamefield d = normalize <$> gen
, ("existing ssh key", ExistingSshKey) , ("existing ssh key", ExistingSshKey)
] ]
check_username = checkBool (all (`notElem` "/:@ \t") . T.unpack) check_username = checkBool (all (`notElem` ("/:@ \t" :: String)) . T.unpack)
bad_username textField bad_username textField
bad_username = "bad user name" :: Text bad_username = "bad user name" :: Text

View file

@ -8,28 +8,15 @@
{-# LANGUAGE FlexibleContexts, TypeFamilies, QuasiQuotes #-} {-# LANGUAGE FlexibleContexts, TypeFamilies, QuasiQuotes #-}
{-# LANGUAGE MultiParamTypeClasses, TemplateHaskell #-} {-# LANGUAGE MultiParamTypeClasses, TemplateHaskell #-}
{-# LANGUAGE OverloadedStrings, RankNTypes #-} {-# LANGUAGE OverloadedStrings, RankNTypes #-}
{-# LANGUAGE CPP #-}
module Assistant.WebApp.Form where module Assistant.WebApp.Form where
import Assistant.WebApp.Types import Assistant.WebApp.Types
import Assistant.Gpg import Assistant.Gpg
#if MIN_VERSION_yesod(1,2,0)
import Yesod hiding (textField, passwordField) import Yesod hiding (textField, passwordField)
import Yesod.Form.Fields as F import Yesod.Form.Fields as F
#else
import Yesod hiding (textField, passwordField, selectField, selectFieldList)
import Yesod.Form.Fields as F hiding (selectField, selectFieldList)
import Data.String (IsString (..))
import Control.Monad (unless)
import Data.Maybe (listToMaybe)
#endif
#if MIN_VERSION_yesod_form(1,3,8)
import Yesod.Form.Bootstrap3 as Y hiding (bfs) import Yesod.Form.Bootstrap3 as Y hiding (bfs)
#else
import Assistant.WebApp.Bootstrap3 as Y hiding (bfs)
#endif
import Data.Text (Text) import Data.Text (Text)
{- Yesod's textField sets the required attribute for required fields. {- Yesod's textField sets the required attribute for required fields.
@ -61,60 +48,8 @@ passwordField = F.passwordField
|] |]
} }
{- In older Yesod versions attrs is written into the <option> tag instead of the
- surrounding <select>. This breaks the Bootstrap 3 layout of select fields as
- it requires the "form-control" class on the <select> tag.
- We need to change that to behave the same way as in newer versions.
-}
#if ! MIN_VERSION_yesod(1,2,0)
selectFieldList :: (Eq a, RenderMessage master FormMessage, RenderMessage master msg) => [(msg, a)] -> Field sub master a
selectFieldList = selectField . optionsPairs
selectField :: (Eq a, RenderMessage master FormMessage) => GHandler sub master (OptionList a) -> Field sub master a
selectField = selectFieldHelper
(\theId name attrs inside -> [whamlet|<select ##{theId} name=#{name} *{attrs}>^{inside}|]) -- outside
(\_theId _name isSel -> [whamlet|<option value=none :isSel:selected>_{MsgSelectNone}|]) -- onOpt
(\_theId _name _attrs value isSel text -> [whamlet|<option value=#{value} :isSel:selected>#{text}|]) -- inside
selectFieldHelper :: (Eq a, RenderMessage master FormMessage)
=> (Text -> Text -> [(Text, Text)] -> GWidget sub master () -> GWidget sub master ())
-> (Text -> Text -> Bool -> GWidget sub master ())
-> (Text -> Text -> [(Text, Text)] -> Text -> Bool -> Text -> GWidget sub master ())
-> GHandler sub master (OptionList a) -> Field sub master a
selectFieldHelper outside onOpt inside opts' = Field
{ fieldParse = \x -> do
opts <- opts'
return $ selectParser opts x
, fieldView = \theId name attrs val isReq -> do
opts <- fmap olOptions $ lift opts'
outside theId name attrs $ do
unless isReq $ onOpt theId name $ not $ render opts val `elem` map optionExternalValue opts
flip mapM_ opts $ \opt -> inside
theId
name
((if isReq then (("required", "required"):) else id) attrs)
(optionExternalValue opt)
((render opts val) == optionExternalValue opt)
(optionDisplay opt)
}
where
render _ (Left _) = ""
render opts (Right a) = maybe "" optionExternalValue $ listToMaybe $ filter ((== a) . optionInternalValue) opts
selectParser _ [] = Right Nothing
selectParser opts (s:_) = case s of
"" -> Right Nothing
"none" -> Right Nothing
x -> case olReadExternal opts x of
Nothing -> Left $ SomeMessage $ MsgInvalidEntry x
Just y -> Right $ Just y
#endif
{- Makes a note widget be displayed after a field. -} {- Makes a note widget be displayed after a field. -}
#if MIN_VERSION_yesod(1,2,0)
withNote :: (Monad m, ToWidget (HandlerSite m) a) => Field m v -> a -> Field m v withNote :: (Monad m, ToWidget (HandlerSite m) a) => Field m v -> a -> Field m v
#else
withNote :: Field sub master v -> GWidget sub master () -> Field sub master v
#endif
withNote field note = field { fieldView = newview } withNote field note = field { fieldView = newview }
where where
newview theId name attrs val isReq = newview theId name attrs val isReq =
@ -122,11 +57,7 @@ withNote field note = field { fieldView = newview }
in [whamlet|^{fieldwidget}&nbsp;&nbsp;<span>^{note}</span>|] in [whamlet|^{fieldwidget}&nbsp;&nbsp;<span>^{note}</span>|]
{- Note that the toggle string must be unique on the form. -} {- Note that the toggle string must be unique on the form. -}
#if MIN_VERSION_yesod(1,2,0)
withExpandableNote :: (Monad m, ToWidget (HandlerSite m) w) => Field m v -> (String, w) -> Field m v withExpandableNote :: (Monad m, ToWidget (HandlerSite m) w) => Field m v -> (String, w) -> Field m v
#else
withExpandableNote :: Field sub master v -> (String, GWidget sub master ()) -> Field sub master v
#endif
withExpandableNote field (toggle, note) = withNote field $ [whamlet| withExpandableNote field (toggle, note) = withNote field $ [whamlet|
<a .btn .btn-default data-toggle="collapse" data-target="##{ident}">#{toggle}</a> <a .btn .btn-default data-toggle="collapse" data-target="##{ident}">#{toggle}</a>
<div ##{ident} .collapse> <div ##{ident} .collapse>
@ -136,11 +67,7 @@ withExpandableNote field (toggle, note) = withNote field $ [whamlet|
ident = "toggle_" ++ toggle ident = "toggle_" ++ toggle
{- Adds a check box to an AForm to control encryption. -} {- Adds a check box to an AForm to control encryption. -}
#if MIN_VERSION_yesod(1,2,0)
enableEncryptionField :: (RenderMessage site FormMessage) => AForm (HandlerT site IO) EnableEncryption enableEncryptionField :: (RenderMessage site FormMessage) => AForm (HandlerT site IO) EnableEncryption
#else
enableEncryptionField :: RenderMessage master FormMessage => AForm sub master EnableEncryption
#endif
enableEncryptionField = areq (selectFieldList choices) (bfs "Encryption") (Just SharedEncryption) enableEncryptionField = areq (selectFieldList choices) (bfs "Encryption") (Just SharedEncryption)
where where
choices :: [(Text, EnableEncryption)] choices :: [(Text, EnableEncryption)]

View file

@ -5,13 +5,7 @@
- Licensed under the GNU AGPL version 3 or higher. - Licensed under the GNU AGPL version 3 or higher.
-} -}
{-# LANGUAGE CPP, QuasiQuotes, TemplateHaskell, OverloadedStrings #-} {-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings #-}
#if defined VERSION_yesod_default
#if ! MIN_VERSION_yesod_default(1,1,0)
#define WITH_OLD_YESOD
#endif
#endif
module Assistant.WebApp.Notifications where module Assistant.WebApp.Notifications where
@ -26,9 +20,7 @@ import Utility.WebApp
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
#ifndef WITH_OLD_YESOD
import qualified Data.Aeson.Types as Aeson import qualified Data.Aeson.Types as Aeson
#endif
{- Add to any widget to make it auto-update using long polling. {- Add to any widget to make it auto-update using long polling.
- -
@ -42,15 +34,9 @@ import qualified Data.Aeson.Types as Aeson
-} -}
autoUpdate :: Text -> Route WebApp -> Int -> Int -> Widget autoUpdate :: Text -> Route WebApp -> Int -> Int -> Widget
autoUpdate tident geturl ms_delay ms_startdelay = do autoUpdate tident geturl ms_delay ms_startdelay = do
#ifdef WITH_OLD_YESOD
let delay = show ms_delay
let startdelay = show ms_startdelay
let ident = "'" ++ T.unpack tident ++ "'"
#else
let delay = Aeson.String (T.pack (show ms_delay)) let delay = Aeson.String (T.pack (show ms_delay))
let startdelay = Aeson.String (T.pack (show ms_startdelay)) let startdelay = Aeson.String (T.pack (show ms_startdelay))
let ident = Aeson.String tident let ident = Aeson.String tident
#endif
$(widgetFile "notifications/longpolling") $(widgetFile "notifications/longpolling")
{- Notifier urls are requested by the javascript, to avoid allocation {- Notifier urls are requested by the javascript, to avoid allocation

View file

@ -5,7 +5,7 @@
- Licensed under the GNU AGPL version 3 or higher. - Licensed under the GNU AGPL version 3 or higher.
-} -}
{-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings #-} {-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings, FlexibleContexts #-}
module Assistant.WebApp.SideBar where module Assistant.WebApp.SideBar where

View file

@ -8,7 +8,6 @@
{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses #-}
{-# LANGUAGE TemplateHaskell, OverloadedStrings, RankNTypes #-} {-# LANGUAGE TemplateHaskell, OverloadedStrings, RankNTypes #-}
{-# LANGUAGE FlexibleInstances, FlexibleContexts, ViewPatterns #-} {-# LANGUAGE FlexibleInstances, FlexibleContexts, ViewPatterns #-}
{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
module Assistant.WebApp.Types where module Assistant.WebApp.Types where
@ -83,58 +82,30 @@ instance Yesod WebApp where
instance RenderMessage WebApp FormMessage where instance RenderMessage WebApp FormMessage where
renderMessage _ _ = defaultFormMessage renderMessage _ _ = defaultFormMessage
#if MIN_VERSION_yesod(1,2,0)
instance LiftAnnex Handler where instance LiftAnnex Handler where
#else
instance LiftAnnex (GHandler sub WebApp) where
#endif
liftAnnex a = ifM (noAnnex <$> getYesod) liftAnnex a = ifM (noAnnex <$> getYesod)
( error "internal liftAnnex" ( error "internal liftAnnex"
, liftAssistant $ liftAnnex a , liftAssistant $ liftAnnex a
) )
#if MIN_VERSION_yesod(1,2,0)
instance LiftAnnex (WidgetT WebApp IO) where instance LiftAnnex (WidgetT WebApp IO) where
#else
instance LiftAnnex (GWidget WebApp WebApp) where
#endif
liftAnnex = liftH . liftAnnex liftAnnex = liftH . liftAnnex
class LiftAssistant m where class LiftAssistant m where
liftAssistant :: Assistant a -> m a liftAssistant :: Assistant a -> m a
#if MIN_VERSION_yesod(1,2,0)
instance LiftAssistant Handler where instance LiftAssistant Handler where
#else
instance LiftAssistant (GHandler sub WebApp) where
#endif
liftAssistant a = liftIO . flip runAssistant a liftAssistant a = liftIO . flip runAssistant a
=<< assistantData <$> getYesod =<< assistantData <$> getYesod
#if MIN_VERSION_yesod(1,2,0)
instance LiftAssistant (WidgetT WebApp IO) where instance LiftAssistant (WidgetT WebApp IO) where
#else
instance LiftAssistant (GWidget WebApp WebApp) where
#endif
liftAssistant = liftH . liftAssistant liftAssistant = liftH . liftAssistant
#if MIN_VERSION_yesod(1,2,0)
type MkMForm x = MForm Handler (FormResult x, Widget) type MkMForm x = MForm Handler (FormResult x, Widget)
#else
type MkMForm x = MForm WebApp WebApp (FormResult x, Widget)
#endif
#if MIN_VERSION_yesod(1,2,0)
type MkAForm x = AForm Handler x type MkAForm x = AForm Handler x
#else
type MkAForm x = AForm WebApp WebApp x
#endif
#if MIN_VERSION_yesod(1,2,0) type MkField x = forall m. Monad m => RenderMessage (HandlerSite m) FormMessage => Field m x
type MkField x = Monad m => RenderMessage (HandlerSite m) FormMessage => Field m x
#else
type MkField x = RenderMessage master FormMessage => Field sub master x
#endif
data RepoSelector = RepoSelector data RepoSelector = RepoSelector
{ onlyCloud :: Bool { onlyCloud :: Bool
@ -154,12 +125,6 @@ data RemovableDrive = RemovableDrive
data RepoKey = RepoKey KeyId | NoRepoKey data RepoKey = RepoKey KeyId | NoRepoKey
deriving (Read, Show, Eq, Ord) deriving (Read, Show, Eq, Ord)
#if ! MIN_VERSION_path_pieces(0,1,4)
instance PathPiece Bool where
toPathPiece = pack . show
fromPathPiece = readish . unpack
#endif
instance PathPiece RemovableDrive where instance PathPiece RemovableDrive where
toPathPiece = pack . show toPathPiece = pack . show
fromPathPiece = readish . unpack fromPathPiece = readish . unpack

View file

@ -22,7 +22,8 @@ import qualified Data.Map as M
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import qualified Data.ByteString as B import qualified Data.ByteString as B
import Data.XML.Types import Data.XML.Types
import qualified "dataenc" Codec.Binary.Base64 as B64 import qualified "sandi" Codec.Binary.Base64 as B64
import Data.Bits.Utils
{- Name of the git-annex tag, in our own XML namespace. {- Name of the git-annex tag, in our own XML namespace.
- (Not using a namespace URL to avoid unnecessary bloat.) -} - (Not using a namespace URL to avoid unnecessary bloat.) -}
@ -212,10 +213,10 @@ encodeExitCode (ExitFailure n) = n
{- Base 64 encoding a ByteString to use as the content of a tag. -} {- Base 64 encoding a ByteString to use as the content of a tag. -}
encodeTagContent :: ByteString -> [Node] encodeTagContent :: ByteString -> [Node]
encodeTagContent b = [NodeContent $ ContentText $ T.pack $ B64.encode $ B.unpack b] encodeTagContent b = [NodeContent $ ContentText $ T.pack $ w82s $ B.unpack $ B64.encode b]
decodeTagContent :: Element -> Maybe ByteString decodeTagContent :: Element -> Maybe ByteString
decodeTagContent elt = B.pack <$> B64.decode s decodeTagContent elt = either (const Nothing) Just (B64.decode $ B.pack $ s2w8 s)
where where
s = T.unpack $ T.concat $ elementText elt s = T.unpack $ T.concat $ elementText elt

View file

@ -35,13 +35,14 @@ bundledPrograms = catMaybes
#endif #endif
, Just "rsync" , Just "rsync"
#ifndef darwin_HOST_OS #ifndef darwin_HOST_OS
#ifndef mingw32_HOST_OS
-- OS X has ssh installed by default. -- OS X has ssh installed by default.
-- Linux probably has ssh, but not guaranteed. -- Linux probably has ssh, but not guaranteed.
-- On Windows, msysgit provides ssh, but not in PATH, -- On Windows, msysgit provides ssh.
-- so we ship our own.
, Just "ssh" , Just "ssh"
, Just "ssh-keygen" , Just "ssh-keygen"
#endif #endif
#endif
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
, Just "sh" , Just "sh"
#endif #endif

View file

@ -1,5 +1,7 @@
{- Checks system configuration and generates SysConfig.hs. -} {- Checks system configuration and generates SysConfig.hs. -}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Build.Configure where module Build.Configure where
import System.Directory import System.Directory

View file

@ -7,6 +7,7 @@
-} -}
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Build.DesktopFile where module Build.DesktopFile where

View file

@ -10,7 +10,7 @@
import Common.Annex import Common.Annex
import Types.Distribution import Types.Distribution
import Build.Version import Build.Version (getChangelogVersion, Version)
import Utility.UserInfo import Utility.UserInfo
import Utility.Url import Utility.Url
import qualified Git.Construct import qualified Git.Construct

View file

@ -1,7 +1,8 @@
{- Generates a NullSoft installer program for git-annex on Windows. {- Generates a NullSoft installer program for git-annex on Windows.
- -
- To build the installer, git-annex should already be built by cabal, - To build the installer, git-annex should already be built by cabal,
- and ssh and rsync, as well as cygwin libraries, already installed. - and ssh and rsync etc, as well as cygwin libraries, already installed
- from cygwin.
- -
- This uses the Haskell nsis package (cabal install nsis) - This uses the Haskell nsis package (cabal install nsis)
- to generate a .nsi file, which is then used to produce - to generate a .nsi file, which is then used to produce
@ -11,7 +12,7 @@
- exception of git. The user needs to install git separately, - exception of git. The user needs to install git separately,
- and the installer checks for that. - and the installer checks for that.
- -
- Copyright 2013 Joey Hess <id@joeyh.name> - Copyright 2013-2015 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -22,13 +23,17 @@ import Development.NSIS
import System.Directory import System.Directory
import System.FilePath import System.FilePath
import Control.Monad import Control.Monad
import Control.Applicative
import Data.String import Data.String
import Data.Maybe import Data.Maybe
import Data.Char
import Data.List (nub, isPrefixOf)
import Utility.Tmp import Utility.Tmp
import Utility.Path import Utility.Path
import Utility.CopyFile import Utility.CopyFile
import Utility.SafeCommand import Utility.SafeCommand
import Utility.Process
import Build.BundledPrograms import Build.BundledPrograms
main = do main = do
@ -37,17 +42,19 @@ main = do
mustSucceed "ln" [File "dist/build/git-annex/git-annex.exe", File gitannex] mustSucceed "ln" [File "dist/build/git-annex/git-annex.exe", File gitannex]
let license = tmpdir </> licensefile let license = tmpdir </> licensefile
mustSucceed "sh" [Param "-c", Param $ "zcat standalone/licences.gz > '" ++ license ++ "'"] mustSucceed "sh" [Param "-c", Param $ "zcat standalone/licences.gz > '" ++ license ++ "'"]
extrabins <- forM (cygwinPrograms ++ cygwinDlls) $ \f -> do extrabins <- forM (cygwinPrograms) $ \f -> do
p <- searchPath f p <- searchPath f
when (isNothing p) $ when (isNothing p) $
print ("unable to find in PATH", f) print ("unable to find in PATH", f)
return p return p
dlls <- forM (catMaybes extrabins) findCygLibs
dllpaths <- mapM searchPath (nub (concat dlls))
webappscript <- vbsLauncher tmpdir "git-annex-webapp" "git-annex webapp" webappscript <- vbsLauncher tmpdir "git-annex-webapp" "git-annex webapp"
autostartscript <- vbsLauncher tmpdir "git-annex-autostart" "git annex assistant --autostart" autostartscript <- vbsLauncher tmpdir "git-annex-autostart" "git annex assistant --autostart"
let htmlhelp = tmpdir </> "git-annex.html" let htmlhelp = tmpdir </> "git-annex.html"
writeFile htmlhelp htmlHelpText writeFile htmlhelp htmlHelpText
writeFile nsifile $ makeInstaller gitannex license htmlhelp writeFile nsifile $ makeInstaller gitannex license htmlhelp
(catMaybes extrabins) (wrappers ++ catMaybes (extrabins ++ dllpaths))
[ webappscript, autostartscript ] [ webappscript, autostartscript ]
mustSucceed "makensis" [File nsifile] mustSucceed "makensis" [File nsifile]
removeFile nsifile -- left behind if makensis fails removeFile nsifile -- left behind if makensis fails
@ -85,7 +92,7 @@ uninstaller = "git-annex-uninstall.exe"
gitInstallDir :: Exp FilePath gitInstallDir :: Exp FilePath
gitInstallDir = fromString "$PROGRAMFILES\\Git" gitInstallDir = fromString "$PROGRAMFILES\\Git"
-- This intentionall has a different name than git-annex or -- This intentionally has a different name than git-annex or
-- git-annex-webapp, since it is itself treated as an executable file. -- git-annex-webapp, since it is itself treated as an executable file.
-- Also, on XP, the filename is displayed, not the description. -- Also, on XP, the filename is displayed, not the description.
startMenuItem :: Exp FilePath startMenuItem :: Exp FilePath
@ -169,46 +176,6 @@ makeInstaller gitannex license htmlhelp extrabins launchers = nsis $ do
cygwinPrograms :: [FilePath] cygwinPrograms :: [FilePath]
cygwinPrograms = map (\p -> p ++ ".exe") bundledPrograms cygwinPrograms = map (\p -> p ++ ".exe") bundledPrograms
-- These are the dlls needed by Cygwin's rsync, ssh, etc.
-- TODO: Use ldd (available in cygwin) to automatically find all
-- needed libs.
cygwinDlls :: [FilePath]
cygwinDlls =
[ "cygwin1.dll"
, "cygasn1-8.dll"
, "cygattr-1.dll"
, "cygheimbase-1.dll"
, "cygroken-18.dll"
, "cygcom_err-2.dll"
, "cygheimntlm-0.dll"
, "cygsqlite3-0.dll"
, "cygcrypt-0.dll"
, "cyghx509-5.dll"
, "cygssp-0.dll"
, "cygcrypto-1.0.0.dll"
, "cygiconv-2.dll"
, "cyggcc_s-1.dll"
, "cygintl-8.dll"
, "cygwind-0.dll"
, "cyggssapi-3.dll"
, "cygkrb5-26.dll"
, "cygz.dll"
, "cygidn-11.dll"
, "libcurl-4.dll"
, "cyggnutls-26.dll"
, "libcrypto.dll"
, "libssl.dll"
, "cyggcrypt-11.dll"
, "cyggpg-error-0.dll"
, "cygp11-kit-0.dll"
, "cygtasn1-3.dll"
, "cygffi-6.dll"
, "cygbz2-1.dll"
, "cygreadline7.dll"
, "cygncursesw-10.dll"
, "cygusb0.dll"
]
-- msysgit opens Program Files/Git/doc/git/html/git-annex.html -- msysgit opens Program Files/Git/doc/git/html/git-annex.html
-- when git annex --help is run. -- when git annex --help is run.
htmlHelpText :: String htmlHelpText :: String
@ -221,3 +188,18 @@ htmlHelpText = unlines
, "</body>" , "</body>"
, "</html" , "</html"
] ]
-- Find cygwin libraries used by the specified executable.
findCygLibs :: FilePath -> IO [FilePath]
findCygLibs p = filter iscyg . mapMaybe parse . lines <$> readProcess "ldd" [p]
where
parse l = case words (dropWhile isSpace l) of
(dll:"=>":_dllpath:_offset:[]) -> Just dll
_ -> Nothing
iscyg f = "cyg" `isPrefixOf` f || "lib" `isPrefixOf` f
wrappers :: [FilePath]
wrappers =
[ "standalone\\windows\\ssh.cmd"
, "standalone\\windows\\ssh-keygen.cmd"
]

View file

@ -1,5 +1,7 @@
{- Tests the system and generates Build.SysConfig.hs. -} {- Tests the system and generates Build.SysConfig.hs. -}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Build.TestConfig where module Build.TestConfig where
import Utility.Path import Utility.Path

View file

@ -1,5 +1,7 @@
{- Package version determination, for configure script. -} {- Package version determination, for configure script. -}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Build.Version where module Build.Version where
import Data.Maybe import Data.Maybe
@ -18,7 +20,7 @@ type Version = String
{- Set when making an official release. (Distribution vendors should set {- Set when making an official release. (Distribution vendors should set
- this too.) -} - this too.) -}
isReleaseBuild :: IO Bool isReleaseBuild :: IO Bool
isReleaseBuild = isJust <$> catchMaybeIO (getEnv "RELEASE_BUILD") isReleaseBuild = (== Just "1") <$> catchMaybeIO (getEnv "RELEASE_BUILD")
{- Version is usually based on the major version from the changelog, {- Version is usually based on the major version from the changelog,
- plus the date of the last commit, plus the git rev of that commit. - plus the date of the last commit, plus the git rev of that commit.

View file

@ -20,6 +20,7 @@ while (<>) {
s/^[ \n]+//; s/^[ \n]+//;
s/^\t/ /; s/^\t/ /;
s/-/\\-/g; s/-/\\-/g;
s/git\\-annex/git-annex/g;
s/^Warning:.*//g; s/^Warning:.*//g;
s/^$/.PP\n/; s/^$/.PP\n/;
s/^\*\s+(.*)/.IP "$1"/; s/^\*\s+(.*)/.IP "$1"/;

View file

@ -5,8 +5,6 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# LANGUAGE BangPatterns #-}
module CmdLine.Action where module CmdLine.Action where
import Common.Annex import Common.Annex
@ -119,14 +117,11 @@ includeCommandAction a = account =<< tryIO go
account (Right True) = return True account (Right True) = return True
account (Right False) = incerr account (Right False) = incerr
account (Left err) = do account (Left err) = do
showErr err toplevelWarning True (show err)
showEndFail showEndFail
incerr incerr
incerr = do incerr = do
Annex.changeState $ \s -> Annex.incError
let ! c = Annex.errcounter s + 1
! s' = s { Annex.errcounter = c }
in s'
return False return False
{- Runs a single command action through the start, perform and cleanup {- Runs a single command action through the start, perform and cleanup

41
CmdLine/Batch.hs Normal file
View file

@ -0,0 +1,41 @@
{- git-annex batch commands
-
- Copyright 2015 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module CmdLine.Batch where
import Common.Annex
import Command
batchOption :: Option
batchOption = flagOption [] "batch" "enable batch mode"
data BatchMode = Batch | NoBatch
type Batchable t = BatchMode -> t -> CommandStart
-- A Batchable command can run in batch mode, or not.
-- In batch mode, one line at a time is read, parsed, and a reply output to
-- stdout. In non batch mode, the command's parameters are parsed and
-- a reply output for each.
batchable :: ((t -> CommandStart) -> CommandSeek) -> Batchable t -> CommandSeek
batchable seeker starter params = ifM (getOptionFlag batchOption)
( batchloop
, seeker (starter NoBatch) params
)
where
batchloop = do
mp <- liftIO $ catchMaybeIO getLine
case mp of
Nothing -> return ()
Just p -> do
seeker (starter Batch) [p]
batchloop
-- bad input is indicated by an empty line in batch mode. In non batch
-- mode, exit on bad input.
batchBadInput :: BatchMode -> Annex ()
batchBadInput NoBatch = liftIO exitFailure
batchBadInput Batch = liftIO $ putStrLn ""

View file

@ -74,6 +74,7 @@ import qualified Command.Dead
import qualified Command.Group import qualified Command.Group
import qualified Command.Wanted import qualified Command.Wanted
import qualified Command.GroupWanted import qualified Command.GroupWanted
import qualified Command.Required
import qualified Command.Schedule import qualified Command.Schedule
import qualified Command.Ungroup import qualified Command.Ungroup
import qualified Command.Vicfg import qualified Command.Vicfg
@ -149,6 +150,7 @@ cmds = concat
, Command.Group.cmd , Command.Group.cmd
, Command.Wanted.cmd , Command.Wanted.cmd
, Command.GroupWanted.cmd , Command.GroupWanted.cmd
, Command.Required.cmd
, Command.Schedule.cmd , Command.Schedule.cmd
, Command.Ungroup.cmd , Command.Ungroup.cmd
, Command.Vicfg.cmd , Command.Vicfg.cmd

View file

@ -218,8 +218,9 @@ seekHelper a params = do
ll <- inRepo $ \g -> concat <$> forM (segmentXargsOrdered params) ll <- inRepo $ \g -> concat <$> forM (segmentXargsOrdered params)
(runSegmentPaths (\fs -> Git.Command.leaveZombie <$> a fs g)) (runSegmentPaths (\fs -> Git.Command.leaveZombie <$> a fs g))
forM_ (map fst $ filter (null . snd) $ zip params ll) $ \p -> forM_ (map fst $ filter (null . snd) $ zip params ll) $ \p ->
unlessM (isJust <$> liftIO (catchMaybeIO $ getSymbolicLinkStatus p)) $ unlessM (isJust <$> liftIO (catchMaybeIO $ getSymbolicLinkStatus p)) $ do
error $ p ++ " not found" toplevelWarning False (p ++ " not found")
Annex.incError
return $ concat ll return $ concat ll
notSymlink :: FilePath -> IO Bool notSymlink :: FilePath -> IO Bool

View file

@ -116,7 +116,10 @@ start file = ifAnnexed file addpresent add
- Lockdown can fail if a file gets deleted, and Nothing will be returned. - Lockdown can fail if a file gets deleted, and Nothing will be returned.
-} -}
lockDown :: FilePath -> Annex (Maybe KeySource) lockDown :: FilePath -> Annex (Maybe KeySource)
lockDown = either (\e -> showErr e >> return Nothing) (return . Just) <=< lockDown' lockDown = either
(\e -> warning (show e) >> return Nothing)
(return . Just)
<=< lockDown'
lockDown' :: FilePath -> Annex (Either IOException KeySource) lockDown' :: FilePath -> Annex (Either IOException KeySource)
lockDown' file = ifM crippledFileSystem lockDown' file = ifM crippledFileSystem

View file

@ -178,7 +178,7 @@ startWeb relaxed optfile pathdepth s = go $ fromMaybe bad $ parseURI urlstring
pathmax <- liftIO $ fileNameLengthLimit "." pathmax <- liftIO $ fileNameLengthLimit "."
let file = flip fromMaybe optfile $ let file = flip fromMaybe optfile $
truncateFilePath pathmax $ sanitizeFilePath $ truncateFilePath pathmax $ sanitizeFilePath $
Quvi.pageTitle page ++ "." ++ Quvi.linkSuffix link Quvi.pageTitle page ++ "." ++ fromMaybe "m" (Quvi.linkSuffix link)
showStart "addurl" file showStart "addurl" file
next $ performQuvi relaxed urlstring (Quvi.linkUrl link) file next $ performQuvi relaxed urlstring (Quvi.linkUrl link) file
#else #else

View file

@ -20,7 +20,7 @@ import Assistant.Install
import System.Environment import System.Environment
cmd :: [Command] cmd :: [Command]
cmd = [noRepo checkAutoStart $ dontCheck repoExists $ withOptions options $ cmd = [noRepo checkNoRepoOpts $ dontCheck repoExists $ withOptions options $
notBareRepo $ command "assistant" paramNothing seek SectionCommon notBareRepo $ command "assistant" paramNothing seek SectionCommon
"automatically sync changes"] "automatically sync changes"]
@ -30,11 +30,15 @@ options =
, Command.Watch.stopOption , Command.Watch.stopOption
, autoStartOption , autoStartOption
, startDelayOption , startDelayOption
, autoStopOption
] ]
autoStartOption :: Option autoStartOption :: Option
autoStartOption = flagOption [] "autostart" "start in known repositories" autoStartOption = flagOption [] "autostart" "start in known repositories"
autoStopOption :: Option
autoStopOption = flagOption [] "autostop" "stop in known repositories"
startDelayOption :: Option startDelayOption :: Option
startDelayOption = fieldOption [] "startdelay" paramNumber "delay before running startup scan" startDelayOption = fieldOption [] "startdelay" paramNumber "delay before running startup scan"
@ -43,25 +47,31 @@ seek ps = do
stopdaemon <- getOptionFlag Command.Watch.stopOption stopdaemon <- getOptionFlag Command.Watch.stopOption
foreground <- getOptionFlag Command.Watch.foregroundOption foreground <- getOptionFlag Command.Watch.foregroundOption
autostart <- getOptionFlag autoStartOption autostart <- getOptionFlag autoStartOption
autostop <- getOptionFlag autoStopOption
startdelay <- getOptionField startDelayOption (pure . maybe Nothing parseDuration) startdelay <- getOptionField startDelayOption (pure . maybe Nothing parseDuration)
withNothing (start foreground stopdaemon autostart startdelay) ps withNothing (start foreground stopdaemon autostart autostop startdelay) ps
start :: Bool -> Bool -> Bool -> Maybe Duration -> CommandStart start :: Bool -> Bool -> Bool -> Bool -> Maybe Duration -> CommandStart
start foreground stopdaemon autostart startdelay start foreground stopdaemon autostart autostop startdelay
| autostart = do | autostart = do
liftIO $ autoStart startdelay liftIO $ autoStart startdelay
stop stop
| autostop = do
liftIO autoStop
stop
| otherwise = do | otherwise = do
liftIO ensureInstalled liftIO ensureInstalled
ensureInitialized ensureInitialized
Command.Watch.start True foreground stopdaemon startdelay Command.Watch.start True foreground stopdaemon startdelay
{- Run outside a git repository. Check to see if any parameter is {- Run outside a git repository; support autostart and autostop mode. -}
- --autostart and enter autostart mode. -} checkNoRepoOpts :: CmdParams -> IO ()
checkAutoStart :: CmdParams -> IO () checkNoRepoOpts _ = ifM (elem "--autostart" <$> getArgs)
checkAutoStart _ = ifM (elem "--autostart" <$> getArgs)
( autoStart Nothing ( autoStart Nothing
, error "Not in a git repository." , ifM (elem "--autostop" <$> getArgs)
( autoStop
, error "Not in a git repository."
)
) )
autoStart :: Maybe Duration -> IO () autoStart :: Maybe Duration -> IO ()
@ -89,3 +99,15 @@ autoStart startdelay = do
[ Param "assistant" [ Param "assistant"
, Param $ "--startdelay=" ++ fromDuration (fromMaybe (Duration 5) startdelay) , Param $ "--startdelay=" ++ fromDuration (fromMaybe (Duration 5) startdelay)
] ]
autoStop :: IO ()
autoStop = do
dirs <- liftIO readAutoStartFile
program <- programPath
forM_ dirs $ \d -> do
putStrLn $ "git-annex autostop in " ++ d
setCurrentDirectory d
ifM (boolSystem program [Param "assistant", Param "--stop"])
( putStrLn "ok"
, putStrLn "failed"
)

View file

@ -9,19 +9,20 @@ module Command.ContentLocation where
import Common.Annex import Common.Annex
import Command import Command
import CmdLine.Batch
import Annex.Content import Annex.Content
cmd :: [Command] cmd :: [Command]
cmd = [noCommit $ noMessages $ cmd = [withOptions [batchOption] $ noCommit $ noMessages $
command "contentlocation" (paramRepeating paramKey) seek command "contentlocation" (paramRepeating paramKey) seek
SectionPlumbing "looks up content for a key"] SectionPlumbing "looks up content for a key"]
seek :: CommandSeek seek :: CommandSeek
seek = withKeys start seek = batchable withKeys start
start :: Key -> CommandStart start :: Batchable Key
start k = do start batchmode k = do
liftIO . maybe exitFailure putStrLn maybe (batchBadInput batchmode) (liftIO . putStrLn)
=<< inAnnex' (pure True) Nothing check k =<< inAnnex' (pure True) Nothing check k
stop stop
where where

View file

@ -12,7 +12,7 @@ import Command
import qualified Command.Move import qualified Command.Move
import qualified Remote import qualified Remote
import Annex.Wanted import Annex.Wanted
import Config.NumCopies import Annex.NumCopies
cmd :: [Command] cmd :: [Command]
cmd = [withOptions copyOptions $ command "copy" paramPaths seek cmd = [withOptions copyOptions $ command "copy" paramPaths seek

View file

@ -15,7 +15,7 @@ import Annex.UUID
import Logs.Location import Logs.Location
import Logs.Trust import Logs.Trust
import Logs.PreferredContent import Logs.PreferredContent
import Config.NumCopies import Annex.NumCopies
import Annex.Content import Annex.Content
import Annex.Wanted import Annex.Wanted
import Annex.Notification import Annex.Notification
@ -27,7 +27,7 @@ cmd = [withOptions (dropOptions) $ command "drop" paramPaths seek
SectionCommon "indicate content of files not currently wanted"] SectionCommon "indicate content of files not currently wanted"]
dropOptions :: [Option] dropOptions :: [Option]
dropOptions = dropFromOption : annexedMatchingOptions ++ [autoOption] dropOptions = dropFromOption : annexedMatchingOptions ++ [autoOption] ++ keyOptions
dropFromOption :: Option dropFromOption :: Option
dropFromOption = fieldOption ['f'] "from" paramRemote "drop content from a remote" dropFromOption = fieldOption ['f'] "from" paramRemote "drop content from a remote"
@ -36,23 +36,32 @@ seek :: CommandSeek
seek ps = do seek ps = do
from <- getOptionField dropFromOption Remote.byNameWithUUID from <- getOptionField dropFromOption Remote.byNameWithUUID
auto <- getOptionFlag autoOption auto <- getOptionFlag autoOption
withFilesInGit (whenAnnexed $ start auto from) ps withKeyOptions auto
(startKeys auto from)
(withFilesInGit $ whenAnnexed $ start auto from)
ps
start :: Bool -> Maybe Remote -> FilePath -> Key -> CommandStart start :: Bool -> Maybe Remote -> FilePath -> Key -> CommandStart
start auto from file key = checkDropAuto auto from file key $ \numcopies -> start auto from file key = start' auto from key (Just file)
start' :: Bool -> Maybe Remote -> Key -> AssociatedFile -> CommandStart
start' auto from key afile = checkDropAuto auto from afile key $ \numcopies ->
stopUnless want $ stopUnless want $
case from of case from of
Nothing -> startLocal (Just file) numcopies key Nothing Nothing -> startLocal afile numcopies key Nothing
Just remote -> do Just remote -> do
u <- getUUID u <- getUUID
if Remote.uuid remote == u if Remote.uuid remote == u
then startLocal (Just file) numcopies key Nothing then startLocal afile numcopies key Nothing
else startRemote (Just file) numcopies key remote else startRemote afile numcopies key remote
where where
want want
| auto = wantDrop False (Remote.uuid <$> from) (Just key) (Just file) | auto = wantDrop False (Remote.uuid <$> from) (Just key) afile
| otherwise = return True | otherwise = return True
startKeys :: Bool -> Maybe Remote -> Key -> CommandStart
startKeys auto from key = start' auto from key Nothing
startLocal :: AssociatedFile -> NumCopies -> Key -> Maybe Remote -> CommandStart startLocal :: AssociatedFile -> NumCopies -> Key -> Maybe Remote -> CommandStart
startLocal afile numcopies key knownpresentremote = stopUnless (inAnnex key) $ do startLocal afile numcopies key knownpresentremote = stopUnless (inAnnex key) $ do
showStart' "drop" key afile showStart' "drop" key afile
@ -72,7 +81,7 @@ performLocal key afile numcopies knownpresentremote = lockContent key $ \content
(remotes, trusteduuids) <- Remote.keyPossibilitiesTrusted key (remotes, trusteduuids) <- Remote.keyPossibilitiesTrusted key
let trusteduuids' = case knownpresentremote of let trusteduuids' = case knownpresentremote of
Nothing -> trusteduuids Nothing -> trusteduuids
Just r -> nub (Remote.uuid r:trusteduuids) Just r -> Remote.uuid r:trusteduuids
untrusteduuids <- trustGet UnTrusted untrusteduuids <- trustGet UnTrusted
let tocheck = Remote.remotesWithoutUUID remotes (trusteduuids'++untrusteduuids) let tocheck = Remote.remotesWithoutUUID remotes (trusteduuids'++untrusteduuids)
u <- getUUID u <- getUUID
@ -91,17 +100,9 @@ performRemote key afile numcopies remote = do
-- Filter the remote it's being dropped from out of the lists of -- Filter the remote it's being dropped from out of the lists of
-- places assumed to have the key, and places to check. -- places assumed to have the key, and places to check.
-- When the local repo has the key, that's one additional copy, -- When the local repo has the key, that's one additional copy,
-- as long asthe local repo is not untrusted. -- as long as the local repo is not untrusted.
(remotes, trusteduuids) <- Remote.keyPossibilitiesTrusted key (remotes, trusteduuids) <- knownCopies key
present <- inAnnex key let have = filter (/= uuid) trusteduuids
u <- getUUID
trusteduuids' <- if present
then ifM ((<= SemiTrusted) <$> lookupTrust u)
( pure (u:trusteduuids)
, pure trusteduuids
)
else pure trusteduuids
let have = filter (/= uuid) trusteduuids'
untrusteduuids <- trustGet UnTrusted untrusteduuids <- trustGet UnTrusted
let tocheck = filter (/= remote) $ let tocheck = filter (/= remote) $
Remote.remotesWithoutUUID remotes (have++untrusteduuids) Remote.remotesWithoutUUID remotes (have++untrusteduuids)
@ -131,45 +132,20 @@ cleanupRemote key remote ok = do
- --force overrides and always allows dropping. - --force overrides and always allows dropping.
-} -}
canDrop :: UUID -> Key -> AssociatedFile -> NumCopies -> [UUID] -> [Remote] -> [UUID] -> Annex Bool canDrop :: UUID -> Key -> AssociatedFile -> NumCopies -> [UUID] -> [Remote] -> [UUID] -> Annex Bool
canDrop dropfrom key afile numcopies have check skip = ifM (Annex.getState Annex.force) canDrop dropfrom key afile numcopies have check skip =
( return True ifM (Annex.getState Annex.force)
, checkRequiredContent dropfrom key afile ( return True
<&&> , ifM (checkRequiredContent dropfrom key afile
findCopies key numcopies skip have check <&&> verifyEnoughCopies nolocmsg key numcopies skip have check
) )
( return True
findCopies :: Key -> NumCopies -> [UUID] -> [UUID] -> [Remote] -> Annex Bool , do
findCopies key need skip = helper [] [] hint
return False
)
)
where where
helper bad missing have [] nolocmsg = "Rather than dropping this file, try using: git annex move"
| NumCopies (length have) >= need = return True
| otherwise = notEnoughCopies key need have (skip++missing) bad
helper bad missing have (r:rs)
| NumCopies (length have) >= need = return True
| otherwise = do
let u = Remote.uuid r
let duplicate = u `elem` have
haskey <- Remote.hasKey r key
case (duplicate, haskey) of
(False, Right True) -> helper bad missing (u:have) rs
(False, Left _) -> helper (r:bad) missing have rs
(False, Right False) -> helper bad (u:missing) have rs
_ -> helper bad missing have rs
notEnoughCopies :: Key -> NumCopies -> [UUID] -> [UUID] -> [Remote] -> Annex Bool
notEnoughCopies key need have skip bad = do
unsafe
showLongNote $
"Could only verify the existence of " ++
show (length have) ++ " out of " ++ show (fromNumCopies need) ++
" necessary copies"
Remote.showTriedRemotes bad
Remote.showLocations True key (have++skip)
"Rather than dropping this file, try using: git annex move"
hint
return False
where
unsafe = showNote "unsafe"
hint = showLongNote "(Use --force to override this check, or adjust numcopies.)" hint = showLongNote "(Use --force to override this check, or adjust numcopies.)"
checkRequiredContent :: UUID -> Key -> AssociatedFile -> Annex Bool checkRequiredContent :: UUID -> Key -> AssociatedFile -> Annex Bool
@ -187,8 +163,8 @@ requiredContent = do
{- In auto mode, only runs the action if there are enough {- In auto mode, only runs the action if there are enough
- copies on other semitrusted repositories. -} - copies on other semitrusted repositories. -}
checkDropAuto :: Bool -> Maybe Remote -> FilePath -> Key -> (NumCopies -> CommandStart) -> CommandStart checkDropAuto :: Bool -> Maybe Remote -> AssociatedFile -> Key -> (NumCopies -> CommandStart) -> CommandStart
checkDropAuto auto mremote file key a = go =<< getFileNumCopies file checkDropAuto auto mremote afile key a = go =<< maybe getNumCopies getFileNumCopies afile
where where
go numcopies go numcopies
| auto = do | auto = do

View file

@ -14,7 +14,7 @@ import qualified Command.Drop
import qualified Remote import qualified Remote
import qualified Git import qualified Git
import Command.Unused (withUnusedMaps, UnusedMaps(..), startUnused) import Command.Unused (withUnusedMaps, UnusedMaps(..), startUnused)
import Config.NumCopies import Annex.NumCopies
cmd :: [Command] cmd :: [Command]
cmd = [withOptions [Command.Drop.dropFromOption] $ cmd = [withOptions [Command.Drop.dropFromOption] $

View file

@ -9,21 +9,22 @@ module Command.ExamineKey where
import Common.Annex import Common.Annex
import Command import Command
import CmdLine.Batch
import qualified Utility.Format import qualified Utility.Format
import Command.Find (formatOption, getFormat, showFormatted, keyVars) import Command.Find (formatOption, getFormat, showFormatted, keyVars)
import Types.Key import Types.Key
cmd :: [Command] cmd :: [Command]
cmd = [noCommit $ noMessages $ withOptions [formatOption, jsonOption] $ cmd = [noCommit $ noMessages $ withOptions [formatOption, jsonOption, batchOption] $
command "examinekey" (paramRepeating paramKey) seek command "examinekey" (paramRepeating paramKey) seek
SectionPlumbing "prints information from a key"] SectionPlumbing "prints information from a key"]
seek :: CommandSeek seek :: CommandSeek
seek ps = do seek ps = do
format <- getFormat format <- getFormat
withKeys (start format) ps batchable withKeys (start format) ps
start :: Maybe Utility.Format.Format -> Key -> CommandStart start :: Maybe Utility.Format.Format -> Batchable Key
start format key = do start format _ key = do
showFormatted format (key2file key) (keyVars key) showFormatted format (key2file key) (keyVars key)
stop stop

View file

@ -24,21 +24,21 @@ import Annex.Link
import Logs.Location import Logs.Location
import Logs.Trust import Logs.Trust
import Logs.Activity import Logs.Activity
import Config.NumCopies import Logs.TimeStamp
import Annex.NumCopies
import Annex.UUID import Annex.UUID
import Utility.DataUnits import Utility.DataUnits
import Config import Config
import Types.Key import Types.Key
import Types.CleanupActions import Types.CleanupActions
import Utility.HumanTime import Utility.HumanTime
import Utility.CopyFile
import Git.FilePath import Git.FilePath
import Utility.PID import Utility.PID
import qualified Database.Fsck as FsckDb import qualified Database.Fsck as FsckDb
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Data.Time
import System.Posix.Types (EpochTime) import System.Posix.Types (EpochTime)
import System.Locale
cmd :: [Command] cmd :: [Command]
cmd = [withOptions fsckOptions $ command "fsck" paramPaths seek cmd = [withOptions fsckOptions $ command "fsck" paramPaths seek
@ -75,7 +75,7 @@ seek ps = do
(withFilesInGit $ whenAnnexed $ start from i) (withFilesInGit $ whenAnnexed $ start from i)
ps ps
withFsckDb i FsckDb.closeDb withFsckDb i FsckDb.closeDb
recordActivity Fsck u void $ tryIO $ recordActivity Fsck u
start :: Maybe Remote -> Incremental -> FilePath -> Key -> CommandStart start :: Maybe Remote -> Incremental -> FilePath -> Key -> CommandStart
start from inc file key = do start from inc file key = do
@ -111,14 +111,15 @@ performRemote key file backend numcopies remote =
dispatch (Left err) = do dispatch (Left err) = do
showNote err showNote err
return False return False
dispatch (Right True) = withtmp $ \tmpfile -> dispatch (Right True) = withtmp $ \tmpfile -> do
ifM (getfile tmpfile) r <- getfile tmpfile
( go True (Just tmpfile) case r of
, do Nothing -> go True Nothing
Just True -> go True (Just tmpfile)
Just False -> do
warning "failed to download file from remote" warning "failed to download file from remote"
void $ go True Nothing void $ go True Nothing
return False return False
)
dispatch (Right False) = go False Nothing dispatch (Right False) = go False Nothing
go present localcopy = check go present localcopy = check
[ verifyLocationLogRemote key file remote present [ verifyLocationLogRemote key file remote present
@ -134,14 +135,17 @@ performRemote key file backend numcopies remote =
let cleanup = liftIO $ catchIO (removeFile tmp) (const noop) let cleanup = liftIO $ catchIO (removeFile tmp) (const noop)
cleanup cleanup
cleanup `after` a tmp cleanup `after` a tmp
getfile tmp = getfile tmp = ifM (checkDiskSpace (Just tmp) key 0)
ifM (Remote.retrieveKeyFileCheap remote key (Just file) tmp) ( ifM (Remote.retrieveKeyFileCheap remote key (Just file) tmp)
( return True ( return (Just True)
, ifM (Annex.getState Annex.fast) , ifM (Annex.getState Annex.fast)
( return False ( return Nothing
, Remote.retrieveKeyFile remote key (Just file) tmp dummymeter , Just <$>
Remote.retrieveKeyFile remote key Nothing tmp dummymeter
) )
) )
, return (Just False)
)
dummymeter _ = noop dummymeter _ = noop
startKey :: Incremental -> Key -> NumCopies -> CommandStart startKey :: Incremental -> Key -> NumCopies -> CommandStart
@ -273,7 +277,7 @@ checkKeySize key = ifM isDirect
checkKeySizeRemote :: Key -> Remote -> Maybe FilePath -> Annex Bool checkKeySizeRemote :: Key -> Remote -> Maybe FilePath -> Annex Bool
checkKeySizeRemote _ _ Nothing = return True checkKeySizeRemote _ _ Nothing = return True
checkKeySizeRemote key remote (Just file) = checkKeySizeRemote key remote (Just file) =
checkKeySizeOr (badContentRemote remote) key file checkKeySizeOr (badContentRemote remote file) key file
checkKeySizeOr :: (Key -> Annex String) -> Key -> FilePath -> Annex Bool checkKeySizeOr :: (Key -> Annex String) -> Key -> FilePath -> Annex Bool
checkKeySizeOr bad key file = case Types.Key.keySize key of checkKeySizeOr bad key file = case Types.Key.keySize key of
@ -318,7 +322,7 @@ checkBackend backend key mfile = go =<< isDirect
checkBackendRemote :: Backend -> Key -> Remote -> Maybe FilePath -> Annex Bool checkBackendRemote :: Backend -> Key -> Remote -> Maybe FilePath -> Annex Bool
checkBackendRemote backend key remote = maybe (return True) go checkBackendRemote backend key remote = maybe (return True) go
where where
go = checkBackendOr (badContentRemote remote) backend key go file = checkBackendOr (badContentRemote remote file) backend key file
checkBackendOr :: (Key -> Annex String) -> Backend -> Key -> FilePath -> Annex Bool checkBackendOr :: (Key -> Annex String) -> Backend -> Key -> FilePath -> Annex Bool
checkBackendOr bad backend key file = checkBackendOr bad backend key file =
@ -380,13 +384,36 @@ badContentDirect file key = do
logStatus key InfoMissing logStatus key InfoMissing
return "left in place for you to examine" return "left in place for you to examine"
badContentRemote :: Remote -> Key -> Annex String {- Bad content is dropped from the remote. We have downloaded a copy
badContentRemote remote key = do - from the remote to a temp file already (in some cases, it's just a
ok <- Remote.removeKey remote key - symlink to a file in the remote). To avoid any further data loss,
when ok $ - that temp file is moved to the bad content directory unless
- the local annex has a copy of the content. -}
badContentRemote :: Remote -> FilePath -> Key -> Annex String
badContentRemote remote localcopy key = do
bad <- fromRepo gitAnnexBadDir
let destbad = bad </> key2file key
movedbad <- ifM (inAnnex key <||> liftIO (doesFileExist destbad))
( return False
, do
createAnnexDirectory (parentDir destbad)
liftIO $ catchDefaultIO False $
ifM (isSymbolicLink <$> getSymbolicLinkStatus localcopy)
( copyFileExternal CopyTimeStamps localcopy destbad
, do
moveFile localcopy destbad
return True
)
)
dropped <- Remote.removeKey remote key
when dropped $
Remote.logStatus remote key InfoMissing Remote.logStatus remote key InfoMissing
return $ (if ok then "dropped from " else "failed to drop from ") return $ case (movedbad, dropped) of
++ Remote.name remote (True, True) -> "moved from " ++ Remote.name remote ++
" to " ++ destbad
(False, True) -> "dropped from " ++ Remote.name remote
(_, False) -> "failed to drop from" ++ Remote.name remote
runFsck :: Incremental -> FilePath -> Key -> Annex Bool -> CommandStart runFsck :: Incremental -> FilePath -> Key -> Annex Bool -> CommandStart
runFsck inc file key a = ifM (needFsck inc key) runFsck inc file key a = ifM (needFsck inc key)
@ -448,14 +475,11 @@ getStartTime u = do
liftIO $ catchDefaultIO Nothing $ do liftIO $ catchDefaultIO Nothing $ do
timestamp <- modificationTime <$> getFileStatus f timestamp <- modificationTime <$> getFileStatus f
let fromstatus = Just (realToFrac timestamp) let fromstatus = Just (realToFrac timestamp)
fromfile <- readishTime <$> readFile f fromfile <- parsePOSIXTime <$> readFile f
return $ if matchingtimestamp fromfile fromstatus return $ if matchingtimestamp fromfile fromstatus
then Just timestamp then Just timestamp
else Nothing else Nothing
where where
readishTime :: String -> Maybe POSIXTime
readishTime s = utcTimeToPOSIXSeconds <$>
parseTime defaultTimeLocale "%s%Qs" s
matchingtimestamp fromfile fromstatus = matchingtimestamp fromfile fromstatus =
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
fromfile == fromstatus fromfile == fromstatus

View file

@ -12,7 +12,7 @@ import Command
import qualified Remote import qualified Remote
import Annex.Content import Annex.Content
import Annex.Transfer import Annex.Transfer
import Config.NumCopies import Annex.NumCopies
import Annex.Wanted import Annex.Wanted
import qualified Command.Move import qualified Command.Move

View file

@ -8,13 +8,9 @@
module Command.GroupWanted where module Command.GroupWanted where
import Common.Annex import Common.Annex
import qualified Annex
import Command import Command
import Logs.PreferredContent import Logs.PreferredContent
import Types.Messages import Command.Wanted (performGet, performSet)
import Types.Group
import qualified Data.Map as M
cmd :: [Command] cmd :: [Command]
cmd = [command "groupwanted" (paramPair paramGroup (paramOptional paramExpression)) seek cmd = [command "groupwanted" (paramPair paramGroup (paramOptional paramExpression)) seek
@ -24,22 +20,8 @@ seek :: CommandSeek
seek = withWords start seek = withWords start
start :: [String] -> CommandStart start :: [String] -> CommandStart
start (g:[]) = next $ performGet g start (g:[]) = next $ performGet groupPreferredContentMapRaw g
start (g:expr:[]) = do start (g:expr:[]) = do
showStart "groupwanted" g showStart "groupwanted" g
next $ performSet g expr next $ performSet groupPreferredContentSet expr g
start _ = error "Specify a group." start _ = error "Specify a group."
performGet :: Group -> CommandPerform
performGet g = do
Annex.setOutput QuietOutput
m <- groupPreferredContentMapRaw
liftIO $ putStrLn $ fromMaybe "" $ M.lookup g m
next $ return True
performSet :: Group -> String -> CommandPerform
performSet g expr = case checkPreferredContentExpression expr of
Just e -> error $ "Parse error: " ++ e
Nothing -> do
groupPreferredContentSet g expr
next $ return True

View file

@ -9,6 +9,7 @@ module Command.Import where
import Common.Annex import Common.Annex
import Command import Command
import qualified Git
import qualified Annex import qualified Annex
import qualified Command.Add import qualified Command.Add
import Utility.CopyFile import Utility.CopyFile
@ -16,6 +17,10 @@ import Backend
import Remote import Remote
import Types.KeySource import Types.KeySource
import Types.Key import Types.Key
import Annex.CheckIgnore
import Annex.NumCopies
import Types.TrustLevel
import Logs.Trust
cmd :: [Command] cmd :: [Command]
cmd = [withOptions opts $ notBareRepo $ command "import" paramPaths seek cmd = [withOptions opts $ notBareRepo $ command "import" paramPaths seek
@ -58,6 +63,10 @@ getDuplicateMode = go . catMaybes <$> mapM getflag [minBound..maxBound]
seek :: CommandSeek seek :: CommandSeek
seek ps = do seek ps = do
mode <- getDuplicateMode mode <- getDuplicateMode
repopath <- liftIO . absPath =<< fromRepo Git.repoPath
inrepops <- liftIO $ filter (dirContains repopath) <$> mapM absPath ps
unless (null inrepops) $ do
error $ "cannot import files from inside the working tree (use git annex add instead): " ++ unwords inrepops
withPathContents (start mode) ps withPathContents (start mode) ps
start :: DuplicateMode -> (FilePath, FilePath) -> CommandStart start :: DuplicateMode -> (FilePath, FilePath) -> CommandStart
@ -75,23 +84,41 @@ start mode (srcfile, destfile) =
where where
deletedup k = do deletedup k = do
showNote $ "duplicate of " ++ key2file k showNote $ "duplicate of " ++ key2file k
liftIO $ removeFile srcfile ifM (verifiedExisting k destfile)
next $ return True ( do
liftIO $ removeFile srcfile
next $ return True
, do
warning "Could not verify that the content is still present in the annex; not removing from the import location."
stop
)
importfile = do importfile = do
handleexisting =<< liftIO (catchMaybeIO $ getSymbolicLinkStatus destfile) ignored <- not <$> Annex.getState Annex.force <&&> checkIgnored destfile
if ignored
then do
warning $ "not importing " ++ destfile ++ " which is .gitignored (use --force to override)"
stop
else do
existing <- liftIO (catchMaybeIO $ getSymbolicLinkStatus destfile)
case existing of
Nothing -> importfilechecked
(Just s)
| isDirectory s -> notoverwriting "(is a directory)"
| otherwise -> ifM (Annex.getState Annex.force)
( do
liftIO $ nukeFile destfile
importfilechecked
, notoverwriting "(use --force to override, or a duplication option such as --deduplicate to clean up)"
)
importfilechecked = do
liftIO $ createDirectoryIfMissing True (parentDir destfile) liftIO $ createDirectoryIfMissing True (parentDir destfile)
liftIO $ if mode == Duplicate || mode == SkipDuplicates liftIO $ if mode == Duplicate || mode == SkipDuplicates
then void $ copyFileExternal CopyAllMetaData srcfile destfile then void $ copyFileExternal CopyAllMetaData srcfile destfile
else moveFile srcfile destfile else moveFile srcfile destfile
Command.Add.perform destfile Command.Add.perform destfile
handleexisting Nothing = noop notoverwriting why = do
handleexisting (Just s) warning $ "not overwriting existing " ++ destfile ++ " " ++ why
| isDirectory s = notoverwriting "(is a directory)" stop
| otherwise = ifM (Annex.getState Annex.force)
( liftIO $ nukeFile destfile
, notoverwriting "(use --force to override, or a duplication option such as --deduplicate to clean up)"
)
notoverwriting why = error $ "not overwriting existing " ++ destfile ++ " " ++ why
checkdup dupa notdupa = do checkdup dupa notdupa = do
backend <- chooseBackend destfile backend <- chooseBackend destfile
let ks = KeySource srcfile srcfile Nothing let ks = KeySource srcfile srcfile Nothing
@ -107,3 +134,14 @@ start mode (srcfile, destfile) =
CleanDuplicates -> checkdup (Just deletedup) Nothing CleanDuplicates -> checkdup (Just deletedup) Nothing
SkipDuplicates -> checkdup Nothing (Just importfile) SkipDuplicates -> checkdup Nothing (Just importfile)
_ -> return (Just importfile) _ -> return (Just importfile)
verifiedExisting :: Key -> FilePath -> Annex Bool
verifiedExisting key destfile = do
-- Look up the numcopies setting for the file that it would be
-- imported to, if it were imported.
need <- getFileNumCopies destfile
(remotes, trusteduuids) <- knownCopies key
untrusteduuids <- trustGet UnTrusted
let tocheck = Remote.remotesWithoutUUID remotes (trusteduuids++untrusteduuids)
verifyEnoughCopies [] key need trusteduuids [] tocheck

View file

@ -16,7 +16,9 @@ import qualified Data.Set as S
import qualified Data.Map as M import qualified Data.Map as M
import Data.Time.Clock import Data.Time.Clock
import Data.Time.Format import Data.Time.Format
#if ! MIN_VERSION_time(1,5,0)
import System.Locale import System.Locale
#endif
import Common.Annex import Common.Annex
import qualified Annex import qualified Annex
@ -196,7 +198,7 @@ performDownload opts cache todownload = case location todownload of
Just link -> do Just link -> do
let videourl = Quvi.linkUrl link let videourl = Quvi.linkUrl link
checkknown videourl $ checkknown videourl $
rundownload videourl ("." ++ Quvi.linkSuffix link) $ \f -> rundownload videourl ("." ++ fromMaybe "m" (Quvi.linkSuffix link)) $ \f ->
maybeToList <$> addUrlFileQuvi (relaxedOpt opts) quviurl videourl f maybeToList <$> addUrlFileQuvi (relaxedOpt opts) quviurl videourl f
#else #else
return False return False

View file

@ -30,7 +30,7 @@ import Types.Key
import Logs.UUID import Logs.UUID
import Logs.Trust import Logs.Trust
import Logs.Location import Logs.Location
import Config.NumCopies import Annex.NumCopies
import Remote import Remote
import Config import Config
import Utility.Percentage import Utility.Percentage

View file

@ -5,15 +5,19 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# LANGUAGE CPP #-}
module Command.Log where module Command.Log where
import qualified Data.Set as S import qualified Data.Set as S
import qualified Data.Map as M import qualified Data.Map as M
import qualified Data.ByteString.Lazy.Char8 as L import qualified Data.ByteString.Lazy.Char8 as L
import Data.Char
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Data.Time import Data.Time
#if ! MIN_VERSION_time(1,5,0)
import System.Locale import System.Locale
import Data.Char #endif
import Common.Annex import Common.Annex
import Command import Command
@ -172,7 +176,11 @@ parseRaw l = go $ words l
parseTimeStamp :: String -> POSIXTime parseTimeStamp :: String -> POSIXTime
parseTimeStamp = utcTimeToPOSIXSeconds . fromMaybe (error "bad timestamp") . parseTimeStamp = utcTimeToPOSIXSeconds . fromMaybe (error "bad timestamp") .
#if MIN_VERSION_time(1,5,0)
parseTimeM True defaultTimeLocale "%s"
#else
parseTime defaultTimeLocale "%s" parseTime defaultTimeLocale "%s"
#endif
showTimeStamp :: TimeZone -> POSIXTime -> String showTimeStamp :: TimeZone -> POSIXTime -> String
showTimeStamp zone = show . utcToLocalTime zone . posixSecondsToUTCTime showTimeStamp zone = show . utcToLocalTime zone . posixSecondsToUTCTime

View file

@ -9,18 +9,20 @@ module Command.LookupKey where
import Common.Annex import Common.Annex
import Command import Command
import CmdLine.Batch
import Annex.CatFile import Annex.CatFile
import Types.Key import Types.Key
cmd :: [Command] cmd :: [Command]
cmd = [notBareRepo $ noCommit $ noMessages $ cmd = [withOptions [batchOption] $ notBareRepo $ noCommit $ noMessages $
command "lookupkey" (paramRepeating paramFile) seek command "lookupkey" (paramRepeating paramFile) seek
SectionPlumbing "looks up key used for file"] SectionPlumbing "looks up key used for file"]
seek :: CommandSeek seek :: CommandSeek
seek = withStrings start seek = batchable withStrings start
start :: String -> CommandStart start :: Batchable String
start file = do start batchmode file = do
liftIO . maybe exitFailure (putStrLn . key2file) =<< catKeyFile file maybe (batchBadInput batchmode) (liftIO . putStrLn . key2file)
=<< catKeyFile file
stop stop

View file

@ -14,7 +14,7 @@ import qualified Command.Drop
import qualified Command.Get import qualified Command.Get
import qualified Remote import qualified Remote
import Annex.Content import Annex.Content
import Config.NumCopies import Annex.NumCopies
cmd :: [Command] cmd :: [Command]
cmd = [withOptions mirrorOptions $ command "mirror" paramPaths seek cmd = [withOptions mirrorOptions $ command "mirror" paramPaths seek

View file

@ -10,7 +10,7 @@ module Command.NumCopies where
import Common.Annex import Common.Annex
import qualified Annex import qualified Annex
import Command import Command
import Config.NumCopies import Annex.NumCopies
import Types.Messages import Types.Messages
cmd :: [Command] cmd :: [Command]

17
Command/Required.hs Normal file
View file

@ -0,0 +1,17 @@
{- git-annex command
-
- Copyright 2015 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module Command.Required where
import Command
import Logs.PreferredContent
import qualified Command.Wanted
cmd :: [Command]
cmd = Command.Wanted.cmd' "required" "get or set required content expression"
requiredContentMapRaw
requiredContentSet

View file

@ -1,6 +1,6 @@
{- git-annex command {- git-annex command
- -
- Copyright 2013 Joey Hess <id@joeyh.name> - Copyright 2013-2015 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -13,39 +13,47 @@ import Command
import qualified Remote import qualified Remote
import Logs.PreferredContent import Logs.PreferredContent
import Types.Messages import Types.Messages
import Types.StandardGroups
import qualified Data.Map as M import qualified Data.Map as M
cmd :: [Command] cmd :: [Command]
cmd = [command "wanted" (paramPair paramRemote (paramOptional paramExpression)) seek cmd = cmd' "wanted" "get or set preferred content expression"
SectionSetup "get or set preferred content expression"] preferredContentMapRaw
preferredContentSet
seek :: CommandSeek cmd'
seek = withWords start :: String
-> String
start :: [String] -> CommandStart -> Annex (M.Map UUID PreferredContentExpression)
start = parse -> (UUID -> PreferredContentExpression -> Annex ())
-> [Command]
cmd' name desc getter setter = [command name pdesc seek SectionSetup desc]
where where
parse (name:[]) = go name performGet pdesc = paramPair paramRemote (paramOptional paramExpression)
parse (name:expr:[]) = go name $ \uuid -> do
showStart "wanted" name
performSet expr uuid
parse _ = error "Specify a repository."
go name a = do seek = withWords start
u <- Remote.nameToUUID name
start (rname:[]) = go rname (performGet getter)
start (rname:expr:[]) = go rname $ \uuid -> do
showStart name rname
performSet setter expr uuid
start _ = error "Specify a repository."
go rname a = do
u <- Remote.nameToUUID rname
next $ a u next $ a u
performGet :: UUID -> CommandPerform performGet :: Ord a => Annex (M.Map a PreferredContentExpression) -> a -> CommandPerform
performGet uuid = do performGet getter a = do
Annex.setOutput QuietOutput Annex.setOutput QuietOutput
m <- preferredContentMapRaw m <- getter
liftIO $ putStrLn $ fromMaybe "" $ M.lookup uuid m liftIO $ putStrLn $ fromMaybe "" $ M.lookup a m
next $ return True next $ return True
performSet :: String -> UUID -> CommandPerform performSet :: Ord a => (a -> PreferredContentExpression -> Annex ()) -> String -> a -> CommandPerform
performSet expr uuid = case checkPreferredContentExpression expr of performSet setter expr a = case checkPreferredContentExpression expr of
Just e -> error $ "Parse error: " ++ e Just e -> error $ "Parse error: " ++ e
Nothing -> do Nothing -> do
preferredContentSet uuid expr setter a expr
next $ return True next $ return True

View file

@ -143,10 +143,10 @@ firstRun :: Maybe HostName -> IO ()
firstRun listenhost = do firstRun listenhost = do
checkEnvironmentIO checkEnvironmentIO
{- Without a repository, we cannot have an Annex monad, so cannot {- Without a repository, we cannot have an Annex monad, so cannot
- get a ThreadState. Using undefined is only safe because the - get a ThreadState. This is only safe because the
- webapp checks its noAnnex field before accessing the - webapp checks its noAnnex field before accessing the
- threadstate. -} - threadstate. -}
let st = undefined let st = error "annex state not available"
{- Get a DaemonStatus without running in the Annex monad. -} {- Get a DaemonStatus without running in the Annex monad. -}
dstatus <- atomically . newTMVar =<< newDaemonStatus dstatus <- atomically . newTMVar =<< newDaemonStatus
d <- newAssistantData st dstatus d <- newAssistantData st dstatus

View file

@ -5,6 +5,8 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Config.Files where module Config.Files where
import Common import Common

View file

@ -34,8 +34,7 @@ module Crypto (
) where ) where
import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as L
import Data.ByteString.Lazy.UTF8 (fromString) import Data.ByteString.UTF8 (fromString)
import Control.Applicative
import qualified Data.Map as M import qualified Data.Map as M
import Control.Monad.IO.Class import Control.Monad.IO.Class
@ -93,7 +92,7 @@ genSharedCipher highQuality =
{- Updates an existing Cipher, re-encrypting it to add or remove keyids, {- Updates an existing Cipher, re-encrypting it to add or remove keyids,
- depending on whether the first component is True or False. -} - depending on whether the first component is True or False. -}
updateEncryptedCipher :: [(Bool, String)] -> StorableCipher -> IO StorableCipher updateEncryptedCipher :: [(Bool, String)] -> StorableCipher -> IO StorableCipher
updateEncryptedCipher _ SharedCipher{} = undefined updateEncryptedCipher _ SharedCipher{} = error "Cannot update shared cipher"
updateEncryptedCipher [] encipher = return encipher updateEncryptedCipher [] encipher = return encipher
updateEncryptedCipher newkeys encipher@(EncryptedCipher _ variant (KeyIds ks)) = do updateEncryptedCipher newkeys encipher@(EncryptedCipher _ variant (KeyIds ks)) = do
dropKeys <- listKeyIds [ k | (False, k) <- newkeys ] dropKeys <- listKeyIds [ k | (False, k) <- newkeys ]

View file

@ -25,6 +25,7 @@ import qualified Database.Handle as H
import Locations import Locations
import Utility.PosixFiles import Utility.PosixFiles
import Utility.Exception import Utility.Exception
import Common
import Annex import Annex
import Types.Key import Types.Key
import Types.UUID import Types.UUID
@ -33,13 +34,6 @@ import Annex.LockFile
import Database.Persist.TH import Database.Persist.TH
import Database.Esqueleto hiding (Key) import Database.Esqueleto hiding (Key)
import Control.Monad
import Control.Monad.IfElse
import Control.Monad.IO.Class (liftIO)
import System.Directory
import System.FilePath
import Data.Maybe
import Control.Applicative
data FsckHandle = FsckHandle H.DbHandle UUID data FsckHandle = FsckHandle H.DbHandle UUID
@ -55,7 +49,7 @@ Fscked
- -
- This may fail, if other fsck processes are currently running using the - This may fail, if other fsck processes are currently running using the
- database. Removing the database in that situation would lead to crashes - database. Removing the database in that situation would lead to crashes
- or undefined behavior. - or unknown behavior.
-} -}
newPass :: UUID -> Annex Bool newPass :: UUID -> Annex Bool
newPass u = isJust <$> tryExclusiveLock (gitAnnexFsckDbLock u) go newPass u = isJust <$> tryExclusiveLock (gitAnnexFsckDbLock u) go

6
Git.hs
View file

@ -60,7 +60,7 @@ repoLocation Repo { location = Url url } = show url
repoLocation Repo { location = Local { worktree = Just dir } } = dir repoLocation Repo { location = Local { worktree = Just dir } } = dir
repoLocation Repo { location = Local { gitdir = dir } } = dir repoLocation Repo { location = Local { gitdir = dir } } = dir
repoLocation Repo { location = LocalUnknown dir } = dir repoLocation Repo { location = LocalUnknown dir } = dir
repoLocation Repo { location = Unknown } = undefined repoLocation Repo { location = Unknown } = error "unknown repoLocation"
{- Path to a repository. For non-bare, this is the worktree, for bare, {- Path to a repository. For non-bare, this is the worktree, for bare,
- it's the gitdir, and for URL repositories, is the path on the remote - it's the gitdir, and for URL repositories, is the path on the remote
@ -70,12 +70,12 @@ repoPath Repo { location = Url u } = unEscapeString $ uriPath u
repoPath Repo { location = Local { worktree = Just d } } = d repoPath Repo { location = Local { worktree = Just d } } = d
repoPath Repo { location = Local { gitdir = d } } = d repoPath Repo { location = Local { gitdir = d } } = d
repoPath Repo { location = LocalUnknown dir } = dir repoPath Repo { location = LocalUnknown dir } = dir
repoPath Repo { location = Unknown } = undefined repoPath Repo { location = Unknown } = error "unknown repoPath"
{- Path to a local repository's .git directory. -} {- Path to a local repository's .git directory. -}
localGitDir :: Repo -> FilePath localGitDir :: Repo -> FilePath
localGitDir Repo { location = Local { gitdir = d } } = d localGitDir Repo { location = Local { gitdir = d } } = d
localGitDir _ = undefined localGitDir _ = error "unknown localGitDir"
{- Some code needs to vary between URL and normal repos, {- Some code needs to vary between URL and normal repos,
- or bare and non-bare, these functions help with that. -} - or bare and non-bare, these functions help with that. -}

View file

@ -110,4 +110,4 @@ catTree h treeref = go <$> catObjectDetails h treeref
parsemodefile b = parsemodefile b =
let (modestr, file) = separate (== ' ') (decodeBS b) let (modestr, file) = separate (== ' ') (decodeBS b)
in (file, readmode modestr) in (file, readmode modestr)
readmode = fst . fromMaybe (0, undefined) . headMaybe . readOct readmode = fromMaybe 0 . fmap fst . headMaybe . readOct

View file

@ -181,12 +181,13 @@ parseUnmerged s
| otherwise = case words metadata of | otherwise = case words metadata of
(rawblobtype:rawsha:rawstage:_) -> do (rawblobtype:rawsha:rawstage:_) -> do
stage <- readish rawstage :: Maybe Int stage <- readish rawstage :: Maybe Int
unless (stage == 2 || stage == 3) $ if stage /= 2 && stage /= 3
fail undefined -- skip stage 1 then Nothing
blobtype <- readBlobType rawblobtype else do
sha <- extractSha rawsha blobtype <- readBlobType rawblobtype
return $ InternalUnmerged (stage == 2) file sha <- extractSha rawsha
(Just blobtype) (Just sha) return $ InternalUnmerged (stage == 2) file
(Just blobtype) (Just sha)
_ -> Nothing _ -> Nothing
where where
(metadata, file) = separate (== '\t') s (metadata, file) = separate (== '\t') s

View file

@ -13,10 +13,6 @@ module Git.LsTree (
parseLsTree parseLsTree
) where ) where
import Numeric
import Control.Applicative
import System.Posix.Types
import Common import Common
import Git import Git
import Git.Command import Git.Command
@ -24,6 +20,9 @@ import Git.Sha
import Git.FilePath import Git.FilePath
import qualified Git.Filename import qualified Git.Filename
import Numeric
import System.Posix.Types
data TreeItem = TreeItem data TreeItem = TreeItem
{ mode :: FileMode { mode :: FileMode
, typeobj :: String , typeobj :: String

View file

@ -1,4 +1,4 @@
{- git remote stuff {- git remote removal
- -
- Copyright 2012 Joey Hess <id@joeyh.name> - Copyright 2012 Joey Hess <id@joeyh.name>
- -

View file

@ -5,6 +5,8 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Git.Version ( module Git.Version (
installed, installed,
older, older,

View file

@ -15,7 +15,7 @@ import qualified Backend
import Annex.Content import Annex.Content
import Annex.UUID import Annex.UUID
import Logs.Trust import Logs.Trust
import Config.NumCopies import Annex.NumCopies
import Types.TrustLevel import Types.TrustLevel
import Types.Key import Types.Key
import Types.Group import Types.Group

View file

@ -12,7 +12,6 @@ module Logs.Difference (
module Logs.Difference.Pure module Logs.Difference.Pure
) where ) where
import Data.Monoid
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import qualified Data.Map as M import qualified Data.Map as M

View file

@ -10,7 +10,6 @@ module Logs.Difference.Pure (
parseDifferencesLog, parseDifferencesLog,
) where ) where
import Data.Monoid
import qualified Data.Map as M import qualified Data.Map as M
import Common.Annex import Common.Annex

View file

@ -1,3 +1,5 @@
{-# LANGUAGE CPP #-}
{- git-annex Map log {- git-annex Map log
- -
- This is used to store a Map, in a way that can be union merged. - This is used to store a Map, in a way that can be union merged.
@ -13,10 +15,9 @@ module Logs.MapLog where
import qualified Data.Map as M import qualified Data.Map as M
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Data.Time
import System.Locale
import Common import Common
import Logs.TimeStamp
data TimeStamp = Unknown | Date POSIXTime data TimeStamp = Unknown | Date POSIXTime
deriving (Eq, Ord, Show) deriving (Eq, Ord, Show)
@ -42,7 +43,7 @@ parseMapLog fieldparser valueparser = M.fromListWith best . mapMaybe parse . lin
parse line = do parse line = do
let (ts, rest) = splitword line let (ts, rest) = splitword line
(sf, sv) = splitword rest (sf, sv) = splitword rest
date <- Date . utcTimeToPOSIXSeconds <$> parseTime defaultTimeLocale "%s%Qs" ts date <- Date <$> parsePOSIXTime ts
f <- fieldparser sf f <- fieldparser sf
v <- valueparser sv v <- valueparser sv
Just (f, LogEntry date v) Just (f, LogEntry date v)

View file

@ -41,12 +41,11 @@ import qualified Annex.Branch
import qualified Annex import qualified Annex
import Logs import Logs
import Logs.SingleValue import Logs.SingleValue
import Logs.TimeStamp
import qualified Data.Set as S import qualified Data.Set as S
import qualified Data.Map as M import qualified Data.Map as M
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Data.Time.Format
import System.Locale
instance SingleValueSerializable MetaData where instance SingleValueSerializable MetaData where
serialize = Types.MetaData.serialize serialize = Types.MetaData.serialize
@ -86,7 +85,7 @@ getCurrentMetaData k = do
ts = lastchangedval l ts = lastchangedval l
in M.map (const ts) m in M.map (const ts) m
lastchangedval l = S.singleton $ toMetaValue $ showts $ changed l lastchangedval l = S.singleton $ toMetaValue $ showts $ changed l
showts = formatTime defaultTimeLocale "%F@%H-%M-%S" . posixSecondsToUTCTime showts = formatPOSIXTime "%F@%H-%M-%S"
{- Adds in some metadata, which can override existing values, or unset {- Adds in some metadata, which can override existing values, or unset
- them, but otherwise leaves any existing metadata as-is. -} - them, but otherwise leaves any existing metadata as-is. -}

View file

@ -8,11 +8,10 @@
module Logs.Presence.Pure where module Logs.Presence.Pure where
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Data.Time
import System.Locale
import qualified Data.Map as M import qualified Data.Map as M
import Common.Annex import Common.Annex
import Logs.TimeStamp
import Utility.QuickCheck import Utility.QuickCheck
data LogLine = LogLine { data LogLine = LogLine {
@ -29,7 +28,7 @@ parseLog :: String -> [LogLine]
parseLog = mapMaybe parseline . lines parseLog = mapMaybe parseline . lines
where where
parseline l = LogLine parseline l = LogLine
<$> (utcTimeToPOSIXSeconds <$> parseTime defaultTimeLocale "%s%Qs" d) <$> parsePOSIXTime d
<*> parseStatus s <*> parseStatus s
<*> pure rest <*> pure rest
where where

View file

@ -15,11 +15,10 @@ module Logs.SingleValue where
import Common.Annex import Common.Annex
import qualified Annex.Branch import qualified Annex.Branch
import Logs.TimeStamp
import qualified Data.Set as S import qualified Data.Set as S
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Data.Time
import System.Locale
class SingleValueSerializable v where class SingleValueSerializable v where
serialize :: v -> String serialize :: v -> String
@ -42,7 +41,7 @@ parseLog = S.fromList . mapMaybe parse . lines
where where
parse line = do parse line = do
let (ts, s) = splitword line let (ts, s) = splitword line
date <- utcTimeToPOSIXSeconds <$> parseTime defaultTimeLocale "%s%Qs" ts date <- parsePOSIXTime ts
v <- deserialize s v <- deserialize s
Just (LogEntry date v) Just (LogEntry date v)
splitword = separate (== ' ') splitword = separate (== ' ')

30
Logs/TimeStamp.hs Normal file
View file

@ -0,0 +1,30 @@
{- log timestamp parsing
-
- Copyright 2015 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU GPL version 3 or higher.
-}
{-# LANGUAGE CPP #-}
module Logs.TimeStamp where
import Data.Time.Clock.POSIX
import Data.Time
#if ! MIN_VERSION_time(1,5,0)
import System.Locale
#endif
import Control.Applicative
import Prelude
{- Parses how POSIXTime shows itself: "1431286201.113452s"
- Also handles the format with no fractional seconds. -}
parsePOSIXTime :: String -> Maybe POSIXTime
#if MIN_VERSION_time(1,5,0)
parsePOSIXTime s = utcTimeToPOSIXSeconds <$> parseTimeM True defaultTimeLocale "%s%Qs" s
#else
parsePOSIXTime s = utcTimeToPOSIXSeconds <$> parseTime defaultTimeLocale "%s%Qs" s
#endif
formatPOSIXTime :: String -> POSIXTime -> String
formatPOSIXTime fmt t = formatTime defaultTimeLocale fmt (posixSecondsToUTCTime t)

View file

@ -18,11 +18,10 @@ import Utility.Percentage
import Utility.QuickCheck import Utility.QuickCheck
import Utility.PID import Utility.PID
import Utility.LockFile import Utility.LockFile
import Logs.TimeStamp
import Data.Time.Clock import Data.Time.Clock
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Data.Time
import System.Locale
import Control.Concurrent import Control.Concurrent
{- Enough information to uniquely identify a transfer, used as the filename {- Enough information to uniquely identify a transfer, used as the filename
@ -276,10 +275,6 @@ readTransferInfo mpid s = TransferInfo
then Just <$> readish =<< headMaybe (drop 1 bits) then Just <$> readish =<< headMaybe (drop 1 bits)
else pure Nothing -- not failure else pure Nothing -- not failure
parsePOSIXTime :: String -> Maybe POSIXTime
parsePOSIXTime s = utcTimeToPOSIXSeconds
<$> parseTime defaultTimeLocale "%s%Qs" s
{- The directory holding transfer information files for a given Direction. -} {- The directory holding transfer information files for a given Direction. -}
transferDir :: Direction -> Git.Repo -> FilePath transferDir :: Direction -> Git.Repo -> FilePath
transferDir direction r = gitAnnexTransferDir r </> showLcDirection direction transferDir direction r = gitAnnexTransferDir r </> showLcDirection direction

View file

@ -15,11 +15,10 @@
module Logs.Transitions where module Logs.Transitions where
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Data.Time
import System.Locale
import qualified Data.Set as S import qualified Data.Set as S
import Common.Annex import Common.Annex
import Logs.TimeStamp
transitionsLog :: FilePath transitionsLog :: FilePath
transitionsLog = "transitions.log" transitionsLog = "transitions.log"
@ -66,12 +65,13 @@ showTransitionLine :: TransitionLine -> String
showTransitionLine (TransitionLine ts t) = unwords [show t, show ts] showTransitionLine (TransitionLine ts t) = unwords [show t, show ts]
parseTransitionLine :: String -> Maybe TransitionLine parseTransitionLine :: String -> Maybe TransitionLine
parseTransitionLine s = TransitionLine <$> pdate ds <*> readish ts parseTransitionLine s = TransitionLine
<$> parsePOSIXTime ds
<*> readish ts
where where
ws = words s ws = words s
ts = Prelude.head ws ts = Prelude.head ws
ds = unwords $ Prelude.tail ws ds = unwords $ Prelude.tail ws
pdate = utcTimeToPOSIXSeconds <$$> parseTime defaultTimeLocale "%s%Qs"
combineTransitions :: [Transitions] -> Transitions combineTransitions :: [Transitions] -> Transitions
combineTransitions = S.unions combineTransitions = S.unions

View file

@ -30,12 +30,11 @@ module Logs.UUIDBased (
import qualified Data.Map as M import qualified Data.Map as M
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Data.Time
import System.Locale
import Common import Common
import Types.UUID import Types.UUID
import Logs.MapLog import Logs.MapLog
import Logs.TimeStamp
type Log v = MapLog UUID v type Log v = MapLog UUID v
@ -73,9 +72,9 @@ parseLogWithUUID parser = M.fromListWith best . mapMaybe parse . lines
info info
| ts == Unknown = drop 1 ws | ts == Unknown = drop 1 ws
| otherwise = drop 1 $ beginning ws | otherwise = drop 1 $ beginning ws
pdate s = case parseTime defaultTimeLocale "%s%Qs" s of pdate s = case parsePOSIXTime s of
Nothing -> Unknown Nothing -> Unknown
Just d -> Date $ utcTimeToPOSIXSeconds d Just d -> Date d
showLogNew :: (v -> String) -> Log v -> String showLogNew :: (v -> String) -> Log v -> String
showLogNew = showMapLog fromUUID showLogNew = showMapLog fromUUID

View file

@ -32,12 +32,12 @@ import qualified Data.Map as M
import qualified Data.Set as S import qualified Data.Set as S
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Data.Time import Data.Time
import System.Locale
import Common.Annex import Common.Annex
import qualified Annex import qualified Annex
import Types.Key import Types.Key
import Utility.Tmp import Utility.Tmp
import Logs.TimeStamp
-- everything that is stored in the unused log -- everything that is stored in the unused log
type UnusedLog = M.Map Key (Int, Maybe POSIXTime) type UnusedLog = M.Map Key (Int, Maybe POSIXTime)
@ -81,7 +81,7 @@ readUnusedLog prefix = do
, return M.empty , return M.empty
) )
where where
parse line = case (readish sint, file2key skey, utcTimeToPOSIXSeconds <$> parseTime defaultTimeLocale "%s%Qs" ts) of parse line = case (readish sint, file2key skey, parsePOSIXTime ts) of
(Just int, Just key, mtimestamp) -> Just (key, (int, mtimestamp)) (Just int, Just key, mtimestamp) -> Just (key, (int, mtimestamp))
_ -> Nothing _ -> Nothing
where where

View file

@ -140,6 +140,25 @@ linuxstandalone-nobuild: Build/Standalone Build/LinuxMkLibs
cd tmp/git-annex.linux && find . -type l >> git-annex.MANIFEST cd tmp/git-annex.linux && find . -type l >> git-annex.MANIFEST
cd tmp && tar czf git-annex-standalone-$(shell dpkg --print-architecture).tar.gz git-annex.linux cd tmp && tar czf git-annex-standalone-$(shell dpkg --print-architecture).tar.gz git-annex.linux
# Run this target to build git-annex-standalone*.deb
debianstandalone: dpkg-buildpackage-F
# Run this target to build git-annex-standalone*.dsc
debianstandalone-dsc: dpkg-buildpackage-S
prep-standalone:
$(MAKE) undo-standalone
QUILT_PATCHES=debian/patches QUILT_SERIES=series.standalone-build quilt push -a
debian/create-standalone-changelog
undo-standalone:
test -e .git
git checkout debian/changelog
quilt pop -a || true
dpkg-buildpackage%: prep-standalone
umask 022; dpkg-buildpackage -rfakeroot $*
$(MAKE) undo-standalone
OSXAPP_DEST=tmp/build-dmg/git-annex.app OSXAPP_DEST=tmp/build-dmg/git-annex.app
OSXAPP_BASE=$(OSXAPP_DEST)/Contents/MacOS/bundle OSXAPP_BASE=$(OSXAPP_DEST)/Contents/MacOS/bundle
osxapp: Build/Standalone Build/OSXMkLibs osxapp: Build/Standalone Build/OSXMkLibs

View file

@ -20,7 +20,7 @@ module Messages (
showEndFail, showEndFail,
showEndResult, showEndResult,
endResult, endResult,
showErr, toplevelWarning,
warning, warning,
warningIO, warningIO,
indent, indent,
@ -118,15 +118,16 @@ endResult :: Bool -> String
endResult True = "ok" endResult True = "ok"
endResult False = "failed" endResult False = "failed"
showErr :: (Show a) => a -> Annex () toplevelWarning :: Bool -> String -> Annex ()
showErr e = warning' $ "git-annex: " ++ show e toplevelWarning makeway s = warning' makeway ("git-annex: " ++ s)
warning :: String -> Annex () warning :: String -> Annex ()
warning = warning' . indent warning = warning' True . indent
warning' :: String -> Annex () warning' :: Bool -> String -> Annex ()
warning' w = do warning' makeway w = do
handleMessage q $ putStr "\n" when makeway $
handleMessage q $ putStr "\n"
liftIO $ do liftIO $ do
hFlush stdout hFlush stdout
hPutStrLn stderr w hPutStrLn stderr w

View file

@ -282,7 +282,9 @@ showLocations separateuntrusted key exclude nolocmsg = do
let uuidsskipped = filteruuids uuids (u:exclude++uuidswanted) let uuidsskipped = filteruuids uuids (u:exclude++uuidswanted)
ppuuidswanted <- prettyPrintUUIDs "wanted" uuidswanted ppuuidswanted <- prettyPrintUUIDs "wanted" uuidswanted
ppuuidsskipped <- prettyPrintUUIDs "skipped" uuidsskipped ppuuidsskipped <- prettyPrintUUIDs "skipped" uuidsskipped
showLongNote $ message ppuuidswanted ppuuidsskipped let msg = message ppuuidswanted ppuuidsskipped
unless (null msg) $
showLongNote msg
ignored <- filter (remoteAnnexIgnore . gitconfig) <$> remoteList ignored <- filter (remoteAnnexIgnore . gitconfig) <$> remoteList
unless (null ignored) $ unless (null ignored) $
showLongNote $ "(Note that these git remotes have annex-ignore set: " ++ unwords (map name ignored) ++ ")" showLongNote $ "(Note that these git remotes have annex-ignore set: " ++ unwords (map name ignored) ++ ")"

View file

@ -162,9 +162,13 @@ retrieveCheap _ (UnpaddedChunks _) _ _ _ = return False
retrieveCheap _ (LegacyChunks _) _ _ _ = return False retrieveCheap _ (LegacyChunks _) _ _ _ = return False
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
retrieveCheap d NoChunks k _af f = liftIO $ catchBoolIO $ do retrieveCheap d NoChunks k _af f = liftIO $ catchBoolIO $ do
file <- getLocation d k file <- absPath =<< getLocation d k
createSymbolicLink file f ifM (doesFileExist file)
return True ( do
createSymbolicLink file f
return True
, return False
)
#else #else
retrieveCheap _ _ _ _ _ = return False retrieveCheap _ _ _ _ _ = return False
#endif #endif

View file

@ -397,7 +397,7 @@ getGCryptId fast r gc
| Git.repoIsLocal r || Git.repoIsLocalUnknown r = extract <$> | Git.repoIsLocal r || Git.repoIsLocalUnknown r = extract <$>
liftIO (catchMaybeIO $ Git.Config.read r) liftIO (catchMaybeIO $ Git.Config.read r)
| not fast = extract . liftM fst <$> getM (eitherToMaybe <$>) | not fast = extract . liftM fst <$> getM (eitherToMaybe <$>)
[ Ssh.onRemote r (Git.Config.fromPipe r, return (Left undefined)) "configlist" [] [] [ Ssh.onRemote r (Git.Config.fromPipe r, return (Left $ error "configlist failed")) "configlist" [] []
, getConfigViaRsync r gc , getConfigViaRsync r gc
] ]
| otherwise = return (Nothing, r) | otherwise = return (Nothing, r)

View file

@ -200,7 +200,7 @@ tryGitConfigRead :: Git.Repo -> Annex Git.Repo
tryGitConfigRead r tryGitConfigRead r
| haveconfig r = return r -- already read | haveconfig r = return r -- already read
| Git.repoIsSsh r = store $ do | Git.repoIsSsh r = store $ do
v <- Ssh.onRemote r (pipedconfig, return (Left undefined)) "configlist" [] [] v <- Ssh.onRemote r (pipedconfig, return (Left $ error "configlist failed")) "configlist" [] []
case v of case v of
Right r' Right r'
| haveconfig r' -> return r' | haveconfig r' -> return r'
@ -229,9 +229,10 @@ tryGitConfigRead r
uo <- Url.getUrlOptions uo <- Url.getUrlOptions
v <- liftIO $ withTmpFile "git-annex.tmp" $ \tmpfile h -> do v <- liftIO $ withTmpFile "git-annex.tmp" $ \tmpfile h -> do
hClose h hClose h
ifM (Url.downloadQuiet (Git.repoLocation r ++ "/config") tmpfile uo) let url = Git.repoLocation r ++ "/config"
ifM (Url.downloadQuiet url tmpfile uo)
( pipedconfig "git" [Param "config", Param "--null", Param "--list", Param "--file", File tmpfile] ( pipedconfig "git" [Param "config", Param "--null", Param "--list", Param "--file", File tmpfile]
, return $ Left undefined , return $ Left $ error $ "unable to load config from " ++ url
) )
case v of case v of
Left _ -> do Left _ -> do
@ -450,10 +451,17 @@ copyFromRemote' r key file dest meterupdate
copyFromRemoteCheap :: Remote -> Key -> AssociatedFile -> FilePath -> Annex Bool copyFromRemoteCheap :: Remote -> Key -> AssociatedFile -> FilePath -> Annex Bool
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
copyFromRemoteCheap r key af file copyFromRemoteCheap r key af file
| not $ Git.repoIsUrl (repo r) = guardUsable (repo r) (return False) $ do | not $ Git.repoIsUrl (repo r) = guardUsable (repo r) (return False) $ liftIO $ do
loc <- liftIO $ gitAnnexLocation key (repo r) $ loc <- gitAnnexLocation key (repo r) $
fromJust $ remoteGitConfig $ gitconfig r fromJust $ remoteGitConfig $ gitconfig r
liftIO $ catchBoolIO $ createSymbolicLink loc file >> return True ifM (doesFileExist loc)
( do
absloc <- absPath loc
catchBoolIO $ do
createSymbolicLink absloc file
return True
, return False
)
| Git.repoIsSsh (repo r) = | Git.repoIsSsh (repo r) =
ifM (Annex.Content.preseedTmp key file) ifM (Annex.Content.preseedTmp key file)
( parallelMetered Nothing key af $ ( parallelMetered Nothing key af $

View file

@ -72,7 +72,7 @@ chunkKeyStream basek chunksize = ChunkKeyStream $ map mk [1..]
nextChunkKeyStream :: ChunkKeyStream -> (Key, ChunkKeyStream) nextChunkKeyStream :: ChunkKeyStream -> (Key, ChunkKeyStream)
nextChunkKeyStream (ChunkKeyStream (k:l)) = (k, ChunkKeyStream l) nextChunkKeyStream (ChunkKeyStream (k:l)) = (k, ChunkKeyStream l)
nextChunkKeyStream (ChunkKeyStream []) = undefined -- stream is infinite! nextChunkKeyStream (ChunkKeyStream []) = error "expected infinite ChunkKeyStream"
takeChunkKeyStream :: ChunkCount -> ChunkKeyStream -> [Key] takeChunkKeyStream :: ChunkCount -> ChunkKeyStream -> [Key]
takeChunkKeyStream n (ChunkKeyStream l) = genericTake n l takeChunkKeyStream n (ChunkKeyStream l) = genericTake n l

View file

@ -20,7 +20,8 @@ module Remote.Helper.Encryptable (
) where ) where
import qualified Data.Map as M import qualified Data.Map as M
import qualified "dataenc" Codec.Binary.Base64 as B64 import qualified "sandi" Codec.Binary.Base64 as B64
import qualified Data.ByteString as B
import Data.Bits.Utils import Data.Bits.Utils
import Common.Annex import Common.Annex
@ -172,12 +173,12 @@ describeEncryption c = case extractCipher c of
] ]
{- Not using Utility.Base64 because these "Strings" are really {- Not using Utility.Base64 because these "Strings" are really
- bags of bytes and that would convert to unicode and not roung-trip - bags of bytes and that would convert to unicode and not round-trip
- cleanly. -} - cleanly. -}
toB64bs :: String -> String toB64bs :: String -> String
toB64bs = B64.encode . s2w8 toB64bs = w82s . B.unpack . B64.encode . B.pack . s2w8
fromB64bs :: String -> String fromB64bs :: String -> String
fromB64bs s = fromMaybe bad $ w82s <$> B64.decode s fromB64bs s = either (const bad) (w82s . B.unpack) (B64.decode $ B.pack $ s2w8 s)
where where
bad = error "bad base64 encoded data" bad = error "bad base64 encoded data"

View file

@ -199,7 +199,7 @@ specialRemote' cfg c preparestorer prepareretriever prepareremover preparecheckp
readBytes $ \encb -> readBytes $ \encb ->
storer (enck k) (ByteContent encb) p storer (enck k) (ByteContent encb) p
-- call retrieve-r to get chunks; decrypt them; stream to dest file -- call retriever to get chunks; decrypt them; stream to dest file
retrieveKeyFileGen k f dest p enc = retrieveKeyFileGen k f dest p enc =
safely $ prepareretriever k $ safely . go safely $ prepareretriever k $ safely . go
where where

View file

@ -28,6 +28,8 @@ import Control.Monad.Trans.Resource
import Control.Monad.Catch import Control.Monad.Catch
import Data.Conduit import Data.Conduit
import Data.IORef import Data.IORef
import Data.Bits.Utils
import System.Log.Logger
import Common.Annex import Common.Annex
import Types.Remote import Types.Remote
@ -88,13 +90,7 @@ gen r u c gc = do
, availability = GloballyAvailable , availability = GloballyAvailable
, remotetype = remote , remotetype = remote
, mkUnavailable = gen r u (M.insert "host" "!dne!" c) gc , mkUnavailable = gen r u (M.insert "host" "!dne!" c) gc
, getInfo = includeCredsInfo c (AWS.creds u) $ catMaybes , getInfo = includeCredsInfo c (AWS.creds u) (s3Info c)
[ Just ("bucket", fromMaybe "unknown" (getBucketName c))
, if configIA c
then Just ("internet archive item", iaItemUrl $ fromMaybe "unknown" $ getBucketName c)
else Nothing
, Just ("partsize", maybe "unlimited" (roughSize storageUnits False) (getPartSize c))
]
, claimUrl = Nothing , claimUrl = Nothing
, checkUrl = Nothing , checkUrl = Nothing
} }
@ -102,9 +98,9 @@ gen r u c gc = do
s3Setup :: Maybe UUID -> Maybe CredPair -> RemoteConfig -> Annex (RemoteConfig, UUID) s3Setup :: Maybe UUID -> Maybe CredPair -> RemoteConfig -> Annex (RemoteConfig, UUID)
s3Setup mu mcreds c = do s3Setup mu mcreds c = do
u <- maybe (liftIO genUUID) return mu u <- maybe (liftIO genUUID) return mu
s3Setup' u mcreds c s3Setup' (isNothing mu) u mcreds c
s3Setup' :: UUID -> Maybe CredPair -> RemoteConfig -> Annex (RemoteConfig, UUID) s3Setup' :: Bool -> UUID -> Maybe CredPair -> RemoteConfig -> Annex (RemoteConfig, UUID)
s3Setup' u mcreds c = if configIA c then archiveorg else defaulthost s3Setup' new u mcreds c = if configIA c then archiveorg else defaulthost
where where
remotename = fromJust (M.lookup "name" c) remotename = fromJust (M.lookup "name" c)
defbucket = remotename ++ "-" ++ fromUUID u defbucket = remotename ++ "-" ++ fromUUID u
@ -124,7 +120,8 @@ s3Setup' u mcreds c = if configIA c then archiveorg else defaulthost
(c', encsetup) <- encryptionSetup c (c', encsetup) <- encryptionSetup c
c'' <- setRemoteCredPair encsetup c' (AWS.creds u) mcreds c'' <- setRemoteCredPair encsetup c' (AWS.creds u) mcreds
let fullconfig = c'' `M.union` defaults let fullconfig = c'' `M.union` defaults
genBucket fullconfig u when new $
genBucket fullconfig u
use fullconfig use fullconfig
archiveorg = do archiveorg = do
@ -132,7 +129,7 @@ s3Setup' u mcreds c = if configIA c then archiveorg else defaulthost
c' <- setRemoteCredPair noEncryptionUsed c (AWS.creds u) mcreds c' <- setRemoteCredPair noEncryptionUsed c (AWS.creds u) mcreds
-- Ensure user enters a valid bucket name, since -- Ensure user enters a valid bucket name, since
-- this determines the name of the archive.org item. -- this determines the name of the archive.org item.
let validbucket = replace " " "-" $ map toLower $ let validbucket = replace " " "-" $
fromMaybe (error "specify bucket=") $ fromMaybe (error "specify bucket=") $
getBucketName c' getBucketName c'
let archiveconfig = let archiveconfig =
@ -149,7 +146,7 @@ s3Setup' u mcreds c = if configIA c then archiveorg else defaulthost
writeUUIDFile archiveconfig u writeUUIDFile archiveconfig u
use archiveconfig use archiveconfig
-- Sets up a http connection manager for S3 encdpoint, which allows -- Sets up a http connection manager for S3 endpoint, which allows
-- http connections to be reused across calls to the helper. -- http connections to be reused across calls to the helper.
prepareS3 :: Remote -> S3Info -> (S3Handle -> helper) -> Preparer helper prepareS3 :: Remote -> S3Info -> (S3Handle -> helper) -> Preparer helper
prepareS3 r info = resourcePrepare $ const $ prepareS3 r info = resourcePrepare $ const $
@ -388,13 +385,13 @@ sendS3Handle'
=> S3Handle => S3Handle
-> r -> r
-> ResourceT IO a -> ResourceT IO a
sendS3Handle' h = AWS.pureAws (hawscfg h) (hs3cfg h) (hmanager h) sendS3Handle' h r = AWS.pureAws (hawscfg h) (hs3cfg h) (hmanager h) r
withS3Handle :: RemoteConfig -> UUID -> S3Info -> (S3Handle -> Annex a) -> Annex a withS3Handle :: RemoteConfig -> UUID -> S3Info -> (S3Handle -> Annex a) -> Annex a
withS3Handle c u info a = do withS3Handle c u info a = do
creds <- getRemoteCredPairFor "S3" c (AWS.creds u) creds <- getRemoteCredPairFor "S3" c (AWS.creds u)
awscreds <- liftIO $ genCredentials $ fromMaybe nocreds creds awscreds <- liftIO $ genCredentials $ fromMaybe nocreds creds
let awscfg = AWS.Configuration AWS.Timestamp awscreds (AWS.defaultLog AWS.Error) let awscfg = AWS.Configuration AWS.Timestamp awscreds debugMapper
bracketIO (newManager httpcfg) closeManager $ \mgr -> bracketIO (newManager httpcfg) closeManager $ \mgr ->
a $ S3Handle mgr awscfg s3cfg info a $ S3Handle mgr awscfg s3cfg info
where where
@ -450,7 +447,7 @@ extractS3Info c = do
} }
getBucketName :: RemoteConfig -> Maybe BucketName getBucketName :: RemoteConfig -> Maybe BucketName
getBucketName = M.lookup "bucket" getBucketName = map toLower <$$> M.lookup "bucket"
getStorageClass :: RemoteConfig -> S3.StorageClass getStorageClass :: RemoteConfig -> S3.StorageClass
getStorageClass c = case M.lookup "storageclass" c of getStorageClass c = case M.lookup "storageclass" c of
@ -486,7 +483,7 @@ iaMunge = (>>= munge)
where where
munge c munge c
| isAsciiUpper c || isAsciiLower c || isNumber c = [c] | isAsciiUpper c || isAsciiLower c || isNumber c = [c]
| c `elem` "_-.\"" = [c] | c `elem` ("_-.\"" :: String) = [c]
| isSpace c = [] | isSpace c = []
| otherwise = "&" ++ show (ord c) ++ ";" | otherwise = "&" ++ show (ord c) ++ ";"
@ -518,3 +515,26 @@ genCredentials (keyid, secret) = AWS.Credentials
mkLocationConstraint :: AWS.Region -> S3.LocationConstraint mkLocationConstraint :: AWS.Region -> S3.LocationConstraint
mkLocationConstraint "US" = S3.locationUsClassic mkLocationConstraint "US" = S3.locationUsClassic
mkLocationConstraint r = r mkLocationConstraint r = r
debugMapper :: AWS.Logger
debugMapper level t = forward "S3" (T.unpack t)
where
forward = case level of
AWS.Debug -> debugM
AWS.Info -> infoM
AWS.Warning -> warningM
AWS.Error -> errorM
s3Info :: RemoteConfig -> [(String, String)]
s3Info c = catMaybes
[ Just ("bucket", fromMaybe "unknown" (getBucketName c))
, Just ("endpoint", w82s (S.unpack (S3.s3Endpoint s3c)))
, Just ("port", show (S3.s3Port s3c))
, Just ("storage class", show (getStorageClass c))
, if configIA c
then Just ("internet archive item", iaItemUrl $ fromMaybe "unknown" $ getBucketName c)
else Nothing
, Just ("partsize", maybe "unlimited" (roughSize storageUnits False) (getPartSize c))
]
where
s3c = s3Configuration c

View file

@ -177,7 +177,7 @@ getSharedConvergenceSecret configdir = go (60 :: Int)
v <- catchMaybeIO (readFile f) v <- catchMaybeIO (readFile f)
case v of case v of
Just s | "\n" `isSuffixOf` s || "\r" `isSuffixOf` s -> Just s | "\n" `isSuffixOf` s || "\r" `isSuffixOf` s ->
return $ takeWhile (`notElem` "\n\r") s return $ takeWhile (`notElem` ("\n\r" :: String)) s
_ -> do _ -> do
threadDelaySeconds (Seconds 1) threadDelaySeconds (Seconds 1)
go (n - 1) go (n - 1)

View file

@ -1,4 +1,5 @@
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
{- cabal setup file -} {- cabal setup file -}

View file

@ -14,7 +14,6 @@ import Test.Tasty.Runners
import Test.Tasty.HUnit import Test.Tasty.HUnit
import Test.Tasty.QuickCheck import Test.Tasty.QuickCheck
import Test.Tasty.Ingredients.Rerun import Test.Tasty.Ingredients.Rerun
import Data.Monoid
import Options.Applicative hiding (command) import Options.Applicative hiding (command)
import qualified Data.Map as M import qualified Data.Map as M
@ -156,6 +155,7 @@ properties = localOption (QuickCheckTests 1000) $ testGroup "QuickCheck"
, testProperty "prop_read_show_TrustLevel" Types.TrustLevel.prop_read_show_TrustLevel , testProperty "prop_read_show_TrustLevel" Types.TrustLevel.prop_read_show_TrustLevel
, testProperty "prop_parse_show_TrustLog" Logs.Trust.prop_parse_show_TrustLog , testProperty "prop_parse_show_TrustLog" Logs.Trust.prop_parse_show_TrustLog
, testProperty "prop_hashes_stable" Utility.Hash.prop_hashes_stable , testProperty "prop_hashes_stable" Utility.Hash.prop_hashes_stable
, testProperty "prop_mac_stable" Utility.Hash.prop_mac_stable
, testProperty "prop_schedule_roundtrips" Utility.Scheduled.prop_schedule_roundtrips , testProperty "prop_schedule_roundtrips" Utility.Scheduled.prop_schedule_roundtrips
, testProperty "prop_past_sane" Utility.Scheduled.prop_past_sane , testProperty "prop_past_sane" Utility.Scheduled.prop_past_sane
, testProperty "prop_duration_roundtrips" Utility.HumanTime.prop_duration_roundtrips , testProperty "prop_duration_roundtrips" Utility.HumanTime.prop_duration_roundtrips
@ -199,6 +199,7 @@ unitTests note = testGroup ("Unit Tests " ++ note)
, testCase "fsck (bare)" test_fsck_bare , testCase "fsck (bare)" test_fsck_bare
, testCase "fsck (local untrusted)" test_fsck_localuntrusted , testCase "fsck (local untrusted)" test_fsck_localuntrusted
, testCase "fsck (remote untrusted)" test_fsck_remoteuntrusted , testCase "fsck (remote untrusted)" test_fsck_remoteuntrusted
, testCase "fsck --from remote" test_fsck_fromremote
, testCase "migrate" test_migrate , testCase "migrate" test_migrate
, testCase "migrate (via gitattributes)" test_migrate_via_gitattributes , testCase "migrate (via gitattributes)" test_migrate_via_gitattributes
, testCase "unused" test_unused , testCase "unused" test_unused
@ -613,6 +614,10 @@ test_fsck_remoteuntrusted = intmpclonerepo $ do
git_annex "untrust" ["origin"] @? "untrust of origin failed" git_annex "untrust" ["origin"] @? "untrust of origin failed"
fsck_should_fail "content not replicated to enough non-untrusted repositories" fsck_should_fail "content not replicated to enough non-untrusted repositories"
test_fsck_fromremote :: Assertion
test_fsck_fromremote = intmpclonerepo $ do
git_annex "fsck" ["--from", "origin"] @? "fsck --from origin failed"
fsck_should_fail :: String -> Assertion fsck_should_fail :: String -> Assertion
fsck_should_fail m = not <$> git_annex "fsck" [] fsck_should_fail m = not <$> git_annex "fsck" []
@? "fsck failed to fail with " ++ m @? "fsck failed to fail with " ++ m

Some files were not shown because too many files have changed in this diff Show more