-
Notifications
You must be signed in to change notification settings - Fork 21
Support git worktrees in lamdera check git-repo detection
#99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: lamdera-next
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -134,6 +134,8 @@ module Lamdera | |
| , icdiff | ||
| , withStdinYesAll | ||
| , getGitBranch | ||
| , GitRepoStatus(..) | ||
| , gitRepoStatus | ||
| , launchAppZero | ||
| , killAppZero | ||
| , head_ | ||
|
|
@@ -174,7 +176,7 @@ import qualified Data.ByteString as BS | |
| import qualified Data.ByteString.Lazy as BSL | ||
| import Data.FileEmbed (bsToExp) | ||
| import Language.Haskell.TH (runIO) | ||
| import System.FilePath as FP ((</>), joinPath, splitDirectories, takeDirectory) | ||
| import System.FilePath as FP ((</>), joinPath, splitDirectories, takeDirectory, isAbsolute) | ||
| import qualified System.Directory as Dir | ||
| import Control.Monad (unless, filterM) | ||
| import System.Info | ||
|
|
@@ -1117,6 +1119,64 @@ getGitBranch = do | |
| stdout & pack & strip & pure | ||
|
|
||
|
|
||
| {-| Describes how the `.git` entry at a project root presents itself. | ||
|
|
||
| In a normal clone `.git` is a directory. In a git worktree it is instead a text | ||
| file containing a single `gitdir: <path>` line pointing at the real git | ||
| directory under the main checkout's `.git/worktrees/<name>`. The `git` binary | ||
| resolves this transparently, but our own existence checks need to be aware of | ||
| it so we don't mistake a worktree for an uninitialised project. -} | ||
| data GitRepoStatus | ||
| = GitRepoMissing -- ^ no `.git` at all -> safe to `git init` | ||
| | GitRepoDir -- ^ `.git` is a normal directory | ||
| | GitRepoWorktree FilePath -- ^ `.git` is a file pointing at an existing gitdir | ||
| | GitRepoWorktreeBroken String -- ^ `.git` is a file but malformed / dangling (reason) | ||
| deriving (Eq, Show) | ||
|
|
||
|
|
||
| {-| Inspect the `.git` entry at a project root, following the worktree `gitdir:` | ||
| pointer the same way `git` does. When `.git` is a file we verify it has the | ||
| expected `gitdir: <path>` format and that the target directory exists, returning | ||
| a specific reason for each failure case. -} | ||
| gitRepoStatus :: FilePath -> IO GitRepoStatus | ||
| gitRepoStatus root = do | ||
| let dotGit = root </> ".git" | ||
| isDir <- Dir.doesDirectoryExist dotGit | ||
| isFile <- Dir.doesFileExist dotGit | ||
| if isDir | ||
| then pure GitRepoDir | ||
| else if not isFile | ||
|
Comment on lines
+1145
to
+1148
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nitpick 💅] if |
||
| then pure GitRepoMissing | ||
| else do | ||
| contentsM <- readUtf8Text dotGit | ||
| case contentsM >>= parseGitdirPointer of | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the |
||
| Nothing -> | ||
| pure $ GitRepoWorktreeBroken | ||
| ("Found a `.git` file at " <> dotGit <> | ||
| " but it does not contain the expected `gitdir: <path>` pointer.") | ||
| Just rel -> do | ||
| let target = if FP.isAbsolute rel then rel else root </> rel | ||
| targetExists <- Dir.doesDirectoryExist target | ||
| if targetExists | ||
| then pure (GitRepoWorktree target) | ||
| else pure $ GitRepoWorktreeBroken | ||
| ("The `.git` file at " <> dotGit <> | ||
| " points to a git directory that does not exist: " <> target) | ||
|
|
||
|
|
||
| {-| Parse the `gitdir: <path>` pointer from the first line of a worktree `.git` | ||
| file, returning the path. -} | ||
| parseGitdirPointer :: Text -> Maybe FilePath | ||
| parseGitdirPointer contents = | ||
| case T.lines contents of | ||
| [] -> Nothing | ||
| (firstLine:_) -> | ||
| let trimmed = T.strip firstLine | ||
| in if T.isPrefixOf "gitdir:" trimmed | ||
| then Just $ T.unpack $ T.strip $ T.drop (T.length "gitdir:") trimmed | ||
| else Nothing | ||
|
|
||
|
|
||
| launchAppZero :: Text -> IO () | ||
| launchAppZero appId = do | ||
| callCommand $ "~/lamdera/scripts/launchAppZero.sh " <> unpack appId | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ module Test.Check where | |
|
|
||
| import System.FilePath ((</>)) | ||
| import qualified System.Directory as Dir | ||
| import System.IO.Temp (withSystemTempDirectory) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💅 Prefer using an alias and not exposing values here |
||
| import qualified Data.Text as T | ||
| import qualified Data.Map as Map | ||
| import qualified Data.Set as Set | ||
|
|
@@ -193,6 +194,54 @@ suite = tests $ | |
| ,"bar : something" | ||
| ,"bar =\n and bar too, including it's type sig\n"] | ||
| (Lamdera.CLI.Check.extractTopLevelExpressions input) | ||
|
|
||
| , scope "gitRepoStatus detects directories, worktrees, and broken pointers" $ do | ||
|
|
||
| -- No `.git` at all -> safe to offer `git init` | ||
| missing <- io $ withSystemTempDirectory "lamdera-git-missing" $ \tmp -> | ||
| gitRepoStatus tmp | ||
| scope "no .git -> GitRepoMissing" $ expectEqual GitRepoMissing missing | ||
|
|
||
| -- Normal clone: `.git` is a directory | ||
| normal <- io $ withSystemTempDirectory "lamdera-git-dir" $ \tmp -> do | ||
| Ext.Common.bash $ "cd " <> tmp <> " && git init -q" | ||
| gitRepoStatus tmp | ||
| scope ".git directory -> GitRepoDir" $ expectEqual GitRepoDir normal | ||
|
|
||
| -- Worktree: `.git` is a file pointing at an existing gitdir | ||
| worktree <- io $ withSystemTempDirectory "lamdera-git-wt" $ \tmp -> do | ||
| let mainDir = tmp </> "main" | ||
| wtDir = tmp </> "wt" | ||
| Dir.createDirectoryIfMissing True mainDir | ||
| Ext.Common.bash $ "cd " <> mainDir | ||
| <> " && git init -q" | ||
| <> " && git config user.email test@example.com" | ||
| <> " && git config user.name test" | ||
| <> " && git commit -q --allow-empty -m init" | ||
| <> " && git worktree add -q " <> wtDir | ||
| gitRepoStatus wtDir | ||
| scope "worktree .git file -> GitRepoWorktree" $ | ||
| case worktree of | ||
| GitRepoWorktree _ -> ok | ||
| other -> crash $ "expected GitRepoWorktree, got " <> show other | ||
|
|
||
| -- `.git` is a file but doesn't contain a `gitdir:` pointer | ||
| malformed <- io $ withSystemTempDirectory "lamdera-git-malformed" $ \tmp -> do | ||
| writeUtf8 (tmp </> ".git") "not a gitdir line\n" | ||
| gitRepoStatus tmp | ||
| scope "malformed .git file -> GitRepoWorktreeBroken" $ | ||
| case malformed of | ||
| GitRepoWorktreeBroken _ -> ok | ||
| other -> crash $ "expected GitRepoWorktreeBroken (malformed), got " <> show other | ||
|
|
||
| -- `.git` file points at a gitdir that doesn't exist | ||
| dangling <- io $ withSystemTempDirectory "lamdera-git-dangling" $ \tmp -> do | ||
| writeUtf8 (tmp </> ".git") "gitdir: /no/such/git/dir\n" | ||
| gitRepoStatus tmp | ||
| scope "dangling gitdir pointer -> GitRepoWorktreeBroken" $ | ||
| case dangling of | ||
| GitRepoWorktreeBroken _ -> ok | ||
| other -> crash $ "expected GitRepoWorktreeBroken (dangling), got " <> show other | ||
| ] | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're using
isAbsolutequalified below, no need to expose it