Skip to content

nf-diff 0.2.0

Choose a tag to compare

@mribeirodantas mribeirodantas released this 08 Sep 07:44
· 97 commits to main since this release

Added

  • Line-level output diffing — under --diff-outputs, a file classified as
    changed that is text on both sides is now additionally diffed line by line
    (reusing the same LineDiff engine as --diff-logs), so the report answers
    what changed rather than merely that it changed — a VCF, CSV, JSON, or
    report file shows its added/removed lines inline. OutputComparator sniffs
    the head of each changed file for a NUL byte; binary files fall back to the
    existing size/hash verdict and produce no line diff. Reads are bounded: the
    first --outputs-max-lines lines (new option, default 1000) and a hard byte
    cap, so a huge file never blows up memory, with a truncated marker when a
    cap dropped content. Surfaced in all three report formats (HTML unified-diff
    pane with an added/removed line count, Markdown fenced diff block, and
    diff/linesAdded/linesRemoved/truncated fields on each output file in
    JSON). This enriches the existing layer only — an output change still counts
    toward the "identical" verdict and --fail-on-change exactly as before.
  • Config-provenance caveat — the configuration layer now inspects the git
    state of the working tree it resolves config from and warns when that tree has
    drifted from the revision a run was actually launched at. Because
    ConfigLoader rebuilds each run's effective config from the files as they
    exist now
    , a run launched at commit A and re-run at commit B have their
    configs both resolved against whatever is checked out now — silently masking
    config differences driven by code changes between those revisions. The
    metadata layer already records each run's revisionId; this compares it to
    the current HEAD (via git rev-parse, prefix-matching abbreviated ids) and
    also flags an uncommitted (dirty) working tree. When either run drifted, or
    the tree is dirty, a prominent caveat is surfaced in all three report formats
    (HTML warning banner, Markdown blockquote, and a configProvenance object
    with driftedA/driftedB/workingTreeDirty/warning fields in JSON). Git
    state is inspected best-effort — a non-git project, missing git, or a
    command timeout degrades to "unknown" without breaking the diff. The caveat is
    informational only: it never affects the "identical" verdict or
    --fail-on-change.
  • Software & versions diffing — a new always-on layer that compares, per
    process, the distinct container image(s) and Conda package spec(s) that
    process's tasks ran with in each run. Both values are read straight from the
    run cache's trace records, so the layer needs no work directories and is
    always computed. It answers "did a tool version change?" directly — e.g.
    biocontainers/fastqc:0.11.9 → biocontainers/fastqc:0.12.1 — instead of
    leaving it buried in the per-task container field. A process present in only
    one run is added/removed; a process in both whose container or Conda set
    differs is flagged changed. A changed software environment counts toward the
    "identical" verdict and --fail-on-change, which also makes a Conda-only
    change (previously invisible to the task layer) break identity. Surfaced in
    all three report formats (HTML section + summary card, Markdown section +
    summary column, and a software array with a softwareChanged summary count
    in JSON).
  • Output-file diffing — a new opt-in --diff-outputs layer that compares
    the files each task matched in both runs wrote to its work directory,
    classifying them as added / removed / changed / unchanged. Files are compared
    by size first, then by a streamed SHA-256 for same-size files. Staged inputs
    (symlinks) and Nextflow control files (.command.*, .exitcode) are skipped;
    cache-resumed tasks that share a work directory short-circuit as identical.
    Unlike the performance-regressions layer, an output-file change counts toward
    the "identical" verdict and --fail-on-change, so this is the layer that
    answers "did my pipeline actually produce different results?".
  • --outputs-max-bytes=<n> — caps the size of same-size files that are
    hashed under --diff-outputs; larger files are reported content-unverified.
    Default 0 means no limit.
  • Output diffs are surfaced in all three report formats (HTML, JSON, Markdown),
    with an outputsChanged count in the JSON/HTML summary.
  • Failure / log diffing — a new opt-in --diff-logs layer that compares the
    standard log files (.command.out, .command.err, .command.log) each
    matched task wrote to its work directory, line by line. It surfaces the
    exit-code and status change alongside the log contents, so a task that went
    from exit 0 to exit 1 can be inspected side by side — answering not that a
    task failed but what it printed before it did. Reads are bounded (tailed to
    a line cap and a hard byte cap) so an enormous log never blows up memory, and
    tasks sharing a work directory (cache-resumed) short-circuit as identical.
    Because task stdout/stderr legitimately varies between runs (timestamps,
    paths, ordering), this layer is informational only: it never affects the
    "identical" verdict or --fail-on-change — the exit-code change already
    captured by the per-task diff does that.
  • --logs-max-lines=<n> — keeps only the last <n> lines of each log file
    before diffing under --diff-logs (default 200).
  • Log diffs are surfaced in all three report formats (HTML, JSON, Markdown),
    with a logsChanged count in the JSON/HTML summary.

Fixed

  • --last and bare boolean flags dropped by the plugin launcher — Nextflow's
    plugin launcher rewrites forwarded arguments before they reach the verb: a
    bare --flag arrives as --flag true, and --opt=value arrives
    space-separated as --opt value. The diff parser assumed the inline =
    form survived, so nextflow plugin nf-diff:diff --last reached it as
    ['--last', 'true'] and the injected true leaked into the positional list,
    tripping the "--last cannot be combined with explicit run identifiers" guard.
    The same latent bug affected --last=N and every bare boolean flag
    (--fail-on-change, --verbose, --diff-outputs, --diff-logs). The parser
    now tolerates the launcher-normalized forms, consuming an injected/inline
    true/false (or an integer for --last N) instead of treating it as a
    positional run identifier.