# 发布操作手册: # 1. 先将 plugins/auto-fin/pyproject.toml 中的 project.version 更新为待发布版本并合入目标分支。 # 2. 确认插件依赖的 reme-ai 版本已经发布到 PyPI;本工作流会在构建阶段验证该依赖可下载。 # 3. 确认 PyPI Trusted Publisher 已绑定本仓库、此工作流和 pypi environment,且 PyPI 上不存在相同版本。 # 4. 在 GitHub 仓库的 Actions 页面选择“Release / Auto Fin plugin”,点击“Run workflow”。 # 5. 输入与 project.version 完全一致的版本号(例如 0.1.0)后运行;版本也可以带 v 前缀。 # # 推荐发布顺序:reme-ai -> reme-auto-fin -> QwenPaw 更新依赖并通过 plugins: [auto-fin] 启用。 # 当前仅支持 workflow_dispatch 手动触发,不会因 push、tag 或 release 自动发布。 name: Release / Auto Fin plugin run-name: Publish reme-auto-fin ${{ inputs.version }} on: workflow_dispatch: inputs: version: description: Version from plugins/auto-fin/pyproject.toml (for example, 0.1.0) required: true type: string permissions: contents: read concurrency: group: publish-reme-auto-fin cancel-in-progress: false jobs: build: runs-on: ubuntu-latest env: RELEASE_VERSION: ${{ inputs.version }} steps: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 with: persist-credentials: false - name: Set up Python uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: python-version: '3.11' - name: Install test and build dependencies run: | python -m pip install --upgrade pip python -m pip install build packaging pytest pytest-asyncio twine python -m pip install -e ".[core]" python -m pip install --no-deps -e plugins/auto-fin - name: Validate package name and release version id: package run: | python - "${RELEASE_VERSION}" <<'PY' import os import sys import tomllib from pathlib import Path from packaging.requirements import Requirement from packaging.version import Version project = tomllib.loads(Path("plugins/auto-fin/pyproject.toml").read_text(encoding="utf-8"))["project"] expected = Version(sys.argv[1].removeprefix("v")) actual = Version(project["version"]) if project["name"] != "reme-auto-fin": raise SystemExit(f"Expected project name 'reme-auto-fin', found {project['name']!r}") if actual != expected: raise SystemExit(f"Package version is {actual}, but workflow input is {expected}") requirements = [requirement for requirement in project["dependencies"] if requirement.startswith("reme-ai")] if len(requirements) != 1: raise SystemExit(f"Expected one reme-ai dependency, found {requirements!r}") reme_requirement = Requirement(requirements[0]) if reme_requirement.name != "reme-ai" or reme_requirement.extras: raise SystemExit(f"Expected a base reme-ai dependency, found {requirements[0]!r}") if Version("0.4.1.8") in reme_requirement.specifier or Version("0.4.1.9") not in reme_requirement.specifier: raise SystemExit(f"Expected reme-ai>=0.4.1.9, found {requirements[0]!r}") root_project = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["project"] agentscope_requirements = [ Requirement(value) for value in root_project["optional-dependencies"]["as"] ] if len(agentscope_requirements) != 1 or agentscope_requirements[0].name != "agentscope": raise SystemExit(f"Expected one agentscope dependency, found {agentscope_requirements!r}") agentscope_requirement = agentscope_requirements[0] agentscope_specifiers = list(agentscope_requirement.specifier) if ( agentscope_requirement.extras != {"model-ollama"} or agentscope_requirement.marker is not None or len(agentscope_specifiers) != 1 or agentscope_specifiers[0].operator != "==" or Version(agentscope_specifiers[0].version).is_prerelease ): raise SystemExit(f"Expected a stable exact agentscope[model-ollama] pin, found {agentscope_requirement}") with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output: print(f"reme_requirement={reme_requirement}", file=output) print(f"agentscope_requirement={agentscope_requirement}", file=output) print(f"Publishing {project['name']} {actual}") PY - name: Run Auto Fin tests run: python -m pytest plugins/auto-fin -q - name: Require the plugin-enabled ReMe release on PyPI env: REME_REQUIREMENT: ${{ steps.package.outputs.reme_requirement }} run: | python -m pip download --no-deps \ --dest "${RUNNER_TEMP}/reme-auto-fin-base" \ "${REME_REQUIREMENT}" - name: Build and check distributions run: | mkdir -p dist/auto-fin python -m build plugins/auto-fin --outdir dist/auto-fin python -m twine check dist/auto-fin/* - name: Verify distributions and isolated installation env: AGENTSCOPE_REQUIREMENT: ${{ steps.package.outputs.agentscope_requirement }} run: | AUTO_FIN_WHEEL="$(pwd)/$(ls dist/auto-fin/reme_auto_fin-*.whl)" AUTO_FIN_SDIST="$(pwd)/$(ls dist/auto-fin/reme_auto_fin-*.tar.gz)" python -m zipfile -l "${AUTO_FIN_WHEEL}" | grep 'dist-info/licenses/LICENSE' python -m tarfile -l "${AUTO_FIN_SDIST}" | grep '/LICENSE' python -m venv "${RUNNER_TEMP}/reme-auto-fin-smoke" "${RUNNER_TEMP}/reme-auto-fin-smoke/bin/python" -m pip install \ "${AGENTSCOPE_REQUIREMENT}" "${AUTO_FIN_WHEEL}" cd "${RUNNER_TEMP}" "${RUNNER_TEMP}/reme-auto-fin-smoke/bin/python" - <<'PY' from importlib.metadata import distribution from reme.plugin_manifest import load_package_manifest package = distribution("reme-auto-fin") plugins = {entry.name: entry for entry in package.entry_points if entry.group == "reme.plugins"} assert plugins["auto-fin"].value == "reme_auto_fin" manifest = load_package_manifest("reme_auto_fin", plugin_name="auto-fin") assert set(manifest.backends) == { "auto_fin_data_step", "auto_fin_topic_step", "auto_fin_merge_step", } assert set(manifest.application_defaults["jobs"]) == { "auto_fin", "auto_fin_cron", } PY - name: Upload distributions uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: reme-auto-fin-${{ inputs.version }} path: dist/auto-fin/ if-no-files-found: error publish: needs: build runs-on: ubuntu-latest environment: pypi permissions: contents: read id-token: write steps: - name: Download distributions uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 with: name: reme-auto-fin-${{ inputs.version }} path: dist/auto-fin - name: Publish reme-auto-fin uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 with: packages-dir: dist/auto-fin