Skip to content

Commit 02c0fbc

Browse files
committed
issue warning if we write outside of the user-specified output directory
1 parent 61b40c7 commit 02c0fbc

File tree

2 files changed

+86
-48
lines changed

2 files changed

+86
-48
lines changed

cwlupgrader/main.py

Lines changed: 83 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,16 @@ def main(args: Optional[List[str]] = None) -> int:
6666
def run(args: argparse.Namespace) -> int:
6767
"""Main function."""
6868
imports: Set[str] = set()
69-
if args.dir:
70-
out_dir = Path(args.dir)
71-
out_dir.mkdir(parents=True, exist_ok=True)
72-
else:
73-
out_dir = Path.cwd()
69+
root_out_dir = Path(args.dir)
70+
root_out_dir.mkdir(parents=True, exist_ok=True)
7471
for p in args.inputs:
7572
path = Path(os.path.normpath(p))
7673
_logger.info("Processing %s", path)
7774
if not p.startswith("../") and path.resolve() != path:
78-
# common_path = os.path.commonpath([out_dir.resolve(), path.resolve()])
79-
# current_out_dir = out_dir / os.path.relpath(path, start=common_path)
80-
current_out_dir = out_dir / path.parent
75+
current_out_dir = root_out_dir / path.parent
8176
current_out_dir.mkdir(parents=True, exist_ok=True)
8277
else:
83-
current_out_dir = out_dir
78+
current_out_dir = root_out_dir
8479
document = load_cwl_document(path)
8580
if "cwlVersion" not in document:
8681
_logger.warn("No cwlVersion found in %s, skipping it.", path)
@@ -103,6 +98,7 @@ def run(args: argparse.Namespace) -> int:
10398
upgraded_document = upgrade_document(
10499
document,
105100
current_out_dir,
101+
root_out_dir,
106102
target_version=target_version,
107103
imports=imports,
108104
)
@@ -113,6 +109,7 @@ def run(args: argparse.Namespace) -> int:
113109
def upgrade_document(
114110
document: Any,
115111
output_dir: Path,
112+
root_dir: Path,
116113
target_version: Optional[str] = "latest",
117114
imports: Optional[Set[str]] = None,
118115
) -> Any:
@@ -167,8 +164,8 @@ def upgrade_document(
167164
pass # does not happen? How to do the case that base version is v1.0?
168165
else:
169166
_logger.error(f"Unsupported cwlVersion: {version}")
170-
process_imports(document, imports, inner_updater, output_dir)
171-
return main_updater(document, output_dir)
167+
process_imports(document, imports, inner_updater, output_dir, root_dir)
168+
return main_updater(document, output_dir, root_dir)
172169

173170

174171
def load_cwl_document(path: Union[str, Path]) -> Any:
@@ -200,8 +197,23 @@ def write_cwl_document(document: Any, path: Path) -> None:
200197
path.chmod(path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
201198

202199

200+
def check_path(root_dir: Path, new_path: Path) -> None:
201+
"""Issue a warning if the new path is outside of the root path."""
202+
common_path = Path(os.path.commonpath([root_dir.resolve(), new_path.resolve()]))
203+
if common_path != root_dir:
204+
_logger.warn(
205+
"Writing file, '%s', outside of the output directory, '%s'.",
206+
os.path.normpath(new_path),
207+
root_dir,
208+
)
209+
210+
203211
def process_imports(
204-
document: Any, imports: Set[str], updater: Callable[[Any, Path], Any], outdir: Path
212+
document: Any,
213+
imports: Set[str],
214+
updater: Callable[[Any, Path, Path], Any],
215+
out_dir: Path,
216+
root_dir: Path,
205217
) -> None:
206218
"""Find any '$import's and process them."""
207219
if isinstance(document, CommentedMap):
@@ -212,72 +224,85 @@ def process_imports(
212224
import_doc = load_cwl_document(
213225
Path(document.lc.filename).parent / value
214226
)
215-
new_path = (outdir / value).resolve()
227+
new_path = (out_dir / value).resolve()
228+
check_path(root_dir, new_path)
216229
new_path.parent.mkdir(parents=True, exist_ok=True)
217-
write_cwl_document(updater(import_doc, outdir), new_path)
230+
write_cwl_document(updater(import_doc, out_dir, root_dir), new_path)
218231
imports.add(value)
219232
else:
220-
process_imports(value, imports, updater, outdir)
233+
process_imports(value, imports, updater, out_dir, root_dir)
221234
elif isinstance(document, MutableSequence):
222235
for entry in document:
223-
process_imports(entry, imports, updater, outdir)
236+
process_imports(entry, imports, updater, out_dir, root_dir)
224237

225238

226-
def v1_0_to_v1_1(document: CommentedMap, outdir: Path) -> CommentedMap:
239+
def v1_0_to_v1_1(document: CommentedMap, out_dir: Path, root_dir: Path) -> CommentedMap:
227240
"""CWL v1.0.x to v1.1 transformation loop."""
228-
_v1_0_to_v1_1(document, outdir)
241+
_v1_0_to_v1_1(document, out_dir, root_dir)
229242
for key, value in document.items():
230243
with SourceLine(document, key, Exception):
231244
if isinstance(value, CommentedMap):
232-
document[key] = _v1_0_to_v1_1(value, outdir)
245+
document[key] = _v1_0_to_v1_1(value, out_dir, root_dir)
233246
elif isinstance(value, list):
234247
for index, entry in enumerate(value):
235248
if isinstance(entry, CommentedMap):
236-
value[index] = _v1_0_to_v1_1(entry, outdir)
249+
value[index] = _v1_0_to_v1_1(entry, out_dir, root_dir)
237250
document["cwlVersion"] = "v1.1"
238251
return sort_v1_0(document)
239252

240253

241-
def v1_0_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
254+
def v1_0_to_v1_2(document: CommentedMap, out_dir: Path, root_dir: Path) -> CommentedMap:
242255
"""CWL v1.0.x to v1.2 transformation."""
243-
document = v1_0_to_v1_1(document, outdir)
256+
document = v1_0_to_v1_1(document, out_dir, root_dir)
244257
document["cwlVersion"] = "v1.2"
245258
return document
246259

247260

248-
def v1_1_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
261+
def v1_1_to_v1_2(document: CommentedMap, out_dir: Path, root_dir: Path) -> CommentedMap:
249262
"""CWL v1.1 to v1.2 transformation."""
250263
document["cwlVersion"] = "v1.2"
251264
return document
252265

253266

254-
def draft3_to_v1_0(document: CommentedMap, outdir: Path) -> CommentedMap:
267+
def draft3_to_v1_0(
268+
document: CommentedMap, out_dir: Path, root_dir: Path
269+
) -> CommentedMap:
255270
"""Transformation loop."""
256-
_draft3_to_v1_0(document, outdir)
271+
_draft3_to_v1_0(document, out_dir, root_dir)
257272
if isinstance(document, MutableMapping):
258273
for key, value in document.items():
259274
with SourceLine(document, key, Exception):
260275
if isinstance(value, CommentedMap):
261-
document[key] = _draft3_to_v1_0(value, outdir)
276+
document[key] = _draft3_to_v1_0(value, out_dir, root_dir)
262277
elif isinstance(value, list):
263278
for index, entry in enumerate(value):
264279
if isinstance(entry, CommentedMap):
265-
value[index] = _draft3_to_v1_0(entry, outdir)
280+
value[index] = _draft3_to_v1_0(entry, out_dir, root_dir)
266281
document["cwlVersion"] = "v1.0"
267282
return sort_v1_0(document)
268283

269284

270-
def draft3_to_v1_1(document: CommentedMap, outdir: Path) -> CommentedMap:
285+
def draft3_to_v1_1(
286+
document: CommentedMap, out_dir: Path, root_dir: Path
287+
) -> CommentedMap:
271288
"""transformation loop."""
272-
return v1_0_to_v1_1(draft3_to_v1_0(document, outdir), outdir)
289+
return v1_0_to_v1_1(draft3_to_v1_0(document, out_dir, root_dir), out_dir, root_dir)
273290

274291

275-
def draft3_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
292+
def draft3_to_v1_2(
293+
document: CommentedMap, out_dir: Path, root_dir: Path
294+
) -> CommentedMap:
276295
"""transformation loop."""
277-
return v1_1_to_v1_2(v1_0_to_v1_1(draft3_to_v1_0(document, outdir), outdir), outdir)
296+
return v1_1_to_v1_2(
297+
v1_0_to_v1_1(draft3_to_v1_0(document, out_dir, root_dir), out_dir, root_dir),
298+
out_dir,
299+
root_dir,
300+
)
278301

279302

280-
def _draft3_to_v1_0(document: CommentedMap, outdir: Path) -> CommentedMap:
303+
def _draft3_to_v1_0(
304+
document: CommentedMap, out_dir: Path, root_dir: Path
305+
) -> CommentedMap:
281306
"""Inner loop for transforming draft-3 to v1.0."""
282307
if "class" in document:
283308
if document["class"] == "Workflow":
@@ -302,12 +327,16 @@ def _draft3_to_v1_0(document: CommentedMap, outdir: Path) -> CommentedMap:
302327
return document
303328

304329

305-
def _draft3_to_v1_1(document: CommentedMap, outdir: Path) -> CommentedMap:
306-
return v1_0_to_v1_1(_draft3_to_v1_0(document, outdir), outdir)
330+
def _draft3_to_v1_1(
331+
document: CommentedMap, out_dir: Path, root_dir: Path
332+
) -> CommentedMap:
333+
return v1_0_to_v1_1(_draft3_to_v1_0(document, out_dir, root_dir), out_dir, root_dir)
307334

308335

309-
def _draft3_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
310-
return _draft3_to_v1_1(document, outdir) # nothing needs doing for 1.2
336+
def _draft3_to_v1_2(
337+
document: CommentedMap, out_dir: Path, root_dir: Path
338+
) -> CommentedMap:
339+
return _draft3_to_v1_1(document, out_dir, root_dir) # nothing needs doing for 1.2
311340

312341

313342
WORKFLOW_INPUT_INPUTBINDING = (
@@ -325,7 +354,9 @@ def _draft3_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
325354
}
326355

327356

328-
def _v1_0_to_v1_1(document: CommentedMap, outdir: Path) -> CommentedMap:
357+
def _v1_0_to_v1_1(
358+
document: CommentedMap, out_dir: Path, root_dir: Path
359+
) -> CommentedMap:
329360
"""Inner loop for transforming draft-3 to v1.0."""
330361
if "class" in document:
331362
if document["class"] == "Workflow":
@@ -339,7 +370,7 @@ def _v1_0_to_v1_1(document: CommentedMap, outdir: Path) -> CommentedMap:
339370
upgrade_v1_0_hints_and_reqs(entry)
340371
if "run" in entry and isinstance(entry["run"], CommentedMap):
341372
process = entry["run"]
342-
_v1_0_to_v1_1(process, outdir)
373+
_v1_0_to_v1_1(process, out_dir, root_dir)
343374
if "cwlVersion" in process:
344375
del process["cwlVersion"]
345376
elif isinstance(steps, MutableMapping):
@@ -350,18 +381,21 @@ def _v1_0_to_v1_1(document: CommentedMap, outdir: Path) -> CommentedMap:
350381
if "run" in entry:
351382
if isinstance(entry["run"], CommentedMap):
352383
process = entry["run"]
353-
_v1_0_to_v1_1(process, outdir)
384+
_v1_0_to_v1_1(process, out_dir, root_dir)
354385
if "cwlVersion" in process:
355386
del process["cwlVersion"]
356387
elif (
357388
isinstance(entry["run"], str)
358389
and "#" not in entry["run"]
359390
):
360-
path = (
391+
run_path = (
361392
Path(document.lc.filename).parent / entry["run"]
362393
).resolve()
363-
process = v1_0_to_v1_1(load_cwl_document(path), outdir)
364-
new_path = (outdir / entry["run"]).resolve()
394+
process = v1_0_to_v1_1(
395+
load_cwl_document(run_path), out_dir, root_dir
396+
)
397+
new_path = out_dir / entry["run"]
398+
check_path(root_dir, new_path)
365399
new_path.parent.mkdir(parents=True, exist_ok=True)
366400
write_cwl_document(process, new_path)
367401
elif isinstance(entry["run"], str) and "#" in entry["run"]:
@@ -406,11 +440,15 @@ def _v1_0_to_v1_1(document: CommentedMap, outdir: Path) -> CommentedMap:
406440
return document
407441

408442

409-
def _v1_0_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
410-
return _v1_0_to_v1_1(document, outdir) # nothing needs doing for v1.2
443+
def _v1_0_to_v1_2(
444+
document: CommentedMap, out_dir: Path, root_dir: Path
445+
) -> CommentedMap:
446+
return _v1_0_to_v1_1(document, out_dir, root_dir) # nothing needs doing for v1.2
411447

412448

413-
def _v1_1_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
449+
def _v1_1_to_v1_2(
450+
document: CommentedMap, out_dir: Path, root_dir: Path
451+
) -> CommentedMap:
414452
return document
415453

416454

tests/test_complete.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,21 @@ def test_draft3_tool_long_form_arrays(tmp_path: Path) -> None:
3737
def test_invalid_target(tmp_path: Path) -> None:
3838
"""Test for invalid target version"""
3939
doc = load_cwl_document(get_data("testdata/v1.0/listing_deep1.cwl"))
40-
result = upgrade_document(doc, tmp_path, "invalid-version")
40+
result = upgrade_document(doc, tmp_path, tmp_path, "invalid-version")
4141
assert result is None
4242

4343

4444
def test_v1_0_to_v1_1(tmp_path: Path) -> None:
4545
"""Basic CWL v1.0 to CWL v1.1 test."""
4646
doc = load_cwl_document(get_data("testdata/v1.0/listing_deep1.cwl"))
47-
upgraded = upgrade_document(doc, tmp_path, "v1.1")
47+
upgraded = upgrade_document(doc, tmp_path, tmp_path, "v1.1")
4848
assert doc == upgraded
4949

5050

5151
def test_v1_1_to_v1_2(tmp_path: Path) -> None:
5252
"""Basic CWL v1.1 to CWL v1.2 test."""
5353
doc = load_cwl_document(get_data("testdata/v1.1/listing_deep1.cwl"))
54-
upgraded = upgrade_document(doc, tmp_path, "v1.2")
54+
upgraded = upgrade_document(doc, tmp_path, tmp_path, "v1.2")
5555
assert doc == upgraded
5656

5757

0 commit comments

Comments
 (0)