add nextflow d30e48d
This commit is contained in:
515
nextflow/build.gradle
Normal file
515
nextflow/build.gradle
Normal file
@@ -0,0 +1,515 @@
|
||||
/*
|
||||
* Copyright 2013-2026, 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 'idea'
|
||||
}
|
||||
|
||||
// Add ability to test with upcoming versions of Groovy
|
||||
def groovyVer = System.getenv('CI_GROOVY_VERSION')
|
||||
if (groovyVer) {
|
||||
def repo = groovyVer.startsWith('com.github.apache:') ? 'https://jitpack.io' : 'https://oss.jfrog.org/oss-snapshot-local/'
|
||||
logger.lifecycle "Overridden Groovy dependency to use $groovyVer - repository: $repo"
|
||||
allprojects {
|
||||
repositories {
|
||||
maven { url repo }
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
|
||||
if (details.requested.group == 'org.apache.groovy') {
|
||||
if( groovyVer.contains(':') )
|
||||
details.useTarget(groovyVer)
|
||||
else
|
||||
details.useVersion(groovyVer)
|
||||
println ">> Overriding $details.requested with version: $groovyVer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def projects(String...args) {
|
||||
args.collect {project(it)}
|
||||
}
|
||||
|
||||
String gitVersion() {
|
||||
def p = new ProcessBuilder() .command('sh','-c','git rev-parse --short HEAD') .start()
|
||||
def r = p.waitFor()
|
||||
return r==0 ? p.text.trim() : '(unknown)'
|
||||
}
|
||||
|
||||
group = 'io.nextflow'
|
||||
version = rootProject.file('VERSION').text.trim()
|
||||
ext.commitId = gitVersion()
|
||||
|
||||
allprojects {
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'java-test-fixtures'
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'groovy'
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'jacoco'
|
||||
|
||||
java {
|
||||
// these settings apply to all jvm tooling, including groovy
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
sourceCompatibility = 17
|
||||
targetCompatibility = 17
|
||||
}
|
||||
|
||||
idea {
|
||||
module.inheritOutputDirs = true
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url 'https://repo.eclipse.org/content/groups/releases' }
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
|
||||
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
|
||||
all*.exclude group: 'org.apache.groovy', module: 'groovy-all'
|
||||
all*.exclude group: 'org.apache.groovy', module: 'groovy-cli-picocli'
|
||||
// groovydoc libs
|
||||
groovyDoc.extendsFrom runtime
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// 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") }
|
||||
}
|
||||
|
||||
// JUnit Platform launcher required for Gradle 9.1+ when using useJUnitPlatform()
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.5'
|
||||
|
||||
// Documentation required libraries
|
||||
groovyDoc 'org.fusesource.jansi:jansi:2.4.0'
|
||||
groovyDoc "org.apache.groovy:groovy-groovydoc:4.0.31"
|
||||
groovyDoc "org.apache.groovy:groovy-ant:4.0.31"
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
// this is required due to this IDEA bug
|
||||
// https://youtrack.jetbrains.com/issue/IDEA-129282
|
||||
sourceSets {
|
||||
main {
|
||||
output.resourcesDir = 'build/classes/main'
|
||||
}
|
||||
}
|
||||
|
||||
// Disable strict javadoc checks
|
||||
// See http://blog.joda.org/2014/02/turning-off-doclint-in-jdk-8-javadoc.html
|
||||
if (JavaVersion.current().isJava8Compatible()) {
|
||||
tasks.withType(Javadoc) {
|
||||
options.addStringOption('Xdoclint:none', '-quiet')
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(Jar) {
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
}
|
||||
|
||||
// patched as described here
|
||||
// http://forums.gradle.org/gradle/topics/gradle_task_groovydoc_failing_with_noclassdeffounderror
|
||||
tasks.withType(Groovydoc) {
|
||||
groovyClasspath = project.configurations.groovyDoc
|
||||
includes = ["nextflow/**"]
|
||||
}
|
||||
|
||||
// Required to run tests on Java 9 and higher in compatibility mode
|
||||
tasks.withType(Test) {
|
||||
jvmArgs ([
|
||||
'--enable-preview',
|
||||
'--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',
|
||||
'--add-opens=java.base/jdk.internal.vm=ALL-UNNAMED',
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Code coverage with JaCoCo.
|
||||
* See: https://www.jacoco.org/; https://docs.gradle.org/current/userguide/jacoco_plugin.html
|
||||
*/
|
||||
// Code coverage report is always generated after tests run
|
||||
test { finalizedBy jacocoTestReport }
|
||||
jacocoTestReport {
|
||||
// Tests are required to run before generating the code coverage report
|
||||
dependsOn test
|
||||
|
||||
// Remove closure classes from the report, as they are already covered by the enclosing class coverage stats adding only noise.
|
||||
// See: https://stackoverflow.com/questions/39453696
|
||||
afterEvaluate {
|
||||
classDirectories.setFrom(files(classDirectories.files.collect { dir ->
|
||||
fileTree(dir: dir, excludes: ['**/*$*_closure*'])
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// disable jar for root project
|
||||
jar.enabled = false
|
||||
|
||||
/*
|
||||
* Update the build timestamp in the source source file
|
||||
*/
|
||||
task buildInfo {
|
||||
// Always run this task - never consider it up-to-date
|
||||
outputs.upToDateWhen { false }
|
||||
|
||||
doLast {
|
||||
|
||||
def file0 = file('modules/nextflow/src/main/resources/META-INF/build-info.properties')
|
||||
def buildNum = 0
|
||||
|
||||
// Use GitHub Actions run number if available, otherwise increment local counter
|
||||
if (System.getenv('GITHUB_RUN_NUMBER')) {
|
||||
buildNum = System.getenv('GITHUB_RUN_NUMBER').toInteger()
|
||||
println "Using GitHub Actions run number: $buildNum"
|
||||
}
|
||||
|
||||
// -- update build-info file
|
||||
file0.text = """\
|
||||
build=${buildNum}
|
||||
version=${version}
|
||||
timestamp=${System.currentTimeMillis()}
|
||||
commitId=${project.property('commitId')}
|
||||
""".stripIndent()
|
||||
}}
|
||||
|
||||
/*
|
||||
* Update release information in nextflow wrapper, dockerfile, and plugin metadata.
|
||||
*
|
||||
* This task:
|
||||
* 1. Updates the NXF_VER version string in the nextflow launch script
|
||||
* 2. Updates the release version in docker/Dockerfile
|
||||
* 3. Generates plugins-info.txt with current plugin versions from VERSION files
|
||||
*
|
||||
* This task always runs to ensure all release artifacts are updated with current versions.
|
||||
*/
|
||||
task releaseInfo {
|
||||
dependsOn buildInfo
|
||||
// Always run this task - never consider it up-to-date
|
||||
outputs.upToDateWhen { false }
|
||||
|
||||
doLast {
|
||||
|
||||
// -- update 'nextflow' wrapper
|
||||
def file0 = file('nextflow')
|
||||
def src = file0.text
|
||||
src = src.replaceAll(/NXF_VER\=\$\{NXF_VER:-'.*'\}/, 'NXF_VER=\\${NXF_VER:-\'' + version + '\'}')
|
||||
file0.text = src
|
||||
|
||||
// -- update dockerfile
|
||||
file0 = file('docker/Dockerfile')
|
||||
src = file0.text
|
||||
src = src.replaceAll(/releases\/v[0-9a-zA-Z_\-\.]+\//, "releases/v$version/" as String)
|
||||
file0.text = src
|
||||
|
||||
// -- create plugins-info file
|
||||
def plugins = []
|
||||
new File(rootProject.rootDir, 'plugins')
|
||||
.eachDir { if(it.name.startsWith('nf-')) plugins << project(":plugins:${it.name}") }
|
||||
def meta = plugins.collect { "$it.name@$it.version" }
|
||||
file('modules/nextflow/src/main/resources/META-INF/plugins-info.txt').text = meta.toSorted().join('\n')
|
||||
}}
|
||||
|
||||
/*
|
||||
* Validate that plugins-info.txt matches plugin VERSION files and that build-info.properties
|
||||
* contains the correct build number and commit ID when running in GitHub Actions.
|
||||
*
|
||||
* This task ensures:
|
||||
* 1. All plugin versions in plugins-info.txt match their corresponding VERSION files
|
||||
* 2. The build number in build-info.properties matches GITHUB_RUN_NUMBER (in CI)
|
||||
* 3. The commit ID in build-info.properties matches GITHUB_SHA (in CI)
|
||||
*
|
||||
* This validation prevents releases with stale or mismatched metadata.
|
||||
*/
|
||||
task validatePluginVersions {
|
||||
dependsOn buildInfo
|
||||
inputs.file('modules/nextflow/src/main/resources/META-INF/plugins-info.txt')
|
||||
inputs.file('modules/nextflow/src/main/resources/META-INF/build-info.properties')
|
||||
inputs.files(fileTree('plugins') { include '*/VERSION' })
|
||||
|
||||
doLast {
|
||||
// Get expected versions from plugin projects
|
||||
def expected = []
|
||||
new File(rootProject.rootDir, 'plugins')
|
||||
.eachDir { if(it.name.startsWith('nf-')) expected << project(":plugins:${it.name}") }
|
||||
def expectedVersions = expected.collect { "$it.name@$it.version" }.toSorted()
|
||||
|
||||
// Get actual versions from plugins-info.txt
|
||||
def actualVersions = file('modules/nextflow/src/main/resources/META-INF/plugins-info.txt').readLines()
|
||||
|
||||
// Compare plugin versions
|
||||
if (expectedVersions != actualVersions) {
|
||||
def diffs = []
|
||||
expectedVersions.eachWithIndex { exp, i ->
|
||||
def act = actualVersions.size() > i ? actualVersions[i] : 'missing'
|
||||
if (exp != act) diffs << " expected: $exp, actual: $act"
|
||||
}
|
||||
throw new GradleException("Plugin version mismatch:\n${diffs.join('\n')}\nRun 'make assemble' to fix.")
|
||||
}
|
||||
|
||||
// Validate build-info.properties - require GitHub Actions environment variables
|
||||
if (!System.getenv('GITHUB_RUN_NUMBER')) {
|
||||
throw new GradleException("GITHUB_RUN_NUMBER environment variable is required")
|
||||
}
|
||||
if (!System.getenv('GITHUB_SHA')) {
|
||||
throw new GradleException("GITHUB_SHA environment variable is required")
|
||||
}
|
||||
|
||||
def buildInfoFile = file('modules/nextflow/src/main/resources/META-INF/build-info.properties')
|
||||
def props = new Properties()
|
||||
buildInfoFile.withInputStream { props.load(it) }
|
||||
|
||||
def actualBuild = props.getProperty('build')
|
||||
def expectedBuild = System.getenv('GITHUB_RUN_NUMBER')
|
||||
if (actualBuild != expectedBuild) {
|
||||
throw new GradleException("Build number mismatch: build-info.properties has '${actualBuild}' but GITHUB_RUN_NUMBER is '${expectedBuild}'. Run 'make assemble' to fix.")
|
||||
}
|
||||
|
||||
def actualCommit = props.getProperty('commitId')
|
||||
def expectedCommit = System.getenv('GITHUB_SHA').take(9) // GitHub SHA is full hash, we use short form
|
||||
if (actualCommit != expectedCommit) {
|
||||
throw new GradleException("Commit ID mismatch: build-info.properties has '${actualCommit}' but GITHUB_SHA is '${expectedCommit}'. Run 'make assemble' to fix.")
|
||||
}
|
||||
|
||||
println "✅ Build info validation passed: build=${actualBuild}, commitId=${actualCommit}"
|
||||
println "✅ Plugin version validation passed: all ${expected.size()} plugin versions match"
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile sources and copies all libs to target directory
|
||||
*/
|
||||
task compile {
|
||||
dependsOn allprojects.classes
|
||||
}
|
||||
|
||||
def getRuntimeConfigs() {
|
||||
def names = subprojects
|
||||
.findAll { prj -> prj.name in ['nextflow','nf-commons','nf-httpfs','nf-lang','nf-lineage'] }
|
||||
.collect { it.name }
|
||||
|
||||
FileCollection result = null
|
||||
for( def it : names ) {
|
||||
def cfg = project(it).configurations.getByName('runtimeClasspath')
|
||||
if( result==null )
|
||||
result = cfg
|
||||
else
|
||||
result += cfg
|
||||
// this include the module actual jar file
|
||||
// note: migrating to gradle 7 does not work any more
|
||||
//result = result + cfg.getOutgoing().getArtifacts().getFiles()
|
||||
}
|
||||
return result?.files ?: []
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the runtime classpath
|
||||
* NOTE: This task uses a provider to delay execution, but still triggers configuration
|
||||
* resolution when the provider is evaluated. While not ideal for Gradle 9.1's strict
|
||||
* configuration resolution requirements, this approach works in practice for our use case.
|
||||
*/
|
||||
task exportClasspath {
|
||||
dependsOn allprojects.jar
|
||||
|
||||
// Use provider to delay configuration resolution until task execution
|
||||
def configurationFiles = provider {
|
||||
def libs = []
|
||||
|
||||
// Resolve configurations during provider evaluation (not ideal but functional)
|
||||
['nextflow','nf-commons','nf-httpfs','nf-lang','nf-lineage'].each { moduleName ->
|
||||
def moduleProject = project(":$moduleName")
|
||||
def cfg = moduleProject.configurations.getByName('runtimeClasspath')
|
||||
libs.addAll(cfg.files.collect { it.canonicalPath })
|
||||
}
|
||||
|
||||
// Add module jars
|
||||
['nextflow','nf-commons','nf-httpfs','nf-lang','nf-lineage'].each {
|
||||
libs << file("modules/$it/build/libs/${it}-${version}.jar").canonicalPath
|
||||
}
|
||||
|
||||
return libs.unique()
|
||||
}
|
||||
|
||||
inputs.files(configurationFiles)
|
||||
outputs.file('.launch.classpath')
|
||||
|
||||
doLast {
|
||||
def libs = configurationFiles.get()
|
||||
file('.launch.classpath').text = libs.join(':')
|
||||
}
|
||||
}
|
||||
|
||||
ext.nexusUsername = project.findProperty('nexusUsername') ?: System.getenv('AWS_ACCESS_KEY_ID')
|
||||
ext.nexusPassword = project.findProperty('nexusPassword') ?: System.getenv('AWS_SECRET_ACCESS_KEY')
|
||||
ext.nexusFullName = project.findProperty('nexusFullName')
|
||||
ext.nexusEmail = project.findProperty('nexusEmail')
|
||||
|
||||
// `signing.keyId` property needs to be defined in the `gradle.properties` file
|
||||
ext.enableSignArchives = project.findProperty('signing.keyId')
|
||||
|
||||
ext.coreProjects = projects( ':nextflow', ':nf-commons', ':nf-httpfs', ':nf-lang', ':nf-lineage' )
|
||||
|
||||
configure(coreProjects) {
|
||||
group = 'io.nextflow'
|
||||
version = rootProject.file('VERSION').text.trim()
|
||||
}
|
||||
|
||||
/*
|
||||
* Maven central deployment
|
||||
* http://central.sonatype.org/pages/gradle.html
|
||||
*/
|
||||
configure(coreProjects) {
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'signing'
|
||||
|
||||
task javadocJar(type: Jar) {
|
||||
archiveClassifier = 'javadoc'
|
||||
from configurations.groovyDoc
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
archiveClassifier = 'sources'
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
suppressPomMetadataWarningsFor('testFixturesApiElements')
|
||||
suppressPomMetadataWarningsFor('testFixturesRuntimeElements')
|
||||
from components.java
|
||||
versionMapping {
|
||||
usage('java-api') {
|
||||
fromResolutionOf('runtimeClasspath')
|
||||
}
|
||||
usage('java-runtime') {
|
||||
fromResolutionResult()
|
||||
}
|
||||
}
|
||||
pom {
|
||||
name = 'Nextflow'
|
||||
description = 'A DSL modelled around the UNIX pipe concept, that simplifies writing parallel and scalable pipelines in a portable manner'
|
||||
url = 'http://www.nextflow.io'
|
||||
licenses {
|
||||
license {
|
||||
name = 'The Apache License, Version 2.0'
|
||||
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id = nexusUsername
|
||||
name = nexusFullName
|
||||
email = nexusEmail
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection = 'scm:git:https://github.com/nextflow-io/nextflow'
|
||||
developerConnection = 'scm:git:git@github.com:nextflow-io/nextflow.git'
|
||||
url = 'https://github.com/nextflow-io/nextflow'
|
||||
}
|
||||
}
|
||||
|
||||
artifact sourcesJar
|
||||
artifact javadocJar
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = 'Seqera'
|
||||
// change URLs to point to your repos, e.g. http://my.org/repo
|
||||
def releasesRepoUrl = "s3://maven.seqera.io/releases/"
|
||||
def snapshotsRepoUrl = "s3://maven.seqera.io/snapshots/"
|
||||
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
|
||||
credentials(AwsCredentials) {
|
||||
accessKey nexusUsername
|
||||
secretKey nexusPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
required { enableSignArchives }
|
||||
sign publishing.publications.mavenJava
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
String bytesToHex(byte[] bytes) {
|
||||
StringBuffer result = new StringBuffer();
|
||||
for (byte byt : bytes) result.append(Integer.toString((byt & 0xff) + 0x100, 16).substring(1));
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
task makeDigest { doLast {
|
||||
byte[] digest
|
||||
String str = file('nextflow').text
|
||||
// create sha1
|
||||
digest = java.security.MessageDigest.getInstance("SHA1").digest(str.getBytes())
|
||||
file('nextflow.sha1').text = new BigInteger(1, digest).toString(16) + '\n'
|
||||
// create sha-256
|
||||
digest = java.security.MessageDigest.getInstance("SHA-256").digest(str.getBytes())
|
||||
file('nextflow.sha256').text = bytesToHex(digest) + '\n'
|
||||
// create md5
|
||||
digest = java.security.MessageDigest.getInstance("MD5").digest(str.getBytes())
|
||||
file('nextflow.md5').text = bytesToHex(digest) + '\n'
|
||||
}}
|
||||
|
||||
// Make releaseInfo task automatically run makeDigest after updating versions
|
||||
releaseInfo.finalizedBy makeDigest
|
||||
|
||||
|
||||
task upload {
|
||||
dependsOn compile
|
||||
dependsOn coreProjects.publish
|
||||
dependsOn validatePluginVersions
|
||||
}
|
||||
|
||||
|
||||
if( System.env.BUILD_PACK ) {
|
||||
apply from: 'packing.gradle'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user