-
Notifications
You must be signed in to change notification settings - Fork 178
/
create.py
165 lines (138 loc) · 6.9 KB
/
create.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#!/usr/bin/env python
"""Creates a nf-core pipeline matching the current
organization's specification based on a template.
"""
from genericpath import exists
import git
import jinja2
import logging
import mimetypes
import os
import pathlib
import requests
import shutil
import sys
import nf_core
log = logging.getLogger(__name__)
class PipelineCreate(object):
"""Creates a nf-core pipeline a la carte from the nf-core best-practise template.
Args:
name (str): Name for the pipeline.
description (str): Description for the pipeline.
author (str): Authors name of the pipeline.
version (str): Version flag. Semantic versioning only. Defaults to `1.0dev`.
no_git (bool): Prevents the creation of a local Git repository for the pipeline. Defaults to False.
force (bool): Overwrites a given workflow directory with the same name. Defaults to False.
May the force be with you.
outdir (str): Path to the local output directory.
"""
def __init__(self, name, description, author, version="1.0dev", no_git=False, force=False, outdir=None):
self.short_name = name.lower().replace(r"/\s+/", "-").replace("nf-core/", "").replace("/", "-")
self.name = f"nf-core/{self.short_name}"
self.name_noslash = self.name.replace("/", "-")
self.name_docker = self.name.replace("nf-core", "nfcore")
self.description = description
self.author = author
self.version = version
self.no_git = no_git
self.force = force
self.outdir = outdir
if not self.outdir:
self.outdir = os.path.join(os.getcwd(), self.name_noslash)
def init_pipeline(self):
"""Creates the nf-core pipeline. """
# Make the new pipeline
self.render_template()
# Init the git repository and make the first commit
if not self.no_git:
self.git_init_pipeline()
log.info(
"[green bold]!!!!!! IMPORTANT !!!!!!\n\n"
+ "[green not bold]If you are interested in adding your pipeline to the nf-core community,\n"
+ "PLEASE COME AND TALK TO US IN THE NF-CORE SLACK BEFORE WRITING ANY CODE!\n\n"
+ "[default]Please read: [link=https://nf-co.re/developers/adding_pipelines#join-the-community]https://nf-co.re/developers/adding_pipelines#join-the-community[/link]"
)
def render_template(self):
"""Runs Jinja to create a new nf-core pipeline."""
log.info(f"Creating new nf-core pipeline: '{self.name}'")
# Check if the output directory exists
if os.path.exists(self.outdir):
if self.force:
log.warning(f"Output directory '{self.outdir}' exists - continuing as --force specified")
else:
log.error(f"Output directory '{self.outdir}' exists!")
log.info("Use -f / --force to overwrite existing files")
sys.exit(1)
else:
os.makedirs(self.outdir)
# Run jinja2 for each file in the template folder
env = jinja2.Environment(
loader=jinja2.PackageLoader("nf_core", "pipeline-template"), keep_trailing_newline=True
)
template_dir = os.path.join(os.path.dirname(__file__), "pipeline-template")
binary_ftypes = ["image", "application/java-archive"]
object_attrs = vars(self)
object_attrs["nf_core_version"] = nf_core.__version__
# Can't use glob.glob() as need recursive hidden dotfiles - https://stackoverflow.com/a/58126417/713980
template_files = list(pathlib.Path(template_dir).glob("**/*"))
template_files += list(pathlib.Path(template_dir).glob("*"))
ignore_strs = [".pyc", "__pycache__", ".pyo", ".pyd", ".DS_Store", ".egg"]
for template_fn_path_obj in template_files:
template_fn_path = str(template_fn_path_obj)
if os.path.isdir(template_fn_path):
continue
if any([s in template_fn_path for s in ignore_strs]):
log.debug(f"Ignoring '{template_fn_path}' in jinja2 template creation")
continue
# Set up vars and directories
template_fn = os.path.relpath(template_fn_path, template_dir)
output_path = os.path.join(self.outdir, template_fn)
os.makedirs(os.path.dirname(output_path), exist_ok=True)
# Just copy binary files
(ftype, encoding) = mimetypes.guess_type(template_fn_path)
if encoding is not None or (ftype is not None and any([ftype.startswith(ft) for ft in binary_ftypes])):
log.debug(f"Copying binary file: '{output_path}'")
shutil.copy(template_fn_path, output_path)
continue
# Render the template
log.debug(f"Rendering template file: '{template_fn}'")
j_template = env.get_template(template_fn)
rendered_output = j_template.render(object_attrs)
# Write to the pipeline output file
with open(output_path, "w") as fh:
log.debug(f"Writing to output file: '{output_path}'")
fh.write(rendered_output)
# Make a logo and save it
self.make_pipeline_logo()
def make_pipeline_logo(self):
"""Fetch a logo for the new pipeline from the nf-core website"""
logo_url = f"https://nf-co.re/logo/{self.short_name}"
log.debug(f"Fetching logo from {logo_url}")
email_logo_path = f"{self.outdir}/assets/{self.name_noslash}_logo.png"
os.makedirs(os.path.dirname(email_logo_path), exist_ok=True)
log.debug(f"Writing logo to '{email_logo_path}'")
r = requests.get(f"{logo_url}?w=400")
with open(email_logo_path, "wb") as fh:
fh.write(r.content)
readme_logo_path = f"{self.outdir}/docs/images/{self.name_noslash}_logo.png"
log.debug(f"Writing logo to '{readme_logo_path}'")
os.makedirs(os.path.dirname(readme_logo_path), exist_ok=True)
r = requests.get(f"{logo_url}?w=600")
with open(readme_logo_path, "wb") as fh:
fh.write(r.content)
def git_init_pipeline(self):
"""Initialises the new pipeline as a Git repository and submits first commit."""
log.info("Initialising pipeline git repository")
repo = git.Repo.init(self.outdir)
repo.git.add(A=True)
repo.index.commit(f"initial template build from nf-core/tools, version {nf_core.__version__}")
# Add TEMPLATE branch to git repository
repo.git.branch("TEMPLATE")
repo.git.branch("dev")
log.info(
"Done. Remember to add a remote and push to GitHub:\n"
f"[white on grey23] cd {self.outdir} \n"
" git remote add origin git@github.com:USERNAME/REPO_NAME.git \n"
" git push --all origin "
)
log.info("This will also push your newly created dev branch and the TEMPLATE branch for syncing.")