diff --git a/CHANGELOG.md b/CHANGELOG.md index fd009138..4d0903dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ # Changelog -## [0.9.0] TBD +## [0.9.0] 2024-12-23 - Only supports Nextflow 24.10 and later (uses Groovy 4) +- Shift to quiltcore 0.1.6 convenience methods - Shift metadata and configuration to `quilt` scope of nextflow.config - Moved test workflows into `wf` folder - Write the output URI when publishing diff --git a/Makefile b/Makefile index 8329a83b..41b277e9 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,6 @@ NF_DIR ?= ../nextflow NF_GIT ?= $(NF_DIR)/nextflow NF_BIN ?= ./launch.sh PID ?= $$$$ -PIPELINE ?= sarek QUERY ?= ?Name=$(USER)&Owner=Kevin+Moore&Date=2023-03-07&Type=CRISPR&Notebook+URL=http%3A%2F%2Fexample.com VERSION ?= $(shell grep 'Plugin-Version' plugins/$(PROJECT)/src/resources/META-INF/MANIFEST.MF | awk '{ print $$2 }') NXF_VER ?= $(shell cat VERSION) @@ -108,16 +107,6 @@ path-input: compile tower-test: $(NF_BIN) $(NF_BIN) run "https://github.com/quiltdata/nf-quilt" -name local_einstein -with-tower -r main -latest --pub "$(TEST_URI)" -# use `make $(PIPELINE) WRITE_BUCKET=my-s3-bucket` to publish `--outdir` to a Quilt package - -$(PIPELINE): $(NF_BIN) install - echo "Ensure you have docker running" - $(NF_BIN) pull nf-core/$(PIPELINE) - $(NF_BIN) run nf-core/$(PIPELINE) -profile test,docker -plugins $(PROJECT)@$(VERSION) --outdir "$(PIPE_OUT)" - -fetchngs: $(NF_GIT) - NXF_VER=$(NXF_VER) $(NF_BIN) run quiltdata/fetchngs -r master -profile test,docker --input wf/ids.csv --outdir "s3://$(WRITE_BUCKET)/nf-quilt/fetchngs" - nf-git-ver: $(NF_GIT) NXF_VER=$(NXF_VER) $(NF_GIT) -v @@ -139,12 +128,13 @@ install: compile ./gradlew copyPluginZip rm -rf ${HOME}/.nextflow/plugins/$(PROJECT)-${VERSION} cp -r build/plugins/$(PROJECT)-${VERSION} ${HOME}/.nextflow/plugins/ + # # Upload JAR artifacts to Maven Central # publish: - echo "Ensure you have set 'github_organization=' in gradle.properties" - ls gradle.properties # create locally or globally if it does not exist + echo "Ensure you have set 'github_organization=' in ~/.gradle/gradle.properties" + ls $(HOME)/.gradle/gradle.properties # create locally or globally if it does not exist ./gradlew :plugins:$(PROJECT):upload ./gradlew :plugins:publishIndex diff --git a/README-DEV.md b/README-DEV.md index 5ccf2969..993c9254 100644 --- a/README-DEV.md +++ b/README-DEV.md @@ -111,8 +111,8 @@ and publish the plugin. Otherwise, follow these steps: -1. Create a file named `gradle.properties` in the project root containing the - following attributes (this file should not be committed to Git): +1. Create a file named `gradle.properties` in the user's home (NOT project) + directory containing the following attributes: - `github_organization`: the GitHub organisation where the plugin repository is hosted. diff --git a/plugins/build.gradle b/plugins/build.gradle index 02eeb70a..c1867f9f 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -15,11 +15,9 @@ import java.time.format.DateTimeFormatter * * 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. + * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS + * of any kind, either express or implied. + * See the License for specific language governing permissions and limitations. */ plugins { @@ -28,14 +26,19 @@ plugins { id 'io.nextflow.nf-build-plugin' version '1.0.1' } -ext.github_organization = project.findProperty('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' -ext.github_index_url = "https://github.com/${github_organization}/plugins/main/plugins.json" as GStringImpl +/* Project Constants */ +ext { + github_organization = project.findProperty('github_organization') ?: 'nextflow-io' + github_username = project.findProperty('github_username') ?: 'pditommaso' + github_access_token = project.findProperty('github_access_token') ?: System.getenv('GITHUB_TOKEN') + github_commit_email = project.findProperty('github_commit_email') ?: 'paolo.ditommaso@gmail.com' + github_index_url = "https://github.com/${github_organization}/plugins/main/plugins.json" as GStringImpl +} +// Disable default JAR generation jar.enabled = false +/* Utility Functions */ static String computeSha512(File file) { if (!file.exists()) { throw new GradleException("Missing file: $file -- cannot compute SHA-512") @@ -49,7 +52,9 @@ static String now() { List allPlugins() { def plugins = [] - new File(rootProject.rootDir, 'plugins') .eachDir { if (it.name.startsWith('nf-')) plugins.add(it.name) } + new File(rootProject.rootDir, 'plugins').eachDir { + if (it.name.startsWith('nf-')) plugins.add(it.name) + } return plugins } @@ -59,7 +64,7 @@ String metaFromManifest(String meta, File file) { def m = regex.matcher(str) if (m.find()) { def ver = m.group(1) - println "Set plugin '${file.parentFile.parentFile.parentFile.parentFile.name}' version=${ver}" + println "Set plugin '${file.parentFile.parentFile.parentFile.parentFile.name}' version=$ver" return ver } throw new GradleException("Cannot find '$meta' for plugin: $file") @@ -67,6 +72,7 @@ String metaFromManifest(String meta, File file) { def timestamp = now() +/* Configure Subprojects */ subprojects { apply plugin: 'java' apply plugin: 'groovy' @@ -83,9 +89,14 @@ subprojects { duplicatesStrategy = DuplicatesStrategy.INCLUDE } - /* - * Creates plugin zip and json meta file in plugin `build/libs` directory - */ + /* Plugin Variables */ + String pluginVersion = project.version + String archiveName = "${project.name}-${pluginVersion}" + File zipFile = layout.buildDirectory.file("libs/${archiveName}.zip").get().asFile + File jsonFile = layout.buildDirectory.file("libs/${archiveName}-meta.json").get().asFile + File pluginsDir = layout.buildDirectory.dir("plugins").get().asFile + + /* Task to Create Plugin Zip and Metadata */ tasks.register('makeZip', Jar) { into('classes') { with jar } into('lib') { from configurations.runtimeClasspath } @@ -94,56 +105,56 @@ subprojects { preserveFileTimestamps = false reproducibleFileOrder = true + String downloadUrl = "https://github.com/${github_organization}/${project.name}/releases/download/${pluginVersion}/${archiveName}.zip" 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 = """\ + // Generate the metadata file + jsonFile.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)}" + "version": "${pluginVersion}", + "date": "${timestamp}", + "url": "${downloadUrl}", + "requires": "${metaFromManifest('Plugin-Requires', file('src/resources/META-INF/MANIFEST.MF'))}", + "sha512sum": "${computeSha512(zipFile)}" } """ } - outputs.file("$buildDir/libs/${project.name}-${project.version}.zip") + outputs.file(zipFile) } - /* - * Copy the plugin dependencies in the subproject `build/target/libs` directory - */ + /* Task to Copy Plugin Dependencies */ tasks.register('copyPluginLibs', Sync) { from configurations.runtimeClasspath into 'build/target/libs' - duplicatesStrategy = 'exclude' + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } - /* - * Copy the plugin in the project root build/plugins directory - */ + /* Task to Copy Plugin ZIP to Plugins Directory */ tasks.register('copyPluginZip', Copy) { - dependsOn project.tasks.findByName('makeZip') - from makeZip - into "$rootProject.buildDir/plugins" - outputs.file("$rootProject.buildDir/plugins/${project.name}-${project.version}.zip") + dependsOn project.tasks.named('makeZip') + from { tasks.named('makeZip').get().outputs.files.singleFile } + into pluginsDir.absolutePath + outputs.file(zipFile) + + def destDirPath = new File(pluginsDir, archiveName).absolutePath doLast { + if (!zipFile.exists()) { + throw new GradleException("File ${zipFile} does not exist! Cannot unzip.") + } ant.unzip( - src: "$rootProject.buildDir/plugins/${project.name}-${project.version}.zip", - dest: "$rootProject.buildDir/plugins/${project.name}-${project.version}" + src: zipFile.absolutePath, + dest: destDirPath ) } } - /* - * "install" the plugin the project root build/plugins directory - */ - project.parent.tasks.getByName('assemble').dependsOn << copyPluginZip - - task uploadPlugin(type: GithubUploader, dependsOn: makeZip) { - assets = providers.provider { ["$buildDir/libs/${project.name }-${project.version }.zip", - "$buildDir/libs/${project.name}-${project.version}-meta.json" ]} as Provider> + /* "Install" the Plugin */ + def providerList = [zipFile.absolutePath, jsonFile.absolutePath] + project.parent.tasks.named('assemble').configure { + dependsOn(copyPluginZip) + } + tasks.register('uploadPlugin', GithubUploader) { + dependsOn tasks.named('makeZip') // Lazy dependency + assets = providers.provider { providerList } as Provider> release = providers.provider { project.version } as Provider repo = providers.provider { project.name } owner = github_organization @@ -153,13 +164,14 @@ subprojects { } } -tasks.register('upload') { dependsOn[subprojects.uploadPlugin] } +/* Upload Task */ +tasks.register('upload') { + dependsOn subprojects.uploadPlugin +} classes.dependsOn subprojects.copyPluginLibs -/* - * Merge and publish the plugins index file - */ +/* Publish Plugins Index File */ tasks.register('publishIndex', GithubRepositoryPublisher) { indexUrl = github_index_url repos = allPlugins() diff --git a/plugins/nf-quilt/build.gradle b/plugins/nf-quilt/build.gradle index 33e71665..6a18b785 100644 --- a/plugins/nf-quilt/build.gradle +++ b/plugins/nf-quilt/build.gradle @@ -7,24 +7,23 @@ * * 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. + * This file 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. */ plugins { - // Apply the groovy plugin to add support for Groovy + // Apply the conventions and plugins required by the build id 'io.nextflow.groovy-library-conventions' id 'idea' - // id 'com.github.ben-manes.versions' version '0.51.0' id 'jacoco' } group = 'io.nextflow' + // DO NOT SET THE VERSION HERE -// THE VERSION FOR PLUGINS IS DEFINED IN THE `/resources/META-INF/MANIFEST.NF` file +// PLUGIN VERSION IS DEFINED IN THE `META-INF/MANIFEST.MF` FILE + java { toolchain { languageVersion = JavaLanguageVersion.of(javaLangVersion) @@ -35,7 +34,7 @@ compileJava { options.release = jdkVersion.toInteger() } -tasks.withType(GroovyCompile) { +tasks.withType(GroovyCompile).configureEach { sourceCompatibility = jdkVersion targetCompatibility = jdkVersion } @@ -52,39 +51,49 @@ repositories { } configurations { - // see https://docs.gradle.org/4.1/userguide/dependency_management.html#sub:exclude_transitive_dependencies - runtimeClasspath.exclude group: 'org.slf4j', module: 'slf4j-api' + // 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'] + main { + java.srcDirs = [] // Disable Java sources + groovy.srcDirs = ['src/main'] // Use Groovy sources + resources.srcDirs = ['src/resources'] // Use resources + } + test { + java.srcDirs = [] // Disable Java sources for tests + groovy.srcDirs = ['src/test'] // Use Groovy test sources + resources.srcDirs = ['src/testResources'] // Use test resources + } } dependencies { def nextflowVersion = rootProject.file('VERSION').text.trim() - // quiltcore - implementation 'com.quiltdata:quiltcore:0.1.6' - // This dependency is exported to consumers, that is to say found on their compile classpath. + // Quiltcore + implementation 'com.quiltdata:quiltcore:0.1.7' + + // Compile-only dependencies compileOnly "io.nextflow:nextflow:$nextflowVersion" compileOnly 'org.slf4j:slf4j-api:2.1.0-alpha1' compileOnly 'org.pf4j:pf4j:3.13.0' + + // Runtime-only dependencies runtimeOnly 'org.junit.platform:junit-platform-launcher:1.11.4' implementation 'commons-io:commons-io:2.18.0' - // test configuration + // Test dependencies testImplementation "io.nextflow:nextflow:$nextflowVersion" - // testImplementation "io.nextflow:nf-commons:$nextflowVersion" testImplementation "org.${groovySource}.groovy:groovy:$groovyVersion" testImplementation "org.${groovySource}.groovy:groovy-nio:$groovyVersion" - testImplementation ("org.${groovySource}.groovy:groovy-test:$groovyVersion") { exclude group: "org.${groovySource}.groovy" } + testImplementation("org.${groovySource}.groovy:groovy-test:$groovyVersion") { + exclude group: "org.${groovySource}.groovy" + } testImplementation('cglib:cglib-nodep:3.3.0') testImplementation('org.objenesis:objenesis:3.4') + + // Spock framework testImplementation("org.spockframework:spock-core:2.3-$groovyV") { exclude group: "org.${groovySource}.groovy" exclude group: 'net.bytebuddy' @@ -93,34 +102,35 @@ dependencies { exclude group: "org.${groovySource}.groovy" exclude group: 'net.bytebuddy' } - testImplementation('com.google.jimfs:jimfs:1.3.0') + // Miscellaneous test dependencies + testImplementation('com.google.jimfs:jimfs:1.3.0') 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 + // Module replacement modules { - module('commons-logging:commons-logging') { - replacedBy('org.slf4j:jcl-over-slf4j') + module('commons-logging:commons-logging') { + replacedBy('org.slf4j:jcl-over-slf4j') } } } -// use JUnit 5 platform +// Use JUnit 5 platform for testing test { useJUnitPlatform() } jacocoTestReport { - dependsOn test // tests are required to run before generating the report + dependsOn test // The report depends on the completion of tests } jacocoTestCoverageVerification { - dependsOn jacocoTestReport // tests are required to run before generating the report + dependsOn jacocoTestReport // Ensure the test report is generated first violationRules { rule { limit { - minimum = 0.7 + minimum = 0.7 // Ensure at least 70% coverage } } @@ -132,7 +142,7 @@ jacocoTestCoverageVerification { limit { counter = 'LINE' value = 'TOTALCOUNT' - maximum = 0.3 + maximum = 0.3 // Coverage limit for lines } } } diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltObserver.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltObserver.groovy index d40ad544..074d258a 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltObserver.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltObserver.groovy @@ -43,11 +43,9 @@ class QuiltObserver implements TraceObserver { boolean checkExtractedPath(QuiltPathify pathify) { String key = pathify.pkgKey() - println("checkExtractedPath[$key]: $pathify [$publishedPaths]") if (key in publishedPaths) { return true } - println("checkExtractedPath: $key not in publishedPaths") addPublishedPath(key, pathify) return false } @@ -59,7 +57,6 @@ class QuiltObserver implements TraceObserver { } finally { lock.unlock() } - println("addPublishedPath[$key]: $pathify [$publishedPaths]") } int countPublishedPaths() { @@ -77,19 +74,15 @@ class QuiltObserver implements TraceObserver { void onFilePublish(Path destination, Path source) { // Path source may be null, won't work with older versions of Nextflow log.info("onFilePublish.dest:$destination <- src:$source") - println("\tonFilePublish.session: $session") if (session == null) { - log.info('onFilePublish: no session intialized') + log.warn('onFilePublish: no session intialized') return } - println('\tonFilePublish.QuiltPathify') QuiltPathify pathify = new QuiltPathify(destination) - println("\tonFilePublish.pathify: $pathify") if (!pathify.isBucketAccessible()) { log.debug("onFilePublish.isBucketAccessible[false]: $pathify") return } - println("\tonFilePublish.isOverlay: ${pathify.isOverlay}") if (pathify.isOverlay && source == null) { log.error("onFilePublish.isOverlay: no source for $pathify") return diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltObserverFactory.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltObserverFactory.groovy index 92db5024..75a10194 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltObserverFactory.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltObserverFactory.groovy @@ -33,7 +33,7 @@ class QuiltObserverFactory implements TraceObserverFactory { @Override Collection create(Session session) { //log.debug("`create` ${this}") - Collection quiltObservers = new ArrayList<>() + Collection quiltObservers = [] quiltObservers.add(new QuiltObserver()) return quiltObservers as Collection diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltPathify.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltPathify.groovy index 6523e71c..aad43300 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltPathify.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltPathify.groovy @@ -98,14 +98,10 @@ class QuiltPathify { // Constructor takes a Path and finds QuiltPath and QuiltPackage QuiltPathify(Path path) { - println("QuiltPathify: $path") if (path in QuiltPath) { this.path = (QuiltPath) path - println("\tQuiltPathify.QuiltPath: $this.path") this.uri = this.path.toUriString() - println("\t\tQuiltPathify.QuiltPath.uri: $this.uri") this.pkg = this.path.pkg() - println("\t\tQuiltPathify.QuiltPath.pkg: $this.pkg") } else if (!findQuiltPath(path.getFileName().toString())) { makeQuiltPath(path.toString()) this.isOverlay = true @@ -113,44 +109,31 @@ class QuiltPathify { } boolean findQuiltPath(String filename) { - println("findQuiltPath: $filename") String base = QuiltPath.getRootPackage(filename) if (base == null) { return false } uri = "${QuiltParser.SCHEME}://${base}" - println("\tfindQuiltPath.uri: $uri") path = QuiltPathFactory.parse(this.uri) - println("\tfindQuiltPath.path: $path") pkg = path.pkg() - println("\tfindQuiltPath.pkg: $pkg") return true } boolean makeQuiltPath(String s3File) { - println("makeQuiltPath: $s3File") String quiltURI = uriFromS3File(s3File) - println("\tmakeQuiltPath.quiltURI: $quiltURI") this.path = QuiltPathFactory.parse(quiltURI) - println("\tmakeQuiltPath.path: $path") this.uri = this.path.toUriString() - println("\tmakeQuiltPath.uri: $uri") this.pkg = this.path.pkg() - println("\tmakeQuiltPath.pkg: $pkg") return true } boolean copyToCache(Path source) { - println("copyToCache: $source -> $path") if (!this.isOverlay) { return false } String localPath = source.getFileName() // FIXME: should be relative to workdir - println("\tcopyToCache.localPath: $localPath") Path destDir = pkg.packageDest() - println("\tcopyToCache.destDir: $destDir") - println("copyToCache: $source -> $destDir / $localPath") copyFile(source, destDir.toString(), localPath) return true } diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltProduct.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltProduct.groovy index d8b19870..d4dcc8e1 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltProduct.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltProduct.groovy @@ -15,6 +15,8 @@ */ package nextflow.quilt +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.ObjectWriter import nextflow.quilt.jep.QuiltPackage import nextflow.quilt.jep.QuiltParser import nextflow.quilt.nio.QuiltPath @@ -94,8 +96,16 @@ ${nextflow} 'homeDir', 'workDir', 'launchDir', 'manifest', 'configFiles' ] + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() + private static final ObjectWriter OBJECT_WRITER = OBJECT_MAPPER.writerWithDefaultPrettyPrinter() + static String toJson(Object value) { + String result = OBJECT_WRITER.writeValueAsString(value) + return result + } + static void printMap(Map map, String title) { - log.info("\n\n\n# $title ${map.keySet()}") + log.debug("\n\n\n# $title ${map.keySet()}") map.each { key, value -> log.info("\n## ${key}: ${value}") } @@ -151,29 +161,24 @@ ${nextflow} ]) QuiltProduct(QuiltPathify pathify, Session session) { - println("Creating QuiltProduct: ${pathify}") + log.debug("Creating QuiltProduct: ${pathify}") this.session = session this.config = session.config ?: [:] this.path = pathify.path - println('QuiltProduct.path') this.pkg = pathify.pkg - println('QuiltProduct.pkg') this.metadata = collectMetadata() - println('QuiltProduct.metadata') - // println("QuiltProduct.flags: ${flags}") if (session.isSuccess() || flags.getProperty(QuiltParser.P_FORCE) == true) { publish() } else { - log.info("not publishing: ${pkg} [unsuccessful session]") + log.warn("not publishing: ${pkg} [unsuccessful session]") } } Map collectMetadata() { if (shouldSkip(KEY_META)) { - log.info("SKIP: metadata for ${pkg}") + log.debug("SKIP: metadata for ${pkg}") return [:] } - println("collectMetadata.config: ${config}") config.remove('executor') config.remove('params') config.remove('session') @@ -182,13 +187,10 @@ ${nextflow} printMap(config, 'config') Map quilt_cf = extractMap(config, KEY_QUILT) - println("collectMetadata.quilt_cf: ${quilt_cf}") Map pkg_meta = pkg.getMetadata() - println("collectMetadata.pkg_meta: ${pkg_meta}") updateFlags(pkg_meta, quilt_cf) Map params = session.getParams() - println("collectMetadata.params: ${params}") if (params != null) { writeMapToPackage(params, 'params') params.remove('genomes') @@ -196,7 +198,6 @@ ${nextflow} printMap(params, 'params') } Map wf = session.getWorkflowMetadata()?.toMap() - println("collectMetadata.wf: ${wf}") String start = wf?.get('start') String complete = wf?.get('complete') String cmd = wf?.get('commandLine') @@ -213,9 +214,8 @@ ${nextflow} } Map cf_meta = extractMap(quilt_cf, KEY_META) // remove after setting flags - println("getMetadata.cf_meta: ${cf_meta}") Map base_meta = cf_meta + pkg_meta - log.info("getMetadata.base_meta: ${base_meta}") + log.debug("getMetadata.base_meta: ${base_meta}") return base_meta + [ cmd: cmd, now: now(), @@ -250,18 +250,16 @@ ${nextflow} * @param meta Map of package metadata * @param cf Map of config (from nextflow.config)` */ - void updateFlags(Map pkg_meta, Map cf_meta) { - println("updateFlags.pkg_meta: ${pkg_meta}") - println("updateFlags.cf_meta: ${cf_meta}") + void updateFlags(Map pkgMeta, Map cfMeta) { for (String key : flags.getProperties().keySet()) { - if (pkg_meta.containsKey(key)) { - flags.setProperty(key, pkg_meta[key]) - } else if (cf_meta.containsKey(key)) { - flags.setProperty(key, cf_meta[key]) + if (pkgMeta.containsKey(key)) { + flags.setProperty(key, pkgMeta[key]) + } else if (cfMeta.containsKey(key)) { + flags.setProperty(key, cfMeta[key]) } } // TODO: should this only work for names inferred from S3 URIs? - String pkgName = cf_meta.containsKey(QuiltParser.P_PKG) ? cf_meta[QuiltParser.P_PKG] : pkg.packageName + String pkgName = cfMeta.containsKey(QuiltParser.P_PKG) ? cfMeta[QuiltParser.P_PKG] : pkg.getPackageName() flags.setProperty(QuiltParser.P_PKG, pkgName) } @@ -269,7 +267,7 @@ ${nextflow} String filename = "nf-quilt/${prefix}.json" log.debug("writeMapToPackage[$prefix]: ${filename}") try { - writeString(QuiltPackage.toJson(map), pkg, filename) + writeString(toJson(map), pkg, filename) } catch (Exception e) { log.error("writeMapToPackage.toJson failed: ${e.getMessage()}", map) } @@ -281,13 +279,13 @@ ${nextflow} */ void publish() { - log.info("publish.pushing: ${pkg}") + log.debug("publish.pushing: ${pkg}") try { String message = compileMessage() writeReadme(message) writeSummarize() def rc = pkg.push(message, metadata) - log.info("publish.pushed: ${rc}") + log.debug("publish.pushed: ${rc}") } catch (Exception e) { log.error("Exception: ${e}") @@ -306,7 +304,6 @@ ${nextflow} String compileMessage() { String msg = flags.getProperty(KEY_MSG) GStringTemplateEngine engine = new GStringTemplateEngine() - println("compileMessage: ${msg}") try { String output = engine.createTemplate(msg).make(getTemplateArgs()) log.debug("compileMessage.output: ${output}") @@ -320,7 +317,7 @@ ${nextflow} String compileReadme(String msg) { if (shouldSkip(KEY_README)) { - log.info("SKIP: readme for ${pkg}") + log.debug("SKIP: readme for ${pkg}") return null } String raw_readme = flags.getProperty(KEY_README) @@ -394,14 +391,14 @@ ${nextflow} List writeSummarize() { List quilt_summarize = [] if (shouldSkip(KEY_SUMMARIZE)) { - log.info("SKIP: summarize for ${flags}") + log.debug("SKIP: summarize for ${flags}") return quilt_summarize } String summarize = flags.getProperty(KEY_SUMMARIZE) String[] wildcards = summarize.split(',') wildcards.each { wildcard -> List paths = match(wildcard) - println("writeSummarize: ${paths.size()} matches for ${wildcard}") + log.debug("writeSummarize: ${paths.size()} matches for ${wildcard}") paths.each { path -> String filename = path.getFileName() Map entry = ['path': path.toString(), 'title': filename] @@ -410,7 +407,7 @@ ${nextflow} } try { - String qs_json = QuiltPackage.arrayToJson(quilt_summarize) + String qs_json = toJson(quilt_summarize) writeString(qs_json, pkg, SUMMARY_FILE) } catch (Exception e) { diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltPackage.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltPackage.groovy index 1c19f6c8..6113cc0d 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltPackage.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltPackage.groovy @@ -17,7 +17,6 @@ // https://medium.com/geekculture/how-to-execute-python-modules-from-java-2384041a3d6d package nextflow.quilt.jep -import groovy.json.JsonOutput import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import java.nio.file.FileSystems @@ -31,28 +30,23 @@ import java.nio.file.attribute.BasicFileAttributes import java.util.stream.Collectors import java.time.LocalDate -import com.quiltdata.quiltcore.Entry import com.quiltdata.quiltcore.Registry import com.quiltdata.quiltcore.Namespace import com.quiltdata.quiltcore.Manifest -import com.quiltdata.quiltcore.key.LocalPhysicalKey import com.quiltdata.quiltcore.key.S3PhysicalKey import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectWriter -import com.fasterxml.jackson.databind.node.ObjectNode @Slf4j @CompileStatic class QuiltPackage { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() - private static final ObjectWriter OBJECT_WRITER = OBJECT_MAPPER.writerWithDefaultPrettyPrinter() private static final Map PKGS = [:] private static final String INSTALL_PREFIX = 'QuiltPackage' static final Path INSTALL_ROOT = Files.createTempDirectory(INSTALL_PREFIX) - public final String packageName + private final String packageName private final String bucket private final QuiltParser parsed private final String hash @@ -73,46 +67,18 @@ class QuiltPackage { return date.toString() } - static String sanitize(String str) { - return str.replace('\'', '_') - } - - static String arrayToJson(List array) { - List entries = array.collect { dict -> - return toJson(dict) - } - return "[${entries.join(',')}]".toString() - } - - static String toJson(Map dict) { - List entries = dict.collect { key, value -> - String prefix = OBJECT_WRITER.writeValueAsString(key) - log.debug("toJson.${key}: ${value}") - String suffix = "toJson.error[${value}]" - try { - suffix = OBJECT_WRITER.writeValueAsString(value) - } - catch (Exception e) { - log.error(suffix, e) - } - return "${prefix}:${suffix}".toString() - } - return sanitize("{${entries.join(',')}}".toString()) - } - static void resetPackageCache() { PKGS.clear() } static QuiltPackage forParsed(QuiltParser parsed) { - println("QuiltPackage.forParsed: $parsed") boolean isNull = parsed.hasNullBucket() if (isNull && !PKGS.isEmpty()) { return PKGS.values().last() } String pkgKey = parsed.toPackageString(true) // ignore metadata for Key - log.info("QuiltPackage.forParsed[${pkgKey}]") + log.debug("QuiltPackage.forParsed[${pkgKey}]") def pkg = PKGS.get(pkgKey) if (pkg) { return pkg } @@ -222,6 +188,10 @@ class QuiltPackage { return folder } + String getPackageName() { + return packageName + } + Map getMetadata() { return this.meta } @@ -239,10 +209,9 @@ class QuiltPackage { String implicitStr = implicit ? 'implicitly ' : '' try { - log.info("${implicitStr}installing $packageName from $bucket...") - S3PhysicalKey registryPath = new S3PhysicalKey(bucket, '', null) - Registry registry = new Registry(registryPath) - Namespace namespace = registry.getNamespace(packageName) + log.debug("${implicitStr}installing $packageName from $bucket...") + String registryURI = "s3://${bucket}" + Namespace namespace = Registry.CreateNamespaceAtUri(packageName, registryURI) String resolvedHash = (hash == 'latest' || hash == null || hash == 'null') ? namespace.getHash('latest') : hash @@ -250,7 +219,7 @@ class QuiltPackage { Manifest manifest = namespace.getManifest(resolvedHash) manifest.install(dest) - log.info("install: ${implicitStr}installed into $dest)") + log.debug("install: ${implicitStr}installed into $dest)") log.debug("QuiltPackage.install.Children: ${relativeChildren('')}") } catch (IOException e) { if (!implicit) { @@ -298,28 +267,13 @@ class QuiltPackage { return null } String pkgName = pkg ?: packageName - S3PhysicalKey registryPath = new S3PhysicalKey(bucket, '', null) - Registry registry = new Registry(registryPath) - Namespace namespace = registry.getNamespace(pkgName) - - Manifest.Builder builder = Manifest.builder() - - Files.walk(packageDest()).filter(f -> Files.isRegularFile(f)).forEach(f -> { - log.debug("push: ${f} -> ${packageDest()}") - String logicalKey = packageDest().relativize(f) - LocalPhysicalKey physicalKey = new LocalPhysicalKey(f) - long size = Files.size(f) - builder.addEntry(logicalKey, new Entry(physicalKey, size, null, null)) - }) + String registryURI = "s3://${bucket}" + Namespace namespace = Registry.CreateNamespaceAtUri(pkgName, registryURI) - Map fullMeta = [ - 'version': Manifest.VERSION, - 'user_meta': meta + this.meta, - ] - ObjectMapper mapper = new ObjectMapper() - builder.setMetadata((ObjectNode)mapper.valueToTree(fullMeta)) + Map user_meta = meta + this.meta as Map + // public static Manifest BuildFromDir(Path dir, Object user_meta, String regex) { - Manifest m = builder.build() + Manifest m = Manifest.BuildFromDir(packageDest(), user_meta, null) log.debug("push[${pkgName}]: ${m}") try { Manifest manifest = m.push(namespace, "nf-quilt:${today()}-${msg}", parsed.workflowName) diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystem.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystem.groovy index 98587d88..676c934f 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystem.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystem.groovy @@ -45,19 +45,6 @@ import nextflow.quilt.jep.QuiltParser @CompileStatic final class QuiltFileSystem extends FileSystem implements Closeable { - private final String quiltIDS - private final QuiltFileSystemProvider myProvider - - QuiltFileSystem(String quiltIDS, QuiltFileSystemProvider provider) { - this.quiltIDS = quiltIDS - this.myProvider = provider - } - - @Override - String toString() { - return quiltIDS - } - static void copy(QuiltPath source, QuiltPath target) { throw new UnsupportedOperationException("NOT Implemented 'QuiltFileSystem.copy' `$source` -> `$target`") } @@ -68,31 +55,6 @@ final class QuiltFileSystem extends FileSystem implements Closeable { //throw new UnsupportedOperationException("Operation 'delete' is not supported by QuiltFileSystem") } - @Override - FileSystemProvider provider() { - return myProvider - } - - @Override - void close() throws IOException { - // nothing to do - } - - @Override - boolean isOpen() { - return true - } - - @Override - boolean isReadOnly() { - return false - } - - @Override - String getSeparator() { - return QuiltParser.SEP - } - static QuiltFileAttributesView getFileAttributeView(QuiltPath path) { //log.debug("QuiltFileAttributesView QuiltFileSystem.getFileAttributeView($path)") String pathString = path.toUriString() @@ -122,6 +84,56 @@ final class QuiltFileSystem extends FileSystem implements Closeable { return path.pkg().isInstalled() } + static String toUriString(Path path) { + return path in QuiltPath ? ((QuiltPath)path).toUriString() : null + } + + static String getBashLib(Path path) { + return path in QuiltPath ? QuiltBashLib.script() : null + } + + static String getUploadCmd(String source, Path target) { + return target in QuiltPath ? QuiltFileCopyStrategy.uploadCmd(source, target) : null + } + + private final String quiltIDS + private final QuiltFileSystemProvider myProvider + + QuiltFileSystem(String quiltIDS, QuiltFileSystemProvider provider) { + this.quiltIDS = quiltIDS + this.myProvider = provider + } + + @Override + String toString() { + return quiltIDS + } + + @Override + FileSystemProvider provider() { + return myProvider + } + + @Override + void close() throws IOException { + // nothing to do + } + + @Override + boolean isOpen() { + return true + } + + @Override + boolean isReadOnly() { + return false + } + + @Override + String getSeparator() { + return QuiltParser.SEP + } + Iterable getRootDirectories() { throw new UnsupportedOperationException("Operation 'getRootDirectories' is not supported by QuiltFileSystem") } @@ -145,18 +157,6 @@ final class QuiltFileSystem extends FileSystem implements Closeable { return new QuiltPath(this, p) } - static String toUriString(Path path) { - return path in QuiltPath ? ((QuiltPath)path).toUriString() : null - } - - static String getBashLib(Path path) { - return path in QuiltPath ? QuiltBashLib.script() : null - } - - static String getUploadCmd(String source, Path target) { - return target in QuiltPath ? QuiltFileCopyStrategy.uploadCmd(source, target) : null - } - @Override PathMatcher getPathMatcher(String syntaxAndPattern) { throw new UnsupportedOperationException("Operation 'getPathMatcher' is not supported by QuiltFileSystem") diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystemProvider.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystemProvider.groovy index 8c4d664c..c4fb2d8c 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystemProvider.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystemProvider.groovy @@ -77,6 +77,12 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr ) } + static String getQuiltIDS(URI uri) { + assert uri + QuiltParser parsed = QuiltParser.forURI(uri) + return parsed.quiltID().toString() + } + static QuiltFileSystem getQuiltFilesystem(Path path) { final qPath = asQuiltPath(path) final fs = qPath.getFileSystem() @@ -87,6 +93,43 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr return path.getFileSystem().provider() } + static void checkRoot(Path path) { + if (path == Paths.get('/')) { + throw new UnsupportedOperationException("Operation 'checkRoot' not supported on root path") + } + } + + /** + * Open a file for reading or writing. + + * @param path: the path to the file to open or create + * @param options: options specifying how the file is opened, e.g. StandardOpenOption.WRITE + * @param attrs: (not supported, values will be ignored) + * @return + * @throws IOException + */ + static void notifyFilePublish(QuiltPath destination, Path source=null) { + final sess = Global.session + /* groovylint-disable-next-line Instanceof */ + if (sess instanceof Session) { + sess.notifyFilePublish((Path)destination, source) + } + } + + static DirectoryStream emptyStream() throws IOException { + return new DirectoryStream() { + + @Override + Iterator iterator() { + return Collections.emptyIterator() + } + + /* groovylint-disable-next-line CloseWithoutCloseable */ + @Override void close() throws IOException { } + + } + } + /** * QuiltFileSystemProvider */ @@ -106,10 +149,10 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr QuiltPath qPath = asQuiltPath(remoteFile) Path cachedFile = qPath.localPath() QuiltPackage pkg = qPath.pkg() - log.info "download Quilt package: ${pkg} installed? ${pkg.installed}" + log.debug "download Quilt package: ${pkg} installed? ${pkg.installed}" if (!pkg.installed) { Path dest = pkg.install() - log.info "download.installed Quilt package to: $dest" + log.debug "download.installed Quilt package to: $dest" } final CopyOptions opts = CopyOptions.parse(options) @@ -160,12 +203,6 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr return QuiltParser.SCHEME } - static String getQuiltIDS(URI uri) { - assert uri - QuiltParser parsed = QuiltParser.forURI(uri) - return parsed.quiltID().toString() - } - /** * Constructs a new {@code FileSystem} object identified by a URI. This * method is invoked by the {@link java.nio.file.FileSystems#newFileSystem(URI,Map)} @@ -301,29 +338,6 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr return new QuiltPath(fs, parsed) } - static void checkRoot(Path path) { - if (path == Paths.get('/')) { - throw new UnsupportedOperationException("Operation 'checkRoot' not supported on root path") - } - } - - /** - * Open a file for reading or writing. - - * @param path: the path to the file to open or create - * @param options: options specifying how the file is opened, e.g. StandardOpenOption.WRITE - * @param attrs: (not supported, values will be ignored) - * @return - * @throws IOException - */ - static void notifyFilePublish(QuiltPath destination) { //, Path source=null) { - final sess = Global.session - /* groovylint-disable-next-line Instanceof */ - if (sess instanceof Session) { - sess.notifyFilePublish((Path)destination) //, source) - } - } - @Override SeekableByteChannel newByteChannel( Path path, Set options, FileAttribute... attrs) throws IOException { @@ -351,20 +365,6 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr return null } - static DirectoryStream emptyStream() throws IOException { - return new DirectoryStream() { - - @Override - Iterator iterator() { - return Collections.emptyIterator() - } - - /* groovylint-disable-next-line CloseWithoutCloseable */ - @Override void close() throws IOException { } - - } - } - @Override DirectoryStream newDirectoryStream(Path obj, DirectoryStream.Filter filter) throws IOException { final qPath = asQuiltPath(obj) diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltPath.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltPath.groovy index 55dc45df..fa49e65a 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltPath.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltPath.groovy @@ -55,19 +55,15 @@ final class QuiltPath implements Path { } String base = filename if (n_package > 1) { - log.info("\tfindQuiltPath: multiple '#package' in $base") - println('\t\tTO MATCH') + log.warn("\tfindQuiltPath: multiple '#package' in $base") Matcher matches = (filename =~ /^([^#]+#package=.*)?(?:(?!%2f).)*#package=/) if (!matches) { log.error("findQuiltPath: no match found for $filename") return null } - println('\t\tDID MATCH') - println("\tfindQuiltPath.matches: $matches") List parts = matches[0] as List - println("\tfindQuiltPath.parts: $parts") base = parts[1] - log.info("\tfindQuiltPath: trimmed to $base") + log.debug("\tfindQuiltPath: trimmed to $base") } return base } @@ -77,7 +73,7 @@ final class QuiltPath implements Path { this.parsed = parsed this.paths = parsed.getPaths() this.isFileName = isFileName - log.info("Creating QuiltPath: $parsed") + log.debug("Creating QuiltPath: $parsed") } String getBucket() { @@ -93,7 +89,6 @@ final class QuiltPath implements Path { } QuiltPackage pkg() { - println("QuiltPath.pkg: $parsed") QuiltParser source = isAbsolute() ? parsed : QuiltParser.forNullBucket() return QuiltPackage.forParsed(source) } diff --git a/plugins/nf-quilt/src/resources/META-INF/MANIFEST.MF b/plugins/nf-quilt/src/resources/META-INF/MANIFEST.MF index 5db8939a..a6a5c80f 100644 --- a/plugins/nf-quilt/src/resources/META-INF/MANIFEST.MF +++ b/plugins/nf-quilt/src/resources/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Plugin-Class: nextflow.quilt.QuiltPlugin Plugin-Id: nf-quilt -Plugin-Version: 0.8.11 +Plugin-Version: 0.9.0 Plugin-Provider: Quilt Data Plugin-Requires: >=24.10.0 diff --git a/plugins/nf-quilt/src/test/nextflow/quilt/QuiltProductTest.groovy b/plugins/nf-quilt/src/test/nextflow/quilt/QuiltProductTest.groovy index c6c4d86c..4722de03 100644 --- a/plugins/nf-quilt/src/test/nextflow/quilt/QuiltProductTest.groovy +++ b/plugins/nf-quilt/src/test/nextflow/quilt/QuiltProductTest.groovy @@ -44,7 +44,6 @@ class QuiltProductTest extends QuiltSpecification { WorkflowMetadata wf_meta = GroovyMock(WorkflowMetadata) { toMap() >> [start:'2022-01-01', complete:'2022-01-02'] } - println("makeProductFromUrl: ${url}") QuiltPath path = QuiltPathFactory.parse(url) QuiltPathify pathify = new QuiltPathify(path) Session session = GroovyMock(Session) { @@ -115,7 +114,7 @@ class QuiltProductTest extends QuiltSpecification { !product.shouldSkip(QuiltProduct.KEY_SUMMARIZE) !product.shouldSkip(QuiltProduct.KEY_README) !product.shouldSkip(QuiltProduct.KEY_META) - product.compileReadme("msg") + product.compileReadme('msg') } void 'shouldSkip if key is false'() { @@ -129,7 +128,7 @@ class QuiltProductTest extends QuiltSpecification { expect: product.shouldSkip(key) - product.compileReadme("test") == null + product.compileReadme('test') == null where: key << [ @@ -181,11 +180,11 @@ class QuiltProductTest extends QuiltSpecification { void 'match files if present'() { QuiltProduct product = makeProduct() - String filename = "text.txt" - QuiltProduct.writeString("test", product.pkg, filename) + String filename = 'text.txt' + QuiltProduct.writeString('test', product.pkg, filename) expect: - product.match("*")[0].toString() == filename - product.match("*.txt")[0].toString() == filename + product.match('*')[0].toString() == filename + product.match('*.txt')[0].toString() == filename !product.match('temp.txt') } @@ -220,7 +219,6 @@ class QuiltProductTest extends QuiltSpecification { files.size() > 0 } - void 'should create summarize if files are present'() { String readme_text = 'hasREADME' QuiltProduct product = makeProduct("readme=${readme_text}") diff --git a/plugins/nf-quilt/src/test/nextflow/quilt/QuiltSpecification.groovy b/plugins/nf-quilt/src/test/nextflow/quilt/QuiltSpecification.groovy index ef13413f..8b648e54 100644 --- a/plugins/nf-quilt/src/test/nextflow/quilt/QuiltSpecification.groovy +++ b/plugins/nf-quilt/src/test/nextflow/quilt/QuiltSpecification.groovy @@ -82,13 +82,13 @@ class QuiltSpecification extends Specification { @Override protected Manifest readManifestFromDirectory(Path pluginPath) { - if ( !Files.isDirectory(pluginPath) ) + if (!Files.isDirectory(pluginPath)) { return null - + } final manifestPath = pluginPath.resolve('src/resources/META-INF/MANIFEST.MF') - if ( !Files.exists(manifestPath) ) + if (!Files.exists(manifestPath)) { return null - + } final input = Files.newInputStream(manifestPath) return new Manifest(input) } diff --git a/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltPackageTest.groovy b/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltPackageTest.groovy index cf2d5f95..a12d331c 100644 --- a/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltPackageTest.groovy +++ b/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltPackageTest.groovy @@ -214,7 +214,7 @@ class QuiltPackageTest extends QuiltSpecification { opkg opkg.is_force() opkg.bucketAccessible - opkg.packageName.contains('test/observer') + opkg.getPackageName().contains('test/observer') } @IgnoreIf({ env.WRITE_BUCKET == null }) diff --git a/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltParserTest.groovy b/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltParserTest.groovy index 95eb5361..ee8f043d 100644 --- a/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltParserTest.groovy +++ b/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltParserTest.groovy @@ -108,11 +108,11 @@ class QuiltParserTest extends QuiltSpecification { when: QuiltParser parser = QuiltParser.forUriString(testURI) String unparsed = parser.toUriString().replace('%2f', '/') - String no_scheme = testURI.replace('quilt+s3://','') + String no_scheme = testURI.replace('quilt+s3://', '') then: unparsed == testURI - parser.toString() == no_scheme.replace('/','%2f') + parser.toString() == no_scheme.replace('/', '%2f') } void 'should collect array parameters from query string'() {