Skip to content

Commit f145979

Browse files
committed
Version 2.7: rename .checksum to .module-info, remove @ prefix from module scopes, remove version pinning from nextflow.config
Signed-off-by: Ben Sherman <bentshermann@gmail.com>
1 parent b0feed3 commit f145979

File tree

1 file changed

+77
-100
lines changed

1 file changed

+77
-100
lines changed

adr/20251114-module-system.md

Lines changed: 77 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44
- Status: draft
55
- Date: 2025-01-06
66
- Tags: modules, dsl, registry, versioning, architecture
7-
- Version: 2.6
7+
- Version: 2.7
88

99
## Updates
1010

11+
### Version 2.7 (2026-03-09)
12+
- **Renamed `.checksum` to `.module-info`**: Leaves room for additional properties in the future
13+
- **Removed `@` prefix from module scopes**: Local modules are distinguished from remote modules by presence/absence of `./` prefix
14+
- **Removed version pinning from config**: Installed module versions are now inferred from the `meta.yaml` of each module in the `modules/` directory instead of being declared in `nextflow.config`
15+
1116
### Version 2.6 (2026-01-28)
1217
- **Removed module parameters**: Module parameters specification moved to separate spec document.
1318

@@ -60,41 +65,37 @@ Implement a module system with four core capabilities:
6065

6166
**DSL Syntax**:
6267
```groovy
63-
// Import from registry (scoped module name, detected by @scope prefix)
64-
include { BWA_ALIGN } from '@nf-core/bwa-align'
68+
// Include from registry (scoped module name without `./` prefix)
69+
include { BWA_ALIGN } from 'nf-core/bwa-align'
6570
6671
// Existing file-based includes remain supported
6772
include { MY_PROCESS } from './modules/my-process.nf'
6873
```
6974

70-
**Module Naming**: NPM-style scoped packages `@scope/name` (e.g., `@nf-core/salmon`, `@myorg/custom`). Unscoped names (eg. local paths) supported for legacy compatibility. No nested paths with the module are allowed - each module must have a `main.nf` as the entry point.
75+
**Module Naming**: Scoped modules `scope/name` (e.g., `nf-core/salmon`, `myorg/custom`). Local paths supported for backwards compatibility. No nested paths with the module are allowed - each module must have a `main.nf` as the entry point.
7176

72-
**Version Resolution**: Module versions pinned in `nextflow.config`. If not specified, use the latest available locally in `modules/` directory, or downloaded and cached in the `modules/` directory.
77+
**Version Resolution**: Installed module versions are inferred from the `meta.yaml` of each module in the `modules/` directory. If a module is not present locally, the latest available version is downloaded from the registry.
7378

7479
**Resolution Order**:
75-
1. Check `nextflow.config` for declared version
76-
2. Check local `modules/@scope/name/` exists
77-
3. Verify integrity against `.checksum` file
78-
4. Apply resolution rules (see below)
80+
1. Check local `modules/scope/name/` exists
81+
2. Verify integrity against `.module-info` file
82+
3. Apply resolution rules (see below)
7983

8084
**Resolution Rules**:
8185

82-
| Local State | Declared Version | Action |
83-
|-------------|------------------|--------|
84-
| Missing | Any | Download declared version (or latest if not declared) |
85-
| Exists, checksum valid | Same as declared | Use local module |
86-
| Exists, checksum valid | Different from declared | **Replace** local with declared version |
87-
| Exists, checksum mismatch | Same as declared | **Warn**: module was locally modified, do not override |
88-
| Exists, checksum mismatch | Different from declared | **Warn**: locally modified, will NOT replace unless `-force` is used |
86+
| Local State | Action |
87+
|-------------|--------|
88+
| Missing | Download latest from registry |
89+
| Exists, checksum valid | Use local module (version from `meta.yaml`) |
90+
| Exists, checksum mismatch | **Warn**: locally modified, will NOT replace unless `-force` is used |
8991

