mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-18 01:11:55 +08:00
build: release sync to gitee
This commit is contained in:
parent
b1f2eee66a
commit
abcbb811c8
1
.github/workflows/push_image.yml
vendored
1
.github/workflows/push_image.yml
vendored
@ -16,7 +16,6 @@ on:
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
275
.github/workflows/release_sync_gitee.py
vendored
Normal file
275
.github/workflows/release_sync_gitee.py
vendored
Normal file
@ -0,0 +1,275 @@
|
||||
#!/usr/bin/env python3
|
||||
import logging
|
||||
import json
|
||||
import mimetypes
|
||||
import tempfile
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import shutil
|
||||
import time
|
||||
from urllib import request
|
||||
from urllib.error import HTTPError
|
||||
|
||||
GITHUB_REPO = "certimate-go/certimate"
|
||||
GITEE_REPO = "certimate-go/certimate"
|
||||
GITEE_TOKEN = os.getenv("GITEE_TOKEN", "")
|
||||
|
||||
SYNC_MARKER = "SYNCING FROM GITHUB, PLEASE WAIT ..."
|
||||
TEMP_DIR = tempfile.mkdtemp()
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
def do_httpreq(url, method="GET", headers=None, data=None):
|
||||
req = request.Request(url, data=data, method=method)
|
||||
headers = headers or {}
|
||||
for key, value in headers.items():
|
||||
req.add_header(key, value)
|
||||
|
||||
try:
|
||||
with request.urlopen(req) as resp:
|
||||
resp_data = resp.read().decode("utf-8")
|
||||
if resp_data:
|
||||
try:
|
||||
return json.loads(resp_data)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
return None
|
||||
except HTTPError as e:
|
||||
errmsg = ""
|
||||
if e.readable():
|
||||
try:
|
||||
errmsg = e.read().decode('utf-8')
|
||||
errmsg = errmsg.replace("\r", "\\r").replace("\n", "\\n")
|
||||
except:
|
||||
pass
|
||||
logging.error(f"Error occurred when sending request: status={e.status}, response={errmsg}")
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
def get_github_stable_release():
|
||||
page = 1
|
||||
while True:
|
||||
releases = do_httpreq(
|
||||
url=f"https://api.github.com/repos/{GITHUB_REPO}/releases?page={page}&per_page=100",
|
||||
headers={"Accept": "application/vnd.github+json"},
|
||||
)
|
||||
if not releases or len(releases) == 0:
|
||||
break
|
||||
|
||||
for release in releases:
|
||||
release_name = release.get("name", "")
|
||||
if re.match(r"^v[0-9]", release_name):
|
||||
if any(
|
||||
x in release_name
|
||||
for x in ["alpha", "beta", "rc", "preview", "test", "unstable"]
|
||||
):
|
||||
continue
|
||||
return release
|
||||
|
||||
page += 1
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_gitee_release_list():
|
||||
page = 1
|
||||
list = []
|
||||
while True:
|
||||
releases = do_httpreq(
|
||||
url=f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases?access_token={GITEE_TOKEN}&page={page}&per_page=100",
|
||||
)
|
||||
if not releases or len(releases) == 0:
|
||||
break
|
||||
|
||||
list.extend(releases)
|
||||
page += 1
|
||||
|
||||
return list
|
||||
|
||||
|
||||
def get_gitee_release_by_tag(tag_name):
|
||||
releases = get_gitee_release_list()
|
||||
for release in releases:
|
||||
if release.get("tag_name") == tag_name:
|
||||
return release
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def delete_gitee_release(release_info):
|
||||
if not release_info:
|
||||
raise ValueError("Release info is invalid")
|
||||
|
||||
release_id = release_info.get("id", "")
|
||||
release_name = release_info.get("tag_name", "")
|
||||
if not release_id:
|
||||
raise ValueError("Release ID is missing")
|
||||
|
||||
attachpage = 1
|
||||
attachfiles = []
|
||||
while True:
|
||||
releases = do_httpreq(
|
||||
url=f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases/{release_id}/attach_files?access_token={GITEE_TOKEN}&page={attachpage}&per_page=100",
|
||||
)
|
||||
if not releases or len(releases) == 0:
|
||||
break
|
||||
|
||||
attachfiles.extend(releases)
|
||||
attachpage += 1
|
||||
|
||||
for attachfile in attachfiles:
|
||||
attachfile_id = attachfile.get("id")
|
||||
attachfile_name = attachfile.get("name")
|
||||
logging.info("Trying to delete Gitee attach file: %s/%s", release_name, attachfile_name)
|
||||
do_httpreq(
|
||||
url=f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases/{release_id}/attach_files/{attachfile_id}?access_token={GITEE_TOKEN}",
|
||||
method="DELETE",
|
||||
)
|
||||
|
||||
logging.info("Trying to delete Gitee release: %s", release_name)
|
||||
do_httpreq(
|
||||
url=f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases/{release_id}?access_token={GITEE_TOKEN}",
|
||||
method="DELETE",
|
||||
)
|
||||
|
||||
|
||||
def create_gitee_release(name, tag, body, prerelease, gh_assets):
|
||||
release_info = do_httpreq(
|
||||
f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases?access_token={GITEE_TOKEN}",
|
||||
method="POST",
|
||||
headers={"Content-Type": "application/json"},
|
||||
data=json.dumps({
|
||||
"tag_name": tag,
|
||||
"name": name,
|
||||
"body": SYNC_MARKER,
|
||||
"prerelease": prerelease,
|
||||
"target_commitish": "",
|
||||
}).encode("utf-8"),
|
||||
)
|
||||
|
||||
if not release_info or "id" not in release_info:
|
||||
return None
|
||||
logging.info("Gitee release created")
|
||||
|
||||
release_id = release_info["id"]
|
||||
|
||||
assets_dir = os.path.join(TEMP_DIR, "assets")
|
||||
os.makedirs(assets_dir, exist_ok=True)
|
||||
|
||||
gh_assets = gh_assets or []
|
||||
for asset in gh_assets:
|
||||
logging.info("Tring to download asset from GitHub: %s", asset["name"])
|
||||
|
||||
opener = request.build_opener()
|
||||
request.install_opener(opener)
|
||||
download_ts = time.time()
|
||||
download_url = asset.get("browser_download_url")
|
||||
download_path = os.path.join(assets_dir, asset["name"])
|
||||
def _hook(blocknum, blocksize, totalsize):
|
||||
nonlocal download_ts
|
||||
TIMESPAN = 5 # print progress every 5sec
|
||||
ts = time.time()
|
||||
pct = min(round(100 * blocknum * blocksize / totalsize, 2), 100)
|
||||
if (ts - download_ts < TIMESPAN) and (pct < 100):
|
||||
return
|
||||
download_ts = ts
|
||||
logging.info(f"Downloading {download_url} >>> {pct}%")
|
||||
|
||||
# request.urlretrieve(download_url, download_path, _hook)
|
||||
|
||||
for asset in gh_assets:
|
||||
logging.info("Tring to upload asset to Gitee: %s", asset["name"])
|
||||
|
||||
boundary = '----boundary' + ''.join(random.choice('0123456789abcdef') for _ in range(16))
|
||||
print(f"Using boundary: {boundary}")
|
||||
with open(os.path.join(assets_dir, asset["name"]), 'rb') as f:
|
||||
attachfile_mime = mimetypes.guess_type(asset["name"])[0] or 'application/octet-stream'
|
||||
attachfile_req = []
|
||||
attachfile_req.append(f"--{boundary}")
|
||||
attachfile_req.append(f'Content-Disposition: form-data; name="file"; filename="{asset["name"]}"')
|
||||
attachfile_req.append(f"Content-Type: {attachfile_mime}")
|
||||
attachfile_req.append("")
|
||||
attachfile_req.append(f.read().decode('latin-1'))
|
||||
attachfile_req.append(f"--{boundary}--")
|
||||
attachfile_req.append("")
|
||||
attachfile_req = "\r\n".join(attachfile_req).encode('latin-1')
|
||||
|
||||
do_httpreq(
|
||||
f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases/{release_id}/attach_files?access_token={GITEE_TOKEN}",
|
||||
method="POST",
|
||||
headers={'Content-Type': f'multipart/form-data; boundary={boundary}'},
|
||||
data=attachfile_req,
|
||||
)
|
||||
logging.info("Asset uploaded: %s", asset["name"])
|
||||
|
||||
release_info = do_httpreq(
|
||||
f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases/{release_id}?access_token={GITEE_TOKEN}",
|
||||
method="PATCH",
|
||||
headers={"Content-Type": "application/json"},
|
||||
data=json.dumps({
|
||||
"tag_name": tag,
|
||||
"name": name,
|
||||
"body": f"**此发行版同步自 GitHub,完整变更日志请访问 https://github.com/{GITHUB_REPO}/releases/{tag} 查看。**\n\n**因 Gitee 存储空间容量有限,仅能保留最新一个发行版,如需其余版本请访问 GitHub 获取。**\n\n---\n\n" + body,
|
||||
"prerelease": prerelease,
|
||||
}).encode("utf-8"),
|
||||
)
|
||||
logging.info("Gitee release updated")
|
||||
return release_info
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
# 获取 GitHub 最新稳定发行版
|
||||
github_release = get_github_stable_release()
|
||||
if not github_release:
|
||||
logging.warning("GitHub stable release not found. Foget to release?")
|
||||
return
|
||||
else:
|
||||
logging.info("GitHub stable release found: %s", github_release.get('name'))
|
||||
|
||||
# 提取稳定版的信息
|
||||
release_name = github_release.get("name")
|
||||
release_tag = github_release.get("tag_name")
|
||||
release_body = github_release.get("body")
|
||||
release_prerelease = github_release.get("prerelease", False)
|
||||
release_assets = github_release.get("assets", [])
|
||||
|
||||
# 检查 Gitee 是否已有同名发行版
|
||||
gitee_release = get_gitee_release_by_tag(release_tag)
|
||||
if gitee_release and gitee_release.get("body") == SYNC_MARKER:
|
||||
logging.warning("Gitee syncing release found, cleaning up...")
|
||||
delete_gitee_release(gitee_release)
|
||||
elif gitee_release:
|
||||
logging.info("Gitee release already exists, exit.")
|
||||
return
|
||||
|
||||
# 同步发行版
|
||||
gitee_release = create_gitee_release(release_name, release_tag, release_body, release_prerelease, release_assets)
|
||||
if not gitee_release:
|
||||
logging.warning("Failed to create Gitee release.")
|
||||
return
|
||||
|
||||
# 清除历史发行版
|
||||
gitee_release_list = get_gitee_release_list()
|
||||
for release in gitee_release_list:
|
||||
if release.get("tag_name") == release_tag:
|
||||
continue
|
||||
else:
|
||||
delete_gitee_release(release)
|
||||
|
||||
logging.info("Sync release completed.")
|
||||
|
||||
except Exception as e:
|
||||
logging.fatal(str(e))
|
||||
|
||||
finally:
|
||||
if os.path.exists(TEMP_DIR):
|
||||
shutil.rmtree(TEMP_DIR)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
27
.github/workflows/release_sync_gitee.yml
vendored
Normal file
27
.github/workflows/release_sync_gitee.yml
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
name: Release Sync to Gitee
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published, unpublished, deleted]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
sync-to-gitee:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python3
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Run script
|
||||
env:
|
||||
GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }}
|
||||
run: |
|
||||
cd .github/workflows
|
||||
python ./release_sync_gitee.py
|
Loading…
x
Reference in New Issue
Block a user