poetry-plugin-ivcap 0.1.0__tar.gz → 0.2.2__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.1.0 → poetry_plugin_ivcap-0.2.2}/PKG-INFO +44 -6
- poetry_plugin_ivcap-0.2.2/README.md +103 -0
- {poetry_plugin_ivcap-0.1.0 → poetry_plugin_ivcap-0.2.2}/poetry_plugin_ivcap/docker.py +73 -26
- {poetry_plugin_ivcap-0.1.0 → poetry_plugin_ivcap-0.2.2}/poetry_plugin_ivcap/ivcap.py +3 -5
- poetry_plugin_ivcap-0.2.2/poetry_plugin_ivcap/plugin.py +107 -0
- {poetry_plugin_ivcap-0.1.0 → poetry_plugin_ivcap-0.2.2}/poetry_plugin_ivcap/util.py +21 -0
- {poetry_plugin_ivcap-0.1.0 → poetry_plugin_ivcap-0.2.2}/pyproject.toml +1 -1
- poetry_plugin_ivcap-0.1.0/README.md +0 -65
- poetry_plugin_ivcap-0.1.0/poetry_plugin_ivcap/plugin.py +0 -70
- {poetry_plugin_ivcap-0.1.0 → poetry_plugin_ivcap-0.2.2}/AUTHORS.md +0 -0
- {poetry_plugin_ivcap-0.1.0 → poetry_plugin_ivcap-0.2.2}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: poetry-plugin-ivcap
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: A custom Poetry command for IVCAP deployments
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Max Ott
|
|
@@ -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
|
|
|
@@ -40,8 +42,44 @@ poetry self add poetry-plugin-ivcap
|
|
|
40
42
|
## Usage
|
|
41
43
|
|
|
42
44
|
```bash
|
|
43
|
-
poetry ivcap
|
|
44
|
-
poetry ivcap docker
|
|
45
|
+
poetry ivcap run
|
|
46
|
+
poetry ivcap docker-build
|
|
47
|
+
poetry ivcap docker-run
|
|
48
|
+
poetry ivcap docker-publish
|
|
49
|
+
poetry ivcap service-register
|
|
50
|
+
poetry ivcap create-service-id
|
|
51
|
+
poetry ivcap tool-register
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
To get help on the currently installed version
|
|
55
|
+
```
|
|
56
|
+
% poetry ivcap
|
|
57
|
+
|
|
58
|
+
IVCAP plugin
|
|
59
|
+
|
|
60
|
+
Supporting the development of services and tools for the IVCAP platform
|
|
61
|
+
|
|
62
|
+
Available subcommands:
|
|
63
|
+
run Run the service locally
|
|
64
|
+
docker-build Build the docker image for this service
|
|
65
|
+
docker-run Run the service's docker image locally
|
|
66
|
+
docker-publish Publish the service's docker image to IVCAP
|
|
67
|
+
service-register Register the service with IVCAP
|
|
68
|
+
create-service-id Create a unique service ID for the service
|
|
69
|
+
tool-register Register the service as an AI Tool with IVCAP
|
|
70
|
+
|
|
71
|
+
Example:
|
|
72
|
+
poetry ivcap run
|
|
73
|
+
|
|
74
|
+
Configurable optiosn in pyproject.toml:
|
|
75
|
+
|
|
76
|
+
[tool.poetry-plugin-ivcap]
|
|
77
|
+
service-file = "service.py" # The Python file that implements the service
|
|
78
|
+
service-file = "service.py"
|
|
79
|
+
service-id = "urn:ivcap:service:ac158a1f-dfb4-5dac-bf2e-9bf15e0f2cc7" # A unique identifier for the service
|
|
80
|
+
|
|
81
|
+
docker-build-template = "docker buildx build -t #DOCKER_NAME# ."
|
|
82
|
+
docker-run-template = "docker run -rm -p #PORT#:#PORT#"
|
|
45
83
|
```
|
|
46
84
|
|
|
47
85
|
## Development
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# poetry-plugin-ivcap
|
|
2
|
+
|
|
3
|
+
A custom Poetry plugin that adds a `poetry ivcap` command for local and Docker-based deployments.
|
|
4
|
+
|
|
5
|
+
## Example Configuration
|
|
6
|
+
|
|
7
|
+
Add to your `pyproject.toml`:
|
|
8
|
+
|
|
9
|
+
```toml
|
|
10
|
+
[tool.poetry-plugin-ivcap]
|
|
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 }```
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
poetry self add poetry-plugin-ivcap
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
poetry ivcap run
|
|
27
|
+
poetry ivcap docker-build
|
|
28
|
+
poetry ivcap docker-run
|
|
29
|
+
poetry ivcap docker-publish
|
|
30
|
+
poetry ivcap service-register
|
|
31
|
+
poetry ivcap create-service-id
|
|
32
|
+
poetry ivcap tool-register
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To get help on the currently installed version
|
|
36
|
+
```
|
|
37
|
+
% poetry ivcap
|
|
38
|
+
|
|
39
|
+
IVCAP plugin
|
|
40
|
+
|
|
41
|
+
Supporting the development of services and tools for the IVCAP platform
|
|
42
|
+
|
|
43
|
+
Available subcommands:
|
|
44
|
+
run Run the service locally
|
|
45
|
+
docker-build Build the docker image for this service
|
|
46
|
+
docker-run Run the service's docker image locally
|
|
47
|
+
docker-publish Publish the service's docker image to IVCAP
|
|
48
|
+
service-register Register the service with IVCAP
|
|
49
|
+
create-service-id Create a unique service ID for the service
|
|
50
|
+
tool-register Register the service as an AI Tool with IVCAP
|
|
51
|
+
|
|
52
|
+
Example:
|
|
53
|
+
poetry ivcap run
|
|
54
|
+
|
|
55
|
+
Configurable optiosn in pyproject.toml:
|
|
56
|
+
|
|
57
|
+
[tool.poetry-plugin-ivcap]
|
|
58
|
+
service-file = "service.py" # The Python file that implements the service
|
|
59
|
+
service-file = "service.py"
|
|
60
|
+
service-id = "urn:ivcap:service:ac158a1f-dfb4-5dac-bf2e-9bf15e0f2cc7" # A unique identifier for the service
|
|
61
|
+
|
|
62
|
+
docker-build-template = "docker buildx build -t #DOCKER_NAME# ."
|
|
63
|
+
docker-run-template = "docker run -rm -p #PORT#:#PORT#"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Development
|
|
67
|
+
|
|
68
|
+
### Build the Plugin Package
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
poetry build
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This creates .whl and .tar.gz files in the dist/ directory.
|
|
75
|
+
|
|
76
|
+
### Publish to PyPI
|
|
77
|
+
|
|
78
|
+
Create an account at https://pypi.org/account/register/
|
|
79
|
+
|
|
80
|
+
Add your credentials:
|
|
81
|
+
```bash
|
|
82
|
+
poetry config pypi-token.pypi <your-token>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Publish:
|
|
86
|
+
```bash
|
|
87
|
+
poetry publish --build
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Optional: Test on TestPyPI First
|
|
91
|
+
|
|
92
|
+
To verify your setup without publishing to the real PyPI:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
poetry config repositories.test-pypi https://test.pypi.org/legacy/
|
|
96
|
+
poetry publish -r test-pypi --build
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Then test installing via:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
poetry self add --source test-pypi poetry-plugin-deploy
|
|
103
|
+
```
|
|
@@ -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()
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025 Commonwealth Scientific and Industrial Research Organisation (CSIRO). All rights reserved.
|
|
3
|
+
# Use of this source code is governed by a BSD-style license that can be
|
|
4
|
+
# found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
5
|
+
#
|
|
6
|
+
import os
|
|
7
|
+
from poetry.plugins.application_plugin import ApplicationPlugin
|
|
8
|
+
from cleo.commands.command import Command
|
|
9
|
+
from cleo.helpers import argument
|
|
10
|
+
import subprocess
|
|
11
|
+
|
|
12
|
+
from poetry_plugin_ivcap.util import get_version
|
|
13
|
+
|
|
14
|
+
from .ivcap import create_service_id, service_register, tool_register
|
|
15
|
+
from .docker import docker_build, docker_run
|
|
16
|
+
from .ivcap import docker_publish
|
|
17
|
+
|
|
18
|
+
class IvcapCommand(Command):
|
|
19
|
+
name = "ivcap"
|
|
20
|
+
description = "IVCAP plugin `poetry ivcap <subcommand>`"
|
|
21
|
+
help = """\
|
|
22
|
+
|
|
23
|
+
IVCAP plugin
|
|
24
|
+
|
|
25
|
+
Supporting the development of services and tools for the IVCAP platform
|
|
26
|
+
|
|
27
|
+
Available subcommands:
|
|
28
|
+
run Run the service locally
|
|
29
|
+
docker-build Build the docker image for this service
|
|
30
|
+
docker-run Run the service's docker image locally
|
|
31
|
+
docker-publish Publish the service's docker image to IVCAP
|
|
32
|
+
service-register Register the service with IVCAP
|
|
33
|
+
create-service-id Create a unique service ID for the service
|
|
34
|
+
tool-register Register the service as an AI Tool with IVCAP
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
poetry ivcap run -- --port 8080
|
|
38
|
+
|
|
39
|
+
Configurable options in pyproject.toml:
|
|
40
|
+
|
|
41
|
+
[tool.poetry-plugin-ivcap]
|
|
42
|
+
service-file = "service.py" # The Python file that implements the service
|
|
43
|
+
service-file = "service.py"
|
|
44
|
+
service-id = "urn:ivcap:service:ac158a1f-dfb4-5dac-bf2e-9bf15e0f2cc7" # A unique identifier for the service
|
|
45
|
+
|
|
46
|
+
docker-build-template = "docker buildx build -t #DOCKER_NAME# ."
|
|
47
|
+
docker-run-template = "docker run -rm -p #PORT#:#PORT#"
|
|
48
|
+
"""
|
|
49
|
+
arguments = [
|
|
50
|
+
argument("subcommand", optional=True, description="Subcommand: run, deploy, etc."),
|
|
51
|
+
argument("args", optional=True, multiple=True, description="Additional arguments for subcommand"),
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
allow_extra_args = True
|
|
55
|
+
ignore_validation_errors = True
|
|
56
|
+
|
|
57
|
+
def handle(self):
|
|
58
|
+
poetry = self.application.poetry
|
|
59
|
+
data = poetry.pyproject.data
|
|
60
|
+
|
|
61
|
+
sub = self.argument("subcommand")
|
|
62
|
+
args = self.argument("args")
|
|
63
|
+
|
|
64
|
+
if sub == "run":
|
|
65
|
+
self.run_service(data, args, self.line)
|
|
66
|
+
elif sub == "docker-build":
|
|
67
|
+
docker_build(data, self.line)
|
|
68
|
+
elif sub == "docker-run":
|
|
69
|
+
docker_run(data, args, self.line)
|
|
70
|
+
elif sub == "docker-publish":
|
|
71
|
+
docker_publish(data, self.line)
|
|
72
|
+
elif sub == "service-register":
|
|
73
|
+
service_register(data, self.line)
|
|
74
|
+
elif sub == "create-service-id":
|
|
75
|
+
sid = create_service_id(data, self.line)
|
|
76
|
+
print(sid)
|
|
77
|
+
elif sub == "tool-register":
|
|
78
|
+
tool_register(data, self.line)
|
|
79
|
+
else:
|
|
80
|
+
if not (sub == None or sub == "help"):
|
|
81
|
+
self.line(f"<error>Unknown subcommand: {sub}</error>")
|
|
82
|
+
print(self.help)
|
|
83
|
+
|
|
84
|
+
def run_service(self, data, args, line):
|
|
85
|
+
config = data.get("tool", {}).get("poetry-plugin-ivcap", {})
|
|
86
|
+
|
|
87
|
+
service = config.get("service-file")
|
|
88
|
+
if not service:
|
|
89
|
+
self.line("<error>Missing 'service-file' in [tool.poetry-plugin-ivcap]</error>")
|
|
90
|
+
return
|
|
91
|
+
|
|
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)
|
|
104
|
+
|
|
105
|
+
class IvcapPlugin(ApplicationPlugin):
|
|
106
|
+
def activate(self, application):
|
|
107
|
+
application.command_loader.register_factory("ivcap", lambda: IvcapCommand())
|
|
@@ -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,65 +0,0 @@
|
|
|
1
|
-
# poetry-plugin-ivcap
|
|
2
|
-
|
|
3
|
-
A custom Poetry plugin that adds a `poetry ivcap` command for local and Docker-based deployments.
|
|
4
|
-
|
|
5
|
-
## Example Configuration
|
|
6
|
-
|
|
7
|
-
Add to your `pyproject.toml`:
|
|
8
|
-
|
|
9
|
-
```toml
|
|
10
|
-
[tool.poetry-plugin-ivcap]
|
|
11
|
-
default_target = "docker"
|
|
12
|
-
docker_tag = "myapp:dev"
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Installation
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
poetry self add poetry-plugin-ivcap
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Usage
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
poetry ivcap
|
|
25
|
-
poetry ivcap docker
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Development
|
|
29
|
-
|
|
30
|
-
### Build the Plugin Package
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
poetry build
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
This creates .whl and .tar.gz files in the dist/ directory.
|
|
37
|
-
|
|
38
|
-
### Publish to PyPI
|
|
39
|
-
|
|
40
|
-
Create an account at https://pypi.org/account/register/
|
|
41
|
-
|
|
42
|
-
Add your credentials:
|
|
43
|
-
```bash
|
|
44
|
-
poetry config pypi-token.pypi <your-token>
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Publish:
|
|
48
|
-
```bash
|
|
49
|
-
poetry publish --build
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Optional: Test on TestPyPI First
|
|
53
|
-
|
|
54
|
-
To verify your setup without publishing to the real PyPI:
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
poetry config repositories.test-pypi https://test.pypi.org/legacy/
|
|
58
|
-
poetry publish -r test-pypi --build
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
Then test installing via:
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
poetry self add --source test-pypi poetry-plugin-deploy
|
|
65
|
-
```
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Copyright (c) 2025 Commonwealth Scientific and Industrial Research Organisation (CSIRO). All rights reserved.
|
|
3
|
-
# Use of this source code is governed by a BSD-style license that can be
|
|
4
|
-
# found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
5
|
-
#
|
|
6
|
-
from poetry.plugins.application_plugin import ApplicationPlugin
|
|
7
|
-
from cleo.commands.command import Command
|
|
8
|
-
from cleo.helpers import argument
|
|
9
|
-
import subprocess
|
|
10
|
-
|
|
11
|
-
from .ivcap import create_service_id, service_register, tool_register
|
|
12
|
-
from .docker import docker_build, docker_run
|
|
13
|
-
from .ivcap import docker_publish
|
|
14
|
-
|
|
15
|
-
class IvcapCommand(Command):
|
|
16
|
-
name = "ivcap"
|
|
17
|
-
description = "IVCAP plugin `poetry ivcap <subcommand>`"
|
|
18
|
-
help = """\
|
|
19
|
-
IVCAP plugin
|
|
20
|
-
|
|
21
|
-
Available subcommands:
|
|
22
|
-
run Run the configured service
|
|
23
|
-
deploy Deploy using configured settings
|
|
24
|
-
|
|
25
|
-
Example:
|
|
26
|
-
poetry ivcap run
|
|
27
|
-
"""
|
|
28
|
-
arguments = [
|
|
29
|
-
argument("subcommand", optional=True, description="Subcommand: run, deploy, etc.")
|
|
30
|
-
]
|
|
31
|
-
|
|
32
|
-
def handle(self):
|
|
33
|
-
poetry = self.application.poetry
|
|
34
|
-
data = poetry.pyproject.data
|
|
35
|
-
|
|
36
|
-
sub = self.argument("subcommand")
|
|
37
|
-
if sub == "run":
|
|
38
|
-
self.run_service(data)
|
|
39
|
-
elif sub == "docker-build":
|
|
40
|
-
docker_build(data, self.line)
|
|
41
|
-
elif sub == "docker-run":
|
|
42
|
-
docker_run(data, self.line)
|
|
43
|
-
elif sub == "docker-publish":
|
|
44
|
-
docker_publish(data, self.line)
|
|
45
|
-
elif sub == "service-register":
|
|
46
|
-
service_register(data, self.line)
|
|
47
|
-
elif sub == "create-service-id":
|
|
48
|
-
sid = create_service_id(data, self.line)
|
|
49
|
-
print(sid)
|
|
50
|
-
elif sub == "tool-register":
|
|
51
|
-
tool_register(data, self.line)
|
|
52
|
-
else:
|
|
53
|
-
self.line(f"<error>Unknown subcommand: {sub}</error>")
|
|
54
|
-
|
|
55
|
-
def run_service(self, data):
|
|
56
|
-
config = data.get("tool", {}).get("poetry-plugin-ivcap", {})
|
|
57
|
-
|
|
58
|
-
service = config.get("service-file")
|
|
59
|
-
port = config.get("port")
|
|
60
|
-
|
|
61
|
-
if not service or not port:
|
|
62
|
-
self.line("<error>Missing 'service-file' or 'port' in [tool.poetry-plugin-ivcap]</error>")
|
|
63
|
-
return
|
|
64
|
-
|
|
65
|
-
self.line(f"<info>Running: python {service} --port {port}</info>")
|
|
66
|
-
subprocess.run(["poetry", "run", "python", service, "--port", str(port)])
|
|
67
|
-
|
|
68
|
-
class IvcapPlugin(ApplicationPlugin):
|
|
69
|
-
def activate(self, application):
|
|
70
|
-
application.command_loader.register_factory("ivcap", lambda: IvcapCommand())
|
|
File without changes
|
|
File without changes
|