diff --git a/.github/workflows/contributor-build.yml b/.github/workflows/contributor-build.yml index 12e407bce0..4fca587c84 100644 --- a/.github/workflows/contributor-build.yml +++ b/.github/workflows/contributor-build.yml @@ -10,10 +10,12 @@ on: push: branches: - 'main' - pull_request: + # WARNING: Using pull_request_target to access secrets, but we check out the PR head commit. + # See checkout action for details. + pull_request_target: branches: - 'main' - + permissions: {} # none # See https://github.com/hibernate/hibernate-orm/pull/4615 for a description of the behavior we're getting. @@ -23,7 +25,7 @@ concurrency: group: "workflow = ${{ github.workflow }}, ref = ${{ github.event.ref }}, pr = ${{ github.event.pull_request.id }}" # Cancel previous builds in the same concurrency group even if they are in process # for pull requests or pushes to forks (not the upstream repository). - cancel-in-progress: ${{ github.event_name == 'pull_request' || github.repository != 'hibernate/hibernate-orm' }} + cancel-in-progress: ${{ github.event_name == 'pull_request_target' || github.repository != 'hibernate/hibernate-orm' }} jobs: build: @@ -51,9 +53,24 @@ jobs: # Running with HANA requires at least 8GB memory just for the database, which we don't have on GH Actions runners # - rdbms: hana steps: - - uses: actions/checkout@v4 + - name: Check out commit already pushed to branch + if: "! github.event.pull_request.number" + uses: actions/checkout@v4 with: persist-credentials: false + - name: Check out PR head + uses: actions/checkout@v4 + if: github.event.pull_request.number + with: + # WARNING: This is potentially dangerous since we're checking out unreviewed code, + # and since we're using the pull_request_target event we can use secrets. + # Thus, we must be extra careful to never expose secrets to steps that execute this code, + # and to strictly limit our of secrets to those that only pose minor security threats. + # This means in particular we won't expose Develocity credentials to the main gradle executions, + # but instead will execute gradle a second time just to push build scans to Develocity; + # see below. + ref: "refs/pull/${{ github.event.pull_request.number }}/head" + persist-credentials: false - name: Reclaim Disk Space run: .github/ci-prerequisites.sh - name: Start database @@ -84,9 +101,17 @@ jobs: RDBMS: ${{ matrix.rdbms }} # Don't populate Develocity cache in pull requests as that's potentially dangerous POPULATE_REMOTE_GRADLE_CACHE: "${{ github.event_name == 'push' }}" - GRADLE_ENTERPRISE_ACCESS_KEY: "${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}" + # WARNING: exposes secrets, so must only be passed to a step that doesn't run unapproved code. + GRADLE_ENTERPRISE_ACCESS_KEY: "${{ github.event_name == 'push' && secrets.GRADLE_ENTERPRISE_ACCESS_KEY || '' }}" run: ./ci/build-github.sh shell: bash + - name: Publish Develocity build scan for previous build (pull request) + if: "${{ !cancelled() && github.event_name == 'pull_request_target' && github.repository == 'hibernate/hibernate-orm' }}" + run: | + ./gradlew buildScanPublishPrevious + env: + # WARNING: exposes secrets, so must only be passed to a step that doesn't run unapproved code. + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY_PR }} - name: Upload test reports (if Gradle failed) uses: actions/upload-artifact@v4 if: failure() diff --git a/Jenkinsfile b/Jenkinsfile index 9cac80beb2..23381734c1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -159,36 +159,30 @@ stage('Build') { } } stage('Test') { - String cmd = "./ci/build.sh ${buildEnv.additionalOptions ?: ''} ${state[buildEnv.tag]['additionalOptions'] ?: ''}" + String args = "${buildEnv.additionalOptions ?: ''} ${state[buildEnv.tag]['additionalOptions'] ?: ''}" withEnv(["RDBMS=${buildEnv.dbName}"]) { - withCredentials([string(credentialsId: helper.scmSource.pullRequest ? - 'ge.hibernate.org-access-key-pr' : 'ge.hibernate.org-access-key', - variable: 'GRADLE_ENTERPRISE_ACCESS_KEY')]) { - withGradle { // withDevelocity, actually: https://plugins.jenkins.io/gradle/#plugin-content-capturing-build-scans-from-jenkins-pipeline - tryFinally({ - if (buildEnv.dbLockableResource == null) { - withCredentials([file(credentialsId: 'sybase-jconnect-driver', variable: 'jconnect_driver')]) { - sh 'cp -f $jconnect_driver ./drivers/jconn4.jar' - timeout( [time: buildEnv.longRunning ? 480 : 120, unit: 'MINUTES'] ) { - sh cmd - } - } + tryFinally({ + if (buildEnv.dbLockableResource == null) { + withCredentials([file(credentialsId: 'sybase-jconnect-driver', variable: 'jconnect_driver')]) { + sh 'cp -f $jconnect_driver ./drivers/jconn4.jar' + timeout( [time: buildEnv.longRunning ? 480 : 120, unit: 'MINUTES'] ) { + ciBuild buildEnv, args } - else { - lock(label: buildEnv.dbLockableResource, quantity: 1, variable: 'LOCKED_RESOURCE') { - if ( buildEnv.dbLockResourceAsHost ) { - cmd += " -DdbHost=${LOCKED_RESOURCE}" - } - timeout( [time: buildEnv.longRunning ? 480 : 120, unit: 'MINUTES'] ) { - sh cmd - } - } - } - }, { - junit '**/target/test-results/test/*.xml,**/target/test-results/testKitTest/*.xml' - }) + } } - } + else { + lock(label: buildEnv.dbLockableResource, quantity: 1, variable: 'LOCKED_RESOURCE') { + if ( buildEnv.dbLockResourceAsHost ) { + args += " -DdbHost=${LOCKED_RESOURCE}" + } + timeout( [time: buildEnv.longRunning ? 480 : 120, unit: 'MINUTES'] ) { + ciBuild buildEnv, args + } + } + } + }, { + junit '**/target/test-results/test/*.xml,**/target/test-results/testKitTest/*.xml' + }) } } }, { // Finally @@ -239,6 +233,39 @@ void runBuildOnNode(String label, Closure body) { }) } } + +void ciBuild(buildEnv, String args) { + if ( !helper.scmSource.pullRequest ) { + // Not a PR: we can pass credentials to the build, allowing it to populate the build cache + // and to publish build scans directly. + + // On untrusted nodes, we use the same access key as for PRs: + // it has limited access, essentially it can only push build scans. + def develocityCredentialsId = buildEnv.node ? 'ge.hibernate.org-access-key-pr' : 'ge.hibernate.org-access-key' + + withCredentials([string(credentialsId: develocityCredentialsId, + variable: 'GRADLE_ENTERPRISE_ACCESS_KEY')]) { + withGradle { // withDevelocity, actually: https://plugins.jenkins.io/gradle/#plugin-content-capturing-build-scans-from-jenkins-pipeline + sh "./ci/build.sh $args" + } + } + } + else { + // Pull request: we can't pass credentials to the build, since we'd be exposing secrets to e.g. tests. + // We do the build first, then publish the build scan separately. + tryFinally({ + sh "./ci/build.sh $args" + }, { // Finally + withCredentials([string(credentialsId: 'ge.hibernate.org-access-key-pr', + variable: 'GRADLE_ENTERPRISE_ACCESS_KEY')]) { + withGradle { // withDevelocity, actually: https://plugins.jenkins.io/gradle/#plugin-content-capturing-build-scans-from-jenkins-pipeline + sh './gradlew buildScanPublishPrevious' + } + } + }) + } +} + void pruneDockerContainers() { if ( !sh( script: 'command -v docker || true', returnStdout: true ).trim().isEmpty() ) { sh 'docker container prune -f || true'