Compare commits

...

22 Commits

Author SHA1 Message Date
spiritlhl
78b5634306 fix:升级版本 2025-11-04 12:01:54 +00:00
spiritlhl
a0bc2de61a fix:修复在权限不足测试时,icmp报错导致的整个程序退出的问题 2025-11-04 11:55:49 +00:00
spiritlhl
3c45667c55 fix: 更新依赖,ping值检测设置3次测试取平均值 2025-11-04 11:51:10 +00:00
github-actions[bot]
6111554225 chore: update ECS_VERSION to 0.1.98 in goecs.sh 2025-11-03 02:55:13 +00:00
spiritlhl
1bba9cde26 fix:创建了独立的gui分支专门处理安卓问题 2025-11-03 02:40:38 +00:00
spiritlhl
280d292c4c fix:修复公共分支构建覆写问题,删除不再使用的备份文件 2025-11-03 02:33:29 +00:00
spiritlhl
cf85b67ae4 fix:修复相关说明 2025-11-03 02:19:12 +00:00
spiritlhl
3ebbf186fc fix: 升级nt3修复延迟处理 2025-11-03 02:15:59 +00:00
spiritlhl
3ba3301cc7 fix: 更新dockerfile更健壮的处理包安装 2025-11-03 01:58:32 +00:00
github-actions[bot]
8674a82eb0 chore: update ECS_VERSION to 0.1.97 in goecs.sh 2025-11-02 17:01:50 +00:00
spiritlhl
5116493338 fix:更新主体版本 2025-11-02 16:56:27 +00:00
spiritlhl
c6f3e7a285 fix: 进一步重构组织架构 2025-11-02 16:54:38 +00:00
spiritlhl
40f63e6cb1 fix:拆分单文件重构组织形式,便于后续维护 2025-11-02 16:37:07 +00:00
spiritlhl
6b42f626b0 feat:设置额外提供的CI参数设置优先级高于选项本身的预设值 2025-11-02 15:41:39 +00:00
spiritlhl
4503fb55b0 fix: 修复PING值测试的过滤条件 2025-11-02 15:19:21 +00:00
spiritlhl
cb1634cb68 fix:统一输出的间隔 2025-11-02 14:31:20 +00:00
spiritlhl
95ece8c28d fix:更新backtrace的go版本,修复cdn检测可用性的漏洞 2025-11-02 14:11:41 +00:00
spiritlhl
fd44c079b3 fix:删除无效头文字输出 2025-11-02 13:24:53 +00:00
spiritlhl
5c5bd37074 fix:修复相关逻辑支持ping值测试改版 2025-11-02 13:22:47 +00:00
spiritlhl
1676045e3d fix: 更新三网ping值测试支持为tgdc和主流网站测试 2025-11-02 12:56:41 +00:00
spiritlhl
58e6941bfa fix:增加控制三网ping值测试单项的参数 2025-11-01 15:02:02 +00:00
github-actions[bot]
3989708c4e chore: update ECS_VERSION to 0.1.93 in goecs.sh 2025-10-31 07:37:11 +00:00
41 changed files with 1358 additions and 2786 deletions

View File

@@ -1,84 +0,0 @@
before:
hooks:
- go mod tidy -v
builds:
- id: universal
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 -extldflags=-static
flags:
- -trimpath
goos:
- linux
- windows
- freebsd
goarch:
- arm
- arm64
- 386
- amd64
- mips
- mipsle
- s390x
- riscv64
gomips:
- softfloat
ignore:
- goos: windows
goarch: arm
main: ./
binary: goecs
- id: darwin-amd64
env:
- CGO_ENABLED=1
- CC=o64-clang
- CXX=o64-clang++
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
flags:
- -trimpath
goos:
- darwin
goarch:
- amd64
main: ./
binary: goecs
- id: darwin-arm64
env:
- CGO_ENABLED=1
- CC=oa64-clang
- CXX=oa64-clang++
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
flags:
- -trimpath
goos:
- darwin
goarch:
- arm64
main: ./
binary: goecs
universal_binaries:
- name_template: "goecs"
replace: false
checksum:
name_template: "checksums.txt"
snapshot:
name_template: "goecs"
archives:
- name_template: "goecs_{{ .Os }}_{{ .Arch }}"
format: zip
files:
- none*
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^chore"
- Merge pull request
- Merge branch
- go mod tidy
- New translations

View File

@@ -1,522 +0,0 @@
version: 2
project_name: goecs
env:
- GO111MODULE=on
before:
hooks:
- go mod tidy -v
builds:
# Linux AMD64 with CGO
- id: linux-amd64-cgo
env:
- CGO_ENABLED=1
- CC=x86_64-linux-gnu-gcc
- CGO_CFLAGS=-O2 -static -fno-stack-protector
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 -extldflags=-static
flags:
- -trimpath
goos:
- linux
goarch:
- amd64
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for linux/amd64 (CGO)"
post:
- echo "Successfully built linux/amd64 (CGO)"
- echo "---"
# Linux 386 with CGO - 修复了编译器和标志
- id: linux-386-cgo
env:
- CGO_ENABLED=1
- CC=gcc
- CGO_CFLAGS=-m32 -O1 -march=i686 -mtune=generic -fno-stack-protector
- CGO_LDFLAGS=-m32
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 -extldflags="-m32 -static"
flags:
- -trimpath
goos:
- linux
goarch:
- 386
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for linux/386 (CGO)"
post:
- echo "Successfully built linux/386 (CGO)"
- echo "---"
# Linux ARM64 with CGO
- id: linux-arm64-cgo
env:
- CGO_ENABLED=1
- CC=aarch64-linux-gnu-gcc
- CGO_CFLAGS=-O1 -fno-stack-protector
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 -extldflags=-static
flags:
- -trimpath
goos:
- linux
goarch:
- arm64
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for linux/arm64 (CGO)"
post:
- echo "Successfully built linux/arm64 (CGO)"
- echo "---"
# Windows AMD64 with CGO
- id: windows-amd64-cgo
env:
- CGO_ENABLED=1
- CC=x86_64-w64-mingw32-gcc
- CGO_CFLAGS=-O2 -static-libgcc -static-libstdc++
- CGO_LDFLAGS=-static-libgcc -static-libstdc++
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 -extldflags=-static
flags:
- -trimpath
goos:
- windows
goarch:
- amd64
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for windows/amd64 (CGO)"
post:
- echo "Successfully built windows/amd64 (CGO)"
- echo "---"
# Windows 386 with CGO - 修复了编译器名称
- id: windows-386-cgo
env:
- CGO_ENABLED=1
- CC=i686-w64-mingw32-gcc
- CGO_CFLAGS=-O2 -static-libgcc -static-libstdc++
- CGO_LDFLAGS=-static-libgcc -static-libstdc++
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 -extldflags=-static
flags:
- -trimpath
goos:
- windows
goarch:
- 386
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for windows/386 (CGO)"
post:
- echo "Successfully built windows/386 (CGO)"
- echo "---"
# Darwin AMD64 with CGO
- id: darwin-amd64-cgo
env:
- CGO_ENABLED=1
- CC=o64-clang
- CGO_CFLAGS=-O2 -arch x86_64 -mmacosx-version-min=10.12
- CGO_LDFLAGS=-arch x86_64 -mmacosx-version-min=10.12
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
flags:
- -trimpath
goos:
- darwin
goarch:
- amd64
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for darwin/amd64 (CGO)"
- echo "Checking osxcross tools..."
- which o64-clang || echo "o64-clang not found"
- which o64-clang++ || echo "o64-clang++ not found"
post:
- echo "Successfully built darwin/amd64 (CGO)"
- echo "---"
# Darwin ARM64 with CGO
- id: darwin-arm64-cgo
env:
- CGO_ENABLED=1
- CC=oa64-clang
- CGO_CFLAGS=-O2 -arch arm64 -mmacosx-version-min=11.0
- CGO_LDFLAGS=-arch arm64 -mmacosx-version-min=11.0
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
flags:
- -trimpath
goos:
- darwin
goarch:
- arm64
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for darwin/arm64 (CGO)"
- echo "Checking osxcross tools..."
- which oa64-clang || echo "oa64-clang not found"
- which oa64-clang++ || echo "oa64-clang++ not found"
post:
- echo "Successfully built darwin/arm64 (CGO)"
- echo "---"
# Linux RISC-V 64 with CGO
- id: linux-riscv64-cgo
env:
- CGO_ENABLED=1
- CC=riscv64-linux-gnu-gcc
- CGO_CFLAGS=-O1 -fno-stack-protector
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 -extldflags=-static
flags:
- -trimpath
goos:
- linux
goarch:
- riscv64
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for linux/riscv64 (CGO)"
post:
- echo "Successfully built linux/riscv64 (CGO)"
- echo "---"
# Linux MIPS64 with CGO
- id: linux-mips64-cgo
env:
- CGO_ENABLED=1
- CC=mips64-linux-gnuabi64-gcc
- CGO_CFLAGS=-O1 -fno-stack-protector
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 -extldflags=-static
flags:
- -trimpath
goos:
- linux
goarch:
- mips64
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for linux/mips64 (CGO)"
post:
- echo "Successfully built linux/mips64 (CGO)"
- echo "---"
# Linux MIPS64LE with CGO
- id: linux-mips64le-cgo
env:
- CGO_ENABLED=1
- CC=mips64el-linux-gnuabi64-gcc
- CGO_CFLAGS=-O1 -fno-stack-protector
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 -extldflags=-static
flags:
- -trimpath
goos:
- linux
goarch:
- mips64le
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for linux/mips64le (CGO)"
post:
- echo "Successfully built linux/mips64le (CGO)"
- echo "---"
# Linux PPC64LE with CGO
- id: linux-ppc64le-cgo
env:
- CGO_ENABLED=1
- CC=powerpc64le-linux-gnu-gcc
- CGO_CFLAGS=-O1 -fno-stack-protector
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 -extldflags=-static
flags:
- -trimpath
goos:
- linux
goarch:
- ppc64le
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for linux/ppc64le (CGO)"
post:
- echo "Successfully built linux/ppc64le (CGO)"
- echo "---"
# Linux ARM (no CGO)
- id: linux-arm-nocgo
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
flags:
- -trimpath
goos:
- linux
goarch:
- arm
goarm:
- "5"
- "6"
- "7"
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for linux/arm (no CGO)"
post:
- echo "Successfully built linux/arm (no CGO)"
- echo "---"
# Linux S390X (no CGO)
- id: linux-s390x-nocgo
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
flags:
- -trimpath
goos:
- linux
goarch:
- s390x
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for linux/s390x (no CGO)"
post:
- echo "Successfully built linux/s390x (no CGO)"
- echo "---"
# Linux MIPS (no CGO)
- id: linux-mips-nocgo
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
flags:
- -trimpath
goos:
- linux
goarch:
- mips
gomips:
- softfloat
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for linux/mips (no CGO)"
post:
- echo "Successfully built linux/mips (no CGO)"
- echo "---"
# Linux MIPSLE (no CGO)
- id: linux-mipsle-nocgo
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
flags:
- -trimpath
goos:
- linux
goarch:
- mipsle
gomips:
- softfloat
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for linux/mipsle (no CGO)"
post:
- echo "Successfully built linux/mipsle (no CGO)"
- echo "---"
# Linux PPC64 (no CGO)
- id: linux-ppc64-nocgo
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
flags:
- -trimpath
goos:
- linux
goarch:
- ppc64
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for linux/ppc64 (no CGO)"
post:
- echo "Successfully built linux/ppc64 (no CGO)"
- echo "---"
# FreeBSD AMD64 (no CGO)
- id: freebsd-amd64-nocgo
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
flags:
- -trimpath
goos:
- freebsd
goarch:
- amd64
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for freebsd/amd64 (no CGO)"
post:
- echo "Successfully built freebsd/amd64 (no CGO)"
- echo "---"
# FreeBSD ARM64 (no CGO)
- id: freebsd-arm64-nocgo
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
flags:
- -trimpath
goos:
- freebsd
goarch:
- arm64
main: ./
binary: goecs
no_unique_dist_dir: true
hooks:
pre:
- echo "Starting build for freebsd/arm64 (no CGO)"
post:
- echo "Successfully built freebsd/arm64 (no CGO)"
- echo "---"
universal_binaries:
- name_template: "goecs"
replace: false
ids:
- darwin-amd64-cgo
- darwin-arm64-cgo
checksum:
name_template: "checksums.txt"
algorithm: sha256
disable: false
ids:
- linux-amd64-cgo
- linux-386-cgo
- linux-arm64-cgo
- linux-riscv64-cgo
- linux-mips64-cgo
- linux-mips64le-cgo
- linux-ppc64le-cgo
- windows-amd64-cgo
- windows-386-cgo
- darwin-amd64-cgo
- darwin-arm64-cgo
- linux-arm-nocgo
- linux-s390x-nocgo
- linux-mips-nocgo
- linux-mipsle-nocgo
- linux-ppc64-nocgo
- freebsd-amd64-nocgo
- freebsd-arm64-nocgo
extra_files:
- glob: "./goecs.sh"
snapshot:
name_template: "goecs"
archives:
- id: default
name_template: "goecs_{{ .Os }}_{{ .Arch }}"
format: zip
files:
- none*
allow_different_binary_count: true
builds:
- linux-amd64-cgo
- linux-386-cgo
- linux-arm64-cgo
- linux-riscv64-cgo
- linux-mips64-cgo
- linux-mips64le-cgo
- linux-ppc64le-cgo
- windows-amd64-cgo
- windows-386-cgo
- darwin-amd64-cgo
- darwin-arm64-cgo
- linux-arm-nocgo
- linux-s390x-nocgo
- linux-mips-nocgo
- linux-mipsle-nocgo
- linux-ppc64-nocgo
- freebsd-amd64-nocgo
- freebsd-arm64-nocgo
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^chore"
- Merge pull request
- Merge branch
- go mod tidy
- New translations

View File

@@ -1,14 +0,0 @@
// clearScreen 清屏
func clearScreen() {
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("cmd", "/c", "cls")
case "darwin":
cmd = exec.Command("clear")
default:
cmd = exec.Command("clear")
}
cmd.Stdout = os.Stdout
_ = cmd.Run()
}

View File

@@ -1,9 +0,0 @@
package basic1
import (
"testing"
)
func Test_basic(t *testing.T) {
Basic("zh")
}

View File

@@ -1,16 +0,0 @@
package basic1
import (
"fmt"
"github.com/oneclickvirt/basics/network"
"github.com/oneclickvirt/basics/system"
"strings"
)
// 本包不在main中使用仅做测试使用真正调用的在 utils 中的 BasicsAndSecurityCheck
func Basic(language string) {
ipInfo, _, _ := network.NetworkCheck("both", false, language)
systemInfo := system.CheckSystemInfo(language)
basicInfo := strings.ReplaceAll(systemInfo+ipInfo, "\n\n", "\n")
fmt.Print(basicInfo)
}

View File

