You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kie.apache.org by "Kusuma04-dev (via GitHub)" <gi...@apache.org> on 2026/09/18 08:33:38 UTC
[PR] 10.3.x+ stream: Re-write kie-tools release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Kusuma04-dev opened a new pull request, #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019
This PR adds modular, local-first release scripts for `incubator-kie-tools` packages and updates the Jenkins release pipelines to execute them.
**What the Release Scripts Do:**
- `scripts/release/release-all.sh`: Master script that builds and packages all KIE Tools components.
- `scripts/release/release-npm-packages.sh`: Packages and publishes `@kie-tools/*` packages to the npm registry.
- `scripts/release/release-vscode.sh`: Packages and publishes VS Code extensions (`.vsix`) to the Visual Studio Marketplace.
- `scripts/release/release-chrome-extensions.sh`: Packages and uploads Chrome extensions to the Chrome Web Store.
- `scripts/release/release-container-images.sh`: Builds, saves, and pushes container images.
- `scripts/release/release-helm-charts.sh`: Packages and pushes Helm charts to OCI registries.
- `scripts/release/release-github-pages.sh`: Deploys online web applications and editors to GitHub Pages.
- `scripts/release/release-dev-deployment-upload-service.sh`: Builds cross-platform binaries for the Dev Deployment Upload Service.
- `scripts/release/create-source-tarball.sh`: Creates the Apache-compliant source release archive.
**Jenkins Pipeline Updates:**
- `.ci/jenkins/release-jobs/Jenkinsfile.release-candidate`: Updated to run release scripts with `--rc`, sign artifacts (`.asc`, `.sha512`), and stage them to Apache SVN for voting.
- `.ci/jenkins/release-jobs/Jenkinsfile.release-publish`: Updated to run release scripts with `--publish` using injected bot credentials and upload release assets to GitHub.
- **Backups Preserved**: Kept `.backup` copies of original Jenkinsfiles (`Jenkinsfile.release-candidate.backup` and `Jenkinsfile.release-publish.backup`) to safely test and verify the new script-based flow.
For testing Artifacts generated in local dry run, are available in :
https://github.com/Kusuma04-dev/kie-release-10.3/tree/main/artifacts_kie_tools
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] kie-issues#2405: 10.3.x+ stream: Re-write kie-tools release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#discussion_r4223191926
##########
.ci/jenkins/Jenkinsfile.103xplus.release-candidate:
##########
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+
+@Library('jenkins-pipeline-shared-libraries')_
+
+// PHASE 1: Create Release Candidate for Apache Voting
+// This job builds artifacts, signs them, and publishes to Apache SVN for community vote
+// Does NOT publish to npm, VSCode Marketplace, or Chrome Web Store
+//
+// NOTE: kogito-data-index-ephemeral is NOT included in this pipeline because the
+// packages/kogito-data-index-ephemeral-image package no longer exists in this repo.
+// Its presence in older Jenkinsfiles (Jenkinsfile.release-candidate RUNNERS list and
+// release-jobs/Jenkinsfile.kogito-data-index-ephemeral-image) is a historical artifact
+// from pre-10.3 automation and can be cleaned up in a follow-up PR.
+
+pipeline {
+ agent {
+ docker {
+ image 'docker.io/apache/incubator-kie-tools-ci-build:main'
+ args '--shm-size=2g --privileged --group-add docker'
+ label util.avoidFaultyNodes()
+ }
+ }
+
+ options {
+ timeout(time: 480, unit: 'MINUTES')
+ }
+
+ parameters {
+ string(
+ name: 'RELEASE_VERSION',
+ description: 'Release version (e.g., 1.0.0)',
+ defaultValue: '0.0.0'
+ )
+ string(
+ name: 'RELEASE_CANDIDATE_VERSION',
+ description: 'Release candidate version (e.g., rc1)',
+ defaultValue: 'rc1'
+ )
+ string(
+ name: 'BASE_REF',
+ description: 'Git branch or tag to release from',
+ defaultValue: 'main'
+ )
+ }
+
+ environment {
+ KIE_TOOLS_BUILD__runLinters = 'false'
+ KIE_TOOLS_BUILD__runTests = 'false'
+ KIE_TOOLS_BUILD__runEndToEndTests = 'false'
+ KIE_TOOLS_BUILD__buildContainerImages = 'true'
+
+ CHROME_EXTENSION__routerTargetOrigin = 'https://apache.github.io'
+ CHROME_EXTENSION__routerRelativePath = "incubator-kie-kogito-online/chrome-extension/${params.RELEASE_VERSION}"
+ CHROME_EXTENSION__manifestFile = 'manifest.prod.json'
+ CHROME_EXTENSION__onlineEditorUrl = 'https://apache.github.io/incubator-kie-kogito-online'
+
+ ONLINE_EDITOR__corsProxyUrl = 'https://cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud'
+ ONLINE_EDITOR__buildInfo = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__extendedServicesCompatibleVersion = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentBaseImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentBaseImageName = 'incubator-kie-sandbox-dev-deployment-base'
+ ONLINE_EDITOR__devDeploymentBaseImageTag = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'incubator-kie-sandbox-dev-deployment-dmn-form-webapp'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName = 'incubator-kie-sandbox-dev-deployment-quarkus-blank-app'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag = "${params.RELEASE_VERSION}"
+ }
+
+ stages {
+ stage('Load local shared scripts') {
+ steps {
+ script {
+ pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy'
+ buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy'
+ githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy'
+ releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy'
+ }
+ }
+ }
+
+ stage('Start required services') {
+ steps {
+ script {
+ buildUtils.startRequiredServices()
+ }
+ }
+ }
+
+ stage('Clean workspace') {
+ steps {
+ cleanWs(deleteDirs: true, disableDeferredWipeout: true)
+ }
+ }
+
+ stage('Checkout kie-tools') {
+ steps {
+ dir('kie-tools') {
+ script {
+ githubUtils.checkoutRepo(
+ "http://github.com/${pipelineVars.githubRepositorySlug}.git",
+ "${params.BASE_REF}",
+ "${pipelineVars.kieToolsBotGithubCredentialsId}"
+ )
+ }
+ }
+ }
+ }
+
+ stage('Setup PNPM') {
+ steps {
+ dir('kie-tools') {
+ script {
+ buildUtils.setupPnpm("${pipelineVars.mavenSettingsReleaseConfigFileId}")
+ }
+ }
+ }
+ }
+
+ stage('Bootstrap') {
+ steps {
+ dir('kie-tools') {
+ sh '''#!/bin/bash -el
+ pnpm bootstrap
+ '''
+ }
+ }
+ }
+
+ stage('Update versions') {
+ steps {
+ dir('kie-tools') {
+ sh """#!/bin/bash -el
+ pnpm update-version-to ${params.RELEASE_VERSION}
+ pnpm update-kogito-version-to --maven ${params.RELEASE_VERSION}
+ pnpm update-stream-name-to ${params.RELEASE_VERSION}
+ """
+ }
+ }
+ }
+
+ stage('Build Release Candidate Artifacts') {
+ steps {
+ dir('kie-tools') {
+ script {
+ echo "Building release candidate artifacts..."
+ echo "Version: ${params.RELEASE_VERSION}"
+ echo "RC: ${params.RELEASE_CANDIDATE_VERSION}"
+
+ // Build artifacts WITHOUT publishing (no --publish flag)
+ // The --rc flag creates signed artifacts for Apache SVN
+ sh """#!/bin/bash -el
+ ./scripts/release/release-all.sh ${params.RELEASE_VERSION} --rc
+ """
+ }
+ }
+ }
+ }
+
+ stage('Sign Release Candidate Artifacts') {
+ steps {
+ dir('kie-tools/release-artifacts') {
+ script {
+ releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}")
+
+ sh """#!/bin/bash -el
+ echo "Signing all artifacts..."
+ for file in *.zip *.vsix *.tar.gz; do
Review Comment:
Only top-level artifacts are signed, while `release-all.sh` places container images and Helm charts under `container-images/` and `helm-charts/`. Those files remain unsigned; additionally, `releaseUtils.publishArtifacts` uses non-recursive `cp artifacts/*`, which fails on these directories, so SVN staging cannot complete. Flatten the RC output or make both signing and SVN publication recurse over regular artifacts.
##########
.ci/jenkins/Jenkinsfile.103xplus.release-candidate:
##########
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+
+@Library('jenkins-pipeline-shared-libraries')_
+
+// PHASE 1: Create Release Candidate for Apache Voting
+// This job builds artifacts, signs them, and publishes to Apache SVN for community vote
+// Does NOT publish to npm, VSCode Marketplace, or Chrome Web Store
+//
+// NOTE: kogito-data-index-ephemeral is NOT included in this pipeline because the
+// packages/kogito-data-index-ephemeral-image package no longer exists in this repo.
+// Its presence in older Jenkinsfiles (Jenkinsfile.release-candidate RUNNERS list and
+// release-jobs/Jenkinsfile.kogito-data-index-ephemeral-image) is a historical artifact
+// from pre-10.3 automation and can be cleaned up in a follow-up PR.
+
+pipeline {
+ agent {
+ docker {
+ image 'docker.io/apache/incubator-kie-tools-ci-build:main'
+ args '--shm-size=2g --privileged --group-add docker'
+ label util.avoidFaultyNodes()
+ }
+ }
+
+ options {
+ timeout(time: 480, unit: 'MINUTES')
+ }
+
+ parameters {
+ string(
+ name: 'RELEASE_VERSION',
+ description: 'Release version (e.g., 1.0.0)',
+ defaultValue: '0.0.0'
+ )
+ string(
+ name: 'RELEASE_CANDIDATE_VERSION',
+ description: 'Release candidate version (e.g., rc1)',
+ defaultValue: 'rc1'
+ )
+ string(
+ name: 'BASE_REF',
+ description: 'Git branch or tag to release from',
+ defaultValue: 'main'
+ )
+ }
+
+ environment {
+ KIE_TOOLS_BUILD__runLinters = 'false'
+ KIE_TOOLS_BUILD__runTests = 'false'
+ KIE_TOOLS_BUILD__runEndToEndTests = 'false'
+ KIE_TOOLS_BUILD__buildContainerImages = 'true'
+
+ CHROME_EXTENSION__routerTargetOrigin = 'https://apache.github.io'
+ CHROME_EXTENSION__routerRelativePath = "incubator-kie-kogito-online/chrome-extension/${params.RELEASE_VERSION}"
+ CHROME_EXTENSION__manifestFile = 'manifest.prod.json'
+ CHROME_EXTENSION__onlineEditorUrl = 'https://apache.github.io/incubator-kie-kogito-online'
+
+ ONLINE_EDITOR__corsProxyUrl = 'https://cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud'
+ ONLINE_EDITOR__buildInfo = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__extendedServicesCompatibleVersion = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentBaseImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentBaseImageName = 'incubator-kie-sandbox-dev-deployment-base'
+ ONLINE_EDITOR__devDeploymentBaseImageTag = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'incubator-kie-sandbox-dev-deployment-dmn-form-webapp'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName = 'incubator-kie-sandbox-dev-deployment-quarkus-blank-app'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag = "${params.RELEASE_VERSION}"
+ }
+
+ stages {
+ stage('Load local shared scripts') {
+ steps {
+ script {
+ pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy'
+ buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy'
+ githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy'
+ releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy'
+ }
+ }
+ }
+
+ stage('Start required services') {
+ steps {
+ script {
+ buildUtils.startRequiredServices()
+ }
+ }
+ }
+
+ stage('Clean workspace') {
+ steps {
+ cleanWs(deleteDirs: true, disableDeferredWipeout: true)
+ }
+ }
+
+ stage('Checkout kie-tools') {
+ steps {
+ dir('kie-tools') {
+ script {
+ githubUtils.checkoutRepo(
+ "http://github.com/${pipelineVars.githubRepositorySlug}.git",
+ "${params.BASE_REF}",
+ "${pipelineVars.kieToolsBotGithubCredentialsId}"
+ )
+ }
+ }
+ }
+ }
+
+ stage('Setup PNPM') {
+ steps {
+ dir('kie-tools') {
+ script {
+ buildUtils.setupPnpm("${pipelineVars.mavenSettingsReleaseConfigFileId}")
+ }
+ }
+ }
+ }
+
+ stage('Bootstrap') {
+ steps {
+ dir('kie-tools') {
+ sh '''#!/bin/bash -el
+ pnpm bootstrap
+ '''
+ }
+ }
+ }
+
+ stage('Update versions') {
+ steps {
+ dir('kie-tools') {
+ sh """#!/bin/bash -el
+ pnpm update-version-to ${params.RELEASE_VERSION}
+ pnpm update-kogito-version-to --maven ${params.RELEASE_VERSION}
+ pnpm update-stream-name-to ${params.RELEASE_VERSION}
+ """
+ }
+ }
+ }
+
+ stage('Build Release Candidate Artifacts') {
+ steps {
+ dir('kie-tools') {
+ script {
+ echo "Building release candidate artifacts..."
+ echo "Version: ${params.RELEASE_VERSION}"
+ echo "RC: ${params.RELEASE_CANDIDATE_VERSION}"
+
+ // Build artifacts WITHOUT publishing (no --publish flag)
+ // The --rc flag creates signed artifacts for Apache SVN
+ sh """#!/bin/bash -el
+ ./scripts/release/release-all.sh ${params.RELEASE_VERSION} --rc
+ """
Review Comment:
This pipeline builds and stages an RC but never commits the release-version updates or creates/pushes an RC tag. Phase 2 explicitly requires `BASE_REF` to be that tag and verifies its `package.json` version, so there is no revision produced by Phase 1 that Phase 2 can successfully publish. Create and push a release commit/tag before archiving/building, and use that exact ref for the voted source and publish job.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,743 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY, HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
Review Comment:
These commands cannot reliably stop the release when an update fails: each underlying updater catches command errors and exits successfully (`update_version.js:53-57`, `update_kogito_version.js:75-79`, and `update_stream_name.js:52-56`). A partial version update can consequently be built and published. Make every updater rethrow or set a nonzero exit code, then let this script fail immediately.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,743 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY, HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
Review Comment:
Writing the token to `~/.npmrc` overwrites the user's existing npm configuration and leaves the release credential on disk after the script exits. This is especially unsafe for a local-first release script. Use a temporary userconfig, remove it with a trap, and point npm/pnpm at it for this publish only.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,743 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY, HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local access_token
+ access_token=$(curl -sS -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token" \
+ | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local state
+ state=$(curl -sS -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}" | grep -o '"uploadState":"[^"]*"' | cut -d'"' -f4)
+ [[ "${state}" == "SUCCESS" ]] || { echo "ERROR: upload failed for ${ext_id}"; return 1; }
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ curl -sS -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length:0" > /dev/null
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["kogito-base-builder"]="packages/kogito-base-builder-image"
+ ["kogito-data-index-ephemeral"]="packages/kogito-data-index-ephemeral-image"
+ ["kogito-data-index-postgresql"]="packages/kogito-data-index-postgresql-image"
+ ["kogito-jit-runner"]="packages/kogito-jit-runner-image"
+ ["kogito-jobs-service-allinone"]="packages/kogito-jobs-service-allinone-image"
+ ["kogito-jobs-service-ephemeral"]="packages/kogito-jobs-service-ephemeral-image"
+ ["kogito-jobs-service-postgresql"]="packages/kogito-jobs-service-postgresql-image"
+ ["kogito-management-console"]="packages/kogito-management-console"
+ ["kogito-db-migrator-tool"]="packages/kogito-db-migrator-tool-image"
+ ["cors-proxy"]="packages/cors-proxy-image"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || { echo " SKIP ${artifact_name} (${pkg_path} not found)"; continue; }
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}/container-images"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || continue
+ local full_image="${REGISTRY}/incubator-kie-${artifact_name}:${VERSION}"
+ docker tag "$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null | sed 's|@kie-tools/||')" "${full_image}" 2>/dev/null \
+ || docker tag "incubator-kie-${artifact_name}:latest" "${full_image}" 2>/dev/null \
+ || docker tag "apache/incubator-kie-${artifact_name}:main" "${full_image}" 2>/dev/null \
+ || echo " WARN could not tag ${artifact_name}"
Review Comment:
The source tags here do not match the images produced by these packages. For example, `kogito-management-console` builds `docker.io/apache/incubator-kie-kogito-management-console:${VERSION}`, while the attempts here look for `kogito-management-console`, an unqualified `:latest`, or `apache/...:main`. Thus tagging warns and RC image export is skipped (or publish later tries a nonexistent target). Resolve each built image's actual registry/account/name/tag rather than deriving it from the npm package name.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,743 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY, HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local access_token
+ access_token=$(curl -sS -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token" \
+ | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local state
+ state=$(curl -sS -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}" | grep -o '"uploadState":"[^"]*"' | cut -d'"' -f4)
+ [[ "${state}" == "SUCCESS" ]] || { echo "ERROR: upload failed for ${ext_id}"; return 1; }
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ curl -sS -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length:0" > /dev/null
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["kogito-base-builder"]="packages/kogito-base-builder-image"
+ ["kogito-data-index-ephemeral"]="packages/kogito-data-index-ephemeral-image"
+ ["kogito-data-index-postgresql"]="packages/kogito-data-index-postgresql-image"
+ ["kogito-jit-runner"]="packages/kogito-jit-runner-image"
+ ["kogito-jobs-service-allinone"]="packages/kogito-jobs-service-allinone-image"
+ ["kogito-jobs-service-ephemeral"]="packages/kogito-jobs-service-ephemeral-image"
+ ["kogito-jobs-service-postgresql"]="packages/kogito-jobs-service-postgresql-image"
+ ["kogito-management-console"]="packages/kogito-management-console"
+ ["kogito-db-migrator-tool"]="packages/kogito-db-migrator-tool-image"
+ ["cors-proxy"]="packages/cors-proxy-image"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || { echo " SKIP ${artifact_name} (${pkg_path} not found)"; continue; }
Review Comment:
Silently skipping missing package directories hides invalid entries in `IMAGES`: the repository has no directories for the base-builder, data-index, JIT runner, jobs-service, or DB migrator paths listed above, and the sandbox webapp actually lives at `packages/kie-sandbox-webapp-image`. RC and publish runs therefore omit these advertised images while this step can still succeed. Correct/remove the mappings and fail release modes when a configured image package is absent.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,743 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY, HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local access_token
+ access_token=$(curl -sS -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token" \
+ | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local state
+ state=$(curl -sS -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}" | grep -o '"uploadState":"[^"]*"' | cut -d'"' -f4)
+ [[ "${state}" == "SUCCESS" ]] || { echo "ERROR: upload failed for ${ext_id}"; return 1; }
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ curl -sS -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length:0" > /dev/null
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["kogito-base-builder"]="packages/kogito-base-builder-image"
+ ["kogito-data-index-ephemeral"]="packages/kogito-data-index-ephemeral-image"
+ ["kogito-data-index-postgresql"]="packages/kogito-data-index-postgresql-image"
+ ["kogito-jit-runner"]="packages/kogito-jit-runner-image"
+ ["kogito-jobs-service-allinone"]="packages/kogito-jobs-service-allinone-image"
+ ["kogito-jobs-service-ephemeral"]="packages/kogito-jobs-service-ephemeral-image"
+ ["kogito-jobs-service-postgresql"]="packages/kogito-jobs-service-postgresql-image"
+ ["kogito-management-console"]="packages/kogito-management-console"
+ ["kogito-db-migrator-tool"]="packages/kogito-db-migrator-tool-image"
+ ["cors-proxy"]="packages/cors-proxy-image"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || { echo " SKIP ${artifact_name} (${pkg_path} not found)"; continue; }
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}/container-images"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || continue
+ local full_image="${REGISTRY}/incubator-kie-${artifact_name}:${VERSION}"
+ docker tag "$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null | sed 's|@kie-tools/||')" "${full_image}" 2>/dev/null \
+ || docker tag "incubator-kie-${artifact_name}:latest" "${full_image}" 2>/dev/null \
+ || docker tag "apache/incubator-kie-${artifact_name}:main" "${full_image}" 2>/dev/null \
+ || echo " WARN could not tag ${artifact_name}"
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${full_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${full_image}" | gzip > "${tarball}"
+ else
+ echo " SKIP ${artifact_name} (image not found locally)"
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || continue
+ local full_image="${REGISTRY}/incubator-kie-${artifact_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}/helm-charts"
+ mkdir -p "${output_dir}"
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo " SKIP ${slug} (${chart_pkg} not found)"; continue; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo " SKIP ${slug} (Chart.yaml not found)"; continue; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${output_dir}"
+ local found
+ found=$(find "${output_dir}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ [[ -n "${found}" ]] && mv "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz" \
+ && echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${HELM_REGISTRY:-}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${HELM_REGISTRY}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for slug in "${!CHARTS[@]}"; do
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ [[ -f "${tarball}" ]] && helm push "${tarball}" "oci://${HELM_REGISTRY}" && echo " PUSH ${tarball}"
Review Comment:
`helm package` produces a `.tgz`, but the code renames it to `.tar.gz` before passing it to `helm push`; Helm's push command requires a packaged `.tgz` chart. Preserve the `.tgz` for registry publication and create the Apache-named `.tar.gz` copy only for RC staging.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,743 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY, HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local access_token
+ access_token=$(curl -sS -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token" \
+ | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local state
+ state=$(curl -sS -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}" | grep -o '"uploadState":"[^"]*"' | cut -d'"' -f4)
+ [[ "${state}" == "SUCCESS" ]] || { echo "ERROR: upload failed for ${ext_id}"; return 1; }
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ curl -sS -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length:0" > /dev/null
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["kogito-base-builder"]="packages/kogito-base-builder-image"
+ ["kogito-data-index-ephemeral"]="packages/kogito-data-index-ephemeral-image"
+ ["kogito-data-index-postgresql"]="packages/kogito-data-index-postgresql-image"
+ ["kogito-jit-runner"]="packages/kogito-jit-runner-image"
+ ["kogito-jobs-service-allinone"]="packages/kogito-jobs-service-allinone-image"
+ ["kogito-jobs-service-ephemeral"]="packages/kogito-jobs-service-ephemeral-image"
+ ["kogito-jobs-service-postgresql"]="packages/kogito-jobs-service-postgresql-image"
+ ["kogito-management-console"]="packages/kogito-management-console"
+ ["kogito-db-migrator-tool"]="packages/kogito-db-migrator-tool-image"
+ ["cors-proxy"]="packages/cors-proxy-image"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || { echo " SKIP ${artifact_name} (${pkg_path} not found)"; continue; }
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}/container-images"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || continue
+ local full_image="${REGISTRY}/incubator-kie-${artifact_name}:${VERSION}"
+ docker tag "$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null | sed 's|@kie-tools/||')" "${full_image}" 2>/dev/null \
+ || docker tag "incubator-kie-${artifact_name}:latest" "${full_image}" 2>/dev/null \
+ || docker tag "apache/incubator-kie-${artifact_name}:main" "${full_image}" 2>/dev/null \
+ || echo " WARN could not tag ${artifact_name}"
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${full_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${full_image}" | gzip > "${tarball}"
+ else
+ echo " SKIP ${artifact_name} (image not found locally)"
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || continue
+ local full_image="${REGISTRY}/incubator-kie-${artifact_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}/helm-charts"
+ mkdir -p "${output_dir}"
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo " SKIP ${slug} (${chart_pkg} not found)"; continue; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo " SKIP ${slug} (Chart.yaml not found)"; continue; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${output_dir}"
+ local found
+ found=$(find "${output_dir}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ [[ -n "${found}" ]] && mv "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz" \
+ && echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${HELM_REGISTRY:-}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${HELM_REGISTRY}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for slug in "${!CHARTS[@]}"; do
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ [[ -f "${tarball}" ]] && helm push "${tarball}" "oci://${HELM_REGISTRY}" && echo " PUSH ${tarball}"
+ done
+ echo "Helm charts pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, kie-editors-standalone, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${VERSION}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="incubator-kie-sandbox-dev-deployment-base"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${VERSION}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/kie-editors-standalone..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-editors-standalone/dist:apache-kie-${VERSION}-incubating-business-automation-standalone-editors.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
+ fi
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN is required for publishing to GitHub Pages"
+ return 1
+ fi
+
+ local kogito_online_repo="https://github.com/apache/incubator-kie-kogito-online.git"
+ local accelerator_repo="https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+
+ local auth_url="${kogito_online_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --branch gh-pages --depth 1 "${auth_url}" "${tmp_dir}/kogito-online"
+ (
+ cd "${tmp_dir}/kogito-online"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ find . -maxdepth 1 ! -name '.' ! -name 'dev' ! -name 'editors' ! -name 'standalone' \
+ ! -name 'chrome-extension' ! -name '.nojekyll' ! -name 'CNAME' -exec rm -rf {} +
+
+ local online_dist="${REPO_ROOT}/packages/online-editor/dist"
+ if [[ -d "${online_dist}" ]]; then
+ local editors_dir="editors/${VERSION}"
+ rm -rf "${editors_dir}" && mkdir -p "${editors_dir}"
+ rm -rf editors/latest && ln -s "${VERSION}" editors/latest
+ cp -r "${online_dist}/." .
+ fi
+ local standalone_dist="${REPO_ROOT}/packages/kie-editors-standalone/dist"
+ [[ -d "${standalone_dist}" ]] && cp -r "${standalone_dist}/." "editors/${VERSION}/"
+ local chrome_dist="${REPO_ROOT}/packages/chrome-extension-pack-kogito-kie-editors/dist"
+ if [[ -d "${chrome_dist}" ]]; then
+ local chrome_dir="chrome-extension/${VERSION}"
+ rm -rf "${chrome_dir}" && mkdir -p "${chrome_dir}"
+ cp -r "${chrome_dist}/fonts" "${chrome_dir}/" 2>/dev/null || true
+ cp "${chrome_dist}"/*-envelope.* "${chrome_dir}/" 2>/dev/null || true
+ fi
+ git add .
+ git commit -m "Deploy ${VERSION} (Online Editor + Standalone Editors + Chrome Extension editors)" \
+ || echo " (nothing to commit)"
+ git push origin gh-pages
+ )
+ echo "Pushed to GitHub Pages."
+
+ local accel_content="${REPO_ROOT}/packages/kie-sandbox-accelerator-quarkus/dist/git-repo-content"
+ if [[ -d "${accel_content}" ]]; then
+ local auth_accel="${accelerator_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --depth 1 "${auth_accel}" "${tmp_dir}/accelerator"
+ (
+ cd "${tmp_dir}/accelerator"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git checkout --orphan "${VERSION}"
+ cp -r "${accel_content}/." .
+ git add .
+ git commit -m "Apache KIE Sandbox Quarkus Accelerator ${VERSION}"
+ git tag "${VERSION}"
+ git push origin "${VERSION}"
+ )
+ echo "Pushed accelerator to tag ${VERSION}."
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 7. kn-plugin-workflow
+# ---------------------------------------------------------------------------
+release_kn_plugin_workflow() {
+ echo ""
+ echo "--- kn-plugin-workflow ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building kn-plugin-workflow..."
+ pnpm -F "@kie-tools/kn-plugin-workflow..." build:prod
Review Comment:
This package/filter does not exist in the repository, so the unified script cannot build the advertised kn-plugin artifacts and then treats the absent `dist` directory as success. Either point this step at the component's actual source or remove it from this repository's release outputs; do not silently claim completion.
##########
.ci/jenkins/Jenkinsfile.103xplus.release-publish:
##########
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+@Library('jenkins-pipeline-shared-libraries')_
+
+// PHASE 2: Publish Release After Vote Passes
+// This job publishes the approved release candidate to public registries
+// Publishes to: npm, VSCode Marketplace, Chrome Web Store, GitHub Releases
+//
+// NOTE: kogito-data-index-ephemeral is NOT included in this pipeline because the
+// packages/kogito-data-index-ephemeral-image package no longer exists in this repo.
+// Its presence in older Jenkinsfiles (Jenkinsfile.release-candidate RUNNERS list and
+// release-jobs/Jenkinsfile.kogito-data-index-ephemeral-image) is a historical artifact
+// from pre-10.3 automation and can be cleaned up in a follow-up PR.
+
+pipeline {
+ agent {
+ docker {
+ image 'docker.io/apache/incubator-kie-tools-ci-build:main'
+ args '--shm-size=2g --privileged --group-add docker'
+ label util.avoidFaultyNodes()
+ }
+ }
+
+ options {
+ timeout(time: 480, unit: 'MINUTES')
+ }
+
+ parameters {
+ string(
+ name: 'RELEASE_VERSION',
+ description: 'Release version (e.g., 1.0.0) - must match the voted RC',
+ defaultValue: '0.0.0'
+ )
+ string(
+ name: 'BASE_REF',
+ description: 'Git tag to release from (should be the RC tag)',
+ defaultValue: '0.0.0-rc1'
+ )
+ string(
+ name: 'GITHUB_RELEASE_TAG',
+ description: 'GitHub release tag (for uploading assets to the GitHub Release)',
+ defaultValue: ''
+ )
+ }
+
+ environment {
+ KIE_TOOLS_BUILD__runLinters = 'false'
+ KIE_TOOLS_BUILD__runTests = 'false'
+ KIE_TOOLS_BUILD__runEndToEndTests = 'false'
+ KIE_TOOLS_BUILD__buildContainerImages = 'true'
+
+ CHROME_EXTENSION__routerTargetOrigin = 'https://apache.github.io'
+ CHROME_EXTENSION__routerRelativePath = "incubator-kie-kogito-online/chrome-extension/${params.RELEASE_VERSION}"
+ CHROME_EXTENSION__manifestFile = 'manifest.prod.json'
+ CHROME_EXTENSION__onlineEditorUrl = 'https://apache.github.io/incubator-kie-kogito-online'
+
+ ONLINE_EDITOR__corsProxyUrl = 'https://cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud'
+ ONLINE_EDITOR__buildInfo = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__extendedServicesCompatibleVersion = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentBaseImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentBaseImageName = 'incubator-kie-sandbox-dev-deployment-base'
+ ONLINE_EDITOR__devDeploymentBaseImageTag = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'incubator-kie-sandbox-dev-deployment-dmn-form-webapp'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName = 'incubator-kie-sandbox-dev-deployment-quarkus-blank-app'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag = "${params.RELEASE_VERSION}"
+
+ DOCKER_CONFIG = "${WORKSPACE}/.docker"
+ HELM_REGISTRY = 'docker.io'
Review Comment:
The charts are expected under Docker Hub's `apache` namespace (the existing chart docs use `oci://docker.io/apache/...`), but this makes `helm push` target `oci://docker.io` and therefore the root namespace. Set the registry path to the Apache account.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,743 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY, HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local access_token
+ access_token=$(curl -sS -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token" \
+ | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local state
+ state=$(curl -sS -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}" | grep -o '"uploadState":"[^"]*"' | cut -d'"' -f4)
+ [[ "${state}" == "SUCCESS" ]] || { echo "ERROR: upload failed for ${ext_id}"; return 1; }
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ curl -sS -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length:0" > /dev/null
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["kogito-base-builder"]="packages/kogito-base-builder-image"
+ ["kogito-data-index-ephemeral"]="packages/kogito-data-index-ephemeral-image"
+ ["kogito-data-index-postgresql"]="packages/kogito-data-index-postgresql-image"
+ ["kogito-jit-runner"]="packages/kogito-jit-runner-image"
+ ["kogito-jobs-service-allinone"]="packages/kogito-jobs-service-allinone-image"
+ ["kogito-jobs-service-ephemeral"]="packages/kogito-jobs-service-ephemeral-image"
+ ["kogito-jobs-service-postgresql"]="packages/kogito-jobs-service-postgresql-image"
+ ["kogito-management-console"]="packages/kogito-management-console"
+ ["kogito-db-migrator-tool"]="packages/kogito-db-migrator-tool-image"
+ ["cors-proxy"]="packages/cors-proxy-image"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || { echo " SKIP ${artifact_name} (${pkg_path} not found)"; continue; }
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}/container-images"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || continue
+ local full_image="${REGISTRY}/incubator-kie-${artifact_name}:${VERSION}"
+ docker tag "$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null | sed 's|@kie-tools/||')" "${full_image}" 2>/dev/null \
+ || docker tag "incubator-kie-${artifact_name}:latest" "${full_image}" 2>/dev/null \
+ || docker tag "apache/incubator-kie-${artifact_name}:main" "${full_image}" 2>/dev/null \
+ || echo " WARN could not tag ${artifact_name}"
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${full_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${full_image}" | gzip > "${tarball}"
+ else
+ echo " SKIP ${artifact_name} (image not found locally)"
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || continue
+ local full_image="${REGISTRY}/incubator-kie-${artifact_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}/helm-charts"
+ mkdir -p "${output_dir}"
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo " SKIP ${slug} (${chart_pkg} not found)"; continue; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo " SKIP ${slug} (Chart.yaml not found)"; continue; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${output_dir}"
+ local found
+ found=$(find "${output_dir}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ [[ -n "${found}" ]] && mv "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz" \
+ && echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${HELM_REGISTRY:-}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${HELM_REGISTRY}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for slug in "${!CHARTS[@]}"; do
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ [[ -f "${tarball}" ]] && helm push "${tarball}" "oci://${HELM_REGISTRY}" && echo " PUSH ${tarball}"
+ done
+ echo "Helm charts pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, kie-editors-standalone, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${VERSION}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="incubator-kie-sandbox-dev-deployment-base"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${VERSION}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/kie-editors-standalone..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-editors-standalone/dist:apache-kie-${VERSION}-incubating-business-automation-standalone-editors.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
+ fi
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN is required for publishing to GitHub Pages"
+ return 1
+ fi
+
+ local kogito_online_repo="https://github.com/apache/incubator-kie-kogito-online.git"
+ local accelerator_repo="https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+
+ local auth_url="${kogito_online_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --branch gh-pages --depth 1 "${auth_url}" "${tmp_dir}/kogito-online"
+ (
+ cd "${tmp_dir}/kogito-online"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ find . -maxdepth 1 ! -name '.' ! -name 'dev' ! -name 'editors' ! -name 'standalone' \
+ ! -name 'chrome-extension' ! -name '.nojekyll' ! -name 'CNAME' -exec rm -rf {} +
+
+ local online_dist="${REPO_ROOT}/packages/online-editor/dist"
+ if [[ -d "${online_dist}" ]]; then
+ local editors_dir="editors/${VERSION}"
+ rm -rf "${editors_dir}" && mkdir -p "${editors_dir}"
+ rm -rf editors/latest && ln -s "${VERSION}" editors/latest
+ cp -r "${online_dist}/." .
+ fi
+ local standalone_dist="${REPO_ROOT}/packages/kie-editors-standalone/dist"
+ [[ -d "${standalone_dist}" ]] && cp -r "${standalone_dist}/." "editors/${VERSION}/"
+ local chrome_dist="${REPO_ROOT}/packages/chrome-extension-pack-kogito-kie-editors/dist"
+ if [[ -d "${chrome_dist}" ]]; then
+ local chrome_dir="chrome-extension/${VERSION}"
+ rm -rf "${chrome_dir}" && mkdir -p "${chrome_dir}"
+ cp -r "${chrome_dist}/fonts" "${chrome_dir}/" 2>/dev/null || true
+ cp "${chrome_dist}"/*-envelope.* "${chrome_dir}/" 2>/dev/null || true
+ fi
+ git add .
+ git commit -m "Deploy ${VERSION} (Online Editor + Standalone Editors + Chrome Extension editors)" \
+ || echo " (nothing to commit)"
+ git push origin gh-pages
+ )
+ echo "Pushed to GitHub Pages."
+
+ local accel_content="${REPO_ROOT}/packages/kie-sandbox-accelerator-quarkus/dist/git-repo-content"
+ if [[ -d "${accel_content}" ]]; then
+ local auth_accel="${accelerator_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --depth 1 "${auth_accel}" "${tmp_dir}/accelerator"
+ (
+ cd "${tmp_dir}/accelerator"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git checkout --orphan "${VERSION}"
+ cp -r "${accel_content}/." .
+ git add .
+ git commit -m "Apache KIE Sandbox Quarkus Accelerator ${VERSION}"
+ git tag "${VERSION}"
+ git push origin "${VERSION}"
+ )
+ echo "Pushed accelerator to tag ${VERSION}."
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 7. kn-plugin-workflow
+# ---------------------------------------------------------------------------
+release_kn_plugin_workflow() {
+ echo ""
+ echo "--- kn-plugin-workflow ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building kn-plugin-workflow..."
+ pnpm -F "@kie-tools/kn-plugin-workflow..." build:prod
+ fi
+
+ local dist="${REPO_ROOT}/packages/kn-plugin-workflow/dist"
+ if [[ ! -d "${dist}" ]]; then
+ echo "SKIP: packages/kn-plugin-workflow/dist not found"
+ return 0
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ declare -A RC_ZIPS=(
+ ["kn-workflow-linux-amd64"]="apache-kie-${VERSION}-incubating-sonataflow-knative-plugin-linux-x86.zip"
+ ["kn-workflow-darwin-amd64"]="apache-kie-${VERSION}-incubating-sonataflow-knative-plugin-macOS-x86.zip"
+ ["kn-workflow-darwin-arm64"]="apache-kie-${VERSION}-incubating-sonataflow-knative-plugin-macOS-arm64.zip"
+ ["kn-workflow-windows-amd64.exe"]="apache-kie-${VERSION}-incubating-sonataflow-knative-plugin-windows-x86.zip"
+ )
+ (cd "${dist}" && for binary in "${!RC_ZIPS[@]}"; do
+ local zip_name="${RC_ZIPS[$binary]}"
+ [[ -f "${binary}" ]] && zip "${ARTIFACTS_DIR}/${zip_name}" "${binary}" \
+ && echo " ZIP ${zip_name}" || echo " SKIP ${binary} (not found)"
+ done)
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" || -z "${UPLOAD_URL:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN and --upload-url are required for publishing kn-plugin-workflow"
+ return 1
+ fi
+ (cd "${dist}"
+ upload_github_asset "kn-workflow-linux-amd64" "kn-workflow-linux-amd64-${VERSION}"
+ upload_github_asset "kn-workflow-darwin-amd64" "kn-workflow-darwin-amd64-${VERSION}"
+ upload_github_asset "kn-workflow-darwin-arm64" "kn-workflow-darwin-arm64-${VERSION}"
+ upload_github_asset "kn-workflow-windows-amd64.exe" "kn-workflow-windows-amd64-${VERSION}.exe"
+ )
+ echo "kn-plugin-workflow binaries uploaded."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 8. dev-deployment-upload-service
+# ---------------------------------------------------------------------------
+release_dev_deployment_upload_service() {
+ echo ""
+ echo "--- dev-deployment-upload-service ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building dev-deployment-upload-service..."
+ DDUS_VERSION="${VERSION}" pnpm -F "@kie-tools/dev-deployment-upload-service..." build:prod
+ fi
+
+ local dist="${REPO_ROOT}/packages/dev-deployment-upload-service/dist"
+ if [[ ! -d "${dist}" ]]; then
+ echo "SKIP: packages/dev-deployment-upload-service/dist not found"
+ return 0
+ fi
+
+ declare -A PLATFORM_MAP=(
+ ["dev-deployment-upload-service-darwin-arm64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-macOS-arm64.tar.gz"
+ ["dev-deployment-upload-service-darwin-amd64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-macOS-x86.tar.gz"
+ ["dev-deployment-upload-service-linux-amd64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-linux-x86.tar.gz"
+ ["dev-deployment-upload-service-windows-amd64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-windows-x86.tar.gz"
+ )
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_name in "${!PLATFORM_MAP[@]}"; do
+ local rc_name="${PLATFORM_MAP[$src_name]}"
+ [[ -f "${dist}/${src_name}" ]] && cp "${dist}/${src_name}" "${ARTIFACTS_DIR}/${rc_name}" \
+ && echo " COPY ${rc_name}" || echo " SKIP ${src_name} (not found)"
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" || -z "${UPLOAD_URL:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN and --upload-url are required for publishing dev-deployment-upload-service"
+ return 1
+ fi
+ for src_name in "${!PLATFORM_MAP[@]}"; do
+ upload_github_asset "${dist}/${src_name}" "${src_name}" "application/gzip"
+ done
+ echo "dev-deployment-upload-service binaries uploaded."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 9. Source tarball (always created; no --publish step)
+# ---------------------------------------------------------------------------
+release_source_tarball() {
+ echo ""
+ echo "--- Source tarball ---"
+ mkdir -p "${ARTIFACTS_DIR}"
+ local zipfile="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-sources.zip"
+ echo "Creating git archive..."
+ git archive --format=zip --prefix="apache-kie-${VERSION}-incubating/" HEAD > "${zipfile}"
Review Comment:
This archives committed `HEAD`, but the release version and dependency updates performed earlier modify only the working tree. The voted source archive will therefore contain the pre-release versions while the binary artifacts were built from the modified release-versioned tree. Create the source archive from the exact release commit/tag, or otherwise include the validated working-tree updates in the archive.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,743 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY, HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local access_token
+ access_token=$(curl -sS -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token" \
+ | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local state
+ state=$(curl -sS -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}" | grep -o '"uploadState":"[^"]*"' | cut -d'"' -f4)
+ [[ "${state}" == "SUCCESS" ]] || { echo "ERROR: upload failed for ${ext_id}"; return 1; }
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ curl -sS -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length:0" > /dev/null
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["kogito-base-builder"]="packages/kogito-base-builder-image"
+ ["kogito-data-index-ephemeral"]="packages/kogito-data-index-ephemeral-image"
+ ["kogito-data-index-postgresql"]="packages/kogito-data-index-postgresql-image"
+ ["kogito-jit-runner"]="packages/kogito-jit-runner-image"
+ ["kogito-jobs-service-allinone"]="packages/kogito-jobs-service-allinone-image"
+ ["kogito-jobs-service-ephemeral"]="packages/kogito-jobs-service-ephemeral-image"
+ ["kogito-jobs-service-postgresql"]="packages/kogito-jobs-service-postgresql-image"
+ ["kogito-management-console"]="packages/kogito-management-console"
+ ["kogito-db-migrator-tool"]="packages/kogito-db-migrator-tool-image"
+ ["cors-proxy"]="packages/cors-proxy-image"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || { echo " SKIP ${artifact_name} (${pkg_path} not found)"; continue; }
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}/container-images"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || continue
+ local full_image="${REGISTRY}/incubator-kie-${artifact_name}:${VERSION}"
+ docker tag "$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null | sed 's|@kie-tools/||')" "${full_image}" 2>/dev/null \
+ || docker tag "incubator-kie-${artifact_name}:latest" "${full_image}" 2>/dev/null \
+ || docker tag "apache/incubator-kie-${artifact_name}:main" "${full_image}" 2>/dev/null \
+ || echo " WARN could not tag ${artifact_name}"
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${full_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${full_image}" | gzip > "${tarball}"
+ else
+ echo " SKIP ${artifact_name} (image not found locally)"
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || continue
+ local full_image="${REGISTRY}/incubator-kie-${artifact_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}/helm-charts"
+ mkdir -p "${output_dir}"
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo " SKIP ${slug} (${chart_pkg} not found)"; continue; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo " SKIP ${slug} (Chart.yaml not found)"; continue; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${output_dir}"
+ local found
+ found=$(find "${output_dir}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ [[ -n "${found}" ]] && mv "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz" \
+ && echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${HELM_REGISTRY:-}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${HELM_REGISTRY}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for slug in "${!CHARTS[@]}"; do
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ [[ -f "${tarball}" ]] && helm push "${tarball}" "oci://${HELM_REGISTRY}" && echo " PUSH ${tarball}"
+ done
+ echo "Helm charts pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, kie-editors-standalone, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${VERSION}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="incubator-kie-sandbox-dev-deployment-base"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${VERSION}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/kie-editors-standalone..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-editors-standalone/dist:apache-kie-${VERSION}-incubating-business-automation-standalone-editors.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
+ fi
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN is required for publishing to GitHub Pages"
+ return 1
+ fi
+
+ local kogito_online_repo="https://github.com/apache/incubator-kie-kogito-online.git"
+ local accelerator_repo="https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+
+ local auth_url="${kogito_online_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --branch gh-pages --depth 1 "${auth_url}" "${tmp_dir}/kogito-online"
+ (
+ cd "${tmp_dir}/kogito-online"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ find . -maxdepth 1 ! -name '.' ! -name 'dev' ! -name 'editors' ! -name 'standalone' \
+ ! -name 'chrome-extension' ! -name '.nojekyll' ! -name 'CNAME' -exec rm -rf {} +
+
+ local online_dist="${REPO_ROOT}/packages/online-editor/dist"
+ if [[ -d "${online_dist}" ]]; then
+ local editors_dir="editors/${VERSION}"
+ rm -rf "${editors_dir}" && mkdir -p "${editors_dir}"
+ rm -rf editors/latest && ln -s "${VERSION}" editors/latest
+ cp -r "${online_dist}/." .
+ fi
+ local standalone_dist="${REPO_ROOT}/packages/kie-editors-standalone/dist"
+ [[ -d "${standalone_dist}" ]] && cp -r "${standalone_dist}/." "editors/${VERSION}/"
+ local chrome_dist="${REPO_ROOT}/packages/chrome-extension-pack-kogito-kie-editors/dist"
+ if [[ -d "${chrome_dist}" ]]; then
+ local chrome_dir="chrome-extension/${VERSION}"
+ rm -rf "${chrome_dir}" && mkdir -p "${chrome_dir}"
+ cp -r "${chrome_dist}/fonts" "${chrome_dir}/" 2>/dev/null || true
+ cp "${chrome_dist}"/*-envelope.* "${chrome_dir}/" 2>/dev/null || true
+ fi
+ git add .
+ git commit -m "Deploy ${VERSION} (Online Editor + Standalone Editors + Chrome Extension editors)" \
+ || echo " (nothing to commit)"
+ git push origin gh-pages
+ )
+ echo "Pushed to GitHub Pages."
+
+ local accel_content="${REPO_ROOT}/packages/kie-sandbox-accelerator-quarkus/dist/git-repo-content"
+ if [[ -d "${accel_content}" ]]; then
+ local auth_accel="${accelerator_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --depth 1 "${auth_accel}" "${tmp_dir}/accelerator"
+ (
+ cd "${tmp_dir}/accelerator"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git checkout --orphan "${VERSION}"
+ cp -r "${accel_content}/." .
+ git add .
+ git commit -m "Apache KIE Sandbox Quarkus Accelerator ${VERSION}"
+ git tag "${VERSION}"
+ git push origin "${VERSION}"
+ )
+ echo "Pushed accelerator to tag ${VERSION}."
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 7. kn-plugin-workflow
+# ---------------------------------------------------------------------------
+release_kn_plugin_workflow() {
+ echo ""
+ echo "--- kn-plugin-workflow ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building kn-plugin-workflow..."
+ pnpm -F "@kie-tools/kn-plugin-workflow..." build:prod
+ fi
+
+ local dist="${REPO_ROOT}/packages/kn-plugin-workflow/dist"
+ if [[ ! -d "${dist}" ]]; then
+ echo "SKIP: packages/kn-plugin-workflow/dist not found"
+ return 0
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ declare -A RC_ZIPS=(
+ ["kn-workflow-linux-amd64"]="apache-kie-${VERSION}-incubating-sonataflow-knative-plugin-linux-x86.zip"
+ ["kn-workflow-darwin-amd64"]="apache-kie-${VERSION}-incubating-sonataflow-knative-plugin-macOS-x86.zip"
+ ["kn-workflow-darwin-arm64"]="apache-kie-${VERSION}-incubating-sonataflow-knative-plugin-macOS-arm64.zip"
+ ["kn-workflow-windows-amd64.exe"]="apache-kie-${VERSION}-incubating-sonataflow-knative-plugin-windows-x86.zip"
+ )
+ (cd "${dist}" && for binary in "${!RC_ZIPS[@]}"; do
+ local zip_name="${RC_ZIPS[$binary]}"
+ [[ -f "${binary}" ]] && zip "${ARTIFACTS_DIR}/${zip_name}" "${binary}" \
+ && echo " ZIP ${zip_name}" || echo " SKIP ${binary} (not found)"
+ done)
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" || -z "${UPLOAD_URL:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN and --upload-url are required for publishing kn-plugin-workflow"
+ return 1
+ fi
+ (cd "${dist}"
+ upload_github_asset "kn-workflow-linux-amd64" "kn-workflow-linux-amd64-${VERSION}"
+ upload_github_asset "kn-workflow-darwin-amd64" "kn-workflow-darwin-amd64-${VERSION}"
+ upload_github_asset "kn-workflow-darwin-arm64" "kn-workflow-darwin-arm64-${VERSION}"
+ upload_github_asset "kn-workflow-windows-amd64.exe" "kn-workflow-windows-amd64-${VERSION}.exe"
+ )
+ echo "kn-plugin-workflow binaries uploaded."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 8. dev-deployment-upload-service
+# ---------------------------------------------------------------------------
+release_dev_deployment_upload_service() {
+ echo ""
+ echo "--- dev-deployment-upload-service ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building dev-deployment-upload-service..."
+ DDUS_VERSION="${VERSION}" pnpm -F "@kie-tools/dev-deployment-upload-service..." build:prod
+ fi
+
+ local dist="${REPO_ROOT}/packages/dev-deployment-upload-service/dist"
+ if [[ ! -d "${dist}" ]]; then
+ echo "SKIP: packages/dev-deployment-upload-service/dist not found"
+ return 0
+ fi
+
+ declare -A PLATFORM_MAP=(
+ ["dev-deployment-upload-service-darwin-arm64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-macOS-arm64.tar.gz"
+ ["dev-deployment-upload-service-darwin-amd64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-macOS-x86.tar.gz"
+ ["dev-deployment-upload-service-linux-amd64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-linux-x86.tar.gz"
+ ["dev-deployment-upload-service-windows-amd64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-windows-x86.tar.gz"
+ )
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_name in "${!PLATFORM_MAP[@]}"; do
+ local rc_name="${PLATFORM_MAP[$src_name]}"
+ [[ -f "${dist}/${src_name}" ]] && cp "${dist}/${src_name}" "${ARTIFACTS_DIR}/${rc_name}" \
+ && echo " COPY ${rc_name}" || echo " SKIP ${src_name} (not found)"
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" || -z "${UPLOAD_URL:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN and --upload-url are required for publishing dev-deployment-upload-service"
+ return 1
+ fi
+ for src_name in "${!PLATFORM_MAP[@]}"; do
+ upload_github_asset "${dist}/${src_name}" "${src_name}" "application/gzip"
+ done
+ echo "dev-deployment-upload-service binaries uploaded."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 9. Source tarball (always created; no --publish step)
+# ---------------------------------------------------------------------------
+release_source_tarball() {
+ echo ""
+ echo "--- Source tarball ---"
+ mkdir -p "${ARTIFACTS_DIR}"
+ local zipfile="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-sources.zip"
+ echo "Creating git archive..."
+ git archive --format=zip --prefix="apache-kie-${VERSION}-incubating/" HEAD > "${zipfile}"
+ if ! unzip -t "${zipfile}" > /dev/null 2>&1; then
+ echo "ERROR: Source zip verification failed"
+ return 1
+ fi
+ local size
+ size=$(du -h "${zipfile}" | cut -f1)
+ echo "Created: ${zipfile} (${size})"
+ for required in LICENSE NOTICE DISCLAIMER-WIP; do
+ unzip -l "${zipfile}" | grep -q "apache-kie-${VERSION}-incubating/${required}" \
+ || echo "WARN: ${required} not found in source zip"
+ done
+}
+
+# ---------------------------------------------------------------------------
+# Run all release steps
+# ---------------------------------------------------------------------------
+FAILED=()
+run_step() {
+ local name="$1" fn="$2"
+ if "${fn}"; then
+ echo "✅ ${name} — OK"
+ else
+ echo "❌ ${name} — FAILED"
+ FAILED+=("${name}")
+ fi
+}
Review Comment:
Calling the step function as the `if` condition disables Bash's `errexit` behavior for commands inside that function. A failed build or publish command can therefore continue to a later successful `echo`, causing this wrapper to report the component as OK. Run each function in a separate `set -e` subshell outside the conditional, capture its status, and then branch on that status.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,743 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY, HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local access_token
+ access_token=$(curl -sS -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token" \
+ | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
Review Comment:
The Google token response is normal JSON and commonly contains whitespace after the colon, so this exact `"access_token":"..."` grep can return an empty token even on a successful response. The existing Jenkins Chrome utility uses `jq`; parse the field structurally and make HTTP errors fail.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,743 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY, HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local access_token
+ access_token=$(curl -sS -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token" \
+ | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local state
+ state=$(curl -sS -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}" | grep -o '"uploadState":"[^"]*"' | cut -d'"' -f4)
Review Comment:
The upload response is also parsed with a whitespace-sensitive grep, so a valid `"uploadState": "SUCCESS"` response is treated as failure. Parse `.uploadState` with `jq` and use `curl -f` so HTTP errors are not mistaken for JSON responses.
##########
scripts/release/CHROME_STORE_SETUP.md:
##########
@@ -0,0 +1,216 @@
+<!--
+ 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.
+-->
+
+# Chrome Web Store Publishing Setup
+
+## Overview
+
+The `release-chrome-extensions.sh` script can now publish extensions to the Chrome Web Store using the Chrome Web Store API.
Review Comment:
This guide and all of its usage examples reference `release-chrome-extensions.sh`, but that script does not exist in the repository; Chrome publishing is implemented by `release-all.sh`. The guide also documents `CHROME_SWF_EDITOR_EXTENSION_ID`, which the unified script never reads. Update the guide to the actual command and supported credentials so operators do not follow a failing procedure.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,743 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY, HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local access_token
+ access_token=$(curl -sS -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token" \
+ | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local state
+ state=$(curl -sS -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}" | grep -o '"uploadState":"[^"]*"' | cut -d'"' -f4)
+ [[ "${state}" == "SUCCESS" ]] || { echo "ERROR: upload failed for ${ext_id}"; return 1; }
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ curl -sS -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length:0" > /dev/null
Review Comment:
This discards the publish response and uses `curl` without `-f`, so HTTP 4xx/5xx responses and API-level rejection statuses still lead to “Chrome extension published.” The former pipeline accepted only `OK` or `PUBLISHED_WITH_FRICTION_WARNING`; retain that validation here.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,743 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY, HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local access_token
+ access_token=$(curl -sS -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token" \
+ | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local state
+ state=$(curl -sS -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}" | grep -o '"uploadState":"[^"]*"' | cut -d'"' -f4)
+ [[ "${state}" == "SUCCESS" ]] || { echo "ERROR: upload failed for ${ext_id}"; return 1; }
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ curl -sS -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length:0" > /dev/null
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["kogito-base-builder"]="packages/kogito-base-builder-image"
+ ["kogito-data-index-ephemeral"]="packages/kogito-data-index-ephemeral-image"
+ ["kogito-data-index-postgresql"]="packages/kogito-data-index-postgresql-image"
+ ["kogito-jit-runner"]="packages/kogito-jit-runner-image"
+ ["kogito-jobs-service-allinone"]="packages/kogito-jobs-service-allinone-image"
+ ["kogito-jobs-service-ephemeral"]="packages/kogito-jobs-service-ephemeral-image"
+ ["kogito-jobs-service-postgresql"]="packages/kogito-jobs-service-postgresql-image"
+ ["kogito-management-console"]="packages/kogito-management-console"
+ ["kogito-db-migrator-tool"]="packages/kogito-db-migrator-tool-image"
+ ["cors-proxy"]="packages/cors-proxy-image"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || { echo " SKIP ${artifact_name} (${pkg_path} not found)"; continue; }
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}/container-images"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || continue
+ local full_image="${REGISTRY}/incubator-kie-${artifact_name}:${VERSION}"
+ docker tag "$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null | sed 's|@kie-tools/||')" "${full_image}" 2>/dev/null \
+ || docker tag "incubator-kie-${artifact_name}:latest" "${full_image}" 2>/dev/null \
+ || docker tag "apache/incubator-kie-${artifact_name}:main" "${full_image}" 2>/dev/null \
+ || echo " WARN could not tag ${artifact_name}"
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${full_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${full_image}" | gzip > "${tarball}"
+ else
+ echo " SKIP ${artifact_name} (image not found locally)"
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local pkg_path="${IMAGES[$artifact_name]}"
+ [[ -d "${pkg_path}" ]] || continue
+ local full_image="${REGISTRY}/incubator-kie-${artifact_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}/helm-charts"
+ mkdir -p "${output_dir}"
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo " SKIP ${slug} (${chart_pkg} not found)"; continue; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo " SKIP ${slug} (Chart.yaml not found)"; continue; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${output_dir}"
+ local found
+ found=$(find "${output_dir}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ [[ -n "${found}" ]] && mv "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz" \
+ && echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${HELM_REGISTRY:-}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${HELM_REGISTRY}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for slug in "${!CHARTS[@]}"; do
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ [[ -f "${tarball}" ]] && helm push "${tarball}" "oci://${HELM_REGISTRY}" && echo " PUSH ${tarball}"
+ done
+ echo "Helm charts pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, kie-editors-standalone, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${VERSION}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="incubator-kie-sandbox-dev-deployment-base"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${VERSION}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/kie-editors-standalone..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-editors-standalone/dist:apache-kie-${VERSION}-incubating-business-automation-standalone-editors.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
+ fi
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN is required for publishing to GitHub Pages"
+ return 1
+ fi
+
+ local kogito_online_repo="https://github.com/apache/incubator-kie-kogito-online.git"
+ local accelerator_repo="https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+
+ local auth_url="${kogito_online_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --branch gh-pages --depth 1 "${auth_url}" "${tmp_dir}/kogito-online"
+ (
+ cd "${tmp_dir}/kogito-online"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ find . -maxdepth 1 ! -name '.' ! -name 'dev' ! -name 'editors' ! -name 'standalone' \
+ ! -name 'chrome-extension' ! -name '.nojekyll' ! -name 'CNAME' -exec rm -rf {} +
+
+ local online_dist="${REPO_ROOT}/packages/online-editor/dist"
+ if [[ -d "${online_dist}" ]]; then
+ local editors_dir="editors/${VERSION}"
+ rm -rf "${editors_dir}" && mkdir -p "${editors_dir}"
+ rm -rf editors/latest && ln -s "${VERSION}" editors/latest
+ cp -r "${online_dist}/." .
+ fi
+ local standalone_dist="${REPO_ROOT}/packages/kie-editors-standalone/dist"
+ [[ -d "${standalone_dist}" ]] && cp -r "${standalone_dist}/." "editors/${VERSION}/"
+ local chrome_dist="${REPO_ROOT}/packages/chrome-extension-pack-kogito-kie-editors/dist"
+ if [[ -d "${chrome_dist}" ]]; then
+ local chrome_dir="chrome-extension/${VERSION}"
+ rm -rf "${chrome_dir}" && mkdir -p "${chrome_dir}"
+ cp -r "${chrome_dist}/fonts" "${chrome_dir}/" 2>/dev/null || true
+ cp "${chrome_dist}"/*-envelope.* "${chrome_dir}/" 2>/dev/null || true
+ fi
+ git add .
+ git commit -m "Deploy ${VERSION} (Online Editor + Standalone Editors + Chrome Extension editors)" \
+ || echo " (nothing to commit)"
+ git push origin gh-pages
+ )
+ echo "Pushed to GitHub Pages."
+
+ local accel_content="${REPO_ROOT}/packages/kie-sandbox-accelerator-quarkus/dist/git-repo-content"
+ if [[ -d "${accel_content}" ]]; then
+ local auth_accel="${accelerator_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --depth 1 "${auth_accel}" "${tmp_dir}/accelerator"
+ (
+ cd "${tmp_dir}/accelerator"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git checkout --orphan "${VERSION}"
+ cp -r "${accel_content}/." .
+ git add .
+ git commit -m "Apache KIE Sandbox Quarkus Accelerator ${VERSION}"
+ git tag "${VERSION}"
+ git push origin "${VERSION}"
Review Comment:
After creating both a branch and a tag named `${VERSION}`, `git push origin "${VERSION}"` is ambiguous (`src refspec ... matches more than one`) and does not reliably publish the tag. Push both refs explicitly so the accelerator release step can complete.
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] [incubator-kie-tools#4025] 10.3.x+ stream: Re-write `kie-tools` release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "Copilot (via GitHub) via commits" <co...@kie.apache.org>.
Copilot commented on code in PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#discussion_r4492234000
##########
.ci/jenkins/Jenkinsfile.103xplus.release-candidate:
##########
@@ -0,0 +1,377 @@
+/*
+ * 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.
+ */
+
+@Library('jenkins-pipeline-shared-libraries')_
+
+// PHASE 1: Create Release Candidate for Apache Voting
+// This job builds artifacts, signs them, and publishes to Apache SVN for community vote
+// Does NOT publish to npm, VSCode Marketplace, or Chrome Web Store
+//
+// NOTE: kogito-data-index-ephemeral is NOT included in this pipeline because the
+// packages/kogito-data-index-ephemeral-image package no longer exists in this repo.
+// Its presence in older Jenkinsfiles (Jenkinsfile.release-candidate RUNNERS list and
+// release-jobs/Jenkinsfile.kogito-data-index-ephemeral-image) is a historical artifact
+// from pre-10.3 automation and can be cleaned up in a follow-up PR.
+
+pipeline {
+ agent {
+ docker {
+ image 'docker.io/apache/incubator-kie-tools-ci-build:main'
+ args '--shm-size=2g --privileged --group-add docker'
+ label util.avoidFaultyNodes()
+ }
+ }
+
+ options {
+ timeout(time: 480, unit: 'MINUTES')
+ }
+
+ parameters {
+ string(
+ name: 'RELEASE_VERSION',
+ description: 'MANDATORY. Exact release version, e.g. 10.3.0',
+ defaultValue: '0.0.0'
+ )
+ string(
+ name: 'TAG_NAME',
+ description: 'MANDATORY. RC tag name, e.g. 10.3.0-rc1 (must start with RELEASE_VERSION and be a new tag)',
+ defaultValue: '0.0.0-rc1'
+ )
+ booleanParam(
+ name: 'CREATE_STREAM_BRANCH',
+ description: 'Branch cut, part 1: create branch X.Y.x (derived from RELEASE_VERSION) from main and push it. Only for the first RC of a new minor (e.g. 10.3.0-rc1). Fails if X.Y.x already exists',
+ defaultValue: false
+ )
+ booleanParam(
+ name: 'UPDATE_STREAM_VERSION',
+ description: 'Branch cut, part 2: set the development version in all files on X.Y.x (project version X.Y.999, kogito maven X.Y.999-SNAPSHOT, stream name X.Y.x), commit and push. Use together with CREATE_STREAM_BRANCH for a normal branch cut, or alone if X.Y.x was already created',
+ defaultValue: false
+ )
+ }
+
+ environment {
+ KIE_TOOLS_BUILD__runLinters = 'false'
+ KIE_TOOLS_BUILD__runTests = 'false'
+ KIE_TOOLS_BUILD__runEndToEndTests = 'false'
+ KIE_TOOLS_BUILD__buildContainerImages = 'true'
+
+ CHROME_EXTENSION__routerTargetOrigin = 'https://apache.github.io'
+ CHROME_EXTENSION__routerRelativePath = "incubator-kie-kogito-online/chrome-extension/${params.RELEASE_VERSION}"
+ CHROME_EXTENSION__manifestFile = 'manifest.prod.json'
+ CHROME_EXTENSION__onlineEditorUrl = 'https://apache.github.io/incubator-kie-kogito-online'
+
+ ONLINE_EDITOR__corsProxyUrl = 'https://cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud'
+ ONLINE_EDITOR__buildInfo = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__extendedServicesCompatibleVersion = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentBaseImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentBaseImageName = 'incubator-kie-sandbox-dev-deployment-base'
+ ONLINE_EDITOR__devDeploymentBaseImageTag = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'incubator-kie-sandbox-dev-deployment-dmn-form-webapp'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName = 'incubator-kie-sandbox-dev-deployment-quarkus-blank-app'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag = "${params.RELEASE_VERSION}"
+ }
+
+ stages {
+ stage('Load local shared scripts') {
+ steps {
+ script {
+ pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy'
+ buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy'
+ githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy'
+ releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy'
+ }
+ }
+ }
+
+ // The stream branch X.Y.x is derived from RELEASE_VERSION. A branch cut starts from main;
+ // every other run (rc2, patch releases) builds from the existing X.Y.x.
+ stage('Resolve release ref') {
+ steps {
+ script {
+ def m = (params.RELEASE_VERSION =~ /^(\d+)\.(\d+)\..+$/)
+ if (!m.matches()) {
+ error("Cannot derive stream branch from RELEASE_VERSION '${params.RELEASE_VERSION}' (expected e.g. 10.3.0)")
+ }
+ streamBranch = "${m.group(1)}.${m.group(2)}.x"
+ devVersion = "${m.group(1)}.${m.group(2)}.999"
+ m = null
+
+ if (!params.TAG_NAME.startsWith(params.RELEASE_VERSION)) {
+ error("TAG_NAME '${params.TAG_NAME}' must start with RELEASE_VERSION '${params.RELEASE_VERSION}'")
Review Comment:
`startsWith` accepts the final version itself as `TAG_NAME`, such as `10.3.0`. That makes this job push the official release tag before the vote, and the promote job subsequently cannot create that tag. Validate the complete RC tag format, including the `-rc` suffix and candidate number, before any branch or tag is pushed.
##########
.ci/jenkins/Jenkinsfile.103xplus.release-promote:
##########
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+@Library('jenkins-pipeline-shared-libraries')_
+
+// 10.3.x+ promote / tag-release pipeline for incubator-kie-tools.
+// Run after the IPMC vote passes to:
+// 1. Create and push the official release Git tag (e.g. 10.3.0) pointing to the approved RC commit.
+// 2. Create the GitHub Release for incubator-kie-tools (required before release-publish).
+
+pipeline {
+ agent {
+ label util.avoidFaultyNodes()
+ }
+
+ options {
+ timeout(time: 60, unit: 'MINUTES')
+ }
+
+ parameters {
+ string(
+ name: 'RELEASE_VERSION',
+ description: 'Release version (e.g., 10.3.0)',
+ defaultValue: ''
+ )
+ string(
+ name: 'BASE_REF',
+ description: 'Approved RC tag (e.g. 10.3.0-rc1) to tag from',
+ defaultValue: ''
+ )
+ }
+
+ stages {
+ stage('Validate parameters') {
+ steps {
+ script {
+ if (params.RELEASE_VERSION == '') {
+ error("RELEASE_VERSION is required (e.g. 10.3.0)")
+ }
+ if (params.BASE_REF == '') {
+ error("BASE_REF (approved RC tag, e.g. 10.3.0-rc1) is required")
+ }
+ if (!params.BASE_REF.startsWith("${params.RELEASE_VERSION}-rc")) {
+ error("BASE_REF '${params.BASE_REF}' must be an RC tag for RELEASE_VERSION '${params.RELEASE_VERSION}' (e.g. ${params.RELEASE_VERSION}-rc1)")
+ }
+ currentBuild.displayName = "${params.RELEASE_VERSION} (${params.BASE_REF})"
+ }
+ }
+ }
+
+ stage('Load local shared scripts') {
+ steps {
+ script {
+ pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy'
+ githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy'
+ }
+ }
+ }
+
+ stage('Clean workspace') {
+ steps {
+ cleanWs(deleteDirs: true, disableDeferredWipeout: true)
+ }
+ }
+
+ stage('Checkout incubator-kie-tools') {
+ steps {
+ dir('kie-tools') {
+ script {
+ githubUtils.checkoutRepo(
+ "https://github.com/${pipelineVars.githubRepositorySlug}.git",
+ "${params.BASE_REF}",
+ "${pipelineVars.asfGithubPushCredentialsId}"
+ )
+ }
+ }
+ }
+ }
+
+ stage('Tag final release') {
+ steps {
+ dir('kie-tools') {
+ script {
+ withCredentials([
+ usernamePassword(credentialsId: "${pipelineVars.asfGithubPushCredentialsId}", usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN')
+ ]) {
+ sh """#!/bin/bash -el
+ set -euo pipefail
+ git config user.email "dev@kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git config --local credential.helper '!f() { echo username=\$GITHUB_USER; echo password=\$GITHUB_TOKEN; }; f'
+
+ # Fetch the approved RC tag
+ git fetch origin "refs/tags/${params.BASE_REF}:refs/tags/${params.BASE_REF}"
+
+ # Create and push the final release tag pointing to the RC commit
+ git tag -a "${params.RELEASE_VERSION}" "${params.BASE_REF}" -m "Release ${params.RELEASE_VERSION}"
+ git push origin "${params.RELEASE_VERSION}"
Review Comment:
If pushing the final tag succeeds but GitHub Release creation subsequently fails, rerunning this job stops here with an already-existing-tag error and never reaches the failed stage. The pushed tag persists despite workspace cleanup. Make promotion retry-safe: accept an existing final tag only when its dereferenced commit matches the approved RC, and create the GitHub Release only if missing rather than deleting an existing release and its assets.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,810 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+if [[ "${PUBLISH}" == "true" && -z "${UPLOAD_URL}" ]]; then
+ echo "ERROR: --upload-url is required when --publish is specified."
+ exit 1
+fi
+
+# Production defaults for Chrome extension and Online Editor builds
+export CHROME_EXTENSION__routerTargetOrigin="${CHROME_EXTENSION__routerTargetOrigin:-https://apache.github.io}"
+export CHROME_EXTENSION__routerRelativePath="${CHROME_EXTENSION__routerRelativePath:-incubator-kie-kogito-online/chrome-extension/${VERSION}}"
+export CHROME_EXTENSION__manifestFile="${CHROME_EXTENSION__manifestFile:-manifest.prod.json}"
+export CHROME_EXTENSION__onlineEditorUrl="${CHROME_EXTENSION__onlineEditorUrl:-https://apache.github.io/incubator-kie-kogito-online}"
Review Comment:
The production defaults here omit the accelerator settings, which are exported only after the Sandbox image has been built. Without external overrides, that image embeds the development repository URL and `main` ref, and the later Pages build does not repair it. Initialize the accelerator defaults here, before any build, so the image uses the released accelerator.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,810 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+if [[ "${PUBLISH}" == "true" && -z "${UPLOAD_URL}" ]]; then
+ echo "ERROR: --upload-url is required when --publish is specified."
+ exit 1
+fi
+
+# Production defaults for Chrome extension and Online Editor builds
+export CHROME_EXTENSION__routerTargetOrigin="${CHROME_EXTENSION__routerTargetOrigin:-https://apache.github.io}"
+export CHROME_EXTENSION__routerRelativePath="${CHROME_EXTENSION__routerRelativePath:-incubator-kie-kogito-online/chrome-extension/${VERSION}}"
+export CHROME_EXTENSION__manifestFile="${CHROME_EXTENSION__manifestFile:-manifest.prod.json}"
+export CHROME_EXTENSION__onlineEditorUrl="${CHROME_EXTENSION__onlineEditorUrl:-https://apache.github.io/incubator-kie-kogito-online}"
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1 || true)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ [[ -d "packages/chrome-extension-pack-kogito-kie-editors/dist/fonts" ]] && cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/fonts "${tmp_kie}/"
+ cp packages/chrome-extension-pack-kogito-kie-editors/dist/*-envelope.* "${tmp_kie}/" 2>/dev/null || true
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1 || true)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}"
+ mkdir -p "${output_dir}"
+
+ local helm_tmp_pkg
+ helm_tmp_pkg=$(mktemp -d)
+ trap "rm -rf ${helm_tmp_pkg}" EXIT
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo "ERROR: Helm chart directory ${chart_pkg} not found"; return 1; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo "ERROR: Chart.yaml not found in ${chart_pkg}"; return 1; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local chart_name
+ chart_name=$(grep '^name:' "${chart_yaml}" | head -1 | awk '{print $2}' | tr -d '"'"'")
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${helm_tmp_pkg}"
+ rm -rf "${tmp_dir}"
+
+ local found=""
+ if [[ -n "${chart_name}" && -f "${helm_tmp_pkg}/${chart_name}-${VERSION}.tgz" ]]; then
+ found="${helm_tmp_pkg}/${chart_name}-${VERSION}.tgz"
+ else
+ found=$(find "${helm_tmp_pkg}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ fi
+ if [[ -n "${found}" ]]; then
+ cp "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ else
+ echo "ERROR: Failed to package helm chart for ${slug}"
+ return 1
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ local helm_reg="${HELM_REGISTRY:-docker.io/apache}"
+ if [[ -z "${helm_reg}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${helm_reg}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for tgz in "${helm_tmp_pkg}"/*-"${VERSION}".tgz; do
+ if [[ -f "${tgz}" ]]; then
+ echo " PUSH ${tgz} -> oci://${helm_reg}"
+ helm push "${tgz}" "oci://${helm_reg}"
+ fi
+ done
+ echo "Helm charts pushed."
+ fi
+ rm -rf "${helm_tmp_pkg}"
+ trap - EXIT
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${ONLINE_EDITOR__buildInfo:-${VERSION}}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${ONLINE_EDITOR__extendedServicesCompatibleVersion:-${VERSION}}"
+ export ONLINE_EDITOR__corsProxyUrl="${ONLINE_EDITOR__corsProxyUrl:-https://cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud}"
+ export ONLINE_EDITOR__quarkusAcceleratorGitRepoUrl="${ONLINE_EDITOR__quarkusAcceleratorGitRepoUrl:-https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git}"
+ export ONLINE_EDITOR__quarkusAcceleratorGitRef="${ONLINE_EDITOR__quarkusAcceleratorGitRef:-${VERSION}}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="${ONLINE_EDITOR__devDeploymentBaseImageRegistry:-docker.io}"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="${ONLINE_EDITOR__devDeploymentBaseImageAccount:-apache}"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="${ONLINE_EDITOR__devDeploymentBaseImageName:-incubator-kie-sandbox-dev-deployment-base}"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${ONLINE_EDITOR__devDeploymentBaseImageTag:-${VERSION}}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="${ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry:-docker.io}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="${ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount:-apache}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="${ONLINE_EDITOR__devDeploymentDmnFormWebappImageName:-incubator-kie-sandbox-dev-deployment-dmn-form-webapp}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag:-${VERSION}}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="${ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry:-docker.io}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="${ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount:-apache}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="${ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName:-incubator-kie-sandbox-dev-deployment-quarkus-blank-app}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag:-${VERSION}}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
Review Comment:
With `--rc --skip-build`, a missing webapp or accelerator output directory is silently omitted while this step reports success. Return an error for an absent required build directory so an incomplete candidate is not reported as ready.
##########
.ci/jenkins/Jenkinsfile.103xplus.release-publish:
##########
@@ -0,0 +1,260 @@
+/*
+ * 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.
+ */
+
+@Library('jenkins-pipeline-shared-libraries')_
+
+// PHASE 2: Publish Release After Vote Passes
+// This job publishes the approved release candidate to public registries
+// Publishes to: npm, VSCode Marketplace, Chrome Web Store, GitHub Releases
+//
+// NOTE: kogito-data-index-ephemeral is NOT included in this pipeline because the
+// packages/kogito-data-index-ephemeral-image package no longer exists in this repo.
+// Its presence in older Jenkinsfiles (Jenkinsfile.release-candidate RUNNERS list and
+// release-jobs/Jenkinsfile.kogito-data-index-ephemeral-image) is a historical artifact
+// from pre-10.3 automation and can be cleaned up in a follow-up PR.
+
+pipeline {
+ agent {
+ docker {
+ image 'docker.io/apache/incubator-kie-tools-ci-build:main'
+ args '--shm-size=2g --privileged --group-add docker'
+ label util.avoidFaultyNodes()
+ }
+ }
+
+ options {
+ timeout(time: 480, unit: 'MINUTES')
+ }
+
+ parameters {
+ string(
+ name: 'RELEASE_VERSION',
+ description: 'Release version (e.g., 1.0.0) - must match the voted RC',
+ defaultValue: '0.0.0'
+ )
+ string(
+ name: 'BASE_REF',
+ description: 'Git tag to release from (should be the RC tag)',
+ defaultValue: '0.0.0-rc1'
+ )
+ string(
+ name: 'GITHUB_RELEASE_TAG',
+ description: 'GitHub release tag (required: binary assets are uploaded to this existing GitHub Release)',
+ defaultValue: ''
+ )
+ }
+
+ environment {
+ KIE_TOOLS_BUILD__runLinters = 'false'
+ KIE_TOOLS_BUILD__runTests = 'false'
+ KIE_TOOLS_BUILD__runEndToEndTests = 'false'
+ KIE_TOOLS_BUILD__buildContainerImages = 'true'
+
+ CHROME_EXTENSION__routerTargetOrigin = 'https://apache.github.io'
+ CHROME_EXTENSION__routerRelativePath = "incubator-kie-kogito-online/chrome-extension/${params.RELEASE_VERSION}"
+ CHROME_EXTENSION__manifestFile = 'manifest.prod.json'
+ CHROME_EXTENSION__onlineEditorUrl = 'https://apache.github.io/incubator-kie-kogito-online'
+
+ ONLINE_EDITOR__corsProxyUrl = 'https://cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud'
+ ONLINE_EDITOR__buildInfo = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__extendedServicesCompatibleVersion = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentBaseImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentBaseImageName = 'incubator-kie-sandbox-dev-deployment-base'
+ ONLINE_EDITOR__devDeploymentBaseImageTag = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'incubator-kie-sandbox-dev-deployment-dmn-form-webapp'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName = 'incubator-kie-sandbox-dev-deployment-quarkus-blank-app'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag = "${params.RELEASE_VERSION}"
+
+ DOCKER_CONFIG = "${WORKSPACE}/.docker"
+ HELM_REGISTRY = 'docker.io/apache'
+ }
+
+ stages {
+ stage('Validate parameters') {
+ steps {
+ script {
+ if (params.RELEASE_VERSION == '' || params.RELEASE_VERSION == '0.0.0') {
+ error("RELEASE_VERSION must be set to the voted release version")
+ }
+ if (params.BASE_REF == '' || params.BASE_REF == '0.0.0-rc1') {
+ error("BASE_REF must be set to the voted RC tag")
+ }
+ // release-all.sh --publish uploads binary assets to the GitHub Release and fails without it,
+ // after other registries would already have been published. Fail fast instead.
+ if (params.GITHUB_RELEASE_TAG == '') {
+ error("GITHUB_RELEASE_TAG must be set to an existing GitHub Release tag")
+ }
+ }
+ }
+ }
+
+ stage('Load local shared scripts') {
+ steps {
+ script {
+ pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy'
+ buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy'
+ githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy'
+ }
+ }
+ }
+
+ stage('Start required services') {
+ steps {
+ script {
+ buildUtils.startRequiredServices()
+ }
+ }
+ }
+
+ stage('Clean workspace') {
+ steps {
+ cleanWs(deleteDirs: true, disableDeferredWipeout: true)
+ }
+ }
+
+ stage('Checkout kie-tools') {
+ steps {
+ dir('kie-tools') {
+ script {
+ githubUtils.checkoutRepo(
+ "http://github.com/${pipelineVars.githubRepositorySlug}.git",
+ "${params.BASE_REF}",
+ "${pipelineVars.kieToolsBotGithubCredentialsId}"
+ )
+ }
+ }
+ }
+ }
+
+ stage('Verify version') {
+ steps {
+ dir('kie-tools') {
+ script {
+ def packageVersion = sh(
+ returnStdout: true,
+ script: "#!/bin/bash -el \n node -p \"require('./package.json').version\""
+ ).trim()
+
+ if (packageVersion != params.RELEASE_VERSION) {
+ error("Version mismatch! package.json has ${packageVersion} but RELEASE_VERSION is ${params.RELEASE_VERSION}")
+ }
+
+ echo "✅ Version verified: ${packageVersion}"
+ }
+ }
+ }
+ }
+
+ stage('Setup PNPM') {
+ steps {
+ dir('kie-tools') {
+ script {
+ buildUtils.setupPnpm("${pipelineVars.mavenSettingsReleaseConfigFileId}")
+ }
+ }
+ }
+ }
+
+ stage('Bootstrap') {
+ steps {
+ dir('kie-tools') {
+ sh '''#!/bin/bash -el
+ pnpm bootstrap
+ '''
+ }
+ }
+ }
+
+ stage('Build and Publish to Registries') {
+ steps {
+ dir('kie-tools') {
+ script {
+ echo "Publishing release to public registries..."
+ echo "Version: ${params.RELEASE_VERSION}"
+
+ withCredentials([
+ string(credentialsId: "${pipelineVars.npmTokenCredentialsId}", variable: 'NPM_TOKEN'),
+ string(credentialsId: "${pipelineVars.vscePatCredentialsId}", variable: 'VSCE_PAT'),
+ usernamePassword(credentialsId: "${pipelineVars.chromeStoreCredentialsId}",
+ usernameVariable: 'CHROME_CLIENT_ID',
+ passwordVariable: 'CHROME_CLIENT_SECRET'),
+ string(credentialsId: "${pipelineVars.chromeStoreRefreshTokenCredentialsId}", variable: 'CHROME_REFRESH_TOKEN'),
+ string(credentialsId: "${pipelineVars.chromeExtensionIdCredentialsId}", variable: 'CHROME_KIE_EDITORS_EXTENSION_ID'),
+ string(credentialsId: "${pipelineVars.dockerHubUserCredentialsId}", variable: 'DOCKER_USERNAME'),
+ string(credentialsId: "${pipelineVars.dockerHubTokenCredentialsId}", variable: 'DOCKER_PASSWORD'),
+ // Helm charts use the same Docker Hub credentials as the old helm jobs
+ string(credentialsId: "${pipelineVars.dockerHubUserCredentialsId}", variable: 'HELM_USERNAME'),
+ string(credentialsId: "${pipelineVars.dockerHubTokenCredentialsId}", variable: 'HELM_PASSWORD'),
+ string(credentialsId: "${pipelineVars.kieToolsBotGithubTokenCredentialsId}", variable: 'GITHUB_TOKEN')
+ ]) {
+ def releaseJson = sh(
+ returnStdout: true,
+ script: """#!/bin/bash -el
+ set +x
+ curl -sSf -H "Authorization: token \${GITHUB_TOKEN}" "https://api.github.com/repos/${pipelineVars.githubRepositorySlug}/releases/tags/${params.GITHUB_RELEASE_TAG}"
+ """
+ ).trim()
+ def releaseInfo = readJSON text: releaseJson
+ def uploadUrl = githubUtils.parseReleaseAssetUploadUrl("${releaseInfo.upload_url}")
+
+ sh """#!/bin/bash -el
+ ./scripts/release/release-all.sh ${params.RELEASE_VERSION} --publish --upload-url '${uploadUrl}'
Review Comment:
On a clean agent without a global Git identity, the accelerator build fails because it reads the checkout's `user.email` and `user.name` to create a commit. Configure both before invoking the release script so publication does not fail after earlier components have already been published.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,810 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+if [[ "${PUBLISH}" == "true" && -z "${UPLOAD_URL}" ]]; then
+ echo "ERROR: --upload-url is required when --publish is specified."
+ exit 1
+fi
+
+# Production defaults for Chrome extension and Online Editor builds
+export CHROME_EXTENSION__routerTargetOrigin="${CHROME_EXTENSION__routerTargetOrigin:-https://apache.github.io}"
+export CHROME_EXTENSION__routerRelativePath="${CHROME_EXTENSION__routerRelativePath:-incubator-kie-kogito-online/chrome-extension/${VERSION}}"
+export CHROME_EXTENSION__manifestFile="${CHROME_EXTENSION__manifestFile:-manifest.prod.json}"
+export CHROME_EXTENSION__onlineEditorUrl="${CHROME_EXTENSION__onlineEditorUrl:-https://apache.github.io/incubator-kie-kogito-online}"
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1 || true)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ [[ -d "packages/chrome-extension-pack-kogito-kie-editors/dist/fonts" ]] && cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/fonts "${tmp_kie}/"
+ cp packages/chrome-extension-pack-kogito-kie-editors/dist/*-envelope.* "${tmp_kie}/" 2>/dev/null || true
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1 || true)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
Review Comment:
Falling back to `latest` can release an unrelated build. For example, with `--skip-build`, if the requested version is absent but a previous development image exists as `latest`, it is retagged as `${VERSION}` and then signed for the RC or pushed publicly. Resolve only an explicitly version-matching image, or require an explicit, validated source-image override instead of silently treating `latest` as this release.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,810 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+if [[ "${PUBLISH}" == "true" && -z "${UPLOAD_URL}" ]]; then
+ echo "ERROR: --upload-url is required when --publish is specified."
+ exit 1
+fi
+
+# Production defaults for Chrome extension and Online Editor builds
+export CHROME_EXTENSION__routerTargetOrigin="${CHROME_EXTENSION__routerTargetOrigin:-https://apache.github.io}"
+export CHROME_EXTENSION__routerRelativePath="${CHROME_EXTENSION__routerRelativePath:-incubator-kie-kogito-online/chrome-extension/${VERSION}}"
+export CHROME_EXTENSION__manifestFile="${CHROME_EXTENSION__manifestFile:-manifest.prod.json}"
+export CHROME_EXTENSION__onlineEditorUrl="${CHROME_EXTENSION__onlineEditorUrl:-https://apache.github.io/incubator-kie-kogito-online}"
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1 || true)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ [[ -d "packages/chrome-extension-pack-kogito-kie-editors/dist/fonts" ]] && cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/fonts "${tmp_kie}/"
+ cp packages/chrome-extension-pack-kogito-kie-editors/dist/*-envelope.* "${tmp_kie}/" 2>/dev/null || true
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1 || true)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}"
+ mkdir -p "${output_dir}"
+
+ local helm_tmp_pkg
+ helm_tmp_pkg=$(mktemp -d)
+ trap "rm -rf ${helm_tmp_pkg}" EXIT
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo "ERROR: Helm chart directory ${chart_pkg} not found"; return 1; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo "ERROR: Chart.yaml not found in ${chart_pkg}"; return 1; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local chart_name
+ chart_name=$(grep '^name:' "${chart_yaml}" | head -1 | awk '{print $2}' | tr -d '"'"'")
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${helm_tmp_pkg}"
+ rm -rf "${tmp_dir}"
+
+ local found=""
+ if [[ -n "${chart_name}" && -f "${helm_tmp_pkg}/${chart_name}-${VERSION}.tgz" ]]; then
+ found="${helm_tmp_pkg}/${chart_name}-${VERSION}.tgz"
+ else
+ found=$(find "${helm_tmp_pkg}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ fi
+ if [[ -n "${found}" ]]; then
+ cp "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ else
+ echo "ERROR: Failed to package helm chart for ${slug}"
+ return 1
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ local helm_reg="${HELM_REGISTRY:-docker.io/apache}"
+ if [[ -z "${helm_reg}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${helm_reg}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for tgz in "${helm_tmp_pkg}"/*-"${VERSION}".tgz; do
+ if [[ -f "${tgz}" ]]; then
+ echo " PUSH ${tgz} -> oci://${helm_reg}"
+ helm push "${tgz}" "oci://${helm_reg}"
+ fi
+ done
+ echo "Helm charts pushed."
+ fi
+ rm -rf "${helm_tmp_pkg}"
+ trap - EXIT
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${ONLINE_EDITOR__buildInfo:-${VERSION}}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${ONLINE_EDITOR__extendedServicesCompatibleVersion:-${VERSION}}"
+ export ONLINE_EDITOR__corsProxyUrl="${ONLINE_EDITOR__corsProxyUrl:-https://cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud}"
+ export ONLINE_EDITOR__quarkusAcceleratorGitRepoUrl="${ONLINE_EDITOR__quarkusAcceleratorGitRepoUrl:-https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git}"
+ export ONLINE_EDITOR__quarkusAcceleratorGitRef="${ONLINE_EDITOR__quarkusAcceleratorGitRef:-${VERSION}}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="${ONLINE_EDITOR__devDeploymentBaseImageRegistry:-docker.io}"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="${ONLINE_EDITOR__devDeploymentBaseImageAccount:-apache}"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="${ONLINE_EDITOR__devDeploymentBaseImageName:-incubator-kie-sandbox-dev-deployment-base}"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${ONLINE_EDITOR__devDeploymentBaseImageTag:-${VERSION}}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="${ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry:-docker.io}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="${ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount:-apache}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="${ONLINE_EDITOR__devDeploymentDmnFormWebappImageName:-incubator-kie-sandbox-dev-deployment-dmn-form-webapp}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag:-${VERSION}}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="${ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry:-docker.io}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="${ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount:-apache}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="${ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName:-incubator-kie-sandbox-dev-deployment-quarkus-blank-app}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag:-${VERSION}}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
+ fi
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN is required for publishing to GitHub Pages"
+ return 1
+ fi
+
+ local kogito_online_repo="https://github.com/apache/incubator-kie-kogito-online.git"
+ local accelerator_repo="https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+
+ # Write a credential store file so the token is never exposed on the
+ # command line (avoids leaking it via process listings or CI log echoing).
+ local git_cred_file="${tmp_dir}/.git-credentials"
+ echo "https://x-token:${GITHUB_TOKEN}@github.com" > "${git_cred_file}"
+ chmod 600 "${git_cred_file}"
+ local git_auth=(-c "credential.helper=store --file=${git_cred_file}")
+
+ git "${git_auth[@]}" clone --branch gh-pages --depth 1 "${kogito_online_repo}" "${tmp_dir}/kogito-online"
+ (
+ cd "${tmp_dir}/kogito-online"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git config credential.helper "store --file=${git_cred_file}"
+ # Exclude .git so subsequent git operations in this subshell are not broken.
+ find . -maxdepth 1 ! -name '.' ! -name '.git' ! -name 'dev' ! -name 'editors' ! -name 'standalone' \
+ ! -name 'chrome-extension' ! -name '.nojekyll' ! -name 'CNAME' -exec rm -rf {} +
+
+ local online_dist="${REPO_ROOT}/packages/online-editor/dist"
+ if [[ -d "${online_dist}" ]]; then
Review Comment:
With `--publish --skip-build`, a missing Online Editor build still reaches this deletion, but the replacement copy is skipped. The script then commits and pushes the removal of the live site's root assets. Validate the required output, starting with `dist/index.html`, before deleting any live-site files and abort if it is absent.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,810 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+if [[ "${PUBLISH}" == "true" && -z "${UPLOAD_URL}" ]]; then
+ echo "ERROR: --upload-url is required when --publish is specified."
+ exit 1
+fi
+
+# Production defaults for Chrome extension and Online Editor builds
+export CHROME_EXTENSION__routerTargetOrigin="${CHROME_EXTENSION__routerTargetOrigin:-https://apache.github.io}"
+export CHROME_EXTENSION__routerRelativePath="${CHROME_EXTENSION__routerRelativePath:-incubator-kie-kogito-online/chrome-extension/${VERSION}}"
+export CHROME_EXTENSION__manifestFile="${CHROME_EXTENSION__manifestFile:-manifest.prod.json}"
+export CHROME_EXTENSION__onlineEditorUrl="${CHROME_EXTENSION__onlineEditorUrl:-https://apache.github.io/incubator-kie-kogito-online}"
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1 || true)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ [[ -d "packages/chrome-extension-pack-kogito-kie-editors/dist/fonts" ]] && cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/fonts "${tmp_kie}/"
+ cp packages/chrome-extension-pack-kogito-kie-editors/dist/*-envelope.* "${tmp_kie}/" 2>/dev/null || true
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
Review Comment:
With `--skip-build`, an extension with no VSIX is silently omitted from RC collection and executes zero iterations during publication, yet the component reports success. Check every expected extension for a package before collecting or publishing any VSIX files. The publication loop at line 333 needs the same validation.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,810 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+if [[ "${PUBLISH}" == "true" && -z "${UPLOAD_URL}" ]]; then
+ echo "ERROR: --upload-url is required when --publish is specified."
+ exit 1
+fi
+
+# Production defaults for Chrome extension and Online Editor builds
+export CHROME_EXTENSION__routerTargetOrigin="${CHROME_EXTENSION__routerTargetOrigin:-https://apache.github.io}"
+export CHROME_EXTENSION__routerRelativePath="${CHROME_EXTENSION__routerRelativePath:-incubator-kie-kogito-online/chrome-extension/${VERSION}}"
+export CHROME_EXTENSION__manifestFile="${CHROME_EXTENSION__manifestFile:-manifest.prod.json}"
+export CHROME_EXTENSION__onlineEditorUrl="${CHROME_EXTENSION__onlineEditorUrl:-https://apache.github.io/incubator-kie-kogito-online}"
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
Review Comment:
A missing asset returns success here. With `--publish --skip-build`, an existing but incomplete upload-service `dist/` directory therefore causes required platform binaries to be skipped while the component reports successful publication. Return a nonzero status for missing required assets so the release cannot finish successfully without them.
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] [incubator-kie-tools#4025] 10.3.x+ stream: Re-write `kie-tools` release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "Kusuma04-dev (via GitHub) via commits" <co...@kie.apache.org>.
Kusuma04-dev merged PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] [incubator-kie-tools#4025] 10.3.x+ stream: Re-write `kie-tools` release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "Kusuma04-dev (via GitHub) via commits" <co...@kie.apache.org>.
Kusuma04-dev commented on code in PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#discussion_r4493589935
##########
.ci/jenkins/Jenkinsfile.103xplus.release-publish:
##########
@@ -0,0 +1,260 @@
+/*
+ * 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.
+ */
+
+@Library('jenkins-pipeline-shared-libraries')_
+
+// PHASE 2: Publish Release After Vote Passes
+// This job publishes the approved release candidate to public registries
+// Publishes to: npm, VSCode Marketplace, Chrome Web Store, GitHub Releases
+//
+// NOTE: kogito-data-index-ephemeral is NOT included in this pipeline because the
+// packages/kogito-data-index-ephemeral-image package no longer exists in this repo.
+// Its presence in older Jenkinsfiles (Jenkinsfile.release-candidate RUNNERS list and
+// release-jobs/Jenkinsfile.kogito-data-index-ephemeral-image) is a historical artifact
+// from pre-10.3 automation and can be cleaned up in a follow-up PR.
+
+pipeline {
+ agent {
+ docker {
+ image 'docker.io/apache/incubator-kie-tools-ci-build:main'
+ args '--shm-size=2g --privileged --group-add docker'
+ label util.avoidFaultyNodes()
+ }
+ }
+
+ options {
+ timeout(time: 480, unit: 'MINUTES')
+ }
+
+ parameters {
+ string(
+ name: 'RELEASE_VERSION',
+ description: 'Release version (e.g., 1.0.0) - must match the voted RC',
+ defaultValue: '0.0.0'
+ )
+ string(
+ name: 'BASE_REF',
+ description: 'Git tag to release from (should be the RC tag)',
+ defaultValue: '0.0.0-rc1'
+ )
+ string(
+ name: 'GITHUB_RELEASE_TAG',
+ description: 'GitHub release tag (required: binary assets are uploaded to this existing GitHub Release)',
+ defaultValue: ''
+ )
+ }
+
+ environment {
+ KIE_TOOLS_BUILD__runLinters = 'false'
+ KIE_TOOLS_BUILD__runTests = 'false'
+ KIE_TOOLS_BUILD__runEndToEndTests = 'false'
+ KIE_TOOLS_BUILD__buildContainerImages = 'true'
+
+ CHROME_EXTENSION__routerTargetOrigin = 'https://apache.github.io'
+ CHROME_EXTENSION__routerRelativePath = "incubator-kie-kogito-online/chrome-extension/${params.RELEASE_VERSION}"
+ CHROME_EXTENSION__manifestFile = 'manifest.prod.json'
+ CHROME_EXTENSION__onlineEditorUrl = 'https://apache.github.io/incubator-kie-kogito-online'
+
+ ONLINE_EDITOR__corsProxyUrl = 'https://cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud'
+ ONLINE_EDITOR__buildInfo = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__extendedServicesCompatibleVersion = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentBaseImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentBaseImageName = 'incubator-kie-sandbox-dev-deployment-base'
+ ONLINE_EDITOR__devDeploymentBaseImageTag = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'incubator-kie-sandbox-dev-deployment-dmn-form-webapp'
+ ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag = "${params.RELEASE_VERSION}"
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry = 'docker.io'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount = 'apache'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName = 'incubator-kie-sandbox-dev-deployment-quarkus-blank-app'
+ ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag = "${params.RELEASE_VERSION}"
+
+ DOCKER_CONFIG = "${WORKSPACE}/.docker"
+ HELM_REGISTRY = 'docker.io/apache'
+ }
+
+ stages {
+ stage('Validate parameters') {
+ steps {
+ script {
+ if (params.RELEASE_VERSION == '' || params.RELEASE_VERSION == '0.0.0') {
+ error("RELEASE_VERSION must be set to the voted release version")
+ }
+ if (params.BASE_REF == '' || params.BASE_REF == '0.0.0-rc1') {
+ error("BASE_REF must be set to the voted RC tag")
+ }
+ // release-all.sh --publish uploads binary assets to the GitHub Release and fails without it,
+ // after other registries would already have been published. Fail fast instead.
+ if (params.GITHUB_RELEASE_TAG == '') {
+ error("GITHUB_RELEASE_TAG must be set to an existing GitHub Release tag")
+ }
+ }
+ }
+ }
+
+ stage('Load local shared scripts') {
+ steps {
+ script {
+ pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy'
+ buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy'
+ githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy'
+ }
+ }
+ }
+
+ stage('Start required services') {
+ steps {
+ script {
+ buildUtils.startRequiredServices()
+ }
+ }
+ }
+
+ stage('Clean workspace') {
+ steps {
+ cleanWs(deleteDirs: true, disableDeferredWipeout: true)
+ }
+ }
+
+ stage('Checkout kie-tools') {
+ steps {
+ dir('kie-tools') {
+ script {
+ githubUtils.checkoutRepo(
+ "http://github.com/${pipelineVars.githubRepositorySlug}.git",
+ "${params.BASE_REF}",
+ "${pipelineVars.kieToolsBotGithubCredentialsId}"
+ )
+ }
+ }
+ }
+ }
+
+ stage('Verify version') {
+ steps {
+ dir('kie-tools') {
+ script {
+ def packageVersion = sh(
+ returnStdout: true,
+ script: "#!/bin/bash -el \n node -p \"require('./package.json').version\""
+ ).trim()
+
+ if (packageVersion != params.RELEASE_VERSION) {
+ error("Version mismatch! package.json has ${packageVersion} but RELEASE_VERSION is ${params.RELEASE_VERSION}")
+ }
+
+ echo "✅ Version verified: ${packageVersion}"
+ }
+ }
+ }
+ }
+
+ stage('Setup PNPM') {
+ steps {
+ dir('kie-tools') {
+ script {
+ buildUtils.setupPnpm("${pipelineVars.mavenSettingsReleaseConfigFileId}")
+ }
+ }
+ }
+ }
+
+ stage('Bootstrap') {
+ steps {
+ dir('kie-tools') {
+ sh '''#!/bin/bash -el
+ pnpm bootstrap
+ '''
+ }
+ }
+ }
+
+ stage('Build and Publish to Registries') {
+ steps {
+ dir('kie-tools') {
+ script {
+ echo "Publishing release to public registries..."
+ echo "Version: ${params.RELEASE_VERSION}"
+
+ withCredentials([
+ string(credentialsId: "${pipelineVars.npmTokenCredentialsId}", variable: 'NPM_TOKEN'),
+ string(credentialsId: "${pipelineVars.vscePatCredentialsId}", variable: 'VSCE_PAT'),
+ usernamePassword(credentialsId: "${pipelineVars.chromeStoreCredentialsId}",
+ usernameVariable: 'CHROME_CLIENT_ID',
+ passwordVariable: 'CHROME_CLIENT_SECRET'),
+ string(credentialsId: "${pipelineVars.chromeStoreRefreshTokenCredentialsId}", variable: 'CHROME_REFRESH_TOKEN'),
+ string(credentialsId: "${pipelineVars.chromeExtensionIdCredentialsId}", variable: 'CHROME_KIE_EDITORS_EXTENSION_ID'),
+ string(credentialsId: "${pipelineVars.dockerHubUserCredentialsId}", variable: 'DOCKER_USERNAME'),
+ string(credentialsId: "${pipelineVars.dockerHubTokenCredentialsId}", variable: 'DOCKER_PASSWORD'),
+ // Helm charts use the same Docker Hub credentials as the old helm jobs
+ string(credentialsId: "${pipelineVars.dockerHubUserCredentialsId}", variable: 'HELM_USERNAME'),
+ string(credentialsId: "${pipelineVars.dockerHubTokenCredentialsId}", variable: 'HELM_PASSWORD'),
+ string(credentialsId: "${pipelineVars.kieToolsBotGithubTokenCredentialsId}", variable: 'GITHUB_TOKEN')
+ ]) {
+ def releaseJson = sh(
+ returnStdout: true,
+ script: """#!/bin/bash -el
+ set +x
+ curl -sSf -H "Authorization: token \${GITHUB_TOKEN}" "https://api.github.com/repos/${pipelineVars.githubRepositorySlug}/releases/tags/${params.GITHUB_RELEASE_TAG}"
+ """
+ ).trim()
+ def releaseInfo = readJSON text: releaseJson
+ def uploadUrl = githubUtils.parseReleaseAssetUploadUrl("${releaseInfo.upload_url}")
+
+ sh """#!/bin/bash -el
+ ./scripts/release/release-all.sh ${params.RELEASE_VERSION} --publish --upload-url '${uploadUrl}'
Review Comment:
this is not needed.
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] kie-issues#2405: 10.3.x+ stream: Re-write kie-tools release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "tiagobento (via GitHub)" <gi...@apache.org>.
tiagobento commented on PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#issuecomment-5733151625
In the past, we had different release jobs for certain components because we sometimes wanted to release them separately. That's not true anymore. We should have just one release script for all components, under one build. Can you please adjust the scripts to be like that? Thanks!
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] [incubator-kie-tools#4025] 10.3.x+ stream: Re-write `kie-tools` release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "Copilot (via GitHub) via commits" <co...@kie.apache.org>.
Copilot commented on code in PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#discussion_r4490916534
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,786 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
Review Comment:
The local `--publish` command accepts a missing `--upload-url`, although the upload-service step unconditionally requires it. With the registry credentials set, npm, Marketplace and container publishing can already have happened before that step fails, leaving a partially published release. Apply the same fail-fast validation used by the new Jenkins publish pipeline before any builds or publishing start.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,786 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
Review Comment:
Without the Jenkins environment, local `--rc` and `--publish` runs use the Chrome package's development manifest and localhost URLs, leaving the released extension unable to load its hosted editors. Set production defaults before any builds, while allowing explicit environment overrides. This also covers the later Chrome rebuild in `release_github_pages`.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,786 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1 || true)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ [[ -d "packages/chrome-extension-pack-kogito-kie-editors/dist/fonts" ]] && cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/fonts "${tmp_kie}/"
+ cp packages/chrome-extension-pack-kogito-kie-editors/dist/*-envelope.* "${tmp_kie}/" 2>/dev/null || true
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1 || true)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}"
+ mkdir -p "${output_dir}"
+
+ local helm_tmp_pkg
+ helm_tmp_pkg=$(mktemp -d)
+ trap "rm -rf ${helm_tmp_pkg}" EXIT
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo "ERROR: Helm chart directory ${chart_pkg} not found"; return 1; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo "ERROR: Chart.yaml not found in ${chart_pkg}"; return 1; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${helm_tmp_pkg}"
+ rm -rf "${tmp_dir}"
+
+ local found
+ found=$(find "${helm_tmp_pkg}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ if [[ -n "${found}" ]]; then
Review Comment:
Both charts are packaged into the same temporary directory, so this lookup can select the first chart again on the second iteration. That produces two differently named RC artifacts containing the same chart and omits the other chart from the vote. Select the archive by the current chart's name and version.
##########
scripts/release/create-source-tarball.sh:
##########
@@ -0,0 +1,93 @@
+#!/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
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
+
+usage() {
+ echo "Usage: $0 <version> [--git-ref <ref>] [--output-dir <dir>]"
+ echo ""
+ echo "Arguments:"
+ echo " version Release or RC version (e.g., 10.2.0 or 10.2.0-rc1)"
+ echo ""
+ echo "Options:"
+ echo " --git-ref <ref> Git reference to archive (default: HEAD)"
+ echo " --output-dir <d> Output directory (default: ./release-artifacts)"
+ echo " --help"
+ exit 1
+}
+
+VERSION=""
+GIT_REF="HEAD"
+OUTPUT_DIR="$REPO_ROOT/release-artifacts"
+
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --git-ref) GIT_REF="$2"; shift 2 ;;
+ --output-dir) OUTPUT_DIR="$2"; shift 2 ;;
+ --help) usage ;;
+ *)
+ if [[ -z "$VERSION" ]]; then VERSION="$1"; else echo "Unknown argument: $1"; usage; fi
+ shift ;;
+ esac
+done
+
+[[ -z "$VERSION" ]] && { echo "ERROR: version is required"; usage; }
+
+echo "=========================================="
+echo "Source Tarball"
+echo "Version: $VERSION"
+echo "Git ref: $GIT_REF"
+echo "Output: $OUTPUT_DIR"
+echo "=========================================="
+
+mkdir -p "$OUTPUT_DIR"
+ZIPFILE="$OUTPUT_DIR/apache-kie-$VERSION-incubating-sources.zip"
Review Comment:
A relative `--output-dir` is created relative to the caller's directory, but `ZIPFILE` remains relative after the script changes to `REPO_ROOT`. Running this utility from another directory therefore writes somewhere else or fails because the directory does not exist there. Resolve the output directory to an absolute path before changing directories.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,786 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1 || true)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ [[ -d "packages/chrome-extension-pack-kogito-kie-editors/dist/fonts" ]] && cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/fonts "${tmp_kie}/"
+ cp packages/chrome-extension-pack-kogito-kie-editors/dist/*-envelope.* "${tmp_kie}/" 2>/dev/null || true
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1 || true)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}"
+ mkdir -p "${output_dir}"
+
+ local helm_tmp_pkg
+ helm_tmp_pkg=$(mktemp -d)
+ trap "rm -rf ${helm_tmp_pkg}" EXIT
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo "ERROR: Helm chart directory ${chart_pkg} not found"; return 1; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo "ERROR: Chart.yaml not found in ${chart_pkg}"; return 1; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${helm_tmp_pkg}"
+ rm -rf "${tmp_dir}"
+
+ local found
+ found=$(find "${helm_tmp_pkg}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ if [[ -n "${found}" ]]; then
+ cp "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ else
+ echo "ERROR: Failed to package helm chart for ${slug}"
+ return 1
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ local helm_reg="${HELM_REGISTRY:-docker.io/apache}"
+ if [[ -z "${helm_reg}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${helm_reg}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for tgz in "${helm_tmp_pkg}"/*-"${VERSION}".tgz; do
+ if [[ -f "${tgz}" ]]; then
+ echo " PUSH ${tgz} -> oci://${helm_reg}"
+ helm push "${tgz}" "oci://${helm_reg}"
+ fi
+ done
+ echo "Helm charts pushed."
+ fi
+ rm -rf "${helm_tmp_pkg}"
+ trap - EXIT
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${VERSION}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${VERSION}"
Review Comment:
The online-editor defaults still point the CORS proxy and Quarkus accelerator repository to localhost, with the accelerator ref set to `main`. A local release therefore deploys unusable endpoints; Jenkins overrides the proxy but still leaves the accelerator on localhost. Set public endpoint defaults and the released accelerator tag before building, while allowing explicit overrides.
##########
.ci/jenkins/Jenkinsfile.103xplus.release-promote:
##########
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+@Library('jenkins-pipeline-shared-libraries')_
+
+// 10.3.x+ promote / tag-release pipeline for incubator-kie-tools.
+// Run after the IPMC vote passes to:
+// 1. Create and push the official release Git tag (e.g. 10.3.0) pointing to the approved RC commit.
+// 2. Create the GitHub Release for incubator-kie-tools (required before release-publish).
+
+pipeline {
+ agent {
+ label util.avoidFaultyNodes()
+ }
+
+ options {
+ timeout(time: 60, unit: 'MINUTES')
+ }
+
+ parameters {
+ string(
+ name: 'RELEASE_VERSION',
+ description: 'Release version (e.g., 10.3.0)',
+ defaultValue: ''
+ )
+ string(
+ name: 'BASE_REF',
+ description: 'Approved RC tag (e.g. 10.3.0-rc1) to tag from',
+ defaultValue: ''
+ )
+ }
+
+ stages {
+ stage('Validate parameters') {
+ steps {
+ script {
+ if (params.RELEASE_VERSION == '') {
+ error("RELEASE_VERSION is required (e.g. 10.3.0)")
+ }
+ if (params.BASE_REF == '') {
+ error("BASE_REF (approved RC tag, e.g. 10.3.0-rc1) is required")
+ }
+ currentBuild.displayName = "${params.RELEASE_VERSION} (${params.BASE_REF})"
+ }
+ }
+ }
+
+ stage('Load local shared scripts') {
+ steps {
+ script {
+ pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy'
+ githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy'
+ }
+ }
+ }
+
+ stage('Clean workspace') {
+ steps {
+ cleanWs(deleteDirs: true, disableDeferredWipeout: true)
+ }
+ }
+
+ stage('Checkout incubator-kie-tools') {
+ steps {
+ dir('kie-tools') {
+ script {
+ githubUtils.checkoutRepo(
+ "https://github.com/${pipelineVars.githubRepositorySlug}.git",
+ "${params.BASE_REF}",
+ "${pipelineVars.asfGithubPushCredentialsId}"
+ )
+ }
+ }
+ }
+ }
+
+ stage('Tag final release') {
+ steps {
+ dir('kie-tools') {
+ script {
+ withCredentials([
+ usernamePassword(credentialsId: "${pipelineVars.asfGithubPushCredentialsId}", usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN')
+ ]) {
+ sh """#!/bin/bash -el
+ set -euo pipefail
+ git config user.email "dev@kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git config --local credential.helper '!f() { echo username=\$GITHUB_USER; echo password=\$GITHUB_TOKEN; }; f'
+
+ # Fetch the approved RC tag
+ git fetch origin "refs/tags/${params.BASE_REF}:refs/tags/${params.BASE_REF}"
+
+ # Create and push the final release tag pointing to the RC commit
+ git tag -a "${params.RELEASE_VERSION}" "${params.BASE_REF}" -m "Release ${params.RELEASE_VERSION}"
+ git push origin "${params.RELEASE_VERSION}"
+ """
+ }
+ }
+ }
+ }
+ }
+
+ stage('Create GitHub Release') {
+ steps {
+ dir('kie-tools') {
+ script {
+ withCredentials([
+ string(credentialsId: "${pipelineVars.kieToolsBotGithubTokenCredentialsId}", variable: 'GITHUB_TOKEN')
+ ]) {
+ sh """#!/bin/bash -el
+ set -euo pipefail
+
+ FINAL_TAG="${params.RELEASE_VERSION}"
+
+ # Delete any pre-existing release for this tag if needed
+ if gh release view "\${FINAL_TAG}" --repo "${pipelineVars.githubRepositorySlug}" &>/dev/null; then
+ gh release delete "\${FINAL_TAG}" --repo "${pipelineVars.githubRepositorySlug}" --yes
+ fi
+
+ PREV_TAG="\$(git tag --sort=-version:refname | grep -v "^\${FINAL_TAG}\$" | head -1 || true)"
+
+ gh release create "\${FINAL_TAG}" \
+ --repo "${pipelineVars.githubRepositorySlug}" \
+ --title "\${FINAL_TAG}" \
+ --generate-notes \
+ \${PREV_TAG:+--notes-start-tag "\${PREV_TAG}"}
Review Comment:
Filtering out only the final tag leaves the approved RC tag in this list. In a normal release, that RC is selected as the previous version even though it points to the same commit as the final tag, so generated release notes omit the actual changes since the previous release. Let GitHub choose the previous release, or explicitly select a prior final release instead of an RC.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,786 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1 || true)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ [[ -d "packages/chrome-extension-pack-kogito-kie-editors/dist/fonts" ]] && cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/fonts "${tmp_kie}/"
+ cp packages/chrome-extension-pack-kogito-kie-editors/dist/*-envelope.* "${tmp_kie}/" 2>/dev/null || true
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1 || true)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}"
+ mkdir -p "${output_dir}"
+
+ local helm_tmp_pkg
+ helm_tmp_pkg=$(mktemp -d)
+ trap "rm -rf ${helm_tmp_pkg}" EXIT
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo "ERROR: Helm chart directory ${chart_pkg} not found"; return 1; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo "ERROR: Chart.yaml not found in ${chart_pkg}"; return 1; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${helm_tmp_pkg}"
+ rm -rf "${tmp_dir}"
+
+ local found
+ found=$(find "${helm_tmp_pkg}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ if [[ -n "${found}" ]]; then
+ cp "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ else
+ echo "ERROR: Failed to package helm chart for ${slug}"
+ return 1
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ local helm_reg="${HELM_REGISTRY:-docker.io/apache}"
+ if [[ -z "${helm_reg}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${helm_reg}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for tgz in "${helm_tmp_pkg}"/*-"${VERSION}".tgz; do
+ if [[ -f "${tgz}" ]]; then
+ echo " PUSH ${tgz} -> oci://${helm_reg}"
+ helm push "${tgz}" "oci://${helm_reg}"
+ fi
+ done
+ echo "Helm charts pushed."
+ fi
+ rm -rf "${helm_tmp_pkg}"
+ trap - EXIT
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${VERSION}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="incubator-kie-sandbox-dev-deployment-base"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${VERSION}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
+ fi
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN is required for publishing to GitHub Pages"
+ return 1
+ fi
+
+ local kogito_online_repo="https://github.com/apache/incubator-kie-kogito-online.git"
+ local accelerator_repo="https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+
+ # Write a credential store file so the token is never exposed on the
+ # command line (avoids leaking it via process listings or CI log echoing).
+ local git_cred_file="${tmp_dir}/.git-credentials"
+ echo "https://x-token:${GITHUB_TOKEN}@github.com" > "${git_cred_file}"
+ chmod 600 "${git_cred_file}"
+ local git_auth=(-c "credential.helper=store --file=${git_cred_file}")
+
+ git "${git_auth[@]}" clone --branch gh-pages --depth 1 "${kogito_online_repo}" "${tmp_dir}/kogito-online"
+ (
+ cd "${tmp_dir}/kogito-online"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git config credential.helper "store --file=${git_cred_file}"
+ # Exclude .git so subsequent git operations in this subshell are not broken.
+ find . -maxdepth 1 ! -name '.' ! -name '.git' ! -name 'dev' ! -name 'editors' ! -name 'standalone' \
+ ! -name 'chrome-extension' ! -name '.nojekyll' ! -name 'CNAME' -exec rm -rf {} +
+
+ local online_dist="${REPO_ROOT}/packages/online-editor/dist"
+ if [[ -d "${online_dist}" ]]; then
+ local editors_dir="editors/${VERSION}"
+ rm -rf "${editors_dir}" && mkdir -p "${editors_dir}"
+ rm -rf editors/latest && ln -s "${VERSION}" editors/latest
+ cp -r "${online_dist}/." .
+ fi
+ local chrome_dist="${REPO_ROOT}/packages/chrome-extension-pack-kogito-kie-editors/dist"
+ if [[ -d "${chrome_dist}" ]]; then
+ local chrome_dir="chrome-extension/${VERSION}"
+ rm -rf "${chrome_dir}" && mkdir -p "${chrome_dir}"
+ cp -r "${chrome_dist}/fonts" "${chrome_dir}/" 2>/dev/null || true
+ cp "${chrome_dist}"/*-envelope.* "${chrome_dir}/" 2>/dev/null || true
+ fi
+ git add .
+ git commit -m "Deploy ${VERSION} (Online Editor + Chrome Extension editors)" \
+ || echo " (nothing to commit)"
+ git push origin gh-pages
+ )
+ echo "Pushed to GitHub Pages."
+
+ local accel_content="${REPO_ROOT}/packages/kie-sandbox-accelerator-quarkus/dist/git-repo-content"
+ if [[ -d "${accel_content}" ]]; then
+ git "${git_auth[@]}" clone --depth 1 "${accelerator_repo}" "${tmp_dir}/accelerator"
+ (
+ cd "${tmp_dir}/accelerator"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git config credential.helper "store --file=${git_cred_file}"
+ git checkout --orphan "${VERSION}"
+ # Remove all working-tree files carried over from the clone so the
+ # orphan branch contains only the intended accelerator content.
+ git rm -rf . --quiet 2>/dev/null || true
+ cp -r "${accel_content}/." .
+ git add .
+ git commit -m "Apache KIE Sandbox Quarkus Accelerator ${VERSION}"
+ git tag "${VERSION}"
+ git push origin "refs/heads/${VERSION}:refs/heads/${VERSION}" "refs/tags/${VERSION}:refs/tags/${VERSION}"
+ )
+ echo "Pushed accelerator to tag ${VERSION}."
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 7. dev-deployment-upload-service
+# ---------------------------------------------------------------------------
+release_dev_deployment_upload_service() {
+ echo ""
+ echo "--- dev-deployment-upload-service ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building dev-deployment-upload-service..."
+ DDUS_VERSION="${VERSION}" pnpm -F "@kie-tools/dev-deployment-upload-service..." build:prod
+ fi
+
+ local dist="${REPO_ROOT}/packages/dev-deployment-upload-service/dist"
+ if [[ ! -d "${dist}" ]]; then
+ echo "ERROR: packages/dev-deployment-upload-service/dist not found"
+ return 1
+ fi
+
+ declare -A PLATFORM_MAP=(
+ ["dev-deployment-upload-service-darwin-arm64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-macOS-arm64.tar.gz"
+ ["dev-deployment-upload-service-darwin-amd64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-macOS-x86.tar.gz"
+ ["dev-deployment-upload-service-linux-amd64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-linux-x86.tar.gz"
Review Comment:
The build also produces `dev-deployment-upload-service-linux-arm64-${VERSION}.tar.gz` (Makefile:21–29), and the installer explicitly supports Linux ARM64, but that platform is missing from this map. Since both RC collection and publishing iterate the map, its binary is never released and ARM64 installations cannot download it. Include the Linux ARM64 artifact.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,786 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1 || true)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ [[ -d "packages/chrome-extension-pack-kogito-kie-editors/dist/fonts" ]] && cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/fonts "${tmp_kie}/"
+ cp packages/chrome-extension-pack-kogito-kie-editors/dist/*-envelope.* "${tmp_kie}/" 2>/dev/null || true
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1 || true)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}"
+ mkdir -p "${output_dir}"
+
+ local helm_tmp_pkg
+ helm_tmp_pkg=$(mktemp -d)
+ trap "rm -rf ${helm_tmp_pkg}" EXIT
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo "ERROR: Helm chart directory ${chart_pkg} not found"; return 1; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo "ERROR: Chart.yaml not found in ${chart_pkg}"; return 1; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${helm_tmp_pkg}"
+ rm -rf "${tmp_dir}"
+
+ local found
+ found=$(find "${helm_tmp_pkg}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ if [[ -n "${found}" ]]; then
+ cp "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ else
+ echo "ERROR: Failed to package helm chart for ${slug}"
+ return 1
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ local helm_reg="${HELM_REGISTRY:-docker.io/apache}"
+ if [[ -z "${helm_reg}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${helm_reg}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for tgz in "${helm_tmp_pkg}"/*-"${VERSION}".tgz; do
+ if [[ -f "${tgz}" ]]; then
+ echo " PUSH ${tgz} -> oci://${helm_reg}"
+ helm push "${tgz}" "oci://${helm_reg}"
+ fi
+ done
+ echo "Helm charts pushed."
+ fi
+ rm -rf "${helm_tmp_pkg}"
+ trap - EXIT
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${VERSION}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="incubator-kie-sandbox-dev-deployment-base"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${VERSION}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
+ fi
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN is required for publishing to GitHub Pages"
+ return 1
+ fi
+
+ local kogito_online_repo="https://github.com/apache/incubator-kie-kogito-online.git"
+ local accelerator_repo="https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+
+ # Write a credential store file so the token is never exposed on the
+ # command line (avoids leaking it via process listings or CI log echoing).
+ local git_cred_file="${tmp_dir}/.git-credentials"
+ echo "https://x-token:${GITHUB_TOKEN}@github.com" > "${git_cred_file}"
+ chmod 600 "${git_cred_file}"
+ local git_auth=(-c "credential.helper=store --file=${git_cred_file}")
+
+ git "${git_auth[@]}" clone --branch gh-pages --depth 1 "${kogito_online_repo}" "${tmp_dir}/kogito-online"
+ (
+ cd "${tmp_dir}/kogito-online"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git config credential.helper "store --file=${git_cred_file}"
+ # Exclude .git so subsequent git operations in this subshell are not broken.
+ find . -maxdepth 1 ! -name '.' ! -name '.git' ! -name 'dev' ! -name 'editors' ! -name 'standalone' \
+ ! -name 'chrome-extension' ! -name '.nojekyll' ! -name 'CNAME' -exec rm -rf {} +
+
+ local online_dist="${REPO_ROOT}/packages/online-editor/dist"
+ if [[ -d "${online_dist}" ]]; then
+ local editors_dir="editors/${VERSION}"
+ rm -rf "${editors_dir}" && mkdir -p "${editors_dir}"
+ rm -rf editors/latest && ln -s "${VERSION}" editors/latest
+ cp -r "${online_dist}/." .
+ fi
+ local chrome_dist="${REPO_ROOT}/packages/chrome-extension-pack-kogito-kie-editors/dist"
+ if [[ -d "${chrome_dist}" ]]; then
+ local chrome_dir="chrome-extension/${VERSION}"
+ rm -rf "${chrome_dir}" && mkdir -p "${chrome_dir}"
+ cp -r "${chrome_dist}/fonts" "${chrome_dir}/" 2>/dev/null || true
+ cp "${chrome_dist}"/*-envelope.* "${chrome_dir}/" 2>/dev/null || true
+ fi
+ git add .
+ git commit -m "Deploy ${VERSION} (Online Editor + Chrome Extension editors)" \
+ || echo " (nothing to commit)"
+ git push origin gh-pages
+ )
+ echo "Pushed to GitHub Pages."
+
+ local accel_content="${REPO_ROOT}/packages/kie-sandbox-accelerator-quarkus/dist/git-repo-content"
+ if [[ -d "${accel_content}" ]]; then
+ git "${git_auth[@]}" clone --depth 1 "${accelerator_repo}" "${tmp_dir}/accelerator"
+ (
+ cd "${tmp_dir}/accelerator"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git config credential.helper "store --file=${git_cred_file}"
+ git checkout --orphan "${VERSION}"
+ # Remove all working-tree files carried over from the clone so the
+ # orphan branch contains only the intended accelerator content.
+ git rm -rf . --quiet 2>/dev/null || true
+ cp -r "${accel_content}/." .
+ git add .
+ git commit -m "Apache KIE Sandbox Quarkus Accelerator ${VERSION}"
+ git tag "${VERSION}"
+ git push origin "refs/heads/${VERSION}:refs/heads/${VERSION}" "refs/tags/${VERSION}:refs/tags/${VERSION}"
+ )
+ echo "Pushed accelerator to tag ${VERSION}."
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 7. dev-deployment-upload-service
+# ---------------------------------------------------------------------------
+release_dev_deployment_upload_service() {
+ echo ""
+ echo "--- dev-deployment-upload-service ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building dev-deployment-upload-service..."
+ DDUS_VERSION="${VERSION}" pnpm -F "@kie-tools/dev-deployment-upload-service..." build:prod
+ fi
+
+ local dist="${REPO_ROOT}/packages/dev-deployment-upload-service/dist"
+ if [[ ! -d "${dist}" ]]; then
+ echo "ERROR: packages/dev-deployment-upload-service/dist not found"
+ return 1
+ fi
+
+ declare -A PLATFORM_MAP=(
+ ["dev-deployment-upload-service-darwin-arm64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-macOS-arm64.tar.gz"
+ ["dev-deployment-upload-service-darwin-amd64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-macOS-x86.tar.gz"
+ ["dev-deployment-upload-service-linux-amd64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-linux-x86.tar.gz"
+ ["dev-deployment-upload-service-windows-amd64-${VERSION}.tar.gz"]="apache-kie-${VERSION}-incubating-sandbox-dev-deployment-upload-service-windows-x86.tar.gz"
+ )
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_name in "${!PLATFORM_MAP[@]}"; do
+ local rc_name="${PLATFORM_MAP[$src_name]}"
+ if [[ -f "${dist}/${src_name}" ]]; then
+ cp "${dist}/${src_name}" "${ARTIFACTS_DIR}/${rc_name}"
+ echo " COPY ${rc_name}"
+ else
+ echo "ERROR: Artifact ${dist}/${src_name} not found"
+ return 1
+ fi
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" || -z "${UPLOAD_URL:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN and --upload-url are required for publishing dev-deployment-upload-service"
+ return 1
+ fi
+ for src_name in "${!PLATFORM_MAP[@]}"; do
+ upload_github_asset "${dist}/${src_name}" "${src_name}" "application/gzip"
Review Comment:
Only the binary archive is uploaded here; its generated `.sha256` file is omitted. `getDevDeploymentUploadService.sh` downloads that companion asset and verifies it by default (lines 115–147), so installations from these new GitHub Releases fail checksum verification. Upload each archive's checksum as well, as the previous release job did.
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] kie-issues#2405: 10.3.x+ stream: Re-write kie-tools release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "Kusuma04-dev (via GitHub)" <gi...@apache.org>.
Kusuma04-dev commented on PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#issuecomment-5728809679
Thanks @jomarko ,
You don't need to run a full build. Complete build logs and testable artifacts are already available in the [kie-release-10.3/evidence](https://github.com/Kusuma04-dev/kie-release-10.3/tree/main/artifacts_kie_tools) repository for verification.
Authorized users: Anyone can run local builds or dry-runs. Official releases are executed through Jenkins.
Credentials: No credentials are needed for local RC builds or testing. Publishing credentials (NPM, VSCE, GPG, etc.) are pre-configured in Jenkins and used only for official releases.
Output and upload location: For a local dry-run, the scripts generate artifacts under release-artifacts (in kie-tools) and the local .m2 repository (in kie). If the release is run through Jenkins, the generated artifacts are uploaded to Apache SVN dev and Nexus Staging for voting, and are published to public registries after approval.
CI vs local execution: Official releases run on Jenkins, but the release scripts can also be executed locally.
Dry-run commands:
incubator-kie:
./script/release/release-all.sh --version 10.3.0 --tag 10.3.0-rc1 --skip-tests --dry-run
incubator-kie-tools:
./scripts/release/release-all.sh 10.3.0 --rc --skip-build
Repository isolation: Yes. The release scripts are self-contained within their respective repositories (incubator-kie and incubator-kie-tools).
Build scope: Yes. Each script performs a clean production build of all modules in its repository for the specified release version and commit.
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] [incubator-kie-tools#4025] 10.3.x+ stream: Re-write `kie-tools` release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "tiagobento (via GitHub)" <gi...@apache.org>.
tiagobento commented on PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#issuecomment-6433713291
I made some comments in the related Release Procedure PR (https://github.com/apache/incubator-kie-website/pull/62).
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] kie-issues#2405: 10.3.x+ stream: Re-write kie-tools release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#discussion_r4229461509
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,773 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
Review Comment:
The script relies on Bash associative arrays (`declare -A`), which are not supported by the default Bash 3.2 shipped on macOS. Since this is positioned as a 'local-first' release script, consider either documenting the Bash 4+ requirement explicitly in the README (e.g., via Homebrew bash) or refactoring these maps to a Bash-3-compatible representation (e.g., newline-delimited `key=value` lists).
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,773 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}"
+ mkdir -p "${output_dir}"
+
+ local helm_tmp_pkg
+ helm_tmp_pkg=$(mktemp -d)
+ trap "rm -rf ${helm_tmp_pkg}" EXIT
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo "ERROR: Helm chart directory ${chart_pkg} not found"; return 1; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo "ERROR: Chart.yaml not found in ${chart_pkg}"; return 1; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${helm_tmp_pkg}"
+ rm -rf "${tmp_dir}"
+
+ local found
+ found=$(find "${helm_tmp_pkg}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ if [[ -n "${found}" ]]; then
+ cp "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ else
+ echo "ERROR: Failed to package helm chart for ${slug}"
+ return 1
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ local helm_reg="${HELM_REGISTRY:-docker.io/apache}"
+ if [[ -z "${helm_reg}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${helm_reg}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for tgz in "${helm_tmp_pkg}"/*-"${VERSION}".tgz; do
+ if [[ -f "${tgz}" ]]; then
+ echo " PUSH ${tgz} -> oci://${helm_reg}"
+ helm push "${tgz}" "oci://${helm_reg}"
+ fi
+ done
+ echo "Helm charts pushed."
+ fi
+ rm -rf "${helm_tmp_pkg}"
+ trap - EXIT
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, kie-editors-standalone, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${VERSION}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="incubator-kie-sandbox-dev-deployment-base"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${VERSION}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/kie-editors-standalone..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-editors-standalone/dist:apache-kie-${VERSION}-incubating-business-automation-standalone-editors.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
+ fi
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN is required for publishing to GitHub Pages"
+ return 1
+ fi
+
+ local kogito_online_repo="https://github.com/apache/incubator-kie-kogito-online.git"
+ local accelerator_repo="https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+
+ local auth_url="${kogito_online_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --branch gh-pages --depth 1 "${auth_url}" "${tmp_dir}/kogito-online"
+ (
+ cd "${tmp_dir}/kogito-online"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ find . -maxdepth 1 ! -name '.' ! -name 'dev' ! -name 'editors' ! -name 'standalone' \
Review Comment:
This `find ... -exec rm -rf` will also match and delete `.git/` (it is at `-maxdepth 1` and not excluded). Deleting `.git` will break subsequent `git add/commit/push` in this subshell. Exclude `.git` explicitly (and optionally `.github` / other metadata you want to retain) or switch to a safer cleanup approach that only removes known deploy paths.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,773 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
Review Comment:
The script relies on Bash associative arrays (`declare -A`), which are not supported by the default Bash 3.2 shipped on macOS. Since this is positioned as a 'local-first' release script, consider either documenting the Bash 4+ requirement explicitly in the README (e.g., via Homebrew bash) or refactoring these maps to a Bash-3-compatible representation (e.g., newline-delimited `key=value` lists).
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,773 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}"
+ mkdir -p "${output_dir}"
+
+ local helm_tmp_pkg
+ helm_tmp_pkg=$(mktemp -d)
+ trap "rm -rf ${helm_tmp_pkg}" EXIT
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo "ERROR: Helm chart directory ${chart_pkg} not found"; return 1; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo "ERROR: Chart.yaml not found in ${chart_pkg}"; return 1; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
Review Comment:
`sed -i` behavior differs between GNU sed (Linux) and BSD sed (macOS). As written, this is likely to fail on macOS in local runs. To keep the script reliably 'local-first', consider using a portable approach (e.g., write to a temp file then move it into place), or gate this logic on the available `sed` implementation.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,773 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}"
+ mkdir -p "${output_dir}"
+
+ local helm_tmp_pkg
+ helm_tmp_pkg=$(mktemp -d)
+ trap "rm -rf ${helm_tmp_pkg}" EXIT
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo "ERROR: Helm chart directory ${chart_pkg} not found"; return 1; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo "ERROR: Chart.yaml not found in ${chart_pkg}"; return 1; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${helm_tmp_pkg}"
+ rm -rf "${tmp_dir}"
+
+ local found
+ found=$(find "${helm_tmp_pkg}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ if [[ -n "${found}" ]]; then
+ cp "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ else
+ echo "ERROR: Failed to package helm chart for ${slug}"
+ return 1
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ local helm_reg="${HELM_REGISTRY:-docker.io/apache}"
+ if [[ -z "${helm_reg}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${helm_reg}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for tgz in "${helm_tmp_pkg}"/*-"${VERSION}".tgz; do
+ if [[ -f "${tgz}" ]]; then
+ echo " PUSH ${tgz} -> oci://${helm_reg}"
+ helm push "${tgz}" "oci://${helm_reg}"
+ fi
+ done
+ echo "Helm charts pushed."
+ fi
+ rm -rf "${helm_tmp_pkg}"
+ trap - EXIT
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, kie-editors-standalone, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${VERSION}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="incubator-kie-sandbox-dev-deployment-base"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${VERSION}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/kie-editors-standalone..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-editors-standalone/dist:apache-kie-${VERSION}-incubating-business-automation-standalone-editors.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
+ fi
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN is required for publishing to GitHub Pages"
+ return 1
+ fi
+
+ local kogito_online_repo="https://github.com/apache/incubator-kie-kogito-online.git"
+ local accelerator_repo="https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+
+ local auth_url="${kogito_online_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --branch gh-pages --depth 1 "${auth_url}" "${tmp_dir}/kogito-online"
Review Comment:
Embedding `GITHUB_TOKEN` in the clone URL risks leaking the token via process listings and/or command echoing in logs (especially on CI). Prefer passing credentials via `GIT_ASKPASS`, a credential helper, or `http.extraheader` (e.g., `Authorization: Bearer ...`) to avoid placing secrets in command-line arguments.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,773 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}"
+ mkdir -p "${output_dir}"
+
+ local helm_tmp_pkg
+ helm_tmp_pkg=$(mktemp -d)
+ trap "rm -rf ${helm_tmp_pkg}" EXIT
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo "ERROR: Helm chart directory ${chart_pkg} not found"; return 1; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo "ERROR: Chart.yaml not found in ${chart_pkg}"; return 1; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${helm_tmp_pkg}"
+ rm -rf "${tmp_dir}"
+
+ local found
+ found=$(find "${helm_tmp_pkg}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ if [[ -n "${found}" ]]; then
+ cp "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ else
+ echo "ERROR: Failed to package helm chart for ${slug}"
+ return 1
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ local helm_reg="${HELM_REGISTRY:-docker.io/apache}"
+ if [[ -z "${helm_reg}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${helm_reg}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for tgz in "${helm_tmp_pkg}"/*-"${VERSION}".tgz; do
+ if [[ -f "${tgz}" ]]; then
+ echo " PUSH ${tgz} -> oci://${helm_reg}"
+ helm push "${tgz}" "oci://${helm_reg}"
+ fi
+ done
+ echo "Helm charts pushed."
+ fi
+ rm -rf "${helm_tmp_pkg}"
+ trap - EXIT
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, kie-editors-standalone, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${VERSION}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="incubator-kie-sandbox-dev-deployment-base"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${VERSION}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/kie-editors-standalone..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-editors-standalone/dist:apache-kie-${VERSION}-incubating-business-automation-standalone-editors.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
+ fi
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN is required for publishing to GitHub Pages"
+ return 1
+ fi
+
+ local kogito_online_repo="https://github.com/apache/incubator-kie-kogito-online.git"
+ local accelerator_repo="https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+
+ local auth_url="${kogito_online_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --branch gh-pages --depth 1 "${auth_url}" "${tmp_dir}/kogito-online"
+ (
+ cd "${tmp_dir}/kogito-online"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ find . -maxdepth 1 ! -name '.' ! -name 'dev' ! -name 'editors' ! -name 'standalone' \
+ ! -name 'chrome-extension' ! -name '.nojekyll' ! -name 'CNAME' -exec rm -rf {} +
+
+ local online_dist="${REPO_ROOT}/packages/online-editor/dist"
+ if [[ -d "${online_dist}" ]]; then
+ local editors_dir="editors/${VERSION}"
+ rm -rf "${editors_dir}" && mkdir -p "${editors_dir}"
+ rm -rf editors/latest && ln -s "${VERSION}" editors/latest
+ cp -r "${online_dist}/." .
+ fi
+ local standalone_dist="${REPO_ROOT}/packages/kie-editors-standalone/dist"
+ [[ -d "${standalone_dist}" ]] && cp -r "${standalone_dist}/." "editors/${VERSION}/"
+ local chrome_dist="${REPO_ROOT}/packages/chrome-extension-pack-kogito-kie-editors/dist"
+ if [[ -d "${chrome_dist}" ]]; then
+ local chrome_dir="chrome-extension/${VERSION}"
+ rm -rf "${chrome_dir}" && mkdir -p "${chrome_dir}"
+ cp -r "${chrome_dist}/fonts" "${chrome_dir}/" 2>/dev/null || true
+ cp "${chrome_dist}"/*-envelope.* "${chrome_dir}/" 2>/dev/null || true
+ fi
+ git add .
+ git commit -m "Deploy ${VERSION} (Online Editor + Standalone Editors + Chrome Extension editors)" \
+ || echo " (nothing to commit)"
+ git push origin gh-pages
+ )
+ echo "Pushed to GitHub Pages."
+
+ local accel_content="${REPO_ROOT}/packages/kie-sandbox-accelerator-quarkus/dist/git-repo-content"
+ if [[ -d "${accel_content}" ]]; then
+ local auth_accel="${accelerator_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --depth 1 "${auth_accel}" "${tmp_dir}/accelerator"
+ (
+ cd "${tmp_dir}/accelerator"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git checkout --orphan "${VERSION}"
Review Comment:
`git checkout --orphan` keeps the current working tree contents; without removing tracked/untracked files first, the new `${VERSION}` branch/tag can accidentally include files from whatever default branch was cloned. After creating the orphan branch, remove all files (including untracked) before copying `${accel_content}` so the branch contains only the intended release payload.
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,773 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
Review Comment:
The script relies on Bash associative arrays (`declare -A`), which are not supported by the default Bash 3.2 shipped on macOS. Since this is positioned as a 'local-first' release script, consider either documenting the Bash 4+ requirement explicitly in the README (e.g., via Homebrew bash) or refactoring these maps to a Bash-3-compatible representation (e.g., newline-delimited `key=value` lists).
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,773 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/{fonts,*-envelope.*} "${tmp_kie}/"
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}"
+ mkdir -p "${output_dir}"
+
+ local helm_tmp_pkg
+ helm_tmp_pkg=$(mktemp -d)
+ trap "rm -rf ${helm_tmp_pkg}" EXIT
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo "ERROR: Helm chart directory ${chart_pkg} not found"; return 1; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo "ERROR: Chart.yaml not found in ${chart_pkg}"; return 1; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${helm_tmp_pkg}"
+ rm -rf "${tmp_dir}"
+
+ local found
+ found=$(find "${helm_tmp_pkg}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ if [[ -n "${found}" ]]; then
+ cp "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ else
+ echo "ERROR: Failed to package helm chart for ${slug}"
+ return 1
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ local helm_reg="${HELM_REGISTRY:-docker.io/apache}"
+ if [[ -z "${helm_reg}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${helm_reg}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for tgz in "${helm_tmp_pkg}"/*-"${VERSION}".tgz; do
+ if [[ -f "${tgz}" ]]; then
+ echo " PUSH ${tgz} -> oci://${helm_reg}"
+ helm push "${tgz}" "oci://${helm_reg}"
+ fi
+ done
+ echo "Helm charts pushed."
+ fi
+ rm -rf "${helm_tmp_pkg}"
+ trap - EXIT
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, kie-editors-standalone, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${VERSION}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="incubator-kie-sandbox-dev-deployment-base"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${VERSION}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="docker.io"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="apache"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${VERSION}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/kie-editors-standalone..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-editors-standalone/dist:apache-kie-${VERSION}-incubating-business-automation-standalone-editors.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
+ fi
+ done
+ echo "RC artifacts in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${GITHUB_TOKEN:-}" ]]; then
+ echo "ERROR: GITHUB_TOKEN is required for publishing to GitHub Pages"
+ return 1
+ fi
+
+ local kogito_online_repo="https://github.com/apache/incubator-kie-kogito-online.git"
+ local accelerator_repo="https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git"
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ trap "rm -rf ${tmp_dir}" EXIT
+
+ local auth_url="${kogito_online_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --branch gh-pages --depth 1 "${auth_url}" "${tmp_dir}/kogito-online"
+ (
+ cd "${tmp_dir}/kogito-online"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ find . -maxdepth 1 ! -name '.' ! -name 'dev' ! -name 'editors' ! -name 'standalone' \
+ ! -name 'chrome-extension' ! -name '.nojekyll' ! -name 'CNAME' -exec rm -rf {} +
+
+ local online_dist="${REPO_ROOT}/packages/online-editor/dist"
+ if [[ -d "${online_dist}" ]]; then
+ local editors_dir="editors/${VERSION}"
+ rm -rf "${editors_dir}" && mkdir -p "${editors_dir}"
+ rm -rf editors/latest && ln -s "${VERSION}" editors/latest
+ cp -r "${online_dist}/." .
+ fi
+ local standalone_dist="${REPO_ROOT}/packages/kie-editors-standalone/dist"
+ [[ -d "${standalone_dist}" ]] && cp -r "${standalone_dist}/." "editors/${VERSION}/"
+ local chrome_dist="${REPO_ROOT}/packages/chrome-extension-pack-kogito-kie-editors/dist"
+ if [[ -d "${chrome_dist}" ]]; then
+ local chrome_dir="chrome-extension/${VERSION}"
+ rm -rf "${chrome_dir}" && mkdir -p "${chrome_dir}"
+ cp -r "${chrome_dist}/fonts" "${chrome_dir}/" 2>/dev/null || true
+ cp "${chrome_dist}"/*-envelope.* "${chrome_dir}/" 2>/dev/null || true
+ fi
+ git add .
+ git commit -m "Deploy ${VERSION} (Online Editor + Standalone Editors + Chrome Extension editors)" \
+ || echo " (nothing to commit)"
+ git push origin gh-pages
+ )
+ echo "Pushed to GitHub Pages."
+
+ local accel_content="${REPO_ROOT}/packages/kie-sandbox-accelerator-quarkus/dist/git-repo-content"
+ if [[ -d "${accel_content}" ]]; then
+ local auth_accel="${accelerator_repo/https:\/\//https://${GITHUB_TOKEN}@}"
+ git clone --depth 1 "${auth_accel}" "${tmp_dir}/accelerator"
+ (
+ cd "${tmp_dir}/accelerator"
+ git config user.email "asf-ci-kie@jenkins.kie.apache.org"
+ git config user.name "Apache KIE Release Bot"
+ git checkout --orphan "${VERSION}"
+ cp -r "${accel_content}/." .
+ git add .
+ git commit -m "Apache KIE Sandbox Quarkus Accelerator ${VERSION}"
+ git tag "${VERSION}"
+ git push origin "refs/heads/${VERSION}:refs/heads/${VERSION}" "refs/tags/${VERSION}:refs/tags/${VERSION}"
+ )
+ echo "Pushed accelerator to tag ${VERSION}."
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 7. dev-deployment-upload-service
+# ---------------------------------------------------------------------------
+release_dev_deployment_upload_service() {
+ echo ""
+ echo "--- dev-deployment-upload-service ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building dev-deployment-upload-service..."
+ DDUS_VERSION="${VERSION}" pnpm -F "@kie-tools/dev-deployment-upload-service..." build:prod
+ fi
+
+ local dist="${REPO_ROOT}/packages/dev-deployment-upload-service/dist"
+ if [[ ! -d "${dist}" ]]; then
+ echo "ERROR: packages/dev-deployment-upload-service/dist not found"
+ return 1
+ fi
+
+ declare -A PLATFORM_MAP=(
Review Comment:
The script relies on Bash associative arrays (`declare -A`), which are not supported by the default Bash 3.2 shipped on macOS. Since this is positioned as a 'local-first' release script, consider either documenting the Bash 4+ requirement explicitly in the README (e.g., via Homebrew bash) or refactoring these maps to a Bash-3-compatible representation (e.g., newline-delimited `key=value` lists).
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] kie-issues#4025: 10.3.x+ stream: Re-write kie-tools release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "Kusuma04-dev (via GitHub)" <gi...@apache.org>.
Kusuma04-dev commented on PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#issuecomment-6394231696
Thanks for pointing @tiagobento , Updated!
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] [incubator-kie-tools#4025] 10.3.x+ stream: Re-write `kie-tools` release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "Kusuma04-dev (via GitHub) via commits" <co...@kie.apache.org>.
Kusuma04-dev commented on PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#issuecomment-6804497114
Thanks @tiagobento , i have alreaddy addressed valid review comments and CI is green so merging it.
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] kie-issues#2405: 10.3.x+ stream: Re-write kie-tools release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "Kusuma04-dev (via GitHub)" <gi...@apache.org>.
Kusuma04-dev commented on PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#issuecomment-5813348798
Thanks @tiagobento ! I have updated the script.
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] [incubator-kie-tools#4025] 10.3.x+ stream: Re-write `kie-tools` release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "tiagobento (via GitHub) via commits" <co...@kie.apache.org>.
tiagobento commented on PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#issuecomment-6798657067
@Kusuma04-dev Please merge when green. And take a look at the latest Copilot review. I think those are mostly not applicable to us, but worth a closer look.
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org
Re: [PR] [incubator-kie-tools#4025] 10.3.x+ stream: Re-write `kie-tools` release jobs to be local scripts invoked in Apache Jenkins and remove old ones. [incubator-kie-tools]
Posted by "Kusuma04-dev (via GitHub) via commits" <co...@kie.apache.org>.
Kusuma04-dev commented on code in PR #4019:
URL: https://github.com/apache/incubator-kie-tools/pull/4019#discussion_r4493597543
##########
scripts/release/release-all.sh:
##########
@@ -0,0 +1,810 @@
+#!/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
+
+# Release script for Apache KIE Tools — builds and optionally releases all components
+# under a single unified build.
+#
+# Usage:
+# ./release-all.sh <version> # dry run (build only, nothing published)
+# ./release-all.sh <version> --rc # build + collect Apache RC artifacts
+# ./release-all.sh <version> --publish # build + publish to all public registries
+#
+# Optional flags:
+# --skip-build Skip pnpm build:prod (use existing dist/ output)
+# --upload-url <url> GitHub Release upload URL (required by --publish for binary assets)
+# --registry <url> Container registry (default: docker.io/apache)
+# --git-ref <ref> Git reference for source tarball (default: HEAD)
+#
+# Credentials (required only for --publish, sourced from env):
+# NPM_TOKEN, VSCE_PAT,
+# CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN, CHROME_KIE_EDITORS_EXTENSION_ID,
+# DOCKER_USERNAME, DOCKER_PASSWORD,
+# HELM_REGISTRY (default: docker.io/apache), HELM_USERNAME (optional), HELM_PASSWORD (optional),
+# GITHUB_TOKEN
+
+export KIE_TOOLS_BUILD__runLinters=false
+export KIE_TOOLS_BUILD__runTests=false
+export KIE_TOOLS_BUILD__runEndToEndTests=false
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+VERSION="${1:-}"
+PUBLISH=false
+RC_MODE=false
+SKIP_BUILD=false
+UPLOAD_URL=""
+REGISTRY="docker.io/apache"
+GIT_REF="HEAD"
+
+shift || true
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --publish) PUBLISH=true; shift ;;
+ --rc) RC_MODE=true; shift ;;
+ --skip-build) SKIP_BUILD=true; shift ;;
+ --upload-url) UPLOAD_URL="${2:-}"; shift 2 ;;
+ --registry) REGISTRY="${2:-}"; shift 2 ;;
+ --git-ref) GIT_REF="${2:-}"; shift 2 ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>] [--git-ref <ref>]"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "${VERSION}" ]]; then
+ echo "Usage: $0 <version> [--publish] [--rc] [--skip-build] [--upload-url <url>] [--registry <url>]"
+ echo "Example: $0 10.2.0 --rc"
+ echo "Example: $0 10.2.0 --publish"
+ exit 1
+fi
+
+if [[ "${PUBLISH}" == "true" && -z "${UPLOAD_URL}" ]]; then
+ echo "ERROR: --upload-url is required when --publish is specified."
+ exit 1
+fi
+
+# Production defaults for Chrome extension and Online Editor builds
+export CHROME_EXTENSION__routerTargetOrigin="${CHROME_EXTENSION__routerTargetOrigin:-https://apache.github.io}"
+export CHROME_EXTENSION__routerRelativePath="${CHROME_EXTENSION__routerRelativePath:-incubator-kie-kogito-online/chrome-extension/${VERSION}}"
+export CHROME_EXTENSION__manifestFile="${CHROME_EXTENSION__manifestFile:-manifest.prod.json}"
+export CHROME_EXTENSION__onlineEditorUrl="${CHROME_EXTENSION__onlineEditorUrl:-https://apache.github.io/incubator-kie-kogito-online}"
+
+ARTIFACTS_DIR="${REPO_ROOT}/release-artifacts"
+
+echo "=========================================="
+echo "KIE Tools Release"
+echo "Version: ${VERSION}"
+echo "RC mode: ${RC_MODE}"
+echo "Publish: ${PUBLISH}"
+echo "Skip build: ${SKIP_BUILD}"
+echo "Registry: ${REGISTRY}"
+echo "=========================================="
+
+cd "${REPO_ROOT}"
+if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo ""
+ echo "--- Bumping workspace version to ${VERSION} ---"
+ pnpm update-version-to "${VERSION}"
+ pnpm update-kogito-version-to --maven "${VERSION}"
+ pnpm update-stream-name-to "${VERSION}"
+fi
+
+# ---------------------------------------------------------------------------
+# Helper: sign and upload a single asset to a GitHub Release
+# ---------------------------------------------------------------------------
+upload_github_asset() {
+ local file="$1"
+ local asset_name="$2"
+ local content_type="${3:-application/octet-stream}"
+ if [[ ! -f "${file}" ]]; then
+ echo " SKIP ${asset_name} (file not found)"
+ return
+ fi
+ echo " UPLOAD ${asset_name}"
+ curl -sSf \
+ -H "Authorization: token ${GITHUB_TOKEN}" \
+ -H "Content-Type: ${content_type}" \
+ --data-binary "@${file}" \
+ "${UPLOAD_URL}?name=${asset_name}"
+}
+
+# ---------------------------------------------------------------------------
+# 1. NPM packages
+# ---------------------------------------------------------------------------
+release_npm_packages() {
+ echo ""
+ echo "--- NPM packages ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building public NPM packages..."
+ local filter
+ filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)..."; fi')
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ local tmp_dir="${ARTIFACTS_DIR}/npm-packages-tmp"
+ local zip_file="apache-kie-${VERSION}-incubating-tools-npm-packages.zip"
+ mkdir -p "${tmp_dir}" "${ARTIFACTS_DIR}"
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c "pnpm pack --pack-destination ${tmp_dir}"
+ (cd "${tmp_dir}" && zip -r "${ARTIFACTS_DIR}/${zip_file}" .)
+ rm -rf "${tmp_dir}"
+ echo "RC artifact: ${ARTIFACTS_DIR}/${zip_file}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${NPM_TOKEN:-}" ]]; then
+ echo "ERROR: NPM_TOKEN is required for publishing npm packages"
+ return 1
+ fi
+ local npmrc_tmp
+ npmrc_tmp=$(mktemp)
+ trap "rm -f '${npmrc_tmp}'" EXIT
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${npmrc_tmp}"
+ export NPM_CONFIG_USERCONFIG="${npmrc_tmp}"
+
+ local pub_filter
+ pub_filter=$(pnpm -r exec bash -c \
+ 'if [[ $(jq -r ".private" package.json) != "true" ]]; then echo "-F $(jq -r ".name" package.json)"; fi')
+ pnpm ${pub_filter} exec bash -c '
+ PKG_NAME=$(jq -r ".name" package.json)
+ if ! npm view ${PKG_NAME}@'"${VERSION}"' name --userconfig "'"${npmrc_tmp}"'" &>/dev/null; then
+ echo "Publishing ${PKG_NAME}@'"${VERSION}"'"
+ pnpm publish --no-git-checks --access public --userconfig "'"${npmrc_tmp}"'"
+ else
+ echo "Skipping ${PKG_NAME}@'"${VERSION}"' (already published)"
+ fi
+ '
+ rm -f "${npmrc_tmp}"
+ trap - EXIT
+ echo "NPM packages published."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 2. Chrome extensions
+# ---------------------------------------------------------------------------
+release_chrome_extensions() {
+ echo ""
+ echo "--- Chrome extensions ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building Chrome extensions..."
+ pnpm -F "chrome-extension-pack-kogito-kie-editors..." build:prod
+ fi
+
+ local kie_editors_zip
+ kie_editors_zip=$(find "packages/chrome-extension-pack-kogito-kie-editors/dist" \
+ -maxdepth 1 -name "chrome_extension_kogito_kie_editors_*.zip" 2>/dev/null | head -1 || true)
+ if [[ -z "${kie_editors_zip}" ]]; then
+ echo "ERROR: chrome_extension_kogito_kie_editors_*.zip not found — was the build successful?"
+ return 1
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ cp "${kie_editors_zip}" \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension.zip"
+
+ local tmp_kie="${ARTIFACTS_DIR}/kie-editors-tmp"
+ mkdir -p "${tmp_kie}"
+ [[ -d "packages/chrome-extension-pack-kogito-kie-editors/dist/fonts" ]] && cp -r packages/chrome-extension-pack-kogito-kie-editors/dist/fonts "${tmp_kie}/"
+ cp packages/chrome-extension-pack-kogito-kie-editors/dist/*-envelope.* "${tmp_kie}/" 2>/dev/null || true
+ (cd "${tmp_kie}" && zip -r \
+ "${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-business-automation-chrome-extension-editors.zip" .)
+ rm -rf "${tmp_kie}"
+ echo "RC artifacts created in: ${ARTIFACTS_DIR}"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${CHROME_CLIENT_ID:-}" || -z "${CHROME_CLIENT_SECRET:-}" || -z "${CHROME_REFRESH_TOKEN:-}" ]]; then
+ echo "ERROR: CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, and CHROME_REFRESH_TOKEN are required"
+ return 1
+ fi
+
+ local token_res
+ token_res=$(curl -sSf -X POST "https://oauth2.googleapis.com/token" \
+ -d "client_id=${CHROME_CLIENT_ID}" \
+ -d "client_secret=${CHROME_CLIENT_SECRET}" \
+ -d "refresh_token=${CHROME_REFRESH_TOKEN}" \
+ -d "grant_type=refresh_token")
+ local access_token
+ access_token=$(echo "${token_res}" | jq -r '.access_token // empty')
+
+ if [[ -z "${access_token}" ]]; then
+ echo "ERROR: Failed to obtain Chrome Web Store OAuth token: ${token_res}"
+ return 1
+ fi
+
+ _chrome_upload() {
+ local ext_id="$1" zip="$2"
+ local res state
+ res=$(curl -sSf -X PUT \
+ "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${ext_id}" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -T "${zip}")
+ state=$(echo "${res}" | jq -r '.uploadState // empty')
+ if [[ "${state}" != "SUCCESS" ]]; then
+ echo "ERROR: Chrome Web Store upload failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+ _chrome_publish() {
+ local ext_id="$1"
+ local res pub_status
+ res=$(curl -sSf -X POST \
+ "https://www.googleapis.com/chromewebstore/v1.1/items/${ext_id}/publish" \
+ -H "Authorization: Bearer ${access_token}" \
+ -H "x-goog-api-version:2" \
+ -H "Content-Length: 0")
+ pub_status=$(echo "${res}" | jq -r '.status[0] // .status // empty')
+ if [[ "${pub_status}" != "OK" && "${pub_status}" != "PUBLISHED_WITH_FRICTION_WARNING" ]]; then
+ echo "ERROR: Chrome Web Store publish failed for ${ext_id}: ${res}"
+ return 1
+ fi
+ }
+
+ if [[ -n "${CHROME_KIE_EDITORS_EXTENSION_ID:-}" ]]; then
+ _chrome_upload "${CHROME_KIE_EDITORS_EXTENSION_ID}" "${kie_editors_zip}"
+ _chrome_publish "${CHROME_KIE_EDITORS_EXTENSION_ID}"
+ echo "Chrome extension published."
+ else
+ echo "WARN: CHROME_KIE_EDITORS_EXTENSION_ID not set — skipping Chrome publish"
+ fi
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 3. VSCode extensions
+# ---------------------------------------------------------------------------
+release_vscode() {
+ echo ""
+ echo "--- VSCode extensions ---"
+
+ declare -A EXTENSIONS=(
+ ["bpmn-vscode-extension"]="bpmn-vscode-extension"
+ ["dmn-vscode-extension"]="dmn-vscode-extension"
+ ["drl-vscode-extension"]="drl-vscode-extension"
+ ["pmml-vscode-extension"]="pmml-vscode-extension"
+ ["vscode-extension-kogito-bundle"]="kogito-bundle-vscode-extension"
+ ["vscode-extension-kie-ba-bundle"]="business-automation-bundle-vscode-extension"
+ ["extended-services-vscode-extension"]="extended-services-vscode-extension"
+ )
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ local filter=""
+ for ext in "${!EXTENSIONS[@]}"; do filter="${filter} -F ${ext}..."; done
+ echo "Building VSCode extensions..."
+ pnpm ${filter} build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for ext in "${!EXTENSIONS[@]}"; do
+ local suffix="${EXTENSIONS[$ext]}"
+ local vsix
+ vsix=$(find "packages/${ext}" -maxdepth 2 -name "*.vsix" 2>/dev/null | head -1 || true)
+ if [[ -n "${vsix}" ]]; then
+ local dest="${ARTIFACTS_DIR}/apache-kie-${VERSION}-incubating-${suffix}.vsix"
+ echo " COPY $(basename "${dest}")"
+ cp "${vsix}" "${dest}"
+ else
+ echo " SKIP ${ext} (no .vsix found)"
+ fi
+ done
+ echo "VSIX files in: ${ARTIFACTS_DIR}/"
+ fi
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${VSCE_PAT:-}" ]]; then
+ echo "ERROR: VSCE_PAT is required for publishing VSCode extensions"
+ return 1
+ fi
+ echo "Publishing to VSCode Marketplace..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ echo " Publishing: ${vsix}"
+ npx vsce publish --packagePath "${vsix}" --pat "${VSCE_PAT}"
+ done
+ done
+ echo "VSCode extensions published."
+ fi
+
+ if [[ "${PUBLISH}" == "true" && -n "${UPLOAD_URL:-}" && -n "${GITHUB_TOKEN:-}" ]]; then
+ echo "Uploading .vsix files to GitHub Release..."
+ for ext in "${!EXTENSIONS[@]}"; do
+ find "packages/${ext}" -maxdepth 2 -name "*.vsix" | while read -r vsix; do
+ upload_github_asset "${vsix}" "$(basename "${vsix}")" "application/zip"
+ done
+ done
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 4. Container images
+# ---------------------------------------------------------------------------
+release_container_images() {
+ echo ""
+ echo "--- Container images ---"
+
+ declare -A IMAGES=(
+ ["cors-proxy"]="packages/cors-proxy-image:incubator-kie-cors-proxy"
+ ["sandbox-webapp"]="packages/kie-sandbox-webapp-image:incubator-kie-sandbox-webapp"
+ ["sandbox-extended-services"]="packages/kie-sandbox-extended-services-image:incubator-kie-sandbox-extended-services"
+ ["sandbox-dev-deployment-base"]="packages/dev-deployment-base-image:incubator-kie-sandbox-dev-deployment-base"
+ ["sandbox-dev-deployment-dmn-form-webapp"]="packages/dev-deployment-dmn-form-webapp-image:incubator-kie-sandbox-dev-deployment-dmn-form-webapp"
+ ["sandbox-dev-deployment-quarkus-blank-app"]="packages/dev-deployment-quarkus-blank-app-image:incubator-kie-sandbox-dev-deployment-quarkus-blank-app"
+ ["kogito-management-console"]="packages/kogito-management-console:incubator-kie-kogito-management-console"
+ )
+
+ export KIE_TOOLS_BUILD__buildContainerImages=true
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ if ! docker info &>/dev/null; then
+ echo "WARN: Docker is not running — skipping container image build"
+ return 0
+ fi
+ echo "Building container images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local pkg_name
+ pkg_name=$(node -p "require('./${pkg_path}/package.json').name" 2>/dev/null || basename "${pkg_path}")
+ echo " BUILD ${artifact_name} (${pkg_name})"
+ pnpm --filter "${pkg_name}..." build:prod
+ done
+ else
+ if ! docker info &>/dev/null; then
+ echo "SKIP: Docker is not running — cannot save container image tarballs with --skip-build"
+ return 0
+ fi
+ fi
+
+ local output_dir="${ARTIFACTS_DIR}"
+ [[ "${RC_MODE}" == "true" ]] && mkdir -p "${output_dir}"
+
+ echo "Tagging images..."
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local pkg_path="${mapping%%:*}"
+ local image_name="${mapping##*:}"
+ if [[ ! -d "${pkg_path}" ]]; then
+ echo "ERROR: Image package directory ${pkg_path} not found"
+ return 1
+ fi
+ local target_image="${REGISTRY}/${image_name}:${VERSION}"
+
+ # Resolve built image from local docker daemon
+ local source_image=""
+ for candidate in \
+ "docker.io/apache/${image_name}:${VERSION}" \
+ "apache/${image_name}:${VERSION}" \
+ "${image_name}:${VERSION}" \
+ "docker.io/apache/${image_name}:latest" \
+ "apache/${image_name}:latest" \
+ "${image_name}:latest"; do
+ if docker image inspect "${candidate}" &>/dev/null; then
+ source_image="${candidate}"
+ break
+ fi
+ done
+
+ if [[ -n "${source_image}" ]]; then
+ docker tag "${source_image}" "${target_image}"
+ else
+ echo " WARN could not find local image for ${artifact_name} (${image_name})"
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ if docker image inspect "${target_image}" &>/dev/null; then
+ local tarball="${output_dir}/apache-kie-${VERSION}-incubating-${artifact_name}-image.tar.gz"
+ echo " SAVE ${tarball}"
+ docker save "${target_image}" | gzip > "${tarball}"
+ else
+ echo "ERROR: Image ${target_image} not found locally for RC export"
+ return 1
+ fi
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ if [[ -z "${DOCKER_USERNAME:-}" || -z "${DOCKER_PASSWORD:-}" ]]; then
+ echo "ERROR: DOCKER_USERNAME and DOCKER_PASSWORD are required for publishing container images"
+ return 1
+ fi
+ echo "Pushing images to ${REGISTRY}..."
+ echo "${DOCKER_PASSWORD}" | docker login "$(echo "${REGISTRY}" | cut -d/ -f1)" \
+ -u "${DOCKER_USERNAME}" --password-stdin
+ for artifact_name in "${!IMAGES[@]}"; do
+ local mapping="${IMAGES[$artifact_name]}"
+ local image_name="${mapping##*:}"
+ local full_image="${REGISTRY}/${image_name}:${VERSION}"
+ echo " PUSH ${full_image}"
+ docker push "${full_image}"
+ done
+ docker logout "$(echo "${REGISTRY}" | cut -d/ -f1)"
+ echo "Container images pushed."
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# 5. Helm charts
+# ---------------------------------------------------------------------------
+release_helm_charts() {
+ echo ""
+ echo "--- Helm charts ---"
+
+ if ! command -v helm &>/dev/null; then
+ echo "WARN: helm not found — skipping Helm chart release"
+ return 0
+ fi
+
+ declare -A CHARTS=(
+ ["sandbox-helm-chart"]="packages/kie-sandbox-helm-chart"
+ ["runtime-tools-console-helm-chart"]="packages/runtime-tools-consoles-helm-chart"
+ )
+
+ local output_dir="${ARTIFACTS_DIR}"
+ mkdir -p "${output_dir}"
+
+ local helm_tmp_pkg
+ helm_tmp_pkg=$(mktemp -d)
+ trap "rm -rf ${helm_tmp_pkg}" EXIT
+
+ echo "Packaging Helm charts..."
+ for slug in "${!CHARTS[@]}"; do
+ local chart_pkg="${CHARTS[$slug]}"
+ [[ -d "${chart_pkg}" ]] || { echo "ERROR: Helm chart directory ${chart_pkg} not found"; return 1; }
+
+ local chart_yaml=""
+ for candidate in "${chart_pkg}/src/Chart.yaml" "${chart_pkg}/Chart.yaml"; do
+ [[ -f "${candidate}" ]] && { chart_yaml="${candidate}"; break; }
+ done
+ [[ -z "${chart_yaml}" ]] && { echo "ERROR: Chart.yaml not found in ${chart_pkg}"; return 1; }
+
+ local chart_dir
+ chart_dir="$(dirname "${chart_yaml}")"
+ local chart_name
+ chart_name=$(grep '^name:' "${chart_yaml}" | head -1 | awk '{print $2}' | tr -d '"'"'")
+ local tmp_dir
+ tmp_dir=$(mktemp -d)
+ cp -r "${chart_dir}/." "${tmp_dir}/chart"
+ sed -i.bak "s/^version:.*/version: ${VERSION}/" "${tmp_dir}/chart/Chart.yaml"
+ sed -i.bak "s/^appVersion:.*/appVersion: \"${VERSION}\"/" "${tmp_dir}/chart/Chart.yaml"
+ rm -f "${tmp_dir}/chart/Chart.yaml.bak"
+ helm package "${tmp_dir}/chart" --destination "${helm_tmp_pkg}"
+ rm -rf "${tmp_dir}"
+
+ local found=""
+ if [[ -n "${chart_name}" && -f "${helm_tmp_pkg}/${chart_name}-${VERSION}.tgz" ]]; then
+ found="${helm_tmp_pkg}/${chart_name}-${VERSION}.tgz"
+ else
+ found=$(find "${helm_tmp_pkg}" -maxdepth 1 -name "*-${VERSION}.tgz" | head -1)
+ fi
+ if [[ -n "${found}" ]]; then
+ cp "${found}" "${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ echo " DONE ${output_dir}/apache-kie-${VERSION}-incubating-${slug}.tar.gz"
+ else
+ echo "ERROR: Failed to package helm chart for ${slug}"
+ return 1
+ fi
+ done
+
+ if [[ "${PUBLISH}" == "true" ]]; then
+ local helm_reg="${HELM_REGISTRY:-docker.io/apache}"
+ if [[ -z "${helm_reg}" ]]; then
+ echo "ERROR: HELM_REGISTRY is required for publishing Helm charts"
+ return 1
+ fi
+ if [[ -n "${HELM_USERNAME:-}" && -n "${HELM_PASSWORD:-}" ]]; then
+ echo "${HELM_PASSWORD}" | helm registry login "$(echo "${helm_reg}" | cut -d/ -f1)" \
+ --username "${HELM_USERNAME}" --password-stdin
+ fi
+ for tgz in "${helm_tmp_pkg}"/*-"${VERSION}".tgz; do
+ if [[ -f "${tgz}" ]]; then
+ echo " PUSH ${tgz} -> oci://${helm_reg}"
+ helm push "${tgz}" "oci://${helm_reg}"
+ fi
+ done
+ echo "Helm charts pushed."
+ fi
+ rm -rf "${helm_tmp_pkg}"
+ trap - EXIT
+}
+
+# ---------------------------------------------------------------------------
+# 6. GitHub Pages / webapp / accelerator
+# ---------------------------------------------------------------------------
+release_github_pages() {
+ echo ""
+ echo "--- GitHub Pages / webapp / accelerator ---"
+
+ if [[ "${SKIP_BUILD}" == "false" ]]; then
+ echo "Building online-editor, chrome-extension-pack-kogito-kie-editors, kie-sandbox-accelerator-quarkus..."
+ export ONLINE_EDITOR__buildInfo="${ONLINE_EDITOR__buildInfo:-${VERSION}}"
+ export ONLINE_EDITOR__extendedServicesCompatibleVersion="${ONLINE_EDITOR__extendedServicesCompatibleVersion:-${VERSION}}"
+ export ONLINE_EDITOR__corsProxyUrl="${ONLINE_EDITOR__corsProxyUrl:-https://cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud}"
+ export ONLINE_EDITOR__quarkusAcceleratorGitRepoUrl="${ONLINE_EDITOR__quarkusAcceleratorGitRepoUrl:-https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git}"
+ export ONLINE_EDITOR__quarkusAcceleratorGitRef="${ONLINE_EDITOR__quarkusAcceleratorGitRef:-${VERSION}}"
+ export ONLINE_EDITOR__devDeploymentBaseImageRegistry="${ONLINE_EDITOR__devDeploymentBaseImageRegistry:-docker.io}"
+ export ONLINE_EDITOR__devDeploymentBaseImageAccount="${ONLINE_EDITOR__devDeploymentBaseImageAccount:-apache}"
+ export ONLINE_EDITOR__devDeploymentBaseImageName="${ONLINE_EDITOR__devDeploymentBaseImageName:-incubator-kie-sandbox-dev-deployment-base}"
+ export ONLINE_EDITOR__devDeploymentBaseImageTag="${ONLINE_EDITOR__devDeploymentBaseImageTag:-${VERSION}}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry="${ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry:-docker.io}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount="${ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount:-apache}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageName="${ONLINE_EDITOR__devDeploymentDmnFormWebappImageName:-incubator-kie-sandbox-dev-deployment-dmn-form-webapp}"
+ export ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag="${ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag:-${VERSION}}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry="${ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageRegistry:-docker.io}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount="${ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageAccount:-apache}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName="${ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageName:-incubator-kie-sandbox-dev-deployment-quarkus-blank-app}"
+ export ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag="${ONLINE_EDITOR__devDeploymentQuarkusBlankAppImageTag:-${VERSION}}"
+ pnpm \
+ -F "@kie-tools/online-editor..." \
+ -F "@kie-tools/chrome-extension-pack-kogito-kie-editors..." \
+ -F "@kie-tools/kie-sandbox-accelerator-quarkus..." \
+ build:prod
+ fi
+
+ if [[ "${RC_MODE}" == "true" ]]; then
+ mkdir -p "${ARTIFACTS_DIR}"
+ for src_path_suffix in \
+ "packages/online-editor/dist:apache-kie-${VERSION}-incubating-sandbox-webapp.zip" \
+ "packages/kie-sandbox-accelerator-quarkus/dist:apache-kie-${VERSION}-incubating-sandbox-accelerator-quarkus.zip"
+ do
+ local src="${REPO_ROOT}/${src_path_suffix%%:*}"
+ local zip_name="${src_path_suffix##*:}"
+ if [[ -d "${src}" ]]; then
+ echo " ZIP ${zip_name}"
+ (cd "${src}" && zip -r "${ARTIFACTS_DIR}/${zip_name}" .)
+ else
+ echo " SKIP ${zip_name} (${src} not found)"
Review Comment:
This is not needed.
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org