proctypes :: Int -> Dep -> IO [Proc]
proctypes n d@Dep{..} = do
    logDebug $ "Reading " ++ path ++ " ..."
    fs <- decodeYAML path
    ws <- alloc (length fs)
    return $! reverse $ zipWith proc' fs ws
  where
    path = depPath </> "Procfile"

    alloc m = do
        !ss <- replicateM m . sequence $ replicate n sock
        forM ss . mapM $ \s -> fromIntegral <$> socketPort s <* close s

    sock = do
        s <- socket AF_INET Stream defaultProtocol
        a <- inet_addr "127.0.0.1"
        setSocketOption s NoDelay 0
        bind s $ SockAddrInet aNY_PORT a
        return s

    proc' (($ d) -> p@Proc{..}) ws = p
        { procPorts = zipWith ports portRange ws
        }
      where
        ports i w
            | i == bound = Port local remote w
            | otherwise  =
                let s = Text.pack (show i)
                 in Port (local <> s) (remote <> s) w

        bound  = head portRange
        remote = Text.toUpper $ Text.intercalate "_" [depName, procName, local]
        local  = "PORT"
proctypes :: Int -> Dep -> IO [Proc]
proctypes n d@Dep{..} = do
    logDebug $ "Reading " ++ path ++ " ..."
    fs <- decodeYAML path
    ws <- alloc (length fs)
    return $! reverse $ zipWith proc' fs ws
  where
    path = depPath </> "Procfile"

    alloc m = do
        !ss <- replicateM m . sequence $ replicate n sock
        forM ss . mapM $ \s -> fromIntegral <$> socketPort s <* close s

    sock = do
        s <- socket AF_INET Stream defaultProtocol
        a <- inet_addr "127.0.0.1"
        setSocketOption s NoDelay 0
        bind s $ SockAddrInet aNY_PORT a
        return s

    proc' (($ d) -> p@Proc{..}) ws = p
        { procPorts = zipWith ports portRange ws
        }
      where
        ports i w
            | i == bound = Port local remote w
            | otherwise  =
                let s = Text.pack (show i)
                 in Port (local <> s) (remote <> s) w

        bound  = head portRange
        remote = Text.toUpper $ Text.intercalate "_" [depName, procName, local]
        local  = "PORT"
proctypes n d@Dep{..} = do
    logDebug $ "Reading " ++ path ++ " ..."
    fs <- decodeYAML path
    ws <- alloc (length fs)
    return $! reverse $ zipWith proc' fs ws
  where
    path = depPath </> "Procfile"

    alloc m = do
        !ss <- replicateM m . sequence $ replicate n sock
        forM ss . mapM $ \s -> fromIntegral <$> socketPort s <* close s

    sock = do
        s <- socket AF_INET Stream defaultProtocol
        a <- inet_addr "127.0.0.1"
        setSocketOption s NoDelay 0
        bind s $ SockAddrInet aNY_PORT a
        return s

    proc' (($ d) -> p@Proc{..}) ws = p
        { procPorts = zipWith ports portRange ws
        }
      where
        ports i w
            | i == bound = Port local remote w
            | otherwise  =
                let s = Text.pack (show i)
                 in Port (local <> s) (remote <> s) w

        bound  = head portRange
        remote = Text.toUpper $ Text.intercalate "_" [depName, procName, local]
        local  = "PORT"