mirror of
https://gitcode.com/JianFeeeee/homeagent-sdk.git
synced 2026-10-15 12:15:11 +00:00
ci: SDK 仓发布流水线 —— release/** 推送即出 hmapdev 五平台产物
与主仓 Release 流水线同构,差异只在产物与打包命令:
prepare 读 meta.Version;tag 已存在则整轮跳过;go test 门可显式跳过
(改 meta 的提交里写 [skip-release-tests],查该提交而非 HEAD)
build go build(硬门)→ go test → build.sh all hmapdev → 验证 5 平台齐全
→ 生成 SHA256SUMS → artifact
publish 打 tag → gh release create 传附件 → 回读校验
sync-gitcode 有 GITCODE_TOKEN 时同步(无则跳过)
产物清单不是猜的,依 gitcode 上 v1.2.0/v1.3.0 的实际附件(各 6 个):
hmapdev_{linux,darwin}_{amd64,arm64} + hmapdev_windows_amd64.exe + SHA256SUMS
两个易错点都有实测依据:
1. **测试要分两处跑**:tools/hmapdev 是**独立 module**,根模块的
`go test ./...` 不会进入它(Go 的模块边界,不是配置问题)。
2. **gitcode 上传必须显式列文件名**:上传脚本的路径语义是
`os.path.join(ASSET_DIR, name)`,所以要 cd 进目录 + `ASSET_DIR=.` + 裸名;
而它按扩展名识别产物的默认扫描对 hmapdev **无效** —— 五个产物里只有
windows 那个有扩展名,自动扫描会静默地一个都不传。
故把主仓的 upload_assets.py 一并纳入本仓 scripts/(两仓各自独立可取)。
本地已验证:`VERSION=1.4.0 bash package/build.sh all hmapdev` 产出 5 个
二进制(各 27–29M),根模块 go test 通过,actionlint 全绿。
This commit is contained in:
312
.github/workflows/release.yml
vendored
Normal file
312
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
# HomeAgent SDK 仓发布流水线:release/** 推送即发版。
|
||||||
|
#
|
||||||
|
# 与主仓的 Release 流水线同构(见主仓 .github/workflows/release.yml),
|
||||||
|
# 差异只在产物与打包命令:
|
||||||
|
#
|
||||||
|
# 主仓 → 3 个 deb + 1 个 tar.gz(含 719MB 向量模型)
|
||||||
|
# SDK → hmapdev_{linux,darwin}_{amd64,arm64} + hmapdev_windows_amd64.exe
|
||||||
|
# + SHA256SUMS(约 140MB)
|
||||||
|
#
|
||||||
|
# 产物清单依据:gitcode 上 v1.2.0/v1.3.0 的实际附件(各 6 个),
|
||||||
|
# 以及 docs/git-branching.md §七 的「发版产物清单」。
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['release/**']
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
skip_tests:
|
||||||
|
description: '跳过发版前的 go test 门(仅用于已知红的历史维护线)'
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: release-${{ github.ref }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
GOFLAGS: -buildvcs=false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
prepare:
|
||||||
|
name: Prepare
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.ver.outputs.version }}
|
||||||
|
tag: ${{ steps.ver.outputs.tag }}
|
||||||
|
prerelease: ${{ steps.ver.outputs.prerelease }}
|
||||||
|
exists: ${{ steps.ver.outputs.exists }}
|
||||||
|
skip_tests: ${{ steps.ver.outputs.skip_tests }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
# 版本事实源是 meta/meta.go 的 Version,故发版动作 =
|
||||||
|
# 在 release/vX.Y.x 上把它改成目标版本后推送。
|
||||||
|
#
|
||||||
|
# 幂等闸门:tag 已存在 ⇒ 整轮跳过(改文档不会重发版)。
|
||||||
|
#
|
||||||
|
# go test 门可跳过:新旧发布线的测试健康状况不同,硬门会让历史
|
||||||
|
# 维护线完全无法发版。发版人可在**改动 meta 的那个提交**里写
|
||||||
|
# [skip-release-tests] 显式跳过 —— 决定因此可被 git 历史审计。
|
||||||
|
# (标记查在改 meta 的提交上而不是 HEAD:发版提交之后常还会跟
|
||||||
|
# 几个提交,只看 HEAD 会让标记被顶掉、静默失效。)
|
||||||
|
- id: ver
|
||||||
|
name: 读取 meta.Version 并检查 tag
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
V=$(sed -n 's/^[[:space:]]*Version = "\(.*\)"/\1/p' \
|
||||||
|
meta/meta.go | head -1)
|
||||||
|
if [ -z "$V" ]; then
|
||||||
|
echo "ERROR: 无法从 meta/meta.go 读出 Version"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "version=$V" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "tag=v$V" >> "$GITHUB_OUTPUT"
|
||||||
|
case "$V" in
|
||||||
|
*-*) echo "prerelease=true" >> "$GITHUB_OUTPUT" ;;
|
||||||
|
*) echo "prerelease=false" >> "$GITHUB_OUTPUT" ;;
|
||||||
|
esac
|
||||||
|
if git ls-remote --exit-code --tags origin "refs/tags/v$V" \
|
||||||
|
>/dev/null 2>&1; then
|
||||||
|
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||||
|
echo " tag v$V 已存在 —— 跳过发版"
|
||||||
|
else
|
||||||
|
echo "exists=false" >> "$GITHUB_OUTPUT"
|
||||||
|
echo " 将为 v$V 发版"
|
||||||
|
fi
|
||||||
|
|
||||||
|
SKIP="${{ inputs.skip_tests }}"
|
||||||
|
REL_COMMIT=$(git log -1 --format=%H -- meta/meta.go)
|
||||||
|
REL_MSG=$(git log -1 --pretty=%B "$REL_COMMIT")
|
||||||
|
case "$REL_MSG" in
|
||||||
|
*'[skip-release-tests]'*) MARKER=1 ;;
|
||||||
|
*) MARKER=0 ;;
|
||||||
|
esac
|
||||||
|
echo " 发版提交: ${REL_COMMIT:0:12}"
|
||||||
|
if [ "$SKIP" = "true" ] || [ "$MARKER" = "1" ]; then
|
||||||
|
echo "skip_tests=true" >> "$GITHUB_OUTPUT"
|
||||||
|
echo " ⚠️ **已请求跳过发版前的 go test 门**"
|
||||||
|
else
|
||||||
|
echo "skip_tests=false" >> "$GITHUB_OUTPUT"
|
||||||
|
echo " 发版前会跑 go test 门"
|
||||||
|
fi
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build hmapdev
|
||||||
|
needs: prepare
|
||||||
|
if: needs.prepare.outputs.exists == 'false'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 60
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v7
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
# 门:go build 是硬门;go test 可在发版 commit 里显式跳过。
|
||||||
|
#
|
||||||
|
# 测试分两处跑 —— tools/hmapdev 是**独立 module**,根模块的
|
||||||
|
# `go test ./...` 不会进入它(这是 Go 的模块边界,不是配置问题)。
|
||||||
|
- name: go build(硬门)
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
go build ./...
|
||||||
|
|
||||||
|
- name: go test(根模块)
|
||||||
|
if: needs.prepare.outputs.skip_tests != 'true'
|
||||||
|
run: go test ./... -count=1 -timeout 15m
|
||||||
|
|
||||||
|
- name: go test(tools/hmapdev 独立 module)
|
||||||
|
if: needs.prepare.outputs.skip_tests != 'true'
|
||||||
|
working-directory: tools/hmapdev
|
||||||
|
run: go test ./... -count=1 -timeout 15m
|
||||||
|
|
||||||
|
- name: go test 被跳过(显式声明的后果)
|
||||||
|
if: needs.prepare.outputs.skip_tests == 'true'
|
||||||
|
run: |
|
||||||
|
echo "::warning title=go test 门已跳过::本次发版未跑 go test,产物可能建立在单元测试失败的代码上。"
|
||||||
|
|
||||||
|
- name: 打包 hmapdev(5 个平台)
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.prepare.outputs.version }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
rm -rf build
|
||||||
|
bash package/build.sh all hmapdev
|
||||||
|
echo
|
||||||
|
echo " 产物:"
|
||||||
|
for f in build/*; do
|
||||||
|
printf " %8.1fMB %s\n" \
|
||||||
|
"$(stat -c %s "$f" | awk '{print $1/1048576}')" "$(basename "$f")"
|
||||||
|
done
|
||||||
|
|
||||||
|
# 逐个确认 5 个平台都产出了 —— 只查目录非空会漏掉"少了一个平台"。
|
||||||
|
- name: 验证产物齐全
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
missing=0
|
||||||
|
for f in hmapdev_linux_amd64 hmapdev_linux_arm64 \
|
||||||
|
hmapdev_darwin_amd64 hmapdev_darwin_arm64 \
|
||||||
|
hmapdev_windows_amd64.exe; do
|
||||||
|
if [ -s "build/$f" ]; then
|
||||||
|
echo " ✓ $f"
|
||||||
|
else
|
||||||
|
echo " ✗ 缺 $f" >&2
|
||||||
|
missing=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ "$missing" = "0" ] || exit 1
|
||||||
|
|
||||||
|
# 校验和必须**全部产物齐全之后**一次算完(边打边算会漏包);
|
||||||
|
# 且只覆盖本批产物 —— build/ 可能残留上次的,故先清干净再建。
|
||||||
|
- name: 生成 SHA256SUMS
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
mkdir -p /tmp/out
|
||||||
|
cp build/hmapdev_linux_amd64 build/hmapdev_linux_arm64 \
|
||||||
|
build/hmapdev_darwin_amd64 build/hmapdev_darwin_arm64 \
|
||||||
|
build/hmapdev_windows_amd64.exe /tmp/out/
|
||||||
|
cd /tmp/out
|
||||||
|
sha256sum hmapdev_* > SHA256SUMS
|
||||||
|
echo " SHA256SUMS:"
|
||||||
|
sed 's/^/ /' SHA256SUMS
|
||||||
|
sha256sum -c SHA256SUMS
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: hmapdev
|
||||||
|
path: /tmp/out/*
|
||||||
|
retention-days: 7
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
publish:
|
||||||
|
name: Publish
|
||||||
|
needs: [prepare, build]
|
||||||
|
if: needs.prepare.outputs.exists == 'false'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v8
|
||||||
|
with:
|
||||||
|
name: hmapdev
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
- name: 打 tag
|
||||||
|
env:
|
||||||
|
TAG: ${{ needs.prepare.outputs.tag }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git tag -a "$TAG" -m "$TAG"
|
||||||
|
git push origin "$TAG"
|
||||||
|
|
||||||
|
- name: 建 release 并上传附件
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TAG: ${{ needs.prepare.outputs.tag }}
|
||||||
|
VERSION: ${{ needs.prepare.outputs.version }}
|
||||||
|
PRE: ${{ needs.prepare.outputs.prerelease }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
cd dist
|
||||||
|
FLAGS=()
|
||||||
|
[ "$PRE" = "true" ] && FLAGS+=(--prerelease)
|
||||||
|
gh release create "$TAG" \
|
||||||
|
--title "HomeAgent SDK $VERSION" \
|
||||||
|
--notes "HomeAgent 插件 SDK $VERSION
|
||||||
|
|
||||||
|
\`hmapdev\` 工具链(Linux / macOS / Windows,amd64 + arm64)。
|
||||||
|
校验见 SHA256SUMS。
|
||||||
|
|
||||||
|
内核版本需与 SDK 的中版本对齐;协议不配套时插件握手会失败
|
||||||
|
(魔数不匹配),此时升级内核或改用对应版本的 SDK。" \
|
||||||
|
"${FLAGS[@]}" \
|
||||||
|
./hmapdev_* ./SHA256SUMS
|
||||||
|
echo "=== release 内容 ==="
|
||||||
|
gh release view "$TAG" --json assets \
|
||||||
|
--jq '.assets[] | " \(.name) \(.size) 字节"'
|
||||||
|
|
||||||
|
- name: 回读校验
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TAG: ${{ needs.prepare.outputs.tag }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
mkdir -p /tmp/back && cd /tmp/back
|
||||||
|
gh release download "$TAG"
|
||||||
|
for f in *; do
|
||||||
|
printf " %8.1fMB %s\n" \
|
||||||
|
"$(stat -c %s "$f" | awk '{print $1/1048576}')" "$f"
|
||||||
|
done
|
||||||
|
sha256sum -c SHA256SUMS
|
||||||
|
echo " ✓ 回读校验通过"
|
||||||
|
|
||||||
|
sync-gitcode:
|
||||||
|
name: Sync to gitcode
|
||||||
|
needs: [prepare, publish]
|
||||||
|
if: needs.prepare.outputs.exists == 'false'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
- id: tok
|
||||||
|
name: 检查 gitcode 凭据
|
||||||
|
run: |
|
||||||
|
if [ -n "${{ secrets.GITCODE_TOKEN }}" ]; then
|
||||||
|
echo "ok=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "ok=false" >> "$GITHUB_OUTPUT"
|
||||||
|
echo " 未配置 GITCODE_TOKEN —— 跳过 gitcode 同步"
|
||||||
|
fi
|
||||||
|
- uses: actions/download-artifact@v8
|
||||||
|
if: steps.tok.outputs.ok == 'true'
|
||||||
|
with:
|
||||||
|
name: hmapdev
|
||||||
|
path: dist
|
||||||
|
- name: 推 tag 与附件到 gitcode
|
||||||
|
if: steps.tok.outputs.ok == 'true'
|
||||||
|
env:
|
||||||
|
GC_TOKEN: ${{ secrets.GITCODE_TOKEN }}
|
||||||
|
TAG: ${{ needs.prepare.outputs.tag }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git tag -a "$TAG" -m "$TAG" 2>/dev/null || true
|
||||||
|
GC_URL="https://JianFeeeee:${GC_TOKEN}@gitcode.com"
|
||||||
|
git push "${GC_URL}/JianFeeeee/homeagent-sdk.git" "$TAG"
|
||||||
|
curl -sS --max-time 60 -X POST \
|
||||||
|
-H "private-token: ${GC_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"https://gitcode.com/api/v5/repos/JianFeeeee/homeagent-sdk/releases" \
|
||||||
|
-d "{\"tag_name\":\"$TAG\",\"body\":\"同步自 GitHub\"}" \
|
||||||
|
-o /tmp/.gcrel -w " 建 release → %{http_code}\n"
|
||||||
|
cd dist
|
||||||
|
# 脚本的路径语义是 os.path.join(ASSET_DIR, name) ⇒
|
||||||
|
# 必须 cd 进资产目录、ASSET_DIR=.、并传**裸文件名**。
|
||||||
|
# 传 "./x" 或 "dist/x" 都会拼成 dist/dist/x 而找不到文件。
|
||||||
|
#
|
||||||
|
# 为何显式列名而不是让它自动扫描:自动扫描只认 ARTIFACT_SUFFIXES
|
||||||
|
# 里的扩展名,而 hmapdev 的产物多数**没有扩展名**(只有 windows
|
||||||
|
# 那个是 .exe)⇒ 自动扫描会静默地一个都不传。
|
||||||
|
ASSET_DIR=. GITCODE_REPO=JianFeeeee/homeagent-sdk \
|
||||||
|
python3 ../scripts/upload-assets.py "$TAG" "$GC_TOKEN" \
|
||||||
|
hmapdev_linux_amd64 hmapdev_linux_arm64 \
|
||||||
|
hmapdev_darwin_amd64 hmapdev_darwin_arm64 \
|
||||||
|
hmapdev_windows_amd64.exe SHA256SUMS
|
||||||
133
scripts/upload-assets.py
Executable file
133
scripts/upload-assets.py
Executable file
@ -0,0 +1,133 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""上传 release 资产到 gitcode(两步:取签名 URL → PUT 到 OBS)。
|
||||||
|
|
||||||
|
用法: upload_assets.py <tag> <token> [file...]
|
||||||
|
不传 file 时上传 dist/release/ 下全部发布产物。
|
||||||
|
|
||||||
|
环境变量:
|
||||||
|
GITCODE_REPO 目标仓库,默认 JianFeeeee/HomeAgent(SDK 仓传 JianFeeeee/homeagent-sdk)
|
||||||
|
ASSET_DIR 资产目录,默认 <repo>/dist/release
|
||||||
|
|
||||||
|
为何两步:gitcode 的 release 附件不走 API 直传,而是先向
|
||||||
|
`releases/<tag>/upload_url` 要一个 OBS 预签名 URL(带 x-obs-* 回调头),
|
||||||
|
再把文件 PUT 到那个 URL。回调头必须原样透传,否则 OBS 收下了文件但
|
||||||
|
gitcode 侧不会登记为 release 附件。
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import urllib.error
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
REPO = os.environ.get("GITCODE_REPO", "JianFeeeee/HomeAgent")
|
||||||
|
API = "https://gitcode.com/api/v5/repos"
|
||||||
|
|
||||||
|
# 发布产物后缀。注意 Windows 安装器是 HomeAgent_v*_win64.exe,
|
||||||
|
# 与 bin/ 里的裸 .exe 靠 _win64.exe 后缀区分。
|
||||||
|
ARTIFACT_SUFFIXES = (
|
||||||
|
".tar.gz",
|
||||||
|
".zip",
|
||||||
|
".deb",
|
||||||
|
".rpm",
|
||||||
|
".pkg",
|
||||||
|
"_win64.exe",
|
||||||
|
# 插件包。之前不在白名单里,会被静默跳过——而 release 本该带上它们,
|
||||||
|
# 否则用户要自己装 Go + hmapdev 逐插件构建(见 SDK 仓 scripts/build_plugin_bundles.sh)。
|
||||||
|
".hmap",
|
||||||
|
# 插件包汇总校验和(与 SHA256SUMS 同性质,独立文件免得混淆内核包与插件)
|
||||||
|
"SHA256SUMS.plugins",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def is_artifact(name: str) -> bool:
|
||||||
|
return name == "SHA256SUMS" or name.endswith(ARTIFACT_SUFFIXES)
|
||||||
|
|
||||||
|
def get_upload_url(tag: str, token: str, filename: str) -> tuple[str, dict]:
|
||||||
|
q = urllib.parse.urlencode({"file_name": filename})
|
||||||
|
url = f"{API}/{REPO}/releases/{tag}/upload_url?{q}"
|
||||||
|
req = urllib.request.Request(url, headers={"private-token": token})
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as r:
|
||||||
|
data = json.loads(r.read())
|
||||||
|
return data["url"], data.get("headers", {})
|
||||||
|
|
||||||
|
|
||||||
|
def put_file(url: str, headers: dict, path: str) -> tuple[int, str]:
|
||||||
|
size = os.path.getsize(path)
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
body = f.read()
|
||||||
|
req = urllib.request.Request(url, data=body, method="PUT")
|
||||||
|
for k, v in headers.items():
|
||||||
|
req.add_header(k, v)
|
||||||
|
req.add_header("Content-Length", str(size))
|
||||||
|
try:
|
||||||
|
# 大文件(Full 变体安装包近 100MB)给足超时。
|
||||||
|
with urllib.request.urlopen(req, timeout=900) as r:
|
||||||
|
return r.status, r.read().decode("utf-8", "replace")[:300]
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
return e.code, e.read().decode("utf-8", "replace")[:300]
|
||||||
|
except Exception as e: # noqa: BLE001
|
||||||
|
return 0, f"{type(e).__name__}: {e}"
|
||||||
|
|
||||||
|
|
||||||
|
def project_root() -> str:
|
||||||
|
"""向上找带 go.mod 的目录作为仓库根。
|
||||||
|
|
||||||
|
为何不数 dirname:本脚本初版在 scripts/(深度 1),移到 deploy/scripts/
|
||||||
|
(深度 2)后写死的两层 dirname 就指向了 deploy/dist/release,上传直接
|
||||||
|
FileNotFoundError。这正是 v0.7.2 那次 package/ → deploy/packaging/ 打断
|
||||||
|
PROJECT_ROOT 的同一个坑,改成按标记文件定位以后怎么挑位置都不会错。
|
||||||
|
"""
|
||||||
|
d = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
while d != os.path.dirname(d):
|
||||||
|
if os.path.exists(os.path.join(d, "go.mod")):
|
||||||
|
return d
|
||||||
|
d = os.path.dirname(d)
|
||||||
|
# 实在找不到(脚本被单独拷出仓库)就回退到 cwd,给 ASSET_DIR 一个机会
|
||||||
|
return os.getcwd()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
print(__doc__)
|
||||||
|
return 2
|
||||||
|
tag, token = sys.argv[1], sys.argv[2]
|
||||||
|
outdir = os.environ.get("ASSET_DIR") or os.path.join(
|
||||||
|
project_root(), "dist", "release"
|
||||||
|
)
|
||||||
|
if not os.path.isdir(outdir):
|
||||||
|
print(f"error: 资产目录不存在: {outdir}")
|
||||||
|
print(" 用 ASSET_DIR=<目录> 显式指定,或先跑构建生成 dist/release/")
|
||||||
|
return 2
|
||||||
|
files = sys.argv[3:] or sorted(
|
||||||
|
f for f in os.listdir(outdir) if is_artifact(f)
|
||||||
|
)
|
||||||
|
if not files:
|
||||||
|
print(f"error: {outdir} 下没有可识别的发布产物")
|
||||||
|
return 2
|
||||||
|
print(f"repo={REPO} tag={tag} dir={outdir}", flush=True)
|
||||||
|
failed = []
|
||||||
|
for name in files:
|
||||||
|
path = os.path.join(outdir, name)
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
print(f"skip (missing): {name}", flush=True)
|
||||||
|
continue
|
||||||
|
mib = os.path.getsize(path) / 1048576
|
||||||
|
print(f"==> {name} ({mib:.1f} MiB)", flush=True)
|
||||||
|
try:
|
||||||
|
url, headers = get_upload_url(tag, token, name)
|
||||||
|
except Exception as e: # noqa: BLE001
|
||||||
|
print(f" upload_url FAILED: {e}", flush=True)
|
||||||
|
failed.append(name)
|
||||||
|
continue
|
||||||
|
status, body = put_file(url, headers, path)
|
||||||
|
ok = 200 <= status < 300
|
||||||
|
print(f" PUT -> {status} {'OK' if ok else body}", flush=True)
|
||||||
|
if not ok:
|
||||||
|
failed.append(name)
|
||||||
|
print(f"\n{'ALL OK' if not failed else f'{len(failed)} FAILED: ' + ', '.join(failed)}")
|
||||||
|
return 1 if failed else 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user