snap-python 0.1.1__py3-none-any.whl → 0.1.2__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.
- snap_python/components/store.py +94 -3
- snap_python/schemas/store/info.py +53 -13
- snap_python/schemas/store/refresh.py +109 -0
- snap_python/schemas/store/search.py +1 -1
- snap_python/scrape.py +130 -0
- {snap_python-0.1.1.dist-info → snap_python-0.1.2.dist-info}/METADATA +1 -1
- {snap_python-0.1.1.dist-info → snap_python-0.1.2.dist-info}/RECORD +9 -7
- {snap_python-0.1.1.dist-info → snap_python-0.1.2.dist-info}/WHEEL +1 -1
- {snap_python-0.1.1.dist-info → snap_python-0.1.2.dist-info}/LICENSE +0 -0
snap_python/components/store.py
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import functools
|
|
2
|
+
import uuid
|
|
2
3
|
|
|
3
4
|
import retry
|
|
4
|
-
from httpx import AsyncClient
|
|
5
|
+
from httpx import AsyncClient, Response
|
|
5
6
|
|
|
6
7
|
from snap_python.schemas.store.categories import (
|
|
7
8
|
VALID_CATEGORY_FIELDS,
|
|
8
9
|
CategoryResponse,
|
|
9
10
|
SingleCategoryResponse,
|
|
10
11
|
)
|
|
11
|
-
from snap_python.schemas.store.info import
|
|
12
|
+
from snap_python.schemas.store.info import (
|
|
13
|
+
VALID_SNAP_INFO_FIELDS,
|
|
14
|
+
InfoResponse,
|
|
15
|
+
)
|
|
16
|
+
from snap_python.schemas.store.refresh import (
|
|
17
|
+
VALID_SNAP_REFRESH_FIELDS,
|
|
18
|
+
RefreshRevisionResponse,
|
|
19
|
+
)
|
|
12
20
|
from snap_python.schemas.store.search import (
|
|
13
21
|
VALID_SEARCH_CATEGORY_FIELDS,
|
|
14
22
|
ArchSearchResponse,
|
|
@@ -64,7 +72,9 @@ class StoreEndpoints:
|
|
|
64
72
|
response.raise_for_status()
|
|
65
73
|
return response.json()
|
|
66
74
|
|
|
67
|
-
async def get_snap_info(
|
|
75
|
+
async def get_snap_info(
|
|
76
|
+
self, snap_name: str, fields: list[str] | None = None
|
|
77
|
+
) -> InfoResponse:
|
|
68
78
|
"""
|
|
69
79
|
Get information about a snap.
|
|
70
80
|
|
|
@@ -305,3 +315,84 @@ class StoreEndpoints:
|
|
|
305
315
|
response_json = response.json()
|
|
306
316
|
response_json["arch"] = arch
|
|
307
317
|
return ArchSearchResponse.model_validate(response_json)
|
|
318
|
+
|
|
319
|
+
async def snap_refresh(
|
|
320
|
+
self, snap_name: str, payload: dict, extra_headers: dict = None
|
|
321
|
+
) -> Response:
|
|
322
|
+
"""Query the Store's "snap_refresh" endpoint.
|
|
323
|
+
|
|
324
|
+
Useful for more than refreshing a snap. Notably some snap information is only retrievable from this endpoint. E.g. old revision information
|
|
325
|
+
|
|
326
|
+
:param snap_name: The name of the snap.
|
|
327
|
+
:type snap_name: str
|
|
328
|
+
"""
|
|
329
|
+
route = "/snaps/refresh"
|
|
330
|
+
if extra_headers is None:
|
|
331
|
+
extra_headers = {}
|
|
332
|
+
query = {}
|
|
333
|
+
response = await self.store_client.post(
|
|
334
|
+
f"{self.base_url}{route}", json=payload, headers=extra_headers, params=query
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
if not response.is_success:
|
|
338
|
+
# TODO: Add logging here
|
|
339
|
+
pass
|
|
340
|
+
return response
|
|
341
|
+
|
|
342
|
+
async def get_snap_revision_info(
|
|
343
|
+
self, snap_name: str, revision: int, arch: str, fields=None
|
|
344
|
+
) -> RefreshRevisionResponse:
|
|
345
|
+
"""Get information about a snap revision.
|
|
346
|
+
|
|
347
|
+
:param snap_name: The name of the snap.
|
|
348
|
+
:type snap_name: str
|
|
349
|
+
:param revision: The revision of the snap.
|
|
350
|
+
:type revision: int
|
|
351
|
+
:param arch: The architecture of the snap to retrieve details about (e.g. amd64, arm64, riscv64, etc).
|
|
352
|
+
:type arch: str
|
|
353
|
+
|
|
354
|
+
:returns: The snap revision information.
|
|
355
|
+
:rtype: RefreshRevisionResponse
|
|
356
|
+
"""
|
|
357
|
+
|
|
358
|
+
# cast revision to int
|
|
359
|
+
revision = int(revision)
|
|
360
|
+
extra_headers = {"Snap-Device-Architecture": arch}
|
|
361
|
+
snap_info = await self.get_snap_info(snap_name=snap_name)
|
|
362
|
+
|
|
363
|
+
# I don't think this matters for the "context" field, so using info from the first available
|
|
364
|
+
channel_map_item = snap_info.channel_map[0]
|
|
365
|
+
|
|
366
|
+
payload = {
|
|
367
|
+
"context": [
|
|
368
|
+
{
|
|
369
|
+
"tracking-channel": "stable",
|
|
370
|
+
"snap-id": snap_info.snap_id,
|
|
371
|
+
"revision": channel_map_item.revision,
|
|
372
|
+
"instance-key": str(uuid.uuid4()),
|
|
373
|
+
}
|
|
374
|
+
],
|
|
375
|
+
"actions": [
|
|
376
|
+
{
|
|
377
|
+
"action": "download",
|
|
378
|
+
"name": snap_name,
|
|
379
|
+
"revision": revision,
|
|
380
|
+
"instance-key": str(uuid.uuid4()),
|
|
381
|
+
}
|
|
382
|
+
],
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if fields is not None:
|
|
386
|
+
if not all(field in VALID_SNAP_REFRESH_FIELDS for field in fields):
|
|
387
|
+
raise ValueError(
|
|
388
|
+
f"Invalid fields. Allowed fields: {VALID_SNAP_REFRESH_FIELDS}"
|
|
389
|
+
)
|
|
390
|
+
payload["fields"] = fields
|
|
391
|
+
|
|
392
|
+
response = await self.snap_refresh(
|
|
393
|
+
snap_name=snap_name,
|
|
394
|
+
payload=payload,
|
|
395
|
+
extra_headers=extra_headers,
|
|
396
|
+
)
|
|
397
|
+
response.raise_for_status()
|
|
398
|
+
return RefreshRevisionResponse.model_validate_json(response.content)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
from typing import List, Optional
|
|
6
6
|
|
|
7
|
-
from pydantic import AwareDatetime, BaseModel, ConfigDict, Field
|
|
7
|
+
from pydantic import AliasChoices, AwareDatetime, BaseModel, ConfigDict, Field
|
|
8
8
|
|
|
9
9
|
from snap_python.schemas.snaps import StoreSnap
|
|
10
10
|
|
|
@@ -47,7 +47,11 @@ class Channel(BaseModel):
|
|
|
47
47
|
|
|
48
48
|
architecture: str
|
|
49
49
|
name: str
|
|
50
|
-
released_at: Optional[AwareDatetime] = Field(
|
|
50
|
+
released_at: Optional[AwareDatetime] = Field(
|
|
51
|
+
None,
|
|
52
|
+
alias=AliasChoices("released-at", "released_at"),
|
|
53
|
+
serialization_alias="released-at",
|
|
54
|
+
)
|
|
51
55
|
risk: str
|
|
52
56
|
track: str
|
|
53
57
|
|
|
@@ -56,7 +60,9 @@ class Delta(BaseModel):
|
|
|
56
60
|
model_config = ConfigDict(extra="forbid", exclude_unset=True)
|
|
57
61
|
|
|
58
62
|
format: str
|
|
59
|
-
sha3_384: str = Field(
|
|
63
|
+
sha3_384: Optional[str] = Field(
|
|
64
|
+
None, alias=AliasChoices("sha3-384", "sha3_384"), serialization_alias="sha3-384"
|
|
65
|
+
)
|
|
60
66
|
size: float
|
|
61
67
|
source: float
|
|
62
68
|
target: float
|
|
@@ -67,7 +73,9 @@ class Download(BaseModel):
|
|
|
67
73
|
model_config = ConfigDict(extra="forbid", exclude_unset=True)
|
|
68
74
|
|
|
69
75
|
deltas: List[Delta]
|
|
70
|
-
sha3_384: str = Field(
|
|
76
|
+
sha3_384: Optional[str] = Field(
|
|
77
|
+
None, alias=AliasChoices("sha3-384", "sha3_384"), serialization_alias="sha3-384"
|
|
78
|
+
)
|
|
71
79
|
size: float
|
|
72
80
|
url: str
|
|
73
81
|
|
|
@@ -80,7 +88,9 @@ class Epoch(BaseModel):
|
|
|
80
88
|
|
|
81
89
|
|
|
82
90
|
class Download1(BaseModel):
|
|
83
|
-
sha3_384: Optional[str] = Field(
|
|
91
|
+
sha3_384: Optional[str] = Field(
|
|
92
|
+
None, alias=AliasChoices("sha3-384", "sha3_384"), serialization_alias="sha3-384"
|
|
93
|
+
)
|
|
84
94
|
size: Optional[int] = None
|
|
85
95
|
url: Optional[str] = None
|
|
86
96
|
|
|
@@ -89,7 +99,11 @@ class Resource(BaseModel):
|
|
|
89
99
|
model_config = ConfigDict(extra="forbid", exclude_unset=True)
|
|
90
100
|
|
|
91
101
|
architectures: Optional[List[str]] = None
|
|
92
|
-
created_at: Optional[str] = Field(
|
|
102
|
+
created_at: Optional[str] = Field(
|
|
103
|
+
None,
|
|
104
|
+
alias=AliasChoices("created-at", "created_at"),
|
|
105
|
+
serialization_alias="created-at",
|
|
106
|
+
)
|
|
93
107
|
description: Optional[str] = None
|
|
94
108
|
download: Optional[Download1] = None
|
|
95
109
|
name: Optional[str] = None
|
|
@@ -104,14 +118,26 @@ class ChannelMapItem(BaseModel):
|
|
|
104
118
|
architectures: Optional[List[str]] = None
|
|
105
119
|
base: Optional[str] = None
|
|
106
120
|
channel: Channel
|
|
107
|
-
common_ids: Optional[List[str]] = Field(
|
|
121
|
+
common_ids: Optional[List[str]] = Field(
|
|
122
|
+
None,
|
|
123
|
+
alias=AliasChoices("common-ids", "common_ids"),
|
|
124
|
+
serialization_alias="common-ids",
|
|
125
|
+
)
|
|
108
126
|
confinement: Optional[str] = None
|
|
109
|
-
created_at: Optional[AwareDatetime] = Field(
|
|
127
|
+
created_at: Optional[AwareDatetime] = Field(
|
|
128
|
+
None,
|
|
129
|
+
alias=AliasChoices("created-at", "created_at"),
|
|
130
|
+
serialization_alias="created-at",
|
|
131
|
+
)
|
|
110
132
|
download: Optional[Download] = None
|
|
111
133
|
epoch: Optional[Epoch] = None
|
|
112
134
|
resources: Optional[List[Resource]] = None
|
|
113
135
|
revision: Optional[int] = None
|
|
114
|
-
snap_yaml: Optional[str] = Field(
|
|
136
|
+
snap_yaml: Optional[str] = Field(
|
|
137
|
+
None,
|
|
138
|
+
alias=AliasChoices("snap-yaml", "snap_yaml"),
|
|
139
|
+
serialization_alias="snap-yaml",
|
|
140
|
+
)
|
|
115
141
|
type: Optional[str] = None
|
|
116
142
|
version: Optional[str] = None
|
|
117
143
|
|
|
@@ -126,9 +152,23 @@ class ErrorListItem(BaseModel):
|
|
|
126
152
|
class InfoResponse(BaseModel):
|
|
127
153
|
model_config = ConfigDict(extra="forbid", exclude_unset=True)
|
|
128
154
|
|
|
129
|
-
channel_map: List[ChannelMapItem] = Field(
|
|
130
|
-
|
|
131
|
-
|
|
155
|
+
channel_map: List[ChannelMapItem] = Field(
|
|
156
|
+
...,
|
|
157
|
+
alias=AliasChoices("channel-map", "channel_map"),
|
|
158
|
+
serialization_alias="channel-map",
|
|
159
|
+
)
|
|
160
|
+
default_track: Optional[str] = Field(
|
|
161
|
+
None,
|
|
162
|
+
alias=AliasChoices("default-track", "default_track"),
|
|
163
|
+
serialization_alias="default-track",
|
|
164
|
+
)
|
|
165
|
+
error_list: Optional[List[ErrorListItem]] = Field(
|
|
166
|
+
None,
|
|
167
|
+
alias=AliasChoices("error-list", "error_list"),
|
|
168
|
+
serialization_alias="error-list",
|
|
169
|
+
)
|
|
132
170
|
name: str
|
|
133
171
|
snap: StoreSnap
|
|
134
|
-
snap_id: str = Field(
|
|
172
|
+
snap_id: str = Field(
|
|
173
|
+
..., alias=AliasChoices("snap-id", "snap_id"), serialization_alias="snap-id"
|
|
174
|
+
)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import AliasChoices, AwareDatetime, BaseModel, ConfigDict, Field
|
|
4
|
+
|
|
5
|
+
from snap_python.schemas.snaps import Snap
|
|
6
|
+
from snap_python.schemas.store.info import Download, ErrorListItem, Resource
|
|
7
|
+
|
|
8
|
+
VALID_SNAP_REFRESH_FIELDS = [
|
|
9
|
+
"architectures",
|
|
10
|
+
"base",
|
|
11
|
+
"common-ids",
|
|
12
|
+
"confinement",
|
|
13
|
+
"contact",
|
|
14
|
+
"created-at",
|
|
15
|
+
"description",
|
|
16
|
+
"download",
|
|
17
|
+
"epoch",
|
|
18
|
+
"gated-snap-ids",
|
|
19
|
+
"license",
|
|
20
|
+
"links",
|
|
21
|
+
"media",
|
|
22
|
+
"name",
|
|
23
|
+
"prices",
|
|
24
|
+
"private",
|
|
25
|
+
"publisher",
|
|
26
|
+
"resources",
|
|
27
|
+
"revision",
|
|
28
|
+
"snap-id",
|
|
29
|
+
"snap-yaml",
|
|
30
|
+
"summary",
|
|
31
|
+
"title",
|
|
32
|
+
"type",
|
|
33
|
+
"version",
|
|
34
|
+
"website",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SnapRefreshFields(BaseModel):
|
|
39
|
+
architectures: Optional[list[str]] = None
|
|
40
|
+
base: Optional[str] = None
|
|
41
|
+
common_ids: Optional[list[str]] = Field(
|
|
42
|
+
None, alias=AliasChoices("common-ids", "common_ids")
|
|
43
|
+
)
|
|
44
|
+
confinement: Optional[str] = None
|
|
45
|
+
created_at: Optional[AwareDatetime] = Field(
|
|
46
|
+
None,
|
|
47
|
+
alias=AliasChoices("created-at", "created_at"),
|
|
48
|
+
serialization_alias="created-at",
|
|
49
|
+
)
|
|
50
|
+
download: Optional[Download] = None
|
|
51
|
+
epoch: Optional[dict[str, list[int]]] = None
|
|
52
|
+
gated_snap_ids: Optional[list[str]] = Field(
|
|
53
|
+
None, alias=AliasChoices("gated-snap-ids", "gated_snap_ids")
|
|
54
|
+
)
|
|
55
|
+
license: Optional[str] = None
|
|
56
|
+
prices: Optional[dict[str, str]] = None
|
|
57
|
+
resources: list[Resource] = Field(default_factory=list)
|
|
58
|
+
revision: Optional[int] = None
|
|
59
|
+
snap_id: Optional[str] = Field(
|
|
60
|
+
None, alias=AliasChoices("snap-id", "snap_id"), serialization_alias="snap-id"
|
|
61
|
+
)
|
|
62
|
+
snap_yaml: Optional[str] = Field(
|
|
63
|
+
None,
|
|
64
|
+
alias=AliasChoices("snap-yaml", "snap_yaml"),
|
|
65
|
+
serialization_alias="snap-yaml",
|
|
66
|
+
)
|
|
67
|
+
type: Optional[str] = None
|
|
68
|
+
version: Optional[str] = None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class StoreRefreshSnap(Snap, SnapRefreshFields):
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class RefreshResultData(BaseModel):
|
|
76
|
+
model_config = ConfigDict(extra="forbid", exclude_unset=True)
|
|
77
|
+
|
|
78
|
+
default_track: Optional[str] = Field(
|
|
79
|
+
None,
|
|
80
|
+
alias=AliasChoices("default-track", "default_track"),
|
|
81
|
+
serialization_alias="default-track",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
name: str
|
|
85
|
+
snap: StoreRefreshSnap
|
|
86
|
+
instance_key: str = Field(
|
|
87
|
+
...,
|
|
88
|
+
alias=AliasChoices("instance-key", "instance_key"),
|
|
89
|
+
serialization_alias="instance-key",
|
|
90
|
+
)
|
|
91
|
+
released_at: Optional[AwareDatetime] = Field(
|
|
92
|
+
None,
|
|
93
|
+
alias=AliasChoices("released-at", "released_at"),
|
|
94
|
+
serialization_alias="released-at",
|
|
95
|
+
)
|
|
96
|
+
result: str
|
|
97
|
+
|
|
98
|
+
snap_id: str = Field(
|
|
99
|
+
..., alias=AliasChoices("snap-id", "snap_id"), serialization_alias="snap-id"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class RefreshRevisionResponse(BaseModel):
|
|
104
|
+
error_list: Optional[List[ErrorListItem]] = Field(
|
|
105
|
+
None,
|
|
106
|
+
alias=AliasChoices("error-list", "error_list"),
|
|
107
|
+
serialization_alias="error-list",
|
|
108
|
+
)
|
|
109
|
+
results: List[RefreshResultData]
|
|
@@ -142,6 +142,6 @@ class ArchSearchItem(BaseModel):
|
|
|
142
142
|
|
|
143
143
|
class ArchSearchResponse(BaseModel):
|
|
144
144
|
results: list[ArchSearchItem] = Field(
|
|
145
|
-
alias=AliasPath("_embedded", "clickindex:package")
|
|
145
|
+
alias=AliasChoices(AliasPath("_embedded", "clickindex:package"), "results")
|
|
146
146
|
)
|
|
147
147
|
arch: str
|
snap_python/scrape.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
|
|
3
|
+
from snap_python.client import SnapClient
|
|
4
|
+
from snap_python.schemas.store.info import ChannelMapItem
|
|
5
|
+
from snap_python.schemas.store.refresh import VALID_SNAP_REFRESH_FIELDS
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_highest_revision(channel_map: list[ChannelMapItem]) -> ChannelMapItem:
|
|
9
|
+
"""Find the ChannelMapItem with the highest revision number from a list of ChannelMapItems.
|
|
10
|
+
|
|
11
|
+
Find the highest revision in a provided channel map
|
|
12
|
+
|
|
13
|
+
Parameters:
|
|
14
|
+
channel_map (list[ChannelMapItem]): A list of ChannelMapItem objects to search through.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
ChannelMapItem: The item with the highest revision number
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
|
|
21
|
+
>>> items = [ChannelMapItem(revision=1), ChannelMapItem(revision=5), ChannelMapItem(revision=3)]
|
|
22
|
+
>>> highest = get_highest_revision(items)
|
|
23
|
+
>>> highest.revision
|
|
24
|
+
5
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
print(channel_map)
|
|
29
|
+
return max(channel_map, key=lambda x: x.revision)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def download_snap_file(
|
|
33
|
+
snap_client: SnapClient,
|
|
34
|
+
snap_revision_download: str,
|
|
35
|
+
snap_revision_download_path: pathlib.Path,
|
|
36
|
+
):
|
|
37
|
+
"""Asynchronously downloads a snap file from the Snap Store.
|
|
38
|
+
|
|
39
|
+
This function streams the snap file from the specified URL and writes it to
|
|
40
|
+
the specified path on disk.
|
|
41
|
+
|
|
42
|
+
Parameters:
|
|
43
|
+
snap_client (SnapClient): The client for interacting with the Snap Store.
|
|
44
|
+
snap_revision_download (str): The URL to download the snap file from.
|
|
45
|
+
snap_revision_download_path (pathlib.Path): The path where the downloaded snap file will be saved.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
None
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
store_client = snap_client.store.store_client
|
|
53
|
+
async with store_client.stream(
|
|
54
|
+
"GET", snap_revision_download, follow_redirects=True
|
|
55
|
+
) as response:
|
|
56
|
+
with open(snap_revision_download_path, "wb") as f:
|
|
57
|
+
async for chunk in response.aiter_bytes():
|
|
58
|
+
f.write(chunk)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def get_all_snap_content(
|
|
62
|
+
snap_client: SnapClient,
|
|
63
|
+
snap_name: str,
|
|
64
|
+
output_dir: pathlib.Path | str,
|
|
65
|
+
start_revision: int = 1,
|
|
66
|
+
with_snap_files: bool = False,
|
|
67
|
+
):
|
|
68
|
+
"""
|
|
69
|
+
Asynchronously fetches and downloads all content for a specified snap package across multiple revisions.
|
|
70
|
+
|
|
71
|
+
This function retrieves metadata for each revision of a snap package and optionally downloads
|
|
72
|
+
the snap files themselves. All content is organized into directories by revision number.
|
|
73
|
+
|
|
74
|
+
Parameters:
|
|
75
|
+
snap_client (SnapClient): Initialized Snap client instance for API interactions.
|
|
76
|
+
snap_name (str): Name of the snap package to process.
|
|
77
|
+
output_dir (pathlib.Path | str): Directory where snap content will be saved.
|
|
78
|
+
start_revision (int, optional): First revision to process. Defaults to 1.
|
|
79
|
+
with_snap_files (bool, optional): Whether to download the actual snap package files. Defaults to False.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
None
|
|
83
|
+
|
|
84
|
+
Notes:
|
|
85
|
+
- Creates a directory structure where each revision has its own subdirectory
|
|
86
|
+
- Saves metadata as data.json in each revision directory
|
|
87
|
+
- When with_snap_files=True, also downloads the snap package file for each revision
|
|
88
|
+
|
|
89
|
+
"""
|
|
90
|
+
if not isinstance(output_dir, pathlib.Path):
|
|
91
|
+
output_dir = pathlib.Path(output_dir)
|
|
92
|
+
|
|
93
|
+
if not output_dir.exists():
|
|
94
|
+
output_dir.mkdir(parents=True)
|
|
95
|
+
|
|
96
|
+
current_snap_info = await snap_client.store.get_snap_info(
|
|
97
|
+
snap_name, fields=["name", "channel-map", "revision"]
|
|
98
|
+
)
|
|
99
|
+
current_snap_channel = get_highest_revision(current_snap_info.channel_map)
|
|
100
|
+
|
|
101
|
+
print(
|
|
102
|
+
f"Processing snap {snap_name} from revision {start_revision} to {current_snap_channel.revision}"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
for revision in range(1, current_snap_channel.revision + 1):
|
|
106
|
+
print(f"Processing revision {revision}")
|
|
107
|
+
revision_dir = output_dir / str(revision)
|
|
108
|
+
revision_dir.mkdir(exist_ok=True)
|
|
109
|
+
# get snap revision info
|
|
110
|
+
snap_revision_info = await snap_client.store.get_snap_revision_info(
|
|
111
|
+
snap_name, revision, arch="amd64", fields=VALID_SNAP_REFRESH_FIELDS
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
snap_revision_data = snap_revision_info.results[0]
|
|
115
|
+
|
|
116
|
+
with open(revision_dir / "data.json", "w") as f:
|
|
117
|
+
f.write(snap_revision_data.model_dump_json(indent=2))
|
|
118
|
+
|
|
119
|
+
# download snap revision
|
|
120
|
+
snap_revision_download = snap_revision_data.snap.download.url
|
|
121
|
+
|
|
122
|
+
# download the file
|
|
123
|
+
snap_revision_download_path = (
|
|
124
|
+
revision_dir / snap_revision_download.split("/")[-1]
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
if with_snap_files:
|
|
128
|
+
await download_snap_file(
|
|
129
|
+
snap_client, snap_revision_download, snap_revision_download_path
|
|
130
|
+
)
|
|
@@ -3,21 +3,23 @@ snap_python/client.py,sha256=rZAfzcQNhdtTyjQml1N-9-nAianFyEwOaI4H_0FMaFE,6612
|
|
|
3
3
|
snap_python/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
snap_python/components/config.py,sha256=IO_gCrumphEZioINFmXxb8I5OVsKC9cgSa28sQbZ700,1775
|
|
5
5
|
snap_python/components/snaps.py,sha256=FQ362L83jmhu3MLKszOU1w5jGP8l5-WNBlUTLnR6IW8,13176
|
|
6
|
-
snap_python/components/store.py,sha256=
|
|
6
|
+
snap_python/components/store.py,sha256=8r1WUOBu2vPpDXGSSgK9f1HKU3Rfl6wAPu6siTnEcPU,13803
|
|
7
7
|
snap_python/schemas/changes.py,sha256=7SYUFXAQq9arl7hAA3YQ0YubMurPJtknTOVxP8ryqew,1919
|
|
8
8
|
snap_python/schemas/common.py,sha256=GLtC9aBo3O8vLKX9PNvzE-_DKN02OGpwsS7Ik2WK9-8,2608
|
|
9
9
|
snap_python/schemas/config.py,sha256=xXTGhk6TpwI8RlR6_qnfWlBHrBnTwr3hRCO5qCnN-6U,107
|
|
10
10
|
snap_python/schemas/snaps.py,sha256=cO0UwghFZwfIlJtAjYDdNgfLKwU7953B9bgolpXcUL0,2980
|
|
11
11
|
snap_python/schemas/store/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
12
|
snap_python/schemas/store/categories.py,sha256=PewotRqoPs7cMKtlZKsHVNfnJoItmNKMekS1Be-46l0,1082
|
|
13
|
-
snap_python/schemas/store/info.py,sha256=
|
|
13
|
+
snap_python/schemas/store/info.py,sha256=K2y1NOrSLZEszGJur5L0W_1ap4P038cK4ThSjLcyQLA,4407
|
|
14
14
|
snap_python/schemas/store/json/category_by_name_schema.json,sha256=3-95m6V2WXaPCu_DZw-STa9YHPEbr7CI8fbhjaq881s,1674
|
|
15
15
|
snap_python/schemas/store/json/category_schema.json,sha256=c4h_eyyBbChOiNby3hYfjAh9i_1MDbPJnHHU4rGOGss,1898
|
|
16
16
|
snap_python/schemas/store/json/snaps_find_schema.json,sha256=TfKpnievs5QOc7ah97ATMCQTOEfKspONTZnwSwq_0fI,8866
|
|
17
17
|
snap_python/schemas/store/json/snaps_info_schema.json,sha256=udoOovb6Ea7EnE_R9RuZ1CpY1wOJ3-K8xP4-8zKHgrQ,15888
|
|
18
|
-
snap_python/schemas/store/
|
|
18
|
+
snap_python/schemas/store/refresh.py,sha256=EjzQurejMggHJUqALoJUpXt26nh_OClkd1qZYaibews,2928
|
|
19
|
+
snap_python/schemas/store/search.py,sha256=-Fd1G821-V6JVxvbcgfnzXy8VQoWnS9MR1oLdRurqu0,3743
|
|
20
|
+
snap_python/scrape.py,sha256=ZL-kaKdhkz2SfM0iDoJdRLH-lovRyONRNyOm-xivyII,4599
|
|
19
21
|
snap_python/utils.py,sha256=cmUooPg5DzNL197Xxvu7LGD4Gwk3xxsT68wbVytRtV0,1240
|
|
20
|
-
snap_python-0.1.
|
|
21
|
-
snap_python-0.1.
|
|
22
|
-
snap_python-0.1.
|
|
23
|
-
snap_python-0.1.
|
|
22
|
+
snap_python-0.1.2.dist-info/LICENSE,sha256=lYXNZszxC_l2rlBvrHYs-esDegxWh8MKtjrdxA24uOk,1072
|
|
23
|
+
snap_python-0.1.2.dist-info/METADATA,sha256=mdz8xXB3SRa_3cnXmmJjYSlY7f4BuzYOIkw7Rw8chqw,1883
|
|
24
|
+
snap_python-0.1.2.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
|
25
|
+
snap_python-0.1.2.dist-info/RECORD,,
|
|
File without changes
|