@@ -1,132 +0,0 @@
name: Goreleaser
on:
workflow_dispatch:
tags:
- "v*.*.*"
jobs:
goreleaser:
runs-on: ubuntu-latest
container:
# 1.20 是 Windows 7/8 Server 2008/2012 最后一个支持版本
image: goreleaser/goreleaser-cross:v1.20
steps:
- name: Configure git safe directory
run: |
git config --global --add safe.directory /__w/ecs/ecs
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.23.4
- name: Configure Git for Private Modules
run: |
git config --global url."https://${{ secrets.GHT }}@github.com/".insteadOf "https://github.com/"
git config --global url."git@github.com:".insteadOf "https://github.com/"
env:
GITHUB_TOKEN: ${{ secrets.GHT }}
- name: Install missing cross-compilation tools
run: |
echo "Installing missing cross-compilation tools..."
apt-get update
PACKAGES=(
gcc-multilib
g++-multilib
linux-libc-dev
linux-libc-dev:i386
libc6-dev-i386
libc6-dev-i386-cross
gcc-aarch64-linux-gnu
gcc-riscv64-linux-gnu
gcc-mips64-linux-gnuabi64
gcc-mips64el-linux-gnuabi64
gcc-powerpc64le-linux-gnu
gcc-mingw-w64-x86-64
gcc-mingw-w64-i686
libc6-dev-amd64-cross
libc6-dev-arm64-cross
libc6-dev-riscv64-cross
libc6-dev-mips64-cross
libc6-dev-mips64el-cross
libc6-dev-ppc64el-cross
)
for pkg in "${PACKAGES[@]}"; do
echo "Installing $pkg..."
apt-get install -y "$pkg" || echo "Failed to install $pkg, continuing..."
done
- name: Verify cross-compilation tools
run: |
echo "Checking available cross-compilation tools..."
echo "=== GCC compilers ==="
which gcc || echo "gcc not found"
which x86_64-linux-gnu-gcc || echo "x86_64-linux-gnu-gcc not found"
which aarch64-linux-gnu-gcc || echo "aarch64-linux-gnu-gcc not found"
which riscv64-linux-gnu-gcc || echo "riscv64-linux-gnu-gcc not found"
which mips64-linux-gnuabi64-gcc || echo "mips64-linux-gnuabi64-gcc not found"
which mips64el-linux-gnuabi64-gcc || echo "mips64el-linux-gnuabi64-gcc not found"
which powerpc64le-linux-gnu-gcc || echo "powerpc64le-linux-gnu-gcc not found"
echo "=== MinGW compilers ==="
which x86_64-w64-mingw32-gcc || echo "x86_64-w64-mingw32-gcc not found"
which i686-w64-mingw32-gcc || echo "i686-w64-mingw32-gcc not found"
echo "=== OSXCross compilers ==="
which o64-clang || echo "o64-clang not found"
which oa64-clang || echo "oa64-clang not found"
which o64-clang++ || echo "o64-clang++ not found"
which oa64-clang++ || echo "oa64-clang++ not found"
echo "=== Clang compilers ==="
which clang || echo "clang not found"
echo "=== Available gcc binaries ==="
ls -la /usr/bin/*gcc* | head -20
echo "=== Available clang binaries ==="
ls -la /usr/bin/*clang* | head -10
echo "=== OSXCross directory ==="
ls -la /usr/osxcross/bin/ 2>/dev/null || echo "OSXCross not found in /usr/osxcross/bin/"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: latest
args: release --parallelism 1 --verbose
env:
GITHUB_TOKEN: ${{ secrets.GHT }}
GOPRIVATE: github.com/oneclickvirt/security
- name: Update goecs.sh with new version
run: |
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
VERSION="${GITHUB_REF#refs/tags/v}"
else
VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.1.37")
fi
echo "Using version: $VERSION"
FILE="goecs.sh"
BRANCH="master"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global --unset url."git@github.com:".insteadOf || true
git fetch origin $BRANCH
git checkout $BRANCH
if [ ! -f "$FILE" ]; then
echo "Error: $FILE not found"
exit 1
fi
sed -i "s/\(_yellow \"Unable to get version info, using default version \).*\(\".*\)/\1$VERSION\2/" "$FILE"
sed -i "s/\(ECS_VERSION=\"\).*\(\"\)/\1$VERSION\2/" "$FILE"
if git diff --quiet "$FILE"; then
echo "No changes detected in $FILE"
exit 0
fi
git add "$FILE"
git commit -m "chore: update ECS_VERSION to $VERSION in goecs.sh"
git push origin $BRANCH
env:
GITHUB_TOKEN: ${{ secrets.GHT }}

View File

@@ -1,467 +0,0 @@
name: Build and Release
on:
workflow_dispatch:
tags:
- "v*.*.*"
jobs:
build:
name: Release Check And Build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get latest tag
id: tag
run: |
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.1.0")
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=${TAG#v}" >> $GITHUB_OUTPUT
- name: Generate changelog
id: changelog
run: |
TAG="${{ steps.tag.outputs.tag }}"
PREV_TAG=$(git describe --tags --abbrev=0 "$TAG^" 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
CHANGELOG=$(git log --oneline --pretty=format:"* %H %s" "$TAG" | head -20)
else
CHANGELOG=$(git log --oneline --pretty=format:"* %H %s" "$PREV_TAG..$TAG")
fi
FULL_CHANGELOG="## Changelog"$'\n'"$CHANGELOG"
echo "$FULL_CHANGELOG" > changelog.txt
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "$FULL_CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create or update release
run: |
TAG="${{ steps.tag.outputs.tag }}"
CHANGELOG_BODY=$(cat changelog.txt | jq -Rs .)
RELEASE_EXISTS=$(curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" "https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG" | jq -r '.id // empty')
if [ -z "$RELEASE_EXISTS" ]; then
curl -s -X POST -H "Authorization: Bearer ${{ secrets.GHT }}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"body\":$CHANGELOG_BODY,\"draft\":false,\"prerelease\":false}" \
"https://api.github.com/repos/${{ github.repository }}/releases"
else
curl -s -X PATCH -H "Authorization: Bearer ${{ secrets.GHT }}" \
-H "Content-Type: application/json" \
-d "{\"body\":$CHANGELOG_BODY}" \
"https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_EXISTS"
fi
- name: Delete existing release assets
run: |
TAG="${{ steps.tag.outputs.tag }}"
RELEASE_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" "https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG" | jq -r '.id')
if [ "$RELEASE_ID" != "null" ]; then
ASSETS=$(curl -s -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets" | jq -r '.[] | .id')
for asset in $ASSETS; do
curl -s -X DELETE -H "Authorization: Bearer ${{ secrets.GHT }}" "https://api.github.com/repos/${{ github.repository }}/releases/assets/$asset"
done
sleep 30
fi
release-binary:
name: Release Go Binary
needs: build
strategy:
fail-fast: false
matrix:
include:
- goos: linux
goarch: amd64
cgo_enabled: "1"
cc: x86_64-linux-gnu-gcc
cflags: "-O2 -static -fno-stack-protector"
ldflags: "-extldflags=-static -s -w"
packages: "build-essential gcc"
runner: ubuntu-latest
- goos: linux
goarch: 386
cgo_enabled: "1"
cc: x86_64-linux-gnu-gcc
cflags: "-m32 -O1 -march=i686 -mtune=generic -fno-stack-protector"
ldflags: "-extldflags=-static -s -w"
packages: "build-essential gcc-multilib"
runner: ubuntu-latest
- goos: linux
goarch: arm64
cgo_enabled: "1"
cc: aarch64-linux-gnu-gcc
cflags: "-O1 -fno-stack-protector"
ldflags: "-extldflags=-static -s -w"
packages: "build-essential gcc-aarch64-linux-gnu"
runner: ubuntu-latest
- goos: linux
goarch: riscv64
cgo_enabled: "1"
cc: riscv64-linux-gnu-gcc
cflags: "-O1 -fno-stack-protector"
ldflags: "-extldflags=-static -s -w"
packages: "build-essential gcc-riscv64-linux-gnu"
runner: ubuntu-latest
- goos: linux
goarch: mips64
cgo_enabled: "1"
cc: mips64-linux-gnuabi64-gcc
cflags: "-O1 -fno-stack-protector"
ldflags: "-extldflags=-static -s -w"
packages: "build-essential gcc-mips64-linux-gnuabi64"
runner: ubuntu-latest
- goos: linux
goarch: mips64le
cgo_enabled: "1"
cc: mips64el-linux-gnuabi64-gcc
cflags: "-O1 -fno-stack-protector"
ldflags: "-extldflags=-static -s -w"
packages: "build-essential gcc-mips64el-linux-gnuabi64"
runner: ubuntu-latest
- goos: linux
goarch: ppc64le
cgo_enabled: "1"
cc: powerpc64le-linux-gnu-gcc
cflags: "-O1 -fno-stack-protector"
ldflags: "-extldflags=-static -s -w"
packages: "build-essential gcc-powerpc64le-linux-gnu"
runner: ubuntu-latest
- goos: linux
goarch: arm
# goarm: 7
cgo_enabled: "1"
cc: arm-linux-gnueabihf-gcc
cflags: "-O1 -fno-stack-protector"
ldflags: "-extldflags=-static -s -w"
packages: "build-essential gcc-arm-linux-gnueabihf"
runner: ubuntu-latest
- goos: windows
goarch: amd64
cgo_enabled: "1"
cc: x86_64-w64-mingw32-gcc
cflags: "-O2 -static-libgcc -static-libstdc++"
ldflags: "-extldflags=-static -s -w"
packages: "build-essential gcc-mingw-w64-x86-64"
runner: ubuntu-latest
- goos: windows
goarch: 386
cgo_enabled: "1"
cc: i686-w64-mingw32-gcc
cflags: "-O2 -static-libgcc -static-libstdc++"
ldflags: "-extldflags=-static -s -w"
packages: "build-essential gcc-mingw-w64-i686"
runner: ubuntu-latest
- goos: windows
goarch: arm64
cgo_enabled: "0"
ldflags: "-extldflags=-static -s -w"
packages: "build-essential"
runner: ubuntu-latest
- goos: darwin
goarch: amd64
cgo_enabled: "0"
ldflags: "-s -w"
runner: macos-latest
- goos: darwin
goarch: arm64
cgo_enabled: "0"
ldflags: "-s -w"
runner: macos-latest
- goos: linux
goarch: s390x
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
- goos: linux
goarch: mips
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
- goos: linux
goarch: mipsle
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
- goos: linux
goarch: ppc64
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
- goos: windows
goarch: arm64
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
- goos: freebsd
goarch: amd64
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
- goos: freebsd
goarch: arm64
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.25.3
- name: Configure Git for Private Modules
run: |
git config --global url."https://${{ secrets.GHT }}@github.com/".insteadOf "https://github.com/"
git config --global url."git@github.com:".insteadOf "https://github.com/"
env:
GITHUB_TOKEN: ${{ secrets.GHT }}
- name: Install cross-compilation tools
if: matrix.runner != 'macos-latest'
run: |
sudo apt-get update -qq
case "${{ matrix.goos }}-${{ matrix.goarch }}" in
linux-386)
sudo apt-get install -y build-essential gcc-multilib g++-multilib ;;
linux-arm64)
sudo apt-get install -y build-essential gcc-aarch64-linux-gnu ;;
linux-riscv64)
sudo apt-get install -y build-essential gcc-riscv64-linux-gnu ;;
linux-mips64)
sudo apt-get install -y build-essential gcc-mips64-linux-gnuabi64 ;;
linux-mips64le)
sudo apt-get install -y build-essential gcc-mips64el-linux-gnuabi64 ;;
linux-ppc64le)
sudo apt-get install -y build-essential gcc-powerpc64le-linux-gnu ;;
linux-arm)
sudo apt-get install -y build-essential gcc-arm-linux-gnueabihf ;;
windows-amd64|windows-386)
sudo apt-get install -y build-essential gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 ;;
*)
sudo apt-get install -y build-essential ;;
esac
- name: Get latest tag
id: tag
run: |
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.1.0")
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=${TAG#v}" >> $GITHUB_OUTPUT
- name: Build Binary
env:
CGO_ENABLED: ${{ matrix.cgo_enabled }}
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CC: ${{ matrix.cc }}
CGO_CFLAGS: ${{ matrix.cflags }}
CGO_LDFLAGS: ${{ matrix.ldflags }}
run: |
go clean -cache -modcache -testcache
# 设置额外的环境变量
if [[ -n "${{ matrix.goarm }}" ]]; then
export GOARM=${{ matrix.goarm }}
fi
if [[ -n "${{ matrix.gomips }}" ]]; then
export GOMIPS=${{ matrix.gomips }}
fi
# 针对 Darwin 的特殊处理
if [[ "${{ matrix.cgo_enabled }}" == "1" && "${{ matrix.goos }}" == "darwin" ]]; then
if [[ "${{ matrix.goarch }}" == "amd64" ]]; then
export CC="x86_64-apple-darwin21.4-clang"
export CXX="x86_64-apple-darwin21.4-clang++"
elif [[ "${{ matrix.goarch }}" == "arm64" ]]; then
export CC="aarch64-apple-darwin21.4-clang"
export CXX="aarch64-apple-darwin21.4-clang++"
fi
export OSXCROSS_ROOT="${OSXCROSS_ROOT}"
elif [[ "${{ matrix.cgo_enabled }}" == "1" && "${{ matrix.runner }}" != "macos-latest" ]]; then
# 对于 Windows 的特殊处理
if [[ "${{ matrix.goos }}" == "windows" ]]; then
export CGO_LDFLAGS="-static-libgcc -static-libstdc++"
fi
fi
# 测试编译器(仅在启用 CGO 时)
if [[ "${{ matrix.cgo_enabled }}" == "1" && -n "$CC" ]]; then
echo 'int main() { return 0; }' > test.c
$CC $CGO_CFLAGS test.c -o test || exit 1
rm -f test.c test
fi
# 清理和准备
rm -rf vendor/
go mod download
go mod tidy
mkdir -p bin
# 设置二进制文件名
BINARY_NAME="goecs"
if [[ "${{ matrix.goos }}" == "windows" ]]; then
BINARY_NAME="${BINARY_NAME}.exe"
fi
# 构建 LDFLAGS
LDFLAGS="-s -w -X main.version=${{ steps.tag.outputs.version }} -X main.arch=${{ matrix.goarch }}"
if [[ "${{ matrix.cgo_enabled }}" == "1" ]]; then
LDFLAGS="${LDFLAGS} -checklinkname=0 ${{ matrix.ldflags }}"
else
LDFLAGS="${LDFLAGS} -checklinkname=0 ${{ matrix.ldflags }}"
fi
# 执行构建
echo "Building for GOOS=$GOOS GOARCH=$GOARCH CGO_ENABLED=$CGO_ENABLED"
go build -a -o bin/$BINARY_NAME -ldflags="$LDFLAGS" -trimpath ./
# 验证文件是否存在
[[ -f "bin/$BINARY_NAME" ]] || exit 1
# 显示构建信息
echo "Built binary: bin/$BINARY_NAME"
ls -la bin/
if command -v file >/dev/null 2>&1; then
file bin/$BINARY_NAME
fi
- name: Create ZIP archive
run: |
cd bin
BINARY_NAME="goecs"
if [[ "${{ matrix.goos }}" == "windows" ]]; then
BINARY_NAME="${BINARY_NAME}.exe"
fi
ZIP_NAME="goecs_${{ matrix.goos }}_${{ matrix.goarch }}"
if [[ -n "${{ matrix.goarm }}" ]]; then
ZIP_NAME="${ZIP_NAME}v${{ matrix.goarm }}"
fi
if [[ -n "${{ matrix.gomips }}" ]]; then
ZIP_NAME="${ZIP_NAME}_${{ matrix.gomips }}"
fi
ZIP_NAME="${ZIP_NAME}.zip"
zip "$ZIP_NAME" "$BINARY_NAME"
- name: Upload to Release
run: |
TAG="${{ steps.tag.outputs.tag }}"
RELEASE_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" "https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG" | jq -r '.id')
cd bin
for file in *.zip; do
if [[ -f "$file" ]]; then
curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" \
-H "Content-Type: application/zip" \
--data-binary @"$file" \
"https://uploads.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets?name=$file"
fi
done
checksums:
name: Generate Checksums
runs-on: ubuntu-latest
needs: release-binary
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get latest tag
id: tag
run: |
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.1.0")
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Download release assets
run: |
TAG="${{ steps.tag.outputs.tag }}"
RELEASE_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" "https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG" | jq -r '.id')
mkdir -p assets
ASSETS=$(curl -s -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets")
echo "$ASSETS" | jq -r '.[] | select(.name | endswith(".zip")) | .browser_download_url' | while read url; do
filename=$(basename "$url")
curl -L -H "Authorization: Bearer ${{ secrets.GHT }}" "$url" -o "assets/$filename"
done
- name: Generate checksums
run: |
cd assets
sha256sum *.zip > checksums.txt
if [[ -f "../goecs.sh" ]]; then
sha256sum ../goecs.sh >> checksums.txt
fi
- name: Upload checksums
run: |
TAG="${{ steps.tag.outputs.tag }}"
RELEASE_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" "https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG" | jq -r '.id')
curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" \
-H "Content-Type: text/plain" \
--data-binary @assets/checksums.txt \
"https://uploads.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets?name=checksums.txt"
update-script:
name: Update Script Version
runs-on: ubuntu-latest
needs: checksums
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get latest tag
id: tag
run: |
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.1.0")
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=${TAG#v}" >> $GITHUB_OUTPUT
- name: Update goecs.sh version
run: |
VERSION="${{ steps.tag.outputs.version }}"
BRANCH="master"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global --unset url."git@github.com:".insteadOf || true
git fetch origin $BRANCH
git checkout $BRANCH
if [ -f "goecs.sh" ]; then
sed -i "s/\(_yellow \"Unable to get version info, using default version \).*\(\".*\)/\1$VERSION\2/" "goecs.sh"
sed -i "s/\(ECS_VERSION=\"\).*\(\"\)/\1$VERSION\2/" "goecs.sh"
if ! git diff --quiet "goecs.sh"; then
git add "goecs.sh"
git commit -m "chore: update ECS_VERSION to $VERSION in goecs.sh"
git push origin $BRANCH
fi
fi
env:
GITHUB_TOKEN: ${{ secrets.GHT }}

View File

@@ -1,527 +0,0 @@
name: Build and Release
on:
workflow_dispatch:
tags:
- "v*.*.*"
jobs:
build:
name: Release Check And Build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get latest tag
id: tag
run: |
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.1.0")
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=${TAG#v}" >> $GITHUB_OUTPUT
- name: Generate changelog
id: changelog
run: |
TAG="${{ steps.tag.outputs.tag }}"
PREV_TAG=$(git describe --tags --abbrev=0 "$TAG^" 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
CHANGELOG=$(git log --oneline --pretty=format:"* %H %s" "$TAG" | head -20)
else
CHANGELOG=$(git log --oneline --pretty=format:"* %H %s" "$PREV_TAG..$TAG")
fi
FULL_CHANGELOG="## Changelog"$'\n'"$CHANGELOG"
echo "$FULL_CHANGELOG" > changelog.txt
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "$FULL_CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create or update release
run: |
TAG="${{ steps.tag.outputs.tag }}"
CHANGELOG_BODY=$(cat changelog.txt | jq -Rs .)
RELEASE_EXISTS=$(curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" "https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG" | jq -r '.id // empty')
if [ -z "$RELEASE_EXISTS" ]; then
curl -s -X POST -H "Authorization: Bearer ${{ secrets.GHT }}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"body\":$CHANGELOG_BODY,\"draft\":false,\"prerelease\":false}" \
"https://api.github.com/repos/${{ github.repository }}/releases"
else
curl -s -X PATCH -H "Authorization: Bearer ${{ secrets.GHT }}" \
-H "Content-Type: application/json" \
-d "{\"body\":$CHANGELOG_BODY}" \
"https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_EXISTS"
fi
- name: Delete existing release assets
run: |
TAG="${{ steps.tag.outputs.tag }}"
RELEASE_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" "https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG" | jq -r '.id')
if [ "$RELEASE_ID" != "null" ]; then
ASSETS=$(curl -s -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets" | jq -r '.[] | .id')
for asset in $ASSETS; do
curl -s -X DELETE -H "Authorization: Bearer ${{ secrets.GHT }}" "https://api.github.com/repos/${{ github.repository }}/releases/assets/$asset"
done
sleep 30
fi
build-musl-toolchain:
name: Build musl Cross-Compiler Toolchain
runs-on: ubuntu-latest
strategy:
matrix:
target:
- x86_64-linux-musl
- i686-linux-musl
- aarch64-linux-musl
- riscv64-linux-musl
- mips64-linux-musl
- mips64el-linux-musl
- powerpc64le-linux-musl
- arm-linux-musleabihf
steps:
- name: Install build dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y build-essential curl
- name: Cache musl toolchain
id: cache-musl
uses: actions/cache@v3
with:
path: /opt/musl-${{ matrix.target }}
key: musl-toolchain-${{ matrix.target }}-v2
- name: Build musl cross-compiler
if: steps.cache-musl.outputs.cache-hit != 'true'
run: |
# Clone musl-cross-make
git clone https://github.com/richfelker/musl-cross-make.git
cd musl-cross-make
# Create config for target
cat > config.mak << EOF
TARGET = ${{ matrix.target }}
OUTPUT = /opt/musl-${{ matrix.target }}
COMMON_CONFIG += --disable-nls
GCC_CONFIG += --enable-languages=c,c++
GCC_CONFIG += --disable-libquadmath --disable-decimal-float
GCC_CONFIG += --disable-libitm --disable-fixed-point
EOF
# Build the toolchain
make -j$(nproc)
sudo make install
# Verify installation
ls -la /opt/musl-${{ matrix.target }}/bin/
/opt/musl-${{ matrix.target }}/bin/${{ matrix.target }}-gcc --version
- name: Create toolchain artifact
run: |
sudo tar -czf musl-${{ matrix.target }}-toolchain.tar.gz -C /opt musl-${{ matrix.target }}
- name: Upload toolchain artifact
uses: actions/upload-artifact@v4
with:
name: musl-${{ matrix.target }}-toolchain
path: musl-${{ matrix.target }}-toolchain.tar.gz
retention-days: 1
release-binary:
name: Release Go Binary
needs: [build, build-musl-toolchain]
strategy:
fail-fast: false
matrix:
include:
- goos: linux
goarch: amd64
cgo_enabled: "1"
musl_target: x86_64-linux-musl
cflags: "-O2 -static -fno-stack-protector"
ldflags: "-extldflags=-static"
runner: ubuntu-latest
- goos: linux
goarch: 386
cgo_enabled: "1"
musl_target: i686-linux-musl
cflags: "-O1 -march=i686 -mtune=generic -fno-stack-protector"
ldflags: "-extldflags=-static"
runner: ubuntu-latest
- goos: linux
goarch: arm64
cgo_enabled: "1"
musl_target: aarch64-linux-musl
cflags: "-O1 -fno-stack-protector"
ldflags: "-extldflags=-static"
runner: ubuntu-latest
- goos: linux
goarch: riscv64
cgo_enabled: "1"
musl_target: riscv64-linux-musl
cflags: "-O1 -fno-stack-protector"
ldflags: "-extldflags=-static"
runner: ubuntu-latest
- goos: linux
goarch: mips64
cgo_enabled: "1"
musl_target: mips64-linux-musl
cflags: "-O1 -fno-stack-protector"
ldflags: "-extldflags=-static"
runner: ubuntu-latest
- goos: linux
goarch: mips64le
cgo_enabled: "1"
musl_target: mips64el-linux-musl
cflags: "-O1 -fno-stack-protector"
ldflags: "-extldflags=-static"
runner: ubuntu-latest
- goos: linux
goarch: ppc64le
cgo_enabled: "1"
musl_target: powerpc64le-linux-musl
cflags: "-O1 -fno-stack-protector"
ldflags: "-extldflags=-static"
runner: ubuntu-latest
- goos: linux
goarch: arm
cgo_enabled: "1"
musl_target: arm-linux-musleabihf
cflags: "-O1 -fno-stack-protector"
ldflags: "-extldflags=-latomic -static"
runner: ubuntu-latest
- goos: windows
goarch: amd64
cgo_enabled: "1"
cc: x86_64-w64-mingw32-gcc
cflags: "-O2 -static-libgcc -static-libstdc++"
ldflags: "-extldflags=-static"
packages: "build-essential gcc-mingw-w64-x86-64"
runner: ubuntu-latest
- goos: windows
goarch: 386
cgo_enabled: "1"
cc: i686-w64-mingw32-gcc
cflags: "-O2 -static-libgcc -static-libstdc++"
ldflags: "-extldflags=-static"
packages: "build-essential gcc-mingw-w64-i686"
runner: ubuntu-latest
- goos: windows
goarch: arm64
cgo_enabled: "0"
ldflags: "-s -w"
packages: "build-essential"
runner: ubuntu-latest
- goos: darwin
goarch: amd64
cgo_enabled: "0"
ldflags: "-s -w"
runner: macos-latest
- goos: darwin
goarch: arm64
cgo_enabled: "0"
ldflags: "-s -w"
runner: macos-latest
- goos: linux
goarch: s390x
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
- goos: linux
goarch: mips
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
- goos: linux
goarch: mipsle
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
- goos: linux
goarch: ppc64
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
- goos: freebsd
goarch: amd64
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
- goos: freebsd
goarch: arm64
cgo_enabled: "0"
ldflags: "-s -w"
runner: ubuntu-latest
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.25.3
- name: Configure Git for Private Modules
run: |
git config --global url."https://${{ secrets.GHT }}@github.com/".insteadOf "https://github.com/"
git config --global url."git@github.com:".insteadOf "https://github.com/"
env:
GITHUB_TOKEN: ${{ secrets.GHT }}
- name: Download musl toolchain
if: matrix.musl_target != ''
uses: actions/download-artifact@v4
with:
name: musl-${{ matrix.musl_target }}-toolchain
- name: Setup musl toolchain
if: matrix.musl_target != ''
run: |
sudo tar -xzf musl-${{ matrix.musl_target }}-toolchain.tar.gz -C /opt/
echo "/opt/musl-${{ matrix.musl_target }}/bin" >> $GITHUB_PATH
# Verify toolchain is working
/opt/musl-${{ matrix.musl_target }}/bin/${{ matrix.musl_target }}-gcc --version
# Test compiler
echo 'int main() { return 0; }' > test.c
/opt/musl-${{ matrix.musl_target }}/bin/${{ matrix.musl_target }}-gcc ${{ matrix.cflags }} test.c -o test
rm -f test.c test
- name: Install cross-compilation tools (non-musl)
if: matrix.runner != 'macos-latest' && matrix.musl_target == ''
run: |
sudo systemctl restart systemd-resolved || true
sudo apt-get update -qq || (sleep 10 && sudo apt-get update -qq)
case "${{ matrix.goos }}-${{ matrix.goarch }}" in
windows-amd64|windows-386)
for i in 1 2 3; do
sudo apt-get install -y build-essential gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 && break || sleep 10
done ;;
*)
sudo systemctl restart systemd-resolved || true
for i in 1 2 3; do
sudo apt-get install -y build-essential && break || sleep 10
done ;;
esac
- name: Get latest tag
id: tag
run: |
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.1.0")
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=${TAG#v}" >> $GITHUB_OUTPUT
- name: Build Binary
env:
CGO_ENABLED: ${{ matrix.cgo_enabled }}
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_CFLAGS: ${{ matrix.cflags }}
CGO_LDFLAGS: ${{ matrix.ldflags }}
run: |
go clean -cache -modcache -testcache
# Set CC based on target
if [[ "${{ matrix.musl_target }}" != "" ]]; then
export CC="/opt/musl-${{ matrix.musl_target }}/bin/${{ matrix.musl_target }}-gcc"
export CXX="/opt/musl-${{ matrix.musl_target }}/bin/${{ matrix.musl_target }}-g++"
elif [[ "${{ matrix.cc }}" != "" ]]; then
export CC="${{ matrix.cc }}"
fi
# 设置额外的环境变量
if [[ -n "${{ matrix.goarm }}" ]]; then
export GOARM=${{ matrix.goarm }}
fi
if [[ -n "${{ matrix.gomips }}" ]]; then
export GOMIPS=${{ matrix.gomips }}
fi
# 针对 Darwin 的特殊处理
if [[ "${{ matrix.cgo_enabled }}" == "1" && "${{ matrix.goos }}" == "darwin" ]]; then
if [[ "${{ matrix.goarch }}" == "amd64" ]]; then
export CC="x86_64-apple-darwin21.4-clang"
export CXX="x86_64-apple-darwin21.4-clang++"
elif [[ "${{ matrix.goarch }}" == "arm64" ]]; then
export CC="aarch64-apple-darwin21.4-clang"
export CXX="aarch64-apple-darwin21.4-clang++"
fi
export OSXCROSS_ROOT="${OSXCROSS_ROOT}"
fi
# 清理和准备
rm -rf vendor/
go mod download
go mod tidy
mkdir -p bin
# 设置二进制文件名
BINARY_NAME="goecs"
if [[ "${{ matrix.goos }}" == "windows" ]]; then
BINARY_NAME="${BINARY_NAME}.exe"
fi
# 构建 LDFLAGS
LDFLAGS="-s -w -X main.version=${{ steps.tag.outputs.version }} -X main.arch=${{ matrix.goarch }}"
if [[ "${{ matrix.cgo_enabled }}" == "1" ]]; then
LDFLAGS="${LDFLAGS} -checklinkname=0 ${{ matrix.ldflags }}"
else
LDFLAGS="${LDFLAGS} -checklinkname=0 ${{ matrix.ldflags }}"
fi
# 执行构建
echo "Building for GOOS=$GOOS GOARCH=$GOARCH CGO_ENABLED=$CGO_ENABLED"
if [[ -n "$CC" ]]; then
echo "Using CC=$CC"
fi
go build -a -o bin/$BINARY_NAME -ldflags="$LDFLAGS" -trimpath ./
# 验证文件是否存在
[[ -f "bin/$BINARY_NAME" ]] || exit 1
# 显示构建信息
echo "Built binary: bin/$BINARY_NAME"
ls -la bin/
if command -v file >/dev/null 2>&1; then
file bin/$BINARY_NAME
fi
- name: Create ZIP archive
run: |
cd bin
BINARY_NAME="goecs"
if [[ "${{ matrix.goos }}" == "windows" ]]; then
BINARY_NAME="${BINARY_NAME}.exe"
fi
ZIP_NAME="goecs_${{ matrix.goos }}_${{ matrix.goarch }}"
if [[ -n "${{ matrix.goarm }}" ]]; then
ZIP_NAME="${ZIP_NAME}v${{ matrix.goarm }}"
fi
if [[ -n "${{ matrix.gomips }}" ]]; then
ZIP_NAME="${ZIP_NAME}_${{ matrix.gomips }}"
fi
ZIP_NAME="${ZIP_NAME}.zip"
zip "$ZIP_NAME" "$BINARY_NAME"
- name: Upload to Release
run: |
TAG="${{ steps.tag.outputs.tag }}"
RELEASE_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" "https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG" | jq -r '.id')
cd bin
for file in *.zip; do
if [[ -f "$file" ]]; then
curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" \
-H "Content-Type: application/zip" \
--data-binary @"$file" \
"https://uploads.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets?name=$file"
fi
done
checksums:
name: Generate Checksums
runs-on: ubuntu-latest
needs: release-binary
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get latest tag
id: tag
run: |
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.1.0")
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Download release assets
run: |
TAG="${{ steps.tag.outputs.tag }}"
RELEASE_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" "https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG" | jq -r '.id')
mkdir -p assets
ASSETS=$(curl -s -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets")
echo "$ASSETS" | jq -r '.[] | select(.name | endswith(".zip")) | .browser_download_url' | while read url; do
filename=$(basename "$url")
curl -L -H "Authorization: Bearer ${{ secrets.GHT }}" "$url" -o "assets/$filename"
done
- name: Generate checksums
run: |
cd assets
sha256sum *.zip > checksums.txt
if [[ -f "../goecs.sh" ]]; then
sha256sum ../goecs.sh >> checksums.txt
fi
- name: Upload checksums
run: |
TAG="${{ steps.tag.outputs.tag }}"
RELEASE_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" "https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG" | jq -r '.id')
curl -s -H "Authorization: Bearer ${{ secrets.GHT }}" \
-H "Content-Type: text/plain" \
--data-binary @assets/checksums.txt \
"https://uploads.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets?name=checksums.txt"
update-script:
name: Update Script Version
runs-on: ubuntu-latest
needs: checksums
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get latest tag
id: tag
run: |
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.1.0")
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=${TAG#v}" >> $GITHUB_OUTPUT
- name: Update goecs.sh version
run: |
VERSION="${{ steps.tag.outputs.version }}"
BRANCH="master"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global --unset url."git@github.com:".insteadOf || true
git fetch origin $BRANCH
git checkout $BRANCH
if [ -f "goecs.sh" ]; then
sed -i "s/\(_yellow \"Unable to get version info, using default version \).*\(\".*\)/\1$VERSION\2/" "goecs.sh"
sed -i "s/\(ECS_VERSION=\"\).*\(\"\)/\1$VERSION\2/" "goecs.sh"
if ! git diff --quiet "goecs.sh"; then
git add "goecs.sh"
git commit -m "chore: update ECS_VERSION to $VERSION in goecs.sh"
git push origin $BRANCH
fi
fi
env:
GITHUB_TOKEN: ${{ secrets.GHT }}

View File

@@ -1,11 +0,0 @@
package commediatest
import (
"fmt"
"github.com/oneclickvirt/CommonMediaTests/commediatests"
)
func ComMediaTest(language string) {
res := commediatests.MediaTests(language)
fmt.Print(res)
}

View File

@@ -1,10 +0,0 @@
package commediatest
import (
"testing"
)
// 本包仅测试无实际使用
func TestMedia(t *testing.T) {
ComMediaTest("zh")
}

View File

@@ -1,8 +0,0 @@
package network1
import "github.com/oneclickvirt/security/network"
// 本包在main中不使用
func NetworkCheck(checkType string, enableSecurityCheck bool, language string) (string, string, error) {
return network.NetworkCheck(checkType, enableSecurityCheck, language)
}

View File

@@ -1,22 +0,0 @@
package network1
import (
"fmt"
"testing"
)
func TestIpv4SecurityCheck(t *testing.T) {
// 单项测试
//result1, _ := Ipv4SecurityCheck("8.8.8.8", nil, "zh")
//fmt.Println(result1)
//result2, _ := Ipv6SecurityCheck("2001:4860:4860::8844", nil, "zh")
//fmt.Println(result2)
// 全项测试
ipInfo, securityInfo, _ := NetworkCheck("both", true, "zh")
fmt.Println("--------------------------------------------------")
fmt.Print(ipInfo)
fmt.Println("--------------------------------------------------")
fmt.Print(securityInfo)
fmt.Println("--------------------------------------------------")
}

View File

@@ -1,13 +0,0 @@
package port
import (
"fmt"
"github.com/oneclickvirt/portchecker/email"
)
// 常用端口阻断检测 TCP/UDP/ICMP 协议
// 本包不在main中使用
func EmailCheck() {
res := email.EmailCheck()
fmt.Println(res)
}

View File

@@ -1,9 +0,0 @@
package port
import (
"testing"
)
func Test(t *testing.T) {
EmailCheck()
}

View File

@@ -54,8 +54,10 @@ jobs:
- name: Create public branch
run: |
git checkout -b public || git checkout public
git merge ${{ github.ref_name }} --no-edit || true
# 删除本地 public 分支(如果存在)
git branch -D public 2>/dev/null || true
# 基于当前分支创建新的 public 分支(完全覆盖)
git checkout -b public
- name: Remove security package references
run: |

5
.gitignore vendored
View File

@@ -1,2 +1,5 @@
vendor/
.idea/
.idea/
ecs
goecs.txt
*.log

View File

@@ -1,10 +1,24 @@
# syntax=docker/dockerfile:1
FROM alpine:latest
# 安装必要的工具
RUN apk add --no-cache wget curl bash
RUN apk add --no-cache bind-tools
# --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main
RUN apk add --no-cache grep openssl ca-certificates uuidgen
RUN apk update && apk add --no-cache wget curl bash || \
(echo "Standard repo failed, trying edge repo..." && \
apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main wget curl bash)
RUN apk add --no-cache bind-tools || \
(echo "Standard repo failed for bind-tools, trying edge repo..." && \
apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main bind-tools)
RUN apk add --no-cache grep openssl ca-certificates || \
(echo "Standard repo failed, trying edge repo..." && \
apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main grep openssl ca-certificates)
RUN apk add --no-cache uuidgen || \
apk add --no-cache util-linux || \
(echo "Standard repo failed for uuidgen, trying edge repo..." && \
apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main uuidgen) || \
apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main util-linux
RUN export noninteractive=true
# 下载并执行 goecs.sh 脚本
RUN curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && \

View File

@@ -45,7 +45,6 @@ Shell 版本:[https://github.com/spiritLHLS/ecs](https://github.com/spiritLHLS
| 系统 | 说明 |
|----------------|---------------------------|
| Android(arm64) | 存在权限问题未修复非安卓系统的ARM架构无问题 |
| OpenBSD/NetBSD | 部分Goalng的官方库未支持本系统(尤其是net相关项目) |
---
@@ -205,6 +204,8 @@ Usage: goecs [options]
-email
Enable/Disable email port test (default true)
-h Show help information
-help
Show help information
-l string
Set language (supported: en, zh) (default "zh")
-log
@@ -212,26 +213,34 @@ Usage: goecs [options]
-memory
Enable/Disable memory test (default true)
-memorym string
Set memory test method (supported: sysbench, dd, winsat) (default "sysbench")
Set memory test method (supported: stream, sysbench, dd, winsat, auto) (default "stream")
-menu
Enable/Disable menu mode, disable example: -menu=false (default true)
-nt3
Enable/Disable NT3 test (in 'en' language or on windows it always false) (default true)
-nt3loc string
Specify NT3 test location (supported: GZ, SH, BJ, CD for Guangzhou, Shanghai, Beijing, Chengdu) (default "GZ")
Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all) (default "GZ")
-nt3t string
Set NT3 test type (supported: both, ipv4, ipv6) (default "ipv4")
-ping
Enable/Disable ping test
-security
Enable/Disable security test (default true)
-speed
Enable/Disable speed test (default true)
-spnum int
Set the number of servers per operator for speed test (default 2)
-tgdc
Enable/Disable Telegram DC test
-upload
Enable/Disable upload the result (default true)
-ut
Enable/Disable unlock media test (default true)
-v Display version information
-version
Display version information
-web
Enable/Disable popular websites test
```
</details>

View File

@@ -45,7 +45,6 @@ Shell version: [https://github.com/spiritLHLS/ecs/blob/main/README_EN.md](https:
| OS | Notes |
|--------|-------------------------------------------------------------------------------------------------|
| Android(arm64) | Permission issues that are not fixed, no problems with ARM architecture for non-Android systems |
| OpenBSD/NetBSD | Some of Goalng's official libraries do not support this system (especially net-related items) |
---
@@ -204,6 +203,8 @@ Usage: goecs [options]
-email
Enable/Disable email port test (default true)
-h Show help information
-help
Show help information
-l string
Set language (supported: en, zh) (default "zh")
-log
@@ -211,26 +212,34 @@ Usage: goecs [options]
-memory
Enable/Disable memory test (default true)
-memorym string
Set memory test method (supported: sysbench, dd, winsat) (default "sysbench")
Set memory test method (supported: stream, sysbench, dd, winsat, auto) (default "stream")
-menu
Enable/Disable menu mode, disable example: -menu=false (default true)
-nt3
Enable/Disable NT3 test (in 'en' language or on windows it always false) (default true)
-nt3loc string
Specify NT3 test location (supported: GZ, SH, BJ, CD for Guangzhou, Shanghai, Beijing, Chengdu) (default "GZ")
Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all) (default "GZ")
-nt3t string
Set NT3 test type (supported: both, ipv4, ipv6) (default "ipv4")
-ping
Enable/Disable ping test
-security
Enable/Disable security test (default true)
-speed
Enable/Disable speed test (default true)
-spnum int
Set the number of servers per operator for speed test (default 2)
-tgdc
Enable/Disable Telegram DC test
-upload
Enable/Disable upload the result (default true)
-ut
Enable/Disable unlock media test (default true)
-v Display version information
-version
Display version information
-web
Enable/Disable popular websites test
```
</details>

View File

@@ -17,6 +17,7 @@
- [邮件端口检测](#邮件端口检测)
- [上游及回程线路检测](#上游及回程线路检测)
- [三网回程路由检测](#三网回程路由检测)
- [PING值测试](#PING值测试)
- [就近测速](#就近测速)
## English
@@ -27,6 +28,7 @@
- [Streaming Media Unlocking](#Streaming-Media-Unlocking)
- [IP Quality Detection](#IP-Quality-Detection)
- [Email Port Detection](#Email-Port-Detection)
- [PING Testing](#PING-Testing)
- [Nearby Speed Testing](#Nearby-Speed-Testing)
## 日本語
@@ -37,12 +39,15 @@
- [ストリーミングメディアロック解除](#ストリーミングメディアロック解除)
- [IP品質検出](#IP品質検出)
- [メールポート検出](#メールポート検出)
- [PING検出](#PING検出)
- [近隣スピードテスト](#近隣スピードテスト)
---
## 中文
menu模式默认启用执行时显示菜单可选择选项测试在menu模式启用的情况下默认额外提供的CI参数设置优先级高于选项本身的预设值方便用户随时针对某个选项自行修改某些单项测试的参数设置。
### **系统基础信息**
依赖项目:[https://github.com/oneclickvirt/basics](https://github.com/oneclickvirt/basics) [https://github.com/oneclickvirt/gostun](https://github.com/oneclickvirt/gostun)
@@ -407,6 +412,20 @@ Abuser 或 Abuse 的滥用得分会直接影响机器的正常使用(中国境
有时候路由信息完全藏起来了,只知道实际使用的延迟低,实际可能也是优质线路只是查不到信息,这就没办法直接识别了。
所有测不出来失败的地址以及延迟大于等于9999ms的延迟都设为了9999延迟超过这个也证明目标延迟过大影响使用此时认为目标不可用就行。
### **PING值测试**
依赖项目:[https://github.com/oneclickvirt/pingtest](https://github.com/oneclickvirt/pingtest)
对于选项1如果启用中国模式将仅检测三网全国各省份的PING值延迟从小到大排序。如果不启用中国模式默认将不检测三网全国各省份的PING值延迟仅检测TGDC和主流网站的延迟。
对于选项6和选项10默认都进行测试。
对于中国境内的测试测试TGDC和主流跨国网站的延迟无意义所以默认不测试。
对于参数指定的状态,优先级会高于选项中默认的参数设置。
### **就近测速**
依赖项目:[https://github.com/oneclickvirt/speedtest](https://github.com/oneclickvirt/speedtest)
@@ -423,6 +442,8 @@ Abuser 或 Abuse 的滥用得分会直接影响机器的正常使用(中国境
## English
Menu mode is enabled by default, the menu is displayed to select the option test, in the case of menu mode enabled, the default additional CI parameter setting priority is higher than the preset value of the option itself, which is convenient for the user to modify the parameter settings of some single test for a certain option at any time by themselves.
### Basic System Information
Dependency project: [https://github.com/oneclickvirt/basics](https://github.com/oneclickvirt/basics) [https://github.com/oneclickvirt/gostun](https://github.com/oneclickvirt/gostun)
@@ -733,6 +754,14 @@ Dependency project: [https://github.com/oneclickvirt/portchecker](https://github
If the current host doesn't function as a mail server and doesn't send/receive emails, this project indicator can be ignored.
### PING Testing
Dependency project: [https://github.com/oneclickvirt/pingtest](https://github.com/oneclickvirt/pingtest)
Measure the latency from the current IP address to each TG data center and major websites.
All addresses that cannot be tested for failure, as well as those with latency greater than or equal to 9999ms, have their latency set to 9999. Latency exceeding this threshold also indicates excessive target latency that impairs usability. At this point, the target should be considered unavailable.
### Nearby Speed Testing
Dependency project: [https://github.com/oneclickvirt/speedtest](https://github.com/oneclickvirt/speedtest)
@@ -747,6 +776,8 @@ In daily use, I prefer to use servers with 1Gbps bandwidth, at least the speed o
## 日本語
メニューモードはデフォルトで有効化されており、実行時にメニューを表示してオプションテストを選択できます。メニューモードが有効な場合、デフォルトで追加提供されるCIパラメータ設定はオプション自体のプリセット値よりも優先度が高く、ユーザーが特定のオプションに対して随時個別のテストパラメータ設定を変更できるようにします。
### システム基本情報
依存プロジェクト:[https://github.com/oneclickvirt/basics](https://github.com/oneclickvirt/basics) [https://github.com/oneclickvirt/gostun](https://github.com/oneclickvirt/gostun)
@@ -1057,6 +1088,14 @@ IPタイプの分類について詳しく説明する必要がある
現在のホストがメール局として機能せず、電子メールの送受信を行わない場合、この項目指標は無視して構いません。
### PING検出
依存プロジェクト:[https://github.com/oneclickvirt/pingtest](https://github.com/oneclickvirt/pingtest)
現在のIPアドレスからTGの各データセンターおよび主要ウェブサイトまでの遅延を測定します。
検出不能な失敗アドレスおよび遅延が9999ms以上のものは、遅延を9999に設定する。この値を超える遅延は対象の遅延が過大で利用に影響することを示すため、この時点で対象は利用不可と判断すればよい。
### 近隣スピードテスト
依存プロジェクト:[https://github.com/oneclickvirt/speedtest](https://github.com/oneclickvirt/speedtest)

View File

@@ -1,11 +0,0 @@
package cputest
import (
"fmt"
"testing"
)
func Test(t *testing.T) {
_, res := CpuTest("zh", "sysbench", "1")
fmt.Print(res)
}

View File

@@ -1,11 +0,0 @@
package disktest
import (
"fmt"
"testing"
)
func TestDiskIoTest(t *testing.T) {
_, res := DiskTest("zh", "sysbench", "", false, false)
fmt.Print(res)
}

8
go.mod
View File

@@ -6,18 +6,18 @@ require (
github.com/imroc/req/v3 v3.54.0
github.com/oneclickvirt/CommonMediaTests v0.0.4-20250329123841
github.com/oneclickvirt/UnlockTests v0.0.30-20251030112700
github.com/oneclickvirt/backtrace v0.0.7-20250811023541
github.com/oneclickvirt/backtrace v0.0.8-20251102140847
github.com/oneclickvirt/basics v0.0.16-20251030093657
github.com/oneclickvirt/cputest v0.0.12-20250720122317
github.com/oneclickvirt/defaultset v0.0.2-20240624082446
github.com/oneclickvirt/disktest v0.0.10-20250924030424
github.com/oneclickvirt/gostun v0.0.5-20250727155022
github.com/oneclickvirt/memorytest v0.0.10-20250924154648
github.com/oneclickvirt/nt3 v0.0.9-20251031045617
github.com/oneclickvirt/pingtest v0.0.8-20250728015259
github.com/oneclickvirt/nt3 v0.0.10-20251104114914
github.com/oneclickvirt/pingtest v0.0.9-20251104112920
github.com/oneclickvirt/portchecker v0.0.3-20250728015900
github.com/oneclickvirt/security v0.0.7-20251030094114
github.com/oneclickvirt/speedtest v0.0.10-20250728015734
github.com/oneclickvirt/speedtest v0.0.11-20251102151740
)
require (

16
go.sum
View File

@@ -98,8 +98,8 @@ github.com/oneclickvirt/CommonMediaTests v0.0.4-20250329123841 h1:Zef93z9UiZQwRA
github.com/oneclickvirt/CommonMediaTests v0.0.4-20250329123841/go.mod h1:DAmFPRjFV5p9fEzUUSml5jJGn2f1NZJQCzTxITHDjc4=
github.com/oneclickvirt/UnlockTests v0.0.30-20251030112700 h1:dtHHyWZdshox8StzrjgImj3Rvqwup5OZJ8FAZZQjPIA=
github.com/oneclickvirt/UnlockTests v0.0.30-20251030112700/go.mod h1:oOa6wj/qECtRMxwBO6D7o0L0F0Q/5sQ747OCnFQqoGE=
github.com/oneclickvirt/backtrace v0.0.7-20250811023541 h1:GzkzvUC6U9b6Dkz/Bl4JRPeQ7XBGoW7Qw1aWqzhF+MQ=
github.com/oneclickvirt/backtrace v0.0.7-20250811023541/go.mod h1:/+KUtOWz48TyiTTbhVTsp3D6b5WY+4pCgvFBYtUGtns=
github.com/oneclickvirt/backtrace v0.0.8-20251102140847 h1:OiwD06+Mql3pYP8jJFFdP3OUlCrx/qJT7y2YCd/OMiI=
github.com/oneclickvirt/backtrace v0.0.8-20251102140847/go.mod h1:mj9TSow7FNszBb3bQj2Hhm41LwBo7HQP6sgaPtovKdM=
github.com/oneclickvirt/basics v0.0.16-20251030093657 h1:6SWWILNjJfMTXbspqYRpktUEOe/QIVhGonKO8ODC7n4=
github.com/oneclickvirt/basics v0.0.16-20251030093657/go.mod h1:2PV+1ge01zb0Sqzj2V2I7P0wAdFSLF1XgAiumchJJbg=
github.com/oneclickvirt/cputest v0.0.12-20250720122317 h1:toiwAK1hZE5b8klu2mOQ7J4sv5yV9lpPKwgPahfRYBQ=
@@ -118,16 +118,16 @@ github.com/oneclickvirt/mbw v0.0.1-20250808061222 h1:WGXOe6QvHiDRhPVMI0VcctjzW08
github.com/oneclickvirt/mbw v0.0.1-20250808061222/go.mod h1:0Vq6NRpyLmGUdfHfL3uDcFsuZhi7KlG+OCs5ky2757Y=
github.com/oneclickvirt/memorytest v0.0.10-20250924154648 h1:trk6oZ7xs1eVtr+6oIv5IX8LDVtEMG+E6GVzQ810BtU=
github.com/oneclickvirt/memorytest v0.0.10-20250924154648/go.mod h1:4kiHsEWkW9r3/1ZcV5xIweU0smiKP0IRfQj74AUIiVI=
github.com/oneclickvirt/nt3 v0.0.9-20251031045617 h1:g42R4kwOL4s6sDsG+axEXJsI82pldv42HLMYCDFypNg=
github.com/oneclickvirt/nt3 v0.0.9-20251031045617/go.mod h1:yo1ufkduFt9QjqG7nqSUf1D3YlQOmFpdlTYniJfclQI=
github.com/oneclickvirt/pingtest v0.0.8-20250728015259 h1:egoxZRZBOWN3JqBwqEsULDyRo2/dpGMeWcmV3U87zig=
github.com/oneclickvirt/pingtest v0.0.8-20250728015259/go.mod h1:gxwsxxwitNQiGq2OI0ZogYoOLwc8DtuOdSRe6/EvRqs=
github.com/oneclickvirt/nt3 v0.0.10-20251104114914 h1:P4nbdKcIA7+FH4feLLxfiJVf1f3ENi/oo37EsdyX9oI=
github.com/oneclickvirt/nt3 v0.0.10-20251104114914/go.mod h1:yo1ufkduFt9QjqG7nqSUf1D3YlQOmFpdlTYniJfclQI=
github.com/oneclickvirt/pingtest v0.0.9-20251104112920 h1:j3Fjhy0YHT/VF7iuAVVELaRXkquvRd64tWWfFLJs01o=
github.com/oneclickvirt/pingtest v0.0.9-20251104112920/go.mod h1:gxwsxxwitNQiGq2OI0ZogYoOLwc8DtuOdSRe6/EvRqs=
github.com/oneclickvirt/portchecker v0.0.3-20250728015900 h1:AomzdppSOFB70AJESQhlp0IPbsHTTJGimAWDk2TzCWM=
github.com/oneclickvirt/portchecker v0.0.3-20250728015900/go.mod h1:9sjMDPCd4Z40wkYB0S9gQPGH8YPtnNE1ZJthVIuHUzA=
github.com/oneclickvirt/security v0.0.7-20251030094114 h1:Ax8J1TYqprXyiWNAIJJ3xhoyGhvBlKw4m9j6va5Q2nM=
github.com/oneclickvirt/security v0.0.7-20251030094114/go.mod h1:YfDilPFW22szjdUNgv4VOuSwHnZzsFsdPOfRYiMoc3I=
github.com/oneclickvirt/speedtest v0.0.10-20250728015734 h1:HKO7/JQ74ueXA8Wo8NIvcK9DphbEG/YTfAAVz/akSiY=
github.com/oneclickvirt/speedtest v0.0.10-20250728015734/go.mod h1:0W8vnMbA3iucXLXFdGfe9Ia6RPS0izRO7jvu/SnH1P8=
github.com/oneclickvirt/speedtest v0.0.11-20251102151740 h1:1NUrNt5ay6/xVNC5x62UrQjPqK8jgbKtyjBml/3boZg=
github.com/oneclickvirt/speedtest v0.0.11-20251102151740/go.mod h1:fy0II2Wo7kDWVBKTwcHdodZwyfmJo0g8N9V02EwQDZE=
github.com/oneclickvirt/stream v0.0.2-20250924154001 h1:GuJWdiPkoK84+y/+oHKr2Ghl3c/MzS9Z5m1nM+lMmy4=
github.com/oneclickvirt/stream v0.0.2-20250924154001/go.mod h1:oWaizaHTC2VQciBC9RfaLbAOf8qeR6n20/gY7QxriDE=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=

854
goecs.go
View File

@@ -1,16 +1,11 @@
package main
import (
"bufio"
"context"
"flag"
"fmt"
"net/http"
"os"
"os/signal"
"regexp"
"runtime"
"strings"
"sync"
"syscall"
"time"
@@ -21,151 +16,25 @@ import (
basicmodel "github.com/oneclickvirt/basics/model"
cputestmodel "github.com/oneclickvirt/cputest/model"
disktestmodel "github.com/oneclickvirt/disktest/disk"
"github.com/oneclickvirt/ecs/cputest"
"github.com/oneclickvirt/ecs/disktest"
"github.com/oneclickvirt/ecs/memorytest"
"github.com/oneclickvirt/ecs/nexttrace"
"github.com/oneclickvirt/ecs/speedtest"
"github.com/oneclickvirt/ecs/unlocktest"
"github.com/oneclickvirt/ecs/upstreams"
menu "github.com/oneclickvirt/ecs/internal/menu"
params "github.com/oneclickvirt/ecs/internal/params"
"github.com/oneclickvirt/ecs/internal/runner"
"github.com/oneclickvirt/ecs/utils"
gostunmodel "github.com/oneclickvirt/gostun/model"
memorytestmodel "github.com/oneclickvirt/memorytest/memory"
nt3model "github.com/oneclickvirt/nt3/model"
ptmodel "github.com/oneclickvirt/pingtest/model"
"github.com/oneclickvirt/pingtest/pt"
"github.com/oneclickvirt/portchecker/email"
speedtestmodel "github.com/oneclickvirt/speedtest/model"
)
var (
ecsVersion = "v0.1.93"
menuMode bool
onlyChinaTest bool
input, choice string
showVersion bool
enableLogger bool
language string
cpuTestMethod, cpuTestThreadMode string
memoryTestMethod string
diskTestMethod, diskTestPath string
diskMultiCheck bool
nt3CheckType, nt3Location string
spNum int
width = 82
basicStatus, cpuTestStatus, memoryTestStatus, diskTestStatus bool
commTestStatus, utTestStatus, securityTestStatus, emailTestStatus bool
backtraceStatus, nt3Status, speedTestStatus, pingTestStatus bool
autoChangeDiskTestMethod = true
filePath = "goecs.txt"
enabelUpload = true
onlyIpInfoCheckStatus, help bool
goecsFlag = flag.NewFlagSet("goecs", flag.ContinueOnError)
finish bool
ecsVersion = "v0.1.99" // 融合怪版本号
configs = params.NewConfig(ecsVersion) // 全局配置实例
userSetFlags = make(map[string]bool) // 用于跟踪哪些参数是用户显式设置的
)
func getMenuChoice(language string) string {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(sigChan)
inputChan := make(chan string, 1)
go func() {
select {
case <-sigChan:
fmt.Println("\n程序在选择过程中被用户中断")
os.Exit(0)
case <-ctx.Done():
return
}
}()
for {
go func() {
var input string
fmt.Print("请输入选项 / Please enter your choice: ")
fmt.Scanln(&input)
input = strings.TrimSpace(input)
input = strings.TrimRight(input, "\n")
select {
case inputChan <- input:
case <-ctx.Done():
return
}
}()
select {
case input := <-inputChan:
re := regexp.MustCompile(`^\d+$`)
if re.MatchString(input) {
inChoice := input
switch inChoice {
case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10":
return inChoice
default:
if language == "zh" {
fmt.Println("无效的选项")
} else {
fmt.Println("Invalid choice")
}
}
} else {
if language == "zh" {
fmt.Println("输入错误,请输入一个纯数字")
} else {
fmt.Println("Invalid input, please enter a number")
}
}
case <-ctx.Done():
return ""
}
}
}
func parseFlags() {
goecsFlag.BoolVar(&help, "h", false, "Show help information")
goecsFlag.BoolVar(&showVersion, "v", false, "Display version information")
goecsFlag.BoolVar(&menuMode, "menu", true, "Enable/Disable menu mode, disable example: -menu=false") // true 默认启用菜单栏模式
goecsFlag.StringVar(&language, "l", "zh", "Set language (supported: en, zh)")
goecsFlag.BoolVar(&basicStatus, "basic", true, "Enable/Disable basic test")
goecsFlag.BoolVar(&cpuTestStatus, "cpu", true, "Enable/Disable CPU test")
goecsFlag.BoolVar(&memoryTestStatus, "memory", true, "Enable/Disable memory test")
goecsFlag.BoolVar(&diskTestStatus, "disk", true, "Enable/Disable disk test")
goecsFlag.BoolVar(&commTestStatus, "comm", true, "Enable/Disable common media test")
goecsFlag.BoolVar(&utTestStatus, "ut", true, "Enable/Disable unlock media test")
goecsFlag.BoolVar(&securityTestStatus, "security", true, "Enable/Disable security test")
goecsFlag.BoolVar(&emailTestStatus, "email", true, "Enable/Disable email port test")
goecsFlag.BoolVar(&backtraceStatus, "backtrace", true, "Enable/Disable backtrace test (in 'en' language or on windows it always false)")
goecsFlag.BoolVar(&nt3Status, "nt3", true, "Enable/Disable NT3 test (in 'en' language or on windows it always false)")
goecsFlag.BoolVar(&speedTestStatus, "speed", true, "Enable/Disable speed test")
goecsFlag.StringVar(&cpuTestMethod, "cpum", "sysbench", "Set CPU test method (supported: sysbench, geekbench, winsat)")
goecsFlag.StringVar(&cpuTestThreadMode, "cput", "multi", "Set CPU test thread mode (supported: single, multi)")
goecsFlag.StringVar(&memoryTestMethod, "memorym", "stream", "Set memory test method (supported: stream, sysbench, dd, winsat, auto)")
goecsFlag.StringVar(&diskTestMethod, "diskm", "fio", "Set disk test method (supported: fio, dd, winsat)")
goecsFlag.StringVar(&diskTestPath, "diskp", "", "Set disk test path, e.g., -diskp /root")
goecsFlag.BoolVar(&diskMultiCheck, "diskmc", false, "Enable/Disable multiple disk checks, e.g., -diskmc=false")
goecsFlag.StringVar(&nt3Location, "nt3loc", "GZ", "Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all)")
goecsFlag.StringVar(&nt3CheckType, "nt3t", "ipv4", "Set NT3 test type (supported: both, ipv4, ipv6)")
goecsFlag.IntVar(&spNum, "spnum", 2, "Set the number of servers per operator for speed test")
goecsFlag.BoolVar(&enableLogger, "log", false, "Enable/Disable logging in the current path")
goecsFlag.BoolVar(&enabelUpload, "upload", true, "Enable/Disable upload the result")
goecsFlag.Parse(os.Args[1:])
}
func handleHelpAndVersion() bool {
if help {
fmt.Printf("Usage: %s [options]\n", os.Args[0])
goecsFlag.PrintDefaults()
return true
}
if showVersion {
fmt.Println(ecsVersion)
return true
}
return false
}
func initLogger() {
if enableLogger {
if configs.EnableLogger {
gostunmodel.EnableLoger = true
basicmodel.EnableLoger = true
cputestmodel.EnableLoger = true
@@ -180,694 +49,19 @@ func initLogger() {
}
}
func handleMenuMode(preCheck utils.NetCheckResult) {
basicStatus, cpuTestStatus, memoryTestStatus, diskTestStatus = false, false, false, false
commTestStatus, utTestStatus, securityTestStatus, emailTestStatus = false, false, false, false
backtraceStatus, nt3Status, speedTestStatus = false, false, false
autoChangeDiskTestMethod = true
printMenuOptions(preCheck)
Loop:
for {
choice = getMenuChoice(language)
switch choice {
case "0":
os.Exit(0)
case "1":
setFullTestStatus(preCheck)
onlyChinaTest = utils.CheckChina(enableLogger)
break Loop
case "2":
setMinimalTestStatus(preCheck)
break Loop
case "3":
setStandardTestStatus(preCheck)
break Loop
case "4":
setNetworkFocusedTestStatus(preCheck)
break Loop
case "5":
setUnlockFocusedTestStatus(preCheck)
break Loop
case "6":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
setNetworkOnlyTestStatus()
break Loop
case "7":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
setUnlockOnlyTestStatus()
break Loop
case "8":
setHardwareOnlyTestStatus(preCheck)
break Loop
case "9":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
setIPQualityTestStatus()
break Loop
case "10":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
nt3Location = "ALL"
setRouteTestStatus()
break Loop
default:
printInvalidChoice()
}
}
}
func printMenuOptions(preCheck utils.NetCheckResult) {
var stats *utils.StatsResponse
var statsErr error
var githubInfo *utils.GitHubRelease
var githubErr error
// 只有在网络连接正常时才获取统计信息和版本信息
if preCheck.Connected {
var pwg sync.WaitGroup
pwg.Add(2)
go func() {
defer pwg.Done()
stats, statsErr = utils.GetGoescStats()
}()
go func() {
defer pwg.Done()
githubInfo, githubErr = utils.GetLatestEcsRelease()
}()
pwg.Wait()
} else {
statsErr = fmt.Errorf("network not connected")
githubErr = fmt.Errorf("network not connected")
}
var statsInfo string
var cmp int
if preCheck.Connected {
// 网络连接正常时处理统计信息和版本比较
if statsErr != nil {
statsInfo = "NULL"
} else {
switch language {
case "zh":
statsInfo = fmt.Sprintf("总使用量: %s | 今日使用: %s",
utils.FormatGoecsNumber(stats.Total),
utils.FormatGoecsNumber(stats.Daily))
case "en":
statsInfo = fmt.Sprintf("Total Usage: %s | Daily Usage: %s",
utils.FormatGoecsNumber(stats.Total),
utils.FormatGoecsNumber(stats.Daily))
}
}
if githubErr == nil {
cmp = utils.CompareVersions(ecsVersion, githubInfo.TagName)
} else {
cmp = 0
}
}
switch language {
case "zh":
fmt.Printf("VPS融合怪版本: %s\n", ecsVersion)
if preCheck.Connected {
switch cmp {
case -1:
fmt.Printf("检测到新版本 %s 如有必要请更新!\n", githubInfo.TagName)
}
fmt.Printf("使用统计: %s\n", statsInfo)
}
fmt.Println("1. 融合怪完全体(能测全测)")
fmt.Println("2. 极简版(系统信息+CPU+内存+磁盘+测速节点5个)")
fmt.Println("3. 精简版(系统信息+CPU+内存+磁盘+常用流媒体+路由+测速节点5个)")
fmt.Println("4. 精简网络版(系统信息+CPU+内存+磁盘+回程+路由+测速节点5个)")
fmt.Println("5. 精简解锁版(系统信息+CPU+内存+磁盘IO+御三家+常用流媒体+测速节点5个)")
fmt.Println("6. 网络单项(IP质量检测+上游及三网回程+广州三网回程详细路由+全国延迟+测速节点11个)")
fmt.Println("7. 解锁单项(御三家解锁+常用流媒体解锁)")
fmt.Println("8. 硬件单项(系统信息+CPU+dd磁盘测试+fio磁盘测试)")
fmt.Println("9. IP质量检测(15个数据库的IP质量检测+邮件端口检测)")
fmt.Println("10. 三网回程线路检测+三网回程详细路由(北京上海广州成都)+三网延迟测试(全国)")
fmt.Println("0. 退出程序")
case "en":
fmt.Printf("VPS Fusion Monster Test Version: %s\n", ecsVersion)
if preCheck.Connected {
switch cmp {
case -1:
fmt.Printf("New version detected %s update if necessary!\n", githubInfo.TagName)
}
fmt.Printf("%s\n", statsInfo)
}
fmt.Println("1. VPS Fusion Monster Test (Full Test)")
fmt.Println("2. Minimal Test Suite (System Info + CPU + Memory + Disk + 5 Speed Test Nodes)")
fmt.Println("3. Standard Test Suite (System Info + CPU + Memory + Disk + Basic Unlock Tests + 5 Speed Test Nodes)")
fmt.Println("4. Network-Focused Test Suite (System Info + CPU + Memory + Disk + 5 Speed Test Nodes)")
fmt.Println("5. Unlock-Focused Test Suite (System Info + CPU + Memory + Disk IO + Basic Unlock Tests + Common Streaming Services + 5 Speed Test Nodes)")
fmt.Println("6. Network-Only Test (IP Quality Test + 5 Speed Test Nodes)")
fmt.Println("7. Unlock-Only Test (Basic Unlock Tests + Common Streaming Services Unlock)")
fmt.Println("8. Hardware-Only Test (System Info + CPU + Memory + dd Disk Test + fio Disk Test)")
fmt.Println("9. IP Quality Test (IP Test with 15 Databases + Email Port Test)")
fmt.Println("0. Exit Program")
}
}
func setFullTestStatus(preCheck utils.NetCheckResult) {
basicStatus = true
cpuTestStatus = true
memoryTestStatus = true
diskTestStatus = true
if preCheck.Connected {
commTestStatus = true
utTestStatus = true
securityTestStatus = true
emailTestStatus = true
backtraceStatus = true
nt3Status = true
speedTestStatus = true
}
}
func setMinimalTestStatus(preCheck utils.NetCheckResult) {
basicStatus = true
cpuTestStatus = true
memoryTestStatus = true
diskTestStatus = true
if preCheck.Connected {
speedTestStatus = true
}
}
func setStandardTestStatus(preCheck utils.NetCheckResult) {
basicStatus = true
cpuTestStatus = true
memoryTestStatus = true
diskTestStatus = true
if preCheck.Connected {
utTestStatus = true
nt3Status = true
speedTestStatus = true
}
}
func setNetworkFocusedTestStatus(preCheck utils.NetCheckResult) {
basicStatus = true
cpuTestStatus = true
memoryTestStatus = true
diskTestStatus = true
if preCheck.Connected {
backtraceStatus = true
nt3Status = true
speedTestStatus = true
}
}
func setUnlockFocusedTestStatus(preCheck utils.NetCheckResult) {
basicStatus = true
cpuTestStatus = true
memoryTestStatus = true
diskTestStatus = true
if preCheck.Connected {
commTestStatus = true
utTestStatus = true
speedTestStatus = true
}
}
func setNetworkOnlyTestStatus() {
onlyIpInfoCheckStatus = true
securityTestStatus = true
speedTestStatus = true
backtraceStatus = true
nt3Status = true
pingTestStatus = true
}
func setUnlockOnlyTestStatus() {
onlyIpInfoCheckStatus = true
commTestStatus = true
utTestStatus = true
}
func setHardwareOnlyTestStatus(preCheck utils.NetCheckResult) {
_ = preCheck
basicStatus = true
cpuTestStatus = true
memoryTestStatus = true
diskTestStatus = true
securityTestStatus = false
autoChangeDiskTestMethod = false
}
func setIPQualityTestStatus() {
onlyIpInfoCheckStatus = true
securityTestStatus = true
emailTestStatus = true
}
func setRouteTestStatus() {
onlyIpInfoCheckStatus = true
backtraceStatus = true
nt3Status = true
pingTestStatus = true
}
func printInvalidChoice() {
if language == "zh" {
fmt.Println("无效的选项")
} else {
fmt.Println("Invalid choice")
}
}
func handleLanguageSpecificSettings() {
if language == "en" {
backtraceStatus = false
nt3Status = false
if configs.Language == "en" {
configs.BacktraceStatus = false
configs.Nt3Status = false
}
if !enabelUpload {
securityTestStatus = false
}
}
func handleSignalInterrupt(sig chan os.Signal, startTime *time.Time, output *string, _ string, uploadDone chan bool, outputMutex *sync.Mutex) {
select {
case <-sig:
if !finish {
endTime := time.Now()
duration := endTime.Sub(*startTime)
minutes := int(duration.Minutes())
seconds := int(duration.Seconds()) % 60
currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006")
outputMutex.Lock()
timeInfo := utils.PrintAndCapture(func() {
utils.PrintCenteredTitle("", width)
if language == "zh" {
fmt.Printf("花费 : %d 分 %d 秒\n", minutes, seconds)
fmt.Printf("时间 : %s\n", currentTime)
} else {
fmt.Printf("Cost Time : %d min %d sec\n", minutes, seconds)
fmt.Printf("Current Time : %s\n", currentTime)
}
utils.PrintCenteredTitle("", width)
}, "", "")
*output += timeInfo
finalOutput := *output
outputMutex.Unlock()
resultChan := make(chan struct {
httpURL string
httpsURL string
}, 1)
if enabelUpload {
go func() {
httpURL, httpsURL := utils.ProcessAndUpload(finalOutput, filePath, enabelUpload)
resultChan <- struct {
httpURL string
httpsURL string
}{httpURL, httpsURL}
uploadDone <- true
}()
select {
case result := <-resultChan:
if result.httpURL != "" || result.httpsURL != "" {
if language == "en" {
fmt.Printf("Upload successfully!\nHttp URL: %s\nHttps URL: %s\n", result.httpURL, result.httpsURL)
} else {
fmt.Printf("上传成功!\nHttp URL: %s\nHttps URL: %s\n", result.httpURL, result.httpsURL)
}
}
time.Sleep(100 * time.Millisecond)
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(0)
case <-time.After(30 * time.Second):
if language == "en" {
fmt.Println("Upload timeout, program exit")
} else {
fmt.Println("上传超时,程序退出")
}
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(1)
}
} else {
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(0)
}
}
os.Exit(0)
}
}
func runChineseTests(preCheck utils.NetCheckResult, wg1, wg2, wg3 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo *string, output *string, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) {
*output = runBasicTests(preCheck, basicInfo, securityInfo, *output, tempOutput, outputMutex)
*output = runCPUTest(*output, tempOutput, outputMutex)
*output = runMemoryTest(*output, tempOutput, outputMutex)
*output = runDiskTest(*output, tempOutput, outputMutex)
if onlyIpInfoCheckStatus && !basicStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runIpInfoCheck(*output, tempOutput, outputMutex)
}
if utTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" && !onlyChinaTest {
wg1.Add(1)
go func() {
defer wg1.Done()
*mediaInfo = unlocktest.MediaTest(language)
}()
}
if emailTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
wg2.Add(1)
go func() {
defer wg2.Done()
*emailInfo = email.EmailCheck()
}()
}
if (onlyChinaTest || pingTestStatus) && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
wg3.Add(1)
go func() {
defer wg3.Done()
*ptInfo = pt.PingTest()
}()
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runStreamingTests(wg1, mediaInfo, *output, tempOutput, outputMutex)
*output = runSecurityTests(*securityInfo, *output, tempOutput, outputMutex)
*output = runEmailTests(wg2, emailInfo, *output, tempOutput, outputMutex)
}
if runtime.GOOS != "windows" && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runNetworkTests(wg3, ptInfo, *output, tempOutput, outputMutex)
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runSpeedTests(*output, tempOutput, outputMutex)
}
*output = appendTimeInfo(*output, tempOutput, startTime, outputMutex)
}
func runEnglishTests(preCheck utils.NetCheckResult, wg1, wg2 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo *string, output *string, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) {
*output = runBasicTests(preCheck, basicInfo, securityInfo, *output, tempOutput, outputMutex)
*output = runCPUTest(*output, tempOutput, outputMutex)
*output = runMemoryTest(*output, tempOutput, outputMutex)
*output = runDiskTest(*output, tempOutput, outputMutex)
if onlyIpInfoCheckStatus && !basicStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runIpInfoCheck(*output, tempOutput, outputMutex)
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
if utTestStatus {
wg1.Add(1)
go func() {
defer wg1.Done()
*mediaInfo = unlocktest.MediaTest(language)
}()
}
if emailTestStatus {
wg2.Add(1)
go func() {
defer wg2.Done()
*emailInfo = email.EmailCheck()
}()
}
*output = runStreamingTests(wg1, mediaInfo, *output, tempOutput, outputMutex)
*output = runSecurityTests(*securityInfo, *output, tempOutput, outputMutex)
*output = runEmailTests(wg2, emailInfo, *output, tempOutput, outputMutex)
*output = runEnglishSpeedTests(*output, tempOutput, outputMutex)
}
*output = appendTimeInfo(*output, tempOutput, startTime, outputMutex)
}
// runIpInfoCheck 系统和网络基础信息检测不进行测试的时候该函数检测取得本机IP信息并显示(单项测试中输出)
func runIpInfoCheck(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
var ipinfo string
upstreams.IPV4, upstreams.IPV6, ipinfo = utils.OnlyBasicsIpInfo(language)
if ipinfo != "" {
if language == "zh" {
utils.PrintCenteredTitle("IP信息", width)
} else {
utils.PrintCenteredTitle("IP-Information", width)
}
fmt.Printf("%s", ipinfo)
}
}, tempOutput, output)
}
func runBasicTests(preCheck utils.NetCheckResult, basicInfo, securityInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
utils.PrintHead(language, width, ecsVersion)
if basicStatus || securityTestStatus {
if basicStatus {
if language == "zh" {
utils.PrintCenteredTitle("系统基础信息", width)
} else {
utils.PrintCenteredTitle("System-Basic-Information", width)
}
}
if preCheck.Connected && preCheck.StackType == "DualStack" {
upstreams.IPV4, upstreams.IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, nt3CheckType, securityTestStatus)
} else if preCheck.Connected && preCheck.StackType == "IPv4" {
upstreams.IPV4, upstreams.IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "ipv4", securityTestStatus)
} else if preCheck.Connected && preCheck.StackType == "IPv6" {
upstreams.IPV4, upstreams.IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "ipv6", securityTestStatus)
} else {
upstreams.IPV4, upstreams.IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "", false)
securityTestStatus = false
}
if basicStatus {
fmt.Printf("%s", *basicInfo)
} else if (input == "6" || input == "9") && securityTestStatus {
scanner := bufio.NewScanner(strings.NewReader(*basicInfo))
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "IPV") {
fmt.Println(line)
}
}
}
}
}, tempOutput, output)
}
func runCPUTest(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if cpuTestStatus {
realTestMethod, res := cputest.CpuTest(language, cpuTestMethod, cpuTestThreadMode)
if language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("CPU测试-通过%s测试", realTestMethod), width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("CPU-Test--%s-Method", realTestMethod), width)
}
fmt.Print(res)
}
}, tempOutput, output)
}
func runMemoryTest(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if memoryTestStatus {
realTestMethod, res := memorytest.MemoryTest(language, memoryTestMethod)
if language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("内存测试-通过%s测试", realTestMethod), width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Memory-Test--%s-Method", realTestMethod), width)
}
fmt.Print(res)
}
}, tempOutput, output)
}
func runDiskTest(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if diskTestStatus && autoChangeDiskTestMethod {
realTestMethod, res := disktest.DiskTest(language, diskTestMethod, diskTestPath, diskMultiCheck, autoChangeDiskTestMethod)
if language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", realTestMethod), width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", realTestMethod), width)
}
fmt.Print(res)
} else if diskTestStatus && !autoChangeDiskTestMethod {
if language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", "dd"), width)
_, res := disktest.DiskTest(language, "dd", diskTestPath, diskMultiCheck, autoChangeDiskTestMethod)
fmt.Print(res)
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", "fio"), width)
_, res = disktest.DiskTest(language, "fio", diskTestPath, diskMultiCheck, autoChangeDiskTestMethod)
fmt.Print(res)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", "dd"), width)
_, res := disktest.DiskTest(language, "dd", diskTestPath, diskMultiCheck, autoChangeDiskTestMethod)
fmt.Print(res)
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", "fio"), width)
_, res = disktest.DiskTest(language, "fio", diskTestPath, diskMultiCheck, autoChangeDiskTestMethod)
fmt.Print(res)
}
}
}, tempOutput, output)
}
func runStreamingTests(wg1 *sync.WaitGroup, mediaInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if language == "zh" {
if commTestStatus && !onlyChinaTest {
utils.PrintCenteredTitle("御三家流媒体解锁", width)
fmt.Printf("%s", commediatests.MediaTests(language))
}
}
if utTestStatus && (language == "zh" && !onlyChinaTest || language == "en") {
wg1.Wait()
if language == "zh" {
utils.PrintCenteredTitle("跨国流媒体解锁", width)
} else {
utils.PrintCenteredTitle("Cross-Border-Streaming-Media-Unlock", width)
}
fmt.Printf("%s", *mediaInfo)
}
}, tempOutput, output)
}
func runSecurityTests(securityInfo, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if securityTestStatus {
if language == "zh" {
utils.PrintCenteredTitle("IP质量检测", width)
} else {
utils.PrintCenteredTitle("IP-Quality-Check", width)
}
fmt.Printf("%s", securityInfo)
}
}, tempOutput, output)
}
func runEmailTests(wg2 *sync.WaitGroup, emailInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if emailTestStatus {
wg2.Wait()
if language == "zh" {
utils.PrintCenteredTitle("邮件端口检测", width)
} else {
utils.PrintCenteredTitle("Email-Port-Check", width)
}
fmt.Println(*emailInfo)
}
}, tempOutput, output)
}
func runNetworkTests(wg3 *sync.WaitGroup, ptInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if backtraceStatus && !onlyChinaTest {
utils.PrintCenteredTitle("上游及回程线路检测", width)
upstreams.UpstreamsCheck() // 不能在重定向的同时外部并发,此处仅可以顺序执行
}
if nt3Status && !onlyChinaTest {
utils.PrintCenteredTitle("三网回程路由检测", width)
nexttrace.NextTrace3Check(language, nt3Location, nt3CheckType) // 不能在重定向的同时外部并发,此处仅可以顺序执行
}
if (onlyChinaTest || pingTestStatus) && *ptInfo != "" {
wg3.Wait()
utils.PrintCenteredTitle("三网ICMP的PING值检测", width)
fmt.Println(*ptInfo)
}
}, tempOutput, output)
}
func runSpeedTests(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if speedTestStatus {
utils.PrintCenteredTitle("就近节点测速", width)
speedtest.ShowHead(language)
if choice == "1" || !menuMode {
speedtest.NearbySP()
speedtest.CustomSP("net", "global", 2, language)
speedtest.CustomSP("net", "cu", spNum, language)
speedtest.CustomSP("net", "ct", spNum, language)
speedtest.CustomSP("net", "cmcc", spNum, language)
} else if choice == "2" || choice == "3" || choice == "4" || choice == "5" {
speedtest.CustomSP("net", "global", 4, language)
} else if choice == "6" {
speedtest.CustomSP("net", "global", 11, language)
}
}
}, tempOutput, output)
}
func runEnglishSpeedTests(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if speedTestStatus {
utils.PrintCenteredTitle("Speed-Test", width)
speedtest.ShowHead(language)
speedtest.NearbySP()
speedtest.CustomSP("net", "global", -1, language)
}
}, tempOutput, output)
}
func appendTimeInfo(output, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
endTime := time.Now()
duration := endTime.Sub(startTime)
minutes := int(duration.Minutes())
seconds := int(duration.Seconds()) % 60
currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006")
return utils.PrintAndCapture(func() {
utils.PrintCenteredTitle("", width)
if language == "zh" {
fmt.Printf("花费 : %d 分 %d 秒\n", minutes, seconds)
fmt.Printf("时间 : %s\n", currentTime)
} else {
fmt.Printf("Cost Time : %d min %d sec\n", minutes, seconds)
fmt.Printf("Current Time : %s\n", currentTime)
}
utils.PrintCenteredTitle("", width)
}, tempOutput, output)
}
func handleUploadResults(output string) {
httpURL, httpsURL := utils.ProcessAndUpload(output, filePath, enabelUpload)
if httpURL != "" || httpsURL != "" {
if language == "en" {
fmt.Printf("Upload successfully!\nHttp URL: %s\nHttps URL: %s\n", httpURL, httpsURL)
fmt.Println("Each Test Benchmark: https://bash.spiritlhl.net/ecsguide")
} else {
fmt.Printf("上传成功!\nHttp URL: %s\nHttps URL: %s\n", httpURL, httpsURL)
fmt.Println("每项测试基准见: https://bash.spiritlhl.net/ecsguide")
}
if !configs.EnableUpload {
configs.SecurityTestStatus = false
}
}
func main() {
parseFlags()
if handleHelpAndVersion() {
configs.ParseFlags(os.Args[1:])
if configs.HandleHelpAndVersion("goecs") {
return
}
initLogger()
@@ -877,14 +71,14 @@ func main() {
http.Get("https://hits.spiritlhl.net/goecs.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false")
}
}()
if menuMode {
handleMenuMode(preCheck)
if configs.MenuMode {
menu.HandleMenuMode(preCheck, configs)
} else {
onlyIpInfoCheckStatus = true
configs.OnlyIpInfoCheck = true
}
handleLanguageSpecificSettings()
if !preCheck.Connected {
enabelUpload = false
configs.EnableUpload = false
}
var (
wg1, wg2, wg3 sync.WaitGroup
@@ -896,19 +90,19 @@ func main() {
uploadDone := make(chan bool, 1)
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
go handleSignalInterrupt(sig, &startTime, &output, tempOutput, uploadDone, &outputMutex)
switch language {
go runner.HandleSignalInterrupt(sig, configs, &startTime, &output, tempOutput, uploadDone, &outputMutex)
switch configs.Language {
case "zh":
runChineseTests(preCheck, &wg1, &wg2, &wg3, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo, &output, tempOutput, startTime, &outputMutex)
runner.RunChineseTests(preCheck, configs, &wg1, &wg2, &wg3, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo, &output, tempOutput, startTime, &outputMutex)
case "en":
runEnglishTests(preCheck, &wg1, &wg2, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &output, tempOutput, startTime, &outputMutex)
runner.RunEnglishTests(preCheck, configs, &wg1, &wg2, &wg3, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo, &output, tempOutput, startTime, &outputMutex)
default:
fmt.Println("Unsupported language")
}
if preCheck.Connected {
handleUploadResults(output)
runner.HandleUploadResults(configs, output)
}
finish = true
configs.Finish = true
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()

View File

@@ -152,7 +152,7 @@ goecs_check() {
os=$(uname -s 2>/dev/null || echo "Unknown")
arch=$(uname -m 2>/dev/null || echo "Unknown")
check_china
ECS_VERSION="0.1.91"
ECS_VERSION="0.1.98"
for api in \
"https://api.github.com/repos/oneclickvirt/ecs/releases/latest" \
"https://githubapi.spiritlhl.workers.dev/repos/oneclickvirt/ecs/releases/latest" \
@@ -164,8 +164,8 @@ goecs_check() {
sleep 1
done
if [ -z "$ECS_VERSION" ]; then
_yellow "Unable to get version info, using default version 0.1.91"
ECS_VERSION="0.1.91"
_yellow "Unable to get version info, using default version 0.1.98"
ECS_VERSION="0.1.98"
fi
version_output=""
for cmd_path in "goecs" "./goecs" "/usr/bin/goecs" "/usr/local/bin/goecs"; do

365
internal/menu/menu.go Normal file
View File

@@ -0,0 +1,365 @@
package menu
import (
"context"
"fmt"
"os"
"os/signal"
"regexp"
"strings"
"sync"
"syscall"
"github.com/oneclickvirt/ecs/internal/params"
"github.com/oneclickvirt/ecs/utils"
)
// GetMenuChoice prompts user for menu choice
func GetMenuChoice(language string) string {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(sigChan)
inputChan := make(chan string, 1)
go func() {
select {
case <-sigChan:
fmt.Println("\n程序在选择过程中被用户中断")
os.Exit(0)
case <-ctx.Done():
return
}
}()
for {
go func() {
var input string
fmt.Print("请输入选项 / Please enter your choice: ")
fmt.Scanln(&input)
input = strings.TrimSpace(input)
input = strings.TrimRight(input, "\n")
select {
case inputChan <- input:
case <-ctx.Done():
return
}
}()
select {
case input := <-inputChan:
re := regexp.MustCompile(`^\d+$`)
if re.MatchString(input) {
inChoice := input
switch inChoice {
case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10":
return inChoice
default:
if language == "zh" {
fmt.Println("无效的选项")
} else {
fmt.Println("Invalid choice")
}
}
} else {
if language == "zh" {
fmt.Println("输入错误,请输入一个纯数字")
} else {
fmt.Println("Invalid input, please enter a number")
}
}
case <-ctx.Done():
return ""
}
}
}
// PrintMenuOptions displays menu options
func PrintMenuOptions(preCheck utils.NetCheckResult, config *params.Config) {
var stats *utils.StatsResponse
var statsErr error
var githubInfo *utils.GitHubRelease
var githubErr error
if preCheck.Connected {
var pwg sync.WaitGroup
pwg.Add(2)
go func() {
defer pwg.Done()
stats, statsErr = utils.GetGoescStats()
}()
go func() {
defer pwg.Done()
githubInfo, githubErr = utils.GetLatestEcsRelease()
}()
pwg.Wait()
} else {
statsErr = fmt.Errorf("network not connected")
githubErr = fmt.Errorf("network not connected")
}
var statsInfo string
var cmp int
if preCheck.Connected {
if statsErr != nil {
statsInfo = "NULL"
} else {
switch config.Language {
case "zh":
statsInfo = fmt.Sprintf("总使用量: %s | 今日使用: %s",
utils.FormatGoecsNumber(stats.Total),
utils.FormatGoecsNumber(stats.Daily))
case "en":
statsInfo = fmt.Sprintf("Total Usage: %s | Daily Usage: %s",
utils.FormatGoecsNumber(stats.Total),
utils.FormatGoecsNumber(stats.Daily))
}
}
if githubErr == nil {
cmp = utils.CompareVersions(config.EcsVersion, githubInfo.TagName)
} else {
cmp = 0
}
}
switch config.Language {
case "zh":
fmt.Printf("VPS融合怪版本: %s\n", config.EcsVersion)
if preCheck.Connected {
switch cmp {
case -1:
fmt.Printf("检测到新版本 %s 如有必要请更新!\n", githubInfo.TagName)
}
fmt.Printf("使用统计: %s\n", statsInfo)
}
fmt.Println("1. 融合怪完全体(能测全测)")
fmt.Println("2. 极简版(系统信息+CPU+内存+磁盘+测速节点5个)")
fmt.Println("3. 精简版(系统信息+CPU+内存+磁盘+常用流媒体+路由+测速节点5个)")
fmt.Println("4. 精简网络版(系统信息+CPU+内存+磁盘+回程+路由+测速节点5个)")
fmt.Println("5. 精简解锁版(系统信息+CPU+内存+磁盘IO+御三家+常用流媒体+测速节点5个)")
fmt.Println("6. 网络单项(IP质量检测+上游及三网回程+广州三网回程详细路由+全国延迟+TGDC+网站延迟+测速节点11个)")
fmt.Println("7. 解锁单项(御三家解锁+常用流媒体解锁)")
fmt.Println("8. 硬件单项(系统信息+CPU+dd磁盘测试+fio磁盘测试)")
fmt.Println("9. IP质量检测(15个数据库的IP质量检测+邮件端口检测)")
fmt.Println("10. 三网回程线路检测+三网回程详细路由(北京上海广州成都)+全国延迟+TGDC+网站延迟")
fmt.Println("0. 退出程序")
case "en":
fmt.Printf("VPS Fusion Monster Test Version: %s\n", config.EcsVersion)
if preCheck.Connected {
switch cmp {
case -1:
fmt.Printf("New version detected %s update if necessary!\n", githubInfo.TagName)
}
fmt.Printf("%s\n", statsInfo)
}
fmt.Println("1. VPS Fusion Monster Test (Full Test)")
fmt.Println("2. Minimal Test Suite (System Info + CPU + Memory + Disk + 5 Speed Test Nodes)")
fmt.Println("3. Standard Test Suite (System Info + CPU + Memory + Disk + Basic Unlock Tests + 5 Speed Test Nodes)")
fmt.Println("4. Network-Focused Test Suite (System Info + CPU + Memory + Disk + 5 Speed Test Nodes)")
fmt.Println("5. Unlock-Focused Test Suite (System Info + CPU + Memory + Disk IO + Basic Unlock Tests + Common Streaming Services + 5 Speed Test Nodes)")
fmt.Println("6. Network-Only Test (IP Quality Test + TGDC + Websites + 11 Speed Test Nodes)")
fmt.Println("7. Unlock-Only Test (Basic Unlock Tests + Common Streaming Services Unlock)")
fmt.Println("8. Hardware-Only Test (System Info + CPU + Memory + dd Disk Test + fio Disk Test)")
fmt.Println("9. IP Quality Test (IP Test with 15 Databases + Email Port Test)")
fmt.Println("0. Exit Program")
}
}
// HandleMenuMode handles menu selection
func HandleMenuMode(preCheck utils.NetCheckResult, config *params.Config) {
savedParams := config.SaveUserSetParams()
config.BasicStatus = false
config.CpuTestStatus = false
config.MemoryTestStatus = false
config.DiskTestStatus = false
config.CommTestStatus = false
config.UtTestStatus = false
config.SecurityTestStatus = false
config.EmailTestStatus = false
config.BacktraceStatus = false
config.Nt3Status = false
config.SpeedTestStatus = false
config.TgdcTestStatus = false
config.WebTestStatus = false
config.AutoChangeDiskMethod = true
PrintMenuOptions(preCheck, config)
Loop:
for {
config.Choice = GetMenuChoice(config.Language)
switch config.Choice {
case "0":
os.Exit(0)
case "1":
SetFullTestStatus(preCheck, config)
config.OnlyChinaTest = utils.CheckChina(config.EnableLogger)
break Loop
case "2":
SetMinimalTestStatus(preCheck, config)
break Loop
case "3":
SetStandardTestStatus(preCheck, config)
break Loop
case "4":
SetNetworkFocusedTestStatus(preCheck, config)
break Loop
case "5":
SetUnlockFocusedTestStatus(preCheck, config)
break Loop
case "6":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
SetNetworkOnlyTestStatus(config)
break Loop
case "7":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
SetUnlockOnlyTestStatus(config)
break Loop
case "8":
SetHardwareOnlyTestStatus(preCheck, config)
break Loop
case "9":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
SetIPQualityTestStatus(config)
break Loop
case "10":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
config.Nt3Location = "ALL"
SetRouteTestStatus(config)
break Loop
default:
PrintInvalidChoice(config.Language)
}
}
config.RestoreUserSetParams(savedParams)
}
// SetFullTestStatus enables all tests
func SetFullTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.CommTestStatus = true
config.UtTestStatus = true
config.SecurityTestStatus = true
config.EmailTestStatus = true
config.BacktraceStatus = true
config.Nt3Status = true
config.SpeedTestStatus = true
config.TgdcTestStatus = true
config.WebTestStatus = true
}
}
// SetMinimalTestStatus sets minimal test configuration
func SetMinimalTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.SpeedTestStatus = true
}
}
// SetStandardTestStatus sets standard test configuration
func SetStandardTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.UtTestStatus = true
config.Nt3Status = true
config.SpeedTestStatus = true
}
}
// SetNetworkFocusedTestStatus sets network-focused test configuration
func SetNetworkFocusedTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.BacktraceStatus = true
config.Nt3Status = true
config.SpeedTestStatus = true
}
}
// SetUnlockFocusedTestStatus sets unlock-focused test configuration
func SetUnlockFocusedTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.CommTestStatus = true
config.UtTestStatus = true
config.SpeedTestStatus = true
}
}
// SetNetworkOnlyTestStatus sets network-only test configuration
func SetNetworkOnlyTestStatus(config *params.Config) {
config.OnlyIpInfoCheck = true
config.SecurityTestStatus = true
config.SpeedTestStatus = true
config.BacktraceStatus = true
config.Nt3Status = true
config.PingTestStatus = true
config.TgdcTestStatus = true
config.WebTestStatus = true
}
// SetUnlockOnlyTestStatus sets unlock-only test configuration
func SetUnlockOnlyTestStatus(config *params.Config) {
config.OnlyIpInfoCheck = true
config.CommTestStatus = true
config.UtTestStatus = true
}
// SetHardwareOnlyTestStatus sets hardware-only test configuration
func SetHardwareOnlyTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
_ = preCheck
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
config.SecurityTestStatus = false
config.AutoChangeDiskMethod = false
}
// SetIPQualityTestStatus sets IP quality test configuration
func SetIPQualityTestStatus(config *params.Config) {
config.OnlyIpInfoCheck = true
config.SecurityTestStatus = true
config.EmailTestStatus = true
}
// SetRouteTestStatus sets route test configuration
func SetRouteTestStatus(config *params.Config) {
config.OnlyIpInfoCheck = true
config.BacktraceStatus = true
config.Nt3Status = true
config.PingTestStatus = true
config.TgdcTestStatus = true
config.WebTestStatus = true
}
// PrintInvalidChoice prints invalid choice message
func PrintInvalidChoice(language string) {
if language == "zh" {
fmt.Println("无效的选项")
} else {
fmt.Println("Invalid choice")
}
}

369
internal/params/params.go Normal file
View File

@@ -0,0 +1,369 @@
package params
import (
"flag"
"fmt"
)
// Config holds all configuration parameters
type Config struct {
EcsVersion string
MenuMode bool
OnlyChinaTest bool
Input string
Choice string
ShowVersion bool
EnableLogger bool
Language string
CpuTestMethod string
CpuTestThreadMode string
MemoryTestMethod string
DiskTestMethod string
DiskTestPath string
DiskMultiCheck bool
Nt3CheckType string
Nt3Location string
SpNum int
Width int
BasicStatus bool
CpuTestStatus bool
MemoryTestStatus bool
DiskTestStatus bool
CommTestStatus bool
UtTestStatus bool
SecurityTestStatus bool
EmailTestStatus bool
BacktraceStatus bool
Nt3Status bool
SpeedTestStatus bool
PingTestStatus bool
TgdcTestStatus bool
WebTestStatus bool
AutoChangeDiskMethod bool
FilePath string
EnableUpload bool
OnlyIpInfoCheck bool
Help bool
Finish bool
UserSetFlags map[string]bool
GoecsFlag *flag.FlagSet
}
// NewConfig creates a new Config with default values
func NewConfig(version string) *Config {
return &Config{
EcsVersion: version,
MenuMode: true,
Language: "zh",
CpuTestMethod: "sysbench",
CpuTestThreadMode: "multi",
MemoryTestMethod: "stream",
DiskTestMethod: "fio",
SpNum: 2,
Width: 82,
BasicStatus: true,
CpuTestStatus: true,
MemoryTestStatus: true,
DiskTestStatus: true,
CommTestStatus: true,
UtTestStatus: true,
SecurityTestStatus: true,
EmailTestStatus: true,
BacktraceStatus: true,
Nt3Status: true,
SpeedTestStatus: true,
Nt3Location: "GZ",
Nt3CheckType: "ipv4",
AutoChangeDiskMethod: true,
FilePath: "goecs.txt",
EnableUpload: true,
UserSetFlags: make(map[string]bool),
GoecsFlag: flag.NewFlagSet("goecs", flag.ContinueOnError),
}
}
// ParseFlags parses command line flags
func (c *Config) ParseFlags(args []string) {
c.GoecsFlag.BoolVar(&c.Help, "h", false, "Show help information")
c.GoecsFlag.BoolVar(&c.Help, "help", false, "Show help information")
c.GoecsFlag.BoolVar(&c.ShowVersion, "v", false, "Display version information")
c.GoecsFlag.BoolVar(&c.ShowVersion, "version", false, "Display version information")
c.GoecsFlag.BoolVar(&c.MenuMode, "menu", true, "Enable/Disable menu mode, disable example: -menu=false")
c.GoecsFlag.StringVar(&c.Language, "l", "zh", "Set language (supported: en, zh)")
c.GoecsFlag.BoolVar(&c.BasicStatus, "basic", true, "Enable/Disable basic test")
c.GoecsFlag.BoolVar(&c.CpuTestStatus, "cpu", true, "Enable/Disable CPU test")
c.GoecsFlag.BoolVar(&c.MemoryTestStatus, "memory", true, "Enable/Disable memory test")
c.GoecsFlag.BoolVar(&c.DiskTestStatus, "disk", true, "Enable/Disable disk test")
c.GoecsFlag.BoolVar(&c.CommTestStatus, "comm", true, "Enable/Disable common media test")
c.GoecsFlag.BoolVar(&c.UtTestStatus, "ut", true, "Enable/Disable unlock media test")
c.GoecsFlag.BoolVar(&c.SecurityTestStatus, "security", true, "Enable/Disable security test")
c.GoecsFlag.BoolVar(&c.EmailTestStatus, "email", true, "Enable/Disable email port test")
c.GoecsFlag.BoolVar(&c.BacktraceStatus, "backtrace", true, "Enable/Disable backtrace test (in 'en' language or on windows it always false)")
c.GoecsFlag.BoolVar(&c.Nt3Status, "nt3", true, "Enable/Disable NT3 test (in 'en' language or on windows it always false)")
c.GoecsFlag.BoolVar(&c.SpeedTestStatus, "speed", true, "Enable/Disable speed test")
c.GoecsFlag.BoolVar(&c.PingTestStatus, "ping", false, "Enable/Disable ping test")
c.GoecsFlag.BoolVar(&c.TgdcTestStatus, "tgdc", false, "Enable/Disable Telegram DC test")
c.GoecsFlag.BoolVar(&c.WebTestStatus, "web", false, "Enable/Disable popular websites test")
c.GoecsFlag.StringVar(&c.CpuTestMethod, "cpum", "sysbench", "Set CPU test method (supported: sysbench, geekbench, winsat)")
c.GoecsFlag.StringVar(&c.CpuTestThreadMode, "cput", "multi", "Set CPU test thread mode (supported: single, multi)")
c.GoecsFlag.StringVar(&c.MemoryTestMethod, "memorym", "stream", "Set memory test method (supported: stream, sysbench, dd, winsat, auto)")
c.GoecsFlag.StringVar(&c.DiskTestMethod, "diskm", "fio", "Set disk test method (supported: fio, dd, winsat)")
c.GoecsFlag.StringVar(&c.DiskTestPath, "diskp", "", "Set disk test path, e.g., -diskp /root")
c.GoecsFlag.BoolVar(&c.DiskMultiCheck, "diskmc", false, "Enable/Disable multiple disk checks, e.g., -diskmc=false")
c.GoecsFlag.StringVar(&c.Nt3Location, "nt3loc", "GZ", "Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all)")
c.GoecsFlag.StringVar(&c.Nt3CheckType, "nt3t", "ipv4", "Set NT3 test type (supported: both, ipv4, ipv6)")
c.GoecsFlag.IntVar(&c.SpNum, "spnum", 2, "Set the number of servers per operator for speed test")
c.GoecsFlag.BoolVar(&c.EnableLogger, "log", false, "Enable/Disable logging in the current path")
c.GoecsFlag.BoolVar(&c.EnableUpload, "upload", true, "Enable/Disable upload the result")
c.GoecsFlag.Parse(args)
c.GoecsFlag.Visit(func(f *flag.Flag) {
c.UserSetFlags[f.Name] = true
})
}
// HandleHelpAndVersion handles help and version flags
func (c *Config) HandleHelpAndVersion(programName string) bool {
if c.Help {
fmt.Printf("Usage: %s [options]\n", programName)
c.GoecsFlag.PrintDefaults()
return true
}
if c.ShowVersion {
fmt.Println(c.EcsVersion)
return true
}
return false
}
// SaveUserSetParams saves user-set parameters
func (c *Config) SaveUserSetParams() map[string]interface{} {
saved := make(map[string]interface{})
if c.UserSetFlags["basic"] {
saved["basic"] = c.BasicStatus
}
if c.UserSetFlags["cpu"] {
saved["cpu"] = c.CpuTestStatus
}
if c.UserSetFlags["memory"] {
saved["memory"] = c.MemoryTestStatus
}
if c.UserSetFlags["disk"] {
saved["disk"] = c.DiskTestStatus
}
if c.UserSetFlags["comm"] {
saved["comm"] = c.CommTestStatus
}
if c.UserSetFlags["ut"] {
saved["ut"] = c.UtTestStatus
}
if c.UserSetFlags["security"] {
saved["security"] = c.SecurityTestStatus
}
if c.UserSetFlags["email"] {
saved["email"] = c.EmailTestStatus
}
if c.UserSetFlags["backtrace"] {
saved["backtrace"] = c.BacktraceStatus
}
if c.UserSetFlags["nt3"] {
saved["nt3"] = c.Nt3Status
}
if c.UserSetFlags["speed"] {
saved["speed"] = c.SpeedTestStatus
}
if c.UserSetFlags["ping"] {
saved["ping"] = c.PingTestStatus
}
if c.UserSetFlags["tgdc"] {
saved["tgdc"] = c.TgdcTestStatus
}
if c.UserSetFlags["web"] {
saved["web"] = c.WebTestStatus
}
if c.UserSetFlags["cpum"] {
saved["cpum"] = c.CpuTestMethod
}
if c.UserSetFlags["cput"] {
saved["cput"] = c.CpuTestThreadMode
}
if c.UserSetFlags["memorym"] {
saved["memorym"] = c.MemoryTestMethod
}
if c.UserSetFlags["diskm"] {
saved["diskm"] = c.DiskTestMethod
}
if c.UserSetFlags["diskp"] {
saved["diskp"] = c.DiskTestPath
}
if c.UserSetFlags["diskmc"] {
saved["diskmc"] = c.DiskMultiCheck
}
if c.UserSetFlags["nt3loc"] {
saved["nt3loc"] = c.Nt3Location
}
if c.UserSetFlags["nt3t"] {
saved["nt3t"] = c.Nt3CheckType
}
if c.UserSetFlags["spnum"] {
saved["spnum"] = c.SpNum
}
return saved
}
// RestoreUserSetParams restores user-set parameters
func (c *Config) RestoreUserSetParams(saved map[string]interface{}) {
if val, ok := saved["basic"]; ok {
c.BasicStatus = val.(bool)
}
if val, ok := saved["cpu"]; ok {
c.CpuTestStatus = val.(bool)
}
if val, ok := saved["memory"]; ok {
c.MemoryTestStatus = val.(bool)
}
if val, ok := saved["disk"]; ok {
c.DiskTestStatus = val.(bool)
}
if val, ok := saved["comm"]; ok {
c.CommTestStatus = val.(bool)
}
if val, ok := saved["ut"]; ok {
c.UtTestStatus = val.(bool)
}
if val, ok := saved["security"]; ok {
c.SecurityTestStatus = val.(bool)
}
if val, ok := saved["email"]; ok {
c.EmailTestStatus = val.(bool)
}
if val, ok := saved["backtrace"]; ok {
c.BacktraceStatus = val.(bool)
}
if val, ok := saved["nt3"]; ok {
c.Nt3Status = val.(bool)
}
if val, ok := saved["speed"]; ok {
c.SpeedTestStatus = val.(bool)
}
if val, ok := saved["ping"]; ok {
c.PingTestStatus = val.(bool)
}
if val, ok := saved["tgdc"]; ok {
c.TgdcTestStatus = val.(bool)
}
if val, ok := saved["web"]; ok {
c.WebTestStatus = val.(bool)
}
if val, ok := saved["cpum"]; ok {
c.CpuTestMethod = val.(string)
}
if val, ok := saved["cput"]; ok {
c.CpuTestThreadMode = val.(string)
}
if val, ok := saved["memorym"]; ok {
c.MemoryTestMethod = val.(string)
}
if val, ok := saved["diskm"]; ok {
c.DiskTestMethod = val.(string)
}
if val, ok := saved["diskp"]; ok {
c.DiskTestPath = val.(string)
}
if val, ok := saved["diskmc"]; ok {
c.DiskMultiCheck = val.(bool)
}
if val, ok := saved["nt3loc"]; ok {
if c.Choice != "10" {
c.Nt3Location = val.(string)
}
}
if val, ok := saved["nt3t"]; ok {
c.Nt3CheckType = val.(string)
}
if val, ok := saved["spnum"]; ok {
c.SpNum = val.(int)
}
c.ValidateParams()
}
// ValidateParams validates parameter values
func (c *Config) ValidateParams() {
validCpuMethods := map[string]bool{"sysbench": true, "geekbench": true, "winsat": true}
if !validCpuMethods[c.CpuTestMethod] {
if c.Language == "zh" {
fmt.Printf("警告: CPU测试方法 '%s' 无效,使用默认值 'sysbench'\n", c.CpuTestMethod)
} else {
fmt.Printf("Warning: Invalid CPU test method '%s', using default 'sysbench'\n", c.CpuTestMethod)
}
c.CpuTestMethod = "sysbench"
}
validThreadModes := map[string]bool{"single": true, "multi": true}
if !validThreadModes[c.CpuTestThreadMode] {
if c.Language == "zh" {
fmt.Printf("警告: CPU线程模式 '%s' 无效,使用默认值 'multi'\n", c.CpuTestThreadMode)
} else {
fmt.Printf("Warning: Invalid CPU thread mode '%s', using default 'multi'\n", c.CpuTestThreadMode)
}
c.CpuTestThreadMode = "multi"
}
validMemoryMethods := map[string]bool{"stream": true, "sysbench": true, "dd": true, "winsat": true, "auto": true}
if !validMemoryMethods[c.MemoryTestMethod] {
if c.Language == "zh" {
fmt.Printf("警告: 内存测试方法 '%s' 无效,使用默认值 'stream'\n", c.MemoryTestMethod)
} else {
fmt.Printf("Warning: Invalid memory test method '%s', using default 'stream'\n", c.MemoryTestMethod)
}
c.MemoryTestMethod = "stream"
}
validDiskMethods := map[string]bool{"fio": true, "dd": true, "winsat": true}
if !validDiskMethods[c.DiskTestMethod] {
if c.Language == "zh" {
fmt.Printf("警告: 磁盘测试方法 '%s' 无效,使用默认值 'fio'\n", c.DiskTestMethod)
} else {
fmt.Printf("Warning: Invalid disk test method '%s', using default 'fio'\n", c.DiskTestMethod)
}
c.DiskTestMethod = "fio"
}
validNt3Locations := map[string]bool{"GZ": true, "SH": true, "BJ": true, "CD": true, "ALL": true}
if !validNt3Locations[c.Nt3Location] {
if c.Language == "zh" {
fmt.Printf("警告: NT3测试位置 '%s' 无效,使用默认值 'GZ'\n", c.Nt3Location)
} else {
fmt.Printf("Warning: Invalid NT3 location '%s', using default 'GZ'\n", c.Nt3Location)
}
c.Nt3Location = "GZ"
}
validNt3Types := map[string]bool{"both": true, "ipv4": true, "ipv6": true}
if !validNt3Types[c.Nt3CheckType] {
if c.Language == "zh" {
fmt.Printf("警告: NT3测试类型 '%s' 无效,使用默认值 'ipv4'\n", c.Nt3CheckType)
} else {
fmt.Printf("Warning: Invalid NT3 check type '%s', using default 'ipv4'\n", c.Nt3CheckType)
}
c.Nt3CheckType = "ipv4"
}
if c.SpNum < 0 {
if c.Language == "zh" {
fmt.Printf("警告: 测速节点数量 '%d' 无效,使用默认值 2\n", c.SpNum)
} else {
fmt.Printf("Warning: Invalid speed test node count '%d', using default 2\n", c.SpNum)
}
c.SpNum = 2
}
validLanguages := map[string]bool{"zh": true, "en": true}
if !validLanguages[c.Language] {
fmt.Printf("Warning: Invalid language '%s', using default 'zh'\n", c.Language)
c.Language = "zh"
}
}

483
internal/runner/runner.go Normal file
View File

@@ -0,0 +1,483 @@
package runner
import (
"bufio"
"fmt"
"os"
"runtime"
"strings"
"sync"
"time"
"github.com/oneclickvirt/CommonMediaTests/commediatests"
"github.com/oneclickvirt/ecs/internal/params"
"github.com/oneclickvirt/ecs/internal/tests"
"github.com/oneclickvirt/ecs/utils"
"github.com/oneclickvirt/pingtest/pt"
"github.com/oneclickvirt/portchecker/email"
)
// RunChineseTests runs all tests in Chinese mode
func RunChineseTests(preCheck utils.NetCheckResult, config *params.Config, wg1, wg2, wg3 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo *string, output *string, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) {
*output = RunBasicTests(preCheck, config, basicInfo, securityInfo, *output, tempOutput, outputMutex)
*output = RunCPUTest(config, *output, tempOutput, outputMutex)
*output = RunMemoryTest(config, *output, tempOutput, outputMutex)
*output = RunDiskTest(config, *output, tempOutput, outputMutex)
if config.OnlyIpInfoCheck && !config.BasicStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunIpInfoCheck(config, *output, tempOutput, outputMutex)
}
if config.UtTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" && !config.OnlyChinaTest {
wg1.Add(1)
go func() {
defer wg1.Done()
*mediaInfo = tests.MediaTest(config.Language)
}()
}
if config.EmailTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
wg2.Add(1)
go func() {
defer wg2.Done()
*emailInfo = email.EmailCheck()
}()
}
if (config.OnlyChinaTest || config.PingTestStatus) && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
wg3.Add(1)
go func() {
defer wg3.Done()
*ptInfo = pt.PingTest()
}()
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunStreamingTests(config, wg1, mediaInfo, *output, tempOutput, outputMutex)
*output = RunSecurityTests(config, *securityInfo, *output, tempOutput, outputMutex)
*output = RunEmailTests(config, wg2, emailInfo, *output, tempOutput, outputMutex)
}
if runtime.GOOS != "windows" && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunNetworkTests(config, wg3, ptInfo, *output, tempOutput, outputMutex)
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunSpeedTests(config, *output, tempOutput, outputMutex)
}
*output = AppendTimeInfo(config, *output, tempOutput, startTime, outputMutex)
}
// RunEnglishTests runs all tests in English mode
func RunEnglishTests(preCheck utils.NetCheckResult, config *params.Config, wg1, wg2, wg3 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo *string, output *string, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) {
*output = RunBasicTests(preCheck, config, basicInfo, securityInfo, *output, tempOutput, outputMutex)
*output = RunCPUTest(config, *output, tempOutput, outputMutex)
*output = RunMemoryTest(config, *output, tempOutput, outputMutex)
*output = RunDiskTest(config, *output, tempOutput, outputMutex)
if config.OnlyIpInfoCheck && !config.BasicStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunIpInfoCheck(config, *output, tempOutput, outputMutex)
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
if config.UtTestStatus {
wg1.Add(1)
go func() {
defer wg1.Done()
*mediaInfo = tests.MediaTest(config.Language)
}()
}
if config.EmailTestStatus {
wg2.Add(1)
go func() {
defer wg2.Done()
*emailInfo = email.EmailCheck()
}()
}
*output = RunStreamingTests(config, wg1, mediaInfo, *output, tempOutput, outputMutex)
*output = RunSecurityTests(config, *securityInfo, *output, tempOutput, outputMutex)
*output = RunEmailTests(config, wg2, emailInfo, *output, tempOutput, outputMutex)
*output = RunEnglishNetworkTests(config, wg3, ptInfo, *output, tempOutput, outputMutex)
*output = RunEnglishSpeedTests(config, *output, tempOutput, outputMutex)
}
*output = AppendTimeInfo(config, *output, tempOutput, startTime, outputMutex)
}
// RunIpInfoCheck performs IP info check
func RunIpInfoCheck(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
var ipinfo string
tests.IPV4, tests.IPV6, ipinfo = utils.OnlyBasicsIpInfo(config.Language)
if ipinfo != "" {
if config.Language == "zh" {
utils.PrintCenteredTitle("IP信息", config.Width)
} else {
utils.PrintCenteredTitle("IP-Information", config.Width)
}
fmt.Printf("%s", ipinfo)
}
}, tempOutput, output)
}
// RunBasicTests runs basic system tests
func RunBasicTests(preCheck utils.NetCheckResult, config *params.Config, basicInfo, securityInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
utils.PrintHead(config.Language, config.Width, config.EcsVersion)
if config.BasicStatus || config.SecurityTestStatus {
if config.BasicStatus {
if config.Language == "zh" {
utils.PrintCenteredTitle("系统基础信息", config.Width)
} else {
utils.PrintCenteredTitle("System-Basic-Information", config.Width)
}
}
if preCheck.Connected && preCheck.StackType == "DualStack" {
tests.IPV4, tests.IPV6, *basicInfo, *securityInfo, config.Nt3CheckType = utils.BasicsAndSecurityCheck(config.Language, config.Nt3CheckType, config.SecurityTestStatus)
} else if preCheck.Connected && preCheck.StackType == "IPv4" {
tests.IPV4, tests.IPV6, *basicInfo, *securityInfo, config.Nt3CheckType = utils.BasicsAndSecurityCheck(config.Language, "ipv4", config.SecurityTestStatus)
} else if preCheck.Connected && preCheck.StackType == "IPv6" {
tests.IPV4, tests.IPV6, *basicInfo, *securityInfo, config.Nt3CheckType = utils.BasicsAndSecurityCheck(config.Language, "ipv6", config.SecurityTestStatus)
} else {
tests.IPV4, tests.IPV6, *basicInfo, *securityInfo, config.Nt3CheckType = utils.BasicsAndSecurityCheck(config.Language, "", false)
config.SecurityTestStatus = false
}
if config.BasicStatus {
fmt.Printf("%s", *basicInfo)
} else if (config.Input == "6" || config.Input == "9") && config.SecurityTestStatus {
scanner := bufio.NewScanner(strings.NewReader(*basicInfo))
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "IPV") {
fmt.Println(line)
}
}
}
}
}, tempOutput, output)
}
// RunCPUTest runs CPU test
func RunCPUTest(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.CpuTestStatus {
realTestMethod, res := tests.CpuTest(config.Language, config.CpuTestMethod, config.CpuTestThreadMode)
if config.Language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("CPU测试-通过%s测试", realTestMethod), config.Width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("CPU-Test--%s-Method", realTestMethod), config.Width)
}
fmt.Print(res)
}
}, tempOutput, output)
}
// RunMemoryTest runs memory test
func RunMemoryTest(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.MemoryTestStatus {
realTestMethod, res := tests.MemoryTest(config.Language, config.MemoryTestMethod)
if config.Language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("内存测试-通过%s测试", realTestMethod), config.Width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Memory-Test--%s-Method", realTestMethod), config.Width)
}
fmt.Print(res)
}
}, tempOutput, output)
}
// RunDiskTest runs disk test
func RunDiskTest(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.DiskTestStatus && config.AutoChangeDiskMethod {
realTestMethod, res := tests.DiskTest(config.Language, config.DiskTestMethod, config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
if config.Language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", realTestMethod), config.Width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", realTestMethod), config.Width)
}
fmt.Print(res)
} else if config.DiskTestStatus && !config.AutoChangeDiskMethod {
if config.Language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", "dd"), config.Width)
_, res := tests.DiskTest(config.Language, "dd", config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
fmt.Print(res)
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", "fio"), config.Width)
_, res = tests.DiskTest(config.Language, "fio", config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
fmt.Print(res)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", "dd"), config.Width)
_, res := tests.DiskTest(config.Language, "dd", config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
fmt.Print(res)
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", "fio"), config.Width)
_, res = tests.DiskTest(config.Language, "fio", config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
fmt.Print(res)
}
}
}, tempOutput, output)
}
// RunStreamingTests runs streaming media tests
func RunStreamingTests(config *params.Config, wg1 *sync.WaitGroup, mediaInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.Language == "zh" {
if config.CommTestStatus && !config.OnlyChinaTest {
utils.PrintCenteredTitle("御三家流媒体解锁", config.Width)
fmt.Printf("%s", commediatests.MediaTests(config.Language))
}
}
if config.UtTestStatus && (config.Language == "zh" && !config.OnlyChinaTest || config.Language == "en") {
wg1.Wait()
if config.Language == "zh" {
utils.PrintCenteredTitle("跨国流媒体解锁", config.Width)
} else {
utils.PrintCenteredTitle("Cross-Border-Streaming-Media-Unlock", config.Width)
}
fmt.Printf("%s", *mediaInfo)
}
}, tempOutput, output)
}
// RunSecurityTests runs security tests
func RunSecurityTests(config *params.Config, securityInfo, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.SecurityTestStatus {
if config.Language == "zh" {
utils.PrintCenteredTitle("IP质量检测", config.Width)
} else {
utils.PrintCenteredTitle("IP-Quality-Check", config.Width)
}
fmt.Printf("%s", securityInfo)
}
}, tempOutput, output)
}
// RunEmailTests runs email port tests
func RunEmailTests(config *params.Config, wg2 *sync.WaitGroup, emailInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.EmailTestStatus {
wg2.Wait()
if config.Language == "zh" {
utils.PrintCenteredTitle("邮件端口检测", config.Width)
} else {
utils.PrintCenteredTitle("Email-Port-Check", config.Width)
}
fmt.Println(*emailInfo)
}
}, tempOutput, output)
}
// RunNetworkTests runs network tests (Chinese mode)
func RunNetworkTests(config *params.Config, wg3 *sync.WaitGroup, ptInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.BacktraceStatus && !config.OnlyChinaTest {
utils.PrintCenteredTitle("上游及回程线路检测", config.Width)
tests.UpstreamsCheck()
}
if config.Nt3Status && !config.OnlyChinaTest {
utils.PrintCenteredTitle("三网回程路由检测", config.Width)
tests.NextTrace3Check(config.Language, config.Nt3Location, config.Nt3CheckType)
}
if config.OnlyChinaTest && *ptInfo != "" {
wg3.Wait()
utils.PrintCenteredTitle("PING值检测", config.Width)
fmt.Println(*ptInfo)
}
if config.PingTestStatus && *ptInfo != "" {
wg3.Wait()
utils.PrintCenteredTitle("PING值检测", config.Width)
fmt.Println(*ptInfo)
if config.TgdcTestStatus {
fmt.Println(pt.TelegramDCTest())
}
if config.WebTestStatus {
fmt.Println(pt.WebsiteTest())
}
}
if !config.OnlyChinaTest && !config.PingTestStatus && (config.TgdcTestStatus || config.WebTestStatus) {
utils.PrintCenteredTitle("PING值检测", config.Width)
if config.TgdcTestStatus {
fmt.Println(pt.TelegramDCTest())
}
if config.WebTestStatus {
fmt.Println(pt.WebsiteTest())
}
}
}, tempOutput, output)
}
// RunSpeedTests runs speed tests (Chinese mode)
func RunSpeedTests(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.SpeedTestStatus {
utils.PrintCenteredTitle("就近节点测速", config.Width)
tests.ShowHead(config.Language)
if config.Choice == "1" || !config.MenuMode {
tests.NearbySP()
tests.CustomSP("net", "global", 2, config.Language)
tests.CustomSP("net", "cu", config.SpNum, config.Language)
tests.CustomSP("net", "ct", config.SpNum, config.Language)
tests.CustomSP("net", "cmcc", config.SpNum, config.Language)
} else if config.Choice == "2" || config.Choice == "3" || config.Choice == "4" || config.Choice == "5" {
tests.CustomSP("net", "global", 4, config.Language)
} else if config.Choice == "6" {
tests.CustomSP("net", "global", 11, config.Language)
}
}
}, tempOutput, output)
}
// RunEnglishNetworkTests runs network tests (English mode)
func RunEnglishNetworkTests(config *params.Config, wg3 *sync.WaitGroup, ptInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.TgdcTestStatus || config.WebTestStatus {
utils.PrintCenteredTitle("PING-Test", config.Width)
if config.TgdcTestStatus {
fmt.Println(pt.TelegramDCTest())
}
if config.WebTestStatus {
fmt.Println(pt.WebsiteTest())
}
}
}, tempOutput, output)
}
// RunEnglishSpeedTests runs speed tests (English mode)
func RunEnglishSpeedTests(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.SpeedTestStatus {
utils.PrintCenteredTitle("Speed-Test", config.Width)
tests.ShowHead(config.Language)
tests.NearbySP()
tests.CustomSP("net", "global", -1, config.Language)
}
}, tempOutput, output)
}
// AppendTimeInfo appends timing information
func AppendTimeInfo(config *params.Config, output, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
endTime := time.Now()
duration := endTime.Sub(startTime)
minutes := int(duration.Minutes())
seconds := int(duration.Seconds()) % 60
currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006")
return utils.PrintAndCapture(func() {
utils.PrintCenteredTitle("", config.Width)
if config.Language == "zh" {
fmt.Printf("花费 : %d 分 %d 秒\n", minutes, seconds)
fmt.Printf("时间 : %s\n", currentTime)
} else {
fmt.Printf("Cost Time : %d min %d sec\n", minutes, seconds)
fmt.Printf("Current Time : %s\n", currentTime)
}
utils.PrintCenteredTitle("", config.Width)
}, tempOutput, output)
}
// HandleSignalInterrupt handles interrupt signals
func HandleSignalInterrupt(sig chan os.Signal, config *params.Config, startTime *time.Time, output *string, tempOutput string, uploadDone chan bool, outputMutex *sync.Mutex) {
select {
case <-sig:
if !config.Finish {
endTime := time.Now()
duration := endTime.Sub(*startTime)
minutes := int(duration.Minutes())
seconds := int(duration.Seconds()) % 60
currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006")
outputMutex.Lock()
timeInfo := utils.PrintAndCapture(func() {
utils.PrintCenteredTitle("", config.Width)
if config.Language == "zh" {
fmt.Printf("花费 : %d 分 %d 秒\n", minutes, seconds)
fmt.Printf("时间 : %s\n", currentTime)
} else {
fmt.Printf("Cost Time : %d min %d sec\n", minutes, seconds)
fmt.Printf("Current Time : %s\n", currentTime)
}
utils.PrintCenteredTitle("", config.Width)
}, "", "")
*output += timeInfo
finalOutput := *output
outputMutex.Unlock()
resultChan := make(chan struct {
httpURL string
httpsURL string
}, 1)
if config.EnableUpload {
go func() {
httpURL, httpsURL := utils.ProcessAndUpload(finalOutput, config.FilePath, config.EnableUpload)
resultChan <- struct {
httpURL string
httpsURL string
}{httpURL, httpsURL}
uploadDone <- true
}()
select {
case result := <-resultChan:
if result.httpURL != "" || result.httpsURL != "" {
if config.Language == "en" {
fmt.Printf("Upload successfully!\nHttp URL: %s\nHttps URL: %s\n", result.httpURL, result.httpsURL)
} else {
fmt.Printf("上传成功!\nHttp URL: %s\nHttps URL: %s\n", result.httpURL, result.httpsURL)
}
}
time.Sleep(100 * time.Millisecond)
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(0)
case <-time.After(30 * time.Second):
if config.Language == "en" {
fmt.Println("Upload timeout, program exit")
} else {
fmt.Println("上传超时,程序退出")
}
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(1)
}
} else {
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(0)
}
}
os.Exit(0)
}
}
// HandleUploadResults handles uploading results
func HandleUploadResults(config *params.Config, output string) {
httpURL, httpsURL := utils.ProcessAndUpload(output, config.FilePath, config.EnableUpload)
if httpURL != "" || httpsURL != "" {
if config.Language == "en" {
fmt.Printf("Upload successfully!\nHttp URL: %s\nHttps URL: %s\n", httpURL, httpsURL)
fmt.Println("Each Test Benchmark: https://bash.spiritlhl.net/ecsguide")
} else {
fmt.Printf("上传成功!\nHttp URL: %s\nHttps URL: %s\n", httpURL, httpsURL)
fmt.Println("每项测试基准见: https://bash.spiritlhl.net/ecsguide")
}
}
}

View File

@@ -1,4 +1,4 @@
package cputest
package tests
import (
"runtime"

View File

@@ -1,4 +1,4 @@
package disktest
package tests
import (
"runtime"

View File

@@ -1,4 +1,4 @@
package memorytest
package tests
import (
"runtime"

View File

@@ -1,4 +1,4 @@
package nexttrace
package tests
import (
"fmt"
@@ -27,7 +27,7 @@ func NextTrace3Check(language, nt3Location, nt3CheckType string) {
fmt.Println(res)
}
}
return
continue
}
for _, res := range result.Output {
res = strings.TrimSpace(res)

View File

@@ -1,10 +1,11 @@
package speedtest
package tests
import (
"github.com/oneclickvirt/speedtest/model"
"github.com/oneclickvirt/speedtest/sp"
"runtime"
"strings"
"github.com/oneclickvirt/speedtest/model"
"github.com/oneclickvirt/speedtest/sp"
)
func ShowHead(language string) {

View File

@@ -1,8 +1,8 @@
package unlocktest
package tests
import (
"github.com/oneclickvirt/UnlockTests/utils"
"github.com/oneclickvirt/UnlockTests/executor"
"github.com/oneclickvirt/UnlockTests/utils"
"github.com/oneclickvirt/defaultset"
)

View File

@@ -1,4 +1,4 @@
package upstreams
package tests
import (
"fmt"

View File

@@ -1,11 +0,0 @@
package memorytest
import (
"fmt"
"testing"
)
func Test(t *testing.T) {
_, res := MemoryTest("zh", "stream")
fmt.Print(res)
}

View File

@@ -1,13 +0,0 @@
package nexttrace
import (
"testing"
"time"
)
func TestNextTrace3Check(t *testing.T) {
start := time.Now()
NextTrace3Check("zh", "ALL", "ipv4")
duration := time.Since(start)
t.Logf("执行耗时: %s", duration)
}

View File

@@ -1,8 +0,0 @@
package speedtest
import "testing"
func Test(t *testing.T) {
ShowHead("en")
NearbySP()
}

View File

@@ -1,10 +0,0 @@
package unlocktest
import (
"fmt"
"testing"
)
func Test(t *testing.T) {
fmt.Printf("%s", MediaTest("zh"))
}

View File

@@ -1,8 +0,0 @@
package upstreams
import "testing"
func TestUpstreamsCheck(t *testing.T) {
IPV4 = "148.100.85.25"
UpstreamsCheck()
}