diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..387738e --- /dev/null +++ b/build.gradle @@ -0,0 +1,16 @@ +import nextflow.gradle.plugins.NextflowPlugin + +apply plugin: NextflowPlugin + +ext{ + github_organization = 'nextflow-io' + github_username = project.findProperty('github_username') ?: 'abhi18av' + github_access_token = project.findProperty('github_access_token') ?: System.getenv('GITHUB_TOKEN') + github_commit_email = project.findProperty('github_commit_email') ?: 'abhi18av@outlook.com' +} + +nextflowPlugin{ + githubOrganization = github_organization + extensionPoints = [ + ] +} diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 0000000..ddadfb7 --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,12 @@ +plugins { + id 'groovy-gradle-plugin' + id "io.nextflow.nf-build-plugin" version "1.0.1" +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'commons-codec:commons-codec:1.15' +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/nextflow/gradle/plugins/GenerateIdxTask.groovy b/buildSrc/src/main/groovy/nextflow/gradle/plugins/GenerateIdxTask.groovy new file mode 100644 index 0000000..ea3718d --- /dev/null +++ b/buildSrc/src/main/groovy/nextflow/gradle/plugins/GenerateIdxTask.groovy @@ -0,0 +1,41 @@ +package nextflow.gradle.plugins + +import org.gradle.api.DefaultTask +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + + +abstract class GenerateIdxTask extends DefaultTask{ + + @Internal + abstract ListProperty extensionPoints + + @OutputFile + final abstract RegularFileProperty outputFile = + project.objects.fileProperty().convention(project.layout.buildDirectory.file( + "resources/main/META-INF/extensions.idx")) + + GenerateIdxTask() { + setGroup('nextflow') + } + + @TaskAction + def runTask() { + def output = outputFile.get().asFile + + if( extensionPoints.getOrElse([]).size() ){ + output.text = extensionPoints.getOrElse([]).join('\n') + return + } + + def matcher = new SourcesMatcher(project) + def extensionsClassName = matcher.pluginExtensions + def traceClassName = matcher.traceObservers + def all = extensionsClassName+traceClassName + output.text = all.join('\n') + } + +} diff --git a/buildSrc/src/main/groovy/nextflow/gradle/plugins/JsonPluginTask.groovy b/buildSrc/src/main/groovy/nextflow/gradle/plugins/JsonPluginTask.groovy new file mode 100644 index 0000000..d05314d --- /dev/null +++ b/buildSrc/src/main/groovy/nextflow/gradle/plugins/JsonPluginTask.groovy @@ -0,0 +1,67 @@ +package nextflow.gradle.plugins + +import groovy.json.JsonOutput +import org.apache.commons.codec.digest.DigestUtils +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.bundling.Jar + +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter + +abstract class JsonPluginTask extends DefaultTask{ + + @Internal + abstract Property getDownloadUrl() + + @Internal + final abstract RegularFileProperty zipFile = + project.objects.fileProperty().convention(project.layout.buildDirectory.file( + "plugin/${project.name}-${project.version}.zip")) + + @OutputFile + final abstract RegularFileProperty outputFile = + project.objects.fileProperty().convention(project.layout.buildDirectory.file( + "plugin/${project.name}-${project.version}-meta.json")) + + + JsonPluginTask(){ + setGroup('nextflow') + dependsOn 'zipPlugin' + } + + + protected String resolveURL(){ + "${downloadUrl.get()}/${project.version}/${project.name}-${project.version}.zip" + } + + protected static String now() { + OffsetDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME) + } + + protected static String computeSha512(File file) { + if( !file.exists() ) + throw new GradleException("Missing file: $file -- cannot compute SHA-512") + return DigestUtils.sha512Hex(file.bytes) + } + + @TaskAction + def runTask() { + def jarTask = project.tasks.findByName('jar') as Jar + def manifest = jarTask.manifest + def json = [ + version: "$project.version", + date: now(), + url: resolveURL(), + requires: manifest.attributes['Plugin-Requires'], + sha512sum: computeSha512(zipFile.get().asFile) + ] + outputFile.get().asFile.text = JsonOutput.prettyPrint(JsonOutput.toJson(json)) + } + +} diff --git a/buildSrc/src/main/groovy/nextflow/gradle/plugins/NextflowPlugin.groovy b/buildSrc/src/main/groovy/nextflow/gradle/plugins/NextflowPlugin.groovy new file mode 100644 index 0000000..cf00b7e --- /dev/null +++ b/buildSrc/src/main/groovy/nextflow/gradle/plugins/NextflowPlugin.groovy @@ -0,0 +1,29 @@ +package nextflow.gradle.plugins + +import org.gradle.api.Plugin +import org.gradle.api.Project + +class NextflowPlugin implements Plugin{ + + @Override + void apply(Project target) { + + NextflowPluginExtension nextflowPluginExtension = target.extensions.create('nextflowPlugin',NextflowPluginExtension) + + target.subprojects.find{it.name=="plugins"}.subprojects.each{ project -> + project.tasks.register('zipPlugin', ZipPluginTask,{ + + }) + project.tasks.register('unzipPlugin', UnzipPluginTask,{ + + }) + project.tasks.register('jsonPlugin', JsonPluginTask, { + downloadUrl = nextflowPluginExtension.downloadUrl + }) + project.tasks.register('generateIdx', GenerateIdxTask, { + extensionPoints = nextflowPluginExtension.extensionPoints + }) + project.tasks.findByName("processResources")?.dependsOn(project.tasks.findByName("generateIdx")) + } + } +} diff --git a/buildSrc/src/main/groovy/nextflow/gradle/plugins/NextflowPluginExtension.groovy b/buildSrc/src/main/groovy/nextflow/gradle/plugins/NextflowPluginExtension.groovy new file mode 100644 index 0000000..7330cb8 --- /dev/null +++ b/buildSrc/src/main/groovy/nextflow/gradle/plugins/NextflowPluginExtension.groovy @@ -0,0 +1,68 @@ +package nextflow.gradle.plugins + +import org.gradle.api.Project +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property + +class NextflowPluginExtension { + + final Property downloadUrl + + final Property nextflowVersion + + final Property githubOrganization + + final Property pluginClassName + + final ListProperty extensionPoints + + final private Project project + + NextflowPluginExtension(Project project){ + this.project = project + nextflowVersion = project.objects.property(String) + downloadUrl = project.objects.property(String) + githubOrganization = project.objects.property(String) + pluginClassName = project.objects.property(String) + extensionPoints = project.objects.listProperty(String) + } + + void setNextflowVersion(String nextflowVersion){ + this.nextflowVersion.set(nextflowVersion) + } + + String getNextflowVersion(){ + this.nextflowVersion.getOrElse("23.04.0") + } + + void setDownloadUrl(String downloadUrl){ + this.downloadUrl.set(downloadUrl) + } + + String getDownloadUrl() { + return downloadUrl.getOrElse("https://github.com/${getGithubOrganization()}/${project.name}/releases/download") + } + + String getGithubOrganization() { + return githubOrganization.get() + } + + void setGithubOrganization(String githubOrganization){ + this.githubOrganization.set(githubOrganization) + } + + String getPluginClassName() { + SourcesMatcher matcher = new SourcesMatcher(project) + String resolved = matcher.pluginClassName + return pluginClassName.getOrElse(resolved) + } + + void setPluginClassName(String pluginClassName){ + this.pluginClassName.set(pluginClassName) + } + + void setExtensionPoints(List extensions){ + this.extensionPoints.set(extensions) + } + +} diff --git a/buildSrc/src/main/groovy/nextflow/gradle/plugins/SourcesMatcher.groovy b/buildSrc/src/main/groovy/nextflow/gradle/plugins/SourcesMatcher.groovy new file mode 100644 index 0000000..7e9260f --- /dev/null +++ b/buildSrc/src/main/groovy/nextflow/gradle/plugins/SourcesMatcher.groovy @@ -0,0 +1,48 @@ +package nextflow.gradle.plugins + +import org.gradle.api.Project +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.SourceSetContainer + +class SourcesMatcher { + + Project project + SourcesMatcher(Project project){ + this.project = project + } + + + String getPluginClassName(){ + return findSources(/class (\w+) extends BasePlugin/).first() + } + + List getPluginExtensions(){ + return findSources(/class (\w+) extends PluginExtensionPoint/) + + findSources(/class (\w+) extends Executor implements ExtensionPoint/) + } + + List getTraceObservers(){ + return findSources(/class (\w+) implements TraceObserverFactory/) + } + + List findSources( def regexp ){ + def sourceSets = project.extensions.getByType(SourceSetContainer) + def mainSourceSet = sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME) + def sources = mainSourceSet.allSource + def root = project.projectDir + + def matcher = sources.findAll{ + def source = it.text + def matcher = source =~ regexp + if( matcher.size() != 1 ){ + return null + } + it + } + matcher.collect { file -> + def source = file.toString() - "$root.absolutePath/src/main/" + return source.split('\\.').dropRight(1).join().split(File.separator).drop(1).join('.') + } + } + +} diff --git a/buildSrc/src/main/groovy/nextflow/gradle/plugins/UnzipPluginTask.groovy b/buildSrc/src/main/groovy/nextflow/gradle/plugins/UnzipPluginTask.groovy new file mode 100644 index 0000000..f6b915c --- /dev/null +++ b/buildSrc/src/main/groovy/nextflow/gradle/plugins/UnzipPluginTask.groovy @@ -0,0 +1,15 @@ +package nextflow.gradle.plugins +import org.gradle.api.tasks.Copy + + +class UnzipPluginTask extends Copy{ + + UnzipPluginTask(){ + setGroup('nextflow') + dependsOn project.tasks.zipPlugin + outputs.upToDateWhen {false} + from project.zipTree(project.tasks.zipPlugin.outputs.files.first()) + into "${System.getProperty("user.home")}/.nextflow/plugins/${project.name}-${project.version}" + } + +} diff --git a/buildSrc/src/main/groovy/nextflow/gradle/plugins/ZipPluginTask.groovy b/buildSrc/src/main/groovy/nextflow/gradle/plugins/ZipPluginTask.groovy new file mode 100644 index 0000000..b64f72d --- /dev/null +++ b/buildSrc/src/main/groovy/nextflow/gradle/plugins/ZipPluginTask.groovy @@ -0,0 +1,44 @@ +package nextflow.gradle.plugins + + +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.bundling.Jar + + +class ZipPluginTask extends Jar{ + + @OutputFile + final abstract RegularFileProperty outputFile = + project.objects.fileProperty().convention(project.layout.buildDirectory.file( + "plugin/${project.name}-${project.version}.zip")) + + ZipPluginTask() { + setGroup('nextflow') + + dependsOn(project.tasks.findByName('build')) + dependsOn(project.tasks.generateIdx) + + into( 'classes',{ + with project.tasks.findByName('jar') + }) + + into('lib',{ + from(project.configurations.find {it.name=='runtimeClasspath'}) + }) + + manifest { + Jar jar = project.tasks.findByName('jar') as Jar + from(jar.manifest) + } + + archiveExtension.set('zip') + setPreserveFileTimestamps(false) + setReproducibleFileOrder(true) + + def directory = project.objects.fileProperty().convention( + project.layout.buildDirectory.file("plugin")) + getDestinationDirectory().set(directory.get().asFile) + } + +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..8766f31 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +version=0.0.2-rc1 \ No newline at end of file diff --git a/plugins/build.gradle b/plugins/build.gradle index 320f7ad..57c5bcc 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -1,4 +1,5 @@ /* + * Copyright 2024, Evaluacion y Desarrollo de Negocios, Spain * Copyright 2023, Stellenbosch University, South Africa * Copyright 2022, Center for Medical Genetics, Ghent * @@ -17,153 +18,22 @@ 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') ?: 'abhi18av' -ext.github_access_token = project.findProperty('github_access_token') ?: System.getenv('GITHUB_TOKEN') -ext.github_commit_email = project.findProperty('github_commit_email') ?: 'abhi18av@outlook.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) { - group 'nextflow' - - 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() - } - outputs.file("$buildDir/libs/${project.name}-${project.version}.zip") - } - - /* - * Copy the plugin dependencies in the subproject `build/target/libs` directory - */ - task copyPluginLibs(type: Sync) { - group 'nextflow' - - 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')) { - group 'nextflow' - - 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) { - group 'nextflow' - - 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 - } - -} - -task upload(dependsOn: [subprojects.uploadPlugin]) { } - -classes.dependsOn subprojects.copyPluginLibs - -/* - * Merge and publish the plugins index file - */ -task publishIndex( type: io.nextflow.gradle.tasks.GithubRepositoryPublisher ) { - group 'nextflow' - 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 }