From f8980c547ca412e30a35485c6b2e81aca0151226 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sun, 4 May 2025 09:04:11 +0200 Subject: [PATCH 1/5] Add plugin create command [ci fast] Signed-off-by: Paolo Di Tommaso --- .../main/groovy/nextflow/cli/CmdPlugin.groovy | 69 +++++- .../nextflow/util/PluginRefactor.groovy | 201 ++++++++++++++++++ .../nextflow/util/PluginRefactorTest.groovy | 97 +++++++++ 3 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 modules/nextflow/src/main/groovy/nextflow/util/PluginRefactor.groovy create mode 100644 modules/nextflow/src/test/groovy/nextflow/util/PluginRefactorTest.groovy diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy index 2cb0d1df3d..8fee2fa79a 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy @@ -17,14 +17,18 @@ package nextflow.cli +import static nextflow.cli.PluginExecAware.* + +import java.nio.file.Path + import com.beust.jcommander.DynamicParameter import com.beust.jcommander.Parameter import com.beust.jcommander.Parameters import groovy.transform.CompileStatic import nextflow.exception.AbortOperationException import nextflow.plugin.Plugins -import static nextflow.cli.PluginExecAware.CMD_SEP - +import nextflow.util.PluginRefactor +import org.eclipse.jgit.api.Git /** * Plugin manager command * @@ -59,6 +63,9 @@ class CmdPlugin extends CmdBase { throw new AbortOperationException("Missing plugin install target - usage: nextflow plugin install ") Plugins.pull(args[1].tokenize(',')) } + else if( args[0] == 'create' ) { + createPlugin() + } // plugin run command else if( args[0].contains(CMD_SEP) ) { final head = args.pop() @@ -91,4 +98,62 @@ class CmdPlugin extends CmdBase { } } + def createPlugin() { + final refactor = new PluginRefactor() + + // Prompt for plugin name + print "Enter plugin name: " + refactor.withPluginName(readLine()) + + // Prompt for maintainer organization + print "Enter organization: " + refactor.withOrgName(readLine()) + + // Prompt for plugin path (default to the normalised plugin name) + print "Enter project path [${refactor.pluginName}]: " + final targetDir = Path.of(readLine() ?: refactor.pluginName).toFile() + refactor.withPluginDir(targetDir) + + // confirm and proceed + print "All good, are you OK to continue [y/N]? " + final confirm = readLine() + if( confirm!='y' ) + return + + // clone the template repo + clonePluginTemplate(targetDir) + // now refactor the template code + refactor.apply() + // remove git plat + cleanup(targetDir) + // done + println "Plugin created successfully at path: $targetDir" + } + + private String readLine() { + final console = System.console() + return console != null + ? console.readLine() + : new BufferedReader(new InputStreamReader(System.in)).readLine() + } + + private void clonePluginTemplate(File targetDir) { + final templateUri = "https://github.com/nextflow-io/nf-plugin-template.git" + try { + Git.cloneRepository() + .setURI(templateUri) + .setDirectory(targetDir) + .setBranchesToClone(["refs/tags/v1.0.0"]) + .setBranch("refs/tags/v1.0.0") + .call() + } + catch (Exception e) { + throw new AbortOperationException("Unable to clone pluging template repository - cause: ${e.message}") + } + } + + private void cleanup(File targetDir) { + new File(targetDir, '.git').deleteDir() + new File(targetDir, '.github').deleteDir() + } } diff --git a/modules/nextflow/src/main/groovy/nextflow/util/PluginRefactor.groovy b/modules/nextflow/src/main/groovy/nextflow/util/PluginRefactor.groovy new file mode 100644 index 0000000000..45619989c0 --- /dev/null +++ b/modules/nextflow/src/main/groovy/nextflow/util/PluginRefactor.groovy @@ -0,0 +1,201 @@ +/* + * Copyright 2013-2025, 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. + */ + +package nextflow.util + +import java.util.regex.Matcher +import java.util.regex.Pattern + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import nextflow.exception.AbortOperationException + +@Slf4j +@CompileStatic +class PluginRefactor { + + private String pluginName + + private String orgName + + private File pluginDir + + private String pluginClassPrefix + + private File gradleSettingsFile + + private File gradleBuildFile + + private Map tokenMapping = new HashMap<>() + + String getPluginName() { + return pluginName + } + + String getOrgName() { + return orgName + } + + File getPluginDir() { + return pluginDir + } + + PluginRefactor withPluginDir(File directory) { + this.pluginDir = directory + return this + } + + PluginRefactor withPluginName(String name) { + this.pluginName = normalizeToKebabCase(name) + this.pluginClassPrefix = normalizeToClassName(name) + if( pluginName.toLowerCase()=='plugin' ) + throw new IllegalStateException("Invalid plugin name: '$name'") + if( !pluginClassPrefix ) + throw new IllegalStateException("Invalid plugin name: '$name'") + return this + } + + PluginRefactor withOrgName(String name) { + this.orgName = normalizeToPackageNameSegment(name) + if( !orgName ) + throw new AbortOperationException("Invalid organization name: '$name'") + return this + } + + protected void init() { + if( !pluginName ) + throw new IllegalStateException("Missing plugin name") + if( !orgName ) + throw new IllegalStateException("Missing organization name") + // initial + this.gradleBuildFile = new File(pluginDir, 'build.gradle') + this.gradleSettingsFile = new File(pluginDir, 'settings.gradle') + if( !gradleBuildFile.exists() ) + throw new AbortOperationException("Plugin file does not exist: $gradleBuildFile") + if( !gradleSettingsFile.exists() ) + throw new AbortOperationException("Plugin file does not exist: $gradleSettingsFile") + if( !orgName ) + throw new AbortOperationException("Plugin org name is missing") + // packages to be updates + tokenMapping.put('acme', orgName) + tokenMapping.put('nf-plugin-template', pluginName) + } + + void apply() { + init() + replacePrefixInFiles(pluginDir, pluginClassPrefix) + renameDirectory(new File(pluginDir, "src/main/groovy/acme"), new File(pluginDir, "src/main/groovy/${orgName}")) + renameDirectory(new File(pluginDir, "src/test/groovy/acme"), new File(pluginDir, "src/test/groovy/${orgName}")) + updateClassNames(pluginDir) + } + + + protected void replacePrefixInFiles(File rootDir, String newPrefix) { + if (!rootDir.exists() || !rootDir.isDirectory()) { + throw new IllegalStateException("Invalid directory: $rootDir") + } + + rootDir.eachFileRecurse { file -> + if (file.isFile() && file.name.startsWith('My') && file.extension in ['groovy']) { + final newName = file.name.replaceFirst(/^My/, newPrefix) + final renamedFile = new File(file.parentFile, newName) + if (file.renameTo(renamedFile)) { + log.debug "Renamed: ${file.name} -> ${renamedFile.name}" + tokenMapping.put(file.baseName, renamedFile.baseName) + } + else { + throw new IllegalStateException("Failed to rename: ${file.name}") + } + } + } + } + + protected void updateClassNames(File rootDir) { + rootDir.eachFileRecurse { file -> + if (file.isFile() && file.extension in ['groovy','gradle']) { + replaceTokensInFile(file, tokenMapping) + } + } + } + + protected void replaceTokensInFile(File inputFile, Map replacements, File outputFile = inputFile) { + def content = inputFile.text + + // Replace each key with its corresponding value + for( Map.Entry entry : replacements ) { + content = content.replaceAll(Pattern.quote(entry.key), Matcher.quoteReplacement(entry.value)) + } + + outputFile.text = content + log.debug "Replacements done in: ${outputFile.path}" + } + + protected void renameDirectory(File oldDir, File newDir) { + if (!oldDir.exists() || !oldDir.isDirectory()) { + throw new AbortOperationException("Plugin template directory to rename does not exist: $oldDir") + } + + if( oldDir==newDir ) { + log.debug "Unneeded path rename: $oldDir -> $newDir" + } + + if (newDir.exists()) { + throw new AbortOperationException("Plugin target directory already exists: $newDir") + } + + if (oldDir.renameTo(newDir)) { + log.debug "Successfully renamed: $oldDir -> $newDir" + } + else { + throw new AbortOperationException("Unable to replace plugin template path: $oldDir -> $newDir") + } + } + + static String normalizeToClassName(String input) { + // Replace non-alphanumeric characters with spaces (except underscores) + final cleaned = input.replaceAll(/[^a-zA-Z0-9_]/, ' ') + .replaceAll(/_/, ' ') + .trim() + // Split by whitespace, capitalize each word, join them + final parts = cleaned.split(/\s+/).collect { it.capitalize() } + return parts.join('').replace('Plugin','') + } + + static String normalizeToKebabCase(String input) { + // Insert spaces before capital letters (handles CamelCase) + def spaced = input.replaceAll(/([a-z])([A-Z])/, '$1 $2') + .replaceAll(/([A-Z]+)([A-Z][a-z])/, '$1 $2') + // Replace non-alphanumeric characters and underscores with spaces + def cleaned = spaced.replaceAll(/[^a-zA-Z0-9]/, ' ') + .trim() + // Split, lowercase, and join with hyphens + def parts = cleaned.split(/\s+/).collect { it.toLowerCase() } + return parts.join('-') + } + + static String normalizeToPackageNameSegment(String input) { + // Replace non-alphanumeric characters with spaces + def cleaned = input.replaceAll(/[^a-zA-Z0-9]/, ' ') + .trim() + // Split into lowercase words and join + def parts = cleaned.split(/\s+/).collect { it.toLowerCase() } + def name = parts.join('') + + // Strip leading digits + name = name.replaceFirst(/^\d+/, '') + return name ?: null + } +} diff --git a/modules/nextflow/src/test/groovy/nextflow/util/PluginRefactorTest.groovy b/modules/nextflow/src/test/groovy/nextflow/util/PluginRefactorTest.groovy new file mode 100644 index 0000000000..ff57c28e41 --- /dev/null +++ b/modules/nextflow/src/test/groovy/nextflow/util/PluginRefactorTest.groovy @@ -0,0 +1,97 @@ +/* + * Copyright 2013-2025, 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. + */ + +package nextflow.util + +import spock.lang.Ignore +import spock.lang.Specification + +/** + * + * @author Paolo Di Tommaso + */ +class PluginRefactorTest extends Specification { + + @Ignore + def 'should refactor plugin' () { + given: + def refactor = new PluginRefactor() + .withOrgName('seqera') + .withPluginName('Hello') + .withPluginDir(new File('/Users/pditommaso/Projects/nf-plugin-template')) + + when: + refactor.apply() + then: + noExceptionThrown() + } + + + def "should normalize strings into PascalCase class names"() { + given: + def refactor = new PluginRefactor() + expect: + refactor.normalizeToClassName(INPUT) == EXPECTED + + where: + INPUT || EXPECTED + " my-cool_class name! " || "MyCoolClassName" + "acme's #1 utility!" || "AcmeS1Utility" + "hello_world" || "HelloWorld" + "123 start here" || "123StartHere" + " mixed---separators___" || "MixedSeparators" + "alreadyPascalCase" || "AlreadyPascalCase" + "foo-plugin" || "Foo" + } + + def "should normalize strings into kebab-case names"() { + given: + def refactor = new PluginRefactor() + expect: + refactor.normalizeToKebabCase(INPUT) == EXPECTED + + where: + INPUT || EXPECTED + "MyCoolClassName" || "my-cool-class-name" + "my-cool_class name!" || "my-cool-class-name" + "acme's #1 utility!" || "acme-s-1-utility" + "hello_world" || "hello-world" + "123 start here" || "123-start-here" + "Mixed---separators___" || "mixed-separators" + "AlreadyKebab-Case" || "already-kebab-case" + "CamelCaseInput" || "camel-case-input" + "HTMLParserUtility" || "html-parser-utility" + } + + + def "should normalize strings into valid Java package name segments"() { + given: + def refactor = new PluginRefactor() + expect: + refactor.normalizeToPackageNameSegment(INPUT) == EXPECTED + + where: + INPUT || EXPECTED + "Acme Tools!" || "acmetools" + "123-Utility" || "utility" + "com.example" || "comexample" + "__System_Core__" || "systemcore" + "1st-Package" || "stpackage" + "My.App.Service" || "myappservice" + "!@#\$%^&*()" || null + "" || null + } +} From 80fcc14f396f329967d0b819cb9b7e4b62735084 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sun, 4 May 2025 16:19:11 +0200 Subject: [PATCH 2/5] Add tests Signed-off-by: Paolo Di Tommaso --- .../main/groovy/nextflow/cli/CmdPlugin.groovy | 59 ++++++++++------- .../nextflow/cli/CmdPluginCreateTest.groovy | 63 +++++++++++++++++++ .../plugin}/util/PluginRefactor.groovy | 16 +++-- .../plugin}/util/PluginRefactorTest.groovy | 19 +----- 4 files changed, 111 insertions(+), 46 deletions(-) create mode 100644 modules/nextflow/src/test/groovy/nextflow/cli/CmdPluginCreateTest.groovy rename modules/{nextflow/src/main/groovy/nextflow => nf-commons/src/main/nextflow/plugin}/util/PluginRefactor.groovy (93%) rename modules/{nextflow/src/test/groovy/nextflow => nf-commons/src/test/nextflow/plugin}/util/PluginRefactorTest.groovy (87%) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy index 8fee2fa79a..7ec4f5b3c2 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy @@ -27,7 +27,7 @@ import com.beust.jcommander.Parameters import groovy.transform.CompileStatic import nextflow.exception.AbortOperationException import nextflow.plugin.Plugins -import nextflow.util.PluginRefactor +import nextflow.plugin.util.PluginRefactor import org.eclipse.jgit.api.Git /** * Plugin manager command @@ -64,7 +64,7 @@ class CmdPlugin extends CmdBase { Plugins.pull(args[1].tokenize(',')) } else if( args[0] == 'create' ) { - createPlugin() + createPlugin(args) } // plugin run command else if( args[0].contains(CMD_SEP) ) { @@ -98,27 +98,38 @@ class CmdPlugin extends CmdBase { } } - def createPlugin() { - final refactor = new PluginRefactor() - - // Prompt for plugin name - print "Enter plugin name: " - refactor.withPluginName(readLine()) + static createPlugin(List args) { + if( args != ['create'] && (args[0] != 'create' || !(args.size() in [3, 4])) ) + throw new AbortOperationException("Invalid create parameters - usage: nextflow plugin create ") - // Prompt for maintainer organization - print "Enter organization: " - refactor.withOrgName(readLine()) - - // Prompt for plugin path (default to the normalised plugin name) - print "Enter project path [${refactor.pluginName}]: " - final targetDir = Path.of(readLine() ?: refactor.pluginName).toFile() - refactor.withPluginDir(targetDir) + final refactor = new PluginRefactor() + if( args.size()>1 ) { + refactor.withPluginName(args[1]) + refactor.withOrgName(args[2]) + refactor.withPluginDir(Path.of(args[3] ?: refactor.pluginName).toFile()) + } + else { + // Prompt for plugin name + print "Enter plugin name: " + refactor.withPluginName(readLine()) + + // Prompt for maintainer organization + print "Enter organization: " + + // Prompt for plugin path (default to the normalised plugin name) + refactor.withOrgName(readLine()) + print "Enter project path [${refactor.pluginName}]: " + refactor.withPluginDir(Path.of(readLine() ?: refactor.pluginName).toFile()) + + // confirm and proceed + print "All good, are you OK to continue [y/N]? " + final confirm = readLine() + if( confirm!='y' ) + return + } - // confirm and proceed - print "All good, are you OK to continue [y/N]? " - final confirm = readLine() - if( confirm!='y' ) - return + // the final directory where the plugin is created + final File targetDir = refactor.getPluginDir() // clone the template repo clonePluginTemplate(targetDir) @@ -130,14 +141,14 @@ class CmdPlugin extends CmdBase { println "Plugin created successfully at path: $targetDir" } - private String readLine() { + static private String readLine() { final console = System.console() return console != null ? console.readLine() : new BufferedReader(new InputStreamReader(System.in)).readLine() } - private void clonePluginTemplate(File targetDir) { + static private void clonePluginTemplate(File targetDir) { final templateUri = "https://github.com/nextflow-io/nf-plugin-template.git" try { Git.cloneRepository() @@ -152,7 +163,7 @@ class CmdPlugin extends CmdBase { } } - private void cleanup(File targetDir) { + static private void cleanup(File targetDir) { new File(targetDir, '.git').deleteDir() new File(targetDir, '.github').deleteDir() } diff --git a/modules/nextflow/src/test/groovy/nextflow/cli/CmdPluginCreateTest.groovy b/modules/nextflow/src/test/groovy/nextflow/cli/CmdPluginCreateTest.groovy new file mode 100644 index 0000000000..222578d7e5 --- /dev/null +++ b/modules/nextflow/src/test/groovy/nextflow/cli/CmdPluginCreateTest.groovy @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2025, 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. + */ + +package nextflow.cli + +import java.nio.file.Files +import java.nio.file.Path + +import spock.lang.IgnoreIf +import spock.lang.Specification +/** + * + * @author Paolo Di Tommaso + */ +class CmdPluginCreateTest extends Specification { + +// @Requires({System.getenv('NXF_GITHUB_ACCESS_TOKEN')}) + @IgnoreIf({System.getenv('NXF_SMOKE')}) + def 'should clone and create a plugin project' () { + given: + def folder = Files.createTempDirectory('test') + and: + def args = [ + 'create', + 'hello world plugin', + 'foo', + folder.toAbsolutePath().toString() + '/hello'] + + when: + def cmd = new CmdPlugin(args: args) + and: + cmd.run() + + then: + Files.exists(folder.resolve('hello')) + Files.exists(folder.resolve('hello/src/main/groovy/foo/plugin/HelloWorldPlugin.groovy')) + Files.exists(folder.resolve('hello/src/main/groovy/foo/plugin/HelloWorldObserver.groovy')) + Files.exists(folder.resolve('hello/src/main/groovy/foo/plugin/HelloWorldFactory.groovy')) + and: + Files.exists(folder.resolve('hello/src/test/groovy/foo/plugin/HelloWorldObserverTest.groovy')) + and: + Path.of(folder.resolve('hello/settings.gradle').toUri()).text.contains("rootProject.name = 'hello-world-plugin'") + Path.of(folder.resolve('hello/build.gradle').toUri()).text.contains("provider = 'foo'") + Path.of(folder.resolve('hello/build.gradle').toUri()).text.contains("className = 'foo.plugin.HelloWorldPlugin'") + + cleanup: + folder?.deleteDir() + } + +} diff --git a/modules/nextflow/src/main/groovy/nextflow/util/PluginRefactor.groovy b/modules/nf-commons/src/main/nextflow/plugin/util/PluginRefactor.groovy similarity index 93% rename from modules/nextflow/src/main/groovy/nextflow/util/PluginRefactor.groovy rename to modules/nf-commons/src/main/nextflow/plugin/util/PluginRefactor.groovy index 45619989c0..607f13bf70 100644 --- a/modules/nextflow/src/main/groovy/nextflow/util/PluginRefactor.groovy +++ b/modules/nf-commons/src/main/nextflow/plugin/util/PluginRefactor.groovy @@ -14,7 +14,8 @@ * limitations under the License. */ -package nextflow.util +package nextflow.plugin.util + import java.util.regex.Matcher import java.util.regex.Pattern @@ -22,6 +23,7 @@ import java.util.regex.Pattern import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import nextflow.exception.AbortOperationException +import nextflow.extension.FilesEx @Slf4j @CompileStatic @@ -54,7 +56,7 @@ class PluginRefactor { } PluginRefactor withPluginDir(File directory) { - this.pluginDir = directory + this.pluginDir = directory.absoluteFile.canonicalFile return this } @@ -109,12 +111,14 @@ class PluginRefactor { } rootDir.eachFileRecurse { file -> - if (file.isFile() && file.name.startsWith('My') && file.extension in ['groovy']) { + if (file.isFile() && file.name.startsWith('My') && FilesEx.getExtension(file) in ['groovy']) { final newName = file.name.replaceFirst(/^My/, newPrefix) final renamedFile = new File(file.parentFile, newName) if (file.renameTo(renamedFile)) { log.debug "Renamed: ${file.name} -> ${renamedFile.name}" - tokenMapping.put(file.baseName, renamedFile.baseName) + final source = FilesEx.getBaseName(file) + final target = FilesEx.getBaseName(renamedFile) + tokenMapping.put(source, target) } else { throw new IllegalStateException("Failed to rename: ${file.name}") @@ -125,7 +129,7 @@ class PluginRefactor { protected void updateClassNames(File rootDir) { rootDir.eachFileRecurse { file -> - if (file.isFile() && file.extension in ['groovy','gradle']) { + if (file.isFile() && FilesEx.getExtension(file) in ['groovy','gradle']) { replaceTokensInFile(file, tokenMapping) } } @@ -198,4 +202,6 @@ class PluginRefactor { name = name.replaceFirst(/^\d+/, '') return name ?: null } + + } diff --git a/modules/nextflow/src/test/groovy/nextflow/util/PluginRefactorTest.groovy b/modules/nf-commons/src/test/nextflow/plugin/util/PluginRefactorTest.groovy similarity index 87% rename from modules/nextflow/src/test/groovy/nextflow/util/PluginRefactorTest.groovy rename to modules/nf-commons/src/test/nextflow/plugin/util/PluginRefactorTest.groovy index ff57c28e41..39a1d9fc63 100644 --- a/modules/nextflow/src/test/groovy/nextflow/util/PluginRefactorTest.groovy +++ b/modules/nf-commons/src/test/nextflow/plugin/util/PluginRefactorTest.groovy @@ -14,31 +14,16 @@ * limitations under the License. */ -package nextflow.util +package nextflow.plugin.util -import spock.lang.Ignore -import spock.lang.Specification +import spock.lang.Specification /** * * @author Paolo Di Tommaso */ class PluginRefactorTest extends Specification { - @Ignore - def 'should refactor plugin' () { - given: - def refactor = new PluginRefactor() - .withOrgName('seqera') - .withPluginName('Hello') - .withPluginDir(new File('/Users/pditommaso/Projects/nf-plugin-template')) - - when: - refactor.apply() - then: - noExceptionThrown() - } - def "should normalize strings into PascalCase class names"() { given: From 9dad130109b7214fd6aa4bec13e6014aa90b80e4 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sun, 4 May 2025 20:19:37 +0200 Subject: [PATCH 3/5] Fix failing test Signed-off-by: Paolo Di Tommaso --- .../src/test/groovy/nextflow/cli/CmdPluginCreateTest.groovy | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/nextflow/src/test/groovy/nextflow/cli/CmdPluginCreateTest.groovy b/modules/nextflow/src/test/groovy/nextflow/cli/CmdPluginCreateTest.groovy index 222578d7e5..a5afda7544 100644 --- a/modules/nextflow/src/test/groovy/nextflow/cli/CmdPluginCreateTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/cli/CmdPluginCreateTest.groovy @@ -19,6 +19,7 @@ package nextflow.cli import java.nio.file.Files import java.nio.file.Path +import nextflow.plugin.Plugins import spock.lang.IgnoreIf import spock.lang.Specification /** @@ -27,7 +28,10 @@ import spock.lang.Specification */ class CmdPluginCreateTest extends Specification { -// @Requires({System.getenv('NXF_GITHUB_ACCESS_TOKEN')}) + def cleanup() { + Plugins.stop() + } + @IgnoreIf({System.getenv('NXF_SMOKE')}) def 'should clone and create a plugin project' () { given: From f95b4612b7059a86d6db3dc5bc7c7b976f066e29 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Mon, 5 May 2025 10:48:53 +0200 Subject: [PATCH 4/5] Minor change [ci fast] Signed-off-by: Paolo Di Tommaso --- modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy index 7ec4f5b3c2..82771f6bf0 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdPlugin.groovy @@ -17,7 +17,7 @@ package nextflow.cli -import static nextflow.cli.PluginExecAware.* +import static nextflow.cli.PluginExecAware.CMD_SEP import java.nio.file.Path From a49166cb646e3c799df7ed4a8ba5a34e50efc598 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Mon, 5 May 2025 10:50:30 +0200 Subject: [PATCH 5/5] Just blanks [ci ski] Signed-off-by: Paolo Di Tommaso --- .../src/main/nextflow/plugin/util/PluginRefactor.groovy | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/nf-commons/src/main/nextflow/plugin/util/PluginRefactor.groovy b/modules/nf-commons/src/main/nextflow/plugin/util/PluginRefactor.groovy index 607f13bf70..20402870fb 100644 --- a/modules/nf-commons/src/main/nextflow/plugin/util/PluginRefactor.groovy +++ b/modules/nf-commons/src/main/nextflow/plugin/util/PluginRefactor.groovy @@ -16,7 +16,6 @@ package nextflow.plugin.util - import java.util.regex.Matcher import java.util.regex.Pattern @@ -104,7 +103,6 @@ class PluginRefactor { updateClassNames(pluginDir) } - protected void replacePrefixInFiles(File rootDir, String newPrefix) { if (!rootDir.exists() || !rootDir.isDirectory()) { throw new IllegalStateException("Invalid directory: $rootDir") @@ -203,5 +201,4 @@ class PluginRefactor { return name ?: null } - }