Repository: mkdocs/mkdocs

Issue:
gh-deploy command fails silently
If the `git push` command fails for some reason (e.g., creds access), we don't check the result and then fail the deploy.

PR with a fix coming momentarily.



Discussion:

Resolving patch:
diff --git a/mkdocs/commands/gh_deploy.py b/mkdocs/commands/gh_deploy.py
--- a/mkdocs/commands/gh_deploy.py
+++ b/mkdocs/commands/gh_deploy.py
@@ -65,29 +65,32 @@ def gh_deploy(config, message=None):
     log.info("Copying '%s' to '%s' branch and pushing to GitHub.",
              config['site_dir'], config['remote_branch'])
 
-    ghp_import.ghp_import(config['site_dir'], message, remote_name,
-                          remote_branch)
-
-    cname_file = os.path.join(config['site_dir'], 'CNAME')
-    # Does this repository have a CNAME set for GitHub pages?
-    if os.path.isfile(cname_file):
-        # This GitHub pages repository has a CNAME configured.
-        with(open(cname_file, 'r')) as f:
-            cname_host = f.read().strip()
-        log.info('Based on your CNAME file, your documentation should be '
-                 'available shortly at: http://%s', cname_host)
-        log.info('NOTE: Your DNS records must be configured appropriately for '
-                 'your CNAME URL to work.')
-        return
-
-    host, path = _get_remote_url(remote_name)
-
-    if host is None:
-        # This could be a GitHub Enterprise deployment.
-        log.info('Your documentation should be available shortly.')
+    result, error = ghp_import.ghp_import(config['site_dir'], message, remote_name,
+                                          remote_branch)
+    if not result:
+        log.error("Failed to deploy to GitHub with error: \n%s", error)
+        raise SystemExit(1)
     else:
-        username, repo = path.split('/', 1)
-        if repo.endswith('.git'):
-            repo = repo[:-len('.git')]
-        url = 'http://%s.github.io/%s' % (username, repo)
-        log.info('Your documentation should shortly be available at: ' + url)
+        cname_file = os.path.join(config['site_dir'], 'CNAME')
+        # Does this repository have a CNAME set for GitHub pages?
+        if os.path.isfile(cname_file):
+            # This GitHub pages repository has a CNAME configured.
+            with(open(cname_file, 'r')) as f:
+                cname_host = f.read().strip()
+            log.info('Based on your CNAME file, your documentation should be '
+                     'available shortly at: http://%s', cname_host)
+            log.info('NOTE: Your DNS records must be configured appropriately for '
+                     'your CNAME URL to work.')
+            return
+
+        host, path = _get_remote_url(remote_name)
+
+        if host is None:
+            # This could be a GitHub Enterprise deployment.
+            log.info('Your documentation should be available shortly.')
+        else:
+            username, repo = path.split('/', 1)
+            if repo.endswith('.git'):
+                repo = repo[:-len('.git')]
+            url = 'http://%s.github.io/%s' % (username, repo)
+            log.info('Your documentation should shortly be available at: ' + url)
diff --git a/mkdocs/utils/ghp_import.py b/mkdocs/utils/ghp_import.py
--- a/mkdocs/utils/ghp_import.py
+++ b/mkdocs/utils/ghp_import.py
@@ -169,5 +169,8 @@ def ghp_import(directory, message, remote='origin', branch='gh-pages'):
 
     proc = sp.Popen(['git', 'push', remote, branch],
                     stdout=sp.PIPE, stderr=sp.PIPE)
-    proc.communicate()
-    return proc.wait() == 0
+
+    out, err = proc.communicate()
+    result = proc.wait() == 0
+
+    return result, dec(err)


Test patch:
diff --git a/mkdocs/tests/gh_deploy_tests.py b/mkdocs/tests/gh_deploy_tests.py
--- a/mkdocs/tests/gh_deploy_tests.py
+++ b/mkdocs/tests/gh_deploy_tests.py
@@ -66,7 +66,7 @@ def test_get_remote_url_enterprise(self, mock_popeno):
     @mock.patch('mkdocs.commands.gh_deploy._is_cwd_git_repo', return_value=True)
     @mock.patch('mkdocs.commands.gh_deploy._get_current_sha', return_value='shashas')
     @mock.patch('mkdocs.commands.gh_deploy._get_remote_url', return_value=(None, None))
