1
0
mirror of synced 2025-12-19 18:14:56 -05:00

gradle: overall simplification (#35307)

This commit is contained in:
Marius Posta
2024-02-16 04:46:32 -08:00
committed by GitHub
parent dc088bc3a9
commit f93c0df331
13 changed files with 284 additions and 681 deletions

View File

@@ -1,4 +1,4 @@
name: Airbyte CI - Repository Health Check
name: Connector Ops CI - Gradle Check
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -20,47 +20,18 @@ on:
- synchronize
jobs:
# In case of self-hosted EC2 errors, remove this block.
start-check-runner:
name: Start EC2 Runner
timeout-minutes: 10
runs-on: ubuntu-latest
outputs:
label: ${{ steps.start-ec2-runner.outputs.label }}
ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }}
steps:
- name: Checkout Airbyte
uses: actions/checkout@v3
- name: Check PAT rate limits
run: |
./tools/bin/find_non_rate_limited_PAT \
${{ secrets.GH_PAT_BUILD_RUNNER_OSS }} \
${{ secrets.GH_PAT_BUILD_RUNNER_BACKUP }}
- name: Start AWS Runner
id: start-ec2-runner
uses: ./.github/actions/start-aws-runner
with:
aws-access-key-id: ${{ secrets.SELF_RUNNER_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.SELF_RUNNER_AWS_SECRET_ACCESS_KEY }}
# Use a beefier instance type than the default c5.2xlarge, but with the same per-core cost.
# When gradle runs on this instance, it will use up all the available cores anyway.
# There should be little to no difference in total cost, however the job latency will be improved.
# At the time of this writing, the latency doesn't improve much beyond this instance size (approx 5 minutes).
# This is largely thanks to the gradle cache.
ec2-instance-type: "c5.4xlarge"
github-token: ${{ env.PAT }}
run-check:
# In case of self-hosted EC2 errors, removed the `needs` line and switch back to running on ubuntu-latest.
needs: start-check-runner # required to start the main job when the runner is ready
runs-on: ${{ needs.start-check-runner.outputs.label }} # run the job on the newly created runner
# The gradle check task which we will run is embarrassingly parallelizable.
# We therefore run this on a machine with a maximum number of cores.
# We pay per time and per core, so there should be little difference in total cost.
# The latency overhead of setting up gradle prior to running the actual task adds up to about a minute.
runs-on: connector-test-xxlarge
name: Gradle Check
timeout-minutes: 30
steps:
- name: Checkout Airbyte
uses: actions/checkout@v3
# IMPORTANT! This is nessesary to make sure that a status is reported on the PR
# IMPORTANT! This is necessary to make sure that a status is reported on the PR
# even if the workflow is skipped. If we used github actions filters, the workflow
# would not be reported as skipped, but instead would be forever pending.
#
@@ -106,39 +77,6 @@ jobs:
# TODO: be able to remove the skipSlowTests property
arguments: --scan --no-daemon --no-watch-fs check -DskipSlowTests=true
# In case of self-hosted EC2 errors, remove this block.
stop-check-runner:
name: Stop EC2 Runner
timeout-minutes: 10
needs:
- start-check-runner # required to get output from the start-runner job
- run-check # required to wait when the main job is done
runs-on: ubuntu-latest
# Always is required to stop the runner even if the previous job has errors. However always() runs even if the previous step is skipped.
# Thus, we check for skipped here.
if: ${{ always() && needs.start-check-runner.result != 'skipped'}}
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.SELF_RUNNER_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.SELF_RUNNER_AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-2
- name: Checkout Airbyte
uses: actions/checkout@v3
- name: Check PAT rate limits
run: |
./tools/bin/find_non_rate_limited_PAT \
${{ secrets.GH_PAT_BUILD_RUNNER_OSS }} \
${{ secrets.GH_PAT_BUILD_RUNNER_BACKUP }}
- name: Stop EC2 runner
uses: supertopher/ec2-github-runner@base64v1.0.10
with:
mode: stop
github-token: ${{ env.PAT }}
label: ${{ needs.start-check-runner.outputs.label }}
ec2-instance-id: ${{ needs.start-check-runner.outputs.ec2-instance-id }}
set-instatus-incident-on-failure:
name: Create Instatus Incident on Failure
runs-on: ubuntu-latest

View File

@@ -113,9 +113,6 @@ jobs:
fi
env:
GCP_GSM_CREDENTIALS: ${{ secrets.GCP_GSM_CREDENTIALS }}
- name: Build Java CDK Snapshot if Needed
# If a snapshot version is specified for the Java CDK, build publish locally. Otherwise, do nothing.
run: ./gradlew :airbyte-cdk:java:airbyte-cdk:publishSnapshotIfNeeded
- name: Test ${{ github.event.inputs.connector }}
id: test
env:

View File

@@ -122,9 +122,6 @@ jobs:
run: ./gradlew :airbyte-cdk:java:airbyte-cdk:assertCdkVersionNotPublished
- name: Build Java CDK
run: ./gradlew --no-daemon :airbyte-cdk:java:airbyte-cdk:build
- name: Publish Java Modules to MavenLocal (Dry-Run)
if: ${{ !(env.DRY_RUN == 'false') }}
run: ./gradlew --no-daemon :airbyte-cdk:java:airbyte-cdk:publishToMavenLocal
- name: Upload jars as artifacts
if: ${{ !(env.DRY_RUN == 'false') }}
uses: actions/upload-artifact@v2

View File

