Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 58 additions & 32 deletions .github/workflows/append-version-contributor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ jobs:
PR_NUMBER: ${{ github.event.pull_request.number }}
run: git fetch --no-tags origin "pull/${PR_NUMBER}/head:refs/remotes/origin/pr-${PR_NUMBER}"

- name: 设置 Python 环境
uses: actions/setup-python@v7.0.0
with:
python-version: '3.12'

- name: 标记更新条目贡献者
id: append
env:
Expand All @@ -41,61 +46,82 @@ jobs:
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
python - <<'PY'
import json
import os
import subprocess
from pathlib import Path
import sys

def load_version(ref: str) -> dict:
content = subprocess.check_output(
["git", "show", f"{ref}:res/version.json"],
text=True,
encoding="utf-8",
)
return json.loads(content)
sys.path.insert(0, "scripts")
import changelog

def load(ref: str) -> dict:
"""取某个 ref 上的 CHANGELOG.md;取不到或格式不对都当作空,不阻断合并后流程。"""
try:
content = subprocess.check_output(
["git", "show", f"{ref}:CHANGELOG.md"],
text=True,
encoding="utf-8",
stderr=subprocess.DEVNULL,
)
except subprocess.CalledProcessError:
print(f"{ref} 上没有 CHANGELOG.md,跳过对比")
return {}
Comment on lines +65 to +67

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop attribution when the base changelog cannot be loaded

When a merged PR's base commit has no CHANGELOG.md or its file cannot be parsed—as will happen for the first migration PR based on this commit, whose tree has no such file—returning {} does not skip the comparison. It makes every entry in the PR head appear newly added, so the later loop appends that PR author's suffix to every matching unsuffixed historical entry in the current changelog, corrupting contributor attribution; this path should terminate without making attribution changes instead.

Useful? React with 👍 / 👎.

try:
return changelog.parse_changelog(content)[1]
except changelog.ChangelogError as error:
print(f"{ref} 上的 CHANGELOG.md 无法解析,跳过对比:{error}")
return {}

path = Path("res/version.json")
current = json.loads(path.read_text(encoding="utf-8"))
base = load_version(os.environ["PR_BASE_SHA"])
head = load_version(f"origin/pr-{os.environ['PR_NUMBER']}")
base = load(os.environ["PR_BASE_SHA"])
head = load(f"origin/pr-{os.environ['PR_NUMBER']}")
suffix = f" by [@{os.environ['PR_AUTHOR']}]({os.environ['PR_AUTHOR_URL']})"
additions: set[tuple[str, str, str]] = set()

for version, info in head.get("version_info", {}).items():
base_info = base.get("version_info", {}).get(version, {})
for category, items in info.items():
if not isinstance(items, list):
continue
base_items = set(base_info.get(category, []))

# 本次 PR 新增的条目 = head 有而 base 没有的。按 (版本, 分类, 原文) 三元组记,
# 否则同一句话若在旧版本段里也存在,那一条会被一起补上署名。
additions = set()
for version, categories in head.items():
base_categories = base.get(version, {})
for category, items in categories.items():
base_items = set(base_categories.get(category, []))
additions.update(
(version, category, item)
for item in items
if isinstance(item, str) and item not in base_items
if item not in base_items
)

_, current, current_dates = changelog.parse_changelog(
changelog.read_text(changelog.CHANGELOG_PATH)
)

changed = False
for version, category, item in additions:
items = current.get("version_info", {}).get(version, {}).get(category, [])
if item in items and suffix not in item:
items[items.index(item)] = f"{item}{suffix}"
changed = True
for version, categories in current.items():
for category, items in categories.items():
for index, item in enumerate(items):
if (version, category, item) in additions and suffix not in item:
items[index] = f"{item}{suffix}"
changed = True

