feat: nix support

- Provide a dev shell.
- Provide a nix package.
- Provide a nix flake.
- Development environment based on direnv.
- Docs.
- Configure Gitpod to use direnv and nix.
- Configure Cachix out of the box, and document how to use it.
- Add direnv and nix to CI.
- Satisfy some linters that came from Precommix, even when Precommix was later discarded.
- Mark some tests as impure.
- Run only pure tests when building Copier with Nix.
- Add poetry loader to direnv.
- Update contribution guide.
This commit is contained in:
Jairo Llopis 2023-01-18 09:40:08 +00:00 committed by GitHub
parent dae31980a3
commit 67cc4ffde3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1254 additions and 953 deletions

View File

@ -1,12 +1,16 @@
# Defaults for all kinds of source code root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
# Specific language overrides [*]
[*.{yml,yaml,json}] indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
# For isort
profile = black
[*.{code-snippets,code-workspace,json,lock,nix,tf,yaml,yml}{,.jinja}]
indent_size = 2 indent_size = 2

19
.envrc Normal file
View File

@ -0,0 +1,19 @@
# SEE https://github.com/direnv/direnv/wiki/Python#poetry
layout_poetry() {
VIRTUAL_ENV=$(poetry env info --path 2>/dev/null ; true)
if [[ -z $VIRTUAL_ENV || ! -d $VIRTUAL_ENV ]]; then
log_status "No virtual environment exists. Executing \`poetry install\` to create one."
poetry install --with dev,docs
VIRTUAL_ENV=$(poetry env info --path)
fi
PATH_add "$VIRTUAL_ENV/bin"
export POETRY_ACTIVE=1
export VIRTUAL_ENV
}
use flake . \
--extra-substituters 'https://copier.cachix.org https://devenv.cachix.org' \
--extra-trusted-public-keys 'copier.cachix.org-1:sVkdQyyNXrgc53qXPCH9zuS91zpt5eBYcg7JQSmTBG4= devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw='
layout_poetry

View File

@ -1,4 +1,6 @@
[flake8] [flake8]
extend-ignore = W503,E203,E501,D100,D101,D102,D103,D104,D105,D107,SIM117 ignore = E203, E501, W503, B950
max-complexity = 20
max-line-length = 88 max-line-length = 88
select = C,E,F,W,B
per-file-ignores=
__init__.py:F401

View File

@ -68,7 +68,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install poetry poetry-dynamic-versioning python -m pip install poetry poetry-dynamic-versioning
poetry install --with docs -v poetry install --with dev,docs -v
- name: Run pytest - name: Run pytest
run: poetry run poe test --cov=./ --cov-report=xml -ra . run: poetry run poe test --cov=./ --cov-report=xml -ra .
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
@ -84,9 +84,55 @@ jobs:
name: copier name: copier
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
flake-check:
strategy:
fail-fast: false
matrix:
os:
- macos-latest
- ubuntu-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
# Install Nix and set up Cachix
- uses: cachix/install-nix-action@v17
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v11
with:
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
extraPullNames: devenv
name: copier
pushFilter: (-source$|nixpkgs\.tar\.gz$)
# Cache for poetry venv when using direnv
- uses: actions/cache@v3
with:
path: |
.cache
.devenv
.direnv
.venv
key:
direnv|${{ runner.os }}|${{ hashFiles('pyproject.toml', '*.lock', '*.nix')
}}
# Check direnv works as expected
- uses: JRMurr/direnv-nix-action@v2
with:
install-nix: "false"
cache-store: "false"
- run: copier --version
# Run nix checks
- run: nix flake check -L
publish: publish:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
needs: build needs:
- build
- flake-check
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

6
.gitignore vendored
View File

@ -58,3 +58,9 @@ htmlcov/
# macOS # macOS
.DS_Store .DS_Store
# Nix
/.devenv
/.direnv
/.pre-commit-config.yaml
/result*

View File

@ -1,8 +0,0 @@
FROM gitpod/workspace-full
USER gitpod
ENV PIP_USER=no POETRY_VIRTUALENVS_IN_PROJECT=true
RUN pip3 install \
commitizen \
poethepoet \
poetry \
poetry-dynamic-versioning

View File

@ -1,3 +1,10 @@
github:
prebuilds:
addBadge: true
addCheck: prevent-merge-on-error
master: true
pullRequests: true
vscode: vscode:
extensions: extensions:
- bpruitt-goddard.mermaid-markdown-syntax-highlighting - bpruitt-goddard.mermaid-markdown-syntax-highlighting
@ -5,15 +12,10 @@ vscode:
- esbenp.prettier-vscode - esbenp.prettier-vscode
- ms-python.python - ms-python.python
image:
file: .gitpod.dockerfile
ports: ports:
# Mkdocs local server; start it with `poe docs` # Mkdocs local server; start it with `poe docs`
- port: 8000 - port: 8000
onOpen: notify onOpen: notify
tasks: tasks:
- init: - init: direnv allow
export PIP_USER=no && poetry install -E docs && poetry run pre-commit install -t
pre-commit -t commit-msg && poetry run pre-commit install-hooks

View File

@ -1,99 +0,0 @@
default_language_version:
python: python3
default_stages:
- commit
repos:
# checking our hooks themselves
- repo: meta
hooks:
- id: check-hooks-apply
- id: check-useless-excludes
# hooks running from local virtual environment
- repo: local
hooks:
- id: autoflake
name: autoflake
entry: poetry run autoflake
language: system
types: [python]
args: ["-i", "--remove-all-unused-imports", "--ignore-init-module-imports"]
- id: black
name: black
entry: poetry run black
language: system
types: [python]
require_serial: true
- id: flake8
name: flake8
entry: poetry run flake8
language: system
types: [python]
require_serial: true
- id: poetry_check
description: Check the integrity of pyproject.toml
name: poetry_check
entry: poetry check
language: system
pass_filenames: false
require_serial: true
- id: poetry-lock-check
name: poetry-lock-check
entry: poetry lock
args: ["--check"]
language: system
pass_filenames: false
- id: isort
name: isort
entry: poetry run isort
require_serial: true
language: system
types_or: [cython, pyi, python]
args: ["--filter-files"]
- id: commitizen
name: commitizen
entry: poetry run cz check
args: [--allow-abort, --commit-msg-file]
language: system
stages: [commit-msg]
- repo: https://github.com/asottile/pyupgrade
rev: v3.2.1
hooks:
- id: pyupgrade
args: [--py37-plus]
# prettier to format our many yaml files
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.7.1
hooks:
- id: prettier
exclude: |
(?x)
# Those files have wrong syntax and would fail
^tests/demo_invalid/copier.yml|tests/demo_transclude_invalid(_multi)?/demo/copier.yml$
# HACK https://github.com/prettier/prettier/issues/9430
|^tests/demo
# miscellaneous hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: check-added-large-files
- id: check-ast
- id: check-case-conflict
- id: check-docstring-first
- id: check-executables-have-shebangs
- id: check-merge-conflict
- id: check-toml
- id: debug-statements
- id: detect-private-key
- id: end-of-file-fixer
exclude: \.noeof\. # Some tests require no EOF
- id: fix-encoding-pragma
args: ["--remove"]
- id: mixed-line-ending
args: ["--fix=lf"]
- id: trailing-whitespace

View File

@ -1,5 +1,2 @@
# Defaults for all prettier-supported languages
bracketSpacing: false
endOfLine: lf
printWidth: 88 printWidth: 88
proseWrap: always proseWrap: always

View File

