-- | Maintainer: Sean Whitton <spwhitton@spwhitton.name>

module Propellor.Property.Ccache (
	hasCache,
	hasLimits,
	Limit(..),
	DataSize,
) where

import Propellor.Base
import qualified Propellor.Property.File as File
import qualified Propellor.Property.Apt as Apt

import Utility.DataUnits
import System.Posix.Files
import qualified Data.Semigroup as Sem

-- | Limits on the size of a ccache
data Limit
	-- | The maximum size of the cache, as a string such as "4G"
	= MaxSize DataSize
	-- | The maximum number of files in the cache
	| MaxFiles Integer
	-- | A cache with no limit specified
	| NoLimit
	| Limit :+ Limit

instance Sem.Semigroup Limit where
	<> :: Limit -> Limit -> Limit
(<>) = Limit -> Limit -> Limit
(:+)

instance Monoid Limit where
	mempty :: Limit
mempty  = Limit
NoLimit
	mappend :: Limit -> Limit -> Limit
mappend = Limit -> Limit -> Limit
forall a. Semigroup a => a -> a -> a
(Sem.<>)

-- | A string that will be parsed to get a data size.
--
-- Examples: "100 megabytes" or "0.5tb"
type DataSize = String

maxSizeParam :: DataSize -> Maybe String
maxSizeParam :: DataSize -> Maybe DataSize
maxSizeParam DataSize
s = [Unit] -> DataSize -> Maybe ByteSize
readSize [Unit]
dataUnits DataSize
s
	Maybe ByteSize -> (ByteSize -> Maybe DataSize) -> Maybe DataSize
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \ByteSize
sz -> DataSize -> Maybe DataSize
forall a. a -> Maybe a
Just (DataSize -> Maybe DataSize) -> DataSize -> Maybe DataSize
forall a b. (a -> b) -> a -> b
$ DataSize
"--max-size=" DataSize -> DataSize -> DataSize
forall a. [a] -> [a] -> [a]
++ ByteSize -> DataSize
ccacheSizeUnits ByteSize
sz

-- Generates size units as used in ccache.conf.  The smallest unit we can
-- specify in a ccache config files is a kilobyte
ccacheSizeUnits :: Integer -> String
ccacheSizeUnits :: ByteSize -> DataSize
ccacheSizeUnits ByteSize
sz = (Char -> Bool) -> DataSize -> DataSize
forall a. (a -> Bool) -> [a] -> [a]
filter (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
' ') ([Unit] -> Bool -> ByteSize -> DataSize
roughSize [Unit]
cfgfileunits Bool
True ByteSize
sz)
  where
	cfgfileunits :: [Unit]
	cfgfileunits :: [Unit]
cfgfileunits =
	        [ ByteSize -> DataSize -> DataSize -> Unit
Unit (ByteSize -> ByteSize
p ByteSize
4) DataSize
"Ti" DataSize
"terabyte"
		, ByteSize -> DataSize -> DataSize -> Unit
Unit (ByteSize -> ByteSize
p ByteSize
3) DataSize
"Gi" DataSize
"gigabyte"
		, ByteSize -> DataSize -> DataSize -> Unit
Unit (ByteSize -> ByteSize
p ByteSize
2) DataSize
"Mi" DataSize
"megabyte"
		, ByteSize -> DataSize -> DataSize -> Unit
Unit (ByteSize -> ByteSize
p ByteSize
1) DataSize
"Ki" DataSize
"kilobyte"
		]
	p :: Integer -> Integer
	p :: ByteSize -> ByteSize
p ByteSize
n = ByteSize
1024ByteSize -> ByteSize -> ByteSize
forall a b. (Num a, Integral b) => a -> b -> a
^ByteSize
n

-- | Set limits on a given ccache
hasLimits :: FilePath -> Limit -> Property DebianLike
DataSize
path hasLimits :: DataSize -> Limit -> Property DebianLike
`hasLimits` Limit
limit = Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
go Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property DebianLike
-> CombinedType
     (Property
        (MetaTypes
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
     (Property DebianLike)
forall x y. Combines x y => x -> y -> CombinedType x y
`requires` Property DebianLike
installed
  where
	go :: Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
go
		| [DataSize] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [DataSize]
params' = Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall k (t :: k). SingI t => Property (MetaTypes t)
doNothing
		-- We invoke ccache itself to set the limits, so that it can
		-- handle replacing old limits in the config file, duplicates
		-- etc.
		| [DataSize] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [DataSize]
errors =
			DataSize
-> [DataSize]
-> [(DataSize, DataSize)]
-> UncheckedProperty
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
cmdPropertyEnv DataSize
"ccache" [DataSize]
params' [(DataSize
"CCACHE_DIR", DataSize
path)]
			UncheckedProperty
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> DataSize
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall (p :: * -> *) i.
Checkable p i =>
p i -> DataSize -> Property i
`changesFileContent` (DataSize
path DataSize -> DataSize -> DataSize
</> DataSize
"ccache.conf")
		| Bool
