Skip to content

Commit ea135f7

Browse files
committed
New script
1 parent 62f7350 commit ea135f7

4 files changed

Lines changed: 233 additions & 0 deletions

File tree

biocontainers2rseq/cleaner.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env python
2+
3+
import argparse
4+
import logging
5+
6+
from tool import Tool
7+
8+
logging.basicConfig()
9+
logging.root.setLevel(logging.INFO)
10+
11+
if __name__ == '__main__':
12+
parser = argparse.ArgumentParser()
13+
parser.add_argument('input', type=str, help="Path to a Dockerfile")
14+
parser.add_argument('output', type=str, help="Output dir (ie, Research-software-ecosystem repository)")
15+
parser.add_argument('--dry-run', action='store_true', help="Dry run")
16+
args = parser.parse_args()
17+
18+
tool = Tool(args.input, args.output, args.dry_run)
19+
tool.write_yaml()
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import os
4+
from pathlib import Path
5+
from collections import defaultdict
6+
from git import Repo
7+
8+
from tool import Tool
9+
10+
11+
def find_dockerfiles(root_dir):
12+
root = Path(root_dir).resolve()
13+
dockerfiles = []
14+
15+
for dirpath, _, filenames in os.walk(root):
16+
current = Path(dirpath)
17+
18+
for f in filenames:
19+
if f == "Dockerfile":
20+
dockerfiles.append(current / f)
21+
22+
return root, dockerfiles
23+
24+
def get_tool_name(path, root):
25+
rel = path.relative_to(root)
26+
return rel.parts[0] if len(rel.parts) > 1 else "unknown"
27+
28+
29+
def get_last_commit_time(repo, file_path):
30+
"""
31+
Returns last commit timestamp affecting the file.
32+
"""
33+
commits = list(repo.iter_commits(paths=file_path, max_count=1))
34+
if not commits:
35+
return 0
36+
return commits[0].committed_date
37+
38+
39+
if __name__ == '__main__':
40+
parser = argparse.ArgumentParser()
41+
parser.add_argument('biocontainers_repo', type=str, help="Biocontainers reposity")
42+
parser.add_argument('rse_repo', type=str, help="Research-software-ecosystem repository")
43+
parser.add_argument('--dry-run', action='store_true', help="Dry run")
44+
45+
args = parser.parse_args()
46+
47+
repo_path = Path(args.biocontainers_repo).resolve()
48+
repo = Repo(repo_path)
49+
root, dockerfiles = find_dockerfiles(str(repo_path))
50+
51+
grouped = defaultdict(list)
52+
53+
for df in dockerfiles:
54+
tool = get_tool_name(df, root)
55+
grouped[tool].append(df)
56+
57+
selected = {}
58+
59+
for tool, files in grouped.items():
60+
print(f"\nProcessing tool: {tool}")
61+
62+
versions = []
63+
64+
best_file = None
65+
best_ts = -1
66+
67+
for f in files:
68+
versions.append(str(f.relative_to(root)))
69+
70+
rel = str(f.relative_to(root))
71+
ts = get_last_commit_time(repo, rel)
72+
73+
if ts > best_ts:
74+
best_file = f
75+
best_ts = ts
76+
77+
if best_file:
78+
selected[tool] = best_file
79+
print(best_file)
80+
print(f" SELECTED: {best_file.relative_to(root)} among {versions}")
81+
82+
tool = Tool(best_file, args.rse_repo, args.dry_run)
83+
tool.write_yaml()
84+
85+
else:
86+
raise Exception("Error on tool " + tool)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
gitpython

biocontainers2rseq/tool.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import os
2+
import re
3+
4+
from copy import deepcopy
5+
6+
from yaml import dump
7+
try:
8+
from yaml import CDumper as Dumper
9+
except ImportError:
10+
from yaml import Dumper
11+
12+
class Tool:
13+
14+
def parse_label_string(self, label_body):
15+
"""
16+
Parse Docker LABEL string into key-value pairs.
17+
Handles:
18+
key=value
19+
key="value"
20+
key='value'
21+
"""
22+
kv = {}
23+
24+
# Split while preserving quoted values
25+
tokens = re.findall(r'(\S+=".*?"|\S+=\'.*?\'|\S+)', label_body)
26+
27+
for token in tokens:
28+
if "=" not in token:
29+
continue
30+
31+
key, value = token.split("=", 1)
32+
key = key.strip()
33+
value = value.strip()
34+
35+
# Remove surrounding quotes
36+
if (value.startswith('"') and value.endswith('"')) or (
37+
value.startswith("'") and value.endswith("'")
38+
):
39+
value = value[1:-1]
40+
41+
kv[key] = value
42+
43+
return kv
44+
45+
def parse_Dockerfile(self, dockerfile_path):
46+
LABEL_RE = re.compile(r'LABEL\s+(.*)', re.IGNORECASE)
47+
labels = {}
48+
49+
with open(dockerfile_path, 'r') as f:
50+
lines = f.readlines()
51+
52+
buffer = ""
53+
collecting = False
54+
55+
for line in lines:
56+
line = line.strip()
57+
58+
# handle continuation lines
59+
if line.endswith("\\"):
60+
buffer += " " + line[:-1].strip()
61+
collecting = True
62+
continue
63+
else:
64+
if collecting:
65+
buffer += " " + line
66+
full_line = buffer.strip()
67+
buffer = ""
68+
collecting = False
69+
else:
70+
full_line = line
71+
72+
match = LABEL_RE.match(full_line)
73+
if match:
74+
parsed = self.parse_label_string(match.group(1))
75+
labels.update(parsed)
76+
77+
if 'software' not in labels:
78+
raise Exception("Missing 'software' label in Dockerfile")
79+
if 'software.version' not in labels:
80+
raise Exception("Missing 'software.version' label in Dockerfile")
81+
self.labels = labels
82+
83+
def __init__(self, dockerfile, output_folder, dry_run=False):
84+
self.dockerfile = dockerfile
85+
self.output_folder = os.path.abspath(output_folder)
86+
self.dry_run = dry_run
87+
88+
self.parse_Dockerfile(dockerfile)
89+
90+
def write_yaml(self):
91+
name = self.labels['software']
92+
version = self.labels['software.version']
93+
94+
biotools = None
95+
if self.labels.get('extra.identifiers.biotools'):
96+
biotools = self.labels.get('extra.identifiers.biotools').strip()
97+
98+
all_tmpdir = os.path.join(self.output_folder, "import/biocontainers/")
99+
100+
files_to_write = [all_tmpdir + '{}.biocontainers.yaml'.format(name)]
101+
102+
if not os.path.exists(all_tmpdir):
103+
os.makedirs(all_tmpdir)
104+
105+
if biotools is not None:
106+
biotool_tmpdir = self.output_folder + '/data/{}/'.format(biotools)
107+
if not os.path.exists(biotool_tmpdir):
108+
os.makedirs(biotool_tmpdir)
109+
files_to_write.append(biotool_tmpdir + '{}.biocontainers.yaml'.format(name))
110+
111+
data = {
112+
'software': name,
113+
"url": "biocontainers/" + name + ":" + version,
114+
"version": version,
115+
"type": "Container file",
116+
'labels': deepcopy(self.labels)
117+
}
118+
119+
softwares = {'softwares': {}}
120+
softwares["softwares"][name] = data
121+
122+
for file_path in files_to_write:
123+
print("Will write content to " + file_path)
124+
if self.dry_run:
125+
continue
126+
with open(file_path, 'w') as fp:
127+
dump(softwares, fp, Dumper=Dumper)

0 commit comments

Comments
 (0)