qwen-code/.github/workflows/release-sdk-python.yml
jinye 03f66bada5
feat(sdk-python): add PyPI release workflow (#3685)
* feat(sdk-python): add pypi release workflow

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk-python): build cli before smoke test

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk-python): tighten release conflict handling

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk-python): harden python release workflow

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk-python): tighten stable release guards

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk-python): harden prerelease publish flow

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk-python): reuse release branches on rerun

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk-python): resume incomplete releases

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(release): tighten missing-release checks

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk-python): resume stable release reruns

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk-python): tighten release recovery guards

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(sdk-python): cover release version edge cases

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk-python): address release workflow review feedback

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* refactor(sdk-python): address review feedback on release version script

- Remove unreachable `if (type === 'stable')` branch in bumpVersion();
  the stable path was dead code since getVersion() throws for all
  stable conflicts before calling bumpVersion(). Move nightly conflict
  throw to the call site for symmetry.
- Rename getNextPatchBaseVersion → getNextBaseVersion to reflect that
  the function can return a prerelease base without incrementing patch.
- Add test for preview+nightly coexistence where nightly base is higher.

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

* fix(sdk-python): address remaining review feedback on release workflow

- Fix failure-issue gate to read github.event.inputs.dry_run directly
  instead of steps.vars.outputs.is_dry_run (which is empty when early
  steps fail). Add --repo flag for gh issue create when checkout failed.
- Add diagnostic state table to failure-issue body (RELEASE_TAG,
  PACKAGE_VERSION, PUBLISH_CHANNEL, RESUME_EXISTING_RELEASE, etc.)
- Fix release-notes error swallow: only silence release not found /
  Not Found / HTTP 404, emit :⚠️: for other gh release view errors.
- Improve validateVersion error messages to use human-readable format
  keys (X.Y.Z, X.Y.Z-preview.N) matching TS sibling convention.
- Filter fully-yanked versions in getAllVersionsFromPyPI.
- Add console.error log when stable is derived from nightly.
- Add bash regex guard for inputs.version to prevent shell injection.
- Use per-release-type concurrency groups (nightly/preview/stable).
- Add jq null-guard checks for all 6 field extractions.
- Remove misleading --follow-tags from git push (lightweight tags).

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

* fix(sdk-python): rename misleading test description

The test asserts that preview/nightly releases return empty
previousReleaseTag, but the name said "same-channel previous
release tags" which implied non-empty values.

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

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

- Remove -z check in extract_field() that blocked preview/nightly releases
  (previousReleaseTag is legitimately empty for non-stable releases)
- Use static environment.url since step outputs aren't available at job startup
- Use skip-existing for resumed PyPI publish to fill in missing artifacts
- Add AbortSignal.timeout(30s) to PyPI fetch to prevent indefinite hangs
- Add downgrade guard for stable_version_override
- Use GHA :⚠️: annotation instead of console.error for visibility
- Separate yanked/non-yanked version lists so conflict detection includes
  yanked versions (PyPI still reserves those slots)
- Filter current release from previousReleaseTag to avoid self-reference on resume
- Add tests for yanked conflict detection, downgrade guard, and resume previousReleaseTag

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk-python): address final review round on release version script

- Fix getNextBaseVersion() first-release skip: use pyproject.toml version
  directly when PyPI has no stable versions instead of unconditionally
  incrementing
- Fix getNextBaseVersion() off-by-one: change > to >= so equal prerelease
  base continues the existing line instead of incrementing patch
- Add :⚠️: annotation when preview auto-bumps due to orphan git
  tags (tag exists without PyPI version or GitHub release)
- Add set -euo pipefail to 5 workflow steps missing it: release_branch,
  persist_source, Create GitHub release, Delete prerelease branch, Create
  issue on failure
- Fix 2 existing tests affected by first-release change, add 4 new tests

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

* fix(sdk-python): use stderr for GHA warning annotations to avoid corrupting JSON stdout

console.log writes to stdout, which gets captured by VERSION_JSON=$(node ...)
in the workflow and corrupts the JSON output for jq. Switch to console.error
so :⚠️: annotations go to stderr (GHA recognizes workflow commands on
both streams). Also add set -euo pipefail to the "Get the version" step for
consistency with other workflow steps.

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

---------

Co-authored-by: jinye.djy <jinye.djy@alibaba-inc.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-05-04 21:07:21 +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 [[ "${IS_NIGHTLY}" == "true" || "${IS_PREVIEW}" == "true" ]]; then
PRERELEASE_FLAG="--prerelease"
else
PRERELEASE_FLAG=""
fi
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}"
if [[ -n "${PREVIOUS_RELEASE_TAG}" ]]; then
PREVIOUS_NOTES=$(gh release view "sdk-python-${PREVIOUS_RELEASE_TAG}" --json body -q '.body' 2>&1) || {
ERR_MSG="${PREVIOUS_NOTES}"
case "${ERR_MSG}" in
*"release not found"*|*"Not Found"*|*"HTTP 404"*)
PREVIOUS_NOTES='See commit history for changes.'
;;
*)
echo "::warning::Failed to fetch previous release notes: ${ERR_MSG}"
PREVIOUS_NOTES='See commit history for changes.'
;;
esac
}
printf '%s\n' "${PREVIOUS_NOTES}" >> "${NOTES_FILE}"
else
echo "See commit history for changes." >> "${NOTES_FILE}"
fi
gh release create "${TAG_NAME}" \
--target "${RELEASE_TARGET_SHA}" \
--title "SDK Python Release ${RELEASE_TAG}" \
--notes-file "${NOTES_FILE}" \
${PRERELEASE_FLAG}
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}"