|
| 1 | +# coding: utf-8 |
| 2 | + |
| 3 | +from invoke import task |
| 4 | +from github import Github |
| 5 | +from datetime import datetime |
| 6 | + |
| 7 | +import os.path as op |
| 8 | +import os |
| 9 | +import yaml |
| 10 | + |
| 11 | + |
| 12 | +def get_latest_release(address: str) -> str: |
| 13 | + git = Github() |
| 14 | + repo = git.get_repo(address) |
| 15 | + releases = repo.get_releases() |
| 16 | + return releases[0].tag_name |
| 17 | + |
| 18 | + |
| 19 | +snakemake_wrappers_version = os.environ.get("SNAKEMAKE_WRAPPERS_VERSION") |
| 20 | +if not snakemake_wrappers_version: |
| 21 | + print("Searching snakemake wrappers version on the web...") |
| 22 | + snakemake_wrappers_version = get_latest_release("snakemake/snakemake-wrappers") |
| 23 | + |
| 24 | +fair_genome_indexer_version = os.environ.get("FAIR_GENOME_INDEXER_VERSION") |
| 25 | +if not fair_genome_indexer_version: |
| 26 | + print("Searching fair-genome-indexer version on the web...") |
| 27 | + fair_genome_indexer_version = get_latest_release("tdayris/fair_genome_indexer") |
| 28 | + |
| 29 | +locations = { |
| 30 | + "snakefile": "../workflow/Snakefile", |
| 31 | + "rules": "../workflow/rules/", |
| 32 | + "scripts": "../workflow/scripts", |
| 33 | + "envs": "../workflow/envs", |
| 34 | + "changelog": "../CHANGELOG.md", |
| 35 | + "cff": "../citation.cff", |
| 36 | +} |
| 37 | +for location in locations.values(): |
| 38 | + if not op.exists(location): |
| 39 | + raise FileNotFoundError(f"Could not find {location=}") |
| 40 | + |
| 41 | + |
| 42 | +def get_future_version( |
| 43 | + changelog: str = locations["changelog"], |
| 44 | +) -> str: |
| 45 | + with open(changelog, "r") as changelog_stream: |
| 46 | + line = next(changelog_stream) |
| 47 | + version = line.strip("#").strip() |
| 48 | + |
| 49 | + print(f"Future version is: {version=}") |
| 50 | + return version |
| 51 | + |
| 52 | + |
| 53 | +future_version = get_future_version() |
| 54 | + |
| 55 | + |
| 56 | +@task |
| 57 | +def clean( |
| 58 | + c, |
| 59 | + conda: bool = False, |
| 60 | + extra: str = "", |
| 61 | +): |
| 62 | + patterns = [ |
| 63 | + "black.txt", |
| 64 | + "format.txt", |
| 65 | + "linter_info.txt", |
| 66 | + "pipeline.txt", |
| 67 | + "results", |
| 68 | + "resources", |
| 69 | + "report.txt", |
| 70 | + "report.zip", |
| 71 | + "resources.md", |
| 72 | + "resources.tsv", |
| 73 | + "resources.txt", |
| 74 | + "summary.tsv", |
| 75 | + "summary_small.tsv", |
| 76 | + "docs_update.txt", |
| 77 | + "logs", |
| 78 | + "reference", |
| 79 | + "report", |
| 80 | + "tmp", |
| 81 | + "Snakefile", |
| 82 | + "wrappers_update.txt", |
| 83 | + "conda_update.txt", |
| 84 | + "docs_update.txt", |
| 85 | + ] |
| 86 | + if conda: |
| 87 | + patterns.append(".snakemake") |
| 88 | + patterns.append(".conda") |
| 89 | + |
| 90 | + for pattern in patterns: |
| 91 | + if op.exists(pattern): |
| 92 | + print(f"Removing {pattern=}") |
| 93 | + c.run(f"rm -rf '{pattern}'") |
| 94 | + else: |
| 95 | + print(f"Skipping {pattern=}") |
| 96 | + |
| 97 | + |
| 98 | +@task |
| 99 | +def update_docs_cff( |
| 100 | + c, |
| 101 | + to: str = future_version, |
| 102 | + cff_path: str = locations["cff"], |
| 103 | +): |
| 104 | + today = datetime.today().strftime("%Y-%m-%d") |
| 105 | + cff = { |
| 106 | + "cff-version": "1.2.0", |
| 107 | + "message": "If you use this software, please cite it as below.", |
| 108 | + "authors": [ |
| 109 | + { |
| 110 | + "family-names": "Dayris", |
| 111 | + "given-names": "Thibault", |
| 112 | + "orcid": "https://orcid.org/0009-0009-2758-8450", |
| 113 | + } |
| 114 | + ], |
| 115 | + "title": "fair-genome-indexer", |
| 116 | + "version": future_version, |
| 117 | + "date-released": today, |
| 118 | + "url": "https://github.com/tdayris/fair_genome_indexer", |
| 119 | + } |
| 120 | + print(cff) |
| 121 | + with open(cff_path, "w") as yaml_cff_stream: |
| 122 | + yaml.dump(cff, yaml_cff_stream, default_flow_style=False) |
| 123 | + |
| 124 | + |
| 125 | +@task(update_docs_cff) |
| 126 | +def update_docs_wrappers(c, to: str = snakemake_wrappers_version): |
| 127 | + regex = rf"s|v[0-9]\+\.[0-9]\+\.[0-9]\+/wrappers|{to}/wrappers|g" |
| 128 | + print(f"Updating snakemake wrappers in README.md to {to=}") |
| 129 | + c.run(f"sed -i '{regex}' ../README.md >> docs_update.txt 2>&1") |
| 130 | + |
| 131 | + for root, dirs, files in os.walk("../workflow/reports"): |
| 132 | + for file in files: |
| 133 | + if file.endswith(".rst"): |
| 134 | + print(f"Updating snakemake wrappers in '{root}/{file}'...") |
| 135 | + c.run(f"sed -i '{regex}' '{root}/{file}' >> docs_update.txt") |
| 136 | + |
| 137 | + regex = ( |
| 138 | + 's|snakemake_wrappers_prefix: str = "v4.5.0"|' |
| 139 | + f'snakemake_wrappers_prefix: str = "{to}"|g' |
| 140 | + ) |
| 141 | + print("Updating '../workflow/rules/common.smk'...") |
| 142 | + c.run(f"sed -i '{regex}' '../workflow/rules/common.smk' >> update_docs.txt") |
| 143 | + |
| 144 | + |
| 145 | +@task(update_docs_wrappers) |
| 146 | +def update_wrappers_rules( |
| 147 | + c, |
| 148 | + to: str = snakemake_wrappers_version, |
| 149 | + snakefile: str = locations["snakefile"], |
| 150 | + rules: str = locations["rules"], |
| 151 | +): |
| 152 | + print(f"Updating {snakefile=}...") |
| 153 | + c.run( |
| 154 | + "snakedeploy update-snakemake-wrappers " |
| 155 | + f"--git-ref '{snakemake_wrappers_version}' '{snakefile}' " |
| 156 | + ">> wrappers_update.txt 2>&1" |
| 157 | + ) |
| 158 | + |
| 159 | + for root, dirs, files in os.walk(rules): |
| 160 | + for file in files: |
| 161 | + if file.endswith(".smk"): |
| 162 | + print(f"Updating '{root}/{file}'...") |
| 163 | + c.run( |
| 164 | + "snakedeploy update-snakemake-wrappers --git-ref " |
| 165 | + f"'{snakemake_wrappers_version}' '{root}/{file}' " |
| 166 | + ">> wrappers_update.txt 2>&1" |
| 167 | + ) |
| 168 | + else: |
| 169 | + print(f"Skipping '{root}/{file}'...") |
| 170 | + |
| 171 | + |
| 172 | +@task(update_wrappers_rules) |
| 173 | +def update_conda( |
| 174 | + c, |
| 175 | + envs: str = locations["envs"], |
| 176 | +): |
| 177 | + for root, dirs, files in os.walk(envs): |
| 178 | + for file in files: |
| 179 | + if file.endswith(".yaml"): |
| 180 | + print(f"Updating '{root}/{file}'. This may take some time...") |
| 181 | + c.run( |
| 182 | + "snakedeploy update-conda-envs --conda-frontend mamba " |
| 183 | + f"--pin-envs '{root}/{file}' > conda_update.txt 2>&1" |
| 184 | + ) |
| 185 | + |
| 186 | + |
| 187 | +@task |
| 188 | +def black(c, scripts: str = locations["scripts"]): |
| 189 | + log: str = "black.txt" |
| 190 | + for root, dirs, files in os.walk(scripts): |
| 191 | + for file in files: |
| 192 | + if file.endswith(".py"): |
| 193 | + print(f"Formatting '{root}/{file}'...") |
| 194 | + c.run(f"black '{root}/{file}' >> {log} 2>&1") |
| 195 | + |
| 196 | + |
| 197 | +@task(black) |
| 198 | +def snakefmt( |
| 199 | + c, |
| 200 | + snakefile: str = locations["snakefile"], |
| 201 | + rules: str = locations["rules"], |
| 202 | +): |
| 203 | + log: str = "format.txt" |
| 204 | + print(f"Formatting {snakefile=}...") |
| 205 | + c.run(f"snakefmt '{snakefile}' >> '{log}' 2>&1") |
| 206 | + |
| 207 | + for root, dirs, files in os.walk(rules): |
| 208 | + for file in files: |
| 209 | + if file.endswith(".smk"): |
| 210 | + print(f"Formatting '{root}/{file}'...") |
| 211 | + c.run(f"snakefmt '{root}/{file}' >> '{log}' 2>&1") |
| 212 | + |
| 213 | + |
| 214 | +@task(snakefmt) |
| 215 | +def linter( |
| 216 | + c, |
| 217 | + snakefile: str = locations["snakefile"], |
| 218 | +): |
| 219 | + print(f"Linting {snakefile=}...") |
| 220 | + c.run(f"snakemake --lint -s '{snakefile}' > linter_info.txt 2>&1") |
| 221 | + |
| 222 | + |
| 223 | +@task(linter) |
| 224 | +def pipeline( |
| 225 | + c, |
| 226 | + snakefile: str = locations["snakefile"], |
| 227 | +): |
| 228 | + print("Running pipeline...") |
| 229 | + cmd = ( |
| 230 | + f"snakemake -s '{snakefile}' " |
| 231 | + "--cores 7 --restart-times 0 " |
| 232 | + "--rerun-incomplete --printshellcmds " |
| 233 | + "--shadow-prefix 'tmp' --rerun-triggers 'mtime' " |
| 234 | + "--software-deployment-method conda " |
| 235 | + "--benchmark-extended > pipeline.txt 2>&1" |
| 236 | + ) |
| 237 | + print(cmd) |
| 238 | + c.run(cmd) |
| 239 | + |
| 240 | + |
| 241 | +@task(pipeline) |
| 242 | +def report( |
| 243 | + c, |
| 244 | + snakefile: str = locations["snakefile"], |
| 245 | +): |
| 246 | + print("Building test report...") |
| 247 | + c.run(f"snakemake -s '{snakefile}' --report report.zip > report.txt 2>&1") |
| 248 | + |
| 249 | +@task(clean, update_conda, report) |
| 250 | +def all(c): |
| 251 | + print("All done.") |
| 252 | + |
0 commit comments