poetry-plugin-ivcap 0.2.0__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {poetry_plugin_ivcap-0.2.0 → poetry_plugin_ivcap-0.2.3}/PKG-INFO +7 -5
- {poetry_plugin_ivcap-0.2.0 → poetry_plugin_ivcap-0.2.3}/README.md +5 -3
- {poetry_plugin_ivcap-0.2.0 → poetry_plugin_ivcap-0.2.3}/poetry_plugin_ivcap/docker.py +73 -26
- {poetry_plugin_ivcap-0.2.0 → poetry_plugin_ivcap-0.2.3}/poetry_plugin_ivcap/ivcap.py +3 -5
- {poetry_plugin_ivcap-0.2.0 → poetry_plugin_ivcap-0.2.3}/poetry_plugin_ivcap/plugin.py +29 -12
- {poetry_plugin_ivcap-0.2.0 → poetry_plugin_ivcap-0.2.3}/poetry_plugin_ivcap/util.py +21 -0
- {poetry_plugin_ivcap-0.2.0 → poetry_plugin_ivcap-0.2.3}/pyproject.toml +2 -2
- {poetry_plugin_ivcap-0.2.0 → poetry_plugin_ivcap-0.2.3}/AUTHORS.md +0 -0
- {poetry_plugin_ivcap-0.2.0 → poetry_plugin_ivcap-0.2.3}/LICENSE +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: poetry-plugin-ivcap
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: A custom Poetry command for IVCAP deployments
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Max Ott
|
|
7
7
|
Author-email: max.ott@csiro.au
|
|
8
|
-
Requires-Python: >=3.9
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.9
|
|
@@ -27,9 +27,11 @@ Add to your `pyproject.toml`:
|
|
|
27
27
|
|
|
28
28
|
```toml
|
|
29
29
|
[tool.poetry-plugin-ivcap]
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
service-file = "service.py"
|
|
31
|
+
service-id = "urn:ivcap:service:ac158a1f-dfb4-5dac-bf2e-9bf15e0f2cc7"
|
|
32
|
+
port = 8077
|
|
33
|
+
# docker
|
|
34
|
+
# docker-run-opts = { port = 8079 }```
|
|
33
35
|
|
|
34
36
|
## Installation
|
|
35
37
|
|
|
@@ -8,9 +8,11 @@ Add to your `pyproject.toml`:
|
|
|
8
8
|
|
|
9
9
|
```toml
|
|
10
10
|
[tool.poetry-plugin-ivcap]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
service-file = "service.py"
|
|
12
|
+
service-id = "urn:ivcap:service:ac158a1f-dfb4-5dac-bf2e-9bf15e0f2cc7"
|
|
13
|
+
port = 8077
|
|
14
|
+
# docker
|
|
15
|
+
# docker-run-opts = { port = 8079 }```
|
|
14
16
|
|
|
15
17
|
## Installation
|
|
16
18
|
|
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
# Use of this source code is governed by a BSD-style license that can be
|
|
4
4
|
# found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
5
5
|
#
|
|
6
|
+
from datetime import datetime, timezone
|
|
6
7
|
import os
|
|
7
8
|
import re
|
|
8
9
|
import sys
|
|
9
10
|
import tempfile
|
|
10
|
-
from typing import Dict, Optional
|
|
11
|
+
from typing import Dict, List, Optional
|
|
11
12
|
from pydantic import BaseModel, Field
|
|
12
13
|
import subprocess
|
|
13
14
|
|
|
14
|
-
from .util import command_exists, get_name
|
|
15
|
+
from .util import command_exists, get_name, get_version
|
|
15
16
|
|
|
16
17
|
DOCKER_BUILD_TEMPLATE = """
|
|
17
18
|
docker buildx build
|
|
@@ -23,12 +24,20 @@ docker buildx build
|
|
|
23
24
|
--load #PROJECT_DIR#
|
|
24
25
|
"""
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
DOCKER_LAMBDA_RUN_TEMPLATE = """
|
|
27
28
|
docker run -it
|
|
28
29
|
-p #PORT#:#PORT#
|
|
29
30
|
--platform=linux/#ARCH#
|
|
30
31
|
--rm \
|
|
31
|
-
#NAME#_#ARCH#:#TAG#
|
|
32
|
+
#NAME#_#ARCH#:#TAG#
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
DOCKER_BATCH_RUN_TEMPLATE = """
|
|
36
|
+
docker run -it
|
|
37
|
+
--platform=linux/#ARCH#
|
|
38
|
+
-v #PROJECT_DIR#:/data
|
|
39
|
+
--rm \
|
|
40
|
+
#NAME#_#ARCH#:#TAG#
|
|
32
41
|
"""
|
|
33
42
|
|
|
34
43
|
class DockerConfig(BaseModel):
|
|
@@ -37,7 +46,7 @@ class DockerConfig(BaseModel):
|
|
|
37
46
|
arch: Optional[str] = Field(None)
|
|
38
47
|
version: Optional[str] = Field(None)
|
|
39
48
|
dockerfile: Optional[str] = Field("Dockerfile")
|
|
40
|
-
project_dir: Optional[str] = Field(
|
|
49
|
+
project_dir: Optional[str] = Field(os.getcwd())
|
|
41
50
|
|
|
42
51
|
@property
|
|
43
52
|
def docker_name(self) -> str:
|
|
@@ -57,26 +66,61 @@ class DockerConfig(BaseModel):
|
|
|
57
66
|
.split()
|
|
58
67
|
return t
|
|
59
68
|
|
|
60
|
-
def from_run_template(self, data) ->
|
|
69
|
+
def from_run_template(self, data, args, line) -> List[any]:
|
|
61
70
|
pdata = data.get("tool", {}).get("poetry-plugin-ivcap", {})
|
|
62
|
-
template = pdata.get("docker-run-template"
|
|
63
|
-
|
|
71
|
+
template = pdata.get("docker-run-template")
|
|
72
|
+
if template is None:
|
|
73
|
+
smode = pdata.get("service-type")
|
|
74
|
+
if smode is None:
|
|
75
|
+
line("<error>ERROR: 'service-type' is not defined in [poetry-plugin-ivcap]</error>")
|
|
76
|
+
sys.exit(1)
|
|
77
|
+
if smode == "lambda":
|
|
78
|
+
template = DOCKER_LAMBDA_RUN_TEMPLATE
|
|
79
|
+
elif smode == "batch":
|
|
80
|
+
template = DOCKER_BATCH_RUN_TEMPLATE
|
|
81
|
+
else:
|
|
82
|
+
line(f"<error>ERROR: Unknown service type '{smode}' in [poetry-plugin-ivcap]</error>")
|
|
83
|
+
sys.exit(1)
|
|
84
|
+
|
|
85
|
+
opts = pdata.get("docker-run-opts", [])
|
|
86
|
+
port = get_port_value(args)
|
|
87
|
+
port_in_args = True
|
|
88
|
+
if not port:
|
|
89
|
+
port = get_port_value(opts)
|
|
90
|
+
if not port:
|
|
91
|
+
port_in_args = False
|
|
92
|
+
port = str(pdata.get("port", 8000))
|
|
93
|
+
|
|
94
|
+
# elif opts.get('port') is not None:
|
|
95
|
+
# opts['port'] = opts.get('port')
|
|
96
|
+
# elif '--port' in template:
|
|
97
|
+
# # If the template has a port but no port is provided, use a default
|
|
98
|
+
# opts['port'] = '8000'
|
|
99
|
+
|
|
64
100
|
t = template.strip()\
|
|
65
101
|
.replace("#NAME#", self.name)\
|
|
66
102
|
.replace("#TAG#", self.tag)\
|
|
103
|
+
.replace("#PORT#", port)\
|
|
67
104
|
.replace("#ARCH#", self.arch)\
|
|
68
105
|
.replace("#VERSION#", self.version)\
|
|
69
106
|
.replace("#PROJECT_DIR#", self.project_dir)
|
|
70
107
|
|
|
71
|
-
for key, value in opts.items():
|
|
72
|
-
|
|
108
|
+
# for key, value in opts.items():
|
|
109
|
+
# if key != "port":
|
|
110
|
+
# t = t.replace(f"#{key.upper()}#", str(value))
|
|
73
111
|
|
|
74
|
-
|
|
112
|
+
|
|
113
|
+
cmd = t.split()
|
|
114
|
+
cmd.extend(opts)
|
|
115
|
+
cmd.extend(args)
|
|
116
|
+
if smode == "lambda" and not port_in_args:
|
|
117
|
+
cmd.extend(["--port", port])
|
|
118
|
+
return cmd
|
|
75
119
|
|
|
76
120
|
|
|
77
121
|
def docker_build(data: dict, line, arch = None) -> None:
|
|
78
122
|
check_docker_cmd(line)
|
|
79
|
-
config =
|
|
123
|
+
config = docker_cfg(data, line, arch)
|
|
80
124
|
build_cmd = config.from_build_template(data)
|
|
81
125
|
line(f"<info>INFO: {' '.join(build_cmd)}</info>")
|
|
82
126
|
process = subprocess.Popen(build_cmd, stdout=sys.stdout, stderr=sys.stderr)
|
|
@@ -87,10 +131,10 @@ def docker_build(data: dict, line, arch = None) -> None:
|
|
|
87
131
|
line("<info>INFO: Docker build completed successfully</info>")
|
|
88
132
|
return config.docker_name
|
|
89
133
|
|
|
90
|
-
def docker_run(data: dict, line) -> None:
|
|
134
|
+
def docker_run(data: dict, args, line) -> None:
|
|
91
135
|
check_docker_cmd(line)
|
|
92
|
-
config =
|
|
93
|
-
build_run = config.from_run_template(data)
|
|
136
|
+
config = docker_cfg(data, line)
|
|
137
|
+
build_run = config.from_run_template(data, args, line)
|
|
94
138
|
line(f"<info>INFO: {' '.join(build_run)}</info>")
|
|
95
139
|
process = subprocess.Popen(build_run, stdout=sys.stdout, stderr=sys.stderr)
|
|
96
140
|
exit_code = process.wait()
|
|
@@ -99,24 +143,15 @@ def docker_run(data: dict, line) -> None:
|
|
|
99
143
|
else:
|
|
100
144
|
line("<info>INFO: Docker run completed successfully</info>")
|
|
101
145
|
|
|
102
|
-
def
|
|
103
|
-
project_data = data.get("project", {})
|
|
146
|
+
def docker_cfg(data: dict, line, arch = None) -> DockerConfig:
|
|
104
147
|
name = get_name(data)
|
|
105
|
-
version = project_data.get("version", None)
|
|
106
148
|
|
|
107
149
|
pdata = data.get("tool", {}).get("poetry-plugin-ivcap", {})
|
|
108
|
-
config = DockerConfig(name=name,
|
|
150
|
+
config = DockerConfig(name=name, **pdata.get("docker", {}))
|
|
109
151
|
if arch:
|
|
110
152
|
# override architecture if provided
|
|
111
153
|
config.arch = arch
|
|
112
154
|
|
|
113
|
-
if not config.version:
|
|
114
|
-
try:
|
|
115
|
-
config.version = subprocess.check_output(['git', 'describe', '--tags', '--abbrev=0']).decode().strip()
|
|
116
|
-
except Exception as e:
|
|
117
|
-
line(f"<error>Error retrieving latest tag: {e}</error>")
|
|
118
|
-
config.version = "???"
|
|
119
|
-
|
|
120
155
|
if not config.tag:
|
|
121
156
|
try:
|
|
122
157
|
config.tag = subprocess.check_output(['git', 'rev-parse', "--short", 'HEAD']).decode().strip()
|
|
@@ -124,6 +159,9 @@ def _docker_cfg(data: dict, line, arch = None) -> DockerConfig:
|
|
|
124
159
|
line(f"<warning>WARN: retrieving commit hash: {e}</warning>")
|
|
125
160
|
config.tag = "latest"
|
|
126
161
|
|
|
162
|
+
if not config.version:
|
|
163
|
+
config.version = get_version(data, config.tag, line)
|
|
164
|
+
|
|
127
165
|
if not config.arch:
|
|
128
166
|
try:
|
|
129
167
|
config.arch = subprocess.check_output(['uname', '-m']).decode().strip()
|
|
@@ -183,3 +221,12 @@ def check_docker_cmd(line):
|
|
|
183
221
|
if not command_exists("docker"):
|
|
184
222
|
line("<error>'docker' command not found. Please install it first.</error>")
|
|
185
223
|
sys.exit(1)
|
|
224
|
+
|
|
225
|
+
def get_port_value(arr):
|
|
226
|
+
try:
|
|
227
|
+
index = arr.index('--port')
|
|
228
|
+
return arr[index + 1]
|
|
229
|
+
except ValueError:
|
|
230
|
+
return None
|
|
231
|
+
except IndexError:
|
|
232
|
+
return None
|
|
@@ -11,14 +11,12 @@ import tempfile
|
|
|
11
11
|
import uuid
|
|
12
12
|
import humanize
|
|
13
13
|
|
|
14
|
-
from .docker import
|
|
14
|
+
from .docker import docker_cfg, docker_build, docker_push
|
|
15
15
|
from .util import command_exists, execute_subprocess_and_capture_output, get_name, string_to_number
|
|
16
16
|
|
|
17
17
|
def docker_publish(data, line):
|
|
18
18
|
check_ivcap_cmd(line)
|
|
19
|
-
|
|
20
|
-
dname = "gene_onology_term_mapper_amd64:9a9a7cc"
|
|
21
|
-
print(dname)
|
|
19
|
+
dname = docker_build(data, line, arch="amd64")
|
|
22
20
|
|
|
23
21
|
size_cmd = ["docker", "inspect", "--format='{{.Size}}'", dname]
|
|
24
22
|
line(f"<debug>Running: {' '.join(size_cmd)} </debug>")
|
|
@@ -38,7 +36,7 @@ def service_register(data, line):
|
|
|
38
36
|
line("<error>Missing 'service-file' in [tool.poetry-plugin-ivcap]</error>")
|
|
39
37
|
return
|
|
40
38
|
|
|
41
|
-
dcfg =
|
|
39
|
+
dcfg = docker_cfg(data, line, "amd64")
|
|
42
40
|
pkg_cmd = ["ivcap", "package", "list", dcfg.docker_name]
|
|
43
41
|
line(f"<debug>Running: {' '.join(pkg_cmd)} </debug>")
|
|
44
42
|
pkg = subprocess.check_output(pkg_cmd).decode()
|
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
# Use of this source code is governed by a BSD-style license that can be
|
|
4
4
|
# found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
5
5
|
#
|
|
6
|
+
import os
|
|
6
7
|
from poetry.plugins.application_plugin import ApplicationPlugin
|
|
7
8
|
from cleo.commands.command import Command
|
|
8
9
|
from cleo.helpers import argument
|
|
9
10
|
import subprocess
|
|
10
11
|
|
|
12
|
+
from poetry_plugin_ivcap.util import get_version
|
|
13
|
+
|
|
11
14
|
from .ivcap import create_service_id, service_register, tool_register
|
|
12
15
|
from .docker import docker_build, docker_run
|
|
13
16
|
from .ivcap import docker_publish
|
|
@@ -31,9 +34,9 @@ Available subcommands:
|
|
|
31
34
|
tool-register Register the service as an AI Tool with IVCAP
|
|
32
35
|
|
|
33
36
|
Example:
|
|
34
|
-
poetry ivcap run
|
|
37
|
+
poetry ivcap run -- --port 8080
|
|
35
38
|
|
|
36
|
-
Configurable
|
|
39
|
+
Configurable options in pyproject.toml:
|
|
37
40
|
|
|
38
41
|
[tool.poetry-plugin-ivcap]
|
|
39
42
|
service-file = "service.py" # The Python file that implements the service
|
|
@@ -44,20 +47,26 @@ Configurable optiosn in pyproject.toml:
|
|
|
44
47
|
docker-run-template = "docker run -rm -p #PORT#:#PORT#"
|
|
45
48
|
"""
|
|
46
49
|
arguments = [
|
|
47
|
-
argument("subcommand", optional=True, description="Subcommand: run, deploy, etc.")
|
|
50
|
+
argument("subcommand", optional=True, description="Subcommand: run, deploy, etc."),
|
|
51
|
+
argument("args", optional=True, multiple=True, description="Additional arguments for subcommand"),
|
|
48
52
|
]
|
|
49
53
|
|
|
54
|
+
allow_extra_args = True
|
|
55
|
+
ignore_validation_errors = True
|
|
56
|
+
|
|
50
57
|
def handle(self):
|
|
51
58
|
poetry = self.application.poetry
|
|
52
59
|
data = poetry.pyproject.data
|
|
53
60
|
|
|
54
61
|
sub = self.argument("subcommand")
|
|
62
|
+
args = self.argument("args")
|
|
63
|
+
|
|
55
64
|
if sub == "run":
|
|
56
|
-
self.run_service(data)
|
|
65
|
+
self.run_service(data, args, self.line)
|
|
57
66
|
elif sub == "docker-build":
|
|
58
67
|
docker_build(data, self.line)
|
|
59
68
|
elif sub == "docker-run":
|
|
60
|
-
docker_run(data, self.line)
|
|
69
|
+
docker_run(data, args, self.line)
|
|
61
70
|
elif sub == "docker-publish":
|
|
62
71
|
docker_publish(data, self.line)
|
|
63
72
|
elif sub == "service-register":
|
|
@@ -72,18 +81,26 @@ Configurable optiosn in pyproject.toml:
|
|
|
72
81
|
self.line(f"<error>Unknown subcommand: {sub}</error>")
|
|
73
82
|
print(self.help)
|
|
74
83
|
|
|
75
|
-
def run_service(self, data):
|
|
84
|
+
def run_service(self, data, args, line):
|
|
76
85
|
config = data.get("tool", {}).get("poetry-plugin-ivcap", {})
|
|
77
86
|
|
|
78
87
|
service = config.get("service-file")
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if not service or not port:
|
|
82
|
-
self.line("<error>Missing 'service-file' or 'port' in [tool.poetry-plugin-ivcap]</error>")
|
|
88
|
+
if not service:
|
|
89
|
+
self.line("<error>Missing 'service-file' in [tool.poetry-plugin-ivcap]</error>")
|
|
83
90
|
return
|
|
84
91
|
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
if not '--port' in args:
|
|
93
|
+
port = config.get("port")
|
|
94
|
+
if port:
|
|
95
|
+
args.extend(["--port", str(port)])
|
|
96
|
+
|
|
97
|
+
env = os.environ.copy()
|
|
98
|
+
env["VERSION"] = get_version(data, None, line)
|
|
99
|
+
|
|
100
|
+
cmd = ["poetry", "run", "python", service]
|
|
101
|
+
cmd.extend(args)
|
|
102
|
+
self.line(f"<info>Running: {' '.join(cmd)}</info>")
|
|
103
|
+
subprocess.run(cmd, env=env)
|
|
87
104
|
|
|
88
105
|
class IvcapPlugin(ApplicationPlugin):
|
|
89
106
|
def activate(self, application):
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#
|
|
6
6
|
import subprocess
|
|
7
7
|
import platform
|
|
8
|
+
from datetime import datetime, timezone
|
|
8
9
|
|
|
9
10
|
def get_name(data) -> str:
|
|
10
11
|
project_data = data.get("project", {})
|
|
@@ -85,3 +86,23 @@ def execute_subprocess_and_capture_output(command):
|
|
|
85
86
|
full_output += line
|
|
86
87
|
|
|
87
88
|
return full_output
|
|
89
|
+
|
|
90
|
+
def get_version(data, tag, line):
|
|
91
|
+
if not tag:
|
|
92
|
+
try:
|
|
93
|
+
tag = subprocess.check_output(['git', 'rev-parse', "--short", 'HEAD']).decode().strip()
|
|
94
|
+
except Exception as e:
|
|
95
|
+
line(f"<warning>WARN: retrieving commit hash: {e}</warning>")
|
|
96
|
+
tag = "latest"
|
|
97
|
+
|
|
98
|
+
project_data = data.get("project", {})
|
|
99
|
+
v = project_data.get("version", None)
|
|
100
|
+
if not v:
|
|
101
|
+
try:
|
|
102
|
+
v = subprocess.check_output(['git', 'describe', '--tags', '--abbrev=0']).decode().strip()
|
|
103
|
+
except Exception as e:
|
|
104
|
+
line(f"<error>Error retrieving latest tag: {e}</error>")
|
|
105
|
+
v = "???"
|
|
106
|
+
now = datetime.now(timezone.utc).astimezone()
|
|
107
|
+
version = f"{v}|{tag}|{now.replace(microsecond=0).isoformat()}"
|
|
108
|
+
return version
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "poetry-plugin-ivcap"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.3"
|
|
4
4
|
description = "A custom Poetry command for IVCAP deployments"
|
|
5
5
|
authors = ["Max Ott <max.ott@csiro.au>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -8,7 +8,7 @@ readme = "README.md"
|
|
|
8
8
|
packages = [{ include = "poetry_plugin_ivcap" }]
|
|
9
9
|
|
|
10
10
|
[tool.poetry.dependencies]
|
|
11
|
-
python = "
|
|
11
|
+
python = ">=3.9"
|
|
12
12
|
pydantic = "^2.11.5"
|
|
13
13
|
humanize = "^4.12.3"
|
|
14
14
|
|
|
File without changes
|
|
File without changes
|