Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions .github/workflows/publish-ghcr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ on:
push:
paths:
- "**/Dockerfile"
- "images/druid/complete/**"
- ".github/workflows/publish-ghcr.yml"
workflow_dispatch:
inputs:
dockerfile:
Expand Down Expand Up @@ -42,27 +44,32 @@ jobs:
EVENT_NAME: ${{ github.event_name }}
BEFORE_SHA: ${{ github.event.before }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
DOCKERFILE: ${{ inputs.dockerfile }}
REQUESTED_DOCKERFILE: ${{ inputs.dockerfile }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
GITHUB_SHA: ${{ github.sha }}
run: |
set -euo pipefail

if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
changed_files="$DOCKERFILE"
changed_files="$REQUESTED_DOCKERFILE"
elif [[ "$BEFORE_SHA" =~ ^0+$ ]]; then
# A branch's first push has no predecessor SHA. Compare it with
# the default branch instead of treating every repository file as
# newly added.
base_sha="$(git merge-base "origin/$DEFAULT_BRANCH" "$GITHUB_SHA")"
changed_files="$(git diff --name-only --diff-filter=AMR "$base_sha" "$GITHUB_SHA")"
changed_files="$(git diff --name-only --diff-filter=AMRD "$base_sha" "$GITHUB_SHA")"
else
changed_files="$(git diff --name-only --diff-filter=AMR "$BEFORE_SHA" "$GITHUB_SHA")"
changed_files="$(git diff --name-only --diff-filter=AMRD "$BEFORE_SHA" "$GITHUB_SHA")"
fi

mapfile -t dockerfiles < <(
printf '%s\n' "$changed_files" | awk '/(^|\/)Dockerfile$/'
printf '%s\n' "$changed_files" | awk '
/^images\/druid\/complete\// || $0 == ".github/workflows/publish-ghcr.yml" {
print "images/druid/complete/Dockerfile"; next
}
/(^|\/)Dockerfile$/ { print }
' | sort -u
)

if (( ${#dockerfiles[@]} == 0 )); then
Expand Down Expand Up @@ -101,6 +108,17 @@ jobs:
image_tag="$(printf '%s' "$image_tag" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_.-]/-/g')"
image="ghcr.io/$owner/$image_name"

if [[ "$context" == "images/druid/complete" ]]; then
# Test the same cached build before publishing either GHCR tag.
docker buildx build --platform linux/amd64 --load \
--file "$dockerfile" --tag druid:ci "$context"
mkdir -p "$RUNNER_TEMP/druid-test"
docker run --rm --platform linux/amd64 \
--volume "$RUNNER_TEMP/druid-test:/work" \
druid:ci druid smoke-test --work /work \
2>&1 | tee "$RUNNER_TEMP/druid-test.log"
fi

echo "Publishing $dockerfile as $image:$image_tag and $image:sha-$revision"
docker buildx build \
--file "$dockerfile" \
Expand All @@ -114,3 +132,17 @@ jobs:
--push \
"$context"
done

- name: Upload DRUID validation logs and results
if: always()
uses: actions/upload-artifact@v4
with:
name: druid-validation-${{ github.sha }}
if-no-files-found: ignore
retention-days: 14
path: |
${{ runner.temp }}/druid-test.log
${{ runner.temp }}/druid-test/**/Analysis/HEK293_R1/*.csv
${{ runner.temp }}/druid-test/**/Analysis/HEK293_R1/*.pdf
${{ runner.temp }}/druid-test/**/Analysis/HEK293_R1/*provenance.json
${{ runner.temp }}/druid-test/**/Analysis/HEK293_R1/*Info.txt
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,11 @@

### Added

- DRUID complete-analysis container at `images/druid/complete`, including
verified GEO/ENA sample mappings, compatibility fixes, and full workflow tests.
- DRUID builds triggered by supporting-file changes; GitHub Actions tests the
container before publishing `ghcr.io/yeolab/druid:complete` and a commit tag,
and retains validation artifacts for 14 days.

- GitHub Actions publishing of changed Dockerfiles to GitHub Container Registry
(GHCR), including source, SBOM, and provenance metadata.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,28 @@ apptainer pull fastp_0.23.3.sif docker://ghcr.io/yeolab/fastp:sha-<full-commit-s

Set the GHCR package to public from GitHub's **Packages** page if users should
be able to pull it without credentials.

## DRUID complete analysis

The [DRUID build context](images/druid/complete/) includes the software,
GEO/ENA sample manifest, full-analysis runner, and synthetic end-to-end tests.
Changes to any file in `images/druid/complete/` trigger publishing, including
changes to scripts, tests, and configuration. The publishing workflow itself
also triggers a DRUID rebuild. CI builds for `linux/amd64`, runs the complete
synthetic analysis and reference-converter checks, and publishes only after
those checks pass. Validation logs, half-life tables, heatmaps, and provenance
are retained as Actions artifacts for 14 days.

```bash
docker pull --platform linux/amd64 ghcr.io/yeolab/druid:complete
docker run --rm --platform linux/amd64 \
-v /Volumes/X9Pro/Yeo/DRUID:/work \
ghcr.io/yeolab/druid:complete druid all
```

Assign at least 32 GB RAM to Docker before running the real-data analysis
(40 GB recommended). The CI test uses a small synthetic reference and does
not download or analyze the full GEO experiment. See the image's
[README](images/druid/complete/README.md) for separate download, reference,
analysis, and fitting commands. The image is also tagged
`ghcr.io/yeolab/druid:sha-<full-commit-sha>`.
9 changes: 9 additions & 0 deletions images/druid/complete/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*
!Dockerfile
!scripts/
!scripts/**
!tests/
!tests/**
!config/
!config/**
**/__pycache__
12 changes: 12 additions & 0 deletions images/druid/complete/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.DS_Store
upstream/
reference/
vendor/
logs/
work/
tmp/
ExternalData/
RawSequencingData/
Analysis/
__pycache__/
*.pyc
12 changes: 12 additions & 0 deletions images/druid/complete/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Changelog

## 2026-09-08

- Add GitHub Actions publication to `ghcr.io/yeolab/druid:complete` after end-to-end validation, with rebuilds triggered by supporting-file changes.

- Add an amd64 Dockerfile and Compose service for DRUID's complete-analysis workflow.
- Add verified GEO/ENA mappings and resumable, checksum-verified FASTQ downloads for HEK293 replicate 1.
- Automate combined-reference construction, QC, trimming, alignment, exon/intron quantification, and both half-life analyses.
- Update the GenomicFeatures selection API, port coverage calculation to Python 3, fix exact unique-read filtering, and correct DRUID normalization dimensions while retaining upstream originals.
- Add a synthetic end-to-end test, known-half-life and sequencing-depth-invariance checks, and coverage-statistics checks.
- Document resource requirements, compatibility changes, provenance, and limits of validation.
47 changes: 47 additions & 0 deletions images/druid/complete/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# syntax=docker/dockerfile:1
# UCSC's distributed tools are x86-64. Build with --platform linux/amd64.
FROM debian:bookworm-slim@sha256:88200866dfff7ea7f5cbcb6ec7c8a701889efe6fe859fe64d6990e4b07ea4171
ARG TARGETARCH
ARG DRUID_COMMIT=455be347cab87b62a2d1f081ec34cafffdf81622
ENV DEBIAN_FRONTEND=noninteractive LC_ALL=C.UTF-8 LANG=C.UTF-8 \
DRUID_HOME=/opt/DRUID PATH=/opt/DRUID:/opt/DRUID/Scripts:/opt/workflow:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
THREADS=8 TMPDIR=/work/tmp
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN test "$TARGETARCH" = amd64 || { echo 'Build with --platform linux/amd64'; exit 1; }
RUN mkdir -p /work/tmp && apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl wget git perl bash coreutils gawk parallel procps \
samtools bedtools rna-star fastqc trimmomatic default-jre-headless \
python3 python3-numpy python3-htseq python-is-python3 sra-toolkit \
r-base-core r-cran-gplots r-cran-plyr r-bioc-genomicfeatures r-bioc-rtracklayer \
libmariadb3 libpng16-16 libssl3 libuuid1 libgomp1 \
&& dpkg-query -W > /opt/debian-packages.tsv \
&& rm -rf /var/lib/apt/lists/*
RUN git init /opt/DRUID && cd /opt/DRUID \
&& git remote add origin https://github.com/risslandlab/DRUID.git \
&& git fetch --depth 1 origin "$DRUID_COMMIT" && git checkout --detach FETCH_HEAD \
&& test "$(git rev-parse HEAD)" = "$DRUID_COMMIT" \
&& echo "$DRUID_COMMIT" > /opt/DRUID/UPSTREAM_COMMIT && rm -rf .git
# Checksums intentionally fail if UCSC replaces these unversioned downloads.
RUN curl -fL --retry 5 https://hgdownload.soe.ucsc.edu/admin/exe/linux.x86_64/twoBitToFa -o /usr/local/bin/twoBitToFa \
&& curl -fL --retry 5 https://hgdownload.soe.ucsc.edu/admin/exe/linux.x86_64/genePredToGtf -o /usr/local/bin/genePredToGtf \
&& echo '10b2cd0d1f8208e8c0e9990b4a97d90cda4eec754b13930a10d55a40bda56710 /usr/local/bin/twoBitToFa' | sha256sum -c - \
&& echo '3596b456da9c423686a2a3f01edab9915d1969144601c0aac3c8b694375a2c18 /usr/local/bin/genePredToGtf' | sha256sum -c - \
&& chmod 755 /usr/local/bin/twoBitToFa /usr/local/bin/genePredToGtf
COPY scripts/ /opt/workflow/
COPY tests/ /opt/tests/
COPY config/ /opt/config/
RUN python3 /opt/workflow/patch_upstream.py \
&& chmod +x /opt/DRUID/*.sh /opt/DRUID/*.py /opt/DRUID/Scripts/* /opt/workflow/*.sh \
&& ln -s /usr/share/java/trimmomatic.jar /opt/trimmomatic.jar \
&& ln -s /usr/share/trimmomatic /opt/trimmomatic \
&& ln -s /opt/workflow/druid.py /usr/local/bin/druid \
&& chmod +x /opt/workflow/druid.py \
&& Rscript -e 'library(GenomicFeatures); library(rtracklayer); library(plyr); library(gplots); source("/opt/DRUID/DRUID.R"); stopifnot(is.function(DRUID))' \
&& python3 -c 'import numpy, HTSeq' \
&& STAR --version && samtools --version && bedtools --version && fastqc --version \
&& mkdir -p /work/tmp
LABEL org.opencontainers.image.title="DRUID complete analysis" \
org.opencontainers.image.source="https://github.com/risslandlab/DRUID" \
org.opencontainers.image.revision="455be347cab87b62a2d1f081ec34cafffdf81622"
WORKDIR /work
CMD ["bash"]
21 changes: 21 additions & 0 deletions images/druid/complete/LICENSE-DRUID
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2017 risslandlab

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading
Loading