From 47ba91e79a6855d363ae2b70a8476ed1a6521b81 Mon Sep 17 00:00:00 2001 From: Jinkun Liu Date: Wed, 1 Apr 2026 00:56:13 +0800 Subject: [PATCH 01/13] [tools]Add an installation wizard --- tools/install.sh | 838 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 838 insertions(+) create mode 100755 tools/install.sh diff --git a/tools/install.sh b/tools/install.sh new file mode 100755 index 000000000..97a313fad --- /dev/null +++ b/tools/install.sh @@ -0,0 +1,838 @@ +#!/bin/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 + +BOLD='\033[1m' +ACCENT='\033[38;2;255;77;77m' # coral-bright +INFO='\033[38;2;136;146;176m' # text-secondary +SUCCESS='\033[38;2;0;229;204m' # cyan-bright +WARN='\033[38;2;255;176;32m' # amber +ERROR='\033[38;2;230;57;70m' # coral-mid +MUTED='\033[38;2;90;100;128m' # text-muted +NC='\033[0m' # No Color + +TMPFILES=() +cleanup_tmpfiles() { + local f + for f in "${TMPFILES[@]:-}"; do + rm -rf "$f" 2>/dev/null || true + done +} +trap cleanup_tmpfiles EXIT + +mktempfile() { + local f + f="$(mktemp)" + TMPFILES+=("$f") + echo "$f" +} + +DOWNLOADER="" +detect_downloader() { + if command -v curl &> /dev/null; then + DOWNLOADER="curl" + return 0 + fi + if command -v wget &> /dev/null; then + DOWNLOADER="wget" + return 0 + fi + ui_error "Missing downloader (curl or wget required)" + exit 1 +} + +download_file() { + local url="$1" + local output="$2" + if [[ -z "$DOWNLOADER" ]]; then + detect_downloader + fi + if [[ "$DOWNLOADER" == "curl" ]]; then + curl -fsSL --proto '=https' --tlsv1.2 --retry 3 --retry-delay 1 --retry-connrefused -o "$output" "$url" + return + fi + wget -q --https-only --secure-protocol=TLSv1_2 --tries=3 --timeout=20 -O "$output" "$url" +} + +GUM_VERSION="${FLINK_AGENTS_GUM_VERSION:-0.17.0}" +GUM="" +GUM_STATUS="skipped" +GUM_REASON="" + +is_non_interactive_shell() { + if [[ "${NO_PROMPT:-0}" == "1" ]]; then + return 0 + fi + if [[ ! -t 0 || ! -t 1 ]]; then + return 0 + fi + return 1 +} + +gum_is_tty() { + if [[ -n "${NO_COLOR:-}" ]]; then + return 1 + fi + if [[ "${TERM:-dumb}" == "dumb" ]]; then + return 1 + fi + if [[ -t 2 || -t 1 ]]; then + return 0 + fi + if [[ -r /dev/tty && -w /dev/tty ]]; then + return 0 + fi + return 1 +} + +gum_detect_os() { + case "$(uname -s 2>/dev/null || true)" in + Darwin) echo "Darwin" ;; + Linux) echo "Linux" ;; + *) echo "unsupported" ;; + esac +} + +gum_detect_arch() { + case "$(uname -m 2>/dev/null || true)" in + x86_64|amd64) echo "x86_64" ;; + arm64|aarch64) echo "arm64" ;; + i386|i686) echo "i386" ;; + armv7l|armv7) echo "armv7" ;; + armv6l|armv6) echo "armv6" ;; + *) echo "unknown" ;; + esac +} + +verify_sha256sum_file() { + local checksums="$1" + if command -v sha256sum >/dev/null 2>&1; then + sha256sum --ignore-missing -c "$checksums" >/dev/null 2>&1 + return $? + fi + if command -v shasum >/dev/null 2>&1; then + shasum -a 256 --ignore-missing -c "$checksums" >/dev/null 2>&1 + return $? + fi + return 1 +} + +bootstrap_gum_temp() { + GUM="" + GUM_STATUS="skipped" + GUM_REASON="" + + if is_non_interactive_shell; then + GUM_REASON="non-interactive shell (auto-disabled)" + return 1 + fi + + if ! gum_is_tty; then + GUM_REASON="terminal does not support gum UI" + return 1 + fi + + if command -v gum >/dev/null 2>&1; then + GUM="gum" + GUM_STATUS="found" + GUM_REASON="already installed" + return 0 + fi + + if ! command -v tar >/dev/null 2>&1; then + GUM_REASON="tar not found" + return 1 + fi + + local os arch asset base gum_tmpdir gum_path + os="$(gum_detect_os)" + arch="$(gum_detect_arch)" + if [[ "$os" == "unsupported" || "$arch" == "unknown" ]]; then + GUM_REASON="unsupported os/arch ($os/$arch)" + return 1 + fi + + asset="gum_${GUM_VERSION}_${os}_${arch}.tar.gz" + base="https://github.com/charmbracelet/gum/releases/download/v${GUM_VERSION}" + + gum_tmpdir="$(mktemp -d)" + TMPFILES+=("$gum_tmpdir") + + if ! download_file "${base}/${asset}" "$gum_tmpdir/$asset"; then + GUM_REASON="download failed" + return 1 + fi + + if ! download_file "${base}/checksums.txt" "$gum_tmpdir/checksums.txt"; then + GUM_REASON="checksum unavailable or failed" + return 1 + fi + + if ! (cd "$gum_tmpdir" && verify_sha256sum_file "checksums.txt"); then + GUM_REASON="checksum unavailable or failed" + return 1 + fi + + if ! tar -xzf "$gum_tmpdir/$asset" -C "$gum_tmpdir" >/dev/null 2>&1; then + GUM_REASON="extract failed" + return 1 + fi + + gum_path="$(find "$gum_tmpdir" -type f -name gum 2>/dev/null | head -n1 || true)" + if [[ -z "$gum_path" ]]; then + GUM_REASON="gum binary missing after extract" + return 1 + fi + + chmod +x "$gum_path" >/dev/null 2>&1 || true + if [[ ! -x "$gum_path" ]]; then + GUM_REASON="gum binary is not executable" + return 1 + fi + + GUM="$gum_path" + GUM_STATUS="installed" + GUM_REASON="temp, verified" + return 0 +} + +print_gum_status() { + case "$GUM_STATUS" in + found) + ui_success "gum available (${GUM_REASON})" + ;; + installed) + ui_success "gum bootstrapped (${GUM_REASON}, v${GUM_VERSION})" + ;; + *) + if [[ -n "$GUM_REASON" && "$GUM_REASON" != "non-interactive shell (auto-disabled)" ]]; then + ui_info "gum skipped (${GUM_REASON})" + fi + ;; + esac +} + +print_installer_banner() { + if [[ -n "$GUM" ]]; then + local title card + title="$("$GUM" style --foreground "#ff4d4d" --bold "Apache Flink Agents Installer")" + card="$(printf '%s\n' "$title")" + "$GUM" style --border rounded --border-foreground "#ff4d4d" --padding "1 2" "$card" + echo "" + return + fi + + echo -e "${ACCENT}${BOLD}" + echo " Apache Flink Agents Installer" + echo "" +} + +detect_os_or_die() { + OS="unknown" + if [[ "$OSTYPE" == "darwin"* ]]; then + OS="macos" + elif [[ "$OSTYPE" == "linux-gnu"* ]] || [[ -n "${WSL_DISTRO_NAME:-}" ]]; then + OS="linux" + fi + + if [[ "$OS" == "unknown" ]]; then + ui_error "Unsupported operating system" + echo "This installer supports macOS and Linux (including WSL)." + exit 1 + fi + + ui_success "Detected: $OS" +} + +ui_info() { + local msg="$*" + if [[ -n "$GUM" ]]; then + "$GUM" log --level info "$msg" + else + echo -e "${MUTED}·${NC} ${msg}" + fi +} + +ui_warn() { + local msg="$*" + if [[ -n "$GUM" ]]; then + "$GUM" log --level warn "$msg" + else + echo -e "${WARN}!${NC} ${msg}" + fi +} + +ui_success() { + local msg="$*" + if [[ -n "$GUM" ]]; then + local mark + mark="$("$GUM" style --foreground "#00e5cc" --bold "✓")" + echo "${mark} ${msg}" + else + echo -e "${SUCCESS}✓${NC} ${msg}" + fi +} + +ui_error() { + local msg="$*" + if [[ -n "$GUM" ]]; then + "$GUM" log --level error "$msg" + else + echo -e "${ERROR}x${NC} ${msg}" + fi +} + +INSTALL_STAGE_TOTAL=3 +INSTALL_STAGE_CURRENT=0 + +ui_section() { + local title="$1" + if [[ -n "$GUM" ]]; then + "$GUM" style --bold --foreground "#ff4d4d" --padding "1 0" "$title" + else + echo "" + echo -e "${ACCENT}${BOLD}${title}${NC}" + fi +} + +ui_stage() { + local title="$1" + INSTALL_STAGE_CURRENT=$((INSTALL_STAGE_CURRENT + 1)) + ui_section "[${INSTALL_STAGE_CURRENT}/${INSTALL_STAGE_TOTAL}] ${title}" +} + +ui_kv() { + local key="$1" + local value="$2" + if [[ -n "$GUM" ]]; then + local key_part value_part + key_part="$("$GUM" style --foreground "#5a6480" --width 20 "$key")" + value_part="$("$GUM" style --bold "$value")" + "$GUM" join --horizontal "$key_part" "$value_part" + else + echo -e "${MUTED}${key}:${NC} ${value}" + fi +} + +ui_celebrate() { + local msg="$1" + if [[ -n "$GUM" ]]; then + "$GUM" style --bold --foreground "#00e5cc" "$msg" + else + echo -e "${SUCCESS}${BOLD}${msg}${NC}" + fi +} + +is_shell_function() { + local name="${1:-}" + [[ -n "$name" ]] && declare -F "$name" >/dev/null 2>&1 +} + +is_gum_raw_mode_failure() { + local err_log="$1" + [[ -s "$err_log" ]] || return 1 + grep -Eiq '(setrawmode|inappropriate ioctl for device)' "$err_log" +} + +configure_verbose() { + if [[ "$VERBOSE" != "1" ]]; then + return 0 + fi + set -x +} + +FLINK_VERSION="${FLINK_VERSION:-2.2.0}" +FLINK_AGENTS_VERSION="${FLINK_AGENTS_VERSION:-0.2.1}" +FLINK_SCALA_VERSION="${FLINK_SCALA_VERSION:-2.12}" +FLINK_MAJOR_MINOR="${FLINK_VERSION%.*}" +FLINK_BASE_URL="${FLINK_BASE_URL:-https://dlcdn.apache.org/flink}" + +INSTALL_FLINK="${INSTALL_FLINK:-ask}" +ENABLE_PYFLINK="${ENABLE_PYFLINK:-ask}" +INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/flink}" +VENV_DIR="${VENV_DIR:-.flink-agents-env}" +NO_PROMPT="${NO_PROMPT:-0}" +VERBOSE="${FLINK_AGENTS_VERBOSE:-0}" +DRY_RUN="${FLINK_AGENTS_DRY_RUN:-0}" +VERIFY_INSTALL="${FLINK_AGENTS_VERIFY_INSTALL:-0}" +HELP=0 +PYFLINK_ACTUALLY_ENABLED=0 + +ARCHIVE_NAME="flink-${FLINK_VERSION}-bin-scala_${FLINK_SCALA_VERSION}.tgz" +ARCHIVE_URL="${FLINK_BASE_URL}/flink-${FLINK_VERSION}/${ARCHIVE_NAME}" + +require_cmd() { + command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1" +} + +is_valid_tgz() { + local archive="$1" + [[ -f "$archive" ]] || return 1 + tar -tzf "$archive" >/dev/null 2>&1 +} + +is_promptable() { + if [[ "$NO_PROMPT" == "1" ]]; then + return 1 + fi + if [[ -r /dev/tty && -w /dev/tty ]]; then + return 0 + fi + return 1 +} + +choose_install_method_interactive() { + local prompt="$1" + + if ! is_promptable; then + return 1 + fi + + if [[ -n "$GUM" ]] && gum_is_tty; then + local selection + selection="$("$GUM" choose \ + --header "$prompt" \ + --cursor-prefix "❯ " \ + "Yes" "No" < /dev/tty || true)" + [[ "$selection" == "Yes" ]] + return + fi + + local answer="" + printf '%s [y/n]: ' "$prompt" > /dev/tty + read -r answer < /dev/tty || true + + [[ "$answer" =~ ^[Yy]$ ]] +} + +install_flink_if_needed() { + case "$INSTALL_FLINK" in + yes|true|1) + ;; + no|false|0) + ui_info "Skipping Flink download/install (INSTALL_FLINK=${INSTALL_FLINK})." + return + ;; + ask) + if ! choose_install_method_interactive "Do you want this script to download and install Flink ${FLINK_VERSION}?"; then + ui_info "Skipping Flink download/install by user choice." + return + fi + ;; + *) + die "Unsupported INSTALL_FLINK value: ${INSTALL_FLINK}. Use: ask|yes|no" + ;; + esac + + detect_downloader + require_cmd tar + + if ! mkdir -p "$INSTALL_DIR"; then + die "Failed to create INSTALL_DIR=$INSTALL_DIR. Please run with proper permissions or set INSTALL_DIR to a writable path." + fi + if [[ -f "${INSTALL_DIR}/${ARCHIVE_NAME}" ]] && ! is_valid_tgz "${INSTALL_DIR}/${ARCHIVE_NAME}"; then + ui_warn "Existing archive is corrupted; re-downloading: ${INSTALL_DIR}/${ARCHIVE_NAME}" + rm -f "${INSTALL_DIR}/${ARCHIVE_NAME}" + fi + + if [[ ! -f "${INSTALL_DIR}/${ARCHIVE_NAME}" ]]; then + ui_info "Downloading ${ARCHIVE_URL}" + download_file "$ARCHIVE_URL" "${INSTALL_DIR}/${ARCHIVE_NAME}" + else + ui_info "Reusing existing archive: ${INSTALL_DIR}/${ARCHIVE_NAME}" + fi + + if ! is_valid_tgz "${INSTALL_DIR}/${ARCHIVE_NAME}"; then + die "Downloaded archive is invalid or truncated: ${INSTALL_DIR}/${ARCHIVE_NAME}" + fi + + if [[ -d "${INSTALL_DIR}/flink-${FLINK_VERSION}" ]] && [[ ! -d "${INSTALL_DIR}/flink-${FLINK_VERSION}/lib" ]]; then + ui_warn "Existing Flink home is incomplete; re-extracting: ${INSTALL_DIR}/flink-${FLINK_VERSION}" + rm -rf "${INSTALL_DIR}/flink-${FLINK_VERSION}" + fi + + if [[ ! -d "${INSTALL_DIR}/flink-${FLINK_VERSION}" ]]; then + ui_info "Extracting Flink to ${INSTALL_DIR}" + tar -xzf "${INSTALL_DIR}/${ARCHIVE_NAME}" -C "$INSTALL_DIR" + else + ui_info "Reusing existing Flink home: ${INSTALL_DIR}/flink-${FLINK_VERSION}" + fi + + export FLINK_HOME="${INSTALL_DIR}/flink-${FLINK_VERSION}" +} + +resolve_flink_home() { + if [[ -z "${FLINK_HOME:-}" ]]; then + die "FLINK_HOME is not set. Set FLINK_HOME or run with INSTALL_FLINK=yes." + fi + [[ -d "$FLINK_HOME" ]] || die "FLINK_HOME does not exist: $FLINK_HOME" + [[ -d "$FLINK_HOME/lib" ]] || die "Invalid FLINK_HOME (missing lib directory): $FLINK_HOME" +} + +copy_pyflink_jar() { + local pyflink_jar="$FLINK_HOME/opt/flink-python-${FLINK_VERSION}.jar" + [[ -f "$pyflink_jar" ]] || die "Missing required PyFlink jar: $pyflink_jar" + + ui_info "Copying PyFlink jar into Flink lib" + cp "$pyflink_jar" "$FLINK_HOME/lib/" +} + +setup_python_env() { + check_python || die "Python environment check failed. Please install Python >=3.10." + + if [[ ! -d "$VENV_DIR" ]]; then + ui_info "Creating virtual environment: $VENV_DIR" + python3 -m venv "$VENV_DIR" + else + ui_info "Reusing existing virtual environment: $VENV_DIR" + fi + + source "$VENV_DIR/bin/activate" + + export PIP_PROGRESS_BAR=off + export PIP_NO_COLOR=1 + export PIP_NO_INPUT=1 + + ui_info "Installing Python packages" + python -m pip install --upgrade pip + python -m pip install "flink-agents==${FLINK_AGENTS_VERSION}" "apache-flink==${FLINK_VERSION}" +} + +copy_flink_agents_jars() { + local pkg_root + pkg_root="$(python - <<'PY' +import pathlib +import flink_agents +print(pathlib.Path(flink_agents.__file__).resolve().parent) +PY +)" + + local version_lib_dir="${pkg_root}/lib/flink-${FLINK_MAJOR_MINOR}" + + [[ -d "$version_lib_dir" ]] || die "Flink Agents lib directory not found: $version_lib_dir" + + local copied=0 + local jar + for jar in "$version_lib_dir"/flink-agents-dist-*.jar; do + [[ -f "$jar" ]] || continue + cp "$jar" "$FLINK_HOME/lib/" + copied=1 + done + + [[ "$copied" -eq 1 ]] || die "No flink-agents-dist jar found in: $version_lib_dir" +} + +should_enable_pyflink() { + case "$ENABLE_PYFLINK" in + yes|true|1) + return 0 + ;; + no|false|0) + return 1 + ;; + ask) + if choose_install_method_interactive "Create a Python venv with PyFlink and flink-agents? (Only needed for Python API users; Java users can select No)"; then + return 0 + fi + return 1 + ;; + *) + die "Unsupported ENABLE_PYFLINK value: ${ENABLE_PYFLINK}. Use: ask|yes|no" + ;; + esac +} + +print_usage() { + cat </install.sh | bash -s -- [options] + +Options: + --yes, -y Non-interactive mode (accept all defaults) + --install-flink Download and install Apache Flink + --enable-pyflink Enable PyFlink and install Python packages + --verbose Print debug output (set -x) + --dry-run Print install plan without making changes + --verify Run post-install verification checks + --help, -h Show this help + +Environment variables: + FLINK_VERSION Flink version to install (default: 2.2.0) + FLINK_AGENTS_VERSION Flink Agents version to install (default: 0.3.0) + FLINK_SCALA_VERSION Scala version suffix (default: 2.12) + FLINK_BASE_URL Mirror base URL (default: https://dlcdn.apache.org/flink) + INSTALL_FLINK ask|yes|no (default: ask) + ENABLE_PYFLINK ask|yes|no (default: ask) + INSTALL_DIR Flink install directory (default: \$HOME/.local/flink) + VENV_DIR Python venv directory (default: .flink-agents-env) + NO_PROMPT 1 to disable all prompts + FLINK_AGENTS_VERBOSE 1 to enable verbose output + FLINK_AGENTS_DRY_RUN 1 to enable dry-run mode + FLINK_AGENTS_VERIFY_INSTALL 1 to enable post-install verification + +Examples: + bash install.sh --install-flink --enable-pyflink --yes + FLINK_VERSION=2.2.0 bash install.sh --verbose + bash install.sh --dry-run +EOF +} + +check_java() { + if ! command -v java &>/dev/null; then + ui_warn "Java not found on PATH" + if [[ "$OS" == "macos" ]]; then + ui_info "Install Java: brew install openjdk@17" + elif [[ "$OS" == "linux" ]]; then + ui_info "Install Java: sudo apt install openjdk-17-jdk (Debian/Ubuntu)" + ui_info " sudo yum install java-17-openjdk-devel (RHEL/CentOS)" + fi + ui_info "Flink requires Java 11+ to run" + return 1 + fi + + local java_version_output + java_version_output="$(java -version 2>&1 | head -n1)" + local java_major="" + java_major="$(echo "$java_version_output" | sed -n 's/.*version "\([0-9]*\).*/\1/p')" + + if [[ -z "$java_major" ]]; then + ui_warn "Could not parse Java version from: $java_version_output" + return 1 + fi + + if [[ "$java_major" -lt 11 ]]; then + ui_error "Java $java_major detected, but Flink requires Java 11+" + ui_info "Please upgrade your Java installation" + return 1 + fi + + ui_success "Java $java_major found" + + if [[ -z "${JAVA_HOME:-}" ]]; then + ui_info "JAVA_HOME is not set (Flink will try to detect it automatically)" + fi + return 0 +} + +check_python() { + if ! command -v python3 &>/dev/null; then + ui_error "python3 not found on PATH" + return 1 + fi + + local py_version_output + py_version_output="$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null || true)" + + if [[ -z "$py_version_output" ]]; then + ui_error "Could not parse python3 version" + return 1 + fi + + local py_major py_minor + py_major="${py_version_output%%.*}" + py_minor="${py_version_output##*.}" + + if [[ "$py_major" -ne 3 ]] || [[ "$py_minor" -lt 10 ]]; then + ui_error "Python $py_major.$py_minor detected, but Flink Agents requires Python >=3.10 and <3.12" + return 1 + fi + + ui_success "Python $py_major.$py_minor found" + return 0 +} + +show_install_plan() { + ui_section "Install plan" + ui_kv "OS" "$OS" + ui_kv "Flink version" "$FLINK_VERSION" + ui_kv "Flink Agents version" "$FLINK_AGENTS_VERSION" + ui_kv "Install Flink" "$INSTALL_FLINK" + ui_kv "Install directory" "$INSTALL_DIR" + ui_kv "Enable PyFlink" "$ENABLE_PYFLINK" + if [[ "$ENABLE_PYFLINK" =~ ^(yes|true|1)$ ]] || [[ "$PYFLINK_ACTUALLY_ENABLED" -eq 1 ]]; then + ui_kv "Venv directory" "$VENV_DIR" + fi + if [[ -n "${FLINK_HOME:-}" ]]; then + ui_kv "FLINK_HOME" "$FLINK_HOME" + fi + if [[ -n "${JAVA_HOME:-}" ]]; then + ui_kv "JAVA_HOME" "$JAVA_HOME" + fi + if [[ "$DRY_RUN" == "1" ]]; then + ui_kv "Dry run" "yes" + fi + if [[ "$VERIFY_INSTALL" == "1" ]]; then + ui_kv "Verify" "yes" + fi +} + +verify_installation() { + if [[ "${VERIFY_INSTALL}" != "1" ]]; then + return 0 + fi + + ui_stage "Verifying installation" + + if [[ -x "$FLINK_HOME/bin/flink" ]]; then + local flink_ver + flink_ver="$("$FLINK_HOME/bin/flink" --version 2>/dev/null || true)" + if [[ -n "$flink_ver" ]]; then + ui_success "Flink binary: $flink_ver" + else + ui_warn "Flink binary exists but --version failed" + fi + else + ui_warn "Flink binary not found at $FLINK_HOME/bin/flink" + fi + + if [[ "$PYFLINK_ACTUALLY_ENABLED" -eq 1 ]]; then + if python -c "import flink_agents; print('flink-agents', flink_agents.__version__)" 2>/dev/null; then + ui_success "flink-agents Python package verified" + else + ui_error "flink-agents Python package import failed" + return 1 + fi + + local agents_jar_found=0 + for jar in "$FLINK_HOME/lib"/flink-agents-dist-*.jar; do + [[ -f "$jar" ]] && agents_jar_found=1 && break + done + if [[ "$agents_jar_found" -eq 1 ]]; then + ui_success "flink-agents-dist JAR found in FLINK_HOME/lib" + else + ui_error "flink-agents-dist JAR not found in $FLINK_HOME/lib" + return 1 + fi + + if [[ -f "$FLINK_HOME/lib/flink-python-${FLINK_VERSION}.jar" ]]; then + ui_success "flink-python JAR found in FLINK_HOME/lib" + else + ui_warn "flink-python-${FLINK_VERSION}.jar not found in $FLINK_HOME/lib" + fi + fi + + ui_success "Verification complete" +} + +show_footer_links() { + local docs_url="https://nightlies.apache.org/flink/flink-agents-docs-latest/" + local issues_url="https://github.com/apache/flink-agents/issues" + echo "" + ui_info "Documentation: ${docs_url}" + ui_info "Report issues: ${issues_url}" +} + +parse_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + --yes|-y) + NO_PROMPT=1 + shift + ;; + --install-flink) + INSTALL_FLINK=yes + shift + ;; + --enable-pyflink|--enable-pyFlink) + ENABLE_PYFLINK=yes + shift + ;; + --verbose) + VERBOSE=1 + shift + ;; + --dry-run) + DRY_RUN=1 + shift + ;; + --verify) + VERIFY_INSTALL=1 + shift + ;; + --help|-h) + HELP=1 + shift + ;; + *) + ui_warn "Unknown option: $1" + shift + ;; + esac + done +} + +main() { + if [[ "$HELP" == "1" ]]; then + print_usage + return 0 + fi + + bootstrap_gum_temp || true + print_installer_banner + print_gum_status + detect_os_or_die + check_java || die "Java environment check failed. Please install Java 11 or newer." + + show_install_plan + + if [[ "$DRY_RUN" == "1" ]]; then + ui_success "Dry run complete (no changes made)" + return 0 + fi + + ui_stage "Installing Apache Flink" + install_flink_if_needed + resolve_flink_home + + ui_stage "Setting up Python environment" + if should_enable_pyflink; then + PYFLINK_ACTUALLY_ENABLED=1 + copy_pyflink_jar + setup_python_env + copy_flink_agents_jars + else + ui_info "Skipping PyFlink and Python package setup (ENABLE_PYFLINK=${ENABLE_PYFLINK})." + fi + + ui_stage "Finalizing" + + verify_installation + + echo "" + ui_celebrate "Apache Flink Agents installation finished!" + ui_success "FLINK_HOME=$FLINK_HOME" + if [[ "$PYFLINK_ACTUALLY_ENABLED" -eq 1 ]]; then + ui_info "To use Python environment in a new shell :" + ui_info " source ${VENV_DIR}/bin/activate" + fi + ui_info " export FLINK_HOME=${FLINK_HOME}" + + show_footer_links +} + +if [[ "${FLINK_AGENTS_INSTALL_SH_NO_RUN:-0}" != "1" ]]; then + parse_args "$@" + configure_verbose + main +fi + From 9724d34bc675882e7a7d24eed449f574af1d2f36 Mon Sep 17 00:00:00 2001 From: Jinkun Liu Date: Mon, 27 Apr 2026 11:23:59 +0800 Subject: [PATCH 02/13] [tools] Improve install.sh with download timeout, progress hint, and CLI flag rename --- tools/install.sh | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tools/install.sh b/tools/install.sh index 97a313fad..d5cec877e 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash ################################################################################ # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file @@ -65,10 +65,10 @@ download_file() { detect_downloader fi if [[ "$DOWNLOADER" == "curl" ]]; then - curl -fsSL --proto '=https' --tlsv1.2 --retry 3 --retry-delay 1 --retry-connrefused -o "$output" "$url" + curl -fsSL --proto '=https' --tlsv1.2 --connect-timeout 5 --max-time 15 --retry 1 --retry-delay 1 --retry-connrefused -o "$output" "$url" return fi - wget -q --https-only --secure-protocol=TLSv1_2 --tries=3 --timeout=20 -O "$output" "$url" + wget -q --https-only --secure-protocol=TLSv1_2 --tries=2 --timeout=15 -O "$output" "$url" } GUM_VERSION="${FLINK_AGENTS_GUM_VERSION:-0.17.0}" @@ -175,6 +175,8 @@ bootstrap_gum_temp() { gum_tmpdir="$(mktemp -d)" TMPFILES+=("$gum_tmpdir") + echo -e "${INFO}· Installing gum v${GUM_VERSION}, please wait...${NC}" + if ! download_file "${base}/${asset}" "$gum_tmpdir/$asset"; then GUM_REASON="download failed" return 1 @@ -568,7 +570,7 @@ Usage: curl -fsSL /install.sh | bash -s -- [options] Options: - --yes, -y Non-interactive mode (accept all defaults) + --non-interactive Non-interactive mode (accept all defaults) --install-flink Download and install Apache Flink --enable-pyflink Enable PyFlink and install Python packages --verbose Print debug output (set -x) @@ -578,7 +580,7 @@ Options: Environment variables: FLINK_VERSION Flink version to install (default: 2.2.0) - FLINK_AGENTS_VERSION Flink Agents version to install (default: 0.3.0) + FLINK_AGENTS_VERSION Flink Agents version to install (default: 0.2.1) FLINK_SCALA_VERSION Scala version suffix (default: 2.12) FLINK_BASE_URL Mirror base URL (default: https://dlcdn.apache.org/flink) INSTALL_FLINK ask|yes|no (default: ask) @@ -591,7 +593,7 @@ Environment variables: FLINK_AGENTS_VERIFY_INSTALL 1 to enable post-install verification Examples: - bash install.sh --install-flink --enable-pyflink --yes + bash install.sh --install-flink --enable-pyflink --non-interactive FLINK_VERSION=2.2.0 bash install.sh --verbose bash install.sh --dry-run EOF @@ -745,7 +747,7 @@ show_footer_links() { parse_args() { while [[ $# -gt 0 ]]; do case "$1" in - --yes|-y) + --non-interactive) NO_PROMPT=1 shift ;; From 9e9887c9f81d7e6b22f7ef823a6cd772ce887cc8 Mon Sep 17 00:00:00 2001 From: Jinkun Liu Date: Mon, 27 Apr 2026 11:31:35 +0800 Subject: [PATCH 03/13] [tools]Install shell add python version limitation --- tools/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/install.sh b/tools/install.sh index d5cec877e..4f3e2cf94 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -654,7 +654,7 @@ check_python() { py_major="${py_version_output%%.*}" py_minor="${py_version_output##*.}" - if [[ "$py_major" -ne 3 ]] || [[ "$py_minor" -lt 10 ]]; then + if [[ "$py_major" -ne 3 ]] || [[ "$py_minor" -lt 10 ]] || [[ "$py_minor" -ge 12 ]]; then ui_error "Python $py_major.$py_minor detected, but Flink Agents requires Python >=3.10 and <3.12" return 1 fi From 8b05ba7333d8a9449e7a9d1d85dc07e30ef2feb1 Mon Sep 17 00:00:00 2001 From: Jinkun Liu Date: Mon, 27 Apr 2026 11:47:58 +0800 Subject: [PATCH 04/13] [tools]install shell add common jar download --- tools/install.sh | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/tools/install.sh b/tools/install.sh index 4f3e2cf94..084d0a8c7 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -527,17 +527,29 @@ PY )" local version_lib_dir="${pkg_root}/lib/flink-${FLINK_MAJOR_MINOR}" + local common_lib_dir="${pkg_root}/lib/common" [[ -d "$version_lib_dir" ]] || die "Flink Agents lib directory not found: $version_lib_dir" + [[ -d "$common_lib_dir" ]] || die "Flink Agents common lib directory not found: $common_lib_dir" - local copied=0 local jar + + ui_info "Copying Flink Agents common jar into Flink lib" + local common_copied=0 + for jar in "$common_lib_dir"/flink-agents-dist-common-*.jar; do + [[ -f "$jar" ]] || continue + cp "$jar" "$FLINK_HOME/lib/" + common_copied=1 + done + [[ "$common_copied" -eq 1 ]] || die "No flink-agents-dist-common jar found in: $common_lib_dir" + + ui_info "Copying Flink Agents thin jar into Flink lib" + local copied=0 for jar in "$version_lib_dir"/flink-agents-dist-*.jar; do [[ -f "$jar" ]] || continue cp "$jar" "$FLINK_HOME/lib/" copied=1 done - [[ "$copied" -eq 1 ]] || die "No flink-agents-dist jar found in: $version_lib_dir" } @@ -715,14 +727,25 @@ verify_installation() { return 1 fi - local agents_jar_found=0 - for jar in "$FLINK_HOME/lib"/flink-agents-dist-*.jar; do - [[ -f "$jar" ]] && agents_jar_found=1 && break + local common_jar_found=0 + for jar in "$FLINK_HOME/lib"/flink-agents-dist-common-*.jar; do + [[ -f "$jar" ]] && common_jar_found=1 && break + done + if [[ "$common_jar_found" -eq 1 ]]; then + ui_success "flink-agents-dist-common JAR found in FLINK_HOME/lib" + else + ui_error "flink-agents-dist-common JAR not found in $FLINK_HOME/lib" + return 1 + fi + + local thin_jar_found=0 + for jar in "$FLINK_HOME/lib"/flink-agents-dist-flink-*-thin.jar; do + [[ -f "$jar" ]] && thin_jar_found=1 && break done - if [[ "$agents_jar_found" -eq 1 ]]; then - ui_success "flink-agents-dist JAR found in FLINK_HOME/lib" + if [[ "$thin_jar_found" -eq 1 ]]; then + ui_success "flink-agents-dist thin JAR found in FLINK_HOME/lib" else - ui_error "flink-agents-dist JAR not found in $FLINK_HOME/lib" + ui_error "flink-agents-dist thin JAR not found in $FLINK_HOME/lib" return 1 fi From f316d46201efb85753e9f506b7dee8764416d3ec Mon Sep 17 00:00:00 2001 From: Jinkun Liu Date: Mon, 27 Apr 2026 13:50:25 +0800 Subject: [PATCH 05/13] [tools] install.sh: drop redundant true/1/false/0 aliases --- tools/install.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/install.sh b/tools/install.sh index 084d0a8c7..1acbcfefa 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -426,9 +426,9 @@ choose_install_method_interactive() { install_flink_if_needed() { case "$INSTALL_FLINK" in - yes|true|1) + yes) ;; - no|false|0) + no) ui_info "Skipping Flink download/install (INSTALL_FLINK=${INSTALL_FLINK})." return ;; @@ -555,10 +555,10 @@ PY should_enable_pyflink() { case "$ENABLE_PYFLINK" in - yes|true|1) + yes) return 0 ;; - no|false|0) + no) return 1 ;; ask) @@ -683,7 +683,7 @@ show_install_plan() { ui_kv "Install Flink" "$INSTALL_FLINK" ui_kv "Install directory" "$INSTALL_DIR" ui_kv "Enable PyFlink" "$ENABLE_PYFLINK" - if [[ "$ENABLE_PYFLINK" =~ ^(yes|true|1)$ ]] || [[ "$PYFLINK_ACTUALLY_ENABLED" -eq 1 ]]; then + if [[ "$ENABLE_PYFLINK" == "yes" ]] || [[ "$PYFLINK_ACTUALLY_ENABLED" -eq 1 ]]; then ui_kv "Venv directory" "$VENV_DIR" fi if [[ -n "${FLINK_HOME:-}" ]]; then From f045e4eeb705c533098ddade0dc79d17a30cfc80 Mon Sep 17 00:00:00 2001 From: Jinkun Liu Date: Mon, 27 Apr 2026 13:51:47 +0800 Subject: [PATCH 06/13] [tools] install.sh: define missing die helper --- tools/install.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/install.sh b/tools/install.sh index 1acbcfefa..094570274 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -301,6 +301,11 @@ ui_error() { fi } +die() { + ui_error "$*" + exit 1 +} + INSTALL_STAGE_TOTAL=3 INSTALL_STAGE_CURRENT=0 From 150a4256a74a5fa78508f4b4de3e9668d8ccb023 Mon Sep 17 00:00:00 2001 From: Jinkun Liu Date: Mon, 27 Apr 2026 14:12:39 +0800 Subject: [PATCH 07/13] [tools] install.sh: prompt for FLINK_HOME when missing or invalid --- tools/install.sh | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tools/install.sh b/tools/install.sh index 094570274..db6add874 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -485,12 +485,33 @@ install_flink_if_needed() { export FLINK_HOME="${INSTALL_DIR}/flink-${FLINK_VERSION}" } +prompt_flink_home_interactive() { + local input="" + if [[ -n "$GUM" ]] && gum_is_tty; then + input="$("$GUM" input \ + --header "Enter the path to your existing FLINK_HOME" \ + --placeholder "/path/to/flink-${FLINK_VERSION}" \ + --width 70 < /dev/tty || true)" + else + printf 'Enter FLINK_HOME path: ' > /dev/tty + read -r input < /dev/tty || true + fi + input="${input/#\~/$HOME}" + printf '%s' "$input" +} + resolve_flink_home() { - if [[ -z "${FLINK_HOME:-}" ]]; then - die "FLINK_HOME is not set. Set FLINK_HOME or run with INSTALL_FLINK=yes." + if [[ -z "${FLINK_HOME:-}" || ! -d "${FLINK_HOME}" || ! -d "${FLINK_HOME}/lib" ]]; then + if is_promptable; then + FLINK_HOME="$(prompt_flink_home_interactive)" + export FLINK_HOME + fi fi + + [[ -n "${FLINK_HOME:-}" ]] || die "FLINK_HOME is not set." [[ -d "$FLINK_HOME" ]] || die "FLINK_HOME does not exist: $FLINK_HOME" [[ -d "$FLINK_HOME/lib" ]] || die "Invalid FLINK_HOME (missing lib directory): $FLINK_HOME" + ui_success "FLINK_HOME resolved: $FLINK_HOME" } copy_pyflink_jar() { From 45e46637a2c5bcd04cbcb717e8308ff07c2515d4 Mon Sep 17 00:00:00 2001 From: Jinkun Liu Date: Mon, 27 Apr 2026 15:22:35 +0800 Subject: [PATCH 08/13] [tools] install.sh: consolidate Flink resolution into a single function --- tools/install.sh | 115 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 27 deletions(-) diff --git a/tools/install.sh b/tools/install.sh index db6add874..8355b45b4 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -65,10 +65,10 @@ download_file() { detect_downloader fi if [[ "$DOWNLOADER" == "curl" ]]; then - curl -fsSL --proto '=https' --tlsv1.2 --connect-timeout 5 --max-time 15 --retry 1 --retry-delay 1 --retry-connrefused -o "$output" "$url" + curl -fsSL --proto '=https' --tlsv1.2 --connect-timeout 5 --max-time 30 --retry 2 --retry-delay 1 --retry-connrefused -o "$output" "$url" return fi - wget -q --https-only --secure-protocol=TLSv1_2 --tries=2 --timeout=15 -O "$output" "$url" + wget -q --https-only --secure-protocol=TLSv1_2 --tries=2 --timeout=30 -O "$output" "$url" } GUM_VERSION="${FLINK_AGENTS_GUM_VERSION:-0.17.0}" @@ -365,12 +365,18 @@ configure_verbose() { set -x } +FLINK_VERSION_EXPLICIT=0 +if [[ -n "${FLINK_VERSION:-}" ]]; then + FLINK_VERSION_EXPLICIT=1 +fi FLINK_VERSION="${FLINK_VERSION:-2.2.0}" FLINK_AGENTS_VERSION="${FLINK_AGENTS_VERSION:-0.2.1}" FLINK_SCALA_VERSION="${FLINK_SCALA_VERSION:-2.12}" -FLINK_MAJOR_MINOR="${FLINK_VERSION%.*}" FLINK_BASE_URL="${FLINK_BASE_URL:-https://dlcdn.apache.org/flink}" +FLINK_SUPPORTED_VERSIONS=("2.2.0" "2.1.1" "2.0.1" "1.20.3") +FLINK_RECOMMENDED_VERSION="2.2.0" + INSTALL_FLINK="${INSTALL_FLINK:-ask}" ENABLE_PYFLINK="${ENABLE_PYFLINK:-ask}" INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/flink}" @@ -382,9 +388,6 @@ VERIFY_INSTALL="${FLINK_AGENTS_VERIFY_INSTALL:-0}" HELP=0 PYFLINK_ACTUALLY_ENABLED=0 -ARCHIVE_NAME="flink-${FLINK_VERSION}-bin-scala_${FLINK_SCALA_VERSION}.tgz" -ARCHIVE_URL="${FLINK_BASE_URL}/flink-${FLINK_VERSION}/${ARCHIVE_NAME}" - require_cmd() { command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1" } @@ -429,18 +432,67 @@ choose_install_method_interactive() { [[ "$answer" =~ ^[Yy]$ ]] } +prompt_flink_version_interactive() { + if ! is_promptable; then + return 1 + fi + + local labels=() + local v + for v in "${FLINK_SUPPORTED_VERSIONS[@]}"; do + if [[ "$v" == "$FLINK_RECOMMENDED_VERSION" ]]; then + labels+=("$v (recommended)") + else + labels+=("$v") + fi + done + + local selection="" + if [[ -n "$GUM" ]] && gum_is_tty; then + selection="$("$GUM" choose \ + --header "Select Flink version" \ + --cursor-prefix "❯ " \ + "${labels[@]}" < /dev/tty || true)" + selection="${selection%% *}" + else + printf 'Select Flink version:\n' > /dev/tty + local i=1 + for v in "${FLINK_SUPPORTED_VERSIONS[@]}"; do + local suffix="" + [[ "$v" == "$FLINK_RECOMMENDED_VERSION" ]] && suffix=" (recommended)" + printf ' %d) %s%s\n' "$i" "$v" "$suffix" > /dev/tty + i=$((i+1)) + done + local answer="" + printf 'Enter choice [1-%d, default %s]: ' \ + "${#FLINK_SUPPORTED_VERSIONS[@]}" "$FLINK_RECOMMENDED_VERSION" > /dev/tty + read -r answer < /dev/tty || true + if [[ "$answer" =~ ^[0-9]+$ ]] \ + && (( answer >= 1 && answer <= ${#FLINK_SUPPORTED_VERSIONS[@]} )); then + selection="${FLINK_SUPPORTED_VERSIONS[$((answer-1))]}" + fi + fi + + if [[ -z "$selection" ]]; then + selection="$FLINK_RECOMMENDED_VERSION" + fi + FLINK_VERSION="$selection" + return 0 +} + install_flink_if_needed() { + local skip_install=0 case "$INSTALL_FLINK" in yes) ;; no) ui_info "Skipping Flink download/install (INSTALL_FLINK=${INSTALL_FLINK})." - return + skip_install=1 ;; ask) - if ! choose_install_method_interactive "Do you want this script to download and install Flink ${FLINK_VERSION}?"; then + if ! choose_install_method_interactive "Do you want this script to download and install Apache Flink?"; then ui_info "Skipping Flink download/install by user choice." - return + skip_install=1 fi ;; *) @@ -448,6 +500,28 @@ install_flink_if_needed() { ;; esac + if [[ "$skip_install" -eq 1 ]]; then + if [[ -z "${FLINK_HOME:-}" || ! -d "${FLINK_HOME}" || ! -d "${FLINK_HOME}/lib" ]]; then + if is_promptable; then + FLINK_HOME="$(prompt_flink_home_interactive)" + export FLINK_HOME + fi + fi + [[ -n "${FLINK_HOME:-}" ]] || die "FLINK_HOME is not set." + [[ -d "$FLINK_HOME" ]] || die "FLINK_HOME does not exist: $FLINK_HOME" + [[ -d "$FLINK_HOME/lib" ]] || die "Invalid FLINK_HOME (missing lib directory): $FLINK_HOME" + ui_success "FLINK_HOME resolved: $FLINK_HOME" + FLINK_MAJOR_MINOR="${FLINK_VERSION%.*}" + return + fi + + if [[ "$FLINK_VERSION_EXPLICIT" -eq 0 ]]; then + prompt_flink_version_interactive || true + fi + + local ARCHIVE_NAME="flink-${FLINK_VERSION}-bin-scala_${FLINK_SCALA_VERSION}.tgz" + local ARCHIVE_URL="${FLINK_BASE_URL}/flink-${FLINK_VERSION}/${ARCHIVE_NAME}" + detect_downloader require_cmd tar @@ -483,37 +557,25 @@ install_flink_if_needed() { fi export FLINK_HOME="${INSTALL_DIR}/flink-${FLINK_VERSION}" + FLINK_MAJOR_MINOR="${FLINK_VERSION%.*}" + ui_success "FLINK_HOME resolved: $FLINK_HOME" } prompt_flink_home_interactive() { local input="" if [[ -n "$GUM" ]] && gum_is_tty; then input="$("$GUM" input \ - --header "Enter the path to your existing FLINK_HOME" \ + --header "Enter the path to your existing FLINK_HOME which version >= 1.20" \ --placeholder "/path/to/flink-${FLINK_VERSION}" \ --width 70 < /dev/tty || true)" else - printf 'Enter FLINK_HOME path: ' > /dev/tty + printf 'Enter the path to your existing FLINK_HOME which version >= 1.20: ' > /dev/tty read -r input < /dev/tty || true fi input="${input/#\~/$HOME}" printf '%s' "$input" } -resolve_flink_home() { - if [[ -z "${FLINK_HOME:-}" || ! -d "${FLINK_HOME}" || ! -d "${FLINK_HOME}/lib" ]]; then - if is_promptable; then - FLINK_HOME="$(prompt_flink_home_interactive)" - export FLINK_HOME - fi - fi - - [[ -n "${FLINK_HOME:-}" ]] || die "FLINK_HOME is not set." - [[ -d "$FLINK_HOME" ]] || die "FLINK_HOME does not exist: $FLINK_HOME" - [[ -d "$FLINK_HOME/lib" ]] || die "Invalid FLINK_HOME (missing lib directory): $FLINK_HOME" - ui_success "FLINK_HOME resolved: $FLINK_HOME" -} - copy_pyflink_jar() { local pyflink_jar="$FLINK_HOME/opt/flink-python-${FLINK_VERSION}.jar" [[ -f "$pyflink_jar" ]] || die "Missing required PyFlink jar: $pyflink_jar" @@ -617,7 +679,7 @@ Options: --help, -h Show this help Environment variables: - FLINK_VERSION Flink version to install (default: 2.2.0) + FLINK_VERSION Flink version (default: 2.2.0; supported: 2.2.0, 2.1.1, 2.0.1, 1.20.3) FLINK_AGENTS_VERSION Flink Agents version to install (default: 0.2.1) FLINK_SCALA_VERSION Scala version suffix (default: 2.12) FLINK_BASE_URL Mirror base URL (default: https://dlcdn.apache.org/flink) @@ -853,7 +915,6 @@ main() { ui_stage "Installing Apache Flink" install_flink_if_needed - resolve_flink_home ui_stage "Setting up Python environment" if should_enable_pyflink; then From 0823d1ef4ef8a27351826f4faa4f1fc23acf9fa2 Mon Sep 17 00:00:00 2001 From: Jinkun Liu Date: Mon, 27 Apr 2026 17:11:29 +0800 Subject: [PATCH 09/13] [tools]install.sh: support define install location by user --- tools/install.sh | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/tools/install.sh b/tools/install.sh index 8355b45b4..7d5e76012 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -379,7 +379,15 @@ FLINK_RECOMMENDED_VERSION="2.2.0" INSTALL_FLINK="${INSTALL_FLINK:-ask}" ENABLE_PYFLINK="${ENABLE_PYFLINK:-ask}" +INSTALL_DIR_EXPLICIT=0 +if [[ -n "${INSTALL_DIR:-}" ]]; then + INSTALL_DIR_EXPLICIT=1 +fi INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/flink}" +VENV_DIR_EXPLICIT=0 +if [[ -n "${VENV_DIR:-}" ]]; then + VENV_DIR_EXPLICIT=1 +fi VENV_DIR="${VENV_DIR:-.flink-agents-env}" NO_PROMPT="${NO_PROMPT:-0}" VERBOSE="${FLINK_AGENTS_VERBOSE:-0}" @@ -525,6 +533,13 @@ install_flink_if_needed() { detect_downloader require_cmd tar + if [[ "$INSTALL_DIR_EXPLICIT" -eq 0 ]] && is_promptable; then + INSTALL_DIR="$(prompt_path_choice_interactive \ + "Choose Flink install directory" \ + "$INSTALL_DIR" \ + "/path/to/flink-install-dir")" + fi + if ! mkdir -p "$INSTALL_DIR"; then die "Failed to create INSTALL_DIR=$INSTALL_DIR. Please run with proper permissions or set INSTALL_DIR to a writable path." fi @@ -576,6 +591,64 @@ prompt_flink_home_interactive() { printf '%s' "$input" } +prompt_path_choice_interactive() { + local header="$1" + local default_path="$2" + local placeholder="$3" + + if ! is_promptable; then + printf '%s' "$default_path" + return 0 + fi + + local default_label="Default: ${default_path}" + local custom_label="Custom..." + local selection="" + + if [[ -n "$GUM" ]] && gum_is_tty; then + selection="$("$GUM" choose \ + --header "$header" \ + --cursor-prefix "❯ " \ + "$default_label" "$custom_label" < /dev/tty || true)" + else + printf '%s\n' "$header" > /dev/tty + printf ' 1) %s\n' "$default_label" > /dev/tty + printf ' 2) %s\n' "$custom_label" > /dev/tty + local answer="" + printf 'Enter choice [1-2, default 1]: ' > /dev/tty + read -r answer < /dev/tty || true + case "$answer" in + 2) selection="$custom_label" ;; + *) selection="$default_label" ;; + esac + fi + + if [[ "$selection" != "$custom_label" ]]; then + printf '%s' "$default_path" + return 0 + fi + + local input="" + if [[ -n "$GUM" ]] && gum_is_tty; then + input="$("$GUM" input \ + --header "$header" \ + --placeholder "$placeholder" \ + --width 70 < /dev/tty || true)" + else + printf 'Enter custom path: ' > /dev/tty + read -r input < /dev/tty || true + fi + input="${input/#\~/$HOME}" + + if [[ -z "$input" ]]; then + ui_warn "Empty path; falling back to default: $default_path" + printf '%s' "$default_path" + return 0 + fi + + printf '%s' "$input" +} + copy_pyflink_jar() { local pyflink_jar="$FLINK_HOME/opt/flink-python-${FLINK_VERSION}.jar" [[ -f "$pyflink_jar" ]] || die "Missing required PyFlink jar: $pyflink_jar" @@ -587,6 +660,13 @@ copy_pyflink_jar() { setup_python_env() { check_python || die "Python environment check failed. Please install Python >=3.10." + if [[ "$VENV_DIR_EXPLICIT" -eq 0 ]] && is_promptable; then + VENV_DIR="$(prompt_path_choice_interactive \ + "Choose Python venv directory" \ + "$VENV_DIR" \ + "/path/to/venv")" + fi + if [[ ! -d "$VENV_DIR" ]]; then ui_info "Creating virtual environment: $VENV_DIR" python3 -m venv "$VENV_DIR" From 68c9f6ccc920447254dfd310d5499a90abcdfae9 Mon Sep 17 00:00:00 2001 From: Jinkun Liu Date: Mon, 27 Apr 2026 18:13:11 +0800 Subject: [PATCH 10/13] [tools]install.sh support define python bin by user --- tools/install.sh | 109 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 93 insertions(+), 16 deletions(-) diff --git a/tools/install.sh b/tools/install.sh index 7d5e76012..db5e7ea42 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -389,6 +389,7 @@ if [[ -n "${VENV_DIR:-}" ]]; then VENV_DIR_EXPLICIT=1 fi VENV_DIR="${VENV_DIR:-.flink-agents-env}" +PYTHON_BIN="${PYTHON_BIN:-}" NO_PROMPT="${NO_PROMPT:-0}" VERBOSE="${FLINK_AGENTS_VERBOSE:-0}" DRY_RUN="${FLINK_AGENTS_DRY_RUN:-0}" @@ -657,8 +658,22 @@ copy_pyflink_jar() { cp "$pyflink_jar" "$FLINK_HOME/lib/" } +create_venv() { + local venv_err + venv_err="$(mktempfile)" + if "$PYTHON_BIN" -m venv "$VENV_DIR" 2>"$venv_err"; then + return 0 + fi + + ui_error "Failed to create virtual environment at $VENV_DIR" + if [[ -s "$venv_err" ]]; then + sed 's/^/ /' "$venv_err" >&2 || true + fi + die "Virtual environment creation failed" +} + setup_python_env() { - check_python || die "Python environment check failed. Please install Python >=3.10." + resolve_python if [[ "$VENV_DIR_EXPLICIT" -eq 0 ]] && is_promptable; then VENV_DIR="$(prompt_path_choice_interactive \ @@ -669,7 +684,7 @@ setup_python_env() { if [[ ! -d "$VENV_DIR" ]]; then ui_info "Creating virtual environment: $VENV_DIR" - python3 -m venv "$VENV_DIR" + create_venv else ui_info "Reusing existing virtual environment: $VENV_DIR" fi @@ -681,7 +696,6 @@ setup_python_env() { export PIP_NO_INPUT=1 ui_info "Installing Python packages" - python -m pip install --upgrade pip python -m pip install "flink-agents==${FLINK_AGENTS_VERSION}" "apache-flink==${FLINK_VERSION}" } @@ -756,6 +770,7 @@ Options: --verbose Print debug output (set -x) --dry-run Print install plan without making changes --verify Run post-install verification checks + --python Path to a Python3 interpreter (overrides PATH lookup) --help, -h Show this help Environment variables: @@ -767,6 +782,7 @@ Environment variables: ENABLE_PYFLINK ask|yes|no (default: ask) INSTALL_DIR Flink install directory (default: \$HOME/.local/flink) VENV_DIR Python venv directory (default: .flink-agents-env) + PYTHON_BIN Path to Python3 interpreter (default: auto-detect python3 on PATH) NO_PROMPT 1 to disable all prompts FLINK_AGENTS_VERBOSE 1 to enable verbose output FLINK_AGENTS_DRY_RUN 1 to enable dry-run mode @@ -816,30 +832,77 @@ check_java() { return 0 } -check_python() { - if ! command -v python3 &>/dev/null; then - ui_error "python3 not found on PATH" - return 1 - fi +validate_python_bin() { + local bin="$1" + [[ -n "$bin" ]] || return 1 + command -v "$bin" >/dev/null 2>&1 || return 1 local py_version_output - py_version_output="$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null || true)" - - if [[ -z "$py_version_output" ]]; then - ui_error "Could not parse python3 version" - return 1 - fi + py_version_output="$("$bin" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null || true)" + [[ -n "$py_version_output" ]] || return 1 local py_major py_minor py_major="${py_version_output%%.*}" py_minor="${py_version_output##*.}" if [[ "$py_major" -ne 3 ]] || [[ "$py_minor" -lt 10 ]] || [[ "$py_minor" -ge 12 ]]; then - ui_error "Python $py_major.$py_minor detected, but Flink Agents requires Python >=3.10 and <3.12" return 1 fi + return 0 +} + +prompt_python_bin_interactive() { + local input="" + if [[ -n "$GUM" ]] && gum_is_tty; then + input="$("$GUM" input \ + --header "Enter path to a Python interpreter" \ + --placeholder "/path/to/python3" \ + --width 70 < /dev/tty || true)" + else + printf 'Enter path to a Python interpreter: ' > /dev/tty + read -r input < /dev/tty || true + fi + input="${input/#\~/$HOME}" + printf '%s' "$input" +} + +resolve_python() { + if [[ -n "$PYTHON_BIN" ]]; then + if validate_python_bin "$PYTHON_BIN"; then + ui_success "Using Python: $PYTHON_BIN" + return 0 + fi + die "PYTHON_BIN is invalid or unsupported (need >=3.10 and <3.12): $PYTHON_BIN" + fi + + if validate_python_bin python3; then + PYTHON_BIN="python3" + local v + v="$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null)" + ui_success "Python $v found on PATH" + return 0 + fi + + if command -v python3 >/dev/null 2>&1; then + ui_warn "python3 on PATH is incompatible (Flink Agents requires Python >=3.10 and <3.12)" + else + ui_warn "python3 not found on PATH" + fi + + if ! is_promptable; then + die "No compatible Python found. Set PYTHON_BIN or pass --python ." + fi - ui_success "Python $py_major.$py_minor found" + local input + input="$(prompt_python_bin_interactive)" + if [[ -z "$input" ]]; then + die "No Python interpreter provided." + fi + if ! validate_python_bin "$input"; then + die "Provided Python is invalid or unsupported (need >=3.10 and <3.12): $input" + fi + PYTHON_BIN="$input" + ui_success "Using Python: $PYTHON_BIN" return 0 } @@ -853,6 +916,9 @@ show_install_plan() { ui_kv "Enable PyFlink" "$ENABLE_PYFLINK" if [[ "$ENABLE_PYFLINK" == "yes" ]] || [[ "$PYFLINK_ACTUALLY_ENABLED" -eq 1 ]]; then ui_kv "Venv directory" "$VENV_DIR" + if [[ -n "$PYTHON_BIN" ]]; then + ui_kv "Python interpreter" "$PYTHON_BIN" + fi fi if [[ -n "${FLINK_HOME:-}" ]]; then ui_kv "FLINK_HOME" "$FLINK_HOME" @@ -962,6 +1028,17 @@ parse_args() { VERIFY_INSTALL=1 shift ;; + --python) + if [[ $# -lt 2 ]]; then + die "--python requires a path argument" + fi + PYTHON_BIN="$2" + shift 2 + ;; + --python=*) + PYTHON_BIN="${1#*=}" + shift + ;; --help|-h) HELP=1 shift From 012ea9977b8b96f87bfa7d3dcd7e1997b926ed0b Mon Sep 17 00:00:00 2001 From: Jinkun Liu Date: Mon, 27 Apr 2026 19:07:45 +0800 Subject: [PATCH 11/13] [tools] install.sh supprot plan --- tools/install.sh | 156 ++++++++++++++++++++++++++++------------------- 1 file changed, 93 insertions(+), 63 deletions(-) diff --git a/tools/install.sh b/tools/install.sh index db5e7ea42..e70beec80 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -306,7 +306,7 @@ die() { exit 1 } -INSTALL_STAGE_TOTAL=3 +INSTALL_STAGE_TOTAL=5 INSTALL_STAGE_CURRENT=0 ui_section() { @@ -377,8 +377,8 @@ FLINK_BASE_URL="${FLINK_BASE_URL:-https://dlcdn.apache.org/flink}" FLINK_SUPPORTED_VERSIONS=("2.2.0" "2.1.1" "2.0.1" "1.20.3") FLINK_RECOMMENDED_VERSION="2.2.0" -INSTALL_FLINK="${INSTALL_FLINK:-ask}" -ENABLE_PYFLINK="${ENABLE_PYFLINK:-ask}" +INSTALL_FLINK="${INSTALL_FLINK:-Ask}" +ENABLE_PYFLINK="${ENABLE_PYFLINK:-Ask}" INSTALL_DIR_EXPLICIT=0 if [[ -n "${INSTALL_DIR:-}" ]]; then INSTALL_DIR_EXPLICIT=1 @@ -489,27 +489,23 @@ prompt_flink_version_interactive() { return 0 } -install_flink_if_needed() { - local skip_install=0 +plan_flink() { case "$INSTALL_FLINK" in - yes) - ;; - no) - ui_info "Skipping Flink download/install (INSTALL_FLINK=${INSTALL_FLINK})." - skip_install=1 + Yes|No) ;; - ask) - if ! choose_install_method_interactive "Do you want this script to download and install Apache Flink?"; then - ui_info "Skipping Flink download/install by user choice." - skip_install=1 + Ask) + if choose_install_method_interactive "Do you want this script to download and install Apache Flink?"; then + INSTALL_FLINK=Yes + else + INSTALL_FLINK=No fi ;; *) - die "Unsupported INSTALL_FLINK value: ${INSTALL_FLINK}. Use: ask|yes|no" + die "Unsupported INSTALL_FLINK value: ${INSTALL_FLINK}. Use: Ask|Yes|No" ;; esac - if [[ "$skip_install" -eq 1 ]]; then + if [[ "$INSTALL_FLINK" == "No" ]]; then if [[ -z "${FLINK_HOME:-}" || ! -d "${FLINK_HOME}" || ! -d "${FLINK_HOME}/lib" ]]; then if is_promptable; then FLINK_HOME="$(prompt_flink_home_interactive)" @@ -519,7 +515,6 @@ install_flink_if_needed() { [[ -n "${FLINK_HOME:-}" ]] || die "FLINK_HOME is not set." [[ -d "$FLINK_HOME" ]] || die "FLINK_HOME does not exist: $FLINK_HOME" [[ -d "$FLINK_HOME/lib" ]] || die "Invalid FLINK_HOME (missing lib directory): $FLINK_HOME" - ui_success "FLINK_HOME resolved: $FLINK_HOME" FLINK_MAJOR_MINOR="${FLINK_VERSION%.*}" return fi @@ -528,12 +523,6 @@ install_flink_if_needed() { prompt_flink_version_interactive || true fi - local ARCHIVE_NAME="flink-${FLINK_VERSION}-bin-scala_${FLINK_SCALA_VERSION}.tgz" - local ARCHIVE_URL="${FLINK_BASE_URL}/flink-${FLINK_VERSION}/${ARCHIVE_NAME}" - - detect_downloader - require_cmd tar - if [[ "$INSTALL_DIR_EXPLICIT" -eq 0 ]] && is_promptable; then INSTALL_DIR="$(prompt_path_choice_interactive \ "Choose Flink install directory" \ @@ -541,6 +530,70 @@ install_flink_if_needed() { "/path/to/flink-install-dir")" fi + FLINK_HOME="${INSTALL_DIR}/flink-${FLINK_VERSION}" + FLINK_MAJOR_MINOR="${FLINK_VERSION%.*}" +} + +plan_pyflink() { + case "$ENABLE_PYFLINK" in + Yes|No) + ;; + Ask) + if choose_install_method_interactive "Create a Python venv with PyFlink and flink-agents? (Only needed for Python API users; Java users can select No)"; then + ENABLE_PYFLINK=Yes + else + ENABLE_PYFLINK=No + fi + ;; + *) + die "Unsupported ENABLE_PYFLINK value: ${ENABLE_PYFLINK}. Use: Ask|Yes|No" + ;; + esac + + if [[ "$ENABLE_PYFLINK" == "No" ]]; then + return + fi + + PYFLINK_ACTUALLY_ENABLED=1 + resolve_python + + if [[ "$VENV_DIR_EXPLICIT" -eq 0 ]] && is_promptable; then + VENV_DIR="$(prompt_path_choice_interactive \ + "Choose Python venv directory" \ + "$VENV_DIR" \ + "/path/to/venv")" + fi + + case "$VENV_DIR" in + /*) ;; + *) VENV_DIR="$PWD/$VENV_DIR" ;; + esac +} + +confirm_install_plan() { + if [[ "$NO_PROMPT" == "1" ]] || ! is_promptable; then + return 0 + fi + if choose_install_method_interactive "Proceed with installation?"; then + return 0 + fi + ui_info "Installation cancelled by user." + exit 0 +} + +install_flink_if_needed() { + if [[ "$INSTALL_FLINK" == "No" ]]; then + ui_info "Skipping Flink download/install (using existing FLINK_HOME)." + ui_success "FLINK_HOME resolved: $FLINK_HOME" + return + fi + + local ARCHIVE_NAME="flink-${FLINK_VERSION}-bin-scala_${FLINK_SCALA_VERSION}.tgz" + local ARCHIVE_URL="${FLINK_BASE_URL}/flink-${FLINK_VERSION}/${ARCHIVE_NAME}" + + detect_downloader + require_cmd tar + if ! mkdir -p "$INSTALL_DIR"; then die "Failed to create INSTALL_DIR=$INSTALL_DIR. Please run with proper permissions or set INSTALL_DIR to a writable path." fi @@ -572,8 +625,7 @@ install_flink_if_needed() { ui_info "Reusing existing Flink home: ${INSTALL_DIR}/flink-${FLINK_VERSION}" fi - export FLINK_HOME="${INSTALL_DIR}/flink-${FLINK_VERSION}" - FLINK_MAJOR_MINOR="${FLINK_VERSION%.*}" + export FLINK_HOME ui_success "FLINK_HOME resolved: $FLINK_HOME" } @@ -673,15 +725,6 @@ create_venv() { } setup_python_env() { - resolve_python - - if [[ "$VENV_DIR_EXPLICIT" -eq 0 ]] && is_promptable; then - VENV_DIR="$(prompt_path_choice_interactive \ - "Choose Python venv directory" \ - "$VENV_DIR" \ - "/path/to/venv")" - fi - if [[ ! -d "$VENV_DIR" ]]; then ui_info "Creating virtual environment: $VENV_DIR" create_venv @@ -735,26 +778,6 @@ PY [[ "$copied" -eq 1 ]] || die "No flink-agents-dist jar found in: $version_lib_dir" } -should_enable_pyflink() { - case "$ENABLE_PYFLINK" in - yes) - return 0 - ;; - no) - return 1 - ;; - ask) - if choose_install_method_interactive "Create a Python venv with PyFlink and flink-agents? (Only needed for Python API users; Java users can select No)"; then - return 0 - fi - return 1 - ;; - *) - die "Unsupported ENABLE_PYFLINK value: ${ENABLE_PYFLINK}. Use: ask|yes|no" - ;; - esac -} - print_usage() { cat < Date: Tue, 28 Apr 2026 00:26:47 +0800 Subject: [PATCH 12/13] [tools] install.sh: refactor style to align with install_example.sh --- tools/install.sh | 166 +++++++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 93 deletions(-) diff --git a/tools/install.sh b/tools/install.sh index e70beec80..1c133fa83 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -175,7 +175,7 @@ bootstrap_gum_temp() { gum_tmpdir="$(mktemp -d)" TMPFILES+=("$gum_tmpdir") - echo -e "${INFO}· Installing gum v${GUM_VERSION}, please wait...${NC}" + ui_info "Installing gum v${GUM_VERSION}, please wait..." if ! download_file "${base}/${asset}" "$gum_tmpdir/$asset"; then GUM_REASON="download failed" @@ -233,10 +233,9 @@ print_gum_status() { print_installer_banner() { if [[ -n "$GUM" ]]; then - local title card + local title title="$("$GUM" style --foreground "#ff4d4d" --bold "Apache Flink Agents Installer")" - card="$(printf '%s\n' "$title")" - "$GUM" style --border rounded --border-foreground "#ff4d4d" --padding "1 2" "$card" + "$GUM" style --border rounded --border-foreground "#ff4d4d" --padding "1 2" "$title" echo "" return fi @@ -347,28 +346,19 @@ ui_celebrate() { fi } -is_shell_function() { - local name="${1:-}" - [[ -n "$name" ]] && declare -F "$name" >/dev/null 2>&1 -} - -is_gum_raw_mode_failure() { - local err_log="$1" - [[ -s "$err_log" ]] || return 1 - grep -Eiq '(setrawmode|inappropriate ioctl for device)' "$err_log" -} - -configure_verbose() { - if [[ "$VERBOSE" != "1" ]]; then - return 0 +mark_explicit() { + local var="$1" + if [[ -n "${!var:-}" ]]; then + printf -v "${var}_EXPLICIT" '%s' '1' + else + printf -v "${var}_EXPLICIT" '%s' '0' fi - set -x } -FLINK_VERSION_EXPLICIT=0 -if [[ -n "${FLINK_VERSION:-}" ]]; then - FLINK_VERSION_EXPLICIT=1 -fi +mark_explicit FLINK_VERSION +mark_explicit INSTALL_DIR +mark_explicit VENV_DIR + FLINK_VERSION="${FLINK_VERSION:-2.2.0}" FLINK_AGENTS_VERSION="${FLINK_AGENTS_VERSION:-0.2.1}" FLINK_SCALA_VERSION="${FLINK_SCALA_VERSION:-2.12}" @@ -379,15 +369,7 @@ FLINK_RECOMMENDED_VERSION="2.2.0" INSTALL_FLINK="${INSTALL_FLINK:-Ask}" ENABLE_PYFLINK="${ENABLE_PYFLINK:-Ask}" -INSTALL_DIR_EXPLICIT=0 -if [[ -n "${INSTALL_DIR:-}" ]]; then - INSTALL_DIR_EXPLICIT=1 -fi INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/flink}" -VENV_DIR_EXPLICIT=0 -if [[ -n "${VENV_DIR:-}" ]]; then - VENV_DIR_EXPLICIT=1 -fi VENV_DIR="${VENV_DIR:-.flink-agents-env}" PYTHON_BIN="${PYTHON_BIN:-}" NO_PROMPT="${NO_PROMPT:-0}" @@ -397,6 +379,46 @@ VERIFY_INSTALL="${FLINK_AGENTS_VERIFY_INSTALL:-0}" HELP=0 PYFLINK_ACTUALLY_ENABLED=0 +print_usage() { + cat </install.sh | bash -s -- [options] + +Options: + --non-interactive Non-interactive mode (accept all defaults) + --install-flink Download and install Apache Flink + --enable-pyflink Enable PyFlink and install Python packages + --verbose Print debug output (set -x) + --dry-run Print install plan without making changes + --verify Run post-install verification checks + --python Path to a Python3 interpreter (overrides PATH lookup) + --help, -h Show this help + +Environment variables: + FLINK_VERSION Flink version + FLINK_AGENTS_VERSION Flink Agents version to install + FLINK_SCALA_VERSION Scala version suffix (default: 2.12) + FLINK_BASE_URL Mirror base URL (default: https://dlcdn.apache.org/flink) + INSTALL_FLINK Ask|Yes|No (default: Ask) + ENABLE_PYFLINK Ask|Yes|No (default: Ask) + INSTALL_DIR Flink install directory (default: \$HOME/.local/flink) + VENV_DIR Python venv directory (default: .flink-agents-env) + PYTHON_BIN Path to Python3 interpreter (default: auto-detect python3 on PATH) + NO_PROMPT 1 to disable all prompts + FLINK_AGENTS_VERBOSE 1 to enable verbose output + FLINK_AGENTS_DRY_RUN 1 to enable dry-run mode + FLINK_AGENTS_VERIFY_INSTALL 1 to enable post-install verification + +Examples: + bash install.sh --install-flink --enable-pyflink --non-interactive + FLINK_VERSION=2.2.0 bash install.sh --verbose + bash install.sh --dry-run +EOF +} + require_cmd() { command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1" } @@ -508,7 +530,7 @@ plan_flink() { if [[ "$INSTALL_FLINK" == "No" ]]; then if [[ -z "${FLINK_HOME:-}" || ! -d "${FLINK_HOME}" || ! -d "${FLINK_HOME}/lib" ]]; then if is_promptable; then - FLINK_HOME="$(prompt_flink_home_interactive)" + FLINK_HOME="$(prompt_path_input "Enter the path to your existing FLINK_HOME (version >= 1.20)" "/path/to/flink-${FLINK_VERSION}")" export FLINK_HOME fi fi @@ -629,15 +651,17 @@ install_flink_if_needed() { ui_success "FLINK_HOME resolved: $FLINK_HOME" } -prompt_flink_home_interactive() { +prompt_path_input() { + local header="$1" + local placeholder="$2" local input="" if [[ -n "$GUM" ]] && gum_is_tty; then input="$("$GUM" input \ - --header "Enter the path to your existing FLINK_HOME which version >= 1.20" \ - --placeholder "/path/to/flink-${FLINK_VERSION}" \ + --header "$header" \ + --placeholder "$placeholder" \ --width 70 < /dev/tty || true)" else - printf 'Enter the path to your existing FLINK_HOME which version >= 1.20: ' > /dev/tty + printf '%s: ' "$header" > /dev/tty read -r input < /dev/tty || true fi input="${input/#\~/$HOME}" @@ -778,46 +802,6 @@ PY [[ "$copied" -eq 1 ]] || die "No flink-agents-dist jar found in: $version_lib_dir" } -print_usage() { - cat </install.sh | bash -s -- [options] - -Options: - --non-interactive Non-interactive mode (accept all defaults) - --install-flink Download and install Apache Flink - --enable-pyflink Enable PyFlink and install Python packages - --verbose Print debug output (set -x) - --dry-run Print install plan without making changes - --verify Run post-install verification checks - --python Path to a Python3 interpreter (overrides PATH lookup) - --help, -h Show this help - -Environment variables: - FLINK_VERSION Flink version - FLINK_AGENTS_VERSION Flink Agents version to install - FLINK_SCALA_VERSION Scala version suffix (default: 2.12) - FLINK_BASE_URL Mirror base URL (default: https://dlcdn.apache.org/flink) - INSTALL_FLINK Ask|Yes|No (default: Ask) - ENABLE_PYFLINK Ask|Yes|No (default: Ask) - INSTALL_DIR Flink install directory (default: \$HOME/.local/flink) - VENV_DIR Python venv directory (default: .flink-agents-env) - PYTHON_BIN Path to Python3 interpreter (default: auto-detect python3 on PATH) - NO_PROMPT 1 to disable all prompts - FLINK_AGENTS_VERBOSE 1 to enable verbose output - FLINK_AGENTS_DRY_RUN 1 to enable dry-run mode - FLINK_AGENTS_VERIFY_INSTALL 1 to enable post-install verification - -Examples: - bash install.sh --install-flink --enable-pyflink --non-interactive - FLINK_VERSION=2.2.0 bash install.sh --verbose - bash install.sh --dry-run -EOF -} - check_java() { if ! command -v java &>/dev/null; then ui_warn "Java not found on PATH" @@ -834,7 +818,7 @@ check_java() { local java_version_output java_version_output="$(java -version 2>&1 | head -n1)" local java_major="" - java_major="$(echo "$java_version_output" | sed -n 's/.*version "\([0-9]*\).*/\1/p')" + java_major="$(echo "$java_version_output" | sed -E -n 's/.*version "?([0-9]+).*/\1/p')" if [[ -z "$java_major" ]]; then ui_warn "Could not parse Java version from: $java_version_output" @@ -874,21 +858,6 @@ validate_python_bin() { return 0 } -prompt_python_bin_interactive() { - local input="" - if [[ -n "$GUM" ]] && gum_is_tty; then - input="$("$GUM" input \ - --header "Enter path to a Python interpreter" \ - --placeholder "/path/to/python3" \ - --width 70 < /dev/tty || true)" - else - printf 'Enter path to a Python interpreter: ' > /dev/tty - read -r input < /dev/tty || true - fi - input="${input/#\~/$HOME}" - printf '%s' "$input" -} - resolve_python() { if [[ -n "$PYTHON_BIN" ]]; then if validate_python_bin "$PYTHON_BIN"; then @@ -917,7 +886,7 @@ resolve_python() { fi local input - input="$(prompt_python_bin_interactive)" + input="$(prompt_path_input "Enter path to a Python interpreter" "/path/to/python3")" if [[ -z "$input" ]]; then die "No Python interpreter provided." fi @@ -1074,6 +1043,17 @@ parse_args() { done } +configure_verbose() { + if [[ "$VERBOSE" != "1" ]]; then + return 0 + fi + if [[ "$NPM_LOGLEVEL" == "error" ]]; then + NPM_LOGLEVEL="notice" + fi + NPM_SILENT_FLAG="" + set -x +} + main() { if [[ "$HELP" == "1" ]]; then print_usage From 852c6032e3d9453239c184e127b78b271e29e6d2 Mon Sep 17 00:00:00 2001 From: Jinkun Liu Date: Wed, 29 Apr 2026 22:18:47 +0800 Subject: [PATCH 13/13] [tools]Fix download time limit --- tools/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/install.sh b/tools/install.sh index 1c133fa83..d8db4afb6 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -65,10 +65,10 @@ download_file() { detect_downloader fi if [[ "$DOWNLOADER" == "curl" ]]; then - curl -fsSL --proto '=https' --tlsv1.2 --connect-timeout 5 --max-time 30 --retry 2 --retry-delay 1 --retry-connrefused -o "$output" "$url" + curl -fsSL --proto '=https' --tlsv1.2 --retry 3 --max-time 600 --retry-delay 1 -- --retry-connrefused -o "$output" "$url" return fi - wget -q --https-only --secure-protocol=TLSv1_2 --tries=2 --timeout=30 -O "$output" "$url" + wget -q --https-only --secure-protocol=TLSv1_2 --tries=3 --timeout=600 -O "$output" "$url" } GUM_VERSION="${FLINK_AGENTS_GUM_VERSION:-0.17.0}"