gitcode-api 1.1.2__tar.gz → 1.1.3__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.
Files changed (34) hide show
  1. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/PKG-INFO +15 -2
  2. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/README.md +14 -1
  3. gitcode_api-1.1.3/gitcode_api/__main__.py +4 -0
  4. gitcode_api-1.1.3/gitcode_api/cli.py +255 -0
  5. gitcode_api-1.1.3/gitcode_api/version.txt +1 -0
  6. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api.egg-info/PKG-INFO +15 -2
  7. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api.egg-info/SOURCES.txt +4 -0
  8. gitcode_api-1.1.3/gitcode_api.egg-info/entry_points.txt +2 -0
  9. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/pyproject.toml +4 -1
  10. gitcode_api-1.1.3/tests/test_cli.py +127 -0
  11. gitcode_api-1.1.2/gitcode_api/version.txt +0 -1
  12. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/LICENSE +0 -0
  13. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api/__init__.py +0 -0
  14. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api/_base_client.py +0 -0
  15. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api/_client.py +0 -0
  16. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api/_exceptions.py +0 -0
  17. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api/_models.py +0 -0
  18. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api/resources/__init__.py +0 -0
  19. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api/resources/_shared.py +0 -0
  20. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api/resources/account.py +0 -0
  21. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api/resources/collaboration.py +0 -0
  22. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api/resources/misc.py +0 -0
  23. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api/resources/repositories.py +0 -0
  24. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api.egg-info/dependency_links.txt +0 -0
  25. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api.egg-info/requires.txt +0 -0
  26. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/gitcode_api.egg-info/top_level.txt +0 -0
  27. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/setup.cfg +0 -0
  28. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/tests/test_base_client.py +0 -0
  29. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/tests/test_client.py +0 -0
  30. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/tests/test_models.py +0 -0
  31. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/tests/test_resources_account.py +0 -0
  32. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/tests/test_resources_collaboration.py +0 -0
  33. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/tests/test_resources_misc.py +0 -0
  34. {gitcode_api-1.1.2 → gitcode_api-1.1.3}/tests/test_resources_repositories.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitcode-api
3
- Version: 1.1.2
3
+ Version: 1.1.3
4
4
  Summary: Easy to use Python SDK for the GitCode REST API, community-maintained.
5
5
  Author-email: Hugo Huang <hugo@hugohuang.com>
6
6
  License-Expression: MIT
@@ -46,7 +46,7 @@ Dynamic: license-file
46
46
  Install from PyPI:
47
47
 
48
48
  ```bash
49
- pip install gitcode-api
49
+ pip install -U gitcode-api
50
50
  ```
51
51
 
52
52
  ## Authentication
@@ -71,6 +71,19 @@ client = GitCode(
71
71
  )
72
72
  ```
73
73
 
74
+ ## CLI
75
+
76
+ After installation, you can invoke the SDK directly from the command line:
77
+
78
+ ```bash
79
+ gitcode-api repos get --api-key "$GITCODE_ACCESS_TOKEN" --owner SushiNinja --repo GitCode-API
80
+ python -m gitcode_api pulls list --api-key "$GITCODE_ACCESS_TOKEN" --owner SushiNinja --repo GitCode-API --state open
81
+ ```
82
+
83
+ Commands mirror the synchronous resource methods on `GitCode`, using the pattern
84
+ `gitcode-api <resource> <method> ...`. For methods that accept extra `**params`
85
+ or `**payload`, pass repeated `--set key=value` flags or `--set-json '{"key": "value"}'`.
86
+
74
87
  ## Quick Start
75
88
 
76
89
  ### Sync client
@@ -19,7 +19,7 @@
19
19
  Install from PyPI:
20
20
 
21
21
  ```bash
22
- pip install gitcode-api
22
+ pip install -U gitcode-api
23
23
  ```
24
24
 
25
25
  ## Authentication
@@ -44,6 +44,19 @@ client = GitCode(
44
44
  )
45
45
  ```