-    @mock.patch('mkdocs.commands.gh_deploy.ghp_import.ghp_import')
+    @mock.patch('mkdocs.commands.gh_deploy.ghp_import.ghp_import', return_value=(True, ''))
     def test_deploy(self, mock_import, get_remote, get_sha, is_repo):
 
         config = load_config(
@@ -77,7 +77,7 @@ def test_deploy(self, mock_import, get_remote, get_sha, is_repo):
     @mock.patch('mkdocs.commands.gh_deploy._is_cwd_git_repo', return_value=True)
     @mock.patch('mkdocs.commands.gh_deploy._get_current_sha', return_value='shashas')
     @mock.patch('mkdocs.commands.gh_deploy._get_remote_url', return_value=(None, None))
-    @mock.patch('mkdocs.commands.gh_deploy.ghp_import.ghp_import')
+    @mock.patch('mkdocs.commands.gh_deploy.ghp_import.ghp_import', return_value=(True, ''))
     @mock.patch('os.path.isfile', return_value=False)
     def test_deploy_no_cname(self, mock_isfile, mock_import, get_remote,
                              get_sha, is_repo):
@@ -91,10 +91,24 @@ def test_deploy_no_cname(self, mock_isfile, mock_import, get_remote,
     @mock.patch('mkdocs.commands.gh_deploy._get_current_sha', return_value='shashas')
     @mock.patch('mkdocs.commands.gh_deploy._get_remote_url', return_value=(
         u'git@', u'mkdocs/mkdocs.git'))
-    @mock.patch('mkdocs.commands.gh_deploy.ghp_import.ghp_import')
+    @mock.patch('mkdocs.commands.gh_deploy.ghp_import.ghp_import', return_value=(True, ''))
     def test_deploy_hostname(self, mock_import, get_remote, get_sha, is_repo):
 
         config = load_config(
             remote_branch='test',
         )
         gh_deploy.gh_deploy(config)
+
+    @mock.patch('mkdocs.utils.ghp_import.ghp_import')
+    @mock.patch('mkdocs.commands.gh_deploy.log')
+    def test_deploy_error(self, mock_log, mock_import):
+        error_string = 'TestError123'
+        mock_import.return_value = (False, error_string)
+
+        config = load_config(
+            remote_branch='test',
+        )
+
+        self.assertRaises(SystemExit, gh_deploy.gh_deploy, config)
+        mock_log.error.assert_called_once_with('Failed to deploy to GitHub with error: \n%s',
+                                               error_string)
diff --git a/mkdocs/tests/utils/ghp_import_tests.py b/mkdocs/tests/utils/ghp_import_tests.py
--- a/mkdocs/tests/utils/ghp_import_tests.py
+++ b/mkdocs/tests/utils/ghp_import_tests.py
@@ -105,3 +105,32 @@ def test_ghp_import(self, mock_popen, mock_call, mock_get_config,
             self.assertEqual(mock_call.call_count, 0)
         finally:
             shutil.rmtree(directory)
+
+    @mock.patch('mkdocs.utils.ghp_import.try_rebase', return_value=True)
+    @mock.patch('mkdocs.utils.ghp_import.get_prev_commit', return_value='sha')
+    @mock.patch('mkdocs.utils.ghp_import.get_config', return_value='config')
+    @mock.patch('mkdocs.utils.ghp_import.run_import')
+    @mock.patch('subprocess.call', auto_spec=True)
+    @mock.patch('subprocess.Popen', auto_spec=True)
+    def test_ghp_import_error(self, mock_popen, mock_call, mock_get_config,
+                              mock_run_import, mock_get_prev_commit, mock_try_rebase):
+
+        directory = tempfile.mkdtemp()
+        open(os.path.join(directory, 'file'), 'a').close()
+
+        try:
+            popen = mock.Mock()
+            mock_popen.return_value = popen
+
+            error_string = 'TestError123'
+            popen.communicate.return_value = ('', error_string)
+            popen.wait.return_value = 1
+
+            result, ghp_error = ghp_import.ghp_import(directory, "test message",
+                                                      remote='fake-remote-name',
+                                                      branch='fake-branch-name')
+
+            self.assertEqual(result, False)
+            self.assertEqual(ghp_error, error_string)
+        finally:
+            shutil.rmtree(directory)