Skip to content

Commit b098220

Browse files
authored
Merge pull request #123 from seqeralabs/dev
Release v0.4.6
2 parents c5f53f6 + bc32a9b commit b098220

File tree

7 files changed

+142
-74
lines changed

7 files changed

+142
-74
lines changed

README.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ You will need to have an account on Seqera Platform (see [Plans and pricing](htt
1616

1717
`seqerakit` requires the following dependencies:
1818

19-
1. [Seqera Platform CLI](https://github.com/seqeralabs/tower-cli#1-installation)
19+
1. [Seqera Platform CLI (`>=0.9.2`)](https://github.com/seqeralabs/tower-cli#1-installation)
2020

2121
2. [Python (`>=3.8`)](https://www.python.org/downloads/)
2222

@@ -82,23 +82,34 @@ Create a Seqera Platform access token using the [Seqera Platform](https://tower.
8282
`seqerakit` reads this token from the environment variable `TOWER_ACCESS_TOKEN`. Please export it into your terminal as shown below:
8383

8484
```bash
85-
export TOWER_ACCESS_TOKEN=<your access token>
85+
export TOWER_ACCESS_TOKEN=<Your access token>
8686
```
8787

88+
For Enterprise installations of Seqera Platform, you will also need to configure the API endpoint that will be used to connect to the Platform. You can do so by exporting the following environment variable:
89+
```bash
90+
export TOWER_API_ENDPOINT=<Tower API URL>
91+
```
92+
By default, this is set to `https://api.tower.nf` to connect to Seqera Platform Cloud.
93+
94+
8895
## Usage
8996

90-
To confirm the installation of `seqerakit`, configuration of the Seqera Platform CLI and connection is working as expected:
97+
To confirm the installation of `seqerakit`, configuration of the Seqera Platform CLI and connection to the Platform is working as expected. This will run the `tw info` command under the hood:
9198

9299
```bash
93100
seqerakit --info
94101
```
95102

96-
Use the `-h` or `--help `parameter to list the available commands and their associated options:
97-
103+
Use the `--help` or `-h` parameter to list the available commands and their associated options:
98104
```bash
99105
seqerakit --help
100106
```
101107

108+
Use `--version` or `-v` to retrieve the current version of your seqerakit installation:
109+
```bash
110+
seqerakit --version
111+
```
112+
102113
### Dryrun
103114

104115
To print the commands that would executed with `tw` when using a YAML file, you can run `seqerakit` with the `--dryrun` flag:

seqerakit/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import importlib.metadata as importlib_metadata
2+
3+
__version__ = importlib_metadata.version(__name__)

seqerakit/cli.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,48 +24,63 @@
2424
from pathlib import Path
2525
from seqerakit import seqeraplatform, helper, overwrite
2626
from seqerakit.seqeraplatform import ResourceExistsError, ResourceCreationError
27-
27+
from seqerakit import __version__
2828

2929
logger = logging.getLogger(__name__)
3030

3131

3232
def parse_args(args=None):
33-
parser = argparse.ArgumentParser()
34-
parser.add_argument(
33+
parser = argparse.ArgumentParser(
34+
description="Seqerakit: Python wrapper for the Seqera Platform CLI"
35+
)
36+
# General options
37+
general = parser.add_argument_group("General Options")
38+
general.add_argument(
3539
"-l",
3640
"--log_level",
3741
default="INFO",
3842
choices=("CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"),
39-
help="The desired log level (default: INFO).",
40-
type=str.upper,
43+
help="Set the logging level.",
4144
)
42-
parser.add_argument(
45+
general.add_argument(
4346
"--info",
47+
"-i",
4448
action="store_true",
45-
help="Display information about the Seqera Platform and exit",
49+
help="Display Seqera Platform information and exit.",
4650
)
47-
parser.add_argument(
51+
general.add_argument(
4852
"--dryrun",
53+
"-d",
4954
action="store_true",
50-
help="Print the commands that would be executed without running them.",
55+
help="Print the commands that would be executed.",
5156
)
52-
parser.add_argument(
57+
general.add_argument(
58+
"--version",
59+
"-v",
60+
action="version",
61+
version=f"%(prog)s {__version__}",
62+
help="Show version number and exit.",
63+
)
64+
65+
# YAML processing options
66+
yaml_processing = parser.add_argument_group("YAML Processing Options")
67+
yaml_processing.add_argument(
5368
"yaml",
5469
type=Path,
55-
nargs="*", # allow multiple YAML paths
56-
help="One or more YAML files with Seqera Platform resources to create",
70+
nargs="*",
71+
help="One or more YAML files with Seqera Platform resource definitions.",
5772
)
58-
parser.add_argument(
73+
yaml_processing.add_argument(
5974
"--delete",
6075
action="store_true",
61-
help="Recursively delete all resources defined in the YAML file(s)",
76+
help="Recursively delete resources defined in the YAML files.",
6277
)
63-
parser.add_argument(
78+
yaml_processing.add_argument(
6479
"--cli",
6580
dest="cli_args",
6681
type=str,
67-
help="Additional arguments to pass to Seqera Platform"
68-
" CLI enclosed in double quotes (e.g. '--cli=\"--insecure\"')",
82+
help="Additional Seqera Platform CLI specific options to be passed,"
83+
" enclosed in double quotes (e.g. '--cli=\"--insecure\"').",
6984
)
7085
return parser.parse_args(args)
7186

seqerakit/helper.py

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,10 @@ def parse_all_yaml(file_paths, destroy=False):
113113
def parse_block(block_name, item):
114114
# Define the mapping from block names to functions.
115115
block_to_function = {
116-
"credentials": parse_credentials_block,
117-
"compute-envs": parse_compute_envs_block,
116+
"credentials": parse_type_block,
117+
"compute-envs": parse_type_block,
118+
"actions": parse_type_block,
118119
"teams": parse_teams_block,
119-
"actions": parse_actions_block,
120120
"datasets": parse_datasets_block,
121121
"pipelines": parse_pipelines_block,
122122
"launch": parse_launch_block,
@@ -141,27 +141,28 @@ def parse_generic_block(item):
141141
return cmd_args
142142

143143

144-
def parse_credentials_block(item):
144+
def parse_type_block(item, priority_keys=["type", "config-mode", "file-path"]):
145145
cmd_args = []
146-
for key, value in item.items():
147-
if key == "type":
148-
cmd_args.append(str(value))
149-
elif isinstance(value, bool):
150-
if value:
151-
cmd_args.append(f"--{key}")
152-
else:
153-
cmd_args.extend([f"--{key}", str(value)])
154-
return cmd_args
155146

147+
# Ensure at least one of 'type' or 'file-path' is present
148+
if not any(key in item for key in ["type", "file-path"]):
149+
raise ValueError(
150+
"Please specify at least 'type' or 'file-path' for creating the resource."
151+
)
152+
153+
# Process priority keys first
154+
for key in priority_keys:
155+
if key in item:
156+
cmd_args.append(str(item[key]))
157+
del item[key] # Remove the key to avoid repeating in args
156158

157-
def parse_compute_envs_block(item):
158-
cmd_args = []
159159
for key, value in item.items():
160-
if key == "file-path" or key == "type" or key == "config-mode":
161-
cmd_args.append(str(value))
162-
elif isinstance(value, bool):
160+
if isinstance(value, bool):
163161
if value:
164162
cmd_args.append(f"--{key}")
163+
elif key == "params":
164+
temp_file_name = utils.create_temp_yaml(value)
165+
cmd_args.extend(["--params-file", temp_file_name])
165166
else:
166167
cmd_args.extend([f"--{key}", str(value)])
167168
return cmd_args
@@ -194,20 +195,6 @@ def parse_teams_block(item):
194195
return (cmd_args, members_cmd_args)
195196

196197

197-
def parse_actions_block(item):
198-
cmd_args = []
199-
temp_file_name = None
200-
for key, value in item.items():
201-
if key == "type":
202-
cmd_args.append(str(value))
203-
elif key == "params":
204-
temp_file_name = utils.create_temp_yaml(value)
205-
cmd_args.extend(["--params-file", temp_file_name])
206-
else:
207-
cmd_args.extend([f"--{key}", str(value)])
208-
return cmd_args
209-
210-
211198
def parse_datasets_block(item):
212199
cmd_args = []
213200
for key, value in item.items():

seqerakit/overwrite.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def __init__(self, sp):
7373
"workspaces": {
7474
"keys": ["name", "organization"],
7575
"method_args": self._get_workspace_args,
76-
"name_key": "workspaceId",
76+
"name_key": "workspaceName",
7777
},
7878
}
7979

@@ -101,7 +101,6 @@ def handle_overwrite(self, block, args, overwrite=False, destroy=False):
101101
self.block_operations["participants"]["name_key"] = "teamName"
102102
else:
103103
self.block_operations["participants"]["name_key"] = "email"
104-
105104
if self.check_resource_exists(operation["name_key"], sp_args):
106105
# if resource exists and overwrite is true, delete
107106
if overwrite:
@@ -169,11 +168,7 @@ def _get_workspace_args(self, args):
169168
workspaceId used to delete will be retrieved using the _find_workspace_id()
170169
method.
171170
"""
172-
workspace_id = self._find_workspace_id(
173-
json.loads(self.sp.workspaces.list("-o", "json")),
174-
args["organization"],
175-
args["name"],
176-
)
171+
workspace_id = self._find_workspace_id(args["organization"], args["name"])
177172
return ("delete", "--id", str(workspace_id))
178173

179174
def _get_generic_deletion_args(self, args):
@@ -267,12 +262,12 @@ def _find_workspace_id(self, organization, workspace_name):
267262
organization name and workspace name. This ID will be used to delete the
268263
workspace.
269264
"""
270-
if "workspaces" in self.cached_jsondata:
271-
workspaces = self.cached_jsondata["workspaces"]
272-
for workspace in workspaces:
273-
if (
274-
workspace.get("orgName") == organization
275-
and workspace.get("workspaceName") == workspace_name
276-
):
277-
return workspace.get("workspaceId")
265+
jsondata = json.loads(self.cached_jsondata)
266+
workspaces = jsondata["workspaces"]
267+
for workspace in workspaces:
268+
if (
269+
workspace.get("orgName") == organization
270+
and workspace.get("workspaceName") == workspace_name
271+
):
272+
return workspace.get("workspaceId")
278273
return None

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from setuptools import find_packages, setup
44

5-
VERSION = "0.4.5"
5+
VERSION = "0.4.6"
66

77
with open("README.md") as f:
88
readme = f.read()

tests/unit/test_helper.py

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,14 @@ def test_create_mock_dataset_yaml(mock_yaml_file):
130130
assert result["datasets"] == expected_block_output
131131

132132

133-
def test_create_mock_computeevs_yaml(mock_yaml_file):
133+
def test_create_mock_computeevs_source_yaml(mock_yaml_file):
134134
test_data = {
135135
"compute-envs": [
136136
{
137137
"name": "test_computeenv",
138138
"workspace": "my_organization/my_workspace",
139139
"credentials": "my_credentials",
140-
"file-path": "./examples/yaml/computeenvs/computeenvs.yaml",
140+
"file-path": "./computeenvs/computeenv.json",
141141
"wait": "AVAILABLE",
142142
"fusion-v2": True,
143143
"fargate": False,
@@ -149,9 +149,9 @@ def test_create_mock_computeevs_yaml(mock_yaml_file):
149149
expected_block_output = [
150150
{
151151
"cmd_args": [
152+
"./computeenvs/computeenv.json",
152153
"--credentials",
153154
"my_credentials",
154-
"./examples/yaml/computeenvs/computeenvs.yaml",
155155
"--fusion-v2",
156156
"--name",
157157
"test_computeenv",
@@ -171,6 +171,43 @@ def test_create_mock_computeevs_yaml(mock_yaml_file):
171171
assert result["compute-envs"] == expected_block_output
172172

173173

174+
def test_create_mock_computeevs_cli_yaml(mock_yaml_file):
175+
test_data = {
176+
"compute-envs": [
177+
{
178+
"name": "test_computeenv",
179+
"workspace": "my_organization/my_workspace",
180+
"credentials": "my_credentials",
181+
"type": "aws-batch",
182+
"config-mode": "forge",
183+
"wait": "AVAILABLE",
184+
}
185+
],
186+
}
187+
188+
expected_block_output = [
189+
{
190+
"cmd_args": [
191+
"aws-batch",
192+
"forge",
193+
"--credentials",
194+
"my_credentials",
195+
"--name",
196+
"test_computeenv",
197+
"--wait",
198+
"AVAILABLE",
199+
"--workspace",
200+
"my_organization/my_workspace",
201+
],
202+
"overwrite": False,
203+
}
204+
]
205+
file_path = mock_yaml_file(test_data)
206+
result = helper.parse_all_yaml([file_path])
207+
assert "compute-envs" in result
208+
assert result["compute-envs"] == expected_block_output
209+
210+
174211
def test_create_mock_pipeline_add_yaml(mock_yaml_file):
175212
test_data = {
176213
"pipelines": [
@@ -191,7 +228,6 @@ def test_create_mock_pipeline_add_yaml(mock_yaml_file):
191228
}
192229
]
193230
}
194-
195231
# params file cmds parsed separately
196232
expected_block_output = [
197233
{
@@ -239,3 +275,24 @@ def test_empty_yaml_file(mock_yaml_file):
239275
assert f"The file '{file_path}' is empty or does not contain valid data." in str(
240276
e.value
241277
)
278+
279+
280+
def test_error_type_yaml_file(mock_yaml_file):
281+
test_data = {
282+
"compute-envs": [
283+
{
284+
"name": "test_computeenv",
285+
"workspace": "my_organization/my_workspace",
286+
"credentials": "my_credentials",
287+
"wait": "AVAILABLE",
288+
}
289+
],
290+
}
291+
file_path = mock_yaml_file(test_data)
292+
293+
with pytest.raises(ValueError) as e:
294+
helper.parse_all_yaml([file_path])
295+
assert (
296+
"Please specify at least 'type' or 'file-path' for creating the resource."
297+
in str(e.value)
298+
)

0 commit comments

Comments
 (0)