From d306390f3a61d9d0741223d154cc607a3d8502d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=81=E5=B1=BF?= Date: Tue, 26 May 2026 17:34:53 +0800 Subject: [PATCH 1/2] feat: add PyPI dev bootstrap release mode --- .github/workflows/loongsuite-release.yml | 181 +++++++++++++--- docs/loongsuite-release.md | 42 +++- .../loongsuite/select_new_pypi_projects.py | 196 ++++++++++++++++++ 3 files changed, 390 insertions(+), 29 deletions(-) create mode 100644 scripts/loongsuite/select_new_pypi_projects.py diff --git a/.github/workflows/loongsuite-release.yml b/.github/workflows/loongsuite-release.yml index 13d4e413c..6ce87157d 100644 --- a/.github/workflows/loongsuite-release.yml +++ b/.github/workflows/loongsuite-release.yml @@ -1,6 +1,8 @@ # LoongSuite Release Workflow # -# This workflow handles the complete LoongSuite release process: +# This workflow handles the complete LoongSuite release process and an optional +# PyPI dev-bootstrap mode for creating newly added project names before a formal +# release: # # 1. Create release/{version} branch from main # 2. Build packages (bootstrap_gen.py, PyPI wheels, GitHub Release tar.gz) @@ -14,11 +16,14 @@ # run locally for the same workflow. # # Trigger: -# - workflow_dispatch: Manual trigger with version inputs +# - workflow_dispatch: Manual trigger with mode and version inputs # - push tags: Automatic trigger on v* tags # # PyPI / Test PyPI configuration: -# - Production PyPI: Set PYPI_API_TOKEN secret, or configure OIDC trusted publishing on pypi.org +# - Production PyPI: Set PYPI_API_TOKEN secret. +# - OIDC Trusted Publishing can be used instead, but new PyPI project names then +# require Pending Publishers configured with workflow=loongsuite-release.yml +# and environment=pypi. # - Test PyPI: Set TEST_PYPI_TOKEN secret (pypi-xxx from https://test.pypi.org/manage/account/token/) # - Use publish_target: testpypi to publish to Test PyPI instead of production # @@ -27,38 +32,58 @@ # name: LoongSuite Release -run-name: "LoongSuite Release ${{ github.event.inputs.loongsuite_version || github.ref_name }}" +# If this workflow is used with PyPI OIDC Trusted Publishing, Pending Publishers +# are configured with this workflow filename. Renaming this file breaks those +# publishers. +run-name: "LoongSuite ${{ github.event.inputs.mode || 'release' }}" on: workflow_dispatch: inputs: + mode: + description: 'release: full release; dev-bootstrap-new-projects: publish name-reservation dev wheels for missing PyPI projects' + type: choice + options: + - release + - dev-bootstrap-new-projects + default: release loongsuite_version: - description: 'LoongSuite version (e.g., 0.1.0) - for loongsuite-* packages' + description: 'LoongSuite release/base version (e.g., 0.6.0). Dev bootstrap derives a unique .dev version.' required: true upstream_version: - description: 'Upstream OTel version (e.g., 0.60b1) - for opentelemetry-* packages in bootstrap_gen.py' - required: true + description: 'Release mode: upstream OTel version (e.g., 0.60b1). Ignored when mode=dev-bootstrap-new-projects.' + required: false skip_pypi: - description: 'Skip PyPI publish (for testing)' + description: 'Release mode only: skip PyPI publish (ignored when mode=dev-bootstrap-new-projects)' type: boolean default: false publish_target: - description: 'Publish target: pypi or testpypi' + description: 'Release mode only: publish target (ignored when mode=dev-bootstrap-new-projects)' type: choice options: - pypi - testpypi default: pypi + dry_run: + description: 'Dev bootstrap only: preview missing PyPI projects without publishing (ignored in release mode)' + type: boolean + default: true push: tags: - 'v*' +concurrency: + group: loongsuite-release-${{ github.event.inputs.mode || 'release' }} + cancel-in-progress: false + env: PYTHON_VERSION: '3.11' + DEFAULT_UPSTREAM_VERSION: '0.60b1' jobs: - # Build all packages, create release branch, archive changelogs + # Release-mode build: create release branch, build packages, and archive changelogs. build: + if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.mode == 'release' }} runs-on: ubuntu-latest permissions: contents: write @@ -72,14 +97,17 @@ jobs: - name: Set versions from tag or input id: version + env: + INPUT_LOONGSUITE_VERSION: ${{ github.event.inputs.loongsuite_version }} + INPUT_UPSTREAM_VERSION: ${{ github.event.inputs.upstream_version }} run: | if [[ "${{ github.event_name }}" == "push" && "${{ github.ref_type }}" == "tag" ]]; then tag="${GITHUB_REF#refs/tags/}" loongsuite_version="${tag#v}" - upstream_version="${UPSTREAM_VERSION:-0.60b1}" + upstream_version="${INPUT_UPSTREAM_VERSION:-$DEFAULT_UPSTREAM_VERSION}" else - loongsuite_version="${{ github.event.inputs.loongsuite_version }}" - upstream_version="${{ github.event.inputs.upstream_version }}" + loongsuite_version="$INPUT_LOONGSUITE_VERSION" + upstream_version="$INPUT_UPSTREAM_VERSION" fi if [[ -z "$loongsuite_version" ]]; then @@ -91,8 +119,8 @@ jobs: exit 1 fi - echo "loongsuite_version=$loongsuite_version" >> $GITHUB_OUTPUT - echo "upstream_version=$upstream_version" >> $GITHUB_OUTPUT + echo "loongsuite_version=$loongsuite_version" >> "$GITHUB_OUTPUT" + echo "upstream_version=$upstream_version" >> "$GITHUB_OUTPUT" echo "LoongSuite version: $loongsuite_version" echo "Upstream version: $upstream_version" @@ -107,10 +135,13 @@ jobs: git config user.email "github-actions[bot]@users.noreply.github.com" - name: Run release script + env: + LOONGSUITE_VERSION: ${{ steps.version.outputs.loongsuite_version }} + UPSTREAM_VERSION: ${{ steps.version.outputs.upstream_version }} run: | ./scripts/loongsuite/loongsuite_release.sh \ - --loongsuite-version ${{ steps.version.outputs.loongsuite_version }} \ - --upstream-version ${{ steps.version.outputs.upstream_version }} \ + --loongsuite-version "$LOONGSUITE_VERSION" \ + --upstream-version "$UPSTREAM_VERSION" \ --skip-install \ --skip-github-release \ --skip-post-release-pr @@ -133,18 +164,95 @@ jobs: dist/loongsuite-python-agent-*.tar.gz dist/release-notes.md + # Build dev wheels only for LoongSuite distributions whose PyPI projects do not exist yet. + dev-bootstrap-build: + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.mode == 'dev-bootstrap-new-projects' }} + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + dev_version: ${{ steps.version.outputs.dev_version }} + has_new_projects: ${{ steps.select.outputs.has_new_projects }} + missing_distributions: ${{ steps.select.outputs.missing_distributions }} + steps: + - uses: actions/checkout@v4 + + - name: Validate selected ref + run: | + if [[ "$GITHUB_REF_NAME" != "main" ]]; then + echo "ERROR: dev-bootstrap-new-projects must be run from main after the plugin PR is merged." + echo "Selected ref: $GITHUB_REF_NAME" + exit 1 + fi + + - uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Install build dependencies + run: python -m pip install -r pkg-requirements.txt + + - name: Set dev version + id: version + env: + BASE_VERSION: ${{ github.event.inputs.loongsuite_version }} + run: | + if [[ -z "$BASE_VERSION" ]]; then + echo "ERROR: loongsuite_version is required" + exit 1 + fi + dev_number=$((GITHUB_RUN_NUMBER * 100 + GITHUB_RUN_ATTEMPT)) + dev_version="${BASE_VERSION}.dev${dev_number}" + echo "dev_version=$dev_version" >> "$GITHUB_OUTPUT" + echo "Using dev bootstrap version: $dev_version" + + - name: Build PyPI wheels + env: + DEV_VERSION: ${{ steps.version.outputs.dev_version }} + run: | + rm -rf dist dist-pypi + mkdir -p dist-pypi + # Name-reservation wheels pin LoongSuite packages to this run's dev + # version; they are not intended as installable release candidates. + python scripts/loongsuite/build_loongsuite_package.py \ + --build-pypi \ + --version "$DEV_VERSION" \ + --util-genai-version "$DEV_VERSION" \ + --dist-dir dist-pypi + + - name: Keep only missing PyPI projects + id: select + env: + DRY_RUN: ${{ github.event.inputs.dry_run }} + run: | + args=(--dist-dir dist-pypi) + if [[ "$DRY_RUN" == "true" ]]; then + args+=(--dry-run) + fi + python scripts/loongsuite/select_new_pypi_projects.py "${args[@]}" + + - name: Upload dev PyPI artifacts + if: ${{ steps.select.outputs.has_new_projects == 'true' && !inputs.dry_run }} + uses: actions/upload-artifact@v4 + with: + name: dev-pypi-packages + path: dist-pypi/*.whl + if-no-files-found: error + # Publish to production PyPI publish-pypi: needs: build runs-on: ubuntu-latest if: | - (github.event_name != 'workflow_dispatch' || !inputs.skip_pypi) && - (github.event_name != 'workflow_dispatch' || github.event.inputs.publish_target != 'testpypi') + github.event_name != 'workflow_dispatch' || + ( + github.event.inputs.mode == 'release' && + !inputs.skip_pypi && + github.event.inputs.publish_target != 'testpypi' + ) environment: name: pypi url: https://pypi.org/project/loongsuite-distro/ - permissions: - id-token: write steps: - name: Download PyPI artifacts uses: actions/download-artifact@v4 @@ -155,13 +263,36 @@ jobs: - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: + username: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} + skip-existing: true + + # Publish dev wheels that create newly added PyPI project names before a formal release. + publish-dev-bootstrap-pypi: + needs: dev-bootstrap-build + runs-on: ubuntu-latest + if: ${{ needs.dev-bootstrap-build.outputs.has_new_projects == 'true' && !inputs.dry_run }} + environment: + name: pypi + steps: + - name: Download dev PyPI artifacts + uses: actions/download-artifact@v4 + with: + name: dev-pypi-packages + path: dist/ + + - name: Publish dev bootstrap wheels to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + username: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} skip-existing: true # Publish to Test PyPI (for testing before production) publish-testpypi: needs: build runs-on: ubuntu-latest - if: ${{ github.event_name == 'workflow_dispatch' && !inputs.skip_pypi && inputs.publish_target == 'testpypi' }} + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.mode == 'release' && !inputs.skip_pypi && inputs.publish_target == 'testpypi' }} steps: - name: Download PyPI artifacts uses: actions/download-artifact@v4 @@ -181,6 +312,7 @@ jobs: github-release: needs: build runs-on: ubuntu-latest + if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.mode == 'release' }} permissions: contents: write steps: @@ -193,8 +325,8 @@ jobs: - name: Create GitHub release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ needs.build.outputs.loongsuite_version }} run: | - VERSION="${{ needs.build.outputs.loongsuite_version }}" gh release create "v${VERSION}" \ --title "loongsuite-python-agent ${VERSION}" \ --notes-file dist/release-notes.md \ @@ -204,6 +336,7 @@ jobs: post-release-pr: needs: build runs-on: ubuntu-latest + if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.mode == 'release' }} permissions: contents: write pull-requests: write @@ -225,8 +358,8 @@ jobs: - name: Create post-release branch and PR env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ needs.build.outputs.loongsuite_version }} run: | - VERSION="${{ needs.build.outputs.loongsuite_version }}" BRANCH="post-release/${VERSION}" TODAY=$(date -u +%Y-%m-%d) diff --git a/docs/loongsuite-release.md b/docs/loongsuite-release.md index de429ad4e..a13720a83 100644 --- a/docs/loongsuite-release.md +++ b/docs/loongsuite-release.md @@ -449,6 +449,7 @@ Dry Run 模式**不会**创建分支、归档 changelog、提交代码或创建 1. 进入 GitHub 仓库 → **Actions** → **LoongSuite Release** 2. 点击 **Run workflow** 3. 填写参数: + - `mode`: `release` - `loongsuite_version`: `0.1.0` - `upstream_version`: `0.60b1` - `skip_pypi`: 测试时可勾选 @@ -456,7 +457,35 @@ Dry Run 模式**不会**创建分支、归档 changelog、提交代码或创建 CI 工作流会调用同一个脚本完成构建和分支管理,然后在独立 job 中发布 PyPI、创建 GitHub Release,并自动创建 post-release PR 到 main。 -#### 方式 3: Tag 触发 +#### 方式 3: 新 PyPI 项目 Dev Bootstrap + +当新插件合入 `main` 后,可以先创建正式 PyPI 项目名,避免正式 release 时一次性首次创建过多项目触发 PyPI 限流。 + +1. 确认新插件目录在 `instrumentation-loongsuite/` 下,且没有被 `loongsuite_pypi_manifest.py` 或 `loongsuite-build-config.json` 跳过。 +2. 确认 GitHub Actions 中的 `PYPI_API_TOKEN` 有权限上传并创建新的 PyPI project。API Token 发布模式不需要为每个新项目提前配置 Pending Publisher;只有改用 OIDC Trusted Publishing 时才需要在 PyPI 为新项目添加 Pending Publisher。 +3. 进入 GitHub 仓库 → **Actions** → **LoongSuite Release** → **Run workflow**,并选择 `main` 分支: + - `mode`: `dev-bootstrap-new-projects` + - `loongsuite_version`: 下一个正式版本号,例如 `0.6.0` + - `upstream_version`: 该模式下不参与发布决策,可留空 + - `skip_pypi`、`publish_target` 在该模式下不参与发布决策 + - `dry_run`: 首次建议保持 `true`,只在确认 Actions summary 中的 missing projects 后改为 `false` 再执行上传 +4. 工作流会构建唯一 dev 版本:`.dev`,其中 `N = github.run_number * 100 + github.run_attempt`,因此同一次 workflow rerun 也不会复用版本号。随后查询 `https://pypi.org/pypi//json`,只保留 PyPI 上还不存在的项目对应 wheel;`dry_run=false` 时才上传。 + +该模式不会创建 release 分支,不会创建 GitHub Release,也不会创建 post-release PR。如果所有 PyPI 项目都已存在,publish job 会自动跳过。这个 dev wheel 只用于预创建正式 PyPI 项目名;因为依赖版本会指向同一个临时 dev 版本,默认不保证 `pip install ==` 可用,正式安装验证仍以后续正式 release 为准。 + +`publish-dev-bootstrap-pypi` job 仍使用 GitHub Environment `pypi`。如果团队希望上传前有人工确认,请在 GitHub Settings → Environments → `pypi` 中配置 required reviewers;如果不配置 required reviewers,流程上的确认点就是先用默认 `dry_run=true` 查看 Actions summary,再手动重跑并改为 `dry_run=false`。 + +本地可以先 dry-run 筛选逻辑: + +```bash +rm -rf dist-pypi +python scripts/loongsuite/build_loongsuite_package.py \ + --build-pypi --version 0.6.0.dev0 --dist-dir dist-pypi +python scripts/loongsuite/select_new_pypi_projects.py \ + --dist-dir dist-pypi --dry-run +``` + +#### 方式 4: Tag 触发 ```bash git tag v0.1.0 @@ -474,19 +503,21 @@ git push origin v0.1.0 | GitHub Release | 脚本内 `gh release create`(可 skip) | 独立 job | | PyPI publish | 不执行(本地不做) | 独立 job 通过 OIDC/Token | | Post-release PR | 脚本内创建 PR(需 `gh` CLI) | 独立 job,自动创建 | +| 新 PyPI 项目 dev-bootstrap | 不执行 | `mode=dev-bootstrap-new-projects` 独立执行,仅构建和上传缺失项目名的 dev wheel | #### PyPI / Test PyPI 发布配置 -**发布到生产 PyPI(二选一):** +**发布到生产 PyPI:** -1. **API Token**:在 GitHub 仓库 Settings → Secrets → Actions 中添加: - - `PYPI_API_TOKEN`:从 [pypi.org/manage/account/token/](https://pypi.org/manage/account/token/) 创建 +1. **API Token(当前 workflow 使用)**:在 GitHub 仓库 Settings → Secrets → Actions 中添加: + - `PYPI_API_TOKEN`:从 [pypi.org/manage/account/token/](https://pypi.org/manage/account/token/) 创建。使用 API Token 发布时,首次上传新 distribution 会创建对应 PyPI project,不需要 Pending Publisher。 -2. **OIDC Trusted Publishing**(推荐): +2. **OIDC Trusted Publishing(可选替代方案)**: - PyPI 项目设置 → Publishing → Add a new pending publisher - Owner: `alibaba`,Repository: `loongsuite-python-agent` - Workflow: `loongsuite-release.yml`,Environment: `pypi` - 在 GitHub 仓库中创建 Environment `pypi`(Settings → Environments) + - 如果用 OIDC 创建新 project,需要为每个新 project 配置 Pending Publisher。 **发布到 Test PyPI(测试用):** @@ -497,6 +528,7 @@ git push origin v0.1.0 **重要说明:** - `dist-pypi/` 中的 `loongsuite_util_genai-*.whl`、`loongsuite_distro-*.whl` 以及 `loongsuite_instrumentation_*.whl`(`instrumentation-loongsuite` 中当前参与 PyPI 构建的插件;`agno` / `mcp` / `dify` 暂排除)会上传到 PyPI;每个新插件首次发布前需在 PyPI 上完成项目/Trusted Publisher 配置 +- 新插件可以先通过 `mode=dev-bootstrap-new-projects` 发布唯一 dev 版本来创建正式 PyPI 项目名;正式 release 后续只上传正式版本 - `loongsuite-python-agent-*.tar.gz` 仅用于 GitHub Release,**禁止**上传到 PyPI ### 5.4 Post-Release PR diff --git a/scripts/loongsuite/select_new_pypi_projects.py b/scripts/loongsuite/select_new_pypi_projects.py new file mode 100644 index 000000000..814e7c0ce --- /dev/null +++ b/scripts/loongsuite/select_new_pypi_projects.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 + +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Keep only wheels whose PyPI projects do not exist yet.""" + +from __future__ import annotations + +import argparse +import os +import re +import sys +import time +import urllib.error +import urllib.parse +import urllib.request +from pathlib import Path + +from loongsuite_pypi_manifest import list_pypi_distribution_names + + +def normalize_distribution_name(name: str) -> str: + return re.sub(r"[-_.]+", "-", name).lower() + + +def distribution_name_from_wheel(wheel_path: Path) -> str: + return wheel_path.name.split("-", 1)[0].replace("_", "-") + + +def project_exists_on_pypi(distribution_name: str, *, timeout: float) -> bool: + normalized = normalize_distribution_name(distribution_name) + url = f"https://pypi.org/pypi/{urllib.parse.quote(normalized)}/json" + request = urllib.request.Request( + url, + headers={"User-Agent": "loongsuite-python-agent-release/1.0"}, + ) + for attempt in range(1, 4): + try: + with urllib.request.urlopen(request, timeout=timeout) as response: + return 200 <= response.status < 300 + except urllib.error.HTTPError as exc: + if exc.code in (404, 410): + return False + retryable = exc.code == 429 or exc.code >= 500 + if not retryable or attempt == 3: + raise RuntimeError( + f"PyPI project lookup failed for {distribution_name} " + f"at {url}: HTTP {exc.code}" + ) from exc + except OSError as exc: + if attempt == 3: + raise RuntimeError( + f"PyPI project lookup failed for {distribution_name} " + f"at {url}: {exc}" + ) from exc + + time.sleep(attempt) + + +def write_github_outputs(output_path: Path, values: dict[str, str]) -> None: + with output_path.open("a", encoding="utf-8") as f: + for key, value in values.items(): + f.write(f"{key}={value}\n") + + +def write_step_summary( + summary_path: Path, + missing_distributions: list[str], + kept_wheels: list[Path], + removed_wheels: list[Path], +) -> None: + lines = ["## LoongSuite PyPI dev bootstrap", ""] + if missing_distributions: + lines.append("New PyPI projects to create:") + lines.extend(f"- `{name}`" for name in missing_distributions) + else: + lines.append("No missing PyPI projects were found.") + lines.extend( + [ + "", + f"Kept wheels: {len(kept_wheels)}", + f"Removed wheels: {len(removed_wheels)}", + ] + ) + with summary_path.open("a", encoding="utf-8") as summary: + summary.write("\n".join(lines) + "\n") + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Prune built LoongSuite wheels to new PyPI project names." + ) + parser.add_argument( + "--base-dir", + type=Path, + default=Path(__file__).resolve().parent.parent.parent, + ) + parser.add_argument( + "--dist-dir", + type=Path, + default=Path(__file__).resolve().parent.parent.parent / "dist-pypi", + ) + parser.add_argument("--timeout", type=float, default=15.0) + parser.add_argument("--dry-run", action="store_true") + parser.add_argument( + "--github-output", + type=Path, + default=Path(os.environ["GITHUB_OUTPUT"]) + if "GITHUB_OUTPUT" in os.environ + else None, + ) + parser.add_argument( + "--summary", + type=Path, + default=Path(os.environ["GITHUB_STEP_SUMMARY"]) + if "GITHUB_STEP_SUMMARY" in os.environ + else None, + ) + args = parser.parse_args() + + expected_distributions = list_pypi_distribution_names(args.base_dir) + if not expected_distributions: + raise RuntimeError( + "No PyPI distributions were found in the LoongSuite manifest; " + "refusing to prune wheels." + ) + missing_distributions = [ + name + for name in expected_distributions + if not project_exists_on_pypi(name, timeout=args.timeout) + ] + missing_normalized = { + normalize_distribution_name(name) for name in missing_distributions + } + + kept_wheels: list[Path] = [] + removed_wheels: list[Path] = [] + for wheel_path in sorted(args.dist_dir.glob("*.whl")): + wheel_distribution = normalize_distribution_name( + distribution_name_from_wheel(wheel_path) + ) + if wheel_distribution in missing_normalized: + kept_wheels.append(wheel_path) + else: + removed_wheels.append(wheel_path) + if not args.dry_run: + wheel_path.unlink() + + kept_normalized = { + normalize_distribution_name(distribution_name_from_wheel(wheel)) + for wheel in kept_wheels + } + missing_without_wheel = sorted(missing_normalized - kept_normalized) + if missing_without_wheel: + raise RuntimeError( + "PyPI projects are missing, but no matching wheel was built: " + + ", ".join(missing_without_wheel) + ) + + print("Missing PyPI projects:") + for name in missing_distributions or ["(none)"]: + print(f" - {name}") + print(f"Kept wheels: {len(kept_wheels)}") + print(f"Removed wheels: {len(removed_wheels)}") + + outputs = { + "has_new_projects": "true" if missing_distributions else "false", + "missing_distributions": ",".join(missing_distributions), + } + if args.github_output: + write_github_outputs(args.github_output, outputs) + if args.summary: + write_step_summary( + args.summary, + missing_distributions, + kept_wheels, + removed_wheels, + ) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 2c80af74a98c38f829efb874f16d3fd8bee59245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=81=E5=B1=BF?= Date: Wed, 27 May 2026 10:55:31 +0800 Subject: [PATCH 2/2] fix: address release workflow review comments --- .github/workflows/loongsuite-release.yml | 18 +++++++++--------- docs/loongsuite-release.md | 7 ++++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/loongsuite-release.yml b/.github/workflows/loongsuite-release.yml index 6ce87157d..5d9b801ed 100644 --- a/.github/workflows/loongsuite-release.yml +++ b/.github/workflows/loongsuite-release.yml @@ -20,10 +20,11 @@ # - push tags: Automatic trigger on v* tags # # PyPI / Test PyPI configuration: -# - Production PyPI: Set PYPI_API_TOKEN secret. -# - OIDC Trusted Publishing can be used instead, but new PyPI project names then -# require Pending Publishers configured with workflow=loongsuite-release.yml -# and environment=pypi. +# - Production PyPI: Set PYPI_API_TOKEN secret. This workflow currently uses +# API-token publishing, not PyPI OIDC Trusted Publishing. +# - If switching to OIDC Trusted Publishing later, add id-token permissions and +# configure Pending Publishers for new PyPI project names with +# workflow=loongsuite-release.yml and environment=pypi. # - Test PyPI: Set TEST_PYPI_TOKEN secret (pypi-xxx from https://test.pypi.org/manage/account/token/) # - Use publish_target: testpypi to publish to Test PyPI instead of production # @@ -32,9 +33,8 @@ # name: LoongSuite Release -# If this workflow is used with PyPI OIDC Trusted Publishing, Pending Publishers -# are configured with this workflow filename. Renaming this file breaks those -# publishers. +# Keep this workflow filename stable; any future PyPI OIDC Trusted Publishing +# setup would reference it from Pending Publisher entries. run-name: "LoongSuite ${{ github.event.inputs.mode || 'release' }}" on: @@ -51,7 +51,7 @@ on: description: 'LoongSuite release/base version (e.g., 0.6.0). Dev bootstrap derives a unique .dev version.' required: true upstream_version: - description: 'Release mode: upstream OTel version (e.g., 0.60b1). Ignored when mode=dev-bootstrap-new-projects.' + description: 'Release mode: upstream OTel version. Blank uses DEFAULT_UPSTREAM_VERSION. Ignored when mode=dev-bootstrap-new-projects.' required: false skip_pypi: description: 'Release mode only: skip PyPI publish (ignored when mode=dev-bootstrap-new-projects)' @@ -107,7 +107,7 @@ jobs: upstream_version="${INPUT_UPSTREAM_VERSION:-$DEFAULT_UPSTREAM_VERSION}" else loongsuite_version="$INPUT_LOONGSUITE_VERSION" - upstream_version="$INPUT_UPSTREAM_VERSION" + upstream_version="${INPUT_UPSTREAM_VERSION:-$DEFAULT_UPSTREAM_VERSION}" fi if [[ -z "$loongsuite_version" ]]; then diff --git a/docs/loongsuite-release.md b/docs/loongsuite-release.md index a13720a83..424c5586e 100644 --- a/docs/loongsuite-release.md +++ b/docs/loongsuite-release.md @@ -451,7 +451,7 @@ Dry Run 模式**不会**创建分支、归档 changelog、提交代码或创建 3. 填写参数: - `mode`: `release` - `loongsuite_version`: `0.1.0` - - `upstream_version`: `0.60b1` + - `upstream_version`: 上游 OTel 版本;留空时使用 workflow 中的 `DEFAULT_UPSTREAM_VERSION` - `skip_pypi`: 测试时可勾选 4. 执行 @@ -501,7 +501,7 @@ git push origin v0.1.0 | 收集/归档 changelog | 脚本内调用 Python 脚本 | 同上 | | Commit + Push | 脚本内 `git commit` + `git push` | 同上 | | GitHub Release | 脚本内 `gh release create`(可 skip) | 独立 job | -| PyPI publish | 不执行(本地不做) | 独立 job 通过 OIDC/Token | +| PyPI publish | 不执行(本地不做) | 独立 job 通过 API Token(未来可迁移到 OIDC) | | Post-release PR | 脚本内创建 PR(需 `gh` CLI) | 独立 job,自动创建 | | 新 PyPI 项目 dev-bootstrap | 不执行 | `mode=dev-bootstrap-new-projects` 独立执行,仅构建和上传缺失项目名的 dev wheel | @@ -691,7 +691,8 @@ pip install loongsuite-util-genai **问题**: PyPI 发布 403 Forbidden ```bash -# 解决: 检查 OIDC trusted publishing 配置或 API token +# 解决: 检查 PYPI_API_TOKEN 是否有效、未过期,并且有目标 project 的上传权限 +# 如果未来改用 OIDC Trusted Publishing,再检查对应的 publisher 配置 ``` **问题**: 版本号已存在