Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use copier instead of cookiecutter #190

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Test cookiecutter
name: Test plugin template

on:
pull_request:
Expand Down Expand Up @@ -33,7 +33,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install pytest pytest-cookies tox
python -m pip install -r requirements.txt

- name: Test
uses: aganders3/headless-gui@v2
Expand Down
4 changes: 2 additions & 2 deletions PROMPTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# napari Plugin Prompt Reference

When you first run cookiecutter to build a napari plugin, you will be prompted
When you first run the template to build a napari plugin, you will be prompted
for some configuration options. Your answers to these prompts will determine
some aspects of your plugin package including its name, versioning behaviour,
license, etc. None of these configuration options are set in stone - you
Expand Down Expand Up @@ -127,7 +127,7 @@ add `version = 0.0.1` to your `setup.cfg`.
If you choose `"y"` for this prompt, your package will be set up to have
[`setuptools_scm`](https://github.com/pypa/setuptools_scm) manage versions for
you based on your git tags. See the
[readme](https://github.com/napari/cookiecutter-napari-plugin#automatic-deployment-and-version-management)
[readme](https://github.com/napari/napari-plugin-template#automatic-deployment-and-version-management)
for details.

This option typically requires the least effort to manage versioning for your
Expand Down
41 changes: 22 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
# cookiecutter-napari-plugin
# napari-plugin-template

[Cookiecutter] template for authoring ([npe2]-based) [napari] plugins.
[Copier](https://copier.readthedocs.io/en/stable/) template for authoring ([npe2](https://github.com/napari/npe2)-based) [napari](https://napari.org/) plugins.

**NOTE: This repo is not meant to be cloned/forked directly! Please read "Getting Started" below**

## Getting Started

### Create your plugin package

Install [Cookiecutter] and generate a new napari plugin project:
nstall [Copier](https://copier.readthedocs.io/en/stable/) and the [jinja2-time](https://pypi.org/project/jinja2-time/) extension.
Optionally install the napari plugin engine [npe2](https://github.com/napari/npe2), to help validate your new plugin is configured correctly.

Then you can generate a new napari plugin project:

```bash
pip install cookiecutter
cookiecutter https://github.com/napari/cookiecutter-napari-plugin
python -m pip install copier jinja2-time
python -m pip install npe2
copier copy --trust https://github.com/napari/napari-plugin-template new-plugin-name
```

Cookiecutter prompts you for information regarding your plugin
Copier prompts you for information regarding your plugin
(A new folder will be created in your current working directory):

```bash
Expand Down Expand Up @@ -192,8 +196,7 @@ pytest
### Create your documentation

Documentation generation is not included in this template.
We recommend following the getting started guides for one of the following
documentation generation tools:
We recommend following the getting started guides for one of the following documentation generation tools:

1. [Sphinx]
2. [MkDocs]
Expand Down Expand Up @@ -242,27 +245,28 @@ Details on why this plugin template is using the `src` layout can be found [here

## Issues

If you encounter any problems with this cookiecutter template, please [file an
issue] along with a detailed description.
If you encounter any problems with this template, please
[file an issue](https://github.com/napari/napari-plugin-template/issues/new)
along with a detailed description.

## License

Distributed under the terms of the [BSD-3] license, `cookiecutter-napari-plugin`
Distributed under the terms of the [BSD-3] license, `napari-plugin-template`
is free and open source software.

[napari organization]: https://github.com/napari/
[gitter_badge]: https://badges.gitter.im/Join%20Chat.svg
[gitter]: https://gitter.im/napari/cookiecutter-napari-plugin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge "Join Chat on Gitter.im"
[travis_badge]: https://travis-ci.org/napari/cookiecutter-napari-plugin.svg?branch=main
[travis]: https://travis-ci.org/napari/cookiecutter-napari-plugin "See Build Status on Travis CI"
[docs_badge]: https://readthedocs.org/projects/cookiecutter-napari-plugin/badge/?version=latest
[documentation]: https://cookiecutter-napari-plugin.readthedocs.io/en/latest/ "Documentation"
[cookiecutter]: https://github.com/audreyr/cookiecutter
[gitter]: https://gitter.im/napari/napari-plugin-template?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge "Join Chat on Gitter.im"
[travis_badge]: https://travis-ci.org/napari/napari-plugin-template.svg?branch=main
[travis]: https://travis-ci.org/napari/napari-plugin-template "See Build Status on Travis CI"
[docs_badge]: https://readthedocs.org/projects/napari-plugin-template/badge/?version=latest
[documentation]: https://napari-plugin-template.readthedocs.io/en/latest/ "Documentation"
[copier]: https://github.com/copier-org/copier
[napari]: https://github.com/napari/napari
[npe2]: https://github.com/napari/npe2
[pypi]: https://pypi.org/
[tox]: https://tox.readthedocs.io/en/latest/
[file an issue]: https://github.com/napari/cookiecutter-napari-plugin/issues
[file an issue]: https://github.com/napari/napari-plugin-template/issues
[sphinx]: https://www.sphinx-doc.org/en/master/usage/quickstart.html
[mkdocs]: https://www.mkdocs.org/getting-started/
[jupyterbook]: https://jupyterbook.org/en/stable/start/your-first-book.html
Expand All @@ -275,7 +279,6 @@ is free and open source software.
[travis ci]: https://travis-ci.com/
[appveyor]: http://www.appveyor.com/
[pypa code of conduct]: https://www.pypa.io/en/latest/code-of-conduct/
[shortbread]: https://github.com/audreyr/cookiecutter/releases/tag/1.4.0
[osi_certified]: https://opensource.org/trademarks/osi-certified/web/osi-certified-120x100.png
[osi]: https://opensource.org/
[github actions]: https://github.com/features/actions
Expand Down
216 changes: 216 additions & 0 deletions _tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
from argparse import ArgumentParser
import logging
import os
from pathlib import Path
import re
import subprocess
import sys


def module_name_pep8_compliance(module_name):
"""Validate that the plugin module name is PEP8 compliant."""
if not re.match(r"^[a-z][_a-z0-9]+$", module_name):
link = "https://www.python.org/dev/peps/pep-0008/#package-and-module-names"
logger.error("Module name should be pep-8 compliant.")
logger.error(f" More info: {link}")
sys.exit(1)


def pypi_package_name_compliance(plugin_name):
"""Check there are no underscores in the plugin name"""
if re.search(r"_", plugin_name):
logger.error("PyPI.org and pip discourage package names with underscores.")
sys.exit(1)


def validate_manifest(module_name, project_directory):
"""Validate the new plugin repository against napari requirements."""
try:
from npe2 import PluginManifest
except ImportError as e:
logger.error("npe2 is not installed. Skipping manifest validation.")
return True

current_directory = Path('.').absolute()
if (current_directory.match(project_directory) and not Path(project_directory).is_absolute()):
project_directory = current_directory

path=Path(project_directory) / "src" / Path(module_name) / "napari.yaml"

valid = False
try:
pm = PluginManifest.from_file(path)
msg = f"✔ Manifest for {(pm.display_name or pm.name)!r} valid!"
valid = True
except PluginManifest.ValidationError as err:
msg = f"🅇 Invalid! {err}"
logger.error(msg.encode("utf-8"))
sys.exit(1)
except Exception as err:
msg = f"🅇 Failed to read {path!r}. {type(err).__name__}: {err}"
logger.error(msg.encode("utf-8"))
sys.exit(1)
else:
logger.info(msg.encode("utf-8"))
return valid


def initialize_new_repository(
install_precommit=False,
plugin_name="napari-foobar",
github_repository_url="provide later",
github_username_or_organization="githubuser",
):
"""Initialize new plugin repository with git, and optionally pre-commit."""

msg = ""

# Configure git line ending settings
# https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration
if os.name == 'nt': # if on Windows, configure git line ending characters
subprocess.run(["git", "config", "--global", "core.autocrlf", "true"])
else: # for Linux and Mac
subprocess.run(["git", "config", "--global", "core.autocrlf", "input"])

# try to run git init
try:
subprocess.run(["git", "init", "-q"])
subprocess.run(["git", "checkout", "-b", "main"])
except Exception:
logger.error("Error in git initialization.")

if install_precommit is True:
# try to install and update pre-commit
try:
print("install pre-commit ...")
subprocess.run(["python", "-m", "pip", "install", "pre-commit"], stdout=subprocess.DEVNULL)
print("updating pre-commit...")
subprocess.run(["pre-commit", "autoupdate"], stdout=subprocess.DEVNULL)
subprocess.run(["git", "add", "."])
subprocess.run(["pre-commit", "run", "black", "-a"], capture_output=True)
except Exception:
logger.error("Error pip installing then running pre-commit.")

try:
subprocess.run(["git", "add", "."])
subprocess.run(["git", "commit", "-q", "-m", "initial commit"])
except Exception:
logger.error("Error creating initial git commit.")
msg += f"""
Your plugin template is ready! Next steps:
1. `cd` into your new directory and initialize a git repo
(this is also important for version control!)
cd {plugin_name}
git init -b main
git add .
git commit -m 'initial commit'
# you probably want to install your new package into your environment
pip install -e .
"""
else:
msg +=f"""
Your plugin template is ready! Next steps:
1. `cd` into your new directory
cd {plugin_name}
# you probably want to install your new package into your env
pip install -e .
"""
# Ensure full reqd/write/execute permissions for .git files
if os.name == 'nt': # if on Windows OS
# Avoid permission denied errors on Github Actions CI
subprocess.run(["attrib", "-h", "rr", ".git", "/s", "/d"])

if install_precommit is True:
# try to install and update pre-commit
# installing after commit to avoid problem with comments in setup.cfg.
try:
print("install pre-commit hook...")
subprocess.run(["pre-commit", "install"])
except Exception:
logger.error("Error at pre-commit install, skipping pre-commit")

if github_repository_url != 'provide later':
msg += f"""
2. Create a github repository with the name '{plugin_name}':
https://github.com/{github_username_or_organization}/{plugin_name}.git
3. Add your newly created github repo as a remote and push:
git remote add origin https://github.com/{github_username_or_organization}/{plugin_name}.git
git push -u origin main
4. The following default URLs have been added to `setup.cfg`:
Bug Tracker = https://github.com/{github_username_or_organization}/{plugin_name}/issues
Documentation = https://github.com/{github_username_or_organization}/{plugin_name}#README.md
Source Code = https://github.com/{github_username_or_organization}/{plugin_name}
User Support = https://github.com/{github_username_or_organization}/{plugin_name}/issues
These URLs will be displayed on your plugin's napari hub page.
You may wish to change these before publishing your plugin!"""
else:
msg += """
2. Create a github repository for your plugin:
https://github.com/new
3. Add your newly created github repo as a remote and push:
git remote add origin https://github.com/your-repo-username/your-repo-name.git
git push -u origin main
Don't forget to add this url to setup.cfg!
[metadata]
url = https://github.com/your-repo-username/your-repo-name.git
4. Consider adding additional links for documentation and user support to setup.cfg
using the project_urls key e.g.
[metadata]
project_urls =
Bug Tracker = https://github.com/your-repo-username/your-repo-name/issues
Documentation = https://github.com/your-repo-username/your-repo-name#README.md
Source Code = https://github.com/your-repo-username/your-repo-name
User Support = https://github.com/your-repo-username/your-repo-name/issues"""

msg += """
5. Read the README for more info: https://github.com/napari/napari-plugin-template
6. We've provided a template description for your plugin page on the napari hub at `.napari-hub/DESCRIPTION.md`.
You'll likely want to edit this before you publish your plugin.
7. Consider customizing the rest of your plugin metadata for display on the napari hub:
https://github.com/chanzuckerberg/napari-hub/blob/main/docs/customizing-plugin-listing.md
"""
return msg


if __name__=="__main__":
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("pre_gen_project")
parser = ArgumentParser()
parser.add_argument("--plugin_name",
dest="plugin_name",
help="The name of your plugin")
parser.add_argument("--module_name",
dest="module_name",
help="Plugin module name")
parser.add_argument("--project_directory",
dest="project_directory",
help="Project directory")
parser.add_argument("--install_precommit",
dest="install_precommit",
help="Install pre-commit",
default="False")
parser.add_argument("--github_repository_url",
dest="github_repository_url",
help="Github repository URL",
default='provide later')
parser.add_argument("--github_username_or_organization",
dest="github_username_or_organization",
help="Github user or organisation name",
default='githubuser')
args = parser.parse_args()

# Since bool("False") returns True, we need to check the actual string value
if str(args.install_precommit).lower() == "true":
install_precommit = True
else:
install_precommit = False
module_name_pep8_compliance(args.module_name)
pypi_package_name_compliance(args.plugin_name)
validate_manifest(args.module_name, args.project_directory)
msg = initialize_new_repository(
install_precommit=install_precommit,
plugin_name=args.plugin_name,
github_repository_url=args.github_repository_url,
github_username_or_organization=args.github_username_or_organization,
)
print(msg)
25 changes: 0 additions & 25 deletions cookiecutter.json

This file was deleted.

Loading