fixes extraction logic, found a bug in System.Process

This commit is contained in:
Keagan McClelland
2021-09-28 20:19:32 -06:00
parent 427c31c886
commit 37c9a2bf6f
2 changed files with 67 additions and 18 deletions

View File

@@ -10,7 +10,11 @@
module Lib.External.AppMgr where module Lib.External.AppMgr where
import Startlude hiding ( catch ) import Startlude hiding ( bracket
, catch
, finally
, handle
)
import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Lazy as LBS
import Data.String.Interpolate.IsString import Data.String.Interpolate.IsString
@@ -22,11 +26,25 @@ import Conduit ( (.|)
) )
import qualified Data.Conduit.List as CL import qualified Data.Conduit.List as CL
import Data.Conduit.Process.Typed import Data.Conduit.Process.Typed
import GHC.IO.Exception ( IOErrorType(NoSuchThing)
, IOException
( IOError
, ioe_description
, ioe_errno
, ioe_filename
, ioe_handle
, ioe_location
, ioe_type
)
)
import Lib.Error import Lib.Error
import System.FilePath ( (</>) ) import System.FilePath ( (</>) )
import UnliftIO ( MonadUnliftIO import UnliftIO ( MonadUnliftIO
, catch , catch
) )
import UnliftIO ( bracket )
import UnliftIO ( finally )
import UnliftIO.Exception ( handle )
readProcessWithExitCode' :: MonadIO m => String -> [String] -> ByteString -> m (ExitCode, ByteString, ByteString) readProcessWithExitCode' :: MonadIO m => String -> [String] -> ByteString -> m (ExitCode, ByteString, ByteString)
readProcessWithExitCode' a b c = liftIO $ do readProcessWithExitCode' a b c = liftIO $ do
@@ -41,45 +59,70 @@ readProcessWithExitCode' a b c = liftIO $ do
(LBS.toStrict <$> getStdout process) (LBS.toStrict <$> getStdout process)
(LBS.toStrict <$> getStderr process) (LBS.toStrict <$> getStderr process)
readProcessInheritStderr :: MonadUnliftIO m readProcessInheritStderr :: forall m a
. MonadUnliftIO m
=> String => String
-> [String] -> [String]
-> ByteString -> ByteString
-> (ConduitT () ByteString m () -> m a) -- this is because we can't clean up the process in the unCPS'ed version of this -> (ConduitT () ByteString m () -> m a) -- this is because we can't clean up the process in the unCPS'ed version of this
-> m a -> m a
readProcessInheritStderr a b c sink = do readProcessInheritStderr a b c sink = handle help $ do
let pc = let pc =
setStdin (byteStringInput $ LBS.fromStrict c) setStdin (byteStringInput $ LBS.fromStrict c)
$ setEnvInherit $ setEnvInherit
$ setStderr (useHandleOpen stderr)
$ setStdout createSource $ setStdout createSource
$ System.Process.Typed.proc a b $ System.Process.Typed.proc a b
withProcessTerm_ pc $ \p -> sink (getStdout p) withProcessTerm' pc $ \p -> sink (getStdout p)
where
withProcessTerm' :: (MonadUnliftIO m)
=> ProcessConfig stdin stdout stderr
-> (Process stdin stdout stderr -> m a)
-> m a
withProcessTerm' cfg = bracket (startProcess cfg) $ \p -> do
stopProcess p
`catch` (\e -> if ioe_type e == NoSuchThing && ioe_description e == "No child processes"
then pure ()
else throwIO e
)
help e@IOError {..} = do
print $ ioe_handle
print $ ioe_type
print $ ioe_location
print $ ioe_description
print $ ioe_errno
print $ ioe_filename
throwIO e
sourceManifest :: (MonadUnliftIO m) => FilePath -> FilePath -> (ConduitT () ByteString m () -> m r) -> m r sourceManifest :: (MonadUnliftIO m) => FilePath -> FilePath -> (ConduitT () ByteString m () -> m r) -> m r
sourceManifest appmgrPath pkgFile sink = do sourceManifest appmgrPath pkgFile sink = do
let appmgr = readProcessInheritStderr (appmgrPath </> "embassy-sdk") ["inspect", "manifest", pkgFile] "" let appmgr = readProcessInheritStderr (appmgrPath </> "embassy-sdk") ["inspect", "manifest", pkgFile] ""
appmgr sink `catch` \ece -> throwIO (AppMgrE [i|embassy-sdk inspect manifest #{pkgFile}|] (eceExitCode ece)) appmgr sink `catch` \ece ->
print ece *> throwIO (AppMgrE [i|embassy-sdk inspect manifest #{pkgFile}|] (eceExitCode ece))
sourceIcon :: (MonadUnliftIO m) => FilePath -> FilePath -> (ConduitT () ByteString m () -> m r) -> m r sourceIcon :: (MonadUnliftIO m) => FilePath -> FilePath -> (ConduitT () ByteString m () -> m r) -> m r
sourceIcon appmgrPath pkgFile sink = do sourceIcon appmgrPath pkgFile sink = do
let appmgr = readProcessInheritStderr (appmgrPath <> "embassy-sdk") ["inspect", "icon", pkgFile] "" let appmgr = readProcessInheritStderr (appmgrPath </> "embassy-sdk") ["inspect", "icon", pkgFile] ""
appmgr sink `catch` \ece -> throwIO $ AppMgrE [i|embassy-sdk inspect icon #{pkgFile}|] (eceExitCode ece) appmgr sink
`catch` \ece -> print ece *> throwIO (AppMgrE [i|embassy-sdk inspect icon #{pkgFile}|] (eceExitCode ece))
getPackageHash :: (MonadUnliftIO m) => FilePath -> FilePath -> m ByteString getPackageHash :: (MonadUnliftIO m) => FilePath -> FilePath -> m ByteString
getPackageHash appmgrPath pkgFile = do getPackageHash appmgrPath pkgFile = do
let appmgr = readProcessInheritStderr (appmgrPath <> "embassy-sdk") ["inspect", "hash", pkgFile] "" let appmgr = readProcessInheritStderr (appmgrPath </> "embassy-sdk") ["inspect", "hash", pkgFile] ""
appmgr (\bsSource -> runConduit $ bsSource .| CL.foldMap id) appmgr (\bsSource -> runConduit $ bsSource .| CL.foldMap id)
`catch` \ece -> throwIO $ AppMgrE [i|embassy-sdk inspect hash #{pkgFile}|] (eceExitCode ece) `catch` \ece -> print ece *> throwIO (AppMgrE [i|embassy-sdk inspect hash #{pkgFile}|] (eceExitCode ece))
sourceInstructions :: (MonadUnliftIO m) => FilePath -> FilePath -> (ConduitT () ByteString m () -> m r) -> m r sourceInstructions :: (MonadUnliftIO m) => FilePath -> FilePath -> (ConduitT () ByteString m () -> m r) -> m r
sourceInstructions appmgrPath pkgFile sink = do sourceInstructions appmgrPath pkgFile sink = do
let appmgr = readProcessInheritStderr (appmgrPath <> "embassy-sdk") ["inspect", "instructions", pkgFile] "" let appmgr = readProcessInheritStderr (appmgrPath </> "embassy-sdk") ["inspect", "instructions", pkgFile] ""
appmgr sink `catch` \ece -> throwIO $ AppMgrE [i|embassy-sdk inspect instructions #{pkgFile}|] (eceExitCode ece) appmgr sink `catch` \ece ->
print ece *> throwIO (AppMgrE [i|embassy-sdk inspect instructions #{pkgFile}|] (eceExitCode ece))
sourceLicense :: (MonadUnliftIO m) => FilePath -> FilePath -> (ConduitT () ByteString m () -> m r) -> m r sourceLicense :: (MonadUnliftIO m) => FilePath -> FilePath -> (ConduitT () ByteString m () -> m r) -> m r
sourceLicense appmgrPath pkgFile sink = do sourceLicense appmgrPath pkgFile sink = do
let appmgr = readProcessInheritStderr (appmgrPath <> "embassy-sdk") ["inspect", "license", pkgFile] "" let appmgr = readProcessInheritStderr (appmgrPath </> "embassy-sdk") ["inspect", "license", pkgFile] ""
appmgr sink `catch` \ece -> throwIO $ AppMgrE [i|embassy-sdk inspect license #{pkgFile}|] (eceExitCode ece) appmgr sink
`catch` \ece -> print ece *> throwIO (AppMgrE [i|embassy-sdk inspect license #{pkgFile}|] (eceExitCode ece))
sinkMem :: (Monad m, Monoid a) => ConduitT () a m () -> m a sinkMem :: (Monad m, Monoid a) => ConduitT () a m () -> m a
sinkMem c = runConduit $ c .| CL.foldMap id sinkMem c = runConduit $ c .| CL.foldMap id

View File

@@ -62,6 +62,7 @@ import Startlude ( ($)
, MonadIO(liftIO) , MonadIO(liftIO)
, MonadReader , MonadReader
, Show , Show
, SomeException(SomeException)
, filter , filter
, find , find
, for_ , for_
@@ -69,10 +70,12 @@ import Startlude ( ($)
, headMay , headMay
, not , not
, partitionEithers , partitionEithers
, print
, pure , pure
, show , show
, sortOn , sortOn
, throwIO , throwIO
, void
) )
import System.FSNotify ( Event(Added) import System.FSNotify ( Event(Added)
, eventPath , eventPath
@@ -89,6 +92,7 @@ import UnliftIO ( MonadUnliftIO
, askRunInIO , askRunInIO
, async , async
, mapConcurrently , mapConcurrently
, mapConcurrently_
, newEmptyMVar , newEmptyMVar
, onException , onException
, takeMVar , takeMVar
@@ -101,6 +105,7 @@ import UnliftIO.Directory ( getFileSize
, removeFile , removeFile
, renameFile , renameFile
) )
import UnliftIO.Exception ( handle )
import Yesod.Core.Content ( typeGif import Yesod.Core.Content ( typeGif
, typeJpeg , typeJpeg
, typePlain , typePlain
@@ -137,11 +142,10 @@ getBestVersion pkg spec = headMay . sortOn Down <$> getViableVersions pkg spec
-- extract all package assets into their own respective files -- extract all package assets into their own respective files
extractPkg :: (MonadUnliftIO m, MonadReader r m, Has PkgRepo r, MonadLoggerIO m) => FilePath -> m () extractPkg :: (MonadUnliftIO m, MonadReader r m, Has PkgRepo r, MonadLoggerIO m) => FilePath -> m ()
extractPkg fp = (`onException` cleanup) $ do extractPkg fp = handle @_ @SomeException cleanup $ do
$logInfo [i|Extracting package: #{fp}|] $logInfo [i|Extracting package: #{fp}|]
PkgRepo { pkgRepoAppMgrBin = appmgr } <- ask PkgRepo { pkgRepoAppMgrBin = appmgr } <- ask
let pkgRoot = takeDirectory fp let pkgRoot = takeDirectory fp
-- let s9pk = pkgRoot </> show pkg <.> "s9pk"
manifestTask <- async $ liftIO . runResourceT $ AppMgr.sourceManifest appmgr fp $ sinkIt manifestTask <- async $ liftIO . runResourceT $ AppMgr.sourceManifest appmgr fp $ sinkIt
(pkgRoot </> "manifest.json") (pkgRoot </> "manifest.json")
pkgHashTask <- async $ AppMgr.getPackageHash appmgr fp pkgHashTask <- async $ AppMgr.getPackageHash appmgr fp
@@ -165,11 +169,13 @@ extractPkg fp = (`onException` cleanup) $ do
wait licenseTask wait licenseTask
where where
sinkIt fp source = runConduit $ source .| sinkFileCautious fp sinkIt fp source = runConduit $ source .| sinkFileCautious fp
cleanup = do cleanup e = do
$logError $ show e
let pkgRoot = takeDirectory fp let pkgRoot = takeDirectory fp
fs <- listDirectory pkgRoot fs <- listDirectory pkgRoot
let toRemove = filter (not . (== ".s9pk") . takeExtension) fs let toRemove = filter (not . (== ".s9pk") . takeExtension) fs
mapConcurrently (removeFile . (pkgRoot </>)) toRemove mapConcurrently_ (removeFile . (pkgRoot </>)) toRemove
throwIO e
watchPkgRepoRoot :: (MonadUnliftIO m, MonadReader r m, Has PkgRepo r, MonadLoggerIO m) => m (IO Bool) watchPkgRepoRoot :: (MonadUnliftIO m, MonadReader r m, Has PkgRepo r, MonadLoggerIO m) => m (IO Bool)
watchPkgRepoRoot = do watchPkgRepoRoot = do
@@ -181,7 +187,7 @@ watchPkgRepoRoot = do
stop <- watchTree watchManager root onlyAdded $ \evt -> do stop <- watchTree watchManager root onlyAdded $ \evt -> do
let pkg = eventPath evt let pkg = eventPath evt
-- TODO: validate that package path is an actual s9pk and is in a correctly conforming path. -- TODO: validate that package path is an actual s9pk and is in a correctly conforming path.
runInIO (extractPkg pkg) void . forkIO $ runInIO (extractPkg pkg)
takeMVar box takeMVar box
stop stop
pure $ tryPutMVar box () pure $ tryPutMVar box ()