Message-ID: From: "vlsi (@vlsi)" To: "pgjdbc/pgjdbc" Date: Fri, 05 Jun 2026 07:56:30 +0000 Subject: [pgjdbc/pgjdbc] PR #4158: Fix Fedora Copr build and surface its failures in CI List-Id: X-GitHub-Additions: 159 X-GitHub-Author-Id: 213894 X-GitHub-Author-Login: vlsi X-GitHub-Base: master X-GitHub-Changed-Files: 9 X-GitHub-Commits: 4 X-GitHub-Deletions: 36 X-GitHub-Head-Branch: fix-fedora-copr-build X-GitHub-Head-SHA: e547d8daa3e640a3c9b11602cbde1cd440015137 X-GitHub-Issue: 4158 X-GitHub-Labels: chore X-GitHub-Merge-SHA: 87f1b25dec1a39cdd38b5d40e9289503ecbc8d89 X-GitHub-Repo: pgjdbc/pgjdbc X-GitHub-State: open X-GitHub-Type: pull_request X-GitHub-Url: https://github.com/pgjdbc/pgjdbc/pull/4158 Content-Type: text/plain; charset=utf-8 ## Why The Fedora Copr build was failing, and the GitHub check hid it. Several Fedora-side changes had accumulated since it last passed: - Fedora 44 dropped `java-21-openjdk-devel`. - The build-logic plugins required a pinned JDK 21 toolchain, which the chroot cannot provide. - The Gradle signing property was renamed from `signing.gpg.enabled` to `signing.pgp.enabled`. - `maven-local` was split per JDK; the generic package is gone on Fedora 44+. - Fedora's packaged JUnit lags pgjdbc master. ## What Four commits, by area: 1. **build-logic**: build with the current JVM when `jdkBuildVersion=0`, instead of requiring a pinned toolchain the chroot cannot provide. 2. **packaging (source distribution)**: depend on the versioned `java-25-openjdk-devel` (a single package, because Copr splits `--script-builddeps` on whitespace and the SRPM step runs only in the fedora-latest chroot), build with `-PjdkBuildVersion=0`, and pass the renamed `-Psigning.pgp.enabled=OFF`. 3. **packaging (RPM)**: require `(maven-local-openjdk25 or maven-local-openjdk21)`, a boolean dep that keeps one versioned template working across all chroots, and skip the test suite (see below). 4. **ci**: run the build from `submit-copr.sh` (no remote script fetched at run time) and surface the first error lines of a failing log inline. The check stays non-blocking (`continue-on-error`), but the inline log means a green check no longer hides a failure. ### Why the test suite is skipped (`runselftest 0`) pgjdbc master needs **JUnit 5.13** to compile its tests (the `org.junit.jupiter.params.Parameter` API) and **5.14** for one runtime test, but stable Fedora ships older JUnit: 5.10 on f42, 5.13 on f43/f44. Fedora does not raise JUnit's major version within a released branch, so f42 will never get it. The build therefore skips the test suite by default so the package still **builds and packages** against Fedora's own dependencies; re-enable with `--define "runselftest 1"` once Fedora ships 5.14. ## How to verify - `./gradlew :postgresql:sourceDistribution -Prelease -PjdkBuildVersion=0 -Psigning.pgp.enabled=OFF` builds the source tarball on the JDK in `PATH`. - The Copr build is green across all four chroots: f42, f43, f44 and rawhide. ## Follow-ups - Re-enable the test suite (`runselftest 1`) once Fedora ships JUnit 5.14. - The upstream `.spec.tpl` deliberately diverges from the Fedora package spec (boolean BuildRequires, `runselftest 0`); agreed with the Fedora maintainer to keep the boolean in our template and the Fedora dist-git spec per-branch and clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) diff --git a/.github/workflows/fedora-copr-build.yml b/.github/workflows/fedora-copr-build.yml index b45824baee..95cbe14217 100644 --- a/.github/workflows/fedora-copr-build.yml +++ b/.github/workflows/fedora-copr-build.yml @@ -11,24 +11,21 @@ permissions: jobs: build: - name: Submit a Copr build + name: Copr build runs-on: ubuntu-latest steps: - name: Check out proper version of sources uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Submit the build - # See https://github.com/orgs/community/discussions/15452 - # continue-on-error on the job level still marks the CI checks as failed, - # so we use continue-on-error on the step level. - # The drawback is that the step and the job display as "fully successful", so it is hard to tell - # if it failed or not without digging into the log. + - name: Copr build + # Keep the Copr check non-blocking (step-level continue-on-error; see + # https://github.com/orgs/community/discussions/15452) so it does not block merges, + # e.g. while the @pgjdbc/pgjdbc config is outdated. The step still prints the failure + # log inline, so a green check no longer hides the details. continue-on-error: true env: COPR_PR_WEBHOOK: https://copr.fedorainfracloud.org/webhooks/custom/55296/cf449c12-03b4-4f5b-ae84-3f5aa1d2b462/postgresql-jdbc/ COPR_PUSH_WEBHOOK: ${{ secrets.COPR_PUSH_WEBHOOK }} PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - curl https://raw.githubusercontent.com/praiskup/copr-ci-tooling/main/copr-gh-actions-submit > submit - bash submit "$PR_NUMBER" + run: bash packaging/rpm/setup-copr/submit-copr.sh "$PR_NUMBER" diff --git a/build-logic-commons/gradle-plugin/build.gradle.kts b/build-logic-commons/gradle-plugin/build.gradle.kts index d23bc57ca7..e63897c106 100644 --- a/build-logic-commons/gradle-plugin/build.gradle.kts +++ b/build-logic-commons/gradle-plugin/build.gradle.kts @@ -14,14 +14,17 @@ dependencies { implementation("org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:$expectedKotlinDslPluginsVersion") } -// We need to figure out a version that is supported by the current JVM, and by the Kotlin Gradle plugin -// So we settle on 21, 17, or 11 if the current JVM supports it -listOf(21, 17, 11) - .firstOrNull { JavaVersion.toVersion(it) <= JavaVersion.current() } - ?.let { buildScriptJvmTarget -> - java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(buildScriptJvmTarget)) +// jdkBuildVersion=0 means "build with the current JVM" (as in the main build); skip the +// toolchain so Gradle uses the JVM it already runs on and needs no separate JDK installed. +// Otherwise pin a Kotlin-supported target (21, 17, or 11) the current JVM can provide. +if (providers.gradleProperty("jdkBuildVersion").orNull != "0") { + listOf(21, 17, 11) + .firstOrNull { JavaVersion.toVersion(it) <= JavaVersion.current() } + ?.let { buildScriptJvmTarget -> + java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(buildScriptJvmTarget)) + } } } - } +} diff --git a/build-logic-commons/gradle-plugin/src/main/kotlin/build-logic.kotlin-dsl-gradle-plugin.gradle.kts b/build-logic-commons/gradle-plugin/src/main/kotlin/build-logic.kotlin-dsl-gradle-plugin.gradle.kts index 24ee745d27..5d14330880 100644 --- a/build-logic-commons/gradle-plugin/src/main/kotlin/build-logic.kotlin-dsl-gradle-plugin.gradle.kts +++ b/build-logic-commons/gradle-plugin/src/main/kotlin/build-logic.kotlin-dsl-gradle-plugin.gradle.kts @@ -8,14 +8,17 @@ tasks.validatePlugins { enableStricterValidation.set(true) } -// We need to figure out a version that is supported by the current JVM, and by the Kotlin Gradle plugin -// So we settle on 21, 17, or 11 if the current JVM supports it -listOf(21, 17, 11) - .firstOrNull { JavaVersion.toVersion(it) <= JavaVersion.current() } - ?.let { buildScriptJvmTarget -> - java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(buildScriptJvmTarget)) +// jdkBuildVersion=0 means "build with the current JVM" (as in the main build); skip the +// toolchain so Gradle uses the JVM it already runs on and needs no separate JDK installed. +// Otherwise pin a Kotlin-supported target (21, 17, or 11) the current JVM can provide. +if (providers.gradleProperty("jdkBuildVersion").orNull != "0") { + listOf(21, 17, 11) + .firstOrNull { JavaVersion.toVersion(it) <= JavaVersion.current() } + ?.let { buildScriptJvmTarget -> + java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(buildScriptJvmTarget)) + } } } - } +} diff --git a/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts index 044a724356..70567abbd2 100644 --- a/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts +++ b/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts @@ -63,9 +63,10 @@ tasks.configureEach { } ) compilerArgs.add("-parameters") - if (buildParameters.jdkBuildVersion >= 21 && buildParameters.targetJavaVersion < 11) { - // We know target Java 8 is deprecated with Java 21, so silence the warning - // otherwise the build fails due to -Werror below + if (buildParameters.buildJdkVersion >= 21 && buildParameters.targetJavaVersion < 11) { + // JDK 21+ reports an obsolete-source warning for target Java 8; silence it, + // otherwise -Werror below fails the build. Use buildJdkVersion (the resolved + // JDK) so jdkBuildVersion=0 (build with the current JDK) is covered too. compilerArgs.add("-Xlint:-options") } if (!buildParameters.enableCheckerframework) { diff --git a/docs/content/documentation/setup.md b/docs/content/documentation/setup.md index cffa539477..682cb09b72 100644 --- a/docs/content/documentation/setup.md +++ b/docs/content/documentation/setup.md @@ -37,7 +37,7 @@ when building pgJDBC for distributions, the pgJDBC Gradle build provides a conve The Maven-based project contains a version of the JDBC driver with complete functionality, which can be used in production and is still validly buildable within the Maven build environment. -The Maven-based project is created with **`gradlew -d :postgresql:sourceDistribution -Prelease -Psigning.gpg.enabled=OFF`**. +The Maven-based project is created with **`gradlew -d :postgresql:sourceDistribution -Prelease -Psigning.pgp.enabled=OFF`**. The produced `*-src.tar.gz` can be then found in `pgjdbc/build/distributions/` directory. JDBC driver can be built from the Maven-based project with **mvn package** or, when the tests are to be skipped, with **`mvn -DskipTests package`**. diff --git a/packaging/rpm/postgresql-jdbc.spec.tpl b/packaging/rpm/postgresql-jdbc.spec.tpl index 27642dfabb..0396ec9251 100644 --- a/packaging/rpm/postgresql-jdbc.spec.tpl +++ b/packaging/rpm/postgresql-jdbc.spec.tpl @@ -42,7 +42,10 @@ # [4] https://copr.fedorainfracloud.org/coprs/g/pgjdbc/pgjdbc-ci/ # ============================================================================ -%{!?runselftest:%global runselftest 1} +# Skip the test suite by default: Fedora's packaged JUnit lags pgjdbc master +# (no JUnit 5.13/5.14 on current releases), so the tests fail to build there. +# Re-enable once Fedora catches up with --define "runselftest 1". +%{!?runselftest:%global runselftest 0} %global section devel %global source_path pgjdbc/src/main/java/org/postgresql @@ -60,7 +63,9 @@ Provides: pgjdbc = %version-%release BuildArch: noarch ExclusiveArch: %{java_arches} noarch BuildRequires: java-devel >= 1.8 -BuildRequires: maven-local +# maven-local was split per JDK (the generic provide is gone on Fedora 44+). +# A boolean dep lets one template span all chroots with versioned packages. +BuildRequires: (maven-local-openjdk25 or maven-local-openjdk21) BuildRequires: maven-bundle-plugin BuildRequires: mvn(com.ongres.scram:scram-client) diff --git a/packaging/rpm/setup-copr/build-script b/packaging/rpm/setup-copr/build-script index 867cebae73..d29af6ebb9 100644 --- a/packaging/rpm/setup-copr/build-script +++ b/packaging/rpm/setup-copr/build-script @@ -33,7 +33,9 @@ cd "$workdir" test -f "$hook_payload" && webhook-checkout "$hook_payload" # generate source tarball -./gradlew :postgresql:sourceDistribution -Prelease -PjdkBuildVersion=21 -Psigning.gpg.enabled=OFF +# jdkBuildVersion=0 builds with whatever JDK the chroot ships (the running JDK), +# so we never pin a java-NN-openjdk-devel package that Fedora later drops. +./gradlew :postgresql:sourceDistribution -Prelease -PjdkBuildVersion=0 -Psigning.pgp.enabled=OFF project_version=$(grep pgjdbc.version gradle.properties | cut -d "=" -f2-) cp pgjdbc/build/distributions/postgresql-$project_version-jdbc-src.tar.gz "$resultdir" diff --git a/packaging/rpm/setup-copr/copr-setup b/packaging/rpm/setup-copr/copr-setup index ae2ef89874..98a9d0621f 100755 --- a/packaging/rpm/setup-copr/copr-setup +++ b/packaging/rpm/setup-copr/copr-setup @@ -11,7 +11,7 @@ PROJECT_PUSH=@pgjdbc/pgjdbc build_deps=( git - java-21-openjdk-devel + java-25-openjdk-devel python-unversioned-command ) diff --git a/packaging/rpm/setup-copr/submit-copr.sh b/packaging/rpm/setup-copr/submit-copr.sh new file mode 100755 index 0000000000..0fce9dde75 --- /dev/null +++ b/packaging/rpm/setup-copr/submit-copr.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# +# Trigger a Fedora Copr build for this commit (or PR) and wait for it to finish. +# On failure, print the Copr build log so the error shows in the CI step output. +# +# This runs in GitHub Actions (it is NOT the script Copr runs; that one is +# `build-script` in this directory). It reimplements the submit-and-poll that +# copr-ci-tooling's copr-gh-actions-submit does over the Copr webhook and REST +# API, so the workflow does not fetch a remote script whose content can change. +# +# Required environment: +# COPR_PR_WEBHOOK custom webhook URL for PR builds (public) +# COPR_PUSH_WEBHOOK custom webhook URL for push builds (secret) +# +# Usage: submit-copr.sh [PR_NUMBER] + +set -uo pipefail + +pr_id=${1:-} + +if [ -n "$pr_id" ]; then + # actions/checkout leaves a merge ref checked out; fetch the PR head so the + # webhook payload carries the commit Copr should build. + ref=refs/pull/$pr_id + git fetch --depth=1 origin "+pull/$pr_id/head:$ref" \ + || { echo "::error::cannot fetch PR #$pr_id"; exit 1; } + webhook=${COPR_PR_WEBHOOK:-} + payload=$(printf '{"type":"PR","pr_id":"%s","git_hash":"%s"}' "$pr_id" "$(git rev-parse "$ref")") +else + webhook=${COPR_PUSH_WEBHOOK:-} + payload=$(printf '{"type":"PUSH","git_hash":"%s"}' "$(git rev-parse HEAD)") +fi + +if [ -z "$webhook" ]; then + echo "::error::Copr webhook URL is not set" + exit 1 +fi + +echo "Submitting Copr build: $payload" +build_id=$(curl -fsS -X POST -H 'Content-Type: application/json' --data "$payload" "$webhook" | grep -oE '[0-9]+' | head -1) +if [ -z "$build_id" ]; then + echo "::error::Copr build submission failed" + exit 1 +fi +echo "Submitted build: https://copr.fedorainfracloud.org/coprs/build/$build_id/" + +# Poll until the build reaches a terminal state; tolerate transient API errors. +# Counted (not while-true) so an unexpected or stuck state cannot hang the job. +api="https://copr.fedorainfracloud.org/api_3/build/$build_id" +state="" +for ((i = 0; i < 240; i++)); do # up to ~2h at 30s intervals + sleep 30 + state=$(curl -fsS "$api" | jq -r '.state') || { state=""; continue; } + echo "build $build_id state: $state" + case $state in + succeeded) exit 0 ;; + failed | canceled) break ;; + pending | starting | running | importing | waiting | imported | "" | null) ;; + *) echo "::error::unexpected Copr build state: $state"; exit 1 ;; + esac +done + +# Reached on failed/canceled (loop broke) or on timeout (loop exhausted). +case $state in + failed | canceled) ;; # fall through to the failure log below + *) echo "::error::Copr build $build_id did not finish in time (last state: ${state:-unknown})"; exit 1 ;; +esac + +echo "::error title=Copr build $build_id failed::https://copr.fedorainfracloud.org/coprs/build/$build_id/" +build_json=$(curl -fsS "$api") +repo_url=$(printf '%s' "$build_json" | jq -r '.repo_url // empty') +srpm_version=$(printf '%s' "$build_json" | jq -r '.source_package.version // empty') +padded=$(printf '%08d' "$build_id") + +# High-signal error markers across dnf, rpmbuild, Maven and Gradle output. +err_pattern='What went wrong|BUILD FAILURE|FAILURE: Build failed|BUILD FAILED|COMPILATION ERROR|\[ERROR\]|cannot find symbol|error:|No match for argument|Failed to resolve|Could not resolve|Tests run: .*Failures: [1-9]' + +# Print the first error lines with context; the first failure is usually the root cause. +# grep --color highlights the matched marker (GitHub renders ANSI). +show() { # $1: label, $2: .log.gz URL, $3: "group" to collapse the output + [ "${3:-}" = group ] && echo "::group::$1" || echo "=== $1 ===" + echo "log: $2" + if curl -sSf "$2" 2>/dev/null | gunzip -c > log.txt; then + local matches + matches=$(grep --color=always -n -m 40 -iE -B2 -A5 "$err_pattern" log.txt) || true + if [ -n "$matches" ]; then + printf '%s\n' "$matches" | head -200 + else + echo "(no known error markers; tail of the log)" + tail -n 20 log.txt + fi + else + echo "(no log at $2)" + fi + [ "${3:-}" = group ] && echo "::endgroup::" +} + +# Show the SRPM log only when the SRPM stage itself failed (no source version built). +# When the SRPM succeeds, the failures are in the per-chroot RPM builds below. +if [ -z "$srpm_version" ]; then + show "SRPM build log" "$repo_url/srpm-builds/$padded/builder-live.log.gz" +fi + +# Per-chroot RPM build logs, for chroots that got far enough to produce one. +# Collapsed, since several chroots can fail at once. +curl -fsS "https://copr.fedorainfracloud.org/api_3/build-chroot/list?build_id=$build_id" \ + | jq -r '.items[] | select(.state == "failed" and .result_url != null) | "\(.name) \(.result_url)"' \ + | while read -r name url; do + show "chroot $name" "${url%/}/builder-live.log.gz" group + done + +exit 1