@@ -1,17 +1,11 @@
allprojects {
apply plugin: 'java-library'
apply plugin: 'maven-publish'
apply plugin: 'airbyte-java-cdk'
apply plugin: 'airbyte-integration-test-java'
apply plugin: 'airbyte-performance-test-java'
apply plugin: 'java-test-fixtures'
group 'io.airbyte.cdk'
version = getCdkTargetVersion()
}
subprojects { subproject ->
def artifactBaseName = 'airbyte-cdk-' + subproject.name
def artifactBaseName = 'airbyte-cdk-' + project.name
// E.g. airbyte-cdk-core, airbyte-cdk-db-sources, airbyte-cdk-db-destinations, etc.
publishing {
@@ -25,7 +19,7 @@ subprojects { subproject ->
groupId = 'io.airbyte.cdk'
artifactId = artifactBaseName + '-test-fixtures'
version = project.version
artifact subproject.tasks.testFixturesJar
artifact project.tasks.testFixturesJar
}
}
// This repository is only defined and used in the context of an artifact publishing
@@ -52,69 +46,30 @@ subprojects { subproject ->
description = "Airbyte Connector Development Kit (CDK) for Java."
def recursiveTasks = [
'assemble',
'build',
'integrationTestJava',
'publish',
'publishToMavenLocal',
'test',
]
recursiveTasks.each { taskName ->
tasks.named(taskName).configure {
dependsOn subprojects.collect { it.tasks.named(taskName) }
}
}
// The `publishSnapshotIfNeeded` task will be a no-op if CDK version does not end with '-SNAPSHOT'.
task publishSnapshotIfNeeded {}
if (version.endsWith("-SNAPSHOT")) {
logger.lifecycle("Version ${version} ends with '-SNAPSHOT'. Enqueing 'publishToMavenLocal'...")
publishSnapshotIfNeeded.dependsOn publishToMavenLocal
} else {
// Uncomment as needed for debugging:
// logger.lifecycle("Version ${version} does not end with '-SNAPSHOT'. Skipping task 'publishToMavenLocal'.")
}
task assertCdkVersionNotPublished {
tasks.register('assertCdkVersionNotPublished') {
doLast {
def checkGroupId = "io.airbyte.cdk"
def checkArtifactId = "airbyte-cdk-core"
def checkVersion = getCdkTargetVersion()
def repoUrl = "https://airbyte.mycloudrepo.io/public/repositories/airbyte-public-jars"
def groupIdUrl = checkGroupId.replace('.', '/')
def artifactUrl = "${repoUrl}/${groupIdUrl}/${checkArtifactId}/${checkVersion}/${checkArtifactId}-${checkVersion}.pom"
var props = new Properties()
file("core/src/main/resources/version.properties").withInputStream(props::load)
def connection = artifactUrl.toURL().openConnection() as HttpURLConnection
var checkGroupId = "io.airbyte.cdk"
var checkArtifactId = "airbyte-cdk-core"
var checkVersion = props.getProperty('version') ?: 'undefined'
var repoUrl = "https://airbyte.mycloudrepo.io/public/repositories/airbyte-public-jars"
var groupIdUrl = "${repoUrl}/${checkGroupId.replace('.', '/')}"
var artifactUrl = "${groupIdUrl}/${checkArtifactId}/${checkVersion}/${checkArtifactId}-${checkVersion}.pom"
var connection = artifactUrl.toURL().openConnection() as HttpURLConnection
connection.setRequestMethod("HEAD")
connection.connect()
def responseCode = connection.getResponseCode()
var responseCode = connection.getResponseCode()
if (responseCode == 200) {
throw new GradleException("Assert failed. Java CDK '${checkVersion}' already published at: ${artifactUrl}")
throw new GradleException("Java CDK '${checkVersion}' already published at ${groupIdUrl}")
} else if (responseCode == 404) {
logger.lifecycle(
"Assert succeeded. Version ${checkVersion} of ${checkArtifactId} has not been published. " +
"Checked: ${artifactUrl}"
)
logger.lifecycle("Java CDK '${checkVersion}' not yet published at ${groupIdUrl}.")
} else {
logger.error("Received unexpected HTTP response code ${responseCode}. Ensure the repository is accessible.")
throw new GradleException("Error during assertion. Received unexpected HTTP response code ${responseCode}.")
throw new GradleException("Unexpected HTTP response code ${responseCode} from ${artifactUrl}, expected either 200 or 404.")
}
}
}
def cleanLocalCache = tasks.register('cleanLocalCache') {
def userHome = System.getProperty("user.home")
doLast {
delete '.gradle'
delete '${userHome}/.m2/repository/io/airbyte/'
delete '${userHome}/.gradle/caches/modules-2/files-2.1/io.airbyte.cdk/'
}
}
cleanLocalCache.configure {
dependsOn tasks.named('clean')
dependsOn subprojects.collect { it.tasks.named('clean') }
}

View File

@@ -1,13 +0,0 @@
rootProject.name = 'airbyte'
include ':airbyte-cdk:java:airbyte-cdk:dependencies'
include ':airbyte-cdk:java:airbyte-cdk:core'
include ':airbyte-cdk:java:airbyte-cdk:db-sources'
include ':airbyte-cdk:java:airbyte-cdk:db-destinations'
include ':airbyte-cdk:java:airbyte-cdk:s3-destinations'
include ':airbyte-cdk:java:airbyte-cdk:typing-deduping'
include ':airbyte-cdk:java:airbyte-cdk:azure-destinations'
include ':airbyte-cdk:java:airbyte-cdk:gcs-destinations'
include ':airbyte-cdk:java:airbyte-cdk:datastore-bigquery'
include ':airbyte-cdk:java:airbyte-cdk:datastore-mongo'
include ':airbyte-cdk:java:airbyte-cdk:datastore-postgres'

View File

@@ -0,0 +1,104 @@
plugins {
id 'base'
id 'ru.vyarus.use-python' version '2.3.0'
}
// Pyenv support.
try {
def pyenvRoot = "pyenv root".execute()
if (pyenvRoot.waitFor() == 0) {
ext.pyenvRoot = pyenvRoot.text.trim()
}
} catch (IOException _) {
// Swallow exception if pyenv is not installed.
}
def pythonBin = layout.buildDirectory.file('.venv/bin/python').get().asFile.absolutePath
// python is required by the connectors project to run airbyte-ci from source to build connector images.
python {
envPath = layout.buildDirectory.file('.venv').get().asFile
minPythonVersion = '3.10' // should be 3.10 for local development
// Pyenv support.
try {
def pyenvRoot = "pyenv root".execute()
def pyenvLatest = "pyenv latest ${minPythonVersion}".execute()
// Pyenv definitely exists at this point: use 'python' instead of 'python3' in all cases.
pythonBinary "python"
if (pyenvRoot.waitFor() == 0 && pyenvLatest.waitFor() == 0) {
pythonPath "${pyenvRoot.text.trim()}/versions/${pyenvLatest.text.trim()}/bin"
}
} catch (IOException _) {
// Swallow exception if pyenv is not installed.
}
scope = 'VIRTUALENV'
installVirtualenv = true
// poetry is required for installing and running airbyte-ci
pip 'poetry:1.5.1'
}
def poetryInstallAirbyteCI = tasks.register('poetryInstallAirbyteCI', Exec) {
workingDir rootProject.file('airbyte-ci/connectors/pipelines')
commandLine pythonBin
args "-m", "poetry", "install", "--no-cache"
}
poetryInstallAirbyteCI.configure {
dependsOn tasks.named('pipInstall')
}
def poetryCleanVirtualenv = tasks.register('cleanPoetryVirtualenv', Exec) {
workingDir rootProject.file('airbyte-ci/connectors/pipelines')
commandLine pythonBin
args "-m", "poetry", "env", "remove", "--all"
onlyIf {
layout.buildDirectory.file('venv/bin/python').get().asFile.exists()
}
}
tasks.named('clean').configure {
dependsOn poetryCleanVirtualenv
}
allprojects {
// Evaluate CDK project before evaluating the connector.
evaluationDependsOn(':airbyte-cdk:java:airbyte-cdk')
// Adds airbyte-ci task.
def airbyteCIConnectorsTask = { String taskName, String... connectorsArgs ->
def task = tasks.register(taskName, Exec) {
workingDir rootDir
environment "CI", "1" // set to use more suitable logging format
commandLine pythonBin
args "-m", "poetry"
args "--directory", "${rootProject.file('airbyte-ci/connectors/pipelines').absolutePath}"
args "run"
args "airbyte-ci", "connectors", "--name=${project.name}"
args connectorsArgs
// Forbid these kinds of tasks from running concurrently.
// We can induce serial execution by giving them all a common output directory.
outputs.dir rootProject.file("${rootProject.buildDir}/airbyte-ci-lock")
outputs.upToDateWhen { false }
}
task.configure { dependsOn poetryInstallAirbyteCI }
return task
}
// Build connector image as part of 'assemble' task.
// This is required for local 'integrationTest' execution.
def buildConnectorImage = airbyteCIConnectorsTask(
'buildConnectorImage', '--disable-report-auto-open', 'build', '--use-host-gradle-dist-tar')
buildConnectorImage.configure {
// Images for java projects always rely on the distribution tarball.
dependsOn tasks.matching { it.name == 'distTar' }
// Ensure that all files exist beforehand.
dependsOn tasks.matching { it.name == 'generate' }
}
tasks.named('assemble').configure {
// We may revisit the dependency on assemble but the dependency should always be on a base task.
dependsOn buildConnectorImage
}
// Convenience tasks for local airbyte-ci execution.
airbyteCIConnectorsTask('airbyteCIConnectorBuild', 'build')
airbyteCIConnectorsTask('airbyteCIConnectorTest', 'test')
}

View File

@@ -5,183 +5,41 @@ import com.github.spotbugs.snom.SpotBugsTask
plugins {
id 'base'
id 'com.github.spotbugs' version '6.0.7'
id 'version-catalog'
id 'ru.vyarus.use-python'
}
Properties env = new Properties()
rootProject.file('gradle.properties').withInputStream { env.load(it) }
if (!env.containsKey('VERSION')) {
throw new Exception('Version not specified in .env file...')
}
// `version` is used as the application build version for artifacts like jars
// `image_tag` is used as the docker tag applied to built images.
// These values are the same for building an specific Airbyte release or branch via the 'VERSION' environment variable.
// For local development builds, the 'VERSION' environment variable is unset, and built images are tagged with 'dev'.
ext {
version = System.getenv("VERSION") ?: env.VERSION
image_tag = System.getenv("VERSION") ?: 'dev'
skipSlowTests = (System.getProperty('skipSlowTests', 'false') != 'false')
}
// Pyenv support.
try {
def pyenvRoot = "pyenv root".execute()
if (pyenvRoot.waitFor() == 0) {
ext.pyenvRoot = pyenvRoot.text.trim()
}
} catch (IOException _) {
// Swallow exception if pyenv is not installed.
}
def isConnectorProject = { Project project ->
if (project.parent == null || project.parent.name != 'connectors') {
return false
}
return project.name.startsWith("source-") || project.name.startsWith("destination-")
}
allprojects {
apply plugin: 'base'
apply plugin: 'java'
apply plugin: 'java-test-fixtures'
apply plugin: 'com.github.spotbugs'
// by default gradle uses directory as the project name. That works very well in a single project environment but
// By default gradle uses directory as the project name. That works very well in a single project environment but
// projects clobber each other in an environments with subprojects when projects are in directories named identically.
def sub = rootDir.relativePath(projectDir.parentFile).replace('/', '.')
group = "io.${rootProject.name}${sub.isEmpty() ? '' : ".$sub"}"
project.base.archivesName = "${project.group}-${project.name}"
version = rootProject.ext.version
}
// python is required by the root project to run CAT tests for connectors
python {
envPath = '.venv'
minPythonVersion = '3.10' // should be 3.10 for local development
// Amazon Linux support.
// The airbyte-ci tool runs gradle tasks in AL2023-based containers.
// In AL2023, `python3` is necessarily v3.9, and later pythons need to be installed and named explicitly.
// See https://github.com/amazonlinux/amazon-linux-2023/issues/459 for details.
try {
if ("python3.11 --version".execute().waitFor() == 0) {
// python3.11 definitely exists at this point, use it instead of 'python3'.
pythonBinary "python3.11"
}
} catch (IOException _) {
// Swallow exception if python3.11 is not installed.
}
// Pyenv support.
try {
def pyenvRoot = "pyenv root".execute()
def pyenvLatest = "pyenv latest ${minPythonVersion}".execute()
// Pyenv definitely exists at this point: use 'python' instead of 'python3' in all cases.
pythonBinary "python"
if (pyenvRoot.waitFor() == 0 && pyenvLatest.waitFor() == 0) {
pythonPath "${pyenvRoot.text.trim()}/versions/${pyenvLatest.text.trim()}/bin"
}
} catch (IOException _) {
// Swallow exception if pyenv is not installed.
// Produce reproducible archives
// (see https://docs.gradle.org/current/userguide/working_with_files.html#sec:reproducible_archives)
tasks.withType(AbstractArchiveTask).configureEach {
preserveFileTimestamps = false
reproducibleFileOrder = true
}
scope = 'VIRTUALENV'
installVirtualenv = true
// poetry is required for installing and running airbyte-ci
pip 'poetry:1.5.1'
}
def cleanPythonVenv = rootProject.tasks.register('cleanPythonVenv', Exec) {
commandLine 'rm'
args '-rf', "${rootProject.projectDir.absolutePath}/.venv"
}
rootProject.tasks.named('clean').configure {
dependsOn cleanPythonVenv
}
def getCDKTargetVersion() {
def props = new Properties()
file("airbyte-cdk/java/airbyte-cdk/src/main/resources/version.properties").withInputStream { props.load(it) }
return props.getProperty('version')
}
static def getLatestFileModifiedTimeFromFiles(files) {
if (files.isEmpty()) {
return null
}
return files.findAll { it.isFile() }
.collect { it.lastModified() }
.max()
}
def checkCDKJarExists(requiredSnapshotVersion) {
if (requiredSnapshotVersion == null) {
// Connector does not require CDK snapshot.
return
}
final boolean checkFileChanges = true
final cdkTargetVersion = getCDKTargetVersion()
if (requiredSnapshotVersion != cdkTargetVersion) {
if (!cdkTargetVersion.contains("-SNAPSHOT")) {
throw new GradleException(
"CDK JAR version is not publishing snapshot but connector requires version ${requiredSnapshotVersion}.\n" +
"Please check that the version in the CDK properties file matches the connector build.gradle."
)
}
throw new GradleException(
"CDK JAR version ${cdkTargetVersion} does not match connector's required version ${requiredSnapshotVersion}.\n" +
"Please check that the version in the CDK properties file matches the connector build.gradle."
)
}
def cdkJar = file("${System.properties['user.home']}/.m2/repository/io/airbyte/airbyte-cdk/${cdkTargetVersion}/airbyte-cdk-${cdkTargetVersion}.jar")
if (!cdkJar.exists()) {
println("WARNING: CDK JAR does not exist at ${cdkJar.path}.\nPlease run './gradlew :airbyte-cdk:java:airbyte-cdk:build'.")
}
if (checkFileChanges) {
def latestJavaFileTimestamp = getLatestFileModifiedTimeFromFiles(file("${rootDir}/airbyte-cdk/java/airbyte-cdk/src").listFiles().findAll { it.isFile() })
if (cdkJar.lastModified() < latestJavaFileTimestamp) {
throw new GradleException("CDK JAR is out of date. Please run './gradlew :airbyte-cdk:java:airbyte-cdk:build'.")
}
}
}
static def getCDKSnapshotRequirement(dependenciesList) {
def cdkSnapshotRequirement = dependenciesList.find {
it.requested instanceof ModuleComponentSelector &&
it.requested.group == 'io.airbyte' &&
it.requested.module == 'airbyte-cdk' &&
it.requested.version.endsWith('-SNAPSHOT')
}
if (cdkSnapshotRequirement == null) {
return null
} else {
return cdkSnapshotRequirement.requested.version
}
}
// Common configurations for 'assemble'.
allprojects {
// Common configurations for 'assemble'.
tasks.withType(Tar).configureEach {
duplicatesStrategy DuplicatesStrategy.INCLUDE
}
tasks.withType(Zip).configureEach {
duplicatesStrategy DuplicatesStrategy.INCLUDE
// Disabling distZip causes the build to break for some reason, so: instead of disabling it, make it fast.
entryCompression ZipEntryCompression.STORED
}
}
// Java projects common configurations.
subprojects { subproj ->
// Convenience task to list all dependencies per project
tasks.register('listAllDependencies', DependencyReportTask) {}
if (!subproj.file('src/main/java').directory) {
return
}
apply plugin: 'java'
apply plugin: 'java-test-fixtures'
apply plugin: 'com.github.spotbugs'
// Common java configurations
java {
sourceCompatibility = JavaVersion.VERSION_21
@@ -200,26 +58,9 @@ subprojects { subproj ->
}
}
if (isConnectorProject(subproj)) {
// This is a Java connector project.
// Evaluate CDK project before evaluating the connector.
evaluationDependsOn(':airbyte-cdk:java:airbyte-cdk')
if (!gradle.startParameter.taskNames.any { it.contains(':airbyte-cdk:') } &&
gradle.startParameter.taskNames.any { it.contains(':source-') || it.contains(':destination-') }) {
// We are building a connector. Warn if the CDK JAR is missing or out of date.
final String cdkRelativePath = 'airbyte-cdk/java/airbyte-cdk'
afterEvaluate {
def cdkVersionNeeded = getCDKSnapshotRequirement(configurations.compileClasspath.incoming.resolutionResult.allDependencies)
checkCDKJarExists(cdkVersionNeeded)
}
}
}
spotbugs {
ignoreFailures = false
effort = Effort.valueOf(rootProject.ext.skipSlowTests ? 'MIN' : 'MAX')
effort = Effort.valueOf(System.getProperty('skipSlowTests', 'false') == 'false' ? 'MAX' : 'MIN')
excludeFilter.set rootProject.file('spotbugs-exclude-filter-file.xml')
reportLevel = Confidence.valueOf('HIGH')
showProgress = false
@@ -264,7 +105,7 @@ subprojects { subproj ->
// Order test classes by annotation.
systemProperty 'junit.jupiter.testclass.order.default', 'org.junit.jupiter.api.ClassOrderer$OrderAnnotation'
if (!subproj.hasProperty('testExecutionConcurrency')) {
if (!project.hasProperty('testExecutionConcurrency')) {
// By default, let gradle spawn as many independent workers as it wants.
maxParallelForks = Runtime.runtime.availableProcessors()
maxHeapSize = '3G'
@@ -274,7 +115,7 @@ subprojects { subproj ->
maxParallelForks = 1
maxHeapSize = '8G'
// Manage test execution concurrency in JUnit.
String concurrency = subproj.property('testExecutionConcurrency').toString()
String concurrency = project.property('testExecutionConcurrency').toString()
if (concurrency.isInteger() && (concurrency as int) > 0) {
// Define a fixed number of threads when the property is set to a positive integer.
systemProperty 'junit.jupiter.execution.parallel.config.fixed.parallelism', concurrency
@@ -283,34 +124,41 @@ subprojects { subproj ->
systemProperty 'junit.jupiter.execution.parallel.config.strategy', 'dynamic'
}
}
// Exclude all connector unit tests upon request.
if (rootProject.ext.skipSlowTests) {
exclude '**/io/airbyte/integrations/source/**'
exclude '**/io/airbyte/integrations/destination/**'
}
}
dependencies {
// Lombok dependencies
compileOnly libs.lombok
annotationProcessor libs.lombok
testCompileOnly libs.lombok
testAnnotationProcessor libs.lombok
testFixturesCompileOnly libs.lombok
testFixturesAnnotationProcessor libs.lombok
// Lombok dependencies.
def lombok = "org.projectlombok:lombok:1.18.30"
compileOnly lombok
annotationProcessor lombok
testCompileOnly lombok
testAnnotationProcessor lombok
testFixturesCompileOnly lombok
testFixturesAnnotationProcessor lombok
// JUnit dependencies
testRuntimeOnly libs.junit.jupiter.engine
testImplementation libs.bundles.junit
testImplementation libs.assertj.core
testImplementation libs.junit.pioneer
testFixturesImplementation libs.bundles.junit
testFixturesImplementation libs.assertj.core
testFixturesImplementation libs.junit.pioneer
// JUnit dependencies.
def vAssertJ = "3.21.0"
def vJUnit = "5.9.1"
def vJUnitJupiter = "5.10.0"
def vJUnitPioneer = "1.7.1"
// adds owasp plugin
implementation 'com.github.spotbugs:spotbugs-annotations:4.8.3'
testFixturesImplementation "org.junit.jupiter:junit-jupiter-api:${vJUnit}"
testFixturesImplementation "org.junit.jupiter:junit-jupiter-params:${vJUnit}"
testFixturesImplementation "org.mockito:mockito-junit-jupiter:${vJUnitJupiter}"
testFixturesImplementation "org.assertj:assertj-core:${vAssertJ}"
testFixturesImplementation "org.junit-pioneer:junit-pioneer:${vJUnitPioneer}"
testImplementation "org.junit.jupiter:junit-jupiter-api:${vJUnit}"
testImplementation "org.junit.jupiter:junit-jupiter-params:${vJUnit}"
testImplementation "org.mockito:mockito-junit-jupiter:${vJUnitJupiter}"
testImplementation "org.assertj:assertj-core:${vAssertJ}"
testImplementation "org.junit-pioneer:junit-pioneer:${vJUnitPioneer}"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${vJUnit}"
// Spotbugs dependencies.
def vSpotbugs = "4.8.3"
implementation "com.github.spotbugs:spotbugs-annotations:${vSpotbugs}"
}
tasks.withType(SpotBugsTask).configureEach {
@@ -323,107 +171,3 @@ subprojects { subproj ->
javadoc.options.addStringOption('Xdoclint:none', '-quiet')
}
// integration and performance test tasks per project
allprojects {
tasks.register('integrationTest') {
dependsOn tasks.matching {
[
'integrationTestJava',
'integrationTestPython',
].contains(it.name)
}
}
tasks.register('performanceTest') {
dependsOn tasks.matching {
[
'performanceTestJava',
].contains(it.name)
}
}
}
// convenience task to list all dependencies per project
subprojects {
tasks.register('listAllDependencies', DependencyReportTask) {}
}
// airbyte-ci tasks for local development
def poetryInstallAirbyteCI = tasks.register('poetryInstallAirbyteCI', Exec) {
workingDir rootProject.file('airbyte-ci/connectors/pipelines')
commandLine rootProject.file('.venv/bin/python').absolutePath
args "-m", "poetry", "install", "--no-cache"
}
poetryInstallAirbyteCI.configure {
dependsOn tasks.named('pipInstall')
}
def poetryCleanVirtualenv = tasks.register('cleanVirtualenv', Exec) {
workingDir rootProject.file('airbyte-ci/connectors/pipelines')
commandLine rootProject.file('.venv/bin/python').absolutePath
args "-m", "poetry", "env", "remove", "--all"
onlyIf {
rootProject.file('.venv/bin/python').exists()
}
}
cleanPythonVenv.configure {
dependsOn poetryCleanVirtualenv
}
subprojects {
if (!isConnectorProject(project)) {
return
}
def airbyteCIConnectorsTask = { String taskName, String... connectorsArgs ->
def task = tasks.register(taskName, Exec) {
workingDir rootDir
environment "CI", "1" // set to use more suitable logging format
commandLine rootProject.file('.venv/bin/python').absolutePath
args "-m", "poetry"
args "--directory", "${rootProject.file('airbyte-ci/connectors/pipelines').absolutePath}"
args "run"
args "airbyte-ci", "connectors", "--name=${project.name}"
args connectorsArgs
// Forbid these kinds of tasks from running concurrently.
// We can induce serial execution by giving them all a common output directory.
outputs.dir rootProject.file("${rootProject.buildDir}/airbyte-ci-lock")
outputs.upToDateWhen { false }
}
task.configure { dependsOn poetryInstallAirbyteCI }
return task
}
// Build connector image as part of 'assemble' task.
// This is required for local 'integrationTest' execution.
def buildConnectorImage = airbyteCIConnectorsTask(
'buildConnectorImage', '--disable-report-auto-open', 'build', '--use-host-gradle-dist-tar')
buildConnectorImage.configure {
// Images for java projects always rely on the distribution tarball.
dependsOn tasks.matching { it.name == 'distTar' }
// Ensure that all files exist beforehand.
dependsOn tasks.matching { it.name == 'generate' }
}
tasks.named('assemble').configure {
// We may revisit the dependency on assemble but the dependency should always be on a base task.
dependsOn buildConnectorImage
}
// Convenience tasks for local airbyte-ci execution.
airbyteCIConnectorsTask('airbyteCIConnectorBuild', 'build')
airbyteCIConnectorsTask('airbyteCIConnectorTest', 'test')
}
// produce reproducible archives
// (see https://docs.gradle.org/current/userguide/working_with_files.html#sec:reproducible_archives)
tasks.withType(AbstractArchiveTask).configureEach {
preserveFileTimestamps = false
reproducibleFileOrder = true
}
// pin dependency versions according to ./deps.toml
catalog {
versionCatalog {
from(files("deps.toml"))
}
}

View File

@@ -4,20 +4,7 @@ plugins {
repositories {
// # Gradle looks for dependency artifacts in repositories listed in 'repositories' blocks in descending order.
// ## Prefer repos controlled by Airbyte.
// TODO: add airbyte-controlled proxy repos here
// ## Look into other, public repos.
// Gradle plugin portal.
gradlePluginPortal()
// Maven Central has most of everything.
mavenCentral()
}
dependencies {
implementation 'ru.vyarus:gradle-use-python-plugin:2.3.0'
implementation 'org.apache.commons:commons-text:1.10.0'
}
tasks.withType(Jar).configureEach {

View File

@@ -1,62 +0,0 @@
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
class AirbyteIntegrationTestJavaPlugin implements Plugin<Project> {
void apply(Project project) {
project.sourceSets {
integrationTestJava {
java {
srcDir 'src/test-integration/java'
}
resources {
srcDir 'src/test-integration/resources'
}
}
}
project.tasks.named('check').configure {
dependsOn project.tasks.matching { it.name == 'compileIntegrationTestJavaJava' }
dependsOn project.tasks.matching { it.name == 'spotbugsIntegrationTestJava' }
}
project.configurations {
integrationTestJavaImplementation.extendsFrom testImplementation
integrationTestJavaRuntimeOnly.extendsFrom testRuntimeOnly
}
def integrationTestJava = project.tasks.register('integrationTestJava', Test) {
testClassesDirs = project.sourceSets.integrationTestJava.output.classesDirs
classpath += project.sourceSets.integrationTestJava.runtimeClasspath
useJUnitPlatform()
testLogging() {
events 'skipped', 'started', 'passed', 'failed'
exceptionFormat 'full'
// Swallow the logs when running in airbyte-ci, rely on test reports instead.
showStandardStreams = !System.getenv().containsKey("RUN_IN_AIRBYTE_CI")
}
jvmArgs = project.test.jvmArgs
systemProperties = project.test.systemProperties
maxParallelForks = project.test.maxParallelForks
maxHeapSize = project.test.maxHeapSize
// Tone down the JIT when running the containerized connector to improve overall performance.
// The JVM default settings are optimized for long-lived processes in steady-state operation.
// Unlike in production, the connector containers in these tests are always short-lived.
// It's very much worth injecting a JAVA_OPTS environment variable into the container with
// flags which will reduce startup time at the detriment of long-term performance.
environment 'JOB_DEFAULT_ENV_JAVA_OPTS', '-XX:TieredStopAtLevel=1'
// Always re-run integration tests no matter what.
outputs.upToDateWhen { false }
}
integrationTestJava.configure {
mustRunAfter project.tasks.named('check')
dependsOn project.tasks.matching { it.name == 'assemble' }
}
project.tasks.named('build').configure {
dependsOn integrationTestJava
}
}
}

View File

@@ -1,70 +0,0 @@
/*
This class facilites detecting the Java CDK target version via readCdkTargetVersion().
*/
import java.util.Properties
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
class AirbyteJavaCdkPlugin implements Plugin<Project> {
static String CDK_VERSION_FILE = "airbyte-cdk/java/airbyte-cdk/core/src/main/resources/version.properties"
String readCdkTargetVersion(Project project) {
Properties cdkVersionProps = new Properties()
project.file("${project.rootDir}/${CDK_VERSION_FILE}").withInputStream {
cdkVersionProps.load(it)
}
return cdkVersionProps.getProperty('version') ?: 'undefined'
}
@Override
void apply(Project project) {
project.ext.getCdkTargetVersion = {
return readCdkTargetVersion(project)
}
project.getTasks().create("disableLocalCdkRefs", DisableLocalCdkRefsTask.class)
project.getTasks().create("assertNotUsingLocalCdk", AssertNotUsingLocalCdkTask.class)
}
public static class DisableLocalCdkRefsTask extends DefaultTask {
@TaskAction
public void disableLocalCdkRefs() {
// Step through the project tree and set useLocalCdk to false on all connectors
getProject().rootProject.fileTree(dir: '.', include: '**/build.gradle').forEach(file -> {
String content = file.getText()
if (content.contains("useLocalCdk = true")) {
content = content.replace("useLocalCdk = true", "useLocalCdk = false")
file.setText(content)
System.out.println("Updated " + file.getPath())
}
})
}
}
public static class AssertNotUsingLocalCdkTask extends DefaultTask {
@TaskAction
public void assertNotUsingLocalCdk() {
List<String> foundPaths = new ArrayList<>()
for (File file : getProject().rootProject.fileTree(dir: '.', include: '**/build.gradle')) {
String content = file.getText()
if (content.contains("useLocalCdk = true")) {
System.err.println("Found usage of 'useLocalCdk = true' in " + file.getPath())
foundPaths.add(file.getPath())
}
}
if (!foundPaths.isEmpty()) {
String errorMessage = String.format(
"Detected usage of 'useLocalCdk = true' in the following files:\n%s\n" +
"This must be set to 'false' before merging to the main branch. \n" +
"NOTE: You can run './gradlew disableLocalCdkRefs' to automatically set it to 'false' on all projects.",
String.join("\n", foundPaths)
)
throw new RuntimeException(errorMessage)
}
}
}
}

View File

@@ -5,6 +5,7 @@ Also facilitates importing and working with the Java CDK.
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
class AirbyteJavaConnectorExtension {
@@ -125,13 +126,106 @@ class AirbyteJavaConnectorPlugin implements Plugin<Project> {
void apply(Project project) {
project.plugins.apply('application')
project.plugins.apply('java-test-fixtures')
project.plugins.apply(AirbyteIntegrationTestJavaPlugin)
project.plugins.apply(AirbytePerformanceTestJavaPlugin)
project.sourceSets {
integrationTestJava {
java {
srcDir 'src/test-integration/java'
}
resources {
srcDir 'src/test-integration/resources'
}
}
performanceTestJava {
java {
srcDir 'src/test-performance/java'
}
resources {
srcDir 'src/test-performance/resources'
}
}
}
project.tasks.named('check').configure {
dependsOn project.tasks.matching { it.name ==~ /(compile|spotbugs)[a-zA-Z]*Java/ }
}
project.configurations {
testFixturesImplementation.extendsFrom implementation
testFixturesRuntimeOnly.extendsFrom runtimeOnly
integrationTestJavaImplementation.extendsFrom testImplementation
integrationTestJavaRuntimeOnly.extendsFrom testRuntimeOnly
performanceTestJavaImplementation.extendsFrom testImplementation
performanceTestJavaRuntimeOnly.extendsFrom testRuntimeOnly
}
boolean withSlowTests = System.getProperty('skipSlowTests', 'false') == 'false'
project.test {
onlyIf { withSlowTests }
}
def integrationTestJava = project.tasks.register('integrationTestJava', Test) {
testClassesDirs = project.sourceSets.integrationTestJava.output.classesDirs
classpath += project.sourceSets.integrationTestJava.runtimeClasspath
useJUnitPlatform()
testLogging() {
events 'skipped', 'started', 'passed', 'failed'
exceptionFormat 'full'
// Swallow the logs when running in airbyte-ci, rely on test reports instead.
showStandardStreams = !System.getenv().containsKey("RUN_IN_AIRBYTE_CI")
}
jvmArgs = project.test.jvmArgs
systemProperties = project.test.systemProperties
maxParallelForks = project.test.maxParallelForks
maxHeapSize = project.test.maxHeapSize
// Tone down the JIT when running the containerized connector to improve overall performance.
// The JVM default settings are optimized for long-lived processes in steady-state operation.
// Unlike in production, the connector containers in these tests are always short-lived.
// It's very much worth injecting a JAVA_OPTS environment variable into the container with
// flags which will reduce startup time at the detriment of long-term performance.
environment 'JOB_DEFAULT_ENV_JAVA_OPTS', '-XX:TieredStopAtLevel=1'
// Always re-run integration tests no matter what.
outputs.upToDateWhen { false }
}
integrationTestJava.configure {
mustRunAfter project.tasks.named('check')
dependsOn project.tasks.matching { it.name == 'assemble' }
onlyIf { withSlowTests }
}
project.tasks.register('integrationTest').configure {
dependsOn integrationTestJava
}
project.tasks.named('build').configure {
dependsOn integrationTestJava
}
def performanceTestJava = project.tasks.register('performanceTestJava', Test) {
testClassesDirs = project.sourceSets.performanceTestJava.output.classesDirs
classpath += project.sourceSets.performanceTestJava.runtimeClasspath
systemProperty "cpuLimit", System.getProperty("cpuLimit")
systemProperty "memoryLimit", System.getProperty("memoryLimit")
useJUnitPlatform()
testLogging() {
events "passed", "failed"
exceptionFormat "full"
showStandardStreams = true
}
outputs.upToDateWhen { false }
maxHeapSize = '3g'
}
performanceTestJava.configure {
mustRunAfter project.tasks.named('check')
dependsOn project.tasks.matching { it.name == 'assemble' }
onlyIf { withSlowTests }
}
project.tasks.register('performanceTest').configure {
dependsOn performanceTestJava
}
project.dependencies {

View File

@@ -1,48 +0,0 @@
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
class AirbytePerformanceTestJavaPlugin implements Plugin<Project> {
void apply(Project project) {
project.sourceSets {
performanceTestJava {
java {
srcDir 'src/test-performance/java'
}
resources {
srcDir 'src/test-performance/resources'
}
}
}
project.tasks.named('check').configure {
dependsOn project.tasks.matching { it.name == 'compilePerformanceTestJavaJava' }
dependsOn project.tasks.matching { it.name == 'spotbugsPerformanceTestJava' }
}
project.configurations {
performanceTestJavaImplementation.extendsFrom testImplementation
performanceTestJavaRuntimeOnly.extendsFrom testRuntimeOnly
}
def performanceTestJava = project.tasks.register('performanceTestJava', Test) {
testClassesDirs = project.sourceSets.performanceTestJava.output.classesDirs
classpath += project.sourceSets.performanceTestJava.runtimeClasspath
systemProperty "cpuLimit", System.getProperty("cpuLimit")
systemProperty "memoryLimit", System.getProperty("memoryLimit")
useJUnitPlatform()
testLogging() {
events "passed", "failed"
exceptionFormat "full"
showStandardStreams = true
}
outputs.upToDateWhen { false }
maxHeapSize = '3g'
}
performanceTestJava.configure {
mustRunAfter project.tasks.named('check')
dependsOn project.tasks.matching { it.name == 'assemble' }
}
}
}

View File

@@ -32,9 +32,6 @@ dependencyResolutionManagement {
repositories {
// # Gradle looks for dependency artifacts in repositories listed in 'repositories' blocks in descending order.
// ## Start with the local filesystem.
mavenLocal()
// ## Prefer repos controlled by Airbyte.
maven {
@@ -105,20 +102,6 @@ dependencyResolutionManagement {
includeGroup 'org.apache.kafka'
}
}
// Node.js download repository, required only by com.github.node-gradle.node plugin.
ivy {
name = "Node.js"
setUrl("https://nodejs.org/dist/")
patternLayout {
artifact("v[revision]/[artifact](-v[revision]-[classifier]).[ext]")
}
metadataSources {
artifact()
}
content {
includeModule("org.nodejs", "node")
}
}
}
versionCatalogs {
@@ -159,22 +142,14 @@ if (isCiServer || isAirbyteCI) {
rootProject.name = 'airbyte'
include ':airbyte-cdk:java:airbyte-cdk'
include ':airbyte-cdk:java:airbyte-cdk:dependencies'
include ':airbyte-cdk:java:airbyte-cdk:core'
include ':airbyte-cdk:java:airbyte-cdk:db-destinations'
include ':airbyte-cdk:java:airbyte-cdk:db-sources'
include ':airbyte-cdk:java:airbyte-cdk:s3-destinations'
include ':airbyte-cdk:java:airbyte-cdk:typing-deduping'
include ':airbyte-cdk:java:airbyte-cdk:azure-destinations'
include ':airbyte-cdk:java:airbyte-cdk:gcs-destinations'
include ':airbyte-cdk:java:airbyte-cdk:datastore-bigquery'
include ':airbyte-cdk:java:airbyte-cdk:datastore-mongo'
include ':airbyte-cdk:java:airbyte-cdk:datastore-postgres'
include ':airbyte-integrations:connector-templates:generator'
include ':airbyte-integrations:connectors-performance:source-harness'
include ':airbyte-integrations:connectors-performance:destination-harness'
// Include all java CDK modules.
def cdkPath = rootDir.toPath().resolve('airbyte-cdk/java/airbyte-cdk')
cdkPath.eachDir { dir ->
def buildFiles = file(dir).list { file, name -> name == "build.gradle" }
if (buildFiles.length == 1) {
include ":airbyte-cdk:java:airbyte-cdk:${dir.getFileName()}"
}
}
// Include all java connector projects.
def integrationsPath = rootDir.toPath().resolve('airbyte-integrations/connectors')
@@ -184,3 +159,8 @@ integrationsPath.eachDir { dir ->
include ":airbyte-integrations:connectors:${dir.getFileName()}"
}
}
// Include miscellaneous modules.
include ':airbyte-integrations:connector-templates:generator'
include ':airbyte-integrations:connectors-performance:source-harness'
include ':airbyte-integrations:connectors-performance:destination-harness'