From a958f5f6cc54561c169b6c59130a1eb7a52f670a Mon Sep 17 00:00:00 2001 From: Brian Yee Date: Mon, 28 Sep 2026 14:13:06 -0700 Subject: [PATCH 1/2] Add PacBio differential analysis containers --- CHANGELOG.md | 5 +++ README.md | 21 ++++++++++++ images/deseq2/1.52.0/Dockerfile | 15 +++++++++ images/deseq2/1.52.0/README.md | 15 +++++++++ images/deseq2/1.52.0/verify.R | 20 ++++++++++++ images/dexseq/1.58.0/Dockerfile | 15 +++++++++ images/dexseq/1.58.0/README.md | 15 +++++++++ images/dexseq/1.58.0/verify.R | 32 ++++++++++++++++++ images/drimseq/1.40.0/Dockerfile | 15 +++++++++ images/drimseq/1.40.0/README.md | 16 +++++++++ images/drimseq/1.40.0/verify.R | 39 ++++++++++++++++++++++ images/suppa2/2.4/Dockerfile | 39 ++++++++++++++++++++++ images/suppa2/2.4/README.md | 15 +++++++++ images/suppa2/2.4/verify.sh | 32 ++++++++++++++++++ images/tappas/1.1.3/Dockerfile | 56 ++++++++++++++++++++++++++++++++ images/tappas/1.1.3/README.md | 23 +++++++++++++ images/tappas/1.1.3/verify.R | 15 +++++++++ 17 files changed, 388 insertions(+) create mode 100644 images/deseq2/1.52.0/Dockerfile create mode 100644 images/deseq2/1.52.0/README.md create mode 100644 images/deseq2/1.52.0/verify.R create mode 100644 images/dexseq/1.58.0/Dockerfile create mode 100644 images/dexseq/1.58.0/README.md create mode 100644 images/dexseq/1.58.0/verify.R create mode 100644 images/drimseq/1.40.0/Dockerfile create mode 100644 images/drimseq/1.40.0/README.md create mode 100644 images/drimseq/1.40.0/verify.R create mode 100644 images/suppa2/2.4/Dockerfile create mode 100644 images/suppa2/2.4/README.md create mode 100644 images/suppa2/2.4/verify.sh create mode 100644 images/tappas/1.1.3/Dockerfile create mode 100644 images/tappas/1.1.3/README.md create mode 100644 images/tappas/1.1.3/verify.R diff --git a/CHANGELOG.md b/CHANGELOG.md index 0242572..62e1ca5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,11 @@ ### Added +- Latest stable containers for all tools in the PacBio full-length isoform + sequencing note's differential-analysis sections: tappAS 1.1.3, DESeq2 + 1.52.0, DRIMSeq 1.40.0, DEXSeq 1.58.0, and SUPPA2 2.4. Each image includes a + build-time version and functional smoke test and is published under its + versioned GHCR tag. - HOMER 5.1 (`images/homer/5.1`) with bedtools 2.31.1 and Python 3.12 for FASTA-mode motif discovery, published as `ghcr.io/yeolab/homer:5.1`. - skipper-clipper-compare 1.0.0 (`images/skipper-clipper-compare/1.0.0`): the diff --git a/README.md b/README.md index d909f81..b1557d5 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,27 @@ write`, already declared in the workflow. If organization policy prevents a publish, allow GitHub Actions to create packages for this repository or replace the token with an approved package-write token. +## PacBio full-length isoform differential analysis + +The tools listed under **Differential transcript analysis** and **Differential +splicing usage** in PacBio's full-length isoform sequencing application note +are available as individually versioned images: + +| Application-note section | Tool | Image | +| --- | --- | --- | +| Differential transcript analysis | tappAS 1.1.3 | `ghcr.io/yeolab/tappas:1.1.3` | +| Differential transcript analysis | DESeq2 1.52.0 | `ghcr.io/yeolab/deseq2:1.52.0` | +| Differential transcript analysis | DRIMSeq 1.40.0 | `ghcr.io/yeolab/drimseq:1.40.0` | +| Differential splicing usage | DEXSeq 1.58.0 | `ghcr.io/yeolab/dexseq:1.58.0` | +| Differential splicing usage | SUPPA2 2.4 | `ghcr.io/yeolab/suppa2:2.4` | + +DESeq2, DRIMSeq, and DEXSeq use the Bioconductor 3.23 release on R 4.6. +SUPPA2 is a command-line image whose arguments are passed directly to +`suppa.py`. tappAS is an x86-64 GUI image and needs an X11 display; see each +image directory's README for launch examples and resource requirements. Every +Dockerfile contains a build-time smoke test, so GitHub Actions publishes an +image only after its installed version and a representative operation pass. + ## Pulling a Singularity/Apptainer image from GHCR Install [Apptainer](https://apptainer.org/) (or SingularityCE), then pull a diff --git a/images/deseq2/1.52.0/Dockerfile b/images/deseq2/1.52.0/Dockerfile new file mode 100644 index 0000000..6c96891 --- /dev/null +++ b/images/deseq2/1.52.0/Dockerfile @@ -0,0 +1,15 @@ +FROM bioconductor/bioconductor_docker:RELEASE_3_23@sha256:821dbf9ac119eac41f177531c7ca8fc7084c99c04eb218a1aa7f52cb63bad5d9 + +LABEL software="DESeq2" \ + software.version="1.52.0" \ + about.summary="Differential gene expression analysis based on the negative binomial distribution" \ + about.url="https://bioconductor.org/packages/3.23/bioc/html/DESeq2.html" \ + about.license="LGPL-3.0-or-later" \ + about.tags="Genomics,RNA-seq,differential-expression" + +RUN Rscript -e 'BiocManager::install("DESeq2", ask = FALSE, update = FALSE)' + +COPY verify.R /tmp/verify.R +RUN Rscript /tmp/verify.R && rm /tmp/verify.R + +CMD ["R"] diff --git a/images/deseq2/1.52.0/README.md b/images/deseq2/1.52.0/README.md new file mode 100644 index 0000000..a5897bc --- /dev/null +++ b/images/deseq2/1.52.0/README.md @@ -0,0 +1,15 @@ +# DESeq2 1.52.0 + +DESeq2 1.52.0 from Bioconductor 3.23 on R 4.6. The image build loads the +package and runs a small two-condition differential-expression analysis. + +```bash +docker run --rm -it -v "$PWD:/work" -w /work ghcr.io/yeolab/deseq2:1.52.0 +``` + +The default command starts R. Run an analysis script directly with: + +```bash +docker run --rm -v "$PWD:/work" -w /work \ + ghcr.io/yeolab/deseq2:1.52.0 Rscript analysis.R +``` diff --git a/images/deseq2/1.52.0/verify.R b/images/deseq2/1.52.0/verify.R new file mode 100644 index 0000000..d19294d --- /dev/null +++ b/images/deseq2/1.52.0/verify.R @@ -0,0 +1,20 @@ +suppressPackageStartupMessages(library(DESeq2)) + +stopifnot(packageVersion("DESeq2") == "1.52.0") + +set.seed(52) +counts <- matrix(rnbinom(600, mu = 100, size = 2), nrow = 100) +counts[1:10, 4:6] <- counts[1:10, 4:6] + 150L +storage.mode(counts) <- "integer" +col_data <- data.frame( + condition = factor(rep(c("control", "treated"), each = 3)), + row.names = paste0("sample", seq_len(ncol(counts))) +) +colnames(counts) <- row.names(col_data) + +dds <- DESeqDataSetFromMatrix(counts, col_data, design = ~ condition) +dds <- DESeq(dds, quiet = TRUE) +res <- results(dds) + +stopifnot(nrow(res) == nrow(counts), "log2FoldChange" %in% colnames(res)) +message("DESeq2 ", packageVersion("DESeq2"), " smoke test passed") diff --git a/images/dexseq/1.58.0/Dockerfile b/images/dexseq/1.58.0/Dockerfile new file mode 100644 index 0000000..6e8b4ea --- /dev/null +++ b/images/dexseq/1.58.0/Dockerfile @@ -0,0 +1,15 @@ +FROM bioconductor/bioconductor_docker:RELEASE_3_23@sha256:821dbf9ac119eac41f177531c7ca8fc7084c99c04eb218a1aa7f52cb63bad5d9 + +LABEL software="DEXSeq" \ + software.version="1.58.0" \ + about.summary="Inference of differential exon usage in RNA-seq" \ + about.url="https://bioconductor.org/packages/3.23/bioc/html/DEXSeq.html" \ + about.license="GPL-3.0-or-later" \ + about.tags="Genomics,RNA-seq,differential-exon-usage" + +RUN Rscript -e 'BiocManager::install("DEXSeq", ask = FALSE, update = FALSE)' + +COPY verify.R /tmp/verify.R +RUN Rscript /tmp/verify.R && rm /tmp/verify.R + +CMD ["R"] diff --git a/images/dexseq/1.58.0/README.md b/images/dexseq/1.58.0/README.md new file mode 100644 index 0000000..531c857 --- /dev/null +++ b/images/dexseq/1.58.0/README.md @@ -0,0 +1,15 @@ +# DEXSeq 1.58.0 + +DEXSeq 1.58.0 from Bioconductor 3.23 on R 4.6. The image build loads the +package and constructs a small differential-exon-usage dataset. + +```bash +docker run --rm -it -v "$PWD:/work" -w /work ghcr.io/yeolab/dexseq:1.58.0 +``` + +The default command starts R. Run an analysis script directly with: + +```bash +docker run --rm -v "$PWD:/work" -w /work \ + ghcr.io/yeolab/dexseq:1.58.0 Rscript analysis.R +``` diff --git a/images/dexseq/1.58.0/verify.R b/images/dexseq/1.58.0/verify.R new file mode 100644 index 0000000..124ef91 --- /dev/null +++ b/images/dexseq/1.58.0/verify.R @@ -0,0 +1,32 @@ +suppressPackageStartupMessages(library(DEXSeq)) + +stopifnot(packageVersion("DEXSeq") == "1.58.0") + +set.seed(58) +sample_data <- data.frame( + condition = factor(rep(c("control", "treated"), each = 3)), + sample = factor(paste0("sample", 1:6)), + row.names = paste0("sample", 1:6) +) +count_data <- matrix( + rnbinom(72, mu = 80, size = 3), + nrow = 12, + dimnames = list(NULL, row.names(sample_data)) +) +storage.mode(count_data) <- "integer" + +dxd <- DEXSeqDataSet( + countData = count_data, + sampleData = sample_data, + design = ~ sample + exon + condition:exon, + featureID = rep(paste0("E", 1:3), 4), + groupID = rep(paste0("gene", 1:4), each = 3) +) + +stopifnot( + nrow(dxd) == nrow(count_data), + ncol(dxd) == 2L * nrow(sample_data), + all(c("sample", "exon", "condition") %in% colnames(colData(dxd))), + is.function(DEXSeq) +) +message("DEXSeq ", packageVersion("DEXSeq"), " smoke test passed") diff --git a/images/drimseq/1.40.0/Dockerfile b/images/drimseq/1.40.0/Dockerfile new file mode 100644 index 0000000..5a2f5c9 --- /dev/null +++ b/images/drimseq/1.40.0/Dockerfile @@ -0,0 +1,15 @@ +FROM bioconductor/bioconductor_docker:RELEASE_3_23@sha256:821dbf9ac119eac41f177531c7ca8fc7084c99c04eb218a1aa7f52cb63bad5d9 + +LABEL software="DRIMSeq" \ + software.version="1.40.0" \ + about.summary="Differential transcript usage with a Dirichlet-multinomial model" \ + about.url="https://bioconductor.org/packages/3.23/bioc/html/DRIMSeq.html" \ + about.license="GPL-3.0-or-later" \ + about.tags="Genomics,RNA-seq,differential-transcript-usage" + +RUN Rscript -e 'BiocManager::install("DRIMSeq", ask = FALSE, update = FALSE)' + +COPY verify.R /tmp/verify.R +RUN Rscript /tmp/verify.R && rm /tmp/verify.R + +CMD ["R"] diff --git a/images/drimseq/1.40.0/README.md b/images/drimseq/1.40.0/README.md new file mode 100644 index 0000000..c8bdff5 --- /dev/null +++ b/images/drimseq/1.40.0/README.md @@ -0,0 +1,16 @@ +# DRIMSeq 1.40.0 + +DRIMSeq 1.40.0 from Bioconductor 3.23 on R 4.6. The image build loads the +package and validates construction and filtering of a small transcript-count +dataset. + +```bash +docker run --rm -it -v "$PWD:/work" -w /work ghcr.io/yeolab/drimseq:1.40.0 +``` + +The default command starts R. Run an analysis script directly with: + +```bash +docker run --rm -v "$PWD:/work" -w /work \ + ghcr.io/yeolab/drimseq:1.40.0 Rscript analysis.R +``` diff --git a/images/drimseq/1.40.0/verify.R b/images/drimseq/1.40.0/verify.R new file mode 100644 index 0000000..b2c9f3c --- /dev/null +++ b/images/drimseq/1.40.0/verify.R @@ -0,0 +1,39 @@ +suppressPackageStartupMessages(library(DRIMSeq)) + +stopifnot(packageVersion("DRIMSeq") == "1.40.0") + +sample_ids <- paste0("sample", 1:6) +samples <- data.frame( + sample_id = sample_ids, + group = factor(rep(c("control", "treated"), each = 3)) +) +counts <- data.frame( + gene_id = rep(paste0("gene", 1:4), each = 2), + feature_id = paste0("tx", 1:8), + matrix( + c( + 80, 82, 79, 20, 18, 22, 20, 18, 21, 82, 84, 80, + 60, 63, 58, 55, 57, 59, 40, 37, 42, 45, 43, 41, + 90, 92, 88, 86, 91, 89, 10, 12, 11, 14, 9, 13, + 55, 51, 57, 25, 28, 27, 45, 49, 43, 75, 72, 73 + ), + nrow = 8, + byrow = TRUE, + dimnames = list(NULL, sample_ids) + ), + check.names = FALSE +) + +d <- dmDSdata(counts = counts, samples = samples) +d <- dmFilter( + d, + min_samps_feature_expr = 2, + min_feature_expr = 1, + min_samps_feature_prop = 2, + min_feature_prop = 0.01, + min_samps_gene_expr = 2, + min_gene_expr = 1 +) + +stopifnot(nrow(counts(d)) == 8, nrow(samples(d)) == 6) +message("DRIMSeq ", packageVersion("DRIMSeq"), " smoke test passed") diff --git a/images/suppa2/2.4/Dockerfile b/images/suppa2/2.4/Dockerfile new file mode 100644 index 0000000..bc47558 --- /dev/null +++ b/images/suppa2/2.4/Dockerfile @@ -0,0 +1,39 @@ +FROM python:3.13-slim@sha256:7c61056e61ac89e852de05f3dc6fa51a6dd2181797bceed46aa725dd7cb2cd3b + +LABEL software="SUPPA2" \ + software.version="2.4" \ + about.summary="Differential splicing and transcript usage analysis" \ + about.url="https://github.com/comprna/SUPPA" \ + about.license="MIT" \ + about.tags="Genomics,RNA-seq,alternative-splicing" + +ARG SUPPA_URL="https://github.com/comprna/SUPPA/archive/refs/tags/v2.4.tar.gz" +ARG SUPPA_SHA256="a71b3017dd13ceb4b8b837dfae201eb3bc55fb11dfcb201434f6ff1e4733093c" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL "$SUPPA_URL" -o /tmp/suppa.tar.gz \ + && echo "$SUPPA_SHA256 /tmp/suppa.tar.gz" | sha256sum -c - \ + && mkdir -p /opt/suppa \ + && tar -xzf /tmp/suppa.tar.gz --strip-components=1 -C /opt/suppa \ + && rm /tmp/suppa.tar.gz \ + && sed -i "s/version='%(prog)s 2.3'/version='%(prog)s 2.4'/" /opt/suppa/suppa.py \ + && sed -i 's/from statsmodels\.sandbox\.stats\.multicomp import multipletests/from statsmodels.stats.multitest import multipletests/' /opt/suppa/lib/diff_tools.py \ + && ln -s /opt/suppa/suppa.py /usr/local/bin/suppa.py \ + && chmod +x /opt/suppa/suppa.py + +RUN pip install --no-cache-dir \ + matplotlib==3.11.2 \ + numpy==2.5.3 \ + pandas==3.0.6 \ + scikit-learn==1.9.1 \ + scipy==1.18.1 \ + statsmodels==0.15.0 + +COPY verify.sh /tmp/verify.sh +RUN bash /tmp/verify.sh && rm /tmp/verify.sh + +WORKDIR /work +ENTRYPOINT ["python3", "/opt/suppa/suppa.py"] +CMD ["--help"] diff --git a/images/suppa2/2.4/README.md b/images/suppa2/2.4/README.md new file mode 100644 index 0000000..000d683 --- /dev/null +++ b/images/suppa2/2.4/README.md @@ -0,0 +1,15 @@ +# SUPPA2 2.4 + +SUPPA2 2.4 from the upstream GitHub release, with its Python dependencies +pinned. The upstream 2.4 tag still reports `2.3`; this image corrects that +release-string typo to `2.4` and updates SUPPA's legacy statsmodels import to +its current public location. The image build imports every dependency and +generates an isoform-event file from a small GTF. + +```bash +docker run --rm ghcr.io/yeolab/suppa2:2.4 --version +docker run --rm -v "$PWD:/work" ghcr.io/yeolab/suppa2:2.4 \ + generateEvents -i /work/transcripts.gtf -o /work/events -f ioe -e SE SS MX RI FL +``` + +Arguments are passed directly to `suppa.py`. diff --git a/images/suppa2/2.4/verify.sh b/images/suppa2/2.4/verify.sh new file mode 100644 index 0000000..ebc6edc --- /dev/null +++ b/images/suppa2/2.4/verify.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -euo pipefail + +test "$(python3 /opt/suppa/suppa.py --version)" = "SUPPA 2.4" + +python3 - <<'PY' +import matplotlib +import numpy +import pandas +import scipy +import sklearn +import statsmodels +PY + +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT +cat > "$work/transcripts.gtf" <<'GTF' +chr1 test exon 1 100 . + . gene_id "gene1"; transcript_id "tx1"; +chr1 test exon 201 300 . + . gene_id "gene1"; transcript_id "tx1"; +chr1 test exon 1 100 . + . gene_id "gene1"; transcript_id "tx2"; +chr1 test exon 151 175 . + . gene_id "gene1"; transcript_id "tx2"; +chr1 test exon 201 300 . + . gene_id "gene1"; transcript_id "tx2"; +GTF + +python3 /opt/suppa/suppa.py generateEvents \ + -i "$work/transcripts.gtf" \ + -o "$work/events" \ + -f ioi + +test -s "$work/events.ioi" +grep -q 'gene1;tx1' "$work/events.ioi" +echo "SUPPA2 2.4 smoke test passed" diff --git a/images/tappas/1.1.3/Dockerfile b/images/tappas/1.1.3/Dockerfile new file mode 100644 index 0000000..1d0eaa4 --- /dev/null +++ b/images/tappas/1.1.3/Dockerfile @@ -0,0 +1,56 @@ +FROM bioconductor/bioconductor_docker:RELEASE_3_23@sha256:821dbf9ac119eac41f177531c7ca8fc7084c99c04eb218a1aa7f52cb63bad5d9 + +LABEL software="tappAS" \ + software.version="1.1.3" \ + about.summary="Functional analysis of alternative transcript expression mechanisms" \ + about.url="https://github.com/ConesaLab/tappAS" \ + about.license="GPL-3.0-or-later" \ + about.tags="Genomics,RNA-seq,alternative-splicing,GUI" + +ARG TAPPAS_URL="https://github.com/ConesaLab/tappAS/releases/download/v1.1.3/tappAS-linux.zip" +ARG TAPPAS_SHA256="fa7eda0072610412acbb8a21281a2c563a69fc39255dce632f72690996643761" +ARG GOGLM_REF="e4d3f462b30514b9df9c6765c91ea2042439dcea" + +ENV GOGLM_REF=${GOGLM_REF} + +USER root + +RUN test "$(uname -m)" = "x86_64" \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + libasound2t64 \ + libgl1 \ + libgtk-3-0t64 \ + libxi6 \ + libxrender1 \ + libxtst6 \ + unzip \ + xvfb \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL "$TAPPAS_URL" -o /tmp/tappas.zip \ + && echo "$TAPPAS_SHA256 /tmp/tappas.zip" | sha256sum -c - \ + && unzip -q /tmp/tappas.zip -d /tmp/tappas-release \ + && mv /tmp/tappas-release/dist/linux/tappas /opt/tappas \ + && rm -rf /tmp/tappas.zip /tmp/tappas-release \ + && sed -i 's/java-options=-Xms8g/java-options=-Xms2g/' /opt/tappas/lib/app/tappas.cfg \ + && ln -s /opt/tappas/bin/tappas /usr/local/bin/tappas \ + && ln -s /usr/local/bin/Rscript /usr/bin/Rscript \ + && printf '%s\n' '1.1.3' > /opt/tappas/VERSION + +RUN Rscript -e 'BiocManager::install(c("DEXSeq", "edgeR", "NOISeq", "goseq", "maSigPro"), ask = FALSE, update = FALSE)' + +RUN Rscript -e 'install.packages(c("callr", "devtools", "ggpubr", "ggplot2", "MASS", "plyr", "remotes", "VennDiagram", "ggrepel", "cowplot", "tidyverse", "UpSetR"))' + +RUN Rscript -e 'remotes::install_github("gu-mi/GOglm", ref = Sys.getenv("GOGLM_REF"), upgrade = "never")' + +COPY verify.R /tmp/verify.R +RUN Rscript /tmp/verify.R \ + && test "$(cat /opt/tappas/VERSION)" = "1.1.3" \ + && unzip -l /opt/tappas/lib/app/tappas-jar-with-dependencies.jar | grep -q 'tappas/Tappas.class' \ + && /opt/tappas/lib/runtime/bin/java -version \ + && rm /tmp/verify.R + +WORKDIR /work +ENTRYPOINT ["/usr/local/bin/tappas"] diff --git a/images/tappas/1.1.3/README.md b/images/tappas/1.1.3/README.md new file mode 100644 index 0000000..68cf163 --- /dev/null +++ b/images/tappas/1.1.3/README.md @@ -0,0 +1,23 @@ +# tappAS 1.1.3 + +The upstream tappAS 1.1.3 Linux application with R 4.6 and the complete set of +R packages checked by tappAS preinstalled. The vendor application is x86-64 +only, so this image is published for `linux/amd64`. Its bundled 8 GB initial +Java heap is reduced to 2 GB for container and CI compatibility; Java may use +up to 75% of the container memory. + +tappAS is a desktop GUI and needs access to an X11 display. On a Linux desktop: + +```bash +xhost +local:docker +docker run --rm --platform linux/amd64 \ + -e DISPLAY \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + -v "$PWD:/work" \ + -v tappas-home:/root \ + ghcr.io/yeolab/tappas:1.1.3 +``` + +The named home volume keeps downloaded annotations, settings, logs, and +projects between runs. Allocate at least 4 CPUs, 6 GB RAM, and 20 GB of free +disk, matching tappAS's own minimum-resource checks. diff --git a/images/tappas/1.1.3/verify.R b/images/tappas/1.1.3/verify.R new file mode 100644 index 0000000..9ff9bb6 --- /dev/null +++ b/images/tappas/1.1.3/verify.R @@ -0,0 +1,15 @@ +required <- c( + "DEXSeq", "edgeR", "NOISeq", "goseq", "maSigPro", + "callr", "devtools", "ggplot2", "MASS", "plyr", "VennDiagram", + "ggrepel", "cowplot", "tidyverse", "UpSetR", "GOglm" +) + +missing <- required[!vapply(required, requireNamespace, logical(1), quietly = TRUE)] +stopifnot(length(missing) == 0) +stopifnot(packageVersion("DEXSeq") == "1.58.0") + +message( + "tappAS R dependency smoke test passed (R ", + paste(R.version$major, R.version$minor, sep = "."), + ")" +) From c74efb6b6e76292bd39a2bfb0750dcd47d2ccedc Mon Sep 17 00:00:00 2001 From: Brian Yee Date: Tue, 29 Sep 2026 16:40:05 -0700 Subject: [PATCH 2/2] Add PacBio isoform analysis containers --- CHANGELOG.md | 6 +++ README.md | 33 +++++++++++- images/SQANTI3/6.0.2/Dockerfile | 19 +++++++ images/SQANTI3/6.0.2/README.md | 15 ++++++ images/SQANTI3/6.0.2/verify.py | 31 +++++++++++ images/bambu/3.14.0/Dockerfile | 16 ++++++ images/bambu/3.14.0/README.md | 12 +++++ images/bambu/3.14.0/verify.R | 12 +++++ images/cerberus/1.1/Dockerfile | 65 +++++++++++++++++++++++ images/cerberus/1.1/README.md | 16 ++++++ images/cerberus/1.1/pyranges-compat.patch | 25 +++++++++ images/cerberus/1.1/verify.sh | 26 +++++++++ images/flair/3.0.1/Dockerfile | 27 ++++++++++ images/flair/3.0.1/README.md | 13 +++++ images/flair/3.0.1/verify.sh | 15 ++++++ images/ggtranscript/1.0.0/Dockerfile | 28 ++++++++++ images/ggtranscript/1.0.0/README.md | 14 +++++ images/ggtranscript/1.0.0/verify.R | 21 ++++++++ images/isoquant/4.0.0/Dockerfile | 27 ++++++++++ images/isoquant/4.0.0/README.md | 14 +++++ images/lapa/0.0.5/Dockerfile | 32 +++++++++++ images/lapa/0.0.5/README.md | 13 +++++ images/lapa/0.0.5/verify.py | 13 +++++ images/lr-kallisto/0.52.0/Dockerfile | 27 ++++++++++ images/lr-kallisto/0.52.0/README.md | 14 +++++ images/lr-kallisto/0.52.0/verify.sh | 15 ++++++ images/oarfish/0.10.3/Dockerfile | 27 ++++++++++ images/oarfish/0.10.3/README.md | 13 +++++ images/oarfish/0.10.3/verify.sh | 11 ++++ images/swan/3.2/Dockerfile | 29 ++++++++++ images/swan/3.2/README.md | 14 +++++ images/swan/3.2/verify.py | 24 +++++++++ images/talon/6.0.1/Dockerfile | 15 ++++++ images/talon/6.0.1/README.md | 15 ++++++ images/talon/6.0.1/verify.sh | 31 +++++++++++ 35 files changed, 727 insertions(+), 1 deletion(-) create mode 100644 images/SQANTI3/6.0.2/Dockerfile create mode 100644 images/SQANTI3/6.0.2/README.md create mode 100644 images/SQANTI3/6.0.2/verify.py create mode 100644 images/bambu/3.14.0/Dockerfile create mode 100644 images/bambu/3.14.0/README.md create mode 100644 images/bambu/3.14.0/verify.R create mode 100644 images/cerberus/1.1/Dockerfile create mode 100644 images/cerberus/1.1/README.md create mode 100644 images/cerberus/1.1/pyranges-compat.patch create mode 100644 images/cerberus/1.1/verify.sh create mode 100644 images/flair/3.0.1/Dockerfile create mode 100644 images/flair/3.0.1/README.md create mode 100644 images/flair/3.0.1/verify.sh create mode 100644 images/ggtranscript/1.0.0/Dockerfile create mode 100644 images/ggtranscript/1.0.0/README.md create mode 100644 images/ggtranscript/1.0.0/verify.R create mode 100644 images/isoquant/4.0.0/Dockerfile create mode 100644 images/isoquant/4.0.0/README.md create mode 100644 images/lapa/0.0.5/Dockerfile create mode 100644 images/lapa/0.0.5/README.md create mode 100644 images/lapa/0.0.5/verify.py create mode 100644 images/lr-kallisto/0.52.0/Dockerfile create mode 100644 images/lr-kallisto/0.52.0/README.md create mode 100644 images/lr-kallisto/0.52.0/verify.sh create mode 100644 images/oarfish/0.10.3/Dockerfile create mode 100644 images/oarfish/0.10.3/README.md create mode 100644 images/oarfish/0.10.3/verify.sh create mode 100644 images/swan/3.2/Dockerfile create mode 100644 images/swan/3.2/README.md create mode 100644 images/swan/3.2/verify.py create mode 100644 images/talon/6.0.1/Dockerfile create mode 100644 images/talon/6.0.1/README.md create mode 100644 images/talon/6.0.1/verify.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 62e1ca5..880484b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,12 @@ ### Added +- Latest stable containers for the PacBio full-length isoform sequencing + note's transcript-visualization and isoform-classification/quantification + tools: Swan 3.2, ggtranscript 1.0.0, SQANTI3 6.0.2, TALON 6.0.1, Cerberus + 1.1, LAPA 0.0.5, FLAIR 3.0.1, lr-kallisto 0.52.0, IsoQuant 4.0.0, Bambu + 3.14.0, and Oarfish 0.10.3. Each image includes a build-time functional + verification and a versioned GHCR publishing target. - Latest stable containers for all tools in the PacBio full-length isoform sequencing note's differential-analysis sections: tappAS 1.1.3, DESeq2 1.52.0, DRIMSeq 1.40.0, DEXSeq 1.58.0, and SUPPA2 2.4. Each image includes a diff --git a/README.md b/README.md index b1557d5..a6ed326 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ write`, already declared in the workflow. If organization policy prevents a publish, allow GitHub Actions to create packages for this repository or replace the token with an approved package-write token. -## PacBio full-length isoform differential analysis +## PacBio full-length isoform analysis The tools listed under **Differential transcript analysis** and **Differential splicing usage** in PacBio's full-length isoform sequencing application note @@ -64,6 +64,37 @@ image directory's README for launch examples and resource requirements. Every Dockerfile contains a build-time smoke test, so GitHub Actions publishes an image only after its installed version and a representative operation pass. +The application note's transcript-visualization tools are also available: + +| Application-note section | Tool | Image | +| --- | --- | --- | +| Transcript visualization | Swan 3.2 | `ghcr.io/yeolab/swan:3.2` | +| Transcript visualization | ggtranscript 1.0.0 | `ghcr.io/yeolab/ggtranscript:1.0.0` | + +The latest stable releases of the note's isoform classification and +quantification tools are packaged as: + +| Tool | Image | +| --- | --- | +| SQANTI3 6.0.2 | `ghcr.io/yeolab/sqanti3:6.0.2` | +| TALON 6.0.1 | `ghcr.io/yeolab/talon:6.0.1` | +| Cerberus 1.1 | `ghcr.io/yeolab/cerberus:1.1` | +| LAPA 0.0.5 | `ghcr.io/yeolab/lapa:0.0.5` | +| FLAIR 3.0.1 | `ghcr.io/yeolab/flair:3.0.1` | +| lr-kallisto / kallisto LongKmer 0.52.0 | `ghcr.io/yeolab/lr-kallisto:0.52.0` | +| IsoQuant 4.0.0 | `ghcr.io/yeolab/isoquant:4.0.0` | +| Bambu 3.14.0 | `ghcr.io/yeolab/bambu:3.14.0` | +| Oarfish 0.10.3 | `ghcr.io/yeolab/oarfish:0.10.3` | + +Swan and the Python command-line tools use checksum-pinned release artifacts; +ggtranscript is pinned to the current upstream commit because the project has +not published GitHub releases. SQANTI3 and TALON use exact Bioconda builds, +Bambu uses Bioconductor 3.23, and lr-kallisto uses kallisto's official +LongKmer binary. The per-image READMEs document entrypoints, architecture, and +upstream version caveats. Build-time tests exercise transcript parsing, +annotation conversion, database creation, indexing, or quantification as +appropriate instead of checking only `--version` output. + ## Pulling a Singularity/Apptainer image from GHCR Install [Apptainer](https://apptainer.org/) (or SingularityCE), then pull a diff --git a/images/SQANTI3/6.0.2/Dockerfile b/images/SQANTI3/6.0.2/Dockerfile new file mode 100644 index 0000000..bb59275 --- /dev/null +++ b/images/SQANTI3/6.0.2/Dockerfile @@ -0,0 +1,19 @@ +FROM quay.io/biocontainers/sqanti3:6.0.2--hdfd78af_1@sha256:3ebeb658fb2e0e308e73cba50dd3b97c63963d8225f303f298ad8ee76264939d + +LABEL software="SQANTI3" \ + software.version="6.0.2" \ + about.summary="Quality control and classification of long-read transcript isoforms" \ + about.url="https://github.com/ConesaLab/SQANTI3" \ + about.license="GPL-3.0-only" \ + about.tags="Genomics,long-read-RNA,isoform-classification" + +ENV MPLBACKEND=Agg \ + PYTHONPATH=/usr/local/share/sqanti3-6.0.2-1 \ + PYTHONWARNINGS=ignore::DeprecationWarning + +COPY verify.py /tmp/verify.py +RUN python /tmp/verify.py && rm /tmp/verify.py + +WORKDIR /work +ENTRYPOINT [] +CMD ["bash"] diff --git a/images/SQANTI3/6.0.2/README.md b/images/SQANTI3/6.0.2/README.md new file mode 100644 index 0000000..d7acaa4 --- /dev/null +++ b/images/SQANTI3/6.0.2/README.md @@ -0,0 +1,15 @@ +# SQANTI3 6.0.2 + +This image is pinned to the official BioContainers image for SQANTI3's exact +Bioconda 6.0.2 build and includes the complete aligner, command-line, Python, +R, and report-generation stack. + +```bash +docker run --rm -it -v "$PWD:/work" ghcr.io/yeolab/sqanti3:6.0.2 +``` + +The build verifies both SQANTI3's reported version and R dependencies, checks +the external tools, and parses a synthetic two-exon genePred transcript through +SQANTI3's own parser. + +Upstream: diff --git a/images/SQANTI3/6.0.2/verify.py b/images/SQANTI3/6.0.2/verify.py new file mode 100644 index 0000000..f1ebf1c --- /dev/null +++ b/images/SQANTI3/6.0.2/verify.py @@ -0,0 +1,31 @@ +import subprocess +import tempfile +from pathlib import Path + +from src.config import __version__ +from src.parsers import isoforms_parser + + +assert __version__ == "6.0.2" +assert "SQANTI3 6.0.2" in subprocess.check_output( + ["sqanti3_qc.py", "--version"], text=True +) + +Path("/test/logs").mkdir(parents=True, exist_ok=True) +with tempfile.TemporaryDirectory() as directory: + gene_pred = Path(directory) / "isoforms.genePred" + gene_pred.write_text( + "tx1\tchr1\t+\t0\t200\t0\t200\t2\t0,100,\t50,200,\t0\tgene1\tcmpl\tcmpl\t0,0,\n" + ) + parsed = isoforms_parser(str(gene_pred)) + assert len(parsed["chr1"]) == 1 + assert parsed["chr1"][0].junctions == [(50, 100)] + +for executable in ("gffread", "gmap", "minimap2", "samtools", "STAR"): + subprocess.run(["bash", "-c", f"command -v {executable}"], check=True) + +subprocess.run( + ["Rscript", "-e", "stopifnot(as.character(packageVersion('RColorConesa')) == '1.0.0')"], + check=True, +) +print("SQANTI3 6.0.2 smoke test passed") diff --git a/images/bambu/3.14.0/Dockerfile b/images/bambu/3.14.0/Dockerfile new file mode 100644 index 0000000..b8ec0e1 --- /dev/null +++ b/images/bambu/3.14.0/Dockerfile @@ -0,0 +1,16 @@ +FROM bioconductor/bioconductor_docker:RELEASE_3_23@sha256:821dbf9ac119eac41f177531c7ca8fc7084c99c04eb218a1aa7f52cb63bad5d9 + +LABEL software="Bambu" \ + software.version="3.14.0" \ + about.summary="Long-read transcript discovery and quantification" \ + about.url="https://bioconductor.org/packages/3.23/bioc/html/bambu.html" \ + about.license="GPL-3.0-only" \ + about.tags="Genomics,long-read-RNA,isoform-quantification" + +RUN Rscript -e 'BiocManager::install("bambu", ask = FALSE, update = FALSE)' + +COPY verify.R /tmp/verify.R +RUN Rscript /tmp/verify.R && rm /tmp/verify.R + +WORKDIR /work +CMD ["R"] diff --git a/images/bambu/3.14.0/README.md b/images/bambu/3.14.0/README.md new file mode 100644 index 0000000..10ebdf7 --- /dev/null +++ b/images/bambu/3.14.0/README.md @@ -0,0 +1,12 @@ +# Bambu 3.14.0 + +This image installs Bambu 3.14.0 from Bioconductor 3.23 on R 4.6. + +```bash +docker run --rm -it -v "$PWD:/work" ghcr.io/yeolab/bambu:3.14.0 +``` + +The build verifies the package version and converts a synthetic two-exon GTF +into Bambu's prepared annotation representation. + +Upstream: diff --git a/images/bambu/3.14.0/verify.R b/images/bambu/3.14.0/verify.R new file mode 100644 index 0000000..803e0e8 --- /dev/null +++ b/images/bambu/3.14.0/verify.R @@ -0,0 +1,12 @@ +suppressPackageStartupMessages(library(bambu)) +stopifnot(as.character(packageVersion("bambu")) == "3.14.0") + +gtf <- tempfile(fileext = ".gtf") +writeLines(c( + 'chr1\tsmoke\ttranscript\t1\t200\t.\t+\t.\tgene_id "g1"; transcript_id "t1";', + 'chr1\tsmoke\texon\t1\t50\t.\t+\t.\tgene_id "g1"; transcript_id "t1"; exon_id "e1";', + 'chr1\tsmoke\texon\t101\t200\t.\t+\t.\tgene_id "g1"; transcript_id "t1"; exon_id "e2";' +), gtf) +annotations <- prepareAnnotations(gtf) +stopifnot(length(annotations) == 1L) +message("Bambu 3.14.0 smoke test passed") diff --git a/images/cerberus/1.1/Dockerfile b/images/cerberus/1.1/Dockerfile new file mode 100644 index 0000000..336bcde --- /dev/null +++ b/images/cerberus/1.1/Dockerfile @@ -0,0 +1,65 @@ +FROM python:3.10-slim@sha256:9d53d8d4c0e882f61913025db53b3aec4ef74336082a9ab47d8a14e9e8329b00 + +LABEL software="Cerberus" \ + software.version="1.1" \ + about.summary="Transcript start, end, and intron-chain annotation" \ + about.url="https://github.com/mortazavilab/cerberus" \ + about.license="MIT" \ + about.tags="Genomics,long-read-RNA,isoform-classification" + +ARG LAPA_URL="https://files.pythonhosted.org/packages/64/cc/e9059f9e9108f5a9a22e08164e29be3a937bab6c7636a8c2e51bf5b85ceb/lapa-0.0.5.tar.gz" +ARG LAPA_SHA256="3240405cbdd0e68c344676bc780e62368359024726f0ef2c04ef487d49793669" +ARG SWAN_URL="https://files.pythonhosted.org/packages/39/8e/153cc2311f3b7e3888540a18689673b312b3e72c68ef65cce9f7c482f8ed/swan_vis-3.2.tar.gz" +ARG SWAN_SHA256="b7da08a52cc37592378428f72a22a7e994af8d63118c8949c2b758b5dfc7c1db" +ARG CERBERUS_URL="https://github.com/mortazavilab/cerberus/archive/refs/tags/v1.1.tar.gz" +ARG CERBERUS_SHA256="c1693ef8ab48cdf841e769eb49d5d957b1d426635a6f7fdfe162914673d7de9f" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends build-essential ca-certificates curl patch \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL "$LAPA_URL" -o /tmp/lapa.tar.gz \ + && echo "$LAPA_SHA256 /tmp/lapa.tar.gz" | sha256sum -c - \ + && curl -fsSL "$SWAN_URL" -o /tmp/swan.tar.gz \ + && echo "$SWAN_SHA256 /tmp/swan.tar.gz" | sha256sum -c - \ + && pip install --no-cache-dir \ + "numpy==1.23.0" \ + "pandas==1.5.3" \ + "scipy==1.10.1" \ + "matplotlib==3.7.5" \ + "h5py==3.8.0" \ + "blosc2==2.0.0" \ + "tables==3.8.0" \ + "pandarallel==1.6.5" \ + "anndata==0.9.2" \ + "scanpy==1.9.8" \ + "numba==0.57.1" \ + "scikit-learn==1.3.2" \ + "umap-learn==0.5.5" \ + "sparse==0.14.0" \ + "python-ternary==1.0.8" \ + "cython<3" \ + && pip install --no-cache-dir --no-build-isolation "sorted-nearest==0.0.33" \ + && pip install --no-cache-dir "pyranges==0.1.4" \ + && pip install --no-cache-dir /tmp/lapa.tar.gz \ + && pip install --no-cache-dir /tmp/swan.tar.gz \ + && rm -rf /tmp/lapa.tar.gz /tmp/swan.tar.gz \ + && apt-get purge -y --auto-remove build-essential + +COPY pyranges-compat.patch /tmp/pyranges-compat.patch + +RUN curl -fsSL "$CERBERUS_URL" -o /tmp/cerberus.tar.gz \ + && echo "$CERBERUS_SHA256 /tmp/cerberus.tar.gz" | sha256sum -c - \ + && mkdir /tmp/cerberus \ + && tar -xzf /tmp/cerberus.tar.gz --strip-components=1 -C /tmp/cerberus \ + && sed -i "s/version='0.0.idk'/version='1.1'/" /tmp/cerberus/setup.py \ + && patch -d /tmp/cerberus -p1 < /tmp/pyranges-compat.patch \ + && pip install --no-cache-dir --no-deps /tmp/cerberus \ + && rm -rf /tmp/cerberus.tar.gz /tmp/cerberus /tmp/pyranges-compat.patch \ + && apt-get purge -y --auto-remove curl patch + +COPY verify.sh /tmp/verify.sh +RUN bash /tmp/verify.sh && rm /tmp/verify.sh + +WORKDIR /work +ENTRYPOINT ["cerberus"] +CMD ["--help"] diff --git a/images/cerberus/1.1/README.md b/images/cerberus/1.1/README.md new file mode 100644 index 0000000..87d8171 --- /dev/null +++ b/images/cerberus/1.1/README.md @@ -0,0 +1,16 @@ +# Cerberus 1.1 + +This image installs the latest Cerberus tag from a checksum-pinned source +archive. Upstream's placeholder package version is normalized to 1.1 so the +installed metadata agrees with the immutable tag. +The image also carries a compatibility patch that preserves strand +metadata when Cerberus reconstructs a PyRanges object with PyRanges 0.1.4. + +```bash +docker run --rm -v "$PWD:/work" ghcr.io/yeolab/cerberus:1.1 --help +``` + +Arguments are passed directly to `cerberus`. The build converts a synthetic +two-exon GTF to both TSS BED and intron-chain TSV outputs. + +Upstream: diff --git a/images/cerberus/1.1/pyranges-compat.patch b/images/cerberus/1.1/pyranges-compat.patch new file mode 100644 index 0000000..e55921c --- /dev/null +++ b/images/cerberus/1.1/pyranges-compat.patch @@ -0,0 +1,25 @@ +diff --git a/cerberus/cerberus.py b/cerberus/cerberus.py +index 35a87f3..ad34177 100644 +--- a/cerberus/cerberus.py ++++ b/cerberus/cerberus.py +@@ -232,0 +233 @@ def number_gtf_ends(bed, gtf, mode): ++ gr_gtf['Strand'] = gr_gtf['Strand'].astype(str) +@@ -237,0 +239 @@ def number_gtf_ends(bed, gtf, mode): ++ bed['Strand'] = bed['Strand'].astype(str) +@@ -245 +247 @@ def number_gtf_ends(bed, gtf, mode): +- temp = pr.PyRanges(gr_gtf.features.tss().df[cols]) ++ temp = gr_gtf.features.tss().df[cols] +@@ -247 +249,3 @@ def number_gtf_ends(bed, gtf, mode): +- temp = pr.PyRanges(gr_gtf.features.tes().df[cols]) ++ temp = gr_gtf.features.tes().df[cols] ++ temp['Strand'] = temp['Strand'].astype(str) ++ temp = pr.PyRanges(temp) +@@ -1665,0 +1670 @@ def get_ends_from_gtf(gtf, mode, dist, slack): ++ gr_gtf['Strand'] = gr_gtf['Strand'].astype(str) +@@ -1678 +1683,3 @@ def get_ends_from_gtf(gtf, mode, dist, slack): +- bed = pr.PyRanges(bed.rename({'ThickStart': 'gene_id'}, axis=1)) ++ bed = bed.rename({'ThickStart': 'gene_id'}, axis=1) ++ bed['Strand'] = bed['Strand'].astype(str) ++ bed = pr.PyRanges(bed) +@@ -1778,0 +1786 @@ def get_ics_from_gtf(gtf): ++ df['Strand'] = df['Strand'].astype(str) diff --git a/images/cerberus/1.1/verify.sh b/images/cerberus/1.1/verify.sh new file mode 100644 index 0000000..3970336 --- /dev/null +++ b/images/cerberus/1.1/verify.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -euo pipefail + +python - <<'PY' +from importlib.metadata import version +assert version("cerberus") == "1.1" +import cerberus.cerberus +PY +python -m pip check +cerberus --help >/dev/null + +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT +printf '%b\n' \ + 'chr1\tsmoke\tgene\t1\t200\t.\t+\t.\tgene_id "g1"; gene_name "G1";' \ + 'chr1\tsmoke\ttranscript\t1\t200\t.\t+\t.\tgene_id "g1"; transcript_id "t1"; gene_name "G1"; transcript_name "T1";' \ + 'chr1\tsmoke\texon\t1\t50\t.\t+\t.\tgene_id "g1"; transcript_id "t1"; exon_number "1";' \ + 'chr1\tsmoke\texon\t101\t200\t.\t+\t.\tgene_id "g1"; transcript_id "t1"; exon_number "2";' \ + > "$work/input.gtf" + +cerberus gtf_to_bed --gtf "$work/input.gtf" --mode tss -o "$work/tss.bed" +cerberus gtf_to_ics --gtf "$work/input.gtf" -o "$work/ics.tsv" +test -s "$work/tss.bed" +test -s "$work/ics.tsv" +grep -q 'g1_1' "$work/ics.tsv" +echo "Cerberus 1.1 smoke test passed" diff --git a/images/flair/3.0.1/Dockerfile b/images/flair/3.0.1/Dockerfile new file mode 100644 index 0000000..ad9b913 --- /dev/null +++ b/images/flair/3.0.1/Dockerfile @@ -0,0 +1,27 @@ +FROM python:3.13-slim@sha256:7c61056e61ac89e852de05f3dc6fa51a6dd2181797bceed46aa725dd7cb2cd3b + +LABEL software="FLAIR" \ + software.version="3.0.1" \ + about.summary="Full-length alternative isoform analysis of RNA" \ + about.url="https://github.com/BrooksLabUCSC/flair" \ + about.license="BSD-3-Clause" \ + about.tags="Genomics,long-read-RNA,isoform-discovery,isoform-quantification" + +ARG FLAIR_URL="https://files.pythonhosted.org/packages/64/c6/a5fb6bf91a36577e594753ebf97008c3536da9de0959e68343e7c5e33d5f/flair_brookslab-3.0.1.tar.gz" +ARG FLAIR_SHA256="04554034b62f95a74b1f062d5c48d39fcb21ffd122085ed7ffddb6b13de60bcf" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends bedtools build-essential ca-certificates curl minimap2 samtools zlib1g-dev \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL "$FLAIR_URL" -o /tmp/flair.tar.gz \ + && echo "$FLAIR_SHA256 /tmp/flair.tar.gz" | sha256sum -c - \ + && pip install --no-cache-dir /tmp/flair.tar.gz \ + && rm /tmp/flair.tar.gz \ + && apt-get purge -y --auto-remove build-essential zlib1g-dev + +COPY verify.sh /tmp/verify.sh +RUN bash /tmp/verify.sh && rm /tmp/verify.sh + +WORKDIR /work +ENTRYPOINT ["flair"] +CMD ["--help"] diff --git a/images/flair/3.0.1/README.md b/images/flair/3.0.1/README.md new file mode 100644 index 0000000..cff279f --- /dev/null +++ b/images/flair/3.0.1/README.md @@ -0,0 +1,13 @@ +# FLAIR 3.0.1 + +This image installs the latest stable FLAIR release plus its external runtime +tools: minimap2, samtools, and bedtools. + +```bash +docker run --rm -v "$PWD:/work" ghcr.io/yeolab/flair:3.0.1 --help +``` + +Arguments are passed directly to `flair`. The build checks all companion +executables and performs a BED-to-GTF conversion. + +Upstream: diff --git a/images/flair/3.0.1/verify.sh b/images/flair/3.0.1/verify.sh new file mode 100644 index 0000000..fc65313 --- /dev/null +++ b/images/flair/3.0.1/verify.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -euo pipefail + +[[ "$(flair --version)" == "FLAIR 3.0.1" ]] +for command in minimap2 samtools bedtools gtf_to_bed bed_to_gtf; do + command -v "$command" >/dev/null +done + +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT +printf 'chr1\t1\t100\ttranscript1\t0\t+\t1\t100\t0\t1\t99,\t0,\n' > "$work/input.bed" +bed_to_gtf --force "$work/input.bed" > "$work/output.gtf" +test -s "$work/output.gtf" +grep -q 'transcript_id "transcript1"' "$work/output.gtf" +echo "FLAIR 3.0.1 smoke test passed" diff --git a/images/ggtranscript/1.0.0/Dockerfile b/images/ggtranscript/1.0.0/Dockerfile new file mode 100644 index 0000000..907214f --- /dev/null +++ b/images/ggtranscript/1.0.0/Dockerfile @@ -0,0 +1,28 @@ +FROM bioconductor/bioconductor_docker:RELEASE_3_23@sha256:821dbf9ac119eac41f177531c7ca8fc7084c99c04eb218a1aa7f52cb63bad5d9 + +LABEL software="ggtranscript" \ + software.version="1.0.0" \ + about.summary="Transcript structure and annotation visualization with ggplot2" \ + about.url="https://github.com/dzhang32/ggtranscript" \ + about.license="MIT" \ + about.tags="Genomics,RNA-seq,transcript-visualization" + +ARG GGTRANSCRIPT_COMMIT="682a0df688ad242fa262d0b0557bf973dc202787" +ARG GGTRANSCRIPT_SHA256="004fc8d7de0dea3d53c11ac2bb625c649e778b90e0c9ee7b845370282b007001" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL "https://github.com/dzhang32/ggtranscript/archive/${GGTRANSCRIPT_COMMIT}.tar.gz" -o /tmp/ggtranscript.tar.gz \ + && echo "$GGTRANSCRIPT_SHA256 /tmp/ggtranscript.tar.gz" | sha256sum -c - + +RUN Rscript -e 'BiocManager::install(c("GenomicRanges", "S4Vectors", "GenomeInfoDb"), ask = FALSE, update = FALSE)' \ + && Rscript -e 'install.packages(c("dplyr", "ggplot2", "magrittr", "rlang", "ggrepel"), repos = "https://cloud.r-project.org")' \ + && R CMD INSTALL /tmp/ggtranscript.tar.gz \ + && rm /tmp/ggtranscript.tar.gz + +COPY verify.R /tmp/verify.R +RUN Rscript /tmp/verify.R && rm /tmp/verify.R + +WORKDIR /work +CMD ["R"] diff --git a/images/ggtranscript/1.0.0/README.md b/images/ggtranscript/1.0.0/README.md new file mode 100644 index 0000000..25941ee --- /dev/null +++ b/images/ggtranscript/1.0.0/README.md @@ -0,0 +1,14 @@ +# ggtranscript 1.0.0 + +ggtranscript extends ggplot2 with geoms and helpers for transcript structures. +The project has no release tags, so this image pins the current upstream +version 1.0.0 to commit `682a0df688ad242fa262d0b0557bf973dc202787`. + +```bash +docker run --rm -it -v "$PWD:/work" ghcr.io/yeolab/ggtranscript:1.0.0 +``` + +The build verifies package version 1.0.0, derives an intron from synthetic +exons, and builds a ggplot using `geom_range()`. + +Upstream: diff --git a/images/ggtranscript/1.0.0/verify.R b/images/ggtranscript/1.0.0/verify.R new file mode 100644 index 0000000..cc2e437 --- /dev/null +++ b/images/ggtranscript/1.0.0/verify.R @@ -0,0 +1,21 @@ +suppressPackageStartupMessages({ + library(ggplot2) + library(ggtranscript) +}) + +stopifnot(as.character(packageVersion("ggtranscript")) == "1.0.0") + +exons <- data.frame( + seqnames = c("chr1", "chr1", "chr1"), + start = c(1, 101, 1), + end = c(50, 150, 150), + strand = c("+", "+", "+"), + transcript_name = c("tx1", "tx1", "tx2") +) +introns <- to_intron(exons, "transcript_name") +stopifnot(nrow(introns) == 1L) + +plot <- ggplot(exons, aes(xstart = start, xend = end, y = transcript_name)) + + geom_range() +stopifnot(length(ggplot_build(plot)$data) == 1L) +message("ggtranscript 1.0.0 smoke test passed") diff --git a/images/isoquant/4.0.0/Dockerfile b/images/isoquant/4.0.0/Dockerfile new file mode 100644 index 0000000..0435f70 --- /dev/null +++ b/images/isoquant/4.0.0/Dockerfile @@ -0,0 +1,27 @@ +FROM python:3.12-slim@sha256:f77ac9e44ae96ef2c90b8053ea08c31f8be030f824196b0ae4db6d462c84e51f + +LABEL software="IsoQuant" \ + software.version="4.0.0" \ + about.summary="Isoform reconstruction and quantification from long RNA reads" \ + about.url="https://github.com/ablab/IsoQuant" \ + about.license="GPL-2.0-only" \ + about.tags="Genomics,long-read-RNA,isoform-discovery,isoform-quantification" + +ARG ISOQUANT_URL="https://files.pythonhosted.org/packages/42/f9/28b0f11012d105f710ef9e8ecc5f6482baf3fc854630260af8e6d211deaf/isoquant-4.0.0.tar.gz" +ARG ISOQUANT_SHA256="68fa634646525abcc6ca159f18924e430fe28455b646e9a6f49fac8bcc319293" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends build-essential ca-certificates curl minimap2 samtools zlib1g-dev \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL "$ISOQUANT_URL" -o /tmp/isoquant.tar.gz \ + && echo "$ISOQUANT_SHA256 /tmp/isoquant.tar.gz" | sha256sum -c - \ + && pip install --no-cache-dir /tmp/isoquant.tar.gz \ + && rm /tmp/isoquant.tar.gz \ + && apt-get purge -y --auto-remove build-essential zlib1g-dev + +RUN isoquant --test \ + && rm -rf /tmp/isoquant_test /isoquant_test + +WORKDIR /work +ENTRYPOINT ["isoquant"] +CMD ["--help"] diff --git a/images/isoquant/4.0.0/README.md b/images/isoquant/4.0.0/README.md new file mode 100644 index 0000000..41c43f0 --- /dev/null +++ b/images/isoquant/4.0.0/README.md @@ -0,0 +1,14 @@ +# IsoQuant 4.0.0 + +This image installs the latest stable IsoQuant release with minimap2 and +samtools. + +```bash +docker run --rm -v "$PWD:/work" ghcr.io/yeolab/isoquant:4.0.0 --help +``` + +Arguments are passed directly to `isoquant`. The build runs upstream's bundled +`isoquant --test` toy-data workflow, exercising mapping, isoform discovery, +and quantification. + +Upstream: diff --git a/images/lapa/0.0.5/Dockerfile b/images/lapa/0.0.5/Dockerfile new file mode 100644 index 0000000..54880f2 --- /dev/null +++ b/images/lapa/0.0.5/Dockerfile @@ -0,0 +1,32 @@ +FROM python:3.10-slim@sha256:9d53d8d4c0e882f61913025db53b3aec4ef74336082a9ab47d8a14e9e8329b00 + +LABEL software="LAPA" \ + software.version="0.0.5" \ + about.summary="Alternative polyadenylation and transcript-end analysis" \ + about.url="https://github.com/mortazavilab/lapa" \ + about.license="MIT" \ + about.tags="Genomics,long-read-RNA,transcript-ends" + +ARG LAPA_URL="https://files.pythonhosted.org/packages/64/cc/e9059f9e9108f5a9a22e08164e29be3a937bab6c7636a8c2e51bf5b85ceb/lapa-0.0.5.tar.gz" +ARG LAPA_SHA256="3240405cbdd0e68c344676bc780e62368359024726f0ef2c04ef487d49793669" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends build-essential ca-certificates curl \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL "$LAPA_URL" -o /tmp/lapa.tar.gz \ + && echo "$LAPA_SHA256 /tmp/lapa.tar.gz" | sha256sum -c - \ + && pip install --no-cache-dir "numpy==1.23.0" "cython<3" \ + && pip install --no-cache-dir --no-build-isolation "sorted-nearest==0.0.33" \ + && pip install --no-cache-dir /tmp/lapa.tar.gz \ + && rm /tmp/lapa.tar.gz \ + && apt-get purge -y --auto-remove build-essential + +COPY verify.py /tmp/verify.py +RUN python /tmp/verify.py \ + && lapa --help >/dev/null \ + && lapa_tss --help >/dev/null \ + && rm /tmp/verify.py + +WORKDIR /work +ENTRYPOINT ["lapa"] +CMD ["--help"] diff --git a/images/lapa/0.0.5/README.md b/images/lapa/0.0.5/README.md new file mode 100644 index 0000000..9e4f7c3 --- /dev/null +++ b/images/lapa/0.0.5/README.md @@ -0,0 +1,13 @@ +# LAPA 0.0.5 + +This image installs the latest stable LAPA release from its checksum-pinned +PyPI source archive. + +```bash +docker run --rm -v "$PWD:/work" ghcr.io/yeolab/lapa:0.0.5 --help +``` + +Arguments are passed directly to `lapa`. The build also checks `lapa_tss` and +performs LAPA's peak selection on a synthetic transcript-end cluster. + +Upstream: diff --git a/images/lapa/0.0.5/verify.py b/images/lapa/0.0.5/verify.py new file mode 100644 index 0000000..366ec3d --- /dev/null +++ b/images/lapa/0.0.5/verify.py @@ -0,0 +1,13 @@ +from importlib.metadata import version + +from lapa.cluster import Cluster + + +assert version("lapa") == "0.0.5" +cluster = Cluster("chr1", 10, 11, "+") +cluster.extend(11, 1) +cluster.extend(12, 5) +cluster.extend(13, 1) +assert cluster.total_count == 7 +assert cluster.peak(window=3, std=1) == 12 +print("LAPA 0.0.5 smoke test passed") diff --git a/images/lr-kallisto/0.52.0/Dockerfile b/images/lr-kallisto/0.52.0/Dockerfile new file mode 100644 index 0000000..d6c2788 --- /dev/null +++ b/images/lr-kallisto/0.52.0/Dockerfile @@ -0,0 +1,27 @@ +FROM debian:bookworm-slim@sha256:3783cc01769c7b2b1b83a5c5ad96c815348e28ed7da68e2e3687004faa906251 + +LABEL software="lr-kallisto" \ + software.version="0.52.0" \ + about.summary="Long-read pseudoalignment and transcript quantification with kallisto" \ + about.url="https://github.com/pachterlab/kallisto" \ + about.license="BSD-2-Clause" \ + about.tags="Genomics,long-read-RNA,isoform-quantification" + +ARG KALLISTO_URL="https://github.com/pachterlab/kallisto/releases/download/v0.52.0/kallisto_linux_LongKmer-v0.52.0.tar.gz" +ARG KALLISTO_SHA256="88ea2e1f76537d2c866d54a2b9f7d2d4fa6ef095712b0da3cb65ebd74bc7b30c" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL "$KALLISTO_URL" -o /tmp/kallisto.tar.gz \ + && echo "$KALLISTO_SHA256 /tmp/kallisto.tar.gz" | sha256sum -c - \ + && tar -xzf /tmp/kallisto.tar.gz -C /tmp \ + && install -m 0755 /tmp/kallisto/kallisto /usr/local/bin/kallisto \ + && rm -rf /tmp/kallisto /tmp/kallisto.tar.gz + +COPY verify.sh /tmp/verify.sh +RUN bash /tmp/verify.sh && rm /tmp/verify.sh + +WORKDIR /work +ENTRYPOINT ["kallisto"] +CMD ["version"] diff --git a/images/lr-kallisto/0.52.0/README.md b/images/lr-kallisto/0.52.0/README.md new file mode 100644 index 0000000..46bbb35 --- /dev/null +++ b/images/lr-kallisto/0.52.0/README.md @@ -0,0 +1,14 @@ +# lr-kallisto 0.52.0 + +The PacBio note's lr-kallisto link now points to the main kallisto project. +This image uses kallisto 0.52.0's official checksum-pinned `LongKmer` Linux +artifact, which supports long k-mers for long-read quantification. + +```bash +docker run --rm -v "$PWD:/work" ghcr.io/yeolab/lr-kallisto:0.52.0 version +``` + +Arguments are passed directly to `kallisto`. The build creates a 63-mer index +and quantifies a synthetic single read. + +Upstream: diff --git a/images/lr-kallisto/0.52.0/verify.sh b/images/lr-kallisto/0.52.0/verify.sh new file mode 100644 index 0000000..a18bcc6 --- /dev/null +++ b/images/lr-kallisto/0.52.0/verify.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -euo pipefail + +[[ "$(kallisto version)" == "kallisto, version 0.52.0" ]] +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT + +sequence="ACGTACGTTGCAAGTCGATCGTACGATGCTAGCTAGGCTAACGTTGCACTGATCGTAGCTAGCATCGATGCTAGCTACGATCGTACGTTAGC" +printf '>tx1\n%s%s%s\n' "$sequence" "$sequence" "$sequence" > "$work/transcripts.fa" +printf '@read1\n%s\n+\n%s\n' "$sequence" "$(printf '%*s' "${#sequence}" '' | tr ' ' I)" > "$work/reads.fq" +kallisto index -k 63 -i "$work/transcripts.idx" "$work/transcripts.fa" +kallisto quant --single -l "${#sequence}" -s 10 -i "$work/transcripts.idx" -o "$work/quant" "$work/reads.fq" +test -s "$work/quant/abundance.tsv" +grep -q '^tx1' "$work/quant/abundance.tsv" +echo "lr-kallisto 0.52.0 smoke test passed" diff --git a/images/oarfish/0.10.3/Dockerfile b/images/oarfish/0.10.3/Dockerfile new file mode 100644 index 0000000..7f29ae1 --- /dev/null +++ b/images/oarfish/0.10.3/Dockerfile @@ -0,0 +1,27 @@ +FROM debian:bookworm-slim@sha256:3783cc01769c7b2b1b83a5c5ad96c815348e28ed7da68e2e3687004faa906251 + +LABEL software="Oarfish" \ + software.version="0.10.3" \ + about.summary="Fast and accurate long-read transcript quantification" \ + about.url="https://github.com/COMBINE-lab/oarfish" \ + about.license="BSD-3-Clause" \ + about.tags="Genomics,long-read-RNA,isoform-quantification" + +ARG OARFISH_URL="https://github.com/COMBINE-lab/oarfish/releases/download/v0.10.3/oarfish-x86_64-unknown-linux-gnu.tar.xz" +ARG OARFISH_SHA256="ad23cd748ad306ea736e76670d40298ee66704bbcbcb4ddeedb301bfaee57461" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl xz-utils \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL "$OARFISH_URL" -o /tmp/oarfish.tar.xz \ + && echo "$OARFISH_SHA256 /tmp/oarfish.tar.xz" | sha256sum -c - \ + && tar -xJf /tmp/oarfish.tar.xz -C /tmp \ + && install -m 0755 /tmp/oarfish-x86_64-unknown-linux-gnu/oarfish /usr/local/bin/oarfish \ + && rm -rf /tmp/oarfish-x86_64-unknown-linux-gnu /tmp/oarfish.tar.xz + +COPY verify.sh /tmp/verify.sh +RUN bash /tmp/verify.sh && rm /tmp/verify.sh + +WORKDIR /work +ENTRYPOINT ["oarfish"] +CMD ["--help"] diff --git a/images/oarfish/0.10.3/README.md b/images/oarfish/0.10.3/README.md new file mode 100644 index 0000000..6d33b7b --- /dev/null +++ b/images/oarfish/0.10.3/README.md @@ -0,0 +1,13 @@ +# Oarfish 0.10.3 + +This image installs the latest stable Oarfish Linux release from its official +checksum-pinned binary archive. + +```bash +docker run --rm -v "$PWD:/work" ghcr.io/yeolab/oarfish:0.10.3 --help +``` + +Arguments are passed directly to `oarfish`. The build verifies the version and +constructs an Oarfish index from a synthetic transcriptome. + +Upstream: diff --git a/images/oarfish/0.10.3/verify.sh b/images/oarfish/0.10.3/verify.sh new file mode 100644 index 0000000..1c85971 --- /dev/null +++ b/images/oarfish/0.10.3/verify.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +[[ "$(oarfish --version)" == "oarfish 0.10.3" ]] +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT +sequence="ACGTACGTTGCAAGTCGATCGTACGATGCTAGCTAGGCTAACGTTGCACTGATCGTAGCTAGCATCGATGCTAGCTACGATCGTACGTTAGC" +printf '>tx1\n%s%s%s%s%s%s\n' "$sequence" "$sequence" "$sequence" "$sequence" "$sequence" "$sequence" > "$work/transcripts.fa" +oarfish --only-index --annotated "$work/transcripts.fa" --index-out "$work/index" --seq-tech pac-bio-hifi +test -s "$work/index" +echo "Oarfish 0.10.3 smoke test passed" diff --git a/images/swan/3.2/Dockerfile b/images/swan/3.2/Dockerfile new file mode 100644 index 0000000..9200373 --- /dev/null +++ b/images/swan/3.2/Dockerfile @@ -0,0 +1,29 @@ +FROM python:3.10-slim@sha256:9d53d8d4c0e882f61913025db53b3aec4ef74336082a9ab47d8a14e9e8329b00 + +LABEL software="Swan" \ + software.version="3.2" \ + about.summary="Analysis and visualization of transcript isoforms" \ + about.url="https://github.com/mortazavilab/swan_vis" \ + about.license="MIT" \ + about.tags="Genomics,long-read-RNA,transcript-visualization" + +ARG SWAN_URL="https://files.pythonhosted.org/packages/39/8e/153cc2311f3b7e3888540a18689673b312b3e72c68ef65cce9f7c482f8ed/swan_vis-3.2.tar.gz" +ARG SWAN_SHA256="b7da08a52cc37592378428f72a22a7e994af8d63118c8949c2b758b5dfc7c1db" + +ENV MPLBACKEND=Agg + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl gcc g++ \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL "$SWAN_URL" -o /tmp/swan.tar.gz \ + && echo "$SWAN_SHA256 /tmp/swan.tar.gz" | sha256sum -c - \ + && pip install --no-cache-dir "numpy==1.26.4" \ + && pip install --no-cache-dir /tmp/swan.tar.gz \ + && rm /tmp/swan.tar.gz \ + && apt-get purge -y --auto-remove gcc g++ + +COPY verify.py /tmp/verify.py +RUN python /tmp/verify.py && rm /tmp/verify.py + +WORKDIR /work +CMD ["python"] diff --git a/images/swan/3.2/README.md b/images/swan/3.2/README.md new file mode 100644 index 0000000..f04ffff --- /dev/null +++ b/images/swan/3.2/README.md @@ -0,0 +1,14 @@ +# Swan 3.2 + +Swan analyzes and visualizes transcript isoforms. This image installs the +latest stable PyPI release from its checksum-pinned source archive. + +```bash +docker run --rm -it -v "$PWD:/work" ghcr.io/yeolab/swan:3.2 +``` + +The build verifies the installed distribution version and loads a synthetic +two-exon GTF into a `SwanGraph`. Matplotlib uses the non-interactive `Agg` +backend so plots can be rendered on headless compute nodes. + +Upstream: diff --git a/images/swan/3.2/verify.py b/images/swan/3.2/verify.py new file mode 100644 index 0000000..7c92b79 --- /dev/null +++ b/images/swan/3.2/verify.py @@ -0,0 +1,24 @@ +from importlib.metadata import version +from pathlib import Path + +import swan_vis as swan + + +assert version("swan-vis") == "3.2" + +gtf = Path("/tmp/swan-smoke.gtf") +gtf.write_text( + 'chr1\tsmoke\tgene\t1\t200\t.\t+\t.\tgene_id "g1"; gene_name "G1";\n' + 'chr1\tsmoke\ttranscript\t1\t200\t.\t+\t.\tgene_id "g1"; ' + 'transcript_id "t1"; gene_name "G1"; transcript_name "T1";\n' + 'chr1\tsmoke\texon\t1\t50\t.\t+\t.\tgene_id "g1"; ' + 'transcript_id "t1"; exon_number "1";\n' + 'chr1\tsmoke\texon\t101\t200\t.\t+\t.\tgene_id "g1"; ' + 'transcript_id "t1"; exon_number "2";\n' +) + +graph = swan.SwanGraph() +graph.add_transcriptome(str(gtf)) +assert graph.t_df.shape[0] == 1 +assert graph.edge_df.shape[0] == 3 +print("Swan 3.2 smoke test passed") diff --git a/images/talon/6.0.1/Dockerfile b/images/talon/6.0.1/Dockerfile new file mode 100644 index 0000000..cb40066 --- /dev/null +++ b/images/talon/6.0.1/Dockerfile @@ -0,0 +1,15 @@ +FROM quay.io/biocontainers/talon:6.0.1--pyhdfd78af_0@sha256:789e65a7506e8dac4059e479d7790f909f85279c288dac558b1272c6311cdbb2 + +LABEL software="TALON" \ + software.version="6.0.1" \ + about.summary="Technology-agnostic long-read isoform discovery and quantification" \ + about.url="https://github.com/mortazavilab/TALON" \ + about.license="MIT" \ + about.tags="Genomics,long-read-RNA,isoform-classification,isoform-quantification" + +COPY verify.sh /tmp/verify.sh +RUN bash /tmp/verify.sh && rm /tmp/verify.sh + +WORKDIR /work +ENTRYPOINT [] +CMD ["talon", "--help"] diff --git a/images/talon/6.0.1/README.md b/images/talon/6.0.1/README.md new file mode 100644 index 0000000..ba753b1 --- /dev/null +++ b/images/talon/6.0.1/README.md @@ -0,0 +1,15 @@ +# TALON 6.0.1 + +This image is pinned to the official BioContainers image for the latest tagged +TALON release's exact Bioconda build. Upstream's Python distribution metadata +still reports 5.0, while the tag, Bioconda package, and image version are +6.0.1. + +```bash +docker run --rm -it -v "$PWD:/work" ghcr.io/yeolab/talon:6.0.1 +``` + +The build checks every main TALON command and initializes a SQLite database +from a synthetic two-exon annotation, then verifies its transcript table. + +Upstream: diff --git a/images/talon/6.0.1/verify.sh b/images/talon/6.0.1/verify.sh new file mode 100644 index 0000000..b2ab6df --- /dev/null +++ b/images/talon/6.0.1/verify.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +python - <<'PY' +from importlib_metadata import version +assert version("talon") == "5.0" +PY + +for command in talon talon_initialize_database talon_abundance talon_create_GTF talon_create_adata; do + command -v "$command" >/dev/null + "$command" --help >/dev/null +done + +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT +printf '%b\n' \ + 'chr1\tsmoke\tgene\t1\t200\t.\t+\t.\tgene_id "g1"; gene_name "G1";' \ + 'chr1\tsmoke\ttranscript\t1\t200\t.\t+\t.\tgene_id "g1"; transcript_id "t1"; gene_name "G1"; transcript_name "T1";' \ + 'chr1\tsmoke\texon\t1\t50\t.\t+\t.\tgene_id "g1"; transcript_id "t1"; exon_number "1"; exon_id "e1";' \ + 'chr1\tsmoke\texon\t101\t200\t.\t+\t.\tgene_id "g1"; transcript_id "t1"; exon_number "2"; exon_id "e2";' \ + > "$work/input.gtf" + +talon_initialize_database --f "$work/input.gtf" --g smoke --a smoke --o "$work/talon" +test -s "$work/talon.db" +python - "$work/talon.db" <<'PY' +import sqlite3 +import sys +with sqlite3.connect(sys.argv[1]) as connection: + assert connection.execute("SELECT COUNT(*) FROM transcripts").fetchone()[0] == 1 +PY +echo "TALON 6.0.1 smoke test passed"