46
46
 
47
+ ## CLI
48
+
49
+ After installation, you can invoke the SDK directly from the command line:
50
+
51
+ ```bash
52
+ gitcode-api repos get --api-key "$GITCODE_ACCESS_TOKEN" --owner SushiNinja --repo GitCode-API
53
+ python -m gitcode_api pulls list --api-key "$GITCODE_ACCESS_TOKEN" --owner SushiNinja --repo GitCode-API --state open
54
+ ```
55
+
56
+ Commands mirror the synchronous resource methods on `GitCode`, using the pattern
57
+ `gitcode-api <resource> <method> ...`. For methods that accept extra `**params`
58
+ or `**payload`, pass repeated `--set key=value` flags or `--set-json '{"key": "value"}'`.
59
+
47
60
  ## Quick Start
48
61
 
49
62
  ### Sync client
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
@@ -0,0 +1,255 @@
1
+ """Command-line interface for the GitCode SDK."""
2
+
3
+ import argparse
4
+ import inspect
5
+ import json
6
+ import sys
7
+ from collections.abc import Mapping, Sequence
8
+ from pathlib import Path
9
+ from typing import Any, List, Optional, Union, get_args, get_origin
10
+
11
+ from . import GitCode, __version__
12
+ from ._base_client import DEFAULT_BASE_URL, DEFAULT_TOKEN_ENV
13
+ from ._exceptions import GitCodeError
14
+ from .resources._shared import SyncResource
15
+
16
+
17
+ def _unwrap_optional(annotation: Any) -> Any:
18
+ origin = get_origin(annotation)
19
+ if origin is Union:
20
+ args = [arg for arg in get_args(annotation) if arg is not type(None)]
21
+ if len(args) == 1:
22
+ return args[0]
23
+ return annotation
24
+
25
+
26
+ def _is_list_annotation(annotation: Any) -> bool:
27
+ annotation = _unwrap_optional(annotation)
28
+ return get_origin(annotation) in (list, List)
29
+
30
+
31
+ def _list_item_type(annotation: Any) -> Any:
32
+ annotation = _unwrap_optional(annotation)
33
+ args = get_args(annotation)
34
+ if not args:
35
+ return str
36
+ item_type = _unwrap_optional(args[0])
37
+ if item_type in (int, float):
38
+ return item_type
39
+ return str
40
+
41
+
42
+ def _argument_kwargs(parameter: inspect.Parameter) -> dict[str, Any]:
43
+ annotation = _unwrap_optional(parameter.annotation)
44
+ if annotation is bool:
45
+ return {"action": argparse.BooleanOptionalAction, "default": parameter.default}
46
+ if _is_list_annotation(parameter.annotation):
47
+ return {"nargs": "+", "type": _list_item_type(parameter.annotation), "default": None}
48
+ if annotation in (int, float):
49
+ return {"type": annotation}
50
+ return {"type": str}
51
+
52
+
53
+ def _first_doc_line(obj: Any) -> str:
54
+ doc = inspect.getdoc(obj) or ""
55
+ return doc.splitlines()[0] if doc else ""
56
+
57
+
58
+ def _resource_types() -> dict[str, type[SyncResource]]:
59
+ resources: dict[str, type[SyncResource]] = {}
60
+ for name, annotation in GitCode.__annotations__.items():
61
+ if inspect.isclass(annotation) and issubclass(annotation, SyncResource):
62
+ resources[name] = annotation
63
+ return resources
64
+
65
+
66
+ def _iter_resource_methods(resource_type: type[SyncResource]) -> list[tuple[str, Any]]:
67
+ methods: list[tuple[str, Any]] = []
68
+ for name, value in resource_type.__dict__.items():
69
+ if name.startswith("_") or not inspect.isfunction(value):
70
+ continue
71
+ methods.append((name, value))
72
+ return methods
73
+
74
+
75
+ def _kebab_case(value: str) -> str:
76
+ return value.replace("_", "-")
77
+
78
+
79
+ def _load_json_value(raw: str) -> Any:
80
+ if raw.startswith("@"):
81
+ return json.loads(Path(raw[1:]).read_text(encoding="utf-8"))
82
+ return json.loads(raw)
83
+
84
+
85
+ def _parse_scalar(raw: str) -> Any:
86
+ try:
87
+ return _load_json_value(raw)
88
+ except (OSError, ValueError, json.JSONDecodeError):
89
+ return raw
90
+
91
+
92
+ def _parse_key_value(raw: str) -> tuple[str, Any]:
93
+ if "=" not in raw:
94
+ raise ValueError(f"Expected KEY=VALUE, got: {raw}")
95
+ key, value = raw.split("=", maxsplit=1)
96
+ if not key:
97
+ raise ValueError(f"Expected KEY=VALUE, got: {raw}")
98
+ return key, _parse_scalar(value)
99
+
100
+
101
+ def _to_data(value: Any) -> Any:
102
+ if hasattr(value, "to_dict") and callable(value.to_dict):
103
+ return _to_data(value.to_dict())
104
+ if isinstance(value, Mapping):
105
+ return {key: _to_data(item) for key, item in value.items()}
106
+ if isinstance(value, list):
107
+ return [_to_data(item) for item in value]
108
+ return value
109
+
110
+
111
+ def _write_output(value: Any, *, output_file: Optional[str], compact: bool) -> None:
112
+ if value is None:
113
+ return
114
+ if isinstance(value, bytes):
115
+ if output_file:
116
+ Path(output_file).write_bytes(value)
117
+ else:
118
+ sys.stdout.buffer.write(value)
119
+ return
120
+
121
+ payload = _to_data(value)
122
+ if isinstance(payload, str):
123
+ text = payload
124
+ else:
125
+ text = json.dumps(payload, indent=None if compact else 2, ensure_ascii=True, sort_keys=True)
126
+
127
+ if output_file:
128
+ Path(output_file).write_text(text + ("\n" if not text.endswith("\n") else ""), encoding="utf-8")
129
+ else:
130
+ print(text)
131
+
132
+
133
+ def _global_parent_parser() -> argparse.ArgumentParser:
134
+ parser = argparse.ArgumentParser(add_help=False)
135
+ parser.add_argument("--api-key", help=f"GitCode access token. Defaults to {DEFAULT_TOKEN_ENV}.")
136
+ parser.add_argument("--owner", help="Default repository owner.")
137
+ parser.add_argument("--repo", help="Default repository name.")
138
+ parser.add_argument("--base-url", default=DEFAULT_BASE_URL, help="Base URL for the GitCode REST API.")
139
+ parser.add_argument("--timeout", type=float, default=None, help="Request timeout in seconds.")
140
+ parser.add_argument("--output-file", help="Write the response to a file instead of stdout.")
141
+ parser.add_argument("--compact", action="store_true", help="Print JSON without indentation.")
142
+ return parser
143
+
144
+
145
+ def build_parser() -> argparse.ArgumentParser:
146
+ common = _global_parent_parser()
147
+ parser = argparse.ArgumentParser(
148
+ prog="gitcode-api",
149
+ description="Invoke any synchronous gitcode-api resource method from the command line.",
150
+ epilog='Use `--set key=value` and `--set-json \'{"key": "value"}\'` for methods with `**params` or `**payload`.',
151
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
152
+ parents=[common],
153
+ )
154
+ parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
155
+
156
+ resource_parsers = parser.add_subparsers(dest="resource", required=True)
157
+ for resource_name, resource_type in _resource_types().items():
158
+ resource_parser = resource_parsers.add_parser(
159
+ _kebab_case(resource_name),
160
+ help=_first_doc_line(resource_type),
161
+ )
162
+ method_parsers = resource_parser.add_subparsers(dest="method", required=True)
163
+
164
+ for method_name, method in _iter_resource_methods(resource_type):
165
+ method_parser = method_parsers.add_parser(
166
+ _kebab_case(method_name),
167
+ help=_first_doc_line(method),
168
+ description=inspect.getdoc(method),
169
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
170
+ parents=[common],
171
+ )
172
+ signature = inspect.signature(method)
173
+ for parameter in signature.parameters.values():
174
+ if parameter.name == "self":
175
+ continue
176
+ if parameter.kind == inspect.Parameter.VAR_KEYWORD:
177
+ method_parser.add_argument(
178
+ "--set",
179
+ dest="extra_items",
180
+ action="append",
181
+ default=None,
182
+ metavar="KEY=VALUE",
183
+ help="Additional keyword arguments for `**params` or `**payload`.",
184
+ )
185
+ method_parser.add_argument(
186
+ "--set-json",
187
+ dest="extra_json",
188
+ default=None,
189
+ metavar="JSON_OR_@FILE",
190
+ help="JSON object merged into extra keyword arguments.",
191
+ )
192
+ continue
193
+
194
+ flag = f"--{parameter.name.replace('_', '-')}"
195
+ if flag in method_parser._option_string_actions:
196
+ continue
197
+ kwargs = _argument_kwargs(parameter)
198
+ kwargs["dest"] = parameter.name
199
+ kwargs["required"] = parameter.default is inspect.Signature.empty
200
+ method_parser.add_argument(flag, **kwargs)
201
+
202
+ method_parser.set_defaults(resource_name=resource_name, method_name=method_name)
203
+
204
+ return parser
205
+
206
+
207
+ def _collect_kwargs(args: argparse.Namespace, method: Any) -> dict[str, Any]:
208
+ signature = inspect.signature(method)
209
+ kwargs: dict[str, Any] = {}
210
+ for parameter in signature.parameters.values():
211
+ if parameter.name == "self":
212
+ continue
213
+ if parameter.kind == inspect.Parameter.VAR_KEYWORD:
214
+ extra_kwargs: dict[str, Any] = {}
215
+ if getattr(args, "extra_json", None):
216
+ raw_extra = _load_json_value(args.extra_json)
217
+ if not isinstance(raw_extra, dict):
218
+ raise ValueError("--set-json must decode to a JSON object.")
219
+ extra_kwargs.update(raw_extra)
220
+ for item in getattr(args, "extra_items", []) or []:
221
+ key, value = _parse_key_value(item)
222
+ extra_kwargs[key] = value
223
+ kwargs.update(extra_kwargs)
224
+ continue
225
+
226
+ value = getattr(args, parameter.name)
227
+ if value is None:
228
+ if parameter.default is inspect.Signature.empty:
229
+ raise ValueError(f"--{parameter.name.replace('_', '-')} is required.")
230
+ continue
231
+ kwargs[parameter.name] = value
232
+ return kwargs
233
+
234
+
235
+ def main(argv: Optional[Sequence[str]] = None) -> int:
236
+ parser = build_parser()
237
+ args = parser.parse_args(argv)
238
+
239
+ try:
240
+ with GitCode(
241
+ api_key=args.api_key,
242
+ owner=args.owner,
243
+ repo=args.repo,
244
+ base_url=args.base_url,
245
+ timeout=args.timeout,
246
+ ) as client:
247
+ resource = getattr(client, args.resource_name)
248
+ method = getattr(resource, args.method_name)
249
+ result = method(**_collect_kwargs(args, method))
250
+ except (GitCodeError, OSError, TypeError, ValueError) as exc: # pragma: no cover - integration style
251
+ print(f"error: {exc}", file=sys.stderr)
252
+ return 1
253
+
254
+ _write_output(result, output_file=args.output_file, compact=args.compact)
255
+ return 0
@@ -0,0 +1 @@
1
+ 1.1.3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitcode-api
3
- Version: 1.1.2
3
+ Version: 1.1.3
4
4
  Summary: Easy to use Python SDK for the GitCode REST API, community-maintained.
