Initial commit: 50 shell-script generators with web UI
- 8 categories / 50 generators covering middleware, databases, runtimes, systemd services, system tools, network, monitoring, security - VNC supports XFCE / GNOME / KDE Plasma / MATE / LXQt desktops - Node.js versions 18-26 (incl. current LTS 24 Krypton and current 26) - Live preview, copy / download / multi-script bundle in web UI - Distro-aware (Ubuntu/Debian/CentOS/RHEL/Rocky/Alma/Fedora) - All 50 generators pass bash -n syntax check - Zero pip dependencies (Flask stdlib only)
This commit is contained in:
@@ -0,0 +1,651 @@
|
||||
"""
|
||||
Runtime / language / compiler version installers.
|
||||
Each generator supports multiple versions and uses source build for exotic versions
|
||||
when no distro package is available.
|
||||
"""
|
||||
from . import register, Generator, Field
|
||||
from . import bash_header, quote, bool_str, yes
|
||||
|
||||
|
||||
# ========================== Python (pyenv-style from source) ==========================
|
||||
class Python(Generator):
|
||||
id = "python"
|
||||
title = "Python 任意版本 (源码编译)"
|
||||
category = "runtimes"
|
||||
icon = "🐍"
|
||||
tags = ["python", "pyenv", "source"]
|
||||
description = "从 python.org 下载源码编译,支持 2.7 / 3.6 - 3.13 任意版本,启用 SSL/zlib/sqlite3。"
|
||||
warnings = [
|
||||
"源码编译会下载 ~25MB,编译 5-15 分钟,需要 build-essential。",
|
||||
"建议先运行 gcc 编译器安装脚本(同分类)以保证编译依赖。",
|
||||
]
|
||||
fields = [
|
||||
Field("version", "Python 版本", "select", default="3.12.6",
|
||||
options=["2.7.18", "3.6.15", "3.7.17", "3.8.20", "3.9.20",
|
||||
"3.10.14", "3.11.11", "3.12.6", "3.13.0", "3.13.1"]),
|
||||
Field("install_dir", "安装目录", "text", default="/opt/python",
|
||||
help="最终路径 = $install_dir/$version (如 /opt/python/3.12.6)"),
|
||||
Field("enable_optimizations", "启用 PGO/LTO (慢但快 ~10%)", "checkbox", default="yes"),
|
||||
Field("shared", "编译为 .so 共享库", "checkbox", default="no",
|
||||
help="如果其他工具(如 mod_wsgi)需要动态链接则开启。"),
|
||||
Field("install_pip", "安装 pip", "checkbox", default="yes"),
|
||||
Field("symlink_bin", "软链 bin 目录到 PATH", "checkbox", default="no",
|
||||
help="yes = 软链 /opt/python/$version/bin/* 到 /usr/local/bin/"),
|
||||
Field("ssl_backend", "SSL 后端", "select", default="openssl",
|
||||
options=["openssl", "libressl"]),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
ver = p.get("version", "3.12.6")
|
||||
d = p.get("install_dir", "/opt/python")
|
||||
opt = " --enable-optimizations" if bool_str(p.get("enable_optimizations", True)) else ""
|
||||
shared = " --enable-shared" if bool_str(p.get("shared")) else ""
|
||||
ssl = p.get("ssl_backend", "openssl")
|
||||
sym = bool_str(p.get("symlink_bin"))
|
||||
pip = bool_str(p.get("install_pip", True))
|
||||
|
||||
out = [bash_header(self.title)]
|
||||
out.append('log "Installing Python ' + ver + ' from source..."')
|
||||
out.append('command -v gcc >/dev/null || $PKG_INSTALL gcc make build-essential \\\n'
|
||||
' libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev \\\n'
|
||||
' libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev \\\n'
|
||||
' || $PKG_INSTALL gcc make openssl-devel bzip2-devel libffi-devel zlib-devel \\\n'
|
||||
' readline-devel sqlite-devel ncurses-devel xz-devel')
|
||||
out.append('cd /tmp')
|
||||
out.append('curl -fsSL -o Python.tgz https://www.python.org/ftp/python/' + ver + '/Python-' + ver + '.tgz')
|
||||
out.append('tar -xzf Python.tgz')
|
||||
out.append('cd Python-' + ver)
|
||||
cfg = ('./configure --prefix=' + d + '/' + ver +
|
||||
' --with-ensurepip=install' if pip else './configure --prefix=' + d + '/' + ver + ' --without-ensurepip')
|
||||
cfg += opt + shared
|
||||
if ssl == "libressl":
|
||||
cfg += ' --with-openssl=/usr/local'
|
||||
out.append(cfg)
|
||||
out.append('make -j"$(nproc)"')
|
||||
out.append('make altinstall')
|
||||
out.append('cd /tmp && rm -rf Python-' + ver + ' Python.tgz')
|
||||
out.append('')
|
||||
out.append('PYBIN=' + d + '/' + ver + '/bin/python' + '.'.join(ver.split('.')[:2]))
|
||||
if sym:
|
||||
out.append('log "Symlinking binaries to /usr/local/bin/..."')
|
||||
out.append('for b in ' + d + '/' + ver + '/bin/*; do\n'
|
||||
' ln -sf "$b" /usr/local/bin/"$(basename "$b")" || true\n'
|
||||
'done')
|
||||
out.append('log "Verifying..."')
|
||||
out.append('$PYBIN --version')
|
||||
if pip:
|
||||
out.append('$PYBIN -m pip --version || $PYBIN -m ensurepip --upgrade')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# ========================== Node.js (nvm-style from binary) ==========================
|
||||
class NodeJS(Generator):
|
||||
id = "nodejs"
|
||||
title = "Node.js 任意版本 (官方二进制)"
|
||||
category = "runtimes"
|
||||
icon = "🟢"
|
||||
tags = ["nodejs", "node", "npm"]
|
||||
description = "从 nodejs.org 下载预编译二进制,可选装 yarn / pnpm / nvm。"
|
||||
fields = [
|
||||
Field("version", "Node 版本", "select", default="24",
|
||||
options=["18", "20", "22", "24", "26"]),
|
||||
Field("install_dir", "安装目录", "text", default="/opt/nodejs"),
|
||||
Field("symlink_bin", "软链 bin 目录", "checkbox", default="yes"),
|
||||
Field("install_yarn", "安装 yarn", "checkbox", default="yes"),
|
||||
Field("install_pnpm", "安装 pnpm", "checkbox", default="yes"),
|
||||
Field("npm_registry", "NPM Registry", "text",
|
||||
default="https://registry.npmmirror.com",
|
||||
help="默认走国内镜像,留空 = 官方。"),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
ver = p.get("version", "20")
|
||||
d = p.get("install_dir", "/opt/nodejs")
|
||||
sym = bool_str(p.get("symlink_bin", True))
|
||||
yarn = bool_str(p.get("install_yarn", True))
|
||||
pnpm = bool_str(p.get("install_pnpm", True))
|
||||
reg = p.get("npm_registry", "https://registry.npmmirror.com")
|
||||
|
||||
out = [bash_header(self.title)]
|
||||
out.append('log "Installing Node.js v' + ver + '..."')
|
||||
out.append('cd /tmp')
|
||||
out.append('curl -fsSL -o node.tar.xz https://nodejs.org/dist/v' + ver + '.x/node-v' + ver + '.x-linux-x64.tar.xz')
|
||||
out.append('rm -rf ' + d)
|
||||
out.append('mkdir -p ' + d)
|
||||
out.append('tar -xJf node.tar.xz --strip-components=1 -C ' + d)
|
||||
out.append('rm -f node.tar.xz')
|
||||
if sym:
|
||||
out.append('for b in ' + d + '/bin/*; do ln -sf "$b" /usr/local/bin/"$(basename "$b")"; done')
|
||||
if reg:
|
||||
out.append('npm config set registry ' + quote(reg))
|
||||
if yarn:
|
||||
out.append('npm install -g yarn')
|
||||
if pnpm:
|
||||
out.append('npm install -g pnpm')
|
||||
out.append('node --version')
|
||||
out.append('npm --version')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# ========================== GCC (from source) ==========================
|
||||
class GCC(Generator):
|
||||
id = "gcc"
|
||||
title = "GCC 任意版本 (源码编译)"
|
||||
category = "runtimes"
|
||||
icon = "🛠️"
|
||||
tags = ["gcc", "compiler", "source"]
|
||||
description = "从 gcc.gnu.org 下载源码,编译并安装到 /opt/gcc-<ver>。"
|
||||
warnings = [
|
||||
"GCC 12+ 编译需 30-60 分钟,需要约 5GB 磁盘。",
|
||||
"建议另装 'system gcc' (apt/yum) 留作系统默认,本工具不替换系统 gcc。",
|
||||
]
|
||||
fields = [
|
||||
Field("version", "GCC 版本", "select", default="13.2.0",
|
||||
options=["8.5.0", "9.5.0", "10.5.0", "11.4.0", "12.3.0",
|
||||
"13.2.0", "14.1.0"]),
|
||||
Field("languages", "支持语言", "text", default="c,c++,fortran",
|
||||
help="逗号分隔,如 c,c++,objc,fortran,go"),
|
||||
Field("install_dir", "安装目录", "text", default="/opt/gcc",
|
||||
help="最终 = $install_dir/<ver>"),
|
||||
Field("enable_lto", "启用 LTO (慢但快 ~10%)", "checkbox", default="no"),
|
||||
Field("enable_libsan", "启用 Sanitizers (ASan/TSan)", "checkbox", default="no"),
|
||||
Field("make_jobs", "make -j 任务数", "text", default="$(nproc)",
|
||||
help="默认 = CPU 核数,改小可降低内存峰值。"),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
ver = p.get("version", "13.2.0")
|
||||
langs = p.get("languages", "c,c++,fortran")
|
||||
d = p.get("install_dir", "/opt/gcc")
|
||||
lto = " --enable-lto" if bool_str(p.get("enable_lto")) else ""
|
||||
libsan = " --enable-libsanitizer" if bool_str(p.get("enable_libsan")) else ""
|
||||
jobs = p.get("make_jobs", "$(nproc)")
|
||||
|
||||
out = [bash_header(self.title)]
|
||||
out.append('log "Installing GCC ' + ver + ' from source..."')
|
||||
out.append('$PKG_INSTALL wget gcc gcc-c++ make texinfo bison flex')
|
||||
out.append('cd /tmp')
|
||||
out.append('curl -fsSL -O https://ftp.gnu.org/gnu/gcc/gcc-' + ver + '/gcc-' + ver + '.tar.xz')
|
||||
out.append('tar -xJf gcc-' + ver + '.tar.xz')
|
||||
out.append('cd gcc-' + ver)
|
||||
out.append('./contrib/download_prerequisites')
|
||||
out.append('mkdir -p build && cd build')
|
||||
out.append('../configure --prefix=' + d + '/' + ver +
|
||||
' --enable-languages=' + langs +
|
||||
' --disable-multilib --with-system-zlib' + lto + libsan)
|
||||
out.append('make -j' + jobs)
|
||||
out.append('make install')
|
||||
out.append('cd /tmp && rm -rf gcc-' + ver + ' gcc-' + ver + '.tar.xz build')
|
||||
out.append('log "Verifying..."')
|
||||
out.append(d + '/' + ver + '/bin/gcc --version')
|
||||
out.append('echo "Add to PATH: export PATH=' + d + '/' + ver + '/bin:$PATH"')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# ========================== Make ==========================
|
||||
class Make(Generator):
|
||||
id = "make"
|
||||
title = "GNU Make 任意版本 (源码编译)"
|
||||
category = "runtimes"
|
||||
icon = "🔨"
|
||||
tags = ["make", "build", "gnu"]
|
||||
description = "从 gnu.org 下载 make 源码并安装到 /opt。"
|
||||
fields = [
|
||||
Field("version", "Make 版本", "select", default="4.4.1",
|
||||
options=["4.2.1", "4.3", "4.4", "4.4.1"]),
|
||||
Field("install_dir", "安装目录", "text", default="/opt/make"),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
ver = p.get("version", "4.4.1")
|
||||
d = p.get("install_dir", "/opt/make")
|
||||
out = [bash_header(self.title)]
|
||||
out.append('log "Installing make ' + ver + '..."')
|
||||
out.append('$PKG_INSTALL gcc make')
|
||||
out.append('cd /tmp')
|
||||
out.append('curl -fsSL -O https://ftp.gnu.org/gnu/make/make-' + ver + '.tar.gz')
|
||||
out.append('tar -xzf make-' + ver + '.tar.gz')
|
||||
out.append('cd make-' + ver)
|
||||
out.append('./configure --prefix=' + d + '/' + ver)
|
||||
out.append('make -j"$(nproc)"')
|
||||
out.append('make install')
|
||||
out.append('cd /tmp && rm -rf make-' + ver + ' make-' + ver + '.tar.gz')
|
||||
out.append(d + '/' + ver + '/bin/make --version')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# ========================== CMake ==========================
|
||||
class CMake(Generator):
|
||||
id = "cmake"
|
||||
title = "CMake 任意版本 (官方脚本安装)"
|
||||
category = "runtimes"
|
||||
icon = "🧱"
|
||||
tags = ["cmake", "build", "makefile"]
|
||||
description = "从 cmake.org 下载官方二进制,安装到 /opt。"
|
||||
fields = [
|
||||
Field("version", "CMake 版本", "select", default="3.30.0",
|
||||
options=["3.20.0", "3.22.0", "3.24.0", "3.25.0", "3.26.0",
|
||||
"3.27.0", "3.28.0", "3.29.0", "3.30.0"]),
|
||||
Field("install_dir", "安装目录", "text", default="/opt/cmake"),
|
||||
Field("symlink_bin", "软链到 /usr/local/bin", "checkbox", default="yes"),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
ver = p.get("version", "3.30.0")
|
||||
d = p.get("install_dir", "/opt/cmake")
|
||||
sym = bool_str(p.get("symlink_bin", True))
|
||||
out = [bash_header(self.title)]
|
||||
out.append('log "Installing CMake ' + ver + '..."')
|
||||
out.append('cd /tmp')
|
||||
out.append('curl -fsSL -O https://github.com/Kitware/CMake/releases/download/v' + ver + '/cmake-' + ver + '-linux-x86_64.tar.gz')
|
||||
out.append('rm -rf ' + d)
|
||||
out.append('mkdir -p ' + d)
|
||||
out.append('tar -xzf cmake-' + ver + '-linux-x86_64.tar.gz --strip-components=1 -C ' + d)
|
||||
out.append('rm -f cmake-' + ver + '-linux-x86_64.tar.gz')
|
||||
if sym:
|
||||
out.append('for b in ' + d + '/bin/*; do ln -sf "$b" /usr/local/bin/"$(basename "$b")"; done')
|
||||
out.append('cmake --version')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# ========================== Go ==========================
|
||||
class GoLang(Generator):
|
||||
id = "golang"
|
||||
title = "Go 任意版本 (官方二进制)"
|
||||
category = "runtimes"
|
||||
icon = "🐹"
|
||||
tags = ["go", "golang"]
|
||||
description = "从 go.dev 下载 Go 官方二进制,设置 GOPROXY 国内镜像。"
|
||||
fields = [
|
||||
Field("version", "Go 版本", "select", default="1.22.5",
|
||||
options=["1.18.10", "1.19.13", "1.20.14", "1.21.12", "1.22.5", "1.23.0"]),
|
||||
Field("install_dir", "安装目录", "text", default="/opt/go"),
|
||||
Field("goproxy", "GOPROXY", "text", default="https://goproxy.cn,direct",
|
||||
help="默认 goproxy.cn,留空 = GOPROXY=off"),
|
||||
Field("symlink_bin", "软链到 /usr/local/bin", "checkbox", default="yes"),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
ver = p.get("version", "1.22.5")
|
||||
d = p.get("install_dir", "/opt/go")
|
||||
proxy = p.get("goproxy", "https://goproxy.cn,direct")
|
||||
sym = bool_str(p.get("symlink_bin", True))
|
||||
|
||||
out = [bash_header(self.title)]
|
||||
out.append('log "Installing Go ' + ver + '..."')
|
||||
out.append('cd /tmp')
|
||||
out.append('curl -fsSL -O https://go.dev/dl/go' + ver + '.linux-amd64.tar.gz')
|
||||
out.append('rm -rf ' + d)
|
||||
out.append('tar -C ' + d.rsplit('/', 1)[0] + ' -xzf go' + ver + '.linux-amd64.tar.gz')
|
||||
out.append('mv ' + d.rsplit('/', 1)[0] + '/go ' + d)
|
||||
out.append('rm -f go' + ver + '.linux-amd64.tar.gz')
|
||||
if sym:
|
||||
out.append('for b in ' + d + '/bin/*; do ln -sf "$b" /usr/local/bin/"$(basename "$b")"; done')
|
||||
if proxy:
|
||||
out.append('go env -w GOPROXY=' + quote(proxy))
|
||||
out.append('go env -w GOSUMDB=sum.golang.google.cn')
|
||||
out.append('go version')
|
||||
out.append('echo "Add to PATH: export PATH=' + d + '/bin:$PATH"')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# ========================== OpenJDK ==========================
|
||||
class Java(Generator):
|
||||
id = "java"
|
||||
title = "OpenJDK 任意版本 (官方二进制)"
|
||||
category = "runtimes"
|
||||
icon = "☕"
|
||||
tags = ["java", "jdk", "openjdk"]
|
||||
description = "从 Adoptium 仓库下载 OpenJDK 预编译版本,无需 apt 仓库。"
|
||||
fields = [
|
||||
Field("version", "JDK 版本", "select", default="21",
|
||||
options=["8", "11", "17", "21"]),
|
||||
Field("install_dir", "安装目录", "text", default="/opt/jdk"),
|
||||
Field("symlink_bin", "软链到 /usr/local/bin", "checkbox", default="yes"),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
ver = p.get("version", "21")
|
||||
d = p.get("install_dir", "/opt/jdk")
|
||||
sym = bool_str(p.get("symlink_bin", True))
|
||||
out = [bash_header(self.title)]
|
||||
out.append('log "Installing OpenJDK ' + ver + '..."')
|
||||
out.append('cd /tmp')
|
||||
out.append('curl -fsSL -o jdk.tar.gz "https://download.java.net/java/GA/jdk' + ver + '.0.2/f2283984656d49d69e91c558476027ac/13/GPL/openjdk-' + ver + '.0.2_linux-x64_bin.tar.gz"')
|
||||
out.append('rm -rf ' + d)
|
||||
out.append('mkdir -p ' + d)
|
||||
out.append('tar -xzf jdk.tar.gz --strip-components=1 -C ' + d)
|
||||
out.append('rm -f jdk.tar.gz')
|
||||
if sym:
|
||||
out.append('update-alternatives --install /usr/bin/java java ' + d + '/bin/java 9999 || true')
|
||||
out.append('update-alternatives --install /usr/bin/javac javac ' + d + '/bin/javac 9999 || true')
|
||||
out.append('for b in ' + d + '/bin/*; do ln -sf "$b" /usr/local/bin/"$(basename "$b")"; done')
|
||||
out.append('export JAVA_HOME=' + d)
|
||||
out.append(d + '/bin/java --version')
|
||||
out.append('echo "Add to PATH: export PATH=' + d + '/bin:$PATH"')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# ========================== PHP ==========================
|
||||
class PHP(Generator):
|
||||
id = "php"
|
||||
title = "PHP 任意版本 (源码编译)"
|
||||
category = "runtimes"
|
||||
icon = "🐘"
|
||||
tags = ["php", "apache", "nginx", "fpm"]
|
||||
description = "从 php.net 编译 PHP,启用 fpm、常用扩展。"
|
||||
fields = [
|
||||
Field("version", "PHP 版本", "select", default="8.3.10",
|
||||
options=["7.4.33", "8.0.30", "8.1.29", "8.2.22", "8.3.10"]),
|
||||
Field("install_dir", "安装目录", "text", default="/opt/php"),
|
||||
Field("enable_fpm", "启用 PHP-FPM", "checkbox", default="yes"),
|
||||
Field("extensions", "编译扩展", "text",
|
||||
default="mysqli,pdo,pdo_mysql,gd,mbstring,curl,xml,zip,intl,opcache,bcmath"),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
ver = p.get("version", "8.3.10")
|
||||
d = p.get("install_dir", "/opt/php")
|
||||
fpm = bool_str(p.get("enable_fpm", True))
|
||||
exts = p.get("extensions", "mysqli,pdo,pdo_mysql,gd,mbstring,curl,xml,zip,intl,opcache,bcmath")
|
||||
out = [bash_header(self.title)]
|
||||
out.append('log "Installing PHP ' + ver + ' from source..."')
|
||||
out.append('$PKG_INSTALL gcc make libxml2-dev libssl-dev libcurl4-openssl-dev \\\n'
|
||||
' libonig-dev libzip-dev libicu-dev libsqlite3-dev libpng-dev libjpeg-dev \\\n'
|
||||
' libfreetype6-dev libwebp-dev libxslt1-dev libreadline-dev 2>/dev/null \\\n'
|
||||
' || $PKG_INSTALL gcc make libxml2-devel openssl-devel libcurl-devel \\\n'
|
||||
' libonig-devel libzip-devel libicu-devel sqlite-devel libpng-devel \\\n'
|
||||
' libjpeg-devel freetype-devel libwebp-devel libxslt-devel readline-devel')
|
||||
out.append('cd /tmp')
|
||||
out.append('curl -fsSL -O https://www.php.net/distributions/php-' + ver + '.tar.gz')
|
||||
out.append('tar -xzf php-' + ver + '.tar.gz')
|
||||
out.append('cd php-' + ver)
|
||||
cfg = './configure --prefix=' + d + '/' + ver
|
||||
cfg += ' --with-config-file-path=' + d + '/' + ver + '/etc'
|
||||
cfg += ' --with-config-file-scan-dir=' + d + '/' + ver + '/etc/php.d'
|
||||
cfg += ' --enable-mbstring --enable-fpm' if fpm else ''
|
||||
cfg += ' --with-curl --with-openssl --with-zip --with-zlib'
|
||||
cfg += ' --with-pdo-mysql --with-mysqli'
|
||||
cfg += ' --enable-opcache'
|
||||
cfg += ' --enable-bcmath --enable-intl --enable-pcntl --enable-sockets'
|
||||
for e in exts.split(','):
|
||||
e = e.strip()
|
||||
if not e:
|
||||
continue
|
||||
if e in ("mysqli", "pdo_mysql", "pdo", "gd", "mbstring", "curl", "xml",
|
||||
"zip", "intl", "opcache", "bcmath", "fpm"):
|
||||
continue
|
||||
out.append(cfg)
|
||||
out.append('make -j"$(nproc)"')
|
||||
out.append('make install')
|
||||
out.append('mkdir -p ' + d + '/' + ver + '/etc/php.d')
|
||||
out.append('cp php.ini-production ' + d + '/' + ver + '/etc/php.ini')
|
||||
out.append('cp sapi/fpm/php-fpm.conf ' + d + '/' + ver + '/etc/ || true')
|
||||
out.append('cp sapi/fpm/www.conf.default ' + d + '/' + ver + '/etc/www.conf || true')
|
||||
out.append('cd /tmp && rm -rf php-' + ver + ' php-' + ver + '.tar.gz')
|
||||
out.append(d + '/' + ver + '/bin/php --version')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# ========================== Ruby ==========================
|
||||
class Ruby(Generator):
|
||||
id = "ruby"
|
||||
title = "Ruby 任意版本 (源码编译)"
|
||||
category = "runtimes"
|
||||
icon = "💎"
|
||||
tags = ["ruby", "rails"]
|
||||
description = "从 cache.ruby-lang.org 编译 Ruby,启用 readline/openssl/zlib。"
|
||||
fields = [
|
||||
Field("version", "Ruby 版本", "select", default="3.3.3",
|
||||
options=["2.7.8", "3.0.7", "3.1.6", "3.2.4", "3.3.3"]),
|
||||
Field("install_dir", "安装目录", "text", default="/opt/ruby"),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
ver = p.get("version", "3.3.3")
|
||||
d = p.get("install_dir", "/opt/ruby")
|
||||
out = [bash_header(self.title)]
|
||||
out.append('log "Installing Ruby ' + ver + '..."')
|
||||
out.append('$PKG_INSTALL gcc make build-essential libssl-dev libreadline-dev zlib1g-dev \\\n'
|
||||
' libyaml-dev libgmp-dev libffi-dev libgdbm-dev libdb-dev libncurses5-dev 2>/dev/null \\\n'
|
||||
' || $PKG_INSTALL gcc make openssl-devel readline-devel zlib-devel \\\n'
|
||||
' libyaml-devel gmp-devel libffi-devel gdbm-devel ncurses-devel')
|
||||
out.append('cd /tmp')
|
||||
out.append('curl -fsSL -O https://cache.ruby-lang.org/pub/ruby/' + ver.split('.')[0] + '.' + ver.split('.')[1] + '/ruby-' + ver + '.tar.gz')
|
||||
out.append('tar -xzf ruby-' + ver + '.tar.gz')
|
||||
out.append('cd ruby-' + ver)
|
||||
out.append('./configure --prefix=' + d + '/' + ver + ' --enable-shared --disable-install-doc')
|
||||
out.append('make -j"$(nproc)"')
|
||||
out.append('make install')
|
||||
out.append('cd /tmp && rm -rf ruby-' + ver + ' ruby-' + ver + '.tar.gz')
|
||||
out.append(d + '/' + ver + '/bin/ruby --version')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# ========================== Rust ==========================
|
||||
class RustLang(Generator):
|
||||
id = "rust"
|
||||
title = "Rust 工具链 (rustup)"
|
||||
category = "runtimes"
|
||||
icon = "🦀"
|
||||
tags = ["rust", "rustup", "cargo"]
|
||||
description = "通过 rustup 安装 Rust,可指定 nightly / stable 及 toolchain 路径。"
|
||||
fields = [
|
||||
Field("channel", "Channel", "select", default="stable",
|
||||
options=["stable", "beta", "nightly"]),
|
||||
Field("install_dir", "安装目录", "text", default="/opt/rust"),
|
||||
Field("default_toolchain", "默认 toolchain", "text", default="stable"),
|
||||
Field("install_components", "组件列表", "text",
|
||||
default="rustfmt,clippy,rust-src,rust-analyzer"),
|
||||
Field("mirror", "rsproxy 国内镜像", "checkbox", default="yes",
|
||||
help="启用后会写 RUSTUP_DIST_SERVER 环境变量。"),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
ch = p.get("channel", "stable")
|
||||
d = p.get("install_dir", "/opt/rust")
|
||||
tool = p.get("default_toolchain", "stable")
|
||||
comps = p.get("install_components", "rustfmt,clippy,rust-src,rust-analyzer")
|
||||
mirror = bool_str(p.get("mirror", True))
|
||||
|
||||
out = [bash_header(self.title)]
|
||||
out.append('log "Installing rustup + ' + ch + '..."')
|
||||
out.append('command -v gcc >/dev/null || $PKG_INSTALL gcc make build-essential \\\n'
|
||||
' || $PKG_INSTALL gcc make')
|
||||
if mirror:
|
||||
out.append('export RUSTUP_DIST_SERVER=https://rsproxy.cn')
|
||||
out.append('export RUSTUP_UPDATE_ROOT=https://rsproxy.cn/rustup')
|
||||
out.append('curl --proto =https --tlsv1.2 -sSf https://sh.rustup.rs | '
|
||||
'sh -s -- -y --default-toolchain none --no-modify-path --prefix=' + d)
|
||||
out.append('. ' + d + '/env')
|
||||
out.append('rustup default ' + tool)
|
||||
out.append('rustup toolchain install ' + ch)
|
||||
for c in comps.split(','):
|
||||
c = c.strip()
|
||||
if c:
|
||||
out.append('rustup component add ' + c + ' --toolchain ' + ch)
|
||||
out.append('rustc --version')
|
||||
out.append('cargo --version')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# ========================== PostgreSQL ==========================
|
||||
class PostgreSQL(Generator):
|
||||
id = "postgresql"
|
||||
title = "PostgreSQL 任意版本 (官方 apt/yum 源)"
|
||||
category = "databases"
|
||||
icon = "🐘"
|
||||
tags = ["postgresql", "pg", "database"]
|
||||
description = "从 PostgreSQL Global Development Group 官方源安装任意版本。"
|
||||
fields = [
|
||||
Field("version", "PG 版本", "select", default="16",
|
||||
options=["12", "13", "14", "15", "16"]),
|
||||
Field("port", "端口", "number", default="5432", min_=1, max_=65535),
|
||||
Field("listen_addresses", "listen_addresses", "text", default="*"),
|
||||
Field("admin_user", "超级用户", "text", default="postgres"),
|
||||
Field("admin_password", "超级用户密码", "password", default="changeme"),
|
||||
Field("data_dir", "数据目录", "text", default="/var/lib/pgsql/data"),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
ver = p.get("version", "16")
|
||||
port = str(p.get("port", "5432"))
|
||||
listen = p.get("listen_addresses", "*")
|
||||
user = p.get("admin_user", "postgres")
|
||||
pwd = p.get("admin_password", "changeme")
|
||||
out = [bash_header(self.title)]
|
||||
out.append('log "Installing PostgreSQL ' + ver + '..."')
|
||||
out.append('case "$PKG" in\n'
|
||||
' apt-get)\n'
|
||||
' $PKG_INSTALL wget gnupg lsb-release ca-certificates\n'
|
||||
' curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /usr/share/keyrings/pgdg.gpg\n'
|
||||
' echo "deb [signed-by=/usr.share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list\n'
|
||||
' apt-get update\n'
|
||||
' $PKG_INSTALL postgresql-' + ver + ' postgresql-client-' + ver + ' postgresql-contrib-' + ver + '\n'
|
||||
' ;;\n'
|
||||
' yum|dnf)\n'
|
||||
' $PKG_INSTALL https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm\n'
|
||||
' $PKG_INSTALL postgresql' + ver + '-server postgresql' + ver + '-contrib\n'
|
||||
' /usr/pgsql-' + ver + '/bin/postgresql-' + ver + '-setup initdb\n'
|
||||
' ;;\n'
|
||||
'esac')
|
||||
out.append('systemctl enable --now postgresql')
|
||||
out.append('sleep 2')
|
||||
out.append('sudo -u postgres psql -c "ALTER USER ' + user + ' WITH PASSWORD \'' + pwd + '\';"')
|
||||
out.append('echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/' + ver + '/main/pg_hba.conf 2>/dev/null || true')
|
||||
out.append('echo "host all all 0.0.0.0/0 md5" >> /var/lib/pgsql/data/pg_hba.conf 2>/dev/null || true')
|
||||
out.append('sed -i "s/^#listen_addresses.*/listen_addresses = \'' + listen + '\'/" /etc/postgresql/' + ver + '/main/postgresql.conf 2>/dev/null || true')
|
||||
out.append('sed -i "s/^#listen_addresses.*/listen_addresses = \'' + listen + '\'/" /var/lib/pgsql/data/postgresql.conf 2>/dev/null || true')
|
||||
out.append('sed -i "s/^port.*/port = ' + port + '/" /etc/postgresql/' + ver + '/main/postgresql.conf 2>/dev/null || true')
|
||||
out.append('sed -i "s/^port.*/port = ' + port + '/" /var/lib/pgsql/data/postgresql.conf 2>/dev/null || true')
|
||||
out.append('systemctl restart postgresql')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# ========================== MySQL ==========================
|
||||
class MySQL(Generator):
|
||||
id = "mysql"
|
||||
title = "MySQL / MariaDB 任意版本"
|
||||
category = "databases"
|
||||
icon = "🐬"
|
||||
tags = ["mysql", "mariadb", "database"]
|
||||
description = "支持 MySQL 5.7/8.0/8.4 或 MariaDB 10.x,自动初始化 root 密码。"
|
||||
fields = [
|
||||
Field("variant", "变体", "select", default="mysql",
|
||||
options=["mysql", "mariadb"]),
|
||||
Field("version", "版本", "select", default="8.0",
|
||||
options=["5.7", "8.0", "8.4", "10.11", "11.4"]),
|
||||
Field("port", "端口", "number", default="3306", min_=1, max_=65535),
|
||||
Field("root_password", "root 密码", "password", default="changeme"),
|
||||
Field("bind_address", "Bind 地址", "text", default="0.0.0.0"),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
var = p.get("variant", "mysql")
|
||||
ver = p.get("version", "8.0")
|
||||
port = str(p.get("port", "3306"))
|
||||
pwd = p.get("root_password", "changeme")
|
||||
bind = p.get("bind_address", "0.0.0.0")
|
||||
out = [bash_header(self.title)]
|
||||
if var == "mysql":
|
||||
out.append('log "Installing MySQL ' + ver + '..."')
|
||||
out.append('case "$PKG" in\n'
|
||||
' apt-get)\n'
|
||||
' $PKG_INSTALL wget gnupg lsb-release\n'
|
||||
' wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb\n'
|
||||
' DEBIAN_FRONTEND=noninteractive dpkg -i mysql-apt-config_0.8.29-1_all.deb || true\n'
|
||||
' apt-get update\n'
|
||||
' $PKG_INSTALL mysql-server mysql-client\n'
|
||||
' ;;\n'
|
||||
' yum|dnf)\n'
|
||||
' $PKG_INSTALL https://dev.mysql.com/get/mysql80-community-release-el$(rpm -E %{rhel})-1.noarch.rpm\n'
|
||||
' $PKG_INSTALL mysql-community-server mysql-community-client\n'
|
||||
' ;;\n'
|
||||
'esac')
|
||||
else:
|
||||
out.append('log "Installing MariaDB ' + ver + '..."')
|
||||
out.append('case "$PKG" in\n'
|
||||
' apt-get) $PKG_INSTALL mariadb-server mariadb-client ;;\n'
|
||||
' yum|dnf) $PKG_INSTALL mariadb-server mariadb ;;\n'
|
||||
'esac')
|
||||
out.append('systemctl enable --now mysql || systemctl enable --now mariadb')
|
||||
out.append('sleep 3')
|
||||
out.append('mysql -u root -e "ALTER USER \'root\'@\'localhost\' IDENTIFIED BY \'' + pwd + '\';" 2>/dev/null || true')
|
||||
out.append('mysql -u root -p\'' + pwd + '\' -e "CREATE USER IF NOT EXISTS \'root\'@\'%\' IDENTIFIED BY \'' + pwd + '\';" 2>/dev/null || true')
|
||||
out.append('mysql -u root -p\'' + pwd + '\' -e "GRANT ALL ON *.* TO \'root\'@\'%\' WITH GRANT OPTION; FLUSH PRIVILEGES;" 2>/dev/null || true')
|
||||
out.append('sed -i "s/^bind-address.*/bind-address = ' + bind + '/" /etc/mysql/mysql.conf.d/mysqld.cnf 2>/dev/null || true')
|
||||
out.append('sed -i "s/^bind-address.*/bind-address = ' + bind + '/" /etc/my.cnf 2>/dev/null || true')
|
||||
out.append('sed -i "s/^port.*/port = ' + port + '/" /etc/mysql/mysql.conf.d/mysqld.cnf 2>/dev/null || true')
|
||||
out.append('sed -i "s/^port.*/port = ' + port + '/" /etc/my.cnf 2>/dev/null || true')
|
||||
out.append('systemctl restart mysql || systemctl restart mariadb')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# ========================== MongoDB ==========================
|
||||
class MongoDB(Generator):
|
||||
id = "mongodb"
|
||||
title = "MongoDB 任意版本"
|
||||
category = "databases"
|
||||
icon = "🍃"
|
||||
tags = ["mongodb", "nosql", "document"]
|
||||
description = "从 MongoDB 官方源安装任意版本,启用 replica set。"
|
||||
fields = [
|
||||
Field("version", "MongoDB 版本", "select", default="7.0",
|
||||
options=["4.4", "5.0", "6.0", "7.0"]),
|
||||
Field("port", "端口", "number", default="27017", min_=1, max_=65535),
|
||||
Field("bind_ip", "Bind IP", "text", default="0.0.0.0"),
|
||||
Field("enable_auth", "启用鉴权", "checkbox", default="yes"),
|
||||
Field("root_user", "root 用户", "text", default="root"),
|
||||
Field("root_password", "root 密码", "password", default="changeme"),
|
||||
Field("repl_set", "副本集名 (留空=单点)", "text", default=""),
|
||||
]
|
||||
|
||||
def render(self, p):
|
||||
ver = p.get("version", "7.0")
|
||||
port = str(p.get("port", "27017"))
|
||||
bind = p.get("bind_ip", "0.0.0.0")
|
||||
auth = bool_str(p.get("enable_auth", True))
|
||||
user = p.get("root_user", "root")
|
||||
pwd = p.get("root_password", "changeme")
|
||||
repl = p.get("repl_set", "").strip()
|
||||
out = [bash_header(self.title)]
|
||||
out.append('log "Installing MongoDB ' + ver + '..."')
|
||||
out.append('case "$PKG" in\n'
|
||||
' apt-get)\n'
|
||||
' $PKG_INSTALL wget gnupg curl\n'
|
||||
' curl -fsSL https://www.mongodb.org/static/pgp/server-' + ver + '.asc | gpg --dearmor -o /usr/share/keyrings/mongodb.gpg\n'
|
||||
' echo "deb [signed-by=/usr/share/keyrings/mongodb.gpg] https://repo.mongodb.org/apt/ubuntu $(grep VERSION_CODENAME /etc/os-release | cut -d= -f2)/mongodb-org/' + ver + ' multiverse" > /etc/apt/sources.list.d/mongodb-org-' + ver + '.list\n'
|
||||
' apt-get update\n'
|
||||
' $PKG_INSTALL mongodb-org\n'
|
||||
' ;;\n'
|
||||
' yum|dnf)\n'
|
||||
' cat > /etc/yum.repos.d/mongodb-org-' + ver + '.repo <<REPO_EOF\n'
|
||||
'[mongodb-org-' + ver + ']\n'
|
||||
'name=MongoDB Repository\n'
|
||||
'baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/' + ver + '/x86_64/\n'
|
||||
'gpgcheck=1\n'
|
||||
'enabled=1\n'
|
||||
'gpgkey=https://www.mongodb.org/static/pgp/server-' + ver + '.asc\n'
|
||||
'REPO_EOF\n'
|
||||
' $PKG_INSTALL mongodb-org\n'
|
||||
' ;;\n'
|
||||
'esac')
|
||||
out.append('systemctl enable --now mongod')
|
||||
out.append('sleep 3')
|
||||
if auth:
|
||||
out.append('log "Creating root user..."')
|
||||
out.append('mongosh --quiet --eval \''
|
||||
'db.getSiblingDB("admin").createUser({user:"' + user + '",pwd:"' + pwd + '",roles:[{role:"root",db:"admin"}]})\' || warn "auth may already be set"')
|
||||
out.append('sed -i "s/^#security:/{ security: { authorization: \"enabled\" },/g" /etc/mongod.conf')
|
||||
out.append('sed -i "s/^ bindIp:.*/ bindIp: ' + bind + '/" /etc/mongod.conf')
|
||||
out.append('grep -q "^ port:" /etc/mongod.conf || sed -i "/^ bindIp:/a\\ port: ' + port + '" /etc/mongod.conf')
|
||||
if repl:
|
||||
out.append('grep -q "^replication:" /etc/mongod.conf || cat >> /etc/mongod.conf <<EOF\\nreplication:\\n replSetName: ' + repl + '\\nEOF')
|
||||
out.append('systemctl restart mongod')
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
# Register all
|
||||
for _g in [Python, NodeJS, GCC, Make, CMake, GoLang, Java, PHP, Ruby, RustLang,
|
||||
PostgreSQL, MySQL, MongoDB]:
|
||||
register(_g())
|
||||
Reference in New Issue
Block a user