1
0
forked from Eeveid/lightOps

新增 Gitea 自动发布流程

This commit is contained in:
2026-05-25 11:44:51 +08:00
parent 4b2b5a2df6
commit f3162fba44
4 changed files with 388 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
name: 发布 Linux 二进制
on:
push:
tags:
- "v*"
jobs:
release-linux-x86_64:
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装系统依赖
run: |
sudo apt-get update
sudo apt-get install -y build-essential pkg-config sqlite3 curl tar
- name: 安装 Rust
uses: dtolnay/rust-toolchain@stable
- name: 安装 Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: 构建发布包
run: |
TAG="${GITHUB_REF_NAME:-${GITEA_REF_NAME:-${GITHUB_REF##*/}}}"
VERSION="${TAG#v}"
bash scripts/build-release.sh --version "$VERSION" --target x86_64-unknown-linux-gnu
- name: 发布到 Gitea Release
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_URL: https://gitea.kmux.cn
GITEA_OWNER: Eeveid
GITEA_REPO: lightOps
run: |
TAG="${GITHUB_REF_NAME:-${GITEA_REF_NAME:-${GITHUB_REF##*/}}}"
bash scripts/publish-gitea-release.sh \
--tag "$TAG" \
--title "LightOps ${TAG}" \
--target "$GITHUB_SHA" \
--no-create-tag \
--no-push-tag \
--package target/releases/*.tar.gz \
--package target/releases/*.tar.gz.sha256

View File

@@ -73,6 +73,37 @@ lightops/
推荐生产环境使用“发布包安装”。目标服务器只需要下载发布包、解压并注册 systemd 服务,不需要安装 Rust、Node.js也不需要现场编译。 推荐生产环境使用“发布包安装”。目标服务器只需要下载发布包、解压并注册 systemd 服务,不需要安装 Rust、Node.js也不需要现场编译。
## 自动发布 Release
仓库已内置 Gitea Actions 工作流:推送 `v*` 标签后Linux runner 会自动构建 `x86_64-unknown-linux-gnu` 发布包,并上传到 Gitea Release。
首次使用前,在 Gitea 仓库设置中添加 Actions Secret
```text
GITEA_TOKEN=<具备当前仓库 Release 写入权限的访问令牌>
```
发布新版本:
```bash
git tag -a v0.1.0 -m "LightOps v0.1.0"
git push origin v0.1.0
```
如果不使用 Gitea Actions也可以在任意构建机手动构建并上传
```bash
bash scripts/build-release.sh --version 0.1.0 --target x86_64-unknown-linux-gnu
GITEA_TOKEN=<token> bash scripts/publish-gitea-release.sh --tag v0.1.0 --package target/releases/lightops-0.1.0-x86_64-unknown-linux-gnu.tar.gz --package target/releases/lightops-0.1.0-x86_64-unknown-linux-gnu.tar.gz.sha256
```
Windows PowerShell 手动上传:
```powershell
$env:GITEA_TOKEN="<token>"
pwsh -File scripts\publish-gitea-release.ps1 -Tag v0.1.0 -Package target\releases\lightops-0.1.0-x86_64-pc-windows-gnu.zip,target\releases\lightops-0.1.0-x86_64-pc-windows-gnu.zip.sha256
```
先在构建机或 CI 上生成发布包: 先在构建机或 CI 上生成发布包:
```bash ```bash

View File

@@ -0,0 +1,120 @@
param(
[string]$GiteaUrl = $(if ($env:GITEA_URL) { $env:GITEA_URL } else { "https://gitea.kmux.cn" }),
[string]$Owner = $(if ($env:GITEA_OWNER) { $env:GITEA_OWNER } else { "Eeveid" }),
[string]$Repo = $(if ($env:GITEA_REPO) { $env:GITEA_REPO } else { "lightOps" }),
[Parameter(Mandatory = $true)][string]$Tag,
[string]$Title = "",
[string]$Notes = "",
[string]$Target = "main",
[Parameter(Mandatory = $true)][string[]]$Package,
[switch]$NoCreateTag,
[switch]$NoPushTag,
[switch]$Prerelease,
[switch]$SkipCertificateCheck
)
$ErrorActionPreference = "Stop"
if (-not $env:GITEA_TOKEN) {
throw "必须设置环境变量 GITEA_TOKEN"
}
if (-not $Title) {
$Title = $Tag
}
foreach ($item in $Package) {
if (-not (Test-Path -LiteralPath $item -PathType Leaf)) {
throw "发布包不存在:$item"
}
}
function Write-Step {
param([string]$Message)
Write-Host "[LightOps] $Message" -ForegroundColor Green
}
function Api-Url {
param([string]$Path)
"$($GiteaUrl.TrimEnd('/'))/api/v1/repos/$Owner/$Repo/$Path"
}
function Invoke-Gitea {
param(
[string]$Method,
[string]$Uri,
$Body = $null,
[string]$ContentType = "application/json"
)
$headers = @{ Authorization = "token $env:GITEA_TOKEN" }
$args = @{
Method = $Method
Uri = $Uri
Headers = $headers
}
if ($Body -ne $null) {
$args.Body = $Body
$args.ContentType = $ContentType
}
if ($SkipCertificateCheck) {
$args.SkipCertificateCheck = $true
}
Invoke-RestMethod @args
}
if (-not $NoCreateTag) {
git rev-parse $Tag *> $null
if ($LASTEXITCODE -ne 0) {
Write-Step "创建本地 tag$Tag"
git tag -a $Tag $Target -m $Title
if ($LASTEXITCODE -ne 0) {
throw "创建 tag 失败"
}
}
}
if (-not $NoPushTag) {
Write-Step "推送 tag$Tag"
git push origin $Tag
if ($LASTEXITCODE -ne 0) {
throw "推送 tag 失败"
}
}
$release = $null
try {
$release = Invoke-Gitea -Method Get -Uri (Api-Url "releases/tags/$Tag")
Write-Step "Release 已存在:$Tag"
} catch {
Write-Step "创建 Release$Tag"
$payload = @{
tag_name = $Tag
target_commitish = $Target
name = $Title
body = $Notes
draft = $false
prerelease = [bool]$Prerelease
} | ConvertTo-Json -Depth 5
$release = Invoke-Gitea -Method Post -Uri (Api-Url "releases") -Body $payload
}
foreach ($item in $Package) {
$file = Get-Item -LiteralPath $item
Write-Step "上传附件:$($file.Name)"
$headers = @{ Authorization = "token $env:GITEA_TOKEN" }
$uri = "$(Api-Url "releases/$($release.id)/assets")?name=$([uri]::EscapeDataString($file.Name))"
$form = @{ attachment = $file }
$args = @{
Method = "Post"
Uri = $uri
Headers = $headers
Form = $form
}
if ($SkipCertificateCheck) {
$args.SkipCertificateCheck = $true
}
Invoke-RestMethod @args | Out-Null
}
Write-Host ""
Write-Host "Release 发布完成:$($GiteaUrl.TrimEnd('/'))/$Owner/$Repo/releases/tag/$Tag"

186
scripts/publish-gitea-release.sh Executable file
View File

@@ -0,0 +1,186 @@
#!/usr/bin/env bash
set -Eeuo pipefail
GITEA_URL="${GITEA_URL:-https://gitea.kmux.cn}"
OWNER="${GITEA_OWNER:-Eeveid}"
REPO="${GITEA_REPO:-lightOps}"
TAG=""
TITLE=""
NOTES=""
TARGET="main"
CREATE_TAG="true"
PUSH_TAG="true"
PRERELEASE="false"
PACKAGES=()
usage() {
cat <<'EOF'
LightOps Gitea Release 自动发布脚本
用法:
GITEA_TOKEN=<token> bash scripts/publish-gitea-release.sh --tag v0.1.0 --package target/releases/lightops.tar.gz
选项:
--gitea-url <url> Gitea 地址,默认 https://gitea.kmux.cn
--owner <owner> 仓库所有者,默认 Eeveid
--repo <repo> 仓库名,默认 lightOps
--tag <tag> 发布标签,例如 v0.1.0,必填
--title <title> Release 标题,默认等于 tag
--notes <text> Release 说明
--target <ref> tag 指向的分支或提交,默认 main
--package <path> 要上传的发布包,可重复传入
--no-create-tag 不自动创建本地 tag
--no-push-tag 不自动推送 tag
--prerelease 标记为预发布
-h, --help 显示帮助
环境变量:
GITEA_TOKEN Gitea Access Token必须具备仓库 Release 写入权限
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--gitea-url)
GITEA_URL="${2:?缺少 --gitea-url 参数值}"
shift 2
;;
--owner)
OWNER="${2:?缺少 --owner 参数值}"
shift 2
;;
--repo)
REPO="${2:?缺少 --repo 参数值}"
shift 2
;;
--tag)
TAG="${2:?缺少 --tag 参数值}"
shift 2
;;
--title)
TITLE="${2:?缺少 --title 参数值}"
shift 2
;;
--notes)
NOTES="${2:?缺少 --notes 参数值}"
shift 2
;;
--target)
TARGET="${2:?缺少 --target 参数值}"
shift 2
;;
--package)
PACKAGES+=("${2:?缺少 --package 参数值}")
shift 2
;;
--no-create-tag)
CREATE_TAG="false"
shift
;;
--no-push-tag)
PUSH_TAG="false"
shift
;;
--prerelease)
PRERELEASE="true"
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "未知参数:$1" >&2
usage >&2
exit 2
;;
esac
done
log() {
printf '\033[1;32m[LightOps]\033[0m %s\n' "$*"
}
fail() {
printf '\033[1;31m[LightOps]\033[0m %s\n' "$*" >&2
exit 1
}
require_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "缺少命令:$1"
}
json_escape() {
python -c 'import json,sys; print(json.dumps(sys.stdin.read()))'
}
api_url() {
printf '%s/api/v1/repos/%s/%s/%s' "${GITEA_URL%/}" "$OWNER" "$REPO" "$1"
}
api_get() {
curl -fsS -H "Authorization: token ${GITEA_TOKEN}" "$1"
}
api_post_json() {
curl -fsS -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "$2" \
"$1"
}
release_id_from_json() {
python -c 'import json,sys; print(json.load(sys.stdin)["id"])'
}
[[ -n "$TAG" ]] || fail "必须指定 --tag"
[[ -n "${GITEA_TOKEN:-}" ]] || fail "必须设置 GITEA_TOKEN"
[[ ${#PACKAGES[@]} -gt 0 ]] || fail "至少指定一个 --package"
[[ -n "$TITLE" ]] || TITLE="$TAG"
require_cmd curl
require_cmd git
require_cmd python
for package in "${PACKAGES[@]}"; do
[[ -f "$package" ]] || fail "发布包不存在:$package"
done
if [[ "$CREATE_TAG" == "true" ]] && ! git rev-parse "$TAG" >/dev/null 2>&1; then
log "创建本地 tag$TAG"
git tag -a "$TAG" "$TARGET" -m "$TITLE"
fi
if [[ "$PUSH_TAG" == "true" ]]; then
log "推送 tag$TAG"
git push origin "$TAG"
fi
release_url="$(api_url "releases/tags/${TAG}")"
release_json=""
if release_json="$(api_get "$release_url" 2>/dev/null)"; then
log "Release 已存在:$TAG"
else
log "创建 Release$TAG"
title_json="$(printf '%s' "$TITLE" | json_escape)"
notes_json="$(printf '%s' "$NOTES" | json_escape)"
payload="{\"tag_name\":\"${TAG}\",\"target_commitish\":\"${TARGET}\",\"name\":${title_json},\"body\":${notes_json},\"draft\":false,\"prerelease\":${PRERELEASE}}"
release_json="$(api_post_json "$(api_url releases)" "$payload")"
fi
release_id="$(printf '%s' "$release_json" | release_id_from_json)"
for package in "${PACKAGES[@]}"; do
name="$(basename "$package")"
log "上传附件:$name"
curl -fsS -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-F "attachment=@${package}" \
"$(api_url "releases/${release_id}/assets")?name=${name}" >/dev/null
done
cat <<EOF
Release 发布完成:
${GITEA_URL%/}/${OWNER}/${REPO}/releases/tag/${TAG}
EOF