if changed:
path.write_text(
json.dumps(current, ensure_ascii=False, indent=4) + "\n",
encoding="utf-8",
changelog.write_text(
changelog.CHANGELOG_PATH,
changelog.render_changelog(current, current_dates),
)

with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output:
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
output.write(f"changed={str(changed).lower()}\n")
PY

# 署名写进 CHANGELOG.md 正文后,res/version.json 要跟着重算。
- name: 重新生成版本信息
if: steps.append.outputs.changed == 'true'
run: python scripts/changelog.py sync

- name: 提交贡献者信息
if: steps.append.outputs.changed == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add res/version.json
# sync 只有在 dev 本来就不一致时才会动后四个文件,一并纳入避免 rebase 撞未暂存改动。
git add CHANGELOG.md res/version.json frontend/package.json app/core/config.py pyproject.toml uv.lock
git commit -m "chore(version): mark PR entry contributors"
git pull --rebase origin dev
git push origin HEAD:dev
2 changes: 1 addition & 1 deletion .github/workflows/build-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ env:
# latest:Runtime 有自己的发布节奏,本仓库的改动不应该在没有联调的情况下自动带出一个新
# Runtime。Runtime 不自更新,只随这里构建的安装包整体升级;本仓库如有依赖 Runtime 新行为
# 的改动(T13 系列),必须等 Runtime 一侧先发布对应版本,再手动把这个版本号提上去。
RUNTIME_VERSION: v0.1.3
RUNTIME_VERSION: v0.1.4

jobs:

Expand Down
63 changes: 63 additions & 0 deletions .github/workflows/check-changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# AUTO-MAS: A Multi-Script, Multi-Config Management and Automation Software
# Copyright © 2025-2026 AUTO-MAS Team

# This file is part of AUTO-MAS.

# AUTO-MAS is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.

# AUTO-MAS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
# the GNU Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with AUTO-MAS. If not, see <https://www.gnu.org/licenses/>.

# Contact: DLmaster_361@163.com

name: 检查更新日志

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]

permissions:
contents: read
pull-requests: read

jobs:
check-changelog:
name: 检查 CHANGELOG.md 与生成物一致
runs-on: ubuntu-latest

steps:
- name: 检出代码
uses: actions/checkout@v7.0.1

- name: 设置 Python 环境
uses: actions/setup-python@v7.0.0
with:
python-version: '3.12'

# CHANGELOG.md 是唯一手写来源,res/version.json 与四处版本号都由它生成。
# 这一步同时校验更新日志格式、五处版本号一致、生成物是最新的。
- name: 检查更新日志格式与版本号一致性
run: python scripts/changelog.py check
Comment thread
sourcery-ai[bot] marked this conversation as resolved.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the changelog tool before invoking it

For every opened or synchronized PR, this step checks out the reviewed tree and immediately runs a file that is not present: a repo-wide search of this commit finds neither scripts/changelog.py nor any changelog.py. The command therefore exits with [Errno 2] before performing any validation, making this required check fail for every PR; the same missing module also breaks the updated contributor workflow's import changelog and later sync invocation after merges.

Useful? React with 👍 / 👎.


- name: 检查 PR 文件列表
uses: actions/github-script@v9.0.0
with:
script: |
const files = await github.paginate(github.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
per_page: 100,
});

if (!files.some(file => file.filename === 'CHANGELOG.md')) {
core.setFailed('每个 PR 都必须包含对 CHANGELOG.md 的更改。');
Comment on lines +61 to +62

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Align contributor instructions with the new changelog source

Once the missing tooling is supplied, this check makes CHANGELOG.md the required hand-edited file and rejects PRs that omit it, but the repository's mandatory engineering guidance still directs agents to update res/version.json itself. Contributors following that documented workflow will edit the generated artifact and have their PR rejected, so the source-of-truth migration needs to update the contributor instructions at the same time. .agents/skills/mas-skills/SKILL.mdL55-L55

Useful? React with 👍 / 👎.

}
135 changes: 0 additions & 135 deletions .github/workflows/check-version-json.yml

