- Enhanced the installation process by creating a new virtual environment for Python dependencies, ensuring a clean setup. - Added environment variables to disable pip version checks and input prompts. - Upgraded pip, setuptools, and wheel, and specified exact versions for requests and requests-toolbelt. - Included checks for installed packages to verify successful installation.
708 lines
23 KiB
YAML
708 lines
23 KiB
YAML
name: Publish Gitee Release
|
||
|
||
on:
|
||
workflow_run:
|
||
workflows: ["Build and Release"]
|
||
types: [completed]
|
||
|
||
workflow_dispatch:
|
||
inputs:
|
||
tag:
|
||
description: "Release tag, for example v1.1.14"
|
||
required: true
|
||
type: string
|
||
upload_small_assets:
|
||
description: "Upload assets smaller than MAX_GITEE_ASSET_MB to Gitee"
|
||
required: false
|
||
default: "true"
|
||
type: choice
|
||
options:
|
||
- "true"
|
||
- "false"
|
||
|
||
concurrency:
|
||
group: gitee-release-${{ github.event.workflow_run.id || inputs.tag || github.run_id }}
|
||
cancel-in-progress: false
|
||
|
||
permissions:
|
||
contents: read
|
||
|
||
env:
|
||
GITEE_OWNER: ${{ vars.GITEE_OWNER }}
|
||
GITEE_REPO: ${{ vars.GITEE_REPO }}
|
||
GITEE_API_BASE: ${{ vars.GITEE_API_BASE || 'https://gitee.com/api/v5' }}
|
||
MAX_GITEE_ASSET_MB: ${{ vars.MAX_GITEE_ASSET_MB || '99' }}
|
||
R2_PUBLIC_BASE_URL: ${{ vars.R2_PUBLIC_BASE_URL }}
|
||
UPLOAD_SMALL_ASSETS: ${{ inputs.upload_small_assets || 'true' }}
|
||
FAIL_ON_GITEE_ASSET_UPLOAD_ERROR: "false"
|
||
|
||
jobs:
|
||
publish-gitee-release:
|
||
name: Create or update Gitee Release
|
||
if: >-
|
||
${{
|
||
github.event_name == 'workflow_dispatch' ||
|
||
(
|
||
github.event_name == 'workflow_run' &&
|
||
github.event.workflow_run.conclusion == 'success'
|
||
)
|
||
}}
|
||
runs-on: [self-hosted, linux, x64, gitee-release]
|
||
|
||
steps:
|
||
- name: Resolve release tag
|
||
env:
|
||
MANUAL_TAG: ${{ inputs.tag }}
|
||
UPSTREAM_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
|
||
if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
|
||
TAG="${MANUAL_TAG}"
|
||
else
|
||
HEAD_SHA="${UPSTREAM_HEAD_SHA}"
|
||
|
||
if [[ -z "$HEAD_SHA" ]]; then
|
||
echo "::error::Upstream workflow head SHA is empty"
|
||
exit 1
|
||
fi
|
||
|
||
echo "Resolving release tag for upstream commit: ${HEAD_SHA}"
|
||
|
||
TAG="$(
|
||
git ls-remote --tags "https://github.com/${GITHUB_REPOSITORY}.git" |
|
||
awk -v sha="$HEAD_SHA" '
|
||
$1 == sha {
|
||
ref = $2
|
||
sub(/^refs\/tags\//, "", ref)
|
||
sub(/\^\{\}$/, "", ref)
|
||
print ref
|
||
}
|
||
' |
|
||
sort -Vu |
|
||
tail -n 1
|
||
)"
|
||
fi
|
||
|
||
if [[ -z "$TAG" ]]; then
|
||
echo "::error::Unable to resolve release tag"
|
||
echo "::error::For workflow_run, make sure Build and Release runs against the tagged commit."
|
||
exit 1
|
||
fi
|
||
|
||
echo "TAG=$TAG" >> "$GITHUB_ENV"
|
||
echo "Publishing Gitee release for tag: $TAG"
|
||
|
||
- name: Validate Gitee config
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
|
||
if [[ -z "${GITEE_OWNER}" ]]; then
|
||
echo "::error::GITEE_OWNER is empty. Please set it in GitHub repository Variables."
|
||
exit 1
|
||
fi
|
||
|
||
if [[ -z "${GITEE_REPO}" ]]; then
|
||
echo "::error::GITEE_REPO is empty. Please set it in GitHub repository Variables."
|
||
exit 1
|
||
fi
|
||
|
||
echo "Gitee repository: ${GITEE_OWNER}/${GITEE_REPO}"
|
||
|
||
- name: Prepare clean workspace
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
|
||
rm -rf release-assets
|
||
rm -f github-release.json github-release.json.tmp
|
||
rm -rf "$RUNNER_TEMP/gitee-release-venv"
|
||
mkdir -p release-assets
|
||
|
||
- name: Wait for GitHub release assets
|
||
env:
|
||
GH_TOKEN: ${{ github.token }}
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
|
||
echo "Waiting for GitHub release and assets: ${TAG}"
|
||
|
||
for i in {1..30}; do
|
||
rm -f github-release.json.tmp
|
||
|
||
if gh release view "$TAG" \
|
||
--repo "$GITHUB_REPOSITORY" \
|
||
--json tagName,name,body,isPrerelease,isDraft,url,targetCommitish,assets \
|
||
> github-release.json.tmp; then
|
||
|
||
IS_DRAFT="$(jq -r '.isDraft' github-release.json.tmp)"
|
||
ASSET_COUNT="$(jq '.assets | length' github-release.json.tmp)"
|
||
|
||
echo "GitHub release found: draft=${IS_DRAFT}, assets=${ASSET_COUNT}"
|
||
|
||
if [[ "$IS_DRAFT" == "false" && "$ASSET_COUNT" -gt 0 ]]; then
|
||
mv github-release.json.tmp github-release.json
|
||
|
||
echo "GitHub release metadata:"
|
||
jq '{
|
||
tagName,
|
||
name,
|
||
isPrerelease,
|
||
isDraft,
|
||
url,
|
||
targetCommitish,
|
||
assetCount: (.assets | length)
|
||
}' github-release.json
|
||
|
||
exit 0
|
||
fi
|
||
else
|
||
echo "GitHub release ${TAG} is not available yet."
|
||
fi
|
||
|
||
echo "Release assets are not ready, waiting 10s... ($i/30)"
|
||
sleep 10
|
||
done
|
||
|
||
echo "::error::GitHub release ${TAG} did not expose any assets after waiting."
|
||
echo "::error::Make sure Build and Release uploads all assets before the workflow completes."
|
||
exit 1
|
||
|
||
- name: Download GitHub release assets
|
||
env:
|
||
GH_TOKEN: ${{ github.token }}
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
|
||
gh release download "$TAG" \
|
||
--repo "$GITHUB_REPOSITORY" \
|
||
--dir release-assets \
|
||
--clobber
|
||
|
||
ASSET_COUNT="$(find release-assets -maxdepth 1 -type f | wc -l)"
|
||
|
||
if [[ "$ASSET_COUNT" -eq 0 ]]; then
|
||
echo "::error::No GitHub release assets were downloaded for ${TAG}"
|
||
exit 1
|
||
fi
|
||
|
||
echo "Downloaded assets (${ASSET_COUNT}):"
|
||
find release-assets -maxdepth 1 -type f -printf '%f %s bytes\n' | sort
|
||
|
||
- name: Wait for Gitee mirror tag
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
|
||
echo "Waiting for Gitee mirror tag: ${TAG}"
|
||
|
||
for i in {1..30}; do
|
||
if git ls-remote --tags \
|
||
"https://gitee.com/${GITEE_OWNER}/${GITEE_REPO}.git" \
|
||
"refs/tags/${TAG}" |
|
||
grep -q "refs/tags/${TAG}$"; then
|
||
echo "Gitee tag found: ${TAG}"
|
||
exit 0
|
||
fi
|
||
|
||
echo "Gitee tag ${TAG} not found yet, waiting 20s... ($i/30)"
|
||
sleep 20
|
||
done
|
||
|
||
echo "::error::Gitee tag ${TAG} was not found after waiting."
|
||
echo "::error::Make sure GitHub -> Gitee mirror has synced tags."
|
||
exit 1
|
||
|
||
- name: Install Python dependencies
|
||
shell: bash
|
||
env:
|
||
PIP_DISABLE_PIP_VERSION_CHECK: "1"
|
||
PIP_NO_INPUT: "1"
|
||
run: |
|
||
set -euo pipefail
|
||
|
||
VENV_DIR="$RUNNER_TEMP/gitee-release-venv"
|
||
PYTHON_BIN="$VENV_DIR/bin/python"
|
||
|
||
# 防止自托管 Runner 残留损坏或不完整的虚拟环境
|
||
rm -rf "$VENV_DIR"
|
||
python3 -m venv "$VENV_DIR"
|
||
|
||
echo "Python:"
|
||
"$PYTHON_BIN" --version
|
||
|
||
echo "Effective pip configuration:"
|
||
"$PYTHON_BIN" -m pip config debug || true
|
||
|
||
"$PYTHON_BIN" -m pip install \
|
||
--upgrade \
|
||
--no-cache-dir \
|
||
--index-url "https://pypi.org/simple" \
|
||
pip setuptools wheel
|
||
|
||
"$PYTHON_BIN" -m pip install \
|
||
--upgrade \
|
||
--no-cache-dir \
|
||
--index-url "https://pypi.org/simple" \
|
||
"requests==2.34.2" \
|
||
"requests-toolbelt==1.0.0"
|
||
|
||
"$PYTHON_BIN" -m pip check
|
||
|
||
echo "PYTHON_BIN=$PYTHON_BIN" >> "$GITHUB_ENV"
|
||
|
||
- name: Create or update Gitee release and upload assets
|
||
env:
|
||
GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }}
|
||
shell: bash
|
||
run: |
|
||
"$PYTHON_BIN" <<'PY'
|
||
import json
|
||
import mimetypes
|
||
import os
|
||
import pathlib
|
||
import sys
|
||
import time
|
||
import urllib.parse
|
||
|
||
import requests
|
||
from requests_toolbelt.multipart.encoder import MultipartEncoder, MultipartEncoderMonitor
|
||
|
||
|
||
def require_env(name: str) -> str:
|
||
value = os.environ.get(name, "").strip()
|
||
if not value:
|
||
print(f"::error::{name} is required")
|
||
sys.exit(1)
|
||
return value
|
||
|
||
|
||
token = require_env("GITEE_TOKEN")
|
||
owner = require_env("GITEE_OWNER")
|
||
repo = require_env("GITEE_REPO")
|
||
tag = require_env("TAG")
|
||
|
||
api_base = os.environ.get("GITEE_API_BASE", "https://gitee.com/api/v5").rstrip("/")
|
||
r2_public_base_url = os.environ.get("R2_PUBLIC_BASE_URL", "").strip().rstrip("/")
|
||
max_mb = float(os.environ.get("MAX_GITEE_ASSET_MB", "99"))
|
||
max_bytes = int(max_mb * 1024 * 1024)
|
||
upload_small_assets = os.environ.get("UPLOAD_SMALL_ASSETS", "true").lower() == "true"
|
||
fail_on_upload_error = os.environ.get("FAIL_ON_GITEE_ASSET_UPLOAD_ERROR", "false").lower() == "true"
|
||
|
||
assets_dir = pathlib.Path("release-assets")
|
||
release_json_path = pathlib.Path("github-release.json")
|
||
|
||
if not release_json_path.exists():
|
||
print("::error::github-release.json not found")
|
||
sys.exit(1)
|
||
|
||
release = json.loads(release_json_path.read_text(encoding="utf-8"))
|
||
|
||
release_name = release.get("name") or tag
|
||
github_body = release.get("body") or ""
|
||
is_prerelease = bool(release.get("isPrerelease"))
|
||
target_commitish = release.get("targetCommitish") or ""
|
||
|
||
owner_q = urllib.parse.quote(owner, safe="")
|
||
repo_q = urllib.parse.quote(repo, safe="")
|
||
tag_q = urllib.parse.quote(tag, safe="")
|
||
|
||
session = requests.Session()
|
||
|
||
|
||
def gitee_request(method: str, path: str, *, expected=(200, 201), allow_404=False, **kwargs):
|
||
url = f"{api_base}{path}"
|
||
params = kwargs.pop("params", {}) or {}
|
||
params["access_token"] = token
|
||
|
||
response = session.request(
|
||
method,
|
||
url,
|
||
params=params,
|
||
timeout=120,
|
||
**kwargs,
|
||
)
|
||
|
||
if allow_404 and response.status_code == 404:
|
||
return None
|
||
|
||
if response.status_code not in expected:
|
||
print(f"::error::Gitee API failed: {method} {path} -> HTTP {response.status_code}")
|
||
print(response.text[:3000])
|
||
sys.exit(1)
|
||
|
||
if response.text.strip():
|
||
try:
|
||
return response.json()
|
||
except Exception:
|
||
return response.text
|
||
|
||
return None
|
||
|
||
|
||
def human_size(size: int) -> str:
|
||
units = ["B", "KB", "MB", "GB"]
|
||
value = float(size)
|
||
|
||
for unit in units:
|
||
if value < 1024 or unit == units[-1]:
|
||
if unit == "B":
|
||
return f"{int(value)} {unit}"
|
||
return f"{value:.2f} {unit}"
|
||
value /= 1024
|
||
|
||
return f"{size} B"
|
||
|
||
def make_upload_progress_callback(filename: str, total_bytes: int):
|
||
started_at = time.monotonic()
|
||
state = {
|
||
"last_percent": -1,
|
||
"last_report_at": 0.0,
|
||
}
|
||
|
||
def callback(monitor: MultipartEncoderMonitor):
|
||
if total_bytes <= 0:
|
||
return
|
||
|
||
now = time.monotonic()
|
||
uploaded = min(monitor.bytes_read, total_bytes)
|
||
percent = int(uploaded * 100 / total_bytes)
|
||
|
||
should_report = (
|
||
percent == 0
|
||
or percent == 100
|
||
or percent >= state["last_percent"] + 10
|
||
or now - state["last_report_at"] >= 15
|
||
)
|
||
|
||
if not should_report:
|
||
return
|
||
|
||
elapsed = max(now - started_at, 0.001)
|
||
speed = uploaded / elapsed
|
||
|
||
print(
|
||
f"Upload progress: {filename}: "
|
||
f"{percent}% "
|
||
f"({human_size(uploaded)}/{human_size(total_bytes)}, "
|
||
f"{human_size(int(speed))}/s)",
|
||
flush=True,
|
||
)
|
||
|
||
state["last_percent"] = percent
|
||
state["last_report_at"] = now
|
||
|
||
return callback
|
||
|
||
def build_r2_url(filename: str) -> str:
|
||
if not r2_public_base_url:
|
||
return ""
|
||
return (
|
||
f"{r2_public_base_url}/"
|
||
f"{urllib.parse.quote(tag, safe='')}/"
|
||
f"{urllib.parse.quote(filename, safe='')}"
|
||
)
|
||
|
||
|
||
asset_files = []
|
||
if assets_dir.exists():
|
||
asset_files = sorted(
|
||
[p for p in assets_dir.iterdir() if p.is_file()],
|
||
key=lambda p: p.name.lower(),
|
||
)
|
||
|
||
|
||
def build_release_body(
|
||
*,
|
||
uploadable_assets,
|
||
skipped_assets,
|
||
failed_assets,
|
||
no_assets=False,
|
||
uploading=False,
|
||
) -> str:
|
||
body = github_body.rstrip()
|
||
|
||
lines = []
|
||
lines.append("")
|
||
lines.append("---")
|
||
lines.append("")
|
||
lines.append("## 下载")
|
||
lines.append("")
|
||
|
||
if uploading:
|
||
lines.append("> 正在同步发行版附件,请稍后刷新页面。")
|
||
return body + "\n" + "\n".join(lines) + "\n"
|
||
|
||
if no_assets:
|
||
lines.append("- 本次 GitHub Release 未检测到附件。")
|
||
return body + "\n" + "\n".join(lines) + "\n"
|
||
|
||
if uploadable_assets:
|
||
lines.append("> 安装包已同步到本发行版附件区,请在本页附件区下载。")
|
||
elif not upload_small_assets:
|
||
lines.append("> 本次未上传附件到 Gitee Release 附件区。")
|
||
else:
|
||
lines.append("> 本次没有可上传到 Gitee Release 附件区的附件。")
|
||
|
||
fallback_lines = []
|
||
|
||
for file_path in skipped_assets:
|
||
size_text = human_size(file_path.stat().st_size)
|
||
r2_url = build_r2_url(file_path.name)
|
||
|
||
if r2_url:
|
||
fallback_lines.append(f"- [{file_path.name}]({r2_url}) `{size_text}`")
|
||
else:
|
||
fallback_lines.append(f"- `{file_path.name}` `{size_text}`(未上传到 Gitee 附件区)")
|
||
|
||
for file_path, reason in failed_assets:
|
||
size_text = human_size(file_path.stat().st_size)
|
||
r2_url = build_r2_url(file_path.name)
|
||
|
||
if r2_url:
|
||
fallback_lines.append(f"- [{file_path.name}]({r2_url}) `{size_text}`(Gitee 附件同步失败)")
|
||
else:
|
||
fallback_lines.append(f"- `{file_path.name}` `{size_text}`(Gitee 附件同步失败:{reason})")
|
||
|
||
if fallback_lines:
|
||
lines.append("")
|
||
lines.append("### 备用下载")
|
||
lines.append("")
|
||
lines.extend(fallback_lines)
|
||
|
||
return body + "\n" + "\n".join(lines) + "\n"
|
||
|
||
|
||
existing = gitee_request(
|
||
"GET",
|
||
f"/repos/{owner_q}/{repo_q}/releases/tags/{tag_q}",
|
||
expected=(200,),
|
||
allow_404=True,
|
||
)
|
||
|
||
initial_body = build_release_body(
|
||
uploadable_assets=[],
|
||
skipped_assets=[],
|
||
failed_assets=[],
|
||
no_assets=not bool(asset_files),
|
||
uploading=bool(asset_files),
|
||
)
|
||
|
||
if existing:
|
||
release_id = existing.get("id")
|
||
print(f"Updating existing Gitee release: id={release_id}, tag={tag}")
|
||
|
||
updated = gitee_request(
|
||
"PATCH",
|
||
f"/repos/{owner_q}/{repo_q}/releases/{release_id}",
|
||
data={
|
||
"tag_name": tag,
|
||
"name": release_name,
|
||
"body": initial_body,
|
||
"prerelease": str(is_prerelease).lower(),
|
||
},
|
||
expected=(200, 201),
|
||
)
|
||
|
||
if isinstance(updated, dict) and updated.get("id"):
|
||
release_id = updated["id"]
|
||
else:
|
||
print(f"Creating Gitee release: tag={tag}")
|
||
|
||
payload = {
|
||
"tag_name": tag,
|
||
"name": release_name,
|
||
"body": initial_body,
|
||
"prerelease": str(is_prerelease).lower(),
|
||
}
|
||
|
||
if target_commitish:
|
||
payload["target_commitish"] = target_commitish
|
||
|
||
created = gitee_request(
|
||
"POST",
|
||
f"/repos/{owner_q}/{repo_q}/releases",
|
||
data=payload,
|
||
expected=(200, 201),
|
||
)
|
||
|
||
if not isinstance(created, dict) or not created.get("id"):
|
||
print("::error::Gitee release was created but release id was not returned")
|
||
print(created)
|
||
sys.exit(1)
|
||
|
||
release_id = created["id"]
|
||
|
||
print(f"Gitee release id: {release_id}")
|
||
|
||
if not asset_files:
|
||
print("No assets to upload.")
|
||
|
||
final_body = build_release_body(
|
||
uploadable_assets=[],
|
||
skipped_assets=[],
|
||
failed_assets=[],
|
||
no_assets=True,
|
||
uploading=False,
|
||
)
|
||
|
||
gitee_request(
|
||
"PATCH",
|
||
f"/repos/{owner_q}/{repo_q}/releases/{release_id}",
|
||
data={
|
||
"tag_name": tag,
|
||
"name": release_name,
|
||
"body": final_body,
|
||
"prerelease": str(is_prerelease).lower(),
|
||
},
|
||
expected=(200, 201),
|
||
)
|
||
|
||
sys.exit(0)
|
||
|
||
uploadable_files = []
|
||
skipped_files = []
|
||
|
||
if upload_small_assets:
|
||
for file_path in asset_files:
|
||
size = file_path.stat().st_size
|
||
|
||
if size <= max_bytes:
|
||
uploadable_files.append(file_path)
|
||
else:
|
||
skipped_files.append(file_path)
|
||
else:
|
||
skipped_files = asset_files[:]
|
||
|
||
for file_path in skipped_files:
|
||
print(
|
||
f"Skip asset: {file_path.name} "
|
||
f"({human_size(file_path.stat().st_size)}, threshold={max_mb} MB, upload_small_assets={upload_small_assets})"
|
||
)
|
||
|
||
uploaded_or_existing_files = []
|
||
failed_files = []
|
||
|
||
|
||
def upload_asset(file_path: pathlib.Path) -> tuple[bool, str]:
|
||
filename = file_path.name
|
||
content_type = mimetypes.guess_type(filename)[0] or "application/octet-stream"
|
||
|
||
for attempt in range(1, 4):
|
||
print(f"Uploading asset to Gitee: {filename}, attempt {attempt}/3")
|
||
|
||
try:
|
||
with file_path.open("rb") as file_handle:
|
||
encoder = MultipartEncoder(
|
||
fields={
|
||
"name": filename,
|
||
"file": (filename, file_handle, content_type),
|
||
}
|
||
)
|
||
|
||
monitor = MultipartEncoderMonitor(
|
||
encoder,
|
||
make_upload_progress_callback(filename, encoder.len),
|
||
)
|
||
|
||
response = session.post(
|
||
f"{api_base}/repos/{owner_q}/{repo_q}/releases/{release_id}/attach_files",
|
||
params={"access_token": token},
|
||
data=monitor,
|
||
headers={
|
||
"Content-Type": monitor.content_type,
|
||
},
|
||
timeout=300,
|
||
)
|
||
except Exception as exc:
|
||
reason = str(exc)
|
||
|
||
if attempt < 3:
|
||
print(f"::warning::Upload failed with exception: {reason}. Retrying...")
|
||
time.sleep(5 * attempt)
|
||
continue
|
||
|
||
return False, reason
|
||
|
||
if response.status_code in (200, 201):
|
||
print(f"Uploaded: {filename}")
|
||
return True, "uploaded"
|
||
|
||
text = response.text or ""
|
||
lowered = text.lower()
|
||
|
||
duplicate_markers = [
|
||
"already",
|
||
"exist",
|
||
"duplicate",
|
||
"taken",
|
||
"已存在",
|
||
"重复",
|
||
]
|
||
|
||
if response.status_code in (400, 409, 422) and any(marker in lowered or marker in text for marker in duplicate_markers):
|
||
print(f"::warning::Asset may already exist, treat as available: {filename}")
|
||
print(text[:1000])
|
||
return True, "already_exists"
|
||
|
||
if attempt < 3:
|
||
print(f"::warning::Upload failed: HTTP {response.status_code}. Retrying...")
|
||
print(text[:1000])
|
||
time.sleep(5 * attempt)
|
||
continue
|
||
|
||
return False, f"HTTP {response.status_code}: {text[:1000]}"
|
||
|
||
|
||
for file_path in uploadable_files:
|
||
ok, reason = upload_asset(file_path)
|
||
|
||
if ok:
|
||
uploaded_or_existing_files.append(file_path)
|
||
else:
|
||
message = f"Upload failed permanently: {file_path.name}, {reason}"
|
||
|
||
if fail_on_upload_error:
|
||
print(f"::error::{message}")
|
||
sys.exit(1)
|
||
|
||
print(f"::warning::{message}")
|
||
failed_files.append((file_path, reason))
|
||
|
||
final_body = build_release_body(
|
||
uploadable_assets=uploaded_or_existing_files,
|
||
skipped_assets=skipped_files,
|
||
failed_assets=failed_files,
|
||
no_assets=False,
|
||
uploading=False,
|
||
)
|
||
|
||
gitee_request(
|
||
"PATCH",
|
||
f"/repos/{owner_q}/{repo_q}/releases/{release_id}",
|
||
data={
|
||
"tag_name": tag,
|
||
"name": release_name,
|
||
"body": final_body,
|
||
"prerelease": str(is_prerelease).lower(),
|
||
},
|
||
expected=(200, 201),
|
||
)
|
||
|
||
print("Gitee release publish completed.")
|
||
print(f"Uploaded or existing assets: {len(uploaded_or_existing_files)}")
|
||
print(f"Skipped assets: {len(skipped_files)}")
|
||
print(f"Failed assets: {len(failed_files)}")
|
||
PY
|
||
|
||
- name: Cleanup release assets
|
||
if: always()
|
||
shell: bash
|
||
run: |
|
||
rm -rf release-assets
|
||
rm -f github-release.json
|
||
rm -rf "$RUNNER_TEMP/gitee-release-venv" |