git-copilot-commit 0.4.5__py3-none-any.whl → 0.4.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
git_copilot_commit/cli.py CHANGED
@@ -3,6 +3,7 @@ git-copilot-commit - AI-powered Git commit assistant
3
3
  """
4
4
 
5
5
  from pathlib import Path
6
+ from typing import Annotated
6
7
 
7
8
  import rich
8
9
  import typer
@@ -18,6 +19,27 @@ from . import github_copilot
18
19
  console = Console()
19
20
  app = typer.Typer(help=__doc__, add_completion=False)
20
21
 
22
+ CA_BUNDLE_HELP = (
23
+ "Path to a custom CA bundle (PEM). Use this to test internal / company CAs."
24
+ )
25
+ NATIVE_TLS_HELP = (
26
+ "Use the OS's native certificate store via 'truststore' for httpx instead of "
27
+ "the Python bundle. Ignored if --ca-bundle or --insecure is used."
28
+ )
29
+
30
+ CaBundleOption = Annotated[
31
+ str | None,
32
+ typer.Option("--ca-bundle", metavar="PATH", help=CA_BUNDLE_HELP),
33
+ ]
34
+ InsecureOption = Annotated[
35
+ bool,
36
+ typer.Option("--insecure", help="Disable SSL certificate verification."),
37
+ ]
38
+ NativeTlsOption = Annotated[
39
+ bool,
40
+ typer.Option("--native-tls/--no-native-tls", help=NATIVE_TLS_HELP),
41
+ ]
42
+
21
43
 
22
44
  def version_callback(value: bool):
23
45
  if value:
@@ -84,8 +106,24 @@ def load_system_prompt() -> str:
84
106
  raise typer.Exit(1)
85
107
 
86
108
 
109
+ def build_http_client_config(
110
+ *,
111
+ ca_bundle: str | None,
112
+ insecure: bool,
113
+ native_tls: bool,
114
+ ) -> github_copilot.HttpClientConfig:
115
+ return github_copilot.HttpClientConfig(
116
+ native_tls=native_tls,
117
+ insecure=insecure,
118
+ ca_bundle=ca_bundle,
119
+ )
120
+
121
+
87
122
  def generate_commit_message(
88
- repo: GitRepository, model: str | None = None, context: str = ""
123
+ repo: GitRepository,
124
+ model: str | None = None,
125
+ context: str = "",
126
+ http_client_config: github_copilot.HttpClientConfig | None = None,
89
127
  ) -> str:
90
128
  """Generate a conventional commit message using Copilot API."""
91
129
 
@@ -127,6 +165,7 @@ def generate_commit_message(
127
165
  {prompt}
128
166
  """,
129
167
  model=model,
168
+ http_client_config=http_client_config,
130
169
  )
131
170
 
132
171
 
@@ -157,10 +196,21 @@ def authenticate(
157
196
  force: bool = typer.Option(
158
197
  False, "--force", help="Replace cached GitHub Copilot credentials"
159
198
  ),
199
+ ca_bundle: CaBundleOption = None,
200
+ insecure: InsecureOption = False,
201
+ native_tls: NativeTlsOption = False,
160
202
  ):
161
203
  """Authenticate with GitHub Copilot and cache credentials locally."""
204
+ http_client_config = build_http_client_config(
205
+ ca_bundle=ca_bundle,
206
+ insecure=insecure,
207
+ native_tls=native_tls,
208
+ )
162
209
  try:
163
- github_copilot.login(force=force)
210
+ github_copilot.login(
211
+ force=force,
212
+ http_client_config=http_client_config,
213
+ )
164
214
  except github_copilot.CopilotError as exc:
165
215
  console.print(f"[red]Authentication failed: {exc}[/red]")
166
216
  raise typer.Exit(1)
@@ -183,6 +233,9 @@ def commit(
183
233
  "-c",
184
234
  help="Optional user-provided context to guide commit message",
185
235
  ),
236
+ ca_bundle: CaBundleOption = None,
237
+ insecure: InsecureOption = False,
238
+ native_tls: NativeTlsOption = False,
186
239
  ):
