packages feed

propellor 5.8.0 → 5.9.0

raw patch · 15 files changed

+305/−91 lines, 15 filesdep +type-errorsPVP ok

version bump matches the API change (PVP)

Dependencies added: type-errors

API changes (from Hackage documentation)

- Propellor.Types: instance forall a (x :: [a]) (y :: [a]). (Propellor.Types.MetaTypes.CheckCombinable x y Data.Type.Equality.~ 'Propellor.Types.MetaTypes.CanCombine, Propellor.Types.Singletons.SingI (Propellor.Types.MetaTypes.Combine x y)) => Propellor.Types.Combines (Propellor.Types.Property (Propellor.Types.MetaTypes.MetaTypes x)) (Propellor.Types.Property (Propellor.Types.MetaTypes.MetaTypes y))
- Propellor.Types: instance forall a1 a2 (x :: [a2]) (y :: [a2]) (x' :: [a1]) (y' :: [a1]). (Propellor.Types.MetaTypes.CheckCombinable x y Data.Type.Equality.~ 'Propellor.Types.MetaTypes.CanCombine, Propellor.Types.MetaTypes.CheckCombinable x' y' Data.Type.Equality.~ 'Propellor.Types.MetaTypes.CanCombine, Propellor.Types.Singletons.SingI (Propellor.Types.MetaTypes.Combine x y), Propellor.Types.Singletons.SingI (Propellor.Types.MetaTypes.Combine x' y')) => Propellor.Types.Combines (Propellor.Types.RevertableProperty (Propellor.Types.MetaTypes.MetaTypes x) (Propellor.Types.MetaTypes.MetaTypes x')) (Propellor.Types.RevertableProperty (Propellor.Types.MetaTypes.MetaTypes y) (Propellor.Types.MetaTypes.MetaTypes y'))
- Propellor.Types: instance forall k a (x :: [a]) (y :: [a]) (x' :: k). (Propellor.Types.MetaTypes.CheckCombinable x y Data.Type.Equality.~ 'Propellor.Types.MetaTypes.CanCombine, Propellor.Types.Singletons.SingI (Propellor.Types.MetaTypes.Combine x y)) => Propellor.Types.Combines (Propellor.Types.RevertableProperty (Propellor.Types.MetaTypes.MetaTypes x) (Propellor.Types.MetaTypes.MetaTypes x')) (Propellor.Types.Property (Propellor.Types.MetaTypes.MetaTypes y))
- Propellor.Types: instance forall k a (x :: [a]) (y :: [a]) (y' :: k). (Propellor.Types.MetaTypes.CheckCombinable x y Data.Type.Equality.~ 'Propellor.Types.MetaTypes.CanCombine, Propellor.Types.Singletons.SingI (Propellor.Types.MetaTypes.Combine x y)) => Propellor.Types.Combines (Propellor.Types.Property (Propellor.Types.MetaTypes.MetaTypes x)) (Propellor.Types.RevertableProperty (Propellor.Types.MetaTypes.MetaTypes y) (Propellor.Types.MetaTypes.MetaTypes y'))
- Propellor.Types.MetaTypes: CanCombine :: CheckCombine
- Propellor.Types.MetaTypes: CannotCombineTargets :: CheckCombine
- Propellor.Types.MetaTypes: data CheckCombine
+ Propellor.Property.SiteSpecific.JoeySites: noExim :: Property DebianLike
+ Propellor.Types: instance forall a (x :: [a]) (y :: [a]). (Propellor.Types.MetaTypes.CheckCombinable x y, Propellor.Types.Singletons.SingI (Propellor.Types.MetaTypes.Combine x y)) => Propellor.Types.Combines (Propellor.Types.Property (Propellor.Types.MetaTypes.MetaTypes x)) (Propellor.Types.Property (Propellor.Types.MetaTypes.MetaTypes y))
+ Propellor.Types: instance forall a1 a2 (x :: [a2]) (y :: [a2]) (x' :: [a1]) (y' :: [a1]). (Propellor.Types.MetaTypes.CheckCombinable x y, Propellor.Types.MetaTypes.CheckCombinable x' y', Propellor.Types.Singletons.SingI (Propellor.Types.MetaTypes.Combine x y), Propellor.Types.Singletons.SingI (Propellor.Types.MetaTypes.Combine x' y')) => Propellor.Types.Combines (Propellor.Types.RevertableProperty (Propellor.Types.MetaTypes.MetaTypes x) (Propellor.Types.MetaTypes.MetaTypes x')) (Propellor.Types.RevertableProperty (Propellor.Types.MetaTypes.MetaTypes y) (Propellor.Types.MetaTypes.MetaTypes y'))
+ Propellor.Types: instance forall k a (x :: [a]) (y :: [a]) (x' :: k). (Propellor.Types.MetaTypes.CheckCombinable x y, Propellor.Types.Singletons.SingI (Propellor.Types.MetaTypes.Combine x y)) => Propellor.Types.Combines (Propellor.Types.RevertableProperty (Propellor.Types.MetaTypes.MetaTypes x) (Propellor.Types.MetaTypes.MetaTypes x')) (Propellor.Types.Property (Propellor.Types.MetaTypes.MetaTypes y))
+ Propellor.Types: instance forall k a (x :: [a]) (y :: [a]) (y' :: k). (Propellor.Types.MetaTypes.CheckCombinable x y, Propellor.Types.Singletons.SingI (Propellor.Types.MetaTypes.Combine x y)) => Propellor.Types.Combines (Propellor.Types.Property (Propellor.Types.MetaTypes.MetaTypes x)) (Propellor.Types.RevertableProperty (Propellor.Types.MetaTypes.MetaTypes y) (Propellor.Types.MetaTypes.MetaTypes y'))
+ Propellor.Types.MetaTypes: data DelayErrorFcf (a :: ErrorMessage) (b :: k) :: forall k. () => ErrorMessage -> k -> Type
+ Propellor.Types.MetaTypes: type DelayError (err :: ErrorMessage) = Eval (DelayErrorFcf err :: k -> Type)
- Propellor: (!) :: CheckCombinable x z ~ 'CanCombine => Props (MetaTypes x) -> RevertableProperty (MetaTypes y) (MetaTypes z) -> Props (MetaTypes (Combine x z))
+ Propellor: (!) :: CheckCombinableNote x z (NoteFor ( 'Text "!")) => Props (MetaTypes x) -> RevertableProperty (MetaTypes y) (MetaTypes z) -> Props (MetaTypes (Combine x z))
- Propellor: (&) :: (IsProp p, MetaTypes y ~ GetMetaTypes p, CheckCombinable x y ~ 'CanCombine) => Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
+ Propellor: (&) :: (IsProp p, MetaTypes y ~ GetMetaTypes p, CheckCombinableNote x y (NoteFor ( 'Text "&"))) => Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
- Propellor.EnsureProperty: ensureProperty :: (Cannot_ensureProperty_WithInfo inner ~ 'True, (Targets inner `NotSuperset` Targets outer) ~ 'CanCombine) => OuterMetaTypesWitness outer -> Property (MetaTypes inner) -> Propellor Result
+ Propellor.EnsureProperty: ensureProperty :: EnsurePropertyAllowed inner outer => OuterMetaTypesWitness outer -> Property (MetaTypes inner) -> Propellor Result
- Propellor.EnsureProperty: type family Cannot_ensureProperty_WithInfo (l :: [a]) :: Bool
+ Propellor.EnsureProperty: type family EnsurePropertyAllowed inner outer :: Constraint
- Propellor.PropAccum: (!) :: CheckCombinable x z ~ 'CanCombine => Props (MetaTypes x) -> RevertableProperty (MetaTypes y) (MetaTypes z) -> Props (MetaTypes (Combine x z))
+ Propellor.PropAccum: (!) :: CheckCombinableNote x z (NoteFor ( 'Text "!")) => Props (MetaTypes x) -> RevertableProperty (MetaTypes y) (MetaTypes z) -> Props (MetaTypes (Combine x z))
- Propellor.PropAccum: (&) :: (IsProp p, MetaTypes y ~ GetMetaTypes p, CheckCombinable x y ~ 'CanCombine) => Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
+ Propellor.PropAccum: (&) :: (IsProp p, MetaTypes y ~ GetMetaTypes p, CheckCombinableNote x y (NoteFor ( 'Text "&"))) => Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
- Propellor.PropAccum: (&^) :: (IsProp p, MetaTypes y ~ GetMetaTypes p, CheckCombinable x y ~ 'CanCombine) => Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
+ Propellor.PropAccum: (&^) :: (IsProp p, MetaTypes y ~ GetMetaTypes p, CheckCombinableNote x y (NoteFor ( 'Text "&^"))) => Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
- Propellor.Property: ensureProperty :: (Cannot_ensureProperty_WithInfo inner ~ 'True, (Targets inner `NotSuperset` Targets outer) ~ 'CanCombine) => OuterMetaTypesWitness outer -> Property (MetaTypes inner) -> Propellor Result
+ Propellor.Property: ensureProperty :: EnsurePropertyAllowed inner outer => OuterMetaTypesWitness outer -> Property (MetaTypes inner) -> Propellor Result
- Propellor.Property.Atomic: atomicDirUpdate :: (Cannot_ensureProperty_WithInfo t ~ 'True, (Targets t `NotSuperset` Targets t) ~ 'CanCombine) => SingI t => FilePath -> (FilePath -> Property (MetaTypes t)) -> Property (MetaTypes t)
+ Propellor.Property.Atomic: atomicDirUpdate :: EnsurePropertyAllowed t t => SingI t => FilePath -> (FilePath -> Property (MetaTypes t)) -> Property (MetaTypes t)
- Propellor.Property.Atomic: atomicUpdate :: (Cannot_ensureProperty_WithInfo t ~ 'True, (Targets t `NotSuperset` Targets t) ~ 'CanCombine) => SingI t => AtomicResourcePair a -> CheckAtomicResourcePair a -> SwapAtomicResourcePair a -> (a -> Property (MetaTypes t)) -> Property (MetaTypes t)
+ Propellor.Property.Atomic: atomicUpdate :: EnsurePropertyAllowed t t => SingI t => AtomicResourcePair a -> CheckAtomicResourcePair a -> SwapAtomicResourcePair a -> (a -> Property (MetaTypes t)) -> Property (MetaTypes t)
- Propellor.Types: tightenTargets :: (TightenTargets p, (Targets untightened `NotSuperset` Targets tightened) ~ 'CanCombine, (NonTargets tightened `NotSuperset` NonTargets untightened) ~ 'CanCombine, SingI tightened) => p (MetaTypes untightened) -> p (MetaTypes tightened)
+ Propellor.Types: tightenTargets :: (TightenTargets p, TightenTargetsAllowed untightened tightened, SingI tightened) => p (MetaTypes untightened) -> p (MetaTypes tightened)
- Propellor.Types.MetaTypes: type family Union (list1 :: [a]) (list2 :: [a]) :: [a]
+ Propellor.Types.MetaTypes: type family IfStuck (expr :: k) (b :: k1) (c :: Exp k1) :: k1

Files

CHANGELOG view
@@ -1,3 +1,32 @@+propellor (5.9.0) unstable; urgency=medium++  * Added custom type error messages when Properties don't combine due to+    conflicting MetaTypes.+  * Added custom type error messages for ensureProperty and tightenTargets.+  * Note that those changes made ghc 8.0.1 in a few cases unable to infer+    types when ensureProperty or tightenTargets is used, while later ghc+    versions had no difficulty. If this affects building your properties,+    adding a type annotation to the code will work around the problem.+  * Added custom type error messages displayed when type inference+    fails when using ensureProperty and tightenTargets, that suggest+    adding a type annotation.+  * Use the type-errors library to detect when the type checker gets stuck+    unable to reduce type-level operations on MetaTypes, and avoid +    displaying massive error messages.+  * But, since type-errors is a new library not available in eg Debian+    yet, added a WithTypeErrors build flag. When the library is not+    available, cabal will automatically disable that build flag,+    and it will build without the type-errors library.+  * EnsurePropertyAllowed, TightenTargetsAllowed, and CheckCombinable+    types have changed to Constraint.+    (API change)+  * Try harder to avoid displaying an excessive amount of type error+    messages when many properties have been combined in a props list.+  * Libvirt.installed: install libvirt-daemon-system+    Thanks, David Bremner++ -- Joey Hess <id@joeyh.name>  Tue, 02 Jul 2019 16:27:07 -0400+ propellor (5.8.0) unstable; urgency=medium    * Fix bug in File.containsShellSetting that replaced whole shell conffile
debian/changelog view
@@ -1,3 +1,32 @@+propellor (5.9.0) unstable; urgency=medium++  * Added custom type error messages when Properties don't combine due to+    conflicting MetaTypes.+  * Added custom type error messages for ensureProperty and tightenTargets.+  * Note that those changes made ghc 8.0.1 in a few cases unable to infer+    types when ensureProperty or tightenTargets is used, while later ghc+    versions had no difficulty. If this affects building your properties,+    adding a type annotation to the code will work around the problem.+  * Added custom type error messages displayed when type inference+    fails when using ensureProperty and tightenTargets, that suggest+    adding a type annotation.+  * Use the type-errors library to detect when the type checker gets stuck+    unable to reduce type-level operations on MetaTypes, and avoid +    displaying massive error messages.+  * But, since type-errors is a new library not available in eg Debian+    yet, added a WithTypeErrors build flag. When the library is not+    available, cabal will automatically disable that build flag,+    and it will build without the type-errors library.+  * EnsurePropertyAllowed, TightenTargetsAllowed, and CheckCombinable+    types have changed to Constraint.+    (API change)+  * Try harder to avoid displaying an excessive amount of type error+    messages when many properties have been combined in a props list.+  * Libvirt.installed: install libvirt-daemon-system+    Thanks, David Bremner++ -- Joey Hess <id@joeyh.name>  Tue, 02 Jul 2019 16:27:07 -0400+ propellor (5.8.0) unstable; urgency=medium    * Fix bug in File.containsShellSetting that replaced whole shell conffile
joeyconfig.hs view
@@ -417,7 +417,8 @@ 	& Apt.serviceInstalledRunning "swapspace" 	& Cron.runPropellor (Cron.Times "30 * * * *") 	& Apt.installed ["etckeeper", "sudo"]-	& Apt.removed ["nfs-common", "exim4", "exim4-base", "exim4-daemon-light", "rsyslog", "acpid", "rpcbind", "at"]+	& JoeySites.noExim+	& Apt.removed ["nfs-common", "rsyslog", "acpid", "rpcbind", "at"]  	& User.hasSomePassword (User "root") 	& User.accountFor (User "joey")@@ -525,13 +526,14 @@ 	& Apt.installed ["vim", "screen", "less"] 	& Cron.runPropellor (Cron.Times "30 * * * *") 	-- I use postfix, or no MTA.-	& Apt.removed ["exim4", "exim4-daemon-light", "exim4-config", "exim4-base"]-		`onChange` Apt.autoRemove+	& JoeySites.noExim  -- This is my standard container setup, Featuring automatic upgrades. standardContainer :: DebianSuite -> Property (HasInfo + Debian) standardContainer suite = propertyList "standard container" $ props 	& osDebian suite X86_64+	-- Do not want to run mail daemon inside a random container..+	& JoeySites.noExim 	& Apt.stdSourcesList `onChange` Apt.upgrade 	& Apt.unattendedUpgrades 	& Apt.cacheCleaned
propellor.cabal view
@@ -1,5 +1,5 @@ Name: propellor-Version: 5.8.0+Version: 5.9.0 Cabal-Version: 1.20 License: BSD2 Maintainer: Joey Hess <id@joeyh.name>@@ -35,11 +35,12 @@  .  It is configured using haskell. +Flag WithTypeErrors+  Description: Build with type-errors library for better error messages+ Library   Default-Language: Haskell98   GHC-Options: -Wall -fno-warn-tabs -O0-  if impl(ghc >= 8.0)-    GHC-Options: -fno-warn-redundant-constraints   Default-Extensions: TypeOperators   Hs-Source-Dirs: src   Build-Depends:@@ -49,6 +50,9 @@     directory, filepath, IfElse, process, bytestring, hslogger, split,     unix, unix-compat, ansi-terminal, containers (>= 0.5), network, async,     time, mtl, transformers, exceptions (>= 0.6), stm, text, hashable+  if flag(WithTypeErrors)+    Build-Depends: type-errors+    CPP-Options: -DWITH_TYPE_ERRORS    Exposed-Modules:     Propellor
src/Propellor/EnsureProperty.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} @@ -8,7 +9,7 @@ 	( ensureProperty 	, property' 	, OuterMetaTypesWitness-	, Cannot_ensureProperty_WithInfo+	, EnsurePropertyAllowed 	) where  import Propellor.Types@@ -16,6 +17,9 @@ import Propellor.Types.MetaTypes import Propellor.Exception +import GHC.TypeLits+import GHC.Exts (Constraint)+import Data.Type.Bool import Data.Monoid import Prelude @@ -41,19 +45,40 @@ 		-- -Wredundant-constraints is turned off because 		-- this constraint appears redundant, but is actually 		-- crucial.-		( Cannot_ensureProperty_WithInfo inner ~ 'True-		, (Targets inner `NotSuperset` Targets outer) ~ 'CanCombine-		)+		( EnsurePropertyAllowed inner outer) 	=> OuterMetaTypesWitness outer 	-> Property (MetaTypes inner) 	-> Propellor Result ensureProperty _ = maybe (return NoChange) catchPropellor . getSatisfy --- The name of this was chosen to make type errors a bit more understandable.-type family Cannot_ensureProperty_WithInfo (l :: [a]) :: Bool where-	Cannot_ensureProperty_WithInfo '[] = 'True-	Cannot_ensureProperty_WithInfo (t ': ts) =-		Not (t `EqT` 'WithInfo) && Cannot_ensureProperty_WithInfo ts+type family EnsurePropertyAllowed inner outer :: Constraint where+	EnsurePropertyAllowed inner outer = 'True ~+		((EnsurePropertyNoInfo inner)+			&&+		(EnsurePropertyTargetOSMatches inner outer))++type family EnsurePropertyNoInfo (l :: [a]) :: Bool where+	EnsurePropertyNoInfo '[] = 'True+	EnsurePropertyNoInfo (t ': ts) = If (Not (t `EqT` 'WithInfo))+		(EnsurePropertyNoInfo ts)+		(TypeError ('Text "Cannot use ensureProperty with a Property that HasInfo."))++type family EnsurePropertyTargetOSMatches inner outer where+	EnsurePropertyTargetOSMatches inner outer = +		If (Targets outer `IsSubset` Targets inner)+			'True+			(IfStuck (Targets outer)+				(DelayError+					('Text "ensureProperty outer Property type is not able to be inferred here."+					 ':$$: 'Text "Consider adding a type annotation."+					)+				)+				(DelayErrorFcf+					('Text "ensureProperty inner Property is missing support for: "+					 ':$$: PrettyPrintMetaTypes (Difference (Targets outer) (Targets inner))+					)+				)+			)  -- | Constructs a property, like `property`, but provides its -- `OuterMetaTypesWitness`.
src/Propellor/PropAccum.hs view
@@ -19,6 +19,7 @@ import Propellor.Types.Core import Propellor.Property +import GHC.TypeLits import Data.Monoid import Prelude @@ -45,6 +46,16 @@ 	GetMetaTypes (Property (MetaTypes t)) = MetaTypes t 	GetMetaTypes (RevertableProperty (MetaTypes t) undo) = MetaTypes t +-- When many properties are combined, ghc error message+-- can include quite a lot of code, typically starting with+-- `props  and including all the properties up to and including the+-- one that fails to combine. Point the user in the right direction.+type family NoteFor symbol :: ErrorMessage where+	NoteFor symbol =+		'Text "Probably the problem is with the last property added with "+			':<>: symbol+			':<>: 'Text " in the code excerpt below."+ -- | Adds a property to a Props. -- -- Can add Properties and RevertableProperties@@ -55,7 +66,7 @@ 		-- this constraint appears redundant, but is actually 		-- crucial. 		, MetaTypes y ~ GetMetaTypes p-		, CheckCombinable x y ~ 'CanCombine+		, CheckCombinableNote x y (NoteFor ('Text "&")) 		) 	=> Props (MetaTypes x) 	-> p@@ -70,7 +81,7 @@ 		-- this constraint appears redundant, but is actually 		-- crucial. 		, MetaTypes y ~ GetMetaTypes p-		, CheckCombinable x y ~ 'CanCombine+		, CheckCombinableNote x y (NoteFor ('Text "&^")) 		) 	=> Props (MetaTypes x) 	-> p@@ -82,7 +93,7 @@ 	-- -Wredundant-constraints is turned off because 	-- this constraint appears redundant, but is actually 	-- crucial.-	:: (CheckCombinable x z ~ 'CanCombine)+	:: CheckCombinableNote x z (NoteFor ('Text "!")) 	=> Props (MetaTypes x) 	-> RevertableProperty (MetaTypes y) (MetaTypes z) 	-> Props (MetaTypes (Combine x z))
src/Propellor/Property/Aiccu.hs view
@@ -47,8 +47,7 @@ hasConfig t u = prop `onChange` restarted   where   	prop :: Property (HasInfo + UnixLike)-	prop = withSomePrivData [(Password (u++"/"++t)), (Password u)] (Context "aiccu") $-		property' "aiccu configured" . writeConfig-	writeConfig getpassword w = getpassword $ ensureProperty w . go+	prop = withSomePrivData [(Password (u++"/"++t)), (Password u)] (Context "aiccu") $ \getpassword ->+		property' "aiccu configured" $ \w -> getpassword $ ensureProperty w . go 	go (Password u', p) = confPath `File.hasContentProtected` config u' t p 	go (f, _) = error $ "Unexpected type of privdata: " ++ show f
src/Propellor/Property/Atomic.hs view
@@ -46,10 +46,8 @@ -- inactiveAtomicResource, and if it was successful, -- atomically activating that resource. atomicUpdate-	-- Constriaints inherited from ensureProperty.-	:: ( Cannot_ensureProperty_WithInfo t ~ 'True-	   , (Targets t `NotSuperset` Targets t) ~ 'CanCombine-	   )+	-- Constriaint inherited from ensureProperty.+	:: EnsurePropertyAllowed t t 	=> SingI t 	=> AtomicResourcePair a 	-> CheckAtomicResourcePair a@@ -92,10 +90,8 @@ -- children: a symlink with the name of the directory itself, and two copies -- of the directory, with names suffixed with ".1" and ".2" atomicDirUpdate-	-- Constriaints inherited from ensureProperty.-	:: ( Cannot_ensureProperty_WithInfo t ~ 'True-	   , (Targets t `NotSuperset` Targets t) ~ 'CanCombine-	   )+	-- Constriaint inherited from ensureProperty.+	:: EnsurePropertyAllowed t t 	=> SingI t 	=> FilePath 	-> (FilePath -> Property (MetaTypes t))
src/Propellor/Property/Libvirt.hs view
@@ -34,7 +34,7 @@  -- | Install basic libvirt components installed :: Property DebianLike-installed = Apt.installed ["libvirt-clients", "virtinst"]+installed = Apt.installed ["libvirt-clients", "virtinst", "libvirt-daemon", "libvirt-daemon-system"]  -- | Ensure that the default libvirt network is set to autostart, and start it. --
src/Propellor/Property/SiteSpecific/JoeySites.hs view
@@ -910,15 +910,16 @@ 	& Systemd.enabled setupservicename 		`requires` setupserviceinstalled 		`onChange` Systemd.started setupservicename-	& Systemd.enabled watchdogservicename-		`requires` watchdogserviceinstalled-		`onChange` Systemd.started watchdogservicename 	& Systemd.enabled pollerservicename 		`requires` pollerserviceinstalled 		`onChange` Systemd.started pollerservicename 	& Systemd.enabled controllerservicename 		`requires` controllerserviceinstalled 		`onChange` Systemd.started controllerservicename+	& Systemd.enabled watchdogservicename+		`requires` watchdogserviceinstalled+		`onChange` Systemd.started watchdogservicename+	& Apt.serviceInstalledRunning "watchdog" 	& User.hasGroup user (Group "dialout") 	& Group.exists (Group "gpio") Nothing 	& User.hasGroup user (Group "gpio")@@ -1025,7 +1026,7 @@ 		] 	-- Any changes to the rsync command will need my .authorized_keys 	-- rsync server command to be updated too.-	rsynccommand = "rsync -e 'ssh -i" ++ sshkeyfile ++ "' -avz rrds/ joey@kitenet.net:/srv/web/house.joeyh.name/rrds/"+	rsynccommand = "rsync -e 'ssh -i" ++ sshkeyfile ++ "' -avz rrds/ joey@kitenet.net:/srv/web/house.joeyh.name/rrds/ >/dev/null 2>&1"  	websitesymlink :: Property UnixLike 	websitesymlink = check (not . isSymbolicLink <$> getSymbolicLinkStatus "/var/www/html")@@ -1321,3 +1322,7 @@   where 	-- rsync.net has a newer borg here 	os' = Borg.UsesEnvVar ("BORG_REMOTE_PATH", "borg1") : os++noExim :: Property DebianLike+noExim = Apt.removed ["exim4", "exim4-base", "exim4-daemon-light"]+	`onChange` Apt.autoRemove
src/Propellor/Property/Systemd.hs view
@@ -391,6 +391,7 @@ containerCfg :: String -> RevertableProperty (HasInfo + Linux) (HasInfo + Linux) containerCfg p = RevertableProperty (mk True) (mk False)   where+	mk :: Bool -> Property (HasInfo + Linux) 	mk b = tightenTargets $ 		pureInfoProperty desc $ 			mempty { _chrootCfg = SystemdNspawnCfg [(p', b)] }
src/Propellor/Property/Tor.hs view
@@ -172,20 +172,22 @@   where 	desc = unwords ["hidden service data available in", varLib </> hn] 	installonion :: FilePath -> Property (HasInfo + DebianLike)-	installonion f = withPrivData (PrivFile $ varLib </> hn </> f) context $ \getcontent ->-		property' desc $ \w -> getcontent $ install w $ varLib </> hn </> f-	install w f privcontent = ifM (liftIO $ doesFileExist f)-		( noChange-		, ensureProperty w $ propertyList desc $ toProps-			[ property desc $ makeChange $ do-				createDirectoryIfMissing True (takeDirectory f)-				writeFileProtected f (unlines (privDataLines privcontent))-			, File.mode (takeDirectory f) $ combineModes-				[ownerReadMode, ownerWriteMode, ownerExecuteMode]-			, File.ownerGroup (takeDirectory f) user (userGroup user)-			, File.ownerGroup f user (userGroup user)-			]-		)+	installonion basef =+		let f = varLib </> hn </> basef+		in withPrivData (PrivFile f) context $ \getcontent ->+		property' desc $ \w -> getcontent $ \privcontent ->+			ifM (liftIO $ doesFileExist f)+				( noChange+				, ensureProperty w $ propertyList desc $ toProps+					[ property desc $ makeChange $ do+						createDirectoryIfMissing True (takeDirectory f)+						writeFileProtected f (unlines (privDataLines privcontent))+					, File.mode (takeDirectory f) $ combineModes+						[ownerReadMode, ownerWriteMode, ownerExecuteMode]+					, File.ownerGroup (takeDirectory f) user (userGroup user)+					, File.ownerGroup f user (userGroup user)+					]+				)  restarted :: Property DebianLike restarted = Service.restarted "tor"
src/Propellor/Types.hs view
@@ -4,6 +4,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveDataTypeable #-}@@ -31,6 +32,7 @@ 	, HasInfo 	, type (+) 	, TightenTargets(..)+	, TightenTargetsAllowed 	-- * Combining and modifying properties 	, Combines(..) 	, CombinedType@@ -44,6 +46,9 @@ 	, module Propellor.Types.ZFS 	) where +import GHC.TypeLits hiding (type (+))+import GHC.Exts (Constraint)+import Data.Type.Bool import qualified Data.Semigroup as Sem import Data.Monoid import Control.Applicative@@ -59,7 +64,7 @@ import Propellor.Types.ZFS  -- | The core data type of Propellor, this represents a property--- that the system should have, with a descrition, and an action to ensure+-- that the system should have, with a description, and an action to ensure -- it has the property. -- -- There are different types of properties that target different OS's,@@ -185,17 +190,17 @@ 		-> y 		-> CombinedType x y -instance (CheckCombinable x y ~ 'CanCombine, SingI (Combine x y)) => Combines (Property (MetaTypes x)) (Property (MetaTypes y)) where+instance (CheckCombinable x y, SingI (Combine x y)) => Combines (Property (MetaTypes x)) (Property (MetaTypes y)) where 	combineWith f _ (Property _ d1 a1 i1 c1) (Property _ d2 a2 i2 c2) = 		Property sing d1 (f a1 a2) i1 (ChildProperty d2 a2 i2 c2 : c1)-instance (CheckCombinable x y ~ 'CanCombine, CheckCombinable x' y' ~ 'CanCombine, SingI (Combine x y), SingI (Combine x' y')) => Combines (RevertableProperty (MetaTypes x) (MetaTypes x')) (RevertableProperty (MetaTypes y) (MetaTypes y')) where+instance (CheckCombinable x y, CheckCombinable x' y', SingI (Combine x y), SingI (Combine x' y')) => Combines (RevertableProperty (MetaTypes x) (MetaTypes x')) (RevertableProperty (MetaTypes y) (MetaTypes y')) where 	combineWith sf tf (RevertableProperty s1 t1) (RevertableProperty s2 t2) = 		RevertableProperty 			(combineWith sf tf s1 s2) 			(combineWith tf sf t1 t2)-instance (CheckCombinable x y ~ 'CanCombine, SingI (Combine x y)) => Combines (RevertableProperty (MetaTypes x) (MetaTypes x')) (Property (MetaTypes y)) where+instance (CheckCombinable x y, SingI (Combine x y)) => Combines (RevertableProperty (MetaTypes x) (MetaTypes x')) (Property (MetaTypes y)) where 	combineWith sf tf (RevertableProperty x _) y = combineWith sf tf x y-instance (CheckCombinable x y ~ 'CanCombine, SingI (Combine x y)) => Combines (Property (MetaTypes x)) (RevertableProperty (MetaTypes y) (MetaTypes y')) where+instance (CheckCombinable x y, SingI (Combine x y)) => Combines (Property (MetaTypes x)) (RevertableProperty (MetaTypes y) (MetaTypes y')) where 	combineWith sf tf x (RevertableProperty y _) = combineWith sf tf x y  class TightenTargets p where@@ -209,13 +214,30 @@ 	-- > upgraded = tightenTargets $ cmdProperty "apt-get" ["upgrade"] 	tightenTargets 		:: -			-- Note that this uses PolyKinds-			( (Targets untightened `NotSuperset` Targets tightened) ~ 'CanCombine-			, (NonTargets tightened `NotSuperset` NonTargets untightened) ~ 'CanCombine+			( TightenTargetsAllowed untightened tightened 			, SingI tightened 			) 		=> p (MetaTypes untightened) 		-> p (MetaTypes tightened)++-- Note that this uses PolyKinds+type family TightenTargetsAllowed untightened tightened :: Constraint where+	TightenTargetsAllowed untightened tightened =+		If (Targets tightened `IsSubset` Targets untightened+		    && NonTargets untightened `IsSubset` NonTargets tightened)+			('True ~ 'True)+			(IfStuck (Targets tightened)+				(DelayError+					('Text "Unable to infer desired Property type in this use of tightenTargets."+					 ':$$: ('Text "Consider adding a type annotation.")+					)+				)+				(DelayErrorFcf+					('Text "This use of tightenTargets would widen, not narrow, adding: "+					 ':$$: PrettyPrintMetaTypes (Difference (Targets tightened) (Targets untightened))+					)+				)+			)  instance TightenTargets Property where 	tightenTargets (Property _ d a i c) = Property sing d a i c
src/Propellor/Types/MetaTypes.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE TypeOperators, PolyKinds, DataKinds, TypeFamilies, UndecidableInstances, FlexibleInstances, GADTs #-}+{-# LANGUAGE TypeOperators, PolyKinds, DataKinds, ConstraintKinds #-}+{-# LANGUAGE TypeFamilies, UndecidableInstances, FlexibleInstances, GADTs #-}+{-# LANGUAGE CPP #-}  module Propellor.Types.MetaTypes ( 	MetaType(..),@@ -17,21 +19,40 @@ 	IncludesInfo, 	Targets, 	NonTargets,-	NotSuperset,+	PrettyPrintMetaTypes,+	IsSubset, 	Combine,-	CheckCombine(..), 	CheckCombinable,+	CheckCombinableNote, 	type (&&), 	Not, 	EqT, 	Union,+	Intersect,+	Difference,+	IfStuck,+	DelayError,+	DelayErrorFcf, ) where  import Propellor.Types.Singletons import Propellor.Types.OS +import GHC.TypeLits hiding (type (+))+import GHC.Exts (Constraint) import Data.Type.Bool +#ifdef WITH_TYPE_ERRORS+import Type.Errors+#else+type family IfStuck (expr :: k) (b :: k1) (c :: k1) :: k1 where+	IfStuck expr b c = c+type family DelayError msg where+	DelayError msg = TypeError msg+type family DelayErrorFcf msg where+	DelayErrorFcf msg = TypeError msg+#endif+ data MetaType 	= Targeting TargetOS -- ^ A target OS of a Property 	| WithInfo           -- ^ Indicates that a Property has associated Info@@ -113,42 +134,70 @@ 			(Targets list1 `Intersect` Targets list2) 		) --- | Checks if two MetaTypes lists can be safely combined.------ This should be used anywhere Combine is used, as an additional--- constraint. For example:+-- | Checks if two MetaTypes lists can be safly combined;+-- eg they have at least one Target in common.+type family IsCombinable (list1 :: [a]) (list2 :: [a]) :: Bool where+	-- As a special case, if either list is empty or only WithInfo, +	-- let it be combined with the other. This relies on MetaTypes+	-- list always containing at least one Target, so can only happen+	-- if there's already been a type error. This special case lets the+	-- type checker show only the original type error, and not+	-- subsequent errors due to later CheckCombinable constraints.+	IsCombinable '[] list2 = 'True+	IsCombinable list1 '[] = 'True+	IsCombinable ('WithInfo ': list1) list2 = IsCombinable list1 list2+	IsCombinable list1 ('WithInfo ': list2) = IsCombinable list1 list2+	IsCombinable list1 list2 =+		Not (Null (Combine (Targets list1) (Targets list2)))++-- | This (or CheckCombinableNote) should be used anywhere Combine is used, +-- as an additional constraint. For example: ----- > foo :: (CheckCombinable x y ~ 'CanCombine) => x -> y -> Combine x y-type family CheckCombinable (list1 :: [a]) (list2 :: [a]) :: CheckCombine where-	-- As a special case, if either list is empty, let it be combined-	-- with the other. This relies on MetaTypes list always containing-	-- at least one target, so can only happen if there's already been-	-- a type error. This special case lets the type checker show only-	-- the original type error, and not an extra error due to a later-	-- CheckCombinable constraint.-	CheckCombinable '[] list2 = 'CanCombine-	CheckCombinable list1 '[] = 'CanCombine-	CheckCombinable (l1 ': list1) (l2 ': list2) =-		CheckCombinable' (Combine (l1 ': list1) (l2 ': list2))-type family CheckCombinable' (combinedlist :: [a]) :: CheckCombine where-	CheckCombinable' '[] = 'CannotCombineTargets-	CheckCombinable' (a ': rest) -		= If (IsTarget a)-			'CanCombine-			(CheckCombinable' rest)+-- > foo :: CheckCombinable x y => x -> y -> Combine x y+type family CheckCombinable (list1 :: [a]) (list2 :: [a]) :: Constraint where+	CheckCombinable list1 list2 =+		If (IsCombinable list1 list2)+			('True ~ 'True)+			(CannotCombine list1 list2 'Nothing) -data CheckCombine = CannotCombineTargets | CanCombine+-- | Allows providing an additional note.+type family CheckCombinableNote (list1 :: [a]) (list2 :: [a]) (note :: ErrorMessage) :: Constraint where+	CheckCombinableNote list1 list2 note =+		If (IsCombinable list1 list2)+			('True ~ 'True)+			(CannotCombine list1 list2+				('Just ('Text "(" ':<>: note ':<>: 'Text ")"))+			) --- | Every item in the subset must be in the superset.------ The name of this was chosen to make type errors more understandable.-type family NotSuperset (superset :: [a]) (subset :: [a]) :: CheckCombine where-	NotSuperset superset '[] = 'CanCombine-	NotSuperset superset (s ': rest) =-		If (Elem s superset)-			(NotSuperset superset rest)-			'CannotCombineTargets+-- Checking IfStuck is to avoid massive and useless error message leaking +-- type families from this module.+type family CannotCombine (list1 :: [a]) (list2 :: [a]) (note :: Maybe ErrorMessage) :: Constraint where+	CannotCombine list1 list2 note = +		IfStuck list1+			(IfStuck list2+				(DelayError (CannotCombineMessage UnknownType UnknownType UnknownTypeNote))+				(DelayErrorFcf (CannotCombineMessage UnknownType (PrettyPrintMetaTypes list2) UnknownTypeNote))+			)+			(IfStuck list2+				(DelayError (CannotCombineMessage (PrettyPrintMetaTypes list1) UnknownType UnknownTypeNote))+				(DelayErrorFcf (CannotCombineMessage (PrettyPrintMetaTypes list1) (PrettyPrintMetaTypes list2) note))+			) +type family UnknownType :: ErrorMessage where+	UnknownType = 'Text "<unknown>"++type family UnknownTypeNote :: Maybe ErrorMessage where+	UnknownTypeNote = 'Just ('Text "(Property <unknown> is often caused by applying a Property constructor to the wrong number of arguments.)")++type family CannotCombineMessage (a :: ErrorMessage) (b :: ErrorMessage) (note :: Maybe ErrorMessage) :: ErrorMessage where+	CannotCombineMessage a b ('Just note) =+		CannotCombineMessage a b 'Nothing+			':$$: note+	CannotCombineMessage a b 'Nothing =+		'Text "Cannot combine Properties:"+			':$$: ('Text "  Property " ':<>: a)+			':$$: ('Text "  Property " ':<>: b)+ type family IsTarget (a :: t) :: Bool where 	IsTarget ('Targeting a) = 'True 	IsTarget 'WithInfo = 'False@@ -167,6 +216,21 @@ 			(NonTargets xs) 			(x ': NonTargets xs) +-- | Pretty-prints a list of MetaTypes for display in a type error message.+type family PrettyPrintMetaTypes (l :: [MetaType]) :: ErrorMessage where+	PrettyPrintMetaTypes '[] = 'Text "<none>"+	PrettyPrintMetaTypes (t ': '[]) = PrettyPrintMetaType t+	PrettyPrintMetaTypes (t ': ts) = +		PrettyPrintMetaType t ':<>: 'Text " + " ':<>: PrettyPrintMetaTypes ts++type family PrettyPrintMetaType t :: ErrorMessage where+	PrettyPrintMetaType 'WithInfo = 'ShowType HasInfo+	PrettyPrintMetaType ('Targeting 'OSDebian) = 'ShowType Debian+	PrettyPrintMetaType ('Targeting 'OSBuntish) = 'ShowType Buntish+	PrettyPrintMetaType ('Targeting 'OSFreeBSD) = 'ShowType FreeBSD+	PrettyPrintMetaType ('Targeting 'OSArchLinux) = 'ShowType ArchLinux+	PrettyPrintMetaType ('Targeting t) = 'ShowType t+ -- | Type level elem type family Elem (a :: t) (list :: [t]) :: Bool where 	Elem a '[] = 'False@@ -187,6 +251,28 @@ 		If (Elem a list2 && Not (Elem a rest)) 			(a ': Intersect rest list2) 			(Intersect rest list2)++-- | Type level difference. Items that are in the first list, but not in+-- the second.+type family Difference (list1 :: [a]) (list2 :: [a]) :: [a] where+	Difference '[] list2 = '[]+	Difference (a ': rest) list2 =+		If (Elem a list2)+			(Difference rest list2)+			(a ': Difference rest list2)++-- | Every item in the subset must be in the superset.+type family IsSubset (subset :: [a]) (superset :: [a]) :: Bool where+	IsSubset '[] superset = 'True+	IsSubset (s ': rest) superset =+		If (Elem s superset)+			(IsSubset rest superset)+			'False++-- | Type level null.+type family Null (list :: [a]) :: Bool where+	Null '[] = 'True+	Null l = 'False  -- | Type level equality of metatypes. type family EqT (a :: MetaType) (b :: MetaType) where
stack.yaml view
@@ -2,3 +2,6 @@ resolver: lts-9.21 packages: - '.'+extra-deps:+- type-errors-0.1.0.0+- first-class-families-0.5.0.0