Repository: VirtusLab/git-machete
Pull request #1045 (2023-09-05 12:05:12)

Fix reading tokens from `~/.github-token` for GitHub Enterprise domains


Patch:
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 5cd1fbf..b58b452 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -2,6 +2,7 @@
 
 ## New in git-machete 3.18.2
 
+- fixed: reading tokens from `~/.github-token` for GitHub Enterprise domains (reported by @mkondratek)
 - fixed: `git machete github retarget-pr`, when invoked without `--ignore-if-missing`, actually fails now if there is no PR for the branch
 - improved: GitHub tokens are automatically redacted from command outputs in `--debug` mode
 
diff --git a/docs/man/git-machete.1 b/docs/man/git-machete.1
index 2f83929..6190b1f 100644
--- a/docs/man/git-machete.1
+++ b/docs/man/git-machete.1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
 ..
-.TH "GIT-MACHETE" "1" "Aug 31, 2023" "" "git-machete"
+.TH "GIT-MACHETE" "1" "Sep 05, 2023" "" "git-machete"
 .SH NAME
 git-machete \- git-machete 3.18.2
 .sp
diff --git a/git_machete/github.py b/git_machete/github.py
index ba5937c..029440a 100644
--- a/git_machete/github.py
+++ b/git_machete/github.py
@@ -79,7 +79,7 @@ class GitHubToken(NamedTuple):
 
     @classmethod
     def __get_token_from_env(cls) -> Optional["GitHubToken"]:
-        debug(f"1. Trying to authenticate via `{GITHUB_TOKEN_ENV_VAR}` environment variable...")
+        debug(f"1. Trying to find token in `{GITHUB_TOKEN_ENV_VAR}` environment variable...")
         github_token = os.environ.get(GITHUB_TOKEN_ENV_VAR)
         if github_token:
             return cls(value=github_token,
@@ -88,12 +88,13 @@ class GitHubToken(NamedTuple):
 
     @classmethod
     def __get_token_from_file_in_home_directory(cls, domain: str) -> Optional["GitHubToken"]:
-        debug("2. Trying to authenticate via `~/.github-token`...")
+        debug("2. Trying to find token in `~/.github-token`...")
         required_file_name = '.github-token'
         provider = f'auth token for {domain} from `~/.github-token`'
         file_full_path = os.path.expanduser(f'~/{required_file_name}')
 
         if os.path.isfile(file_full_path):
+            debug(f"  File `{file_full_path}` exists")
             with open(file_full_path) as file:
                 # ~/.github-token is a file with a structure similar to:
                 #
@@ -102,7 +103,7 @@ class GitHubToken(NamedTuple):
                 # ghp_yetanothertoken_for_git_example_com git.example.com
 
                 for line in file.readlines():
-                    if line.endswith(" " + domain):
+                    if line.rstrip().endswith(" " + domain):
                         token = line.split(" ")[0]
                         return cls(value=token, provider=provider)
                     elif domain == GitHubClient.DEFAULT_GITHUB_DOMAIN and " " not in line.rstrip():
@@ -111,7 +112,7 @@ class GitHubToken(NamedTuple):
 
     @classmethod
     def __get_token_from_gh(cls, domain: str) -> Optional["GitHubToken"]:
-        debug("3. Trying to authenticate via `gh` GitHub CLI...")
+        debug("3. Trying to find token via `gh` GitHub CLI...")
         # Abort without error if `gh` isn't available
         gh = shutil.which('gh')
         if not gh:
@@ -161,7 +162,7 @@ class GitHubToken(NamedTuple):
 
     @classmethod
     def __get_token_from_hub(cls, domain: str) -> Optional["GitHubToken"]:
-        debug("4. Trying to authenticate via `hub` GitHub CLI...")
+        debug("4. Trying to find token via `hub` GitHub CLI...")
         home_path: str = str(Path.home())
         config_hub_path: str = os.path.join(home_path, ".config", "hub")
         if os.path.isfile(config_hub_path):


Test patch:
diff --git a/tests/test_github.py b/tests/test_github.py
index 395816a..39a902a 100644
--- a/tests/test_github.py
+++ b/tests/test_github.py
@@ -197,13 +197,13 @@ class TestGitHub(BaseTest):
         )
 
         expected_output = ["__get_token_from_env(cls=<class 'git_machete.github.GitHubToken'>): "
-                           "1. Trying to authenticate via `GITHUB_TOKEN` environment variable...",
+                           "1. Trying to find token in `GITHUB_TOKEN` environment variable...",
                            "__get_token_from_file_in_home_directory(cls=<class 'git_machete.github.GitHubToken'>, domain=github.com): "
-                           "2. Trying to authenticate via `~/.github-token`...",
+                           "2. Trying to find token in `~/.github-token`...",
                            "__get_token_from_gh(cls=<class 'git_machete.github.GitHubToken'>, domain=github.com): "
-                           "3. Trying to authenticate via `gh` GitHub CLI...",
+                           "3. Trying to find token via `gh` GitHub CLI...",
                            "__get_token_from_hub(cls=<class 'git_machete.github.GitHubToken'>, domain=github.com): "
-                           "4. Trying to authenticate via `hub` GitHub CLI..."]
+                           "4. Trying to find token via `hub` GitHub CLI..."]
 
         assert list(itertools.dropwhile(
             lambda line: '__get_token_from_env' not in line,
@@ -227,9 +227,7 @@ class TestGitHub(BaseTest):
         github_token_contents = ('ghp_mytoken_for_github_com\n'
                                  'ghp_myothertoken_for_git_example_org git.example.org\n'
                                  'ghp_yetanothertoken_for_git_example_com git.example.com')
-        _mock_open = mock_open(read_data=github_token_contents)
-        _mock_open.return_value.readlines.return_value = github_token_contents.split('\n')
-        self.patch_symbol(mocker, 'builtins.open', _mock_open)
+        self.patch_symbol(mocker, 'builtins.open', mock_open(read_data=github_token_contents))
         self.patch_symbol(mocker, 'os.path.isfile', lambda _file: True)
 
         domain = GitHubClient.DEFAULT_GITHUB_DOMAIN
@@ -238,12 +236,20 @@ class TestGitHub(BaseTest):
         assert github_token.provider == f'auth token for {domain} from `~/.github-token`'
         assert github_token.value == 'ghp_mytoken_for_github_com'
 
+        # Line ends with \n
         domain = 'git.example.org'
         github_token = GitHubToken.for_domain(domain=domain)
         assert github_token is not None
         assert github_token.provider == f'auth token for {domain} from `~/.github-token`'
         assert github_token.value == 'ghp_myothertoken_for_git_example_org'
 
+        # Last line, doesn't end with \n
+        domain = 'git.example.com'
+        github_token = GitHubToken.for_domain(domain=domain)
+        assert github_token is not None
+        assert github_token.provider == f'auth token for {domain} from `~/.github-token`'
+        assert github_token.value == 'ghp_yetanothertoken_for_git_example_com'
+
         domain = 'git.example.net'
         github_token = GitHubToken.for_domain(domain=domain)
         assert github_token is None