diff --git a/build/recipes/setup_to_meta/test/test_setup_to_meta.py b/build/recipes/setup_to_meta/test/test_setup_to_meta.py
index dc6a201f942406e8e380f6405be831412a23b618..7b0162ae61965a5d88941af339f95761072d795e 100644
--- a/build/recipes/setup_to_meta/test/test_setup_to_meta.py
+++ b/build/recipes/setup_to_meta/test/test_setup_to_meta.py
@@ -3,32 +3,41 @@ from .. import setup_to_meta
 class TestSetupToMeta:
     def test_del_substrings(self):
         """
-        Test that del_substrings function properly deletes substrings from a given string
+        Tests that del_substrings function properly deletes substrings from a given string
         """
         replaced = setup_to_meta.del_substrings('heallob, woarlcd', ['a', 'b', 'c'])
         assert replaced == 'hello, world'
 
     def test_get_names(self):
         """
-        Test that setup_to_meta correctly gets the package name from the package path
+        Tests that setup_to_meta correctly gets the package name from the package path
         """
         d = 'apps/cli/executables/null'
         assert setup_to_meta.get_names([d]) == ['null']
 
     def test_get_dirs(self):
         """
-        Test that setup_to_meta correctly finds and stores directory paths
+        Tests that setup_to_meta correctly finds and stores directory paths
         of packages containing setup.py files
         """
         assert './apps/cli/executables/null' in setup_to_meta.get_dirs()
 
     def test_get_outputs(self):
         """
-        Test that tests that setup_to_meta correctly generates a list of output paths given
+        Tests that setup_to_meta correctly generates a list of output paths given
         a list of package names.
         """
         assert setup_to_meta.get_outputs(['null']) == ['build/metadata/null/meta.yaml']
 
+    def test_parse_setup(self):
+        """
+        Tests that parse_setup correctly parses a setup.py file into a dictionary.
+        """
+        setup_data = setup_to_meta.parse_setup('apps/cli/executables/null')
+        keys = ['name', 'version', 'description', 'license']
+        for key in keys:
+            assert key in setup_data
+
     def test_output(self, recipe):
         """
         Test that metadata was successfully created and contains data.
diff --git a/build/tools/transfer_to_builder.py b/build/tools/transfer_to_builder.py
index 64d7e4fc7ff61c4fdc8b32d4f8c7fd627f7981d8..fa43851a3f4ccf5312ba40249f082155b2e1e1b1 100644
--- a/build/tools/transfer_to_builder.py
+++ b/build/tools/transfer_to_builder.py
@@ -1,11 +1,17 @@
 import subprocess
 import paramiko
+import logging
 import fnmatch
-import os
 import getpass
+import sys
+import os
 
 from scp import SCPClient
 
+logger = logging.getLogger("buildtools/transfer_to_builder")
+logger.setLevel(logging.INFO)
+hander = logging.StreamHandler(stream=sys.stdout)
+
 def get_build_pkg_names():
     """
     Search through pkgs directory for built .tar.bz2 packages
@@ -13,9 +19,12 @@ def get_build_pkg_names():
     """
     pkg_names = []
     d = "build/pkgs/noarch/"
-    for file in os.listdir(d):
-        if fnmatch.fnmatch(file, "*.tar.bz2"):
-            pkg_names.append(d + file)
+    try:
+        for file in os.listdir(d):
+            if fnmatch.fnmatch(file, "*.tar.bz2"):
+                pkg_names.append(d + file)
+    except FileNotFoundError as e:
+        logger.error(e)
 
     return pkg_names
 
@@ -29,10 +38,18 @@ def create_ssh_client(server):
     client.load_system_host_keys()
     client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 
-    username = input("Enter NRAO username: ")
-    password = getpass.getpass(prompt="Enter NRAO password: ")
+    while True:
+        username = input("Enter NRAO username: ")
+        password = getpass.getpass(prompt="Enter NRAO password: ")
+
+        try:
+            client.connect(server, username=username, password=password)
+        except paramiko.AuthenticationException as e:
+            logger.error(e)
+            logger.error("Invalid credentials. Try again.")
+            continue
+        break
 
-    client.connect(server, username=username, password=password)
     return client
 
 def transfer_packages(pkg_names):
@@ -40,6 +57,8 @@ def transfer_packages(pkg_names):
     Use shell commands to transfer build archives to builder and update its conda package index.
     :param pkg_names: Names of the .tar.bz2 files for the built packages.
     """
+    logger.addHandler(hander)
+
     if len(pkg_names):
         builder_addr = "builder.aoc.nrao.edu"
         builder_path = "/home/builder.aoc.nrao.edu/content/conda/noarch"
@@ -54,7 +73,8 @@ def transfer_packages(pkg_names):
                         cmd_index + " && " +
                         cmd_chmod])
     else:
-        print("No packages found in build/pkgs/noarch. Did conda build successfully build the package(s)?")
+        logger.error("No packages found in build/pkgs/noarch. "
+              "Did conda build successfully build the package(s)?")
 
 if __name__ == "__main__":
     transfer_packages(get_build_pkg_names())
\ No newline at end of file