Skip to content

Commit

Permalink
FEAT: automatically create Pixi environment (#374)
Browse files Browse the repository at this point in the history
* DX: avoid installing `sphinx-autobuild` v2024.4
* DX: highlight `pixi.lock` as YAML file
* DX: ignore `.gitattributes` with cSpell
* ENH: check if pixi.lock is linguist YAML
* ENH: ensure that `.pixi/` is listed under `.gitignore`
* ENH: execute `direnv` check after `pixi` update
* ENH: import environment variables
* ENH: import environment variables from Conda
* ENH: include default arguments for `posargs`
* ENH: pack strings in single quotation marks in `tox.ini`
* ENH: run `cpsell` check at end of `check-dev-files`
* ENH: switch to post-release version scheme
  This avoids git clutter in the editable package version
 
* FEAT: define combined local CI job for Pixi
* FIX: specify Pixi environment name in `.envrc`
* MAINT: remove redundant quotation marks in `environment.yml`
  • Loading branch information
redeboer authored Aug 12, 2024
1 parent ab251ea commit 2834e0e
Show file tree
Hide file tree
Showing 14 changed files with 611 additions and 24 deletions.
4 changes: 4 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"*.rst_t",
".editorconfig",
".envrc",
".gitattributes",
".gitignore",
".gitpod.*",
".pre-commit-config.yaml",
Expand Down Expand Up @@ -75,6 +76,9 @@
"commitlint",
"conda",
"direnv",
"doclive",
"docnb",
"docnblive",
"envrc",
"fromdict",
"indentless",
Expand Down
3 changes: 3 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ if [ -e .venv ]; then
source .venv/bin/activate
elif [ -e venv ]; then
source venv/bin/activate
elif [ -e .pixi ]; then
watch_file pixi.lock
eval "$(pixi shell-hook)"
else
layout anaconda
fi
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pixi.lock linguist-language=YAML linguist-generated=true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ prof/

# Virtual environments
*venv/
.pixi/
.tox/
pyvenv*/
pixi.lock

# Settings
.idea/
Expand Down
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
"cSpell.enabled": true,
"diffEditor.experimental.showMoves": true,
"editor.formatOnSave": true,
"files.associations": {
"**/pixi.lock": "yaml"
},
"files.watcherExclude": {
"**/*_cache/**": true,
"**/.eggs/**": true,
Expand Down
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ dependencies:
- pip:
- -e .[dev]
variables:
PRETTIER_LEGACY_CLI: "1"
PRETTIER_LEGACY_CLI: 1
49 changes: 49 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ dev = [
"pydeps",
"sphinx-autobuild",
"tox >=1.9", # for skip_install, use_develop
'sphinx-autobuild!=2024.4.*; python_version <"3.10.0"',
]
doc = [
"Sphinx",
Expand Down Expand Up @@ -115,6 +116,8 @@ namespaces = false
where = ["src"]

[tool.setuptools_scm]
local_scheme = "no-local-version"
version_scheme = "post-release"
write_to = "src/compwa_policy/version.py"

[tool.coverage.run]
Expand Down Expand Up @@ -147,6 +150,52 @@ module = ["ruamel.*"]
ignore_missing_imports = true
module = ["nbformat.*"]

[tool.pixi.project]
channels = ["conda-forge"]
platforms = ["linux-64"]

[tool.pixi.activation]
env = {PRETTIER_LEGACY_CLI = "1"}

[tool.pixi.dependencies]
python = "3.9.*"

[tool.pixi.environments]
default = {features = [
"dev",
"doc",
"sty",
"test",
"types",
]}

[tool.pixi.feature.dev.tasks.ci]
depends_on = ["cov", "doc", "linkcheck", "sty"]

[tool.pixi.feature.dev.tasks.cov]
cmd = "tox -e cov"

[tool.pixi.feature.dev.tasks.doc]
cmd = "tox -e doc"

[tool.pixi.feature.dev.tasks.doclive]
cmd = "tox -e doclive"

[tool.pixi.feature.dev.tasks.linkcheck]
cmd = "tox -e linkcheck"

[tool.pixi.feature.dev.tasks.pydeps]
cmd = "tox -e pydeps"

[tool.pixi.feature.dev.tasks.sty]
cmd = "pre-commit run --all-files"

[tool.pixi.feature.dev.tasks.tests]
cmd = "pytest"

[tool.pixi.pypi-dependencies]
compwa-policy = {path = ".", editable = true}

[tool.pyright]
exclude = [
"**/.git",
Expand Down
1 change: 1 addition & 0 deletions src/compwa_policy/.template/.cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
".constraints/*.txt",
".editorconfig",
".envrc",
".gitattributes",
".gitignore",
".gitpod.*",
".mypy.ini",
Expand Down
12 changes: 10 additions & 2 deletions src/compwa_policy/check_dev_files/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
jupyter,
mypy,
nbstripout,
pixi,
precommit,
prettier,
pyright,
Expand Down Expand Up @@ -57,9 +58,7 @@ def main(argv: Sequence[str] | None = None) -> int:
do(citation.main, precommit_config)
do(commitlint.main)
do(conda.main, dev_python_version)
do(cspell.main, precommit_config, args.no_cspell_update)
do(dependabot.main, args.dependabot)
do(direnv.main)
do(editorconfig.main, precommit_config)
if not args.allow_labels:
do(github_labels.main)
Expand All @@ -82,6 +81,8 @@ def main(argv: Sequence[str] | None = None) -> int:
if has_notebooks:
do(jupyter.main, args.no_ruff)
do(nbstripout.main, precommit_config, _to_list(args.allowed_cell_metadata))
do(pixi.main, is_python_repo, dev_python_version, args.outsource_pixi_to_tox)
do(direnv.main)
do(toml.main, precommit_config) # has to run before pre-commit
do(prettier.main, precommit_config, args.no_prettierrc)
if is_python_repo:
Expand Down Expand Up @@ -112,6 +113,7 @@ def main(argv: Sequence[str] | None = None) -> int:
do(gitpod.main, args.no_gitpod, dev_python_version)
do(precommit.main, precommit_config, has_notebooks)
do(tox.main, has_notebooks)
do(cspell.main, precommit_config, args.no_cspell_update)
return 1 if do.error_messages else 0


Expand Down Expand Up @@ -178,6 +180,12 @@ def _create_argparse() -> ArgumentParser:
action="store_true",
default=False,
)
parser.add_argument(
"--outsource-pixi-to-tox",
action="store_true",
default=False,
help="Run ",
)
parser.add_argument(
"--no-cspell-update",
action="store_true",
Expand Down
61 changes: 42 additions & 19 deletions src/compwa_policy/check_dev_files/direnv.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,60 @@

from textwrap import dedent, indent

import rtoml

from compwa_policy.check_dev_files.pixi import has_pixi_config
from compwa_policy.errors import PrecommitError
from compwa_policy.utilities import CONFIG_PATH
from compwa_policy.utilities.pyproject import Pyproject

__SCRIPTS = {
"conda": "layout anaconda",
"pixi": """
watch_file pixi.lock
eval "$(pixi shell-hook)"
""",
"venv": "source venv/bin/activate",
"uv-venv": "source .venv/bin/activate",
}


def main() -> None:
statements: list[tuple[str | None, str]] = [
(".venv", __SCRIPTS["uv-venv"]),
("venv", __SCRIPTS["venv"]),
(".venv", "source .venv/bin/activate"),
("venv", "source venv/bin/activate"),
]
if (
CONFIG_PATH.pixi_lock.exists()
or CONFIG_PATH.pixi_toml.exists()
or (CONFIG_PATH.pyproject.exists() and Pyproject.load().has_table("tool.pixi"))
):
statements.append((".pixi", __SCRIPTS["pixi"]))
if has_pixi_config():
dev_environment = __determine_pixi_dev_environment()
if dev_environment is None:
environment_flag = ""
else:
environment_flag = f" --environment {dev_environment}"
script = f"""
watch_file pixi.lock
eval "$(pixi shell-hook{environment_flag})"
"""
statements.append((".pixi", script))
if CONFIG_PATH.conda.exists():
statements.append((None, __SCRIPTS["conda"]))
statements.append((None, "layout anaconda"))
_update_envrc(statements)


def __determine_pixi_dev_environment() -> str | None:
search_terms = ["dev"]
if CONFIG_PATH.pyproject.exists():
pyproject = Pyproject.load()
package_name = pyproject.get_package_name()
if package_name is not None:
search_terms.append(package_name)
available_environments = __get_pixi_environment_names()
for candidate in search_terms:
if candidate in available_environments:
return candidate
return None


def __get_pixi_environment_names() -> set[str]:
if CONFIG_PATH.pixi_toml.exists():
pixi_config = rtoml.load(CONFIG_PATH.pixi_toml)
return set(pixi_config.get("environments", set()))
if CONFIG_PATH.pyproject.exists():
pyproject = Pyproject.load()
if pyproject.has_table("tool.pixi.environments"):
return set(pyproject.get_table("tool.pixi.environments"))
return set()


def _update_envrc(statements: list[tuple[str | None, str]]) -> None:
expected = ""
for i, (trigger_path, script) in enumerate(statements):
Expand Down
Loading

0 comments on commit 2834e0e

Please sign in to comment.