main
NoahLan 1 week ago
parent 82b3daa9a4
commit e851f83b13

@ -26,8 +26,13 @@ AI 修改本项目时优先读:
```toml ```toml
dependencies = [ dependencies = [
<<<<<<< HEAD
"iti-flask @ git+ssh://git@example.com/iTi-Flask.git@v0.2.0", "iti-flask @ git+ssh://git@example.com/iTi-Flask.git@v0.2.0",
"iti-system @ git+ssh://git@example.com/iTi-System.git@v0.2.1", "iti-system @ git+ssh://git@example.com/iTi-System.git@v0.2.1",
=======
"iti-flask @ git+https://git.noahlan.cn/iti-framework/iTi-Flask.git@v0.2.2",
"iti-system @ git+https://git.noahlan.cn/iti-framework/iTi-System.git@v0.2.2",
>>>>>>> 9c8246e (docs: docs更新)
] ]
``` ```
@ -97,5 +102,5 @@ uv run pytest -q
```bash ```bash
./scripts/iti-system.sh release ./scripts/iti-system.sh release
./scripts/iti-system.sh release v0.2.1 ./scripts/iti-system.sh release v0.2.2
``` ```

@ -1,4 +1,8 @@
# SPDX-FileCopyrightText: 2025-present NoahLan <6995syu@163.com> # SPDX-FileCopyrightText: 2025-present NoahLan <6995syu@163.com>
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
<<<<<<< HEAD
__version__ = "0.2.1" __version__ = "0.2.1"
=======
__version__ = "0.2.2"
>>>>>>> 9c8246e (docs: docs更新)

@ -35,7 +35,7 @@ iti_system = [
] ]
[tool.uv.sources] [tool.uv.sources]
iti-flask = { path = "../iTi-Flask", editable = true } iti-flask = { git = "https://git.noahlan.cn/iti-framework/iTi-Flask.git", tag = "v0.2.2" }
[tool.pytest.ini_options] [tool.pytest.ini_options]
testpaths = ["tests"] testpaths = ["tests"]

@ -42,7 +42,7 @@ case "$command" in
uv run pytest -q uv run pytest -q
;; ;;
release) release)
uv run python scripts/release.py "$@" sh scripts/release.sh "$@"
;; ;;
*) *)
echo "未知命令:$command" >&2 echo "未知命令:$command" >&2

