mirror of
https://github.com/Shu-Ying/main_plugin_farm.git
synced 2026-10-29 21:59:58 +08:00
fixed # 修复部分工作流BUG
This commit is contained in:
@@ -54,6 +54,13 @@ def build(root, output, *, help_image=None):
|
||||
"requirements.txt", "config_migration.py",
|
||||
):
|
||||
files[name] = (integration / name).read_bytes()
|
||||
# 真寻市场静态读取版本字段,生成时写入字面量而非运行时变量。
|
||||
marker = b'"__FARM_VERSION__"'
|
||||
if files['__init__.py'].count(marker) != 1:
|
||||
raise ValueError('Zhenxun entry requires one version placeholder')
|
||||
files['__init__.py'] = files['__init__.py'].replace(
|
||||
marker, json.dumps(version).encode('ascii'),
|
||||
)
|
||||
files["runtime.py"] = (
|
||||
root / "integrations/shared/runtime.py"
|
||||
).read_bytes()
|
||||
|
||||
@@ -97,6 +97,8 @@ def main():
|
||||
token = sync["steps"][-1]["env"]["FARM_RELEASE_TOKEN"]
|
||||
if token != "${{ secrets.FARM_RELEASE_TOKEN }}":
|
||||
raise ValueError("NoneBot must reuse FARM_RELEASE_TOKEN")
|
||||
if '--release-tag' not in sync['steps'][-1]['run'].split():
|
||||
raise ValueError('NoneBot sync must create its version release tag')
|
||||
generated = yaml.load((
|
||||
root / "release/nonebot/verify.yml"
|
||||
).read_text(encoding="utf-8"), Loader=yaml.BaseLoader)
|
||||
@@ -113,6 +115,21 @@ def main():
|
||||
"github.repository == 'Shu-Ying/nonebot_plugin_farm'"
|
||||
)):
|
||||
raise ValueError("NoneBot PyPI boundary differs")
|
||||
for workflow, job in ((generated, 'verify'), (publishing, 'build')):
|
||||
steps = workflow['jobs'][job]['steps']
|
||||
if not any(step.get('run') == (
|
||||
'python -m build --sdist --wheel --outdir dist'
|
||||
) for step in steps):
|
||||
raise ValueError('NoneBot distributions must build inside dist')
|
||||
upload = next(step for step in steps
|
||||
if step.get('uses', '').startswith(
|
||||
'actions/upload-artifact@'))
|
||||
if (upload['with']['path'] != 'dist/*'
|
||||
or upload['with'].get('if-no-files-found') != 'error'):
|
||||
raise ValueError('NoneBot upload path must be dist/*')
|
||||
if not any(step.get('run') == 'python -m twine check dist/*'
|
||||
for step in publishing['jobs']['build']['steps']):
|
||||
raise ValueError('NoneBot distribution check must use dist/*')
|
||||
steps = publishing["jobs"]["publish"]["steps"]
|
||||
if steps[-1]["with"]["password"] != "${{ secrets.PYPI_TOKEN }}":
|
||||
raise ValueError("Keep the existing PyPI secret")
|
||||
|
||||
@@ -4,6 +4,7 @@ import argparse
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from urllib.error import HTTPError
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
@@ -37,13 +38,19 @@ def github(method, path, payload, token, allow_missing=False):
|
||||
) from None
|
||||
|
||||
|
||||
def publish(package, confirmed=False, request=None):
|
||||
def publish(package, confirmed=False, request=None, *, release_tag=False):
|
||||
files = read_package(package)
|
||||
manifest = verify_files(files)
|
||||
if not re.fullmatch(r'[0-9]+\.[0-9]+\.[0-9]+(?:-[a-z0-9.-]+)?',
|
||||
manifest['version']):
|
||||
raise ValueError('Invalid release version in version.txt')
|
||||
message = commit_message(manifest)
|
||||
result = {"repository": REPOSITORY, "branch": BRANCH,
|
||||
"version": manifest["version"], "message": message,
|
||||
"published": False}
|
||||
tag = 'v' + manifest['version']
|
||||
if release_tag:
|
||||
result.update(tag=tag, tag_created=False)
|
||||
if not confirmed:
|
||||
return result
|
||||
if manifest["source"]["dirty"]:
|
||||
@@ -53,10 +60,23 @@ def publish(package, confirmed=False, request=None):
|
||||
raise ValueError("Configure FARM_RELEASE_TOKEN in a protected job")
|
||||
call = request or (lambda method, path, data: github(
|
||||
method, path, data, token,
|
||||
allow_missing=path.startswith(("git/ref/heads/", "contents/")),
|
||||
allow_missing=path.startswith((
|
||||
"git/ref/heads/", "git/ref/tags/", "contents/",
|
||||
)),
|
||||
))
|
||||
if not call("GET", "", None):
|
||||
raise ValueError(f"Target repository is not accessible: {REPOSITORY}")
|
||||
if release_tag and call('GET', f'git/ref/tags/{tag}', None):
|
||||
owned = call('GET', f'contents/FARM-MANIFEST.json?ref={tag}', None)
|
||||
if owned is None or owned.get('encoding') != 'base64':
|
||||
raise ValueError('Existing release is not managed; bump version.txt')
|
||||
previous = json.loads(base64.b64decode(owned['content']))
|
||||
if (previous.get('target') != 'nonebot'
|
||||
or previous.get('plugin') != manifest['plugin']
|
||||
or previous.get('version') != manifest['version']
|
||||
or previous.get('files') != manifest['files']):
|
||||
raise ValueError('Release tag has different files; bump version.txt')
|
||||
return {**result, 'tag_reused': True}
|
||||
ref = call("GET", f"git/ref/heads/{BRANCH}", None)
|
||||
parent = ref["object"]["sha"] if ref else None
|
||||
if parent:
|
||||
@@ -92,15 +112,25 @@ def publish(package, confirmed=False, request=None):
|
||||
call("POST", "git/refs", {
|
||||
"ref": f"refs/heads/{BRANCH}", "sha": commit["sha"],
|
||||
})
|
||||
return {**result, "published": True, "commit": commit["sha"]}
|
||||
result.update(published=True, commit=commit['sha'])
|
||||
if release_tag:
|
||||
# 仅创建不可变标签;失败保留已同步main,重跑时重新检查,不移动旧标签。
|
||||
call('POST', 'git/refs', {
|
||||
'ref': f'refs/tags/{tag}', 'sha': commit['sha'],
|
||||
})
|
||||
result['tag_created'] = True
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("package")
|
||||
parser.add_argument("--publish", action="store_true")
|
||||
parser.add_argument('--release-tag', action='store_true',
|
||||
help='同步后创建v版本标签,触发目标仓库PyPI发布')
|
||||
args = parser.parse_args()
|
||||
print(json.dumps(publish(args.package, args.publish), ensure_ascii=False))
|
||||
print(json.dumps(publish(args.package, args.publish,
|
||||
release_tag=args.release_tag), ensure_ascii=False))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""独立验证生成的真寻包;仅标准库,不导入或启动插件。"""
|
||||
|
||||
import argparse
|
||||
import ast
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path, PurePosixPath
|
||||
@@ -45,6 +46,16 @@ def verify_files(files):
|
||||
raise ValueError('Resource ZIP must not be in plugin repository')
|
||||
if not required <= set(files):
|
||||
raise ValueError("Incomplete plugin")
|
||||
tree = ast.parse(files['__init__.py'])
|
||||
versions = [
|
||||
keyword.value for node in ast.walk(tree)
|
||||
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name)
|
||||
and node.func.id == 'PluginExtraData'
|
||||
for keyword in node.keywords if keyword.arg == 'version'
|
||||
]
|
||||
if (len(versions) != 1 or not isinstance(versions[0], ast.Constant)
|
||||
or versions[0].value != manifest['version']):
|
||||
raise ValueError('PluginExtraData requires matching literal version')
|
||||
return manifest
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user