summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/changelog2
-rw-r--r--doc/todo/spin_failure_HEAD.mdwn26
-rw-r--r--src/Propellor/Spin.hs102
3 files changed, 80 insertions, 50 deletions
diff --git a/debian/changelog b/debian/changelog
index d26e007c..086c82c0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,7 @@
propellor (4.0.6) UNRELEASED; urgency=medium
+ * Fix bug that sometimes made --spin fail with
+ "fatal: Couldn't find remote ref HEAD"
* Display error and warning messages to stderr, not stdout.
-- Joey Hess <id@joeyh.name> Sun, 18 Jun 2017 16:19:41 -0400
diff --git a/doc/todo/spin_failure_HEAD.mdwn b/doc/todo/spin_failure_HEAD.mdwn
index 1fbd8688..f838e469 100644
--- a/doc/todo/spin_failure_HEAD.mdwn
+++ b/doc/todo/spin_failure_HEAD.mdwn
@@ -99,12 +99,7 @@ Sending privdata (73139 bytes) to kite.kitenet.net ... done
> > I added debug dumping to gitPushHelper, and it seems to be
> > reading the same truncated data, so it seems the problem is not there.
> >
-> > Just after the gitPushMarker is sent, I made it read
-> > a line from stdin, and it got the first line of the protocol.
-> > So the data is being sent over the ssh connection ok, and
-> > the mangling must happening after that point and before gitPushHelper.
-> >
-> > Aha! The problem seems to have to do with the dupping of stdin.
+> > Aha! The problem comes from stdin/stdInput confusion here:
req NeedGitPush gitPushMarker $ \_ -> do
hin <- dup stdInput
@@ -113,5 +108,20 @@ Sending privdata (73139 bytes) to kite.kitenet.net ... done
hClose stdout
> > A line read from stdin just before the dup gets the first line of the protocol
-> > as expected. A line read from hin afterwards does not. Could
-> > is be a handle buffering issue?
+> > as expected. But reading from stdInput starts with a later line.
+> > Apparently data is being buffered in the stdin Handle, so gitPushHelper,
+> > which reads from the Fd, does not see it.
+> >
+> > Here's a simple test case. Feeding this 2 lines on stdin will
+> > print the first and then fail with "hGetLine: end of file".
+> > The second line is lost in the buffer. This test case behaves
+> > like that reliably, so I'm surprised propellor only fails sometimes.
+
+ main = do
+ l <- hGetLine stdin
+ print l
+ bob <- fdToHandle stdInput
+ l2 <- hGetLine bob
+ print l2
+
+> > [[fixed|done]] --[[Joey]]
diff --git a/src/Propellor/Spin.hs b/src/Propellor/Spin.hs
index d0ce4d03..cc5fa0e8 100644
--- a/src/Propellor/Spin.hs
+++ b/src/Propellor/Spin.hs
@@ -186,26 +186,8 @@ update forhost = do
writeFileProtected privfile
whenM hasGitRepo $
- req NeedGitPush gitPushMarker $ \_ -> do
- hin <- dup stdInput
- hout <- dup stdOutput
- hClose stdin
- hClose stdout
- -- Not using git pull because git 2.5.0 badly
- -- broke its option parser.
- unlessM (boolSystemNonConcurrent "git" (pullparams hin hout)) $
- errorMessage "git fetch from client failed"
- unlessM (boolSystemNonConcurrent "git" [Param "merge", Param "FETCH_HEAD"]) $
- errorMessage "git merge from client failed"
+ gitPullFromUpdateServer
where
- pullparams hin hout =
- [ Param "fetch"
- , Param "--progress"
- , Param "--upload-pack"
- , Param $ "./propellor --gitpush " ++ show hin ++ " " ++ show hout
- , Param "."
- ]
-
-- When --spin --relay is run, get a privdata file
-- to be relayed to the target host.
privfile = maybe privDataLocal privDataRelay forhost
@@ -336,29 +318,6 @@ sendPrecompiled hn = void $ actionMessage "Uploading locally compiled propellor
, "rm -f " ++ remotetarball
]
--- Shim for git push over the propellor ssh channel.
--- Reads from stdin and sends it to hout;
--- reads from hin and sends it to stdout.
-gitPushHelper :: Fd -> Fd -> IO ()
-gitPushHelper hin hout = void $ fromstdin `concurrently` tostdout
- where
- fromstdin = do
- h <- fdToHandle hout
- connect stdin h
- tostdout = do
- h <- fdToHandle hin
- connect h stdout
- connect fromh toh = do
- b <- B.hGetSome fromh 40960
- if B.null b
- then do
- hClose fromh
- hClose toh
- else do
- B.hPut toh b
- hFlush toh
- connect fromh toh
-
mergeSpin :: IO ()
mergeSpin = do
branch <- getCurrentBranch
@@ -386,3 +345,62 @@ findLastNonSpinCommit = do
spinCommitMessage :: String
spinCommitMessage = "propellor spin"
+
+-- Stdin and stdout are connected to the updateServer over ssh.
+-- Request that it run git upload-pack, and connect that up to a git fetch
+-- to receive the data.
+gitPullFromUpdateServer :: IO ()
+gitPullFromUpdateServer = req NeedGitPush gitPushMarker $ \_ -> do
+ -- IO involving stdin can cause data to be buffered in the Handle
+ -- (even when it's set NoBuffering), but we need to pass a FD to
+ -- git fetch containing all of stdin after the gitPushMarker,
+ -- including any that has been buffered.
+ --
+ -- To do so, create a pipe, and forward stdin, including any
+ -- buffered part, through it.
+ (pread, pwrite) <- System.Posix.IO.createPipe
+ hwrite <- fdToHandle pwrite
+ _ <- async $ stdin *>* hwrite
+ let hin = pread
+ hout <- dup stdOutput
+ hClose stdout
+ -- Not using git pull because git 2.5.0 badly
+ -- broke its option parser.
+ unlessM (boolSystemNonConcurrent "git" (fetchparams hin hout)) $
+ errorMessage "git fetch from client failed"
+ unlessM (boolSystemNonConcurrent "git" [Param "merge", Param "FETCH_HEAD"]) $
+ errorMessage "git merge from client failed"
+ where
+ fetchparams hin hout =
+ [ Param "fetch"
+ , Param "--progress"
+ , Param "--upload-pack"
+ , Param $ "./propellor --gitpush " ++ show hin ++ " " ++ show hout
+ , Param "."
+ ]
+
+-- Shim for git push over the propellor ssh channel.
+-- Reads from stdin and sends it to hout;
+-- reads from hin and sends it to stdout.
+gitPushHelper :: Fd -> Fd -> IO ()
+gitPushHelper hin hout = void $ fromstdin `concurrently` tostdout
+ where
+ fromstdin = do
+ h <- fdToHandle hout
+ stdin *>* h
+ tostdout = do
+ h <- fdToHandle hin
+ h *>* stdout
+
+-- Forward data from one handle to another.
+(*>*) :: Handle -> Handle -> IO ()
+fromh *>* toh = do
+ b <- B.hGetSome fromh 40960
+ if B.null b
+ then do
+ hClose fromh
+ hClose toh
+ else do
+ B.hPut toh b
+ hFlush toh
+ fromh *>* toh