Skip to content

Auto Update & Manual Release #276

Auto Update & Manual Release

Auto Update & Manual Release #276

name: Auto Update & Manual Release
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
inputs:
job_type:
description: '任务类型'
required: true
default: 'check_update'
type: choice
options:
- check_update
- force_release
permissions:
contents: write
concurrency:
group: update-webui-kernel-${{ github.ref }}
cancel-in-progress: false
env:
TZ: Asia/Shanghai
jobs:
process-and-release:
if: github.ref_name == 'main'
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- name: Checkout Code
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
# ==========================================
# 步骤 1 & 2: 下载更新
# ==========================================
- name: Update MetaCubeXD
id: webui
if: github.event.inputs.job_type != 'force_release'
run: |
set -euo pipefail
echo "🔍 检查 MetaCubeXD..."
LATEST_DATA=$(curl -sf "https://api.github.com/repos/MetaCubeX/metacubexd/releases/latest") || { echo "❌ API 请求失败"; exit 1; }
VERSION=$(echo "$LATEST_DATA" | jq -r '.tag_name')
ASSET=$(echo "$LATEST_DATA" | jq -ec '[.assets[] | select(.name == "compressed-dist.tgz")][0]')
DOWNLOAD_URL=$(echo "$ASSET" | jq -er '.browser_download_url')
EXPECTED_SHA=$(echo "$ASSET" | jq -er '.digest | select(startswith("sha256:")) | sub("^sha256:"; "")')
if ! printf '%s' "$VERSION" | grep -Eq '^v?[0-9]+([.][0-9]+){1,3}([-+][A-Za-z0-9._-]+)?$'; then
echo "❌ MetaCubeXD 版本格式不可信: $VERSION"
exit 1
fi
echo "ver=$VERSION" >> "$GITHUB_OUTPUT"
curl -fL --retry 3 --retry-delay 5 -o webui.tgz "$DOWNLOAD_URL" || { echo "❌ 下载失败"; exit 1; }
echo "$EXPECTED_SHA webui.tgz" | sha256sum --check --strict
tar -tzf webui.tgz > webui-members.txt
tar -tvzf webui.tgz > webui-members-long.txt
if grep -E '(^/|(^|/)\.\.(/|$))' webui-members.txt >/dev/null; then
echo "❌ WebUI 压缩包包含越界路径"
exit 1
fi
if grep -E '^[lh]' webui-members-long.txt >/dev/null; then
echo "❌ WebUI 压缩包包含链接"
exit 1
fi
mkdir -p temp_webui
tar -xzf webui.tgz -C temp_webui
TARGET_DIR="maodie/config/webui"
rm -rf "$TARGET_DIR"
mkdir -p "$TARGET_DIR"
cp -r temp_webui/* "$TARGET_DIR"/
rm -rf temp_webui webui.tgz
- name: Update Mihomo Kernel
id: kernel
if: github.event.inputs.job_type != 'force_release'
run: |
set -euo pipefail
echo "🔍 检查 Mihomo Kernel..."
LATEST_DATA=$(curl -sf "https://api.github.com/repos/MetaCubeX/mihomo/releases/latest") || { echo "❌ API 请求失败"; exit 1; }
VERSION=$(echo "$LATEST_DATA" | jq -r '.tag_name')
ASSET_NAME="mihomo-android-arm64-v8-${VERSION}.gz"
ASSET=$(echo "$LATEST_DATA" | jq -ec --arg name "$ASSET_NAME" '[.assets[] | select(.name == $name)][0]')
DOWNLOAD_URL=$(echo "$ASSET" | jq -er '.browser_download_url')
EXPECTED_SHA=$(echo "$ASSET" | jq -er '.digest | select(startswith("sha256:")) | sub("^sha256:"; "")')
if ! printf '%s' "$VERSION" | grep -Eq '^v?[0-9]+([.][0-9]+){1,3}([-+][A-Za-z0-9._-]+)?$'; then
echo "❌ Mihomo 版本格式不可信: $VERSION"
exit 1
fi
echo "ver=$VERSION" >> "$GITHUB_OUTPUT"
curl -fL --retry 3 --retry-delay 5 -o Mihomo.gz "$DOWNLOAD_URL" || { echo "❌ 下载失败"; exit 1; }
echo "$EXPECTED_SHA Mihomo.gz" | sha256sum --check --strict
gzip -d Mihomo.gz
file Mihomo | grep -q 'ELF 64-bit.*ARM aarch64'
chmod +x Mihomo
mkdir -p maodie/kernel
mv -f Mihomo maodie/kernel/Mihomo
# ==========================================
# 步骤 3: 核心逻辑 (含 Commit 提取功能)
# ==========================================
- name: Prepare Release Info
id: commit
env:
JOB_TYPE: ${{ github.event.inputs.job_type }}
WEBUI_VER: ${{ steps.webui.outputs.ver }}
KERNEL_VER: ${{ steps.kernel.outputs.ver }}
REPOSITORY: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
RUN_ATTEMPT: ${{ github.run_attempt }}
run: |
set -euo pipefail
MODE="$JOB_TYPE"
: "${MODE:=check_update}"
SHORT_HASH=$(git rev-parse --short HEAD)
TAG_NAME="v$(date +'%Y%m%d')-${RUN_ID}-${RUN_ATTEMPT}"
# --- 提取 Git 变更日志 ---
PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true)
if [ -z "$PREV_TAG" ]; then
COMMIT_LOGS=$(git log --pretty=format:"- %s (%h)" -n 10)
else
COMMIT_LOGS=$(git log --pretty=format:"- %s (%h)" "$PREV_TAG"..HEAD)
fi
: "${COMMIT_LOGS:=- (No new commits)}"
SHOULD_RELEASE="false"
COMMIT_MSG=""
# --- 分支逻辑 ---
if [ "$MODE" = "force_release" ]; then
echo "🚀 强制发布模式"
SHOULD_RELEASE="true"
COMMIT_MSG="chore: manual release $TAG_NAME"
cat > /tmp/body.md <<BODYEOF
📦 **手动发布**
- 版本: $TAG_NAME
- 基础 Commit: $SHORT_HASH
### 📝 变更日志 (Changelog):
$COMMIT_LOGS
BODYEOF
else
# 检测更新模式
git add maodie/config/webui maodie/kernel/Mihomo
CHANGED_FILES=$(git diff --staged --name-only)
if [ -z "$CHANGED_FILES" ]; then
echo "✅ 无更新"
echo "release=false" >> $GITHUB_OUTPUT
exit 0
fi
SHOULD_RELEASE="true"
IS_WEBUI_CHANGED=$(echo "$CHANGED_FILES" | grep -c "maodie/config/webui" || true)
IS_KERNEL_CHANGED=$(echo "$CHANGED_FILES" | grep -c "maodie/kernel/Mihomo" || true)
SUMMARY="🔧 **自动更新**"
if [ "$IS_WEBUI_CHANGED" -gt 0 ] && [ "$IS_KERNEL_CHANGED" -gt 0 ]; then
COMMIT_MSG="chore: update Kernel($KERNEL_VER) & WebUI($WEBUI_VER)"
SUMMARY="🔥 **双重更新**\n- 内核: $KERNEL_VER\n- 面板: $WEBUI_VER"
elif [ "$IS_KERNEL_CHANGED" -gt 0 ]; then
COMMIT_MSG="chore: update Kernel to $KERNEL_VER"
SUMMARY="🚀 **内核更新**\n- 版本: $KERNEL_VER"
elif [ "$IS_WEBUI_CHANGED" -gt 0 ]; then
COMMIT_MSG="chore: update WebUI to $WEBUI_VER"
SUMMARY="🎨 **面板更新**\n- 版本: $WEBUI_VER"
fi
cat > /tmp/body.md <<BODYEOF
$(echo -e "$SUMMARY")
### 📝 变更日志 (Changelog):
$COMMIT_LOGS
📌 **Release Tag**: $TAG_NAME
BODYEOF
fi
# --- 更新配置文件 ---
NEW_VERSION_CODE=$(date +'%s')
OLD_VERSION_CODE=$(sed -n 's/^versionCode=//p' module.prop)
if [ -n "$OLD_VERSION_CODE" ] && [ "$NEW_VERSION_CODE" -le "$OLD_VERSION_CODE" ]; then
echo "❌ versionCode 未严格递增"
exit 1
fi
UPDATE_URL="https://raw.githubusercontent.com/$REPOSITORY/main/update.json"
CHANGELOG_URL="https://raw.githubusercontent.com/$REPOSITORY/main/changelog.md"
cp /tmp/body.md changelog.md
sed -i "s|^version=.*|version=$TAG_NAME|" module.prop
sed -i "s|^versionCode=.*|versionCode=$NEW_VERSION_CODE|" module.prop
sed -i "s|^updateJson=.*|updateJson=$UPDATE_URL|" module.prop
jq -n \
--arg ver "$TAG_NAME" \
--argjson code "$NEW_VERSION_CODE" \
--arg zip "https://github.com/$REPOSITORY/releases/download/$TAG_NAME/Maodie-Launcher-$TAG_NAME.zip" \
--arg log "$CHANGELOG_URL" \
'{ version: $ver, versionCode: $code, zipUrl: $zip, changelog: $log }' > update.json
git add module.prop update.json changelog.md
if git diff --cached --quiet; then
echo "✅ 没有需要提交的变更"
echo "release=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "release=$SHOULD_RELEASE" >> $GITHUB_OUTPUT
echo "release_tag=$TAG_NAME" >> $GITHUB_OUTPUT
echo "commit_msg=$COMMIT_MSG" >> $GITHUB_OUTPUT
git config user.name 'github-actions[bot]'
git config user.email 'github-actions[bot]@users.noreply.github.com'
git commit -m "$COMMIT_MSG"
git push origin "HEAD:refs/heads/release-staging/$TAG_NAME"
echo "release_commit=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
{
echo 'body<<EOF'
cat /tmp/body.md
echo 'EOF'
} >> $GITHUB_OUTPUT
# ==========================================
# 步骤 4: 打包
# ==========================================
- name: Zip Module
if: steps.commit.outputs.release == 'true'
run: |
set -euo pipefail
mkdir -p maodie/run
FINAL_ZIP="Maodie-Launcher-${{ steps.commit.outputs.release_tag }}.zip"
zip -r "$FINAL_ZIP" META-INF maodie webroot customize.sh module.prop service.sh post-fs-data.sh uninstall.sh action.sh \
-x "maodie/run/*" "maodie/config/proxy_providers/*" \
"maodie/config/cache.db" "maodie/config/adblock.enabled" \
"maodie/config/adblock.state" "maodie/config/config.yaml.last-good"
zip "$FINAL_ZIP" maodie/run/.gitkeep
if [ ! -f "$FINAL_ZIP" ]; then
echo "❌ 错误:文件未生成!"
exit 1
fi
unzip -t "$FINAL_ZIP" >/dev/null
unzip -Z1 "$FINAL_ZIP" > zip-members.txt
for required in module.prop customize.sh service.sh maodie/kernel/Mihomo maodie/config/config.yaml; do
grep -Fx "$required" zip-members.txt >/dev/null || { echo "❌ ZIP 缺少 $required"; exit 1; }
done
echo "✅ 打包完成: $(ls -lh "$FINAL_ZIP" | awk '{print $5, $NF}')"
echo "zip_name=$FINAL_ZIP" >> $GITHUB_ENV
# ==========================================
# 步骤 5: 发布 Release
# ==========================================
- name: Create Draft Release
if: steps.commit.outputs.release == 'true'
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
with:
tag_name: ${{ steps.commit.outputs.release_tag }}
target_commitish: ${{ steps.commit.outputs.release_commit }}
name: Maodie Launcher ${{ steps.commit.outputs.release_tag }}
body: ${{ steps.commit.outputs.body }}
draft: true
prerelease: false
files: ${{ env.zip_name }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish Release And Metadata
if: steps.commit.outputs.release == 'true'
env:
COMMIT_MSG: ${{ steps.commit.outputs.commit_msg }}
RELEASE_TAG: ${{ steps.commit.outputs.release_tag }}
RELEASE_COMMIT: ${{ steps.commit.outputs.release_commit }}
TARGET_BRANCH: ${{ github.ref_name }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
git push origin "$RELEASE_COMMIT:refs/heads/$TARGET_BRANCH"
if ! gh release edit "$RELEASE_TAG" --draft=false; then
git revert --no-edit "$RELEASE_COMMIT"
git push origin "HEAD:refs/heads/$TARGET_BRANCH"
exit 1
fi
git push origin --delete "release-staging/$RELEASE_TAG"