Skip to content
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
1984ac5
Initial commit
klimaj Jan 16, 2026
bbcb188
Test using quarterly releases
klimaj Jan 16, 2026
db646de
Fix local segment
klimaj Jan 16, 2026
00abf3d
Test weekly build pipeline
klimaj Jan 16, 2026
98a77a8
Test uv with weekly and quarterly releases
klimaj Jan 16, 2026
839f654
Fix job name
klimaj Jan 16, 2026
da4820c
Update PyRosetta version
klimaj Jan 16, 2026
fb03177
Use west mirror
klimaj Jan 16, 2026
82ebc4b
Test pyrosetta-installer using east mirror
klimaj Jan 16, 2026
fc8d8ac
Fix --mirror_order
klimaj Jan 16, 2026
9ab8ad3
Update README.md
klimaj Jan 17, 2026
0fcfb6d
Merge branch 'uv_quarterly' of github.com:klimaj/tests into uv_quarterly
klimaj Jan 17, 2026
6194541
Initial commit
klimaj Jan 17, 2026
b38a6cc
Update README.md
klimaj Jan 17, 2026
3feba6f
Merge branch 'uv_quarterly' of github.com:klimaj/tests into uv_quarterly
klimaj Jan 17, 2026
b3f544c
Update README.md
klimaj Jan 17, 2026
0b54613
Merge branch 'uv_quarterly' of github.com:klimaj/tests into uv_quarterly
klimaj Jan 17, 2026
5c2c62c
Initial commit
klimaj Jan 17, 2026
020e100
Create README.md
klimaj Jan 17, 2026
dd82410
Update README.md
klimaj Jan 17, 2026
c94184e
Update README.md
klimaj Jan 17, 2026
d25008a
Update README.md
klimaj Jan 17, 2026
ed2b090
Update README.md
klimaj Jan 17, 2026
c33acae
Merge branch 'uv_quarterly' of github.com:klimaj/tests into uv_quarterly
klimaj Jan 17, 2026
5da8c78
Add Python version comment
klimaj Jan 17, 2026
2d170f6
Use west mirror
klimaj Jan 17, 2026
f1a0cc6
Update README.md
klimaj Jan 17, 2026
71d9400
Update README.md
klimaj Jan 17, 2026
df05b9c
Update README.md
klimaj Jan 17, 2026
f720fd1
Merge branch 'uv_quarterly' of github.com:klimaj/tests into uv_quarterly
klimaj Jan 17, 2026
e02a435
Fix --mirror_order validation
klimaj Jan 17, 2026
e6e0743
Update README.md
klimaj Jan 17, 2026
8f61fb6
Update README.md
klimaj Jan 19, 2026
b3a54be
Merge branch 'uv_quarterly' of github.com:klimaj/tests into uv_quarterly
klimaj Jan 19, 2026
96d017e
Add trailing forward slash
klimaj Jan 22, 2026
e16f0f2
Update README.md
klimaj Jan 22, 2026
2f2472f
Add comment
klimaj Jan 22, 2026
33e76a4
Merge branch 'uv_quarterly' of github.com:klimaj/tests into uv_quarterly
klimaj Jan 22, 2026
f700b68
Update README.md
klimaj Jan 22, 2026
156e6fa
Update README.md
klimaj Jan 22, 2026
bb8fbd9
Merge branch 'uv_quarterly' of github.com:klimaj/tests into uv_quarterly
klimaj Jan 22, 2026
fbd68d2
Merge branch 'main' of github.com:klimaj/tests into uv_quarterly
klimaj Jan 22, 2026
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
39 changes: 37 additions & 2 deletions .github/workflows/pyrosettacluster.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ jobs:


# ============================================================
# UV JOB
# UV JOB (QUARTERLY RELEASES)
# ============================================================
uv:
name: Uv
name: Uv (quarterly)
runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand Down Expand Up @@ -90,6 +90,41 @@ jobs:
python -u -m unittest actions.pyrosettacluster.test_env_reproducibility.TestEnvironmentReproducibility.test_recreate_environment_uv -v


# ============================================================
# UV JOB (WEEKLY RELEASES)
# ============================================================
uv_pyrosetta_installer:
name: Uv (pyrosetta-installer)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
uv --version