9092
**Key Behaviors**:
91-
- **Version change**: When the declared version differs from the installed version (and local is unmodified), the local module is automatically replaced with the declared version
92-
- **Local modification**: When the local module content was manually changed (checksum mismatch with `.checksum`), Nextflow warns and does NOT override to prevent accidental loss of local changes
93+
- **Local modification**: When the local module content was manually changed (checksum mismatch with `.module-info`), Nextflow warns and does NOT override to prevent accidental loss of local changes
9394
- **Force flag**: Use `-force` with `nextflow module install` to override locally modified modules
9495

9596
**Resolution Timing**: Modules resolved at workflow parse time (after plugin resolution at startup).
9697

97-
**Local Storage**: Downloaded modules stored in `modules/@scope/name/` directory in project root (not global cache). Each module must contain a `main.nf` file as the required entry point. It is intended that module source code will be committed to the pipeline git repository.
98+
**Local Storage**: Downloaded modules stored in `modules/scope/name/` directory in project root (not global cache). Each module must contain a `main.nf` file as the required entry point. It is intended that module source code will be committed to the pipeline git repository.
9899

99100
### 2. Semantic Versioning and Configuration
100101

@@ -103,15 +104,8 @@ include { MY_PROCESS } from './modules/my-process.nf'
103104
- **MINOR**: New processes, backward-compatible enhancements
104105
- **PATCH**: Bug fixes, documentation updates
105106

106-
**Workflow Configuration** (`nextflow.config`):
107+
**Registry Configuration** (`nextflow.config`):
107108
```groovy
108-
// Module versions (exact versions only, no ranges)
109-
modules {
110-
'@nf-core/salmon' = '1.1.0'
111-
'@nf-core/bwa-align' = '1.2.0'
112-
}
113-
114-
// Registry configuration (separate block)
115109
registry {
116110
url = 'https://registry.nextflow.io' // Default registry
117111
@@ -162,8 +156,9 @@ Using comparison operators (`>=`, `<`) with comma-separated ranges provides the
162156
npm-style `^` and `~` notation while maintaining consistency with existing Nextflow version constraint syntax.
163157
This avoids introducing new notation that would require additional parser support.
164158

165-
**Dependency Resolution**:
166-
- Workflow's `nextflow.config` specifies exact versions for module dependencies
159+
**Module Resolution**:
160+
161+
Installed module versions are inferred from the `meta.yaml` file for each module in the `modules/` directory.
167162

168163
### 3. Unified Nextflow Registry
169164

@@ -209,12 +204,15 @@ Note: The `{name}` parameter includes the namespace prefix (e.g., "nf-core/fastq
209204
```bash
210205
nextflow module run scope/name # Run a module directly without a wrapper script
211206
nextflow module search <query> # Search registry
212-
nextflow module install [scope/name] # Install all from config, or specific module
207+
nextflow module install scope/name # Install a module
213208
nextflow module list # Show installed vs configured
214209
nextflow module remove scope/name # Remove from config + local cache
215210
nextflow module publish scope/name # Publish to registry (requires api key)
216211
```
217212

213+
**General Notes**:
214+
- All commands respect the `registry.url` configuration for custom registries
215+
218216
#### `nextflow module run scope/name`
219217

220218
Run a module directly without requiring a wrapper workflow script. This command enables standalone execution of any module by automatically mapping command-line arguments to the module's process inputs. If the module is not available locally, it is automatically installed before execution.
@@ -230,13 +228,13 @@ Run a module directly without requiring a wrapper workflow script. This command
230228
**Behavior**:
231229
1. Checks if module is installed locally; if not, downloads from registry
232230
2. Parses the module's `main.nf` to identify the main process and its input declarations
233-
3. Validates command-line arguments against the process input schema
231+
3. Validates command-line arguments against the process input declarations
234232
4. Generates an implicit workflow that wires CLI arguments to process inputs
235233
5. Executes the workflow using standard Nextflow runtime
236234

237235
**Input Mapping**:
238236
- Named arguments (`--reads`, `--reference`) are mapped to corresponding process inputs
239-
- File paths are automatically converted to file channels
237+
- File paths are automatically converted to files for process file inputs
240238
- Multiple values can be provided for inputs expecting collections
241239
- Required inputs without defaults must be provided; optional inputs use declared defaults
242240

@@ -282,30 +280,28 @@ nextflow module search "alignment" -limit 50
282280

283281
---
284282

285-
#### `nextflow module install [scope/name]`
283+
#### `nextflow module install <scope/name>`
286284

287-
Download and install modules to the local `modules/` directory. When called without arguments, installs all modules declared in `nextflow.config`. When a specific module is provided, installs that module and adds it to the configuration.
285+
Download and install a module to the local `modules/` directory.
288286

289287
**Arguments**:
290-
- `[scope/name]`: Optional module identifier. If omitted, installs all modules from config
288+
- `<scope/name>`: Module identifier.
291289

292290
**Options**:
293291
- `-version <ver>`: Install a specific version (default: latest)
294-
- `-force`: Re-download even if already installed locally
292+
- `-force`: Overwrite any local changes
295293

296294
**Behavior**:
297-
1. If `-version` not specified, resolves the module version from `nextflow.config` or queries registry for latest
298-
2. Checks if local module exists and verifies integrity against `.checksum` file
295+
1. If `-version` not specified, queries registry for the latest available version
296+
2. Checks if local module exists and verifies integrity against `.module-info` file
299297
3. If local module is unmodified and version differs: replaces with requested version
300298
4. If local module was modified (checksum mismatch): warns and aborts unless `-force` is used
301299
5. Downloads the module archive from the registry
302-
6. Extracts to `modules/@scope/name/` directory
303-
7. Stores `.checksum` file from registry's X-Checksum response header
304-
8. Updates `nextflow.config` if installing a new module not already configured
300+
6. Extracts to `modules/scope/name/` directory
301+
7. Stores `.module-info` file from registry's X-Checksum response header
305302

306303
**Example**:
307304
```bash
308-
nextflow module install # Install all from config
309305
nextflow module install nf-core/bwa-align # Install specific module (latest)
310306
nextflow module install nf-core/salmon -version 1.2.0
311307
```
@@ -321,11 +317,10 @@ Display the status of all modules, comparing what is configured in `nextflow.con
321317
- `-outdated`: Only show modules with available updates
322318

