Skip to content

Commit 2247e65

Browse files
committed
feat(audit): add WSL detection and GitLab rate limit display
- Add is_wsl() function to detect WSL environment - Add get_gitlab_rate_limit() for GitLab API rate limit checking - Show WSL platform info in update mode header - Show GitLab rate limit alongside GitHub rate limit
1 parent f96aee6 commit 2247e65

3 files changed

Lines changed: 103 additions & 1 deletion

File tree

audit.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from cli_audit.detection import audit_tool_installation # noqa: E402
2929
from cli_audit.snapshot import load_snapshot, write_snapshot, render_from_snapshot, get_snapshot_path # noqa: E402
3030
from cli_audit.render import render_table, print_summary, status_icon # noqa: E402
31-
from cli_audit.collectors import get_github_rate_limit, get_github_rate_limit_help # noqa: E402
31+
from cli_audit.collectors import get_github_rate_limit, get_github_rate_limit_help, get_gitlab_rate_limit, is_wsl # noqa: E402
3232
from cli_audit import collectors # noqa: E402
3333
from cli_audit.logging_config import setup_logging # noqa: E402
3434
# Split file support (Phase 2.1)
@@ -344,6 +344,10 @@ def cmd_update(args: argparse.Namespace) -> int:
344344
print("Update Mode", file=sys.stderr)
345345
print("=" * 80, file=sys.stderr)
346346

347+
# Show platform info
348+
if is_wsl():
349+
print("ℹ️ Platform: WSL (Windows Subsystem for Linux)", file=sys.stderr)
350+
347351
# Get tools to audit
348352
tools_list = filter_tools(args.tools) if args.tools else all_tools()
349353
total = len(tools_list)
@@ -375,6 +379,27 @@ def cmd_update(args: argparse.Namespace) -> int:
375379
else:
376380
print(f"✓ GitHub rate limit: {remaining}/{limit} remaining{auth_info}", file=sys.stderr)
377381

382+
# Show GitLab rate limit if we have GitLab tools
383+
gitlab_rate = get_gitlab_rate_limit()
384+
if gitlab_rate:
385+
gl_remaining = gitlab_rate.get("remaining", 0)
386+
gl_limit = gitlab_rate.get("limit", 0)
387+
gl_authenticated = gitlab_rate.get("authenticated", False)
388+
gl_token_source = gitlab_rate.get("token_source", "")
389+
gl_host = gitlab_rate.get("host", "gitlab.com")
390+
391+
gl_auth_info = ""
392+
if gl_authenticated:
393+
if gl_token_source == "glab_cli":
394+
gl_auth_info = " (via glab CLI)"
395+
else:
396+
gl_auth_info = " (via GITLAB_TOKEN)"
397+
398+
if gl_remaining < gl_limit * 0.2:
399+
print(f"⚠️ GitLab rate limit ({gl_host}): {gl_remaining}/{gl_limit} remaining{gl_auth_info}", file=sys.stderr)
400+
else:
401+
print(f"✓ GitLab rate limit ({gl_host}): {gl_remaining}/{gl_limit} remaining{gl_auth_info}", file=sys.stderr)
402+
378403
print(f"# Collecting fresh data for {total} tools...", file=sys.stderr)
379404
est_time = int((total / MAX_WORKERS) * 3 * 1.5)
380405
print(f"# Estimated time: ~{est_time}s (timeout=3s per tool, {MAX_WORKERS} workers)", file=sys.stderr)

cli_audit/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
collect_crates,
2626
normalize_version_tag,
2727
get_github_rate_limit,
28+
get_gitlab_rate_limit,
29+
is_wsl,
2830
)
2931
from .tools import Tool, all_tools, filter_tools, get_tool, tool_homepage_url, latest_target_url # noqa: E402
3032
from .detection import ( # noqa: E402
@@ -139,6 +141,8 @@
139141
"normalize_version_tag",
140142
"extract_version_number",
141143
"get_github_rate_limit",
144+
"get_gitlab_rate_limit",
145+
"is_wsl",
142146
# Breaking changes
143147
"is_major_upgrade",
144148
"check_breaking_change_policy",

cli_audit/collectors.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,3 +489,76 @@ def get_github_rate_limit_help() -> str:
489489
lines.append(" export GITHUB_TOKEN='ghp_your_token_here'")
490490

491491
return "\n".join(lines)
492+
493+
494+
def get_gitlab_rate_limit(host: str = "gitlab.com") -> dict[str, Any]:
495+
"""Get GitLab API rate limit status.
496+
497+
Args:
498+
host: GitLab host (default: gitlab.com)
499+
500+
Returns:
501+
Dictionary with rate limit info or empty dict on failure
502+
"""
503+
import os
504+
505+
try:
506+
headers = {"User-Agent": "ai-cli-preparation/2.0"}
507+
508+
# Try to get token from environment
509+
token = os.environ.get("GITLAB_TOKEN") or os.environ.get("GITLAB_PRIVATE_TOKEN")
510+
token_source = "GITLAB_TOKEN" if token else None
511+
512+
# Try glab CLI if no token in environment
513+
if not token:
514+
import subprocess
515+
try:
516+
result = subprocess.run(
517+
["glab", "auth", "token", "--hostname", host],
518+
capture_output=True, text=True, timeout=5
519+
)
520+
if result.returncode == 0 and result.stdout.strip():
521+
token = result.stdout.strip()
522+
token_source = "glab_cli"
523+
except Exception:
524+
pass
525+
526+
if token:
527+
headers["PRIVATE-TOKEN"] = token
528+
529+
# GitLab returns rate limit info in response headers
530+
# We make a simple API call and check the headers
531+
import urllib.request
532+
url = f"https://{host}/api/v4/user"
533+
req = urllib.request.Request(url, headers=headers)
534+
535+
with urllib.request.urlopen(req, timeout=5) as resp:
536+
# GitLab rate limit headers
537+
limit = int(resp.headers.get("RateLimit-Limit", 0))
538+
remaining = int(resp.headers.get("RateLimit-Remaining", 0))
539+
reset = int(resp.headers.get("RateLimit-Reset", 0))
540+
541+
return {
542+
"limit": limit,
543+
"remaining": remaining,
544+
"reset": reset,
545+
"authenticated": token is not None,
546+
"token_source": token_source,
547+
"host": host,
548+
}
549+
except Exception as e:
550+
logger.debug(f"Failed to get GitLab rate limit: {e}")
551+
return {}
552+
553+
554+
def is_wsl() -> bool:
555+
"""Detect if running in Windows Subsystem for Linux.
556+
557+
Returns:
558+
True if running in WSL, False otherwise
559+
"""
560+
try:
561+
with open("/proc/version", "r") as f:
562+
return "microsoft" in f.read().lower()
563+
except Exception:
564+
return False

0 commit comments

Comments
 (0)