mirror of
https://github.com/zsbai/wechat-versions.git
synced 2026-05-13 07:16:43 +00:00
refactor: rewrite destVersionForMac in python; remove redundant code & files; new workflow parameter: Force create release using latest WeChat dmg;
This commit is contained in:
11
.github/workflows/destversion.yml
vendored
11
.github/workflows/destversion.yml
vendored
@@ -4,15 +4,20 @@ on:
|
||||
schedule:
|
||||
- cron: '0 7 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
force_release:
|
||||
description: 'Force create release (ignore version checks)'
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
save_new_wechat:
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# - name: Test Github Action Server Time
|
||||
# run: echo `date`
|
||||
|
||||
- name: Check new version and push
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: bash -x ./scripts/destVersionForMac.sh ${{ github.event.inputs.download_link }}
|
||||
FORCE_RELEASE: ${{ github.event.inputs.force_release }}
|
||||
run: python3 ./scripts/destVersionForMac.py
|
||||
|
||||
18
.github/workflows/notify.yml
vendored
18
.github/workflows/notify.yml
vendored
@@ -1,18 +0,0 @@
|
||||
name: Wechat Release Notify
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
|
||||
jobs:
|
||||
notify_to_tg:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Check new version and push
|
||||
env:
|
||||
GHTOKEN: ${{ secrets.GHTOKEN }}
|
||||
BOTTOKEN: ${{ secrets.BOTTOKEN }}
|
||||
CHATIDS: ${{ secrets.CHATIDS }}
|
||||
run: bash -x ./scripts/notify.sh
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
temp/
|
||||
|
||||
################################################################################
|
||||
# This below was automatically created for macOS system
|
||||
################################################################################
|
||||
|
||||
10
README.md
10
README.md
@@ -1,16 +1,6 @@
|
||||
# Wechat-Versions-For-macOS
|
||||
收集 Mac 微信版本并保存
|
||||
|
||||
## 目录结构
|
||||
```shell
|
||||
├── README.md # 自述文件
|
||||
├── WeChatSetup # 微信安装包临时目录
|
||||
│ └── temp # 临时目录
|
||||
└── scripts # 脚本目录
|
||||
├── destVersioForMac.sh # 获取安装包及取得版本号与 hash 值的脚本
|
||||
└── notify.sh # 新release 时调用通知的脚本
|
||||
```
|
||||
|
||||
## 说明
|
||||
|
||||
* 2024.10.1 更新:通过转换dmg为img后解压,获取精确的微信版本号(例如:3.8.9.xx);在此之前,后两位小版本号无法获取,所以通过添加更新日期后缀来区分大版本中的小版本,需在下载前自行判断。
|
||||
|
||||
491
scripts/destVersionForMac.py
Normal file
491
scripts/destVersionForMac.py
Normal file
@@ -0,0 +1,491 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import datetime
|
||||
import hashlib
|
||||
import html.parser
|
||||
import os
|
||||
import plistlib
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
WEBSITE_URL = "https://mac.weixin.qq.com/?t=mac&lang=zh_CN"
|
||||
BASE_DIR = Path.cwd() / "WeChatMac"
|
||||
TEMP_DIR = BASE_DIR / "temp"
|
||||
|
||||
class DownloadLinkParser(html.parser.HTMLParser):
|
||||
"""
|
||||
从微信 Mac 官方网站的 HTML 中解析下载链接。
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.link = ""
|
||||
|
||||
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
|
||||
if tag != "a" or self.link:
|
||||
return
|
||||
attrs_dict = {key: value or "" for key, value in attrs}
|
||||
classes = attrs_dict.get("class", "").split()
|
||||
if "download-button" in classes:
|
||||
self.link = attrs_dict.get("href", "").strip()
|
||||
|
||||
def run(
|
||||
cmd: list[str], check: bool = True, capture: bool = True
|
||||
) -> subprocess.CompletedProcess:
|
||||
"""
|
||||
运行子进程命令的辅助函数,默认捕获输出并检查返回码。
|
||||
|
||||
Args:
|
||||
cmd (list[str]): 要执行的命令列表。
|
||||
check (bool, optional): 是否检查命令返回码。默认为 True。
|
||||
capture (bool, optional): 是否捕获命令输出。默认为 True。
|
||||
|
||||
Returns:
|
||||
subprocess.CompletedProcess: 子进程执行结果。
|
||||
"""
|
||||
return subprocess.run(
|
||||
cmd,
|
||||
check=check,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE if capture else None,
|
||||
stderr=subprocess.STDOUT if capture else None,
|
||||
)
|
||||
|
||||
def log(message: str) -> None:
|
||||
"""
|
||||
打印日志信息并立即刷新输出缓冲区。
|
||||
|
||||
Args:
|
||||
message (str): 要打印的日志信息。
|
||||
"""
|
||||
print(message, flush=True)
|
||||
|
||||
def fetch_download_link() -> str:
|
||||
"""
|
||||
从微信 Mac 官方网站获取最新的下载链接
|
||||
|
||||
Raises:
|
||||
RuntimeError: 如果在网站上未找到下载链接
|
||||
|
||||
Returns:
|
||||
str: 下载链接 URL
|
||||
"""
|
||||
# Fetch HTML and extract the first download link on the page.
|
||||
with urllib.request.urlopen(WEBSITE_URL, timeout=30) as response:
|
||||
html = response.read().decode("utf-8", errors="replace")
|
||||
parser = DownloadLinkParser()
|
||||
parser.feed(html)
|
||||
if not parser.link:
|
||||
raise RuntimeError("Download link not found on website.")
|
||||
return parser.link
|
||||
|
||||
def fetch_head_metadata(url: str) -> dict[str, str]:
|
||||
"""
|
||||
使用 HEAD 请求从直接文件链接读取元数据。
|
||||
Args:
|
||||
url (str): 文件的直接下载链接
|
||||
Returns:
|
||||
dict[str, str]: 从 HEAD 响应中提取的元数据字典,键为小写字符串,值为对应的响应头值
|
||||
"""
|
||||
|
||||
# Use HEAD request to read metadata from the direct file link.
|
||||
request = urllib.request.Request(url, method="HEAD")
|
||||
with urllib.request.urlopen(request, timeout=30) as response:
|
||||
return {key.lower(): value.strip() for key, value in response.headers.items()}
|
||||
|
||||
def download_with_retry(url: str, dest: Path) -> None:
|
||||
"""
|
||||
下载软件包
|
||||
|
||||
Args:
|
||||
url (str): 文件的直接下载链接
|
||||
dest (Path): 目标文件路径
|
||||
"""
|
||||
# Keep temp files under current working directory for debugging.
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
attempts = 2
|
||||
last_error: Exception | None = None
|
||||
for attempt in range(1, attempts + 1):
|
||||
try:
|
||||
run(
|
||||
[
|
||||
"wget",
|
||||
"--quiet",
|
||||
"--tries",
|
||||
"5",
|
||||
"--waitretry",
|
||||
"5",
|
||||
"--retry-connrefused",
|
||||
"--timeout",
|
||||
"30",
|
||||
url,
|
||||
"-O",
|
||||
str(dest),
|
||||
]
|
||||
)
|
||||
return
|
||||
except Exception as exc:
|
||||
last_error = exc
|
||||
if attempt < attempts:
|
||||
log(f"Download failed (attempt {attempt}). Waiting before retry...")
|
||||
time.sleep(10)
|
||||
if last_error:
|
||||
raise last_error
|
||||
|
||||
def mount_dmg(dmg_path: Path) -> str:
|
||||
"""
|
||||
挂载目标 dmg 镜像到本地
|
||||
|
||||
Args:
|
||||
dmg_path (Path): dmg 镜像文件路径
|
||||
|
||||
Raises:
|
||||
RuntimeError: 如果挂载失败或未找到挂载点
|
||||
|
||||
Returns:
|
||||
str: 挂载点路径
|
||||
"""
|
||||
# Mount DMG and capture the mount point under /Volumes.
|
||||
result = run(["hdiutil", "attach", str(dmg_path), "-nobrowse"])
|
||||
matches = re.findall(r"(/Volumes/[^\n]+)", result.stdout)
|
||||
if not matches:
|
||||
raise RuntimeError("Failed to mount DMG.")
|
||||
return matches[-1].strip()
|
||||
|
||||
def detach_dmg(mount_dir: str) -> None:
|
||||
"""
|
||||
解除挂载对应目录
|
||||
|
||||
Args:
|
||||
mount_dir (str): 挂载点路径
|
||||
"""
|
||||
run(["hdiutil", "detach", mount_dir], check=False)
|
||||
|
||||
def get_tag_from_plist(mount_dir: str) -> str:
|
||||
"""
|
||||
解析 Info.plist 构建 Tag 标签
|
||||
|
||||
如果 WeChatBundleVersion 存在则直接使用,否则使用 CFBundleShortVersionString 和 CFBundleVersion 组合的形式。
|
||||
|
||||
Args:
|
||||
mount_dir (str): 挂载路径
|
||||
|
||||
Raises:
|
||||
RuntimeError: 如果 Info.plist 文件未找到
|
||||
RuntimeError: 如果 CFBundleShortVersionString 未找到
|
||||
RuntimeError: 如果 CFBundleVersion 未找到
|
||||
|
||||
Returns:
|
||||
str: 构建标签
|
||||
"""
|
||||
# Info.plist lives inside the mounted WeChat.app bundle.
|
||||
info_plist = Path(mount_dir) / "WeChat.app" / "Contents" / "Info.plist"
|
||||
if not info_plist.exists():
|
||||
raise RuntimeError("Info.plist not found in mounted volume.")
|
||||
with info_plist.open("rb") as handle:
|
||||
data = plistlib.load(handle)
|
||||
short_version = str(data.get("CFBundleShortVersionString", "")).strip()
|
||||
build = str(data.get("CFBundleVersion", "")).strip()
|
||||
version = str(data.get("WeChatBundleVersion", "")).strip()
|
||||
|
||||
if not short_version:
|
||||
raise RuntimeError("CFBundleShortVersionString not found.")
|
||||
if not build:
|
||||
raise RuntimeError("CFBundleVersion not found.")
|
||||
if version:
|
||||
tag = version
|
||||
else:
|
||||
tag = f"{short_version}+build.{build}"
|
||||
return tag
|
||||
|
||||
def compute_sha256(file_path: Path) -> str:
|
||||
"""
|
||||
计算文件的 sha256
|
||||
|
||||
Args:
|
||||
file_path (Path): 文件路径
|
||||
|
||||
Returns:
|
||||
str: 文件的 sha256 值
|
||||
"""
|
||||
digest = hashlib.sha256()
|
||||
with file_path.open("rb") as handle:
|
||||
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
def parse_release_body(body: str) -> dict[str, str]:
|
||||
"""
|
||||
从 GitHub release 的 body 文本中解析出键值对信息,返回一个字典。
|
||||
|
||||
Args:
|
||||
body (str): GitHub release 的 body 文本,预期包含多行 "Key: Value" 格式的内容。
|
||||
|
||||
Returns:
|
||||
dict[str, str]: 从 body 中解析出的键值对字典.
|
||||
"""
|
||||
info: dict[str, str] = {}
|
||||
for line in body.splitlines():
|
||||
if ":" not in line:
|
||||
continue
|
||||
key, value = line.split(":", 1)
|
||||
info[key.lstrip("- ").strip()] = value.strip()
|
||||
return info
|
||||
|
||||
def get_latest_release_info() -> dict[str, str]:
|
||||
"""
|
||||
获取最新 GitHub release 的信息,返回一个包含键值对的字典。
|
||||
|
||||
Returns:
|
||||
dict[str, str]: 包含最新 release 信息的字典
|
||||
"""
|
||||
result = run(
|
||||
["gh", "release", "view", "--json", "body", "--jq", ".body"], check=False
|
||||
)
|
||||
if result.returncode != 0 or not result.stdout:
|
||||
return {}
|
||||
return parse_release_body(result.stdout)
|
||||
|
||||
def tag_exists(tag: str) -> bool:
|
||||
"""
|
||||
检查指定的 Git 标签是否已经存在于远程仓库中。
|
||||
|
||||
Args:
|
||||
tag (str): Git 标签名称
|
||||
|
||||
Returns:
|
||||
bool: 指定的 Git 标签是否存在
|
||||
"""
|
||||
result = run(["gh", "release", "view", tag, "--json", "tagName"], check=False)
|
||||
return result.returncode == 0
|
||||
|
||||
def build_release_notes(
|
||||
tag: str,
|
||||
download_link: str,
|
||||
remote_md5: str,
|
||||
sha256_sum: str,
|
||||
remote_size: str,
|
||||
remote_last_modified: str,
|
||||
) -> str:
|
||||
"""
|
||||
构建 Release 发布信息
|
||||
|
||||
Args:
|
||||
tag (str): 构建版本号
|
||||
download_link (str): 下载链接
|
||||
remote_md5 (str): 远端md5的值
|
||||
sha256_sum (str): sha256的值
|
||||
remote_size (str): 文件大小
|
||||
remote_last_modified (str): 最后修改时间
|
||||
|
||||
Returns:
|
||||
str: 最终的发布信息
|
||||
"""
|
||||
lines = [
|
||||
"WeChat for Mac automatic release",
|
||||
"",
|
||||
"Download and integrity details are below.",
|
||||
"",
|
||||
"Release details",
|
||||
f"- DestVersion: {tag}",
|
||||
"",
|
||||
"Source and checksums",
|
||||
f"- DownloadFrom: {download_link}",
|
||||
f"- Md5: {remote_md5}",
|
||||
f"- Sha256: {sha256_sum}",
|
||||
]
|
||||
if remote_size:
|
||||
lines.append(f"- ContentLength: {remote_size}")
|
||||
if remote_last_modified:
|
||||
lines.append(f"- LastModified: {remote_last_modified}")
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
def write_sha_file(
|
||||
sha_file: Path,
|
||||
tag: str,
|
||||
download_link: str,
|
||||
sha256_sum: str,
|
||||
remote_md5: str,
|
||||
remote_size: str,
|
||||
remote_last_modified: str,
|
||||
) -> None:
|
||||
"""
|
||||
写入 SHA 文件
|
||||
|
||||
Args:
|
||||
sha_file (Path): SHA 文件路径
|
||||
tag (str): 构建标签
|
||||
download_link (str): 下载链接
|
||||
sha256_sum (str): sha256的值
|
||||
remote_md5 (str): 远端md5的值
|
||||
remote_size (str): 文件大小
|
||||
remote_last_modified (str): 最后修改时间
|
||||
"""
|
||||
timestamp = datetime.datetime.now(datetime.timezone.utc).strftime(
|
||||
"%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
lines = [
|
||||
f"DestVersion: {tag}",
|
||||
f"Md5: {remote_md5}",
|
||||
f"Sha256: {sha256_sum}",
|
||||
]
|
||||
if remote_size:
|
||||
lines.append(f"ContentLength: {remote_size}")
|
||||
if remote_last_modified:
|
||||
lines.append(f"LastModified: {remote_last_modified}")
|
||||
lines.extend(
|
||||
[
|
||||
f"UpdateTime: {timestamp} (UTC)",
|
||||
f"DownloadFrom: {download_link}",
|
||||
]
|
||||
)
|
||||
sha_file.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
|
||||
def main() -> int:
|
||||
BASE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
TEMP_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
mount_dir = ""
|
||||
try:
|
||||
force_release = os.environ.get("FORCE_RELEASE", "").strip().lower() in {
|
||||
"1",
|
||||
"true",
|
||||
"yes",
|
||||
"on",
|
||||
}
|
||||
log(f"Force release: {'true' if force_release else 'false'}")
|
||||
|
||||
# Step 1: resolve download link from website.
|
||||
log("Resolving download link from website...")
|
||||
download_link = fetch_download_link()
|
||||
log(f"Download link: {download_link}")
|
||||
|
||||
# Step 2: read metadata from HEAD response.
|
||||
log("Fetching HEAD metadata...")
|
||||
headers = fetch_head_metadata(download_link)
|
||||
remote_md5 = headers.get("x-cos-meta-md5", "")
|
||||
remote_size = headers.get("content-length", "")
|
||||
remote_last_modified = headers.get("last-modified", "")
|
||||
log(
|
||||
"HEAD metadata: "
|
||||
f"md5={remote_md5 or 'n/a'}, "
|
||||
f"size={remote_size or 'n/a'}, "
|
||||
f"last_modified={remote_last_modified or 'n/a'}"
|
||||
)
|
||||
|
||||
# Step 3: compare with latest release by MD5 to avoid downloads.
|
||||
log("Fetching latest GitHub release info...")
|
||||
latest_info = get_latest_release_info()
|
||||
latest_md5 = latest_info.get("Md5", "")
|
||||
latest_sha256 = latest_info.get("Sha256", "")
|
||||
log(
|
||||
"Latest release: "
|
||||
f"md5={latest_md5 or 'n/a'}, "
|
||||
f"sha256={latest_sha256 or 'n/a'}"
|
||||
)
|
||||
|
||||
if remote_md5 and latest_md5 and remote_md5 == latest_md5:
|
||||
if force_release:
|
||||
log("MD5 matches latest release, but force release is enabled.")
|
||||
else:
|
||||
log("No new version detected by MD5. Skipping download.")
|
||||
return 0
|
||||
|
||||
# Step 4: download DMG with retry.
|
||||
log("Downloading DMG...")
|
||||
dmg_path = TEMP_DIR / "WeChatMac.dmg"
|
||||
download_with_retry(download_link, dmg_path)
|
||||
log(f"Downloaded DMG to {dmg_path}")
|
||||
|
||||
# Step 5: mount DMG and read plist values.
|
||||
log("Mounting DMG and reading Info.plist...")
|
||||
mount_dir = mount_dmg(dmg_path)
|
||||
tag = get_tag_from_plist(mount_dir)
|
||||
detach_dmg(mount_dir)
|
||||
mount_dir = ""
|
||||
log(f"Detected tag: {tag}")
|
||||
|
||||
# Step 6: prepare release assets in workspace.
|
||||
log("Preparing release assets...")
|
||||
version_dir = BASE_DIR / tag
|
||||
version_dir.mkdir(parents=True, exist_ok=True)
|
||||
final_dmg = version_dir / f"WeChatMac-{tag}.dmg"
|
||||
shutil.copy2(dmg_path, final_dmg)
|
||||
|
||||
sha256_sum = compute_sha256(final_dmg)
|
||||
log(f"Computed SHA256: {sha256_sum}")
|
||||
sha_file = version_dir / f"WeChatMac-{tag}.dmg.sha256"
|
||||
write_sha_file(
|
||||
sha_file,
|
||||
tag,
|
||||
download_link,
|
||||
sha256_sum,
|
||||
remote_md5,
|
||||
remote_size,
|
||||
remote_last_modified,
|
||||
)
|
||||
|
||||
if not latest_md5 and latest_sha256 and sha256_sum == latest_sha256:
|
||||
if force_release:
|
||||
log("SHA256 matches latest release, but force release is enabled.")
|
||||
else:
|
||||
log("No new version detected by SHA256. Skipping release.")
|
||||
return 0
|
||||
if not latest_md5:
|
||||
log("Latest release has no MD5, used SHA256 fallback check.")
|
||||
|
||||
if tag_exists(tag):
|
||||
suffix = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%d")
|
||||
tag = f"{tag}_{suffix}"
|
||||
log(f"Release tag: {tag}")
|
||||
|
||||
title = f"Wechat For Mac {tag}"
|
||||
notes_content = build_release_notes(
|
||||
tag,
|
||||
download_link,
|
||||
remote_md5,
|
||||
sha256_sum,
|
||||
remote_size,
|
||||
remote_last_modified,
|
||||
)
|
||||
notes_file = TEMP_DIR / "release_notes.txt"
|
||||
notes_file.write_text(notes_content, encoding="utf-8")
|
||||
log(f"Release notes written to {notes_file}")
|
||||
|
||||
# Step 7: publish release with assets and notes.
|
||||
log("Creating GitHub release...")
|
||||
run(
|
||||
[
|
||||
"gh",
|
||||
"release",
|
||||
"create",
|
||||
tag,
|
||||
str(final_dmg),
|
||||
str(sha_file),
|
||||
"-F",
|
||||
str(notes_file),
|
||||
"-t",
|
||||
title,
|
||||
]
|
||||
)
|
||||
log("GitHub release created.")
|
||||
|
||||
return 0
|
||||
finally:
|
||||
# Always detach and cleanup to keep workspace tidy.
|
||||
if mount_dir:
|
||||
detach_dmg(mount_dir)
|
||||
shutil.rmtree(BASE_DIR, ignore_errors=True)
|
||||
log("Cleanup completed.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
sys.exit(main())
|
||||
except Exception as exc:
|
||||
print(f"Error: {exc}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@@ -1,221 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# ====================================================
|
||||
# 配置变量
|
||||
# ====================================================
|
||||
TEMP_PATH="WeChatMac/temp"
|
||||
WEBSITE_URL="https://mac.weixin.qq.com/?t=mac&lang=zh_CN"
|
||||
DOWNLOAD_LINK=""
|
||||
|
||||
# ====================================================
|
||||
# 函数定义
|
||||
# ====================================================
|
||||
|
||||
# 打印分隔线
|
||||
print_separator() {
|
||||
printf '%*s\n' 60 | tr ' ' '#'
|
||||
}
|
||||
|
||||
# 彩色输出函数
|
||||
echo_color() {
|
||||
local color="$1"
|
||||
shift
|
||||
local message="$*"
|
||||
case "$color" in
|
||||
yellow)
|
||||
echo -e "\033[1;33m$message\033[0m"
|
||||
;;
|
||||
red)
|
||||
echo -e "\033[1;31m$message\033[0m" >&2
|
||||
;;
|
||||
green)
|
||||
echo -e "\033[1;32m$message\033[0m"
|
||||
;;
|
||||
*)
|
||||
echo "$message"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 安装依赖项
|
||||
install_depends() {
|
||||
print_separator
|
||||
echo_color "yellow" "Installing dependencies: wget, curl, git, gh, shasum, pup"
|
||||
print_separator
|
||||
|
||||
brew install wget curl git gh pup
|
||||
}
|
||||
|
||||
# 下载 WeChat DMG
|
||||
download_wechat() {
|
||||
DOWNLOAD_LINK=$(curl -s "$WEBSITE_URL" | pup 'a.download-button:nth-of-type(1) attr{href}')
|
||||
|
||||
print_separator
|
||||
echo_color "yellow" "Downloading the newest WeChatMac..."
|
||||
print_separator
|
||||
|
||||
mkdir -p "$TEMP_PATH"
|
||||
|
||||
wget -q "$DOWNLOAD_LINK" -O "${TEMP_PATH}/WeChatMac.dmg"
|
||||
if [ "$?" -ne 0 ]; then
|
||||
echo_color "red" "Download Failed, please check your network!"
|
||||
clean_data 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 从 Info.plist 提取版本信息
|
||||
get_version() {
|
||||
print_separator
|
||||
echo_color "yellow" "Extracting version from DMG (macOS)..."
|
||||
print_separator
|
||||
|
||||
# 挂载 dmg
|
||||
MOUNT_DIR=$(hdiutil attach "${TEMP_PATH}/WeChatMac.dmg" -nobrowse | sed -n 's/^.*\(\/Volumes\/.*\)$/\1/p' | tail -n1)
|
||||
|
||||
if [ -z "$MOUNT_DIR" ]; then
|
||||
echo_color "red" "Failed to mount DMG!"
|
||||
clean_data 1
|
||||
fi
|
||||
|
||||
# 定位 Info.plist
|
||||
# INFO_PLIST=$(find "${MOUNT_DIR}" -type f -name "Info.plist" | head -n 1)
|
||||
INFO_PLIST="${MOUNT_DIR}/WeChat.app/Contents/Info.plist"
|
||||
|
||||
if [ ! -f "$INFO_PLIST" ]; then
|
||||
echo_color "red" "Info.plist not found in mounted volume!"
|
||||
hdiutil detach "$MOUNT_DIR"
|
||||
clean_data 1
|
||||
fi
|
||||
|
||||
# 使用 grep 和 sed 提取版本号
|
||||
VERSION=$(grep -A1 '<key>CFBundleShortVersionString</key>' "$INFO_PLIST" | grep '<string>' | sed -E 's/.*<string>([^<]+)<\/string>.*/\1/')
|
||||
|
||||
# 使用 grep 和 sed 提取构建版本号
|
||||
BUILD_VERSION=$(grep -A1 '<key>CFBundleVersion</key>' "$INFO_PLIST" | grep '<string>' | sed -E 's/.*<string>([^<]+)<\/string>.*/\1/')
|
||||
|
||||
# 卸载 dmg
|
||||
hdiutil detach "$MOUNT_DIR"
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo_color "red" "Version information not found in Info.plist!"
|
||||
clean_data 1
|
||||
fi
|
||||
|
||||
echo "Version: $VERSION"
|
||||
}
|
||||
|
||||
|
||||
# 计算 SHA256
|
||||
compute_sha256() {
|
||||
local file_path="$1"
|
||||
shasum -a 256 "$file_path" | awk '{print $1}'
|
||||
}
|
||||
|
||||
# 准备提交(复制 DMG 并创建 .sha256 文件)
|
||||
prepare_commit() {
|
||||
print_separator
|
||||
echo_color "yellow" "Preparing to commit new version..."
|
||||
print_separator
|
||||
|
||||
VERSION_DIR="WeChatMac/$VERSION"
|
||||
mkdir -p "$VERSION_DIR"
|
||||
|
||||
cp "${TEMP_PATH}/WeChatMac.dmg" "$VERSION_DIR/WeChatMac-$VERSION.dmg"
|
||||
|
||||
NOW_SUM256=$(compute_sha256 "$VERSION_DIR/WeChatMac-$VERSION.dmg")
|
||||
|
||||
cat > "$VERSION_DIR/WeChatMac-$VERSION.dmg.sha256" <<EOF
|
||||
DestVersion: $VERSION
|
||||
DestBuild: $BUILD_VERSION
|
||||
Sha256: $NOW_SUM256
|
||||
UpdateTime: $(date -u '+%Y-%m-%d %H:%M:%S') (UTC)
|
||||
DownloadFrom: $DOWNLOAD_LINK
|
||||
EOF
|
||||
|
||||
echo "SHA256: $NOW_SUM256"
|
||||
}
|
||||
|
||||
# 获取最新的 GitHub Release 信息
|
||||
get_latest_release_info() {
|
||||
print_separator
|
||||
echo_color "yellow" "Getting latest GitHub release info..."
|
||||
print_separator
|
||||
|
||||
LATEST_BODY=$(gh release view --json body --jq ".body" || true)
|
||||
|
||||
if [ -z "$LATEST_BODY" ]; then
|
||||
LATEST_SUM256=""
|
||||
LATEST_VERSION=""
|
||||
else
|
||||
LATEST_SUM256=$(echo "$LATEST_BODY" | grep 'Sha256:' | awk -F': ' '{print $2}')
|
||||
LATEST_VERSION=$(echo "$LATEST_BODY" | grep 'DestVersion:' | awk -F': ' '{print $2}')
|
||||
fi
|
||||
|
||||
echo "Latest Version: $LATEST_VERSION"
|
||||
echo "Latest SHA256: $LATEST_SUM256"
|
||||
}
|
||||
|
||||
# 创建新的 GitHub Release
|
||||
create_release() {
|
||||
print_separator
|
||||
echo_color "yellow" "Creating new GitHub release..."
|
||||
print_separator
|
||||
|
||||
if [ "$VERSION" = "$LATEST_VERSION" ]; then
|
||||
VERSION_TAG="${VERSION}_$(date -u '+%Y%m%d')"
|
||||
else
|
||||
VERSION_TAG="$VERSION"
|
||||
fi
|
||||
|
||||
gh release create "v$VERSION_TAG" "WeChatMac/$VERSION/WeChatMac-$VERSION.dmg" -F "WeChatMac/$VERSION/WeChatMac-$VERSION.dmg.sha256" -t "Wechat For Mac v$VERSION_TAG"
|
||||
}
|
||||
|
||||
# 清理临时数据并退出
|
||||
clean_data() {
|
||||
print_separator
|
||||
echo_color "yellow" "Cleaning runtime and exiting..."
|
||||
print_separator
|
||||
|
||||
rm -rf "WeChatMac"
|
||||
exit "$1"
|
||||
}
|
||||
|
||||
# ====================================================
|
||||
# 主流程
|
||||
# ====================================================
|
||||
main() {
|
||||
# 创建临时目录
|
||||
mkdir -p "$TEMP_PATH"
|
||||
|
||||
# 安装依赖项
|
||||
install_depends
|
||||
|
||||
# 下载 WeChat DMG
|
||||
download_wechat
|
||||
|
||||
# 提取版本信息
|
||||
get_version
|
||||
|
||||
# 准备提交(复制 DMG 并创建 .sha256 文件)
|
||||
prepare_commit
|
||||
|
||||
# 获取最新的 GitHub Release 信息
|
||||
get_latest_release_info
|
||||
|
||||
# 比较 SHA256 值
|
||||
if [ "$NOW_SUM256" = "$LATEST_SUM256" ] && [ -n "$LATEST_SUM256" ]; then
|
||||
echo_color "green" "This is the newest Version!"
|
||||
clean_data 0
|
||||
fi
|
||||
|
||||
# 创建新的 GitHub Release
|
||||
create_release
|
||||
|
||||
# 清理临时数据并退出
|
||||
clean_data 0
|
||||
}
|
||||
|
||||
# 执行主流程
|
||||
main
|
||||
@@ -1,84 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
|
||||
if [ -z $GHTOKEN ]; then
|
||||
>&2 echo -e "\033[1;31mMissing Github Token(GHTOKEN)! Please get a BotToken from 'Github Settings->Developer settings->Personal access tokens' and set it in Repo Secrect\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z $BOTTOKEN ]; then
|
||||
>&2 echo -e "\033[1;31mMissing Bot Token(BOTTOKEN)! Please get a BotToken from @Botfather on Telegram and set it in Repo Secrect\033[0m"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ -z $CHATIDS ]; then
|
||||
>&2 echo -e "\033[1;31mMissing ChatIds(CHATIDS)! Please get ChatId from @GroupIDbot on Telegram Chats(Muti chatids split with comma ',') and set it in Repo Environment Values\033[0m"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
function login_gh() {
|
||||
printf "#%.0s" {1..60}
|
||||
echo
|
||||
echo -e "## \033[1;33mLogin to github to use github-cli...\033[0m"
|
||||
printf "#%.0s" {1..60}
|
||||
echo
|
||||
|
||||
echo $GHTOKEN > WeChatSetup/temp/GHTOKEN
|
||||
gh auth login --with-token < WeChatSetup/temp/GHTOKEN
|
||||
if [ "$?" -ne 0 ]; then
|
||||
>&2 echo -e "\033[1;31mLogin Failed, please check your network or token!\033[0m"
|
||||
clean_data 1
|
||||
fi
|
||||
rm -rfv WeChatSetup/temp/GHTOKEN
|
||||
}
|
||||
|
||||
### https://kodango.com/sed-and-awk-notes-part-5
|
||||
## start=${1:-""} means as follows in general
|
||||
## if ($1) then
|
||||
## start=$1
|
||||
## else
|
||||
## start=""
|
||||
## end
|
||||
function join_lines() {
|
||||
local delim=${1:-,}
|
||||
sed 'H;$!d;${x;s/^\n//;s/\n/\'$delim'/g}'
|
||||
}
|
||||
|
||||
function clean_data() {
|
||||
printf "#%.0s" {1..60}
|
||||
echo
|
||||
echo -e "## \033[1;33mClean runtime and exit...\033[0m"
|
||||
printf "#%.0s" {1..60}
|
||||
echo
|
||||
|
||||
rm -rfv WeChatSetup/*
|
||||
exit $1
|
||||
}
|
||||
|
||||
function main() {
|
||||
temp_path="WeChatSetup/temp"
|
||||
mkdir -p ${temp_path}
|
||||
|
||||
login_gh
|
||||
|
||||
gh release view --json body --jq ".body" > ${temp_path}/release.info
|
||||
|
||||
release_info=`awk '!/^$|Sha256/ { $1="*"$1"*";sub("UpdateTime", "CheckTime"); if ( match($2, /https?:\/\/([\w\.\/:])*/) ) $2="[Url]("$2")"; print $0 }' ${temp_path}/release.info | join_lines '%0A' | sed 's/ /%20/g'`
|
||||
dest_version=`awk '/DestVersion/ { print $2 }' ${temp_path}/release.info`
|
||||
release_info="$release_info%0A%0A*NotifyFrom:*%20[Github](https://github.com/tom-snow/wechat-windows-versions/releases/tag/v$dest_version)"
|
||||
|
||||
echo $CHATIDS | sed 's/,/\n/g' > ${temp_path}/chat_ids
|
||||
# while IFS="" read -r chatid || [ -n "$chatid" ]
|
||||
while IFS="" read -r chatid
|
||||
do
|
||||
api_link="https://api.telegram.org/bot$BOTTOKEN/sendMessage?chat_id=$chatid&text=*New%20WeChat%20Windows%20Version!!*%0A%0A$release_info&parse_mode=Markdown&disable_web_page_preview=true"
|
||||
curl -s -o /dev/null $api_link
|
||||
done < ${temp_path}/chat_ids
|
||||
|
||||
gh auth logout --hostname github.com | echo "y"
|
||||
clean_data 0
|
||||
}
|
||||
|
||||
main
|
||||
Reference in New Issue
Block a user