diff --git a/Makefile b/Makefile index ad40b01..2d01abd 100644 --- a/Makefile +++ b/Makefile @@ -1,72 +1,21 @@ - -config ?= compileClasspath - -ifdef module -mm = :${module}: -else -mm = -endif +# Build the plugin +assemble: + ./gradlew assemble clean: rm -rf .nextflow* rm -rf work rm -rf build - rm -rf plugins/*/build ./gradlew clean -compile: - ./gradlew :nextflow:exportClasspath compileGroovy - @echo "DONE `date`" - - -check: - ./gradlew check - - -# -# Show dependencies try `make deps config=runtime`, `make deps config=google` -# -deps: - ./gradlew -q ${mm}dependencies --configuration ${config} - -deps-all: - ./gradlew -q dependencyInsight --configuration ${config} --dependency ${module} - -# -# Refresh SNAPSHOTs dependencies -# -refresh: - ./gradlew --refresh-dependencies - -# -# Run all tests or selected ones -# +# Run plugin unit tests test: -ifndef class - ./gradlew ${mm}test -else - ./gradlew ${mm}test --tests ${class} -endif - -assemble: - ./gradlew assemble - -# -# generate build zips under build/plugins -# you can install the plugin copying manually these files to $HOME/.nextflow/plugins -# -buildPlugins: - ./gradlew copyPluginZip - -# -# Upload JAR artifacts to Maven Central -# -upload: - ./gradlew upload - + ./gradlew test -upload-plugins: - ./gradlew plugins:upload +# Install the plugin into local nextflow plugins dir +install: + ./gradlew install -publish-index: - ./gradlew plugins:publishIndex +# Publish the plugin +release: + ./gradlew releasePlugin diff --git a/README.md b/README.md index dc8bc09..337e744 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# nf-hello plugin - +# nf-hello plugin + This project contains a simple Nextflow plugin called `nf-hello` which provides examples of different plugin extensions: - A custom trace observer that prints a message when the workflow starts and when the workflow completes @@ -7,108 +7,73 @@ This project contains a simple Nextflow plugin called `nf-hello` which provides - A custom operator called `goodbye` - A custom function called `randomString` -NOTE: If you want to use this project as a starting point for a custom plugin, you must rename the `plugins/nf-hello` folder and update `settings.gradle` with your plugin name. +NOTE: If you want to use this project as a starting point for a custom plugin, you must rename the `plugins/nf-hello` +folder and update `settings.gradle` with your plugin name. -See the [Nextflow documentation](https://nextflow.io/docs/latest/plugins.html) for more information about developing plugins. +See the [Nextflow documentation](https://nextflow.io/docs/latest/plugins.html) for more information about developing +plugins. ## Plugin structure - -- `settings.gradle` - - Gradle project settings. - -- `plugins/nf-hello` - - The plugin implementation base directory. - -- `plugins/nf-hello/build.gradle` - - Plugin Gradle build file. Project dependencies should be added here. -- `plugins/nf-hello/src/resources/META-INF/MANIFEST.MF` - - Manifest file defining the plugin attributes e.g. name, version, etc. The attribute `Plugin-Class` declares the plugin main class. This class should extend the base class `nextflow.plugin.BasePlugin` e.g. `nextflow.hello.HelloPlugin`. +- `settings.gradle` -- `plugins/nf-hello/src/resources/META-INF/extensions.idx` - - This file declares one or more extension classes provided by the plugin. Each line should contain the fully qualified name of a Java class that implements the `org.pf4j.ExtensionPoint` interface (or a sub-interface). + Gradle project settings. -- `plugins/nf-hello/src/main` +- `src/main` - The plugin implementation sources. + The plugin implementation sources. -- `plugins/nf-hello/src/test` +- `src/test` - The plugin unit tests. + The plugin unit tests. ## Plugin classes - `HelloConfig`: shows how to handle options from the Nextflow configuration -- `HelloExtension`: shows how to create custom channel factories, operators, and fuctions that can be included into pipeline scripts +- `HelloExtension`: shows how to create custom channel factories, operators, and fuctions that can be included into + pipeline scripts - `HelloFactory` and `HelloObserver`: shows how to react to workflow events with custom behavior - `HelloPlugin`: the plugin entry point -## Unit testing - -To run your unit tests, run the following command in the project root directory (ie. where the file `settings.gradle` is located): +## Building +To compile and assemble the plugin, run the following command in the project root directory: ```bash -./gradlew check +make assemble ``` -## Testing and debugging - -To build and test the plugin during development, configure a local Nextflow build with the following steps: +## Unit testing -1. Clone the Nextflow repository in your computer into a sibling directory: - ```bash - git clone --depth 1 https://github.com/nextflow-io/nextflow ../nextflow - ``` - -2. Configure the plugin build to use the local Nextflow code: - ```bash - echo "includeBuild('../nextflow')" >> settings.gradle - ``` - - (Make sure to not add it more than once!) - -3. Compile the plugin alongside the Nextflow code: - ```bash - make assemble - ``` - -4. Run Nextflow with the plugin, using `./launch.sh` as a drop-in replacement for the `nextflow` command, and adding the option `-plugins nf-hello` to load the plugin: - ```bash - ./launch.sh run nextflow-io/hello -plugins nf-hello - ``` +To run your unit tests, run the following command in the project root directory: +```bash +make test +``` -## Testing without Nextflow build +## Testing with Nextflow -The plugin can be tested without using a local Nextflow build using the following steps: +The plugin can be tested without a local Nextflow installation: -1. Build the plugin: `make buildPlugins` -2. Copy `build/plugins/` to `$HOME/.nextflow/plugins` -3. Create a pipeline that uses your plugin and run it: `nextflow run ./my-pipeline-script.nf` +1. Build and install the plugin to your local Nextflow installation: `make install` +2. Create a pipeline that uses your plugin and run it: `nextflow run ./my-pipeline-script.nf` ## Package, upload, and publish -The project should be hosted in a GitHub repository whose name matches the name of the plugin, that is the name of the directory in the `plugins` folder (e.g. `nf-hello`). - Follow these steps to package, upload and publish the plugin: -1. Create a file named `gradle.properties` in the project root containing the following attributes (this file should not be committed to Git): +1. In `build.gradle` make sure that + * `github.repository` matches the repository of the plugin + * `github.indexUrl` points to your fork of the plugins index repository. + +2. Create a file named `$HOME/.gradle/gradle.properties`, where $HOME is your home directory. Add the following properties: - * `github_organization`: the GitHub organisation where the plugin repository is hosted. - * `github_username`: The GitHub username granting access to the plugin repository. - * `github_access_token`: The GitHub access token required to upload and commit changes to the plugin repository. - * `github_commit_email`: The email address associated with your GitHub account. + * `github_username`: The GitHub username granting access to the plugin repository. + * `github_access_token`: The GitHub access token required to upload and commit changes to the plugin repository. + * `github_commit_email`: The email address associated with your GitHub account. -2. Use the following command to package and create a release for your plugin on GitHub: - ```bash - ./gradlew :plugins:nf-hello:upload - ``` +3. Use the following command to package and create a release for your plugin on GitHub: `make release` -3. Create a pull request against [nextflow-io/plugins](https://github.com/nextflow-io/plugins/blob/main/plugins.json) to make the plugin accessible to Nextflow. +4. Create a pull request against [nextflow-io/plugins](https://github.com/nextflow-io/plugins/blob/main/plugins.json) to + make the plugin accessible to Nextflow. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..ddf0f09 --- /dev/null +++ b/build.gradle @@ -0,0 +1,30 @@ +plugins { + id 'io.nextflow.nextflow-plugin' version '0.0.1-alpha' +} + +// plugin version +version = '0.6.0' + +nextflowPlugin { + // minimum nextflow version + nextflowVersion = '24.11.0-edge' + + // plugin metadata + provider = 'nextflow' + className = 'nextflow.hello.HelloPlugin' + extensionPoints = [ + 'nextflow.hello.HelloFactory', + 'nextflow.hello.HelloExtension' + ] + + publishing { + github { + repository = 'nextflow-io/nf-hello' + userName = project.findProperty('github_username') + authToken = project.findProperty('github_access_token') + email = project.findProperty('github_commit_email') + + indexUrl = 'https://github.com/nextflow-io/plugins/blob/main/plugins.json' + } + } +} diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle deleted file mode 100644 index 66a0569..0000000 --- a/buildSrc/build.gradle +++ /dev/null @@ -1,14 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - */ - -plugins { - // Support convention plugins written in Groovy. Convention plugins are build scripts in 'src/main' that automatically become available as plugins in the main build. - id 'groovy-gradle-plugin' -} - -repositories { - // Use the plugin portal to apply community plugins in convention plugins. - gradlePluginPortal() -} - diff --git a/buildSrc/src/main/groovy/io.nextflow.groovy-application-conventions.gradle b/buildSrc/src/main/groovy/io.nextflow.groovy-application-conventions.gradle deleted file mode 100644 index ecc029b..0000000 --- a/buildSrc/src/main/groovy/io.nextflow.groovy-application-conventions.gradle +++ /dev/null @@ -1,11 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - */ - -plugins { - // Apply the common convention plugin for shared build configuration between library and application projects. - id 'io.nextflow.groovy-common-conventions' - - // Apply the application plugin to add support for building a CLI application in Java. - id 'application' -} diff --git a/buildSrc/src/main/groovy/io.nextflow.groovy-common-conventions.gradle b/buildSrc/src/main/groovy/io.nextflow.groovy-common-conventions.gradle deleted file mode 100644 index d51949f..0000000 --- a/buildSrc/src/main/groovy/io.nextflow.groovy-common-conventions.gradle +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - */ - -plugins { - // Apply the groovy Plugin to add support for Groovy. - id 'groovy' -} - -repositories { - // Use Maven Central for resolving dependencies. - mavenCentral() -} - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } -} - -compileJava { - options.release.set(17) -} - -tasks.withType(GroovyCompile) { - sourceCompatibility = '17' - targetCompatibility = '17' -} - -tasks.withType(Test) { - jvmArgs ([ - '--add-opens=java.base/java.lang=ALL-UNNAMED', - '--add-opens=java.base/java.io=ALL-UNNAMED', - '--add-opens=java.base/java.nio=ALL-UNNAMED', - '--add-opens=java.base/java.nio.file.spi=ALL-UNNAMED', - '--add-opens=java.base/java.net=ALL-UNNAMED', - '--add-opens=java.base/java.util=ALL-UNNAMED', - '--add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED', - '--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED', - '--add-opens=java.base/sun.nio.ch=ALL-UNNAMED', - '--add-opens=java.base/sun.nio.fs=ALL-UNNAMED', - '--add-opens=java.base/sun.net.www.protocol.http=ALL-UNNAMED', - '--add-opens=java.base/sun.net.www.protocol.https=ALL-UNNAMED', - '--add-opens=java.base/sun.net.www.protocol.ftp=ALL-UNNAMED', - '--add-opens=java.base/sun.net.www.protocol.file=ALL-UNNAMED', - '--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED', - ]) -} diff --git a/buildSrc/src/main/groovy/io.nextflow.groovy-library-conventions.gradle b/buildSrc/src/main/groovy/io.nextflow.groovy-library-conventions.gradle deleted file mode 100644 index 0183e2c..0000000 --- a/buildSrc/src/main/groovy/io.nextflow.groovy-library-conventions.gradle +++ /dev/null @@ -1,11 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - */ - -plugins { - // Apply the common convention plugin for shared build configuration between library and application projects. - id 'io.nextflow.groovy-common-conventions' - // Apply the java-library plugin for API and implementation separation. - id 'java-library' -} - diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c..7f93135 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e411586..df97d72 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0..1aa94a4 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,99 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +119,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/launch.sh b/launch.sh deleted file mode 100755 index 20ae0fd..0000000 --- a/launch.sh +++ /dev/null @@ -1,2 +0,0 @@ -export NXF_PLUGINS_DEV=$PWD/plugins - ../nextflow/launch.sh "$@" diff --git a/nextflow.config b/nextflow.config deleted file mode 100644 index 3084913..0000000 --- a/nextflow.config +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id 'nf-hello' -} \ No newline at end of file diff --git a/plugins/build.gradle b/plugins/build.gradle deleted file mode 100644 index ed5a617..0000000 --- a/plugins/build.gradle +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2021-2022, Seqera Labs - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -plugins { - id "java" - id "io.nextflow.nf-build-plugin" version "1.0.1" -} - -ext.github_organization = 'nextflow-io' -ext.github_username = project.findProperty('github_username') ?: 'pditommaso' -ext.github_access_token = project.findProperty('github_access_token') ?: System.getenv('GITHUB_TOKEN') -ext.github_commit_email = project.findProperty('github_commit_email') ?: 'paolo.ditommaso@gmail.com' - -jar.enabled = false - -String computeSha512(File file) { - if( !file.exists() ) - throw new GradleException("Missing file: $file -- cannot compute SHA-512") - return org.apache.commons.codec.digest.DigestUtils.sha512Hex(file.bytes) -} - -String now() { - "${java.time.OffsetDateTime.now().format(java.time.format.DateTimeFormatter.ISO_DATE_TIME)}" -} - -List allPlugins() { - def plugins = [] - new File(rootProject.rootDir, 'plugins') .eachDir { if(it.name.startsWith('nf-')) plugins.add(it.name) } - return plugins -} - -String metaFromManifest(String meta, File file) { - def str = file.text - def regex = ~/(?m)^$meta:\s*([\w-\.<>=]+)$/ - def m = regex.matcher(str) - if( m.find() ) { - def ver = m.group(1) - //println "Set plugin '${file.parentFile.parentFile.parentFile.parentFile.name}' version=${ver}" - return ver - } - throw new GradleException("Cannot find '$meta' for plugin: $file") -} - -def timestamp = now() - -subprojects { - apply plugin: 'java' - apply plugin: 'groovy' - apply plugin: 'io.nextflow.nf-build-plugin' - - repositories { - mavenLocal() - mavenCentral() - } - - version = metaFromManifest('Plugin-Version',file('src/resources/META-INF/MANIFEST.MF')) - - tasks.withType(Jar) { - duplicatesStrategy = DuplicatesStrategy.INCLUDE - } - - /* - * Creates plugin zip and json meta file in plugin `build/libs` directory - */ - task makeZip(type: Jar) { - into('classes') { with jar } - into('lib') { from configurations.runtimeClasspath } - manifest.from file('src/resources/META-INF/MANIFEST.MF') - archiveExtension = 'zip' - preserveFileTimestamps = false - reproducibleFileOrder = true - - doLast { - // create the meta file - final zip = new File("$buildDir/libs/${project.name}-${project.version}.zip") - final json = new File("$buildDir/libs/${project.name}-${project.version}-meta.json") - json.text = """\ - { - "version": "${project.version}", - "date": "${timestamp}", - "url": "https://github.com/${github_organization}/${project.name}/releases/download/${project.version}/${project.name}-${project.version}.zip", - "requires": "${metaFromManifest('Plugin-Requires',file('src/resources/META-INF/MANIFEST.MF'))}", - "sha512sum": "${computeSha512(zip)}" - } - """.stripIndent() - // cleanup tmp dir - file("$buildDir/tmp/makeZip").deleteDir() - } - outputs.file("$buildDir/libs/${project.name}-${project.version}.zip") - } - - /* - * Copy the plugin dependencies in the subproject `build/target/libs` directory - */ - task copyPluginLibs(type: Sync) { - from configurations.runtimeClasspath - into 'build/target/libs' - } - - /* - * Copy the plugin in the project root build/plugins directory - */ - task copyPluginZip(type: Copy, dependsOn: project.tasks.findByName('makeZip')) { - from makeZip - into "$rootProject.buildDir/plugins" - outputs.file("$rootProject.buildDir/plugins/${project.name}-${project.version}.zip") - doLast { - ant.unzip( - src: "$rootProject.buildDir/plugins/${project.name}-${project.version}.zip", - dest: "$rootProject.buildDir/plugins/${project.name}-${project.version}" - ) - } - } - - /* - * "install" the plugin the project root build/plugins directory - */ - project.parent.tasks.getByName("assemble").dependsOn << copyPluginZip - - task uploadPlugin(type: io.nextflow.gradle.tasks.GithubUploader, dependsOn: makeZip) { - assets = providers.provider {["$buildDir/libs/${project.name}-${project.version}.zip", - "$buildDir/libs/${project.name}-${project.version}-meta.json" ]} - release = providers.provider { project.version } - repo = providers.provider { project.name } - owner = github_organization - userName = github_username - authToken = github_access_token - skipExisting = true - } - - jar { - from sourceSets.main.allSource - doLast { - file("$buildDir/tmp/jar").deleteDir() - } - } - - tasks.withType(GenerateModuleMetadata) { - enabled = false - } - - task upload(dependsOn: [uploadPlugin] ) { } -} - -/* - * Upload all plugins to the corresponding GitHub repos - */ -task upload(dependsOn: [subprojects.uploadPlugin]) { } - -/* - * Copies the plugins required dependencies in the corresponding lib directory - */ -classes.dependsOn subprojects.copyPluginLibs - -/* - * Merge and publish the plugins index file - */ -task publishIndex( type: io.nextflow.gradle.tasks.GithubRepositoryPublisher ) { - indexUrl = 'https://github.com/nextflow-io/plugins/main/plugins.json' - repos = allPlugins() - owner = github_organization - githubUser = github_username - githubEmail = github_commit_email - githubToken = github_access_token -} diff --git a/plugins/nf-hello/build.gradle b/plugins/nf-hello/build.gradle deleted file mode 100644 index e7c1f22..0000000 --- a/plugins/nf-hello/build.gradle +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2021-2022, Seqera Labs - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -plugins { - // Apply the groovy plugin to add support for Groovy - id 'io.nextflow.groovy-library-conventions' - id 'idea' -} - -group = 'io.nextflow' -// DO NOT SET THE VERSION HERE -// THE VERSION FOR PLUGINS IS DEFINED IN THE `/resources/META-INF/MANIFEST.NF` file - -idea { - module.inheritOutputDirs = true -} - -repositories { - mavenCentral() - maven { url = 'https://jitpack.io' } - maven { url = 'https://s3-eu-west-1.amazonaws.com/maven.seqera.io/releases' } - maven { url = 'https://s3-eu-west-1.amazonaws.com/maven.seqera.io/snapshots' } -} - -configurations { - // see https://docs.gradle.org/4.1/userguide/dependency_management.html#sub:exclude_transitive_dependencies - runtimeClasspath.exclude group: 'org.slf4j', module: 'slf4j-api' -} - -sourceSets { - main.java.srcDirs = [] - main.groovy.srcDirs = ['src/main'] - main.resources.srcDirs = ['src/resources'] - test.groovy.srcDirs = ['src/test'] - test.java.srcDirs = [] - test.resources.srcDirs = ['src/testResources'] -} - -ext{ - nextflowVersion = '24.11.0-edge' -} - -dependencies { - // This dependency is exported to consumers, that is to say found on their compile classpath. - compileOnly "io.nextflow:nextflow:$nextflowVersion" - compileOnly 'org.slf4j:slf4j-api:1.7.10' - compileOnly 'org.pf4j:pf4j:3.4.1' - // add here plugins depepencies - - // test configuration - testImplementation "org.apache.groovy:groovy:4.0.18" - testImplementation "org.apache.groovy:groovy-nio:4.0.18" - testImplementation "io.nextflow:nextflow:$nextflowVersion" - testImplementation ("org.apache.groovy:groovy-test:4.0.18") { exclude group: 'org.apache.groovy' } - testImplementation ("cglib:cglib-nodep:3.3.0") - testImplementation ("org.objenesis:objenesis:3.1") - testImplementation ("org.spockframework:spock-core:2.3-groovy-4.0") { exclude group: 'org.apache.groovy'; exclude group: 'net.bytebuddy' } - testImplementation ('org.spockframework:spock-junit4:2.3-groovy-4.0') { exclude group: 'org.apache.groovy'; exclude group: 'net.bytebuddy' } - testImplementation ('com.google.jimfs:jimfs:1.1') - - testImplementation(testFixtures("io.nextflow:nextflow:$nextflowVersion")) - testImplementation(testFixtures("io.nextflow:nf-commons:$nextflowVersion")) - - // see https://docs.gradle.org/4.1/userguide/dependency_management.html#sec:module_replacement - modules { - module("commons-logging:commons-logging") { replacedBy("org.slf4j:jcl-over-slf4j") } - } -} - -// use JUnit 5 platform -test { - useJUnitPlatform() -} - diff --git a/plugins/nf-hello/src/resources/META-INF/MANIFEST.MF b/plugins/nf-hello/src/resources/META-INF/MANIFEST.MF deleted file mode 100644 index d01e0c8..0000000 --- a/plugins/nf-hello/src/resources/META-INF/MANIFEST.MF +++ /dev/null @@ -1,6 +0,0 @@ -Manifest-Version: 1.0 -Plugin-Id: nf-hello -Plugin-Version: 0.6.0 -Plugin-Class: nextflow.hello.HelloPlugin -Plugin-Provider: nextflow -Plugin-Requires: >=24.11.0-edge diff --git a/plugins/nf-hello/src/resources/META-INF/extensions.idx b/plugins/nf-hello/src/resources/META-INF/extensions.idx deleted file mode 100644 index 29d93e9..0000000 --- a/plugins/nf-hello/src/resources/META-INF/extensions.idx +++ /dev/null @@ -1,2 +0,0 @@ -nextflow.hello.HelloFactory -nextflow.hello.HelloExtension \ No newline at end of file diff --git a/plugins/nf-hello/src/test/nextflow/hello/MockHelpers.groovy b/plugins/nf-hello/src/test/nextflow/hello/MockHelpers.groovy deleted file mode 100644 index ac81cb3..0000000 --- a/plugins/nf-hello/src/test/nextflow/hello/MockHelpers.groovy +++ /dev/null @@ -1,182 +0,0 @@ -package nextflow.hello - -import groovy.util.logging.Slf4j -import groovyx.gpars.dataflow.DataflowBroadcast -import nextflow.Session -import nextflow.executor.Executor -import nextflow.executor.ExecutorFactory -import nextflow.processor.TaskHandler -import nextflow.processor.TaskMonitor -import nextflow.processor.TaskRun -import nextflow.processor.TaskStatus -import nextflow.script.BaseScript -import nextflow.script.ChannelOut -import nextflow.script.ScriptRunner -import nextflow.script.ScriptType - -import java.nio.file.Paths - -class MockScriptRunner extends ScriptRunner { - - MockScriptRunner() { - super(new MockSession()) - } - - MockScriptRunner(Map config) { - super(new MockSession(config)) - } - - MockScriptRunner setScript(String str) { - def script = TestHelper.createInMemTempFile('main.nf', str) - setScript(script) - return this - } - - MockScriptRunner invoke() { - execute() - return this - } - - BaseScript getScript() { getScriptObj() } - - @Override - def normalizeOutput(output) { - if( output instanceof ChannelOut ) { - def list = new ArrayList(output.size()) - for( int i=0; i(output.size()) - for( def item : output ) { - ((List)result).add(read0(item)) - } - return result - } - - else { - return read0(output) - } - } - - - private read0( obj ) { - if( obj instanceof DataflowBroadcast ) - return obj.createReadChannel() - return obj - } - -} - -class MockSession extends Session { - - @Override - Session start() { - this.executorFactory = new MockExecutorFactory() - return super.start() - } - - MockSession() { - super() - } - - MockSession(Map config) { - super(config) - } -} - -class MockExecutorFactory extends ExecutorFactory { - @Override - protected Class getExecutorClass(String executorName) { - return MockExecutor - } - - @Override - protected boolean isTypeSupported(ScriptType type, Object executor) { - true - } -} - -/** - * - * @author Paolo Di Tommaso - */ -class MockExecutor extends Executor { - - @Override - void signal() { } - - protected TaskMonitor createTaskMonitor() { - new MockMonitor() - } - - @Override - TaskHandler createTaskHandler(TaskRun task) { - return new MockTaskHandler(task) - } -} - -class MockMonitor implements TaskMonitor { - - void schedule(TaskHandler handler) { - handler.submit() - } - - /** - * Remove the {@code TaskHandler} instance from the queue of tasks to be processed - * - * @param handler A not null {@code TaskHandler} instance - */ - boolean evict(TaskHandler handler) { } - - /** - * Start the monitoring activity for the queued tasks - * @return The instance itself, useful to chain methods invocation - */ - TaskMonitor start() { } - - /** - * Notify when a task terminates - */ - void signal() { } -} - -@Slf4j -class MockTaskHandler extends TaskHandler { - - protected MockTaskHandler(TaskRun task) { - super(task) - } - - @Override - void submit() { - log.info ">> launching mock task: ${task}" - if( task.type == ScriptType.SCRIPTLET ) { - task.workDir = Paths.get('.').complete() - task.stdout = task.script - task.exitStatus = 0 - } - else { - task.code.call() - } - status = TaskStatus.COMPLETED - task.processor.finalizeTask(task) - } - - @Override - boolean checkIfRunning() { - return false - } - - @Override - boolean checkIfCompleted() { - true - } - - @Override - void kill() { } - -} diff --git a/plugins/nf-hello/src/test/nextflow/hello/TestHelper.groovy b/plugins/nf-hello/src/test/nextflow/hello/TestHelper.groovy deleted file mode 100644 index d63444b..0000000 --- a/plugins/nf-hello/src/test/nextflow/hello/TestHelper.groovy +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2020-2022, Seqera Labs - * Copyright 2013-2019, Centre for Genomic Regulation (CRG) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package nextflow.hello - -import com.google.common.jimfs.Configuration -import com.google.common.jimfs.Jimfs -import groovy.transform.Memoized - -import java.nio.file.Files -import java.nio.file.Path -import java.util.zip.GZIPInputStream - -/** - * - * @author Paolo Di Tommaso - */ -class TestHelper { - - static private fs = Jimfs.newFileSystem(Configuration.unix()); - - static Path createInMemTempFile(String name='temp.file', String content=null) { - Path tmp = fs.getPath("/tmp"); - tmp.mkdir() - def result = Files.createTempDirectory(tmp, 'test').resolve(name) - if( content ) - result.text = content - return result - } - -} diff --git a/settings.gradle b/settings.gradle index 494db0c..a8fcf50 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,26 +1,9 @@ -/* - * Copyright 2021, Seqera Labs - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -plugins { - // required to download the toolchain (jdk) from a remote repository - // https://github.com/gradle/foojay-toolchains - // https://docs.gradle.org/current/userguide/toolchains.html#sub:download_repositories - id("org.gradle.toolchains.foojay-resolver-convention") version "0.7.0" +// only needed until gradle plugin is published +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + } } rootProject.name = 'nf-hello' -include('plugins') -include('plugins:nf-hello') diff --git a/plugins/nf-hello/src/main/nextflow/hello/HelloConfig.groovy b/src/main/groovy/nextflow/hello/HelloConfig.groovy similarity index 100% rename from plugins/nf-hello/src/main/nextflow/hello/HelloConfig.groovy rename to src/main/groovy/nextflow/hello/HelloConfig.groovy diff --git a/plugins/nf-hello/src/main/nextflow/hello/HelloExtension.groovy b/src/main/groovy/nextflow/hello/HelloExtension.groovy similarity index 100% rename from plugins/nf-hello/src/main/nextflow/hello/HelloExtension.groovy rename to src/main/groovy/nextflow/hello/HelloExtension.groovy diff --git a/plugins/nf-hello/src/main/nextflow/hello/HelloFactory.groovy b/src/main/groovy/nextflow/hello/HelloFactory.groovy similarity index 100% rename from plugins/nf-hello/src/main/nextflow/hello/HelloFactory.groovy rename to src/main/groovy/nextflow/hello/HelloFactory.groovy diff --git a/plugins/nf-hello/src/main/nextflow/hello/HelloObserver.groovy b/src/main/groovy/nextflow/hello/HelloObserver.groovy similarity index 100% rename from plugins/nf-hello/src/main/nextflow/hello/HelloObserver.groovy rename to src/main/groovy/nextflow/hello/HelloObserver.groovy diff --git a/plugins/nf-hello/src/main/nextflow/hello/HelloPlugin.groovy b/src/main/groovy/nextflow/hello/HelloPlugin.groovy similarity index 100% rename from plugins/nf-hello/src/main/nextflow/hello/HelloPlugin.groovy rename to src/main/groovy/nextflow/hello/HelloPlugin.groovy diff --git a/plugins/nf-hello/src/test/nextflow/hello/HelloDslTest.groovy b/src/test/groovy/nextflow/hello/HelloDslTest.groovy similarity index 78% rename from plugins/nf-hello/src/test/nextflow/hello/HelloDslTest.groovy rename to src/test/groovy/nextflow/hello/HelloDslTest.groovy index e006a3a..d041af9 100644 --- a/plugins/nf-hello/src/test/nextflow/hello/HelloDslTest.groovy +++ b/src/test/groovy/nextflow/hello/HelloDslTest.groovy @@ -1,8 +1,5 @@ package nextflow.hello -import java.nio.file.Files -import java.util.jar.Manifest - import nextflow.Channel import nextflow.plugin.Plugins import nextflow.plugin.TestPluginDescriptorFinder @@ -12,9 +9,11 @@ import org.pf4j.PluginDescriptorFinder import spock.lang.Shared import spock.lang.Timeout import test.Dsl2Spec +import test.MockScriptRunner +import java.nio.file.Files import java.nio.file.Path - +import java.util.jar.Manifest /** * Unit test for Hello DSL @@ -22,9 +21,10 @@ import java.nio.file.Path * @author : jorge */ @Timeout(10) -class HelloDslTest extends Dsl2Spec{ +class HelloDslTest extends Dsl2Spec { - @Shared String pluginsMode + @Shared + String pluginsMode def setup() { // reset previous instances @@ -34,18 +34,18 @@ class HelloDslTest extends Dsl2Spec{ System.setProperty('pf4j.mode', 'dev') // the plugin root should def root = Path.of('.').toAbsolutePath().normalize() - def manager = new TestPluginManager(root){ + def manager = new TestPluginManager(root) { @Override protected PluginDescriptorFinder createPluginDescriptorFinder() { - return new TestPluginDescriptorFinder(){ + return new TestPluginDescriptorFinder() { @Override protected Manifest readManifestFromDirectory(Path pluginPath) { - if( !Files.isDirectory(pluginPath) ) + if (!Files.isDirectory(pluginPath)) return null - final manifestPath = pluginPath.resolve('build/resources/main/META-INF/MANIFEST.MF') - if( !Files.exists(manifestPath) ) + final manifestPath = pluginPath.resolve('build/tmp/jar/MANIFEST.MF') + if (!Files.exists(manifestPath)) return null final input = Files.newInputStream(manifestPath) @@ -60,23 +60,23 @@ class HelloDslTest extends Dsl2Spec{ def cleanup() { Plugins.stop() PluginExtensionProvider.reset() - pluginsMode ? System.setProperty('pf4j.mode',pluginsMode) : System.clearProperty('pf4j.mode') + pluginsMode ? System.setProperty('pf4j.mode', pluginsMode) : System.clearProperty('pf4j.mode') } - def 'should perform a hi and create a channel' () { + def 'should perform a hi and create a channel'() { when: def SCRIPT = ''' include {reverse} from 'plugin/nf-hello' channel.reverse('hi!') ''' and: - def result = new MockScriptRunner([hello:[prefix:'>>']]).setScript(SCRIPT).execute() + def result = new MockScriptRunner([hello: [prefix: '>>']]).setScript(SCRIPT).execute() then: result.val == 'hi!'.reverse() result.val == Channel.STOP } - def 'should store a goodbye' () { + def 'should store a goodbye'() { when: def SCRIPT = ''' include {goodbye} from 'plugin/nf-hello' @@ -89,10 +89,10 @@ class HelloDslTest extends Dsl2Spec{ then: result.val == 'Goodbye folks' result.val == Channel.STOP - + } - def 'can use an imported function' () { + def 'can use an imported function'() { when: def SCRIPT = ''' include {randomString} from 'plugin/nf-hello' @@ -106,4 +106,4 @@ class HelloDslTest extends Dsl2Spec{ result.val == Channel.STOP } -} +} \ No newline at end of file diff --git a/plugins/nf-hello/src/test/nextflow/hello/HelloFactoryTest.groovy b/src/test/groovy/nextflow/hello/HelloFactoryTest.groovy similarity index 93% rename from plugins/nf-hello/src/test/nextflow/hello/HelloFactoryTest.groovy rename to src/test/groovy/nextflow/hello/HelloFactoryTest.groovy index cc0ab15..a3827f3 100644 --- a/plugins/nf-hello/src/test/nextflow/hello/HelloFactoryTest.groovy +++ b/src/test/groovy/nextflow/hello/HelloFactoryTest.groovy @@ -25,12 +25,12 @@ import spock.lang.Specification */ class HelloFactoryTest extends Specification { - def 'should return observer' () { + def 'should return observer'() { when: def result = new HelloFactory().create(Mock(Session)) then: - result.size()==1 + result.size() == 1 result[0] instanceof HelloObserver } -} +} \ No newline at end of file