Copilot commented on code in PR #7123:
URL: https://github.com/apache/incubator-kie/pull/7123#discussion_r4229460540


##########
.ci/jenkins/Jenkinsfile.103xplus.promote:
##########
@@ -0,0 +1,293 @@
+/*
+ * 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())
+                }
+            }
+        }
+
+        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('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('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.
+            when {
+                expression { return getGitTag()?.trim() }
+            }
+            steps {
+                withCredentials([
+                    sshUserPrivateKey(credentialsId: 
env.GIT_AUTHOR_PUSH_CREDS_ID, keyFileVariable: 'GIT_SSH_KEY')
+                ]) {
+                    script {
+                        dir(getRepoName()) {
+                            sh """#!/usr/bin/env bash
+                            set -euo pipefail
+                            # Fetch the RC tag so the script can resolve it.
+                            git fetch origin 
"refs/tags/${getGitTag()}:refs/tags/${getGitTag()}"
+                            chmod +x script/release/05-tag-release.sh
+                            ./script/release/05-tag-release.sh --rc-tag 
"${getGitTag()}" --push
+                            """
+                        }
+                    }
+                }
+            }
+        }

Review Comment:
   `05-tag-release.sh` expects an RC tag (e.g. `10.3.0-rc2`) as `--rc-tag`, but 
`getGitTag()` defaults to `getProjectVersion()` when `params.GIT_TAG` is empty. 
That default will pass a non-RC tag value (e.g. `10.3.0`) into `--rc-tag`, 
which will fail or behave incorrectly. Make `GIT_TAG` (or a renamed `RC_TAG`) 
mandatory for this stage and/or change the getter to avoid defaulting to the 
project version when the value is specifically used as an RC tag.



##########
.ci/jenkins/Jenkinsfile.103xplus.promote:
##########
@@ -0,0 +1,293 @@
+/*
+ * 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())
+                }
+            }
+        }
+
+        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('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('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.
+            when {
+                expression { return getGitTag()?.trim() }
+            }
+            steps {
+                withCredentials([
+                    sshUserPrivateKey(credentialsId: 
env.GIT_AUTHOR_PUSH_CREDS_ID, keyFileVariable: 'GIT_SSH_KEY')
+                ]) {
+                    script {
+                        dir(getRepoName()) {
+                            sh """#!/usr/bin/env bash
+                            set -euo pipefail
+                            # Fetch the RC tag so the script can resolve it.
+                            git fetch origin 
"refs/tags/${getGitTag()}:refs/tags/${getGitTag()}"
+                            chmod +x script/release/05-tag-release.sh
+                            ./script/release/05-tag-release.sh --rc-tag 
"${getGitTag()}" --push
+                            """
+                        }
+                    }
+                }

Review Comment:
   Same as the release pipeline: `sshUserPrivateKey(..., keyFileVariable: 
'GIT_SSH_KEY')` alone doesn’t ensure `git fetch` / `git push` uses that key. 
This can break both the `git fetch origin ...` and the `--push` inside 
`05-tag-release.sh`. Use `sshagent([env.GIT_AUTHOR_PUSH_CREDS_ID])` or set 
`GIT_SSH_COMMAND` to reference `$GIT_SSH_KEY` within the `sh` block so Git 
operations are authenticated reliably.



##########
.ci/jenkins/project/Jenkinsfile.103xplus.release:
##########
@@ -0,0 +1,134 @@
+/*
+ * 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'),
+                        sshUserPrivateKey(credentialsId: 
env.GIT_AUTHOR_PUSH_CREDS_ID, keyFileVariable: 'GIT_SSH_KEY')
+                    ]) {
+                        script {
+                            // Configure Git identity for the R commit.
+                            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'}"
+                            """
+
+                            // 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(' ')}
+                                """

Review Comment:
   The pipeline binds an SSH private key to `GIT_SSH_KEY`, but `git push` 
inside `release-all.sh`/`02-rc-commit.sh` won’t automatically use that key. As 
written, `--push-tag` can fail due to missing SSH agent / SSH command 
configuration. Fix by using `sshagent([env.GIT_AUTHOR_PUSH_CREDS_ID])` around 
the script execution, or exporting `GIT_SSH_COMMAND=ssh -i \"$GIT_SSH_KEY\" -o 
IdentitiesOnly=yes` (and any required known_hosts handling) so Git consistently 
uses the injected key.



##########
.ci/jenkins/Jenkinsfile.103xplus.promote:
##########
@@ -0,0 +1,293 @@
+/*
+ * 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())
+                }
+            }
+        }
+
+        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('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('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.
+            when {
+                expression { return getGitTag()?.trim() }
+            }
+            steps {
+                withCredentials([
+                    sshUserPrivateKey(credentialsId: 
env.GIT_AUTHOR_PUSH_CREDS_ID, keyFileVariable: 'GIT_SSH_KEY')
+                ]) {
+                    script {
+                        dir(getRepoName()) {
+                            sh """#!/usr/bin/env bash
+                            set -euo pipefail
+                            # Fetch the RC tag so the script can resolve it.
+                            git fetch origin 
"refs/tags/${getGitTag()}:refs/tags/${getGitTag()}"
+                            chmod +x script/release/05-tag-release.sh
+                            ./script/release/05-tag-release.sh --rc-tag 
"${getGitTag()}" --push
+                            """
+                        }
+                    }
+                }
+            }
+        }
+
+        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')
+}
+
+String getGitTag() {
+    return params.GIT_TAG != '' ? params.GIT_TAG : getProjectVersion()

Review Comment:
   `05-tag-release.sh` expects an RC tag (e.g. `10.3.0-rc2`) as `--rc-tag`, but 
`getGitTag()` defaults to `getProjectVersion()` when `params.GIT_TAG` is empty. 
That default will pass a non-RC tag value (e.g. `10.3.0`) into `--rc-tag`, 
which will fail or behave incorrectly. Make `GIT_TAG` (or a renamed `RC_TAG`) 
mandatory for this stage and/or change the getter to avoid defaulting to the 
project version when the value is specifically used as an RC tag.



##########
script/release/03-build.sh:
##########
@@ -0,0 +1,98 @@
+#!/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
+#
+# 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 so spaces inside individual opts are
+# preserved correctly (avoids unquoted word-splitting and command injection).
+EXTRA_MVN_OPTS=()

Review Comment:
   The comment claims spaces inside individual opts are preserved, but `read 
-a` with `IFS=' '` does not honor shell-style quoting. For example, 
`--maven-opts '-Dfoo=\"a b\"'` will be split into multiple args incorrectly. 
Either adjust the contract/docs to state quoting isn’t supported, or change the 
interface to accept repeated `--maven-opt <singleOpt>` flags (so Jenkins/Groovy 
can pass each option as its own argv token without needing parsing).



##########
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}/docs_htdocs/drools/release/${STREAM}.x"
+REMOTE_DOCS_DIR="${FILEMGMT_BASE_PATH}/docs_htdocs/kogito/release/${STREAM}.x"

Review Comment:
   The script header and Jenkins stage indicate this uploads 'Drools release 
binaries and documentation', but docs are uploaded under a `kogito` path while 
binaries go under `drools`. If these docs are meant to be Drools release docs, 
the remote docs directory should be under the Drools tree as well; if they’re 
intentionally Kogito docs, then the script name/comments (and Jenkins stage 
label) should be updated to avoid publishing to the wrong site tree.



##########
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.

Review Comment:
   The script header and Jenkins stage indicate this uploads 'Drools release 
binaries and documentation', but docs are uploaded under a `kogito` path while 
binaries go under `drools`. If these docs are meant to be Drools release docs, 
the remote docs directory should be under the Drools tree as well; if they’re 
intentionally Kogito docs, then the script name/comments (and Jenkins stage 
label) should be updated to avoid publishing to the wrong site tree.



##########
script/release/03-build.sh:
##########
@@ -0,0 +1,98 @@
+#!/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
+#
+# 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 so spaces inside individual opts are
+# preserved correctly (avoids unquoted word-splitting and command injection).
+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 safely
+            # into array elements using read.  No eval or command substitution.
+            IFS=' ' read -r -a _extra <<< "${2:-}"
+            EXTRA_MVN_OPTS+=("${_extra[@]}")
+            shift 2

Review Comment:
   The comment claims spaces inside individual opts are preserved, but `read 
-a` with `IFS=' '` does not honor shell-style quoting. For example, 
`--maven-opts '-Dfoo=\"a b\"'` will be split into multiple args incorrectly. 
Either adjust the contract/docs to state quoting isn’t supported, or change the 
interface to accept repeated `--maven-opt <singleOpt>` flags (so Jenkins/Groovy 
can pass each option as its own argv token without needing parsing).



##########
script/release/05-tag-release.sh:
##########
@@ -0,0 +1,129 @@
+#!/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
+

Review Comment:
   If `--rc-tag` is passed a value that doesn’t end in `-rc<N>` (e.g. 
`10.3.0`), `RELEASE_TAG` will equal `RC_TAG`, and the later `git tag -a 
\"${RELEASE_TAG}\" \"${RC_TAG}\" ...` will attempt to create a tag with the 
same name as an existing one. Add an explicit validation that `RC_TAG` matches 
the expected `*-rc[0-9]+` format and emit a clear error message if it doesn’t.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to