Skip to content

Commit 8d23e19

Browse files
committed
refactor: harden prerelease handling and nightly constants
1 parent 1b9738c commit 8d23e19

7 files changed

Lines changed: 151 additions & 35 deletions

File tree

.github/workflows/release.yml

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ on:
2020
permissions:
2121
contents: write
2222

23+
env:
24+
NIGHTLY_TAG: nightly
25+
2326
jobs:
2427
resolve-release-context:
2528
name: Resolve Release Context
@@ -44,7 +47,7 @@ jobs:
4447
default_branch="master"
4548
fi
4649
47-
if [ "$event_name" = "schedule" ] || { [ "$event_name" = "workflow_dispatch" ] && [ "$input_tag" = "nightly" ]; }; then
50+
if [ "$event_name" = "schedule" ] || { [ "$event_name" = "workflow_dispatch" ] && [ "$input_tag" = "$NIGHTLY_TAG" ]; }; then
4851
ref="refs/heads/${default_branch}"
4952
elif [ -n "$input_ref" ]; then
5053
ref="$input_ref"
@@ -71,7 +74,7 @@ jobs:
7174
fi
7275
7376
if [ "$event_name" = "schedule" ]; then
74-
tag="nightly"
77+
tag="$NIGHTLY_TAG"
7578
elif [ "$event_name" = "push" ]; then
7679
tag="${GITHUB_REF_NAME}"
7780
elif [ -n "$dispatch_tag" ]; then
@@ -159,7 +162,7 @@ jobs:
159162
path: dashboard/AstrBot-${{ needs.resolve-release-context.outputs.tag }}-dashboard.zip
160163

161164
- name: Upload dashboard package to Cloudflare R2
162-
if: ${{ env.R2_ACCOUNT_ID != '' && env.R2_ACCESS_KEY_ID != '' && env.R2_SECRET_ACCESS_KEY != '' && needs.resolve-release-context.outputs.tag != 'nightly' }}
165+
if: ${{ env.R2_ACCOUNT_ID != '' && env.R2_ACCESS_KEY_ID != '' && env.R2_SECRET_ACCESS_KEY != '' && needs.resolve-release-context.outputs.tag != env.NIGHTLY_TAG }}
163166
env:
164167
R2_BUCKET_NAME: "astrbot"
165168
R2_OBJECT_NAME: "astrbot-webui-latest.zip"
@@ -188,8 +191,8 @@ jobs:
188191
name: Publish GitHub Release
189192
runs-on: ubuntu-24.04
190193
concurrency:
191-
group: ${{ needs.resolve-release-context.outputs.tag == 'nightly' && 'nightly-release' || format('release-{0}', needs.resolve-release-context.outputs.tag) }}
192-
cancel-in-progress: ${{ needs.resolve-release-context.outputs.tag == 'nightly' }}
194+
group: ${{ needs.resolve-release-context.outputs.tag == env.NIGHTLY_TAG && 'nightly-release' || format('release-{0}', needs.resolve-release-context.outputs.tag) }}
195+
cancel-in-progress: ${{ needs.resolve-release-context.outputs.tag == env.NIGHTLY_TAG }}
193196
needs:
194197
- resolve-release-context
195198
- verify-core
@@ -206,7 +209,7 @@ jobs:
206209
shell: bash
207210
run: |
208211
tag="${{ needs.resolve-release-context.outputs.tag }}"
209-
if [ "$tag" = "nightly" ]; then
212+
if [ "$tag" = "$NIGHTLY_TAG" ]; then
210213
if ! short_sha="$(git rev-parse --short=8 HEAD 2>/tmp/git_rev_parse_error.log)"; then
211214
echo "Failed to resolve HEAD short SHA for nightly title." >&2
212215
cat /tmp/git_rev_parse_error.log >&2 || true
@@ -216,7 +219,7 @@ jobs:
216219
if [ -z "$base_version" ]; then
217220
base_version="v0.0.0"
218221
fi
219-
title="${base_version}-nightly-${short_sha}"
222+
title="${base_version}-${NIGHTLY_TAG}-${short_sha}"
220223
else
221224
base_version="$tag"
222225
title="$tag"
@@ -225,7 +228,7 @@ jobs:
225228
echo "base_version=$base_version" >> "$GITHUB_OUTPUT"
226229
227230
- name: Force-update nightly tag
228-
if: ${{ needs.resolve-release-context.outputs.tag == 'nightly' }}
231+
if: ${{ needs.resolve-release-context.outputs.tag == env.NIGHTLY_TAG }}
229232
env:
230233
GH_TOKEN: ${{ github.token }}
231234
shell: bash
@@ -235,8 +238,8 @@ jobs:
235238
cat /tmp/git_rev_parse_error.log >&2 || true
236239
exit 1
237240
fi
238-
git tag -f nightly "${current_sha}"
239-
git push --force origin refs/tags/nightly
241+
git tag -f "$NIGHTLY_TAG" "${current_sha}"
242+
git push --force origin "refs/tags/${NIGHTLY_TAG}"
240243
241244
- name: Download dashboard artifact
242245
uses: actions/download-artifact@v8
@@ -250,7 +253,7 @@ jobs:
250253
shell: bash
251254
run: |
252255
tag="${{ needs.resolve-release-context.outputs.tag }}"
253-
if [ "$tag" = "nightly" ]; then
256+
if [ "$tag" = "$NIGHTLY_TAG" ]; then
254257
note_file="$(mktemp)"
255258
python3 scripts/release/generate_nightly_release_notes.py \
256259
--base-tag "${{ steps.release-meta.outputs.base_version }}" \
@@ -272,14 +275,14 @@ jobs:
272275
run: |
273276
tag="${{ needs.resolve-release-context.outputs.tag }}"
274277
title="${{ steps.release-meta.outputs.title }}"
275-
if [ "$tag" = "nightly" ]; then
278+
if [ "$tag" = "$NIGHTLY_TAG" ]; then
276279
pre_flag="--prerelease"
277280
else
278281
pre_flag=""
279282
fi
280283
if ! gh release view "$tag" >/dev/null 2>&1; then
281284
gh release create "$tag" --title "$title" --notes-file "${{ steps.notes.outputs.file }}" $pre_flag
282-
elif [ "$tag" = "nightly" ]; then
285+
elif [ "$tag" = "$NIGHTLY_TAG" ]; then
283286
gh release edit "$tag" --title "$title" --notes-file "${{ steps.notes.outputs.file }}" --prerelease
284287
fi
285288
@@ -307,7 +310,7 @@ jobs:
307310
308311
publish-pypi:
309312
name: Publish PyPI
310-
if: ${{ needs.resolve-release-context.outputs.tag != 'nightly' }}
313+
if: ${{ needs.resolve-release-context.outputs.tag != env.NIGHTLY_TAG }}
311314
runs-on: ubuntu-24.04
312315
needs:
313316
- resolve-release-context

