Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f75106e
feat: add hlahd module (v1.7.1)
johnoooh Mar 4, 2026
15ba70f
test: add hlahd module tests
johnoooh Mar 5, 2026
98749d7
feat: add hlahd_from_bam subworkflow and tests
johnoooh Mar 5, 2026
459877e
fix: update hlahd container URL to JFrog registry
johnoooh Mar 5, 2026
2278abf
fix: correct HLAHD output paths, update container to dev, refresh sna…
johnoooh Mar 9, 2026
13a2ba3
fix: update author/maintainer to GitHub username
johnoooh Mar 9, 2026
e460124
ci: add JFrog registry authentication to nf-test workflow
johnoooh Mar 20, 2026
50afb02
Merge branch 'develop' into feature/hlahd
johnoooh Mar 20, 2026
3d733d6
ci: skip hlahd module and subworkflow for conda profile
johnoooh Mar 20, 2026
d92ec76
fix: use versioned HLA_gene.split.3.50.0.txt in hlahd module
johnoooh Apr 21, 2026
9380a7b
feat: install nf-core deps in CI; modernize subworkflow for current c…
johnoooh Apr 27, 2026
f60c1ba
test: regenerate hlahd_from_bam snapshot for modernized subworkflow
johnoooh Apr 27, 2026
7936f97
fix: switch hlahd to prod registry and auth singularity for JFrog
johnoooh Apr 30, 2026
66953d9
Update container image to development version
johnoooh Apr 30, 2026
4e40292
fix: target prod registry and prepend hlahd bin to PATH
johnoooh Apr 30, 2026
fb2b961
diag: dump /opt/hlahd contents to confirm prod image state
johnoooh Apr 30, 2026
c61c5cc
revert: drop /opt/hlahd diagnostic dump (root cause confirmed)
johnoooh Apr 30, 2026
eb5f41f
test: switch hlahd container to dev registry for CI verification
johnoooh May 5, 2026
b90cda1
ci: retrigger nf-test now that dev image is published
johnoooh May 5, 2026
cb94553
fix: point hlahd container at prod registry where rebuilt image lives
johnoooh May 5, 2026
fbc29a2
diag: print final.result.txt content to verify HLA calls before snaps…
johnoooh May 5, 2026
9239277
test: rebuild hlahd snapshot for HLA-A-only data, point at dev image
johnoooh May 8, 2026
ab01043
fix: point hlahd container at docker.io/orgeraj while JFrog image rep…
johnoooh May 8, 2026
017a916
fix(ci): scope JFrog auth to mskcc.jfrog.io for apptainer
johnoooh May 8, 2026
af172cb
test: refresh hlahd_from_bam snapshot for HLA-A-only test data
johnoooh May 8, 2026
692d4ba
test: point hlahd container at dev JFrog registry
johnoooh May 11, 2026
4082453
revert: point hlahd container back at docker.io/orgeraj (dev JFrog ma…
johnoooh May 11, 2026
ee3a77b
Merge branch 'develop' into feature/hlahd
johnoooh May 14, 2026
0bc0b28
fix: point hlahd container at JFrog prod-local where 1.7.1 exists
johnoooh May 14, 2026
d3e16b6
test(hlahd): narrow result assertion to HLA-A line
johnoooh May 14, 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
44 changes: 44 additions & 0 deletions .github/actions/nf-test-action/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ inputs:
paths:
description: "Test paths"
required: true
jfrog_username:
description: "JFrog registry username"
required: false
default: ""
jfrog_password:
description: "JFrog registry password or token"
required: false
default: ""

runs:
using: "composite"
Expand All @@ -32,6 +40,14 @@ runs:
with:
python-version: "3.11"

- name: Install cross-repo component dependencies
shell: bash
run: |
mapfile -t metas < <(find subworkflows -name meta.yml)
if (( ${#metas[@]} > 0 )); then
bash .github/scripts/install-components.sh "${metas[@]}"
fi

- name: Install nf-test
uses: nf-core/setup-nf-test@v1
with:
Expand Down Expand Up @@ -72,6 +88,34 @@ runs:
nextflow secrets set ONCOKB_TOKEN $ONCOKB_TOKEN


- name: Login to JFrog Container Registry (docker)
if: ${{ inputs.profile == 'docker' && inputs.jfrog_username != '' }}
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
with:
registry: mskcc.jfrog.io
username: ${{ inputs.jfrog_username }}
password: ${{ inputs.jfrog_password }}

- name: Configure JFrog auth for Apptainer/Singularity
if: ${{ contains(inputs.profile, 'singularity') && inputs.jfrog_username != '' }}
shell: bash
env:
JFROG_USERNAME: ${{ inputs.jfrog_username }}
JFROG_PASSWORD: ${{ inputs.jfrog_password }}
run: |
# Scope JFrog creds to mskcc.jfrog.io only via a Docker-format auth
# file. Setting APPTAINER_DOCKER_USERNAME/PASSWORD globally would send
# JFrog basic-auth to every docker:// pull (incl. ghcr.io, quay.io),
# which the other registries reject with 403.
AUTH=$(printf '%s:%s' "$JFROG_USERNAME" "$JFROG_PASSWORD" | base64 -w0)
for d in "$HOME/.apptainer" "$HOME/.singularity"; do
mkdir -p "$d"
cat > "$d/docker-config.json" <<EOF
{"auths":{"mskcc.jfrog.io":{"auth":"$AUTH"}}}
EOF
chmod 600 "$d/docker-config.json"
done

# TODO Skip failing conda tests and document their failures
# https://github.com/nf-core/modules/issues/7017
- name: Run nf-test
Expand Down
73 changes: 73 additions & 0 deletions .github/scripts/install-components.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env bash
# Install cross-repo nf-core/MSK component dependencies declared in a
# subworkflow's meta.yml.
#
# Usage: install-components.sh META_YML [META_YML …]
#
# Each META_YML may declare a `components:` list. Bare-string entries
# (e.g. `- hlahd`) refer to components in this repo and are skipped.
# Dict entries (e.g. `- {name: samtools/view, git_remote: …, org_path: …}`)
# are sparse-checked-out into `modules/<org_path>/<name>/`.
#
# Notes:
# - Only leaf components are installed; transitive `components:` in the
# fetched modules are not recursively resolved.
# - An existing `modules/<org>/<name>/` directory is treated as a cache hit
# and not refreshed even if the declared ref has changed. CI always
# starts from a fresh checkout, so this is fine in practice; for local
# re-runs, delete the directory to force a refetch.
# - Default ref is `master`. Set `git_sha:` or `branch:` per component to
# override.

set -euo pipefail

DEFAULT_REMOTE="https://github.com/nf-core/modules.git"
DEFAULT_REF="master"

# Allowlist for path components built from meta.yml input.
NAME_RE='^[a-z0-9_]+(/[a-z0-9_]+)?$'
ORG_RE='^[a-z0-9_-]+$'

TMPROOT=$(mktemp -d)
trap 'rm -rf "$TMPROOT"' EXIT

declare -A seen

for meta in "$@"; do
[[ -f "$meta" ]] || { echo "skip: $meta not found"; continue; }
n=$(yq '.components | length // 0' "$meta")
for i in $(seq 0 $((n - 1))); do
kind=$(yq ".components[$i] | tag" "$meta")
[[ "$kind" == "!!str" ]] && continue # bare = local, skip

name=$(yq -r ".components[$i].name" "$meta")
org=$(yq -r ".components[$i].org_path // \"nf-core\"" "$meta")
remote=$(yq -r ".components[$i].git_remote // \"$DEFAULT_REMOTE\"" "$meta")
ref=$(yq -r ".components[$i].git_sha // .components[$i].branch // \"$DEFAULT_REF\"" "$meta")

[[ "$name" =~ $NAME_RE ]] || { echo "ERROR: invalid component name: $name (in $meta)" >&2; exit 1; }
[[ "$org" =~ $ORG_RE ]] || { echo "ERROR: invalid org_path: $org (in $meta)" >&2; exit 1; }

key="$remote|$org|$name|$ref"
[[ -n "${seen[$key]:-}" ]] && continue
seen[$key]=1

dest="modules/$org/$name"
if [[ -d "$dest" ]]; then
echo "✓ $dest already present"
continue
fi

echo "→ fetching $org/$name from $remote@$ref"
tmp="$TMPROOT/$org-${name//\//_}-$ref"
mkdir -p "$tmp"
git -C "$tmp" init -q
git -C "$tmp" remote add origin "$remote"
git -C "$tmp" config core.sparseCheckout true
echo "modules/$org/$name/" > "$tmp/.git/info/sparse-checkout"
git -C "$tmp" fetch --depth 1 origin "$ref" -q
git -C "$tmp" checkout -q FETCH_HEAD
mkdir -p "$(dirname "$dest")"
mv "$tmp/modules/$org/$name" "$dest"
done
done
4 changes: 3 additions & 1 deletion .github/skip_nf_test.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
"subworkflows/msk/phylowgs",
"subworkflows/msk/generate_mutated_peptides",
"subworkflows/msk/neoantigen_editing",
"subworkflows/msk/traceback"
"subworkflows/msk/traceback",
"modules/msk/hlahd",
"subworkflows/msk/hlahd_from_bam"
],
"docker": [],
"singularity": []
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/nf-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ jobs:
shard: ${{ matrix.shard }}
total_shards: ${{ env.TOTAL_SHARDS }}
paths: "${{ join(fromJson(steps.filter.outputs.filtered_paths), ' ') }}"
jfrog_username: ${{ secrets.JFROG_USERNAME }}
jfrog_password: ${{ secrets.JFROG_PASSWORD }}

confirm-pass:
runs-on: ubuntu-latest
Expand Down
7 changes: 7 additions & 0 deletions modules/msk/hlahd/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json
channels:
- conda-forge
- bioconda
dependencies:
- "YOUR-TOOL=HERE"
64 changes: 64 additions & 0 deletions modules/msk/hlahd/main.nf
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
process HLAHD {
tag "$meta.id"
label 'process_high'

conda "${moduleDir}/environment.yml"
container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ?
'docker://mskcc.jfrog.io/omicswf-docker-prod-local/mskcc-omics-workflows/hlahd:1.7.1':
'mskcc.jfrog.io/omicswf-docker-prod-local/mskcc-omics-workflows/hlahd:1.7.1' }"

input:
tuple val(meta), path(fastq_1), path(fastq_2)

output:
tuple val(meta), path("${prefix}/result/${prefix}_final.result.txt"), emit: result
tuple val(meta), path("${prefix}/result/${prefix}_*.est.txt"), emit: result_per_locus
path "versions.yml", emit: versions

when:
task.ext.when == null || task.ext.when

script:
def args = task.ext.args ?: ''
def min_read = task.ext.args2 ?: '100'
prefix = task.ext.prefix ?: "${meta.id}"
def install_dir = '/opt/hlahd/current'
"""
if [[ \$(ulimit -n) -lt 1024 ]]; then ulimit -n 1024; fi

ln -sf /usr/bin/python3 ./python
export PATH=\$PWD:${install_dir}/bin:\$PATH

mkdir -p ${prefix}

bash ${install_dir}/bin/hlahd.sh \\
-t ${task.cpus} \\
-m ${min_read} \\
-f ${install_dir}/freq_data \\
${args} \\
${fastq_1} \\
${fastq_2} \\
${install_dir}/HLA_gene.split.3.50.0.txt \\
${install_dir}/dictionary \\
${prefix} \\
.

cat <<-END_VERSIONS > versions.yml
"${task.process}":
hlahd: \$(bash ${install_dir}/bin/hlahd.sh 2>&1 | grep -oP 'HLA-HD version \\K[0-9.]+' | head -1)
END_VERSIONS
"""

stub:
prefix = task.ext.prefix ?: "${meta.id}"
"""
mkdir -p ${prefix}/result
touch ${prefix}/result/${prefix}_final.result.txt
touch ${prefix}/result/${prefix}_A.est.txt

cat <<-END_VERSIONS > versions.yml
"${task.process}":
hlahd: \$(bash /opt/hlahd/current/bin/hlahd.sh 2>&1 | grep -oP 'HLA-HD version \\K[0-9.]+' | head -1)
END_VERSIONS
"""
}
67 changes: 67 additions & 0 deletions modules/msk/hlahd/meta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: "hlahd"
description: HLA typing from paired-end FASTQ reads using HLA-HD
keywords:
- HLA
- immunology
- typing
- genomics
tools:
- "hlahd":
description:
"HLA-HD (HLA typing from High-quality Dictionary) performs HLA typing
from paired-end FASTQ reads using bowtie2 alignment against HLA allele dictionaries."
homepage: "https://w3.genome.med.kyoto-u.ac.jp/HLA-HD/"
documentation: "https://w3.genome.med.kyoto-u.ac.jp/HLA-HD/"
licence:
- "ACADEMIC SOFTWARE LICENSE"
identifier: ""
input:
- - meta:
type: map
description: |
Groovy Map containing sample information
e.g. `[ id:'sample1', single_end:false ]`
- fastq_1:
type: file
description: First read of paired-end FASTQ input
pattern: "*.{fastq,fastq.gz,fq,fq.gz}"
ontologies:
- edam: http://edamontology.org/format_1930
- fastq_2:
type: file
description: Second read of paired-end FASTQ input
pattern: "*.{fastq,fastq.gz,fq,fq.gz}"
ontologies:
- edam: http://edamontology.org/format_1930
output:
result:
- - meta:
type: map
description: |
Groovy Map containing sample information
e.g. `[ id:'sample1', single_end:false ]`
- ${prefix}/result/${prefix}_final.result.txt:
type: file
description: Final HLA typing result file containing best-call alleles for all loci
pattern: "**/result/*_final.result.txt"
result_per_locus:
- - meta:
type: map
description: |
Groovy Map containing sample information
e.g. `[ id:'sample1', single_end:false ]`
- ${prefix}/result/${prefix}_*.est.txt:
type: file
description: Per-locus HLA estimation files (one file per HLA gene)
pattern: "**/result/*_*.est.txt"
versions:
- versions.yml:
type: file
description: File containing software versions
pattern: "versions.yml"
ontologies:
- edam: http://edamontology.org/format_3750
authors:
- "@johnoooh"
maintainers:
- "@johnoooh"
66 changes: 66 additions & 0 deletions modules/msk/hlahd/tests/main.nf.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
nextflow_process {

name "Test Process HLAHD"
script "../main.nf"
process "HLAHD"

tag "modules"
tag "modules_msk"
tag "hlahd"

test("hlahd - fastq pair - result txt") {

when {
process {
"""
input[0] = [
[ id:'test_sample', single_end:false ], // meta map
file(params.test_data_mskcc['hlahd']['fastq_1'], checkIfExists: true),
file(params.test_data_mskcc['hlahd']['fastq_2'], checkIfExists: true)
]
"""
}
}

then {
assertAll(
{ assert process.success },
{ assert snapshot(
process.out.result.collect { meta, f ->
[meta, path(f).readLines().findAll { it.startsWith("A\t") }]
},
process.out.versions
).match()
}
)
}

}

test("hlahd - fastq pair - stub") {

options "-stub"

when {
process {
"""
input[0] = [
[ id:'test_sample', single_end:false ], // meta map
file(params.test_data_mskcc['hlahd']['fastq_1'], checkIfExists: true),
file(params.test_data_mskcc['hlahd']['fastq_2'], checkIfExists: true)
]
"""
}
}

then {
assertAll(
{ assert process.success },
{ assert path(process.out.result.get(0).get(1)).exists() },
{ assert snapshot(process.out.versions).match() }
)
}

}

}
Loading
Loading