Skip to content
Open
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
17 changes: 17 additions & 0 deletions modules/nextflow/src/main/groovy/nextflow/Session.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,23 @@ class Session implements ISession {
NF.isModuleBinariesEnabled()
}

/**
* Whether the entry script was launched directly as a module via
* `nextflow module run`. Used to decide whether the entry script's
* `resources/` bundle (and module bin paths) should be picked up
* even though the script is not being loaded via `include`.
*/
private volatile boolean moduleRun

boolean isModuleRun() {
return moduleRun
}

Session setModuleRun(boolean value) {
this.moduleRun = value
return this
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, there is an inconsistency with setters, setBinding and this one are using fluent but others like setBaseDir and setLibDir return void. Looking at the calls looks like it is not needed.

}

boolean failOnIgnore() {
config.navigate('workflow.failOnIgnore', false) as boolean
}
Expand Down
7 changes: 7 additions & 0 deletions modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ class CmdRun extends CmdBase implements HubOptions {
runner.session.agentLog = SysEnv.isAgentMode()
runner.session.debug = launcher.options.remoteDebug
runner.session.disableJobsCancellation = getDisableJobsCancellation()
runner.session.setModuleRun(isModuleRun())

final isTowerEnabled = config.navigate('tower.enabled') as Boolean
final isDataEnabled = config.navigate("lineage.enabled") as Boolean
Expand Down Expand Up @@ -491,6 +492,12 @@ class CmdRun extends CmdBase implements HubOptions {
printLaunchInfo(repo, head, revision)
}

/**
* @return {@code true} when the entry script is being launched directly as a
* module via `nextflow module run`. Overridden by {@link nextflow.cli.module.CmdModuleRun}.
*/
protected boolean isModuleRun() { false }

static void detectModuleBinaryFeature(ConfigMap config) {
final moduleBinaries = config.navigate('nextflow.enable.moduleBinaries', false)
if( moduleBinaries ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class CmdModuleRun extends CmdRun {
return 'run'
}

@Override
protected boolean isModuleRun() { true }

@Override
void run() {
if( !args ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1569,7 +1569,12 @@ class TaskProcessor {
ResourcesBundle getModuleBundle() {
final script = this.getOwnerScript()
final meta = ScriptMeta.get(script)
return meta?.isModule() ? meta.getModuleBundle() : null
if( meta?.scriptPath == null )
return null
// The bundle is resolved when the owner script is either an included
// module, or it is the entry script of a `nextflow module run`
// invocation -- see #7087
return (meta.isModule() || session.isModuleRun()) ? meta.getModuleBundle() : null
}

@Memoized
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import nextflow.script.BaseScript
import nextflow.script.BodyDef
import nextflow.script.ProcessConfig
import nextflow.script.ProcessConfigV1
import nextflow.script.ScriptMeta
import nextflow.script.ScriptType
import nextflow.script.bundle.ResourcesBundle
import nextflow.script.params.FileInParam
Expand Down Expand Up @@ -100,6 +101,49 @@ class TaskProcessorTest extends Specification {

}

@Unroll
def 'should resolve module bundle for entry script when running as module #desc' () {
given:
def folder = Files.createTempDirectory('test')
def mod = folder.resolve('mod1'); mod.mkdir()
def bin = mod.resolve('resources/usr/bin'); bin.mkdirs()
def scriptPath = mod.resolve('main.nf'); Files.createFile(scriptPath)
Files.createFile(bin.resolve('echo.sh'))
and:
def script = Mock(BaseScript)
def meta = Mock(ScriptMeta) {
getScriptPath() >> scriptPath
isModule() >> IS_MODULE
getModuleBundle() >> ResourcesBundle.scan(mod.resolve('resources'))
}
and:
def session = Mock(Session) {
getConfig() >> [:]
isModuleRun() >> IS_MODULE_RUN
}
def executor = Mock(Executor) {}
def processor = Spy(TaskProcessor, constructorArgs: [[session:session, executor:executor]])
processor.getOwnerScript() >> script

when:
ResourcesBundle bundle
GroovyMock(ScriptMeta, global: true)
ScriptMeta.get(script) >> meta
bundle = processor.getModuleBundle()

then:
(bundle != null) == EXPECTED

cleanup:
folder?.deleteDir()

where:
desc | IS_MODULE | IS_MODULE_RUN | EXPECTED
'(included module)' | true | false | true
'(nextflow module run entry)' | false | true | true
'(plain entry, no module run flag)' | false | false | false
}

@Unroll
def 'should add module bin paths to task env' () {
given:
Expand Down
Loading