File: Config.hs

package info (click to toggle)
git-annex 10.20250416-2
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 73,572 kB
  • sloc: haskell: 90,656; javascript: 9,103; sh: 1,469; makefile: 211; perl: 137; ansic: 44
file content (216 lines) | stat: -rw-r--r-- 6,243 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
{- git-annex command
 -
 - Copyright 2017-2023 Joey Hess <id@joeyh.name>
 -
 - Licensed under the GNU AGPL version 3 or higher.
 -}

{-# LANGUAGE OverloadedStrings #-}

module Command.Config where

import Command
import Logs.Config
import Config
import Types.GitConfig (globalConfigs)
import Git.Types (fromConfigValue, fromConfigKey)
import qualified Git.Command
import Utility.SafeOutput
import Annex.CheckAttr
import Types.NumCopies
import Logs.NumCopies

import qualified Data.ByteString.Char8 as S8

cmd :: Command
cmd = noMessages $ command "config" SectionSetup
	"configuration stored in git-annex branch"
	paramNothing (seek <$$> optParser)

data Action
	= SetConfig ConfigKey ConfigValue
	| GetConfig ConfigKey
	| UnsetConfig ConfigKey
	| ShowOrigin ConfigKey (Maybe FilePath)

type Name = String
type Value = String

optParser :: CmdParamsDesc -> Parser Action
optParser _ = setconfig <|> getconfig <|> unsetconfig <|> showorigin
  where
	setconfig = SetConfig
		<$> strOption
			( long "set"
			<> help "set configuration"
			<> metavar paramName
			)
		<*> strArgument
			( metavar paramValue
			)
	getconfig = GetConfig <$> strOption
		( long "get"
		<> help "get configuration"
		<> metavar paramName
		)
	unsetconfig = UnsetConfig <$> strOption
		( long "unset"
		<> help "unset configuration"
		<> metavar paramName
		)
	showorigin = ShowOrigin
		<$> strOption
			( long "show-origin"
			<> help "explain where a value is configured"
			<> metavar paramName
			)
		<*> optional (strOption
			( long "for-file"
			<> help "filename to check for in gitattributes"
			<> metavar paramFile
			))

seek :: Action -> CommandSeek
seek (SetConfig ck@(ConfigKey name) val) = checkIsGlobalConfig ck $ \setter _unsetter _getter ->
	commandAction $ startingUsualMessages (decodeBS name) ai si $ do
		setter val
		when (needLocalUpdate ck) $
			setConfig ck (fromConfigValue val)
		next $ return True
  where
	ai = ActionItemOther (Just (UnquotedString (fromConfigValue val)))
	si = SeekInput [decodeBS name]
seek (UnsetConfig ck@(ConfigKey name)) = checkIsGlobalConfig ck $ \_setter unsetter _getter ->
	commandAction $ startingUsualMessages (decodeBS name) ai si $ do
		unsetter
		when (needLocalUpdate ck) $
			unsetConfig ck
		next $ return True
  where
	ai = ActionItemOther (Just "unset")
	si = SeekInput [decodeBS name]
seek (GetConfig ck) = checkIsGlobalConfig ck $ \_setter _unsetter getter ->
	commandAction $	startingCustomOutput ai $ do
		getter >>= \case
			Just (ConfigValue v) -> liftIO $ S8.putStrLn $ safeOutput v
			Just NoConfigValue -> return ()
			Nothing -> return ()
		next $ return True
  where
	ai = ActionItemOther Nothing
seek (ShowOrigin ck@(ConfigKey name) forfile) = commandAction $
	startingCustomOutput ai $ next $ checknotconfigured $
		case checkIsGlobalConfig' ck of
			Just (_setter, _unsetter, getter) ->
				ifM gitconfigorigin
					( return True
					, checkattrs (checkconfigbranch getter)
					)
			Nothing -> ifM gitconfigorigin
				( return True
				, checkattrs checkgitconfigunderride
				)
  where
	ai = ActionItemOther Nothing

	gitconfigorigin
		| name `elem` gitconfigdoesnotoverride = return False
		| otherwise = gitconfigorigin'
	gitconfigorigin' = inRepo $ Git.Command.runBool
			[ Param "config"
			, Param "--show-origin"
			, Param (decodeBS name)
			]
	
	-- git configs for these do not override values from git attributes
	-- or the branch
	gitconfigdoesnotoverride =
		[ "annex.numcopies"
		, "annex.mincopies"
		]

	-- the git config for annex.numcopies is a special case; it's only
	-- used if not configured anywhere else
	checkgitconfigunderride
		| name == "annex.numcopies" = gitconfigorigin'
		| otherwise = return False

	-- Display similar to git config --show-origin
	showval loc v = liftIO $ do
		putStrLn $ loc ++ "\t" ++ v
		return True
	
	configbranch v
		| needLocalUpdate ck = checkgitconfigunderride
		| otherwise = showval "branch:git-annex" (decodeBS v)
	
	checkconfigbranch getter = getter >>= \case
		Just (ConfigValue v) -> configbranch v
		_ -> checkgitconfigunderride
	
	checkattrs cont
		| decodeBS name `elem` annexAttrs =
			case forfile of
				Just file -> do
					v <- checkAttr (decodeBS name) (toOsPath file)
					if null v
						then cont
						else showval "gitattributes" v		
				Nothing -> do
					warnforfile
					cont
		| otherwise = cont
	
	warnforfile = warning $ UnquotedString $ configKeyMessage ck $ unwords
		[ "may be configured in gitattributes."
		, "Pass --for-file= with a filename to check"
		]
	
	checknotconfigured a = do
		ok <- a
		unless ok $
			warning $ UnquotedString $ configKeyMessage ck
				"is not configured"
		return ok

type Setter = ConfigValue -> Annex ()
type Unsetter = Annex ()
type Getter = Annex (Maybe ConfigValue)

checkIsGlobalConfig :: ConfigKey -> (Setter -> Unsetter -> Getter -> Annex a) -> Annex a
checkIsGlobalConfig ck a = case checkIsGlobalConfig' ck of
	Just (setter, unsetter, getter) -> a setter unsetter getter
	Nothing -> giveup $ configKeyMessage ck "is not a configuration setting that can be stored in the git-annex branch"

checkIsGlobalConfig' :: ConfigKey -> Maybe (Setter, Unsetter, Getter)
checkIsGlobalConfig' ck
	| elem ck globalConfigs = Just
		( setGlobalConfig ck
		, unsetGlobalConfig ck
		, getGlobalConfig ck
		)
	-- These came before this command, but are also global configs,
	-- so support them here as well.
	| ck == ConfigKey "annex.numcopies" = Just
		( mksetter (setGlobalNumCopies . configuredNumCopies)
		, error "unsetting annex.numcopies is not supported"
		, mkgetter fromNumCopies getGlobalNumCopies
		)
	| ck == ConfigKey "annex.mincopies" = Just
		( mksetter (setGlobalMinCopies . configuredMinCopies)
		, error "unsetting annex.mincopies is not supported"
		, mkgetter fromMinCopies getGlobalMinCopies
		)
	| otherwise = Nothing
  where
	mksetter f = 
		maybe (error ("invalid value for " ++ fromConfigKey ck)) f 
			. readish . decodeBS . fromConfigValue
	mkgetter f g = fmap (ConfigValue . encodeBS . show . f) <$> g

configKeyMessage :: ConfigKey -> String -> String
configKeyMessage (ConfigKey name) msg = decodeBS name ++ " " ++ msg

needLocalUpdate :: ConfigKey -> Bool
needLocalUpdate (ConfigKey "annex.securehashesonly") = True
needLocalUpdate _ = False