@@ -1,9 +1,6 @@
11{-# LANGUAGE FlexibleContexts #-}
22{-# LANGUAGE LambdaCase #-}
33{-# LANGUAGE OverloadedStrings #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
5−{-# LANGUAGE TupleSections #-}
6−
74module Obelisk.Command.Project
85 ( InitSource (..)
96 , findProjectObeliskCommand
@@ -19,18 +16,12 @@ module Obelisk.Command.Project
1916 , toObeliskDir
2017 , withProjectRoot
2118 , bashEscape
22− , getHaskellManifestProjectPath
23− , AssetSource(..)
24− , describeImpureAssetSource
25− , watchStaticFilesDerivation
2619 ) where
2720
2821import Control.Concurrent.MVar (MVar, newMVar, withMVarMasked)
29−import Control.Lens ((.~), (?~), (<&>), (^.), _2, _3)
22+import Control.Lens ((.~), (?~), (<&>))
3023import Control.Monad
31−import Control.Monad.Except
3224import Control.Monad.IO.Class (liftIO)
33−import Control.Monad.Log
3425import Control.Monad.State
3526import qualified Data.Aeson as Json
3627import qualified Data.ByteString.UTF8 as BSU
@@ -43,26 +34,20 @@ import Data.Text (Text)
4334import qualified Data.Text as T
4435import Data.Text.Encoding (decodeUtf8, encodeUtf8)
4536import Data.Traversable (for)
46−import Reflex
47−import Reflex.FSNotify
48−import Reflex.Host.Headless
4937import System.Directory
5038import System.Environment (lookupEnv)
51−import System.Exit (ExitCode(..))
5239import System.FilePath
53−import System.FSNotify (defaultConfig, eventPath)
5440import System.IO.Temp
5541import System.IO.Unsafe (unsafePerformIO)
5642import System.PosixCompat.Files
5743import System.PosixCompat.Types
5844import System.PosixCompat.User
59−import qualified System.Process as Proc
6045import Text.ShellEscape (bash, bytes)
6146
6247import GitHub.Data.GitData (Branch)
6348import GitHub.Data.Name (Name)
6449
65−import Obelisk.App (MonadObelisk, runObelisk, getObelisk)
50+import Obelisk.App (MonadObelisk)
6651import Obelisk.CliApp
6752import Obelisk.Command.Nix
6853import Obelisk.Command.Thunk
@@ -349,21 +334,7 @@ nixShellWithHoogle root isPure shell' command = do
349334 \})).project.shells.${shell}"
350335 & nixShellConfig_common . nixCmdConfig_args .~ [ strArg "shell" shell' ]
351336
352−-- | Describes the provenance of static assets (i.e., are they the result of a derivation
353−-- that was built, or just a folder full of files.
354−data AssetSource = AssetSource_Derivation
355− | AssetSource_Files
356− deriving (Eq)
357−
358−-- | Some log messages to make it easier to tell where static files are coming from
359−describeImpureAssetSource :: AssetSource -> Text -> Text
360−describeImpureAssetSource src path = case src of
361− AssetSource_Files -> "Assets impurely loaded from: " <> path
362− AssetSource_Derivation -> "Assets derivation built and impurely loaded from: " <> path
363−
364−-- | Determine where the static files of a project are and whether they're plain files or a derivation.
365−-- If they are a derivation, that derivation will be built.
366−findProjectAssets :: MonadObelisk m => FilePath -> m (AssetSource, Text)
337+findProjectAssets :: MonadObelisk m => FilePath -> m Text
367338findProjectAssets root = do
368339 isDerivation <- readProcessAndLogStderr Debug $ setCwd (Just root) $
369340 proc nixExePath
@@ -375,87 +346,10 @@ findProjectAssets root = do
375346 ]
376347 -- Check whether the impure static files are a derivation (and so must be built)
377348 if isDerivation == "1"
378− then do
379− _ <- buildStaticFilesDerivationAndSymlink
380− (readProcessAndLogStderr Debug)
381− root
382− pure (AssetSource_Derivation, T.pack $ root </> "static.out")
383− else fmap (AssetSource_Files,) $ readProcessAndLogStderr Debug $ setCwd (Just root) $
384− proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]
385−
386−-- | Get the nix store path to the generated static asset manifest module (e.g., "obelisk-generated-static")
387−getHaskellManifestProjectPath :: MonadObelisk m => FilePath -> m Text
388−getHaskellManifestProjectPath root = fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $
389− proc nixBuildExePath
390− [ "--no-out-link"
391− , "-E"
392− , "(let a = import ./. {}; in a.passthru.processedStatic.haskellManifest)"
393− ]
394−
395−-- | Watch the project directory for file changes and check whether those file changes
396−-- cause changes in the static files nix derivation. If so, rebuild it.
397−watchStaticFilesDerivation
398− :: (MonadIO m, MonadObelisk m)
399− => FilePath
400− -> m ()
401−watchStaticFilesDerivation root = do
402− ob <- getObelisk
403− drv0 <- showDerivation
404− liftIO $ runHeadlessApp $ do
405− pb <- getPostBuild
406− checkForChanges <- batchOccurrences 0.25 =<< watchDirectoryTree
407− defaultConfig
408− (root <$ pb)
409− ((/="static.out") . takeFileName . eventPath)
410− drv <- performEvent $ ffor checkForChanges $ \_ ->
411− liftIO $ runObelisk ob showDerivation
412− drvs <- foldDyn (\new (_, old, _) -> (old, new, old /= new)) (drv0, drv0, False) drv
413− out <- throttleBatchWithLag
414− (\e -> performEvent $ ffor e $ \_ ->
415− liftIO $ runObelisk ob $ buildStaticCatchErrors >> pure ())
416− (void $ fmapMaybe (^._2) $ ffilter (^._3) $ updated drvs)
417− performEvent_ $ ffor out $ \_ -> runObelisk ob $
418− putLog Notice "Static assets rebuilt and symlinked to static.out"
419− pure never
420− where
421− handleBuildFailure
422− :: MonadObelisk m
423− => (ExitCode, String, String)
424− -> m (Maybe Text)
425− handleBuildFailure (ex, out, err) = case ex of
426− ExitSuccess -> pure $ Just $ T.pack out
427− _ -> do
428− putLog Error $
429− "Static assets build failed: " <> T.pack err
430− pure Nothing
431− showDerivation :: MonadObelisk m => m (Maybe Text)
432− showDerivation =
433− handleBuildFailure <=< readCreateProcessWithExitCode $
434− setCwd (Just root) $ ProcessSpec
435− { _processSpec_createProcess = Proc.proc nixExePath
436− [ "show-derivation"
437− , "-f", "."
438− , "passthru.staticFilesImpure"
439− ]
440− , _processSpec_overrideEnv = Nothing
441− }
442− buildStaticCatchErrors :: MonadObelisk m => m (Maybe Text)
443− buildStaticCatchErrors = handleBuildFailure =<<
444− buildStaticFilesDerivationAndSymlink
445− readCreateProcessWithExitCode
446− root
447−
448−buildStaticFilesDerivationAndSymlink
449− :: MonadObelisk m
450− => (ProcessSpec -> m a)
451− -> FilePath
452− -> m a
453−buildStaticFilesDerivationAndSymlink f root = f $
454− setCwd (Just root) $ ProcessSpec
455− { _processSpec_createProcess = Proc.proc
456− nixBuildExePath
457− [ "-o", "static.out"
349+ then fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $ -- Strip whitespace here because nix-build has no --raw option
350+ proc nixBuildExePath
351+ [ "--no-out-link"
458352 , "-E", "(import ./. {}).passthru.staticFilesImpure"
459353 ]
460− , _processSpec_overrideEnv = Nothing
461− }
354+ else readProcessAndLogStderr Debug $ setCwd (Just root) $
355+ proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]