ghnova 0.3.0__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.
Files changed (60) hide show
  1. ghnova/__init__.py +8 -0
  2. ghnova/__main__.py +8 -0
  3. ghnova/cli/__init__.py +1 -0
  4. ghnova/cli/config/__init__.py +1 -0
  5. ghnova/cli/config/add.py +48 -0
  6. ghnova/cli/config/delete.py +50 -0
  7. ghnova/cli/config/list.py +40 -0
  8. ghnova/cli/config/main.py +27 -0
  9. ghnova/cli/config/update.py +59 -0
  10. ghnova/cli/issue/__init__.py +7 -0
  11. ghnova/cli/issue/create.py +155 -0
  12. ghnova/cli/issue/get.py +119 -0
  13. ghnova/cli/issue/list.py +267 -0
  14. ghnova/cli/issue/lock.py +110 -0
  15. ghnova/cli/issue/main.py +31 -0
  16. ghnova/cli/issue/unlock.py +101 -0
  17. ghnova/cli/issue/update.py +164 -0
  18. ghnova/cli/main.py +117 -0
  19. ghnova/cli/repository/__init__.py +1 -0
  20. ghnova/cli/repository/list.py +201 -0
  21. ghnova/cli/repository/main.py +21 -0
  22. ghnova/cli/user/__init__.py +1 -0
  23. ghnova/cli/user/ctx_info.py +105 -0
  24. ghnova/cli/user/get.py +98 -0
  25. ghnova/cli/user/list.py +78 -0
  26. ghnova/cli/user/main.py +27 -0
  27. ghnova/cli/user/update.py +164 -0
  28. ghnova/cli/utils/__init__.py +7 -0
  29. ghnova/cli/utils/auth.py +67 -0
  30. ghnova/client/__init__.py +8 -0
  31. ghnova/client/async_github.py +121 -0
  32. ghnova/client/base.py +78 -0
  33. ghnova/client/github.py +107 -0
  34. ghnova/config/__init__.py +8 -0
  35. ghnova/config/manager.py +209 -0
  36. ghnova/config/model.py +58 -0
  37. ghnova/issue/__init__.py +8 -0
  38. ghnova/issue/async_issue.py +554 -0
  39. ghnova/issue/base.py +469 -0
  40. ghnova/issue/issue.py +584 -0
  41. ghnova/repository/__init__.py +8 -0
  42. ghnova/repository/async_repository.py +134 -0
  43. ghnova/repository/base.py +124 -0
  44. ghnova/repository/repository.py +134 -0
  45. ghnova/resource/__init__.py +8 -0
  46. ghnova/resource/async_resource.py +88 -0
  47. ghnova/resource/resource.py +88 -0
  48. ghnova/user/__init__.py +8 -0
  49. ghnova/user/async_user.py +285 -0
  50. ghnova/user/base.py +214 -0
  51. ghnova/user/user.py +285 -0
  52. ghnova/utils/__init__.py +16 -0
  53. ghnova/utils/log.py +70 -0
  54. ghnova/utils/response.py +67 -0
  55. ghnova/version.py +11 -0
  56. ghnova-0.3.0.dist-info/METADATA +194 -0
  57. ghnova-0.3.0.dist-info/RECORD +60 -0
  58. ghnova-0.3.0.dist-info/WHEEL +4 -0
  59. ghnova-0.3.0.dist-info/entry_points.txt +2 -0
  60. ghnova-0.3.0.dist-info/licenses/LICENSE +21 -0