- name: Add repo root to PYTHONPATH
run: echo "PYTHONPATH=$PYTHONPATH:${{ github.workspace }}" >> $GITHUB_ENV

- name: Run tests (uv)
shell: bash
run: |
which python
python --version
python -u -m unittest actions.pyrosettacluster.test_env_reproducibility.TestEnvironmentReproducibility.test_recreate_environment_uv_pyrosetta_installer -v


# ============================================================
# CONDA JOB
# ============================================================
Expand Down
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,18 @@
> Downloads of PyRosetta software are provided to academic and non-commercial users under the [PyRosetta Software Non-Commercial License Agreement](https://github.com/RosettaCommons/rosetta/blob/main/LICENSE.PyRosetta.md).
> While scripts in this GitHub repository are made publicly available under the [MIT License](https://github.com/RosettaCommons/pyrosetta-extras/blob/main/LICENSE), the scripts themselves may require download and installation of PyRosetta software to run properly, and therefore use of the scripts for commercial purposes (including fee-for-service work by academic users) may require purchase of a separate license. Please see [here](https://els2.comotion.uw.edu/product/pyrosetta) or email license@uw.edu for more information.

## ✏️ Contributions:
---

## 🗂️ Features
<div align="left">
<a href="pyrosettacluster/">
<img src="pyrosettacluster/images/title.png" width="67%" />
</a>
</div>

- Visit the [PyRosettaCluster extra documentation](pyrosettacluster) for more info.

---

## ✏️ Contributions
Please use Rosetta's [Fork-and-PR model](https://github.com/RosettaCommons/rosetta?tab=contributing-ov-file#starting-rosetta-developement) for development. See https://www.rosettacommons.org/docs/latest/internal_documentation/GithubWorkflow for more information about the recommended workflow.
77 changes: 71 additions & 6 deletions actions/pyrosettacluster/setup_envs.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
from pathlib import Path

from actions.pyrosettacluster.utils import (
PYROSETTA_FIND_LINKS_PATH,
ROSETTACOMMONS_CONDA_CHANNEL,
USE_WEST_MIRROR,
detect_platform,
)

Expand Down Expand Up @@ -63,8 +65,13 @@ def setup_pixi_environment(env_dir, timeout):
print(f"Pixi environment setup complete in directory: '{env_path}'.")


def setup_uv_environment(env_dir, timeout):
def setup_uv_environment_pyrosetta_installer(env_dir, timeout):
"""
*Deprecated* This uv project setup function uses the 'pyrosetta-installer' package,
which does not support long-term reproducibility of PyRosetta package installations.
Please see the `setup_uv_environment` function, which instead implements the quarterly
PyRosetta package installations that are maintained for long-term reproducibility.

Create a fresh uv environment using the 'pyrosetta-installer' package.

Note: this requires that `uv` is an executable installed and on `${PATH}`. This function:
Expand All @@ -80,7 +87,7 @@ def setup_uv_environment(env_dir, timeout):
print(f"Creating uv environment at '{env_path}'...")
os.makedirs(env_dir, exist_ok=True)
py_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
template_toml_file = Path(__file__).resolve().parent / "uv" / "pyproject.toml"
template_toml_file = Path(__file__).resolve().parent / "uv" / "pyproject_pyrosetta_installer.toml"
with open(template_toml_file, "r") as f:
toml_data = f.read().format(
name=os.path.basename(env_dir),
Expand All @@ -91,8 +98,8 @@ def setup_uv_environment(env_dir, timeout):
f.write(toml_data)

# Install pyrosetta-installer
print("Adding 'pyrosetta-installer', 'pip', and `pyrosetta.distributed` depedencies to uv environment...")
requirements_txt_file = Path(__file__).resolve().parent / "uv" / "requirements.txt"
print("Adding 'pyrosetta-installer', 'pip', and `pyrosetta.distributed` dependencies to uv environment...")
requirements_txt_file = Path(__file__).resolve().parent / "uv" / "requirements_pyrosetta_installer.txt"
subprocess.run(
[
"uv",
Expand All @@ -108,9 +115,55 @@ def setup_uv_environment(env_dir, timeout):
# Run PyRosetta installer with mirror fallback
print("Running PyRosetta installer in uv environment...")
install_pyrosetta_file = Path(__file__).resolve().parent.parent.parent / "pyrosettacluster" / "install_pyrosetta.py"
mirror_order_str = "0 1" if USE_WEST_MIRROR else "1 0"
subprocess.run(
["uv", "run", "--project", str(env_path), "python", install_pyrosetta_file, "--mirror_order", *mirror_order_str.split()],
check=True,
timeout=timeout,
)

print(f"Uv environment setup complete in directory: '{env_path}'.")


def setup_uv_environment(env_dir, timeout):
"""
Create a fresh uv environment using the PyRosetta quarterly builds for long-term reproducibility.

Note: this requires that `uv` is an executable installed and on `${PATH}`. This function:
- detects the current Python version
- adds PyRosetta and `pyrosetta.distributed` dependencies via `uv add ...`
"""
env_path = Path(env_dir)
if env_path.exists():
raise FileExistsError(f"The specified uv environment path already exists: '{env_path}'.")

# Create uv environment using the current Python
print(f"Creating uv environment at '{env_path}'...")
os.makedirs(env_dir, exist_ok=True)
py_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
template_toml_file = Path(__file__).resolve().parent / "uv" / "pyproject.toml"
with open(template_toml_file, "r") as f:
toml_data = f.read().format(
name=os.path.basename(env_dir),
py_version=py_version,
pyrosetta_find_links_path=PYROSETTA_FIND_LINKS_PATH,
)
toml_file = env_path / "pyproject.toml"
with open(toml_file, "w") as f:
f.write(toml_data)

# Install pyrosetta-installer
print("Adding PyRosetta and `pyrosetta.distributed` dependencies to uv environment...")
requirements_txt_file = Path(__file__).resolve().parent / "uv" / "requirements.txt"
subprocess.run(
["uv", "run", "--project", str(env_path), "python", install_pyrosetta_file],
[
"uv",
"add",
"--project", str(env_path),
"--requirements", str(requirements_txt_file),
],
check=True,
cwd=str(env_path),
timeout=timeout,
)

Expand Down Expand Up @@ -172,11 +225,23 @@ def setup_conda_environment(env_dir, timeout, env_manager="conda"):
parser.add_argument('--env_manager', type=str)
parser.add_argument('--env_dir', type=str)
parser.add_argument('--timeout', type=float)
parser.add_argument('--use_pyrosetta_installer', dest='use_pyrosetta_installer', action='store_true')
parser.set_defaults(use_pyrosetta_installer=False)
args = parser.parse_args()
# Validate
if args.use_pyrosetta_installer and args.env_manager != "uv":
raise ValueError(
"If passing the '--use_pyrosetta_installer' flag, the '--env_manager' flag "
f"must be set to 'uv'. Received: '{args.env_manager}'"
)
# Run setup
if args.env_manager == "pixi":
setup_pixi_environment(args.env_dir, args.timeout)
elif args.env_manager == "uv":
setup_uv_environment(args.env_dir, args.timeout)
if args.use_pyrosetta_installer:
setup_uv_environment_pyrosetta_installer(args.env_dir, args.timeout)
else:
setup_uv_environment(args.env_dir, args.timeout)
elif args.env_manager in ("conda", "mamba"):
setup_conda_environment(args.env_dir, args.timeout, env_manager=args.env_manager)
else:
Expand Down
17 changes: 16 additions & 1 deletion actions/pyrosettacluster/test_env_reproducibility.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
from pathlib import Path


from actions.pyrosettacluster.utils import USE_WEST_MIRROR


class TestEnvironmentReproducibility(unittest.TestCase):
@classmethod
def setUpClass(cls):
Expand Down Expand Up @@ -75,11 +78,15 @@ def run_subprocess(cmd, module_dir=None, cwd=None):
def recreate_environment_test(
self,
environment_manager="conda",
use_pyrosetta_installer=False,
timeout=(3 * 60 * 60), # 3 hours per subprocess
verbose=False,
):
"""Test for PyRosettaCluster decoy reproducibility in a recreated virtual environment."""
self.assertIn(environment_manager, ("conda", "mamba", "uv", "pixi"))
self.assertIsInstance(use_pyrosetta_installer, bool)
if use_pyrosetta_installer:
self.assertEqual(environment_manager, "uv")

test_script = os.path.join(os.path.dirname(__file__), "recreate_environment_test_runs.py")

Expand All @@ -94,6 +101,8 @@ def recreate_environment_test(
f"--env_dir '{original_env_dir}' "
f"--timeout '{timeout}'"
)
if use_pyrosetta_installer:
cmd += " --use_pyrosetta_installer"
returncode = TestEnvironmentReproducibility.run_subprocess(
cmd,
module_dir=os.path.dirname(setup_env_script),
Expand Down Expand Up @@ -190,11 +199,13 @@ def recreate_environment_test(
f"Reproduced '{environment_manager}' environment directory was not created: '{reproduce_env_dir}'",
)
recreate_env_script = Path(__file__).resolve().parent.parent.parent / "pyrosettacluster" / "recreate_env.py"
mirror_order_str = "0 1" if USE_WEST_MIRROR else "1 0"
cmd = (
f"{sys.executable} -u {recreate_env_script} "
f"--env_dir '{reproduce_env_dir}' "
f"--env_manager '{environment_manager}' "
f"--timeout '{timeout}'"
f"--timeout '{timeout}' "
f"--mirror_order {mirror_order_str}"
)
returncode = TestEnvironmentReproducibility.run_subprocess(
cmd,
Expand Down Expand Up @@ -329,6 +340,10 @@ def test_recreate_environment_mamba(self):
def test_recreate_environment_uv(self):
return self.recreate_environment_test(environment_manager="uv")

@unittest.skipIf(shutil.which("uv") is None, "The executable 'uv' is not available.")
def test_recreate_environment_uv_pyrosetta_installer(self):
return self.recreate_environment_test(environment_manager="uv", use_pyrosetta_installer=True)

@unittest.skipIf(shutil.which("pixi") is None, "The executable 'pixi' is not available.")
def test_recreate_environment_pixi(self):
return self.recreate_environment_test(environment_manager="pixi")
8 changes: 7 additions & 1 deletion actions/pyrosettacluster/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
import platform


ROSETTACOMMONS_CONDA_CHANNEL = "https://conda.rosettacommons.org" # "https://conda.graylab.jhu.edu"
USE_WEST_MIRROR: bool = True
if USE_WEST_MIRROR:
ROSETTACOMMONS_CONDA_CHANNEL: str = "https://conda.rosettacommons.org"
PYROSETTA_FIND_LINKS_PATH: str = "https://west.rosettacommons.org/pyrosetta/quarterly/release.cxx11thread.serialization"
else:
ROSETTACOMMONS_CONDA_CHANNEL: str = "https://conda.graylab.jhu.edu"
PYROSETTA_FIND_LINKS_PATH: str = "https://graylab.jhu.edu/download/PyRosetta4/archive/release-quarterly/release.cxx11thread.serialization"


def detect_platform():
Expand Down
3 changes: 3 additions & 0 deletions actions/pyrosettacluster/uv/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ name = "{name}"
version = "0.1.0"
requires-python = "=={py_version}"
dependencies = []

[tool.uv]
find-links = ['{pyrosetta_find_links_path}']
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[project]
name = "{name}"
version = "0.1.0"
requires-python = "=={py_version}"
dependencies = []
3 changes: 1 addition & 2 deletions actions/pyrosettacluster/uv/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ gitpython>=3.1.1
jupyter>=1.0.0
numpy>=1.17.3
pandas>=0.25.2
pip>=25.3
py3Dmol>=0.8.0
pyrosetta-installer>=0.1.2
pyrosetta>=2026.3
python-xz>=0.4.0
scipy>=1.4.1
traitlets>=4.3.3
18 changes: 18 additions & 0 deletions actions/pyrosettacluster/uv/requirements_pyrosetta_installer.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
attrs>=19.3.0
billiard>=3.6.3.0
blosc>=1.8.3
cloudpickle>=1.5.0
cryptography>=2.8
dask>=2.16.0
dask-jobqueue>=0.7.0
distributed>=2.16.0
gitpython>=3.1.1
jupyter>=1.0.0
numpy>=1.17.3
pandas>=0.25.2
pip>=25.3
py3Dmol>=0.8.0
pyrosetta-installer>=0.1.2
python-xz>=0.4.0
scipy>=1.4.1
traitlets>=4.3.3
Loading
Loading