Skip to content

v1.1.23: Add COS object storage upload functionality #32

v1.1.23: Add COS object storage upload functionality

v1.1.23: Add COS object storage upload functionality #32

name: Build and Publish Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
publish-linux:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Debug environment
run: |
echo "GitHub ref: ${{ github.ref }}"
echo "Tag name: ${{ github.ref_name }}"
echo "Repository: ${{ github.repository }}"
echo "Is tag push: ${{ startsWith(github.ref, 'refs/tags/') }}"
echo "GITHUB_TOKEN exists: ${{ secrets.GITHUB_TOKEN != '' }}"
node --version
npm --version
- name: Publish Linux (AppImage and deb)
run: npm run publish:linux
env:
GH_TOKEN: ${{ secrets.atriordsa }}
DEBUG: electron-builder
publish-windows:
runs-on: windows-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Debug environment
run: |
echo "GitHub ref: ${{ github.ref }}"
echo "Tag name: ${{ github.ref_name }}"
echo "Repository: ${{ github.repository }}"
echo "Is tag push: ${{ startsWith(github.ref, 'refs/tags/') }}"
echo "GITHUB_TOKEN exists: ${{ secrets.GITHUB_TOKEN != '' }}"
node --version
npm --version
- name: Publish Windows
run: npm run publish:win
env:
GH_TOKEN: ${{ secrets.atriordsa }}
DEBUG: electron-builder
publish-macos:
runs-on: macos-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Debug environment
run: |
echo "GitHub ref: ${{ github.ref }}"
echo "Tag name: ${{ github.ref_name }}"
echo "Repository: ${{ github.repository }}"
echo "Is tag push: ${{ startsWith(github.ref, 'refs/tags/') }}"
echo "GITHUB_TOKEN exists: ${{ secrets.GITHUB_TOKEN != '' }}"
node --version
npm --version
- name: Publish macOS
run: npm run publish:mac
env:
GH_TOKEN: ${{ secrets.atriordsa }}
DEBUG: electron-builder
cleanup-release:
runs-on: ubuntu-latest
needs: [publish-linux, publish-windows, publish-macos]
if: always() && (needs.publish-linux.result == 'success' || needs.publish-windows.result == 'success' || needs.publish-macos.result == 'success')
permissions:
contents: write
actions: read
steps:
- name: Debug GitHub token and permissions
run: |
echo "🔍 Debugging GitHub environment..."
echo "Repository: ${{ github.repository }}"
echo "Actor: ${{ github.actor }}"
echo "Token exists: ${{ secrets.atriordsa != '' }}"
echo "Token length: ${#GITHUB_TOKEN}"
# 尝试不同的 API 调用
echo "🧪 Testing different API endpoints..."
# 测试基本用户信息
echo "Testing /user endpoint:"
curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user | jq -r '.login // "Failed"' || echo "curl failed"
# 测试仓库信息
echo "Testing repository endpoint:"
curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/${{ github.repository }} | jq -r '.name // "Failed"' || echo "curl failed"
env:
GITHUB_TOKEN: ${{ secrets.atriordsa }}
- name: Clean unwanted files from release
run: |
set -e # 启用错误时退出
echo "🧹 Starting release cleanup for tag: ${{ github.ref_name }}"
# 直接使用 curl 而不是 gh CLI,因为 gh CLI 可能有权限问题
echo "🔗 Testing GitHub API with curl..."
# 验证环境变量
if [ -z "$GITHUB_TOKEN" ]; then
echo "❌ GITHUB_TOKEN not set"
exit 1
fi
# 测试 API 连接
USER_RESPONSE=$(curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user)
if ! echo "$USER_RESPONSE" | jq -e '.login' > /dev/null 2>&1; then
echo "❌ Cannot authenticate with GitHub API"
echo "Response: $USER_RESPONSE"
exit 1
fi
echo "✅ GitHub API authentication successful"
# 等待确保所有上传完成
echo "⏰ Waiting 30 seconds for uploads to complete..."
sleep 30
echo "📡 Getting release for tag: ${{ github.ref_name }}"
# 首先尝试通过标签获取 release,如果失败则从所有 releases 中查找
RELEASE_RESPONSE=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/repos/${{ github.repository }}/releases/tags/${{ github.ref_name }})
# 如果直接通过标签获取失败,则从所有 releases 中查找
if ! echo "$RELEASE_RESPONSE" | jq -e '.id' > /dev/null 2>&1; then
echo "⚠️ Direct tag lookup failed, searching in all releases..."
echo "Direct response: $RELEASE_RESPONSE"
ALL_RELEASES=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/repos/${{ github.repository }}/releases)
RELEASE_RESPONSE=$(echo "$ALL_RELEASES" | jq ".[] | select(.tag_name == \"${{ github.ref_name }}\")")
if ! echo "$RELEASE_RESPONSE" | jq -e '.id' > /dev/null 2>&1; then
echo "❌ Could not find release for tag ${{ github.ref_name }}"
echo "Available releases:"
echo "$ALL_RELEASES" | jq -r '.[] | "- \(.tag_name) (ID: \(.id))"' | head -10
exit 1
fi
fi
RELEASE_ID=$(echo "$RELEASE_RESPONSE" | jq -r '.id')
echo "✅ Found release ID: $RELEASE_ID"
# 获取 assets
echo "📁 Getting release assets..."
ASSETS_RESPONSE=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets)
if ! echo "$ASSETS_RESPONSE" | jq -e '. | length' > /dev/null 2>&1; then
echo "❌ Failed to get assets"
echo "Response: $ASSETS_RESPONSE"
exit 1
fi
echo "📁 Current release assets:"
echo "$ASSETS_RESPONSE" | jq -r '.[] | "- \(.name) (ID: \(.id))"'
# 查找并删除不需要的文件
echo "🔍 Looking for unwanted assets..."
# 删除 .yml, .yaml, .blockmap 文件
echo "Finding .yml, .yaml, .blockmap files..."
YML_ASSETS=$(echo "$ASSETS_RESPONSE" | jq -r 'if type == "array" then .[] | select(.name | test("\\.(yml|yaml|blockmap)$")) | .id else empty end' 2>/dev/null || echo "")
# 删除不带架构标识的 Windows 文件
echo "Finding Windows files without architecture identifiers..."
WINDOWS_NO_ARCH_ASSETS=$(echo "$ASSETS_RESPONSE" | jq -r 'if type == "array" then .[] | select(.name | test("windows.*\\.(exe)$") and (.name | test("_(x64|arm64)_") | not)) | .id else empty end' 2>/dev/null || echo "")
# 合并所有要删除的 assets,过滤空行
ALL_UNWANTED_ASSETS=""
if [ -n "$YML_ASSETS" ]; then
ALL_UNWANTED_ASSETS="$YML_ASSETS"
fi
if [ -n "$WINDOWS_NO_ARCH_ASSETS" ]; then
if [ -n "$ALL_UNWANTED_ASSETS" ]; then
ALL_UNWANTED_ASSETS="$ALL_UNWANTED_ASSETS\n$WINDOWS_NO_ARCH_ASSETS"
else
ALL_UNWANTED_ASSETS="$WINDOWS_NO_ARCH_ASSETS"
fi
fi
if [ -n "$ALL_UNWANTED_ASSETS" ]; then
echo "🎯 Found unwanted assets to delete:"
# 显示要删除的文件列表
echo "$ASSETS_RESPONSE" | jq -r 'if type == "array" then .[] | select((.name | test("\\.(yml|yaml|blockmap)$")) or (.name | test("windows.*\\.(exe)$") and (.name | test("_(x64|arm64)_") | not))) | "- \(.name) (ID: \(.id))" else empty end' 2>/dev/null || echo "Error displaying file list"
echo -e "$ALL_UNWANTED_ASSETS" | while read -r asset_id; do
if [ -n "$asset_id" ] && [ "$asset_id" != "null" ] && [ "$asset_id" != "" ]; then
ASSET_NAME=$(echo "$ASSETS_RESPONSE" | jq -r "if type == \"array\" then .[] | select(.id == $asset_id) | .name else \"unknown\" end" 2>/dev/null)
echo "🗑️ Deleting: $ASSET_NAME (ID: $asset_id)"
DELETE_RESPONSE=$(curl -s -w "%{http_code}" -o /dev/null \
-X DELETE \
-H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/repos/${{ github.repository }}/releases/assets/$asset_id)
if [ "$DELETE_RESPONSE" = "204" ]; then
echo "✅ Successfully deleted: $ASSET_NAME"
else
echo "❌ Failed to delete: $ASSET_NAME (HTTP: $DELETE_RESPONSE)"
fi
fi
done
else
echo "🎉 No unwanted files found!"
fi
echo "🏁 Cleanup completed successfully!"
env:
GITHUB_TOKEN: ${{ secrets.atriordsa }}
upload-to-cos:
runs-on: ubuntu-latest
needs: [cleanup-release]
if: always() && needs.cleanup-release.result == 'success'
steps:
- name: Setup Python for COS upload
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install COS SDK
run: |
pip install cos-python-sdk-v5 requests
- name: Upload release assets to COS
run: |
cat > upload_to_cos.py << 'EOF'
import os
import sys
import requests
import json
from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
from urllib.parse import urlparse
import time
def main():
# 获取环境变量
github_token = os.getenv('GITHUB_TOKEN')
cos_secret_id = os.getenv('COS_SECRET_ID')
cos_secret_key = os.getenv('COS_SECRET_KEY')
bucket = os.getenv('BUCKET')
region = os.getenv('REGION')
cdn_url = os.getenv('CDN_URL')
tag_name = os.getenv('TAG_NAME')
repository = os.getenv('REPOSITORY')
print(f"🚀 Starting COS upload for release: {tag_name}")
# 初始化 COS 客户端
config = CosConfig(Region=region, SecretId=cos_secret_id, SecretKey=cos_secret_key)
client = CosS3Client(config)
# 获取 GitHub release 信息
release_url = f"https://api.github.com/repos/{repository}/releases/tags/{tag_name}"
headers = {'Authorization': f'token {github_token}'}
response = requests.get(release_url, headers=headers)
if response.status_code != 200:
print(f"❌ Failed to get release info: {response.status_code}")
sys.exit(1)
release_data = response.json()
assets = release_data.get('assets', [])
print(f"📦 Found {len(assets)} assets to upload")
# 创建版本目录
version_path = f"releases/{tag_name.replace('v', '')}"
uploaded_files = []
# 上传每个资源文件
for asset in assets:
asset_name = asset['name']
download_url = asset['browser_download_url']
print(f"📥 Downloading: {asset_name}")
# 下载文件
asset_response = requests.get(download_url, headers=headers)
if asset_response.status_code != 200:
print(f"❌ Failed to download {asset_name}")
continue
# 上传到 COS
cos_key = f"{version_path}/{asset_name}"
try:
print(f"☁️ Uploading to COS: {cos_key}")
client.put_object(
Bucket=bucket,
Body=asset_response.content,
Key=cos_key,
ContentType='application/octet-stream'
)
# 构建 CDN URL
cdn_file_url = f"{cdn_url.rstrip('/')}/{cos_key}"
uploaded_files.append({
'name': asset_name,
'cos_key': cos_key,
'cdn_url': cdn_file_url,
'size': len(asset_response.content)
})
print(f"✅ Successfully uploaded: {asset_name}")
print(f" CDN URL: {cdn_file_url}")
except Exception as e:
print(f"❌ Failed to upload {asset_name}: {str(e)}")
# 创建索引文件
index_data = {
'version': tag_name,
'upload_time': int(time.time()),
'files': uploaded_files
}
index_key = f"{version_path}/index.json"
try:
client.put_object(
Bucket=bucket,
Body=json.dumps(index_data, indent=2).encode('utf-8'),
Key=index_key,
ContentType='application/json'
)
print(f"📋 Created index file: {cdn_url.rstrip('/')}/{index_key}")
except Exception as e:
print(f"❌ Failed to create index file: {str(e)}")
print(f"🎉 COS upload completed! Uploaded {len(uploaded_files)} files")
print(f"📁 Version directory: {cdn_url.rstrip('/')}/{version_path}/")
if __name__ == "__main__":
main()
EOF
python upload_to_cos.py
env:
GITHUB_TOKEN: ${{ secrets.atriordsa }}
COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }}
COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }}
BUCKET: ${{ secrets.BUCKET }}
REGION: ${{ secrets.REGION }}
CDN_URL: ${{ secrets.CDN_URL }}
TAG_NAME: ${{ github.ref_name }}
REPOSITORY: ${{ github.repository }}