@@ -254,41 +254,20 @@ func (s *server) answer(channel ssh.Channel, requests <-chan *ssh.Request, conda
254254 channel .Stderr ().Write ([]byte ("No repo given" ))
255255 return err
256256 }
257- if err = s .pushLock .Lock (repoName , time .Duration (0 )); err != nil {
257+ wrapErr := wrapInLock (s .pushLock , repoName , time .Duration (0 ), s .runReceive (req , sshconn , channel , repoName , parts , condata ))
258+ if wrapErr == errAlreadyLocked {
258259 log .Info (multiplePush )
259260 // The error must be in git format
260- if err := gitPktLine (channel , fmt .Sprintf ("ERR %v\n " , multiplePush )); err != nil {
261+ if pktErr := gitPktLine (channel , fmt .Sprintf ("ERR %v\n " , multiplePush )); pktErr != nil {
261262 log .Err ("Failed to write to channel: %s" , err )
262263 }
263264 sendExitStatus (1 , channel )
264265 req .Reply (false , nil )
265266 return nil
266267 }
267268
268- req .Reply (true , nil ) // We processed. Yay.
269- if ! strings .Contains (sshconn .Permissions .Extensions ["apps" ], repoName ) {
270- if err := s .pushLock .Unlock (repoName , time .Duration (0 )); err != nil {
271- log .Err ("unable to unlock repository lock for %s (%s)" , repoName , err )
272- // TODO: this is an important error case that needs to be covered
273- // Probably the best solution is to change the lock into a lease so that even on unlock
274- // failures, RepositoryLock will eventually yield
275- }
276- return errBuildAppPerm
277- }
278- repo := repoName + ".git"
279- err = git .Receive (repo , parts [0 ], s .gitHome , channel , sshconn .Permissions .Extensions ["fingerprint" ], sshconn .Permissions .Extensions ["user" ], condata , s .receivetype )
280- if err != nil {
281- if err := s .pushLock .Unlock (repoName , time .Duration (0 )); err != nil {
282- log .Err ("unable to unlock repository lock for %s (%s)" , repoName , err )
283- // TODO: this is an important error case that needs to be covered
284- // Probably the best solution is to change the lock into a lease so that even on unlock
285- // failures, RepositoryLock will eventually yield
286- }
287- return err
288- }
289-
290269 var xs uint32
291- if err != nil {
270+ if wrapErr != nil {
292271 log .Err ("Failed git receive: %v" , err )
293272 xs = 1
294273 }
@@ -316,6 +295,37 @@ func (s *server) answer(channel ssh.Channel, requests <-chan *ssh.Request, conda
316295 return nil
317296}
318297
298+ func (s * server ) runReceive (
299+ req * ssh.Request ,
300+ sshConn * ssh.ServerConn ,
301+ channel ssh.Channel ,
302+ repoName string ,
303+ parts []string ,
304+ connData string ,
305+ ) func () error {
306+ return func () error {
307+ req .Reply (true , nil ) // We processed. Yay.
308+ if ! strings .Contains (sshConn .Permissions .Extensions ["apps" ], repoName ) {
309+ return errBuildAppPerm
310+ }
311+ repo := repoName + ".git"
312+ recvErr := git .Receive (
313+ repo ,
314+ parts [0 ],
315+ s .gitHome ,
316+ channel ,
317+ sshConn .Permissions .Extensions ["fingerprint" ],
318+ sshConn .Permissions .Extensions ["user" ],
319+ connData ,
320+ s .receivetype ,
321+ )
322+ if recvErr != nil {
323+ return recvErr
324+ }
325+ return nil
326+ }
327+ }
328+
319329// ExecCmd is an SSH exec request.
320330type ExecCmd struct {
321331 Value string
0 commit comments