@ -47,14 +47,21 @@ Feel free to discuss with our community through
## Dev Environment Setup ## Dev Environment Setup
The recommended way is:
1. Click on
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/copier-org/copier)
1. Wait until the terminal that pops up is ready.
1. Accept the direnv and nix pop-ups that appear.
For local or more complex setups, continue reading.
We use some tools as part of our development workflow which you'll need to install into We use some tools as part of our development workflow which you'll need to install into
your host environment: your host environment:
- [Poetry](https://python-poetry.org/) v1.2+ for packaging and dependency management - [Nix](https://nixos.org/download.html) to provide a reproducible development
environment.
Or you can use - [Direnv](https://direnv.net/) to load that environment automatically in your shell.
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/copier-org/copier)
to start hacking with one click!
## Get Started! ## Get Started!
@ -68,22 +75,18 @@ Ready to contribute? Here's how to set up the project for local development.
cd copier cd copier
``` ```
1. Use Poetry to set up a development environment: 1. Use Direnv to set up a development environment:
```shell ```shell
# Tell Poetry to create the virtualenv in the project directory # Let direnv do its magic
poetry config virtualenvs.in-project true --local direnv allow
# Create a virtualenv with all dependencies from pyproject.toml
poetry install --with docs
# Install development helper tools
poetry run pre-commit install -t pre-commit -t commit-msg
# Create a new shell with the virtualenv activated
poetry shell
``` ```
Direnv will take some time to load for the 1st time. It will download all
development dependencies, including [Poetry](https://python-poetry.org/), and it
will use it to create a virtualenv and install Copier with all its development
dependencies too.
1. Create a branch for local development: 1. Create a branch for local development:
```shell ```shell
@ -99,9 +102,6 @@ Ready to contribute? Here's how to set up the project for local development.
poe lint poe lint
``` ```
To have multiple Python versions on the same machine for running `tox`, I recommend
using [pyenv](https://github.com/pyenv/pyenv) (_do not_ confuse it with `pipenv`,).
1. Commit your changes and push your branch to GitHub: 1. Commit your changes and push your branch to GitHub:
```shell ```shell
@ -118,6 +118,7 @@ Before you submit a pull request, check that it meets these guidelines:
1. The pull request has code, it should include tests. 1. The pull request has code, it should include tests.
1. Check that all checks pass on GitHub CI. 1. Check that all checks pass on GitHub CI.
1. If something significant changed, modify docs.
## Tips ## Tips
@ -126,3 +127,13 @@ To run a subset of tests:
```shell ```shell
poe test tests/the-tests-file.py poe test tests/the-tests-file.py
``` ```
### Nix binary cache
Our direnv configuration is configured to use binary caches by default.
However, to add our binary caches permanently:
```shell
nix-shell -p cachix --run 'cachix use copier && cachix use devenv'
```

View File

@ -25,6 +25,7 @@ A library and CLI app for rendering project templates.
1. Install Git 2.27 or newer. 1. Install Git 2.27 or newer.
1. To use as a CLI app: `pipx install copier` 1. To use as a CLI app: `pipx install copier`
1. To use as a library: `pip install copier` or `conda install -c conda-forge copier` 1. To use as a library: `pip install copier` or `conda install -c conda-forge copier`
1. To use with 100% reproducibility: `nix profile install github:copier-org/copier`
## Quick start ## Quick start

View File

@ -1,4 +1,7 @@
"""Copier CLI entrypoint."""
from copier.cli import CopierApp from copier.cli import CopierApp
# HACK https://github.com/nix-community/poetry2nix/issues/504
copier_app_run = CopierApp.run
if __name__ == "__main__": if __name__ == "__main__":
CopierApp.run() copier_app_run()

View File

@ -1,4 +1,3 @@
#!/usr/bin/env python
""" """
Command line entrypoint. This module declares the Copier CLI applications. Command line entrypoint. This module declares the Copier CLI applications.
@ -83,6 +82,7 @@ class CopierApp(cli.Application):
Attributes: Attributes:
answers_file: Set [answers_file][] option. answers_file: Set [answers_file][] option.
conflict: Set [conflict][] option.
exclude: Set [exclude][] option. exclude: Set [exclude][] option.
vcs_ref: Set [vcs_ref][] option. vcs_ref: Set [vcs_ref][] option.
pretend: Set [pretend][] option. pretend: Set [pretend][] option.
@ -356,6 +356,3 @@ if __doc__:
CopierApp.run(["copier", "--help-all"], exit=False) CopierApp.run(["copier", "--help-all"], exit=False)
help_io.seek(0) help_io.seek(0)
__doc__ += f"\n\nCLI help generated from `copier --help-all`:\n\n```\n{help_io.read()}\n```" __doc__ += f"\n\nCLI help generated from `copier --help-all`:\n\n```\n{help_io.read()}\n```"
if __name__ == "__main__":
CopierApp.run()

View File

@ -77,7 +77,9 @@ class Worker:
Example: Example:
```python ```python
with Worker(src_path="https://github.com/copier-org/autopretty.git", "output") as worker: with Worker(
src_path="https://github.com/copier-org/autopretty.git", "output"
) as worker:
worker.run_copy() worker.run_copy()
``` ```
@ -171,9 +173,11 @@ class Worker:
conflict: str = "rej" conflict: str = "rej"
def __enter__(self): def __enter__(self):
"""Allow using worker as a context manager."""
return self return self
def __exit__(self, type, value, traceback): def __exit__(self, type, value, traceback):
"""Clean up garbage files after worker usage ends."""
if value is not None: if value is not None:
# exception was raised from code inside context manager: # exception was raised from code inside context manager:
# try to clean up, ignoring any exception, then re-raise # try to clean up, ignoring any exception, then re-raise
@ -302,7 +306,6 @@ class Worker:
"""Determine if a file or directory can be rendered. """Determine if a file or directory can be rendered.
Args: Args:
dst_relpath: dst_relpath:
Relative path to destination. Relative path to destination.
is_dir: is_dir:
@ -473,7 +476,6 @@ class Worker:
"""Render one file. """Render one file.
Args: Args:
src_abspath: src_abspath:
The absolute path to the file that will be rendered. The absolute path to the file that will be rendered.
""" """
@ -824,7 +826,7 @@ class Worker:
def run_copy( def run_copy(
src_path: str, src_path: str,
dst_path: StrOrPath = ".", dst_path: StrOrPath = ".",
data: AnyByStrDict = None, data: Optional[AnyByStrDict] = None,
**kwargs, **kwargs,
) -> Worker: ) -> Worker:
"""Copy a template to a destination, from zero. """Copy a template to a destination, from zero.
@ -842,7 +844,7 @@ def run_copy(
def run_update( def run_update(
dst_path: StrOrPath = ".", dst_path: StrOrPath = ".",
data: AnyByStrDict = None, data: Optional[AnyByStrDict] = None,
**kwargs, **kwargs,
) -> Worker: ) -> Worker:
"""Update a subproject, from its template. """Update a subproject, from its template.
@ -861,7 +863,7 @@ def run_update(
def run_auto( def run_auto(
src_path: OptStr = None, src_path: OptStr = None,
dst_path: StrOrPath = ".", dst_path: StrOrPath = ".",
data: AnyByStrDict = None, data: Optional[AnyByStrDict] = None,
**kwargs, **kwargs,
) -> Worker: ) -> Worker:
"""Generate or update a subproject. """Generate or update a subproject.

View File

@ -39,7 +39,7 @@ class Subproject:
answers_relpath: Path = Path(".copier-answers.yml") answers_relpath: Path = Path(".copier-answers.yml")
def is_dirty(self) -> bool: def is_dirty(self) -> bool:
"""Indicates if the local template root is dirty. """Indicate if the local template root is dirty.
Only applicable for VCS-tracked templates. Only applicable for VCS-tracked templates.
""" """
@ -50,7 +50,7 @@ class Subproject:
@property @property
def _raw_answers(self) -> AnyByStrDict: def _raw_answers(self) -> AnyByStrDict:
"""The last answers, loaded raw as yaml.""" """Get last answers, loaded raw as yaml."""
try: try:
return yaml.safe_load( return yaml.safe_load(
(self.local_abspath / self.answers_relpath).read_text() (self.local_abspath / self.answers_relpath).read_text()

View File

@ -360,7 +360,7 @@ class Template:
@cached_property @cached_property
def min_copier_version(self) -> Optional[Version]: def min_copier_version(self) -> Optional[Version]:
"""Gets minimal copier version for the template and validates it. """Get minimal copier version for the template and validates it.
See [min_copier_version][]. See [min_copier_version][].
""" """

View File

@ -18,16 +18,18 @@ from pydantic import StrictBool
from .types import IntSeq from .types import IntSeq
try: # TODO Remove condition when dropping python 3.8 support
from importlib.metadata import version if sys.version_info < (3, 8):
except ImportError:
# Python < 3.8
from importlib_metadata import version from importlib_metadata import version
else:
from importlib.metadata import version
colorama.init() colorama.init()
class Style: class Style:
"""Common color styles."""
OK: IntSeq = [colorama.Fore.GREEN, colorama.Style.BRIGHT] OK: IntSeq = [colorama.Fore.GREEN, colorama.Style.BRIGHT]
WARNING: IntSeq = [colorama.Fore.YELLOW, colorama.Style.BRIGHT] WARNING: IntSeq = [colorama.Fore.YELLOW, colorama.Style.BRIGHT]
IGNORE: IntSeq = [colorama.Fore.CYAN] IGNORE: IntSeq = [colorama.Fore.CYAN]
@ -63,6 +65,7 @@ def printf(
quiet: Union[bool, StrictBool] = False, quiet: Union[bool, StrictBool] = False,
file_: TextIO = sys.stdout, file_: TextIO = sys.stdout,
) -> Optional[str]: ) -> Optional[str]:
"""Print string with common format."""
if quiet: if quiet:
return None # HACK: Satisfy MyPy return None # HACK: Satisfy MyPy
_msg = str(msg) _msg = str(msg)
@ -78,6 +81,7 @@ def printf(
def printf_exception( def printf_exception(
e: Exception, action: str, msg: str = "", indent: int = 0, quiet: bool = False e: Exception, action: str, msg: str = "", indent: int = 0, quiet: bool = False
) -> None: ) -> None:
"""Print exception with common format."""
if not quiet: if not quiet:
print("", file=sys.stderr) print("", file=sys.stderr)
printf(action, msg=msg, style=Style.DANGER, indent=indent, file_=sys.stderr) printf(action, msg=msg, style=Style.DANGER, indent=indent, file_=sys.stderr)
@ -114,6 +118,7 @@ def cast_str_to_bool(value: Any) -> bool:
def copy_file(src_path: Path, dst_path: Path, follow_symlinks: bool = True) -> None: def copy_file(src_path: Path, dst_path: Path, follow_symlinks: bool = True) -> None:
"""Copy one file to another place."""
shutil.copy2(src_path, dst_path, follow_symlinks=follow_symlinks) shutil.copy2(src_path, dst_path, follow_symlinks=follow_symlinks)
@ -166,6 +171,7 @@ class TemporaryDirectory(tempfile.TemporaryDirectory):
warnings.warn(warn_message, ResourceWarning) warnings.warn(warn_message, ResourceWarning)
def cleanup(self): def cleanup(self):
"""Remove directory safely."""
if self._finalizer.detach(): if self._finalizer.detach():
self._robust_cleanup(self.name) self._robust_cleanup(self.name)

View File

@ -40,11 +40,14 @@ Env = Mapping[str, str]
class AllowArbitraryTypes: class AllowArbitraryTypes:
"""Allow any type for this class."""
arbitrary_types_allowed = True arbitrary_types_allowed = True
# Validators # Validators
def path_is_absolute(value: Path) -> Path: def path_is_absolute(value: Path) -> Path:
"""Require absolute paths in an argument."""
if not value.is_absolute(): if not value.is_absolute():
from .errors import PathNotAbsoluteError from .errors import PathNotAbsoluteError
@ -53,6 +56,7 @@ def path_is_absolute(value: Path) -> Path:
def path_is_relative(value: Path) -> Path: def path_is_relative(value: Path) -> Path:
"""Require relative paths in an argument."""
if value.is_absolute(): if value.is_absolute():
from .errors import PathNotRelativeError from .errors import PathNotRelativeError
@ -67,12 +71,16 @@ if TYPE_CHECKING:
else: else:
class AbsolutePath(Path): class AbsolutePath(Path):
"""Require absolute paths in an argument."""
@classmethod @classmethod
def __get_validators__(cls) -> "CallableGenerator": def __get_validators__(cls) -> "CallableGenerator":
yield path_validator yield path_validator
yield path_is_absolute yield path_is_absolute
class RelativePath(Path): class RelativePath(Path):
"""Require relative paths in an argument."""
@classmethod @classmethod
def __get_validators__(cls) -> "CallableGenerator": def __get_validators__(cls) -> "CallableGenerator":
yield path_validator yield path_validator

View File

@ -135,6 +135,7 @@ class AnswersMap:
) )
def old_commit(self) -> OptStr: def old_commit(self) -> OptStr:
"""Commit when the project was updated from this template the last time."""
return self.last.get("_commit") return self.last.get("_commit")

View File

@ -56,7 +56,7 @@ def is_git_bundle(path: Path) -> bool:
def get_repo(url: str) -> OptStr: def get_repo(url: str) -> OptStr:
"""Transforms `url` into a git-parseable origin URL. """Transform `url` into a git-parseable origin URL.
Args: Args:
url: url:
@ -133,7 +133,6 @@ def clone(url: str, ref: OptStr = None) -> str:
ref: ref:
Reference to checkout. For Git repos, defaults to `HEAD`. Reference to checkout. For Git repos, defaults to `HEAD`.
""" """
location = mkdtemp(prefix=f"{__name__}.clone.") location = mkdtemp(prefix=f"{__name__}.clone.")
_clone = git["clone", "--no-checkout", url, location] _clone = git["clone", "--no-checkout", url, location]
# Faster clones if possible # Faster clones if possible

14
default.nix Normal file
View File

@ -0,0 +1,14 @@
(
import
(
let
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
in
fetchTarball {
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
sha256 = lock.nodes.flake-compat.locked.narHash;
}
)
{src = ./.;}
)
.defaultNix

View File

@ -1,3 +1,4 @@
"""Development helper tasks."""
import os import os
import shutil import shutil
from pathlib import Path from pathlib import Path
@ -5,9 +6,7 @@ from subprocess import check_call
def clean(): def clean():
""" """Clean build, test or other process artifacts from the project workspace."""
Clean build, test or other process artefacts from the project workspace
"""
build_artefacts = ( build_artefacts = (
"build/", "build/",
"dist/", "dist/",
@ -33,7 +32,7 @@ def clean():
def dev_setup(): def dev_setup():
"""Setup a development environment.""" """Set up a development environment."""
# Gitpod sets PIP_USER=yes, which breaks poetry # Gitpod sets PIP_USER=yes, which breaks poetry
env = dict(os.environ, PIP_USER="no") env = dict(os.environ, PIP_USER="no")
check_call(["poetry", "install", "--with", "docs"], env=env) check_call(["poetry", "install", "--with", "docs"], env=env)

302
flake.lock generated Normal file
View File

@ -0,0 +1,302 @@
{
"nodes": {
"devenv": {
"inputs": {
"flake-compat": "flake-compat",
"nix": "nix",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks"
},
"locked": {
"lastModified": 1671716968,
"narHash": "sha256-LThNtwAXH/0KVMgyTFBwl93ktuWpWTZhCh6NBlydBbQ=",
"owner": "cachix",
"repo": "devenv",
"rev": "ba6818f4c39fd95aebdb6dc441401f2b60484652",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "v0.5",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1668681692,
"narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "009399224d5e398d03b22badca40a37ac85412a1",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1668681692,
"narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "009399224d5e398d03b22badca40a37ac85412a1",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_3": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"devenv",
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1660459072,
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"lowdown-src": {
"flake": false,
"locked": {
"lastModified": 1633514407,
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
"owner": "kristapsdz",
"repo": "lowdown",
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
"type": "github"
},
"original": {
"owner": "kristapsdz",
"repo": "lowdown",
"type": "github"
}
},
"nix": {
"inputs": {
"lowdown-src": "lowdown-src",
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1671638174,
"narHash": "sha256-FeEmVix8l/HglWtRgeHOfjqEm2etvp+MLYd1C/raq3Y=",
"owner": "domenkozar",
"repo": "nix",
"rev": "51b770e985f9e1b84fb5e03a983ef1e19f18c3e9",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "relaxed-flakes",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1671458120,
"narHash": "sha256-2+k/OONN4OF21TeoNjKB5sXVZv6Zvm/uEyQIW9OYCg8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e37ef84b478fa8da0ced96522adfd956fde9047a",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1671271954,
"narHash": "sha256-cSvu+bnvN08sOlTBWbBrKaBHQZq8mvk8bgpt0ZJ2Snc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d513b448cc2a6da2c8803e3c197c9fc7e67b19e3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1673800717,
"narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1673940514,
"narHash": "sha256-jM1c0V6EBqea9fLMyrfIrB4gbnXrW0gVswYjGdu0JiE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "29a7c9631b7405dcf480138f417682af8a4069c7",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"poetry2nix": {
"inputs": {
"flake-utils": "flake-utils_3",
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1673926875,
"narHash": "sha256-QOsT76Al0Igpo0u5vtQJuDSOxrocX3sTD523pLPEklc=",
"owner": "nix-community",
"repo": "poetry2nix",
"rev": "a5c454a834cd290dd4d33102ab8b4aa37d850e65",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "poetry2nix",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
"flake-utils": "flake-utils",
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1671452357,
"narHash": "sha256-HqzXiQEegpRQ4VEl9pEPgHSIxhJrNJ27HfN1wOc7w2E=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "200790e9c77064c53eaf95805b013d96615ecc27",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"flake-compat": "flake-compat_2",
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs_2",
"poetry2nix": "poetry2nix"
}
}
},
"root": "root",
"version": 7
}

110
flake.nix Normal file
View File

@ -0,0 +1,110 @@
{
inputs = {
devenv.url = "github:cachix/devenv/v0.5";
flake-compat = {
url = github:edolstra/flake-compat;
flake = false;
};
flake-utils.url = github:numtide/flake-utils;
nixpkgs.url = github:NixOS/nixpkgs/nixos-22.11;
poetry2nix.url = github:nix-community/poetry2nix;
};
outputs = inputs:
with inputs;
flake-utils.lib.eachDefaultSystem (system: let
pkgs = import nixpkgs {
inherit system;
overlays = [poetry2nix.overlay];
};
lastRelease = (pkgs.lib.importTOML ./pyproject.toml).tool.commitizen.version;
version = "${lastRelease}.dev${self.sourceInfo.lastModifiedDate}+nix-git-${self.sourceInfo.shortRev or "dirty"}";
# Builders
copierApp = pkgs.poetry2nix.mkPoetryApplication {
inherit version;
name = "copier-${version}";
POETRY_DYNAMIC_VERSIONING_BYPASS = version;
projectDir = ./.;
overrides = pkgs.poetry2nix.overrides.withDefaults (final: prev: {
pydantic = prev.pydantic.overrideAttrs (old: {
buildInputs = old.buildInputs ++ [pkgs.libxcrypt];
});
});
# Test configuration
checkInputs = [pkgs.git];
pythonImportsCheck = ["copier"];
doCheck = true;
installCheckPhase = ''
patchShebangs tests
env \
GIT_AUTHOR_EMAIL=copier@example.com \
GIT_AUTHOR_NAME=copier \
GIT_COMMITTER_EMAIL=copier@example.com \
GIT_COMMITTER_NAME=copier \
PATH=$out/bin:$PATH \
POETRY_VIRTUALENVS_PATH=$NIX_BUILD_TOP/virtualenvs \
PYTHONOPTIMIZE= \
pytest --color=yes -m 'not impure'
'';
};
copierModule = pkgs.python3.pkgs.toPythonModule copierApp;
in rec {
devShells.default = devenv.lib.mkShell {
inherit inputs pkgs;
modules = [
{
packages = with pkgs; [
# Essential dev tools
(pkgs.python3.withPackages (ps:
with ps; [
poetry
poetry-dynamic-versioning
]))
# IDE integration tools
alejandra
black
commitizen
isort
mypy
nodePackages.prettier
];
difftastic.enable = true;
pre-commit.hooks = {
alejandra.enable = true;
black.enable = true;
commitizen.enable = true;
editorconfig-checker.enable = true;
editorconfig-checker.excludes = [
"\.md$"
"\.noeof\."
"\.bundle$"
];
flake8.enable = true;
isort.enable = true;
prettier.enable = true;
prettier.excludes = [
# Those files have wrong syntax and would fail
"^tests/demo_invalid/copier.yml$"
"^tests/demo_transclude_invalid(_multi)?/demo/copier.yml$"
# HACK https://github.com/prettier/prettier/issues/9430
"^tests/demo"
];
};
}
];
};
packages.default = copierModule;
apps =
builtins.mapAttrs
(name: value: flake-utils.lib.mkApp {drv = value;})
packages;
checks =
packages
// {
devenv-ci = devShells.default.ci;
};
});
}

View File

@ -1,145 +1,156 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#" xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 191.55487 81.254478" viewBox="0 0 191.55487 81.254478"
version="1.1" version="1.1"
id="svg3773" id="svg3773"
sodipodi:docname="copier-logo.svg" sodipodi:docname="copier-logo.svg"
width="191.55487" width="191.55487"
height="81.254478" height="81.254478"
inkscape:version="0.92.2 5c3e80d, 2017-08-06" inkscape:version="0.92.2 5c3e80d, 2017-08-06"
inkscape:export-filename="/Users/jps/Projects/copier/copier-logo.png" inkscape:export-filename="/Users/jps/Projects/copier/copier-logo.png"
inkscape:export-xdpi="118.14733" inkscape:export-xdpi="118.14733"
inkscape:export-ydpi="118.14733"> inkscape:export-ydpi="118.14733"
<metadata >
id="metadata3779"> <metadata id="metadata3779">
<rdf:RDF> <rdf:RDF>
<cc:Work <cc:Work rdf:about="">
rdf:about=""> <dc:format>image/svg+xml</dc:format>
<dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:type <dc:title />
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> </cc:Work>
<dc:title></dc:title> </rdf:RDF>
</cc:Work> </metadata>
</rdf:RDF> <defs id="defs3777" />
</metadata> <sodipodi:namedview
<defs pagecolor="#ffffff"
id="defs3777" /> bordercolor="#666666"
<sodipodi:namedview borderopacity="1"
pagecolor="#ffffff" objecttolerance="10"
bordercolor="#666666" gridtolerance="10"
borderopacity="1" guidetolerance="10"
objecttolerance="10" inkscape:pageopacity="0"
gridtolerance="10" inkscape:pageshadow="2"
guidetolerance="10" inkscape:window-width="640"
inkscape:pageopacity="0" inkscape:window-height="480"
inkscape:pageshadow="2" id="namedview3775"
inkscape:window-width="640" showgrid="false"
inkscape:window-height="480" inkscape:showpageshadow="false"
id="namedview3775" fit-margin-top="2"
showgrid="false" fit-margin-left="2"
inkscape:showpageshadow="false" fit-margin-bottom="2"
fit-margin-top="2" fit-margin-right="2"
fit-margin-left="2" inkscape:zoom="1.98"
fit-margin-bottom="2" inkscape:cx="54.825884"
fit-margin-right="2" inkscape:cy="5.6908511"
inkscape:zoom="1.98" inkscape:current-layer="svg3773"
inkscape:cx="54.825884" />
inkscape:cy="5.6908511" <g id="g4609" transform="translate(-46.017835,-64.600465)">
inkscape:current-layer="svg3773" /> <path
<g id="path3789"
id="g4609" style="fill:#dddddd;fill-opacity:1;stroke:#dddddd;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
transform="translate(-46.017835,-64.600465)"> d="m 123.24991,75.191221 17.81702,13.845468 c 4.03331,3.134247 4.75709,8.904518 1.62285,12.937821 l -27.45681,35.33276 c -3.13425,4.03331 -8.90452,4.7571 -12.93783,1.62285 L 84.478114,125.08465 c -4.03332,-3.13424 -4.7571,-8.90452 -1.62285,-12.93783 L 110.31207,76.814066 c 3.13425,-4.033308 8.90452,-4.757096 12.93784,-1.622845 z m -19.88432,-4.519638 20.53044,9.361854 c 4.64755,2.119276 6.68294,7.566941 4.56367,12.214494 l -18.5654,40.713719 c -2.11928,4.64755 -7.56694,6.68295 -12.214496,4.56367 l -20.530438,-9.36185 c -4.647556,-2.11927 -6.682954,-7.56694 -4.563679,-12.2145 L 91.151084,75.235257 c 2.11928,-4.647554 7.56695,-6.682951 12.214506,-4.563674 z m -15.929666,-0.941169 22.248486,3.76145 c 5.03647,0.851492 8.3594,5.58354 7.45052,10.609973 l -7.96203,44.032793 c -0.90888,5.02643 -5.69521,8.38748 -10.731676,7.53599 L 76.19274,131.90917 c -5.036474,-0.8515 -8.359407,-5.58354 -7.450526,-10.60997 l 7.962027,-44.032795 c 0.908882,-5.026433 5.695213,-8.387485 10.731683,-7.535991 z m -14.041662,2.732 22.546252,-0.900074 c 5.103856,-0.203752 9.376796,3.741113 9.580546,8.844992 l 1.78493,44.711238 c 0.20375,5.10387 -3.74111,9.3768 -8.844976,9.58055 l -22.546252,0.90007 c -5.103881,0.20376 -9.376811,-3.74111 -9.580563,-8.84498 L 64.549271,82.042976 c -0.203754,-5.103879 3.741113,-9.376809 8.844991,-9.580562 z m -15.955511,6.057349 21.50141,-6.843458 c 4.867353,-1.549178 10.033003,1.122133 11.582183,5.989487 l 13.571186,42.639218 c 1.54918,4.86735 -1.12213,10.033 -5.989476,11.58218 l -21.501411,6.84346 c -4.867356,1.54918 -10.033012,-1.12213 -11.582189,-5.98948 L 51.449263,90.101952 c -1.549179,-4.867354 1.122131,-10.03301 5.989488,-11.582189 z"
<path inkscape:connector-curvature="0"
id="path3789" />
style="fill:#dddddd;fill-opacity:1;stroke:#dddddd;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" <g id="g3771" transform="translate(-0.801806,-1.5851988)">
d="m 123.24991,75.191221 17.81702,13.845468 c 4.03331,3.134247 4.75709,8.904518 1.62285,12.937821 l -27.45681,35.33276 c -3.13425,4.03331 -8.90452,4.7571 -12.93783,1.62285 L 84.478114,125.08465 c -4.03332,-3.13424 -4.7571,-8.90452 -1.62285,-12.93783 L 110.31207,76.814066 c 3.13425,-4.033308 8.90452,-4.757096 12.93784,-1.622845 z m -19.88432,-4.519638 20.53044,9.361854 c 4.64755,2.119276 6.68294,7.566941 4.56367,12.214494 l -18.5654,40.713719 c -2.11928,4.64755 -7.56694,6.68295 -12.214496,4.56367 l -20.530438,-9.36185 c -4.647556,-2.11927 -6.682954,-7.56694 -4.563679,-12.2145 L 91.151084,75.235257 c 2.11928,-4.647554 7.56695,-6.682951 12.214506,-4.563674 z m -15.929666,-0.941169 22.248486,3.76145 c 5.03647,0.851492 8.3594,5.58354 7.45052,10.609973 l -7.96203,44.032793 c -0.90888,5.02643 -5.69521,8.38748 -10.731676,7.53599 L 76.19274,131.90917 c -5.036474,-0.8515 -8.359407,-5.58354 -7.450526,-10.60997 l 7.962027,-44.032795 c 0.908882,-5.026433 5.695213,-8.387485 10.731683,-7.535991 z m -14.041662,2.732 22.546252,-0.900074 c 5.103856,-0.203752 9.376796,3.741113 9.580546,8.844992 l 1.78493,44.711238 c 0.20375,5.10387 -3.74111,9.3768 -8.844976,9.58055 l -22.546252,0.90007 c -5.103881,0.20376 -9.376811,-3.74111 -9.580563,-8.84498 L 64.549271,82.042976 c -0.203754,-5.103879 3.741113,-9.376809 8.844991,-9.580562 z m -15.955511,6.057349 21.50141,-6.843458 c 4.867353,-1.549178 10.033003,1.122133 11.582183,5.989487 l 13.571186,42.639218 c 1.54918,4.86735 -1.12213,10.033 -5.989476,11.58218 l -21.501411,6.84346 c -4.867356,1.54918 -10.033012,-1.12213 -11.582189,-5.98948 L 51.449263,90.101952 c -1.549179,-4.867354 1.122131,-10.03301 5.989488,-11.582189 z" <path
inkscape:connector-curvature="0" /> id="path3761"
<g style="fill:#3bb3c4;fill-opacity:1;stroke:#ffffff;stroke-width:2.42168283;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="g3771" d="m 58.240557,80.104962 21.50141,-6.843458 c 4.867351,-1.549178 10.033006,1.122133 11.582184,5.989487 l 13.571189,42.639219 c 1.54918,4.86735 -1.12213,10.033 -5.989477,11.58218 l -21.501414,6.84346 c -4.867356,1.54918 -10.033012,-1.12213 -11.582189,-5.98948 L 52.251069,91.687151 C 50.70189,86.819797 53.3732,81.654141 58.240557,80.104962 Z"
transform="translate(-0.801806,-1.5851988)"> inkscape:connector-curvature="0"
<path />
id="path3761" <path
style="fill:#3bb3c4;fill-opacity:1;stroke:#ffffff;stroke-width:2.42168283;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="path3763"
d="m 58.240557,80.104962 21.50141,-6.843458 c 4.867351,-1.549178 10.033006,1.122133 11.582184,5.989487 l 13.571189,42.639219 c 1.54918,4.86735 -1.12213,10.033 -5.989477,11.58218 l -21.501414,6.84346 c -4.867356,1.54918 -10.033012,-1.12213 -11.582189,-5.98948 L 52.251069,91.687151 C 50.70189,86.819797 53.3732,81.654141 58.240557,80.104962 Z" style="fill:#b74f8e;fill-opacity:1;stroke:#ffffff;stroke-width:2.42168283;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" /> d="m 74.196068,74.047613 22.546257,-0.900074 c 5.103855,-0.203752 9.376795,3.741113 9.580545,8.844992 l 1.78493,44.711239 c 0.20375,5.10387 -3.74111,9.3768 -8.844979,9.58055 l -22.546253,0.90007 c -5.103881,0.20376 -9.376811,-3.74111 -9.580563,-8.84498 L 65.351077,83.628175 c -0.203754,-5.103879 3.741113,-9.376809 8.844991,-9.580562 z"
<path inkscape:connector-curvature="0"
id="path3763" />
style="fill:#b74f8e;fill-opacity:1;stroke:#ffffff;stroke-width:2.42168283;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" <path
d="m 74.196068,74.047613 22.546257,-0.900074 c 5.103855,-0.203752 9.376795,3.741113 9.580545,8.844992 l 1.78493,44.711239 c 0.20375,5.10387 -3.74111,9.3768 -8.844979,9.58055 l -22.546253,0.90007 c -5.103881,0.20376 -9.376811,-3.74111 -9.580563,-8.84498 L 65.351077,83.628175 c -0.203754,-5.103879 3.741113,-9.376809 8.844991,-9.580562 z" id="path3765"
inkscape:connector-curvature="0" /> style="fill:#e91414;fill-opacity:1;stroke:#ffffff;stroke-width:2.42160416;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
<path d="m 88.237734,71.315613 22.248486,3.76145 c 5.03647,0.851492 8.3594,5.58354 7.45052,10.609973 l -7.96203,44.032794 c -0.90888,5.02643 -5.69521,8.38748 -10.731683,7.53599 l -22.248481,-3.76145 c -5.036474,-0.8515 -8.359407,-5.58354 -7.450526,-10.60997 l 7.962027,-44.032796 c 0.908882,-5.026433 5.695213,-8.387485 10.731687,-7.535991 z"
id="path3765" inkscape:connector-curvature="0"
style="fill:#e91414;fill-opacity:1;stroke:#ffffff;stroke-width:2.42160416;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
d="m 88.237734,71.315613 22.248486,3.76145 c 5.03647,0.851492 8.3594,5.58354 7.45052,10.609973 l -7.96203,44.032794 c -0.90888,5.02643 -5.69521,8.38748 -10.731683,7.53599 l -22.248481,-3.76145 c -5.036474,-0.8515 -8.359407,-5.58354 -7.450526,-10.60997 l 7.962027,-44.032796 c 0.908882,-5.026433 5.695213,-8.387485 10.731687,-7.535991 z" <path
inkscape:connector-curvature="0" /> id="path3767"
<path style="fill:#e57813;fill-opacity:1;stroke:#ffffff;stroke-width:2.42168283;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3767" d="m 104.1674,72.256782 20.53044,9.361854 c 4.64755,2.119276 6.68294,7.566941 4.56367,12.214494 l -18.5654,40.71372 c -2.11928,4.64755 -7.56694,6.68295 -12.214496,4.56367 l -20.530442,-9.36185 c -4.647556,-2.11927 -6.682954,-7.56694 -4.563679,-12.2145 L 91.952891,76.820456 c 2.119279,-4.647554 7.566948,-6.682951 12.214509,-4.563674 z"
style="fill:#e57813;fill-opacity:1;stroke:#ffffff;stroke-width:2.42168283;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" inkscape:connector-curvature="0"
d="m 104.1674,72.256782 20.53044,9.361854 c 4.64755,2.119276 6.68294,7.566941 4.56367,12.214494 l -18.5654,40.71372 c -2.11928,4.64755 -7.56694,6.68295 -12.214496,4.56367 l -20.530442,-9.36185 c -4.647556,-2.11927 -6.682954,-7.56694 -4.563679,-12.2145 L 91.952891,76.820456 c 2.119279,-4.647554 7.566948,-6.682951 12.214509,-4.563674 z" />
inkscape:connector-curvature="0" /> <path
<path id="path3769"
id="path3769" style="fill:#f8c301;fill-opacity:0.9905213;stroke:#ffffff;stroke-width:2.42168283;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
style="fill:#f8c301;fill-opacity:0.9905213;stroke:#ffffff;stroke-width:2.42168283;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 124.05172,76.77642 17.81702,13.845468 c 4.03331,3.134247 4.75709,8.904518 1.62285,12.937822 l -27.45681,35.33276 c -3.13425,4.03331 -8.90452,4.7571 -12.93783,1.62285 L 85.279918,126.66985 c -4.033313,-3.13424 -4.757099,-8.90452 -1.622847,-12.93783 L 111.11388,78.399265 c 3.13425,-4.033308 8.90452,-4.757096 12.93784,-1.622845 z"
d="m 124.05172,76.77642 17.81702,13.845468 c 4.03331,3.134247 4.75709,8.904518 1.62285,12.937822 l -27.45681,35.33276 c -3.13425,4.03331 -8.90452,4.7571 -12.93783,1.62285 L 85.279918,126.66985 c -4.033313,-3.13424 -4.757099,-8.90452 -1.622847,-12.93783 L 111.11388,78.399265 c 3.13425,-4.033308 8.90452,-4.757096 12.93784,-1.622845 z" inkscape:connector-curvature="0"
inkscape:connector-curvature="0" /> />
</g>
</g> </g>
</g> <g id="g4656" transform="translate(-22)">
<g <g
id="g4656" transform="translate(40)"
transform="translate(-22)"> id="text4617"
<g style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;line-height:1.25;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
transform="translate(40)" aria-label="Copier"
id="text4617" >
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;line-height:1.25;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" <path
aria-label="Copier"> inkscape:connector-curvature="0"
<path id="path4619"
inkscape:connector-curvature="0" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4619" d="m 80.853458,28.297318 v -2.525313 h 2.920845 v 7.34775 h -2.981696 q -1.125742,-2.205847 -3.088185,-3.438078 -1.962443,-1.232231 -4.305204,-1.232231 -2.266697,0 -4.046587,1.201806 -1.779889,1.186593 -2.78393,3.194674 -0.988827,1.992868 -0.988827,4.28999 0,2.373187 1.034465,4.381268 1.049679,2.00808 2.875207,3.209886 1.825528,1.201806 4.0618,1.201806 2.753505,0 4.868074,-1.6734 2.11457,-1.688614 3.088185,-4.776799 l 2.920845,1.551699 q -1.186593,3.833609 -4.198714,5.948179 -2.996909,2.099357 -7.043496,2.099357 -3.331588,0 -5.917753,-1.582124 -2.586165,-1.597337 -4.031374,-4.274778 -1.429997,-2.692654 -1.429997,-5.902541 0,-3.483716 1.460422,-6.206795 1.475635,-2.723079 4.0618,-4.213927 2.586165,-1.506061 5.917753,-1.506061 2.236272,0 4.122651,0.745424 1.886379,0.745424 3.483716,2.160208 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
d="m 80.853458,28.297318 v -2.525313 h 2.920845 v 7.34775 h -2.981696 q -1.125742,-2.205847 -3.088185,-3.438078 -1.962443,-1.232231 -4.305204,-1.232231 -2.266697,0 -4.046587,1.201806 -1.779889,1.186593 -2.78393,3.194674 -0.988827,1.992868 -0.988827,4.28999 0,2.373187 1.034465,4.381268 1.049679,2.00808 2.875207,3.209886 1.825528,1.201806 4.0618,1.201806 2.753505,0 4.868074,-1.6734 2.11457,-1.688614 3.088185,-4.776799 l 2.920845,1.551699 q -1.186593,3.833609 -4.198714,5.948179 -2.996909,2.099357 -7.043496,2.099357 -3.331588,0 -5.917753,-1.582124 -2.586165,-1.597337 -4.031374,-4.274778 -1.429997,-2.692654 -1.429997,-5.902541 0,-3.483716 1.460422,-6.206795 1.475635,-2.723079 4.0618,-4.213927 2.586165,-1.506061 5.917753,-1.506061 2.236272,0 4.122651,0.745424 1.886379,0.745424 3.483716,2.160208 z" /> <path
<path inkscape:connector-curvature="0"
inkscape:connector-curvature="0" id="path4621"
id="path4621" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 103.91596,40.726121 q 0,2.327549 -1.11053,4.259566 -1.11053,1.932017 -3.027332,3.057759 -1.916804,1.125742 -4.244352,1.125742 -2.403612,0 -4.335629,-1.125742 -1.932017,-1.125742 -3.012121,-3.057759 -1.064892,-1.932017 -1.064892,-4.259566 0,-2.434037 1.064892,-4.350841 1.080104,-1.916805 2.996908,-2.996909 1.916805,-1.095317 4.350842,-1.095317 2.357974,0 4.274778,1.156168 1.916806,1.140955 3.012126,3.072972 1.09531,1.932017 1.09531,4.213927 z m -8.382214,5.385308 q 1.399571,0 2.570952,-0.730211 1.17138,-0.730211 1.825528,-1.962443 0.654144,-1.232231 0.654144,-2.692654 0,-1.429996 -0.654144,-2.662228 -0.654148,-1.232231 -1.825528,-1.977655 -1.171381,-0.760637 -2.601378,-0.760637 -1.399571,0 -2.570951,0.745424 -1.171381,0.730211 -1.840741,1.977655 -0.66936,1.247445 -0.66936,2.677441 0,1.429997 0.66936,2.677442 0.66936,1.247444 1.855953,1.977655 1.186593,0.730211 2.586165,0.730211 z"
d="m 103.91596,40.726121 q 0,2.327549 -1.11053,4.259566 -1.11053,1.932017 -3.027332,3.057759 -1.916804,1.125742 -4.244352,1.125742 -2.403612,0 -4.335629,-1.125742 -1.932017,-1.125742 -3.012121,-3.057759 -1.064892,-1.932017 -1.064892,-4.259566 0,-2.434037 1.064892,-4.350841 1.080104,-1.916805 2.996908,-2.996909 1.916805,-1.095317 4.350842,-1.095317 2.357974,0 4.274778,1.156168 1.916806,1.140955 3.012126,3.072972 1.09531,1.932017 1.09531,4.213927 z m -8.382214,5.385308 q 1.399571,0 2.570952,-0.730211 1.17138,-0.730211 1.825528,-1.962443 0.654144,-1.232231 0.654144,-2.692654 0,-1.429996 -0.654144,-2.662228 -0.654148,-1.232231 -1.825528,-1.977655 -1.171381,-0.760637 -2.601378,-0.760637 -1.399571,0 -2.570951,0.745424 -1.171381,0.730211 -1.840741,1.977655 -0.66936,1.247445 -0.66936,2.677441 0,1.429997 0.66936,2.677442 0.66936,1.247444 1.855953,1.977655 1.186593,0.730211 2.586165,0.730211 z" /> />
<path <path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
id="path4623" id="path4623"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 124.95517,40.786972 q 0,2.236272 -1.03446,4.183502 -1.03447,1.94723 -2.82957,3.072972 -1.77989,1.125742 -3.95531,1.125742 -3.20989,0 -5.6135,-2.464462 v 6.358922 h 3.11861 v 2.799143 h -9.15807 v -2.799143 h 2.89042 V 35.523367 h -2.67744 v -2.814356 h 5.82648 v 2.555739 q 1.18659,-1.399571 2.61659,-2.084144 1.42999,-0.684573 3.05776,-0.684573 2.26669,0 4.03137,1.125742 1.76468,1.11053 2.73829,3.012121 0.98883,1.901592 0.98883,4.153076 z m -7.86498,5.50701 q 1.30829,0 2.35797,-0.760637 1.04968,-0.760637 1.64298,-2.068932 0.59329,-1.308295 0.59329,-2.768717 0,-1.475635 -0.65414,-2.707867 -0.65415,-1.247444 -1.79511,-1.977655 -1.14095,-0.745424 -2.57095,-0.745424 -1.49085,0 -2.67744,0.791062 -1.18659,0.77585 -1.84074,2.068932 -0.65415,1.293082 -0.65415,2.844781 0,2.494888 1.56691,3.909673 1.58213,1.414784 4.03138,1.414784 z" /> d="m 124.95517,40.786972 q 0,2.236272 -1.03446,4.183502 -1.03447,1.94723 -2.82957,3.072972 -1.77989,1.125742 -3.95531,1.125742 -3.20989,0 -5.6135,-2.464462 v 6.358922 h 3.11861 v 2.799143 h -9.15807 v -2.799143 h 2.89042 V 35.523367 h -2.67744 v -2.814356 h 5.82648 v 2.555739 q 1.18659,-1.399571 2.61659,-2.084144 1.42999,-0.684573 3.05776,-0.684573 2.26669,0 4.03137,1.125742 1.76468,1.11053 2.73829,3.012121 0.98883,1.901592 0.98883,4.153076 z m -7.86498,5.50701 q 1.30829,0 2.35797,-0.760637 1.04968,-0.760637 1.64298,-2.068932 0.59329,-1.308295 0.59329,-2.768717 0,-1.475635 -0.65414,-2.707867 -0.65415,-1.247444 -1.79511,-1.977655 -1.14095,-0.745424 -2.57095,-0.745424 -1.49085,0 -2.67744,0.791062 -1.18659,0.77585 -1.84074,2.068932 -0.65415,1.293082 -0.65415,2.844781 0,2.494888 1.56691,3.909673 1.58213,1.414784 4.03138,1.414784 z"
<path />
inkscape:connector-curvature="0" <path
id="path4625" inkscape:connector-curvature="0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="path4625"
d="m 132.7593,27.323703 q 0,0.77585 -0.56287,1.338721 -0.56287,0.562871 -1.33872,0.562871 -0.77585,0 -1.32351,-0.562871 -0.54766,-0.562871 -0.54766,-1.338721 0,-0.775849 0.54766,-1.33872 0.54766,-0.562871 1.32351,-0.562871 0.80627,0 1.35393,0.547658 0.54766,0.547659 0.54766,1.353933 z m 2.25149,18.574748 v 2.799143 h -8.07797 v -2.799143 h 2.49489 V 35.523367 h -2.49489 v -2.814356 h 5.64393 v 13.18944 z" /> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
<path d="m 132.7593,27.323703 q 0,0.77585 -0.56287,1.338721 -0.56287,0.562871 -1.33872,0.562871 -0.77585,0 -1.32351,-0.562871 -0.54766,-0.562871 -0.54766,-1.338721 0,-0.775849 0.54766,-1.33872 0.54766,-0.562871 1.32351,-0.562871 0.80627,0 1.35393,0.547658 0.54766,0.547659 0.54766,1.353933 z m 2.25149,18.574748 v 2.799143 h -8.07797 v -2.799143 h 2.49489 V 35.523367 h -2.49489 v -2.814356 h 5.64393 v 13.18944 z"
inkscape:connector-curvature="0" />
id="path4627" <path
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" inkscape:connector-curvature="0"
d="m 153.7833,41.501971 h -13.40242 q 0.28904,2.297123 1.77989,3.635843 1.49085,1.323508 3.65106,1.323508 1.58212,0 2.93605,-0.654148 1.35394,-0.654147 2.2667,-2.00808 l 2.76872,1.247444 q -1.3083,2.038506 -3.43808,3.088185 -2.11457,1.034465 -4.60946,1.034465 -2.58616,0 -4.59424,-1.080104 -2.00808,-1.080104 -3.13383,-3.012121 -1.11053,-1.932017 -1.11053,-4.350842 0,-2.403611 1.09532,-4.335629 1.11053,-1.932017 3.02734,-3.012121 1.9168,-1.095317 4.22914,-1.095317 2.52531,0 4.39647,1.125743 1.88638,1.110529 2.96649,3.194674 1.0801,2.068931 1.17138,4.8985 z m -8.47349,-6.602326 q -1.77989,0 -3.11861,1.201806 -1.33872,1.186593 -1.71904,3.149035 h 10.00998 q -0.44117,-1.962442 -1.87117,-3.149035 -1.43,-1.201806 -3.30116,-1.201806 z" /> id="path4627"
<path style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" d="m 153.7833,41.501971 h -13.40242 q 0.28904,2.297123 1.77989,3.635843 1.49085,1.323508 3.65106,1.323508 1.58212,0 2.93605,-0.654148 1.35394,-0.654147 2.2667,-2.00808 l 2.76872,1.247444 q -1.3083,2.038506 -3.43808,3.088185 -2.11457,1.034465 -4.60946,1.034465 -2.58616,0 -4.59424,-1.080104 -2.00808,-1.080104 -3.13383,-3.012121 -1.11053,-1.932017 -1.11053,-4.350842 0,-2.403611 1.09532,-4.335629 1.11053,-1.932017 3.02734,-3.012121 1.9168,-1.095317 4.22914,-1.095317 2.52531,0 4.39647,1.125743 1.88638,1.110529 2.96649,3.194674 1.0801,2.068931 1.17138,4.8985 z m -8.47349,-6.602326 q -1.77989,0 -3.11861,1.201806 -1.33872,1.186593 -1.71904,3.149035 h 10.00998 q -0.44117,-1.962442 -1.87117,-3.149035 -1.43,-1.201806 -3.30116,-1.201806 z"
id="path4629" />
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" <path
d="m 162.01339,42.27782 v 3.620631 h 3.48372 v 2.799143 h -9.81222 v -2.799143 h 3.20989 V 35.523367 h -3.20989 v -2.814356 h 5.70478 v 3.620631 q 0.9584,-2.144996 2.37318,-2.981696 1.43,-0.851913 4.07702,-0.851913 h 0.715 v 3.179461 h -0.68458 q -2.55574,0 -3.80318,0.66936 -1.24744,0.669361 -1.65819,2.023294 -0.39553,1.353933 -0.39553,3.909672 z" /> inkscape:connector-curvature="0"
id="path4629"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#dddddd;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 162.01339,42.27782 v 3.620631 h 3.48372 v 2.799143 h -9.81222 v -2.799143 h 3.20989 V 35.523367 h -3.20989 v -2.814356 h 5.70478 v 3.620631 q 0.9584,-2.144996 2.37318,-2.981696 1.43,-0.851913 4.07702,-0.851913 h 0.715 v 3.179461 h -0.68458 q -2.55574,0 -3.80318,0.66936 -1.24744,0.669361 -1.65819,2.023294 -0.39553,1.353933 -0.39553,3.909672 z"
/>
</g>
<text
id="text4613"
y="48.697594"
x="100.25542"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;line-height:1.25;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.77889198"
xml:space="preserve"
>
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.77889198"
y="48.697594"
x="100.25542"
id="tspan4611"
sodipodi:role="line"
>
Copier
</tspan>
</text>
</g> </g>
<text
id="text4613"
y="48.697594"
x="100.25542"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;line-height:1.25;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.77889198"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.1556778px;font-family:Rockwell;-inkscape-font-specification:'Rockwell, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.77889198"
y="48.697594"
x="100.25542"
id="tspan4611"
sodipodi:role="line">Copier</tspan></text>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

1028
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@ repository = "https://github.com/copier-org/copier"
readme = "README.md" readme = "README.md"
[tool.poetry.scripts] [tool.poetry.scripts]
copier = "copier.cli:CopierApp.run" copier = "copier.__main__:copier_app_run"
[tool.poetry.urls] [tool.poetry.urls]
"Bug Tracker" = "https://github.com/copier-org/copier/issues" "Bug Tracker" = "https://github.com/copier-org/copier/issues"
@ -44,25 +44,20 @@ pyyaml-include = ">=1.2"
questionary = ">=1.8.1" questionary = ">=1.8.1"
typing-extensions = { version = ">=3.7.4,<5.0.0", python = "<3.8" } typing-extensions = { version = ">=3.7.4,<5.0.0", python = "<3.8" }
[tool.poetry.group.dev]
optional = true
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
autoflake = ">=1.4"
black = ">=22.1"
commitizen = ">=2.32.2"
flake8 = ">=4.0.1"
flake8-bugbear = ">=22.1.11"
flake8-comprehensions = ">=3.8.0"
flake8-debugger = ">=4.0.0"
flake8-simplify = ">=0.19.3"
isort = ">=5.10.1"
mypy = ">=0.931" mypy = ">=0.931"
pexpect = ">=4.8.0" pexpect = ">=4.8.0"
poethepoet = ">=0.12.3" poethepoet = ">=0.12.3"
pre-commit = ">=2.17.0" pre-commit = ">=2.17.0"
pytest = ">=7.0.1" pytest = ">=7.2.0"
pytest-cov = ">=3.0.0" pytest-cov = ">=3.0.0"
pytest-xdist = ">=2.5.0" pytest-xdist = ">=2.5.0"
types-backports = ">=0.1.3" types-backports = ">=0.1.3"
types-pyyaml = ">=6.0.4" types-pyyaml = ">=6.0.4"
types-psutil = "*"
[tool.poetry.group.docs] [tool.poetry.group.docs]
optional = true optional = true
@ -117,8 +112,15 @@ ignore_missing_imports = true
plugins = ["pydantic.mypy"] plugins = ["pydantic.mypy"]
warn_no_return = false warn_no_return = false
[tool.pydocstyle]
match_dir = "^copier"
add_ignore = ["D105", "D107"]
[tool.pytest.ini_options] [tool.pytest.ini_options]
addopts = "-n auto -ra" addopts = "-n auto -ra"
markers = [
"impure: needs network or is not 100% reproducible"
]
[tool.commitizen] [tool.commitizen]
annotated_tag = true annotated_tag = true
@ -128,5 +130,5 @@ update_changelog_on_bump = true
version = "7.1.0a0" version = "7.1.0a0"
[build-system] [build-system]
requires = ["poetry_core>=1.0.0", "poetry-dynamic-versioning"] requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]
build-backend = "poetry_dynamic_versioning.backend" build-backend = "poetry_dynamic_versioning.backend"

12
shell.nix Normal file
View File

@ -0,0 +1,12 @@
{lock ? builtins.fromJSON (builtins.readFile ./flake.lock)}:
(
import
(
fetchTarball {
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
sha256 = lock.nodes.flake-compat.locked.narHash;
}
)
{src = ./.;}
)
.shellNix

View File

@ -134,6 +134,7 @@ def test_copy(tmp_path):
assert os.path.exists(tmp_path / "py3_folder" / "thing.py") assert os.path.exists(tmp_path / "py3_folder" / "thing.py")
@pytest.mark.impure
def test_copy_repo(tmp_path): def test_copy_repo(tmp_path):
copier.copy( copier.copy(
"gh:copier-org/copier.git", "gh:copier-org/copier.git",

View File

@ -113,11 +113,11 @@ def test_pre_migration_modifies_answers(tmp_path_factory):
"copier.yml": f"""\ "copier.yml": f"""\
_envops: {BRACKET_ENVOPS_JSON} _envops: {BRACKET_ENVOPS_JSON}
best_song_list: best_song_list:
default: [paranoid android] default: [paranoid android]
_migrations: _migrations:
- version: v2 - version: v2
before: before:
- - python - - python
- -c - -c
- | - |
import sys, json, pathlib import sys, json, pathlib
@ -157,21 +157,21 @@ def test_prereleases(tmp_path: Path):
"copier.yaml": f""" "copier.yaml": f"""
_envops: {BRACKET_ENVOPS_JSON} _envops: {BRACKET_ENVOPS_JSON}
_migrations: _migrations:
- version: v1.9 - version: v1.9
before: before:
- [python, -c, "import pathlib; pathlib.Path('v1.9').touch()"] - [python, -c, "import pathlib; pathlib.Path('v1.9').touch()"]
- version: v2.dev0 - version: v2.dev0
before: before:
- [python, -c, "import pathlib; pathlib.Path('v2.dev0').touch()"] - [python, -c, "import pathlib; pathlib.Path('v2.dev0').touch()"]
- version: v2.dev2 - version: v2.dev2
before: before:
- [python, -c, "import pathlib; pathlib.Path('v2.dev2').touch()"] - [python, -c, "import pathlib; pathlib.Path('v2.dev2').touch()"]
- version: v2.a1 - version: v2.a1
before: before:
- [python, -c, "import pathlib; pathlib.Path('v2.a1').touch()"] - [python, -c, "import pathlib; pathlib.Path('v2.a1').touch()"]
- version: v2.a2 - version: v2.a2
before: before:
- [python, -c, "import pathlib; pathlib.Path('v2.a2').touch()"] - [python, -c, "import pathlib; pathlib.Path('v2.a2').touch()"]
""", """,
} }
) )

View File

@ -1,24 +1,12 @@
import platform
from pathlib import Path from pathlib import Path
from stat import S_IREAD from stat import S_IREAD
import pytest
from plumbum.cmd import git from plumbum.cmd import git
from poethepoet.app import PoeThePoet from poethepoet.app import PoeThePoet
from copier.tools import TemporaryDirectory from copier.tools import TemporaryDirectory
@pytest.mark.skipif(
condition=platform.system() == "Windows",
reason="Windows does weird things with line endings.",
)
def test_lint():
"""Ensure source code formatting"""
result = PoeThePoet(Path("."))(["lint", "--show-diff-on-failure", "--color=always"])
assert result == 0
def test_types(): def test_types():
"""Ensure source code static typing.""" """Ensure source code static typing."""
result = PoeThePoet(Path("."))(["types"]) result = PoeThePoet(Path("."))(["types"])
@ -38,5 +26,5 @@ def test_temporary_directory_with_readonly_files_deletion():
def test_temporary_directory_with_git_repo_deletion(): def test_temporary_directory_with_git_repo_deletion():
"""Ensure temporary directories containing git repositories are properly deleted, whatever the OS.""" """Ensure temporary directories containing git repositories are properly deleted, whatever the OS."""
with TemporaryDirectory() as tmp_dir: with TemporaryDirectory() as tmp_dir:
git("clone", "--depth=1", ".", Path(tmp_dir) / "repo") git("init")
assert not Path(tmp_dir).exists() assert not Path(tmp_dir).exists()

View File

@ -17,6 +17,7 @@ from copier.types import RelativePath
from .helpers import BRACKET_ENVOPS_JSON, SUFFIX_TMPL, build_file_tree from .helpers import BRACKET_ENVOPS_JSON, SUFFIX_TMPL, build_file_tree
@pytest.mark.impure
def test_updatediff(tmp_path_factory): def test_updatediff(tmp_path_factory):
src, dst = map(tmp_path_factory.mktemp, ("src", "dst")) src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
# Prepare repo bundle # Prepare repo bundle
@ -285,6 +286,7 @@ def test_updatediff(tmp_path_factory):
@pytest.mark.xfail( @pytest.mark.xfail(
condition=platform.system() == "Windows", reason="Git broken on Windows?" condition=platform.system() == "Windows", reason="Git broken on Windows?"
) )
@pytest.mark.impure
def test_commit_hooks_respected(tmp_path_factory): def test_commit_hooks_respected(tmp_path_factory):
"""Commit hooks are taken into account when producing the update diff.""" """Commit hooks are taken into account when producing the update diff."""
# Prepare source template v1 # Prepare source template v1
@ -296,9 +298,9 @@ def test_commit_hooks_respected(tmp_path_factory):
_envops: {BRACKET_ENVOPS_JSON} _envops: {BRACKET_ENVOPS_JSON}
_templates_suffix: {SUFFIX_TMPL} _templates_suffix: {SUFFIX_TMPL}
_tasks: _tasks:
- git init - git init
- pre-commit install - pre-commit install
- pre-commit run -a || true - pre-commit run -a || true
what: grog what: grog
""", """,
"[[ _copier_conf.answers_file ]].tmpl": """ "[[ _copier_conf.answers_file ]].tmpl": """
@ -306,17 +308,17 @@ def test_commit_hooks_respected(tmp_path_factory):
""", """,
".pre-commit-config.yaml": r""" ".pre-commit-config.yaml": r"""
repos: repos:
- repo: https://github.com/pre-commit/mirrors-prettier - repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.0.4 rev: v2.0.4
hooks: hooks:
- id: prettier - id: prettier
- repo: local - repo: local
hooks: hooks:
- id: forbidden-files - id: forbidden-files
name: forbidden files name: forbidden files
entry: found forbidden files; remove them entry: found forbidden files; remove them
language: fail language: fail
files: "\\.rej$" files: "\\.rej$"
""", """,
"life.yml.tmpl": """ "life.yml.tmpl": """
# Following code should be reformatted by pre-commit after copying # Following code should be reformatted by pre-commit after copying
@ -476,7 +478,7 @@ def test_skip_update(tmp_path_factory):
git("commit", "-m1") git("commit", "-m1")
git("tag", "1.0.0") git("tag", "1.0.0")
run_copy(str(src), dst, defaults=True, overwrite=True) run_copy(str(src), dst, defaults=True, overwrite=True)
skip_me: Path = dst / "skip_me" skip_me = dst / "skip_me"
answers_file = dst / ".copier-answers.yml" answers_file = dst / ".copier-answers.yml"
answers = yaml.safe_load(answers_file.read_text()) answers = yaml.safe_load(answers_file.read_text())
assert skip_me.read_text() == "1" assert skip_me.read_text() == "1"

View File

@ -3,6 +3,7 @@ import shutil
from os.path import exists, join from os.path import exists, join
from pathlib import Path from pathlib import Path
import pytest
from plumbum import local from plumbum import local
from plumbum.cmd import git from plumbum.cmd import git
@ -60,6 +61,7 @@ def test_get_repo():
) )
@pytest.mark.impure
def test_clone(): def test_clone():
tmp = vcs.clone("https://github.com/copier-org/copier.git") tmp = vcs.clone("https://github.com/copier-org/copier.git")
assert tmp assert tmp
@ -67,6 +69,7 @@ def test_clone():
shutil.rmtree(tmp, ignore_errors=True) shutil.rmtree(tmp, ignore_errors=True)
@pytest.mark.impure
def test_removes_temporary_clone(tmp_path): def test_removes_temporary_clone(tmp_path):
src_path = "https://github.com/copier-org/autopretty.git" src_path = "https://github.com/copier-org/autopretty.git"
with Worker(src_path=src_path, dst_path=tmp_path, defaults=True) as worker: with Worker(src_path=src_path, dst_path=tmp_path, defaults=True) as worker:
@ -75,6 +78,7 @@ def test_removes_temporary_clone(tmp_path):
assert not temp_clone.exists() assert not temp_clone.exists()
@pytest.mark.impure
def test_dont_remove_local_clone(tmp_path): def test_dont_remove_local_clone(tmp_path):
src_path = vcs.clone("https://github.com/copier-org/autopretty.git") src_path = vcs.clone("https://github.com/copier-org/autopretty.git")
with Worker(src_path=src_path, dst_path=tmp_path, defaults=True) as worker: with Worker(src_path=src_path, dst_path=tmp_path, defaults=True) as worker:
@ -82,6 +86,7 @@ def test_dont_remove_local_clone(tmp_path):
assert exists(src_path) assert exists(src_path)
@pytest.mark.impure
def test_update_using_local_source_path_with_tilde(tmp_path): def test_update_using_local_source_path_with_tilde(tmp_path):
# first, get a local repository clone # first, get a local repository clone
src_path = vcs.clone("https://github.com/copier-org/autopretty.git") src_path = vcs.clone("https://github.com/copier-org/autopretty.git")