323319
**Output columns**:
324-
- Module name (`@scope/name`)
325-
- Configured version (from `nextflow.config`)
326-
- Installed version (from `modules/` directory)
320+
- Module name (`scope/name`)
321+
- Installed version (from `modules/scope/name/meta.yaml`)
327322
- Latest available version (from registry)
328-
- Status indicator (up-to-date, outdated, missing, not configured)
323+
- Status indicator (up-to-date, outdated, missing)
329324

330325
**Example**:
331326
```bash
@@ -337,19 +332,16 @@ nextflow module list -outdated
337332

338333
#### `nextflow module remove scope/name`
339334

340-
Remove a module from both the local `modules/` directory and the `nextflow.config` configuration.
335+
Remove a module from the local `modules/` directory.
341336

342337
**Arguments**:
343338
- `scope/name`: Module identifier to remove (required)
344339

345340
**Options**:
346-
- `-keep-config`: Remove local files but keep the entry in `nextflow.config`
347-
- `-keep-files`: Remove from config but keep local files
341+
- `-keep-files`: Remove `.module-info` file but keep local files
348342

349343
**Behavior**:
350-
1. Removes the module directory from `modules/@scope/name/`
351-
2. Removes the module entry from the `modules {}` block in `nextflow.config`
352-
3. Warns if the module is still referenced in workflow files
344+
1. Removes the module directory from `modules/scope/name/`
353345

354346
**Example**:
355347
```bash
@@ -373,8 +365,8 @@ Publish a module to the Nextflow registry, making it available for others to ins
373365

