mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-29 00:51:53 +08:00
feat: 完善 GitHub Actions 工作流和构建系统
- 添加自动下载缺失文件功能,支持 .xz 压缩格式 - 优化构建流程,增加文件清理和压缩功能 - 修复发布资产上传步骤,确保预发布标记正确设置 - 调整发布标签格式,包含版本号、设备目标和运行 ID - 升级 Actions 版本,使用 softprops/action-gh-release@v1 - 移除 NFS 挂载依赖,简化部署流程 - 增强错误处理和日志输出
This commit is contained in:
@@ -2,12 +2,15 @@
|
||||
|
||||
# --- 配置 ---
|
||||
# 允许通过环境变量覆盖默认路径
|
||||
SRCPATH="${SRCPATH:-/mnt/nfs/lfs/src}"
|
||||
SRCPATH="${SRCPATH:-/mnt/src}"
|
||||
BOOTFS="${BOOTFS:-/tmp/bootfs}"
|
||||
ROOTFS="${ROOTFS:-/tmp/rootfs}"
|
||||
OUTPUTDIR="${OUTPUTDIR:-/mnt/nfs/lfs/src/output}"
|
||||
OUTPUTDIR="${OUTPUTDIR:-/mnt/output}"
|
||||
TMPDIR="${TMPDIR:-$SRCPATH/tmp}"
|
||||
|
||||
# 远程文件下载配置
|
||||
REMOTE_PREFIX="${REMOTE_PREFIX:-https://files.mofeng.run/src}"
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
# 全局变量
|
||||
@@ -132,6 +135,9 @@ build_target() {
|
||||
;;
|
||||
esac
|
||||
|
||||
# 在 GitHub Actions 环境中清理下载的文件
|
||||
cleanup_downloaded_files
|
||||
|
||||
echo "=================================================="
|
||||
echo "信息:目标 $target 构建完成!"
|
||||
echo "=================================================="
|
||||
|
||||
@@ -172,9 +172,96 @@ write_meta() {
|
||||
run_in_chroot "sed -i 's/localhost.localdomain/$hostname/g' /etc/kvmd/meta.yaml"
|
||||
}
|
||||
|
||||
# 检测是否在 GitHub Actions 环境中
|
||||
is_github_actions() {
|
||||
[[ -n "$GITHUB_ACTIONS" ]]
|
||||
}
|
||||
|
||||
# 记录下载的文件列表(仅在 GitHub Actions 环境中)
|
||||
DOWNLOADED_FILES_LIST="/tmp/downloaded_files.txt"
|
||||
|
||||
# 自动下载文件函数
|
||||
download_file_if_missing() {
|
||||
local file_path="$1"
|
||||
local relative_path=""
|
||||
|
||||
# 如果文件已存在,直接返回
|
||||
if [[ -f "$file_path" ]]; then
|
||||
echo "信息:文件已存在: $file_path"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 计算相对于 SRCPATH 的路径
|
||||
if [[ "$file_path" == "$SRCPATH"/* ]]; then
|
||||
relative_path="${file_path#$SRCPATH/}"
|
||||
else
|
||||
echo "错误:文件路径 $file_path 不在 SRCPATH ($SRCPATH) 下" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "信息:文件不存在,尝试下载: $file_path"
|
||||
echo "信息:相对路径: $relative_path"
|
||||
|
||||
# 确保目标目录存在
|
||||
local target_dir="$(dirname "$file_path")"
|
||||
ensure_dir "$target_dir"
|
||||
|
||||
# 首先尝试直接下载
|
||||
local remote_url="${REMOTE_PREFIX}/${relative_path}"
|
||||
echo "信息:尝试下载: $remote_url"
|
||||
|
||||
if curl -f -L -o "$file_path" "$remote_url" 2>/dev/null; then
|
||||
echo "信息:下载成功: $file_path"
|
||||
# 在 GitHub Actions 环境中记录下载的文件
|
||||
if is_github_actions; then
|
||||
echo "$file_path" >> "$DOWNLOADED_FILES_LIST"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 如果直接下载失败,尝试添加 .xz 后缀
|
||||
echo "信息:直接下载失败,尝试 .xz 压缩版本..."
|
||||
local xz_url="${remote_url}.xz"
|
||||
local xz_file="${file_path}.xz"
|
||||
|
||||
if curl -f -L -o "$xz_file" "$xz_url" 2>/dev/null; then
|
||||
echo "信息:下载 .xz 文件成功,正在解压..."
|
||||
if xz -d "$xz_file"; then
|
||||
echo "信息:解压成功: $file_path"
|
||||
# 在 GitHub Actions 环境中记录下载的文件
|
||||
if is_github_actions; then
|
||||
echo "$file_path" >> "$DOWNLOADED_FILES_LIST"
|
||||
fi
|
||||
return 0
|
||||
else
|
||||
echo "错误:解压 .xz 文件失败" >&2
|
||||
rm -f "$xz_file"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "错误:无法下载文件 $file_path (尝试了原始版本和 .xz 版本)" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# 清理下载的文件(仅在 GitHub Actions 环境中)
|
||||
cleanup_downloaded_files() {
|
||||
if is_github_actions && [[ -f "$DOWNLOADED_FILES_LIST" ]]; then
|
||||
echo "信息:清理 GitHub Actions 环境中下载的文件..."
|
||||
while IFS= read -r file_path; do
|
||||
if [[ -f "$file_path" ]]; then
|
||||
echo "信息:删除下载的文件: $file_path"
|
||||
rm -f "$file_path"
|
||||
fi
|
||||
done < "$DOWNLOADED_FILES_LIST"
|
||||
rm -f "$DOWNLOADED_FILES_LIST"
|
||||
echo "信息:下载文件清理完成"
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查必要的外部工具
|
||||
check_required_tools() {
|
||||
local required_tools="sudo docker losetup mount umount parted e2fsck resize2fs qemu-img curl tar python3 pip3 rsync git simg2img img2simg dd cat rm mkdir mv cp sed chmod chown ln grep printf id"
|
||||
local required_tools="sudo docker losetup mount umount parted e2fsck resize2fs qemu-img curl tar python3 pip3 rsync git simg2img img2simg dd cat rm mkdir mv cp sed chmod chown ln grep printf id xz"
|
||||
|
||||
for cmd in $required_tools; do
|
||||
if ! command -v "$cmd" &> /dev/null; then
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
onecloud_rootfs() {
|
||||
local unpacker="$SRCPATH/image/onecloud/AmlImg_v0.3.1_linux_amd64"
|
||||
local source_image="$SRCPATH/image/onecloud/Armbian_by-SilentWind_24.5.0-trunk_Onecloud_bookworm_legacy_5.9.0-rc7_minimal.burn.img"
|
||||
local source_image="$SRCPATH/image/onecloud/Armbian_by-SilentWind_24.5.0-trunk_Onecloud_bookworm_legacy_5.9.0-rc7_minimal_support-dvd-emulation.burn.img"
|
||||
local bootfs_img="$TMPDIR/bootfs.img"
|
||||
local rootfs_img="$TMPDIR/rootfs.img"
|
||||
local bootfs_sparse="$TMPDIR/6.boot.PARTITION.sparse"
|
||||
@@ -16,6 +16,13 @@ onecloud_rootfs() {
|
||||
ensure_dir "$TMPDIR"
|
||||
ensure_dir "$BOOTFS"
|
||||
|
||||
# 自动下载 AmlImg 工具(如果不存在)
|
||||
download_file_if_missing "$unpacker" || { echo "错误:下载 AmlImg 工具失败" >&2; exit 1; }
|
||||
sudo chmod +x "$unpacker" || { echo "错误:设置 AmlImg 工具执行权限失败" >&2; exit 1; }
|
||||
|
||||
# 自动下载源镜像文件(如果不存在)
|
||||
download_file_if_missing "$source_image" || { echo "错误:下载 Onecloud 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:解包 Onecloud burn 镜像..."
|
||||
sudo "$unpacker" unpack "$source_image" "$TMPDIR" || { echo "错误:解包失败" >&2; exit 1; }
|
||||
|
||||
@@ -30,7 +37,12 @@ onecloud_rootfs() {
|
||||
sudo losetup "$bootfs_loopdev" "$bootfs_img" || { echo "错误:关联 bootfs 镜像到 $bootfs_loopdev 失败" >&2; exit 1; }
|
||||
sudo mount "$bootfs_loopdev" "$BOOTFS" || { echo "错误:挂载 bootfs ($bootfs_loopdev) 失败" >&2; exit 1; }
|
||||
BOOTFS_MOUNTED=1
|
||||
sudo cp "$SRCPATH/image/onecloud/meson8b-onecloud-fix.dtb" "$BOOTFS/dtb/meson8b-onecloud.dtb" || { echo "错误:复制修复后的 DTB 文件失败" >&2; exit 1; }
|
||||
|
||||
# 自动下载 DTB 文件(如果不存在)
|
||||
local dtb_file="$SRCPATH/image/onecloud/meson8b-onecloud-fix.dtb"
|
||||
download_file_if_missing "$dtb_file" || { echo "错误:下载 Onecloud DTB 文件失败" >&2; exit 1; }
|
||||
|
||||
sudo cp "$dtb_file" "$BOOTFS/dtb/meson8b-onecloud.dtb" || { echo "错误:复制修复后的 DTB 文件失败" >&2; exit 1; }
|
||||
sudo umount "$BOOTFS" || { echo "警告:卸载 bootfs ($BOOTFS) 失败" >&2; BOOTFS_MOUNTED=0; } # 卸载失败不应中断流程
|
||||
BOOTFS_MOUNTED=0
|
||||
echo "信息:分离 bootfs loop 设备 $bootfs_loopdev..."
|
||||
@@ -60,6 +72,10 @@ cumebox2_rootfs() {
|
||||
|
||||
echo "信息:准备 Cumebox2 Rootfs..."
|
||||
ensure_dir "$TMPDIR"
|
||||
|
||||
# 自动下载源镜像文件(如果不存在)
|
||||
download_file_if_missing "$source_image" || { echo "错误:下载 Cumebox2 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
cp "$source_image" "$target_image" || { echo "错误:复制 Cumebox2 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:调整镜像分区大小..."
|
||||
@@ -86,6 +102,10 @@ chainedbox_rootfs_and_fix_dtb() {
|
||||
|
||||
echo "信息:准备 Chainedbox Rootfs 并修复 DTB..."
|
||||
ensure_dir "$TMPDIR"; ensure_dir "$BOOTFS"
|
||||
|
||||
# 自动下载源镜像文件(如果不存在)
|
||||
download_file_if_missing "$source_image" || { echo "错误:下载 Chainedbox 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
cp "$source_image" "$target_image" || { echo "错误:复制 Chainedbox 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:挂载 boot 分区并修复 DTB..."
|
||||
@@ -95,7 +115,12 @@ chainedbox_rootfs_and_fix_dtb() {
|
||||
sudo losetup --offset "$boot_offset" "$bootfs_loopdev" "$target_image" || { echo "错误:设置 boot 分区 loop 设备 $bootfs_loopdev 失败" >&2; exit 1; }
|
||||
sudo mount "$bootfs_loopdev" "$BOOTFS" || { echo "错误:挂载 boot 分区 ($bootfs_loopdev) 失败" >&2; exit 1; }
|
||||
BOOTFS_MOUNTED=1
|
||||
sudo cp "$SRCPATH/image/chainedbox/rk3328-l1pro-1296mhz-fix.dtb" "$BOOTFS/dtb/rockchip/rk3328-l1pro-1296mhz.dtb" || { echo "错误:复制修复后的 DTB 文件失败" >&2; exit 1; }
|
||||
|
||||
# 自动下载 DTB 文件(如果不存在)
|
||||
local dtb_file="$SRCPATH/image/chainedbox/rk3328-l1pro-1296mhz-fix.dtb"
|
||||
download_file_if_missing "$dtb_file" || { echo "错误:下载 Chainedbox DTB 文件失败" >&2; exit 1; }
|
||||
|
||||
sudo cp "$dtb_file" "$BOOTFS/dtb/rockchip/rk3328-l1pro-1296mhz.dtb" || { echo "错误:复制修复后的 DTB 文件失败" >&2; exit 1; }
|
||||
sudo umount "$BOOTFS" || { echo "警告:卸载 boot 分区 ($BOOTFS) 失败" >&2; BOOTFS_MOUNTED=0; }
|
||||
BOOTFS_MOUNTED=0
|
||||
echo "信息:分离 boot loop 设备 $bootfs_loopdev..."
|
||||
@@ -116,6 +141,10 @@ vm_rootfs() {
|
||||
|
||||
echo "信息:准备 Vm Rootfs..."
|
||||
ensure_dir "$TMPDIR"
|
||||
|
||||
# 自动下载源镜像文件(如果不存在)
|
||||
download_file_if_missing "$source_image" || { echo "错误:下载 Vm 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
cp "$source_image" "$target_image" || { echo "错误:复制 Vm 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:设置带偏移量的 loop 设备..."
|
||||
@@ -134,6 +163,10 @@ e900v22c_rootfs() {
|
||||
|
||||
echo "信息:准备 E900V22C Rootfs..."
|
||||
ensure_dir "$TMPDIR"
|
||||
|
||||
# 自动下载源镜像文件(如果不存在)
|
||||
download_file_if_missing "$source_image" || { echo "错误:下载 E900V22C 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
cp "$source_image" "$target_image" || { echo "错误:复制 E900V22C 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:扩展镜像文件 (${add_size_mb}MB)..."
|
||||
@@ -164,6 +197,10 @@ octopus_flanet_rootfs() {
|
||||
|
||||
echo "信息:准备 Octopus-Planet Rootfs..."
|
||||
ensure_dir "$TMPDIR"; ensure_dir "$BOOTFS"
|
||||
|
||||
# 自动下载源镜像文件(如果不存在)
|
||||
download_file_if_missing "$source_image" || { echo "错误:下载 Octopus-Planet 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
cp "$source_image" "$target_image" || { echo "错误:复制 Octopus-Planet 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:挂载 boot 分区并修改 uEnv.txt (使用 VIM2 DTB)..."
|
||||
@@ -199,14 +236,30 @@ octopus_flanet_rootfs() {
|
||||
config_cumebox2_files() {
|
||||
echo "信息:为 Cumebox2 配置特定文件 (OLED, DTB)..."
|
||||
ensure_dir "$ROOTFS/etc/oled"
|
||||
|
||||
# 自动下载 Cumebox2 相关文件(如果不存在)
|
||||
local dtb_file="$SRCPATH/image/cumebox2/v-fix.dtb"
|
||||
local ssd_file="$SRCPATH/image/cumebox2/ssd"
|
||||
local config_file="$SRCPATH/image/cumebox2/config.json"
|
||||
|
||||
download_file_if_missing "$dtb_file" || echo "警告:下载 Cumebox2 DTB 失败"
|
||||
download_file_if_missing "$ssd_file" || echo "警告:下载 Cumebox2 ssd 脚本失败"
|
||||
download_file_if_missing "$config_file" || echo "警告:下载 Cumebox2 配置文件失败"
|
||||
|
||||
# 注意 DTB 路径可能需要根据实际 Armbian 版本调整
|
||||
sudo cp "$SRCPATH/image/cumebox2/v-fix.dtb" "$ROOTFS/boot/dtb/amlogic/meson-gxl-s905x-khadas-vim.dtb" || echo "警告:复制 Cumebox2 DTB 失败"
|
||||
sudo cp "$SRCPATH/image/cumebox2/ssd" "$ROOTFS/usr/bin/" || echo "警告:复制 Cumebox2 ssd 脚本失败"
|
||||
sudo cp "$dtb_file" "$ROOTFS/boot/dtb/amlogic/meson-gxl-s905x-khadas-vim.dtb" || echo "警告:复制 Cumebox2 DTB 失败"
|
||||
sudo cp "$ssd_file" "$ROOTFS/usr/bin/" || echo "警告:复制 Cumebox2 ssd 脚本失败"
|
||||
sudo chmod +x "$ROOTFS/usr/bin/ssd" || echo "警告:设置 ssd 脚本执行权限失败"
|
||||
sudo cp "$SRCPATH/image/cumebox2/config.json" "$ROOTFS/etc/oled/config.json" || echo "警告:复制 OLED 配置文件失败"
|
||||
sudo cp "$config_file" "$ROOTFS/etc/oled/config.json" || echo "警告:复制 OLED 配置文件失败"
|
||||
}
|
||||
|
||||
config_octopus_flanet_files() {
|
||||
echo "信息:为 Octopus-Planet 配置特定文件 (model_database.conf)..."
|
||||
sudo cp "$SRCPATH/image/octopus-flanet/model_database.conf" "$ROOTFS/etc/model_database.conf" || echo "警告:复制 model_database.conf 失败"
|
||||
|
||||
# 自动下载 Octopus-Planet 相关文件(如果不存在)
|
||||
local config_file="$SRCPATH/image/octopus-flanet/model_database.conf"
|
||||
|
||||
download_file_if_missing "$config_file" || echo "警告:下载 Octopus-Planet 配置文件失败"
|
||||
|
||||
sudo cp "$config_file" "$ROOTFS/etc/model_database.conf" || echo "警告:复制 model_database.conf 失败"
|
||||
}
|
||||
@@ -21,7 +21,12 @@ delete_armbian_verify(){
|
||||
|
||||
prepare_external_binaries() {
|
||||
local platform="$1" # linux/armhf or linux/amd64 or linux/aarch64
|
||||
local docker_image="registry.cn-hangzhou.aliyuncs.com/silentwind/kvmd-stage-0"
|
||||
# 如果在 GitHub Actions 环境下,使用 silentwind0/kvmd-stage-0,否则用阿里云镜像
|
||||
if is_github_actions; then
|
||||
local docker_image="silentwind0/kvmd-stage-0"
|
||||
else
|
||||
local docker_image="registry.cn-hangzhou.aliyuncs.com/silentwind/kvmd-stage-0"
|
||||
fi
|
||||
|
||||
echo "信息:准备外部预编译二进制文件 (平台: $platform)..."
|
||||
ensure_dir "$PREBUILT_DIR"
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
# --- 压缩函数 ---
|
||||
|
||||
# 压缩镜像文件(仅在 GitHub Actions 环境中)
|
||||
compress_image_file() {
|
||||
local file_path="$1"
|
||||
|
||||
if is_github_actions && [[ -f "$file_path" ]]; then
|
||||
echo "信息:压缩镜像文件: $file_path"
|
||||
if xz -9 -vv "$file_path"; then
|
||||
echo "信息:压缩完成: ${file_path}.xz"
|
||||
else
|
||||
echo "警告:压缩文件 $file_path 失败"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# --- 打包函数 ---
|
||||
|
||||
pack_img() {
|
||||
@@ -29,7 +45,22 @@ pack_img() {
|
||||
sudo qemu-img convert -f raw -O vmdk "$raw_img" "$vmdk_img" || echo "警告:转换为 VMDK 失败"
|
||||
echo "信息:转换为 VDI..."
|
||||
sudo qemu-img convert -f raw -O vdi "$raw_img" "$vdi_img" || echo "警告:转换为 VDI 失败"
|
||||
|
||||
# 在 GitHub Actions 环境中压缩 VM 镜像文件
|
||||
if is_github_actions; then
|
||||
echo "信息:在 GitHub Actions 环境中压缩 VM 镜像文件..."
|
||||
compress_image_file "$raw_img"
|
||||
compress_image_file "$vmdk_img"
|
||||
compress_image_file "$vdi_img"
|
||||
fi
|
||||
else
|
||||
# 在 GitHub Actions 环境中压缩镜像文件
|
||||
if is_github_actions; then
|
||||
echo "信息:在 GitHub Actions 环境中压缩镜像文件..."
|
||||
compress_image_file "$OUTPUTDIR/$target_img_name"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "信息:镜像打包完成: $OUTPUTDIR/$target_img_name"
|
||||
}
|
||||
|
||||
@@ -48,6 +79,10 @@ pack_img_onecloud() {
|
||||
unmount_all
|
||||
fi
|
||||
|
||||
# 自动下载 AmlImg 工具(如果不存在)
|
||||
download_file_if_missing "$aml_packer" || { echo "错误:下载 AmlImg 工具失败" >&2; exit 1; }
|
||||
sudo chmod +x "$aml_packer" || { echo "错误:设置 AmlImg 工具执行权限失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:将 raw rootfs 转换为 sparse image..."
|
||||
# 先删除可能存在的旧 sparse 文件
|
||||
sudo rm -f "$rootfs_sparse_img"
|
||||
@@ -55,11 +90,16 @@ pack_img_onecloud() {
|
||||
sudo rm "$rootfs_raw_img" # 删除 raw 文件,因为它已被转换
|
||||
|
||||
echo "信息:使用 AmlImg 工具打包..."
|
||||
sudo chmod +x "$aml_packer"
|
||||
sudo "$aml_packer" pack "$OUTPUTDIR/$target_img_name" "$TMPDIR/" || { echo "错误:AmlImg 打包失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:清理 Onecloud 临时文件..."
|
||||
sudo rm -f "$TMPDIR/6.boot.PARTITION.sparse" "$TMPDIR/7.rootfs.PARTITION.sparse" "$TMPDIR/dts.img"
|
||||
|
||||
# 在 GitHub Actions 环境中压缩 Onecloud 镜像文件
|
||||
if is_github_actions; then
|
||||
echo "信息:在 GitHub Actions 环境中压缩 Onecloud 镜像文件..."
|
||||
compress_image_file "$OUTPUTDIR/$target_img_name"
|
||||
fi
|
||||
|
||||
echo "信息:Onecloud burn 镜像打包完成: $OUTPUTDIR/$target_img_name"
|
||||
}
|
||||
21
build/record.txt
Normal file
21
build/record.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
wget https://github.com/hzyitc/AmlImg/releases/download/v0.3.1/AmlImg_v0.3.1_linux_amd64 -O /mnt/src/image/onecloud/AmlImg_v0.3.1_linux_amd64
|
||||
chmod +x /mnt/src/image/onecloud/AmlImg_v0.3.1_linux_amd64
|
||||
|
||||
|
||||
#!/bin/bash
|
||||
# 文件映射脚本
|
||||
# 本地目录前缀:/mnt
|
||||
# 远程URL前缀:https://files.mofeng.run
|
||||
|
||||
LOCAL_PREFIX="/mnt"
|
||||
REMOTE_PREFIX="https://files.mofeng.run"
|
||||
|
||||
# 文件相对路径
|
||||
REL_PATH="src/image/onecloud/Armbian_by-SilentWind_24.5.0-trunk_Onecloud_bookworm_legacy_5.9.0-rc7_minimal_support-dvd-emulation.burn.img"
|
||||
|
||||
LOCAL_FILE="$LOCAL_PREFIX/$REL_PATH"
|
||||
REMOTE_URL="$REMOTE_PREFIX/$REL_PATH"
|
||||
|
||||
echo "下载 $REMOTE_URL 到 $LOCAL_FILE"
|
||||
mkdir -p "$(dirname "$LOCAL_FILE")"
|
||||
wget -O "$LOCAL_FILE" "$REMOTE_URL"
|
||||
Reference in New Issue
Block a user