youtrack-cli 0.3.4__tar.gz → 0.3.6__tar.gz
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.
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/PKG-INFO +1 -1
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/pyproject.toml +1 -1
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/uv.lock +1 -1
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/admin.py +61 -39
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/main.py +9 -5
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/.claude/settings.local.json +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/.github/dependabot.yml +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/.github/workflows/ci.yml +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/.github/workflows/release.yml +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/.gitignore +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/.pre-commit-config.yaml +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/.readthedocs.yaml +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/CLAUDE.md +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/PUBLISHING.md +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/README.md +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/Makefile +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/api/index.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/changelog.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/command-aliases.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/commands/admin.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/commands/articles.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/commands/auth.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/commands/boards.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/commands/config.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/commands/index.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/commands/issues.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/commands/projects.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/commands/reports.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/commands/time.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/commands/users.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/conf.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/configuration.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/development.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/index.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/installation.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/learning-path.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/logging.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/performance.md +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/progress-indicators.md +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/quickstart.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/requirements.txt +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/security.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/troubleshooting.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/workflows.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/docs/youtrack-concepts.rst +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/justfile +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/package-lock.json +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/package.json +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/__init__.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/conftest.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_admin.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_articles.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_auth.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_boards.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_config.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_issues.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_logging.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_main.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_projects.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_reports.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_security.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_time.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tests/test_users.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/tox.ini +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/__init__.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/articles.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/auth.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/boards.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/cache.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/cli_utils/__init__.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/cli_utils/aliases.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/client.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/commands/__init__.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/commands/articles.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/commands/boards.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/commands/common.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/commands/issues.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/commands/projects.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/commands/time_tracking.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/commands/users.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/common.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/config.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/exceptions.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/issues.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/logging.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/performance.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/progress.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/projects.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/py.typed +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/reports.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/security.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/time.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/users.py +0 -0
- {youtrack_cli-0.3.4 → youtrack_cli-0.3.6}/youtrack_cli/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: youtrack-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: YouTrack CLI - Command line interface for JetBrains YouTrack issue tracking system
|
|
5
5
|
Project-URL: Homepage, https://github.com/ryan-murphy/yt-cli
|
|
6
6
|
Project-URL: Documentation, https://yt-cli.readthedocs.io/
|
|
@@ -5,7 +5,6 @@ from typing import Any, Optional
|
|
|
5
5
|
import httpx
|
|
6
6
|
from rich.console import Console
|
|
7
7
|
from rich.table import Table
|
|
8
|
-
from rich.text import Text
|
|
9
8
|
|
|
10
9
|
from .auth import AuthManager
|
|
11
10
|
|
|
@@ -201,7 +200,7 @@ class AdminManager:
|
|
|
201
200
|
async with httpx.AsyncClient() as client:
|
|
202
201
|
try:
|
|
203
202
|
response = await client.get(
|
|
204
|
-
f"{credentials.base_url.rstrip('/')}/api/admin/globalSettings/license
|
|
203
|
+
f"{credentials.base_url.rstrip('/')}/api/admin/globalSettings/license?fields=id,username,license,error",
|
|
205
204
|
headers=headers,
|
|
206
205
|
timeout=10.0,
|
|
207
206
|
)
|
|
@@ -242,8 +241,8 @@ class AdminManager:
|
|
|
242
241
|
|
|
243
242
|
# List of endpoints to try, in order of preference
|
|
244
243
|
endpoints = [
|
|
244
|
+
"/api/admin/globalSettings/systemSettings?fields=baseUrl,isApplicationReadOnly,maxUploadFileSize,maxExportItems,allowStatisticsCollection",
|
|
245
245
|
"/api/admin/globalSettings/systemSettings",
|
|
246
|
-
"/api/admin/globalSettings/systemSettings?fields=baseUrl,isApplicationReadOnly,maxUploadFileSize,maxExportItems",
|
|
247
246
|
]
|
|
248
247
|
|
|
249
248
|
async with httpx.AsyncClient() as client:
|
|
@@ -370,7 +369,7 @@ class AdminManager:
|
|
|
370
369
|
async with httpx.AsyncClient() as client:
|
|
371
370
|
try:
|
|
372
371
|
response = await client.get(
|
|
373
|
-
f"{credentials.base_url.rstrip('/')}/api/rest/usergroups",
|
|
372
|
+
f"{credentials.base_url.rstrip('/')}/hub/api/rest/usergroups",
|
|
374
373
|
headers=headers,
|
|
375
374
|
params=params,
|
|
376
375
|
timeout=10.0,
|
|
@@ -540,19 +539,32 @@ class AdminManager:
|
|
|
540
539
|
license_info: License information dictionary
|
|
541
540
|
"""
|
|
542
541
|
self.console.print("\n[bold blue]License Information[/bold blue]")
|
|
543
|
-
self.console.print(f"[cyan]License Type:[/cyan] {license_info.get('type', 'N/A')}")
|
|
544
|
-
self.console.print(f"[cyan]Licensed To:[/cyan] {license_info.get('licensedTo', 'N/A')}")
|
|
545
542
|
|
|
546
|
-
|
|
547
|
-
|
|
543
|
+
# Display username (maps to "Licensed To")
|
|
544
|
+
username = license_info.get("username", "N/A")
|
|
545
|
+
self.console.print(f"[cyan]Licensed To:[/cyan] {username}")
|
|
548
546
|
|
|
549
|
-
|
|
550
|
-
|
|
547
|
+
# Display license key (truncated for security)
|
|
548
|
+
license_key = license_info.get("license", "N/A")
|
|
549
|
+
if license_key != "N/A" and len(license_key) > 20:
|
|
550
|
+
# Show only first 20 characters for security
|
|
551
|
+
license_display = f"{license_key[:20]}..."
|
|
552
|
+
else:
|
|
553
|
+
license_display = license_key
|
|
554
|
+
self.console.print(f"[cyan]License Key:[/cyan] {license_display}")
|
|
555
|
+
|
|
556
|
+
# Display error status if present
|
|
557
|
+
error = license_info.get("error")
|
|
558
|
+
if error:
|
|
559
|
+
self.console.print(f"[cyan]Error:[/cyan] [red]{error}[/red]")
|
|
560
|
+
status_color = "red"
|
|
561
|
+
status_text = "Error"
|
|
562
|
+
else:
|
|
563
|
+
# If no error and license key exists, assume active
|
|
564
|
+
is_active = license_key != "N/A" and license_key is not None
|
|
565
|
+
status_color = "green" if is_active else "red"
|
|
566
|
+
status_text = "Active" if is_active else "No License"
|
|
551
567
|
|
|
552
|
-
# Display active status
|
|
553
|
-
is_active = license_info.get("isActive", False)
|
|
554
|
-
status_color = "green" if is_active else "red"
|
|
555
|
-
status_text = "Active" if is_active else "Inactive"
|
|
556
568
|
self.console.print(f"[cyan]Status:[/cyan] [{status_color}]{status_text}[/{status_color}]")
|
|
557
569
|
|
|
558
570
|
def display_system_health(self, health_info: dict[str, Any]) -> None:
|
|
@@ -561,31 +573,41 @@ class AdminManager:
|
|
|
561
573
|
Args:
|
|
562
574
|
health_info: Health information dictionary
|
|
563
575
|
"""
|
|
564
|
-
self.console.print("\n[bold blue]System
|
|
565
|
-
|
|
566
|
-
#
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
if
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
576
|
+
self.console.print("\n[bold blue]System Settings[/bold blue]")
|
|
577
|
+
|
|
578
|
+
# Display system settings information
|
|
579
|
+
base_url = health_info.get("baseUrl", "N/A")
|
|
580
|
+
self.console.print(f"[cyan]Base URL:[/cyan] {base_url}")
|
|
581
|
+
|
|
582
|
+
# Display read-only status
|
|
583
|
+
is_read_only = health_info.get("isApplicationReadOnly", False)
|
|
584
|
+
read_only_color = "red" if is_read_only else "green"
|
|
585
|
+
read_only_text = "Read-Only" if is_read_only else "Read-Write"
|
|
586
|
+
self.console.print(f"[cyan]Application Mode:[/cyan] [{read_only_color}]{read_only_text}[/{read_only_color}]")
|
|
587
|
+
|
|
588
|
+
# Display file upload settings
|
|
589
|
+
max_upload_size = health_info.get("maxUploadFileSize", "N/A")
|
|
590
|
+
if max_upload_size != "N/A":
|
|
591
|
+
# Convert bytes to MB for readability
|
|
592
|
+
max_upload_mb = max_upload_size / (1024 * 1024)
|
|
593
|
+
self.console.print(f"[cyan]Max Upload Size:[/cyan] {max_upload_mb:.1f} MB")
|
|
594
|
+
else:
|
|
595
|
+
self.console.print(f"[cyan]Max Upload Size:[/cyan] {max_upload_size}")
|
|
596
|
+
|
|
597
|
+
# Display export settings
|
|
598
|
+
max_export_items = health_info.get("maxExportItems", "N/A")
|
|
599
|
+
self.console.print(f"[cyan]Max Export Items:[/cyan] {max_export_items}")
|
|
600
|
+
|
|
601
|
+
# Display statistics collection status
|
|
602
|
+
stats_collection = health_info.get("allowStatisticsCollection", False)
|
|
603
|
+
stats_color = "green" if stats_collection else "yellow"
|
|
604
|
+
stats_text = "Enabled" if stats_collection else "Disabled"
|
|
605
|
+
self.console.print(f"[cyan]Statistics Collection:[/cyan] [{stats_color}]{stats_text}[/{stats_color}]")
|
|
606
|
+
|
|
607
|
+
# Overall system status based on read-only mode
|
|
608
|
+
overall_status = "Healthy" if not is_read_only else "Read-Only Mode"
|
|
609
|
+
status_color = "green" if not is_read_only else "yellow"
|
|
610
|
+
self.console.print(f"[cyan]Overall Status:[/cyan] [{status_color}]{overall_status}[/{status_color}]")
|
|
589
611
|
|
|
590
612
|
def display_user_groups(self, groups: list[dict[str, Any]]) -> None:
|
|
591
613
|
"""Display user groups in a formatted table.
|
|
@@ -1044,11 +1044,15 @@ def usage(ctx: click.Context) -> None:
|
|
|
1044
1044
|
console.print(f"[red]Error:[/red] {result['message']}")
|
|
1045
1045
|
return
|
|
1046
1046
|
|
|
1047
|
-
|
|
1048
|
-
console.print("[bold]License
|
|
1049
|
-
console.print(f"
|
|
1050
|
-
console.print(f"
|
|
1051
|
-
console.print(f"
|
|
1047
|
+
license_data = result["data"]
|
|
1048
|
+
console.print("[bold]License Information:[/bold]")
|
|
1049
|
+
console.print(f"License ID: {license_data.get('id', 'N/A')}")
|
|
1050
|
+
console.print(f"Username: {license_data.get('username', 'N/A')}")
|
|
1051
|
+
console.print(f"License Key: {license_data.get('license', 'N/A')}")
|
|
1052
|
+
if license_data.get("error"):
|
|
1053
|
+
console.print(f"[red]Error:[/red] {license_data['error']}")
|
|
1054
|
+
else:
|
|
1055
|
+
console.print("[green]Status: Valid[/green]")
|
|
1052
1056
|
|
|
1053
1057
|
asyncio.run(run_license_usage())
|
|
1054
1058
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|