374366
**Behavior**:
375367
1. Validates `meta.yaml` schema and required fields (name, version, description)
376-
2. Verifies `main.nf` exists and is valid Nextflow syntax
377-
3. Checks that `README.md` documentation is present
368+
2. Verifies that `main.nf` exists and is valid Nextflow syntax
369+
3. Verifies that `README.md` documentation is present
378370
4. Authenticates with registry using configured credentials
379371
5. Creates a release draft and uploads the module archive
380372
6. Publishes the release, making it available for installation
@@ -392,10 +384,6 @@ nextflow module publish myorg/my-process
392384
nextflow module publish myorg/my-process -dry-run
393385
```
394386

395-
**General Notes**:
396-
- All commands respect the `registry.url` configuration for custom registries
397-
- Modules are automatically downloaded on `nextflow run` if missing but configured
398-
399387
## Module Structure
400388

401389
**Directory Layout**:
@@ -405,7 +393,7 @@ my-module/
405393
├── main.nf # Required: entry point for module
406394
├── meta.yaml # Required: Module spec (version, metadata, I/O specs)
407395
├── README.md # Required: Module description
408-
└── tests/ # Optional test workflows
396+
└── tests/ # Optional tests
409397
```
410398

411399
**Module Spec extension** (`meta.yaml`):
@@ -427,25 +415,25 @@ project-root/
427415
├── nextflow.config
428416
├── main.nf
429417
└── modules/ # Local module cache
430-
├── @nf-core/
418+
├── nf-core/
431419
│ ├── bwa-align/
432-
│ │ ├── .checksum # Cached registry checksum
420+
│ │ ├── .module-info # Cached registry checksum
433421
│ │ ├── meta.yaml
434422
│ │ └── main.nf # Required entry point
435423
│ └── samtools/view/
436-
│ ├── .checksum
424+
│ ├── .module-info
437425
│ ├── meta.yaml
438426
│ └── main.nf # Required entry point
439-
└── @myorg/
427+
└── myorg/
440428
└── custom-process/
441-
├── .checksum
429+
├── .module-info
442430
├── meta.yaml
443431
└── main.nf # Required entry point
444432
```
445433

446434
**Module Integrity Verification**:
447-
- On install: `.checksum` file created from registry's X-Checksum response header
448-
- On run: Local module checksum compared against `.checksum` file
435+
- On install: `.module-info` file created from registry's X-Checksum response header
436+
- On run: Local module checksum compared against `.module-info` file
449437
- If match: Proceed without network call
450438
- If mismatch: Report warning (module may have been locally modified)
451439

@@ -463,24 +451,21 @@ project-root/
463451

464452
## Technical Details
465453

466-
**Dependency Resolution Flow**:
467-
1. Parse `include` statements → extract module names (e.g., `@nf-core/bwa-align`)
454+
**Module Resolution Flow**:
455+
1. Parse `include` statements → extract module names (e.g., `nf-core/bwa-align`)
468456
2. For each module:
469-
a. Check `nextflow.config` modules section for declared version
470-
b. Check local `modules/@scope/name/` exists
471-
c. Verify local module integrity against `.checksum` file
472-
d. Apply resolution rules:
473-
- Missing → download declared version from registry
474-
- Exists, checksum valid, same version → use local
475-
- Exists, checksum valid, different version → replace with declared version
476-
- Exists, checksum mismatch → warn and do NOT override (local changes detected)
477-
3. On download: store module to `modules/@scope/name/` with `.checksum` file
457+
a. Check local `modules/scope/name/` exists
458+
- If exists → read installed version from `modules/scope/name/meta.yaml`
459+
- If missing → download latest version from registry
460+
b. Verify local module integrity against `.module-info` file
461+
- Checksum mismatch → warn and do NOT override (local changes detected)
462+
3. On download: store module to `modules/scope/name/` with `.module-info` file
478463
4. Read `meta.yaml` file: Validates Nextflow requirement → Fail if not fulfilled
479-
5. Parse module's `main.nf` file → make processes available```
464+
5. Parse module's `main.nf` file → make processes available
480465

481466
**Security**:
482-
- SHA-256 checksum verification on download (stored in `.checksum` file)
483-
- Integrity verification on run (local checksum vs `.checksum` file)
467+
- SHA-256 checksum verification on download (stored in `.module-info` file)
468+
- Integrity verification on run (local checksum vs `.module-info` file)
484469
- Authentication required for publishing
485470
- Support for private registries
486471