otherwise = DataSize
-> Propellor Result
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall k (metatypes :: k).
SingI metatypes =>
DataSize -> Propellor Result -> Property (MetaTypes metatypes)
property DataSize
"couldn't parse ccache limits" (Propellor Result
 -> Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
-> Propellor Result
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall a b. (a -> b) -> a -> b
$
			DataSize -> Propellor Result
forall (m :: * -> *) a. MonadIO m => DataSize -> m a
errorMessage (DataSize -> Propellor Result) -> DataSize -> Propellor Result
forall a b. (a -> b) -> a -> b
$ [DataSize] -> DataSize
unlines [DataSize]
errors

	params :: [Either DataSize DataSize]
params = Limit -> [Either DataSize DataSize]
limitToParams Limit
limit
	([DataSize]
errors, [DataSize]
params') = [Either DataSize DataSize] -> ([DataSize], [DataSize])
forall a b. [Either a b] -> ([a], [b])
partitionEithers [Either DataSize DataSize]
params

limitToParams :: Limit -> [Either String String]
limitToParams :: Limit -> [Either DataSize DataSize]
limitToParams Limit
NoLimit = []
limitToParams (MaxSize DataSize
s) = case DataSize -> Maybe DataSize
maxSizeParam DataSize
s of
	Just DataSize
param -> [DataSize -> Either DataSize DataSize
forall a b. b -> Either a b
Right DataSize
param]
	Maybe DataSize
Nothing -> [DataSize -> Either DataSize DataSize
forall a b. a -> Either a b
Left (DataSize -> Either DataSize DataSize)
-> DataSize -> Either DataSize DataSize
forall a b. (a -> b) -> a -> b
$ DataSize
"unable to parse data size " DataSize -> DataSize -> DataSize
forall a. [a] -> [a] -> [a]
++ DataSize
s]
limitToParams (MaxFiles ByteSize
f) = [DataSize -> Either DataSize DataSize
forall a b. b -> Either a b
Right (DataSize -> Either DataSize DataSize)
-> DataSize -> Either DataSize DataSize
forall a b. (a -> b) -> a -> b
$ DataSize
"--max-files=" DataSize -> DataSize -> DataSize
forall a. [a] -> [a] -> [a]
++ ByteSize -> DataSize
forall t. ConfigurableValue t => t -> DataSize
val ByteSize
f]
limitToParams (Limit
l1 :+ Limit
l2) = Limit -> [Either DataSize DataSize]
limitToParams Limit
l1 [Either DataSize DataSize]
-> [Either DataSize DataSize] -> [Either DataSize DataSize]
forall a. Semigroup a => a -> a -> a
<> Limit -> [Either DataSize DataSize]
limitToParams Limit
l2

-- | Configures a ccache in /var/cache for a group
--
-- If you say
--
-- > & (Group "foo") `Ccache.hasGroupCache`
-- > 	(Ccache.MaxSize "4G" <> Ccache.MaxFiles 10000)
--
-- you instruct propellor to create a ccache in /var/cache/ccache-foo owned and
-- writeable by the foo group, with a maximum cache size of 4GB or 10000 files.
hasCache :: Group -> Limit -> RevertableProperty DebianLike UnixLike
group :: Group
group@(Group DataSize
g) hasCache :: Group
-> Limit
-> RevertableProperty
     DebianLike
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
`hasCache` Limit
limit = (Property DebianLike
make Property DebianLike
-> Property DebianLike
-> CombinedType (Property DebianLike) (Property DebianLike)
forall x y. Combines x y => x -> y -> CombinedType x y
`requires` Property DebianLike
installed) Property DebianLike
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> RevertableProperty
     DebianLike
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall setupmetatypes undometatypes.
Property setupmetatypes
-> Property undometatypes
-> RevertableProperty setupmetatypes undometatypes
<!> Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
delete
  where
	make :: Property DebianLike
make = DataSize -> Props DebianLike -> Property DebianLike
forall k (metatypes :: k).
SingI metatypes =>
DataSize
-> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
propertyList (DataSize
"ccache for " DataSize -> DataSize -> DataSize
forall a. [a] -> [a] -> [a]
++ DataSize
g DataSize -> DataSize -> DataSize
forall a. [a] -> [a] -> [a]
++ DataSize
" group exists") (Props DebianLike -> Property DebianLike)
-> Props DebianLike -> Property DebianLike
forall a b. (a -> b) -> a -> b
$ Props
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
props
			Props
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Props
     (MetaTypes
        (Combine
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
forall a p (y :: [a]) (x :: [a]).
(IsProp p, MetaTypes y ~ GetMetaTypes p,
 CheckCombinableNote x y (NoteFor ('Text "&"))) =>
Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
& DataSize
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
File.dirExists DataSize
path
			Props
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Props
     (MetaTypes
        (Combine
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
forall a p (y :: [a]) (x :: [a]).
(IsProp p, MetaTypes y ~ GetMetaTypes p,
 CheckCombinableNote x y (NoteFor ('Text "&"))) =>
Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
& DataSize
-> User
-> Group
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
File.ownerGroup DataSize
path (DataSize -> User
User DataSize
"root") Group
group
			Props
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Props
     (MetaTypes
        (Combine
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
forall a p (y :: [a]) (x :: [a]).
(IsProp p, MetaTypes y ~ GetMetaTypes p,
 CheckCombinableNote x y (NoteFor ('Text "&"))) =>
Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
& DataSize
-> FileMode
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
File.mode DataSize
path ([FileMode] -> FileMode
combineModes ([FileMode] -> FileMode) -> [FileMode] -> FileMode
forall a b. (a -> b) -> a -> b
$
					[FileMode]
readModes [FileMode] -> [FileMode] -> [FileMode]
forall a. [a] -> [a] -> [a]
++ [FileMode]
executeModes [FileMode] -> [FileMode] -> [FileMode]
forall a. [a] -> [a] -> [a]
++
					[ FileMode
ownerWriteMode
					, FileMode
groupWriteMode
					, FileMode
setGroupIDMode
					]) Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> CombinedType
     (Property
        (MetaTypes
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
     (Property
        (MetaTypes
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
forall x y. Combines x y => x -> y -> CombinedType x y
`onChange` Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
fixSetgidBit
				-- here, we use onChange to catch upgrades from
				-- 3.0.5 where the setGroupIDMode line was not
				-- present
			Props
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property DebianLike
-> Props
     (MetaTypes
        (Combine
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
forall a p (y :: [a]) (x :: [a]).
(IsProp p, MetaTypes y ~ GetMetaTypes p,
 CheckCombinableNote x y (NoteFor ('Text "&"))) =>
Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
& DataSize -> Limit -> Property DebianLike
hasLimits DataSize
path Limit
limit

	delete :: Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
delete = IO Bool
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall (p :: * -> *) i (m :: * -> *).
(Checkable p i, LiftPropellor m) =>
m Bool -> p i -> Property i
check (DataSize -> IO Bool
doesDirectoryExist DataSize
path) (Property
   (MetaTypes
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
 -> Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall a b. (a -> b) -> a -> b
$
		DataSize
-> [DataSize]
-> UncheckedProperty
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
cmdProperty DataSize
"rm" [DataSize
"-r", DataSize
path] UncheckedProperty
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Result
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall (p :: * -> *) i.
Checkable p i =>
p i -> Result -> Property i
`assume` Result
MadeChange
		Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> DataSize
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall p. IsProp p => p -> DataSize -> p
`describe` (DataSize
"ccache for " DataSize -> DataSize -> DataSize
forall a. [a] -> [a] -> [a]
++ DataSize
g DataSize -> DataSize -> DataSize
forall a. [a] -> [a] -> [a]
++ DataSize
" does not exist")

	-- Here we deal with a bug in Propellor 3.0.5.  If the ccache was
	-- created with that version, it will not have the setgid bit set.  That
	-- means its subdirectories won't have inherited the setgid bit, and
	-- then the files in those directories won't be owned by group sbuild.
	-- This breaks ccache.
	fixSetgidBit :: Property UnixLike
	fixSetgidBit :: Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
fixSetgidBit =
		(DataSize
-> [DataSize]
-> UncheckedProperty
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
cmdProperty DataSize
"find"
			[ DataSize
path
			, DataSize
"-type", DataSize
"d"
			, DataSize
"-exec", DataSize
"chmod", DataSize
"g+s"
			, DataSize
"{}", DataSize
"+"
			] UncheckedProperty
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Result
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall (p :: * -> *) i.
Checkable p i =>
p i -> Result -> Property i
`assume` Result
MadeChange)
		Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> CombinedType
     (Property
        (MetaTypes
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
     (Property
        (MetaTypes
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
forall x y. Combines x y => x -> y -> CombinedType x y
`before`
		(DataSize
-> [DataSize]
-> UncheckedProperty
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
cmdProperty DataSize
"chown"
			[ DataSize
"-R"
			, DataSize
"root:" DataSize -> DataSize -> DataSize
forall a. [a] -> [a] -> [a]
++ DataSize
g
			, DataSize
path
			] UncheckedProperty
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Result
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall (p :: * -> *) i.
Checkable p i =>
p i -> Result -> Property i
`assume` Result
MadeChange)

	path :: DataSize
path = DataSize
"/var/cache/ccache-" DataSize -> DataSize -> DataSize
forall a. [a] -> [a] -> [a]
++ DataSize
g

installed :: Property DebianLike
installed :: Property DebianLike
installed = [DataSize] -> Property DebianLike
Apt.installed [DataSize
"ccache"]