wenjin272 commented on code in PR #599:
URL: https://github.com/apache/flink-agents/pull/599#discussion_r3144748442


##########
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/"

Review Comment:
   The thin jar contains only the code for flink-agents itself and does not 
include any Integrations. We need copy both the corresponding thin jar and 
common jar to flink lib.



-- 
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]

Reply via email to