#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REPO_URL="https://gitcode.com/hearkenundermoon/MMail"
VERSION=""
MESSAGE=""
ASSUME_YES=0
while [[ $# -gt 0 ]]; do
case "$1" in
-m|--message)
MESSAGE="${2:-}"
shift 2
;;
--yes|-y)
ASSUME_YES=1
shift
;;
-h|--help)
sed -n '2,25p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
exit 0
;;
-*)
echo "错误:未知参数 $1" >&2
exit 1
;;
*)
if [[ -n "$VERSION" ]]; then
echo "错误:版本号只能给一个(已有 $VERSION)" >&2
exit 1
fi
VERSION="$1"
shift
;;
esac
done
cd "$ROOT"
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "错误:不在 git 仓库中。" >&2
exit 1
fi
if ! git remote get-url origin >/dev/null 2>&1; then
echo "错误:未配置 origin,无法推送。" >&2
exit 1
fi
BRANCH="$(git branch --show-current)"
LAST_TAG="$(git describe --tags --match 'v[0-9]*' --abbrev=0 2>/dev/null || true)"
if [[ -n "$LAST_TAG" ]]; then
COMMITS_SINCE="$(git rev-list --count "${LAST_TAG}..HEAD")"
else
COMMITS_SINCE="$(git rev-list --count HEAD)"
fi
if [[ -z "$VERSION" ]]; then
echo "用法:$0 <版本号> [-m \"提交说明\"] [--yes]"
echo ""
echo "当前状态:"
echo " 分支 : $BRANCH"
echo " 最近的版本tag: ${LAST_TAG:-(还没有任何一个)}"
echo " 该 tag 之后 : $COMMITS_SINCE 个提交"
echo " 工作区 : $([[ -n "$(git status --porcelain)" ]] && echo '有未提交改动' || echo '干净')"
echo ""
echo "版本号需要你自己定:破坏性变更跳 major,新功能跳 minor,只修 bug 跳 patch。"
exit 1
fi
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "错误:版本号应为 x.y.z 形式(如 0.1.0),收到「$VERSION」。" >&2
exit 1
fi
TAG="v$VERSION"
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
echo "错误:tag $TAG 在本地已存在。" >&2
exit 1
fi
if git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then
echo "错误:远端 origin 上已有 tag $TAG。" >&2
exit 1
fi
DIRTY="$(git status --porcelain)"
if [[ -n "$DIRTY" && -z "$MESSAGE" ]]; then
echo "错误:工作区有未提交改动,但没给 -m 提交说明。" >&2
echo " 要么自己先提交干净,要么用:" >&2
echo " $0 $VERSION -m \"提交说明\"" >&2
echo "" >&2
echo " 待提交的内容:" >&2
echo "$DIRTY" | head -20 >&2
exit 1
fi
confirm() {
if [[ "$ASSUME_YES" == "1" ]]; then
echo " $1 → 已用 --yes 跳过确认"
return 0
fi
local reply=""
read -r -p " $1 [y/N] " reply || true
[[ "$reply" == "y" || "$reply" == "Y" ]]
}
echo "==> 发版计划"
echo " 版本号 : $VERSION(tag $TAG)"
echo " 分支 : $BRANCH"
echo " 上一个 tag: ${LAST_TAG:-无}"
echo " 推送目标 : $(git remote get-url origin)"
echo " 提交 : $([[ -n "$DIRTY" ]] && echo "有未提交改动,将一并提交(说明:$MESSAGE)" || echo '无(工作区干净)')"
echo " 产物 : publish/(执行完 publish.sh --release 后生成)"
if [[ "$BRANCH" != "main" && "$BRANCH" != "master" ]]; then
echo ""
echo " 注意:当前不在 main/master 分支上,而是在 $BRANCH。"
fi
echo ""
if ! confirm "确认按上述计划发版?"; then
echo "已取消,未做任何改动。"
exit 0
fi
if [[ -n "$DIRTY" ]]; then
echo ""
echo "==> 提交改动"
git add -A
git commit -q -m "$MESSAGE"
echo " 已提交:$(git log -1 --format=%s)"
fi
echo ""
echo "==> 打 tag $TAG"
git tag -a "$TAG" -m "$TAG"
echo ""
echo "==> 推送到 origin"
if ! git push origin "$BRANCH"; then
echo "" >&2
echo "错误:分支推送失败。tag $TAG 已在本地创建,但没有推上去。" >&2
echo " 解决后手动执行:git push origin $TAG" >&2
echo " 或撤销这个 tag:git tag -d $TAG" >&2
exit 1
fi
if ! git push origin "$TAG"; then
echo "" >&2
echo "错误:tag 推送失败(分支已推上去)。" >&2
echo " 解决后手动执行:git push origin $TAG" >&2
echo " 或撤销这个 tag:git tag -d $TAG && git push origin --delete $TAG" >&2
exit 1
fi
echo ""
echo "==> 生成产物"
if ! "$ROOT/scripts/publish.sh" --release; then
echo "" >&2
echo "错误:产物生成失败,但 tag $TAG 已经推上去了。" >&2
echo " 代码与 tag 都是好的,修好构建问题后重新执行即可:" >&2
echo " ./scripts/release.sh $(( ${VERSION%%.*} )).x.x # 换一个新版本号" >&2
echo " 或者只是想重出产物(不打新 tag):" >&2
echo " git checkout $TAG && ./scripts/publish.sh --release" >&2
exit 1
fi
echo ""
echo "==> 上传产物到 Release"
TOKEN_FILE="$ROOT/atomgit_token"
if [[ ! -f "$TOKEN_FILE" ]]; then
echo ""
echo " 跳过:没找到凭据文件 atomgit_token。"
echo " Release 还没建,产物也没上传,需要手工做:"
echo ""
echo " $REPO_URL/releases/new?tag=$TAG"
echo ""
echo " 把 token 放进项目根的 atomgit_token 之后,可以只补上传这一步:"
echo " ./scripts/upload-release.py $TAG"
echo ""
echo "==> 发版完成:$TAG(产物未上传)"
exit 0
fi
if python3 "$ROOT/scripts/upload-release.py" "$TAG"; then
echo ""
echo "==> 发版完成:$TAG"
echo " $REPO_URL/releases/tag/$TAG"
echo " 已部署实例的设置页点「检查更新」就能看到这个版本。"
else
echo "" >&2
echo "错误:产物上传失败。但代码、tag、本地产物都是好的,只需补传:" >&2
echo " ./scripts/upload-release.py $TAG" >&2
exit 1
fi