5
5
  Author-email: Hugo Huang <hugo@hugohuang.com>
6
6
  License-Expression: MIT
@@ -46,7 +46,7 @@ Dynamic: license-file
46
46
  Install from PyPI:
47
47
 
48
48
  ```bash
49
- pip install gitcode-api
49
+ pip install -U gitcode-api
50
50
  ```
51
51
 
52
52
  ## Authentication
@@ -71,6 +71,19 @@ client = GitCode(
71
71
  )
72
72
  ```
73
73
 
74
+ ## CLI
75
+
76
+ After installation, you can invoke the SDK directly from the command line:
77
+
78
+ ```bash
79
+ gitcode-api repos get --api-key "$GITCODE_ACCESS_TOKEN" --owner SushiNinja --repo GitCode-API
80
+ python -m gitcode_api pulls list --api-key "$GITCODE_ACCESS_TOKEN" --owner SushiNinja --repo GitCode-API --state open
81
+ ```
82
+
83
+ Commands mirror the synchronous resource methods on `GitCode`, using the pattern
84
+ `gitcode-api <resource> <method> ...`. For methods that accept extra `**params`
85
+ or `**payload`, pass repeated `--set key=value` flags or `--set-json '{"key": "value"}'`.
86
+
74
87
  ## Quick Start
75
88
 
76
89
  ### Sync client
@@ -2,14 +2,17 @@ LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
4
  gitcode_api/__init__.py
5
+ gitcode_api/__main__.py
5
6
  gitcode_api/_base_client.py
6
7
  gitcode_api/_client.py
7
8
  gitcode_api/_exceptions.py
8
9
  gitcode_api/_models.py
10
+ gitcode_api/cli.py
9
11
  gitcode_api/version.txt
10
12
  gitcode_api.egg-info/PKG-INFO
11
13
  gitcode_api.egg-info/SOURCES.txt
12
14
  gitcode_api.egg-info/dependency_links.txt
15
+ gitcode_api.egg-info/entry_points.txt
13
16
  gitcode_api.egg-info/requires.txt
14
17
  gitcode_api.egg-info/top_level.txt
15
18
  gitcode_api/resources/__init__.py
@@ -19,6 +22,7 @@ gitcode_api/resources/collaboration.py
19
22
  gitcode_api/resources/misc.py
20
23
  gitcode_api/resources/repositories.py
21
24
  tests/test_base_client.py
25
+ tests/test_cli.py
22
26
  tests/test_client.py
23
27
  tests/test_models.py
24
28
  tests/test_resources_account.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ gitcode-api = gitcode_api.cli:main
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gitcode-api"
3
- version = "1.1.2"
3
+ version = "1.1.3"
4
4
  description = "Easy to use Python SDK for the GitCode REST API, community-maintained."
5
5
  keywords = ["gitcode", "git", "devops", "api", "sdk", "python", "httpx", "client"]
6
6
  readme = "README.md"
@@ -51,6 +51,9 @@ documentation = "https://gitcode-api.readthedocs.io"
51
51
  gitcode = "https://gitcode.com/SushiNinja/GitCode-API"
52
52
  github = "https://github.com/Trenza1ore/GitCode-API"
53
53
 
54
+ [project.scripts]
55
+ gitcode-api = "gitcode_api.cli:main"
56
+
54
57
  [tool.uv]
55
58
  default-groups = ['test']
56
59
 
@@ -0,0 +1,127 @@
1
+ from pathlib import Path
2
+ from typing import Any
3
+
4
+ import httpx
5
+
6
+ from gitcode_api.cli import build_parser, main
7
+
8
+
9
+ def _mock_sync_client(monkeypatch: Any, handler: Any) -> None:
10
+ mock_transport = httpx.MockTransport(handler)
11
+ real_client = httpx.Client
12
+
13
+ def client_with_mock_transport(*args: object, **kwargs: object) -> httpx.Client:
14
+ merged = dict(kwargs)
15
+ merged.setdefault("transport", mock_transport)
16
+ return real_client(*args, **merged)
17
+
18
+ monkeypatch.setattr(httpx, "Client", client_with_mock_transport)
19
+
20
+
21
+ def test_cli_build_parser_exposes_generated_commands() -> None:
22
+ parser = build_parser()
23
+ args = parser.parse_args(
24
+ [
25
+ "oauth",
26
+ "build-authorize-url",
27
+ "--api-key",
28
+ "test-token",
29
+ "--client-id",
30
+ "cid",
31
+ "--redirect-uri",
32
+ "https://example.com/callback",
33
+ ]
34
+ )
35
+
36
+ assert args.resource_name == "oauth"
37
+ assert args.method_name == "build_authorize_url"
38
+
39
+
40
+ def test_cli_invokes_resource_methods_and_prints_json(capsys: Any, monkeypatch: Any) -> None:
41
+ captured: dict[str, Any] = {}
42
+
43
+ def handler(request: httpx.Request) -> httpx.Response:
44
+ captured["url"] = str(request.url)
45
+ return httpx.Response(200, json={"full_name": "SushiNinja/GitCode-API"})
46
+
47
+ _mock_sync_client(monkeypatch, handler)
48
+
49
+ exit_code = main(
50
+ [
51
+ "repos",
52
+ "get",
53
+ "--api-key",
54
+ "test-token",
55
+ "--owner",
56
+ "SushiNinja",
57
+ "--repo",
58
+ "GitCode-API",
59
+ ]
60
+ )
61
+
62
+ stdout = capsys.readouterr().out
63
+ assert exit_code == 0
64
+ assert "/repos/SushiNinja/GitCode-API" in captured["url"]
65
+ assert '"full_name": "SushiNinja/GitCode-API"' in stdout
66
+
67
+
68
+ def test_cli_supports_extra_kwargs_via_set_flags(capsys: Any, monkeypatch: Any) -> None:
69
+ captured: dict[str, Any] = {}
70
+
71
+ def handler(request: httpx.Request) -> httpx.Response:
72
+ captured["params"] = dict(request.url.params)
73
+ return httpx.Response(200, json={"open": 3, "closed": 1})
74
+
75
+ _mock_sync_client(monkeypatch, handler)
76
+
77
+ exit_code = main(
78
+ [
79
+ "pulls",
80
+ "list",
81
+ "--api-key",
82
+ "test-token",
83
+ "--owner",
84
+ "SushiNinja",
85
+ "--repo",
86
+ "GitCode-API",
87
+ "--set",
88
+ "only_count=true",
89
+ "--set",
90
+ "reviewer=octocat",
91
+ ]
92
+ )
93
+
94
+ stdout = capsys.readouterr().out
95
+ assert exit_code == 0
96
+ assert captured["params"]["only_count"] == "true"
97
+ assert captured["params"]["reviewer"] == "octocat"
98
+ assert '"open": 3' in stdout
99
+
100
+
101
+ def test_cli_writes_raw_bytes_to_output_file(tmp_path: Path, monkeypatch: Any) -> None:
102
+ output_path = tmp_path / "README.txt"
103
+
104
+ def handler(_request: httpx.Request) -> httpx.Response:
105
+ return httpx.Response(200, content=b"hello from gitcode")
106
+
107
+ _mock_sync_client(monkeypatch, handler)
108
+
109
+ exit_code = main(
110
+ [
111
+ "contents",
112
+ "get-raw",
113
+ "--api-key",
114
+ "test-token",
115
+ "--owner",
116
+ "SushiNinja",
117
+ "--repo",
118
+ "GitCode-API",
119
+ "--path",
120
+ "README.md",
121
+ "--output-file",
122
+ str(output_path),
123
+ ]
124
+ )
125
+
126
+ assert exit_code == 0
127
+ assert output_path.read_bytes() == b"hello from gitcode"
@@ -1 +0,0 @@
1
- 1.1.2
File without changes
File without changes