Author SHA1 Message Date
JoJo 628bea38be fix: 完善苹果系统构建与文件兼容
动机:解决苹果系统发布包缺少执行权限和特殊文件误处理问题。
改动:校验发布包权限,支持两类芯片架构并改进文件识别。
影响:苹果系统可直接运行发布包,其他平台构建与使用方式兼容。
验证:运行 go test ./...、go vet ./... 和完整发布构建,结果通过。
关联:无
2026-09-07 16:14:11 +08:00
4 changed files with 127 additions and 38 deletions
+32 -6
View File
@@ -12,14 +12,41 @@
## 安装
你可以使用去 [releases](https://git.taurusxin.com/taurusxin/ncmdump-go/releases/latest) 下载最新版预编译好的二进制文件,或者你也可以用包管理器安装
你可以前往 [releases](https://git.taurusxin.com/taurusxin/ncmdump-go/releases/latest) 下载最新版预编译好的二进制文件,或者使用包管理器安装
### macOS
项目同时支持 Apple SiliconM1/M2/M3/M4 等)和 Intel Mac
- Apple Silicon 下载文件名包含 `darwin_arm64` 的压缩包;
- Intel Mac 下载文件名包含 `darwin_amd64` 的压缩包;
- 不确定芯片架构时,可运行 `uname -m`:输出 `arm64` 对应 Apple Silicon,输出 `x86_64` 对应 Intel。
下载并解压后,在终端执行:
```shell
chmod +x ncmdump-go
sudo install -d /usr/local/bin
sudo install -m 0755 ncmdump-go /usr/local/bin/ncmdump-go
ncmdump-go --version
```
新发布的 macOS 压缩包已经包含可执行权限;这里保留 `chmod`,以兼容早期发布包。如果 macOS 提示无法验证开发者,可在确认文件来自本项目发布页后、执行安装命令前移除下载隔离属性:
```shell
xattr -d com.apple.quarantine ncmdump-go
```
也可以安装 Go 1.23 或更高版本后从源码构建:
```shell
go build -o ncmdump-go .
```
```shell
# Windows Scoop
scoop bucket add taurusxin https://git.taurusxin.com/taurusxin/scoop-bucket.git # 添加 scoop 源
scoop install ncmdump-go # 安装 ncmdump-go
# macOS & Linux 之后会支持
```
## 使用方法
@@ -69,10 +96,10 @@ ncmdump-go -d source_dir -o output_dir -r
## 开发
使用 go module 下载 ncmdump-go 包
使用 Go module 下载 ncmdump-go 包
```shell
go get -u git.taurusxin.com/taurusxin/ncmdump-go
go get git.taurusxin.com/taurusxin/ncmdump-go@latest
```
导入并使用
@@ -111,4 +138,3 @@ func main() {
}
}
```
+53 -27
View File
@@ -1,37 +1,63 @@
#!/usr/bin/env bash
VERSION=1.7.5
set -euo pipefail
# Clean up the build directory
rm -rf build
mkdir build
VERSION="${VERSION:-1.7.5}"
VERSION="${VERSION#v}"
BUILD_DIR="${BUILD_DIR:-build}"
PACKAGE="git.taurusxin.com/taurusxin/ncmdump-go"
LDFLAGS="-w -s -X main.version=${VERSION}"
# Linux amd64
echo "Building for Linux amd64..."
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o build/ncmdump-go git.taurusxin.com/taurusxin/ncmdump-go
tar zcf build/ncmdump-go_linux_amd64_$VERSION.tar.gz -C build ncmdump-go
rm build/ncmdump-go
mkdir -p "${BUILD_DIR}"
rm -f \
"${BUILD_DIR}/ncmdump-go" \
"${BUILD_DIR}/ncmdump-go.exe" \
"${BUILD_DIR}/ncmdump-go_linux_amd64_${VERSION}.tar.gz" \
"${BUILD_DIR}/ncmdump-go_linux_arm64_${VERSION}.tar.gz" \
"${BUILD_DIR}/ncmdump-go_darwin_amd64_${VERSION}.tar.gz" \
"${BUILD_DIR}/ncmdump-go_darwin_arm64_${VERSION}.tar.gz" \
"${BUILD_DIR}/ncmdump-go_windows_amd64_${VERSION}.zip"
# Linux arm64
echo "Building for Linux arm64..."
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-w -s" -o build/ncmdump-go git.taurusxin.com/taurusxin/ncmdump-go
tar zcf build/ncmdump-go_linux_arm64_$VERSION.tar.gz -C build ncmdump-go
rm build/ncmdump-go
create_tar_archive() {
local os="$1"
local arch="$2"
local binary="${BUILD_DIR}/ncmdump-go"
local archive="${BUILD_DIR}/ncmdump-go_${os}_${arch}_${VERSION}.tar.gz"
local tar_version
# macOS amd64
echo "Building for macOS amd64..."
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="-w -s" -o build/ncmdump-go git.taurusxin.com/taurusxin/ncmdump-go
tar zcf build/ncmdump-go_darwin_amd64_$VERSION.tar.gz -C build ncmdump-go
rm build/ncmdump-go
echo "Building for ${os} ${arch}..."
CGO_ENABLED=0 GOOS="${os}" GOARCH="${arch}" \
go build -trimpath -ldflags="${LDFLAGS}" -o "${binary}" "${PACKAGE}"
# macOS arm64
echo "Building for macOS arm64..."
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="-w -s" -o build/ncmdump-go git.taurusxin.com/taurusxin/ncmdump-go
tar zcf build/ncmdump-go_darwin_arm64_$VERSION.tar.gz -C build ncmdump-go
rm build/ncmdump-go
# Builds are sometimes produced from Git Bash on Windows, where chmod does
# not change NTFS permissions. Force the executable bit into GNU tar's
# header so macOS/Linux users can run the extracted binary immediately.
chmod 0755 "${binary}"
tar_version="$(tar --version 2>/dev/null || true)"
if [[ "${tar_version}" == *"GNU tar"* ]]; then
tar --mode=0755 -zcf "${archive}" -C "${BUILD_DIR}" ncmdump-go
else
tar -zcf "${archive}" -C "${BUILD_DIR}" ncmdump-go
fi
if ! tar -tvzf "${archive}" | awk 'NR == 1 { exit(substr($1, 1, 10) == "-rwxr-xr-x" ? 0 : 1) }'; then
echo "Archive validation failed: ncmdump-go is not executable in ${archive}" >&2
exit 1
fi
rm "${binary}"
}
create_tar_archive linux amd64
create_tar_archive linux arm64
create_tar_archive darwin amd64
create_tar_archive darwin arm64
# Windows amd64
echo "Building for Windows amd64..."
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-w -s" -o build/ncmdump-go.exe git.taurusxin.com/taurusxin/ncmdump-go
zip -q -j build/ncmdump-go_windows_amd64_$VERSION.zip ./build/ncmdump-go.exe
rm build/ncmdump-go.exe
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 \
go build -trimpath -ldflags="${LDFLAGS}" -o "${BUILD_DIR}/ncmdump-go.exe" "${PACKAGE}"
zip -q -j "${BUILD_DIR}/ncmdump-go_windows_amd64_${VERSION}.zip" "${BUILD_DIR}/ncmdump-go.exe"
rm "${BUILD_DIR}/ncmdump-go.exe"
echo "Release archives written to ${BUILD_DIR}/"
+15 -5
View File
@@ -2,17 +2,27 @@ package main
import (
"fmt"
"git.taurusxin.com/taurusxin/ncmdump-go/ncmcrypt"
"git.taurusxin.com/taurusxin/ncmdump-go/utils"
"os"
"path/filepath"
"strings"
"git.taurusxin.com/taurusxin/ncmdump-go/ncmcrypt"
"git.taurusxin.com/taurusxin/ncmdump-go/utils"
flag "github.com/spf13/pflag"
)
// version is replaced by build.sh for release builds.
var version = "1.7.5"
func isNCMFile(filePath string) bool {
baseName := filepath.Base(filePath)
return !strings.HasPrefix(baseName, "._") && strings.EqualFold(filepath.Ext(baseName), ".ncm")
}
func processFile(filePath string, outputDir string) error {
// skip if the extension is not .ncm
if filePath[len(filePath)-4:] != ".ncm" {
if !isNCMFile(filePath) {
return nil
}
@@ -59,7 +69,7 @@ func main() {
}
if *showVersion {
fmt.Println("ncmdump version 1.7.5")
fmt.Printf("ncmdump version %s\n", version)
os.Exit(0)
}
@@ -132,7 +142,7 @@ func main() {
// process files from args
for _, filePath := range flag.Args() {
// skip if the extension is not .ncm
if filePath[len(filePath)-4:] != ".ncm" {
if !isNCMFile(filePath) {
continue
}
if outputDirSpecified {
+27
View File
@@ -0,0 +1,27 @@
package main
import "testing"
func TestIsNCMFile(t *testing.T) {
tests := []struct {
name string
path string
want bool
}{
{name: "lowercase", path: "/Users/test/Music/song.ncm", want: true},
{name: "uppercase", path: "/Users/test/Music/song.NCM", want: true},
{name: "unicode path", path: "/Users/test/音乐/歌曲.ncm", want: true},
{name: "macOS AppleDouble file", path: "/Users/test/Music/._song.ncm", want: false},
{name: "short name", path: "a", want: false},
{name: "extension only", path: ".ncm", want: true},
{name: "different extension", path: "song.flac", want: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isNCMFile(tt.path); got != tt.want {
t.Fatalf("isNCMFile(%q) = %v, want %v", tt.path, got, tt.want)
}
})
}
}