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 "[email protected]" + 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 "[email protected]" + 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: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
