From 619cc952aada520313123d8cecc4aa208ba63643 Mon Sep 17 00:00:00 2001 From: Adam Valenta Date: Tue, 25 Nov 2025 15:34:45 +0100 Subject: [PATCH 1/7] Change python credentials id --- scripts/jenkins/jenkinsfiles/Jenkinsfile-Release | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release index 2e960a60f3d1..dad31bda765b 100644 --- a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release +++ b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release @@ -82,7 +82,7 @@ try { echo "Testing credentials for PyPI, Anaconda, Docker, and S3..." // Test PyPI credentials - withCredentials([usernamePassword(credentialsId: 'pypi-credentials', usernameVariable: 'TWINE_USERNAME', passwordVariable: 'TWINE_PASSWORD')]) { + withCredentials([usernamePassword(credentialsId: 'H2O-PYPI-TOKEN', usernameVariable: 'TWINE_USERNAME', passwordVariable: 'TWINE_PASSWORD')]) { insideDocker([], pipelineContext.getBuildConfig().getReleaseImage(), pipelineContext.getBuildConfig().DOCKER_REGISTRY, pipelineContext.getBuildConfig(), 2, 'HOURS') { sh """ echo "=== Testing PyPI Credentials ===" @@ -422,7 +422,7 @@ EOF try { pipelineContext.getBuildSummary().addStageSummary(this, BUILD_PYPI_STAGE_NAME, env.BUILD_NUMBER_DIR) pipelineContext.getBuildSummary().setStageDetails(this, BUILD_PYPI_STAGE_NAME, env.NODE_NAME, env.WORKSPACE) - withCredentials([usernamePassword(credentialsId: 'pypi-credentials', usernameVariable: 'TWINE_USERNAME', passwordVariable: 'TWINE_PASSWORD')]) { + withCredentials([usernamePassword(credentialsId: 'H2O-PYPI-TOKEN', usernameVariable: 'TWINE_USERNAME', passwordVariable: 'TWINE_PASSWORD')]) { insideDocker([], pipelineContext.getBuildConfig().getReleaseImage(), pipelineContext.getBuildConfig().DOCKER_REGISTRY, pipelineContext.getBuildConfig(), 2, 'HOURS') { sh """ echo "Activating Python ${env.PYTHON_VERSION}" From 66bdd8cddab7c311cf497466c971bd9c85f87e03 Mon Sep 17 00:00:00 2001 From: Adam Valenta Date: Tue, 25 Nov 2025 15:35:02 +0100 Subject: [PATCH 2/7] Properly escape anaconda login --- scripts/jenkins/jenkinsfiles/Jenkinsfile-Release | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release index dad31bda765b..b3eb6a94fb5b 100644 --- a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release +++ b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release @@ -497,7 +497,7 @@ EOF echo '****** Upload to Conda ******' # Right now packages for all platforms are in the current directory # upload all distribution packages - anaconda login --username ${ANACONDA_USERNAME} --password ${ANACONDA_PASSWORD} + anaconda login --username "$ANACONDA_USERNAME" --password "$ANACONDA_PASSWORD" anaconda upload osx-64/\${PKG_NAME} anaconda upload linux-64/\${PKG_NAME} anaconda upload win-64/\${PKG_NAME} From 2947e3e5206b058992e039e6525ec04de2ad1586 Mon Sep 17 00:00:00 2001 From: Adam Valenta Date: Tue, 25 Nov 2025 16:00:34 +0100 Subject: [PATCH 3/7] Fix Jenkinsfile-release default values for build 0 where params.TEST_RELEASE is null and cannot be used to define other default values --- scripts/jenkins/jenkinsfiles/Jenkinsfile-Release | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release index b3eb6a94fb5b..ff9bf2f1cc95 100644 --- a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release +++ b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release @@ -767,21 +767,24 @@ private setReleaseJobProperties(final pipelineContext) { def RELEASE_BUCKET = 's3://h2o-release/h2o' final boolean isReleaseBranch = env.BRANCH_NAME.startsWith(pipelineContext.getBuildConfig().RELEASE_BRANCH_PREFIX) + + # Fix for build 0 where parameters are null and cannot be used in default value setup + final boolean testRelease = params.containsKey('TEST_RELEASE') ? params.TEST_RELEASE : !isReleaseBranch && env.BRANCH_NAME != 'master' def jobProperties = [ disableConcurrentBuilds(), parameters([ booleanParam(defaultValue: !isReleaseBranch && env.BRANCH_NAME != 'master', description: "If set don't upload to PyPI and Conda, just build the packages if required; also push to ${TEST_RELEASE_BUCKET} instead of ${RELEASE_BUCKET}", name: 'TEST_RELEASE'), booleanParam(defaultValue: env.BRANCH_NAME == 'master', description: 'If set, test all credentials (PyPI, Anaconda, Docker, S3) before building.', name: 'TEST_CREDENTIALS'), - booleanParam(defaultValue: isReleaseBranch && !params.TEST_RELEASE, description: 'If set, update top-level latest links and latest DOCKER tag', name: 'UPDATE_LATEST'), + booleanParam(defaultValue: isReleaseBranch && !testRelease, description: 'If set, update top-level latest links and latest DOCKER tag', name: 'UPDATE_LATEST'), booleanParam(defaultValue: true, description: 'If set, update latest links for this branch', name: 'UPDATE_LATEST_BRANCH'), - booleanParam(defaultValue: isReleaseBranch && !params.TEST_RELEASE, description: 'If set, publish to Nexus', name: 'UPLOAD_NEXUS'), + booleanParam(defaultValue: isReleaseBranch && !testRelease, description: 'If set, publish to Nexus', name: 'UPLOAD_NEXUS'), booleanParam(defaultValue: true, description: 'If set, build with Hadoop support. Should be unchecked only for test releases when you do not want to wait for the full build.', name: 'BUILD_HADOOP'), booleanParam(defaultValue: true, description: 'If set, build PyPI package and upload it to S3', name: 'BUILD_PYPI'), - booleanParam(defaultValue: isReleaseBranch && !params.TEST_RELEASE, description: 'If set and building rel- branch, publish to PyPI', name: 'UPLOAD_TO_PYPI'), + booleanParam(defaultValue: isReleaseBranch && !testRelease, description: 'If set and building rel- branch, publish to PyPI', name: 'UPLOAD_TO_PYPI'), booleanParam(defaultValue: true, description: 'If set, build conda packages and upload them to S3', name: 'BUILD_CONDA'), - booleanParam(defaultValue: isReleaseBranch && !params.TEST_RELEASE, description: 'If set and building rel- branch, publish to Anaconda', name: 'UPLOAD_TO_ANACONDA'), - booleanParam(defaultValue: isReleaseBranch && !params.TEST_RELEASE, description: 'If set, build H2O Docker image and push to H2O official docker hub.', name: 'BUILD_H2O_DOCKER'), + booleanParam(defaultValue: isReleaseBranch && !testRelease, description: 'If set and building rel- branch, publish to Anaconda', name: 'UPLOAD_TO_ANACONDA'), + booleanParam(defaultValue: isReleaseBranch && !testRelease, description: 'If set, build H2O Docker image and push to H2O official docker hub.', name: 'BUILD_H2O_DOCKER'), ]) ] if (env.BRANCH_NAME == 'master') { From 99cd0f460a5a4774080864a750dfebcd2b0b14d1 Mon Sep 17 00:00:00 2001 From: Adam Valenta Date: Tue, 25 Nov 2025 16:07:58 +0100 Subject: [PATCH 4/7] Fix PyPI credentials to use string type instead of usernamePassword --- scripts/jenkins/jenkinsfiles/Jenkinsfile-Release | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release index ff9bf2f1cc95..89bdd7701f4c 100644 --- a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release +++ b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release @@ -82,7 +82,7 @@ try { echo "Testing credentials for PyPI, Anaconda, Docker, and S3..." // Test PyPI credentials - withCredentials([usernamePassword(credentialsId: 'H2O-PYPI-TOKEN', usernameVariable: 'TWINE_USERNAME', passwordVariable: 'TWINE_PASSWORD')]) { + withCredentials([string(credentialsId: 'H2O-PYPI-TOKEN', variable: 'PYPI_TOKEN')]) { insideDocker([], pipelineContext.getBuildConfig().getReleaseImage(), pipelineContext.getBuildConfig().DOCKER_REGISTRY, pipelineContext.getBuildConfig(), 2, 'HOURS') { sh """ echo "=== Testing PyPI Credentials ===" @@ -90,7 +90,8 @@ try { . /envs/h2o_env_python${env.PYTHON_VERSION}/bin/activate # Test PyPI login by checking if credentials work - python -c "import requests; auth=('\$TWINE_USERNAME', '\$TWINE_PASSWORD'); r=requests.get('https://upload.pypi.org/', auth=auth); print('PyPI credentials: OK' if r.status_code in [200, 405] else 'PyPI credentials: FAILED')" + # PyPI API tokens use '__token__' as the username + python -c "import requests; auth=('__token__', '\$PYPI_TOKEN'); r=requests.get('https://upload.pypi.org/', auth=auth); print('PyPI credentials: OK' if r.status_code in [200, 405] else 'PyPI credentials: FAILED')" """ } } @@ -422,7 +423,7 @@ EOF try { pipelineContext.getBuildSummary().addStageSummary(this, BUILD_PYPI_STAGE_NAME, env.BUILD_NUMBER_DIR) pipelineContext.getBuildSummary().setStageDetails(this, BUILD_PYPI_STAGE_NAME, env.NODE_NAME, env.WORKSPACE) - withCredentials([usernamePassword(credentialsId: 'H2O-PYPI-TOKEN', usernameVariable: 'TWINE_USERNAME', passwordVariable: 'TWINE_PASSWORD')]) { + withCredentials([string(credentialsId: 'H2O-PYPI-TOKEN', variable: 'PYPI_TOKEN')]) { insideDocker([], pipelineContext.getBuildConfig().getReleaseImage(), pipelineContext.getBuildConfig().DOCKER_REGISTRY, pipelineContext.getBuildConfig(), 2, 'HOURS') { sh """ echo "Activating Python ${env.PYTHON_VERSION}" @@ -438,6 +439,9 @@ EOF echo '****** WARNING! Upload to PyPI suppressed ******' else echo '****** Upload to PyPI ******' + # PyPI API tokens use '__token__' as the username + export TWINE_USERNAME=__token__ + export TWINE_PASSWORD=\$PYPI_TOKEN twine upload dist/h2o-${env.PROJECT_VERSION}.tar.gz echo '****** Upload h2o_client to PyPI ******' cd ../client/dist From f113aec6de68c33e4b4ee57a77fa3313594d77aa Mon Sep 17 00:00:00 2001 From: Adam Valenta Date: Tue, 25 Nov 2025 16:15:36 +0100 Subject: [PATCH 5/7] Fix comment syntax and complete null-safe param handling --- scripts/jenkins/jenkinsfiles/Jenkinsfile-Release | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release index 89bdd7701f4c..b92782466f3d 100644 --- a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release +++ b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release @@ -771,8 +771,8 @@ private setReleaseJobProperties(final pipelineContext) { def RELEASE_BUCKET = 's3://h2o-release/h2o' final boolean isReleaseBranch = env.BRANCH_NAME.startsWith(pipelineContext.getBuildConfig().RELEASE_BRANCH_PREFIX) - - # Fix for build 0 where parameters are null and cannot be used in default value setup + + // Fix for build 0 where parameters are null and cannot be used in default value setup final boolean testRelease = params.containsKey('TEST_RELEASE') ? params.TEST_RELEASE : !isReleaseBranch && env.BRANCH_NAME != 'master' def jobProperties = [ @@ -803,7 +803,7 @@ private setReleaseJobProperties(final pipelineContext) { } properties(jobProperties) - if (!params.TEST_RELEASE && (env.BRANCH_NAME == 'master' || isReleaseBranch)) { + if (!testRelease && (env.BRANCH_NAME == 'master' || isReleaseBranch)) { env.S3_ROOT = RELEASE_BUCKET } else { env.S3_ROOT = TEST_RELEASE_BUCKET @@ -813,7 +813,7 @@ private setReleaseJobProperties(final pipelineContext) { if (env.BRANCH_NAME == 'master') { // we are building nightly build env.NIGHTLY_BUILD = true - } else if (params.TEST_RELEASE || isReleaseBranch) { + } else if (testRelease || isReleaseBranch) { // in case of release branch and enabled upload to Maven, we have to set DO_RELEASE if (params.UPLOAD_NEXUS) { env.DO_RELEASE = true From e4c9881072102f1fefb41f445bc47ee9e5010c8d Mon Sep 17 00:00:00 2001 From: Adam Valenta Date: Tue, 25 Nov 2025 16:19:01 +0100 Subject: [PATCH 6/7] Separate testReleaseDefault for parameter defaults from runtime testRelease --- .../jenkins/jenkinsfiles/Jenkinsfile-Release | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release index b92782466f3d..3dfaef8b8965 100644 --- a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release +++ b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release @@ -773,22 +773,26 @@ private setReleaseJobProperties(final pipelineContext) { final boolean isReleaseBranch = env.BRANCH_NAME.startsWith(pipelineContext.getBuildConfig().RELEASE_BRANCH_PREFIX) // Fix for build 0 where parameters are null and cannot be used in default value setup - final boolean testRelease = params.containsKey('TEST_RELEASE') ? params.TEST_RELEASE : !isReleaseBranch && env.BRANCH_NAME != 'master' + // Compute testRelease once based on branch logic for use in parameter defaults + final boolean testReleaseDefault = !isReleaseBranch && env.BRANCH_NAME != 'master' + + // For execution logic, use the actual parameter value if available, otherwise fall back to default + final boolean testRelease = params.containsKey('TEST_RELEASE') ? params.TEST_RELEASE : testReleaseDefault def jobProperties = [ disableConcurrentBuilds(), parameters([ - booleanParam(defaultValue: !isReleaseBranch && env.BRANCH_NAME != 'master', description: "If set don't upload to PyPI and Conda, just build the packages if required; also push to ${TEST_RELEASE_BUCKET} instead of ${RELEASE_BUCKET}", name: 'TEST_RELEASE'), + booleanParam(defaultValue: testReleaseDefault, description: "If set don't upload to PyPI and Conda, just build the packages if required; also push to ${TEST_RELEASE_BUCKET} instead of ${RELEASE_BUCKET}", name: 'TEST_RELEASE'), booleanParam(defaultValue: env.BRANCH_NAME == 'master', description: 'If set, test all credentials (PyPI, Anaconda, Docker, S3) before building.', name: 'TEST_CREDENTIALS'), - booleanParam(defaultValue: isReleaseBranch && !testRelease, description: 'If set, update top-level latest links and latest DOCKER tag', name: 'UPDATE_LATEST'), + booleanParam(defaultValue: isReleaseBranch && !testReleaseDefault, description: 'If set, update top-level latest links and latest DOCKER tag', name: 'UPDATE_LATEST'), booleanParam(defaultValue: true, description: 'If set, update latest links for this branch', name: 'UPDATE_LATEST_BRANCH'), - booleanParam(defaultValue: isReleaseBranch && !testRelease, description: 'If set, publish to Nexus', name: 'UPLOAD_NEXUS'), + booleanParam(defaultValue: isReleaseBranch && !testReleaseDefault, description: 'If set, publish to Nexus', name: 'UPLOAD_NEXUS'), booleanParam(defaultValue: true, description: 'If set, build with Hadoop support. Should be unchecked only for test releases when you do not want to wait for the full build.', name: 'BUILD_HADOOP'), booleanParam(defaultValue: true, description: 'If set, build PyPI package and upload it to S3', name: 'BUILD_PYPI'), - booleanParam(defaultValue: isReleaseBranch && !testRelease, description: 'If set and building rel- branch, publish to PyPI', name: 'UPLOAD_TO_PYPI'), + booleanParam(defaultValue: isReleaseBranch && !testReleaseDefault, description: 'If set and building rel- branch, publish to PyPI', name: 'UPLOAD_TO_PYPI'), booleanParam(defaultValue: true, description: 'If set, build conda packages and upload them to S3', name: 'BUILD_CONDA'), - booleanParam(defaultValue: isReleaseBranch && !testRelease, description: 'If set and building rel- branch, publish to Anaconda', name: 'UPLOAD_TO_ANACONDA'), - booleanParam(defaultValue: isReleaseBranch && !testRelease, description: 'If set, build H2O Docker image and push to H2O official docker hub.', name: 'BUILD_H2O_DOCKER'), + booleanParam(defaultValue: isReleaseBranch && !testReleaseDefault, description: 'If set and building rel- branch, publish to Anaconda', name: 'UPLOAD_TO_ANACONDA'), + booleanParam(defaultValue: isReleaseBranch && !testReleaseDefault, description: 'If set, build H2O Docker image and push to H2O official docker hub.', name: 'BUILD_H2O_DOCKER'), ]) ] if (env.BRANCH_NAME == 'master') { From 01198955ec6ddaa23843f68ef10af38e1a3b57c4 Mon Sep 17 00:00:00 2001 From: Adam Valenta Date: Tue, 25 Nov 2025 17:08:26 +0100 Subject: [PATCH 7/7] Update scripts/jenkins/jenkinsfiles/Jenkinsfile-Release Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/jenkins/jenkinsfiles/Jenkinsfile-Release | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release index 3dfaef8b8965..7bf9cc8d1313 100644 --- a/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release +++ b/scripts/jenkins/jenkinsfiles/Jenkinsfile-Release @@ -777,7 +777,7 @@ private setReleaseJobProperties(final pipelineContext) { final boolean testReleaseDefault = !isReleaseBranch && env.BRANCH_NAME != 'master' // For execution logic, use the actual parameter value if available, otherwise fall back to default - final boolean testRelease = params.containsKey('TEST_RELEASE') ? params.TEST_RELEASE : testReleaseDefault + final boolean testRelease = params.TEST_RELEASE != null ? params.TEST_RELEASE : testReleaseDefault def jobProperties = [ disableConcurrentBuilds(),