Skip to content

Merge:'fix/RedDot-Log&v2'| 二值化红点v2终版公测. #1526

Merge:'fix/RedDot-Log&v2'| 二值化红点v2终版公测.

Merge:'fix/RedDot-Log&v2'| 二值化红点v2终版公测. #1526

Workflow file for this run

name: install
on:
push:
tags:
- "v*"
branches:
- "**"
paths:
- ".github/workflows/install.yml"
- "assets/**"
- "**.py"
pull_request:
branches:
- "**"
paths:
- ".github/workflows/install.yml"
- "assets/**"
- "**.py"
workflow_dispatch:
inputs:
deploy_beta:
type: boolean
description: "发布公测版"
default: false # 默认不勾选,避免误触
deploy_alpha:
type: boolean
description: "发布内测版"
default: false
ci_as_stable:
type: boolean
description: "CI版本作为正式发布(非预发布)"
default: false
notice_text:
type: string
description: "⚠️ 说明 / Note"
required: false
default: "不勾选任何选项,手动触发即为CI发版"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.deploy_alpha || 'false' }}-${{ inputs.deploy_beta || 'false' }}
cancel-in-progress: true
jobs:
meta:
runs-on: ubuntu-latest
if: "github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/') || !contains(github.event.head_commit.message, '[deploy-sync]')"
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: set_tag
# 通过环境变量传递 Commit Message,防止反引号注入攻击
env:
HEAD_COMMIT_MSG: ${{ github.event.head_commit.message }}
run: |
# 使用环境变量获取引用信息
is_tag_push=false
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
is_tag_push=true
fi
# 获取提交信息(用于检测 [deploy-beta])
commit_message="$HEAD_COMMIT_MSG"
if [ "$is_tag_push" = true ]; then
# 正式标签:直接使用标签名
tag="${GITHUB_REF#refs/tags/}"
version_type="release"
else
# 获取最新的正式版本标签
base_tag=$(git tag -l "v*" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1)
# 如果没有找到纯版本号标签,使用默认值
if [ -z "$base_tag" ] || [[ "$base_tag" != v*.*.* ]]; then
base_tag="v0.0.0"
fi
commit_short=$(git rev-parse --short HEAD)
date_suffix=$(date +"%y%m%d")
# 判断版本类型
# 优先判断 Alpha
if ([ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ] && [ "${{ github.event.inputs.deploy_alpha }}" = "true" ]) || echo "$commit_message" | tail -n1 | grep -q "\[deploy-alpha\]"; then
# 内测版:在基础版本上增加 2 个补丁版本号 (Base + 2)
# 修改点:将 $3+1 改为 $3+2
incremented_tag=$(echo "$base_tag" | awk -F. '{printf "%s.%s.%s", $1, $2, $3+2}')
tag="${incremented_tag}-alpha.${date_suffix}.${commit_short}"
version_type="alpha"
# 原有的 Beta 判断
elif ([ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ] && [ "${{ github.event.inputs.deploy_beta }}" = "true" ]) || echo "$commit_message" | tail -n1 | grep -q "\[deploy-beta\]"; then
# 公测版:在基础版本上增加 1 个补丁版本号 (Base + 1)
incremented_tag=$(echo "$base_tag" | awk -F. '{printf "%s.%s.%s", $1, $2, $3+1}')
tag="${incremented_tag}-beta.${date_suffix}.${commit_short}"
version_type="beta"
# 原有的 CI 判断 (else 分支)
else
tag="${base_tag}-ci.${date_suffix}.${commit_short}"
version_type="ci"
fi
fi
# 添加调试信息
echo "Base tag: $base_tag"
echo "Generated tag: $tag"
echo "Commit short: $commit_short"
echo "Date suffix: $date_suffix"
echo "Version type: $version_type"
echo "tag=$tag" | tee -a $GITHUB_OUTPUT
echo "is_tag_push=$is_tag_push" | tee -a $GITHUB_OUTPUT
echo "is_dev_version=$([ "$version_type" = "ci" ] && echo "true" || echo "false")" | tee -a $GITHUB_OUTPUT
echo "version_type=$version_type" | tee -a $GITHUB_OUTPUT
# 2. 下载资源 (用于探测)
- name: Parse Configuration
id: parse_config
run: |
python3 -c "
import re, os, sys
req_file = 'requirements.txt'
mfaa_tag = ''
if os.path.exists(req_file):
with open(req_file, 'r', encoding='utf-8') as f:
for line in f:
m = re.match(r'^#\s*MFAA_TAG=(v[\w\.\-]+)', line.strip())
if m: mfaa_tag = m.group(1); break
if not mfaa_tag:
print('::error::未找到 # MFAA_TAG 配置'); sys.exit(1)
print(f'✅ 目标资源版本: {mfaa_tag}')
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write(f'mfaa_tag={mfaa_tag}\n')
"
# [修改] 下载资源 (改用 steps.parse_config.outputs.mfaa_tag)
- name: Download MFAA for Detection
uses: robinraju/release-downloader@v1
with:
repository: MaaXYZ/MFAAvalonia
latest: false
tag: ${{ steps.parse_config.outputs.mfaa_tag }}
fileName: "MFAAvalonia-*-linux-x64*"
out-file-path: "temp_detect"
extract: true
# 3. 探测内核版本
- name: Detect Core Version
id: detect_maa
run: |
# 1. 使用 find 查找文件的【绝对路径】(加上 $(pwd))
SO_FILE=$(find "$(pwd)/temp_detect" -name "libMaaFramework.so" | head -n 1)
if [ -z "$SO_FILE" ]; then
echo "::error::根本找不到 libMaaFramework.so 文件!"
exit 1
fi
SO_DIR=$(dirname "$SO_FILE")
echo "📍 定位到库文件: $SO_FILE"
echo "🔧 库目录: $SO_DIR"
# 2. 【关键】设置 LD_LIBRARY_PATH 为绝对路径
# 这告诉 Linux:"加载库的时候,去这个目录里找依赖!"
export LD_LIBRARY_PATH="$SO_DIR:$LD_LIBRARY_PATH"
# 3. 【调试神器】运行 ldd
# 如果后面 Python 报错,看这部分日志就知道缺哪个库了
echo "=== 🕵️‍♂️ 依赖检查 (ldd) ==="
ldd "$SO_FILE" || true
echo "========================="
# 4. 生成探测脚本
cat << 'EOF' > detect.py
import sys, ctypes, os
# 从环境变量获取刚才找到的绝对路径
so_path = os.environ.get("TARGET_SO_PATH")
print(f"🐍 Python loading: {so_path}")
try:
# mode=ctypes.RTLD_GLOBAL 有时能解决跨库符号依赖问题
lib = ctypes.CDLL(so_path, mode=ctypes.RTLD_GLOBAL)
lib.MaaVersion.restype = ctypes.c_char_p
ver = lib.MaaVersion().decode('utf-8')
print(f"✅ Detected Core: {ver}")
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write(f"version={ver}\n")
except Exception as e:
print(f"::error::加载失败: {e}")
sys.exit(1)
EOF
# 5. 运行脚本 (传入路径变量)
export TARGET_SO_PATH="$SO_FILE"
python3 detect.py
outputs:
tag: ${{ steps.set_tag.outputs.tag }}
is_tag_push: ${{ steps.set_tag.outputs.is_tag_push }}
is_dev_version: ${{ steps.set_tag.outputs.is_dev_version }}
version_type: ${{ steps.set_tag.outputs.version_type }}
maa_version: ${{ steps.detect_maa.outputs.version }}
mfaa_tag: ${{ steps.parse_config.outputs.mfaa_tag }}
resource_check:
name: Check Resources
needs: meta
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install maafw
env:
TARGET_VERSION: ${{ needs.meta.outputs.maa_version }}
run: |
python -m pip install --upgrade pip
echo "📦 Installing maafw==$TARGET_VERSION (Linux)..."
python -m pip install maafw=="$TARGET_VERSION"
- name: Restore Assets (Clone from Resource Repo)
uses: actions/checkout@v4
with:
repository: sunyink/MFABD2-Assets
ref: master
path: temp_assets
fetch-depth: 1
- name: Merge Assets
shell: bash
run: |
mkdir -p assets/MaaCommonAssets
cp -r temp_assets/MaaCommonAssets/* assets/MaaCommonAssets/
rm -rf temp_assets
- name: Check Resource
run: |
# 赋予执行权限,防止 Linux 下报错
chmod +x ./check_resource.py
python ./check_resource.py ./assets/resource/
install:
needs: [meta, resource_check]
# PR 只跑资源检查,不跑完整构建
if: github.event_name != 'pull_request'
# 动态选择运行环境:Win跑Win,Mac跑Mac,Linux/安卓跑Ubuntu
runs-on: ${{ matrix.runner }}
env:
MAA_VERSION: ${{ needs.meta.outputs.maa_version }}
MFAA_TAG: ${{ needs.meta.outputs.mfaa_tag }}
# 解决 Windows 乱码问题
PYTHONUTF8: 1
PYTHONIOENCODING: utf-8
# 解决部分 Windows 终端不认 ANSI 颜色的问题(可选)
TERM: xterm
strategy:
fail-fast: false
matrix:
include:
# -------------------------------------------------------------------
# 🪟 Windows (x86_64) - 使用 Python 3.10
# -------------------------------------------------------------------
- os: win
arch: x86_64
runner: windows-latest
mfa_os: "win"
mfa_arch: "x64"
# Windows 专用配置
embed_version: "3.10.11" # Python 嵌入包版本
py_ver_short: "3.10" # 用于 pip 参数
py_arch: "amd64" # Python 官网文件名后缀
pip_plat: "win_amd64" # pip 平台标签
numpy_req: "numpy<2"
is_android: false
# -------------------------------------------------------------------
# 🪟 Windows (ARM64) - 使用 Python 3.11 (原生支持更好)
# -------------------------------------------------------------------
- os: win
arch: aarch64
runner: windows-latest
mfa_os: "win"
mfa_arch: "arm64"
# Windows 专用配置 (切到 3.11)
embed_version: "3.11.9"
py_ver_short: "3.11"
py_arch: "arm64"
pip_plat: "win_arm64"
numpy_req: "numpy>=2" # winarm64 没有<2的预编译包.
is_android: false
# -------------------------------------------------------------------
# 🍎 macOS (x64)
# -------------------------------------------------------------------
- os: macos
arch: x86_64
runner: macos-latest
mfa_os: "osx"
mfa_arch: "x64"
numpy_req: "numpy<2"
is_android: false
standalone_url: "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.10.13+20240107-x86_64-apple-darwin-install_only.tar.gz"
standalone_sha256: "47da6831e269ad6af603f59bbd4767636c5749bd70a24363c9cf003c32c8ecfc"
# -------------------------------------------------------------------
# 🍎 macOS (ARM64)
# -------------------------------------------------------------------
- os: macos
arch: aarch64
runner: macos-latest
mfa_os: "osx"
mfa_arch: "arm64"
numpy_req: "numpy<2"
is_android: false
standalone_url: "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.10.13+20240107-aarch64-apple-darwin-install_only.tar.gz"
standalone_sha256: "4d19a0509c274f360a5c250d71a2af7263ebaf898ecdb71427f3532cbc6b2cf7"
# -------------------------------------------------------------------
# 🐧 Linux (x64)
# -------------------------------------------------------------------
- os: linux
arch: x86_64
runner: ubuntu-latest
mfa_os: "linux"
mfa_arch: "x64"
numpy_req: "numpy<2"
is_android: false
# -------------------------------------------------------------------
# 🐧 Linux (ARM64)
# -------------------------------------------------------------------
- os: linux
arch: aarch64
runner: ubuntu-latest
mfa_os: "linux"
mfa_arch: "arm64"
numpy_req: "numpy<2"
is_android: false
# -------------------------------------------------------------------
# 🤖 Android (ARM64) - 特殊逻辑
# -------------------------------------------------------------------
- os: android
arch: aarch64
runner: ubuntu-latest
is_android: true
steps:
- uses: actions/checkout@v4
with:
submodules: true
# 设置构建环境的 Python (用于运行 install.py 等脚本)
# 这里的版本不影响 Windows 打包进去的嵌入式 Python 版本
- name: Set up Build Environment
uses: actions/setup-python@v5
with:
python-version: "3.10"
# 显式指定 bash,确保在 Windows 上也能用 cp/rm/mkdir 等命令
- name: Install Build Dependencies
shell: bash
run: |
python -m pip install --upgrade pip
pip install requests json-with-comments
# ----------------------------------------------------------------
# 📦 资源下载 (根据 Matrix 变量动态处理)
# ----------------------------------------------------------------
# [桌面端] 下载 MFAAvalonia
- name: Download MFAAvalonia
if: matrix.is_android == false
uses: robinraju/release-downloader@v1
with:
repository: MaaXYZ/MFAAvalonia
latest: false
tag: ${{ env.MFAA_TAG }}
# 直接使用 Matrix 里的变量,无需复杂判断
fileName: "MFAAvalonia-*-${{ matrix.mfa_os }}-${{ matrix.mfa_arch }}*"
out-file-path: "MFA"
extract: true
# [安卓端] 下载 MaaFramework Core
- name: Download MaaFramework (Android)
if: matrix.is_android == true
uses: robinraju/release-downloader@v1
with:
repository: MaaXYZ/MaaFramework
latest: false
tag: "${{ env.MAA_VERSION }}"
# Android 文件名格式通常是 MAA-android-arm64-xxxx
fileName: "MAA-android-${{ matrix.arch }}*"
out-file-path: "AndroidCore"
extract: true
- name: Restore Assets (Clone from Resource Repo)
uses: actions/checkout@v4
with:
repository: sunyink/MFABD2-Assets
ref: master
path: temp_assets
fetch-depth: 1
- name: Merge Assets
shell: bash
run: |
# 创建目标目录结构(如果不存在)
mkdir -p assets/MaaCommonAssets
# 解开 MaaCommonAssets/MaaCommonAssets 套娃
cp -r temp_assets/MaaCommonAssets/* assets/MaaCommonAssets/
rm -rf temp_assets
echo "✅ 资源合并完成"
# ----------------------------------------------------------------
# Windows 专属:嵌入式 Python 打包
# ----------------------------------------------------------------
- name: Setup Embed Python (Windows)
if: matrix.os == 'win'
shell: bash
run: |
echo "⬇️ Downloading Python Embed ${{ matrix.embed_version }} for ${{ matrix.py_arch }}..."
# 1. 下载:利用 Matrix 变量 (支持 3.10 和 3.11)
curl -L -o python_embed.zip "https://www.python.org/ftp/python/${{ matrix.embed_version }}/python-${{ matrix.embed_version }}-embed-${{ matrix.py_arch }}.zip"
# 2. 解压 (Windows Runner 自带 7z)
mkdir -p install/python
7z x python_embed.zip -oinstall/python
# 开启 site 支持
python3 -c "import pathlib; p = next(pathlib.Path('install/python').glob('*._pth')); p.write_text(p.read_text().replace('#import site', 'import site'))"
# 3. 开启 site 支持
# 注意:这里我们构造 python3x._pth 文件名,因为 3.10 是 python310._pth,3.11 是 python311._pth
# 简单的做法是通配符,或者用 Python 脚本找
python3 -c "
import pathlib
import re
pth_file = next(pathlib.Path('install/python').glob('python*._pth'))
print(f'Modifying {pth_file}')
pth_file.write_text(pth_file.read_text().replace('#import site', 'import site'))
"
echo "📦 Installing Dependencies for ${{ matrix.pip_plat }} (Python ${{ matrix.py_ver_short }})..."
# 4. 重点:安装依赖
# 使用 --only-binary=:all: 强制下载 Wheel 包 (这对 Pillow/Numpy 在 Windows 上至关重要)
# 使用 Python 精准剔除 numpy,防止误伤注释或其他包
python3 -c "
import re
with open('requirements.txt', 'r') as f_in, open('requirements_no_numpy.txt', 'w') as f_out:
for line in f_in:
# 正则解释:
# ^\s* : 行首允许有空格
# numpy : 必须是 numpy
# \s* : 允许 numpy 后面有空格
# ([<>=!~;]|$) : 后面必须跟着版本比较符、分号,或者是行尾
if not re.match(r'^\s*numpy\s*([<>=!~;]|$)', line, re.IGNORECASE):
f_out.write(line)
"
python3 -m pip install \
--platform ${{ matrix.pip_plat }} \
--python-version ${{ matrix.py_ver_short }} \
--only-binary=:all: \
--no-cache-dir \
--target install/python/Lib/site-packages \
"${{ matrix.numpy_req }}" \
-r requirements_no_numpy.txt \
maafw==$MAA_VERSION
# ----------------------------------------------------------------
# [新增] macOS 专属:Portable Python 打包
# ----------------------------------------------------------------
- name: Setup Portable Python (macOS)
if: matrix.os == 'macos'
shell: bash
run: |
echo "⬇️ Downloading Portable Python for macOS..."
# 失败重试和SHA校验
curl -fL --retry 3 --retry-delay 2 -o python_mac.tar.gz "${{ matrix.standalone_url }}"
echo "${{ matrix.standalone_sha256 }} python_mac.tar.gz" | shasum -a 256 -c -
# 解压 (standalone 包结构通常是 python/install/...)
mkdir -p temp_python
tar -xzf python_mac.tar.gz -C temp_python
# 💡 调试输出一下目录结构,防止以后包结构又变
echo "📂 预计解压后的目录结构如下:"
ls -la temp_python/python/
# 移动到 install/python
mkdir -p install/python
cp -r temp_python/python/* install/python/
rm -rf temp_python python_mac.tar.gz
# 设置权限 (非常重要)
chmod +x install/python/bin/python3
echo "📦 Installing Dependencies (macOS)..."
PY_EXEC="install/python/bin/python3"
# 剔除 numpy
"$PY_EXEC" -c "
import re
with open('requirements.txt', 'r') as f_in, open('requirements_no_numpy.txt', 'w') as f_out:
for line in f_in:
if not re.match(r'^\s*numpy\s*([<>=!~;]|$)', line, re.IGNORECASE):
f_out.write(line)
"
# 安装依赖到便携版环境
"$PY_EXEC" -m pip install --upgrade pip
"$PY_EXEC" -m pip install "${{ matrix.numpy_req }}" -r requirements_no_numpy.txt maafw==$MAA_VERSION
# 清理缓存
"$PY_EXEC" -m pip cache purge
find install/python -name "__pycache__" -type d -exec rm -rf {} +
# 🔧 macOS 便携 Python 的 pip 安装时可能丢失 wheel 内的 .dylibs 目录
# (如 numpy/.dylibs/libopenblas64_.0.dylib, PIL/.dylibs/libtiff.6.dylib 等)
# 此处扫描所有 .so 文件的 @loader_path 依赖,缺失则从对应 wheel 中提取补回
echo "🔍 Verifying native dylibs..."
"$PY_EXEC" -c "
import os, sys, subprocess, tempfile, zipfile, shutil, re, importlib.metadata, sysconfig
from pathlib import Path
site = Path(sysconfig.get_path('purelib'))
if not site.exists():
sys.exit(0)
# 导入名 -> PyPI 分发名的映射 (部分包的目录名与分发名不一致)
DIS_NAME = {
'PIL': 'Pillow',
'cv2': 'opencv-python-headless',
}
try:
for dist, names in importlib.metadata.packages_distributions().items():
for n in names:
DIS_NAME.setdefault(n, dist)
except Exception:
pass
def resolve_dist(pkg):
'''目录名 -> PyPI 分发名, 多级回退'''
for k, v in DIS_NAME.items():
if k.lower() == pkg.lower():
return v
return pkg
broken = {} # pkg_dir -> {'dist': dist_name, 'dylibs': set()}
for so in site.rglob('*.so'):
try:
out = subprocess.check_output(['otool', '-L', str(so)], text=True)
except Exception:
continue
for line in out.split('\n'):
m = re.match(r'\s+(@loader_path/\S+)', line)
if not m:
continue
ref = m.group(1)
resolved = (so.parent / ref.replace('@loader_path/', '')).resolve()
if not resolved.exists():
pkg = so.relative_to(site).parts[0]
broken.setdefault(pkg, {'dist': resolve_dist(pkg), 'dylibs': set()})
broken[pkg]['dylibs'].add(os.path.basename(ref))
if not broken:
print('✅ All native dylibs present')
sys.exit(0)
print(f'⚠️ {len(broken)} package(s) with missing dylibs, repairing...')
plat = sysconfig.get_platform().replace('-', '_').replace('.', '_')
py_ver = f'{sys.version_info.major}.{sys.version_info.minor}'
for pkg, info in sorted(broken.items()):
dist_name = info['dist']
try:
ver = importlib.metadata.version(dist_name)
except Exception:
print(f' ⚠️ Skip {pkg} (dist: {dist_name}): cannot determine version')
continue
print(f' 📦 {pkg} -> {dist_name}=={ver}')
with tempfile.TemporaryDirectory() as tmp:
result = subprocess.run([
sys.executable, '-m', 'pip', 'download',
f'{dist_name}=={ver}', '--no-deps', '--dest', tmp,
'--platform', plat, '--python-version', py_ver,
'--only-binary=:all:', '--no-cache-dir',
], capture_output=True, text=True)
if result.returncode != 0:
print(f' ⚠️ pip download failed: {result.stderr.strip()}')
continue
whls = [f for f in os.listdir(tmp) if f.endswith('.whl')]
if not whls:
print(f' ⚠️ No wheel found')
continue
with zipfile.ZipFile(os.path.join(tmp, whls[0])) as zf:
# 提取 wheel 内所有 .dylib (而非仅缺失的),
# 确保同包的多个 dylib 版本一致, 避免部分替换导致 ABI 不匹配
names = [n for n in zf.namelist() if n.endswith('.dylib')]
if not names:
print(f' ℹ️ No dylibs in wheel')
continue
dst = site / pkg / '.dylibs'
os.makedirs(dst, exist_ok=True)
for n in names:
zf.extract(n, tmp)
shutil.copy2(os.path.join(tmp, n), dst / os.path.basename(n))
os.chmod(dst / os.path.basename(n), 0o755)
print(f' ✅ {os.path.basename(n)}')
print('✅ Dylib repair complete')
"
# ----------------------------------------------------------------
# 🏗️ 构建与组装
# ----------------------------------------------------------------
- name: Install & Merge
shell: bash
run: |
# 1. 运行 install.py (生成配置、版本号等)
# install.py 需要处理好路径分隔符
python ./install.py ${{ needs.meta.outputs.tag }} ${{ matrix.os }} ${{ env.MAA_VERSION }}
# 2. [桌面端逻辑] 合并 MFAAvalonia
if [[ "${{ matrix.is_android }}" == "false" ]]; then
if [ -d "MFA" ]; then
mkdir -p install
# 这里的 cleanup 移到专门的步骤里做,防止 bash 前没删干净
# 为了稳健,先在源目录删一次
find MFA -name "*.zip" -delete 2>/dev/null || true
find MFA -name "*.tar.gz" -delete 2>/dev/null || true
# 使用 bash 覆盖
cp -rf MFA/* install/
fi
# 3. [安卓逻辑] 合并 MaaFramework Core
else
echo "🤖 处理 Android 内核合并..."
if [ -d "AndroidCore" ]; then
cp -r AndroidCore/* install/
echo "✅ Android 内核已合并"
else
echo "❌ 未找到 AndroidCore 目录"
exit 1
fi
fi
# [核心修复 1] 强力清理残留的压缩包
- name: Cleanup Archives
shell: bash
run: |
echo "🧹 Cleaning up zip/tar.gz files..."
rm -f *.zip *.tar.gz
rm -rf MFA AndroidCore
# 清理 install 目录里可能被拷进去的压缩包
find install -name "*.zip" ! -name "python*.zip" -delete 2>/dev/null || true
find install -name "*.tar.gz" -delete 2>/dev/null || true
echo "✅ Cleanup done."
# [核心修复 2] 原版逻辑:利用 Python 脚本清理不匹配的 Runtimes
- name: Prune Runtimes (Architecture Slimming)
if: matrix.is_android == false
shell: bash
run: |
python3 -c "
import shutil, os
from pathlib import Path
# 从 Matrix 获取目标特征
target_os = '${{ matrix.mfa_os }}' # win, osx, linux
target_arch = '${{ matrix.mfa_arch }}' # x64, arm64
print(f'🎯 Target Runtime: {target_os}-{target_arch}')
# 定义需要保留的关键词
keep_keywords = []
keep_keywords.append(target_os)
if target_arch == 'x64': keep_keywords.extend(['x64', 'amd64'])
else: keep_keywords.extend(['arm64', 'aarch64'])
runtimes_dir = Path('install/runtimes')
if runtimes_dir.exists():
for p in runtimes_dir.iterdir():
if not p.is_dir(): continue
# 逻辑:必须包含 OS,且必须包含架构
# 1. 检查是否匹配 OS (如果不包含 target_os 且包含其他os名字 -> 删)
name = p.name.lower()
# 这里做一个简化的强匹配逻辑:
# 只要文件夹名字里包含了 'win','linux','osx' 中的任意一个,它就必须包含 target_os
has_os_tag = any(k in name for k in ['win', 'linux', 'osx'])
if has_os_tag and target_os not in name:
shutil.rmtree(p); continue
# 2. 检查架构
has_arch_tag = any(k in name for k in ['x64', 'amd64', 'arm64', 'aarch64'])
# 检查是否包含我们要的架构
match_target_arch = any(k in name for k in keep_keywords if k in ['x64', 'amd64', 'arm64', 'aarch64'])
if has_arch_tag and not match_target_arch:
print(f'🗑️ Pruning mismatched arch: {name}')
shutil.rmtree(p)
"
- name: Inject Announcement & Verify
shell: bash
run: |
echo "::group::🚀 开始注入公告"
python scripts/inject_announcement.py "${{ needs.meta.outputs.tag }}"
echo "::endgroup::"
echo "::group::👀 验证注入结果"
TARGET="install/resource/Announcement/1.公告.md"
if [ -f "$TARGET" ]; then
AFTER_ANCHOR=$(grep -A 3 "Msg-Anch" "$TARGET" | tail -n +2 | tr -d '[:space:]')
if [ -n "$AFTER_ANCHOR" ]; then
echo "✅ 已注入通知"
echo "--- 注入内容预览 ---"
grep -A 20 "Msg-Anch" "$TARGET" | head -25
else
echo "ℹ️ 本次未注入(草稿为空 / 不在目标版本范围 / 等)"
fi
else
echo "::error::目标文件不存在: $TARGET"
exit 1
fi
echo "::endgroup::"
- name: Remove redundant MAA binaries (Win Only)
if: matrix.os == 'win'
shell: bash
run: |
# 仅针对 Windows,因为只有 Windows 下载了 Python 包
python3 -c "
import shutil
from pathlib import Path
root = Path('install')
# 1. 寻找源 plugins 目录 (在 python 包里)
src_plugins = next(root.rglob('**/site-packages/maa/bin/plugins'), None)
# 2. 寻找目标 native 目录 (在 runtimes 里)
# 假设 install/runtimes 下只有一个平台文件夹,或者我们只关心 native 层级
target_native = next(root.rglob('runtimes/*/native'), None)
# 3. 如果两者都存在,执行移动
if src_plugins and target_native and src_plugins.exists():
dest_plugins = target_native / 'plugins'
if not dest_plugins.exists():
print(f'Moving plugins from {src_plugins} to {dest_plugins}')
shutil.move(str(src_plugins), str(dest_plugins))
else:
print(f'Target plugins dir already exists: {dest_plugins}')
# 4. 安全删除所有冗余的 bin 目录
# 删除 install/python/.../maa/bin,因为 runtimes 里已经有了
for p in Path('install').rglob('**/site-packages/maa/bin'):
if p.is_dir():
print(f'Removing redundant bin: {p}')
shutil.rmtree(p)
"
- uses: actions/upload-artifact@v5
with:
name: MFABD2-${{ needs.meta.outputs.tag }}-${{ matrix.os }}-${{ matrix.is_android && 'arm64' || matrix.arch }}
path: "install"
include-hidden-files: true
changelog:
name: Generate changelog
runs-on: ubuntu-latest
needs: meta
outputs:
release_body: ${{ steps.set_release_body.outputs.content }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install requests
# ✅ 使用新系统替换git-cliff
- name: Generate comprehensive changelog
id: merge_changelog
run: |
cd scripts
python changelog_generator.py
env:
CURRENT_TAG: ${{ needs.meta.outputs.tag }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
# 上传 CHANGES.md 到构建物
# =======================================================
- name: Upload Changelog Artifact
uses: actions/upload-artifact@v5
with:
name: CHANGES
path: CHANGES.md
# =======================================================
- name: Set release body
id: set_release_body
run: |
# 使用随机定界符防止内容包含 EOF 导致截断
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "content<<$EOF" >> $GITHUB_OUTPUT
cat CHANGES.md >> $GITHUB_OUTPUT
echo "$EOF" >> $GITHUB_OUTPUT
release:
if: (needs.meta.outputs.is_tag_push == 'true') || contains(needs.meta.outputs.tag, '-beta') || contains(needs.meta.outputs.tag, '-alpha') || (github.event_name == 'workflow_dispatch' && contains(needs.meta.outputs.tag, '-ci'))
needs: [meta, install, changelog]
runs-on: ubuntu-latest
steps:
- name: Get current timestamp
id: get_time
run: echo "time=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
- uses: actions/download-artifact@v5
with:
path: artifacts
- name: Package release assets
run: |
cd artifacts
mkdir -p release_assets
for dir in *; do
# 直接使用目录名作为 zip 文件名
zip_name="${dir}.zip"
# 创建压缩包
(cd "$dir" && zip -r "../release_assets/$zip_name" .)
done
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: artifacts/release_assets/*.zip
tag_name: ${{ needs.meta.outputs.tag }}
name: "MFABD2 ${{ needs.meta.outputs.tag }}"
target_commitish: ${{ github.sha }}
body: |
${{ contains(needs.changelog.outputs.release_body, '## ') && needs.changelog.outputs.release_body || '### 无显著变更' }}
draft: false
prerelease: ${{ contains(needs.meta.outputs.tag, '-beta') || contains(needs.meta.outputs.tag, '-alpha') || (contains(needs.meta.outputs.tag, '-ci') && !(github.event_name == 'workflow_dispatch' && github.event.inputs.ci_as_stable == 'true')) }}
env: # 添加环境变量以支持高限额API调用
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Trigger MirrorChyanUploading
if: github.repository_owner == 'sunyink' && ((needs.meta.outputs.is_tag_push == 'true') || contains(needs.meta.outputs.tag, '-beta') || contains(needs.meta.outputs.tag, '-alpha'))
shell: bash
env:
tag: ${{ needs.meta.outputs.tag }}
CHANGELOG_BODY: ${{ needs.changelog.outputs.release_body }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "安全触发镜像服务: 版本=$tag"
# 将 " 替换为 \"
ESCAPED_BODY="${CHANGELOG_BODY//\"/\\\"}"
# 触发包发布流程
gh workflow run --repo $GITHUB_REPOSITORY --ref ${{ github.ref }} mirrorchyan.yml -f tag=$tag
# 触发更新信息推送流程 (现在可以安全处理带引号的日志了)
gh workflow run --repo $GITHUB_REPOSITORY --ref ${{ github.ref }} mirrorchyan_release_note.yml -f tag=$tag -f body="$ESCAPED_BODY"
notify:
needs: [meta, install]
if: always() && needs.install.result == 'success' && (github.event_name == 'workflow_dispatch' || needs.meta.outputs.version_type != 'ci')
runs-on: ubuntu-latest
steps:
- name: Extract commit info
id: commit-info
env:
RAW_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
RAW_COMMIT_TIMESTAMP: ${{ github.event.head_commit.timestamp }}
run: |
# 调试:显示所有可用参数
echo "=== 调试信息 ==="
echo "GITHUB_EVENT_NAME: $GITHUB_EVENT_NAME"
echo "HEAD_COMMIT_MESSAGE: $RAW_COMMIT_MESSAGE"
echo "HEAD_COMMIT_TIMESTAMP: $RAW_COMMIT_TIMESTAMP"
echo "GITHUB_SHA: $GITHUB_SHA"
# 设置默认值,直接引用环境变量
COMMIT_MSG="$RAW_COMMIT_MESSAGE"
if [ -z "$COMMIT_MSG" ]; then
COMMIT_MSG="手动触发工作流"
fi
COMMIT_TIMESTAMP="$RAW_COMMIT_TIMESTAMP"
if [ -z "$COMMIT_TIMESTAMP" ]; then
COMMIT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
fi
COMMIT_SHA="$GITHUB_SHA"
if [ -z "$COMMIT_SHA" ]; then
COMMIT_SHA="unknown"
fi
# 提取第一行作为摘要
SUBJECT=$(echo "$COMMIT_MSG" | head -n 1)
# 简化描述处理
if [ $(echo "$COMMIT_MSG" | wc -l) -gt 1 ]; then
BODY=$(echo "$COMMIT_MSG" | tail -n +2 | head -c 100 | tr -d '\n')
if [ ${#BODY} -eq 100 ]; then
BODY="$BODY..."
fi
else
BODY=""
fi
# 格式化时间戳为东八区时间
TIME=$(TZ='Asia/Shanghai' date -d "$COMMIT_TIMESTAMP" +"%Y-%m-%d %H:%M:%S")
SHORT_SHA=$(echo "$COMMIT_SHA" | cut -c1-7)
# 输出变量
echo "subject=$SUBJECT" >> $GITHUB_OUTPUT
echo "body=$BODY" >> $GITHUB_OUTPUT
echo "time=$TIME" >> $GITHUB_OUTPUT
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
echo "=== 处理结果 ==="
echo "Subject: $SUBJECT"
echo "Body: $BODY"
echo "Time: $TIME"
echo "Short SHA: $SHORT_SHA"
- name: Send Notification
uses: Y2Nk4/qmsg-action@master
with:
# qq: ${{ needs.meta.outputs.version_type == 'ci' && secrets.QMSG_QQ_DEV || secrets.QMSG_QQ_PUB }}
groups: ${{ needs.meta.outputs.version_type == 'ci' && secrets.QMSG_GROUPS_DEV || secrets.QMSG_GROUPS_PUB }}
# groups: ${{ secrets.QMSG_NOTIFY_GROUPS }}
key: ${{ secrets.QMSG_KEY }}
message: |
🏗️ [${{ github.repository }}] 构建完成
👤 提交者: ${{ github.actor }}
🌿 分支: ${{ github.ref_name }}
🏷️ 版本: ${{ needs.meta.outputs.tag }}
📦 类型: ${{ contains(needs.meta.outputs.tag, '-alpha') && '内测版' || (contains(needs.meta.outputs.tag, '-beta') && '公测版' || (contains(needs.meta.outputs.tag, '-ci') && '开发版' || '正式版')) }}
🆔 提交哈希: ${{ steps.commit-info.outputs.short_sha }}
⏱️ 提交时间: ${{ steps.commit-info.outputs.time }}
📝 摘要: ${{ steps.commit-info.outputs.subject }}
${{ steps.commit-info.outputs.body != '' && format('📖 描述: {0}', steps.commit-info.outputs.body) || '📖 描述: 无' }}
🔗 查看提交: https://github.com/${{ github.repository }}/commit/${{ github.sha }}
🔨 构建下载: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}