This is an automated email from the ASF dual-hosted git repository. Kusuma04-dev pushed a commit to branch test_10.3.x in repository https://gitbox.apache.org/repos/asf/incubator-kie.git
commit 17515e1e33b8f30eac43ebebc231c32fff83e41e Author: Kusuma <[email protected]> AuthorDate: Mon Oct 12 18:52:45 2026 +0530 test --- .ci/jenkins/Jenkinsfile.103xplus.deploy | 295 ++++++++++++++++++++++ .ci/jenkins/Jenkinsfile.103xplus.promote | 303 +++++++++++++++++++++++ .ci/jenkins/project/Jenkinsfile.103xplus.release | 138 +++++++++++ script/release/01-update-version.sh | 108 ++++++++ script/release/02-rc-commit.sh | 236 ++++++++++++++++++ script/release/03-build.sh | 100 ++++++++ script/release/04-deploy-to-staging.sh | 179 +++++++++++++ script/release/05-tag-release.sh | 134 ++++++++++ script/release/README.md | 270 ++++++++++++++++++++ script/release/release-all.sh | 201 +++++++++++++++ script/release/upload_filemgmt.sh | 127 ++++++++++ 11 files changed, 2091 insertions(+) diff --git a/.ci/jenkins/Jenkinsfile.103xplus.deploy b/.ci/jenkins/Jenkinsfile.103xplus.deploy new file mode 100644 index 00000000000..138043229a2 --- /dev/null +++ b/.ci/jenkins/Jenkinsfile.103xplus.deploy @@ -0,0 +1,295 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// 10.3.x+ deploy pipeline — mirrors Jenkinsfile.deploy with 10.3+ specifics: +// - no RELEASE env-var path (tagging/signing handled by Jenkinsfile.103xplus.release) +// - PR creation mode retained for stream-snapshot version bumps +// - deployment.properties filename to match DSL PROPERTIES_FILE_NAME + +import org.jenkinsci.plugins.workflow.libs.Library +@Library('jenkins-pipeline-shared-libraries')_ + +import org.kie.jenkins.MavenCommand + +deployProperties = [:] + +pipeline { + agent { + docker { + image env.AGENT_DOCKER_BUILDER_IMAGE + args env.AGENT_DOCKER_BUILDER_ARGS + label util.avoidFaultyNodes() + } + } + + options { + timestamps() + timeout(time: 180, unit: 'MINUTES') + } + + environment { + DROOLS_CI_EMAIL_TO = credentials("${JENKINS_EMAIL_CREDS_ID}") + + PR_BRANCH_HASH = "${util.generateHash(10)}" + } + + stages { + stage('Initialize') { + steps { + script { + cleanWs(disableDeferredWipeout: true) + + if (params.DISPLAY_NAME) { + currentBuild.displayName = params.DISPLAY_NAME + } + + if (isCreatePr()) { + // Verify version is set + assert getProjectVersion() + } + + dir(getRepoName()) { + checkoutRepo() + } + } + } + post { + success { + script { + setDeployPropertyIfNeeded('git.branch', getBuildBranch()) + setDeployPropertyIfNeeded('git.author', getGitAuthor()) + setDeployPropertyIfNeeded('project.version', getProjectVersion()) + } + } + } + } + + stage('Prepare for PR') { + when { + expression { return isCreatePr() } + } + steps { + script { + dir(getRepoName()) { + if (githubscm.isBranchExist('origin', getPRBranch())) { + githubscm.removeRemoteBranch('origin', getPRBranch(), getGitAuthorPushCredsId()) + } + githubscm.createBranch(getPRBranch()) + } + } + } + } + + stage('Update project version') { + when { + expression { return getProjectVersion() } + } + steps { + script { + dir(getRepoName()) { + configFileProvider([configFile(fileId: env.MAVEN_SETTINGS_CONFIG_FILE_ID, variable: 'MAVEN_SETTINGS_FILE')]) { + maven.mvnVersionsSet( + getMavenCommand().withSettingsXmlFile(MAVEN_SETTINGS_FILE), + getProjectVersion(), + !isStreamDevVersion() + ) + } + } + } + } + } + + stage('Build & Deploy repo') { + steps { + script { + dir(getRepoName()) { + withCredentials([usernamePassword(credentialsId: env.MAVEN_REPO_CREDS_ID, usernameVariable: 'REPOSITORY_USER', passwordVariable: 'REPOSITORY_TOKEN')]) { + def installOrDeploy + if (shouldDeployToRepository()) { + installOrDeploy = "deploy -DdeployAtEnd -Dapache.repository.username=${REPOSITORY_USER} -Dapache.repository.password=${REPOSITORY_TOKEN} -DretryFailedDeploymentCount=5" + } else { + installOrDeploy = 'install' + } + def mavenCommand = getMavenCommand() + .withOptions(env.BUILD_MVN_OPTS ? [ env.BUILD_MVN_OPTS ] : []) + .withOptions(env.DROOLS_BUILD_MVN_OPTS ? [ env.DROOLS_BUILD_MVN_OPTS ] : []) + .withOptions(env.BUILD_MVN_OPTS_CURRENT ? [ env.BUILD_MVN_OPTS_CURRENT ] : []) + .withProperty('maven.test.failure.ignore', true) + .skipTests(params.SKIP_TESTS) + + configFileProvider([configFile(fileId: env.MAVEN_SETTINGS_CONFIG_FILE_ID, variable: 'MAVEN_SETTINGS_FILE')]) { + mavenCommand.withSettingsXmlFile(MAVEN_SETTINGS_FILE).run("clean ${installOrDeploy}") + } + } + } + } + } + post { + always { + script { + dir(getRepoName()) { + saveReports() + util.archiveConsoleLog() + } + } + } + } + } + + stage('Create PR') { + when { + expression { return isCreatePr() } + } + steps { + script { + dir(getRepoName()) { + if (githubscm.isThereAnyChanges()) { + commitAndCreatePR() + } else { + println '[WARN] no changes to commit' + } + } + } + } + post { + success { + script { + setDeployPropertyIfNeeded("${getRepoName()}.pr.source.uri", "https://github.com/${getGitAuthor()}/${getRepoName()}") + setDeployPropertyIfNeeded("${getRepoName()}.pr.source.ref", getPRBranch()) + setDeployPropertyIfNeeded("${getRepoName()}.pr.target.uri", "https://github.com/${getGitAuthor()}/${getRepoName()}") + setDeployPropertyIfNeeded("${getRepoName()}.pr.target.ref", getBuildBranch()) + } + } + } + } + } + post { + always { + script { + setDeployPropertyIfNeeded("${getRepoName()}.deploy.build.run", true) + def propertiesStr = deployProperties.collect { entry -> "${entry.key}=${entry.value}" }.join('\n') + writeFile(text: propertiesStr, file: PROPERTIES_FILE_NAME) + archiveArtifacts(artifacts: PROPERTIES_FILE_NAME) + } + } + unsuccessful { + sendNotification() + } + cleanup { + script { + util.cleanNode() + } + } + } +} + +void saveReports() { + junit testResults: '**/target/surefire-reports/**/*.xml, **/target/failsafe-reports/**/*.xml, **/target/invoker-reports/**/TEST-*.xml', allowEmptyResults: true +} + +void checkoutRepo() { + deleteDir() + checkout(githubscm.resolveRepository(getRepoName(), getGitAuthor(), getBuildBranch(), false, getGitAuthorCredsId())) + // need to manually checkout branch since on a detached branch after checkout command + sh "git checkout ${getBuildBranch()}" +} + +void commitAndCreatePR() { + def commitMsg = "[${getBuildBranch()}] Update version to ${getProjectVersion()}" + def prBody = "Generated by build ${BUILD_TAG}: ${BUILD_URL}.\nPlease do not merge, it should be merged automatically." + githubscm.setUserConfigFromCreds(getGitAuthorPushCredsId()) + githubscm.commitChanges(commitMsg, { + githubscm.findAndStageNotIgnoredFiles('pom.xml') + githubscm.findAndStageNotIgnoredFiles('antora.yml') + }) + githubscm.pushObject('origin', getPRBranch(), getGitAuthorPushCredsId()) + deployProperties["${getRepoName()}.pr.link"] = githubscm.createPRWithLabels(commitMsg, prBody, getBuildBranch(), ['skip-ci'] as String[], getGitAuthorCredsId()) +} + +void sendNotification() { + if (params.SEND_NOTIFICATION) { + mailer.sendMarkdownTestSummaryNotification('Deploy', "[${getBuildBranch()}] Drools", [env.DROOLS_CI_EMAIL_TO]) + } else { + echo 'No notification sent per configuration' + } +} + +boolean shouldDeployToRepository() { + return env.MAVEN_DEPLOY_REPOSITORY || getGitAuthor() == 'apache' +} + +boolean isCreatePr() { + return params.CREATE_PR +} + +boolean isStreamDevVersion() { + // STREAM_DEV_VERSION is not a DSL param for the 103xplus deploy job. + // A "stream dev" version is any snapshot version (contains -SNAPSHOT), + // mirroring the old Jenkinsfile.deploy's !isRelease() logic. + return getProjectVersion()?.contains('-SNAPSHOT') ?: false +} + +String getRepoName() { + return env.REPO_NAME +} + +String getGitAuthor() { + // GIT_AUTHOR can be env or param + return "${GIT_AUTHOR}" +} + +String getBuildBranch() { + return params.BUILD_BRANCH_NAME +} + +String getProjectVersion() { + return params.PROJECT_VERSION +} + +String getPRBranch() { + // PR_BRANCH_PREFIX is not a DSL param; fall back to a safe generated name. + return params.DROOLS_PR_BRANCH ?: "drools-${getProjectVersion()}_${PR_BRANCH_HASH}" +} + +String getGitAuthorCredsId() { + return env.GIT_AUTHOR_CREDS_ID +} + +String getGitAuthorPushCredsId() { + return env.GIT_AUTHOR_PUSH_CREDS_ID ?: env.GIT_AUTHOR_CREDS_ID +} + +void setDeployPropertyIfNeeded(String key, def value) { + if (value) { + deployProperties[key] = value + } +} + +MavenCommand getMavenCommand(String directory = '') { + directory = directory ?: getRepoName() + def mvnCmd = new MavenCommand(this, ['-fae', '-ntp']) + .withOptions(env.BUILD_MVN_OPTS ? [ env.BUILD_MVN_OPTS ] : []) + .inDirectory(directory) + .withProperty('full') + return mvnCmd +} + +String getGitTagName() { + return params.GIT_TAG_NAME +} diff --git a/.ci/jenkins/Jenkinsfile.103xplus.promote b/.ci/jenkins/Jenkinsfile.103xplus.promote new file mode 100644 index 00000000000..5dfb4e1d2c3 --- /dev/null +++ b/.ci/jenkins/Jenkinsfile.103xplus.promote @@ -0,0 +1,303 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// 10.3.x+ promote pipeline — mirrors Jenkinsfile.promote with 10.3+ specifics: +// - 'Create release' stage uses Nexus nexus-staging:release (same as old) +// - 'Tag final release' stage delegates to 05-tag-release.sh (tags the +// approved RC commit, not the branch tip as the old pipeline did) +// - 'Upload drools binaries and documentation' stage retained for main stream + +import org.jenkinsci.plugins.workflow.libs.Library +@Library('jenkins-pipeline-shared-libraries')_ + +import org.kie.jenkins.MavenCommand + +deployProperties = [:] +pipelineProperties = [:] + +pipeline { + agent { + docker { + image env.AGENT_DOCKER_BUILDER_IMAGE + args env.AGENT_DOCKER_BUILDER_ARGS + label util.avoidFaultyNodes() + } + } + + options { + timestamps() + timeout(time: 180, unit: 'MINUTES') + } + + environment { + DROOLS_CI_EMAIL_TO = credentials("${JENKINS_EMAIL_CREDS_ID}") + } + + stages { + stage('Initialization') { + steps { + script { + cleanWs() + + if (params.DISPLAY_NAME != '') { + currentBuild.displayName = params.DISPLAY_NAME + } + + readDeployProperties() + + assert getProjectVersion() + assert getBuildBranch() == util.getReleaseBranchFromVersion(getProjectVersion()) + // GIT_TAG must be the approved RC tag; the final tag is derived from it. + assert getRcTag() ==~ /.+-rc[0-9]+/ : "GIT_TAG must be the approved RC tag (e.g. 10.3.0-rc2), got '${getRcTag()}'" + } + } + } + + stage('Merge deploy PR') { + // NOTE: tagLatest() removed here intentionally. + // In the 103xplus flow the final release tag is created by the + // separate "Tag final release" stage below, which delegates to + // 05-tag-release.sh so the tag always points to the approved + // RC commit — NOT the current branch tip. Calling tagLatest() + // here would create a duplicate tag at the wrong commit. + steps { + script { + dir(getRepoName()) { + approveAndMergePR(getDeployPrLink()) + checkoutRepo() + } + } + } + } + + stage('Tag final release') { + // Delegate final-tag creation to 05-tag-release.sh so the release tag + // always points to the approved RC commit — NOT the current branch tip. + // This matches the documented strategy: the R commit lives under the RC + // tag only; the development branch is never disturbed. + // Runs before 'Create release' so the GitHub release attaches to this + // tag instead of creating one at the branch tip. + steps { + withCredentials([ + usernamePassword(credentialsId: env.GIT_AUTHOR_PUSH_CREDS_ID, usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN') + ]) { + script { + dir(getRepoName()) { + sh """#!/usr/bin/env bash + set -euo pipefail + git config user.email "${env.GIT_AUTHOR_EMAIL ?: '[email protected]'}" + git config user.name "${env.GIT_AUTHOR ?: 'Apache KIE Release Bot'}" + git config --local credential.helper '!f() { echo username=\$GITHUB_USER; echo password=\$GITHUB_TOKEN; }; f' + # Fetch the RC tag so the script can resolve it. + git fetch origin "refs/tags/${getRcTag()}:refs/tags/${getRcTag()}" + chmod +x script/release/05-tag-release.sh + ./script/release/05-tag-release.sh --rc-tag "${getRcTag()}" --push + """ + } + } + } + } + } + + stage('Create release') { + steps { + script { + dir(getRepoName()) { + checkoutRepo() + if (githubscm.isReleaseExist(getGitTag(), getGitAuthorCredsId())) { + githubscm.deleteRelease(getGitTag(), getGitAuthorPushCredsId()) + } + githubscm.createReleaseWithGeneratedReleaseNotes(getGitTag(), getBuildBranch(), githubscm.getPreviousTagFromVersion(getGitTag()), getGitAuthorPushCredsId()) + githubscm.updateReleaseBody(getGitTag(), getGitAuthorPushCredsId()) + } + } + } + } + + stage('Upload drools binaries and documentation') { + when { + expression { return isMainStream() } + } + steps { + script { + configFileProvider([configFile(fileId: env.MAVEN_SETTINGS_CONFIG_FILE_ID, variable: 'MAVEN_SETTINGS_FILE')]) { + getMavenCommand() + .withOptions(env.DROOLS_BUILD_MVN_OPTS ? [ env.DROOLS_BUILD_MVN_OPTS ] : []) + .withOptions(env.BUILD_MVN_OPTS_CURRENT ? [ env.BUILD_MVN_OPTS_CURRENT ] : []) + .inDirectory(getRepoName()) + .skipTests(true) + .withProperty('full') + .withSettingsXmlFile(MAVEN_SETTINGS_FILE) + .run('clean install') + } + uploadFileMgmt(getRepoName()) + } + } + } + } + post { + unsuccessful { + sendNotification() + } + cleanup { + script { + util.cleanNode() + } + } + } +} + +void sendNotification() { + if (params.SEND_NOTIFICATION) { + mailer.sendMarkdownTestSummaryNotification('Promote', "[${getBuildBranch()}] Drools", [env.DROOLS_CI_EMAIL_TO]) + } else { + echo 'No notification sent per configuration' + } +} + +////////////////////////////////////////////////////////////////////////////// +// Deployment properties +////////////////////////////////////////////////////////////////////////////// + +void readDeployProperties() { + String deployUrl = params.DEPLOY_BUILD_URL + if (deployUrl != '') { + if (!deployUrl.endsWith('/')) { + deployUrl += '/' + } + sh "wget ${deployUrl}artifact/${PROPERTIES_FILE_NAME} -O ${PROPERTIES_FILE_NAME}" + deployProperties = readProperties file: PROPERTIES_FILE_NAME + // echo all properties + echo deployProperties.collect { entry -> "${entry.key}=${entry.value}" }.join('\n') + } +} + +boolean hasDeployProperty(String key) { + return deployProperties[key] != null +} + +String getDeployProperty(String key) { + if (hasDeployProperty(key)) { + return deployProperties[key] + } + return '' +} + +String getParamOrDeployProperty(String paramKey, String deployPropertyKey) { + if (params[paramKey] != '') { + return params[paramKey] + } + return getDeployProperty(deployPropertyKey) +} + +////////////////////////////////////////////////////////////////////////////// +// Getter / Setter +////////////////////////////////////////////////////////////////////////////// + +String getRepoName() { + return env.REPO_NAME +} + +String getProjectVersion() { + return getParamOrDeployProperty('PROJECT_VERSION', 'project.version') +} + +// Approved RC tag to promote (e.g. 10.3.0-rc2). Mandatory, validated in Initialization. +String getRcTag() { + return params.GIT_TAG ?: '' +} + +// Final release tag, derived from the RC tag (e.g. 10.3.0-rc2 -> 10.3.0). +String getGitTag() { + return getRcTag().replaceFirst(/-rc[0-9]+$/, '') +} + +String getBuildBranch() { + return params.BUILD_BRANCH_NAME +} + +String getGitAuthor() { + return env.GIT_AUTHOR +} + +String getGitAuthorCredsId() { + return env.GIT_AUTHOR_CREDS_ID +} + +String getGitAuthorPushCredsId() { + return env.GIT_AUTHOR_PUSH_CREDS_ID +} + +String getDeployPrLink() { + return getDeployProperty("${getRepoName()}.pr.link") +} + +////////////////////////////////////////////////////////////////////////////// +// Git +////////////////////////////////////////////////////////////////////////////// + +void checkoutRepo() { + deleteDir() + checkout(githubscm.resolveRepository(getRepoName(), getGitAuthor(), getBuildBranch(), false, getGitAuthorCredsId())) + // need to manually checkout branch since on a detached branch after checkout command + sh "git checkout ${getBuildBranch()}" +} + +void approveAndMergePR(String prLink) { + if (prLink?.trim()) { + githubscm.approvePR(prLink, getGitAuthorPushCredsId()) + githubscm.mergePR(prLink, getGitAuthorPushCredsId()) + } +} + +void tagLatest() { + if (getGitTag()) { + githubscm.tagLocalAndRemoteRepository('origin', getGitTag(), getGitAuthorPushCredsId(), env.BUILD_TAG, true) + } +} + +MavenCommand getMavenCommand() { + mvnCmd = new MavenCommand(this, ['-fae', '-ntp']) + .withOptions(env.BUILD_MVN_OPTS ? [ env.BUILD_MVN_OPTS ] : []) + if (env.MAVEN_DEPENDENCIES_REPOSITORY) { + mvnCmd.withDependencyRepositoryInSettings('deps-repo', env.MAVEN_DEPENDENCIES_REPOSITORY) + } + return mvnCmd +} + +void uploadFileMgmt(String directory) { + if (isNotTestingBuild()) { + echo "upload binaries and docs for ${directory}" + dir(directory) { + sshagent(['drools-filemgmt']) { + sh "script/release/upload_filemgmt.sh ${getProjectVersion()}" + } + } + } else { + echo 'No uploadFileMgmt due to testing build' + } +} + +boolean isNotTestingBuild() { + return getGitAuthor() == 'apache' +} + +boolean isMainStream() { + return env.DROOLS_STREAM == 'main' +} diff --git a/.ci/jenkins/project/Jenkinsfile.103xplus.release b/.ci/jenkins/project/Jenkinsfile.103xplus.release new file mode 100644 index 00000000000..46887f161ef --- /dev/null +++ b/.ci/jenkins/project/Jenkinsfile.103xplus.release @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +pipeline { + agent { + label 'ubuntu' + } + + options { + timestamps() + timeout(time: 360, unit: 'MINUTES') + } + + parameters { + string(name: 'RELEASE_VERSION', defaultValue: '10.3.0', description: 'Exact release version, e.g. 10.3.0', trim: true) + string(name: 'TAG_NAME', defaultValue: '10.3.0-rc1', description: 'RC or Release tag name, e.g. 10.3.0-rc1', trim: true) + booleanParam(name: 'SKIP_TESTS', defaultValue: true, description: 'Skip running tests during reactor build') + booleanParam(name: 'DEPLOY', defaultValue: true, description: 'Deploy artifacts to Apache Nexus staging') + string(name: 'STAGING_URL', defaultValue: '', description: 'Custom Nexus staging URL (optional)') + booleanParam(name: 'PUSH_TAG', defaultValue: true, description: 'Push the git tag to origin') + string(name: 'EXTRA_MAVEN_OPTS', defaultValue: '', description: 'Extra Maven CLI options to pass to build.sh') + booleanParam(name: 'DRY_RUN', defaultValue: false, description: 'Dry run without making remote git or staging changes') + booleanParam(name: 'SEND_NOTIFICATION', defaultValue: false, description: 'Send a notification on CI channel when pipeline finishes') + } + + environment { + DROOLS_CI_EMAIL_TO = credentials("${JENKINS_EMAIL_CREDS_ID}") + } + + stages { + stage('Initialize & Validate') { + steps { + script { + assert params.RELEASE_VERSION : 'RELEASE_VERSION parameter is mandatory' + assert params.TAG_NAME : 'TAG_NAME parameter is mandatory' + currentBuild.displayName = "Release ${params.RELEASE_VERSION} (${params.TAG_NAME})" + } + } + } + + stage('Execute Release Scripts') { + steps { + // MAVEN_SETTINGS_CONFIG_FILE_ID is a Jenkins Config File ID, not a + // secret credential — it must be injected via configFileProvider, + // which writes it to a temp path and exposes MAVEN_SETTINGS_FILE. + // GPG_PASSPHRASE_CREDS_ID and GIT_AUTHOR_PUSH_CREDS_ID are real + // credentials and use withCredentials. + configFileProvider([configFile(fileId: env.MAVEN_SETTINGS_CONFIG_FILE_ID, variable: 'MAVEN_SETTINGS_FILE')]) { + withCredentials([ + string(credentialsId: env.GPG_PASSPHRASE_CREDS_ID, variable: 'MAVEN_GPG_PASSPHRASE'), + usernamePassword(credentialsId: env.GIT_AUTHOR_PUSH_CREDS_ID, usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN') + ]) { + script { + // Configure Git identity for the R commit and authenticate + // the raw `git push` in 02-rc-commit.sh. Same mechanism as + // githubscm.pushObject: the helper reads the token from the + // environment at push time, so it is never written to disk. + sh """#!/usr/bin/env bash + set -euo pipefail + git config user.email "${env.GIT_AUTHOR_EMAIL ?: '[email protected]'}" + git config user.name "${env.GIT_AUTHOR ?: 'Apache KIE Release Bot'}" + git config --local credential.helper '!f() { echo username=\$GITHUB_USER; echo password=\$GITHUB_TOKEN; }; f' + """ + + // Build the argv array safely — each parameter is a + // separate shell word, no string concatenation or eval. + def releaseArgs = [ + './script/release/release-all.sh', + '--version', params.RELEASE_VERSION, + '--tag', params.TAG_NAME + ] + if (params.SKIP_TESTS) releaseArgs << '--skip-tests' + if (params.DEPLOY) releaseArgs << '--deploy' + if (params.STAGING_URL?.trim()) releaseArgs += ['--staging-url', params.STAGING_URL.trim()] + if (params.PUSH_TAG) releaseArgs << '--push-tag' + if (params.EXTRA_MAVEN_OPTS?.trim()) releaseArgs += ['--maven-opts', params.EXTRA_MAVEN_OPTS.trim()] + if (params.DRY_RUN) releaseArgs << '--dry-run' + + // MAVEN_SETTINGS_FILE is injected by configFileProvider above. + // MAVEN_GPG_PASSPHRASE is already in env from withCredentials + // and is read by the GPG plugin directly (not a CLI arg). + withEnv(["MAVEN_SETTINGS=${MAVEN_SETTINGS_FILE}"]) { + sh """#!/usr/bin/env bash + set -euo pipefail + chmod +x script/release/*.sh + ${releaseArgs.collect { "'${it.replace("'", "'\\''")}'" }.join(' ')} + """ + } + } + } + } + } + } + } + + post { + always { + cleanWs() + } + success { + script { + sendNotification() + } + } + unsuccessful { + sendErrorNotification() + } + } +} + +void sendNotification() { + if (params.SEND_NOTIFICATION) { + mailer.sendMarkdownTestSummaryNotification('Release', "[${params.TAG_NAME}] Drools", [env.DROOLS_CI_EMAIL_TO]) + } else { + echo 'No notification sent per configuration' + } +} + +void sendErrorNotification() { + sendNotification() + mailer.sendMarkdownErrorNotification('Release', "[${params.TAG_NAME}] Drools", [env.DROOLS_CI_EMAIL_TO]) +} diff --git a/script/release/01-update-version.sh b/script/release/01-update-version.sh new file mode 100755 index 00000000000..1cb08f58a48 --- /dev/null +++ b/script/release/01-update-version.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +set -euo pipefail + +# Updates the Maven version across the Apache KIE repo (drools + optaplanner + +# kogito-runtimes + kogito-apps are all modules of the same root POM since the +# 10.3.x consolidation). +# +# Usage: +# ./script/release/01-update-version.sh <version> +# +# Examples: +# ./script/release/01-update-version.sh 10.3.999-SNAPSHOT # dev/stream version +# ./script/release/01-update-version.sh 10.3.0 # release version +# +# The script also updates the data-index ephemeral image tag property that lives +# inside kogito-quarkus (was a separate step in the old multi-repo flow). + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +NEW_VERSION="${1:-}" + +if [[ -z "${NEW_VERSION}" ]]; then + echo "Usage: $0 <version>" + echo " e.g. $0 10.3.999-SNAPSHOT" + echo " e.g. $0 10.3.0" + exit 1 +fi + +# Derive the stream name (e.g. 10.3.0 → 10.3.x, 10.3.999-SNAPSHOT → 10.3.x). +# Used for the data-index image tag property. +# Special case: 999-SNAPSHOT is the main-stream version; the image tag is "main". +if [[ "${NEW_VERSION}" == "999-SNAPSHOT" ]]; then + STREAM_NAME="main" +else + STREAM_NAME="$(echo "${NEW_VERSION}" | sed 's/^\([0-9]*\.[0-9]*\)\..*/\1.x/')" +fi + +# Detect the current version from the root POM so we can pass -DoldVersion, +# which skips the full reactor scan and is much faster on a large multi-module +# repo like this one. +CURRENT_VERSION="$(mvn -q help:evaluate -Dexpression=project.version -DforceStdout -f "${REPO_ROOT}/pom.xml" 2>/dev/null)" + +if [[ -z "${CURRENT_VERSION}" ]]; then + echo "ERROR: Could not determine current project version from pom.xml." + echo " Make sure Maven is available and the POM is resolvable." + exit 1 +fi + +echo "========================================" +echo "Apache KIE repo — version update" +echo "Current version : ${CURRENT_VERSION}" +echo "New version : ${NEW_VERSION}" +echo "Stream name : ${STREAM_NAME}" +echo "========================================" + +cd "${REPO_ROOT}" + +echo "" +echo "--- Updating all Maven module versions ---" +mvn versions:set \ + -DoldVersion="${CURRENT_VERSION}" \ + -DnewVersion="${NEW_VERSION}" \ + -DprocessAllModules \ + -DgenerateBackupPoms=false + +# The data-index ephemeral image tag property is controlled via a dedicated +# property inside kogito-quarkus-workflow-common-deployment. Update it to +# track the stream name so nightly image pulls resolve correctly. +DATA_INDEX_MODULE=":kogito-quarkus-workflow-common-deployment" +if mvn -q help:evaluate -Dexpression=project.artifactId -pl "${DATA_INDEX_MODULE}" 2>/dev/null; then + echo "" + echo "--- Updating data-index ephemeral image tag (${STREAM_NAME}) ---" + mvn -pl "${DATA_INDEX_MODULE}" versions:set-property \ + -Dproperty=data-index-ephemeral.image.tagVersion \ + -DnewVersion="${STREAM_NAME}" \ + -DgenerateBackupPoms=false +else + echo "(skipping data-index image tag update — module not found)" +fi + +echo "" +echo "========================================" +echo "Version update complete: ${NEW_VERSION}" +echo "========================================" +echo "" +echo "Next steps:" +echo " git diff # review changes" +echo " git add -A && git commit -m \"chore: update version to ${NEW_VERSION}\"" diff --git a/script/release/02-rc-commit.sh b/script/release/02-rc-commit.sh new file mode 100755 index 00000000000..aff6d8cecf1 --- /dev/null +++ b/script/release/02-rc-commit.sh @@ -0,0 +1,236 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +set -euo pipefail + +# Creates the "R commit" (release commit) and the corresponding RC git tag. +# +# The R commit contains only the version bump to the exact release version. +# It is made on a short-lived local release branch that is never pushed — only +# the tag is pushed. This mirrors the branch strategy from release.txt: +# +# 10.2.x branch ──── c ──── c ──── [D commit] ──── c ────> ... +# | +# (local release branch) +# └── [R commit] ──> tag: 10.3.0-rc1 +# +# Usage: +# ./script/release/02-rc-commit.sh <version> [--tag <tag-name>] [--push] [--dry-run] +# ./script/release/02-rc-commit.sh --version <release-version> --tag <tag-name> +# +# Examples: +# ./script/release/02-rc-commit.sh 10.3.0 --tag 10.3.0-rc1 +# ./script/release/02-rc-commit.sh --version 10.3.0 --tag 10.3.0-rc2 --push +# +# Options: +# <version> | --version <ver> Exact release version, e.g. 10.3.0 +# --tag | --rc-tag <tag> Git tag name, e.g. 10.3.0-rc1 (defaults to <version>-rc1) +# --rc Accepted for parity +# --push | --push-tag Push the tag to origin (default: local only) +# --dry-run Print what would happen without making any changes + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +RELEASE_VERSION="" +TAG_NAME="" +PUSH=false +DRY_RUN=false + +while [[ $# -gt 0 ]]; do + case $1 in + --version) + RELEASE_VERSION="${2:-}" + shift 2 + ;; + --tag|--rc-tag) + TAG_NAME="${2:-}" + shift 2 + ;; + --rc) + shift + ;; + --push|--push-tag) + PUSH=true + shift + ;; + --dry-run) + DRY_RUN=true + shift + ;; + *) + if [[ -z "${RELEASE_VERSION}" && ! "$1" =~ ^- ]]; then + RELEASE_VERSION="$1" + shift + else + echo "Unknown option: $1" + echo "Usage: $0 [<version> | --version <version>] [--tag <tag>] [--push] [--dry-run]" + exit 1 + fi + ;; + esac +done + +if [[ -z "${RELEASE_VERSION}" ]]; then + echo "ERROR: Version is required." + echo "Usage: $0 10.3.0 [--tag 10.3.0-rc1] [--push] [--dry-run]" + exit 1 +fi + +if [[ -z "${TAG_NAME}" ]]; then + TAG_NAME="${RELEASE_VERSION}-rc1" +fi + +# Derive the expected development branch name (e.g. 10.3.0 → 10.3.x). +STREAM_BRANCH="$(echo "${RELEASE_VERSION}" | sed 's/^\([0-9]*\.[0-9]*\)\..*/\1.x/')" +RELEASE_BRANCH="release/${TAG_NAME}" + +cd "${REPO_ROOT}" + +# Capture original ref: branch name when attached, commit SHA when detached +# (Jenkins SCM checkouts are typically detached; `git rev-parse --abbrev-ref HEAD` +# returns "HEAD" in that case, which cannot be restored via `git checkout HEAD` +# because the branch is still pointing there. We need the actual commit SHA.) +if git symbolic-ref --quiet HEAD &>/dev/null; then + CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" +else + CURRENT_BRANCH="$(git rev-parse HEAD)" +fi + +echo "========================================" +echo "Apache KIE repo — RC commit + tag" +echo "Release version : ${RELEASE_VERSION}" +echo "Tag : ${TAG_NAME}" +echo "Release branch : ${RELEASE_BRANCH} (local only)" +echo "Push tag : ${PUSH}" +echo "Dry run : ${DRY_RUN}" +echo "Current branch : ${CURRENT_BRANCH}" +echo "========================================" + +# Warn if we're not on the expected stream branch (not a fatal error — someone +# may legitimately be on a different commit). +if [[ "${CURRENT_BRANCH}" != "${STREAM_BRANCH}" ]]; then + echo "" + echo "WARNING: Current branch '${CURRENT_BRANCH}' is not the expected stream" + echo " branch '${STREAM_BRANCH}'. Proceed with caution." + echo "" +fi + +# Check there are no uncommitted changes (staged, unstaged, or untracked) before +# we do anything. Untracked files matter here because git add -A below will +# include them in the R commit. +if ! git diff --quiet || ! git diff --cached --quiet; then + echo "ERROR: There are uncommitted changes in the working tree." + echo " Commit or stash them before running this script." + exit 1 +fi +if [[ -n "$(git ls-files --others --exclude-standard)" ]]; then + echo "ERROR: There are untracked files in the working tree:" + git ls-files --others --exclude-standard | sed 's/^/ /' + echo " Remove or .gitignore them before running this script." + exit 1 +fi + +if [[ "${DRY_RUN}" == "true" ]]; then + echo "" + echo "[DRY RUN] Would execute:" + echo " git checkout -b ${RELEASE_BRANCH}" + echo " ./script/release/01-update-version.sh ${RELEASE_VERSION}" + echo " git add -A" + echo " git commit -m \"chore: release ${RELEASE_VERSION}\"" + echo " git tag -a ${TAG_NAME} -m \"Release ${TAG_NAME}\"" + if [[ "${PUSH}" == "true" ]]; then + echo " git push origin ${TAG_NAME}" + fi + echo " git checkout ${CURRENT_BRANCH}" + echo " git branch -D ${RELEASE_BRANCH}" + exit 0 +fi + +# ── Guard: stale release branch from a previous interrupted run ────────────── +if git rev-parse --verify "${RELEASE_BRANCH}" &>/dev/null; then + echo "" + echo "ERROR: Branch '${RELEASE_BRANCH}' already exists from a previous" + echo " (possibly interrupted) run." + echo "" + echo "To clean up and start fresh:" + echo " git checkout ${CURRENT_BRANCH}" + echo " git branch -D ${RELEASE_BRANCH}" + if git rev-parse --verify "${TAG_NAME}" &>/dev/null; then + echo " git tag -d ${TAG_NAME}" + fi + exit 1 +fi + +# ── Guard: tag already exists ───────────────────────────────────────────────── +if git rev-parse --verify "${TAG_NAME}" &>/dev/null; then + echo "" + echo "ERROR: Tag '${TAG_NAME}' already exists." + echo " Delete it first if you want to recreate it:" + echo " git tag -d ${TAG_NAME}" + exit 1 +fi + +echo "" +echo "--- Creating local release branch: ${RELEASE_BRANCH} ---" +git checkout -b "${RELEASE_BRANCH}" + +echo "" +echo "--- Updating version to ${RELEASE_VERSION} ---" +"${SCRIPT_DIR}/01-update-version.sh" "${RELEASE_VERSION}" + +echo "" +echo "--- Committing R commit ---" +git add -A +git commit -m "chore: release ${RELEASE_VERSION}" + +echo "" +echo "--- Tagging ${TAG_NAME} ---" +git tag -a "${TAG_NAME}" -m "Release ${TAG_NAME}" + +if [[ "${PUSH}" == "true" ]]; then + echo "" + echo "--- Pushing tag ${TAG_NAME} to origin ---" + git push origin "${TAG_NAME}" +fi + +echo "" +echo "--- Returning to ${CURRENT_BRANCH} ---" +git checkout "${CURRENT_BRANCH}" + +echo "" +echo "--- Deleting local release branch ${RELEASE_BRANCH} ---" +git branch -D "${RELEASE_BRANCH}" + +echo "" +echo "========================================" +echo "RC commit + tag complete." +echo "" +echo "Tag '${TAG_NAME}' points to the R commit (version ${RELEASE_VERSION})." +if [[ "${PUSH}" == "false" ]]; then + echo "" + echo "Tag is LOCAL only. To push when ready:" + echo " git push origin ${TAG_NAME}" +fi +echo "" +echo "Next steps:" +echo " ./script/release/03-build.sh --skip-tests" +echo " ./script/release/04-deploy-to-staging.sh --tag ${TAG_NAME}" +echo "========================================" diff --git a/script/release/03-build.sh b/script/release/03-build.sh new file mode 100755 index 00000000000..f9507e2ab34 --- /dev/null +++ b/script/release/03-build.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +set -euo pipefail + +# Builds the Apache KIE repo for a release (skipping tests by default). +# +# Because drools, optaplanner, kogito-runtimes, and kogito-apps now all live in +# the same reactor, a single `mvn clean install` is enough. +# +# Usage: +# ./script/release/03-build.sh [--skip-tests] [--maven-opts <opts>] +# +# Flags: +# --skip-tests Skip all tests (default: tests are run) +# --maven-opts <opts> Extra Maven options appended to the command. Split on +# whitespace only; shell-style quoting is NOT honoured, +# so individual options must not contain spaces. +# +# Examples: +# ./script/release/03-build.sh --skip-tests +# ./script/release/03-build.sh --skip-tests --maven-opts "-T 4" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +SKIP_TESTS=false +# Extra Maven opts stored as an array and expanded quoted, so no glob expansion, +# eval, or command substitution happens. Options are split on whitespace only. +EXTRA_MVN_OPTS=() + +while [[ $# -gt 0 ]]; do + case $1 in + --skip-tests) + SKIP_TESTS=true + shift + ;; + --maven-opts) + # Accept a single-argument "extra opts" string and split it on + # whitespace into array elements using read. Quotes are not parsed. + IFS=' ' read -r -a _extra <<< "${2:-}" + EXTRA_MVN_OPTS+=("${_extra[@]}") + shift 2 + ;; + *) + echo "Unknown option: $1" + echo "Usage: $0 [--skip-tests] [--maven-opts <opts>]" + exit 1 + ;; + esac +done + +cd "${REPO_ROOT}" + +# ── Assemble Maven flags as a Bash array ────────────────────────────────────── + +MVN_FLAGS=("-Dfull") + +if [[ "${SKIP_TESTS}" == "true" ]]; then + MVN_FLAGS+=("-DskipTests") +fi + +if [[ "${#EXTRA_MVN_OPTS[@]}" -gt 0 ]]; then + MVN_FLAGS+=("${EXTRA_MVN_OPTS[@]}") +fi + +# ── Build ────────────────────────────────────────────────────────────────────── + +echo "========================================" +echo "Apache KIE repo — build" +echo "Skip tests : ${SKIP_TESTS}" +echo "Maven flags : ${MVN_FLAGS[*]}" +echo "========================================" +echo "" + +mvn clean install "${MVN_FLAGS[@]}" + +echo "" +echo "========================================" +echo "Build complete." +echo "" +echo "JARs installed to local Maven repository (~/.m2/repository)." +echo "========================================" diff --git a/script/release/04-deploy-to-staging.sh b/script/release/04-deploy-to-staging.sh new file mode 100755 index 00000000000..ea9f4db3563 --- /dev/null +++ b/script/release/04-deploy-to-staging.sh @@ -0,0 +1,179 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +set -euo pipefail + +# Deploys the locally-built JARs (already in ~/.m2) to a Nexus staging +# repository for Apache release voting. +# +# By default this script runs in DRY RUN mode and will NOT push anything +# remotely. Pass --deploy to actually upload to Nexus. +# +# Usage: +# ./script/release/04-deploy-to-staging.sh --tag <rc-tag> [--deploy] [--staging-url <url>] +# +# Examples: +# # dry run (safe — prints the Maven command that would be run) +# ./script/release/04-deploy-to-staging.sh --tag 10.3.0-rc1 +# +# # actually deploy to Apache Nexus staging +# ./script/release/04-deploy-to-staging.sh --tag 10.3.0-rc1 --deploy +# +# # deploy to a custom staging URL +# ./script/release/04-deploy-to-staging.sh --tag 10.3.0-rc1 --deploy \ +# --staging-url https://repository.apache.org/service/local/staging/deploy/maven2 +# +# Environment variables consumed when --deploy is active: +# MAVEN_SETTINGS Path to a settings.xml with Nexus credentials (required). +# The settings file must define a server with id "apache.releases.https" +# (or the id set via --server-id) carrying the deployer credentials. +# MAVEN_GPG_PASSPHRASE GPG passphrase for signing (required for Apache releases). +# Passed to the GPG agent via the gpg-agent pinentry loopback +# mechanism; NOT exposed on the Maven command line. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +TAG_NAME="" +DEPLOY=false +STAGING_URL="https://repository.apache.org/service/local/staging/deploy/maven2" +SERVER_ID="apache.releases.https" + +while [[ $# -gt 0 ]]; do + case $1 in + --tag) + TAG_NAME="${2:-}" + shift 2 + ;; + --deploy) + DEPLOY=true + shift + ;; + --staging-url) + STAGING_URL="${2:-}" + shift 2 + ;; + --server-id) + SERVER_ID="${2:-}" + shift 2 + ;; + *) + echo "Unknown option: $1" + echo "Usage: $0 --tag <rc-tag> [--deploy] [--staging-url <url>] [--server-id <id>]" + exit 1 + ;; + esac +done + +if [[ -z "${TAG_NAME}" ]]; then + echo "ERROR: --tag is required." + echo "Usage: $0 --tag 10.3.0-rc1 [--deploy]" + exit 1 +fi + +cd "${REPO_ROOT}" + +# ── Resolve the version from the tag name ──────────────────────────────────── +# Tag format: <version>-rc<N> (e.g. 10.3.0-rc1) +RELEASE_VERSION="$(echo "${TAG_NAME}" | sed 's/-rc[0-9]*$//')" + +echo "========================================" +echo "Apache KIE repo — deploy to staging" +echo "Tag : ${TAG_NAME}" +echo "Release version : ${RELEASE_VERSION}" +echo "Staging URL : ${STAGING_URL}" +echo "Server ID : ${SERVER_ID}" +echo "Deploy (real) : ${DEPLOY}" +echo "========================================" + +# ── Verify we are on / can resolve the RC tag ──────────────────────────────── +if ! git rev-parse "${TAG_NAME}" &>/dev/null; then + echo "" + echo "ERROR: Git tag '${TAG_NAME}' not found in this repository." + echo " Run rc-commit.sh first, or check out the tag manually." + exit 1 +fi + +# ── Assemble Maven flags ───────────────────────────────────────────────────── + +DEPLOY_MVN_FLAGS=( + "-DskipTests" + "-Dfull" + # Activate the apache-release profile so the source/javadoc and GPG-signing + # goals defined there are executed (required for Apache releases). + "-Papache-release" + "-DaltDeploymentRepository=${SERVER_ID}::default::${STAGING_URL}" +) + +if [[ -n "${MAVEN_SETTINGS:-}" ]]; then + DEPLOY_MVN_FLAGS+=("-s" "${MAVEN_SETTINGS}") +fi + +# NOTE: MAVEN_GPG_PASSPHRASE is intentionally NOT added to the Maven command +# line — exposing it there would print it in captured console logs. +# The standard GPG Maven plugin (version 3+) reads the passphrase from the +# environment variable MAVEN_GPG_PASSPHRASE via pinentry-loopback automatically. +# For older plugin versions, configure loopback in ~/.gnupg/gpg-agent.conf: +# allow-loopback-pinentry +# and pass: -Dgpg.useagent=false -Dgpg.loopback=true in MAVEN_SETTINGS. + +echo "" +if [[ "${DEPLOY}" == "false" ]]; then + echo "[DRY RUN] Would run from tag ${TAG_NAME}:" + echo "" + echo " git checkout ${TAG_NAME}" + echo " mvn deploy ${DEPLOY_MVN_FLAGS[*]}" + echo " git checkout -" + echo "" + echo "Pass --deploy to actually upload artifacts." + exit 0 +fi + +# ── Checkout the exact tag commit before deploying ─────────────────────────── +# Save the original ref as the commit SHA when in detached HEAD state (e.g., +# Jenkins SCM checkout). `git rev-parse --abbrev-ref HEAD` returns "HEAD" when +# detached, which cannot be used to restore the checkout afterwards. +if git symbolic-ref --quiet HEAD &>/dev/null; then + ORIG_REF="$(git rev-parse --abbrev-ref HEAD)" +else + ORIG_REF="$(git rev-parse HEAD)" +fi + +echo "--- Checking out tag ${TAG_NAME} ---" +git checkout "${TAG_NAME}" + +echo "" +echo "--- Deploying to Nexus staging (${STAGING_URL}) ---" +mvn deploy "${DEPLOY_MVN_FLAGS[@]}" + +echo "" +echo "--- Returning to ${ORIG_REF} ---" +git checkout "${ORIG_REF}" + +echo "" +echo "========================================" +echo "Deploy to staging complete." +echo "" +echo "Next steps:" +echo " 1. Log in to https://repository.apache.org and close the staging repo." +echo " 2. Share the staging repo URL with the release vote thread." +echo " 3. Once the vote passes, run:" +echo " ./script/release/05-tag-release.sh --rc-tag ${TAG_NAME}" +echo "========================================" diff --git a/script/release/05-tag-release.sh b/script/release/05-tag-release.sh new file mode 100755 index 00000000000..cc0debdbec6 --- /dev/null +++ b/script/release/05-tag-release.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +set -euo pipefail + +# Promotes an approved RC tag to the final release tag (AUTOMATION F). +# +# After the community vote passes on an RC (e.g. 10.3.0-rc2), this script +# creates the final release tag pointing to the same commit: +# +# git tag 10.3.0 10.3.0-rc2 +# +# By default the tag is created locally only. Pass --push to publish it. +# +# Usage: +# ./script/release/05-tag-release.sh --rc-tag <rc-tag> [--push] [--dry-run] +# +# Examples: +# ./script/release/05-tag-release.sh --rc-tag 10.3.0-rc2 +# ./script/release/05-tag-release.sh --rc-tag 10.3.0-rc2 --push + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +RC_TAG="" +PUSH=false +DRY_RUN=false + +while [[ $# -gt 0 ]]; do + case $1 in + --rc-tag) + RC_TAG="${2:-}" + shift 2 + ;; + --push) + PUSH=true + shift + ;; + --dry-run) + DRY_RUN=true + shift + ;; + *) + echo "Unknown option: $1" + echo "Usage: $0 --rc-tag <rc-tag> [--push] [--dry-run]" + exit 1 + ;; + esac +done + +if [[ -z "${RC_TAG}" ]]; then + echo "ERROR: --rc-tag is required." + echo "Usage: $0 --rc-tag 10.3.0-rc2 [--push]" + exit 1 +fi + +if [[ ! "${RC_TAG}" =~ -rc[0-9]+$ ]]; then + echo "ERROR: --rc-tag must be an RC tag ending in -rc<N> (e.g. 10.3.0-rc2), got '${RC_TAG}'." + exit 1 +fi + +# Derive the final release tag (strip -rcN suffix). +RELEASE_TAG="$(echo "${RC_TAG}" | sed 's/-rc[0-9]*$//')" + +cd "${REPO_ROOT}" + +# Verify the RC tag exists. +if ! git rev-parse "${RC_TAG}" &>/dev/null; then + echo "ERROR: RC tag '${RC_TAG}' not found." + echo " Make sure you have fetched the tag: git fetch origin ${RC_TAG}" + exit 1 +fi + +RC_COMMIT="$(git rev-parse "${RC_TAG}")" + +echo "========================================" +echo "Apache KIE repo — tag release" +echo "RC tag : ${RC_TAG} (${RC_COMMIT:0:12})" +echo "Release tag : ${RELEASE_TAG}" +echo "Push : ${PUSH}" +echo "Dry run : ${DRY_RUN}" +echo "========================================" + +if [[ "${DRY_RUN}" == "true" ]]; then + echo "" + echo "[DRY RUN] Would execute:" + echo " git tag -a ${RELEASE_TAG} ${RC_TAG} -m \"Release ${RELEASE_TAG}\"" + if [[ "${PUSH}" == "true" ]]; then + echo " git push origin ${RELEASE_TAG}" + fi + exit 0 +fi + +echo "" +echo "--- Creating release tag ${RELEASE_TAG} at ${RC_TAG} ---" +git tag -a "${RELEASE_TAG}" "${RC_TAG}" -m "Release ${RELEASE_TAG}" + +if [[ "${PUSH}" == "true" ]]; then + echo "" + echo "--- Pushing release tag ${RELEASE_TAG} to origin ---" + git push origin "${RELEASE_TAG}" +fi + +echo "" +echo "========================================" +echo "Release tag complete: ${RELEASE_TAG}" +if [[ "${PUSH}" == "false" ]]; then + echo "" + echo "Tag is LOCAL only. To push when ready:" + echo " git push origin ${RELEASE_TAG}" +fi +echo "" +echo "Next steps (AUTOMATION G — promote artifacts):" +echo " 1. Promote the Nexus staging repository to release." +echo " 2. Run svn mv to move dist artifacts from dev/ to release/." +echo " 3. Announce the release on [email protected]." +echo "========================================" diff --git a/script/release/README.md b/script/release/README.md new file mode 100644 index 00000000000..e3e71ce6e00 --- /dev/null +++ b/script/release/README.md @@ -0,0 +1,270 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + --> + +# Release scripts + +Local-first release tooling for the Apache KIE repository. +Since the 10.3.x consolidation, **drools, optaplanner, kogito-runtimes, and +kogito-apps are all modules of the same root POM** — so the release process is +now a single-repo, single-command workflow. + +These scripts implement the "local-first" approach: every step runs on your +machine without needing Jenkins. Jenkins can still call the same scripts, but +you no longer depend on it for the mechanics. + +--- + +## Terminology & Lifecycle Stages + +To avoid confusion between different release phases: + +| Term | Stage | Meaning & Scope | +|---|---|---| +| **`update-version`** | Automation A / D | Updates Maven POM versions across all modules in the reactor to stream snapshots (`10.3.999-SNAPSHOT`) or exact release versions (`10.3.0`). | +| **`rc-commit` / `rc-tag`** | Automation D.1 | Creates a temporary branch, bumps to the release version, creates the R-commit, tags the Git RC (`10.3.0-rc1`), and cleans up the temporary branch. | +| **`build`** | Automation D.2 | Compiles the full Maven reactor and installs artifacts to the local `~/.m2/repository`. | +| **`deploy` / `staging`** | Automation D.3 | GPG signs artifacts and deploys them to the Apache Nexus Staging repository for release vote inspection. | +| **`tag-release`** | Automation F | Promotes an approved RC tag (e.g., `10.3.0-rc2`) to the final release tag (`10.3.0`) once the release vote passes. | +| **`promote` / `publish`** | Automation G | Releases the staging repository in Nexus to Maven Central and publishes release distribution files. | + +--- + +## Numbered Scripts + +Scripts are numbered to clearly communicate their intended execution order: + +| Number / Script | Automation Stage | Purpose | +|---|---|---| +| **[`01-update-version.sh`](#01-update-versionsh)** *(alias: `update-version.sh`)* | Automation A / D | Update Maven POM versions + data-index image tag property | +| **[`02-rc-commit.sh`](#02-rc-commitsh)** *(alias: `rc-commit.sh`)* | Automation D.1 | Create temporary branch, R commit, and RC git tag | +| **[`03-build.sh`](#03-buildsh)** *(alias: `build.sh`)* | Automation D.2 | Build the full reactor to local `~/.m2/repository` | +| **[`04-deploy-to-staging.sh`](#04-deploy-to-stagingsh)** *(alias: `deploy-to-staging.sh`)* | Automation D.3 | Deploy signed JARs to Apache Nexus staging repository | +| **[`05-tag-release.sh`](#05-tag-releasesh)** *(alias: `tag-release.sh`)* | Automation F | Promote an approved RC tag → final release tag | +| **[`release-all.sh`](#release-allsh)** | Orchestrator (D) | Orchestrate steps 02 → 03 → 04 in one single command | + +Make the scripts executable once: + +```bash +chmod +x script/release/*.sh +``` + +--- + +## Quick start — full RC candidate locally + +```bash +# 1. Be on the development stream branch +git checkout 10.3.x + +# 2. Run the full RC flow (no remote side-effects — dry run first) +./script/release/release-all.sh \ + --version 10.3.0 --tag 10.3.0-rc1 \ + --skip-tests \ + --dry-run + +# 3. If the dry run looks right, remove --dry-run +./script/release/release-all.sh \ + --version 10.3.0 --tag 10.3.0-rc1 \ + --skip-tests +``` + +This leaves the RC tag locally (`git tag -l 10.3.0-rc1`). Nothing has been +pushed or published. When you are ready to share the RC: + +```bash +# Push the RC tag and deploy to Apache Nexus staging +./script/release/release-all.sh \ + --version 10.3.0 --tag 10.3.0-rc1 \ + --skip-tests --push-tag --deploy +``` + +--- + +## Scripts in detail + +### `01-update-version.sh` + +Updates every Maven module's `<version>` in the reactor and the +`data-index-ephemeral.image.tagVersion` property in +`kogito-quarkus-workflow-common-deployment`. + +```bash +# Switch to a dev (SNAPSHOT) version — D commit (Automation A) +./script/release/01-update-version.sh 10.3.999-SNAPSHOT + +# Switch to the exact release version — R commit (usually automated by 02-rc-commit.sh) +./script/release/01-update-version.sh 10.3.0 +``` + +> **Stream vs release version.** Development branches keep +> `major.minor.999-SNAPSHOT` as their version. The version is only set to +> the exact release version (`10.3.0`) in the short-lived R commit created by +> `02-rc-commit.sh`. + +--- + +### `02-rc-commit.sh` + +Creates the **R commit** (version bump to the exact release version) on a +short-lived *local-only* release branch, then tags that commit. The release +branch is deleted afterwards — only the tag survives. + +```bash +# Local tag only (safe — nothing is pushed) +./script/release/02-rc-commit.sh --version 10.3.0 --tag 10.3.0-rc1 + +# Push the tag to origin when you're ready +./script/release/02-rc-commit.sh --version 10.3.0 --tag 10.3.0-rc1 --push + +# See what would happen without making changes +./script/release/02-rc-commit.sh --version 10.3.0 --tag 10.3.0-rc1 --dry-run +``` + +The script refuses to run if there are any uncommitted changes in the working +tree. + +--- + +### `03-build.sh` + +Builds the full reactor. JARs are installed to your local `~/.m2` repository. + +```bash +# Standard release build (tests skipped) +./script/release/03-build.sh --skip-tests + +# With extra Maven options (e.g. parallelism) +./script/release/03-build.sh --skip-tests --maven-opts "-T 4" +``` + +--- + +### `04-deploy-to-staging.sh` + +Deploys the locally-built JARs to Apache Nexus staging. **Dry run by +default** — the `--deploy` flag is required to actually upload anything. + +```bash +# Dry run — prints the Maven command without executing it +./script/release/04-deploy-to-staging.sh --tag 10.3.0-rc1 + +# Actually deploy to Apache Nexus staging +MAVEN_SETTINGS=/path/to/settings.xml \ +MAVEN_GPG_PASSPHRASE=secret \ +./script/release/04-deploy-to-staging.sh --tag 10.3.0-rc1 --deploy +``` + +After deployment, log in to <https://repository.apache.org>, close the +staging repository, and share the URL with the release vote thread on +`[email protected]`. + +**Required environment variables when `--deploy` is active:** + +| Variable | Description | +|---|---| +| `MAVEN_SETTINGS` | Path to a `settings.xml` with Nexus deployer credentials | +| `MAVEN_GPG_PASSPHRASE` | GPG key passphrase for signing release artifacts | + +--- + +### `05-tag-release.sh` + +Promotes a winning RC tag to the final release tag (AUTOMATION F). + +```bash +# Local tag only +./script/release/05-tag-release.sh --rc-tag 10.3.0-rc2 + +# Push the release tag to origin +./script/release/05-tag-release.sh --rc-tag 10.3.0-rc2 --push +``` + +--- + +### `release-all.sh` + +Orchestrates the full RC cycle in one command. + +```bash +./script/release/release-all.sh \ + --version 10.3.0 \ + --tag 10.3.0-rc1 \ + [--skip-tests] \ + [--maven-opts "<opts>"] \ + [--deploy] \ + [--staging-url <url>] \ + [--push-tag] \ + [--dry-run] +``` + +Steps executed in order: + +1. `02-rc-commit.sh` — R commit + tag +2. `03-build.sh` — full build at the tag commit +3. `04-deploy-to-staging.sh` — Nexus staging upload *(only if `--deploy`)* + +--- + +## Development stream vs release version + +| Situation | Version | Command | +|---|---|---| +| Daily development on `main` | `999-SNAPSHOT` | `update-version.sh 999-SNAPSHOT` | +| Daily development on `10.3.x` | `10.3.999-SNAPSHOT` | `update-version.sh 10.3.999-SNAPSHOT` | +| Creating RC / release | `10.3.0` | Handled automatically by `rc-commit.sh` | + +--- + +## Jenkins integration + +Jenkins calls these exact same scripts. The only difference is that Jenkins +supplies credentials via environment variables: + +```groovy +withCredentials([ + file(credentialsId: 'kie-release-settings', variable: 'MAVEN_SETTINGS'), + string(credentialsId: 'GPG_KEY_PASSPHRASE', variable: 'MAVEN_GPG_PASSPHRASE') +]) { + sh """ + ./script/release/release-all.sh \ + --version \${RELEASE_VERSION} \ + --tag \${GIT_TAG_NAME} \ + --skip-tests --deploy --push-tag + """ +} +``` + +--- + +## Relationship to the 10.2.x multi-repo flow + +In 10.2.x the release involved five separate repositories +(drools → optaplanner → kogito-runtimes → kogito-apps → kie-tools), each with +its own Jenkins job. Now that the first four repos are merged, the steps map +as follows: + +| Old step | New equivalent | +|---|---| +| Trigger Drools release job | `./script/release/release-all.sh` | +| Trigger OptaPlanner release job | *(same command — same reactor)* | +| Trigger Kogito Runtimes release job | *(same command — same reactor)* | +| Trigger Kogito Apps release job | *(same command — same reactor)* | +| `git tag 10.x.0 10.x.0-rcN` in all repos | `./script/release/tag-release.sh` | + +`kie-tools` remains a separate repository and is unaffected by these scripts. diff --git a/script/release/release-all.sh b/script/release/release-all.sh new file mode 100755 index 00000000000..8464c4579aa --- /dev/null +++ b/script/release/release-all.sh @@ -0,0 +1,201 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +set -euo pipefail + +# Master orchestrator for the local-first release workflow. +# +# Runs the individual release scripts in the correct order for a full release +# candidate cycle (02-rc-commit.sh -> 03-build.sh -> 04-deploy-to-staging.sh). +# Each step can also be run independently — see script/release/README.md. +# +# Usage: +# ./script/release/release-all.sh <version> [--tag <tag>] [OPTIONS] +# ./script/release/release-all.sh --version <version> [--tag <tag>] [OPTIONS] +# +# Required (one of): +# <version> or --version <ver> Exact release version, e.g. 10.3.0 +# +# Optional tags: +# --tag <tag> | --rc-tag <tag> RC tag name, e.g. 10.3.0-rc1 (defaults to <version>-rc1) +# --rc Explicit flag indicating RC mode +# +# Optional build flags: +# --skip-tests | --skip-build Skip tests during the build +# --maven-opts <opts> Extra Maven options forwarded to build.sh +# +# Optional deploy/publish flags: +# --deploy | --publish Deploy JARs to Nexus staging after the build +# --staging-url <url> Nexus staging URL (default: Apache Nexus) +# +# Optional git flags: +# --push | --push-tag Push the RC tag to origin after creating it +# +# Other: +# --dry-run Print what would happen without executing anything +# +# Examples: +# # Positional or flag version +# ./script/release/release-all.sh 10.3.0 --rc --skip-tests +# ./script/release/release-all.sh --version 10.3.0 --tag 10.3.0-rc1 --skip-tests +# +# # Full RC with deploy to Apache Nexus staging and push tag to origin +# ./script/release/release-all.sh 10.3.0 --tag 10.3.0-rc1 --skip-tests --deploy --push-tag + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +RELEASE_VERSION="" +TAG_NAME="" +SKIP_TESTS=false +EXTRA_MVN_OPTS="" +DEPLOY=false +STAGING_URL="" +PUSH_TAG=false +DRY_RUN=false + +while [[ $# -gt 0 ]]; do + case $1 in + --version) RELEASE_VERSION="${2:-}"; shift 2 ;; + --tag|--rc-tag) TAG_NAME="${2:-}"; shift 2 ;; + --rc) shift ;; # Accepted for parity with tools repo + --skip-tests|--skip-build) SKIP_TESTS=true; shift ;; + --maven-opts) EXTRA_MVN_OPTS="${2:-}"; shift 2 ;; + --deploy|--publish) DEPLOY=true; shift ;; + --staging-url) STAGING_URL="${2:-}"; shift 2 ;; + --push|--push-tag) PUSH_TAG=true; shift ;; + --dry-run) DRY_RUN=true; shift ;; + *) + if [[ -z "${RELEASE_VERSION}" && ! "$1" =~ ^- ]]; then + RELEASE_VERSION="$1" + shift + else + echo "Unknown option: $1" + echo "Usage: $0 [<version> | --version <version>] [--tag <tag>] [OPTIONS]" + exit 1 + fi + ;; + esac +done + +if [[ -z "${RELEASE_VERSION}" ]]; then + echo "ERROR: Version is required." + echo "Usage: $0 10.3.0 [--tag 10.3.0-rc1] [OPTIONS]" + exit 1 +fi + +if [[ -z "${TAG_NAME}" ]]; then + TAG_NAME="${RELEASE_VERSION}-rc1" +fi + +cd "${REPO_ROOT}" + +echo "" +echo "==========================================" +echo "Apache KIE repo — release-all" +echo "Version : ${RELEASE_VERSION}" +echo "RC tag : ${TAG_NAME}" +echo "Skip tests : ${SKIP_TESTS}" +echo "Deploy staging : ${DEPLOY}" +echo "Push tag : ${PUSH_TAG}" +echo "Dry run : ${DRY_RUN}" +echo "==========================================" +echo "" + +# ── Helper ─────────────────────────────────────────────────────────────────── + +run_step() { + local step_name="$1" + shift + echo "" + echo "────────────────────────────────────────" + echo "STEP: ${step_name}" + echo "────────────────────────────────────────" + if [[ "${DRY_RUN}" == "true" ]]; then + echo "[DRY RUN] $*" + else + "$@" + fi + echo "✅ ${step_name} — done" +} + +# ── STEP 1 (Automation D.1): R commit + RC tag ─────────────────────────────── + +RC_COMMIT_SCRIPT="${SCRIPT_DIR}/02-rc-commit.sh" + +RC_COMMIT_ARGS=("${RC_COMMIT_SCRIPT}" "--version" "${RELEASE_VERSION}" "--tag" "${TAG_NAME}") +[[ "${PUSH_TAG}" == "true" ]] && RC_COMMIT_ARGS+=("--push") +[[ "${DRY_RUN}" == "true" ]] && RC_COMMIT_ARGS+=("--dry-run") + +run_step "R commit + RC tag (02-rc-commit.sh)" "${RC_COMMIT_ARGS[@]}" + +# ── STEP 2 (Automation D.2): Build ─────────────────────────────────────────── + +BUILD_SCRIPT="${SCRIPT_DIR}/03-build.sh" + +BUILD_ARGS=("${BUILD_SCRIPT}") +[[ "${SKIP_TESTS}" == "true" ]] && BUILD_ARGS+=("--skip-tests") +[[ -n "${EXTRA_MVN_OPTS}" ]] && BUILD_ARGS+=("--maven-opts" "${EXTRA_MVN_OPTS}") + +# The build must run at the RC tag commit. 02-rc-commit.sh leaves HEAD on the +# development branch, so we check out the tag, build, then return. +if [[ "${DRY_RUN}" == "true" ]]; then + run_step "Build @ ${TAG_NAME} (03-build.sh)" echo "[DRY RUN] git checkout ${TAG_NAME} && ${BUILD_ARGS[*]} && git checkout -" +else + echo "" + echo "────────────────────────────────────────" + echo "STEP: Build @ ${TAG_NAME} (03-build.sh)" + echo "────────────────────────────────────────" + git checkout "${TAG_NAME}" + "${BUILD_ARGS[@]}" + git checkout - + echo "✅ Build — done" +fi + +# ── STEP 3 (Automation D.3): Deploy to Nexus staging (optional) ────────────── + +if [[ "${DEPLOY}" == "true" ]]; then + DEPLOY_SCRIPT="${SCRIPT_DIR}/04-deploy-to-staging.sh" + + DEPLOY_ARGS=("${DEPLOY_SCRIPT}" "--tag" "${TAG_NAME}" "--deploy") + [[ -n "${STAGING_URL}" ]] && DEPLOY_ARGS+=("--staging-url" "${STAGING_URL}") + run_step "Deploy to Nexus staging (04-deploy-to-staging.sh)" "${DEPLOY_ARGS[@]}" +fi + +# ── Summary ─────────────────────────────────────────────────────────────────── + +echo "" +echo "==========================================" +echo "release-all complete ✅" +echo "" +echo " RC tag : ${TAG_NAME}" +echo " Version : ${RELEASE_VERSION}" +echo "" +if [[ "${DEPLOY}" == "true" ]]; then + echo "Artifacts are in Nexus staging." + echo "Close the staging repo, then start the vote on [email protected]." +else + echo "Artifacts are in your local ~/.m2 repository only." + echo "Run 04-deploy-to-staging.sh --tag ${TAG_NAME} --deploy when ready." +fi +echo "" +echo "When the vote passes, promote the RC to a final release tag:" +echo " ./script/release/05-tag-release.sh --rc-tag ${TAG_NAME} --push" +echo "==========================================" diff --git a/script/release/upload_filemgmt.sh b/script/release/upload_filemgmt.sh new file mode 100755 index 00000000000..50fe339e254 --- /dev/null +++ b/script/release/upload_filemgmt.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +set -euo pipefail + +# Uploads Drools release binaries and documentation to the Apache file +# management server (people.apache.org) via SSH/rsync. +# +# Called by Jenkinsfile.103xplus.promote (and the original Jenkinsfile.promote) +# inside an sshagent('drools-filemgmt') block, so SSH authentication is already +# in the agent — no credential parameters needed here. +# +# Usage: +# ./script/release/upload_filemgmt.sh <release-version> +# +# Examples: +# ./script/release/upload_filemgmt.sh 10.3.0 +# +# Environment variables (all have defaults): +# FILEMGMT_HOST Remote host (default: people.apache.org) +# FILEMGMT_BASE_PATH Remote base path (default: /home/drools/filemgmt) +# DRY_RUN Set to 'true' to print commands without running them + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +RELEASE_VERSION="${1:-}" + +if [[ -z "${RELEASE_VERSION}" ]]; then + echo "ERROR: release version argument is required." + echo "Usage: $0 <release-version>" + echo " e.g. $0 10.3.0" + exit 1 +fi + +FILEMGMT_HOST="${FILEMGMT_HOST:-people.apache.org}" +FILEMGMT_BASE_PATH="${FILEMGMT_BASE_PATH:-/home/drools/filemgmt}" +DRY_RUN="${DRY_RUN:-false}" + +# Derive major.minor stream (e.g. 10.3.0 → 10.3) +STREAM="$(echo "${RELEASE_VERSION}" | sed 's/^\([0-9]*\.[0-9]*\)\..*/\1/')" + +REMOTE_BINARIES_DIR="${FILEMGMT_BASE_PATH}/downloads_htdocs/drools/release/${STREAM}.x" +REMOTE_DOCS_DIR="${FILEMGMT_BASE_PATH}/docs_htdocs/drools/release/${STREAM}.x" + +echo "========================================" +echo "Apache KIE — upload binaries & docs" +echo "Release version : ${RELEASE_VERSION}" +echo "Stream : ${STREAM}" +echo "Remote host : ${FILEMGMT_HOST}" +echo "Remote binaries : ${REMOTE_BINARIES_DIR}" +echo "Remote docs : ${REMOTE_DOCS_DIR}" +echo "Dry run : ${DRY_RUN}" +echo "========================================" + +cd "${REPO_ROOT}" + +# ── Locate locally-built distribution zips ─────────────────────────────────── +# Maven -Dfull produces zip artifacts under each module's target/ directory. +# We upload everything matching the release version. +BINARY_ZIPS=() +while IFS= read -r -d '' f; do + BINARY_ZIPS+=("${f}") +done < <(find . -path "*/target/*-${RELEASE_VERSION}*.zip" ! -path "*/target/checkout/*" -print0 2>/dev/null) + +if [[ ${#BINARY_ZIPS[@]} -eq 0 ]]; then + echo "WARNING: No distribution zips found for version ${RELEASE_VERSION}." + echo " Run 03-build.sh with -Dfull before uploading." +fi + +# ── Helper: run or echo ─────────────────────────────────────────────────────── +run() { + if [[ "${DRY_RUN}" == "true" ]]; then + echo "[DRY RUN] $*" + else + "$@" + fi +} + +# ── Create remote directories ───────────────────────────────────────────────── +run ssh "${FILEMGMT_HOST}" "mkdir -p '${REMOTE_BINARIES_DIR}/${RELEASE_VERSION}' '${REMOTE_DOCS_DIR}/${RELEASE_VERSION}'" + +# ── Upload distribution zips ────────────────────────────────────────────────── +if [[ ${#BINARY_ZIPS[@]} -gt 0 ]]; then + echo "" + echo "--- Uploading ${#BINARY_ZIPS[@]} distribution zip(s) ---" + for zip in "${BINARY_ZIPS[@]}"; do + echo " ${zip}" + run rsync -avz --progress \ + "${zip}" \ + "${FILEMGMT_HOST}:${REMOTE_BINARIES_DIR}/${RELEASE_VERSION}/" + done +fi + +# ── Upload HTML documentation (if generated) ───────────────────────────────── +DOCS_DIR="target/docs" +if [[ -d "${DOCS_DIR}" ]]; then + echo "" + echo "--- Uploading docs from ${DOCS_DIR} ---" + run rsync -avz --progress --delete \ + "${DOCS_DIR}/" \ + "${FILEMGMT_HOST}:${REMOTE_DOCS_DIR}/${RELEASE_VERSION}/" +else + echo "(skipping docs upload — ${DOCS_DIR} not found)" +fi + +echo "" +echo "========================================" +echo "Upload complete for version ${RELEASE_VERSION}." +echo "========================================" --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
