propellor 5.17 → 5.18
raw patch · 12 files changed
+239/−308 lines, 12 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Propellor: (<>) :: Semigroup a => a -> a -> a
- Propellor: All :: Bool -> All
- Propellor: Alt :: f a -> Alt (f :: k -> Type) (a :: k)
- Propellor: Any :: Bool -> Any
- Propellor: Ap :: f a -> Ap (f :: k -> Type) (a :: k)
- Propellor: Dual :: a -> Dual a
- Propellor: Endo :: (a -> a) -> Endo a
- Propellor: First :: Maybe a -> First a
- Propellor: Last :: Maybe a -> Last a
- Propellor: Product :: a -> Product a
- Propellor: Sum :: a -> Sum a
- Propellor: [appEndo] :: Endo a -> a -> a
- Propellor: [getAll] :: All -> Bool
- Propellor: [getAlt] :: Alt (f :: k -> Type) (a :: k) -> f a
- Propellor: [getAny] :: Any -> Bool
- Propellor: [getAp] :: Ap (f :: k -> Type) (a :: k) -> f a
- Propellor: [getDual] :: Dual a -> a
- Propellor: [getFirst] :: First a -> Maybe a
- Propellor: [getLast] :: Last a -> Maybe a
- Propellor: [getProduct] :: Product a -> a
- Propellor: [getSum] :: Sum a -> a
- Propellor: class Semigroup a => Monoid a
- Propellor: infixr 6 <>
- Propellor: mappend :: Monoid a => a -> a -> a
- Propellor: mconcat :: Monoid a => [a] -> a
- Propellor: mempty :: Monoid a => a
- Propellor: newtype All
- Propellor: newtype Alt (f :: k -> Type) (a :: k)
- Propellor: newtype Any
- Propellor: newtype Ap (f :: k -> Type) (a :: k)
- Propellor: newtype Dual a
- Propellor: newtype Endo a
- Propellor: newtype First a
- Propellor: newtype Last a
- Propellor: newtype Product a
- Propellor: newtype Sum a
- Propellor.Property.SiteSpecific.JoeySites: USBDriveId :: String -> String -> USBDriveId
- Propellor.Property.SiteSpecific.JoeySites: USBHubPort :: String -> Int -> USBHubPort
- Propellor.Property.SiteSpecific.JoeySites: [driveProductId] :: USBDriveId -> String
- Propellor.Property.SiteSpecific.JoeySites: [driveVendorId] :: USBDriveId -> String
- Propellor.Property.SiteSpecific.JoeySites: [hubPort] :: USBHubPort -> Int
- Propellor.Property.SiteSpecific.JoeySites: [hubVendor] :: USBHubPort -> String
- Propellor.Property.SiteSpecific.JoeySites: autoMountDrive :: Label -> Maybe FilePath -> Property DebianLike
- Propellor.Property.SiteSpecific.JoeySites: autoMountDrive' :: [String] -> Label -> Maybe FilePath -> Property DebianLike
- Propellor.Property.SiteSpecific.JoeySites: autoMountDrivePort :: Label -> USBHubPort -> USBDriveId -> Maybe FilePath -> Property DebianLike
- Propellor.Property.SiteSpecific.JoeySites: data USBDriveId
- Propellor.Property.SiteSpecific.JoeySites: data USBHubPort
- Propellor.Property.SiteSpecific.JoeySites: githubBackup :: Property (HasInfo + DebianLike)
- Propellor.Property.SiteSpecific.JoeySites: githubKeys :: Property (HasInfo + UnixLike)
- Propellor.Property.SiteSpecific.JoeySites: homeNAS :: Property DebianLike
- Propellor.Property.SiteSpecific.JoeySites: podcatcher :: Property DebianLike
+ Propellor.Base: (-<.>) :: FilePath -> String -> FilePath
+ Propellor.Base: (<.>) :: FilePath -> String -> FilePath
+ Propellor.Base: (</>) :: FilePath -> FilePath -> FilePath
+ Propellor.Base: XdgState :: XdgDirectory
+ Propellor.Base: addExtension :: FilePath -> String -> FilePath
+ Propellor.Base: addTrailingPathSeparator :: FilePath -> FilePath
+ Propellor.Base: combine :: FilePath -> FilePath -> FilePath
+ Propellor.Base: dropDrive :: FilePath -> FilePath
+ Propellor.Base: dropExtension :: FilePath -> FilePath
+ Propellor.Base: dropExtensions :: FilePath -> FilePath
+ Propellor.Base: dropFileName :: FilePath -> FilePath
+ Propellor.Base: dropTrailingPathSeparator :: FilePath -> FilePath
+ Propellor.Base: equalFilePath :: FilePath -> FilePath -> Bool
+ Propellor.Base: extSeparator :: Char
+ Propellor.Base: getSearchPath :: IO [FilePath]
+ Propellor.Base: hasDrive :: FilePath -> Bool
+ Propellor.Base: hasExtension :: FilePath -> Bool
+ Propellor.Base: hasTrailingPathSeparator :: FilePath -> Bool
+ Propellor.Base: infixr 5 </>
+ Propellor.Base: infixr 7 <.>
+ Propellor.Base: isAbsolute :: FilePath -> Bool
+ Propellor.Base: isDrive :: FilePath -> Bool
+ Propellor.Base: isExtSeparator :: Char -> Bool
+ Propellor.Base: isExtensionOf :: String -> FilePath -> Bool
+ Propellor.Base: isPathSeparator :: Char -> Bool
+ Propellor.Base: isRelative :: FilePath -> Bool
+ Propellor.Base: isSearchPathSeparator :: Char -> Bool
+ Propellor.Base: isValid :: FilePath -> Bool
+ Propellor.Base: joinDrive :: FilePath -> FilePath -> FilePath
+ Propellor.Base: joinPath :: [FilePath] -> FilePath
+ Propellor.Base: makeRelative :: FilePath -> FilePath -> FilePath
+ Propellor.Base: makeValid :: FilePath -> FilePath
+ Propellor.Base: normalise :: FilePath -> FilePath
+ Propellor.Base: pathSeparator :: Char
+ Propellor.Base: pathSeparators :: [Char]
+ Propellor.Base: replaceBaseName :: FilePath -> String -> FilePath
+ Propellor.Base: replaceDirectory :: FilePath -> String -> FilePath
+ Propellor.Base: replaceExtension :: FilePath -> String -> FilePath
+ Propellor.Base: replaceExtensions :: FilePath -> String -> FilePath
+ Propellor.Base: replaceFileName :: FilePath -> String -> FilePath
+ Propellor.Base: searchPathSeparator :: Char
+ Propellor.Base: splitDirectories :: FilePath -> [FilePath]
+ Propellor.Base: splitDrive :: FilePath -> (FilePath, FilePath)
+ Propellor.Base: splitExtension :: FilePath -> (String, String)
+ Propellor.Base: splitExtensions :: FilePath -> (FilePath, String)
+ Propellor.Base: splitFileName :: FilePath -> (String, String)
+ Propellor.Base: splitPath :: FilePath -> [FilePath]
+ Propellor.Base: splitSearchPath :: String -> [FilePath]
+ Propellor.Base: stripExtension :: String -> FilePath -> Maybe FilePath
+ Propellor.Base: takeBaseName :: FilePath -> String
+ Propellor.Base: takeDirectory :: FilePath -> FilePath
+ Propellor.Base: takeDrive :: FilePath -> FilePath
+ Propellor.Base: takeExtension :: FilePath -> String
+ Propellor.Base: takeExtensions :: FilePath -> String
+ Propellor.Base: takeFileName :: FilePath -> FilePath
+ Propellor.Base: type FilePath = String
+ Propellor.Property.Git: daemonService :: FilePath -> Property Linux
+ Propellor.Property.SiteSpecific.JoeySites: epsolarUsbSetup :: Property UnixLike
+ Propellor.Property.SiteSpecific.JoeySites: thinkPadLedsOff :: Property Linux
+ Propellor.Types.Singletons: type family DemoteRep (kparam :: KProxy k)
+ Propellor.Utilities: XdgState :: XdgDirectory
- Propellor: (!) :: CheckCombinableNote x z (NoteFor ('Text "!")) => Props (MetaTypes x) -> RevertableProperty (MetaTypes y) (MetaTypes z) -> Props (MetaTypes (Combine x z))
+ Propellor: (!) :: forall {a} {k} (x :: [a]) (z :: [a]) (y :: k). 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, CheckCombinableNote x y (NoteFor ('Text "&"))) => Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
+ Propellor: (&) :: 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))
- Propellor.EnsureProperty: data OuterMetaTypesWitness metatypes
+ Propellor.EnsureProperty: data OuterMetaTypesWitness (metatypes :: k)
- Propellor.EnsureProperty: ensureProperty :: EnsurePropertyAllowed inner outer => OuterMetaTypesWitness outer -> Property (MetaTypes inner) -> Propellor Result
+ Propellor.EnsureProperty: ensureProperty :: forall (inner :: [MetaType]) (outer :: [MetaType]). EnsurePropertyAllowed inner outer => OuterMetaTypesWitness outer -> Property (MetaTypes inner) -> Propellor Result
- Propellor.EnsureProperty: property' :: SingI metatypes => Desc -> (OuterMetaTypesWitness metatypes -> Propellor Result) -> Property (MetaTypes metatypes)
+ Propellor.EnsureProperty: property' :: forall {k} (metatypes :: k). SingI metatypes => Desc -> (OuterMetaTypesWitness metatypes -> Propellor Result) -> Property (MetaTypes metatypes)
- Propellor.EnsureProperty: type family EnsurePropertyAllowed inner outer :: Constraint
+ Propellor.EnsureProperty: type family EnsurePropertyAllowed (inner :: [MetaType]) (outer :: [MetaType])
- Propellor.Info: setInfoProperty :: (MetaTypes metatypes' ~ (+) HasInfo metatypes, SingI metatypes') => Property metatypes -> Info -> Property (MetaTypes metatypes')
+ Propellor.Info: setInfoProperty :: forall {k} (metatypes' :: k) metatypes. (MetaTypes metatypes' ~ (HasInfo + metatypes), SingI metatypes') => Property metatypes -> Info -> Property (MetaTypes metatypes')
- Propellor.PropAccum: (!) :: CheckCombinableNote x z (NoteFor ('Text "!")) => Props (MetaTypes x) -> RevertableProperty (MetaTypes y) (MetaTypes z) -> Props (MetaTypes (Combine x z))
+ Propellor.PropAccum: (!) :: forall {a} {k} (x :: [a]) (z :: [a]) (y :: k). 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, CheckCombinableNote x y (NoteFor ('Text "&"))) => Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
+ Propellor.PropAccum: (&) :: 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))
- Propellor.PropAccum: (&^) :: (IsProp p, MetaTypes y ~ GetMetaTypes p, CheckCombinableNote x y (NoteFor ('Text "&^"))) => Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
+ Propellor.PropAccum: (&^) :: 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))
- Propellor.Property: class Checkable p i
+ Propellor.Property: class Checkable (p :: Type -> Type) i
- Propellor.Property: data OuterMetaTypesWitness metatypes
+ Propellor.Property: data OuterMetaTypesWitness (metatypes :: k)
- Propellor.Property: doNothing :: SingI t => Property (MetaTypes t)
+ Propellor.Property: doNothing :: forall {k} (t :: k). SingI t => Property (MetaTypes t)
- Propellor.Property: ensureProperty :: EnsurePropertyAllowed inner outer => OuterMetaTypesWitness outer -> Property (MetaTypes inner) -> Propellor Result
+ Propellor.Property: ensureProperty :: forall (inner :: [MetaType]) (outer :: [MetaType]). EnsurePropertyAllowed inner outer => OuterMetaTypesWitness outer -> Property (MetaTypes inner) -> Propellor Result
- Propellor.Property: impossible :: SingI t => String -> Property (MetaTypes t)
+ Propellor.Property: impossible :: forall {k} (t :: k). SingI t => String -> Property (MetaTypes t)
- Propellor.Property: pickOS :: HasCallStack => (SingKind ('KProxy :: KProxy ka), SingKind ('KProxy :: KProxy kb), DemoteRep ('KProxy :: KProxy ka) ~ [MetaType], DemoteRep ('KProxy :: KProxy kb) ~ [MetaType], SingI c) => Property (MetaTypes (a :: ka)) -> Property (MetaTypes (b :: kb)) -> Property (MetaTypes c)
+ Propellor.Property: pickOS :: forall {k} ka kb (c :: k) (a :: ka) (b :: kb). (HasCallStack, SingKind ('KProxy :: KProxy ka), SingKind ('KProxy :: KProxy kb), DemoteRep ('KProxy :: KProxy ka) ~ [MetaType], DemoteRep ('KProxy :: KProxy kb) ~ [MetaType], SingI c) => Property (MetaTypes a) -> Property (MetaTypes b) -> Property (MetaTypes c)
- Propellor.Property: property :: SingI metatypes => Desc -> Propellor Result -> Property (MetaTypes metatypes)
+ Propellor.Property: property :: forall {k} (metatypes :: k). SingI metatypes => Desc -> Propellor Result -> Property (MetaTypes metatypes)
- Propellor.Property: property' :: SingI metatypes => Desc -> (OuterMetaTypesWitness metatypes -> Propellor Result) -> Property (MetaTypes metatypes)
+ Propellor.Property: property' :: forall {k} (metatypes :: k). SingI metatypes => Desc -> (OuterMetaTypesWitness metatypes -> Propellor Result) -> Property (MetaTypes metatypes)
- Propellor.Property: withOS :: SingI metatypes => Desc -> (OuterMetaTypesWitness metatypes -> Maybe System -> Propellor Result) -> Property (MetaTypes metatypes)
+ Propellor.Property: withOS :: forall {k} (metatypes :: k). SingI metatypes => Desc -> (OuterMetaTypesWitness metatypes -> Maybe System -> Propellor Result) -> Property (MetaTypes metatypes)
- Propellor.Property.Atomic: atomicDirUpdate :: EnsurePropertyAllowed t t => SingI t => FilePath -> (FilePath -> Property (MetaTypes t)) -> Property (MetaTypes t)
+ Propellor.Property.Atomic: atomicDirUpdate :: forall (t :: [MetaType]). (EnsurePropertyAllowed t t, SingI t) => FilePath -> (FilePath -> 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.Property.Atomic: atomicUpdate :: forall (t :: [MetaType]) a. (EnsurePropertyAllowed t t, SingI t) => AtomicResourcePair a -> CheckAtomicResourcePair a -> SwapAtomicResourcePair a -> (a -> Property (MetaTypes t)) -> Property (MetaTypes t)
- Propellor.Property.Atomic: type CheckAtomicResourcePair a = AtomicResourcePair a -> Propellor (AtomicResourcePair a)
+ Propellor.Property.Atomic: type CheckAtomicResourcePair a = AtomicResourcePair a -> Propellor AtomicResourcePair a
- Propellor.Property.Chroot: [Chroot] :: ChrootBootstrapper b => FilePath -> b -> InfoPropagator -> Host -> Chroot
+ Propellor.Property.Chroot: [Chroot] :: forall b. ChrootBootstrapper b => FilePath -> b -> InfoPropagator -> Host -> Chroot
- Propellor.Property.List: combineProperties :: SingI metatypes => Desc -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
+ Propellor.Property.List: combineProperties :: forall {k} (metatypes :: k). SingI metatypes => Desc -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
- Propellor.Property.List: propertyList :: SingI metatypes => Desc -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
+ Propellor.Property.List: propertyList :: forall {k} (metatypes :: k). SingI metatypes => Desc -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
- Propellor.Property.List: toProps :: [Property (MetaTypes metatypes)] -> Props (MetaTypes metatypes)
+ Propellor.Property.List: toProps :: forall {k} (metatypes :: k). [Property (MetaTypes metatypes)] -> Props (MetaTypes metatypes)
- Propellor.Property.SiteSpecific.JoeySites: homeRouter :: Interfaces -> String -> HostapdConfig -> Property DebianLike
+ Propellor.Property.SiteSpecific.JoeySites: homeRouter :: String -> Interfaces -> String -> HostapdConfig -> Property DebianLike
- Propellor.Types: class LiftPropellor m
+ Propellor.Types: class LiftPropellor (m :: Type -> Type)
- Propellor.Types: class TightenTargets p
+ Propellor.Types: class TightenTargets (p :: Type -> Type)
- Propellor.Types: property :: SingI metatypes => Desc -> Propellor Result -> Property (MetaTypes metatypes)
+ Propellor.Types: property :: forall {k} (metatypes :: k). SingI metatypes => Desc -> Propellor Result -> Property (MetaTypes metatypes)
- Propellor.Types: property'' :: SingI metatypes => Desc -> Maybe (Propellor Result) -> Property (MetaTypes metatypes)
+ Propellor.Types: property'' :: forall {k} (metatypes :: k). SingI metatypes => Desc -> Maybe (Propellor Result) -> Property (MetaTypes metatypes)
- Propellor.Types: tightenTargets :: (TightenTargets p, TightenTargetsAllowed untightened tightened, SingI tightened) => p (MetaTypes untightened) -> p (MetaTypes tightened)
+ Propellor.Types: tightenTargets :: forall (untightened :: [MetaType]) (tightened :: [MetaType]). (TightenTargets p, TightenTargetsAllowed untightened tightened, SingI tightened) => p (MetaTypes untightened) -> p (MetaTypes tightened)
- Propellor.Types: type ResultCombiner = Maybe (Propellor Result) -> Maybe (Propellor Result) -> Maybe (Propellor Result)
+ Propellor.Types: type ResultCombiner = Maybe Propellor Result -> Maybe Propellor Result -> Maybe Propellor Result
- Propellor.Types.Core: class LiftPropellor m
+ Propellor.Types.Core: class LiftPropellor (m :: Type -> Type)
- Propellor.Types.Info: [InfoEntry] :: (IsInfo v, Typeable v) => v -> InfoEntry
+ Propellor.Types.Info: [InfoEntry] :: forall v. (IsInfo v, Typeable v) => v -> InfoEntry
- Propellor.Types.MetaTypes: class SingI t
+ Propellor.Types.MetaTypes: class SingI (t :: k)
- Propellor.Types.MetaTypes: type MetaTypes = Sing
+ Propellor.Types.MetaTypes: type MetaTypes = Sing :: k -> Type
- Propellor.Types.ResultCheck: class Checkable p i
+ Propellor.Types.ResultCheck: class Checkable (p :: Type -> Type) i
- Propellor.Types.Singletons: class SingI t
+ Propellor.Types.Singletons: class SingI (t :: k)
- Propellor.Types.Singletons: class (kparam ~ 'KProxy) => SingKind (kparam :: KProxy k) where {
+ Propellor.Types.Singletons: class kparam ~ 'KProxy :: KProxy k => SingKind (kparam :: KProxy k) where {
- Propellor.Types.Singletons: fromSing :: SingKind kparam => Sing (a :: k) -> DemoteRep kparam
+ Propellor.Types.Singletons: fromSing :: forall (a :: k). SingKind kparam => Sing a -> DemoteRep kparam
- Propellor.Types.Singletons: type DemoteRep kparam :: Type;
+ Propellor.Types.Singletons: type DemoteRep (kparam :: KProxy k);
- Propellor.Types.ZFS: fromValue :: (Value a, IsString a) => String -> a
+ Propellor.Types.ZFS: fromValue :: Value a => String -> a
- Propellor.Utilities: bracket :: MonadMask m => m a -> (a -> m c) -> (a -> m b) -> m b
+ Propellor.Utilities: bracket :: (HasCallStack, MonadMask m) => m a -> (a -> m c) -> (a -> m b) -> m b
- Propellor.Utilities: bracketOnError :: MonadMask m => m a -> (a -> m c) -> (a -> m b) -> m b
+ Propellor.Utilities: bracketOnError :: (HasCallStack, MonadMask m) => m a -> (a -> m c) -> (a -> m b) -> m b
- Propellor.Utilities: bracket_ :: MonadMask m => m a -> m c -> m b -> m b
+ Propellor.Utilities: bracket_ :: (HasCallStack, MonadMask m) => m a -> m c -> m b -> m b
- Propellor.Utilities: catch :: (MonadCatch m, Exception e) => m a -> (e -> m a) -> m a
+ Propellor.Utilities: catch :: (MonadCatch m, HasCallStack, Exception e) => m a -> (e -> m a) -> m a
- Propellor.Utilities: catchAll :: MonadCatch m => m a -> (SomeException -> m a) -> m a
+ Propellor.Utilities: catchAll :: (HasCallStack, MonadCatch m) => m a -> (SomeException -> m a) -> m a
- Propellor.Utilities: catchIOError :: MonadCatch m => m a -> (IOError -> m a) -> m a
+ Propellor.Utilities: catchIOError :: (HasCallStack, MonadCatch m) => m a -> (IOError -> m a) -> m a
- Propellor.Utilities: catchIf :: (MonadCatch m, Exception e) => (e -> Bool) -> m a -> (e -> m a) -> m a
+ Propellor.Utilities: catchIf :: (HasCallStack, MonadCatch m, Exception e) => (e -> Bool) -> m a -> (e -> m a) -> m a
- Propellor.Utilities: catchJust :: (MonadCatch m, Exception e) => (e -> Maybe b) -> m a -> (b -> m a) -> m a
+ Propellor.Utilities: catchJust :: (HasCallStack, MonadCatch m, Exception e) => (e -> Maybe b) -> m a -> (b -> m a) -> m a
- Propellor.Utilities: catches :: (Foldable f, MonadCatch m) => m a -> f (Handler m a) -> m a
+ Propellor.Utilities: catches :: (HasCallStack, Foldable f, MonadCatch m) => m a -> f (Handler m a) -> m a
- Propellor.Utilities: finally :: MonadMask m => m a -> m b -> m a
+ Propellor.Utilities: finally :: (HasCallStack, MonadMask m) => m a -> m b -> m a
- Propellor.Utilities: generalBracket :: MonadMask m => m a -> (a -> ExitCase b -> m c) -> (a -> m b) -> m (b, c)
+ Propellor.Utilities: generalBracket :: (MonadMask m, HasCallStack) => m a -> (a -> ExitCase b -> m c) -> (a -> m b) -> m (b, c)
- Propellor.Utilities: handle :: (MonadCatch m, Exception e) => (e -> m a) -> m a -> m a
+ Propellor.Utilities: handle :: (HasCallStack, MonadCatch m, Exception e) => (e -> m a) -> m a -> m a
- Propellor.Utilities: handleAll :: MonadCatch m => (SomeException -> m a) -> m a -> m a
+ Propellor.Utilities: handleAll :: (HasCallStack, MonadCatch m) => (SomeException -> m a) -> m a -> m a
- Propellor.Utilities: handleIOError :: MonadCatch m => (IOError -> m a) -> m a -> m a
+ Propellor.Utilities: handleIOError :: (HasCallStack, MonadCatch m) => (IOError -> m a) -> m a -> m a
- Propellor.Utilities: handleIf :: (MonadCatch m, Exception e) => (e -> Bool) -> (e -> m a) -> m a -> m a
+ Propellor.Utilities: handleIf :: (HasCallStack, MonadCatch m, Exception e) => (e -> Bool) -> (e -> m a) -> m a -> m a
- Propellor.Utilities: handleJust :: (MonadCatch m, Exception e) => (e -> Maybe b) -> (b -> m a) -> m a -> m a
+ Propellor.Utilities: handleJust :: (HasCallStack, MonadCatch m, Exception e) => (e -> Maybe b) -> (b -> m a) -> m a -> m a
- Propellor.Utilities: mask :: MonadMask m => ((forall a. () => m a -> m a) -> m b) -> m b
+ Propellor.Utilities: mask :: (MonadMask m, HasCallStack) => ((forall a. () => m a -> m a) -> m b) -> m b
- Propellor.Utilities: mask_ :: MonadMask m => m a -> m a
+ Propellor.Utilities: mask_ :: (HasCallStack, MonadMask m) => m a -> m a
- Propellor.Utilities: onError :: MonadMask m => m a -> m b -> m a
+ Propellor.Utilities: onError :: (HasCallStack, MonadMask m) => m a -> m b -> m a
- Propellor.Utilities: onException :: MonadCatch m => m a -> m b -> m a
+ Propellor.Utilities: onException :: (HasCallStack, MonadCatch m) => m a -> m b -> m a
- Propellor.Utilities: throwM :: (MonadThrow m, Exception e) => e -> m a
+ Propellor.Utilities: throwM :: (MonadThrow m, HasCallStack, Exception e) => e -> m a
- Propellor.Utilities: try :: (MonadCatch m, Exception e) => m a -> m (Either e a)
+ Propellor.Utilities: try :: (HasCallStack, MonadCatch m, Exception e) => m a -> m (Either e a)
- Propellor.Utilities: tryJust :: (MonadCatch m, Exception e) => (e -> Maybe b) -> m a -> m (Either b a)
+ Propellor.Utilities: tryJust :: (HasCallStack, MonadCatch m, Exception e) => (e -> Maybe b) -> m a -> m (Either b a)
- Propellor.Utilities: uninterruptibleMask :: MonadMask m => ((forall a. () => m a -> m a) -> m b) -> m b
+ Propellor.Utilities: uninterruptibleMask :: (MonadMask m, HasCallStack) => ((forall a. () => m a -> m a) -> m b) -> m b
- Propellor.Utilities: uninterruptibleMask_ :: MonadMask m => m a -> m a
+ Propellor.Utilities: uninterruptibleMask_ :: (HasCallStack, MonadMask m) => m a -> m a
Files
- CHANGELOG +13/−0
- config-freebsd.hs +1/−1
- debian/changelog +13/−0
- joeyconfig.hs +70/−60
- propellor.cabal +1/−1
- src/Propellor/Info.hs +2/−2
- src/Propellor/Property/Git.hs +32/−0
- src/Propellor/Property/Sbuild.hs +1/−1
- src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs +1/−1
- src/Propellor/Property/SiteSpecific/JoeySites.hs +98/−241
- src/Propellor/Property/Systemd.hs +6/−0
- src/Propellor/Types/OS.hs +1/−1
CHANGELOG view
@@ -1,3 +1,16 @@+propellor (5.18) unstable; urgency=medium++ * Git.daemonRunning: Worked around git's unfortunate new+ requirement that safe.directory be set when running git-daemon from+ inetd.+ * Git.daemonService: New property which runs git-daemon as a systemd+ service.+ * Systemd.nspawned: Fix a bug that prevented propellor from running in+ a systemd machine due to /usr/local/propellor not being visible+ inside the machine.++ -- Joey Hess <id@joeyh.name> Sun, 10 Aug 2025 11:24:34 -0400+ propellor (5.17) unstable; urgency=medium * Apt: Enable the non-free-firmware section, when used with Debian
config-freebsd.hs view
@@ -59,7 +59,7 @@ -- A generic webserver in a Docker container. webserverContainer :: Docker.Container webserverContainer = Docker.container "webserver" (Docker.latestImage "debian") $ props- & osDebian' KFreeBSD (Stable "bookworm") X86_64+ & osDebian' KFreeBSD (Stable "trixie") X86_64 & Apt.stdSourcesList & Docker.publish "80:80" & Docker.volume "/var/www:/var/www"
debian/changelog view
@@ -1,3 +1,16 @@+propellor (5.18) unstable; urgency=medium++ * Git.daemonRunning: Worked around git's unfortunate new+ requirement that safe.directory be set when running git-daemon from+ inetd.+ * Git.daemonService: New property which runs git-daemon as a systemd+ service.+ * Systemd.nspawned: Fix a bug that prevented propellor from running in+ a systemd machine due to /usr/local/propellor not being visible+ inside the machine.++ -- Joey Hess <id@joeyh.name> Sun, 10 Aug 2025 11:24:34 -0400+ propellor (5.17) unstable; urgency=medium * Apt: Enable the non-free-firmware section, when used with Debian
joeyconfig.hs view
@@ -31,7 +31,6 @@ import qualified Propellor.Property.FlashKernel as FlashKernel import qualified Propellor.Property.Laptop as Laptop import qualified Propellor.Property.LightDM as LightDM-import qualified Propellor.Property.Debootstrap as Debootstrap import qualified Propellor.Property.HostingProvider.Linode as Linode import qualified Propellor.Property.SiteSpecific.GitHome as GitHome import qualified Propellor.Property.SiteSpecific.GitAnnexBuilder as GitAnnexBuilder@@ -48,7 +47,7 @@ , dragon , oyster , house- , sky+ , gate , kite , sparrow , beaver@@ -62,47 +61,43 @@ darkstar :: Host darkstar = host "darkstar.kitenet.net" $ props & osDebian Unstable X86_64- & ipv6 "2001:4830:1600:187::2" & Hostname.sane & Hostname.mailname & Apt.serviceInstalledRunning "swapspace" & Laptop.powertopAutoTuneOnBoot & Laptop.trimSSD- & Grub.cmdline_Linux_default "i915.enable_psr=1" ! Grub.cmdline_Linux_default "quiet" & User.hasGroup (User "joey") (Group "dialout") & JoeySites.dkimMilter & JoeySites.postfixSaslPasswordClient- -- & JoeySites.alarmClock "*-*-* 7:30" (User "joey")- -- "/usr/bin/timeout 45m /home/joey/bin/goodmorning" & JoeySites.laptopSoftware & JoeySites.userDirHtml+ & JoeySites.thinkPadLedsOff & Ssh.userKeys (User "joey") hostContext [ (SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICfFntnesZcYz2B2T41ay45igfckXRSh5uVffkuCQkLv joey@darkstar") ]- & imageBuiltFor house+ ! imageBuiltFor house (RawDiskImage "/srv/house.img") (Debootstrapped mempty) dragon :: Host dragon = host "dragon.kitenet.net" $ props- & ipv6 "2001:4830:1600:187::2" & JoeySites.dkimMilter & JoeySites.postfixSaslPasswordClient oyster :: Host oyster = host "oyster.kitenet.net" $ props- & standardSystem (Stable "buster") X86_64+ & standardSystem (Stable "bookworm") X86_64 ["Unreliable server. Anything here may be lost at any time!" ]- & ipv4 "45.138.157.89"+ & ipv4 "45.87.155.118" & User.hasPassword (User "root") & Apt.unattendedUpgrades house :: Host house = host "house.lan" $ props- & standardSystem Testing ARMHF [ "House mail computer." ]+ & standardSystem (Stable "bookworm") ARMHF [ "House main computer." ] & Apt.removed ["rsyslog"] & cubietech_Cubietruck@@ -133,53 +128,69 @@ (Context "house.joeyh.name") (SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMAmVYddg/RgCbIj+cLcEiddeFXaYFnbEJ3uGj9G/EyV joey@honeybee") - -- Use iwd to connect to wifi on whatever usb wifi is connected.- & Apt.installed ["iwd"]- & File.hasContent "/var/lib/iwd/.known_network.freq"- [ "[7085c93b-9e5f-5cdb-a13d-bf72db7c3adf]"- , "name=/var/lib/iwd//hollow.open"- , "list= 2457"- ]- & File.hasContent "/var/lib/iwd/hollow.open"- [ "[IPv4]"- , "Address=10.1.1.2"- , "Netmask=255.255.255.0"- , "Gateway=10.1.1.1"- , "Broadcast=10.1.1.255"- , "DNS=10.1.1.1"+ -- Use network-manager to connect to wifi on whatever usb wifi is connected.+ & Apt.installed ["network-manager"]+ & File.hasContent "/etc/NetworkManager/system-connections/hollow.nmconnection"+ [ "[connection]"+ , "id=hollow"+ , "uuid=dfa672be-79d2-4151-9a26-ae7cb3729681"+ , "type=wifi"+ , "interface-name=wlan1"+ , ""+ , "[wifi]"+ , "mode=infrastructure"+ , "ssid=hollow"+ , ""+ , "[ipv4]"+ , "method=manual"+ , "addresses=10.1.1.2/24"+ , "gateway=10.1.1.1"+ , "dns=10.1.1.1"+ , ""+ , "[ipv6]"+ , "addr-gen-mode=default"+ , "method=auto"+ , ""+ , "[proxy]" ]- & Systemd.enabled "iwd"+ & Systemd.enabled "NetworkManager" & "/etc/resolv.conf" `File.containsLine` "search lan" - & JoeySites.homeNAS & Apt.installed ["mtr-tiny", "iftop", "screen", "nmap"]- -- Currently manually building the xr_usb_serial module.- & Apt.installed ["linux-headers-armmp-lpae"] & Postfix.satellite -sky :: Host-sky = host "sky.lan" $ props- & standardSystem Testing ARMHF [ "Wifi router." ]+-- This is a raspberry pi and is installed using https://raspi.debian.net/tested-images/+-- before propellor is run on it.+gate :: Host+gate = host "gate.lan" $ props+ & standardSystem (Stable "trixie") ARM64 [ "Wifi router." ] & Apt.removed ["rsyslog"] & Apt.installed ["raspi-firmware", "firmware-misc-nonfree"] & Apt.serviceInstalledRunning "systemd-timesyncd" -- no hardware clock + & "/etc/systemd/network/10-gate.link" `File.containsLines`+ [ "[Match]"+ , "MACAddress=9c:ef:d5:fc:d6:f3" -- USB wifi adapter+ , ""+ , "[Link]"+ , "Name=" ++ JoeySites.wifiInterface ifs+ ]+ & Ssh.hostKeys hostContext [ (SshEcdsa, "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBG19v7K59SzFp/OntM9iyhlKOj2pShFSPJeqR1aiYhPF2NqztcmsY6WvIDqh6jmaISnyV1IqZZ60zvGTVRoOyMY=") ] & Ssh.userKeys (User "joey") hostContext [ (SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOmwEfM5qTjA7xuJgygEgHfa1Y/WrRBpu7xBY8D82ul+") ]+ & Postfix.satellite & JoeySites.connectStarlinkDish ifs- & JoeySites.homeRouter ifs "hollow" JoeySites.hostapd2GhzConfig_mt76- & Apt.installed ["mtr-tiny", "iftop", "screen", "nmap", "net-tools"]- & Postfix.satellite+ & JoeySites.homeRouter "gate.lan" ifs "hollow" JoeySites.hostapd2GhzConfig_mt76 where ifs = JoeySites.Interfaces { JoeySites.ethernetInterface = "eth0"- , JoeySites.wifiInterface = "wlan0"+ , JoeySites.wifiInterface = "wlan-gate" , JoeySites.wifiInterfaceOld = "wlan1" } @@ -188,12 +199,9 @@ -- with propellor. kite :: Host kite = host "kite.kitenet.net" $ props- & standardSystemUnhardened Testing X86_64 [ "Welcome to kite!" ]+ & standardSystemUnhardened (Stable "trixie") X86_64 [ "Welcome to kite!" ] & ipv4 "66.228.36.95"- -- disabled because I've had complaints of mail bouncing, and it- -- seems mail servers are only trying ipv6 and then bouncing if- -- they have an ipv6 routing issue.- -- & ipv6 "2600:3c03::f03c:91ff:fe73:b0d2"+ & ipv6 "2600:3c03::f03c:91ff:fe73:b0d2" & alias "kitenet.net" & Ssh.hostKeys (Context "kitenet.net") [ (SshDsa, "ssh-dss AAAAB3NzaC1kc3MAAACBAO9tnPUT4p+9z7K6/OYuiBNHaij4Nzv5YVBih1vMl+ALz0gYAj8RWJzXmqp5buFAyfgOoLw+H9s1bBS01Sy3i07Dm6cx1fWG4RXL/E/3w1tavX99GD2bBxDBu890ebA5Tp+eFRJkS9+JwSvFiF6CP7NbVjifCagoUO56Ig048RwDAAAAFQDPY2xM3q6KwsVQliel23nrd0rV2QAAAIEAga3hj1hL00rYPNnAUzT8GAaSP62S4W68lusErH+KPbsMwFBFY/Ib1FVf8k6Zn6dZLh/HH/RtJi0JwdzPI1IFW+lwVbKfwBvhQ1lw9cH2rs1UIVgi7Wxdgfy8gEWxf+QIqn62wG+Ulf/HkWGvTrRpoJqlYRNS/gnOWj9Z/4s99koAAACBAM/uJIo2I0nK15wXiTYs/NYUZA7wcErugFn70TRbSgduIFH6U/CQa3rgHJw9DCPCQJLq7pwCnFH7too/qaK+czDk04PsgqV0+Jc7957gU5miPg50d60eJMctHV4eQ1FpwmGGfXxRBR9k2ZvikWYatYir3L6/x1ir7M0bA9IzNU45")@@ -214,6 +222,7 @@ & Fail2Ban.installed -- since ssh password authentication is allowed -- Allow ssh -R to forward ports via kite & Ssh.setSshdConfig "GatewayPorts" "clientspecified"+ & Apt.installed ["wtmpdb"] & Apt.serviceInstalledRunning "ntp" & "/etc/timezone" `File.hasContent` ["US/Eastern"] @@ -274,9 +283,6 @@ & alias "kgb.kitenet.net" & JoeySites.kgbServer - & alias "podcatcher.kitenet.net"- & JoeySites.podcatcher- & alias "ns4.kitenet.net" & myDnsPrimary "kitenet.net" [ (RelDomain "mouse-onion", CNAME $ AbsDomain "htieo6yu2qtcn2j3.onion")@@ -313,11 +319,14 @@ & Systemd.nspawned (GitAnnexBuilder.autoBuilderContainer (GitAnnexBuilder.standardAutoBuilder True) Unstable X86_64 mempty Nothing (Cron.Times "15 * * * *") "2h")+ & Systemd.nspawned (GitAnnexBuilder.autoBuilderContainer+ (GitAnnexBuilder.standardAutoBuilder True)+ Unstable X86_32 mempty Nothing (Cron.Times "30 * * * *") "2h") & Apt.serviceInstalledRunning "swapspace" sparrow :: Host sparrow = host "sparrow.kitenet.net" $ props- & standardSystem Testing ARM64 [ "Welcome to sparrow!" ]+ & standardSystem (Stable "trixie") ARM64 [ "Welcome to sparrow!" ] & ipv4 "128.140.52.168" & ipv6 "2a01:4f8:c17:ed3a::1" & Apt.installed ["ssh"]@@ -326,16 +335,6 @@ & Apt.serviceInstalledRunning "swapspace" & Systemd.nspawned (GitAnnexBuilder.autoBuilderContainer- -- qemu hangs running unattended-upgrades on i386- -- so disable installing that- (GitAnnexBuilder.standardAutoBuilder False)- Unstable X86_32 mempty Nothing (Cron.Times "30 * * * *") "4h")- & Systemd.nspawned (GitAnnexBuilder.autoBuilderContainer- GitAnnexBuilder.stackAutoBuilder- (Stable "jessie") X86_32 - (Debootstrap.UseOldGpgKeyring Debootstrap.:+ Debootstrap.DebootstrapMirror "http://archive.debian.org/debian/")- (Just "ancient") (Cron.Times "45 * * * *") "4h")- & Systemd.nspawned (GitAnnexBuilder.autoBuilderContainer (GitAnnexBuilder.standardAutoBuilder True) Testing ARM64 mempty Nothing (Cron.Times "1 * * * *") "2h") & Systemd.nspawned (GitAnnexBuilder.autoBuilderContainer@@ -344,9 +343,21 @@ (Just "ancient") (Cron.Times "20 * * * *") "2h") & Systemd.nspawned (GitAnnexBuilder.autoBuilderContainer (GitAnnexBuilder.armAutoBuilder (GitAnnexBuilder.standardAutoBuilder True))- Testing ARMEL mempty Nothing (Cron.Times "15 15 * * *") "2h")+ Testing ARMEL mempty Nothing (Cron.Times "40 * * * *") "2h") - & Systemd.nspawned ancientKitenet+ ! Systemd.nspawned ancientKitenet+ + & Borg.backup "/root" (JoeySites.rsyncNetBorgRepo "sparrow.borg" []) Cron.Daily+ [ ]+ [ Borg.KeepDays 7+ , Borg.KeepWeeks 4+ , Borg.KeepMonths 3+ ]+ `requires` Ssh.knownHost hosts "usw-s002.rsync.net" (User "root")+ `requires` Ssh.userKeys (User "root")+ (Context "sparrow.kitenet.net")+ [ (SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID3mgBXLLzp+DnzZqH6Azly+0uZe/d2TK69oZYcFz+gN root@sparrow")+ ] beaver :: Host beaver = host "beaver.kitenet.net" $ props@@ -364,7 +375,6 @@ mouse :: Host mouse = host "mouse.kitenet.net" $ props- & ipv4 "67.223.19.96" & Apt.installed ["ssh"] & Tor.installed & Tor.hiddenServiceAvailable "ssh" (Port 22)@@ -403,7 +413,7 @@ & alias "dist-bugs.kitenet.net" & alias "family.kitenet.net" - & osDebian (Stable "buster") X86_32+ & osDebian (Stable "bookworm") X86_32 & Apt.installed ["linux-image-686-pae"] & Apt.stdSourcesList `onChange` Apt.upgrade & Apt.unattendedUpgrades@@ -423,7 +433,7 @@ -- Exhibit: kite's 90's website on port 1994. ancientKitenet :: Systemd.Container ancientKitenet = Systemd.debContainer "ancient-kitenet" $ props- & standardContainer (Stable "buster") ARM64+ & standardContainer (Stable "trixie") ARM64 & alias hn & Git.cloned (User "root") "git://kitenet-net.branchable.com/" "/var/www/html" (Just "remotes/origin/old-kitenet.net")
propellor.cabal view
@@ -1,5 +1,5 @@ Name: propellor-Version: 5.17+Version: 5.18 Cabal-Version: 1.20 License: BSD2 Maintainer: Joey Hess <id@joeyh.name>
src/Propellor/Info.hs view
@@ -91,13 +91,13 @@ -- It also lets the type checker know that all the properties of the -- host must support Debian. ----- > & osDebian (Stable "bookworm") X86_64+-- > & osDebian (Stable "trixie") X86_64 osDebian :: DebianSuite -> Architecture -> Property (HasInfo + Debian) osDebian = osDebian' Linux -- Use to specify a different `DebianKernel` than the default `Linux` ----- > & osDebian' KFreeBSD (Stable "bookworm") X86_64+-- > & osDebian' KFreeBSD (Stable "trixie") X86_64 osDebian' :: DebianKernel -> DebianSuite -> Architecture -> Property (HasInfo + Debian) osDebian' kernel suite arch = tightenTargets $ os (System (Debian kernel suite) arch)
src/Propellor/Property/Git.hs view
@@ -4,6 +4,7 @@ import Propellor.Property.File import qualified Propellor.Property.Apt as Apt import qualified Propellor.Property.Service as Service+import qualified Propellor.Property.Systemd as Systemd import Data.List @@ -40,12 +41,43 @@ , "nobody" , "/usr/bin/git" , "git"+ -- safe.directory configuration is needed by git for+ -- git-daemon to serve repositories owned by any other user.+ -- But since git-daemon is explicitly configured which+ -- repositories to operate on, it adds no security.+ , "-c", "safe.directory=*" , "daemon" , "--inetd" , "--export-all" , "--base-path=" ++ exportdir , exportdir ]++-- | Exports all git repos in a directory (that user nobody can read)+-- using git-daemon, run as a systemd service.+daemonService :: FilePath -> Property Linux+daemonService exportdir = + servicefile `onChange` + Systemd.running "git-daemon"+ where+ servicefile = "/etc/systemd/system/git-daemon.service" `hasContent`+ [ "[Unit]"+ , "Description=Git Daemon"+ , "Documentation=man:git-daemon(1)"+ , ""+ , " [Service]"+ -- safe.directory configuration is needed by git for+ -- git-daemon to serve repositories owned by any other user.+ -- But since git-daemon is explicitly configured which+ -- repositories to operate on, it adds no security.+ , "Environment=GIT_CONFIG_COUNT='1' GIT_CONFIG_KEY_0='safe.directory' GIT_CONFIG_VALUE_0=\"*\""+ , "ExecStart=/usr/bin/git daemon --export-all --base-path=" ++ exportdir ++ " " ++ exportdir+ , "DynamicUser=yes"+ , ""+ , "[Install]"+ , "WantedBy=multi-user.target"+ ]+ `describe` "git-daemon systemd service file" installed :: Property DebianLike installed = Apt.installed ["git"]
src/Propellor/Property/Sbuild.hs view
@@ -21,7 +21,7 @@ Suggested usage in @config.hs@: > mybox = host "mybox.example.com" $ props-> & osDebian (Stable "bookworm") X86_64+> & osDebian (Stable "trixie") X86_64 > & Apt.useLocalCacher > & sidSchrootBuilt > & Sbuild.usableBy (User "spwhitton")
src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs view
@@ -183,7 +183,7 @@ ARMEL -> "https://github.com/commercialhaskell/stack/releases/download/v1.9.3/stack-1.9.3-linux-arm.tar.gz" -- Newer version because older version did not -- support installing ghc on arm64- ARM64 -> "https://github.com/commercialhaskell/stack/releases/download/v2.1.3/stack-2.1.3-linux-aarch64.tar.gz"+ ARM64 -> "https://github.com/commercialhaskell/stack/releases/download/v3.3.1/stack-3.3.1-linux-aarch64.tar.gz" -- Probably not available. a -> "https://www.stackage.org/stack/linux-" ++ architectureToDebianArchString a binstack = "/usr/bin/stack"
src/Propellor/Property/SiteSpecific/JoeySites.hs view
@@ -16,7 +16,6 @@ import qualified Propellor.Property.Service as Service import qualified Propellor.Property.User as User import qualified Propellor.Property.Group as Group-import qualified Propellor.Property.Sudo as Sudo import qualified Propellor.Property.Borg as Borg import qualified Propellor.Property.Apache as Apache import qualified Propellor.Property.Postfix as Postfix@@ -24,10 +23,8 @@ import qualified Propellor.Property.Network as Network import qualified Propellor.Property.Fail2Ban as Fail2Ban import qualified Propellor.Property.LetsEncrypt as LetsEncrypt-import qualified Propellor.Property.Mount as Mount import Utility.Split -import Data.List import System.Posix.Files kgbServer :: Property (HasInfo + DebianLike)@@ -60,7 +57,7 @@ & Apt.installed ["kgb-client"] & File.hasPrivContentExposed "/etc/kgb-bot/kgb-client.conf" anyContext `requires` File.dirExists "/etc/kgb-bot/"- & Git.daemonRunning "/srv/git"+ & Git.daemonService "/srv/git" & "/etc/cgitrc" `File.hasContent` [ "clone-url=https://git.joeyh.name/git/$CGIT_REPO_URL git://git.joeyh.name/$CGIT_REPO_URL" , "css=/cgit-css/cgit.css"@@ -216,38 +213,11 @@ where conf = "/home/znc/.znc/configs/znc.conf" -githubBackup :: Property (HasInfo + DebianLike)-githubBackup = propertyList "github-backup box" $ props- & Apt.installed ["github-backup", "moreutils"]- & githubKeys- & Cron.niceJob "github-backup run" (Cron.Times "30 4 * * *") (User "joey")- "/home/joey/lib/backup" backupcmd- where- backupcmd = intercalate "&&" $- [ "mkdir -p github"- , "cd github"- , ". $HOME/.github-keys"- , "github-backup joeyh"- ]--githubKeys :: Property (HasInfo + UnixLike)-githubKeys =- let f = "/home/joey/.github-keys"- in File.hasPrivContent f anyContext- `onChange` File.ownerGroup f (User "joey") (Group "joey")-- rsyncNetBackup :: [Host] -> Property DebianLike rsyncNetBackup hosts = Cron.niceJob "rsync.net copied in daily" (Cron.Times "30 5 * * *") (User "joey") "/home/joey/lib/backup" "mkdir -p rsync.net && rsync --delete -az 2318@usw-s002.rsync.net: rsync.net" `requires` Ssh.knownHost hosts "usw-s002.rsync.net" (User "joey") -podcatcher :: Property DebianLike-podcatcher = Cron.niceJob "podcatcher run hourly" (Cron.Times "55 * * * *")- (User "joey") "/home/joey/lib/sound/podcasts"- "timeout 2h xargs git-annex importfeed -c annex.genmetadata=true < feeds; mr --quiet update"- `requires` Apt.installed ["git-annex", "myrepos"]- spamdEnabled :: Property DebianLike spamdEnabled = Apt.serviceInstalledRunning "spamd" @@ -267,12 +237,19 @@ ] `describe` "spamassassin configured" `requires` Apt.serviceInstalledRunning "cron"+ & "/etc/spamassassin/local.cf" `File.containsLines`+ -- dnswl.org started intentionally sending false responses+ -- for spam senders when they don't like the amount of+ -- queries being made. This causes spamassassin to skip+ -- all other checks and miss flagging spam. This is+ -- irresponsible behavior, and so they should not be used.+ [ "dns_query_restriction deny dnswl.org"+ ] kiteMailServer :: Property (HasInfo + DebianLike) kiteMailServer = propertyList "kitenet.net mail server" $ props & Postfix.installed & Apt.installed ["postfix-pcre"]- & Apt.serviceInstalledRunning "postgrey" & spamassassinConfigured & Apt.serviceInstalledRunning "spamass-milter" -- Add -m to prevent modifying messages Subject or body.@@ -281,20 +258,6 @@ `onChange` Service.restarted "spamass-milter" `describe` "spamass-milter configured" - & Apt.serviceInstalledRunning "amavisd-milter"- & "/etc/default/amavisd-milter" `File.containsLines`- [ "# Propellor deployed"- , "MILTERSOCKET=/var/spool/postfix/amavis/amavis.sock"- , "MILTERSOCKETOWNER=\"postfix:postfix\""- , "MILTERSOCKETMODE=\"0660\""- ]- `onChange` Service.restarted "amavisd-milter"- `describe` "amavisd-milter configured for postfix"- & Apt.serviceInstalledRunning "clamav-freshclam"- -- Workaround https://bugs.debian.org/569150- & Cron.niceJob "amavis-expire" Cron.Daily (User "root") "/"- "find /var/lib/amavis/virusmails/ -type f -ctime +2 -delete"- & dkimInstalled & Postfix.saslAuthdInstalled@@ -309,11 +272,8 @@ [ "# Global maildrop filter file (deployed with propellor)" , "DEFAULT=\"$HOME/Maildir\"" , "MAILBOX=\"$DEFAULT/.\""- , "# Filter spam to a spam folder, unless .keepspam exists" , "if (/^X-Spam-Status: Yes/)" , "{"- , " `test -e \"$HOME/.keepspam\"`"- , " if ( $RETURNCODE != 0 )" , " to ${MAILBOX}spam" , "}" ]@@ -372,11 +332,11 @@ , "smtpd_sasl_security_options = noanonymous" , "smtpd_sasl_local_domain = kitenet.net" - , "# Enable postgrey and sasl auth and client certs."- , "smtpd_recipient_restrictions = permit_tls_clientcerts,permit_sasl_authenticated,,permit_mynetworks,reject_unauth_destination,check_policy_service inet:127.0.0.1:10023"+ , "# Enable sasl auth and client certs."+ , "smtpd_recipient_restrictions = permit_tls_clientcerts,permit_sasl_authenticated,,permit_mynetworks,reject_unauth_destination" - , "# Enable spamass-milter, amavis-milter (opendkim is not enabled because it causes mails forwarded from eg gmail to be rejected)"- , "smtpd_milters = unix:/spamass/spamass.sock unix:amavis/amavis.sock"+ , "# Enable spamass-milter (opendkim is not enabled because it causes mails forwarded from eg gmail to be rejected)"+ , "smtpd_milters = unix:/spamass/spamass.sock" , "# opendkim is used for outgoing mail" , "non_smtpd_milters = inet:localhost:8891" , "milter_connect_macros = j {daemon_name} v {if_name} _"@@ -389,7 +349,7 @@ , "smtpd_tls_key_file = /etc/ssl/private/postfix.pem" , "smtpd_tls_loglevel = 1" , "smtpd_tls_received_header = yes"- , "smtpd_use_tls = yes"+ , "smtpd_tls_security_level = may" , "smtpd_tls_ask_ccert = yes" , "smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache" @@ -398,7 +358,7 @@ , "smtp_tls_cert_file = /etc/ssl/certs/postfix.pem" , "smtp_tls_key_file = /etc/ssl/private/postfix.pem" , "smtp_tls_loglevel = 1"- , "smtp_use_tls = yes"+ , "smtp_tls_security_level = may" , "smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache" , "# Allow larger attachments, up to 200 mb."@@ -422,6 +382,14 @@ "!include auth-passwdfile.conf.ext" `onChange` Service.restarted "dovecot" `describe` "dovecot auth.conf"+ & "/etc/dovecot/conf.d/10-ssl.conf" `File.containsLines`+ [ "ssl_cert = </etc/letsencrypt/live/kitenet.net/fullchain.pem"+ , "ssl_key = </etc/letsencrypt/live/kitenet.net/privkey.pem"+ ]+ `onChange` Service.reloaded "dovecot"+ `describe` "dovecot ssl.conf using letsencrypt"+ & Cron.niceJob "dovecot restarts to update letsenctypt certificate"+ Cron.Daily (User "root") "/" "systemctl restart dovecot" & File.hasPrivContent dovecotusers ctx `onChange` (dovecotusers `File.mode` combineModes [ownerReadMode, groupReadMode])@@ -519,7 +487,7 @@ , "smtp_sasl_auth_enable = yes" , "smtp_tls_security_level = encrypt" , "smtp_sasl_tls_security_options = noanonymous"- , "relayhost = kitenet.net:587"+ , "relayhost = kitenet.net:465" , "smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd" ] `onChange` Postfix.reloaded@@ -607,7 +575,7 @@ , "rewriterule (.*) http://joeyh.name$1 [r]" ] & alias "house.joeyh.name"- & apacheSite "house.joeyh.name"+ & Apache.httpsVirtualHost' "house.joeyh.name" "/srv/web/house.joeyh.name" letos [ "DocumentRoot /srv/web/house.joeyh.name" , "<Directory /srv/web/house.joeyh.name>" , " Options Indexes ExecCGI"@@ -729,6 +697,29 @@ & "/etc/systemd/logind.conf" `ConfFile.containsIniSetting` ("Login", "LidSwitchIgnoreInhibited", "no") +epsolarUsbSetup :: Property UnixLike+epsolarUsbSetup = propertyList "epsolar usb setup" $ props+ & "/etc/udev/rules.d/99-epsolar-usb.rules" `File.hasContent`+ -- Each epever usb cable has its own serial number and gets+ -- its own stable device name.+ [ "SUBSYSTEM==\"tty\", ATTRS{idVendor}==\"04e2\", ATTRS{idProduct}==\"1411\", ATTRS{serial}==\"G9107376441\", SYMLINK+=\"epsolar1\""+ , "SUBSYSTEM==\"tty\", ATTRS{idVendor}==\"1a86\", ATTRS{idProduct}==\"55d3\", ATTRS{serial}==\"5876071260\", SYMLINK+=\"epsolar2\""+ , "SUBSYSTEM==\"tty\", ATTRS{idVendor}==\"1a86\", ATTRS{idProduct}==\"55d3\", ATTRS{serial}==\"5875017909\", SYMLINK+=\"epsolar3\""+ , "SUBSYSTEM==\"tty\", ATTRS{idVendor}==\"1a86\", ATTRS{idProduct}==\"55d3\", ATTRS{serial}==\"5876071261\", SYMLINK+=\"epsolar4\""+ , "SUBSYSTEM==\"tty\", ATTRS{idVendor}==\"1a86\", ATTRS{idProduct}==\"55d3\", ATTRS{serial}==\"5875017825\", SYMLINK+=\"epsolar5\""+ , "SUBSYSTEM==\"tty\", ATTRS{idVendor}==\"1a86\", ATTRS{idProduct}==\"55d3\", ATTRS{serial}==\"589A039325\", SYMLINK+=\"epsolar6\""+ ]+ -- The xr-serial module wants to handle the epever usb cables+ -- (at least older ones, new ones are handled by ACM), but+ -- it does not work in linux 6.1. Instead I am currently installing+ -- an out of tree module, xr_usb_serial_common. In linux 6.6,+ -- the xr-serial module has been reported to work,+ -- see https://github.com/kasbert/epsolar-tracer/pull/61#issuecomment-2056442735+ & "/etc/modprobe.d/blacklist.conf" `File.containsLines`+ [ "blacklist xr-serial"+ , "blacklist xr-serial"+ ]+ house :: IsContext c => User -> [Host] -> c -> (SshKeyType, Ssh.PubKeyText) -> Property (HasInfo + DebianLike) house user hosts ctx sshkey = propertyList "home automation" $ props & Apache.installed@@ -736,6 +727,7 @@ & Git.cloned user "https://git.joeyh.name/git/joey/house.git" d Nothing & websitesymlink & build+ & epsolarUsbSetup & Systemd.enabled setupservicename `requires` setupserviceinstalled `onChange` Systemd.started setupservicename@@ -767,7 +759,6 @@ user (userGroup user) `requires` File.dirExists (takeDirectory sshkeyfile) `requires` Ssh.knownHost hosts "kitenet.net" user- & File.hasPrivContentExposed "/etc/darksky-forecast-url" anyContext where d = "/home/joey/house" sshkeyfile = d </> ".ssh/key"@@ -804,7 +795,7 @@ , "" , "[Install]" , "WantedBy=multi-user.target"- , "WantedBy=house-controller.target"+ , "WantedBy=house-controller.service" ] controllerservicename = "house-controller" controllerservicefile = "/etc/systemd/system/" ++ controllerservicename ++ ".service"@@ -853,9 +844,9 @@ , "" , "[Install]" , "WantedBy=multi-user.target"- , "WantedBy=house-poller.target"- , "WantedBy=house-controller.target"- , "WantedBy=house-watchdog.target"+ , "WantedBy=house-poller.service"+ , "WantedBy=house-controller.service"+ , "WantedBy=house-watchdog.service" ] -- Any changes to the rsync command will need my .authorized_keys -- rsync server command to be updated too.@@ -878,16 +869,15 @@ connectStarlinkDish :: Interfaces -> Property DebianLike connectStarlinkDish ifs = propertyList "connected via starlink dish" $ props -- Use dhcpcd for ipv6 prefix delegation to the wifi interface.- & Apt.installed ["dhcpcd"]- -- dhcpcd is run by ifup on boot. When the daemon was enabled,- -- that somehow prevented prefix delegation from happening,- -- so disable the daemon from being run by systemd.- & Systemd.stopped "dhcpcd"- & Systemd.masked "dhcpcd"+ & Apt.installed ["dhcpcd-base"]+ & Apt.removed ["isc-dhcp-client"] & "/etc/dhcpcd.conf" `File.containsLine` ("ia_pd 1 " ++ wifiInterface ifs) & "/etc/dhcpcd.conf" `File.lacksLine` ("ia_pd 1 " ++ wifiInterfaceOld ifs)+ -- Avoid using dns servers sent by starlink.+ & "/etc/dhcpcd.conf" `File.containsLine`+ "nohook resolv.conf" & Network.dhcp (ethernetInterface ifs) `requires` Network.cleanInterfacesFile @@ -902,8 +892,8 @@ `requires` Network.cleanInterfacesFile -- My home router, running hostapd and dnsmasq.-homeRouter :: Interfaces -> String -> HostapdConfig -> Property DebianLike-homeRouter ifs wifinetworkname (HostapdConfig hostapdconfig) = propertyList "home router" $ props+homeRouter :: String -> Interfaces -> String -> HostapdConfig -> Property DebianLike+homeRouter hn ifs wifinetworkname (HostapdConfig hostapdconfig) = propertyList "home router" $ props & File.notPresent (Network.interfaceDFile (wifiInterfaceOld ifs)) & Network.static (wifiInterface ifs) (IPv4 "10.1.1.1") Nothing `requires` Network.cleanInterfacesFile@@ -916,8 +906,9 @@ `requires` File.hasContent "/etc/default/hostapd" [ "DAEMON_CONF=/etc/hostapd/hostapd.conf" ] `onChange` Service.running "hostapd"+ ! Systemd.masked "hostapd" & Systemd.enabled "hostapd"- & File.hasContent "/etc/resolv.conf"+ & File.hasContent "/etc/resolv.conf.head" [ "domain kitenet.net" , "search kitenet.net" , "nameserver 8.8.8.8"@@ -933,7 +924,7 @@ -- controller wants to know when clients disconnect , "dhcp-range=10.1.1.100,10.1.1.150,10m" , "no-hosts"- , "address=/sky.lan/10.1.1.1"+ , "address=/" ++ hn ++ "/10.1.1.1" , "address=/house.lan/10.1.1.2" -- allow accessing starlink dish when it's not online yet , "address=/dishy.starlink.com/192.168.100.1"@@ -960,7 +951,11 @@ , "};" ] `onChange` Service.restarted "radvd"- & "/etc/sysctl.conf" `File.containsLine` "net.ipv6.conf.all.forwarding=1"+ & "/etc/sysctl.d/ip-forwarding.conf" + `File.containsLine` "net.ipv6.conf.all.forwarding=1"+ -- Programs that I find useful on a router.+ & Apt.installed ["net-tools", "wireless-tools"]+ & Apt.installed ["mtr-tiny", "iftop", "screen", "nmap"] -- | Enable IP masqerading, on whatever other interfaces come up, besides the -- provided intif.@@ -987,11 +982,12 @@ laptopSoftware = Apt.installed [ "intel-microcode", "acpi" , "procmeter3", "xfce4", "procmeter3", "unclutter-xfixes"- , "mplayer", "fbreader", "firefox", "chromium"+ , "vlc", "mpv", "mplayer", "fbreader", "foliate", "firefox", "chromium"+ , "yt-dlp" , "libdatetime-event-sunrise-perl", "libtime-duration-perl" , "network-manager", "network-manager-openvpn-gnome", "openvpn"- , "gtk-redshift", "powertop"- , "gimp", "gthumb", "inkscape", "sozi", "xzgv", "hugin"+ , "powertop"+ , "gimp", "gthumb", "inkscape", "xzgv", "hugin" , "mpc", "mpd", "ncmpc", "sonata", "mpdtoys" , "bsdgames", "nethack-console" , "xmonad", "libghc-xmonad-dev", "libghc-xmonad-contrib-dev"@@ -999,23 +995,25 @@ , "mairix", "offlineimap", "mutt", "slrn" , "mtr", "nmap", "whois", "wireshark", "tcpdump", "iftop" , "pmount", "tree", "pv"- , "arbtt", "hledger", "bc"+ , "hledger", "bc" , "apache2", "ikiwiki", "libhighlight-perl" , "avahi-daemon", "avahi-discover"- , "pal"+ , "pal", "ncal" , "yeahconsole", "xkbset", "xinput" , "assword", "pumpa" , "vorbis-tools", "audacity"- , "ekiga"- , "bluez-firmware", "blueman", "pulseaudio-module-bluetooth"+ , "bluez-firmware", "blueman", "pulseaudio-module-bluetooth" + , "pulseaudio-utils" , "fwupd"- , "xul-ext-ublock-origin", "xul-ext-pdf.js", "xul-ext-status4evar"+ , "webext-ublock-origin-firefox", "webext-ublock-origin-chromium" , "vim-syntastic", "vim-fugitive" , "adb", "gthumb" , "w3m", "sm", "weechat" , "borgbackup", "wipe", "smartmontools", "libgfshare-bin" , "units"- , "virtualbox", "virtualbox-guest-additions-iso", "qemu-kvm"+ , "libnotify-bin"+ , "libmodbus-dev"+ , "qemu-kvm" ] `requires` baseSoftware `requires` devSoftware@@ -1023,17 +1021,17 @@ baseSoftware :: Property DebianLike baseSoftware = Apt.installed [ "bash", "bash-completion", "vim", "screen", "less", "moreutils"- , "git", "mr", "etckeeper", "git-annex", "ssh", "vim-vimoutliner"+ , "git", "mr", "etckeeper", "git-annex", "ssh" --, "vim-vimoutliner" ] devSoftware :: Property DebianLike devSoftware = Apt.installed [ "build-essential", "debhelper", "devscripts" , "ghc", "cabal-install", "haskell-stack"- , "hothasktags", "hdevtools", "hlint"+ , "hasktags", "hlint" , "gdb", "time" , "dpkg-repack", "lintian"- , "pristine-tar", "github-backup"+ , "pristine-tar" ] cubieTruckOneWire :: Property DebianLike@@ -1072,161 +1070,6 @@ , "};" ] --- My home networked attached storage server.-homeNAS :: Property DebianLike-homeNAS = propertyList "home NAS" $ props- & Apt.installed ["uhubctl"]- & "/etc/udev/rules.d/52-startech-hub.rules" `File.hasContent`- [ "# let users power control startech hub with uhubctl"- , "ATTR{idVendor}==\"" ++ hubvendor ++ "\", ATTR{idProduct}==\"005a\", MODE=\"0666\""- ]- & autoMountDrivePort "archive-10"- (USBHubPort hubvendor 1)- (USBDriveId wd "1230")- (Just "archive-oldest")- & autoMountDrivePort "archive-11"- (USBHubPort hubvendor 2)- (USBDriveId wd "25ee")- (Just "archive-older")- & autoMountDrivePort "archive-12"- (USBHubPort hubvendor 3)- (USBDriveId seagate "3322")- (Just "archive-old")- & autoMountDrivePort "archive-13"- (USBHubPort hubvendor 4)- (USBDriveId wd "25a3")- (Just "archive")- & autoMountDrivePort "archive-14"- (USBHubPort hubvendor 2)- (USBDriveId wd "25a3")- Nothing- & autoMountDrive "passport" Nothing- & Apt.installed ["git-annex", "borgbackup"]- where- hubvendor = "0409"- wd = "1058"- seagate = "0bc2"--data USBHubPort = USBHubPort- { hubVendor :: String- , hubPort :: Int- }--data USBDriveId = USBDriveId- { driveVendorId :: String- , driveProductId :: String- }---- Makes a USB drive with the given label automount, and unmount after idle--- for a while.------ The hub port is turned on and off automatically as needed, using--- uhubctl.-autoMountDrivePort :: Mount.Label -> USBHubPort -> USBDriveId -> Maybe FilePath -> Property DebianLike-autoMountDrivePort label hp drive malias = propertyList desc $ props- & File.hasContent ("/etc/systemd/system/" ++ hub)- [ "[Unit]"- , "Description=Startech usb hub port " ++ show (hubPort hp) ++ " vendor " ++ driveVendorId drive ++ " driveid " ++ driveProductId drive- , "PartOf=" ++ mount- , "[Service]"- , "Type=oneshot"- , "RemainAfterExit=true"- , "ExecStart=/bin/sh -c 'uhubctl -a on " ++ selecthubport ++ "'"- , "ExecStop=/bin/sh -c 'uhubctl -a off " ++ selecthubport- -- Powering off the port does not remove device- -- files, so ask udev to remove the devfile; it will- -- be added back after the drive next spins up- -- and so avoid mount happening before the drive is- -- spun up.- -- (This only works when the devfile is in- -- by-label.)- ++ "; udevadm trigger --action=remove " ++ devfile ++ " || true'"- , "[Install]"- , "WantedBy="- ]- `onChange` Systemd.daemonReloaded- & autoMountDrive' - [ "Requires=" ++ hub- , "After=" ++ hub- ] label malias- where- devfile = "/dev/disk/by-label/" ++ label- mountpoint = "/media/joey/" ++ label- desc = "auto mount with hub port power control " ++ mountpoint- hub = "startech-hub-port-" ++ show (hubPort hp) ++ "-vendor-" ++ driveVendorId drive ++ "-drivedid-" ++ driveProductId drive ++ ".service"- mount = svcbase ++ ".mount"- svcbase = Systemd.escapePath mountpoint- selecthubport = unwords- [ "-p", show (hubPort hp)- , "-n", hubVendor hp- , "-l", concat- -- The hub's location id, eg "1-1.4", does not seem- -- as stable as uhubctl claims it will be,- -- and the vendor is not sufficient since I have 2- -- hubs from the same vendor. So search for the- -- drive lsusb to find that. This works even if the- -- port is powered off, as long as it's been on at- -- some point before.- [ "$(lsusb -tvv | perl -lne \"if (\\\\$h && m!/sys/bus/usb/devices/(.*?) !) {\\\\$v=\\\\$1}; if (m/Hub/) { \\\\$h=1 } else { \\\\$h=0 }; if (/"- , driveVendorId drive ++ ":" ++ driveProductId drive- ++ "/) { print \\\\$v; last}\")"- ]- ]---- Makes a USB drive with the given label automount, and unmount after idle--- for a while.-autoMountDrive :: Mount.Label -> Maybe FilePath -> Property DebianLike-autoMountDrive = autoMountDrive' []--autoMountDrive' :: [String] -> Mount.Label -> Maybe FilePath -> Property DebianLike-autoMountDrive' mountunitadd label malias = propertyList desc $ props- & File.ownerGroup mountpoint (User "joey") (Group "joey")- `requires` File.dirExists mountpoint- & case malias of- Just t -> ("/media/joey/" ++ t) `File.isSymlinkedTo`- File.LinkTarget mountpoint- Nothing -> doNothing <!> doNothing- & File.hasContent ("/etc/systemd/system/" ++ mount)- ([ "[Unit]"- , "Description=" ++ label- ] ++ mountunitadd ++- [ "[Mount]"- -- avoid mounting whenever the block device is available,- -- only want to automount on demand- , "Options=noauto"- , "What=" ++ devfile- , "Where=" ++ mountpoint- , "[Install]"- , "WantedBy="- ])- `onChange` Systemd.daemonReloaded- & File.hasContent ("/etc/systemd/system/" ++ automount)- [ "[Unit]"- , "Description=Automount " ++ label- , "[Automount]"- , "Where=" ++ mountpoint- , "TimeoutIdleSec=300"- , "[Install]"- , "WantedBy=multi-user.target"- ]- `onChange` Systemd.daemonReloaded- & Systemd.enabled automount- & Systemd.started automount- & Sudo.sudoersDFile ("automount-" ++ label)- [ "joey ALL= NOPASSWD: " ++ sudocommands- ]- where- devfile = "/dev/disk/by-label/" ++ label- mountpoint = "/media/joey/" ++ label- desc = "auto mount " ++ mountpoint- automount = svcbase ++ ".automount"- mount = svcbase ++ ".mount"- svcbase = Systemd.escapePath mountpoint- sudocommands = intercalate " , " $ map (\c -> "/bin/systemctl " ++ c)- [ "stop " ++ mountpoint- , "start " ++ mountpoint- ]- rsyncNetBorgRepo :: String -> [Borg.BorgRepoOpt] -> Borg.BorgRepo rsyncNetBorgRepo d os = Borg.BorgRepoUsing os' ("2318@usw-s002.rsync.net:" ++ d) where@@ -1252,7 +1095,7 @@ -- For wifi adapters such as the Alfa AWUS036ACHM -- -- Note that for maximum speed, this needs channel 5 or 6.--- This should make it be capable of 150 Mb/s.+-- This should make it be capable of 300 Mb/s. hostapd2GhzConfig_mt76 :: HostapdConfig hostapd2GhzConfig_mt76 = HostapdConfig $ c ++ [ "ht_capab=[HT40+][HT40-][GF][SHORT-GI-20][SHORT-GI-40]"@@ -1270,4 +1113,18 @@ , "ieee80211ac=1" , "wmm_enabled=1" ]++thinkPadLedsOff :: Property Linux+thinkPadLedsOff =+ "/etc/systemd/system/thinkpad_leds_off.service" `File.hasContent`+ [ "[Unit]"+ , "After=suspend.target"+ , "[Service]"+ , "Type=simple"+ , "ExecStart=sh -c 'echo 0 > /sys/devices/platform/thinkpad_acpi/leds/tpacpi::lid_logo_dot/brightness; echo 0 > /sys/devices/platform/thinkpad_acpi/leds/tpacpi::power/brightness'"+ , "[Install]"+ , "WantedBy=suspend.target"+ ]+ `onChange` (Systemd.daemonReloaded+ `before` Systemd.enabled "thinkpad_leds_off")
src/Propellor/Property/Systemd.hs view
@@ -314,6 +314,12 @@ , "--boot" , "--directory=" ++ containerDir name , "--machine=" ++ name+ -- While propellChroot bind mounts the propellor directory,+ -- when systemd stops and then restarts the machine, it+ -- will mask any existing bind mounts to the directory.+ -- So, tell systemd to bind mount the propellor directory+ -- as well.+ , "--bind=" ++ localdir ] ++ nspawnServiceParams cfg overrideconfigured = File.hasContent overridefile overridecontent
src/Propellor/Types/OS.hs view
@@ -59,7 +59,7 @@ deriving (Show, Eq) -- | Debian has several rolling suites, and a number of stable releases,--- such as Stable "bookworm".+-- such as Stable "trixie". data DebianSuite = Experimental | Unstable | Testing | Stable Release deriving (Show, Eq)