portmgr 1.8.0.dev1__tar.gz → 1.8.0.dev3__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.
- portmgr-1.8.0.dev3/.gitignore +5 -0
- portmgr-1.8.0.dev3/.python-version +1 -0
- {portmgr-1.8.0.dev1/src/portmgr.egg-info → portmgr-1.8.0.dev3}/PKG-INFO +4 -5
- portmgr-1.8.0.dev3/dckrsub.schema.json +22 -0
- portmgr-1.8.0.dev3/json2yml/dckrcnf.schema.json +160 -0
- portmgr-1.8.0.dev3/json2yml/dckrjsn.py +38 -0
- portmgr-1.8.0.dev3/json2yml/json2yml.py +1 -0
- {portmgr-1.8.0.dev1 → portmgr-1.8.0.dev3}/pyproject.toml +7 -2
- portmgr-1.8.0.dev3/release.sh +8 -0
- portmgr-1.8.0.dev1/src/portmgr.egg-info/requires.txt → portmgr-1.8.0.dev3/requirements.txt +3 -3
- portmgr-1.8.0.dev3/src/portmgr/__init__.py +2 -0
- portmgr-1.8.0.dev3/src/portmgr/__main__.py +15 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/__init__.py +0 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/attach.py +51 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/build.py +25 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/down.py +27 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/logs.py +19 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/ps.py +19 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/pull.py +19 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/push.py +76 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/scan.py +43 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/stats.py +52 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/stop.py +26 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/top.py +18 -0
- portmgr-1.8.0.dev3/src/portmgr/commands/up.py +26 -0
- portmgr-1.8.0.dev3/src/portmgr/portmgr.py +170 -0
- portmgr-1.8.0.dev3/src/portmgr/wrapper.py +67 -0
- portmgr-1.8.0.dev3/uv.lock +150 -0
- portmgr-1.8.0.dev1/PKG-INFO +0 -82
- portmgr-1.8.0.dev1/setup.cfg +0 -4
- portmgr-1.8.0.dev1/src/portmgr/__init__.py +0 -2
- portmgr-1.8.0.dev1/src/portmgr.egg-info/SOURCES.txt +0 -10
- portmgr-1.8.0.dev1/src/portmgr.egg-info/dependency_links.txt +0 -1
- portmgr-1.8.0.dev1/src/portmgr.egg-info/entry_points.txt +0 -2
- portmgr-1.8.0.dev1/src/portmgr.egg-info/top_level.txt +0 -1
- {portmgr-1.8.0.dev1 → portmgr-1.8.0.dev3}/LICENSE +0 -0
- {portmgr-1.8.0.dev1 → portmgr-1.8.0.dev3}/README.md +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: portmgr
|
|
3
|
-
Version: 1.8.0.
|
|
3
|
+
Version: 1.8.0.dev3
|
|
4
4
|
Summary: Simple command interface to manage multiple Docker container
|
|
5
|
-
License-Expression: MIT
|
|
6
5
|
Project-URL: Website, https://github.com/Craeckie/portmgr
|
|
7
6
|
Project-URL: Repository, https://github.com/Craeckie/portmgr.git
|
|
8
|
-
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
7
|
+
License-Expression: MIT
|
|
10
8
|
License-File: LICENSE
|
|
9
|
+
Requires-Python: >=3.11
|
|
11
10
|
Requires-Dist: humanfriendly~=10.0
|
|
12
11
|
Requires-Dist: jsonschema~=3.2.0
|
|
13
12
|
Requires-Dist: pyyaml~=6.0.2
|
|
14
13
|
Requires-Dist: tabulate~=0.8.9
|
|
15
|
-
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
16
15
|
|
|
17
16
|
# portmgr
|
|
18
17
|
portmgr is a wrapper around [docker-compose](https://docs.docker.com/compose/) that allows running typical docker-compose commands recursively. Additionally, it shortens commands to a single letter.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"title": "dckrsub",
|
|
4
|
+
|
|
5
|
+
"description": "dckrmgr subfolders description",
|
|
6
|
+
|
|
7
|
+
"type": "array",
|
|
8
|
+
|
|
9
|
+
"items" : {
|
|
10
|
+
"type": "object",
|
|
11
|
+
|
|
12
|
+
"properties": {
|
|
13
|
+
"folder": {
|
|
14
|
+
"type": "string"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
"additionalProperties": false
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
"uniqueItems": true
|
|
22
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"title": "dckrcnf",
|
|
4
|
+
|
|
5
|
+
"description": "dckrmgr container description",
|
|
6
|
+
|
|
7
|
+
"type": "object",
|
|
8
|
+
|
|
9
|
+
"properties": {
|
|
10
|
+
"name": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"image": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
|
|
17
|
+
"properties": {
|
|
18
|
+
"name": {
|
|
19
|
+
"type": "string"
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
"version": {
|
|
23
|
+
"type": "string"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
|
|
29
|
+
"required": ["name", "version"]
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
"hostname": {
|
|
33
|
+
"type": "string"
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
"environment": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
|
|
39
|
+
"items": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
|
|
42
|
+
"properties": {
|
|
43
|
+
"name": {
|
|
44
|
+
"type": "string"
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
"value": {
|
|
48
|
+
"type": "string"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
"additionalProperties": false,
|
|
53
|
+
|
|
54
|
+
"required": ["name", "value"]
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
"minitems": 0
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
"volumes": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
|
|
63
|
+
"items": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
|
|
66
|
+
"properties": {
|
|
67
|
+
"host_path": {
|
|
68
|
+
"type": "string"
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
"container_path": {
|
|
72
|
+
"type": "string"
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
"mode": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
|
|
78
|
+
"pattern": "^((ro)|(rw))(?!(.|\n))"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
"additionalProperties": false,
|
|
83
|
+
|
|
84
|
+
"required": ["host_path", "container_path", "mode"]
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
"minitems": 0
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
"ports": {
|
|
91
|
+
"type": "array",
|
|
92
|
+
|
|
93
|
+
"items": {
|
|
94
|
+
"type": "object",
|
|
95
|
+
|
|
96
|
+
"properties": {
|
|
97
|
+
"container_port": {
|
|
98
|
+
"type": "integer",
|
|
99
|
+
|
|
100
|
+
"minimum": 0,
|
|
101
|
+
|
|
102
|
+
"maximum": 65535
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
"host_port": {
|
|
106
|
+
"type": "integer",
|
|
107
|
+
|
|
108
|
+
"minimum": 0,
|
|
109
|
+
|
|
110
|
+
"maximum": 65535
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
"address": {
|
|
114
|
+
"type": "string"
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
"protocol": {
|
|
118
|
+
"type": "string",
|
|
119
|
+
|
|
120
|
+
"pattern": "^((tcp)|(udp))(?!(.|\n))"
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
"additionalProperties": false,
|
|
125
|
+
|
|
126
|
+
"required": ["container_port", "host_port"]
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
"minitems": 0
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
"links": {
|
|
133
|
+
"type": "array",
|
|
134
|
+
|
|
135
|
+
"items": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
|
|
138
|
+
"properties": {
|
|
139
|
+
"name": {
|
|
140
|
+
"type": "string"
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
"alias": {
|
|
144
|
+
"type": "string"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
"additionalProperties": false,
|
|
149
|
+
|
|
150
|
+
"required": ["name", "alias"]
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
"minitems": 0
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
"additionalProperties": false,
|
|
158
|
+
|
|
159
|
+
"required": ["name", "image"]
|
|
160
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import jsonschema
|
|
4
|
+
|
|
5
|
+
def read_json(pth, sch=None):
|
|
6
|
+
bsn = os.path.basename(pth)
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
f_jsn = open(pth, 'r')
|
|
10
|
+
except FileNotFoundError:
|
|
11
|
+
print('Couldn\'t open ' + bsn + ': Not found')
|
|
12
|
+
exit(1)
|
|
13
|
+
except PermissionError:
|
|
14
|
+
print('Couldn\'t open ' + bsn + ': Insufficient rights')
|
|
15
|
+
exit(1)
|
|
16
|
+
except OSError:
|
|
17
|
+
print('Couldn\'t open ' + bsn)
|
|
18
|
+
exit(1)
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
jsn = json.load(f_jsn)
|
|
22
|
+
except ValueError:
|
|
23
|
+
print('Couldn\'t deserialize ' + bsn + ': Invalid json')
|
|
24
|
+
exit(1)
|
|
25
|
+
finally:
|
|
26
|
+
f_jsn.close()
|
|
27
|
+
|
|
28
|
+
if sch is not None:
|
|
29
|
+
try:
|
|
30
|
+
jsonschema.validate(jsn, sch)
|
|
31
|
+
except jsonschema.exceptions.SchemaError:
|
|
32
|
+
print('Couldn\'t validate ' + bsn + ': Invalid schema')
|
|
33
|
+
exit(1)
|
|
34
|
+
except jsonschema.exceptions.ValidationError as error:
|
|
35
|
+
print('Couldn\'t accept '+ bsn + ': ' + error.message)
|
|
36
|
+
exit(1)
|
|
37
|
+
|
|
38
|
+
return jsn
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/bin/usr/python3
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "portmgr"
|
|
3
|
-
version = "1.8.
|
|
3
|
+
version = "1.8.0dev3"
|
|
4
4
|
description = "Simple command interface to manage multiple Docker container"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
7
|
-
requires-python = ">=3.
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
8
|
dependencies = [
|
|
9
9
|
"humanfriendly~=10.0",
|
|
10
10
|
"jsonschema~=3.2.0",
|
|
11
11
|
"pyyaml~=6.0.2",
|
|
12
12
|
"tabulate~=0.8.9",
|
|
13
13
|
]
|
|
14
|
+
[tool.uv]
|
|
15
|
+
package = true
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["hatchling"]
|
|
18
|
+
build-backend = "hatchling.build"
|
|
14
19
|
[project.urls]
|
|
15
20
|
Website = "https://github.com/Craeckie/portmgr"
|
|
16
21
|
Repository = "https://github.com/Craeckie/portmgr.git"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import unicode_literals
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
if __package__ is None and not hasattr(sys, 'frozen'):
|
|
7
|
+
# direct call of __main__.py
|
|
8
|
+
import os.path
|
|
9
|
+
path = os.path.realpath(os.path.abspath(__file__))
|
|
10
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(path)))
|
|
11
|
+
|
|
12
|
+
import portmgr
|
|
13
|
+
|
|
14
|
+
if __name__ == '__main__':
|
|
15
|
+
portmgr.main()
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
from portmgr.wrapper import getServicesRunning
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def func(action):
|
|
8
|
+
directory = action['directory']
|
|
9
|
+
relative = action['relative']
|
|
10
|
+
|
|
11
|
+
names = getServicesRunning()
|
|
12
|
+
|
|
13
|
+
index = 0
|
|
14
|
+
cont_count = len(names)
|
|
15
|
+
if cont_count == 0:
|
|
16
|
+
print("No containers found!")
|
|
17
|
+
return 1
|
|
18
|
+
elif cont_count > 1:
|
|
19
|
+
i = 0
|
|
20
|
+
for cont in names:
|
|
21
|
+
print("(" + str(i) + ") " + cont)
|
|
22
|
+
i += 1
|
|
23
|
+
|
|
24
|
+
while True:
|
|
25
|
+
choice = input("Choose container: ")
|
|
26
|
+
if choice:
|
|
27
|
+
try:
|
|
28
|
+
index = int(choice)
|
|
29
|
+
if index >= 0 and index < cont_count:
|
|
30
|
+
break
|
|
31
|
+
except ValueError:
|
|
32
|
+
print("Please enter a number!")
|
|
33
|
+
pass
|
|
34
|
+
print("Please enter a number between 0 and " + cont_count - 1 + "!")
|
|
35
|
+
|
|
36
|
+
container_id = names[index]
|
|
37
|
+
print("Attaching to " + container_id)
|
|
38
|
+
subprocess.call(["docker", "exec", "-it", container_id, "sh"])
|
|
39
|
+
|
|
40
|
+
# res = subprocess.call(["docker-compose", "logs", "--follow", "--tail=200"])
|
|
41
|
+
|
|
42
|
+
# if res != 0:
|
|
43
|
+
# print("Error showing logs for " + relative + "!\n")
|
|
44
|
+
|
|
45
|
+
return 0
|
|
46
|
+
|
|
47
|
+
command_list['a'] = {
|
|
48
|
+
'hlp': 'Attach to process',
|
|
49
|
+
'ord': 'nrm',
|
|
50
|
+
'fnc': func
|
|
51
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def func(action):
|
|
5
|
+
directory = action['directory']
|
|
6
|
+
relative = action['relative']
|
|
7
|
+
|
|
8
|
+
res = runCompose(
|
|
9
|
+
['build', '--pull'],
|
|
10
|
+
env={
|
|
11
|
+
'COMPOSE_DOCKER_CLI_BUILD': '1',
|
|
12
|
+
'DOCKER_BUILDKIT': '1'
|
|
13
|
+
},
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if res != 0:
|
|
17
|
+
print("Error building " + relative + "!")
|
|
18
|
+
|
|
19
|
+
return res
|
|
20
|
+
|
|
21
|
+
command_list['b'] = {
|
|
22
|
+
'hlp': 'Build local image',
|
|
23
|
+
'ord': 'nrm',
|
|
24
|
+
'fnc': func
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def func(action):
|
|
5
|
+
directory = action['directory']
|
|
6
|
+
relative = action['relative']
|
|
7
|
+
|
|
8
|
+
res = runCompose(["down"])
|
|
9
|
+
# p = subprocess.Popen(["docker-compose", "down"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
10
|
+
|
|
11
|
+
# out, err = p.communicate()
|
|
12
|
+
|
|
13
|
+
# if out != "":
|
|
14
|
+
# print(out.decode("UTF-8"))
|
|
15
|
+
|
|
16
|
+
if not res == 0:
|
|
17
|
+
print("Error removing " + relative + "!")
|
|
18
|
+
|
|
19
|
+
# print(bcolors.FAIL + err.decode("UTF-8") + bcolors.ENDC)
|
|
20
|
+
|
|
21
|
+
return 0
|
|
22
|
+
|
|
23
|
+
command_list['d'] = {
|
|
24
|
+
'hlp': 'Stop and remove container',
|
|
25
|
+
'ord': 'rev',
|
|
26
|
+
'fnc': func
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def func(action):
|
|
5
|
+
directory = action['directory']
|
|
6
|
+
relative = action['relative']
|
|
7
|
+
|
|
8
|
+
res = runCompose(["logs", "--follow", "--tail=200"])
|
|
9
|
+
|
|
10
|
+
if res != 0:
|
|
11
|
+
print("Error showing logs for " + relative + "!\n")
|
|
12
|
+
|
|
13
|
+
return 0
|
|
14
|
+
|
|
15
|
+
command_list['l'] = {
|
|
16
|
+
'hlp': 'Show logs',
|
|
17
|
+
'ord': 'nrm',
|
|
18
|
+
'fnc': func
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def func(action):
|
|
5
|
+
directory = action['directory']
|
|
6
|
+
relative = action['relative']
|
|
7
|
+
|
|
8
|
+
res = runCompose(["ps"])
|
|
9
|
+
|
|
10
|
+
if res != 0:
|
|
11
|
+
print("Error listing containers for " + relative + "!\n")
|
|
12
|
+
|
|
13
|
+
return 0
|
|
14
|
+
|
|
15
|
+
command_list['c'] = {
|
|
16
|
+
'hlp': 'List containers',
|
|
17
|
+
'ord': 'nrm',
|
|
18
|
+
'fnc': func
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def func(action):
|
|
5
|
+
directory = action['directory']
|
|
6
|
+
relative = action['relative']
|
|
7
|
+
|
|
8
|
+
res = runCompose(["pull"])
|
|
9
|
+
|
|
10
|
+
if res != 0:
|
|
11
|
+
print("Error pulling " + relative + "!")
|
|
12
|
+
|
|
13
|
+
return 0
|
|
14
|
+
|
|
15
|
+
command_list['p'] = {
|
|
16
|
+
'hlp': 'Pull image from repository',
|
|
17
|
+
'ord': 'nrm',
|
|
18
|
+
'fnc': func
|
|
19
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose, runBuildx
|
|
2
|
+
import subprocess
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from portmgr.wrapper import getServices
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def func(action):
|
|
9
|
+
directory = action['directory']
|
|
10
|
+
relative = action['relative']
|
|
11
|
+
|
|
12
|
+
services = getServices(includeOnlyBuildable=True)
|
|
13
|
+
|
|
14
|
+
print('Services to build: ' + ', '.join(services))
|
|
15
|
+
|
|
16
|
+
res = 0
|
|
17
|
+
for service in services.keys():
|
|
18
|
+
print(f"\nBuilding {service}")
|
|
19
|
+
|
|
20
|
+
new_res = 2
|
|
21
|
+
if multi_platform := os.environ.get("PORTMGR_MULTI_PLATFORM", "").lower():
|
|
22
|
+
try:
|
|
23
|
+
new_res = runBuildx(
|
|
24
|
+
['bake',
|
|
25
|
+
'--pull',
|
|
26
|
+
'--push',
|
|
27
|
+
'--set', f'*.platform={multi_platform}',
|
|
28
|
+
service
|
|
29
|
+
], timeout=1200 #kill after 20mins
|
|
30
|
+
)
|
|
31
|
+
if new_res != 0:
|
|
32
|
+
res = new_res
|
|
33
|
+
print(f"Error building {service}!")
|
|
34
|
+
except TimeoutExpired:
|
|
35
|
+
print(f"Error building {service}! Build timed out.")
|
|
36
|
+
else:
|
|
37
|
+
try:
|
|
38
|
+
new_res = runCompose(
|
|
39
|
+
['build',
|
|
40
|
+
'--pull',
|
|
41
|
+
'--force-rm',
|
|
42
|
+
'--compress',
|
|
43
|
+
service
|
|
44
|
+
], timeout=1200
|
|
45
|
+
)
|
|
46
|
+
if new_res != 0:
|
|
47
|
+
res = new_res
|
|
48
|
+
print(f"Error building {service}!")
|
|
49
|
+
else:
|
|
50
|
+
new_res = runCompose(
|
|
51
|
+
['push',
|
|
52
|
+
'--ignore-push-failures',
|
|
53
|
+
service
|
|
54
|
+
]
|
|
55
|
+
)
|
|
56
|
+
print(f"Error pushing {service}!")
|
|
57
|
+
except TimeoutExpired:
|
|
58
|
+
print(f"Error building {service}! Build timed out.")
|
|
59
|
+
if new_res != 1:
|
|
60
|
+
res = new_res
|
|
61
|
+
if os.environ.get("PORTMGR_CLEAN_AFTER_PUSH", "").lower() == "true":
|
|
62
|
+
subprocess.call(['docker', 'system', 'prune', '--all', '--force'])
|
|
63
|
+
subprocess.call(['docker', 'buildx', 'prune', '--all', '--force'])
|
|
64
|
+
|
|
65
|
+
if res != 0:
|
|
66
|
+
print("Error building&pushing " + relative + "!")
|
|
67
|
+
return res
|
|
68
|
+
|
|
69
|
+
return res
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
command_list['r'] = {
|
|
73
|
+
'hlp': 'build, push to registry & remove image',
|
|
74
|
+
'ord': 'nrm',
|
|
75
|
+
'fnc': func
|
|
76
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from portmgr import command_list
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
from portmgr.wrapper import getImages
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def func(action):
|
|
8
|
+
directory = action['directory']
|
|
9
|
+
relative = action['relative']
|
|
10
|
+
|
|
11
|
+
images = getImages()
|
|
12
|
+
|
|
13
|
+
res = 0
|
|
14
|
+
|
|
15
|
+
for image in images:
|
|
16
|
+
image_name = image["Name"]
|
|
17
|
+
print(f'Scanning {image_name} of {image["ContainerName"]}')
|
|
18
|
+
scan_res = subprocess.run(['trivy', '-q', 'image', '-s', 'CRITICAL', '--exit-code', '1', image_name], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
|
|
19
|
+
if scan_res.returncode != 0:
|
|
20
|
+
print('Found vulnerabilites:')
|
|
21
|
+
print(scan_res.stdout)
|
|
22
|
+
res = scan_res.returncode
|
|
23
|
+
|
|
24
|
+
#print("Name: %s" % container.name)
|
|
25
|
+
#names.append(container.name)
|
|
26
|
+
#config_str = subprocess.check_output(["docker", "inspect", "-f", '{{json .}}', container.id], stdout=subprocess.PIPE)
|
|
27
|
+
#json.loads(config_str)
|
|
28
|
+
#print("IP: %s" % ip)
|
|
29
|
+
#print(container.inspect)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
#ID = subprocess.run(["docker-compose", "ps", '-q'], stdout=subprocess.PIPE).stdout
|
|
33
|
+
|
|
34
|
+
#if res != 0:
|
|
35
|
+
# print("Error listing containers for " + relative + "!\n")
|
|
36
|
+
|
|
37
|
+
return res
|
|
38
|
+
|
|
39
|
+
command_list['v'] = {
|
|
40
|
+
'hlp': 'Scan container images for vulnerabilities',
|
|
41
|
+
'ord': 'nrm',
|
|
42
|
+
'fnc': func
|
|
43
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from functools import cmp_to_key
|
|
2
|
+
|
|
3
|
+
from portmgr import command_list
|
|
4
|
+
|
|
5
|
+
from tabulate import tabulate
|
|
6
|
+
from humanfriendly import format_size, parse_size
|
|
7
|
+
|
|
8
|
+
from portmgr.wrapper import getStats
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def func(action):
|
|
12
|
+
directory = action['directory']
|
|
13
|
+
relative = action['relative']
|
|
14
|
+
|
|
15
|
+
stats_list = getStats()
|
|
16
|
+
|
|
17
|
+
values = []
|
|
18
|
+
has_network_stats = False
|
|
19
|
+
for stats in stats_list:
|
|
20
|
+
name = stats['Name'] if 'Name' in stats else stats['Container']
|
|
21
|
+
#memory = stats["memory_stats"]
|
|
22
|
+
#usage = format_size(memory['usage'])
|
|
23
|
+
#limit = format_size(memory['limit'])
|
|
24
|
+
memory_string = stats["MemUsage"]
|
|
25
|
+
usage, limit = [p.strip() for p in memory_string.split('/')]
|
|
26
|
+
if 'NetIO' in stats:
|
|
27
|
+
network = stats["NetIO"]
|
|
28
|
+
received, sent = [p.strip() for p in memory_string.split('/')]
|
|
29
|
+
#received = format_size(sum(stats['rx_bytes'] for iface, stats in network.items()))
|
|
30
|
+
#sent = format_size(sum(stats['tx_bytes'] for iface, stats in network.items()))
|
|
31
|
+
columns = (stats['Name'], usage, limit, received, sent)
|
|
32
|
+
has_network_stats = True
|
|
33
|
+
else:
|
|
34
|
+
columns = (stats['Name'], usage, limit)
|
|
35
|
+
values.append(columns)
|
|
36
|
+
if values:
|
|
37
|
+
# sort by memory usage
|
|
38
|
+
values = sorted(values, key=cmp_to_key(lambda s1, s2: parse_size(s2[1]) - parse_size(s1[1])))
|
|
39
|
+
print(tabulate(values,
|
|
40
|
+
headers=['Service', 'Mem Usage', 'Mem Limit', 'Net Recv', 'Net Sent'],
|
|
41
|
+
colalign=['left', 'right', 'right', 'right', 'right'] if has_network_stats
|
|
42
|
+
else ['left', 'right', 'right']))
|
|
43
|
+
print('')
|
|
44
|
+
|
|
45
|
+
return 0
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
command_list['o'] = {
|
|
49
|
+
'hlp': 'Show container stats',
|
|
50
|
+
'ord': 'nrm',
|
|
51
|
+
'fnc': func
|
|
52
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
def func(action):
|
|
4
|
+
directory = action['directory']
|
|
5
|
+
relative = action['relative']
|
|
6
|
+
|
|
7
|
+
res = runCompose(["stop"])
|
|
8
|
+
# p = subprocess.Popen(["docker-compose", "stop"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
9
|
+
|
|
10
|
+
#out, err = p.communicate()
|
|
11
|
+
|
|
12
|
+
#if out != "":
|
|
13
|
+
# print(out.decode("UTF-8"))
|
|
14
|
+
|
|
15
|
+
if res != 0:
|
|
16
|
+
print("Error stopping " + relative + "!")
|
|
17
|
+
|
|
18
|
+
# print(bcolors.FAIL + err.decode("UTF-8") + bcolors.ENDC)
|
|
19
|
+
|
|
20
|
+
return 0
|
|
21
|
+
|
|
22
|
+
command_list['s'] = {
|
|
23
|
+
'hlp': 'Stop container',
|
|
24
|
+
'ord': 'rev',
|
|
25
|
+
'fnc': func
|
|
26
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from portmgr import command_list, runCompose
|
|
2
|
+
|
|
3
|
+
def func(action):
|
|
4
|
+
relative = action['relative']
|
|
5
|
+
|
|
6
|
+
res = runCompose(["top"])
|
|
7
|
+
|
|
8
|
+
if res != 0:
|
|
9
|
+
print("Error listing processes of containers in " + relative + "!\n")
|
|
10
|
+
|
|
11
|
+
return 0
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
command_list['t'] = {
|
|
15
|
+
'hlp': 'List processes in containers',
|
|
16
|
+
'ord': 'nrm',
|
|
17
|
+
'fnc': func
|
|
18
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
def func(action):
|
|
4
|
+
directory = action['directory']
|
|
5
|
+
relative = action['relative']
|
|
6
|
+
|
|
7
|
+
res = runCompose(["up", "-d"])
|
|
8
|
+
# p = subprocess.Popen(["docker-compose", "up", "-d"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
9
|
+
|
|
10
|
+
# out, err = p.communicate()
|
|
11
|
+
|
|
12
|
+
# if out != "":
|
|
13
|
+
# print(out.decode("UTF-8"))
|
|
14
|
+
|
|
15
|
+
if res != 0:
|
|
16
|
+
print("Error creating " + relative + "!")
|
|
17
|
+
|
|
18
|
+
# print(bcolors.FAIL + err.decode("UTF-8") + bcolors.ENDC)
|
|
19
|
+
|
|
20
|
+
return 0
|
|
21
|
+
|
|
22
|
+
command_list['u'] = {
|
|
23
|
+
'hlp': 'Create container',
|
|
24
|
+
'ord': 'nrm',
|
|
25
|
+
'fnc': func
|
|
26
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
import os, sys
|
|
3
|
+
import argparse
|
|
4
|
+
import importlib
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
import yaml
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MyParser(argparse.ArgumentParser):
|
|
11
|
+
def error(self, message):
|
|
12
|
+
sys.stderr.write('Error: %s\n' % message)
|
|
13
|
+
self.print_help()
|
|
14
|
+
sys.exit(2)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
sub_names = [x.strip() for x in re.split('[ ,;]+', os.environ.get('PORTMGR_SUB_NAME', 'dckrsub.yml'))]
|
|
18
|
+
compose_names = [x.strip() for x in
|
|
19
|
+
os.environ.get('PORTMGR_COMPOSE_NAME', 'docker-compose.yml, docker-compose.yaml').split(',')]
|
|
20
|
+
|
|
21
|
+
# sub_scheme_name = 'dckrsub.schema.yml'
|
|
22
|
+
|
|
23
|
+
src_path = os.path.dirname(os.path.abspath(__file__))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# conf_scheme_path = os.path.join(src_path, sub_scheme_name)
|
|
27
|
+
# sub_scheme_path = os.path.join(src_path, sub_scheme_name)
|
|
28
|
+
|
|
29
|
+
# conf_scheme = dckrjsn.read_json(conf_scheme_path)
|
|
30
|
+
# sub_scheme = dckrjsn.read_json(sub_scheme_path)
|
|
31
|
+
|
|
32
|
+
class bcolors:
|
|
33
|
+
HEADER = '\033[95m'
|
|
34
|
+
OKBLUE = '\033[94m'
|
|
35
|
+
OKGREEN = '\033[92m'
|
|
36
|
+
WARNING = '\033[93m'
|
|
37
|
+
FAIL = '\033[91m'
|
|
38
|
+
ENDC = '\033[0m'
|
|
39
|
+
BOLD = '\033[1m'
|
|
40
|
+
UNDERLINE = '\033[4m'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
command_list = {}
|
|
44
|
+
action_list = []
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def addCommand(cur_directory):
|
|
48
|
+
if not any(action['directory'] == cur_directory for action in action_list):
|
|
49
|
+
relative_dir = os.path.relpath(cur_directory, base_directory)
|
|
50
|
+
if relative_dir == '.':
|
|
51
|
+
relative_dir = os.path.basename(os.path.normpath(cur_directory))
|
|
52
|
+
action_list.append({
|
|
53
|
+
'directory': cur_directory,
|
|
54
|
+
'relative': relative_dir
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def read_yaml(path):
|
|
59
|
+
with open(path, 'r') as stream:
|
|
60
|
+
try:
|
|
61
|
+
return yaml.load(stream, Loader=yaml.SafeLoader)
|
|
62
|
+
except yaml.YAMLError as exc:
|
|
63
|
+
print(exc)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def traverse(cur_directory):
|
|
67
|
+
# print("Traversing in " + cur_directory)
|
|
68
|
+
for sub_name in sub_names:
|
|
69
|
+
sub_path = os.path.join(cur_directory, sub_name)
|
|
70
|
+
compose_paths = [os.path.join(cur_directory, name) for name in compose_names]
|
|
71
|
+
|
|
72
|
+
# print("Checking file at " + sub_path)
|
|
73
|
+
if os.path.isfile(sub_path): # has sub folders
|
|
74
|
+
# print("Has sub folders!")
|
|
75
|
+
# sub_folders = dckrjsn.read_json(sub_path, sch = sub_scheme)
|
|
76
|
+
sub_folders = read_yaml(sub_path)
|
|
77
|
+
for sub_folder in sub_folders:
|
|
78
|
+
# print("Checking out " + sub_folder)
|
|
79
|
+
next_directory = os.path.join(cur_directory, sub_folder)
|
|
80
|
+
traverse(next_directory)
|
|
81
|
+
elif any(os.path.isfile(path) for path in compose_paths): # has a docker-compose file
|
|
82
|
+
addCommand(cur_directory)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def main():
|
|
86
|
+
# global cli
|
|
87
|
+
global base_directory
|
|
88
|
+
|
|
89
|
+
# cli = docker.Client('unix://var/run/docker.sock')
|
|
90
|
+
|
|
91
|
+
# Include external source files for commands
|
|
92
|
+
# These fill the m_cmd list
|
|
93
|
+
for file in os.listdir(os.path.join(src_path, 'commands')):
|
|
94
|
+
ext_file = os.path.splitext(file)
|
|
95
|
+
|
|
96
|
+
if ext_file[1] == '.py' and not ext_file[0] == '__init__':
|
|
97
|
+
importlib.import_module('portmgr.commands.' + ext_file[0])
|
|
98
|
+
|
|
99
|
+
parser = MyParser()
|
|
100
|
+
parser.add_argument('-D',
|
|
101
|
+
dest='base_directory',
|
|
102
|
+
action='store',
|
|
103
|
+
default='',
|
|
104
|
+
help='Set working directory')
|
|
105
|
+
# parser.add_argument('-R',
|
|
106
|
+
# dest='recursive',
|
|
107
|
+
# action='store_true',
|
|
108
|
+
# help='Use dckrsub.json files to recursively apply operations')
|
|
109
|
+
|
|
110
|
+
for cmd in command_list.items():
|
|
111
|
+
parser.add_argument('-' + cmd[0],
|
|
112
|
+
dest='a_cmd',
|
|
113
|
+
action='append_const',
|
|
114
|
+
const=cmd[0],
|
|
115
|
+
help=cmd[1]['hlp'])
|
|
116
|
+
|
|
117
|
+
argv = sys.argv[1:]
|
|
118
|
+
if len(argv) == 1 and not argv[0].startswith('-'):
|
|
119
|
+
argv[0] = '-' + argv[0]
|
|
120
|
+
args = parser.parse_args(argv)
|
|
121
|
+
|
|
122
|
+
if len(sys.argv) == 1:
|
|
123
|
+
parser.print_help()
|
|
124
|
+
sys.exit(1)
|
|
125
|
+
|
|
126
|
+
base_directory = os.path.join(os.getcwd(), args.base_directory)
|
|
127
|
+
|
|
128
|
+
# if args.recursive:
|
|
129
|
+
traverse(base_directory)
|
|
130
|
+
# else:
|
|
131
|
+
# addCommand(base_directory)
|
|
132
|
+
|
|
133
|
+
last_cmd = ''
|
|
134
|
+
for cmd in args.a_cmd: # loop over all passed arguments (t, r, u)
|
|
135
|
+
cur_cmd = command_list[cmd]
|
|
136
|
+
cmd_function = cur_cmd['fnc']
|
|
137
|
+
cmd_order = cur_cmd['ord'];
|
|
138
|
+
|
|
139
|
+
# if last_cmd == 'r' and cur_cmd == 'u':
|
|
140
|
+
# print("Waiting 3 seconds.. ")
|
|
141
|
+
# sleep(3)
|
|
142
|
+
|
|
143
|
+
if cmd_order == 'nrm':
|
|
144
|
+
action_list_sorted = action_list
|
|
145
|
+
elif cmd_order == 'rev':
|
|
146
|
+
action_list_sorted = reversed(action_list)
|
|
147
|
+
else:
|
|
148
|
+
exit(1)
|
|
149
|
+
|
|
150
|
+
failed_list = []
|
|
151
|
+
|
|
152
|
+
for action in action_list_sorted:
|
|
153
|
+
origWD = os.getcwd()
|
|
154
|
+
newWD = action['directory']
|
|
155
|
+
os.chdir(newWD)
|
|
156
|
+
print('-> ' + action["relative"])
|
|
157
|
+
if cmd_function(action) != 0: # execute the function through reflection
|
|
158
|
+
failed_list.append(action)
|
|
159
|
+
os.chdir(origWD)
|
|
160
|
+
if failed_list:
|
|
161
|
+
print('Failed containers:')
|
|
162
|
+
for action in failed_list:
|
|
163
|
+
print('- ' + action['relative'])
|
|
164
|
+
print("")
|
|
165
|
+
|
|
166
|
+
exit(0)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
if __name__ == '__main__':
|
|
170
|
+
main()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from subprocess import run, check_output
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def getServices(includeOnlyBuildable=False):
|
|
7
|
+
data = check_output(['docker', 'compose', 'config', '--format', 'json'])
|
|
8
|
+
config = json.loads(data)
|
|
9
|
+
services = config['services']
|
|
10
|
+
if includeOnlyBuildable:
|
|
11
|
+
services = {name: values for name, values in services.items() if 'build' in values.keys()}
|
|
12
|
+
return services
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def getServicesRunning():
|
|
16
|
+
data = check_output(['docker', 'compose', 'ps', '--format', 'json'])
|
|
17
|
+
try:
|
|
18
|
+
lines = data.decode().splitlines()
|
|
19
|
+
if len(lines) > 1:
|
|
20
|
+
container_list = []
|
|
21
|
+
for l in lines:
|
|
22
|
+
#print(f'l: {l}')
|
|
23
|
+
container_list.append(json.loads(l.strip()))
|
|
24
|
+
else:
|
|
25
|
+
container_list = json.loads(data)
|
|
26
|
+
except Exception as e:
|
|
27
|
+
print(e)
|
|
28
|
+
print(data.decode())
|
|
29
|
+
if isinstance(container_list, dict):
|
|
30
|
+
container_names = [container_list['Name']]
|
|
31
|
+
else:
|
|
32
|
+
container_names = [s['Name'] for s in container_list]
|
|
33
|
+
return container_names
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def getImages():
|
|
37
|
+
data = check_output(['docker', 'compose', 'images', '--format', 'json'])
|
|
38
|
+
image_list = json.loads(data)
|
|
39
|
+
images = [
|
|
40
|
+
{'ID': image['ID'],
|
|
41
|
+
'Name': image['Repository'],
|
|
42
|
+
'ContainerName': image['ContainerName'],
|
|
43
|
+
'Tag': image['Tag']}
|
|
44
|
+
for image in image_list
|
|
45
|
+
]
|
|
46
|
+
return images
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def getStats():
|
|
50
|
+
containers = getServicesRunning()
|
|
51
|
+
data = check_output(['docker', 'stats', '--format', 'json', '--no-stream'] + containers, text=True).strip()
|
|
52
|
+
data_lines = data.split('\n')
|
|
53
|
+
stats = [json.loads(line) for line in data_lines]
|
|
54
|
+
return stats
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def runCompose(args, **kwargs):
|
|
58
|
+
command = ['docker', 'compose']
|
|
59
|
+
if os.environ.get("PORTMGR_IN_SCRIPT", "").lower() == "true":
|
|
60
|
+
command += ["--ansi", "never"]
|
|
61
|
+
command += list(args)
|
|
62
|
+
return run(command, **kwargs).returncode
|
|
63
|
+
|
|
64
|
+
def runBuildx(args, **kwargs):
|
|
65
|
+
command = ['docker', 'buildx']
|
|
66
|
+
command += list(args)
|
|
67
|
+
return run(command, **kwargs).returncode
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
revision = 2
|
|
3
|
+
requires-python = ">=3.11"
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "attrs"
|
|
7
|
+
version = "25.3.0"
|
|
8
|
+
source = { registry = "https://pypi.org/simple" }
|
|
9
|
+
sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" }
|
|
10
|
+
wheels = [
|
|
11
|
+
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "humanfriendly"
|
|
16
|
+
version = "10.0"
|
|
17
|
+
source = { registry = "https://pypi.org/simple" }
|
|
18
|
+
dependencies = [
|
|
19
|
+
{ name = "pyreadline3", marker = "sys_platform == 'win32'" },
|
|
20
|
+
]
|
|
21
|
+
sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" }
|
|
22
|
+
wheels = [
|
|
23
|
+
{ url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" },
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[[package]]
|
|
27
|
+
name = "jsonschema"
|
|
28
|
+
version = "3.2.0"
|
|
29
|
+
source = { registry = "https://pypi.org/simple" }
|
|
30
|
+
dependencies = [
|
|
31
|
+
{ name = "attrs" },
|
|
32
|
+
{ name = "pyrsistent" },
|
|
33
|
+
{ name = "setuptools" },
|
|
34
|
+
{ name = "six" },
|
|
35
|
+
]
|
|
36
|
+
sdist = { url = "https://files.pythonhosted.org/packages/69/11/a69e2a3c01b324a77d3a7c0570faa372e8448b666300c4117a516f8b1212/jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a", size = 167226, upload-time = "2019-11-18T12:57:10.704Z" }
|
|
37
|
+
wheels = [
|
|
38
|
+
{ url = "https://files.pythonhosted.org/packages/c5/8f/51e89ce52a085483359217bc72cdbf6e75ee595d5b1d4b5ade40c7e018b8/jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163", size = 56305, upload-time = "2019-11-18T12:57:08.454Z" },
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[[package]]
|
|
42
|
+
name = "portmgr"
|
|
43
|
+
version = "1.8.0.dev2"
|
|
44
|
+
source = { editable = "." }
|
|
45
|
+
dependencies = [
|
|
46
|
+
{ name = "humanfriendly" },
|
|
47
|
+
{ name = "jsonschema" },
|
|
48
|
+
{ name = "pyyaml" },
|
|
49
|
+
{ name = "tabulate" },
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[package.metadata]
|
|
53
|
+
requires-dist = [
|
|
54
|
+
{ name = "humanfriendly", specifier = "~=10.0" },
|
|
55
|
+
{ name = "jsonschema", specifier = "~=3.2.0" },
|
|
56
|
+
{ name = "pyyaml", specifier = "~=6.0.2" },
|
|
57
|
+
{ name = "tabulate", specifier = "~=0.8.9" },
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[[package]]
|
|
61
|
+
name = "pyreadline3"
|
|
62
|
+
version = "3.5.4"
|
|
63
|
+
source = { registry = "https://pypi.org/simple" }
|
|
64
|
+
sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" }
|
|
65
|
+
wheels = [
|
|
66
|
+
{ url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" },
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
[[package]]
|
|
70
|
+
name = "pyrsistent"
|
|
71
|
+
version = "0.20.0"
|
|
72
|
+
source = { registry = "https://pypi.org/simple" }
|
|
73
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ce/3a/5031723c09068e9c8c2f0bc25c3a9245f2b1d1aea8396c787a408f2b95ca/pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4", size = 103642, upload-time = "2023-10-25T21:06:56.342Z" }
|
|
74
|
+
wheels = [
|
|
75
|
+
{ url = "https://files.pythonhosted.org/packages/df/63/7544dc7d0953294882a5c587fb1b10a26e0c23d9b92281a14c2514bac1f7/pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958", size = 83481, upload-time = "2023-10-25T21:06:15.238Z" },
|
|
76
|
+
{ url = "https://files.pythonhosted.org/packages/ae/a0/49249bc14d71b1bf2ffe89703acfa86f2017c25cfdabcaea532b8c8a5810/pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8", size = 120222, upload-time = "2023-10-25T21:06:17.144Z" },
|
|
77
|
+
{ url = "https://files.pythonhosted.org/packages/a1/94/9808e8c9271424120289b9028a657da336ad7e43da0647f62e4f6011d19b/pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a", size = 120002, upload-time = "2023-10-25T21:06:18.727Z" },
|
|
78
|
+
{ url = "https://files.pythonhosted.org/packages/3f/f6/9ecfb78b2fc8e2540546db0fe19df1fae0f56664a5958c21ff8861b0f8da/pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224", size = 116850, upload-time = "2023-10-25T21:06:20.424Z" },
|
|
79
|
+
{ url = "https://files.pythonhosted.org/packages/83/c8/e6d28bc27a0719f8eaae660357df9757d6e9ca9be2691595721de9e8adfc/pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656", size = 60775, upload-time = "2023-10-25T21:06:21.815Z" },
|
|
80
|
+
{ url = "https://files.pythonhosted.org/packages/98/87/c6ef52ff30388f357922d08de012abdd3dc61e09311d88967bdae23ab657/pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee", size = 63306, upload-time = "2023-10-25T21:06:22.874Z" },
|
|
81
|
+
{ url = "https://files.pythonhosted.org/packages/15/ee/ff2ed52032ac1ce2e7ba19e79bd5b05d152ebfb77956cf08fcd6e8d760ea/pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e", size = 83537, upload-time = "2023-10-25T21:06:24.17Z" },
|
|
82
|
+
{ url = "https://files.pythonhosted.org/packages/80/f1/338d0050b24c3132bcfc79b68c3a5f54bce3d213ecef74d37e988b971d8a/pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e", size = 122615, upload-time = "2023-10-25T21:06:25.815Z" },
|
|
83
|
+
{ url = "https://files.pythonhosted.org/packages/07/3a/e56d6431b713518094fae6ff833a04a6f49ad0fbe25fb7c0dc7408e19d20/pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3", size = 122335, upload-time = "2023-10-25T21:06:28.631Z" },
|
|
84
|
+
{ url = "https://files.pythonhosted.org/packages/4a/bb/5f40a4d5e985a43b43f607250e766cdec28904682c3505eb0bd343a4b7db/pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d", size = 118510, upload-time = "2023-10-25T21:06:30.718Z" },
|
|
85
|
+
{ url = "https://files.pythonhosted.org/packages/1c/13/e6a22f40f5800af116c02c28e29f15c06aa41cb2036f6a64ab124647f28b/pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174", size = 60865, upload-time = "2023-10-25T21:06:32.742Z" },
|
|
86
|
+
{ url = "https://files.pythonhosted.org/packages/75/ef/2fa3b55023ec07c22682c957808f9a41836da4cd006b5f55ec76bf0fbfa6/pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d", size = 63239, upload-time = "2023-10-25T21:06:34.035Z" },
|
|
87
|
+
{ url = "https://files.pythonhosted.org/packages/23/88/0acd180010aaed4987c85700b7cc17f9505f3edb4e5873e4dc67f613e338/pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b", size = 58106, upload-time = "2023-10-25T21:06:54.387Z" },
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
[[package]]
|
|
91
|
+
name = "pyyaml"
|
|
92
|
+
version = "6.0.2"
|
|
93
|
+
source = { registry = "https://pypi.org/simple" }
|
|
94
|
+
sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" }
|
|
95
|
+
wheels = [
|
|
96
|
+
{ url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" },
|
|
97
|
+
{ url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" },
|
|
98
|
+
{ url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" },
|
|
99
|
+
{ url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" },
|
|
100
|
+
{ url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" },
|
|
101
|
+
{ url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" },
|
|
102
|
+
{ url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" },
|
|
103
|
+
{ url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" },
|
|
104
|
+
{ url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" },
|
|
105
|
+
{ url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" },
|
|
106
|
+
{ url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" },
|
|
107
|
+
{ url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" },
|
|
108
|
+
{ url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" },
|
|
109
|
+
{ url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" },
|
|
110
|
+
{ url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" },
|
|
111
|
+
{ url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" },
|
|
112
|
+
{ url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" },
|
|
113
|
+
{ url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" },
|
|
114
|
+
{ url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" },
|
|
115
|
+
{ url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" },
|
|
116
|
+
{ url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" },
|
|
117
|
+
{ url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" },
|
|
118
|
+
{ url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" },
|
|
119
|
+
{ url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" },
|
|
120
|
+
{ url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" },
|
|
121
|
+
{ url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" },
|
|
122
|
+
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" },
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
[[package]]
|
|
126
|
+
name = "setuptools"
|
|
127
|
+
version = "80.9.0"
|
|
128
|
+
source = { registry = "https://pypi.org/simple" }
|
|
129
|
+
sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
|
|
130
|
+
wheels = [
|
|
131
|
+
{ url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
[[package]]
|
|
135
|
+
name = "six"
|
|
136
|
+
version = "1.17.0"
|
|
137
|
+
source = { registry = "https://pypi.org/simple" }
|
|
138
|
+
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
|
|
139
|
+
wheels = [
|
|
140
|
+
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
[[package]]
|
|
144
|
+
name = "tabulate"
|
|
145
|
+
version = "0.8.10"
|
|
146
|
+
source = { registry = "https://pypi.org/simple" }
|
|
147
|
+
sdist = { url = "https://files.pythonhosted.org/packages/7a/53/afac341569b3fd558bf2b5428e925e2eb8753ad9627c1f9188104c6e0c4a/tabulate-0.8.10.tar.gz", hash = "sha256:6c57f3f3dd7ac2782770155f3adb2db0b1a269637e42f27599925e64b114f519", size = 60154, upload-time = "2022-06-21T16:26:42.76Z" }
|
|
148
|
+
wheels = [
|
|
149
|
+
{ url = "https://files.pythonhosted.org/packages/92/4e/e5a13fdb3e6f81ce11893523ff289870c87c8f1f289a7369fb0e9840c3bb/tabulate-0.8.10-py3-none-any.whl", hash = "sha256:0ba055423dbaa164b9e456abe7920c5e8ed33fcc16f6d1b2f2d152c8e1e8b4fc", size = 29068, upload-time = "2022-06-21T16:26:37.943Z" },
|
|
150
|
+
]
|
portmgr-1.8.0.dev1/PKG-INFO
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: portmgr
|
|
3
|
-
Version: 1.8.0.dev1
|
|
4
|
-
Summary: Simple command interface to manage multiple Docker container
|
|
5
|
-
License-Expression: MIT
|
|
6
|
-
Project-URL: Website, https://github.com/Craeckie/portmgr
|
|
7
|
-
Project-URL: Repository, https://github.com/Craeckie/portmgr.git
|
|
8
|
-
Requires-Python: >=3.12
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Requires-Dist: humanfriendly~=10.0
|
|
12
|
-
Requires-Dist: jsonschema~=3.2.0
|
|
13
|
-
Requires-Dist: pyyaml~=6.0.2
|
|
14
|
-
Requires-Dist: tabulate~=0.8.9
|
|
15
|
-
Dynamic: license-file
|
|
16
|
-
|
|
17
|
-
# portmgr
|
|
18
|
-
portmgr is a wrapper around [docker-compose](https://docs.docker.com/compose/) that allows running typical docker-compose commands recursively. Additionally, it shortens commands to a single letter.
|
|
19
|
-
|
|
20
|
-
Let's say you have organized your compose files like this, you just add a `dckrsub.yml` in each parent folder:
|
|
21
|
-
<pre>
|
|
22
|
-
docker/
|
|
23
|
-
├── <b>dckrsub.yml</b>
|
|
24
|
-
├── reverse-proxy/
|
|
25
|
-
│ └── docker-compose.yml
|
|
26
|
-
├── storage
|
|
27
|
-
│ ├── <b>dckrsub.yml</b>
|
|
28
|
-
│ ├── nextcloud/
|
|
29
|
-
│ │ └── docker-compose.yml
|
|
30
|
-
│ └── immich/
|
|
31
|
-
│ └── docker-compose.yml
|
|
32
|
-
└── scripts
|
|
33
|
-
</pre>
|
|
34
|
-
|
|
35
|
-
Each `dckrsub.yml` has a list of subdirectories, which portmgr should decend into.
|
|
36
|
-
For example, the `dckrsub.yml` in `docker/` might look like this:
|
|
37
|
-
```yaml
|
|
38
|
-
- reverse-proxy
|
|
39
|
-
- storage
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
And the `dckrsub.yml` in `docker/storage/` like this:
|
|
43
|
-
```yaml
|
|
44
|
-
- nextcloud
|
|
45
|
-
- immich
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Now, if you run `portmgr u` in `docker/` it will run `docker compose up -d` in `reverse-proxy/`, `storage/nextcloud/` and `storage/immich/`.
|
|
49
|
-
|
|
50
|
-
portmgr starts from the current directory, so when running it in `docker/storage/`, it will run `docker compose` only in `nextcloud/` and `immich/`. You can also use it in a directory with a `docker-compose.yml` as a shortener for docker-compose commands.
|
|
51
|
-
|
|
52
|
-
### Commands
|
|
53
|
-
The following commands are available. The respective docker-compose commands are in brackets.
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
u Create and start containers (up)
|
|
57
|
-
p Pull images (pull)
|
|
58
|
-
s Stop services (stop)
|
|
59
|
-
d Stop and remove containers (down)
|
|
60
|
-
l Show container logs (logs)
|
|
61
|
-
a Run shell in container (exec -it <service> sh)
|
|
62
|
-
b Build images (build)
|
|
63
|
-
c List containers (ps)
|
|
64
|
-
t List processes in containers (top)
|
|
65
|
-
r Build and push to registry (build, push)
|
|
66
|
-
v Scan container images for vulnerabilities
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
You combine multiple commands. For example `portmgr dul`, runs docker compose with `down`, `up` and `logs`, thus stopping, removing and starting all containers and then showing the logs.
|
|
70
|
-
|
|
71
|
-
### Installation
|
|
72
|
-
```
|
|
73
|
-
sudo pip install portmgr
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Or build it from source (here using the latest commit on master branch)
|
|
77
|
-
```
|
|
78
|
-
sudo pip install https://github.com/Craeckie/portmgr.git
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Tipps
|
|
82
|
-
If you use portmgr a lot like me, you might want to shorten it to one letter. For bash, you can add `alias p='portmgr'` to `~/.bashrc`. For fish-shell you can add `abbr p portmgr` to `~/.config/fish/config.fish`.
|
portmgr-1.8.0.dev1/setup.cfg
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
pyproject.toml
|
|
4
|
-
src/portmgr/__init__.py
|
|
5
|
-
src/portmgr.egg-info/PKG-INFO
|
|
6
|
-
src/portmgr.egg-info/SOURCES.txt
|
|
7
|
-
src/portmgr.egg-info/dependency_links.txt
|
|
8
|
-
src/portmgr.egg-info/entry_points.txt
|
|
9
|
-
src/portmgr.egg-info/requires.txt
|
|
10
|
-
src/portmgr.egg-info/top_level.txt
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
portmgr
|
|
File without changes
|
|
File without changes
|