This file was deleted.

51 changes: 13 additions & 38 deletions scripts/build-local-package.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -54,49 +54,24 @@ if (-not (Get-Command yarn -ErrorAction SilentlyContinue)) {
throw "未找到 Yarn,请先执行:corepack prepare yarn@4.9.1 --activate"
}

# 第一步:确认所有版本来源一致,避免打出版本信息互相冲突的安装包。
$versionConfig = Get-Content -LiteralPath $versionFile -Raw | ConvertFrom-Json
$frontendPackage = Get-Content -LiteralPath $frontendPackageFile -Raw | ConvertFrom-Json
$appVersion = [string]$versionConfig.version

if ($appVersion -notmatch '^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$') {
throw "res/version.json 中的版本格式无效:$appVersion"
}
if ([string]$frontendPackage.version -ne $appVersion) {
throw "frontend/package.json 版本不一致:$($frontendPackage.version),预期 $appVersion"
# 版本一致性由 scripts/changelog.py 判定,这里需要一个可用的 Python。
$venvPython = Join-Path $repoRoot ".venv\Scripts\python.exe"
$pythonExe = if (Test-Path -LiteralPath $venvPython) { $venvPython } else { "python" }
if (-not (Get-Command $pythonExe -ErrorAction SilentlyContinue)) {
throw "未找到 Python,请先安装项目要求的 Python 3.12 环境,或在仓库根创建 .venv。"
}

$backendConfigText = Get-Content -LiteralPath $backendConfigFile -Raw
$backendVersionMatch = [regex]::Match(
$backendConfigText,
'(?m)^\s*VERSION\s*=\s*"(?<version>v[^"]+)"'
)
if (-not $backendVersionMatch.Success -or $backendVersionMatch.Groups['version'].Value -ne $appVersion) {
throw "app/core/config.py 版本与 $appVersion 不一致。"
# 第一步:确认版本信息与 CHANGELOG.md 一致,避免打出版本信息互相冲突的安装包。
# 版本号的唯一手写来源是 CHANGELOG.md,res/version.json 等五处都由 scripts/changelog.py
# 生成;这里只调用它,不重复实现规则。
& $pythonExe (Join-Path $repoRoot "scripts\changelog.py") check
if ($LASTEXITCODE -ne 0) {
throw "版本信息与 CHANGELOG.md 不一致,请先运行:python scripts/changelog.py sync"
}

$versionConfig = Get-Content -LiteralPath $versionFile -Raw | ConvertFrom-Json
$appVersion = [string]$versionConfig.version
$pythonVersion = $appVersion.Substring(1)
$pyprojectText = Get-Content -LiteralPath $pyprojectFile -Raw
$pyprojectVersionMatch = [regex]::Match(
$pyprojectText,
'(?m)^version\s*=\s*"(?<version>[^"]+)"'
)
if (-not $pyprojectVersionMatch.Success -or $pyprojectVersionMatch.Groups['version'].Value -ne $pythonVersion) {
throw "pyproject.toml 版本与 $pythonVersion 不一致。"
}

$expectedLockVersion = $pythonVersion `
-replace '-alpha\.', 'a' `
-replace '-beta\.', 'b' `
-replace '-rc\.', 'rc'
$uvLockText = Get-Content -LiteralPath $uvLockFile -Raw
$uvVersionMatch = [regex]::Match(
$uvLockText,
'(?ms)^\[\[package\]\]\r?\nname = "auto-mas"\r?\nversion = "(?<version>[^"]+)"'
)
if (-not $uvVersionMatch.Success -or $uvVersionMatch.Groups['version'].Value -ne $expectedLockVersion) {
throw "uv.lock 中 auto-mas 的版本与 $expectedLockVersion 不一致,请先运行 uv lock。"
}

$workflowText = Get-Content -LiteralPath $buildWorkflowFile -Raw
$runtimeVersionMatch = [regex]::Match(
Expand Down
Loading
Loading