astrbot/core/release_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
NIGHTLY_TAG = "nightly"

astrbot/core/updator.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@
88

99
from astrbot.core import logger
1010
from astrbot.core.config.default import VERSION
11+
from astrbot.core.release_constants import NIGHTLY_TAG
1112
from astrbot.core.utils.astrbot_path import get_astrbot_path
1213
from astrbot.core.utils.io import download_file
1314

14-
from .zip_updator import FetchReleaseError, ReleaseInfo, RepoZipUpdator
15+
from .zip_updator import (
16+
PRERELEASE_TAG_REGEX,
17+
FetchReleaseError,
18+
ReleaseInfo,
19+
RepoZipUpdator,
20+
)
1521

1622

1723
class AstrBotUpdator(RepoZipUpdator):
@@ -27,7 +33,7 @@ def __init__(self, repo_mirror: str = "") -> None:
2733
self.GITHUB_RELEASE_API = (
2834
"https://api.github.com/repos/AstrBotDevs/AstrBot/releases"
2935
)
30-
self.NIGHTLY_TAG = "nightly"
36+
self.NIGHTLY_TAG = NIGHTLY_TAG
3137

3238
def terminate_child_processes(self) -> None:
3339
"""终止当前进程的所有子进程
@@ -200,7 +206,9 @@ async def _resolve_latest_target(self) -> tuple[str, str]:
200206
(
201207
item
202208
for item in releases
203-
if item.get("tag_name", "").lower() != self.NIGHTLY_TAG
209+
if (tag := item.get("tag_name", ""))
210+
and tag.lower() != self.NIGHTLY_TAG
211+
and not PRERELEASE_TAG_REGEX.search(tag)
204212
),
205213
None,
206214
)

astrbot/core/zip_updator.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010
import certifi
1111

1212
from astrbot.core import logger
13+
from astrbot.core.release_constants import NIGHTLY_TAG
1314
from astrbot.core.utils.io import download_file, on_error
1415
from astrbot.core.utils.version_comparator import VersionComparator
1516

1617
PRERELEASE_TAG_REGEX = re.compile(
17-
r"[\-_.]?(alpha|beta|rc|dev|nightly|pre|preview)[\-_.]?\d*$",
18+
rf"[\-_.]?(alpha|beta|rc|dev|{re.escape(NIGHTLY_TAG)}|pre|preview)[\-_.]?\d*$",
1819
re.IGNORECASE,
1920
)
2021
# Keep this rule aligned with dashboard/src/layouts/full/vertical-header/VerticalHeader.vue.
@@ -71,21 +72,24 @@ def _normalize_release_payload(result: object, url: str) -> list:
7172
"zipball_url",
7273
)
7374
normalized = []
75+
invalid_entry_count = 0
7476
for idx, release in enumerate(releases):
7577
if not isinstance(release, dict):
76-
logger.error(
78+
logger.warning(
7779
f"版本信息第 {idx} 项格式异常,期望字典,实际为: {type(release).__name__}, url: {url}",
7880
)
79-
raise FetchReleaseError("版本信息格式异常")
81+
invalid_entry_count += 1
82+
continue
8083

8184
missing_fields = [
8285
field for field in required_fields if field not in release
8386
]
8487
if missing_fields:
85-
logger.error(
88+
logger.warning(
8689
f"版本信息第 {idx} 项缺少字段: {missing_fields}, url: {url}",
8790
)
88-
raise FetchReleaseError("版本信息字段缺失")
91+
invalid_entry_count += 1
92+
continue
8993

9094
normalized.append(
9195
{
@@ -96,6 +100,15 @@ def _normalize_release_payload(result: object, url: str) -> list:
96100
"zipball_url": release["zipball_url"],
97101
},
98102
)
103+
104+
if invalid_entry_count:
105+
logger.warning(
106+
f"版本信息存在 {invalid_entry_count} 条无效数据,已跳过,url: {url}",
107+
)
108+
109+
if not normalized:
110+
raise FetchReleaseError("版本信息全部无效")
111+
99112
return normalized
100113

101114
async def fetch_release_info(self, url: str) -> list:

scripts/release/generate_nightly_release_notes.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from collections import defaultdict
88
from pathlib import Path
99

10+
from astrbot.core.release_constants import NIGHTLY_TAG
11+
1012

1113
def _run_git(*args: str) -> str:
1214
try:
@@ -54,7 +56,7 @@ def _classify(subject: str) -> str:
5456
def _write_fallback(output_path: Path) -> None:
5557
short_sha = _run_git("rev-parse", "--short=8", "HEAD")
5658
output_path.write_text(
57-
f"## What's Changed\n\n- Nightly build from `{short_sha}`\n",
59+
f"## What's Changed\n\n- {NIGHTLY_TAG.capitalize()} build from `{short_sha}`\n",
5860
encoding="utf-8",
5961
)
6062

@@ -85,7 +87,13 @@ def generate_notes(base_tag: str, repo: str, output_path: Path) -> None:
8587
with output_path.open("w", encoding="utf-8") as file:
8688
file.write("## What's Changed\n\n")
8789
file.write(f"- Baseline tag: `{base_tag}`\n")
88-
file.write(f"- Nightly commit: `{nightly_commit}`\n\n")
90+
file.write(f"- {NIGHTLY_TAG.capitalize()} commit: `{nightly_commit}`\n")
91+
92+
if not any(sections.values()):
93+
file.write(f"- No changes since `{base_tag}`\n\n")
94+
return
95+
96+
file.write("\n")
8997
for title in ("新增", "修复", "优化", "其他"):
9098
items = sections.get(title, [])
9199
if not items:

tests/unit/test_prerelease_rule_sync.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
from pathlib import Path
21
import re
2+
from pathlib import Path
33

44
from astrbot.core.zip_updator import PRERELEASE_TAG_REGEX
55

66

77
def test_prerelease_rule_is_synced_with_dashboard():
88
repo_root = Path(__file__).resolve().parents[2]
9-
vue_path = repo_root / "dashboard/src/layouts/full/vertical-header/VerticalHeader.vue"
9+
vue_path = (
10+
repo_root / "dashboard/src/layouts/full/vertical-header/VerticalHeader.vue"
11+
)
1012
content = vue_path.read_text(encoding="utf-8")
1113

1214
match = re.search(
@@ -17,3 +19,26 @@ def test_prerelease_rule_is_synced_with_dashboard():
1719
vue_pattern, vue_flags = match.groups()
1820
assert vue_pattern == PRERELEASE_TAG_REGEX.pattern
1921
assert ("i" in vue_flags) == bool(PRERELEASE_TAG_REGEX.flags & re.IGNORECASE)
22+
23+
24+
def test_prerelease_rule_matches_expected_examples():
25+
prerelease_tags = (
26+
"v1.2.3-alpha.1",
27+
"v1.2.3-beta",
28+
"v1.2.3-rc1",
29+
"v1.2.3-dev",
30+
"v1.2.3-nightly",
31+
"v1.2.3-pre",
32+
"v1.2.3-preview",
33+
)
34+
stable_tags = (
35+
"v1.2.3",
36+
"v1.2.3+build.1",
37+
"v1.2.3-release",
38+
)
39+
40+
for tag in prerelease_tags:
41+
assert PRERELEASE_TAG_REGEX.search(tag) is not None
42+
43+
for tag in stable_tags:
44+
assert PRERELEASE_TAG_REGEX.search(tag) is None

0 commit comments

Comments
 (0)