Skip to content
Open
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
16 changes: 16 additions & 0 deletions copier.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,18 @@
_subdirectory: template
_answers_file: .copier-answers.ess.yml
# Questions
subpackages:
type: yaml
multiline: true
help: Subpackages information. Name, description and dependencies.
default:
# Include the existing packages as defaults for easier template settings for now.
# They should be deleted once we have the answer file in the ess project.
- name: essreduce
description: Common data reduction tools (core)
dependencies: []
- name: essimaging
description: Neutron imaging (ODIN, TBL, YMIR)
dependencies:
- essreduce

97 changes: 0 additions & 97 deletions template/.github/ISSUE_TEMPLATE/high-level-requirement.yml

This file was deleted.

13 changes: 0 additions & 13 deletions template/.github/dependabot.yml

This file was deleted.

112 changes: 112 additions & 0 deletions template/README.md.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# ESS Data Reduction Monorepo

Monorepo for ESS neutron scattering data reduction packages, managed with [pixi](https://pixi.sh/).

| Package | Description |
|---------|-------------|
{% for package in subpackages %}| [{{ package.name }}](packages/{{ package.name }}/) | {{ package.description }} |
{% endfor %}
## Dependency graph

```
essreduce
└── essimaging
```

---

## Developer Guide

### Prerequisites

Install [pixi](https://pixi.sh/):

```bash
curl -fsSL https://pixi.sh/install.sh | bash
```

### Setup

```bash
git clone git@github.com:scipp/ess.git
cd ess

# Install all packages (editable, with test deps):
pixi install

# Or just one package:
pixi install -e essreduce
```

The `pixi.lock` file pins all dependencies reproducibly. No tox, no pip-compile, no manual virtualenv.

### Running tests

```bash
# Test a package:
pixi run test essreduce
pixi run test essimaging

# Test a single file:
pixi run -e essreduce pytest packages/essreduce/tests/normalization_test.py
```

### Linting and formatting

```bash
pixi run -e lint lint # all pre-commit hooks
pixi run -e lint check # ruff check
pixi run -e lint format # ruff format
```

### Building docs

```bash
pixi run docs essreduce
pixi run docs essimaging
```

### Adding or changing dependencies

Edit the package's `pyproject.toml`, then re-lock:

```bash
pixi install
```

Commit the updated `pixi.lock`.

### Releasing a package

Push a tag with the package prefix:

```bash
git tag essreduce/26.3.0
git push origin main --tags
```

The `release.yml` workflow builds, publishes to PyPI, and deploys docs.

### How CI works

- **On PRs:** Only changed packages are tested (changing `essreduce` also tests `essimaging`)
- **Nightly:** All packages tested against latest deps + lower-bound deps
- **Weekly:** All packages tested on macOS and Windows

### Repo structure

```
pixi.toml ← workspace root (features, tasks, environments)
pixi.lock ← single lockfile for all packages
.pre-commit-config.yaml ← shared linting hooks
packages/
essreduce/
pyproject.toml ← package deps, version, pytest config
src/ess/reduce/ ← source code (ess.reduce namespace)
tests/
docs/
essimaging/
pyproject.toml
src/ess/imaging/ ← source code (ess.imaging namespace)
...
```
62 changes: 62 additions & 0 deletions template/pixi.toml.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
[workspace]
name = "ess"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-64", "osx-arm64", "win-64"]
version = "2026.2.0"

[dependencies]
python = "3.11.*"

# ==================== Parameterized tasks ====================

[tasks.test]
cmd = "pytest packages/{% raw %}{{ package }}{% endraw %}/tests/"
args = ["package"]

[tasks.docs]
cmd = "python -m sphinx -v -b html -d /tmp/docs_doctrees packages/{% raw %}{{ package }}{% endraw %}/docs packages/{% raw %}{{ package }}{% endraw %}/html"
args = ["package"]

# ==================== Package features ====================

{% for package in subpackages %}# {{ package.name }} {% if package.name == "essreduce" %}(core package, no workspace deps){% else %}(depends on {{ ', '.join(package.dependencies) }}){% endif %}
[feature.{{ package.name }}.pypi-dependencies]
{{ package.name }} = { path = "packages/{{ package.name }}", editable = true, extras = ["test"] }

{% endfor %}# ==================== Lint feature ====================

[feature.lint.pypi-dependencies]
pre-commit = "*"
ruff = ">=0.15"

[feature.lint.tasks]
lint = "pre-commit run --all-files"
check = "ruff check ."
format = "ruff format ."

# ==================== Docs feature ====================

[feature.docs.dependencies]
pandoc = "*"

# Per-package docs features (install with docs extra)
{% for package in subpackages %}[feature.docs-{{ package.name }}.pypi-dependencies]
{{ package.name }} = { path = "packages/{{ package.name }}", editable = true, extras = ["test", "docs"] }

{% endfor %}# ==================== Environments ====================

[environments]
# Default: all packages (for full dev setup)
default = { features = [{% for package in subpackages %}"{{ package.name }}"{% if not loop.last %}, {% endif %}{% endfor %}], solve-group = "default" }

# Per-package test environments (include workspace dep features)
{% for package in subpackages %}{{ package.name }} = { features = ["{{ package.name }}"{% if package.dependencies %}, {% endif %}{% for dep in package.dependencies %}"{{ dep }}"{% if not loop.last %}, {% endif %}{% endfor %}], solve-group = "default" }
{% endfor %}
# Lower-bound test environments (separate resolution)
{% for package in subpackages %}lb-{{ package.name }} = { features = ["{{ package.name }}"{% if package.dependencies %}, {% endif %}{% for dep in package.dependencies %}"{{ dep }}"{% if not loop.last %}, {% endif %}{% endfor %}], solve-group = "lower-bound" }
{% endfor %}
# Docs environments (package with docs extra + pandoc)
{% for package in subpackages %}docs-{{ package.name }} = { features = ["docs-{{ package.name }}", {% for dep in package.dependencies %}"docs-{{ dep }}", {% endfor %}"docs"], solve-group = "default" }
{% endfor %}
# Lint environment (standalone, no package deps)
lint = { features = ["lint"], solve-group = "lint" }
Loading