Skip to content

Update version.json #50

Update version.json

Update version.json #50

Workflow file for this run

name: Sync to Aliyun OSS
on:
push:
branches:
- main
paths:
- 'config/**'
- 'upgrade/**'
workflow_dispatch:
inputs:
manual_run:
description: '手动触发同步配置文件和资源文件到 OSS'
required: false
default: 'true'
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Detect environment
run: |
if [ -n "${ACT:-}" ]; then
echo "Running in act (local)"
echo "ACT_ENV=true" >> $GITHUB_ENV
else
echo "Running in GitHub Actions"
echo "ACT_ENV=false" >> $GITHUB_ENV
fi
- name: Validate secrets
env:
OSS_ACCESS_KEY_ID: ${{ secrets.OSS_ACCESS_KEY_ID }}
OSS_ACCESS_KEY_SECRET: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
OSS_ENDPOINT: ${{ secrets.OSS_ENDPOINT }}
OSS_BUCKET: ${{ secrets.OSS_BUCKET }}
run: |
set -euo pipefail
echo "Validating required secrets..."
if [ -z "$OSS_ACCESS_KEY_ID" ]; then
echo "ERROR: OSS_ACCESS_KEY_ID is not set"
exit 1
fi
if [ -z "$OSS_ACCESS_KEY_SECRET" ]; then
echo "ERROR: OSS_ACCESS_KEY_SECRET is not set"
exit 1
fi
if [ -z "$OSS_ENDPOINT" ]; then
echo "ERROR: OSS_ENDPOINT is not set"
exit 1
fi
if [ -z "$OSS_BUCKET" ]; then
echo "ERROR: OSS_BUCKET is not set"
exit 1
fi
echo "All required secrets are present"
- name: Setup ossutil
run: |
set -euo pipefail
echo "Downloading ossutil..."
wget -q https://gosspublic.alicdn.com/ossutil/1.7.15/ossutil64
chmod +x ossutil64
sudo mv ossutil64 /usr/local/bin/ossutil
echo "Verifying ossutil installation..."
ossutil --version
- name: Configure ossutil
env:
OSS_ACCESS_KEY_ID: ${{ secrets.OSS_ACCESS_KEY_ID }}
OSS_ACCESS_KEY_SECRET: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
OSS_ENDPOINT: ${{ secrets.OSS_ENDPOINT }}
run: |
set -euo pipefail
echo "Configuring ossutil..."
ossutil config -e $OSS_ENDPOINT -i $OSS_ACCESS_KEY_ID -k $OSS_ACCESS_KEY_SECRET
echo "Testing OSS connection..."
ossutil ls oss:// || echo "Warning: Could not list OSS buckets"
- name: Install jq
run: |
set -euo pipefail
echo "Installing jq..."
sudo apt-get update -qq
sudo apt-get install -y jq
echo "Verifying jq installation..."
jq --version
- name: Download files from config URLs
env:
OSS_BUCKET: ${{ secrets.OSS_BUCKET }}
run: |
set -eu
mkdir -p downloads
> files_to_sync.txt
> sync_warnings.txt
> sync_errors.txt
echo "========================================="
echo "Starting resource file download process"
echo "========================================="
# 定义需要下载的文件后缀(正则模式)
DOWNLOAD_EXTENSIONS='\.(zip|png|jpg|jpeg|gif|exe|bin|apk|dmg|pdf|tar|gz|tgz|rar|7z|deb|rpm|msi|iso|svg|ico|webp|bmp|mp3|mp4|wav|avi|mov|stl|gcode|snap3dp|snapcnc|snaplaser)$'
# 创建临时文件存储 URL -> MD5 映射
URL_MD5_FILE=$(mktemp)
trap "rm -f $URL_MD5_FILE" EXIT
# 第一遍:从所有 JSON 文件中提取 URL 和对应的 MD5
echo "Building URL to MD5 mapping..."
for json_file in $(find config/ upgrade/ -name "*.json" -type f 2>/dev/null); do
jq -r '.. | objects | select(.file_url?) | "\(.file_url)\t\(.file_md5 // "")"' "$json_file" 2>/dev/null >> "$URL_MD5_FILE" || true
done
# 构建关联数组
declare -A URL_MD5_MAP
while IFS=$'\t' read -r url md5; do
if [ -n "$url" ] && [ "$url" != "null" ]; then
URL_MD5_MAP["$url"]="$md5"
fi
done < "$URL_MD5_FILE"
echo "Found ${#URL_MD5_MAP[@]} URLs with potential MD5 mappings"
# 第二遍:提取所有 URL 并处理
ALL_URLS_FILE=$(mktemp)
trap "rm -f $URL_MD5_FILE $ALL_URLS_FILE" EXIT
for json_file in $(find config/ upgrade/ -name "*.json" -type f 2>/dev/null); do
echo "Extracting URLs from: $json_file"
grep -oE 'https?://[^"'\''[:space:]<>]+' "$json_file" 2>/dev/null >> "$ALL_URLS_FILE" || true
done
# 去重并处理每个 URL
sort -u "$ALL_URLS_FILE" | while read -r url; do
[ -z "$url" ] && continue
# 清理 URL(移除尾部标点)
url=$(echo "$url" | sed 's/[,;:]*$//')
# 提取 URL 的主体部分(去掉查询参数),用于文件扩展名检查
url_main=$(echo "$url" | cut -d'?' -f1)
# 检查是否有文件后缀(使用清理后的 URL)
if ! echo "$url_main" | grep -qiE "$DOWNLOAD_EXTENSIONS"; then
echo "SKIP (no file extension): $url"
continue
fi
echo "Processing: $url"
# 从 URL 提取路径(去掉协议和域名)
url_without_protocol=$(echo "$url" | sed 's|https\?://||')
domain=$(echo "$url_without_protocol" | cut -d'/' -f1)
url_path=$(echo "$url_without_protocol" | cut -d'/' -f2-)
# OSS 路径:download/{path}(不包含域名)
oss_path="oss://$OSS_BUCKET/download/$url_path"
config_md5="${URL_MD5_MAP[$url]:-}"
# 下载文件到 downloads/download/{path}
target_dir="downloads/download/$(dirname "$url_path")"
target_file="downloads/download/$url_path"
mkdir -p "$target_dir"
if wget --show-progress --progress=bar:force --timeout=60 --tries=3 -O "$target_file" "$url"; then
# 计算下载文件的实际 MD5
downloaded_md5=$(md5sum "$target_file" | awk '{print $1}' | tr '[:lower:]' '[:upper:]')
echo "Downloaded: download/$url_path (MD5: $downloaded_md5)"
# 检查配置文件中的 MD5 是否匹配(仅作为警告,不影响上传决策)
if [ -n "$config_md5" ]; then
config_md5_upper=$(echo "$config_md5" | tr '[:lower:]' '[:upper:]')
if [ "$downloaded_md5" != "$config_md5_upper" ]; then
echo "WARNING: Config MD5 mismatch for download/$url_path (Config=$config_md5_upper, Actual=$downloaded_md5)"
echo "CONFIG_MD5_MISMATCH: $url (Config=$config_md5_upper, Actual=$downloaded_md5)" >> sync_warnings.txt
fi
fi
# 检查 OSS 上是否已存在,并比较 x-oss-meta-md5
echo "Checking OSS for: download/$url_path"
oss_info=$(ossutil stat "$oss_path" 2>/dev/null || true)
if [ -n "$oss_info" ]; then
# 获取 x-oss-meta-md5 元数据
oss_meta_md5=$(echo "$oss_info" | grep -i "X-Oss-Meta-Md5" | awk -F': ' '{print $2}' | tr -d ' ' | tr '[:lower:]' '[:upper:]')
if [ -n "$oss_meta_md5" ]; then
# 有 x-oss-meta-md5,使用它比较
if [ "$downloaded_md5" = "$oss_meta_md5" ]; then
echo "SKIP: download/$url_path (x-oss-meta-md5 match: $oss_meta_md5)"
rm -f "$target_file"
continue
else
echo "UPDATE: download/$url_path (OSS meta-md5: $oss_meta_md5, New MD5: $downloaded_md5)"
fi
else
# 无 x-oss-meta-md5,强制重新上传以添加 meta
echo "UPDATE: download/$url_path (No x-oss-meta-md5 found, will re-upload with meta)"
fi
else
echo "NEW: download/$url_path"
fi
# 记录文件用于上传
echo "download/$url_path" >> files_to_sync.txt
else
echo "ERROR: Failed to download $url"
echo "DOWNLOAD_FAILED: $url" >> sync_errors.txt
fi
done || true
# 显示下载统计和问题汇总
echo "========================================="
echo "Download Summary:"
if [ -f files_to_sync.txt ] && [ -s files_to_sync.txt ]; then
echo "Files to sync: $(wc -l < files_to_sync.txt)"
cat files_to_sync.txt
else
echo "No new files to sync"
fi
echo ""
echo "========================================="
echo "Issues Summary:"
echo "========================================="
# 统计警告
if [ -f sync_warnings.txt ] && [ -s sync_warnings.txt ]; then
echo ""
echo "⚠️ WARNINGS ($(wc -l < sync_warnings.txt)):"
cat sync_warnings.txt
else
echo "✅ No warnings"
fi
# 统计错误
if [ -f sync_errors.txt ] && [ -s sync_errors.txt ]; then
echo ""
echo "❌ ERRORS ($(wc -l < sync_errors.txt)):"
cat sync_errors.txt
else
echo "✅ No errors"
fi
echo "========================================="
- name: Create modified copies for OSS upload
run: |
set -eu
echo "Current working directory: $(pwd)"
echo "Listing current directory:"
ls -la
echo "Creating temporary directories for modified files..."
rm -rf temp_oss
mkdir -p temp_oss
echo "Copying files to temporary directory..."
if [ -d "config" ] && [ "$(ls -A config 2>/dev/null)" ]; then
cp -a config temp_oss/
echo "Copied config directory"
else
echo "WARNING: config directory is empty or not found"
ls -la config 2>/dev/null || echo "config directory does not exist"
fi
if [ -d "upgrade" ] && [ "$(ls -A upgrade 2>/dev/null)" ]; then
cp -a upgrade temp_oss/
echo "Copied upgrade directory"
else
echo "WARNING: upgrade directory is empty or not found"
ls -la upgrade 2>/dev/null || echo "upgrade directory does not exist"
fi
echo "Listing temp_oss contents:"
ls -la temp_oss/ || true
find temp_oss -type f 2>/dev/null | head -20 || echo "No files in temp_oss"
echo "Applying domain replacements to temporary copies..."
# 将所有下载文件的 URL 替换为 https://meta-cfg.snapmaker.cn/download/{path}
# 匹配格式: https://domain/path -> https://meta-cfg.snapmaker.cn/download/domain/path
# 只替换带有文件扩展名的 URL(如 .dmg, .exe, .zip 等)
# 定义需要替换的文件扩展名模式
EXTENSIONS="dmg|exe|zip|png|jpg|jpeg|gif|bin|apk|pdf|tar|gz|tgz|rar|7z|deb|rpm|msi|iso|svg|ico|webp|bmp|mp3|mp4|wav|avi|mov|stl|gcode|snap3dp|snapcnc|snaplaser"
# 替换所有匹配的 URL: https://xxx/path/file.ext -> https://meta-cfg.snapmaker.cn/download/path/file.ext
# 注意:不保留原始域名,因为 OSS 存储路径是 download/{path}(不含域名)
find temp_oss -type f -name "*.json" | while read json_file; do
# 使用 sed 替换 URL,移除域名部分
# 使用 # 作为 sed 分隔符,避免与 URL 中的 / 和扩展名中的 | 冲突
sed -i -E "s#https://[a-zA-Z0-9.-]+/([^\"]+\.($EXTENSIONS))#https://meta-cfg.snapmaker.cn/download/\1#gi" "$json_file"
done
# 其他 snapmaker.com 域名(非 URL)替换为 snapmaker.cn
find temp_oss -type f -name "*.json" -exec sed -i 's/snapmaker\.com/snapmaker.cn/g' {} + 2>/dev/null || true
echo "Domain replacement completed"
- name: Sync config files to OSS
env:
OSS_BUCKET: ${{ secrets.OSS_BUCKET }}
run: |
set -euo pipefail
echo "Syncing configuration files to OSS..."
if [ -d "temp_oss/config" ] && [ "$(ls -A temp_oss/config 2>/dev/null)" ]; then
echo "Uploading config files (skipping unchanged files)..."
ossutil sync temp_oss/config/ oss://$OSS_BUCKET/config/ --update \
--meta "Cache-Control:public, max-age=3600"
echo "Config files synced successfully"
else
echo "No config files to sync"
fi
if [ -d "temp_oss/upgrade" ] && [ "$(ls -A temp_oss/upgrade 2>/dev/null)" ]; then
echo "Uploading upgrade files (skipping unchanged files)..."
ossutil sync temp_oss/upgrade/ oss://$OSS_BUCKET/upgrade/ --update \
--meta "Cache-Control:public, max-age=3600"
echo "Upgrade files synced successfully"
else
echo "No upgrade files to sync"
fi
- name: Sync downloaded files to OSS
env:
OSS_BUCKET: ${{ secrets.OSS_BUCKET }}
run: |
set -euo pipefail
# 初始化验证结果文件
>upload_verify_errors.txt
# 检查是否有需要同步的文件
if [ -d "downloads" ] && [ "$(ls -A downloads 2>/dev/null)" ]; then
echo "Syncing downloaded files to OSS..."
# 逐个同步文件,保持路径结构
find downloads/ -type f | while read file; do
# 获取相对路径
rel_path=${file#downloads/}
# 计算本地文件 MD5
local_md5=$(md5sum "$file" | awk '{print $1}' | tr '[:lower:]' '[:upper:]')
echo "Uploading: $rel_path (Local MD5: $local_md5)"
# 上传时附带 x-oss-meta-md5 元数据
ossutil cp -f "$file" "oss://$OSS_BUCKET/$rel_path" \
--meta "Cache-Control:public, max-age=86400" \
--meta "x-oss-meta-md5:$local_md5"
# 上传后验证:获取 OSS 上文件的 x-oss-meta-md5
oss_info=$(ossutil stat "oss://$OSS_BUCKET/$rel_path" 2>/dev/null || true)
if [ -n "$oss_info" ]; then
oss_meta_md5=$(echo "$oss_info" | grep -i "X-Oss-Meta-Md5" | awk -F': ' '{print $2}' | tr -d ' ' | tr '[:lower:]' '[:upper:]')
if [ "$local_md5" = "$oss_meta_md5" ]; then
echo " ✅ VERIFIED: x-oss-meta-md5 matches ($oss_meta_md5)"
else
echo " ❌ MISMATCH: Local MD5=$local_md5, OSS x-oss-meta-md5=$oss_meta_md5"
echo "MD5_VERIFY_FAILED: $rel_path (Local=$local_md5, OSS meta=$oss_meta_md5)" >> upload_verify_errors.txt
fi
else
echo " ⚠️ WARNING: Could not verify OSS file after upload"
echo "VERIFY_FAILED: $rel_path (Could not get OSS file info)" >> upload_verify_errors.txt
fi
done
echo ""
echo "========================================="
echo "Upload Verification Summary:"
echo "========================================="
if [ -f upload_verify_errors.txt ] && [ -s upload_verify_errors.txt ]; then
echo "❌ VERIFICATION ERRORS ($(wc -l < upload_verify_errors.txt)):"
cat upload_verify_errors.txt
echo ""
echo "⚠️ WARNING: Some files have MD5 mismatch!"
else
echo "✅ All uploaded files verified successfully"
fi
echo "========================================="
echo "Resource files sync completed"
else
echo "No new resource files to sync (all files already up-to-date)"
fi
- name: Cleanup temporary files
if: always()
run: |
echo "Cleaning up temporary files..."
rm -rf temp_oss
rm -rf downloads
rm -f files_to_sync.txt
rm -f sync_warnings.txt
rm -f sync_errors.txt
echo "Cleanup completed"
- name: Setup aliyun-cli for CDN
if: success()
run: |
set -euo pipefail
echo "Installing aliyun-cli..."
wget -q https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz
tar -xzf aliyun-cli-linux-latest-amd64.tgz
sudo mv aliyun /usr/local/bin/
rm -f aliyun-cli-linux-latest-amd64.tgz
echo "Verifying aliyun-cli installation..."
aliyun version
- name: Refresh CDN cache
if: success()
env:
CDN_ACCESS_KEY_ID: ${{ secrets.CDN_ACCESS_KEY_ID }}
CDN_ACCESS_KEY_SECRET: ${{ secrets.CDN_ACCESS_KEY_SECRET }}
CDN_DOMAIN: ${{ secrets.CDN_DOMAIN }}
run: |
set -euo pipefail
echo "========================================="
echo "Refreshing CDN cache..."
echo "CDN Domain: $CDN_DOMAIN"
echo "========================================="
# 检查 CDN 密钥是否配置
if [ -z "$CDN_ACCESS_KEY_ID" ] || [ -z "$CDN_ACCESS_KEY_SECRET" ]; then
echo "WARNING: CDN credentials not configured, skipping CDN refresh"
exit 0
fi
# 配置 aliyun-cli
aliyun configure set \
--profile cdn-profile \
--mode AK \
--region cn-hangzhou \
--access-key-id "$CDN_ACCESS_KEY_ID" \
--access-key-secret "$CDN_ACCESS_KEY_SECRET"
# 刷新目录(config/, upgrade/, download/)
REFRESH_PATHS="https://$CDN_DOMAIN/config/
https://$CDN_DOMAIN/upgrade/
https://$CDN_DOMAIN/download/"
echo "Refreshing directories:"
echo "$REFRESH_PATHS"
# 调用 CDN 刷新 API(刷新目录)
RESULT=$(aliyun --profile cdn-profile cdn RefreshObjectCaches \
--ObjectPath "$REFRESH_PATHS" \
--ObjectType Directory 2>&1) || true
echo "CDN Refresh API Response:"
echo "$RESULT"
# 检查是否成功
if echo "$RESULT" | grep -q "RefreshTaskId"; then
TASK_ID=$(echo "$RESULT" | grep -o '"RefreshTaskId":"[^"]*"' | cut -d'"' -f4 || true)
echo "✅ CDN refresh task submitted successfully!"
echo "Task ID: $TASK_ID"
else
echo "⚠️ CDN refresh may have failed, please check manually"
fi
echo "========================================="
- name: Job summary
if: always()
run: |
echo "========================================="
echo "Job Summary"
echo "========================================="
echo "Environment: ${ACT_ENV:-GitHub Actions}"
echo "OSS Bucket: ${{ secrets.OSS_BUCKET }}"
echo "Timestamp: $(date -u +"%Y-%m-%d %H:%M:%S UTC")"
echo "========================================="