glpkg 1.1.0__py3-none-any.whl → 1.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.
- gitlab/__init__.py +3 -1
- gitlab/__main__.py +17 -0
- gitlab/cli_handler.py +236 -25
- gitlab/packages.py +331 -37
- {glpkg-1.1.0.dist-info → glpkg-1.3.0.dist-info}/METADATA +27 -16
- glpkg-1.3.0.dist-info/RECORD +10 -0
- glpkg-1.1.0.dist-info/RECORD +0 -10
- {glpkg-1.1.0.dist-info → glpkg-1.3.0.dist-info}/WHEEL +0 -0
- {glpkg-1.1.0.dist-info → glpkg-1.3.0.dist-info}/entry_points.txt +0 -0
- {glpkg-1.1.0.dist-info → glpkg-1.3.0.dist-info}/licenses/LICENSE.md +0 -0
- {glpkg-1.1.0.dist-info → glpkg-1.3.0.dist-info}/top_level.txt +0 -0
gitlab/__init__.py
CHANGED
gitlab/__main__.py
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
|
+
"""GitLab packages main module"""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
1
4
|
import sys
|
|
2
5
|
|
|
3
6
|
from gitlab.cli_handler import CLIHandler
|
|
4
7
|
|
|
5
8
|
|
|
6
9
|
def cli() -> int:
|
|
10
|
+
"""
|
|
11
|
+
Runs the main program of the glpkg.
|
|
12
|
+
|
|
13
|
+
Uses arguments from command line and executes the given command.
|
|
14
|
+
|
|
15
|
+
Return
|
|
16
|
+
------
|
|
17
|
+
int
|
|
18
|
+
Zero when everything is fine, non-zero otherwise.
|
|
19
|
+
"""
|
|
20
|
+
logging.basicConfig(
|
|
21
|
+
level=logging.DEBUG,
|
|
22
|
+
handlers=[logging.FileHandler("glpkg.log")], # , logging.StreamHandler()],
|
|
23
|
+
)
|
|
7
24
|
handler = CLIHandler()
|
|
8
25
|
return handler.do_it()
|
|
9
26
|
|
gitlab/cli_handler.py
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
|
+
"""CLI handler for glpkg"""
|
|
2
|
+
|
|
1
3
|
import argparse
|
|
2
|
-
import netrc
|
|
4
|
+
from netrc import netrc
|
|
3
5
|
import os
|
|
6
|
+
import sys
|
|
7
|
+
import urllib
|
|
4
8
|
from gitlab import Packages, __version__
|
|
5
9
|
|
|
6
10
|
|
|
7
11
|
class CLIHandler:
|
|
12
|
+
"""Class to parse CLI arguments and run the requested command"""
|
|
13
|
+
|
|
8
14
|
def __init__(self):
|
|
15
|
+
"""
|
|
16
|
+
Creates a new instance of CLIHandler.
|
|
17
|
+
|
|
18
|
+
Parses the arguments from command line and prepares everything
|
|
19
|
+
to be ready to executed. Just use do_it to run it!
|
|
20
|
+
"""
|
|
9
21
|
parser = argparse.ArgumentParser(
|
|
10
22
|
description="Toolbox for GitLab generic packages"
|
|
11
23
|
)
|
|
@@ -14,12 +26,14 @@ class CLIHandler:
|
|
|
14
26
|
subparsers = parser.add_subparsers()
|
|
15
27
|
list_parser = subparsers.add_parser(
|
|
16
28
|
name="list",
|
|
17
|
-
description="Lists the available version of a package from the
|
|
29
|
+
description="Lists the available version of a package from the "
|
|
30
|
+
"package registry.",
|
|
18
31
|
)
|
|
19
32
|
self._register_list_parser(list_parser)
|
|
20
33
|
download_parser = subparsers.add_parser(
|
|
21
34
|
name="download",
|
|
22
|
-
description="Downloads all files from a specific package version
|
|
35
|
+
description="Downloads all files from a specific package version "
|
|
36
|
+
"to the current directory.",
|
|
23
37
|
)
|
|
24
38
|
self._register_download_parser(download_parser)
|
|
25
39
|
upload_parser = subparsers.add_parser(
|
|
@@ -28,27 +42,81 @@ class CLIHandler:
|
|
|
28
42
|
self._register_upload_parser(upload_parser)
|
|
29
43
|
self.args = parser.parse_args()
|
|
30
44
|
|
|
31
|
-
def _print_version(self,
|
|
45
|
+
def _print_version(self, _args: argparse.Namespace) -> int:
|
|
46
|
+
"""
|
|
47
|
+
A handler for printing the version of the tool to the console.
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
_args : argparse.Namespace
|
|
52
|
+
Unused.
|
|
53
|
+
|
|
54
|
+
Return
|
|
55
|
+
------
|
|
56
|
+
int
|
|
57
|
+
Zero when printing to console succeeded.
|
|
58
|
+
"""
|
|
32
59
|
print(__version__)
|
|
33
60
|
return 0
|
|
34
61
|
|
|
35
62
|
def do_it(self) -> int:
|
|
36
|
-
|
|
63
|
+
"""
|
|
64
|
+
Executes the requested command.
|
|
65
|
+
|
|
66
|
+
In case of error, prints to stderr.
|
|
67
|
+
|
|
68
|
+
Return
|
|
69
|
+
------
|
|
70
|
+
int
|
|
71
|
+
Zero when everything went fine, non-zero otherwise.
|
|
72
|
+
"""
|
|
73
|
+
ret = 1
|
|
74
|
+
try:
|
|
75
|
+
ret = self.args.action(self.args)
|
|
76
|
+
except urllib.error.HTTPError as e:
|
|
77
|
+
# GitLab API returns 404 when a resource is not found
|
|
78
|
+
# but also when the user has no access to the resource
|
|
79
|
+
print("Oops! Something did go wrong.", file=sys.stderr)
|
|
80
|
+
print(e, file=sys.stderr)
|
|
81
|
+
print(
|
|
82
|
+
"Note that Error 404 may also indicate authentication issues with GitLab API.",
|
|
83
|
+
file=sys.stderr,
|
|
84
|
+
)
|
|
85
|
+
print("Check your arguments and credentials.", file=sys.stderr)
|
|
86
|
+
return ret
|
|
87
|
+
|
|
88
|
+
def _register_common_arguments(self, parser: argparse.ArgumentParser) -> None:
|
|
89
|
+
"""
|
|
90
|
+
Registers common arguments to the parser:
|
|
91
|
+
- host
|
|
92
|
+
- ci
|
|
93
|
+
- project
|
|
94
|
+
- name
|
|
95
|
+
- token
|
|
96
|
+
- netrc
|
|
37
97
|
|
|
38
|
-
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
parser:
|
|
101
|
+
The argparser where to register the common arguments.
|
|
102
|
+
"""
|
|
39
103
|
group = parser.add_mutually_exclusive_group()
|
|
40
104
|
group.add_argument(
|
|
41
105
|
"-H",
|
|
42
106
|
"--host",
|
|
43
107
|
default="gitlab.com",
|
|
44
108
|
type=str,
|
|
45
|
-
help="The host address of GitLab instance without scheme,
|
|
109
|
+
help="The host address of GitLab instance without scheme, "
|
|
110
|
+
"for example gitlab.com. Note that only https scheme is supported.",
|
|
46
111
|
)
|
|
47
112
|
group.add_argument(
|
|
48
113
|
"-c",
|
|
49
114
|
"--ci",
|
|
50
115
|
action="store_true",
|
|
51
|
-
help="Use this in GitLab jobs. In this case CI_SERVER_HOST, CI_PROJECT_ID,
|
|
116
|
+
help="Use this in GitLab jobs. In this case CI_SERVER_HOST, CI_PROJECT_ID, "
|
|
117
|
+
"and CI_JOB_TOKEN variables from the environment are used. --project and --token "
|
|
118
|
+
"can be used to override project ID and the CI_JOB_TOKEN to a personal or project "
|
|
119
|
+
"access token.",
|
|
52
120
|
)
|
|
53
121
|
parser.add_argument(
|
|
54
122
|
"-p",
|
|
@@ -62,20 +130,77 @@ class CLIHandler:
|
|
|
62
130
|
"-t",
|
|
63
131
|
"--token",
|
|
64
132
|
type=str,
|
|
65
|
-
help="Private or project access token that is used to authenticate with
|
|
133
|
+
help="Private or project access token that is used to authenticate with "
|
|
134
|
+
"the package registry. Leave empty if the registry is public. The token "
|
|
135
|
+
"must have 'read API' or 'API' scope.",
|
|
66
136
|
)
|
|
67
137
|
group2.add_argument(
|
|
68
138
|
"--netrc",
|
|
69
139
|
action="store_true",
|
|
70
|
-
help="Set to use a token from .netrc file (~/.netrc) for the host. The
|
|
140
|
+
help="Set to use a token from .netrc file (~/.netrc) for the host. The "
|
|
141
|
+
".netrc username is ignored due to API restrictions. PRIVATE-TOKEN is used "
|
|
142
|
+
"instead. Note that .netrc file access rights must be correct.",
|
|
71
143
|
)
|
|
72
144
|
|
|
73
|
-
def _register_download_parser(self, parser):
|
|
145
|
+
def _register_download_parser(self, parser: argparse.ArgumentParser):
|
|
146
|
+
"""
|
|
147
|
+
Registers the download command related arguments to the parser:
|
|
148
|
+
- version
|
|
149
|
+
- file
|
|
150
|
+
- destination
|
|
151
|
+
- action
|
|
152
|
+
|
|
153
|
+
Additionally, registers the common args.
|
|
154
|
+
|
|
155
|
+
Parameters
|
|
156
|
+
----------
|
|
157
|
+
parser:
|
|
158
|
+
The argparser where to register the download arguments.
|
|
159
|
+
"""
|
|
74
160
|
self._register_common_arguments(parser)
|
|
75
161
|
parser.add_argument("-v", "--version", type=str, help="The package version.")
|
|
162
|
+
parser.add_argument(
|
|
163
|
+
"-f",
|
|
164
|
+
"--file",
|
|
165
|
+
type=str,
|
|
166
|
+
help="The file to download from the package. If not defined, all "
|
|
167
|
+
"files are downloaded.",
|
|
168
|
+
)
|
|
169
|
+
parser.add_argument(
|
|
170
|
+
"-d",
|
|
171
|
+
"--destination",
|
|
172
|
+
default="",
|
|
173
|
+
type=str,
|
|
174
|
+
help="The path where the file(s) are downloaded. If not defined, "
|
|
175
|
+
"the current working directory is used.",
|
|
176
|
+
)
|
|
76
177
|
parser.set_defaults(action=self._download_handler)
|
|
77
178
|
|
|
78
|
-
def _args(self, args):
|
|
179
|
+
def _args(self, args) -> tuple[str, str, str, str, str]:
|
|
180
|
+
"""
|
|
181
|
+
Returns the connection parameters according to the args
|
|
182
|
+
|
|
183
|
+
Parameters
|
|
184
|
+
----------
|
|
185
|
+
args:
|
|
186
|
+
The args that are used to determined the connection parameters
|
|
187
|
+
|
|
188
|
+
Returns
|
|
189
|
+
-------
|
|
190
|
+
host : str
|
|
191
|
+
The GitLab host name
|
|
192
|
+
project : str
|
|
193
|
+
The Project ID or name to use
|
|
194
|
+
name : str
|
|
195
|
+
The package name
|
|
196
|
+
token_user : str
|
|
197
|
+
The token user according to the args. If ci is used, returns `JOB-TOKEN`, else
|
|
198
|
+
`PRIVATE-TOKEN`.
|
|
199
|
+
token : str
|
|
200
|
+
The token according to the args. If token is set, returns it. If netrc is set,
|
|
201
|
+
reads the token from the .netrc file. If ci is set, reads the environment
|
|
202
|
+
variable CI_JOB_TOKEN. Otherwise returns None.
|
|
203
|
+
"""
|
|
79
204
|
if args.ci:
|
|
80
205
|
host = os.environ["CI_SERVER_HOST"]
|
|
81
206
|
project = os.environ["CI_PROJECT_ID"]
|
|
@@ -92,21 +217,39 @@ class CLIHandler:
|
|
|
92
217
|
token = args.token
|
|
93
218
|
token_user = "PRIVATE-TOKEN"
|
|
94
219
|
if args.netrc:
|
|
95
|
-
_, _, token = netrc
|
|
220
|
+
_, _, token = netrc().authenticators(host)
|
|
96
221
|
token_user = "PRIVATE-TOKEN"
|
|
97
222
|
name = args.name
|
|
98
223
|
return host, project, name, token_user, token
|
|
99
224
|
|
|
100
|
-
def _download_handler(self, args) -> int:
|
|
225
|
+
def _download_handler(self, args: argparse.Namespace) -> int:
|
|
226
|
+
"""
|
|
227
|
+
Downloads package file(s) from GitLab package registry.
|
|
228
|
+
|
|
229
|
+
Parameters
|
|
230
|
+
----------
|
|
231
|
+
args : argparse.Namespace
|
|
232
|
+
The parsed arguments
|
|
233
|
+
|
|
234
|
+
Returns
|
|
235
|
+
-------
|
|
236
|
+
int
|
|
237
|
+
Zero if everything goes well, non-zero otherwise
|
|
238
|
+
"""
|
|
101
239
|
ret = 1
|
|
102
240
|
host, project, name, token_user, token = self._args(args)
|
|
103
241
|
version = args.version
|
|
242
|
+
destination = args.destination
|
|
104
243
|
gitlab = Packages(host, token_user, token)
|
|
105
244
|
package_id = gitlab.get_package_id(project, name, version)
|
|
106
245
|
if package_id:
|
|
107
|
-
files =
|
|
246
|
+
files = []
|
|
247
|
+
if args.file:
|
|
248
|
+
files.append(args.file)
|
|
249
|
+
else:
|
|
250
|
+
files = gitlab.list_files(project, package_id)
|
|
108
251
|
for file in files:
|
|
109
|
-
ret = gitlab.download_file(project, name, version, file)
|
|
252
|
+
ret = gitlab.download_file(project, name, version, file, destination)
|
|
110
253
|
if ret:
|
|
111
254
|
print("Failed to download file " + file)
|
|
112
255
|
break
|
|
@@ -114,11 +257,35 @@ class CLIHandler:
|
|
|
114
257
|
print("No package " + name + " version " + version + " found!")
|
|
115
258
|
return ret
|
|
116
259
|
|
|
117
|
-
def _register_list_parser(self, parser):
|
|
260
|
+
def _register_list_parser(self, parser: argparse.ArgumentParser):
|
|
261
|
+
"""
|
|
262
|
+
Registers the list command related arguments to the parser:
|
|
263
|
+
- action
|
|
264
|
+
|
|
265
|
+
Additionally, registers the common args.
|
|
266
|
+
|
|
267
|
+
Parameters
|
|
268
|
+
----------
|
|
269
|
+
parser:
|
|
270
|
+
The argparser where to register the list arguments.
|
|
271
|
+
"""
|
|
118
272
|
self._register_common_arguments(parser)
|
|
119
273
|
parser.set_defaults(action=self._list_packages)
|
|
120
274
|
|
|
121
275
|
def _list_packages(self, args: argparse.Namespace) -> int:
|
|
276
|
+
"""
|
|
277
|
+
List package versions from GitLab package registry.
|
|
278
|
+
|
|
279
|
+
Parameters
|
|
280
|
+
----------
|
|
281
|
+
args : argparse.Namespace
|
|
282
|
+
The parsed arguments
|
|
283
|
+
|
|
284
|
+
Returns
|
|
285
|
+
-------
|
|
286
|
+
int
|
|
287
|
+
Zero if everything goes well, non-zero otherwise
|
|
288
|
+
"""
|
|
122
289
|
host, project, name, token_user, token = self._args(args)
|
|
123
290
|
gitlab = Packages(host, token_user, token)
|
|
124
291
|
packages = gitlab.list_packages(project, name)
|
|
@@ -126,25 +293,69 @@ class CLIHandler:
|
|
|
126
293
|
for package in packages:
|
|
127
294
|
print(package["name"] + "\t" + package["version"])
|
|
128
295
|
|
|
129
|
-
def _register_upload_parser(self, parser):
|
|
296
|
+
def _register_upload_parser(self, parser: argparse.ArgumentParser):
|
|
297
|
+
"""
|
|
298
|
+
Registers the upload command related arguments to the parser:
|
|
299
|
+
- version
|
|
300
|
+
- file
|
|
301
|
+
- action
|
|
302
|
+
|
|
303
|
+
Additionally, registers the common args.
|
|
304
|
+
|
|
305
|
+
Parameters
|
|
306
|
+
----------
|
|
307
|
+
parser:
|
|
308
|
+
The argparser where to register the upload arguments.
|
|
309
|
+
"""
|
|
130
310
|
self._register_common_arguments(parser)
|
|
131
311
|
parser.add_argument("-v", "--version", type=str, help="The package version.")
|
|
132
312
|
parser.add_argument(
|
|
133
313
|
"-f",
|
|
134
314
|
"--file",
|
|
135
315
|
type=str,
|
|
136
|
-
help="The file to be uploaded, for example my_file.txt. Note that
|
|
316
|
+
help="The file to be uploaded, for example my_file.txt. Note that "
|
|
317
|
+
"only relative paths (to the source) are supported and the relative "
|
|
318
|
+
"path is preserved when uploading the file. If left undefined, all files "
|
|
319
|
+
"of the source directory are uploaded. For example --source=temp --file=myfile "
|
|
320
|
+
"will upload myfile to the GitLab generic package root. However using --source=. "
|
|
321
|
+
"(or omittinge source) --file=temp/myfile will upload the file to temp folder "
|
|
322
|
+
"in the GitLab package.",
|
|
323
|
+
)
|
|
324
|
+
parser.add_argument(
|
|
325
|
+
"-s",
|
|
326
|
+
"--source",
|
|
327
|
+
type=str,
|
|
328
|
+
default="",
|
|
329
|
+
help="The source directory of the uploaded file(s). Defaults to current"
|
|
330
|
+
"working directory.",
|
|
137
331
|
)
|
|
138
332
|
parser.set_defaults(action=self._upload)
|
|
139
333
|
|
|
140
|
-
def _upload(self, args) -> int:
|
|
141
|
-
|
|
334
|
+
def _upload(self, args: argparse.Namespace) -> int:
|
|
335
|
+
"""
|
|
336
|
+
Uploads a file to a GitLab package registry
|
|
337
|
+
|
|
338
|
+
Parameters
|
|
339
|
+
----------
|
|
340
|
+
args : argparse.Namespace
|
|
341
|
+
The arguments from command line
|
|
342
|
+
|
|
343
|
+
Returns
|
|
344
|
+
-------
|
|
345
|
+
int
|
|
346
|
+
Zero if everything went fine, non-zero otherwise.
|
|
347
|
+
"""
|
|
348
|
+
ret = 0
|
|
142
349
|
host, project, name, token_user, token = self._args(args)
|
|
143
350
|
version = args.version
|
|
144
351
|
file = args.file
|
|
145
|
-
|
|
352
|
+
source = args.source
|
|
353
|
+
if file:
|
|
354
|
+
# Check if the uploaded file exists.
|
|
355
|
+
if not os.path.isfile(os.path.join(source, file)):
|
|
356
|
+
print("File " + file + " does not exist!")
|
|
357
|
+
ret = 1
|
|
358
|
+
if not ret:
|
|
146
359
|
gitlab = Packages(host, token_user, token)
|
|
147
|
-
ret = gitlab.upload_file(project, name, version, file)
|
|
148
|
-
else:
|
|
149
|
-
print("File " + file + " does not exist!")
|
|
360
|
+
ret = gitlab.upload_file(project, name, version, file, source)
|
|
150
361
|
return ret
|
gitlab/packages.py
CHANGED
|
@@ -1,66 +1,199 @@
|
|
|
1
|
+
"""GitLab generic packages module"""
|
|
2
|
+
|
|
3
|
+
from glob import glob
|
|
1
4
|
from http.client import HTTPMessage
|
|
2
|
-
import
|
|
5
|
+
from json import loads
|
|
3
6
|
import logging
|
|
7
|
+
import os
|
|
4
8
|
from urllib import request, parse
|
|
5
9
|
|
|
6
10
|
logger = logging.getLogger(__name__)
|
|
7
11
|
|
|
8
12
|
|
|
9
13
|
class Packages:
|
|
14
|
+
"""Class to interact with GitLab packages REST API"""
|
|
15
|
+
|
|
10
16
|
def __init__(self, host: str, token_type: str, token: str):
|
|
17
|
+
"""
|
|
18
|
+
Creates a new instance of Packages class.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
host : str
|
|
23
|
+
The GitLab instance hostname, without schema.
|
|
24
|
+
The host will be used for the package API interaction.
|
|
25
|
+
For example gitlab.com.
|
|
26
|
+
token_type : str
|
|
27
|
+
The token type or "user" to authenticate with GitLab REST API.
|
|
28
|
+
For personal, project, and group tokens this is `PRIVATE-TOKEN`.
|
|
29
|
+
For `CI_JOB_TOKEN` this is `JOB-TOKEN`.
|
|
30
|
+
Can be left empty when authentication is not used.
|
|
31
|
+
token : str
|
|
32
|
+
The token (secret) to authenticate with GitLab REST API.
|
|
33
|
+
This can be a personal token, project token, or`CI_JOB_TOKEN`.
|
|
34
|
+
Leave empty when authentication is not used.
|
|
35
|
+
"""
|
|
11
36
|
self.host = host
|
|
12
37
|
self.token_type = token_type
|
|
13
38
|
self.token = token
|
|
14
39
|
|
|
15
40
|
def api_url(self) -> str:
|
|
16
|
-
|
|
41
|
+
"""
|
|
42
|
+
Returns the GitLab REST API URL by using the host variable.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
str
|
|
47
|
+
The GitLab REST API URL, for example `https://gitlab.com/api/v4/`.
|
|
48
|
+
"""
|
|
49
|
+
return f"https://{self.host}/api/v4/"
|
|
17
50
|
|
|
18
51
|
def project_api_url(self, project: str) -> str:
|
|
19
|
-
|
|
52
|
+
"""
|
|
53
|
+
Returns the project REST API URL of the project
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
project : str
|
|
58
|
+
The project ID or the path of the project, including namespace.
|
|
59
|
+
Examples: `123` or `namespace/project`.
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
str
|
|
64
|
+
The project REST API URL, for example `https://gitlab.com/api/v4/projects/123/`
|
|
65
|
+
or `https://gitlab.com/api/v4/projects/namespace%2Fproject/`
|
|
66
|
+
"""
|
|
67
|
+
return f"{self.api_url()}projects/{parse.quote_plus(project)}/"
|
|
20
68
|
|
|
21
69
|
def get_headers(self) -> dict:
|
|
70
|
+
"""
|
|
71
|
+
Creates headers for a GitLab REST API call.
|
|
72
|
+
|
|
73
|
+
The headers contain token for authentication according to the
|
|
74
|
+
instance variables.
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
dict
|
|
79
|
+
Headers for a REST API request, that contain the authentication token.
|
|
80
|
+
"""
|
|
22
81
|
headers = {}
|
|
23
82
|
if self.token_type and self.token:
|
|
24
83
|
headers = {self.token_type: self.token}
|
|
25
84
|
return headers
|
|
26
85
|
|
|
27
86
|
def _request(self, url: str) -> tuple[int, bytes, HTTPMessage]:
|
|
28
|
-
|
|
87
|
+
"""
|
|
88
|
+
Makes a HTTP request to the given URL, and returns
|
|
89
|
+
the response status, body, and headers.
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
url : str
|
|
95
|
+
The URL of the HTTP request to make.
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
int
|
|
100
|
+
The HTTP response code, such as 200
|
|
101
|
+
bytes
|
|
102
|
+
The HTTP response body read as bytes
|
|
103
|
+
HTTPMessage
|
|
104
|
+
The HTTP response headers
|
|
105
|
+
"""
|
|
106
|
+
logger.debug("Requesting %s", url)
|
|
29
107
|
req = request.Request(url, headers=self.get_headers())
|
|
30
108
|
with request.urlopen(req) as response:
|
|
31
109
|
return response.status, response.read(), response.headers
|
|
32
110
|
|
|
33
111
|
def _get_next_page(self, headers: HTTPMessage) -> int:
|
|
112
|
+
"""
|
|
113
|
+
Returns the next page from headers for pagination.
|
|
114
|
+
|
|
115
|
+
Uses the field x-next-page from the headers.
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
headers : HTTPMessage
|
|
120
|
+
The header from which to get the next page number for
|
|
121
|
+
pagination
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
int
|
|
126
|
+
The next page number. If headers were empty or they
|
|
127
|
+
did not include suitable item, returns 0. In such
|
|
128
|
+
case, do not attempt to fetch a next page.
|
|
129
|
+
"""
|
|
34
130
|
ret = 0
|
|
35
131
|
if headers:
|
|
36
132
|
next_page = headers.get("x-next-page")
|
|
37
133
|
if next_page:
|
|
38
134
|
ret = int(next_page)
|
|
39
|
-
logger.debug("Response incomplete, next page is "
|
|
135
|
+
logger.debug("Response incomplete, next page is %s", next_page)
|
|
40
136
|
else:
|
|
41
137
|
logger.debug("Response complete")
|
|
42
138
|
return ret
|
|
43
139
|
|
|
44
140
|
def _build_query(self, arg: str, page: int) -> str:
|
|
141
|
+
"""
|
|
142
|
+
Builds a query for a GitLab REST API request
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
arg : str
|
|
147
|
+
The args of the query that is endpoint specific
|
|
148
|
+
page : int
|
|
149
|
+
Page number for the pagination of the request.
|
|
150
|
+
Set to 0 to omit the pagination.
|
|
151
|
+
|
|
152
|
+
Returns
|
|
153
|
+
-------
|
|
154
|
+
str
|
|
155
|
+
A query string for a REST API request. Append this to
|
|
156
|
+
the request URL. Example `?arg=this&page=3`.
|
|
157
|
+
"""
|
|
45
158
|
query = ""
|
|
46
159
|
if arg or page:
|
|
47
160
|
if page:
|
|
48
161
|
page = "page=" + str(page)
|
|
49
|
-
query = "?{
|
|
162
|
+
query = f"?{'&'.join(filter(None, (arg, page)))}"
|
|
50
163
|
return query
|
|
51
164
|
|
|
52
|
-
def gl_project_api(self, project: str,
|
|
165
|
+
def gl_project_api(self, project: str, apath: str, arg: str = None) -> list:
|
|
166
|
+
"""
|
|
167
|
+
Returns data from the project REST API for the path. In case
|
|
168
|
+
of multiple pages, all data will be returned.
|
|
169
|
+
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
project : str
|
|
173
|
+
The project ID or the path of the project, including namespace.
|
|
174
|
+
Examples: `123` or `namespace/project`.
|
|
175
|
+
apath : str
|
|
176
|
+
The path of the project API endpoint that is called. For example packages
|
|
177
|
+
arg : str, optional
|
|
178
|
+
Additional arguments for the query of the URL, for example to filter
|
|
179
|
+
results: package_name=mypackage
|
|
180
|
+
|
|
181
|
+
Returns
|
|
182
|
+
-------
|
|
183
|
+
list
|
|
184
|
+
Data from GitLab REST API endpoint with the arguments.
|
|
185
|
+
"""
|
|
53
186
|
data = []
|
|
54
187
|
more = True
|
|
55
188
|
page = None
|
|
56
189
|
while more:
|
|
57
190
|
more = False
|
|
58
191
|
query = self._build_query(arg, page)
|
|
59
|
-
url = self.project_api_url(project) +
|
|
192
|
+
url = self.project_api_url(project) + apath + query
|
|
60
193
|
status, res_data, headers = self._request(url)
|
|
61
|
-
logger.debug("Response status: "
|
|
62
|
-
res_data =
|
|
63
|
-
logger.debug("Response data: "
|
|
194
|
+
logger.debug("Response status: %d", status)
|
|
195
|
+
res_data = loads(res_data)
|
|
196
|
+
logger.debug("Response data: %s", res_data)
|
|
64
197
|
data = data + res_data
|
|
65
198
|
page = self._get_next_page(headers)
|
|
66
199
|
if page:
|
|
@@ -68,8 +201,24 @@ class Packages:
|
|
|
68
201
|
return data
|
|
69
202
|
|
|
70
203
|
def list_packages(self, project: str, package_name: str) -> list:
|
|
204
|
+
"""
|
|
205
|
+
Lists the available versions of the package
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
project : str
|
|
210
|
+
The project ID or the path of the project, including namespace.
|
|
211
|
+
Examples: `123` or `namespace/project`.
|
|
212
|
+
package_name : str
|
|
213
|
+
The name of the package that is listed.
|
|
214
|
+
|
|
215
|
+
Returns
|
|
216
|
+
-------
|
|
217
|
+
list
|
|
218
|
+
List of {package: name, version: version} that are available.
|
|
219
|
+
"""
|
|
71
220
|
packages = []
|
|
72
|
-
logger.debug("Listing packages with name "
|
|
221
|
+
logger.debug("Listing packages with name %s", package_name)
|
|
73
222
|
data = self.gl_project_api(
|
|
74
223
|
project, "packages", "package_name=" + parse.quote_plus(package_name)
|
|
75
224
|
)
|
|
@@ -84,10 +233,26 @@ class Packages:
|
|
|
84
233
|
return packages
|
|
85
234
|
|
|
86
235
|
def list_files(self, project: str, package_id: int) -> list:
|
|
236
|
+
"""
|
|
237
|
+
Lists all files of a specific package ID from GitLab REST API
|
|
238
|
+
|
|
239
|
+
Parameters
|
|
240
|
+
----------
|
|
241
|
+
project : str
|
|
242
|
+
The project ID or the path of the project, including namespace.
|
|
243
|
+
Examples: `123` or `namespace/project`.
|
|
244
|
+
package_id : int
|
|
245
|
+
The package ID that is listed
|
|
246
|
+
|
|
247
|
+
Return
|
|
248
|
+
------
|
|
249
|
+
list
|
|
250
|
+
List of file (names) that are in the package.
|
|
251
|
+
"""
|
|
87
252
|
files = []
|
|
88
|
-
logger.debug("Listing package "
|
|
89
|
-
|
|
90
|
-
data = self.gl_project_api(project,
|
|
253
|
+
logger.debug("Listing package %d files", package_id)
|
|
254
|
+
apath = "packages/" + parse.quote_plus(str(package_id)) + "/package_files"
|
|
255
|
+
data = self.gl_project_api(project, apath)
|
|
91
256
|
for package in data:
|
|
92
257
|
# Only append the filename once to the list of files
|
|
93
258
|
# as there's no way to download them separately through
|
|
@@ -100,28 +265,72 @@ class Packages:
|
|
|
100
265
|
def get_package_id(
|
|
101
266
|
self, project: str, package_name: str, package_version: str
|
|
102
267
|
) -> int:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
268
|
+
"""
|
|
269
|
+
Gets the package ID of a specific package version.
|
|
270
|
+
|
|
271
|
+
Parameters
|
|
272
|
+
----------
|
|
273
|
+
project : str
|
|
274
|
+
The project ID or the path of the project, including namespace.
|
|
275
|
+
Examples: `123` or `namespace/project`.
|
|
276
|
+
package_name : str
|
|
277
|
+
The name of the package.
|
|
278
|
+
package_version : str
|
|
279
|
+
The version of the package
|
|
280
|
+
|
|
281
|
+
Return
|
|
282
|
+
------
|
|
283
|
+
int
|
|
284
|
+
The ID of the package. Zero if no ID was found.
|
|
285
|
+
"""
|
|
286
|
+
package_id = 0
|
|
287
|
+
logger.debug("Fetching package %s (%s) ID", package_name, package_version)
|
|
288
|
+
apath = "packages"
|
|
108
289
|
arg = (
|
|
109
290
|
"package_name="
|
|
110
291
|
+ parse.quote_plus(package_name)
|
|
111
292
|
+ "&package_version="
|
|
112
293
|
+ parse.quote_plus(package_version)
|
|
113
294
|
)
|
|
114
|
-
data = self.gl_project_api(project,
|
|
295
|
+
data = self.gl_project_api(project, apath, arg)
|
|
115
296
|
if len(data) == 1:
|
|
116
297
|
package = data.pop()
|
|
117
|
-
|
|
118
|
-
return
|
|
298
|
+
package_id = package["id"]
|
|
299
|
+
return package_id
|
|
119
300
|
|
|
120
301
|
def download_file(
|
|
121
|
-
self,
|
|
302
|
+
self,
|
|
303
|
+
project: str,
|
|
304
|
+
package_name: str,
|
|
305
|
+
package_version: str,
|
|
306
|
+
filename: str,
|
|
307
|
+
destination: str = "",
|
|
122
308
|
) -> int:
|
|
309
|
+
"""
|
|
310
|
+
Downloads a file from a GitLab generic package
|
|
311
|
+
|
|
312
|
+
Parameters
|
|
313
|
+
----------
|
|
314
|
+
project : str
|
|
315
|
+
The project ID or the path of the project, including namespace.
|
|
316
|
+
Examples: `123` or `namespace/project`.
|
|
317
|
+
package_name : str
|
|
318
|
+
The name of the generic package.
|
|
319
|
+
package_version : str
|
|
320
|
+
The version of the generic package
|
|
321
|
+
filename : str
|
|
322
|
+
The file that is downloaded
|
|
323
|
+
destination : str, optional
|
|
324
|
+
The destination folder of the downloaded file. If not set,
|
|
325
|
+
current working directory is used.
|
|
326
|
+
|
|
327
|
+
Return
|
|
328
|
+
------
|
|
329
|
+
int
|
|
330
|
+
Zero if everything went fine, non-zero coke otherwise.
|
|
331
|
+
"""
|
|
123
332
|
ret = 1
|
|
124
|
-
logger.debug("Downloading file "
|
|
333
|
+
logger.debug("Downloading file %s", filename)
|
|
125
334
|
url = (
|
|
126
335
|
self.project_api_url(project)
|
|
127
336
|
+ "packages/generic/"
|
|
@@ -129,21 +338,107 @@ class Packages:
|
|
|
129
338
|
+ "/"
|
|
130
339
|
+ parse.quote_plus(package_version)
|
|
131
340
|
+ "/"
|
|
132
|
-
+ parse.quote(
|
|
341
|
+
+ parse.quote(filename)
|
|
133
342
|
)
|
|
134
343
|
status, data, _ = self._request(url)
|
|
135
344
|
if status == 200:
|
|
136
|
-
|
|
345
|
+
fpath = os.path.join(destination, filename)
|
|
346
|
+
parent = os.path.dirname(fpath)
|
|
347
|
+
if parent:
|
|
348
|
+
# Create missing directories if needed
|
|
349
|
+
# In case path has no parent, current
|
|
350
|
+
# workind directory is used
|
|
351
|
+
os.makedirs(os.path.dirname(fpath), exist_ok=True)
|
|
352
|
+
with open(fpath, "wb") as file:
|
|
137
353
|
file.write(data)
|
|
138
354
|
ret = 0
|
|
139
355
|
return ret
|
|
140
356
|
|
|
141
357
|
def upload_file(
|
|
142
|
-
self,
|
|
358
|
+
self,
|
|
359
|
+
project: str,
|
|
360
|
+
package_name: str,
|
|
361
|
+
package_version: str,
|
|
362
|
+
pfile: str,
|
|
363
|
+
source: str,
|
|
143
364
|
) -> int:
|
|
365
|
+
"""
|
|
366
|
+
Uploads file(s) to a GitLab generic package.
|
|
367
|
+
|
|
368
|
+
Parameters
|
|
369
|
+
----------
|
|
370
|
+
project : str
|
|
371
|
+
The project ID or the path of the project, including namespace.
|
|
372
|
+
Examples: `123` or `namespace/project`.
|
|
373
|
+
package_name : str
|
|
374
|
+
The name of the generic package.
|
|
375
|
+
package_version : str
|
|
376
|
+
The version of the generic package
|
|
377
|
+
file : str
|
|
378
|
+
The relative path of the file that is uploaded. If left empty,
|
|
379
|
+
all files from the source folder, and it's subfolders, are uploaded.
|
|
380
|
+
source : str
|
|
381
|
+
The source folder that is used as root when uploading. If empty,
|
|
382
|
+
current working directory is used.
|
|
383
|
+
|
|
384
|
+
Return
|
|
385
|
+
------
|
|
386
|
+
int
|
|
387
|
+
Zero if everything went fine, non-zero coke otherwise.
|
|
388
|
+
"""
|
|
389
|
+
files = []
|
|
144
390
|
ret = 1
|
|
145
|
-
|
|
146
|
-
|
|
391
|
+
if pfile:
|
|
392
|
+
files.append(pfile)
|
|
393
|
+
else:
|
|
394
|
+
filelist = glob(os.path.join(source, "**"), recursive=True)
|
|
395
|
+
for item in filelist:
|
|
396
|
+
# Only add files, not folders
|
|
397
|
+
if os.path.isfile(os.path.join(item)):
|
|
398
|
+
# Remove the source folder from the path of the files
|
|
399
|
+
files.append(os.path.relpath(item, source))
|
|
400
|
+
for ufile in files:
|
|
401
|
+
ret = self._upload_file(
|
|
402
|
+
project, package_name, package_version, ufile, source
|
|
403
|
+
)
|
|
404
|
+
if ret:
|
|
405
|
+
break
|
|
406
|
+
return ret
|
|
407
|
+
|
|
408
|
+
def _upload_file(
|
|
409
|
+
self,
|
|
410
|
+
project: str,
|
|
411
|
+
package_name: str,
|
|
412
|
+
package_version: str,
|
|
413
|
+
pfile: str,
|
|
414
|
+
source: str,
|
|
415
|
+
) -> int:
|
|
416
|
+
"""
|
|
417
|
+
Uploads a file to a GitLab generic package.
|
|
418
|
+
|
|
419
|
+
Parameters
|
|
420
|
+
----------
|
|
421
|
+
project : str
|
|
422
|
+
The project ID or the path of the project, including namespace.
|
|
423
|
+
Examples: `123` or `namespace/project`.
|
|
424
|
+
package_name : str
|
|
425
|
+
The name of the generic package.
|
|
426
|
+
package_version : str
|
|
427
|
+
The version of the generic package
|
|
428
|
+
file : str
|
|
429
|
+
The relative path of the file that is uploaded.
|
|
430
|
+
source : str
|
|
431
|
+
The source folder that is used as root when uploading.
|
|
432
|
+
|
|
433
|
+
Return
|
|
434
|
+
------
|
|
435
|
+
int
|
|
436
|
+
Zero if everything went fine, non-zero coke otherwise.
|
|
437
|
+
"""
|
|
438
|
+
ret = 1
|
|
439
|
+
fpath = os.path.join(source, pfile)
|
|
440
|
+
logger.debug("Uploading file %s from %s", pfile, source)
|
|
441
|
+
with open(fpath, "rb") as data:
|
|
147
442
|
url = (
|
|
148
443
|
self.project_api_url(project)
|
|
149
444
|
+ "packages/generic/"
|
|
@@ -151,13 +446,12 @@ class Packages:
|
|
|
151
446
|
+ "/"
|
|
152
447
|
+ parse.quote_plus(package_version)
|
|
153
448
|
+ "/"
|
|
154
|
-
+ parse.quote(
|
|
449
|
+
+ parse.quote(pfile)
|
|
155
450
|
)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
url, method="PUT", data=data, headers=self.get_headers()
|
|
159
|
-
)
|
|
451
|
+
req = request.Request(
|
|
452
|
+
url, method="PUT", data=data, headers=self.get_headers()
|
|
160
453
|
)
|
|
161
|
-
|
|
162
|
-
|
|
454
|
+
with request.urlopen(req) as res:
|
|
455
|
+
if res.status == 201: # 201 is created
|
|
456
|
+
ret = 0
|
|
163
457
|
return ret
|
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: glpkg
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Tool to make GitLab generic package registry operations easy.
|
|
5
5
|
Author-email: bugproduction <bugproduction@outlook.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Repository, https://gitlab.com/bugproduction/glpkg.git
|
|
8
8
|
Keywords: GitLab,packages,registry,generic
|
|
9
|
+
Requires-Python: >=3.9
|
|
9
10
|
Description-Content-Type: text/markdown
|
|
10
11
|
License-File: LICENSE.md
|
|
11
|
-
Provides-Extra: dev
|
|
12
|
-
Requires-Dist: black; extra == "dev"
|
|
13
|
-
Requires-Dist: build; extra == "dev"
|
|
14
|
-
Requires-Dist: pytest; extra == "dev"
|
|
15
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
16
|
-
Requires-Dist: setuptools; extra == "dev"
|
|
17
|
-
Requires-Dist: twine; extra == "dev"
|
|
18
12
|
Dynamic: license-file
|
|
19
13
|
|
|
20
|
-
# glpkg - GitLab Generic Package
|
|
14
|
+
# glpkg - GitLab Generic Package tools
|
|
21
15
|
|
|
22
|
-
glpkg is a tool that makes it easy to work with [GitLab generic
|
|
16
|
+
glpkg is a tool that makes it easy to work with [GitLab generic packages](https://docs.gitlab.com/user/packages/generic_packages/).
|
|
23
17
|
|
|
24
18
|
|
|
25
19
|
## Installation
|
|
@@ -47,7 +41,11 @@ By default, the used GitLab host is gitlab.com. If you use a self-hosted GitLab,
|
|
|
47
41
|
|
|
48
42
|
To authenticate with the package registry in any of the commands below, use `--token readapitoken123` argument where the `readapitoken123` is a [personal](https://docs.gitlab.com/user/profile/personal_access_tokens/#create-a-personal-access-token) or [project](https://docs.gitlab.com/user/project/settings/project_access_tokens/#create-a-project-access-token) access token, with read API scope. In case the package registry is public, you can omit this argument.
|
|
49
43
|
|
|
50
|
-
|
|
44
|
+
Alternatively you can use a token stored in your `.netrc` file by setting `--netrc` argument.
|
|
45
|
+
|
|
46
|
+
> If you use the tool in GitLab CI, read [below](#Use-in-GitLab-pipelines) on how to use the `CI_JOB_TOKEN`.
|
|
47
|
+
|
|
48
|
+
The arguments related to the GitLab host or authentication (`--token`, `--netrc`, and `--ci`) are omitted in the examples below to focus on the commands.
|
|
51
49
|
|
|
52
50
|
In general, run `glpkg --help` when needed.
|
|
53
51
|
|
|
@@ -74,7 +72,7 @@ mypackagename 2.0
|
|
|
74
72
|
|
|
75
73
|
### Download generic package
|
|
76
74
|
|
|
77
|
-
To download
|
|
75
|
+
To download all files from a specific version of a generic package, run
|
|
78
76
|
|
|
79
77
|
```bash
|
|
80
78
|
glpkg download --project 12345 --name mypackagename --version 1.0
|
|
@@ -85,7 +83,13 @@ Where:
|
|
|
85
83
|
- `mypackagename` is the name of the generic package
|
|
86
84
|
- `1.0` is the version of the generic package from which the files are downloaded
|
|
87
85
|
|
|
88
|
-
|
|
86
|
+
By default the files will be downloaded in the current working directory. To download the files to another directory, add argument `--destination` to the command. In all cases, as long as you have permissions to the destination directory, any pre-existing files will be overridden without warning.
|
|
87
|
+
|
|
88
|
+
To download only a specific file from the package, add `--file` argument.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
glpkg download --project 12345 --name mypackagename --version 1.5 --file the_only_one --destination /temp
|
|
92
|
+
```
|
|
89
93
|
|
|
90
94
|
> If a package has multiple files with the same filename, the tool can only download the newest file. This is a restriction of GitLab API.
|
|
91
95
|
|
|
@@ -101,13 +105,21 @@ Where:
|
|
|
101
105
|
- `12345` is your projects ID ([Find the Project ID](https://docs.gitlab.com/user/project/working_with_projects/#find-the-project-id)) or the path of the project (like `namespace/project`)
|
|
102
106
|
- `mypackagename` is the name of the generic package
|
|
103
107
|
- `1.0` is the version of the generic package to which the file is uploaded
|
|
104
|
-
- `my-file.txt` is the file that is uploaded to the generic package.
|
|
108
|
+
- `my-file.txt` is the file that is uploaded to the generic package. Only relative paths are supported, and the relative path (e.g. `folder/file.txt`) is preserved when uploading the file to the package.
|
|
105
109
|
|
|
106
110
|
> A GitLab generic package may have multiple files with the same file name. However, it likely is not a great idea, as they cannot be downloaded separately from the GitLab API.
|
|
107
111
|
|
|
112
|
+
To upload multiple files, or to upload a single file from a different directory, use `--source` argument. If no `--file` argument is set, all of the files in the source directory are uploaded, recursively. As an example, to upload all files from a `upload` folder to the package:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
glpkg upload --project 12345 --name mypackagename --version 1.0 --source upload
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Note: a file in `upload` folder will be uploaded to the root of the package. If you want to upload the file to a `dir` folder in the package, make a structure in the upload folder, like `upload/dir/`.
|
|
119
|
+
|
|
108
120
|
### Use in GitLab pipelines
|
|
109
121
|
|
|
110
|
-
If you use the tool in a GitLab pipeline,
|
|
122
|
+
If you use the tool in a GitLab pipeline, setting argument `--ci` uses [GitLab predefined variables](https://docs.gitlab.com/ci/variables/predefined_variables/) to configure the tool. In this case `CI_SERVER_HOST`, `CI_PROJECT_ID`, and `CI_JOB_TOKEN` environment variables are used. The `--project`, and `--token` arguments can still be used to override the project ID or to use a personal or project access token instead of `CI_JOB_TOKEN`.
|
|
111
123
|
|
|
112
124
|
In other words, you don't need to give the `--host`, `--project`, or `--token` arguments if you are interacting with the package registry of the project where the pipeline is running. Example: uploading `my-file.txt` to generic package `mypackagename` version `1.0` in the project package registry in CI:
|
|
113
125
|
|
|
@@ -122,5 +134,4 @@ To use the `CI_JOB_TOKEN` with package registry of another projects, add `--proj
|
|
|
122
134
|
|
|
123
135
|
The tool is not perfect (yet) and has limitations. The following limitations are known, but more can exist:
|
|
124
136
|
|
|
125
|
-
- Uploading files must be done one-by-one.
|
|
126
137
|
- Only project registries are supported for now.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
gitlab/__init__.py,sha256=ZXRwtUQEDE9bdewmwKYCjMAhm5KsuvAe525_QBiWLHY,105
|
|
2
|
+
gitlab/__main__.py,sha256=UPVixjP98KzJi9Ljth4yqqDTTaE3EfyAuj2xGCQAGys,586
|
|
3
|
+
gitlab/cli_handler.py,sha256=2P3R-yYkT6D5CjNCAEd9FC9HYI09aJ_eyMSMsOHtZzs,12289
|
|
4
|
+
gitlab/packages.py,sha256=50CbLdYxj0r6l8J40TxpIo2hTkKlv1BCFywFR6lg2nc,14785
|
|
5
|
+
glpkg-1.3.0.dist-info/licenses/LICENSE.md,sha256=josGXvZq628dNS0Iru58-DPE7dRpDXzjJxKKT35103g,1065
|
|
6
|
+
glpkg-1.3.0.dist-info/METADATA,sha256=4bQDKmHrf2da0eQ2hjfQKKItoirfcIr1VbWa3hwxbwY,6362
|
|
7
|
+
glpkg-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
+
glpkg-1.3.0.dist-info/entry_points.txt,sha256=xHPZwx2oShYDZ3AyH7WSIvuhFMssy7QLlQk-JAbje_w,46
|
|
9
|
+
glpkg-1.3.0.dist-info/top_level.txt,sha256=MvIaP8p_Oaf4gO_hXmHkX-5y2deHLp1pe6tJR3ukQ6o,7
|
|
10
|
+
glpkg-1.3.0.dist-info/RECORD,,
|
glpkg-1.1.0.dist-info/RECORD
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
gitlab/__init__.py,sha256=EsSFZxYgGAL2i0sL5zkoz71JP7XbsWdg_jv_uOqrrXE,60
|
|
2
|
-
gitlab/__main__.py,sha256=88VNY5Qrmn8g0rNcnjKNdN746--0chHsKBMH3PD3Nao,177
|
|
3
|
-
gitlab/cli_handler.py,sha256=6B22RXAtOzVL-mpm7U3OPX1_SSrepQk12cwTG0TI3eU,6102
|
|
4
|
-
gitlab/packages.py,sha256=l9tFEUsHMWF0vtv5ouVB4nvmWrJ4KDaS6f5e1lpjNyk,5683
|
|
5
|
-
glpkg-1.1.0.dist-info/licenses/LICENSE.md,sha256=josGXvZq628dNS0Iru58-DPE7dRpDXzjJxKKT35103g,1065
|
|
6
|
-
glpkg-1.1.0.dist-info/METADATA,sha256=65URv9IMuUo1XnmqKolpuJ12nkjcFKJg8rKMz4XeUIQ,5500
|
|
7
|
-
glpkg-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
-
glpkg-1.1.0.dist-info/entry_points.txt,sha256=xHPZwx2oShYDZ3AyH7WSIvuhFMssy7QLlQk-JAbje_w,46
|
|
9
|
-
glpkg-1.1.0.dist-info/top_level.txt,sha256=MvIaP8p_Oaf4gO_hXmHkX-5y2deHLp1pe6tJR3ukQ6o,7
|
|
10
|
-
glpkg-1.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|