@@ -497,9 +482,9 @@ project-root/
497482
| Format | JAR files | Source code (.nf) |
498483
| Resolution | Startup | Parse time |
499484
| Metadata | JSON spec | YAML spec |
500-
| Naming | `nf-amazon` | `@nf-core/salmon` |
501-
| Cache Location | `$NXF_HOME/plugins/` | `modules/@scope/name/` |
502-
| Version Config | `plugins {}` in config | `modules {}` in config |
485+
| Naming | `nf-amazon` | `nf-core/salmon` |
486+
| Cache Location | `$NXF_HOME/plugins/` | `modules/scope/name/` |
487+
| Version Config | `plugins {}` in config | `meta.yaml` in `modules/` directory |
503488
| Registry Path | `/api/v1/plugins/` | `/api/modules/{name}` |
504489

505490
## Rationale
@@ -510,18 +495,18 @@ project-root/
510495
- Lower operational overhead
511496
- Type-specific handling maintains separation of concerns
512497

513-
**Why versions in nextflow.config instead of separate lock file?**
514-
- Single source of truth for workflow dependencies
515-
- Simple: exact versions in config, no separate lock file to manage
516-
- Reproducibility via explicit version pinning in config
498+
**Why infer versions from `meta.yaml` instead of pinning in a separate file?**
499+
- Simple: install a version once and it is captured in the module files
500+
- Reproducibility via committing the `modules/` directory (including `meta.yaml`) to the project git repository
501+
- Reduces configuration burden: no need to keep config in sync with installed state
517502

518503
**Why parse-time resolution?**
519504
- Modules are source code, not compiled artifacts
520505
- Allows inspection/modification for reproducibility
521506
- Enables dependency analysis before execution
522507

523-
**Why NPM-style scoped packages?**
524-
- Organization namespacing prevents name collisions (`@nf-core/salmon` vs `@myorg/salmon`)
508+
**Why scoped modules?**
509+
- Organization namespacing prevents name collisions (`nf-core/salmon` vs `myorg/salmon`)
525510
- Clear ownership and provenance of modules
526511
- Supports private registries per scope
527512
- Industry-standard pattern (NPM, Terraform, others)
@@ -535,12 +520,12 @@ project-root/
535520

536521
**Positive**:
537522
- Enables ecosystem-wide code reuse
538-
- Reproducible workflows (exact versions pinned in nextflow.config)
523+
- Reproducible workflows via committing the `modules/` directory (including `meta.yaml`) to the project git repository
539524
- Centralized discovery and distribution via unified registry
540525
- Minimal operational overhead (single registry for both plugins and modules)
541-
- NPM-style scoping enables organization namespaces and private registries
526+
- Module scoping enables organization namespaces and private registries
542527
- Local `modules/` directory provides project isolation
543-
- Simple config model: no separate lock file
528+
- No version duplication: installed `meta.yaml` is the single source of truth
544529
- Simple module structure: each module has single `main.nf` entry point
545530

546531
**Negative**:
@@ -559,12 +544,6 @@ project-root/
559544
- Inspired by: [Go Modules](https://go.dev/ref/mod), [npm](https://docs.npmjs.com), [Cargo](https://doc.rust-lang.org/cargo/)
560545
- Related: [nf-core modules](https://nf-co.re/modules)
561546

562-
## Open Questions
563-
564-
1. **Local vs managed module distinction**: Should local modules use the `@` prefix in include statements, or should a dot file (e.g., `.nf-modules`) be used to distinguish local modules from managed/remote modules?
565-
566-
2. **Module version configuration**: Should pipeline module versions be specified in `nextflow.config` or in a dedicated pipeline spec file (e.g., `pipeline.yaml`)?
567-
568547
---
569548

570549
## Appendix A: Module Schema Specification
@@ -620,8 +599,6 @@ name: myorg/custom-aligner
620599
- Name: lowercase alphanumeric with underscores/hyphens (module identifier)
621600
- Pattern: `^[a-z0-9][a-z0-9-]*/[a-z][a-z0-9_-]*$`
622601

623-
**Note:** The `@` prefix is used only in Nextflow DSL `include` statements (e.g., `include { FASTQC } from '@nf-core/fastqc'`) to distinguish registry modules from local file paths. The meta.yaml `name` field should not include the `@` prefix.
624-
625602
#### `version`
626603

627604
Semantic version following [SemVer 2.0.0](https://semver.org/):

0 commit comments

Comments
 (0)