@ -1,135 +0,0 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import re
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
ABOUT_FILE = ROOT / "iti_system" / "__about__.py"
README_FILE = ROOT / "README.md"
VERSION_RE = re.compile(r"^\d+\.\d+\.\d+$")
ABOUT_VERSION_RE = re.compile(r'(__version__\s*=\s*")([^"]+)(")')
README_DEP_RE = re.compile(
r"(iti-system @ git\+ssh://git@example\.com/iTi-System\.git@)v\d+\.\d+\.\d+"
)
def run(cmd: list[str], *, capture_output: bool = False) -> subprocess.CompletedProcess[str]:
return subprocess.run(
cmd,
cwd=ROOT,
text=True,
capture_output=capture_output,
check=True,
)
def normalize_version(value: str) -> str:
version = value[1:] if value.startswith("v") else value
if not VERSION_RE.fullmatch(version):
raise SystemExit(f"invalid version: {value}")
return version
def version_tuple(version: str) -> tuple[int, int, int]:
major, minor, patch = version.split(".")
return int(major), int(minor), int(patch)
def bump_patch(version: str) -> str:
major, minor, patch = version_tuple(version)
return f"{major}.{minor}.{patch + 1}"
def read_current_version() -> str:
text = ABOUT_FILE.read_text(encoding="utf-8")
match = ABOUT_VERSION_RE.search(text)
if match is None:
raise SystemExit(f"version not found in {ABOUT_FILE}")
return match.group(2)
def write_about_version(version: str) -> None:
text = ABOUT_FILE.read_text(encoding="utf-8")
new_text, count = ABOUT_VERSION_RE.subn(rf"\g<1>{version}\g<3>", text, count=1)
if count != 1:
raise SystemExit(f"version line not updated in {ABOUT_FILE}")
ABOUT_FILE.write_text(new_text, encoding="utf-8")
def write_readme_tags(version: str) -> None:
text = README_FILE.read_text(encoding="utf-8")
new_text, count = README_DEP_RE.subn(rf"\g<1>v{version}", text)
if count != 1:
raise SystemExit(f"dependency examples not updated in {README_FILE}")
README_FILE.write_text(new_text, encoding="utf-8")
def ensure_clean_tree() -> None:
result = run(["git", "status", "--porcelain"], capture_output=True)
if result.stdout.strip():
raise SystemExit("working tree is not clean")
def ensure_branch() -> str:
result = run(["git", "symbolic-ref", "--quiet", "--short", "HEAD"], capture_output=True)
branch = result.stdout.strip()
if not branch:
raise SystemExit("release requires a branch checkout")
return branch
def ensure_tag_absent(tag: str) -> None:
result = subprocess.run(
["git", "rev-parse", "--verify", "--quiet", f"refs/tags/{tag}"],
cwd=ROOT,
text=True,
capture_output=True,
)
if result.returncode == 0:
raise SystemExit(f"tag already exists: {tag}")
def main() -> int:
parser = argparse.ArgumentParser(description="Release the iTi-System package")
parser.add_argument(
"version",
nargs="?",
help="target version, for example 0.2.1 or v0.2.1; defaults to patch bump",
)
args = parser.parse_args()
current_version = read_current_version()
target_version = normalize_version(args.version) if args.version else bump_patch(current_version)
target_tag = f"v{target_version}"
if target_version == current_version:
raise SystemExit(f"version already set to {target_version}")
if version_tuple(target_version) <= version_tuple(current_version):
raise SystemExit(f"target version must be newer than {current_version}")
ensure_clean_tree()
ensure_branch()
ensure_tag_absent(target_tag)
run(["uv", "run", "pytest", "-q"])
write_about_version(target_version)
write_readme_tags(target_version)
run(["git", "add", "iti_system/__about__.py", "README.md"])
run(["git", "commit", "-m", f"chore: release {target_tag}"])
run(["git", "tag", "-a", target_tag, "-m", f"release {target_tag}"])
branch = ensure_branch()
run(["git", "push", "origin", branch, target_tag])
print(f"released {target_tag}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

@ -0,0 +1,176 @@
#!/usr/bin/env sh
set -eu
ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
cd "$ROOT_DIR"
ABOUT_FILE="$ROOT_DIR/iti_system/__about__.py"
README_FILE="$ROOT_DIR/README.md"
die() {
printf '%s\n' "$*" >&2
exit 1
}
parse_version() {
raw=$1
version=${raw#v}
old_ifs=$IFS
IFS=.
set -- $version
IFS=$old_ifs
if [ "$#" -ne 3 ]; then
die "invalid version: $raw"
fi
for part in "$@"; do
case $part in
''|*[!0-9]*)
die "invalid version: $raw"
;;
esac
done
printf '%s %s %s\n' "$1" "$2" "$3"
}
normalize_version() {
set -- $(parse_version "$1")
printf '%s.%s.%s\n' "$1" "$2" "$3"
}
bump_patch() {
set -- $(parse_version "$1")
printf '%s.%s.%s\n' "$1" "$2" "$(( $3 + 1 ))"
}
version_is_newer() {
current_parts=$(parse_version "$1")
set -- $current_parts
current_major=$1
current_minor=$2
current_patch=$3
target_parts=$(parse_version "$2")
set -- $target_parts
target_major=$1
target_minor=$2
target_patch=$3
if [ "$target_major" -gt "$current_major" ]; then
return 0
fi
if [ "$target_major" -lt "$current_major" ]; then
return 1
fi
if [ "$target_minor" -gt "$current_minor" ]; then
return 0
fi
if [ "$target_minor" -lt "$current_minor" ]; then
return 1
fi
[ "$target_patch" -gt "$current_patch" ]
}
read_current_version() {
current_version=$(sed -n 's/^__version__[[:space:]]*=[[:space:]]*"\([^"]*\)".*$/\1/p' "$ABOUT_FILE" | sed -n '1p')
[ -n "$current_version" ] || die "version not found in $ABOUT_FILE"
printf '%s\n' "$current_version"
}
replace_first_exact_line() {
file=$1
old_line=$2
new_line=$3
tmp_file=$(mktemp)
if awk -v old="$old_line" -v new="$new_line" '
BEGIN { done = 0 }
{
if (!done && $0 == old) {
print new
done = 1
} else {
print
}
}
END { if (!done) exit 1 }
' "$file" >"$tmp_file"; then
mv "$tmp_file" "$file"
else
rm -f "$tmp_file"
die "line not updated in $file"
fi
}
write_about_version() {
current_version=$1
target_version=$2
replace_first_exact_line "$ABOUT_FILE" "__version__ = \"$current_version\"" "__version__ = \"$target_version\""
}
write_readme_tags() {
current_version=$1
target_version=$2
old_line=' "iti-system @ git+https://git.noahlan.cn/iti-framework/iTi-System.git@v'"$current_version"'",'
new_line=' "iti-system @ git+https://git.noahlan.cn/iti-framework/iTi-System.git@v'"$target_version"'",'
replace_first_exact_line "$README_FILE" "$old_line" "$new_line"
}
ensure_clean_tree() {
if [ -n "$(git status --porcelain)" ]; then
die "working tree is not clean"
fi
}
ensure_branch() {
if branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null); then
[ -n "$branch" ] || die "release requires a branch checkout"
printf '%s\n' "$branch"
else
die "release requires a branch checkout"
fi
}
ensure_tag_absent() {
tag=$1
if git rev-parse --verify --quiet "refs/tags/$tag" >/dev/null; then
die "tag already exists: $tag"
fi
}
main() {
version_arg=${1:-}
current_version=$(read_current_version)
if [ -n "$version_arg" ]; then
target_version=$(normalize_version "$version_arg")
else
target_version=$(bump_patch "$current_version")
fi
target_tag="v$target_version"
if [ "$target_version" = "$current_version" ]; then
die "version already set to $target_version"
fi
if ! version_is_newer "$current_version" "$target_version"; then
die "target version must be newer than $current_version"
fi
ensure_clean_tree
branch=$(ensure_branch)
ensure_tag_absent "$target_tag"
uv run pytest -q
write_about_version "$current_version" "$target_version"
write_readme_tags "$current_version" "$target_version"
git add iti_system/__about__.py README.md
git commit -m "chore: release $target_tag"
git tag -a "$target_tag" -m "release $target_tag"
git push origin "$branch" "$target_tag"
printf 'released %s\n' "$target_tag"
}
main "$@"
Loading…
Cancel
Save