Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
f7a5046
Add pyrodigal-gv as an optional gene caller, selected via --genecaller
gbouras13 Aug 3, 2026
a1f6aaf
Replace Gurobi with flowpaths (HiGHS-backed) in the MFD solver
gbouras13 Aug 3, 2026
faf006f
Merge branch 'feature/highs-solver' into feature/prostt5-foldseek-det…
gbouras13 Aug 3, 2026
77b44ba
Add ProstT5 -> Foldseek structural phage-gene detection, selected via…
gbouras13 Aug 3, 2026
acd2f14
Add GPU backend selection for ProstT5/foldseek, container support, re…
gbouras13 Aug 3, 2026
274fd1b
Remove the numpy<2.0.0 pin and the Python <3.13 upper bound
gbouras13 Aug 3, 2026
e2531f3
Migrate to Snakemake 8+ and fix a resource conflict it exposes
gbouras13 Aug 3, 2026
3126b70
Merge branch 'chore/snakemake8-migration' into feature/prostt5-foldse…
gbouras13 Aug 3, 2026
3517992
Fix the same mem/mem_mb resource conflict in this branch's own new rules
gbouras13 Aug 3, 2026
b02ebfc
Update bundled phrog_annot.tsv to the accurate PHROG category assignm…
gbouras13 Aug 3, 2026
b6505e1
Merge branch 'fix/update-phrog-annotations' into feature/prostt5-fold…
gbouras13 Aug 3, 2026
9d6a0a3
Merge branch 'fix/smg-exclusion-order-dependence' into feature/prostt…
gbouras13 Aug 3, 2026
e05614c
Add an optional SLURM GPU-routing profile for predict_3di/scan_hallmark
gbouras13 Aug 3, 2026
71b70a1
Fix three CLI/pipeline robustness bugs
gbouras13 Aug 4, 2026
949881e
Match PHROG categories exactly, not by substring
gbouras13 Aug 4, 2026
8435b84
Merge branch 'fix/exact-category-matching' into feature/prostt5-folds…
gbouras13 Aug 4, 2026
7664556
Merge branch 'fix/misc-cli-robustness' into feature/prostt5-foldseek-…
gbouras13 Aug 4, 2026
ab67cb9
Merge pull request #1 from gbouras13/feature/prostt5-foldseek-detection
gbouras13 Aug 4, 2026
7da4e66
Fix: phables install ignores --databases (merge_config missing)
gbouras13 Aug 4, 2026
feaaa56
Fix: pin cogent3 <2026.7 in phylotree.yaml to avoid unsolvable scinex…
gbouras13 Aug 4, 2026
2e90580
Make tree-building (MAFFT + IQ-TREE) opt-in, off by default
gbouras13 Aug 4, 2026
34dc0ed
Merge branch 'fix/remove-phylotree-tree-building' into feature/prostt…
gbouras13 Aug 4, 2026
d22cb95
Fix: predict_3di crashes with model_dir=None under Snakemake's script…
gbouras13 Aug 4, 2026
dea0236
Pin torch>=2.6 in all three prostt5 envs (cpu/cuda/rocm)
gbouras13 Aug 4, 2026
b007ad5
Tighten default --hallmark-evalue from 1e-3 to 1e-8
gbouras13 Aug 4, 2026
5c34801
Extend minlength gate to linear resolved paths, not just single-uniti…
gbouras13 Aug 5, 2026
067fce2
Fix igraph 1.0 incompatibility in get_all_sub_paths (long-read path o…
gbouras13 Aug 5, 2026
afe38c5
Work around broken torchaudio in ROCm container (missing libomp.so)
gbouras13 Aug 10, 2026
86b8c17
Fix torchaudio stub: give it a real ModuleSpec, not a bare ModuleType
gbouras13 Aug 10, 2026
c412f41
Pin prostt5-rocm.yaml to torch==2.7.1+rocm6.3, broaden torchaudio wor…
gbouras13 Aug 10, 2026
a1ac8fd
Replace koverage wrapper with direct CoverM invocation (PLAN.md §4.8)
gbouras13 Aug 10, 2026
6fa8f21
Document koverage_tsv column layout, add regression test
gbouras13 Aug 10, 2026
32bf598
Branch coverm_map's minimap2 preset on config[longreads]
gbouras13 Aug 10, 2026
7a3f9f6
Fix coverm.yaml solver conflict: drop redundant minimap2/samtools pins
gbouras13 Aug 10, 2026
afcb9b2
Fix coverm_map: samtools view needs -b, defaults to SAM text otherwise
gbouras13 Aug 10, 2026
0ecc830
Finish koverage removal: convert postprocess to direct CoverM, drop k…
gbouras13 Aug 11, 2026
0adfe97
Bump version to 2.0.0
gbouras13 Aug 11, 2026
c919fd3
Fix CI: stale Python matrix, missing test-time deps
gbouras13 Aug 11, 2026
430f9ab
Add build_hallmark_db.py + docs (closes a promise the CLI already made)
gbouras13 Aug 11, 2026
2fd2829
build_hallmark_db.py: fix missing _ca suffix
gbouras13 Aug 11, 2026
5935cd1
Wire hallmark_db into `phables install` as a pre-built download
gbouras13 Aug 11, 2026
66924b1
hallmark_db: set real Zenodo URL, drop dereference guidance
gbouras13 Aug 11, 2026
98aa263
docs/usage.md: document the entire fork CLI surface
gbouras13 Aug 11, 2026
6942bbc
Replace stale pre-fork Dockerfile with a working ROCm/Setonix build + CI
gbouras13 Aug 12, 2026
6a2a501
Add --container: workflow-wide container, replacing conda for every rule
gbouras13 Aug 12, 2026
3a38d2d
container: drop cogent3/piqtree, not used in this workflow
gbouras13 Aug 12, 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
52 changes: 52 additions & 0 deletions .github/workflows/build_container.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Build and Push Container to Quay
on: [push, pull_request]

# Don't queue duplicate builds for the same ref — the previous one wins.
concurrency:
group: build-container-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout repo
uses: actions/checkout@v4

# The pawsey/pytorch:2.7.1-rocm6.3.3 base image (same one phold's own
# container build uses, ../../phold/.github/workflows/build_container.yaml)
# is ~14GB compressed and exceeds the ~14GB free disk on a fresh
# ubuntu-latest runner. Without this step the very first ``FROM`` line
# dies with ``ResourceExhausted: ... no space left on device``. The
# ``free-disk-space`` action removes pre-installed toolchains we don't
# use (Android SDK, .NET, Haskell GHC, large apt packages, swap file,
# pre-pulled docker images), freeing ~25-30GB. ``tool-cache: false``
# keeps the runner's node/python toolcache so other actions (checkout,
# docker login) still work fast.
- name: Free up disk space
uses: jlumbroso/free-disk-space@main

Check warning on line 28 in .github/workflows/build_container.yaml

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

.github/workflows/build_container.yaml#L28

An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA. Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release.
with:
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: true
swap-storage: true
docker-images: true

- name: Show free disk after cleanup
run: df -h

- name: Log in to Quay
run: echo "${{ secrets.QUAY_PASSWORD }}" | docker login quay.io -u "${{ secrets.QUAY_USERNAME }}" --password-stdin

- name: Build Docker image
run: |
IMAGE=quay.io/${{ secrets.QUAY_USERNAME }}/phables:${{ github.sha }}
docker build -f container/Dockerfile -t $IMAGE .

- name: Push Docker image
run: |
IMAGE=quay.io/${{ secrets.QUAY_USERNAME }}/phables:${{ github.sha }}
docker push $IMAGE
23 changes: 19 additions & 4 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ["3.9", "3.10"]
# setup.py's python_requires is ">=3.11" (snakemake>=8's own floor --
# see setup.py's comment on why). This matrix was still 3.9/3.10 despite
# that, which fails `pip install .` outright on both -- confirmed by
# reading setup.py, not run against real CI (no Actions access from
# here). Bumped to match what the package actually supports.
python-version: ["3.11", "3.12"]

steps:
- uses: "actions/checkout@v3"
Expand All @@ -38,10 +43,20 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install .

- name: "Generate coverage report on ${{ matrix.os }} for Python ${{ matrix.python-version }}"
run: |
pip install pytest pytest-cov
# pytest itself + the one real (non-deferred) extra import the test
# suite needs beyond `pip install .`: tests/test_gene_caller.py calls
# into workflow/scripts/gene_caller.py directly (not through a
# Snakemake rule), which needs pyrodigal-gv -- normally supplied at
# real runtime by envs/genecall.yaml, a per-rule conda env this base
# CI install never touches. (format_koverage_results.py's own
# pandas/biopython need is deliberately NOT listed here -- those
# imports were moved inside main() specifically so
# test_format_koverage_results.py doesn't need them at all; adding
# them back here would be masking that, not needing it.)
pip install pytest pytest-cov pyrodigal-gv
pytest --cov=./ --cov-report xml --cov-report lcov --cov-append

- name: Coveralls Parallel
Expand All @@ -58,7 +73,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ["3.9", "3.10"]
python-version: ["3.11", "3.12"]
steps:
- name: Coveralls Finished
uses: coverallsapp/github-action@master
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Local benchmark reads/assemblies for the gene-caller ablation — large, fetched from
# ENA/HPC on demand, never intended to be committed
tests/data/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
5 changes: 3 additions & 2 deletions build/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ channels:
- anaconda
- bioconda
dependencies:
- python>=3.9, <3.11
- snakemake>=7.14.0
# See setup.py for why both of these bumped: snakemake>=8 needs python>=3.11.
- python>=3.11
- snakemake>=8.0
- pyyaml>=6.0
- click>=8.1.3
- jinja2>=3.0.2
Expand Down
168 changes: 124 additions & 44 deletions container/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,57 +1,137 @@

#
# phables
# phables -- massive all-in-one image for Setonix (ROCm), replacing per-rule
# --use-conda env creation entirely. See ../docs/container.md for how to run
# it (--container <image> --use-singularity --no-use-conda) and why this
# exists (a real, hit-in-production race condition: every sample's `phables
# run` is its own separate process, so many array tasks needing the same
# shared --use-conda env for the first time at once can corrupt each other's
# `mamba env create`).
#
# Base is Pawsey's own verified-working ROCm+PyTorch image -- the SAME one
# phold's own container (../../phold/container/hpci/Dockerfile) builds from,
# deliberately: torch==2.7.1+rocm6.3 baked in here is exactly what
# envs/prostt5-rocm.yaml pins, confirmed working on real Setonix MI250X
# hardware this session. Nothing below reinstalls or touches torch -- pip
# installs pholdlib and every other pure-Python dependency directly into this
# same system Python, so they see (and don't conflict with) the base image's
# already-correct torch/ROCm stack rather than resolving a second, possibly
# different one.
FROM quay.io/pawsey/pytorch:2.7.1-rocm6.3.3

FROM --platform=linux/amd64 ubuntu:20.04
FROM gurobi/optimizer:latest
ARG DEBIAN_FRONTEND="noninteractive"

ENV DEBIAN_FRONTEND="noninteractive"
RUN apt-get update && apt-get install -y --no-install-recommends \
wget \
tar \
build-essential \
&& rm -rf /var/lib/apt/lists/*

# the base image only provides python3, not python -- some tooling (and
# Snakemake's own script: directive) expects `python` on PATH
RUN ln -sf "$(command -v python3)" /usr/local/bin/python

ARG LIBFABRIC_VERSION=1.18.1
#
# foldseek -- same install as phold's own Dockerfile (direct binary download,
# not conda/pip), since that's the reference build this image otherwise
# mirrors for the ROCm/torch stack
#
RUN wget https://mmseqs.com/foldseek/foldseek-linux-avx2.tar.gz && \
tar -xzf foldseek-linux-avx2.tar.gz && \
rm foldseek-linux-avx2.tar.gz && \
mv foldseek /opt/foldseek
ENV PATH="/opt/foldseek/bin:${PATH}"

# Install required packages and dependencies
RUN apt -y update \
&& apt -y install build-essential wget doxygen gnupg gnupg2 curl apt-transport-https software-properties-common \
git vim gfortran libtool python3-venv ninja-build python3-pip \
libnuma-dev python3-dev \
&& apt -y remove --purge --auto-remove cmake \
&& wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null\
| gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null \
&& apt-add-repository -y "deb https://apt.kitware.com/ubuntu/ jammy-rc main" \
&& apt -y update
#
# Every other per-rule tool that isn't a pure-Python package: bootstrap a
# standalone micromamba (no full miniforge needed) and install them all into
# one prefix, /opt/conda -- kept entirely separate from the base image's own
# system Python (added to PATH, but conda never pulls in a python of its own
# here since none of these packages are Python libraries, only standalone
# binaries -- confirmed by their real recipes: minimap2/samtools/mmseqs2/
# fraggenescan/hmmer/mafft have no python dependency; coverm's own recipe
# pulls minimap2+samtools transitively too, listed here explicitly anyway for
# a single deterministic solve rather than relying on that side effect).
# mmseqs2 pinned to the exact version envs/mmseqs.yaml already uses.
RUN curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba && \
mv bin/micromamba /usr/local/bin/micromamba && \
micromamba create -y -p /opt/conda -c conda-forge -c bioconda \
minimap2 \
samtools \
"coverm>=0.6" \
"mmseqs2=13.45111" \
fraggenescan \
hmmer \
mafft \
&& micromamba clean -a -y
ENV PATH="/opt/conda/bin:${PATH}"

# Build and install libfabric
RUN (if [ -e /tmp/build ]; then rm -rf /tmp/build; fi;) \
&& mkdir -p /tmp/build \
&& cd /tmp/build \
&& wget https://github.com/ofiwg/libfabric/archive/refs/tags/v${LIBFABRIC_VERSION}.tar.gz \
&& tar xf v${LIBFABRIC_VERSION}.tar.gz \
&& cd libfabric-${LIBFABRIC_VERSION} \
&& ./autogen.sh \
&& ./configure \
&& make -j 16 \
&& make install
#
# Everything else -- phables' own pure-Python deps, ProstT5/pholdlib, and
# phables itself -- pip installed directly into the base image's system
# Python. Deliberately NOT a separate venv/conda env: the whole point is for
# all of this to see the base image's already-installed, already-verified
# torch/ROCm build rather than resolving (and risking conflicting with) a
# second one.
#
# pholdlib's own declared torch requirement is unconstrained (confirmed
# earlier this session against envs/prostt5-rocm.yaml/-cpu.yaml/-cuda.yaml) --
# `pip install pholdlib` alone pulls in its transitive deps (transformers,
# sentencepiece, h5py, loguru, etc.) via pip's own resolver without touching
# the already-installed torch.
RUN pip install --ignore-installed PyYAML && \
python -c "import torch; print('torch (pre-existing, untouched):', torch.__version__)"

#
# Install miniforge
# NOT included: cogent3/piqtree (phylotree.yaml's own deps, for the optional
# --phylotree/build_tree rule -- not part of this workflow's usage).
# Deliberately left out rather than fought into pip (every cogent3 release on
# PyPI is an unreleased alpha, which breaks a plain version-range pip
# install). Note: --phylotree is therefore NOT supported under --container --
# build_tree still declares container: CONTAINER_IMAGE (unchanged), so it
# will fail if actually invoked against this image.
#
RUN set -eux ; \
curl -LO https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-x86_64.sh ; \
bash ./Miniforge3-* -b -p /opt/miniforge3 -s ; \
rm -rf ./Miniforge3-*
ENV PATH /opt/miniforge3/bin:$PATH
RUN pip install \
"pholdlib>=0.1.2" \
"pyrodigal-gv>=0.3.1" \
biopython \
python-igraph \
pysam \
"networkx>=2.8.6" \
scipy \
numpy \
pandas \
tqdm \
click \
"metasnek>=0.0.3" \
"flowpaths>=0.2.20"

RUN python -c "import torch; print('torch (post pip installs, should be unchanged):', torch.__version__)"

#
# phables itself -- installed from the exact source in this build context
# (this Dockerfile is meant to be built by CI on every commit, tagged by
# commit SHA, so it's the checked-out commit's own tree, not a fetched
# git+https ref pinned separately).
#
# Install conda environment
#
ARG PHABLES_VERSION=1.3.2
COPY . /opt/phables_src
RUN pip install /opt/phables_src

RUN set -eux ; \
mamba install -y -c conda-forge -c anaconda -c bioconda -c defaults \
phables=${PHABLES_VERSION} ;
ENV PATH /opt/miniforge3/bin:$PATH
RUN conda clean -af -y
RUN mkdir -p /phables /opt/gurobi
RUN ln -s /opt/miniforge3/lib/python3.10/site-packages/phables/workflow/conda /conda
RUN phables install
#
# tests -- fail the build loudly if any of the above didn't actually work,
# rather than shipping a broken image to quay.io
#
RUN foldseek --help
RUN minimap2 --version
RUN samtools --version
RUN mmseqs version
RUN coverm --version
RUN hmmsearch -h
# the bioconda package's actual binary is run_FragGeneScan.pl, not
# `fraggenescan` -- confirmed against genes.smk's own real invocation
RUN run_FragGeneScan.pl 2>&1 | head -5 || true
RUN mafft --version
RUN python --version
RUN python -c "import pholdlib; print('pholdlib OK')"
RUN python -c "import torch; assert torch.__version__.startswith('2.7.1'), torch.__version__"
RUN phables --version
RUN phables run -h
92 changes: 92 additions & 0 deletions docs/container.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Running phables from a single container

`--use-conda`'s per-rule environment creation is convenient but has a real
failure mode at HPC scale: every rule's conda env is named by a hash of its
env file, so every sample's `phables run` needs the *same* env directory —
but each sample is its own separate Snakemake process (a SLURM array task,
say), and Snakemake only serializes env creation *within* one process's DAG.
Two array tasks both needing the same not-yet-built env at the same moment
can race `mamba env create` into the same directory, corrupting it
(`Fatal Python error: init_fs_encoding ... no codec search functions
registered` is what that looks like when it happens).

`container/Dockerfile` builds a single image with every per-rule tool this
workflow needs already installed — `--container` then points the whole
workflow at it, replacing conda entirely so there's nothing left to race.

## Getting an image

CI (`.github/workflows/build_container.yaml`) builds and pushes an image to
`quay.io/<QUAY_USERNAME>/phables:<commit-sha>` on every push and pull request
— tag by the exact commit you want, there's no floating `latest`.

## Running with it

```bash
phables run --input assembly_graph.gfa --reads fastq \
--container quay.io/gbouras13/phables:<commit-sha> \
--no-use-conda \
--use-singularity
```

`--use-singularity` is Snakemake's own flag for running rules inside
Apptainer/Singularity (the standard container runtime on HPC, incl. Setonix)
— pass it through phables' existing `snake_args` passthrough, same as
`--dry-run`/`--keep-going`/etc. `--use-conda` alone does **not** activate
container execution; explicitly turn it off (`--no-use-conda`) when using
`--container`, since combining the two makes Snakemake build a *separate*
conda env *inside* the container instead of using what's already
installed there — exactly the redundant-torch-reinstall problem this image
exists to avoid.

## What's actually in the image

Base: `quay.io/pawsey/pytorch:2.7.1-rocm6.3.3` — the same verified-working
ROCm+PyTorch build `envs/prostt5-rocm.yaml` pins, and the same base phold's
own container (`../../phold/container/hpci/Dockerfile`) builds from. Nothing
in phables' own Dockerfile reinstalls or touches torch — `pip install
pholdlib` and everything else pure-Python go straight into that same system
Python, so they use the base image's already-correct torch/ROCm stack rather
than resolving a second, possibly conflicting one. This is the direct answer
to "don't rebuild the ProstT5 conda env for this container" — there's no
separate ProstT5 env in the container at all; predict_3di runs against the
same Python everything else does.

Everything else — foldseek (direct binary, matching phold's own install),
minimap2/samtools/coverm/mmseqs2/FragGeneScan/HMMER/MAFFT (via a standalone
micromamba install into `/opt/conda`, kept deliberately separate from the
system Python so it can't touch it), and phables' own Python dependencies
(pyrodigal-gv, biopython, python-igraph, pysam, flowpaths, cogent3/piqtree,
...) — covers every `envs/*.yaml` this workflow has, so the whole DAG can run
from this one image with `--no-use-conda`.

## `--container` vs `--prostt5-container`

`--prostt5-container` already existed for pointing *just* `predict_3di` at a
container (e.g. phold's own image, which also has pholdlib+torch). It still
works exactly as before, and if set, it wins for that one rule. `--container`
is new and broader: it's the default container for **every** rule, including
predict_3di if `--prostt5-container` isn't also given. In practice, setting
`--container` alone is enough — there's no reason to use `--prostt5-container`
separately unless you specifically want predict_3di on a *different* image
than the rest of the workflow.

## Building it yourself

```bash
docker build -f container/Dockerfile -t phables:local .
```

The base image is large (~14GB compressed) — building on a standard GitHub
Actions runner needs the disk-cleanup step already in
`build_container.yaml` (`jlumbroso/free-disk-space`), or the very first
`FROM` line fails with `no space left on device`. Building locally needs
proportionate free disk space too.

**Not yet built or run for real** — the Dockerfile and the workflow-wide
`--container` plumbing were written and the Snakemake DAG wiring was verified
via real `--dry-run`s (with and without `--container` set, confirming both
resolve identically at the structural level), but no actual `docker build`
or real Apptainer/Singularity execution against Setonix hardware has
happened yet. Build it, push a first tag, and run one real sample through it
before trusting this for production batches.
Loading
Loading