qwen-code/.github/workflows/release-sdk-python.yml
jinye bf597ad385
feat(sdk-python): replace verbatim release notes inheritance with --generate-notes (#3835)
* feat(sdk-python): replace verbatim release notes inheritance with --generate-notes

The previous implementation fetched the entire body of the previous GitHub
release and appended it to the new release notes. Because each release body
already contained the body of the one before it, this created a linear chain
that grew with every stable release — eventually hitting GitHub's 125 KB
release body limit.

Replace the body-chaining approach with GitHub's built-in --generate-notes
flag, which auto-generates a bounded, PR-based changelog scoped between two
tags via --notes-start-tag. The SDK metadata header (package name + version)
is preserved via --notes-file, which GitHub prepends above the auto-generated
changelog.

For the first-ever release (no previous SDK tag), --generate-notes is skipped
to avoid pulling in unrelated non-SDK commits, falling back to a static
"Initial release" message instead.

Closes #3796

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(sdk-python): address review comments on release notes

- Rename NOTES_START_TAG_FLAG → NOTES_START_TAG_ARG (contains key-value
  pair, not just a flag)
- Fix misleading "Initial release" message — PREVIOUS_RELEASE_TAG is empty
  for all nightly/preview releases, not just the first release
- Add comments explaining why old error handling is safe to remove

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(sdk-python): validate previous tag exists before using --notes-start-tag

If a prior release published to PyPI but failed to create a GitHub
release/tag, the tag won't exist in Git. Using --notes-start-tag with
a nonexistent tag would cause gh release create to fail after PyPI
publish, leaving a partial release state.

Add a git rev-parse check before using --notes-start-tag. When the tag
is missing, fall back to static notes with a :⚠️: annotation,
ensuring the GitHub release is always created.

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(sdk-python): clarify else-branch comment covers first stable + preview/nightly

The comment previously implied the else-branch was only for
preview/nightly, but PREVIOUS_RELEASE_TAG is also empty for the
very first stable release (no prior stable version on PyPI).

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(sdk-python): use Bash array for gh release args to fix SC2086 lint

ShellCheck SC2086 flags unquoted variables containing spaces
(NOTES_START_TAG_ARG holds "--notes-start-tag sdk-python-v0.1.0").
Replace string-based flag variables with a Bash array that is expanded
via "${GH_RELEASE_ARGS[@]}" — properly quoted and shellcheck-safe.

Also consolidates the prerelease flag into the same array, removing the
now-unused PRERELEASE_FLAG variable.

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* refactor(sdk-python): extract PREVIOUS_TAG_NAME to reduce repetition

DRY improvement: sdk-python-${PREVIOUS_RELEASE_TAG} was repeated 3
times. Extract into a local PREVIOUS_TAG_NAME variable, symmetric with
the existing TAG_NAME at the top of the script.

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
2026-05-08 16:35:19 +08:00

515 lines
21 KiB
YAML

name: 'Release Python SDK'
on:
workflow_dispatch:
inputs:
version:
description: 'The version to release (e.g., v0.1.0 for stable, v0.1.1-preview.0 for preview).'
required: false
type: 'string'
ref:
description: 'The protected branch ref to release SDK from. This privileged workflow only permits main.'
required: true
type: 'string'
default: 'main'
dry_run:
description: 'Run the release flow without publishing to PyPI or creating persistent release branches/tags.'
required: true
type: 'boolean'
default: true
create_nightly_release:
description: 'Auto-apply the nightly release tag. Input version is ignored.'
required: false
type: 'boolean'
default: false
create_preview_release:
description: 'Auto-apply the preview release tag. Input version is ignored unless explicitly provided.'
required: false
type: 'boolean'
default: false
force_skip_tests:
description: 'Skip Python checks and smoke test. Production releases should keep this disabled.'
required: false
type: 'boolean'
default: false
concurrency:
group: '${{ github.workflow }}-${{ github.event.inputs.create_nightly_release == ''true'' && ''nightly'' || github.event.inputs.create_preview_release == ''true'' && ''preview'' || ''stable'' }}'
cancel-in-progress: false
jobs:
release-sdk-python:
runs-on: 'ubuntu-latest'
environment:
name: 'production-release'
url: '${{ github.server_url }}/${{ github.repository }}/releases'
if: |-
${{ github.repository == 'QwenLM/qwen-code' }}
permissions:
contents: 'write'
id-token: 'write'
issues: 'write'
pull-requests: 'write'
steps:
- name: 'Validate release inputs'
env:
CREATE_NIGHTLY_RELEASE: '${{ github.event.inputs.create_nightly_release }}'
CREATE_PREVIEW_RELEASE: '${{ github.event.inputs.create_preview_release }}'
DRY_RUN_INPUT: '${{ github.event.inputs.dry_run }}'
FORCE_SKIP_TESTS: '${{ github.event.inputs.force_skip_tests }}'
MANUAL_VERSION: '${{ inputs.version }}'
REQUESTED_REF: '${{ github.event.inputs.ref || github.sha }}'
WORKFLOW_REF: '${{ github.ref }}'
run: |
if [[ "${CREATE_NIGHTLY_RELEASE}" == "true" && "${CREATE_PREVIEW_RELEASE}" == "true" ]]; then
echo "create_nightly_release and create_preview_release cannot both be true" >&2
exit 1
fi
if [[ -n "${MANUAL_VERSION}" ]]; then
if [[ ! "${MANUAL_VERSION}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-preview\.[0-9]+)?$ ]]; then
echo "Invalid version format: ${MANUAL_VERSION}" >&2
exit 1
fi
fi
case "${WORKFLOW_REF}" in
refs/heads/main)
;;
*)
echo "This privileged workflow must be launched from the protected main workflow branch." >&2
exit 1
;;
esac
case "${REQUESTED_REF}" in
main|refs/heads/main)
;;
*)
echo "This privileged workflow must use the protected main ref." >&2
exit 1
;;
esac
if [[ "${DRY_RUN_INPUT}" != "true" ]]; then
if [[ "${FORCE_SKIP_TESTS}" == "true" ]]; then
echo "force_skip_tests cannot be used when dry_run is false" >&2
exit 1
fi
fi
- name: 'Checkout'
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
with:
ref: '${{ github.event.inputs.ref || github.sha }}'
fetch-depth: 0
- name: 'Set booleans for simplified logic'
env:
CREATE_NIGHTLY_RELEASE: '${{ github.event.inputs.create_nightly_release }}'
CREATE_PREVIEW_RELEASE: '${{ github.event.inputs.create_preview_release }}'
DRY_RUN_INPUT: '${{ github.event.inputs.dry_run }}'
id: 'vars'
run: |
is_nightly="false"
if [[ "${CREATE_NIGHTLY_RELEASE}" == "true" ]]; then
is_nightly="true"
fi
echo "is_nightly=${is_nightly}" >> "${GITHUB_OUTPUT}"
is_preview="false"
if [[ "${CREATE_PREVIEW_RELEASE}" == "true" ]]; then
is_preview="true"
fi
echo "is_preview=${is_preview}" >> "${GITHUB_OUTPUT}"
is_dry_run="false"
if [[ "${DRY_RUN_INPUT}" == "true" ]]; then
is_dry_run="true"
fi
echo "is_dry_run=${is_dry_run}" >> "${GITHUB_OUTPUT}"
- name: 'Capture checked-out commit'
id: 'checkout_sha'
run: |
echo "sha=$(git rev-parse HEAD)" >> "${GITHUB_OUTPUT}"
- name: 'Setup Node.js'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- name: 'Get the version'
id: 'version'
run: |
set -euo pipefail
VERSION_ARGS=()
if [[ "${IS_NIGHTLY}" == "true" ]]; then
VERSION_ARGS+=(--type=nightly)
elif [[ "${IS_PREVIEW}" == "true" ]]; then
VERSION_ARGS+=(--type=preview)
if [[ -n "${MANUAL_VERSION}" ]]; then
VERSION_ARGS+=("--preview_version_override=${MANUAL_VERSION}")
fi
else
VERSION_ARGS+=(--type=stable)
if [[ -n "${MANUAL_VERSION}" ]]; then
VERSION_ARGS+=("--stable_version_override=${MANUAL_VERSION}")
fi
fi
VERSION_JSON=$(node packages/sdk-python/scripts/get-release-version.js "${VERSION_ARGS[@]}")
extract_field() {
local value
value=$(echo "$VERSION_JSON" | jq -r ".$1")
if [[ "${value}" == "null" ]]; then
echo "Failed to extract $1 from version JSON: ${VERSION_JSON}" >&2
exit 1
fi
echo "${value}"
}
RELEASE_TAG=$(extract_field releaseTag)
RELEASE_VERSION=$(extract_field releaseVersion)
PACKAGE_VERSION=$(extract_field packageVersion)
PUBLISH_CHANNEL=$(extract_field publishChannel)
PREVIOUS_RELEASE_TAG=$(extract_field previousReleaseTag)
RESUME_EXISTING_RELEASE=$(extract_field resumeExistingRelease)
echo "RELEASE_TAG=${RELEASE_TAG}" >> "$GITHUB_OUTPUT"
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "$GITHUB_OUTPUT"
echo "PACKAGE_VERSION=${PACKAGE_VERSION}" >> "$GITHUB_OUTPUT"
echo "PUBLISH_CHANNEL=${PUBLISH_CHANNEL}" >> "$GITHUB_OUTPUT"
echo "PREVIOUS_RELEASE_TAG=${PREVIOUS_RELEASE_TAG}" >> "$GITHUB_OUTPUT"
echo "RESUME_EXISTING_RELEASE=${RESUME_EXISTING_RELEASE}" >> "$GITHUB_OUTPUT"
echo "========================================"
echo "Python SDK Release Version Info"
echo "========================================"
echo "Release Tag: ${RELEASE_TAG}"
echo "Release Version: ${RELEASE_VERSION}"
echo "Package Version: ${PACKAGE_VERSION}"
echo "Publish Channel: ${PUBLISH_CHANNEL}"
echo "Previous Release: ${PREVIOUS_RELEASE_TAG}"
echo "Resume Existing: ${RESUME_EXISTING_RELEASE}"
echo "========================================"
env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
IS_NIGHTLY: '${{ steps.vars.outputs.is_nightly }}'
IS_PREVIEW: '${{ steps.vars.outputs.is_preview }}'
MANUAL_VERSION: '${{ inputs.version }}'
- name: 'Setup Python'
uses: 'actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065' # ratchet:actions/setup-python@v5
with:
# Keep in sync with packages/sdk-python/pyproject.toml [project] requires-python.
python-version: '3.11'
- name: 'Install Dependencies'
run: |
npm ci
python -m pip install --upgrade pip
python -m pip install -e 'packages/sdk-python[dev]' build
- name: 'Build qwen CLI bundle'
if: |-
${{ github.event.inputs.force_skip_tests != 'true' }}
run: |
npm run build
npm run bundle
- name: 'Set Python package version (local only)'
env:
PACKAGE_VERSION: '${{ steps.version.outputs.PACKAGE_VERSION }}'
run: |
python - <<'PY'
from pathlib import Path
import os
import re
pyproject = Path('packages/sdk-python/pyproject.toml')
content = pyproject.read_text()
updated, count = re.subn(
r'^version = "[^"]+"$',
f'version = "{os.environ["PACKAGE_VERSION"]}"',
content,
flags=re.MULTILINE,
)
if count != 1:
raise SystemExit(
f'pyproject.toml version rewrite matched {count} lines, expected 1'
)
pyproject.write_text(updated)
PY
- name: 'Run Python quality gates'
if: |-
${{ github.event.inputs.force_skip_tests != 'true' }}
run: |
python -m ruff check --config packages/sdk-python/pyproject.toml packages/sdk-python
python -m ruff format --check --config packages/sdk-python/pyproject.toml packages/sdk-python
python -m mypy --config-file packages/sdk-python/pyproject.toml packages/sdk-python/src
python -m pytest -c packages/sdk-python/pyproject.toml packages/sdk-python/tests -q
- name: 'Run real smoke test'
if: |-
${{ github.event.inputs.force_skip_tests != 'true' }}
env:
OPENAI_API_KEY: '${{ secrets.OPENAI_API_KEY }}'
OPENAI_BASE_URL: '${{ secrets.OPENAI_BASE_URL }}'
OPENAI_MODEL: '${{ secrets.OPENAI_MODEL }}'
run: |
python packages/sdk-python/scripts/smoke_real.py --qwen "${GITHUB_WORKSPACE}/dist/cli.js" --cwd "${GITHUB_WORKSPACE}" --json-only
- name: 'Configure Git User'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: 'Create and switch to a release branch'
if: |-
${{ steps.vars.outputs.is_dry_run == 'false' }}
id: 'release_branch'
env:
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
RESUME_EXISTING_RELEASE: '${{ steps.version.outputs.RESUME_EXISTING_RELEASE }}'
run: |
set -euo pipefail
BRANCH_NAME="release/sdk-python/${RELEASE_TAG}"
VERSIONED_PYPROJECT="$(mktemp)"
cp packages/sdk-python/pyproject.toml "${VERSIONED_PYPROJECT}"
git restore --staged --worktree packages/sdk-python/pyproject.toml
if git ls-remote --exit-code --heads origin "${BRANCH_NAME}" >/dev/null 2>&1; then
git fetch origin "${BRANCH_NAME}"
if git show-ref --verify --quiet "refs/heads/${BRANCH_NAME}"; then
git switch "${BRANCH_NAME}"
else
git switch -c "${BRANCH_NAME}" --track "origin/${BRANCH_NAME}"
fi
git reset --hard "origin/${BRANCH_NAME}"
echo "REMOTE_BRANCH_EXISTS=true" >> "${GITHUB_OUTPUT}"
else
if [[ "${RESUME_EXISTING_RELEASE}" == "true" ]]; then
echo "Published version ${RELEASE_TAG} exists without a persisted release branch ${BRANCH_NAME}. To resolve: (1) re-trigger with a different version, or (2) manually create branch ${BRANCH_NAME} from the original release commit." >&2
exit 1
fi
git switch -c "${BRANCH_NAME}"
echo "REMOTE_BRANCH_EXISTS=false" >> "${GITHUB_OUTPUT}"
fi
cp "${VERSIONED_PYPROJECT}" packages/sdk-python/pyproject.toml
rm -f "${VERSIONED_PYPROJECT}"
echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}"
- name: 'Commit and push package version'
if: |-
${{ steps.vars.outputs.is_dry_run == 'false' }}
id: 'persist_source'
env:
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
CHECKED_OUT_SHA: '${{ steps.checkout_sha.outputs.sha }}'
REMOTE_BRANCH_EXISTS: '${{ steps.release_branch.outputs.REMOTE_BRANCH_EXISTS }}'
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
run: |
set -euo pipefail
git add packages/sdk-python/pyproject.toml
if git diff --staged --quiet; then
echo "No version changes to commit"
if [[ "${REMOTE_BRANCH_EXISTS}" == "true" ]]; then
echo "HAS_PERSISTED_SOURCE=true" >> "${GITHUB_OUTPUT}"
echo "RELEASE_TARGET_SHA=$(git rev-parse HEAD)" >> "${GITHUB_OUTPUT}"
else
echo "HAS_PERSISTED_SOURCE=false" >> "${GITHUB_OUTPUT}"
echo "RELEASE_TARGET_SHA=${CHECKED_OUT_SHA}" >> "${GITHUB_OUTPUT}"
fi
else
git commit -m "chore(release): sdk-python ${RELEASE_TAG}"
echo "HAS_PERSISTED_SOURCE=true" >> "${GITHUB_OUTPUT}"
echo "RELEASE_TARGET_SHA=$(git rev-parse HEAD)" >> "${GITHUB_OUTPUT}"
if [[ "${REMOTE_BRANCH_EXISTS}" == "true" ]]; then
git push origin "${BRANCH_NAME}"
else
git push --set-upstream origin "${BRANCH_NAME}"
fi
fi
- name: 'Build Python package'
working-directory: 'packages/sdk-python'
run: |
rm -rf dist
python -m build
- name: 'Publish qwen-code-sdk to PyPI'
if: |-
${{ steps.vars.outputs.is_dry_run == 'false' }}
uses: 'pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b' # ratchet:pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: 'packages/sdk-python/dist'
# skip-existing handles resumed releases where some artifacts were already uploaded
skip-existing: '${{ steps.version.outputs.RESUME_EXISTING_RELEASE }}'
- name: 'Show publish artifacts for dry-run'
if: |-
${{ steps.vars.outputs.is_dry_run == 'true' }}
run: |
ls -la packages/sdk-python/dist
- name: 'Create GitHub release and tag'
if: |-
${{ steps.vars.outputs.is_dry_run == 'false' }}
env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
PACKAGE_VERSION: '${{ steps.version.outputs.PACKAGE_VERSION }}'
PREVIOUS_RELEASE_TAG: '${{ steps.version.outputs.PREVIOUS_RELEASE_TAG }}'
RELEASE_TARGET_SHA: '${{ steps.persist_source.outputs.RELEASE_TARGET_SHA }}'
IS_NIGHTLY: '${{ steps.vars.outputs.is_nightly }}'
IS_PREVIEW: '${{ steps.vars.outputs.is_preview }}'
run: |
set -euo pipefail
TAG_NAME="sdk-python-${RELEASE_TAG}"
if gh release view "${TAG_NAME}" --json tagName >/dev/null 2>&1; then
echo "::warning::GitHub release ${TAG_NAME} already exists; skipping create."
exit 0
fi
if git rev-parse "${TAG_NAME}" >/dev/null 2>&1; then
EXISTING_TAG_SHA="$(git rev-list -n 1 "${TAG_NAME}")"
if [[ "${EXISTING_TAG_SHA}" != "${RELEASE_TARGET_SHA}" ]]; then
echo "Existing tag ${TAG_NAME} points to ${EXISTING_TAG_SHA}, expected ${RELEASE_TARGET_SHA}." >&2
exit 1
fi
fi
NOTES_FILE=$(mktemp)
{
echo "## Published Package"
echo ""
echo "- PyPI package: \`qwen-code-sdk\`"
echo "- Package version: \`${PACKAGE_VERSION}\`"
echo ""
echo "---"
echo ""
} > "${NOTES_FILE}"
GH_RELEASE_ARGS=()
if [[ -n "${PREVIOUS_RELEASE_TAG}" ]]; then
PREVIOUS_TAG_NAME="sdk-python-${PREVIOUS_RELEASE_TAG}"
# Verify the previous tag exists in Git before using --notes-start-tag.
# If a prior release published to PyPI but failed to create a GitHub
# release/tag, the tag won't exist — fall back to static notes to
# avoid failing gh release create after PyPI publish.
if git rev-parse "${PREVIOUS_TAG_NAME}" >/dev/null 2>&1; then
GH_RELEASE_ARGS+=(--generate-notes --notes-start-tag "${PREVIOUS_TAG_NAME}")
else
echo "::warning::Previous tag ${PREVIOUS_TAG_NAME} not found; skipping --generate-notes."
echo "See commit history for changes." >> "${NOTES_FILE}"
fi
else
# PREVIOUS_RELEASE_TAG is empty for preview/nightly (not computed)
# and for the very first stable release (no prior stable on PyPI).
# Skip --generate-notes to avoid including non-SDK commits.
echo "See commit history for changes." >> "${NOTES_FILE}"
fi
if [[ "${IS_NIGHTLY}" == "true" || "${IS_PREVIEW}" == "true" ]]; then
GH_RELEASE_ARGS+=(--prerelease)
fi
gh release create "${TAG_NAME}" \
--target "${RELEASE_TARGET_SHA}" \
--title "SDK Python Release ${RELEASE_TAG}" \
--notes-file "${NOTES_FILE}" \
"${GH_RELEASE_ARGS[@]}"
rm -f "${NOTES_FILE}"
- name: 'Delete prerelease release branch'
if: |-
${{ steps.vars.outputs.is_dry_run == 'false' && (steps.vars.outputs.is_nightly == 'true' || steps.vars.outputs.is_preview == 'true') }}
env:
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
run: |
set -euo pipefail
if git ls-remote --exit-code --heads origin "${BRANCH_NAME}" >/dev/null 2>&1; then
if ! git push origin --delete "${BRANCH_NAME}"; then
echo "::warning::Failed to delete prerelease release branch ${BRANCH_NAME}; release already exists, so remove the branch manually if it still exists."
fi
else
echo "No prerelease release branch to delete for ${BRANCH_NAME}."
fi
- name: 'Create PR to merge release branch into main'
if: |-
${{ steps.vars.outputs.is_dry_run == 'false' && steps.vars.outputs.is_nightly == 'false' && steps.vars.outputs.is_preview == 'false' && steps.persist_source.outputs.HAS_PERSISTED_SOURCE == 'true' }}
id: 'pr'
env:
GITHUB_TOKEN: '${{ secrets.CI_BOT_PAT }}'
RELEASE_BRANCH: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
run: |
set -euo pipefail
pr_url="$(gh pr list --head "${RELEASE_BRANCH}" --base main --json url --jq '.[0].url')"
if [[ -z "${pr_url}" ]]; then
pr_url="$(gh pr create \
--base main \
--head "${RELEASE_BRANCH}" \
--title "chore(release): sdk-python ${RELEASE_TAG}" \
--body "Automated release PR for sdk-python ${RELEASE_TAG}.")"
fi
echo "PR_URL=${pr_url}" >> "${GITHUB_OUTPUT}"
- name: 'Enable auto-merge for release PR'
if: |-
${{ steps.vars.outputs.is_dry_run == 'false' && steps.vars.outputs.is_nightly == 'false' && steps.vars.outputs.is_preview == 'false' && steps.persist_source.outputs.HAS_PERSISTED_SOURCE == 'true' }}
env:
GITHUB_TOKEN: '${{ secrets.CI_BOT_PAT }}'
PR_URL: '${{ steps.pr.outputs.PR_URL }}'
run: |
set -euo pipefail
gh pr merge "${PR_URL}" --merge --auto --delete-branch
- name: 'Create issue on failure'
if: |-
${{ failure() && github.event.inputs.dry_run != 'true' }}
env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
RELEASE_TAG: "${{ steps.version.outputs.RELEASE_TAG || 'N/A' }}"
PACKAGE_VERSION: "${{ steps.version.outputs.PACKAGE_VERSION || 'N/A' }}"
PUBLISH_CHANNEL: "${{ steps.version.outputs.PUBLISH_CHANNEL || 'N/A' }}"
RESUME_EXISTING_RELEASE: "${{ steps.version.outputs.RESUME_EXISTING_RELEASE || 'N/A' }}"
BRANCH_NAME: "${{ steps.release_branch.outputs.BRANCH_NAME || 'N/A' }}"
HAS_PERSISTED_SOURCE: "${{ steps.persist_source.outputs.HAS_PERSISTED_SOURCE || 'N/A' }}"
RELEASE_TARGET_SHA: "${{ steps.persist_source.outputs.RELEASE_TARGET_SHA || 'N/A' }}"
PR_URL: "${{ steps.pr.outputs.PR_URL || 'N/A' }}"
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
run: |
set -euo pipefail
BODY=$(cat <<BODY
The Python SDK release workflow failed.
| Field | Value |
|-------|-------|
| Release Tag | \`${RELEASE_TAG}\` |
| Package Version | \`${PACKAGE_VERSION}\` |
| Publish Channel | \`${PUBLISH_CHANNEL}\` |
| Resume Existing | \`${RESUME_EXISTING_RELEASE}\` |
| Release Branch | \`${BRANCH_NAME}\` |
| Persisted Source | \`${HAS_PERSISTED_SOURCE}\` |
| Target SHA | \`${RELEASE_TARGET_SHA}\` |
| PR URL | ${PR_URL} |
See the full run for details: ${DETAILS_URL}
BODY
)
gh issue create \
--repo "${GITHUB_REPOSITORY}" \
--title "Python SDK release failed for ${RELEASE_TAG} on $(date +'%Y-%m-%d')" \
--body "${BODY}"