ghnova/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """Top-level package for ghnova."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ghnova import client
6
+ from ghnova.version import __version__
7
+
8
+ __all__ = ["__version__", "client"]
ghnova/__main__.py ADDED
@@ -0,0 +1,8 @@
1
+ """Main entry point for the ghnova package."""
2
+
3
+ from __future__ import annotations
4
+
5
+ if __name__ == "__main__":
6
+ from ghnova.utils.log import setup_logger
7
+
8
+ setup_logger(print_version=True)
ghnova/cli/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Command line interface for ghnova."""
@@ -0,0 +1 @@
1
+ """Command line interface for configurations."""
@@ -0,0 +1,48 @@
1
+ """Add command for config CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ import typer
8
+
9
+
10
+ def add_command(
11
+ ctx: typer.Context,
12
+ name: Annotated[
13
+ str,
14
+ typer.Option("--name", help="Name of the account. It does not need to be the same as the GitHub account name."),
15
+ ],
16
+ token: Annotated[str, typer.Option("--token", help="Token for authentication.")],
17
+ base_url: Annotated[str, typer.Option("--base-url", help="Base URL of the platform.")] = "https://github.com",
18
+ is_default: Annotated[bool, typer.Option("--default", help="Set as default account.")] = False,
19
+ ) -> None:
20
+ """Add a new account to the configuration.
21
+
22
+ Args:
23
+ ctx: Typer context.
24
+ name: Name of the account. It does not need to be the same as the GutHub account name.
25
+ token: Token for authentication.
26
+ base_url: Base URL of the platform.
27
+ is_default: Set as default account.
28
+
29
+ """
30
+ import logging # noqa: PLC0415
31
+
32
+ from ghnova.config.manager import ConfigManager # noqa: PLC0415
33
+
34
+ logger = logging.getLogger("ghnova")
35
+
36
+ config_manager = ConfigManager(filename=ctx.obj["config_path"])
37
+
38
+ logger.info("Configuration path: %s", config_manager.config_path)
39
+
40
+ config_manager.load_config()
41
+
42
+ try:
43
+ config_manager.add_account(name=name, token=token, base_url=base_url, is_default=is_default)
44
+ config_manager.save_config()
45
+ logger.info("Account '%s' added successfully.", name)
46
+ except ValueError as e:
47
+ logger.error("Error adding account: %s", e)
48
+ raise typer.Exit(code=1) from e
@@ -0,0 +1,50 @@
1
+ """Delete command for config CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ import typer
8
+
9
+
10
+ def delete_command(
11
+ ctx: typer.Context,
12
+ name: Annotated[
13
+ str,
14
+ typer.Option("--name", help="Name of the account. It does not need to be the same as the GitHub account name."),
15
+ ],
16
+ force: Annotated[bool, typer.Option("--force", "-f", help="Force deletion without confirmation.")] = False,
17
+ ) -> None:
18
+ """Delete the configuration of an existing account.
19
+
20
+ Args:
21
+ ctx: Typer context.
22
+ name: Name of the account.
23
+ force: Force deletion without confirmation.
24
+
25
+ """
26
+ import logging # noqa: PLC0415
27
+
28
+ from ghnova.config.manager import ConfigManager # noqa: PLC0415
29
+
30
+ if not force:
31
+ confirm = typer.confirm(f"Are you sure you want to delete the account '{name}'?")
32
+ if not confirm:
33
+ typer.echo("Deletion cancelled.")
34
+ raise typer.Exit()
35
+
36
+ logger = logging.getLogger("ghnova")
37
+
38
+ config_manager = ConfigManager(filename=ctx.obj["config_path"])
39
+
40
+ logger.info("Configuration path: %s", config_manager.config_path)
41
+
42
+ config_manager.load_config()
43
+
44
+ try:
45
+ config_manager.delete_account(name=name)
46
+ config_manager.save_config()
47
+ logger.info("Account '%s' deleted successfully.", name)
48
+ except ValueError as e:
49
+ logger.error("Error deleting account: %s", e)
50
+ raise typer.Exit(code=1) from e
@@ -0,0 +1,40 @@
1
+ """List command for config CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+
8
+ def list_command(ctx: typer.Context) -> None:
9
+ """List all configured accounts.
10
+
11
+ Args:
12
+ ctx: Typer context.
13
+
14
+ """
15
+ import logging # noqa: PLC0415
16
+
17
+ from ghnova.config.manager import ConfigManager # noqa: PLC0415
18
+
19
+ logger = logging.getLogger("ghnova")
20
+
21
+ config_manager = ConfigManager(filename=ctx.obj["config_path"])
22
+
23
+ logger.info("Configuration path: %s", config_manager.config_path)
24
+
25
+ config_manager.load_config()
26
+
27
+ accounts = config_manager._config.accounts
28
+
29
+ default_account_name = config_manager._config.default_account
30
+ if not default_account_name:
31
+ typer.echo("Default account: None")
32
+ else:
33
+ typer.echo(f"Default account: {default_account_name}")
34
+
35
+ typer.echo("Configured accounts:")
36
+
37
+ for account in accounts.values():
38
+ typer.echo(f" Name: {account.name}")
39
+ typer.echo(f" Base URL: {account.base_url}")
40
+ typer.echo("")
@@ -0,0 +1,27 @@
1
+ """Configuration CLI commands for ghnova."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ config_app = typer.Typer(
8
+ name="config",
9
+ help="Manage github configuration.",
10
+ rich_markup_mode="rich",
11
+ )
12
+
13
+
14
+ def register_commands() -> None:
15
+ """Register config subcommands."""
16
+ from ghnova.cli.config.add import add_command # noqa: PLC0415
17
+ from ghnova.cli.config.delete import delete_command # noqa: PLC0415
18
+ from ghnova.cli.config.list import list_command # noqa: PLC0415
19
+ from ghnova.cli.config.update import update_command # noqa: PLC0415
20
+
21
+ config_app.command(name="add")(add_command)
22
+ config_app.command(name="delete")(delete_command)
23
+ config_app.command(name="list")(list_command)
24
+ config_app.command(name="update")(update_command)
25
+
26
+
27
+ register_commands()
@@ -0,0 +1,59 @@
1
+ """Update command for config CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ import typer
8
+
9
+
10
+ def update_command(
11
+ ctx: typer.Context,
12
+ name: Annotated[str, typer.Option("--name", help="Name of the account.")],
13
+ token: Annotated[str | None, typer.Option("--token", help="Token for authentication.")] = None,
14
+ base_url: Annotated[str | None, typer.Option("--base-url", help="Base URL of the GitHub platform.")] = None,
15
+ default: Annotated[bool | None, typer.Option("--default", help="Set as default account.")] = None,
16
+ ) -> None:
17
+ """Update the configuration of an existing platform.
18
+
19
+ Args:
20
+ ctx: Typer context.
21
+ name: Name of the account.
22
+ token: Token for authentication.
23
+ base_url: Base URL of the platform.
24
+ default: Set as default account.
25
+
26
+ """
27
+ import logging # noqa: PLC0415
28
+
29
+ from ghnova.config.manager import ConfigManager # noqa: PLC0415
30
+
31
+ logger = logging.getLogger("ghnova")
32
+
33
+ config_manager = ConfigManager(filename=ctx.obj["config_path"])
34
+
35
+ logger.info("Configuration path: %s", config_manager.config_path)
36
+
37
+ config_manager.load_config()
38
+
39
+ try:
40
+ config_manager.update_account(name=name, token=token, base_url=base_url, is_default=default)
41
+ config_manager.save_config()
42
+ updated_entries = []
43
+ if token is not None:
44
+ updated_entries.append("token")
45
+ if base_url is not None:
46
+ updated_entries.append("base_url")
47
+ if default is not None:
48
+ updated_entries.append("default_account")
49
+ if updated_entries:
50
+ logger.info(
51
+ "Account '%s' updated successfully. Updated fields: %s",
52
+ name,
53
+ ", ".join(updated_entries),
54
+ )
55
+ else:
56
+ logger.info("Account '%s' updated successfully. No fields changed.", name)
57
+ except ValueError as e:
58
+ logger.error("Error updating account: %s", e)
59
+ raise typer.Exit(code=1) from e
@@ -0,0 +1,7 @@
1
+ """Command line interface for issue-related operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ghnova.cli.issue.main import issue_app
6
+
7
+ __all__ = ["issue_app"]
@@ -0,0 +1,155 @@
1
+ """Create command for issue CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ import typer
8
+
9
+
10
+ def create_command( # noqa: PLR0913
11
+ ctx: typer.Context,
12
+ owner: Annotated[
13
+ str,
14
+ typer.Option(
15
+ "--owner",
16
+ help="The owner of the repository.",
17
+ ),
18
+ ],
19
+ repository: Annotated[
20
+ str,
21
+ typer.Option(
22
+ "--repository",
23
+ help="The name of the repository.",
24
+ ),
25
+ ],
26
+ title: Annotated[
27
+ str,
28
+ typer.Option(
29
+ "--title",
30
+ help="The title of the issue.",
31
+ ),
32
+ ],
33
+ account_name: Annotated[
34
+ str | None,
35
+ typer.Option(
36
+ "--account-name",
37
+ help="Name of the account to use for authentication.",
38
+ ),
39
+ ] = None,
40
+ token: Annotated[
41
+ str | None,
42
+ typer.Option(
43
+ "--token",
44
+ help="Token for authentication. If not provided, the token from the specified account will be used.",
45
+ ),
46
+ ] = None,
47
+ base_url: Annotated[
48
+ str | None,
49
+ typer.Option(
50
+ "--base-url",
51
+ help="Base URL of the GitHub platform. If not provided, the base URL from the specified account will be used.",
52
+ ),
53
+ ] = None,
54
+ body: Annotated[
55
+ str | None,
56
+ typer.Option(
57
+ "--body",
58
+ help="The body of the issue.",
59
+ ),
60
+ ] = None,
61
+ assignee: Annotated[
62
+ str | None,
63
+ typer.Option(
64
+ "--assignee",
65
+ help="The assignee of the issue.",
66
+ ),
67
+ ] = None,
68
+ milestone: Annotated[
69
+ str | None,
70
+ typer.Option(
71
+ "--milestone",
72
+ help="The milestone for the issue.",
73
+ ),
74
+ ] = None,
75
+ labels: Annotated[
76
+ list[str] | None,
77
+ typer.Option(
78
+ "--labels",
79
+ help="A list of labels to assign to the issue.",
80
+ ),
81
+ ] = None,
82
+ assignees: Annotated[
83
+ list[str] | None,
84
+ typer.Option(
85
+ "--assignees",
86
+ help="A list of assignees to assign to the issue.",
87
+ ),
88
+ ] = None,
89
+ issue_type: Annotated[
90
+ str | None,
91
+ typer.Option(
92
+ "--issue-type",
93
+ help="The type of the issue (e.g., 'bug', 'feature').",
94
+ ),
95
+ ] = None,
96
+ ) -> None:
97
+ """Create a new issue in a repository.
98
+
99
+ Args:
100
+ ctx: Typer context.
101
+ owner: The owner of the repository.
102
+ repository: The name of the repository.
103
+ title: The title of the issue.
104
+ account_name: Name of the account to use for authentication.
105
+ token: Token for authentication.
106
+ base_url: Base URL of the GitHub platform.
107
+ body: The body of the issue.
108
+ assignee: The assignee of the issue.
109
+ milestone: The milestone for the issue.
110
+ labels: A list of labels to assign to the issue.
111
+ assignees: A list of assignees to assign to the issue.
112
+ issue_type: The type of the issue.
113
+
114
+ """
115
+ import json # noqa: PLC0415
116
+ import logging # noqa: PLC0415
117
+
118
+ from ghnova.cli.utils.auth import get_auth_params # noqa: PLC0415
119
+ from ghnova.client.github import GitHub # noqa: PLC0415
120
+
121
+ logger = logging.getLogger("ghnova")
122
+
123
+ token, base_url = get_auth_params(
124
+ config_path=ctx.obj["config_path"],
125
+ account_name=account_name,
126
+ token=token,
127
+ base_url=base_url,
128
+ )
129
+
130
+ try:
131
+ with GitHub(token=token, base_url=base_url) as client:
132
+ issue_client = client.issue
133
+ data, status_code, etag_value, last_modified_value = issue_client.create_issue(
134
+ owner=owner,
135
+ repository=repository,
136
+ title=title,
137
+ body=body,
138
+ assignee=assignee,
139
+ milestone=milestone,
140
+ labels=labels,
141
+ assignees=assignees,
142
+ issue_type=issue_type,
143
+ )
144
+ result = {
145
+ "data": data,
146
+ "metadata": {
147
+ "status_code": status_code,
148
+ "etag": etag_value,
149
+ "last_modified": last_modified_value,
150
+ },
151
+ }
152
+ print(json.dumps(result, indent=2, default=str))
153
+ except Exception as e:
154
+ logger.error("Error creating issue: %s", e)
155
+ raise typer.Exit(code=1) from e
@@ -0,0 +1,119 @@
1
+ """Get command for issue CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ import typer
8
+
9
+
10
+ def get_command( # noqa: PLR0913
11
+ ctx: typer.Context,
12
+ owner: Annotated[
13
+ str,
14
+ typer.Option(
15
+ "--owner",
16
+ help="The owner of the repository.",
17
+ ),
18
+ ],
19
+ repository: Annotated[
20
+ str,
21
+ typer.Option(
22
+ "--repository",
23
+ help="The name of the repository.",
24
+ ),
25
+ ],
26
+ issue_number: Annotated[
27
+ int,
28
+ typer.Option(
29
+ "--issue-number",
30
+ help="The issue number.",
31
+ ),
32
+ ],
33
+ account_name: Annotated[
34
+ str | None,
35
+ typer.Option(
36
+ "--account-name",
37
+ help="Name of the account to use for authentication.",
38
+ ),
39
+ ] = None,
40
+ token: Annotated[
41
+ str | None,
42
+ typer.Option(
43
+ "--token",
44
+ help="Token for authentication. If not provided, the token from the specified account will be used.",
45
+ ),
46
+ ] = None,
47
+ base_url: Annotated[
48
+ str | None,
49
+ typer.Option(
50
+ "--base-url",
51
+ help="Base URL of the GitHub platform. If not provided, the base URL from the specified account will be used.",
52
+ ),
53
+ ] = None,
54
+ etag: Annotated[
55
+ str | None,
56
+ typer.Option(
57
+ "--etag",
58
+ help="ETag from a previous request for caching purposes.",
59
+ ),
60
+ ] = None,
61
+ last_modified: Annotated[
62
+ str | None,
63
+ typer.Option(
64
+ "--last-modified",
65
+ help="Last-Modified header from a previous request for caching purposes.",
66
+ ),
67
+ ] = None,
68
+ ) -> None:
69
+ """Get a specific issue by owner, repository, and issue number.
70
+
71
+ Args:
72
+ ctx: Typer context.
73
+ owner: The owner of the repository.
74
+ repository: The name of the repository.
75
+ issue_number: The issue number.
76
+ account_name: Name of the account to use for authentication.
77
+ token: Token for authentication.
78
+ base_url: Base URL of the GitHub platform.
79
+ etag: ETag from a previous request for caching purposes.
80
+ last_modified: Last-Modified header from a previous request for caching purposes.
81
+
82
+ """
83
+ import json # noqa: PLC0415
84
+ import logging # noqa: PLC0415
85
+
86
+ from ghnova.cli.utils.auth import get_auth_params # noqa: PLC0415
87
+ from ghnova.client.github import GitHub # noqa: PLC0415
88
+
89
+ logger = logging.getLogger("ghnova")
90
+
91
+ token, base_url = get_auth_params(
92
+ config_path=ctx.obj["config_path"],
93
+ account_name=account_name,
94
+ token=token,
95
+ base_url=base_url,
96
+ )
97
+
98
+ try:
99
+ with GitHub(token=token, base_url=base_url) as client:
100
+ issue_client = client.issue
101
+ data, status_code, etag_value, last_modified_value = issue_client.get_issue(
102
+ owner=owner,
103
+ repository=repository,
104
+ issue_number=issue_number,
105
+ etag=etag,
106
+ last_modified=last_modified,
107
+ )
108
+ result = {
109
+ "data": data,
110
+ "metadata": {
111
+ "status_code": status_code,
112
+ "etag": etag_value,
113
+ "last_modified": last_modified_value,
114
+ },
115
+ }
116
+ print(json.dumps(result, indent=2, default=str))
117
+ except Exception as e:
118
+ logger.error("Error retrieving issue: %s", e)
119
+ raise typer.Exit(code=1) from e