Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
181 changes: 157 additions & 24 deletions .github/workflows/loongsuite-release.yml
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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.
Comment thread
sipercai marked this conversation as resolved.
Outdated
# - 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
#
Expand All @@ -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
Comment thread
sipercai marked this conversation as resolved.
Outdated
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
Expand All @@ -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
Expand All @@ -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"
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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 \
Expand All @@ -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
Expand All @@ -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)

Expand Down
42 changes: 37 additions & 5 deletions docs/loongsuite-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -449,14 +449,43 @@ 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`: 测试时可勾选
4. 执行

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 版本:`<base>.dev<N>`,其中 `N = github.run_number * 100 + github.run_attempt`,因此同一次 workflow rerun 也不会复用版本号。随后查询 `https://pypi.org/pypi/<distribution>/json`,只保留 PyPI 上还不存在的项目对应 wheel;`dry_run=false` 时才上传。

该模式不会创建 release 分支,不会创建 GitHub Release,也不会创建 post-release PR。如果所有 PyPI 项目都已存在,publish job 会自动跳过。这个 dev wheel 只用于预创建正式 PyPI 项目名;因为依赖版本会指向同一个临时 dev 版本,默认不保证 `pip install ==<dev_version>` 可用,正式安装验证仍以后续正式 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
Expand All @@ -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(测试用):**

Expand All @@ -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
Expand Down
Loading