mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-03-26 11:53:00 +01:00
Compare commits
1 Commits
shell_safe
...
pr-update-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85f4ff2bd3 |
12
CHANGELOG.md
12
CHANGELOG.md
@@ -426,14 +426,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
||||
|
||||
</details>
|
||||
|
||||
## 2026-03-25
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
- #### ✨ New Features
|
||||
|
||||
- Komodo v2: migrate env vars to v2 and update source [@MickLesk](https://github.com/MickLesk) ([#13262](https://github.com/community-scripts/ProxmoxVE/pull/13262))
|
||||
|
||||
## 2026-03-24
|
||||
|
||||
### 🆕 New Scripts
|
||||
@@ -447,10 +439,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
||||
|
||||
- Turnkey: modernize turnkey.sh with shared libraries [@MickLesk](https://github.com/MickLesk) ([#13242](https://github.com/community-scripts/ProxmoxVE/pull/13242))
|
||||
|
||||
- #### 🔧 Refactor
|
||||
|
||||
- chore: replace helper-scripts.com with community-scripts.com [@MickLesk](https://github.com/MickLesk) ([#13244](https://github.com/community-scripts/ProxmoxVE/pull/13244))
|
||||
|
||||
### 🗑️ Deleted Scripts
|
||||
|
||||
- Remove: Booklore [@MickLesk](https://github.com/MickLesk) ([#13265](https://github.com/community-scripts/ProxmoxVE/pull/13265))
|
||||
|
||||
@@ -35,8 +35,6 @@ function update_script() {
|
||||
read -r -p "${TAB}Migrate update function now? [y/N]: " CONFIRM
|
||||
if [[ ! "${CONFIRM,,}" =~ ^(y|yes)$ ]]; then
|
||||
msg_warn "Migration skipped. The old update will continue to work for now."
|
||||
msg_warn "⚠️ Komodo v2 uses :2 image tags. The :latest tag is deprecated and will not receive v2 updates."
|
||||
msg_warn "Please migrate to the addon script to receive Komodo v2."
|
||||
msg_info "Updating ${APP} (legacy)"
|
||||
COMPOSE_FILE=$(find /opt/komodo -maxdepth 1 -type f -name '*.compose.yaml' ! -name 'compose.env' | head -n1)
|
||||
if [[ -z "$COMPOSE_FILE" ]]; then
|
||||
|
||||
@@ -39,8 +39,6 @@ function update_script() {
|
||||
read -r -p "${TAB}Migrate update function now? [y/N]: " CONFIRM
|
||||
if [[ ! "${CONFIRM,,}" =~ ^(y|yes)$ ]]; then
|
||||
msg_warn "Migration skipped. The old update will continue to work for now."
|
||||
msg_warn "⚠️ Komodo v2 uses :2 image tags. The :latest tag is deprecated and will not receive v2 updates."
|
||||
msg_warn "Please migrate to the addon script to receive Komodo v2."
|
||||
msg_info "Updating ${APP} (legacy)"
|
||||
COMPOSE_FILE=$(find /opt/komodo -maxdepth 1 -type f -name '*.compose.yaml' ! -name 'compose.env' | head -n1)
|
||||
if [[ -z "$COMPOSE_FILE" ]]; then
|
||||
|
||||
@@ -504,7 +504,7 @@ detect_gpu() {
|
||||
GPU_PASSTHROUGH="unknown"
|
||||
|
||||
local gpu_line
|
||||
gpu_line=$(lspci 2>/dev/null | grep -iE "VGA|3D|Display" | head -1 || true)
|
||||
gpu_line=$(lspci 2>/dev/null | grep -iE "VGA|3D|Display" | head -1)
|
||||
|
||||
if [[ -n "$gpu_line" ]]; then
|
||||
# Extract model: everything after the colon, clean up
|
||||
@@ -543,7 +543,7 @@ detect_cpu() {
|
||||
|
||||
if [[ -f /proc/cpuinfo ]]; then
|
||||
local vendor_id
|
||||
vendor_id=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ' || true)
|
||||
vendor_id=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ')
|
||||
|
||||
case "$vendor_id" in
|
||||
GenuineIntel) CPU_VENDOR="intel" ;;
|
||||
@@ -557,7 +557,7 @@ detect_cpu() {
|
||||
esac
|
||||
|
||||
# Extract model name and clean it up
|
||||
CPU_MODEL=$(grep -m1 "model name" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed 's/^ *//' | sed 's/(R)//g' | sed 's/(TM)//g' | sed 's/ */ /g' | cut -c1-64 || true)
|
||||
CPU_MODEL=$(grep -m1 "model name" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed 's/^ *//' | sed 's/(R)//g' | sed 's/(TM)//g' | sed 's/ */ /g' | cut -c1-64)
|
||||
fi
|
||||
|
||||
export CPU_VENDOR CPU_MODEL
|
||||
@@ -1347,8 +1347,8 @@ post_addon_to_api() {
|
||||
# Detect OS info
|
||||
local os_type="" os_version=""
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
os_type=$(grep "^ID=" /etc/os-release | cut -d= -f2 | tr -d '"' || true)
|
||||
os_version=$(grep "^VERSION_ID=" /etc/os-release | cut -d= -f2 | tr -d '"' || true)
|
||||
os_type=$(grep "^ID=" /etc/os-release | cut -d= -f2 | tr -d '"')
|
||||
os_version=$(grep "^VERSION_ID=" /etc/os-release | cut -d= -f2 | tr -d '"')
|
||||
fi
|
||||
|
||||
local JSON_PAYLOAD
|
||||
|
||||
@@ -173,10 +173,10 @@ get_current_ip() {
|
||||
# Check for Debian/Ubuntu (uses hostname -I)
|
||||
if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then
|
||||
# Try IPv4 first
|
||||
CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1 || true)
|
||||
CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1)
|
||||
# Fallback to IPv6 if no IPv4
|
||||
if [[ -z "$CURRENT_IP" ]]; then
|
||||
CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E ':' | head -n1 || true)
|
||||
CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E ':' | head -n1)
|
||||
fi
|
||||
# Check for Alpine (uses ip command)
|
||||
elif grep -q 'ID=alpine' /etc/os-release; then
|
||||
@@ -1704,8 +1704,8 @@ ensure_storage_selection_for_vars_file() {
|
||||
|
||||
# Read stored values (if any)
|
||||
local tpl ct
|
||||
tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2- || true)
|
||||
ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2- || true)
|
||||
tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-)
|
||||
ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-)
|
||||
|
||||
if [[ -n "$tpl" && -n "$ct" ]]; then
|
||||
TEMPLATE_STORAGE="$tpl"
|
||||
@@ -1840,7 +1840,7 @@ advanced_settings() {
|
||||
if [[ -n "$BRIDGES" ]]; then
|
||||
while IFS= read -r bridge; do
|
||||
if [[ -n "$bridge" ]]; then
|
||||
local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//;s/^[- ]*//' || true)
|
||||
local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//;s/^[- ]*//')
|
||||
BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }")
|
||||
fi
|
||||
done <<<"$BRIDGES"
|
||||
@@ -3322,7 +3322,7 @@ configure_ssh_settings() {
|
||||
tag="${tag%\"}"
|
||||
tag="${tag#\"}"
|
||||
local line
|
||||
line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2- || true)
|
||||
line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-)
|
||||
[[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE"
|
||||
done
|
||||
;;
|
||||
@@ -3349,7 +3349,7 @@ configure_ssh_settings() {
|
||||
tag="${tag%\"}"
|
||||
tag="${tag#\"}"
|
||||
local line
|
||||
line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2- || true)
|
||||
line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-)
|
||||
[[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE"
|
||||
done
|
||||
else
|
||||
@@ -4050,7 +4050,7 @@ EOF
|
||||
# Fix Debian 13 LXC template bug where / is owned by nobody:nogroup
|
||||
# This must be done from the host as unprivileged containers cannot chown /
|
||||
local rootfs
|
||||
rootfs=$(pct config "$CTID" | grep -E '^rootfs:' | sed 's/rootfs: //' | cut -d',' -f1 || true)
|
||||
rootfs=$(pct config "$CTID" | grep -E '^rootfs:' | sed 's/rootfs: //' | cut -d',' -f1)
|
||||
if [[ -n "$rootfs" ]]; then
|
||||
local mount_point="/var/lib/lxc/${CTID}/rootfs"
|
||||
if [[ -d "$mount_point" ]] && [[ "$(stat -c '%U' "$mount_point")" != "root" ]]; then
|
||||
@@ -5142,7 +5142,7 @@ create_lxc_container() {
|
||||
fi
|
||||
|
||||
msg_info "Validating storage '$CONTAINER_STORAGE'"
|
||||
STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1 || true)
|
||||
STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1)
|
||||
|
||||
if [[ -z "$STORAGE_TYPE" ]]; then
|
||||
msg_error "Storage '$CONTAINER_STORAGE' not found in /etc/pve/storage.cfg"
|
||||
@@ -5181,7 +5181,7 @@ create_lxc_container() {
|
||||
msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated"
|
||||
|
||||
msg_info "Validating template storage '$TEMPLATE_STORAGE'"
|
||||
TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 || true)
|
||||
TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1)
|
||||
|
||||
if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then
|
||||
msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'"
|
||||
|
||||
@@ -524,12 +524,12 @@ is_tool_installed() {
|
||||
case "$tool_name" in
|
||||
mariadb)
|
||||
if command -v mariadb >/dev/null 2>&1; then
|
||||
installed_version=$(mariadb --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)
|
||||
installed_version=$(mariadb --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
||||
fi
|
||||
;;
|
||||
mysql)
|
||||
if command -v mysql >/dev/null 2>&1; then
|
||||
installed_version=$(mysql --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)
|
||||
installed_version=$(mysql --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
||||
fi
|
||||
;;
|
||||
mongodb | mongod)
|
||||
@@ -539,7 +539,7 @@ is_tool_installed() {
|
||||
;;
|
||||
node | nodejs)
|
||||
if command -v node >/dev/null 2>&1; then
|
||||
installed_version=$(node -v 2>/dev/null | grep -oP '^v\K[0-9]+' || true)
|
||||
installed_version=$(node -v 2>/dev/null | grep -oP '^v\K[0-9]+')
|
||||
fi
|
||||
;;
|
||||
php)
|
||||
@@ -4837,7 +4837,7 @@ _setup_nvidia_gpu() {
|
||||
# Use regex to extract version number (###.##.## or ###.## pattern)
|
||||
local nvidia_host_version=""
|
||||
if [[ -f /proc/driver/nvidia/version ]]; then
|
||||
nvidia_host_version=$(grep -oP '\d{3,}\.\d+(\.\d+)?' /proc/driver/nvidia/version 2>/dev/null | head -1 || true)
|
||||
nvidia_host_version=$(grep -oP '\d{3,}\.\d+(\.\d+)?' /proc/driver/nvidia/version 2>/dev/null | head -1)
|
||||
fi
|
||||
|
||||
if [[ -z "$nvidia_host_version" ]]; then
|
||||
@@ -7321,7 +7321,7 @@ function setup_meilisearch() {
|
||||
MEILI_HOST="${MEILISEARCH_HOST:-127.0.0.1}"
|
||||
MEILI_PORT="${MEILISEARCH_PORT:-7700}"
|
||||
MEILI_DUMP_DIR="${MEILISEARCH_DUMP_DIR:-/var/lib/meilisearch/dumps}"
|
||||
MEILI_MASTER_KEY=$(grep -E "^master_key\s*=" /etc/meilisearch.toml 2>/dev/null | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' ' || true)
|
||||
MEILI_MASTER_KEY=$(grep -E "^master_key\s*=" /etc/meilisearch.toml 2>/dev/null | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' ')
|
||||
|
||||
# Create dump before update if migration is needed
|
||||
local DUMP_UID=""
|
||||
@@ -7387,7 +7387,7 @@ function setup_meilisearch() {
|
||||
# We choose option 2: backup and proceed with warning
|
||||
if [[ "$NEEDS_MIGRATION" == "true" ]] && [[ -z "$DUMP_UID" ]]; then
|
||||
local MEILI_DB_PATH
|
||||
MEILI_DB_PATH=$(grep -E "^db_path\s*=" /etc/meilisearch.toml 2>/dev/null | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' ' || true)
|
||||
MEILI_DB_PATH=$(grep -E "^db_path\s*=" /etc/meilisearch.toml 2>/dev/null | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' ')
|
||||
MEILI_DB_PATH="${MEILI_DB_PATH:-/var/lib/meilisearch/data}"
|
||||
|
||||
if [[ -d "$MEILI_DB_PATH" ]] && [[ -n "$(ls -A "$MEILI_DB_PATH" 2>/dev/null)" ]]; then
|
||||
@@ -7407,7 +7407,7 @@ function setup_meilisearch() {
|
||||
# If migration needed and dump was created, remove old data and import dump
|
||||
if [[ "$NEEDS_MIGRATION" == "true" ]] && [[ -n "$DUMP_UID" ]]; then
|
||||
local MEILI_DB_PATH
|
||||
MEILI_DB_PATH=$(grep -E "^db_path\s*=" /etc/meilisearch.toml 2>/dev/null | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' ' || true)
|
||||
MEILI_DB_PATH=$(grep -E "^db_path\s*=" /etc/meilisearch.toml 2>/dev/null | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' ')
|
||||
MEILI_DB_PATH="${MEILI_DB_PATH:-/var/lib/meilisearch/data}"
|
||||
|
||||
msg_info "Removing old MeiliSearch database for migration"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://komo.do/ | Github: https://github.com/moghtech/komodo
|
||||
# Source: https://komo.do/ | Github: https://github.com/mbecker20/komodo
|
||||
if ! command -v curl &>/dev/null; then
|
||||
printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2
|
||||
apt-get update >/dev/null 2>&1 || apk update >/dev/null 2>&1
|
||||
@@ -82,7 +82,6 @@ function update() {
|
||||
msg_error "Failed to create backup of ${COMPOSE_BASENAME}!"
|
||||
exit 235
|
||||
}
|
||||
cp "$COMPOSE_ENV" "${COMPOSE_ENV}.bak_$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
|
||||
|
||||
GITHUB_URL="https://raw.githubusercontent.com/moghtech/komodo/main/compose/${COMPOSE_BASENAME}"
|
||||
if ! curl -fsSL "$GITHUB_URL" -o "$COMPOSE_FILE"; then
|
||||
@@ -91,29 +90,8 @@ function update() {
|
||||
exit 115
|
||||
fi
|
||||
|
||||
# === v2 migration: image tag (latest is deprecated) ===
|
||||
if grep -q '^COMPOSE_KOMODO_IMAGE_TAG=latest' "$COMPOSE_ENV"; then
|
||||
msg_info "Migrating to Komodo v2 image tag"
|
||||
sed -i 's/^COMPOSE_KOMODO_IMAGE_TAG=latest/COMPOSE_KOMODO_IMAGE_TAG=2/' "$COMPOSE_ENV"
|
||||
msg_ok "Migrated image tag to :2"
|
||||
fi
|
||||
|
||||
# === v2 migration: DB credential variable names ===
|
||||
if grep -q '^KOMODO_DB_USERNAME=' "$COMPOSE_ENV"; then
|
||||
msg_info "Migrating database credential variables"
|
||||
sed -i 's/^KOMODO_DB_USERNAME=/KOMODO_DATABASE_USERNAME=/' "$COMPOSE_ENV"
|
||||
sed -i 's/^KOMODO_DB_PASSWORD=/KOMODO_DATABASE_PASSWORD=/' "$COMPOSE_ENV"
|
||||
msg_ok "Migrated DB credential variables"
|
||||
fi
|
||||
|
||||
# === v2 migration: remove deprecated passkey (replaced by PKI) ===
|
||||
if grep -q '^KOMODO_PASSKEY=' "$COMPOSE_ENV"; then
|
||||
sed -i '/^KOMODO_PASSKEY=/d' "$COMPOSE_ENV"
|
||||
fi
|
||||
|
||||
# === ensure backups path is set ===
|
||||
if ! grep -q 'COMPOSE_KOMODO_BACKUPS_PATH=' "$COMPOSE_ENV"; then
|
||||
echo 'COMPOSE_KOMODO_BACKUPS_PATH=/etc/komodo/backups' >>"$COMPOSE_ENV"
|
||||
if ! grep -qxF 'COMPOSE_KOMODO_BACKUPS_PATH=/etc/komodo/backups' "$COMPOSE_ENV"; then
|
||||
sed -i '/^COMPOSE_KOMODO_IMAGE_TAG=latest$/a COMPOSE_KOMODO_BACKUPS_PATH=/etc/komodo/backups' "$COMPOSE_ENV"
|
||||
fi
|
||||
|
||||
$STD docker compose -p komodo -f "$COMPOSE_FILE" --env-file "$COMPOSE_ENV" pull
|
||||
@@ -214,12 +192,14 @@ function install() {
|
||||
|
||||
DB_PASSWORD=$(openssl rand -base64 16 | tr -d '/+=')
|
||||
ADMIN_PASSWORD=$(openssl rand -base64 8 | tr -d '/+=')
|
||||
PASSKEY=$(openssl rand -base64 24 | tr -d '/+=')
|
||||
WEBHOOK_SECRET=$(openssl rand -base64 24 | tr -d '/+=')
|
||||
JWT_SECRET=$(openssl rand -base64 24 | tr -d '/+=')
|
||||
|
||||
sed -i "s/^KOMODO_DATABASE_USERNAME=.*/KOMODO_DATABASE_USERNAME=komodo_admin/" "$COMPOSE_ENV"
|
||||
sed -i "s/^KOMODO_DATABASE_PASSWORD=.*/KOMODO_DATABASE_PASSWORD=${DB_PASSWORD}/" "$COMPOSE_ENV"
|
||||
sed -i "s/^KOMODO_DB_USERNAME=.*/KOMODO_DB_USERNAME=komodo_admin/" "$COMPOSE_ENV"
|
||||
sed -i "s/^KOMODO_DB_PASSWORD=.*/KOMODO_DB_PASSWORD=${DB_PASSWORD}/" "$COMPOSE_ENV"
|
||||
sed -i "s/^KOMODO_INIT_ADMIN_PASSWORD=changeme/KOMODO_INIT_ADMIN_PASSWORD=${ADMIN_PASSWORD}/" "$COMPOSE_ENV"
|
||||
sed -i "s/^KOMODO_PASSKEY=.*/KOMODO_PASSKEY=${PASSKEY}/" "$COMPOSE_ENV"
|
||||
sed -i "s/^KOMODO_WEBHOOK_SECRET=.*/KOMODO_WEBHOOK_SECRET=${WEBHOOK_SECRET}/" "$COMPOSE_ENV"
|
||||
sed -i "s/^KOMODO_JWT_SECRET=.*/KOMODO_JWT_SECRET=${JWT_SECRET}/" "$COMPOSE_ENV"
|
||||
msg_ok "Configured environment"
|
||||
|
||||
@@ -177,8 +177,6 @@ pveam update >/dev/null
|
||||
msg_ok "Updated LXC template list"
|
||||
|
||||
# Build TurnKey selection menu dynamically from available templates
|
||||
# Requires gawk for regex capture groups in match()
|
||||
command -v gawk &>/dev/null || apt-get install -y gawk &>/dev/null
|
||||
declare -A TURNKEY_TEMPLATES
|
||||
TURNKEY_MENU=()
|
||||
MSG_MAX_LENGTH=0
|
||||
@@ -187,7 +185,7 @@ while IFS=$'\t' read -r TEMPLATE_FILE TAG ITEM; do
|
||||
OFFSET=2
|
||||
((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET))
|
||||
TURNKEY_MENU+=("$TAG" "$ITEM " "OFF")
|
||||
done < <(pveam available -section turnkeylinux | gawk '{
|
||||
done < <(pveam available -section turnkeylinux | awk '{
|
||||
tpl = $2
|
||||
if (match(tpl, /debian-([0-9]+)-turnkey-([^_]+)_([^_]+)_/, m)) {
|
||||
app = m[2]; deb = m[1]; ver = m[3]
|
||||
|
||||
Reference in New Issue
Block a user