pierre-storage 0.10.0__py3-none-any.whl → 0.11.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.
- pierre_storage/__init__.py +8 -0
- pierre_storage/client.py +63 -0
- pierre_storage/repo.py +235 -0
- pierre_storage/types.py +98 -0
- {pierre_storage-0.10.0.dist-info → pierre_storage-0.11.0.dist-info}/METADATA +26 -1
- pierre_storage-0.11.0.dist-info/RECORD +15 -0
- pierre_storage-0.10.0.dist-info/RECORD +0 -15
- {pierre_storage-0.10.0.dist-info → pierre_storage-0.11.0.dist-info}/WHEEL +0 -0
- {pierre_storage-0.10.0.dist-info → pierre_storage-0.11.0.dist-info}/licenses/LICENSE +0 -0
- {pierre_storage-0.10.0.dist-info → pierre_storage-0.11.0.dist-info}/top_level.txt +0 -0
pierre_storage/__init__.py
CHANGED
|
@@ -27,8 +27,12 @@ from pierre_storage.types import (
|
|
|
27
27
|
ListBranchesResult,
|
|
28
28
|
ListCommitsResult,
|
|
29
29
|
ListFilesResult,
|
|
30
|
+
ListReposResult,
|
|
31
|
+
NoteReadResult,
|
|
32
|
+
NoteWriteResult,
|
|
30
33
|
RefUpdate,
|
|
31
34
|
Repo,
|
|
35
|
+
RepoInfo,
|
|
32
36
|
RestoreCommitResult,
|
|
33
37
|
)
|
|
34
38
|
from pierre_storage.version import PACKAGE_VERSION
|
|
@@ -71,7 +75,11 @@ __all__ = [
|
|
|
71
75
|
"ListBranchesResult",
|
|
72
76
|
"ListCommitsResult",
|
|
73
77
|
"ListFilesResult",
|
|
78
|
+
"ListReposResult",
|
|
79
|
+
"NoteReadResult",
|
|
80
|
+
"NoteWriteResult",
|
|
74
81
|
"RefUpdate",
|
|
82
|
+
"RepoInfo",
|
|
75
83
|
"Repo",
|
|
76
84
|
"RestoreCommitResult",
|
|
77
85
|
# Webhook
|
pierre_storage/client.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import uuid
|
|
4
4
|
from typing import Any, Dict, Optional
|
|
5
|
+
from urllib.parse import urlencode
|
|
5
6
|
|
|
6
7
|
import httpx
|
|
7
8
|
|
|
@@ -11,7 +12,9 @@ from pierre_storage.repo import DEFAULT_TOKEN_TTL_SECONDS, RepoImpl
|
|
|
11
12
|
from pierre_storage.types import (
|
|
12
13
|
DeleteRepoResult,
|
|
13
14
|
GitStorageOptions,
|
|
15
|
+
ListReposResult,
|
|
14
16
|
Repo,
|
|
17
|
+
RepoInfo,
|
|
15
18
|
)
|
|
16
19
|
from pierre_storage.version import get_user_agent
|
|
17
20
|
|
|
@@ -178,6 +181,66 @@ class GitStorage:
|
|
|
178
181
|
self._generate_jwt,
|
|
179
182
|
)
|
|
180
183
|
|
|
184
|
+
async def list_repos(
|
|
185
|
+
self,
|
|
186
|
+
*,
|
|
187
|
+
cursor: Optional[str] = None,
|
|
188
|
+
limit: Optional[int] = None,
|
|
189
|
+
ttl: Optional[int] = None,
|
|
190
|
+
) -> ListReposResult:
|
|
191
|
+
"""List repositories for the organization."""
|
|
192
|
+
ttl = ttl or DEFAULT_TOKEN_TTL_SECONDS
|
|
193
|
+
jwt = self._generate_jwt(
|
|
194
|
+
"org",
|
|
195
|
+
{"permissions": ["org:read"], "ttl": ttl},
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
params: Dict[str, str] = {}
|
|
199
|
+
if cursor:
|
|
200
|
+
params["cursor"] = cursor
|
|
201
|
+
if limit is not None:
|
|
202
|
+
params["limit"] = str(limit)
|
|
203
|
+
|
|
204
|
+
url = f"{self.options['api_base_url']}/api/v{self.options['api_version']}/repos"
|
|
205
|
+
if params:
|
|
206
|
+
url += f"?{urlencode(params)}"
|
|
207
|
+
|
|
208
|
+
async with httpx.AsyncClient() as client:
|
|
209
|
+
response = await client.get(
|
|
210
|
+
url,
|
|
211
|
+
headers={
|
|
212
|
+
"Authorization": f"Bearer {jwt}",
|
|
213
|
+
"Code-Storage-Agent": get_user_agent(),
|
|
214
|
+
},
|
|
215
|
+
timeout=30.0,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if not response.is_success:
|
|
219
|
+
raise ApiError(
|
|
220
|
+
f"Failed to list repositories: {response.status_code} {response.reason_phrase}",
|
|
221
|
+
status_code=response.status_code,
|
|
222
|
+
response=response,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
data = response.json()
|
|
226
|
+
repos: list[RepoInfo] = []
|
|
227
|
+
for repo in data.get("repos", []):
|
|
228
|
+
entry: RepoInfo = {
|
|
229
|
+
"repo_id": repo.get("repo_id", ""),
|
|
230
|
+
"url": repo.get("url", ""),
|
|
231
|
+
"default_branch": repo.get("default_branch", "main"),
|
|
232
|
+
"created_at": repo.get("created_at", ""),
|
|
233
|
+
}
|
|
234
|
+
if repo.get("base_repo"):
|
|
235
|
+
entry["base_repo"] = repo.get("base_repo")
|
|
236
|
+
repos.append(entry)
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
"repos": repos,
|
|
240
|
+
"next_cursor": data.get("next_cursor"),
|
|
241
|
+
"has_more": data.get("has_more", False),
|
|
242
|
+
}
|
|
243
|
+
|
|
181
244
|
async def find_one(self, *, id: str) -> Optional[Repo]:
|
|
182
245
|
"""Find a repository by ID.
|
|
183
246
|
|
pierre_storage/repo.py
CHANGED
|
@@ -33,6 +33,8 @@ from pierre_storage.types import (
|
|
|
33
33
|
ListBranchesResult,
|
|
34
34
|
ListCommitsResult,
|
|
35
35
|
ListFilesResult,
|
|
36
|
+
NoteReadResult,
|
|
37
|
+
NoteWriteResult,
|
|
36
38
|
RefUpdate,
|
|
37
39
|
RestoreCommitResult,
|
|
38
40
|
)
|
|
@@ -498,6 +500,122 @@ class RepoImpl:
|
|
|
498
500
|
"has_more": data["has_more"],
|
|
499
501
|
}
|
|
500
502
|
|
|
503
|
+
async def get_note(
|
|
504
|
+
self,
|
|
505
|
+
*,
|
|
506
|
+
sha: str,
|
|
507
|
+
ttl: Optional[int] = None,
|
|
508
|
+
) -> NoteReadResult:
|
|
509
|
+
"""Read a git note."""
|
|
510
|
+
sha_clean = sha.strip()
|
|
511
|
+
if not sha_clean:
|
|
512
|
+
raise ValueError("get_note sha is required")
|
|
513
|
+
|
|
514
|
+
ttl = ttl or DEFAULT_TOKEN_TTL_SECONDS
|
|
515
|
+
jwt = self.generate_jwt(self._id, {"permissions": ["git:read"], "ttl": ttl})
|
|
516
|
+
|
|
517
|
+
url = f"{self.api_base_url}/api/v{self.api_version}/repos/notes?{urlencode({'sha': sha_clean})}"
|
|
518
|
+
|
|
519
|
+
async with httpx.AsyncClient() as client:
|
|
520
|
+
response = await client.get(
|
|
521
|
+
url,
|
|
522
|
+
headers={
|
|
523
|
+
"Authorization": f"Bearer {jwt}",
|
|
524
|
+
"Code-Storage-Agent": get_user_agent(),
|
|
525
|
+
},
|
|
526
|
+
timeout=30.0,
|
|
527
|
+
)
|
|
528
|
+
response.raise_for_status()
|
|
529
|
+
data = response.json()
|
|
530
|
+
return {
|
|
531
|
+
"sha": data["sha"],
|
|
532
|
+
"note": data["note"],
|
|
533
|
+
"ref_sha": data["ref_sha"],
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async def create_note(
|
|
537
|
+
self,
|
|
538
|
+
*,
|
|
539
|
+
sha: str,
|
|
540
|
+
note: str,
|
|
541
|
+
expected_ref_sha: Optional[str] = None,
|
|
542
|
+
author: Optional[CommitSignature] = None,
|
|
543
|
+
ttl: Optional[int] = None,
|
|
544
|
+
) -> NoteWriteResult:
|
|
545
|
+
"""Create a git note."""
|
|
546
|
+
return await self._write_note(
|
|
547
|
+
action_label="create_note",
|
|
548
|
+
action="add",
|
|
549
|
+
sha=sha,
|
|
550
|
+
note=note,
|
|
551
|
+
expected_ref_sha=expected_ref_sha,
|
|
552
|
+
author=author,
|
|
553
|
+
ttl=ttl,
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
async def append_note(
|
|
557
|
+
self,
|
|
558
|
+
*,
|
|
559
|
+
sha: str,
|
|
560
|
+
note: str,
|
|
561
|
+
expected_ref_sha: Optional[str] = None,
|
|
562
|
+
author: Optional[CommitSignature] = None,
|
|
563
|
+
ttl: Optional[int] = None,
|
|
564
|
+
) -> NoteWriteResult:
|
|
565
|
+
"""Append to a git note."""
|
|
566
|
+
return await self._write_note(
|
|
567
|
+
action_label="append_note",
|
|
568
|
+
action="append",
|
|
569
|
+
sha=sha,
|
|
570
|
+
note=note,
|
|
571
|
+
expected_ref_sha=expected_ref_sha,
|
|
572
|
+
author=author,
|
|
573
|
+
ttl=ttl,
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
async def delete_note(
|
|
577
|
+
self,
|
|
578
|
+
*,
|
|
579
|
+
sha: str,
|
|
580
|
+
expected_ref_sha: Optional[str] = None,
|
|
581
|
+
author: Optional[CommitSignature] = None,
|
|
582
|
+
ttl: Optional[int] = None,
|
|
583
|
+
) -> NoteWriteResult:
|
|
584
|
+
"""Delete a git note."""
|
|
585
|
+
sha_clean = sha.strip()
|
|
586
|
+
if not sha_clean:
|
|
587
|
+
raise ValueError("delete_note sha is required")
|
|
588
|
+
|
|
589
|
+
ttl = ttl or DEFAULT_TOKEN_TTL_SECONDS
|
|
590
|
+
jwt = self.generate_jwt(self._id, {"permissions": ["git:write"], "ttl": ttl})
|
|
591
|
+
|
|
592
|
+
payload: Dict[str, Any] = {"sha": sha_clean}
|
|
593
|
+
if expected_ref_sha and expected_ref_sha.strip():
|
|
594
|
+
payload["expected_ref_sha"] = expected_ref_sha.strip()
|
|
595
|
+
if author:
|
|
596
|
+
author_name = author.get("name", "").strip()
|
|
597
|
+
author_email = author.get("email", "").strip()
|
|
598
|
+
if not author_name or not author_email:
|
|
599
|
+
raise ValueError("delete_note author name and email are required when provided")
|
|
600
|
+
payload["author"] = {"name": author_name, "email": author_email}
|
|
601
|
+
|
|
602
|
+
url = f"{self.api_base_url}/api/v{self.api_version}/repos/notes"
|
|
603
|
+
|
|
604
|
+
async with httpx.AsyncClient() as client:
|
|
605
|
+
response = await client.request(
|
|
606
|
+
"DELETE",
|
|
607
|
+
url,
|
|
608
|
+
headers={
|
|
609
|
+
"Authorization": f"Bearer {jwt}",
|
|
610
|
+
"Content-Type": "application/json",
|
|
611
|
+
"Code-Storage-Agent": get_user_agent(),
|
|
612
|
+
},
|
|
613
|
+
json=payload,
|
|
614
|
+
timeout=30.0,
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
return self._parse_note_write_response(response, "delete_note")
|
|
618
|
+
|
|
501
619
|
async def get_branch_diff(
|
|
502
620
|
self,
|
|
503
621
|
*,
|
|
@@ -1046,6 +1164,123 @@ class RepoImpl:
|
|
|
1046
1164
|
self.api_version,
|
|
1047
1165
|
)
|
|
1048
1166
|
|
|
1167
|
+
async def _write_note(
|
|
1168
|
+
self,
|
|
1169
|
+
*,
|
|
1170
|
+
action_label: str,
|
|
1171
|
+
action: str,
|
|
1172
|
+
sha: str,
|
|
1173
|
+
note: str,
|
|
1174
|
+
expected_ref_sha: Optional[str],
|
|
1175
|
+
author: Optional[CommitSignature],
|
|
1176
|
+
ttl: Optional[int],
|
|
1177
|
+
) -> NoteWriteResult:
|
|
1178
|
+
sha_clean = sha.strip()
|
|
1179
|
+
if not sha_clean:
|
|
1180
|
+
raise ValueError(f"{action_label} sha is required")
|
|
1181
|
+
|
|
1182
|
+
note_clean = note.strip()
|
|
1183
|
+
if not note_clean:
|
|
1184
|
+
raise ValueError(f"{action_label} note is required")
|
|
1185
|
+
|
|
1186
|
+
ttl = ttl or DEFAULT_TOKEN_TTL_SECONDS
|
|
1187
|
+
jwt = self.generate_jwt(self._id, {"permissions": ["git:write"], "ttl": ttl})
|
|
1188
|
+
|
|
1189
|
+
payload: Dict[str, Any] = {
|
|
1190
|
+
"sha": sha_clean,
|
|
1191
|
+
"action": action,
|
|
1192
|
+
"note": note_clean,
|
|
1193
|
+
}
|
|
1194
|
+
if expected_ref_sha and expected_ref_sha.strip():
|
|
1195
|
+
payload["expected_ref_sha"] = expected_ref_sha.strip()
|
|
1196
|
+
if author:
|
|
1197
|
+
author_name = author.get("name", "").strip()
|
|
1198
|
+
author_email = author.get("email", "").strip()
|
|
1199
|
+
if not author_name or not author_email:
|
|
1200
|
+
raise ValueError(f"{action_label} author name and email are required when provided")
|
|
1201
|
+
payload["author"] = {"name": author_name, "email": author_email}
|
|
1202
|
+
|
|
1203
|
+
url = f"{self.api_base_url}/api/v{self.api_version}/repos/notes"
|
|
1204
|
+
|
|
1205
|
+
async with httpx.AsyncClient() as client:
|
|
1206
|
+
response = await client.post(
|
|
1207
|
+
url,
|
|
1208
|
+
headers={
|
|
1209
|
+
"Authorization": f"Bearer {jwt}",
|
|
1210
|
+
"Content-Type": "application/json",
|
|
1211
|
+
"Code-Storage-Agent": get_user_agent(),
|
|
1212
|
+
},
|
|
1213
|
+
json=payload,
|
|
1214
|
+
timeout=30.0,
|
|
1215
|
+
)
|
|
1216
|
+
|
|
1217
|
+
return self._parse_note_write_response(response, action_label)
|
|
1218
|
+
|
|
1219
|
+
def _parse_note_write_response(
|
|
1220
|
+
self,
|
|
1221
|
+
response: httpx.Response,
|
|
1222
|
+
action_label: str,
|
|
1223
|
+
) -> NoteWriteResult:
|
|
1224
|
+
try:
|
|
1225
|
+
payload = response.json()
|
|
1226
|
+
except Exception as exc:
|
|
1227
|
+
message = f"{action_label} failed with HTTP {response.status_code}"
|
|
1228
|
+
if response.reason_phrase:
|
|
1229
|
+
message += f" {response.reason_phrase}"
|
|
1230
|
+
try:
|
|
1231
|
+
body_text = response.text
|
|
1232
|
+
except Exception:
|
|
1233
|
+
body_text = ""
|
|
1234
|
+
if body_text:
|
|
1235
|
+
message += f": {body_text[:200]}"
|
|
1236
|
+
raise ApiError(message, status_code=response.status_code, response=response) from exc
|
|
1237
|
+
|
|
1238
|
+
if isinstance(payload, dict) and "error" in payload:
|
|
1239
|
+
raise ApiError(
|
|
1240
|
+
str(payload.get("error")),
|
|
1241
|
+
status_code=response.status_code,
|
|
1242
|
+
response=response,
|
|
1243
|
+
)
|
|
1244
|
+
|
|
1245
|
+
if not isinstance(payload, dict) or "result" not in payload:
|
|
1246
|
+
message = f"{action_label} failed with HTTP {response.status_code}"
|
|
1247
|
+
if response.reason_phrase:
|
|
1248
|
+
message += f" {response.reason_phrase}"
|
|
1249
|
+
raise ApiError(message, status_code=response.status_code, response=response)
|
|
1250
|
+
|
|
1251
|
+
result = payload.get("result", {})
|
|
1252
|
+
note_result: NoteWriteResult = {
|
|
1253
|
+
"sha": payload.get("sha", ""),
|
|
1254
|
+
"target_ref": payload.get("target_ref", ""),
|
|
1255
|
+
"new_ref_sha": payload.get("new_ref_sha", ""),
|
|
1256
|
+
"result": {
|
|
1257
|
+
"success": bool(result.get("success")),
|
|
1258
|
+
"status": str(result.get("status", "")),
|
|
1259
|
+
},
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
base_commit = payload.get("base_commit")
|
|
1263
|
+
if isinstance(base_commit, str) and base_commit:
|
|
1264
|
+
note_result["base_commit"] = base_commit
|
|
1265
|
+
if result.get("message"):
|
|
1266
|
+
note_result["result"]["message"] = result.get("message")
|
|
1267
|
+
|
|
1268
|
+
if not result.get("success"):
|
|
1269
|
+
raise RefUpdateError(
|
|
1270
|
+
result.get(
|
|
1271
|
+
"message",
|
|
1272
|
+
f"{action_label} failed with status {result.get('status')}",
|
|
1273
|
+
),
|
|
1274
|
+
status=result.get("status"),
|
|
1275
|
+
ref_update={
|
|
1276
|
+
"branch": payload.get("target_ref", ""),
|
|
1277
|
+
"old_sha": payload.get("base_commit", ""),
|
|
1278
|
+
"new_sha": payload.get("new_ref_sha", ""),
|
|
1279
|
+
},
|
|
1280
|
+
)
|
|
1281
|
+
|
|
1282
|
+
return note_result
|
|
1283
|
+
|
|
1049
1284
|
def _to_ref_update(self, result: Dict[str, Any]) -> RefUpdate:
|
|
1050
1285
|
"""Convert result to ref update."""
|
|
1051
1286
|
return {
|
pierre_storage/types.py
CHANGED
|
@@ -57,6 +57,33 @@ class DeleteRepoResult(TypedDict):
|
|
|
57
57
|
message: str
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
# Repository list types
|
|
61
|
+
class RepoBaseInfo(TypedDict):
|
|
62
|
+
"""Base repository info for listed repositories."""
|
|
63
|
+
|
|
64
|
+
provider: str
|
|
65
|
+
owner: str
|
|
66
|
+
name: str
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class RepoInfo(TypedDict, total=False):
|
|
70
|
+
"""Repository info in list responses."""
|
|
71
|
+
|
|
72
|
+
repo_id: str
|
|
73
|
+
url: str
|
|
74
|
+
default_branch: str
|
|
75
|
+
created_at: str
|
|
76
|
+
base_repo: NotRequired[RepoBaseInfo]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ListReposResult(TypedDict):
|
|
80
|
+
"""Result from listing repositories."""
|
|
81
|
+
|
|
82
|
+
repos: List[RepoInfo]
|
|
83
|
+
next_cursor: Optional[str]
|
|
84
|
+
has_more: bool
|
|
85
|
+
|
|
86
|
+
|
|
60
87
|
# Removed: GetRemoteURLOptions - now uses **kwargs
|
|
61
88
|
# Removed: CreateRepoOptions - now uses **kwargs
|
|
62
89
|
# Removed: FindOneOptions - now uses **kwargs
|
|
@@ -127,6 +154,33 @@ class ListCommitsResult(TypedDict):
|
|
|
127
154
|
has_more: bool
|
|
128
155
|
|
|
129
156
|
|
|
157
|
+
# Git notes types
|
|
158
|
+
class NoteReadResult(TypedDict):
|
|
159
|
+
"""Result from reading a git note."""
|
|
160
|
+
|
|
161
|
+
sha: str
|
|
162
|
+
note: str
|
|
163
|
+
ref_sha: str
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class NoteWriteResultPayload(TypedDict):
|
|
167
|
+
"""Result payload for note writes."""
|
|
168
|
+
|
|
169
|
+
success: bool
|
|
170
|
+
status: str
|
|
171
|
+
message: NotRequired[str]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class NoteWriteResult(TypedDict):
|
|
175
|
+
"""Result from writing a git note."""
|
|
176
|
+
|
|
177
|
+
sha: str
|
|
178
|
+
target_ref: str
|
|
179
|
+
base_commit: NotRequired[str]
|
|
180
|
+
new_ref_sha: str
|
|
181
|
+
result: NoteWriteResultPayload
|
|
182
|
+
|
|
183
|
+
|
|
130
184
|
# Diff types
|
|
131
185
|
class DiffStats(TypedDict):
|
|
132
186
|
"""Statistics about a diff."""
|
|
@@ -417,6 +471,50 @@ class Repo(Protocol):
|
|
|
417
471
|
"""List commits in the repository."""
|
|
418
472
|
...
|
|
419
473
|
|
|
474
|
+
async def get_note(
|
|
475
|
+
self,
|
|
476
|
+
*,
|
|
477
|
+
sha: str,
|
|
478
|
+
ttl: Optional[int] = None,
|
|
479
|
+
) -> NoteReadResult:
|
|
480
|
+
"""Read a git note."""
|
|
481
|
+
...
|
|
482
|
+
|
|
483
|
+
async def create_note(
|
|
484
|
+
self,
|
|
485
|
+
*,
|
|
486
|
+
sha: str,
|
|
487
|
+
note: str,
|
|
488
|
+
expected_ref_sha: Optional[str] = None,
|
|
489
|
+
author: Optional["CommitSignature"] = None,
|
|
490
|
+
ttl: Optional[int] = None,
|
|
491
|
+
) -> NoteWriteResult:
|
|
492
|
+
"""Create a git note."""
|
|
493
|
+
...
|
|
494
|
+
|
|
495
|
+
async def append_note(
|
|
496
|
+
self,
|
|
497
|
+
*,
|
|
498
|
+
sha: str,
|
|
499
|
+
note: str,
|
|
500
|
+
expected_ref_sha: Optional[str] = None,
|
|
501
|
+
author: Optional["CommitSignature"] = None,
|
|
502
|
+
ttl: Optional[int] = None,
|
|
503
|
+
) -> NoteWriteResult:
|
|
504
|
+
"""Append to a git note."""
|
|
505
|
+
...
|
|
506
|
+
|
|
507
|
+
async def delete_note(
|
|
508
|
+
self,
|
|
509
|
+
*,
|
|
510
|
+
sha: str,
|
|
511
|
+
expected_ref_sha: Optional[str] = None,
|
|
512
|
+
author: Optional["CommitSignature"] = None,
|
|
513
|
+
ttl: Optional[int] = None,
|
|
514
|
+
) -> NoteWriteResult:
|
|
515
|
+
"""Delete a git note."""
|
|
516
|
+
...
|
|
517
|
+
|
|
420
518
|
async def get_branch_diff(
|
|
421
519
|
self,
|
|
422
520
|
*,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pierre-storage
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Pierre Git Storage SDK for Python
|
|
5
5
|
Author-email: Pierre <support@pierre.io>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -149,6 +149,10 @@ repo = await storage.create_repo()
|
|
|
149
149
|
# or
|
|
150
150
|
repo = await storage.find_one(id="existing-repo-id")
|
|
151
151
|
|
|
152
|
+
# List repositories for the org
|
|
153
|
+
repos = await storage.list_repos(limit=20)
|
|
154
|
+
print(repos["repos"])
|
|
155
|
+
|
|
152
156
|
# Get file content (streaming)
|
|
153
157
|
response = await repo.get_file_stream(
|
|
154
158
|
path="README.md",
|
|
@@ -190,6 +194,27 @@ commits = await repo.list_commits(
|
|
|
190
194
|
)
|
|
191
195
|
print(commits["commits"])
|
|
192
196
|
|
|
197
|
+
# Read a git note for a commit
|
|
198
|
+
note = await repo.get_note(sha="abc123...")
|
|
199
|
+
print(note["note"])
|
|
200
|
+
|
|
201
|
+
# Add a git note
|
|
202
|
+
note_result = await repo.create_note(
|
|
203
|
+
sha="abc123...",
|
|
204
|
+
note="Release QA approved",
|
|
205
|
+
author={"name": "Release Bot", "email": "release@example.com"},
|
|
206
|
+
)
|
|
207
|
+
print(note_result["new_ref_sha"])
|
|
208
|
+
|
|
209
|
+
# Append to a git note
|
|
210
|
+
await repo.append_note(
|
|
211
|
+
sha="abc123...",
|
|
212
|
+
note="Follow-up review complete",
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# Delete a git note
|
|
216
|
+
await repo.delete_note(sha="abc123...")
|
|
217
|
+
|
|
193
218
|
# Get branch diff
|
|
194
219
|
branch_diff = await repo.get_branch_diff(
|
|
195
220
|
branch="feature-branch",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
pierre_storage/__init__.py,sha256=8t6r3VUY4MSZhSuBXaoxzuxd4FLRAbVLWFxXiC_fcQY,1905
|
|
2
|
+
pierre_storage/auth.py,sha256=aT0KNaUKa9fXERtQvJb8C6zL5IxrfA1KLxMBcprAq5A,2074
|
|
3
|
+
pierre_storage/client.py,sha256=LPU0iZoduQvE7CDAMEKcYnMDn6WDH8vZtJw4BKO8lAg,13213
|
|
4
|
+
pierre_storage/commit.py,sha256=ks5hKScHHricJ3sx8DyLSAASM72CPmVv-tbtUgHbUF4,16766
|
|
5
|
+
pierre_storage/errors.py,sha256=-vuA2BUGwyDlErFtdh2boLdk0fDFDFYBEIohJk4AsIs,2184
|
|
6
|
+
pierre_storage/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
|
|
7
|
+
pierre_storage/repo.py,sha256=wnTDVtHx9v8tJTHJ9cHJs8cn5dImsCA3M3xxx7RqhS8,44117
|
|
8
|
+
pierre_storage/types.py,sha256=MNTp4LYIActTUaxII-Qa0xjsKszVSPJKlLOvaw6nIOY,14425
|
|
9
|
+
pierre_storage/version.py,sha256=HFSPY5BelU4QBXsW9MXZlFAE6Sa70XR3e_RzOQe1RO4,315
|
|
10
|
+
pierre_storage/webhook.py,sha256=hyjSmTlU_x35m612erXDqNXbLUh5i5As5GRw7kxylFc,7425
|
|
11
|
+
pierre_storage-0.11.0.dist-info/licenses/LICENSE,sha256=CFzxoMyurfMUB0u0RaXBFZ6IDeUd6FQhKrLR_IeXtuU,1063
|
|
12
|
+
pierre_storage-0.11.0.dist-info/METADATA,sha256=0TaxHV-8GRD2iX1u6JpUrZHGe7P0wKdmp1sg0F8DyVg,22367
|
|
13
|
+
pierre_storage-0.11.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
14
|
+
pierre_storage-0.11.0.dist-info/top_level.txt,sha256=RzcYFaSdETlcwX-45G9Q39xUgXWZLJEWcOiK0p6ZepY,15
|
|
15
|
+
pierre_storage-0.11.0.dist-info/RECORD,,
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
pierre_storage/__init__.py,sha256=uHHdwkzdHldq7OW6WpE6C2S-ooqvMw35oSxvYMb5oGc,1745
|
|
2
|
-
pierre_storage/auth.py,sha256=aT0KNaUKa9fXERtQvJb8C6zL5IxrfA1KLxMBcprAq5A,2074
|
|
3
|
-
pierre_storage/client.py,sha256=qJlQrxvt5YhVYp1RJAEt7pmmYLf6LwleTDb5lvs30kE,11080
|
|
4
|
-
pierre_storage/commit.py,sha256=ks5hKScHHricJ3sx8DyLSAASM72CPmVv-tbtUgHbUF4,16766
|
|
5
|
-
pierre_storage/errors.py,sha256=-vuA2BUGwyDlErFtdh2boLdk0fDFDFYBEIohJk4AsIs,2184
|
|
6
|
-
pierre_storage/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
|
|
7
|
-
pierre_storage/repo.py,sha256=EK82B9yBkB-69t9-lQPCE3Mj5BQ729IQYqg3C1UV4Yg,35982
|
|
8
|
-
pierre_storage/types.py,sha256=u7iispDYc4C2bs-NweJc-sYVjwNlHNgqZ9ywltL11U4,12369
|
|
9
|
-
pierre_storage/version.py,sha256=HFSPY5BelU4QBXsW9MXZlFAE6Sa70XR3e_RzOQe1RO4,315
|
|
10
|
-
pierre_storage/webhook.py,sha256=hyjSmTlU_x35m612erXDqNXbLUh5i5As5GRw7kxylFc,7425
|
|
11
|
-
pierre_storage-0.10.0.dist-info/licenses/LICENSE,sha256=CFzxoMyurfMUB0u0RaXBFZ6IDeUd6FQhKrLR_IeXtuU,1063
|
|
12
|
-
pierre_storage-0.10.0.dist-info/METADATA,sha256=Erw1tJIeuOgE1eHNdeu_V_p4xr5L7gW6P5aSQdgpCKc,21790
|
|
13
|
-
pierre_storage-0.10.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
14
|
-
pierre_storage-0.10.0.dist-info/top_level.txt,sha256=RzcYFaSdETlcwX-45G9Q39xUgXWZLJEWcOiK0p6ZepY,15
|
|
15
|
-
pierre_storage-0.10.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|