@@ -14,7 +14,7 @@ module Obelisk.Command.Project
1414
1515import Control.Monad
1616import Control.Monad.IO.Class (liftIO)
17−import Control.Monad.Trans.State
17+import Control.Monad.State
1818import Data.Bits
1919import Data.Function (on)
2020import Data.Monoid
@@ -53,12 +53,19 @@ data InitSource
5353 | InitSource_Branch (Name Branch)
5454 | InitSource_Symlink FilePath
5555
56+-- | Path to obelisk directory in given path
57+toObeliskDir :: FilePath -> FilePath
58+toObeliskDir p = p </> ".obelisk"
59+
60+-- | Path to impl file in given path
61+implFile :: FilePath -> FilePath
62+implFile p = toObeliskDir p </> "impl"
63+
5664-- | Create a new project rooted in the current directory
5765initProject :: MonadObelisk m => InitSource -> m ()
5866initProject source = do
59− let obDir = ".obelisk"
60− implDir = obDir </> "impl"
61− liftIO $ createDirectory obDir
67+ let implDir = implFile "."
68+ liftIO $ createDirectory $ toObeliskDir "."
6269 case source of
6370 InitSource_Default -> liftIO $ createThunkWithLatest implDir obeliskSource
6471 InitSource_Branch branch -> liftIO $ createThunkWithLatest implDir $ obeliskSourceWithBranch branch
@@ -84,22 +91,24 @@ initProject source = do
8491 createDirectoryIfMissing False configDir
8592 mapM_ (createDirectoryIfMissing False . (configDir </>)) ["backend", "common", "frontend"]
8693
87−--TODO: Handle errors
8894--TODO: Allow the user to ignore our security concerns
8995-- | Find the Obelisk implementation for the project at the given path
9096findProjectObeliskCommand :: MonadObelisk m => FilePath -> m (Maybe FilePath)
9197findProjectObeliskCommand target = do
9298 myUid <- liftIO getRealUserID
9399 targetStat <- liftIO $ getFileStatus target
94− (result, insecurePaths) <- liftIO $ flip runStateT [] $ do
95− walkToProjectRoot target targetStat myUid >>= \case
96− Nothing -> return Nothing
97− Just projectRoot -> do
100+ (result, insecurePaths) <- flip runStateT [] $ walkToProjectRoot target targetStat myUid >>= \case
101+ Nothing -> pure Nothing
102+ Just projectRoot -> liftIO (doesFileExist $ implFile projectRoot) >>= \case
103+ False -> do
104+ putLog Warning $ "Found obelisk directory in " <> T.pack projectRoot <> " but the implementation (impl) file is missing"
105+ pure Nothing
106+ True -> do
98107 walkToImplDir projectRoot myUid -- For security check
99108 return $ Just projectRoot
100109 case (result, insecurePaths) of
101110 (Just projDir, []) -> do
102− obeliskCommandPkg <- nixBuildAttrWithCache (projDir </> ".obelisk" </> "impl") "command"
111+ obeliskCommandPkg <- nixBuildAttrWithCache (implFile projDir) "command"
103112 return $ Just $ obeliskCommandPkg </> "bin" </> "ob"
104113 (Nothing, _) -> return Nothing
105114 (Just projDir, _) -> do
@@ -126,7 +135,9 @@ withProjectRoot target f = findProjectRoot target >>= \case
126135-- | Walk from the current directory to the containing project's root directory,
127136-- if there is one, accumulating potentially insecure directories that were
128137-- traversed in the process. Return the project root directory, if found.
129−walkToProjectRoot :: FilePath -> FileStatus -> UserID -> StateT [FilePath] IO (Maybe FilePath)
138+walkToProjectRoot
139+ :: (MonadState [FilePath] m, MonadIO m)
140+ => FilePath -> FileStatus -> UserID -> m (Maybe FilePath)
130141walkToProjectRoot this thisStat myUid = liftIO (doesDirectoryExist this) >>= \case
131142 -- It's not a directory, so it can't be a project
132143 False -> do
@@ -135,7 +146,7 @@ walkToProjectRoot this thisStat myUid = liftIO (doesDirectoryExist this) >>= \ca
135146 walkToProjectRoot dir dirStat myUid
136147 True -> do
137148 when (not $ isWritableOnlyBy thisStat myUid) $ modify (this:)
138− liftIO (doesDirectoryExist (this </> ".obelisk")) >>= \case
149+ liftIO (doesDirectoryExist $ toObeliskDir this) >>= \case
139150 True -> return $ Just this
140151 False -> do
141152 let next = this </> ".." -- Use ".." instead of chopping off path segments, so that if the current directory is moved during the traversal, the traversal stays consistent
@@ -149,9 +160,9 @@ walkToProjectRoot this thisStat myUid = liftIO (doesDirectoryExist this) >>= \ca
149160-- | Walk from the given project root directory to its Obelisk implementation
150161-- directory, accumulating potentially insecure directories that were traversed
151162-- in the process.
152−walkToImplDir :: FilePath -> UserID -> StateT [FilePath] IO ()
163+walkToImplDir :: (MonadState [FilePath] m, MonadIO m) => FilePath -> UserID -> m ()
153164walkToImplDir projectRoot myUid = do
154− let obDir = projectRoot </> ".obelisk"
165+ let obDir = toObeliskDir projectRoot
155166 obDirStat <- liftIO $ getFileStatus obDir
156167 when (not $ isWritableOnlyBy obDirStat myUid) $ modify (obDir:)
157168 let implThunk = obDir </> "impl"