Skip to content

Commit 614fdb0

Browse files
author
a.pirogov
committed
start implementing windows support
1 parent 69f151b commit 614fdb0

File tree

4 files changed

+143
-45
lines changed

4 files changed

+143
-45
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,12 @@ recommendations on the state of software citation in academic practice.
8686

8787
## Getting Started
8888

89+
Make sure that you have a working Python interpreter in version at least 3.8,
90+
`git` and [`poetry`](https://python-poetry.org/docs/#installation) installed.
91+
8992
To install the template, run `pip install fair-python-cookiecutter`.
9093

91-
Now you can run the tool to generate a new Python project:
94+
Now you can use the tool to generate a new Python project:
9295

9396
```bash
9497
fair-python-cookiecutter YourProjectName

src/fair_python_cookiecutter/main.py

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,87 @@
11
"""Main functions for controlling the template creation."""
22
from pathlib import Path
3+
from shutil import which
34
from typing import Any, Dict
45

56
from cookiecutter.main import cookiecutter
67

78
from .config import CookiecutterConfig, CookiecutterJson
8-
from .utils import TempDir, copy_template
9+
from .utils import TempDir, copy_template, run_cmd
10+
11+
12+
def check_prerequisites():
13+
needed = ["git", "python", "pip", "poetry"]
14+
for prg in needed:
15+
if not which(prg):
16+
print(f"Program '{prg}' was not found in your environment, cannot proceed!")
17+
exit(1)
18+
19+
20+
def strip_yaml_header(file_contents: str):
21+
"""Given text file contents, remove YAML header bounded by '---' lines."""
22+
content = file_contents.splitlines()
23+
startidx = 1
24+
if content and content[0] == "---":
25+
startidx += 1
26+
while not content[startidx - 1].startswith("---"):
27+
startidx += 1
28+
return "\n".join(content[startidx:])
29+
30+
31+
def create_gl_issue_template_from_gh(proj_root: Path):
32+
"""Given project path, create GitLab issue templates from GitHub ones."""
33+
gh_templates = proj_root / ".github" / "ISSUE_TEMPLATE"
34+
gl_templates = proj_root / ".gitlab" / "issue_templates"
35+
36+
gl_templates.mkdir(parents=True, exist_ok=True)
37+
for file in gh_templates.glob("*"):
38+
with open(gl_templates / file.name, "w") as f:
39+
f.write(strip_yaml_header(open(file).read()))
40+
41+
42+
def remove_unneeded_code(proj_root: Path, conf: CookiecutterConfig):
43+
"""Remove code examples the user did not wish to have in the project."""
44+
pkg = conf.fair_python_cookiecutter.project_package()
45+
to_remove = []
46+
if not conf.fair_python_cookiecutter.init_cli:
47+
to_remove += [
48+
(proj_root / "src" / pkg / "cli.py"),
49+
(proj_root / "tests" / "test_cli.py"),
50+
]
51+
if not conf.fair_python_cookiecutter.init_api:
52+
to_remove += [
53+
(proj_root / "src" / pkg / "api.py"),
54+
(proj_root / "tests" / "test_api.py"),
55+
]
56+
for file in to_remove:
57+
if file.is_file():
58+
file.unlink()
59+
60+
61+
def download_licenses(proj_root: Path, conf: CookiecutterConfig):
62+
"""Download all needed licenses and create main LICENSE file."""
63+
# only install reuse/pipx if it is not found
64+
reuse_cmd = "reuse --suppress-deprecation download --all"
65+
if not which("reuse"):
66+
reuse_cmd = "pipx run " + reuse_cmd
67+
if not which("pipx"):
68+
run_cmd("poetry run pip install pipx", cwd=proj_root)
69+
reuse_cmd = "poetry run " + reuse_cmd
70+
# download licenses
71+
print(reuse_cmd, "root:", proj_root)
72+
run_cmd(reuse_cmd, cwd=proj_root)
73+
# copy main license over from resulting licenses directory
74+
license_name = conf.fair_python_cookiecutter.project_license
75+
license = Path(proj_root) / "LICENSE"
76+
license.write_text((proj_root / "LICENSES" / f"{license_name}.txt").read_text())
77+
78+
79+
def finalize_repository(proj_root: Path, conf: CookiecutterConfig):
80+
"""Finalize instantiated repository based on configuration."""
81+
# remove unneeded and create needed files
82+
create_gl_issue_template_from_gh(proj_root)
83+
remove_unneeded_code(proj_root, conf)
84+
download_licenses(proj_root, conf)
985

1086

1187
def create_repository(
@@ -19,6 +95,7 @@ def create_repository(
1995
cc_json = CookiecutterJson.from_config(conf)
2096
cc_args = cc_args or {}
2197

98+
check_prerequisites()
2299
with TempDir(keep=keep_on_fail) as tmp_root:
23100
copy_template(tmp_root, cookiecutter_json=cc_json)
24101
cookiecutter(
@@ -30,4 +107,6 @@ def create_repository(
30107
**cc_args,
31108
)
32109
repo_dir = output_dir / cc_json.project_slug
110+
finalize_repository(repo_dir, conf)
111+
33112
return repo_dir
Lines changed: 36 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,54 @@
1-
#!/usr/bin/env bash
2-
# Finalization of project template
3-
4-
# create gitlab issue templates by stripping metadata from GH issue templates
5-
for file in .github/ISSUE_TEMPLATE/*; do
6-
cat $file | awk -v x=0 '
7-
{
8-
if ( x > 1 )
9-
{ print }
10-
if ( $1 ~ /^---/)
11-
{ x++; }
12-
}' > .gitlab/issue_templates/$(basename $file)
13-
done
14-
15-
# ----
16-
# remove unneeded demo code
17-
# {% if not cookiecutter.init_cli %}
18-
rm src/{{ cookiecutter.project_package }}/cli.py
19-
rm tests/test_cli.py
20-
# {% endif %}
21-
# {% if not cookiecutter.init_api %}
22-
rm src/{{ cookiecutter.project_package }}/api.py
23-
rm tests/test_api.py
24-
# {% endif %}
25-
# ----
26-
27-
# finalize repo setup
28-
git init
29-
30-
# make sure we are not in some venv (or poetry would just use that!)
1+
: # Magic to deactivate current Python venv (if one is enabled) in a cross-platform way
2+
: # See https://stackoverflow.com/questions/17510688/single-script-to-run-in-both-windows-batch-and-linux-bash
3+
:<<"::CMDLITERAL"
4+
: ---- code for cmd.exe ----
5+
ECHO "TODO: cmd.exe code"
6+
: ----------------------------
7+
GOTO :COMMON
8+
::CMDLITERAL
9+
# ---- bash-specific code ----
3110
venv=$(python -c "import sys; print(sys.prefix if sys.base_prefix != sys.prefix else '')")
3211
if [[ -n "$venv" ]]; then
3312
echo WARNING: deactivating currently active virtual environment "$venv"
3413
source "$venv/bin/activate" # make sure we have 'deactivate' available
3514
deactivate
3615
fi
16+
# ----------------------------
17+
:<<"::CMDLITERAL"
18+
:COMMON
19+
::CMDLITERAL
20+
: #All following code must be hybrid (work for bash and cmd.exe)
21+
: # ------------------------------------------------------------
22+
23+
echo "Initializing the git repository ..."
24+
25+
git init
26+
poetry install --with docs
27+
poetry run poe init-dev
3728

38-
poetry install --with docs # install everything into a new venv
39-
poetry run poe init-dev # init git repo + register pre-commit
40-
poetry run pip install pipx # install pipx into venv without adding it as dep
41-
poetry run pipx run reuse download --all # get license files for REUSE compliance
42-
cp "LICENSES/{{ cookiecutter.project_license }}.txt" LICENSE # copy over the main license
29+
echo "Creating CITATION.cff and codemeta.json using somesy ..."
4330

44-
# create CITATION.cff and codemeta.json
4531
git add .
4632
poetry run pre-commit run somesy
4733
git add .
4834

49-
# create first commit
35+
echo "Creating first commit ..."
36+
5037
poetry run git commit \
5138
-m "generated project using fair-python-cookiecutter {{ cookiecutter._fpc_version }}" \
5239
-m "https://github.com/Materials-Data-Science-and-Informatics/fair-python-cookiecutter"
5340

54-
# make sure that the default branch is called 'main'
41+
echo "Ensuring that the default branch is called 'main' ..."
42+
5543
git branch -M main
5644

57-
# sanity-check that main tasks all work
58-
poetry install --with docs
59-
poetry run poe lint --all-files
60-
poetry run poe test
61-
poetry run poe docs
45+
echo "All done! Your project repository is ready :)"
46+
47+
48+
: # TODO: only do following in test mode
49+
50+
: # sanity-check that main tasks all work
51+
: # poetry install --with docs
52+
: # poetry run poe lint --all-files
53+
: # poetry run poe test
54+
: # poetry run poe docs

src/fair_python_cookiecutter/utils.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
"""Utilities for creation of template repository instances."""
22
import json
3+
import platform
34
import shutil
5+
import subprocess
6+
import sys
47
from pathlib import Path
58
from typing import Optional
69
from uuid import uuid1
@@ -72,3 +75,23 @@ def copy_template(
7275
"Template root directory not identified, this must be a bug!"
7376
)
7477
return template_root
78+
79+
80+
def get_venv_path() -> Optional[Path]:
81+
"""Return path of venv, if we detect being inside one."""
82+
return Path(sys.prefix) if sys.base_prefix != sys.prefix else None
83+
84+
85+
VENV_PATH: Optional[Path] = get_venv_path()
86+
"""If set, the path of the virtual environment this tool is running in."""
87+
88+
89+
def venv_activate_cmd(venv_path: Path):
90+
if platform.system() != "Windows":
91+
return "source " + str(venv_path / "bin" / "activate")
92+
else:
93+
return str(venv_path / "Scripts" / "activate")
94+
95+
96+
def run_cmd(cmd: str, cwd: Path = None):
97+
subprocess.run(cmd.split(), cwd=cwd, check=True) # noqa: S603

0 commit comments

Comments
 (0)