Compare commits
6 Commits
bootstra-n
...
issue-751
| Author | SHA1 | Date | |
|---|---|---|---|
|
72f8e53344
|
|||
|
9c464ec9fc
|
|||
|
f575dcdad6
|
|||
|
6d3e8d65e1
|
|||
|
895e4b3f18
|
|||
|
20f0505120
|
4
.github/scripts/test.sh
vendored
4
.github/scripts/test.sh
vendored
@@ -190,7 +190,7 @@ sha=$(sha_sum "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml")
|
|||||||
# invalidate access time timer, which is 5minutes, so we re-download
|
# invalidate access time timer, which is 5minutes, so we re-download
|
||||||
touch -a -m -t '199901010101' "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml"
|
touch -a -m -t '199901010101' "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml"
|
||||||
# redownload same file with some newlines added
|
# redownload same file with some newlines added
|
||||||
raw_eghcup -s https://www.haskell.org/ghcup/exp/ghcup-${JSON_VERSION}.yaml list
|
raw_eghcup -s https://raw.githubusercontent.com/haskell/ghcup-metadata/exp/ghcup-0.0.7.yaml list
|
||||||
# snapshot new yaml and etags file
|
# snapshot new yaml and etags file
|
||||||
etag2=$(cat "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml.etags")
|
etag2=$(cat "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml.etags")
|
||||||
sha2=$(sha_sum "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml")
|
sha2=$(sha_sum "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml")
|
||||||
@@ -200,7 +200,7 @@ sha2=$(sha_sum "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml")
|
|||||||
# invalidate access time timer, which is 5minutes, but don't expect a re-download
|
# invalidate access time timer, which is 5minutes, but don't expect a re-download
|
||||||
touch -a -m -t '199901010101' "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml"
|
touch -a -m -t '199901010101' "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml"
|
||||||
# this time, we expect the same hash and etag
|
# this time, we expect the same hash and etag
|
||||||
raw_eghcup -s https://www.haskell.org/ghcup/exp/ghcup-${JSON_VERSION}.yaml list
|
raw_eghcup -s https://raw.githubusercontent.com/haskell/ghcup-metadata/exp/ghcup-0.0.7.yaml list
|
||||||
etag3=$(cat "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml.etags")
|
etag3=$(cat "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml.etags")
|
||||||
sha3=$(sha_sum "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml")
|
sha3=$(sha_sum "${GHCUP_DIR}/cache/ghcup-${JSON_VERSION}.yaml")
|
||||||
[ "${etag2}" = "${etag3}" ]
|
[ "${etag2}" = "${etag3}" ]
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ data ConfigCommand
|
|||||||
= ShowConfig
|
= ShowConfig
|
||||||
| SetConfig String (Maybe String)
|
| SetConfig String (Maybe String)
|
||||||
| InitConfig
|
| InitConfig
|
||||||
| AddReleaseChannel URI
|
| AddReleaseChannel Bool URI
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ data ConfigCommand
|
|||||||
--[ Parsers ]--
|
--[ Parsers ]--
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
|
||||||
configP :: Parser ConfigCommand
|
configP :: Parser ConfigCommand
|
||||||
configP = subparser
|
configP = subparser
|
||||||
( command "init" initP
|
( command "init" initP
|
||||||
@@ -74,7 +74,7 @@ configP = subparser
|
|||||||
showP = info (pure ShowConfig) (progDesc "Show current config (default)")
|
showP = info (pure ShowConfig) (progDesc "Show current config (default)")
|
||||||
setP = info argsP (progDesc "Set config KEY to VALUE (or specify as single json value)" <> footerDoc (Just $ text configSetFooter))
|
setP = info argsP (progDesc "Set config KEY to VALUE (or specify as single json value)" <> footerDoc (Just $ text configSetFooter))
|
||||||
argsP = SetConfig <$> argument str (metavar "<JSON_VALUE | YAML_KEY>") <*> optional (argument str (metavar "YAML_VALUE"))
|
argsP = SetConfig <$> argument str (metavar "<JSON_VALUE | YAML_KEY>") <*> optional (argument str (metavar "YAML_VALUE"))
|
||||||
addP = info (AddReleaseChannel <$> argument (eitherReader uriParser) (metavar "URI" <> completer fileUri))
|
addP = info (AddReleaseChannel <$> switch (long "force" <> help "Delete existing entry (if any) and append instead of failing") <*> argument (eitherReader uriParser) (metavar "URI" <> completer fileUri))
|
||||||
(progDesc "Add a release channel from a URI")
|
(progDesc "Add a release channel from a URI")
|
||||||
|
|
||||||
|
|
||||||
@@ -120,21 +120,38 @@ formatConfig :: UserSettings -> String
|
|||||||
formatConfig = UTF8.toString . Y.encode
|
formatConfig = UTF8.toString . Y.encode
|
||||||
|
|
||||||
|
|
||||||
updateSettings :: UserSettings -> Settings -> Settings
|
updateSettings :: UserSettings -> UserSettings -> UserSettings
|
||||||
updateSettings UserSettings{..} Settings{..} =
|
updateSettings usl usr =
|
||||||
let cache' = fromMaybe cache uCache
|
let cache' = uCache usl <|> uCache usr
|
||||||
metaCache' = fromMaybe metaCache uMetaCache
|
metaCache' = uMetaCache usl <|> uMetaCache usr
|
||||||
metaMode' = fromMaybe metaMode uMetaMode
|
metaMode' = uMetaMode usl <|> uMetaMode usr
|
||||||
noVerify' = fromMaybe noVerify uNoVerify
|
noVerify' = uNoVerify usl <|> uNoVerify usr
|
||||||
keepDirs' = fromMaybe keepDirs uKeepDirs
|
verbose' = uVerbose usl <|> uVerbose usr
|
||||||
downloader' = fromMaybe downloader uDownloader
|
keepDirs' = uKeepDirs usl <|> uKeepDirs usr
|
||||||
verbose' = fromMaybe verbose uVerbose
|
downloader' = uDownloader usl <|> uDownloader usr
|
||||||
urlSource' = fromMaybe urlSource uUrlSource
|
urlSource' = uUrlSource usl <|> uUrlSource usr
|
||||||
noNetwork' = fromMaybe noNetwork uNoNetwork
|
noNetwork' = uNoNetwork usl <|> uNoNetwork usr
|
||||||
gpgSetting' = fromMaybe gpgSetting uGPGSetting
|
gpgSetting' = uGPGSetting usl <|> uGPGSetting usr
|
||||||
platformOverride' = uPlatformOverride <|> platformOverride
|
platformOverride' = uPlatformOverride usl <|> uPlatformOverride usr
|
||||||
mirrors' = fromMaybe mirrors uMirrors
|
mirrors' = uMirrors usl <|> uMirrors usr
|
||||||
in Settings cache' metaCache' metaMode' noVerify' keepDirs' downloader' verbose' urlSource' noNetwork' gpgSetting' noColor platformOverride' mirrors'
|
in UserSettings cache' metaCache' metaMode' noVerify' verbose' keepDirs' downloader' (updateKeyBindings (uKeyBindings usl) (uKeyBindings usr)) urlSource' noNetwork' gpgSetting' platformOverride' mirrors'
|
||||||
|
where
|
||||||
|
updateKeyBindings :: Maybe UserKeyBindings -> Maybe UserKeyBindings -> Maybe UserKeyBindings
|
||||||
|
updateKeyBindings Nothing Nothing = Nothing
|
||||||
|
updateKeyBindings (Just kbl) Nothing = Just kbl
|
||||||
|
updateKeyBindings Nothing (Just kbr) = Just kbr
|
||||||
|
updateKeyBindings (Just kbl) (Just kbr) =
|
||||||
|
Just $ UserKeyBindings {
|
||||||
|
kUp = kUp kbl <|> kUp kbr
|
||||||
|
, kDown = kDown kbl <|> kDown kbr
|
||||||
|
, kQuit = kQuit kbl <|> kQuit kbr
|
||||||
|
, kInstall = kInstall kbl <|> kInstall kbr
|
||||||
|
, kUninstall = kUninstall kbl <|> kUninstall kbr
|
||||||
|
, kSet = kSet kbl <|> kSet kbr
|
||||||
|
, kChangelog = kChangelog kbl <|> kChangelog kbr
|
||||||
|
, kShowAll = kShowAll kbl <|> kShowAll kbr
|
||||||
|
, kShowAllTools = kShowAllTools kbl <|> kShowAllTools kbr
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -142,6 +159,9 @@ updateSettings UserSettings{..} Settings{..} =
|
|||||||
--[ Entrypoint ]--
|
--[ Entrypoint ]--
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
data Duplicate = Duplicate -- ^ there is a duplicate somewhere in the middle
|
||||||
|
| NoDuplicate -- ^ there is no duplicate
|
||||||
|
| DuplicateLast -- ^ there's a duplicate, but it's the last element
|
||||||
|
|
||||||
|
|
||||||
config :: forall m. ( Monad m
|
config :: forall m. ( Monad m
|
||||||
@@ -151,10 +171,11 @@ config :: forall m. ( Monad m
|
|||||||
)
|
)
|
||||||
=> ConfigCommand
|
=> ConfigCommand
|
||||||
-> Settings
|
-> Settings
|
||||||
|
-> UserSettings
|
||||||
-> KeyBindings
|
-> KeyBindings
|
||||||
-> (ReaderT LeanAppState m () -> m ())
|
-> (ReaderT LeanAppState m () -> m ())
|
||||||
-> m ExitCode
|
-> m ExitCode
|
||||||
config configCommand settings keybindings runLogger = case configCommand of
|
config configCommand settings userConf keybindings runLogger = case configCommand of
|
||||||
InitConfig -> do
|
InitConfig -> do
|
||||||
path <- getConfigFilePath
|
path <- getConfigFilePath
|
||||||
liftIO $ writeFile path $ formatConfig $ fromSettings settings (Just keybindings)
|
liftIO $ writeFile path $ formatConfig $ fromSettings settings (Just keybindings)
|
||||||
@@ -185,27 +206,55 @@ config configCommand settings keybindings runLogger = case configCommand of
|
|||||||
pure $ ExitFailure 65
|
pure $ ExitFailure 65
|
||||||
VLeft _ -> pure $ ExitFailure 65
|
VLeft _ -> pure $ ExitFailure 65
|
||||||
|
|
||||||
AddReleaseChannel uri -> do
|
AddReleaseChannel force uri -> do
|
||||||
case urlSource settings of
|
r <- runE @'[DuplicateReleaseChannel] $ do
|
||||||
AddSource xs -> do
|
case urlSource settings of
|
||||||
doConfig (defaultUserSettings { uUrlSource = Just $ AddSource (xs <> [Right uri]) })
|
AddSource xs -> do
|
||||||
pure ExitSuccess
|
case checkDuplicate xs (Right uri) of
|
||||||
GHCupURL -> do
|
Duplicate
|
||||||
doConfig (defaultUserSettings { uUrlSource = Just $ AddSource [Right uri] })
|
| not force -> throwE (DuplicateReleaseChannel uri)
|
||||||
pure ExitSuccess
|
DuplicateLast -> pure ()
|
||||||
OwnSource xs -> do
|
_ -> lift $ doConfig (defaultUserSettings { uUrlSource = Just $ AddSource (appendUnique xs (Right uri)) })
|
||||||
doConfig (defaultUserSettings { uUrlSource = Just $ OwnSource (xs <> [Right uri]) })
|
GHCupURL -> do
|
||||||
pure ExitSuccess
|
lift $ doConfig (defaultUserSettings { uUrlSource = Just $ AddSource [Right uri] })
|
||||||
OwnSpec spec -> do
|
pure ()
|
||||||
doConfig (defaultUserSettings { uUrlSource = Just $ OwnSource ([Left spec, Right uri]) })
|
OwnSource xs -> do
|
||||||
|
case checkDuplicate xs (Right uri) of
|
||||||
|
Duplicate
|
||||||
|
| not force -> throwE (DuplicateReleaseChannel uri)
|
||||||
|
DuplicateLast -> pure ()
|
||||||
|
_ -> lift $ doConfig (defaultUserSettings { uUrlSource = Just $ OwnSource (appendUnique xs (Right uri)) })
|
||||||
|
OwnSpec spec -> do
|
||||||
|
lift $ doConfig (defaultUserSettings { uUrlSource = Just $ OwnSource [Left spec, Right uri] })
|
||||||
|
pure ()
|
||||||
|
case r of
|
||||||
|
VRight _ -> do
|
||||||
pure ExitSuccess
|
pure ExitSuccess
|
||||||
|
VLeft e -> do
|
||||||
|
runLogger $ logError $ T.pack $ prettyHFError e
|
||||||
|
pure $ ExitFailure 15
|
||||||
|
|
||||||
where
|
where
|
||||||
|
checkDuplicate :: Eq a => [a] -> a -> Duplicate
|
||||||
|
checkDuplicate xs a
|
||||||
|
| last xs == a = DuplicateLast
|
||||||
|
| a `elem` xs = Duplicate
|
||||||
|
| otherwise = NoDuplicate
|
||||||
|
|
||||||
|
-- appends the element to the end of the list, but also removes it from the existing list
|
||||||
|
appendUnique :: Eq a => [a] -> a -> [a]
|
||||||
|
appendUnique xs' e = go xs'
|
||||||
|
where
|
||||||
|
go [] = [e]
|
||||||
|
go (x:xs)
|
||||||
|
| x == e = go xs -- skip
|
||||||
|
| otherwise = x : go xs
|
||||||
|
|
||||||
doConfig :: MonadIO m => UserSettings -> m ()
|
doConfig :: MonadIO m => UserSettings -> m ()
|
||||||
doConfig usersettings = do
|
doConfig usersettings = do
|
||||||
let settings' = updateSettings usersettings settings
|
let settings' = updateSettings usersettings userConf
|
||||||
path <- liftIO getConfigFilePath
|
path <- liftIO getConfigFilePath
|
||||||
liftIO $ writeFile path $ formatConfig $ fromSettings settings' (Just keybindings)
|
liftIO $ writeFile path $ formatConfig $ settings'
|
||||||
runLogger $ logDebug $ T.pack $ show settings'
|
runLogger $ logDebug $ T.pack $ show settings'
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ import qualified GHCup.Types as Types
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
toSettings :: Options -> IO (Settings, KeyBindings)
|
toSettings :: Options -> IO (Settings, KeyBindings, UserSettings)
|
||||||
toSettings options = do
|
toSettings options = do
|
||||||
noColor <- isJust <$> lookupEnv "NO_COLOR"
|
noColor <- isJust <$> lookupEnv "NO_COLOR"
|
||||||
userConf <- runE @'[ JSONError ] ghcupConfigFile >>= \case
|
userConf <- runE @'[ JSONError ] ghcupConfigFile >>= \case
|
||||||
@@ -73,7 +73,7 @@ toSettings options = do
|
|||||||
pure defaultUserSettings
|
pure defaultUserSettings
|
||||||
_ -> do
|
_ -> do
|
||||||
die "Unexpected error!"
|
die "Unexpected error!"
|
||||||
pure $ mergeConf options userConf noColor
|
pure $ (\(s', k) -> (s', k, userConf)) $ mergeConf options userConf noColor
|
||||||
where
|
where
|
||||||
mergeConf :: Options -> UserSettings -> Bool -> (Settings, KeyBindings)
|
mergeConf :: Options -> UserSettings -> Bool -> (Settings, KeyBindings)
|
||||||
mergeConf Options{..} UserSettings{..} noColor =
|
mergeConf Options{..} UserSettings{..} noColor =
|
||||||
@@ -176,7 +176,7 @@ Report bugs at <https://github.com/haskell/ghcup-hs/issues>|]
|
|||||||
-- create ~/.ghcup dir
|
-- create ~/.ghcup dir
|
||||||
ensureDirectories dirs
|
ensureDirectories dirs
|
||||||
|
|
||||||
(settings, keybindings) <- toSettings opt
|
(settings, keybindings, userConf) <- toSettings opt
|
||||||
|
|
||||||
-- logger interpreter
|
-- logger interpreter
|
||||||
logfile <- runReaderT initGHCupFileLogging dirs
|
logfile <- runReaderT initGHCupFileLogging dirs
|
||||||
@@ -303,7 +303,7 @@ Report bugs at <https://github.com/haskell/ghcup-hs/issues>|]
|
|||||||
Rm rmCommand -> rm rmCommand runAppState runLogger
|
Rm rmCommand -> rm rmCommand runAppState runLogger
|
||||||
DInfo -> dinfo runAppState runLogger
|
DInfo -> dinfo runAppState runLogger
|
||||||
Compile compileCommand -> compile compileCommand settings dirs runAppState runLogger
|
Compile compileCommand -> compile compileCommand settings dirs runAppState runLogger
|
||||||
Config configCommand -> config configCommand settings keybindings runLogger
|
Config configCommand -> config configCommand settings userConf keybindings runLogger
|
||||||
Whereis whereisOptions
|
Whereis whereisOptions
|
||||||
whereisCommand -> whereis whereisCommand whereisOptions runAppState leanAppstate runLogger
|
whereisCommand -> whereis whereisCommand whereisOptions runAppState leanAppstate runLogger
|
||||||
Upgrade uOpts force' fatal -> upgrade uOpts force' fatal dirs runAppState runLogger
|
Upgrade uOpts force' fatal -> upgrade uOpts force' fatal dirs runAppState runLogger
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ import URI.ByteString
|
|||||||
|
|
||||||
import qualified Data.Map.Strict as M
|
import qualified Data.Map.Strict as M
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Text.Encoding as E
|
||||||
|
import qualified Data.Text.Encoding.Error as E
|
||||||
import Data.Data (Proxy(..))
|
import Data.Data (Proxy(..))
|
||||||
|
|
||||||
|
|
||||||
@@ -82,6 +84,7 @@ allHFError = unlines allErrors
|
|||||||
, let proxy = Proxy :: Proxy HadrianNotFound in format proxy
|
, let proxy = Proxy :: Proxy HadrianNotFound in format proxy
|
||||||
, let proxy = Proxy :: Proxy ToolShadowed in format proxy
|
, let proxy = Proxy :: Proxy ToolShadowed in format proxy
|
||||||
, let proxy = Proxy :: Proxy ContentLengthError in format proxy
|
, let proxy = Proxy :: Proxy ContentLengthError in format proxy
|
||||||
|
, let proxy = Proxy :: Proxy DuplicateReleaseChannel in format proxy
|
||||||
, ""
|
, ""
|
||||||
, "# high level errors (4000+)"
|
, "# high level errors (4000+)"
|
||||||
, let proxy = Proxy :: Proxy DownloadFailed in format proxy
|
, let proxy = Proxy :: Proxy DownloadFailed in format proxy
|
||||||
@@ -640,6 +643,19 @@ instance HFErrorProject ContentLengthError where
|
|||||||
eBase _ = 340
|
eBase _ = 340
|
||||||
eDesc _ = "File content length verification failed"
|
eDesc _ = "File content length verification failed"
|
||||||
|
|
||||||
|
data DuplicateReleaseChannel = DuplicateReleaseChannel URI
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
instance HFErrorProject DuplicateReleaseChannel where
|
||||||
|
eBase _ = 350
|
||||||
|
eDesc _ = "Duplicate release channel detected when adding URI.\nGiving up. You can use '--force' to remove and append the duplicate URI (this may change order/semantics)."
|
||||||
|
|
||||||
|
instance Pretty DuplicateReleaseChannel where
|
||||||
|
pPrint (DuplicateReleaseChannel uri) =
|
||||||
|
text $ "Duplicate release channel detected when adding: \n "
|
||||||
|
<> (T.unpack . E.decodeUtf8With E.lenientDecode . serializeURIRef') uri
|
||||||
|
<> "\nGiving up. You can use '--force' to remove and append the duplicate URI (this may change order/semantics)."
|
||||||
|
|
||||||
-------------------------
|
-------------------------
|
||||||
--[ High-level errors ]--
|
--[ High-level errors ]--
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|||||||
@@ -117,7 +117,15 @@ readDirEntPortable :: DirStreamPortable -> IO (DirType, FilePath)
|
|||||||
readDirEntPortable (DirStreamPortable (basedir, dirs)) = do
|
readDirEntPortable (DirStreamPortable (basedir, dirs)) = do
|
||||||
(dt, fp) <- readDirEnt dirs
|
(dt, fp) <- readDirEnt dirs
|
||||||
case (dt, fp) of
|
case (dt, fp) of
|
||||||
(DirType #{const DT_UNKNOWN}, _)
|
(DirType #{const DT_BLK}, _) -> pure (dt, fp)
|
||||||
|
(DirType #{const DT_CHR}, _) -> pure (dt, fp)
|
||||||
|
(DirType #{const DT_DIR}, _) -> pure (dt, fp)
|
||||||
|
(DirType #{const DT_FIFO}, _) -> pure (dt, fp)
|
||||||
|
(DirType #{const DT_LNK}, _) -> pure (dt, fp)
|
||||||
|
(DirType #{const DT_REG}, _) -> pure (dt, fp)
|
||||||
|
(DirType #{const DT_SOCK}, _) -> pure (dt, fp)
|
||||||
|
(DirType #{const DT_UNKNOWN}, _) -> pure (dt, fp)
|
||||||
|
(_, _)
|
||||||
| fp /= "" -> do
|
| fp /= "" -> do
|
||||||
stat <- getSymbolicLinkStatus (basedir </> fp)
|
stat <- getSymbolicLinkStatus (basedir </> fp)
|
||||||
pure $ (, fp) $ if | isBlockDevice stat -> DirType #{const DT_BLK}
|
pure $ (, fp) $ if | isBlockDevice stat -> DirType #{const DT_BLK}
|
||||||
@@ -128,5 +136,4 @@ readDirEntPortable (DirStreamPortable (basedir, dirs)) = do
|
|||||||
| isRegularFile stat -> DirType #{const DT_REG}
|
| isRegularFile stat -> DirType #{const DT_REG}
|
||||||
| isSocket stat -> DirType #{const DT_SOCK}
|
| isSocket stat -> DirType #{const DT_SOCK}
|
||||||
| otherwise -> DirType #{const DT_UNKNOWN}
|
| otherwise -> DirType #{const DT_UNKNOWN}
|
||||||
_ -> pure (dt, fp)
|
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ data GHCupInfo = GHCupInfo
|
|||||||
, _ghcupDownloads :: GHCupDownloads
|
, _ghcupDownloads :: GHCupDownloads
|
||||||
, _globalTools :: Map GlobalTool DownloadInfo
|
, _globalTools :: Map GlobalTool DownloadInfo
|
||||||
}
|
}
|
||||||
deriving (Show, GHC.Generic)
|
deriving (Show, GHC.Generic, Eq)
|
||||||
|
|
||||||
instance NFData GHCupInfo
|
instance NFData GHCupInfo
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ data Requirements = Requirements
|
|||||||
{ _distroPKGs :: [Text]
|
{ _distroPKGs :: [Text]
|
||||||
, _notes :: Text
|
, _notes :: Text
|
||||||
}
|
}
|
||||||
deriving (Show, GHC.Generic)
|
deriving (Show, GHC.Generic, Eq)
|
||||||
|
|
||||||
instance NFData Requirements
|
instance NFData Requirements
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user