@@ -48,6 +48,8 @@ class GitHook(BaseHook):
4848 * ``ssh_config_file`` — path to a custom SSH config file.
4949 * ``host_proxy_cmd`` — SSH ProxyCommand string (e.g. for bastion/jump hosts).
5050 * ``ssh_port`` — non-default SSH port.
51+ * ``github_app_id`` — App ID needed for GitHub App Authentication.
52+ * ``github_installation_id`` — Installation ID needed for GitHub App Authentication.
5153 """
5254
5355 conn_name_attr = "git_conn_id"
@@ -75,6 +77,8 @@ def get_ui_field_behaviour(cls) -> dict[str, Any]:
7577 "ssh_config_file" : "" ,
7678 "host_proxy_cmd" : "" ,
7779 "ssh_port" : "" ,
80+ "github_app_id" : "" ,
81+ "github_installation_id" : "" ,
7882 }
7983 )
8084 },
@@ -103,10 +107,26 @@ def __init__(
103107 self .host_proxy_cmd = extra .get ("host_proxy_cmd" )
104108 self .ssh_port : int | None = int (extra ["ssh_port" ]) if extra .get ("ssh_port" ) else None
105109
110+ # GitHub App Auth Options
111+ self .github_app_id = extra .get ("github_app_id" )
112+ self .github_installation_id : int | None = (
113+ int (extra ["github_installation_id" ]) if extra .get ("github_installation_id" ) else None
114+ )
115+
106116 self .env : dict [str , str ] = {}
107117
108118 if self .key_file and self .private_key :
109119 raise AirflowException ("Both 'key_file' and 'private_key' cannot be provided at the same time" )
120+ if (self .github_app_id and not self .github_installation_id ) or (
121+ not self .github_app_id and self .github_installation_id
122+ ):
123+ raise AirflowException (
124+ "Both 'github_app_id' and 'github_installation_id' must be provided to use GitHub App Authentication"
125+ )
126+ if self .github_app_id and self .github_installation_id and self .private_key :
127+ if not self .key_file and not self .private_key :
128+ raise AirflowException ("Missing inline private_key or key_file for GitHub App Auth" )
129+ self .user_name , self .auth_token = self ._get_github_app_token ()
110130 self ._process_git_auth_url ()
111131
112132 def _build_ssh_command (self , key_path : str | None = None ) -> str :
@@ -134,6 +154,17 @@ def _build_ssh_command(self, key_path: str | None = None) -> str:
134154
135155 return " " .join (parts )
136156
157+ def _get_github_app_token (self ):
158+ from github import Auth as GithubAuth , Github as GithubClient
159+
160+ github_auth = GithubAuth .AppAuth (
161+ app_id = self .github_app_id , private_key = self .private_key
162+ ).get_installation_auth (installation_id = self .github_installation_id )
163+
164+ # Client is needed to generate the token
165+ GithubClient (auth = github_auth )
166+ return "x-access-token" , github_auth .token
167+
137168 def _process_git_auth_url (self ):
138169 if not isinstance (self .repo_url , str ):
139170 return
0 commit comments