Migrated from the legacy deseq2_container wrapper in nodes-io. One
directory = one plugin family = one git-able unit. The original
container-era README moved with the build tree as
container_README.md (verbatim, modulo its now-regenerated kind and
$AUTONOMICS_IMAGE_PREFIX spellings — image references are self-contained
digest pins since the prefix's removal).
manifest.toml— node kinddeseq2_de: params, ports, image provenance, resourcesDockerfile— image build provenance (moved verbatim fromcontainers/deseq2/; build + push still via GHCR)deseq2_runner.R— the baked runner,COPY-ed to/opt/autonomics/deseq2_runner.Rby the Dockerfile; the manifest declares no script, onlyinterpreter = "Rscript"plus fixed argvtest_deseq2.sh— full image baseline (build, run the pasilla model twice, checksum-compare against the fixture baseline, independently recompute size factors / normalized counts, negative-path checks);root=repointed to this directorytest_deseq2_fixture.sh— fixture validation without R or Podman;root=repointed, fixture directory env-overridabletest_deseq2_data_boundary.sh— asserts the image carries no pasilla fixture and no/panels; moved verbatimcontainer_README.md— the original containers/deseq2/README.md
Fixtures did not move. The pasilla fixtures remain in the autonomics
repository at containers/deseq2/fixtures/ because still-live nodes-io
Rust tests read them there (wgcna_container.rs,
limma_voom_container.rs, bulk_rnaseq_real_podman.rs, and the legacy
deseq2_container.rs integration test until that wrapper is deleted).
The moved test scripts default DESEQ2_FIXTURES to the repository path
and accept an override.
- Image:
ghcr.io/auto-nomics/autonomics/deseq2@sha256:8b2e2a78d87293e6cae6dbed2e283dff1dd8461a7f993cb347ca9810698b2b3e, tag1.50.2, fromDockerfile(baserocker/r-ver:4.5.3, itself digest-pinned). - Upstream: official Bioconductor
DESeq2 1.50.2 source tarball
(sha256
514f23ae8d274623d80978c30bfa1c6566acd98188bf1aa563970959ea59522f, revisiond90821a) installed into Bioconductor release 3.22; license LGPL-3.0-or-later.
The golden test (crates/container-plugin/tests/deseq2_migration.rs)
compares the compiled ContainerCommandSpec against the legacy Rust
wrapper (nodes-io/src/deseq2_container.rs): image, command, outputs,
resources, timeout, env keys/values, and the script-less shape are
byte-equal. Deliberate deltas:
- Baked runner preserved as argv. The legacy wrapper passed
["Rscript", "--vanilla", "/opt/autonomics/deseq2_runner.R"]withscript: None; the manifest expresses exactly that withinterpreter = "Rscript"andargv(the DSL's baked-runner shape). Parameters travel through theAUTONOMICS_DESEQ2_*environment variables, so the pinned image needs no rebuild per invocation. - Kind rename:
deseq2_de_container→deseq2_de; the artifact prefix follows the kind (/artifacts/deseq2_de_container→/artifacts/deseq2_de), the same rule the ldsc/mrpresso/mvmr migrations applied. DAG specs referencing the old kind must be regenerated. Note the run report produced inside the pinned image still carries"node": "deseq2_de_container"; that string is baked into the runner and changes only with the next image build. timeout_secs/artifact_prefixare node-level constants (3600 s,/artifacts/deseq2_de) instead of per-instance spec params; the legacy spec accepted per-node overrides, the plugin DSL does not.covariatesshape change: the legacy spec carried a string array and Rust joined it with","intoAUTONOMICS_DESEQ2_COVARIATES. The v0 env renderer space-joins arrays, while the baked runner splits the variable on commas (strsplit(value, ",", fixed = TRUE)), so the manifest declares a plain string carrying the comma-separated list — byte-identical env for every legacy-legal input (legacy covariates are simple R identifiers, which never contain commas or spaces). Same serialized-string pattern as the mvmrpcormigration. The runner re-validates duplicates, reserved names, R-identifier shape, and column existence.fit_typeloses its enum: the v0 param vocabulary has no enum type (it arrives with the panel-selection wave), so it is a string with defaultparametric; the runner rejects anything outsideparametric/local/meanwith the legacy message.- Validation moved into the runner: the DSL cannot express
condition_test != condition_referenceor the covariate identifier rules; the baked runner enforces them (and rank-deficiency, data shape, sample-set equality) with the legacy error messages. The failure point moves from registry build to container start. The DSL carries what it can:alphastrict (0, 1) viaexclusive_min/exclusive_max,threadspinned to exactly 1 viamin = max = 1. - Resources pinned: 2 CPUs,
4Gimemory, pids limit 256,1Gishm, isolated network, read-only rootfs, pull policymissing— the legacy wrapper's exact defaults; the manifest relies on the hardened defaults for the three boolean/enum fields. - Ports: the two labeled input ports (
count_matrix,sample_metadata) carry over exactly. Output ports gain file-stem labels (results,normalized_counts,size_factors,deseq2_dataset,run_report) because the manifest pipeline always names output ports; the legacy ports were unlabeled — the same accepted delta as the mvmr migration.