66
77from keboola .component .exceptions import UserException
88
9- from configuration import GitConfiguration
9+ from configuration import AuthEnum , GitConfiguration
1010
1111
1212class GitHandler :
@@ -16,7 +16,65 @@ def __init__(self, git_cfg: GitConfiguration):
1616 # add path for absolute imports to start at the cloned repository root level
1717 sys .path .append (os .path .join (pathlib .Path (__file__ ).parent .parent , self .REPO_PATH ))
1818
19+ self .env = os .environ .copy ()
1920 self .git_cfg = git_cfg
21+ self .repo_auth_url = None # ‼️ NEVER EVER INCLUDE THIS VARIABLE IN LOGGING OUTPUT ‼️
22+
23+ if not self .git_cfg .url :
24+ raise UserException ("Git repository URL is required" )
25+
26+ if self .git_cfg .auth == AuthEnum .PAT :
27+ self ._set_up_token_auth ()
28+ else :
29+ self ._set_up_ssh_command ()
30+
31+ # do not ask for credentials when git authentication fails
32+ self .env ["GIT_TERMINAL_PROMPT" ] = "0"
33+
34+ def _set_up_token_auth (self ) -> None :
35+ if not self .git_cfg .encrypted_token :
36+ raise UserException ("No personal access token provided" )
37+
38+ if not self .git_cfg .url .startswith ("https://" ):
39+ raise UserException ("PAT authentication is only supported for HTTPS URLs" )
40+
41+ self .repo_auth_url = self .git_cfg .url .replace (
42+ "https://" , f"https://x-token-auth:{ self .git_cfg .encrypted_token } @"
43+ )
44+ logging .info ("Git token authentication set up for HTTPS URL." )
45+
46+ def _set_up_ssh_command (self ) -> None :
47+ if self .git_cfg .auth == AuthEnum .SSH and not self .git_cfg .ssh_keys .keys .encrypted_private :
48+ raise UserException ("SSH key is required for SSH authentication" )
49+
50+ repo_url = self .git_cfg .url
51+ if repo_url .startswith ("git@" ) or repo_url .startswith ("ssh://" ):
52+ logging .warning ("SSH URL detected but no ssh_key_path provided. Trying default SSH configuration." )
53+
54+ ssh_command = [
55+ "ssh" ,
56+ # the following lines could be used to disable strict host key checking, but it is better
57+ # for security reasons to use the known_hosts file prepared in Dockerfile
58+ # "-o",
59+ # "StrictHostKeyChecking=no",
60+ "-o" ,
61+ "BatchMode=yes" , # do not ask for credentials when SSH auth fails
62+ "-o" ,
63+ "ConnectTimeout=30" ,
64+ "-o" ,
65+ "ServerAliveInterval=60" ,
66+ ]
67+
68+ if self .git_cfg .ssh_keys .keys .encrypted_private :
69+ ssh_key_path = os .path .expanduser ("~/.ssh/github_private_key" )
70+ with open (ssh_key_path , "wb" ) as f :
71+ for line in self .git_cfg .ssh_keys .keys .encrypted_private .splitlines ():
72+ f .write (line .encode () + b"\n " )
73+ # ensure SSH key has correct permissions
74+ os .chmod (ssh_key_path , 0o600 )
75+ ssh_command .extend (["-i" , ssh_key_path ])
76+
77+ self .env ["GIT_SSH_COMMAND" ] = " " .join (ssh_command )
2078
2179 def clone_repository (self ):
2280 """
@@ -25,62 +83,24 @@ def clone_repository(self):
2583 Returns:
2684 Path to the main script file to execute
2785 """
28- repo_url = self .git_cfg .url
29- if not repo_url :
30- raise UserException ("Git repository URL is required" )
3186
3287 branch = self .git_cfg .branch or "main"
3388
34- logging .info ("Cloning git repository: %s" , repo_url )
89+ logging .info ("Cloning git repository: %s" , self . git_cfg . url )
3590
3691 try :
3792 clone_args = ["git" , "clone" ]
3893
3994 if branch :
4095 clone_args .extend (["--branch" , branch ])
4196
42- if self .git_cfg .ssh_keys .keys .encrypted_private and self .git_cfg .encrypted_token :
43- self .git_cfg .encrypted_token = None
44-
45- if self .git_cfg .encrypted_token and repo_url .startswith ("https://" ):
46- repo_url = repo_url .replace ("https://" , f"https://x-token-auth:{ self .git_cfg .encrypted_token } @" )
47-
48- clone_args .extend ([repo_url , self .REPO_PATH ])
49-
50- env = os .environ .copy ()
51-
52- ssh_command = [
53- "ssh" ,
54- # the following lines could be used to disable strict host key checking, but it is better
55- # for security reasons to use the known_hosts file prepared in Dockerfile
56- # "-o",
57- # "StrictHostKeyChecking=no",
58- "-o" ,
59- "BatchMode=yes" , # do not ask for credentials when SSH auth fails
60- "-o" ,
61- "ConnectTimeout=30" ,
62- "-o" ,
63- "ServerAliveInterval=60" ,
64- ]
65-
66- if self .git_cfg .ssh_keys .keys .encrypted_private :
67- ssh_key_path = os .path .expanduser ("~/.ssh/github_private_key" )
68- with open (ssh_key_path , "wb" ) as f :
69- for line in self .git_cfg .ssh_keys .keys .encrypted_private .splitlines ():
70- f .write (line .encode () + b"\n " )
71- # ensure SSH key has correct permissions
72- os .chmod (ssh_key_path , 0o600 )
73- ssh_command .extend (["-i" , ssh_key_path ])
74- elif repo_url .startswith ("git@" ) or repo_url .startswith ("ssh://" ):
75- logging .warning ("SSH URL detected but no ssh_key_path provided. Trying default SSH configuration." )
76-
77- env ["GIT_SSH_COMMAND" ] = " " .join (ssh_command )
97+ clone_args .extend ([self .repo_auth_url or self .git_cfg .url , self .REPO_PATH ])
7898
7999 process = subprocess .Popen (
80100 clone_args ,
81101 stdout = subprocess .PIPE ,
82102 stderr = subprocess .PIPE ,
83- env = env ,
103+ env = self . env ,
84104 )
85105 _ , stderr = process .communicate ()
86106
@@ -110,33 +130,23 @@ def get_repository_branches(self):
110130 List of branch names
111131 """
112132 try :
113- repo_url = self .git_cfg .url
114- if not repo_url :
115- raise UserException ("Git repository URL is required" )
116-
117133 branches_args = ["git" , "ls-remote" , "--heads" ]
118134
119- if self .git_cfg .ssh_keys .keys .encrypted_private and self .git_cfg .encrypted_token :
120- self .git_cfg .encrypted_token = None
121-
122- if self .git_cfg .encrypted_token and repo_url .startswith ("https://" ):
123- repo_url = repo_url .replace ("https://" , f"https://x-token-auth:{ self .git_cfg .encrypted_token } @" )
124-
125- branches_args .append (repo_url )
135+ branches_args .append (self .repo_auth_url or self .git_cfg .url )
126136
127137 process = subprocess .Popen (
128138 branches_args ,
129139 stdout = subprocess .PIPE ,
130140 stderr = subprocess .PIPE ,
131- cwd = self .REPO_PATH ,
141+ env = self .env ,
132142 )
133143 stdout , stderr = process .communicate ()
134144
135145 if process .returncode != 0 :
136146 raise UserException (f"Failed to get branches: { stderr .decode ()} " )
137147
138- branches = [line .strip ().split ("refs/heads" )[- 1 ] for line in stdout .decode ().splitlines () if line .strip ()]
139- return branches
148+ branches = [line .strip ().split ("refs/heads/ " )[- 1 ] for line in stdout .decode ().splitlines () if line .strip ()]
149+ return [{ "value" : b , "label" : b } for b in branches ]
140150
141151 except Exception as e :
142152 raise UserException (f"Error getting repository branches: { str (e )} " ) from e
0 commit comments