187
240
  """
188
241
  Generate commit message based on changes in the current git repository and commit them.
@@ -198,9 +251,18 @@ def commit(
198
251
  except github_copilot.CopilotError:
199
252
  existing_credentials = None
200
253
 
254
+ http_client_config = build_http_client_config(
255
+ ca_bundle=ca_bundle,
256
+ insecure=insecure,
257
+ native_tls=native_tls,
258
+ )
259
+
201
260
  if existing_credentials is None:
202
261
  try:
203
- github_copilot.login(force=True)
262
+ github_copilot.login(
263
+ force=True,
264
+ http_client_config=http_client_config,
265
+ )
204
266
  except github_copilot.CopilotError as exc:
205
267
  console.print(f"[red]Authentication failed: {exc}[/red]")
206
268
  raise typer.Exit(1)
@@ -248,13 +310,21 @@ def commit(
248
310
  )
249
311
 
250
312
  try:
251
- github_copilot.ensure_auth_ready(model=model)
313
+ github_copilot.ensure_auth_ready(
314
+ model=model,
315
+ http_client_config=http_client_config,
316
+ )
252
317
 
253
318
  # Generate or use provided commit message
254
319
  with console.status(
255
320
  "[yellow]Generating commit message based on [bold]`git diff --staged`[/] ...[/yellow]"
256
321
  ):
257
- commit_message = generate_commit_message(repo, model, context=context)
322
+ commit_message = generate_commit_message(
323
+ repo,
324
+ model,
325
+ context=context,
326
+ http_client_config=http_client_config,
327
+ )
258
328
  except github_copilot.CopilotError as exc:
259
329
  console.print(f"[red]Could not generate a commit message: {exc}[/red]")
260
330
  raise typer.Exit(1)
@@ -13,7 +13,6 @@ from pathlib import Path
13
13
  from typing import Any, Callable, TypeVar
14
14
 
15
15
  import httpx
16
- import typer
17
16
  from rich.console import Console
18
17
  from rich.panel import Panel
19
18
  from rich.table import Table
@@ -42,14 +41,7 @@ DEFAULT_MODEL_PREFERENCES = (
42
41
  "gpt-4o",
43
42
  )
44
43
 
45
- app = typer.Typer(
46
- add_completion=False,
47
- no_args_is_help=True,
48
- help="General-purpose GitHub Copilot CLI.",
49
- )
50
-
51
44
  console = Console()
52
- console_err = Console(stderr=True)
53
45
  T = TypeVar("T")
54
46
 
55
47
 
@@ -164,6 +156,25 @@ class CopilotModel:
164
156
  )
165
157
 
166
158
 
159
+ @dataclass(frozen=True, slots=True)
160
+ class HttpClientConfig:
161
+ native_tls: bool = False
162
+ insecure: bool = False
163
+ ca_bundle: str | None = None
164
+
165
+ @property
166
+ def use_native_tls(self) -> bool:
167
+ return self.native_tls and not self.insecure and self.ca_bundle is None
168
+
169
+ @property
170
+ def verify(self) -> bool | str:
171
+ if self.insecure:
172
+ return False
173
+ if self.ca_bundle:
174
+ return self.ca_bundle
175
+ return True
176
+
177
+
167
178
  @dataclass(slots=True)
168
179
  class GitHubViewer:
169
180
  login: str
@@ -272,8 +283,34 @@ def get_github_copilot_base_url(
272
283
  return "https://api.individual.githubcopilot.com"
273
284
 
274
285
 
275
- def make_http_client() -> httpx.Client:
286
+ _NATIVE_TLS_ENABLED = False
287
+
288
+
289
+ def _maybe_enable_native_tls(native_tls: bool) -> None:
290
+ """
291
+ Globally switch httpx/requests to use the OS certificate store via truststore.
292
+ Safe to call multiple times; it no-ops after the first.
293
+ """
294
+ global _NATIVE_TLS_ENABLED
295
+ if not native_tls or _NATIVE_TLS_ENABLED:
296
+ return
297
+
298
+ try:
299
+ import truststore
300
+
301
+ truststore.inject_into_ssl()
302
+ except Exception as _:
303
+ return
304
+
305
+ _NATIVE_TLS_ENABLED = True
306
+
307
+
308
+ def make_http_client(http_client_config: HttpClientConfig | None = None) -> httpx.Client:
309
+ config = http_client_config or HttpClientConfig()
310
+ _maybe_enable_native_tls(config.use_native_tls)
311
+
276
312
  return httpx.Client(
313
+ verify=config.verify,
277
314
  follow_redirects=True,
278
315
  timeout=httpx.Timeout(30.0, connect=10.0),
279
316
  )
@@ -1140,7 +1177,12 @@ def print_login_summary(
1140
1177
  console.print(Panel.fit(table, title="Login Summary"))
1141
1178
 
1142
1179
 
1143
- def login(enterprise_domain: str | None = None, force: bool = False) -> None:
1180
+ def login(
1181
+ enterprise_domain: str | None = None,
1182
+ force: bool = False,
1183
+ *,
1184
+ http_client_config: HttpClientConfig | None = None,
1185
+ ) -> None:
1144
1186
  """Authenticate with GitHub and cache Copilot credentials locally."""
1145
1187
  normalized_domain = normalize_domain(enterprise_domain)
1146
1188
  if enterprise_domain and not normalized_domain:
@@ -1159,7 +1201,7 @@ def login(enterprise_domain: str | None = None, force: bool = False) -> None:
1159
1201
  )
1160
1202
 
1161
1203
  domain = normalized_domain or DEFAULT_GITHUB_DOMAIN
1162
- with make_http_client() as client:
1204
+ with make_http_client(http_client_config) as client:
1163
1205
  device = start_device_flow(client, domain)
1164
1206
 
1165
1207
  console.print(
@@ -1220,9 +1262,13 @@ def _ask_once(client: httpx.Client, prompt: str, model: str | None = None) -> st
1220
1262
  )
1221
1263
 
1222
1264
 
1223
- def _with_reauthentication(action: Callable[[httpx.Client], T]) -> T:
1265
+ def _with_reauthentication(
1266
+ action: Callable[[httpx.Client], T],
1267
+ *,
1268
+ http_client_config: HttpClientConfig | None = None,
1269
+ ) -> T:
1224
1270
  try:
1225
- with make_http_client() as client:
1271
+ with make_http_client(http_client_config) as client:
1226
1272
  return action(client)
1227
1273
  except CopilotError as exc:
1228
1274
  if not should_reauthenticate(exc):
@@ -1231,21 +1277,33 @@ def _with_reauthentication(action: Callable[[httpx.Client], T]) -> T:
1231
1277
  console.print(
1232
1278
  "[yellow]Cached GitHub Copilot credentials are missing or no longer valid. Starting authentication...[/yellow]"
1233
1279
  )
1234
- login(force=True)
1280
+ login(force=True, http_client_config=http_client_config)
1235
1281
 
1236
- with make_http_client() as client:
1282
+ with make_http_client(http_client_config) as client:
1237
1283
  return action(client)
1238
1284
 
1239
1285
 
1240
- def ensure_auth_ready(model: str | None = None) -> None:
1286
+ def ensure_auth_ready(
1287
+ model: str | None = None,
1288
+ *,
1289
+ http_client_config: HttpClientConfig | None = None,
1290
+ ) -> None:
1241
1291
  def validate(client: httpx.Client) -> None:
1242
1292
  credentials = ensure_fresh_credentials(client)
1243
1293
  all_models = list_models(client, credentials)
1244
1294
  pick_model(all_models, model)
1245
1295
 
1246
- _with_reauthentication(validate)
1296
+ _with_reauthentication(validate, http_client_config=http_client_config)
1247
1297
 
1248
1298
 
1249
- def ask(prompt: str, model: str | None = None) -> str:
1299
+ def ask(
1300
+ prompt: str,
1301
+ model: str | None = None,
1302
+ *,
1303
+ http_client_config: HttpClientConfig | None = None,
1304
+ ) -> str:
1250
1305
  """Send a prompt to GitHub Copilot and print the reply."""
1251
- return _with_reauthentication(lambda client: _ask_once(client, prompt, model))
1306
+ return _with_reauthentication(
1307
+ lambda client: _ask_once(client, prompt, model),
1308
+ http_client_config=http_client_config,
1309
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-copilot-commit
3
- Version: 0.4.5
3
+ Version: 0.4.6
4
4
  Summary: Automatically generate and commit changes using copilot
5
5
  Author-email: Dheepak Krishnamurthy <1813121+kdheepak@users.noreply.github.com>
6
6
  License-File: LICENSE
@@ -8,6 +8,7 @@ Requires-Python: >=3.12
8
8
  Requires-Dist: httpx>=0.28.0
9
9
  Requires-Dist: platformdirs>=4.0.0
10
10
  Requires-Dist: rich>=14.0.0
11
+ Requires-Dist: truststore>=0.10.4
11
12
  Requires-Dist: typer>=0.16.0
12
13
  Description-Content-Type: text/markdown
13
14
 
@@ -1,13 +1,13 @@
1
1
  git_copilot_commit/__init__.py,sha256=v3x5oBkxwKJEZLv62QqSmP3iqNKLtZgrWZfH8eFzlQg,60
2
- git_copilot_commit/cli.py,sha256=gXRoJDhpW0Ck7-sBDpMmulYztUH1IExuF-SczbgQqF0,9434
2
+ git_copilot_commit/cli.py,sha256=-zC3HeYvbEO3ahPNfB7C-GjRiXJT-juOF9eeLmqKwRo,11375
3
3
  git_copilot_commit/git.py,sha256=f42GawgkyrsFkl127XvDrdg2xVEf87lb-5QO04nuRoU,9459
4
- git_copilot_commit/github_copilot.py,sha256=lP-LLk4mOC4D9-rCtxIBs7XN_oFImZHpoKyHNBQFf-c,39484
4
+ git_copilot_commit/github_copilot.py,sha256=LxLKyqt6Pnoks5yyWJANJbbZSLrPBSJeycgmlXydYY4,40937
5
5
  git_copilot_commit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  git_copilot_commit/settings.py,sha256=asaCxX_TAr5lCRkoSLtHSk1eUrT-y4bJbLUNQZSzAs0,2793
7
7
  git_copilot_commit/version.py,sha256=AieHOUX52g6N67HL0iLWtDKrgOYyulxwHWViu26Jrd4,105
8
8
  git_copilot_commit/prompts/commit-message-generator-prompt.md,sha256=ZvllyqtsLRwj6NmvygNGFajdLKNkO67hUnLsR_P1WOs,2370
9
- git_copilot_commit-0.4.5.dist-info/METADATA,sha256=BFvZy64MhMB6GGeAiBS6L7N5EcPqNS_9miCE8roZ9E8,4059
10
- git_copilot_commit-0.4.5.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
11
- git_copilot_commit-0.4.5.dist-info/entry_points.txt,sha256=Imboc0oJa4Oq1O3C-wWcy7ZxfsVSkkC-OC-iPbTn3Fg,66
12
- git_copilot_commit-0.4.5.dist-info/licenses/LICENSE,sha256=14lNZAoKJPI1U7eGpletjN_PFm1JwP1vT_0jFKY6eWg,1065
13
- git_copilot_commit-0.4.5.dist-info/RECORD,,
9
+ git_copilot_commit-0.4.6.dist-info/METADATA,sha256=QThEE-BBVwlHTRuetVwP9zZHtO3ikrUAGWt0DFtQCXc,4093
10
+ git_copilot_commit-0.4.6.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
11
+ git_copilot_commit-0.4.6.dist-info/entry_points.txt,sha256=Imboc0oJa4Oq1O3C-wWcy7ZxfsVSkkC-OC-iPbTn3Fg,66
12
+ git_copilot_commit-0.4.6.dist-info/licenses/LICENSE,sha256=14lNZAoKJPI1U7eGpletjN_PFm1JwP1vT_0jFKY6eWg,1065
13
+ git_copilot_commit-0.4.6.dist-info/RECORD,,