diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..aeaa50e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_size = 2 +indent_style = space +trim_trailing_whitespace = true +continuation_indent_size = 4 diff --git a/.gitattributes b/.gitattributes index 92ad407..cb5ab64 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ -*.bat text eol=crlf +* text=auto +*.bat text eol=crlf +gradlew text eol=lf diff --git a/.github/actions/docker-install-macos/action.yml b/.github/actions/docker-install-macos/action.yml new file mode 100644 index 0000000..7068fbb --- /dev/null +++ b/.github/actions/docker-install-macos/action.yml @@ -0,0 +1,17 @@ +--- +name: "Install Docker on macOS" +description: "Performs an unattended install of Docker Desktop for MacOS" +runs: + using: "composite" + steps: + # From https://github.com/docker/for-mac/issues/2359#issuecomment-943131345 + - run: | + brew install --cask docker + sudo /Applications/Docker.app/Contents/MacOS/Docker --unattended --install-privileged-components + open -a /Applications/Docker.app --args --unattended --accept-license + while ! /Applications/Docker.app/Contents/Resources/bin/docker info &>/dev/null; do sleep 1; done + shell: bash +branding: + icon: "tag" + color: "blue" +... diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6337115 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 20 diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..b024026 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,33 @@ +--- +name: Publish +on: + push: + branches: + - main +jobs: + publish: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 1 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 8 + cache: 'gradle' + - name: build publish + run: ./gradlew clean build publish --info --stacktrace + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_SIGNING_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_SIGNING_PASSWORD }} + - name: Publish Test Report + if: ${{ always() }} + uses: scacap/action-surefire-report@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + report_paths: '**/build/test-results/test/TEST-*.xml' +... diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5f5e70f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +--- +name: CI +on: + workflow_dispatch: + push: + branches-ignore: + - main +jobs: + ci-build: + strategy: + matrix: + os: [ ubuntu-latest, windows-latest, macos-latest ] + java: [ 8, 11, 17 ] + runs-on: ${{ matrix.os }} + timeout-minutes: 20 + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 1 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: ${{ matrix.java }} + cache: 'gradle' + - name: Install Docker on macOS + if: matrix.os == 'macos-latest' + uses: ./.github/actions/docker-install-macos + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: docker version + run: docker version + - name: docker info + run: docker info + - name: java version + run: java -version + - name: clean build + run: ./gradlew clean build --info --stacktrace + - name: Publish Test Report + if: ${{ always() }} + uses: scacap/action-surefire-report@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + report_paths: '**/build/test-results/test/TEST-*.xml' +... diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c318f23 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,41 @@ +--- +name: Release +on: + release: + types: + - released +# - published + +jobs: + release: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 1 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 8 + cache: 'gradle' + - name: Set artifact version + run: | + echo "RELEASE_VERSION=$(echo '${{ github.event.release.tag_name }}' | sed -e s/^v//)" >> $GITHUB_ENV + - name: build publish + run: ./gradlew clean build publish closeAndReleaseStagingRepository --info --stacktrace -Pversion="${{ env.RELEASE_VERSION }}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_SIGNING_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_SIGNING_PASSWORD }} + SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + - name: Publish Test Report + if: ${{ always() }} + uses: scacap/action-surefire-report@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + report_paths: '**/build/test-results/test/TEST-*.xml' +... diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml new file mode 100644 index 0000000..bcaeb4c --- /dev/null +++ b/.github/workflows/update-gradle-wrapper.yml @@ -0,0 +1,18 @@ +name: Update Gradle Wrapper + +on: + workflow_dispatch: + schedule: + # "weekly" https://crontab.guru/every-week + - cron: "0 0 * * 0" + +jobs: + update-gradle-wrapper: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Update Gradle Wrapper + uses: gradle-update/update-gradle-wrapper-action@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - uses: gradle/wrapper-validation-action@v1 diff --git a/.gitignore b/.gitignore index d49a8b5..fffb395 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,11 @@ .gradle -build .idea +*.iml +build/ +classes/ +.DS_Store +out/ +*.settings +*.project +*bin/ +*.classpath diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..68f98a1 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,15 @@ +## Publishing + +Packages are automatically published from the `main` branch to the GitHub Package Registry. +Manually cut releases will be published to both GitHub Package Registry and Maven Central. + +## Release Workflow + +There are multiple GitHub Action Workflows for the different steps in the package's lifecycle: + +- CI: Builds and checks incoming changes on a pull request + - triggered on every push to a non-default branch +- CD: Publishes the Gradle artifacts to GitHub Package Registry + - triggered only on pushes to the default branch +- Release: Publishes Gradle artifacts to Sonatype and releases them to Maven Central + - triggered on a published GitHub release using the underlying tag as artifact version, e.g. via `git tag -m "$MESSAGE" v$(date +"%Y-%m-%dT%H-%M-%S")` diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..f86229f --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,66 @@ +import java.text.SimpleDateFormat +import java.util.* + +rootProject.extra.set("artifactVersion", SimpleDateFormat("yyyy-MM-dd\'T\'HH-mm-ss").format(Date())) + +plugins { + id("maven-publish") + id("com.github.ben-manes.versions") version "0.42.0" + id("net.ossindex.audit") version "0.4.11" + id("io.freefair.maven-central.validate-poms") version "6.4.1" + id("io.github.gradle-nexus.publish-plugin") version "1.1.0" +} + +val dependencyVersions = listOf( + "org.jetbrains:annotations:23.0.0" +) + +val dependencyVersionsByGroup = mapOf( +) + +subprojects { + repositories { +// mavenLocal() +// fun findProperty(s: String) = project.findProperty(s) as String? +// maven { +// name = "github" +// setUrl("https://maven.pkg.github.com/docker-client/*") +// credentials { +// username = System.getenv("PACKAGE_REGISTRY_USER") ?: findProperty("github.package-registry.username") +// password = System.getenv("PACKAGE_REGISTRY_TOKEN") ?: findProperty("github.package-registry.password") +// } +// } + mavenCentral() + } +} + +allprojects { + configurations.all { + resolutionStrategy { + failOnVersionConflict() + force(dependencyVersions) + eachDependency { + val forcedVersion = dependencyVersionsByGroup[requested.group] + if (forcedVersion != null) { + useVersion(forcedVersion) + } + } + } + } +} + +fun findProperty(s: String) = project.findProperty(s) as String? + +val isSnapshot = project.version == "unspecified" +nexusPublishing { + repositories { + if (!isSnapshot) { + sonatype { + // 'sonatype' is pre-configured for Sonatype Nexus (OSSRH) which is used for The Central Repository + stagingProfileId.set(System.getenv("SONATYPE_STAGING_PROFILE_ID") ?: findProperty("sonatype.staging.profile.id")) //can reduce execution time by even 10 seconds + username.set(System.getenv("SONATYPE_USERNAME") ?: findProperty("sonatype.username")) + password.set(System.getenv("SONATYPE_PASSWORD") ?: findProperty("sonatype.password")) + } + } + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..04ebd19 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,14 @@ +org.gradle.daemon=true + +group=de.gesellix + +github.package-registry.owner=docker-client +github.package-registry.repository=docker-registry +github.package-registry.username= +github.package-registry.password= + +sonatype.snapshot.url=https://oss.sonatype.org/content/repositories/snapshots/ +sonatype.staging.url=https://oss.sonatype.org/service/local/staging/deploy/maven2/ +sonatype.staging.profile.id= +sonatype.username= +sonatype.password= diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 4e96e69..4a651c8 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -1,24 +1,129 @@ +import java.text.SimpleDateFormat +import java.util.* + plugins { - groovy - `java-library` + id("groovy") + id("java-library") + id("maven-publish") + id("signing") + id("com.github.ben-manes.versions") + id("net.ossindex.audit") + id("io.freefair.maven-central.validate-poms") } -repositories { - mavenCentral() +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } dependencies { + constraints { + listOf( + "org.jetbrains.kotlin:kotlin-reflect", + "org.jetbrains.kotlin:kotlin-stdlib", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8", + "org.jetbrains.kotlin:kotlin-stdlib-common", + "org.jetbrains.kotlin:kotlin-test" + ).onEach { + implementation(it) { + version { + strictly("[1.5,1.7)") + prefer("1.6.10") + } + } + } + implementation("com.squareup.okio:okio") { + version { + strictly("[2.5,4)") + prefer("3.0.0") + } + } + } implementation("de.gesellix:docker-remote-api-client:2022-02-23T13-45-00") implementation("de.gesellix:docker-remote-api-model-1-41:2022-02-23T11-47-00") implementation("de.gesellix:docker-engine:2022-02-22T23-12-00") - implementation("org.slf4j:slf4j-api:[1.7,)") + implementation("org.slf4j:slf4j-api:[1.7,)!!1.7.36") testImplementation("ch.qos.logback:logback-classic:[1.2,2)!!1.2.10") - testImplementation("org.codehaus.groovy:groovy:3.0.9") - testImplementation("org.spockframework:spock-core:2.0-groovy-3.0") + testImplementation("org.codehaus.groovy:groovy:[3,4)!!3.0.9") + testImplementation("org.spockframework:spock-core:2.1-groovy-3.0") } tasks.withType { useJUnitPlatform() } + +val javadocJar by tasks.registering(Jar::class) { + dependsOn("classes") + archiveClassifier.set("javadoc") + from(tasks.javadoc) +} + +val sourcesJar by tasks.registering(Jar::class) { + dependsOn("classes") + archiveClassifier.set("sources") + from(sourceSets.main.get().allSource) +} + +artifacts { + add("archives", sourcesJar.get()) + add("archives", javadocJar.get()) +} + +fun findProperty(s: String) = project.findProperty(s) as String? + +val isSnapshot = project.version == "unspecified" +val artifactVersion = if (!isSnapshot) project.version as String else SimpleDateFormat("yyyy-MM-dd\'T\'HH-mm-ss").format(Date())!! +val publicationName = "dockerRegistry" +publishing { + repositories { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/${property("github.package-registry.owner")}/${property("github.package-registry.repository")}") + credentials { + username = System.getenv("GITHUB_ACTOR") ?: findProperty("github.package-registry.username") + password = System.getenv("GITHUB_TOKEN") ?: findProperty("github.package-registry.password") + } + } + } + publications { + register(publicationName, MavenPublication::class) { + pom { + name.set("docker-registry") + description.set("Docker Registry/Distribution abstraction") + url.set("https://github.com/docker-client/docker-registry") + licenses { + license { + name.set("MIT") + url.set("https://opensource.org/licenses/MIT") + } + } + developers { + developer { + id.set("gesellix") + name.set("Tobias Gesellchen") + email.set("tobias@gesellix.de") + } + } + scm { + connection.set("scm:git:github.com/docker-client/docker-registry.git") + developerConnection.set("scm:git:ssh://github.com/docker-client/docker-registry.git") + url.set("https://github.com/docker-client/docker-registry") + } + } + artifactId = "docker-docker-registry" + version = artifactVersion + from(components["java"]) + artifact(sourcesJar.get()) + artifact(javadocJar.get()) + } + } +} + +signing { + val signingKey: String? by project + val signingPassword: String? by project + useInMemoryPgpKeys(signingKey, signingPassword) + sign(publishing.publications[publicationName]) +}