forked from Eeveid/lightOps
新增 Gitea 自动发布流程
This commit is contained in:
51
.gitea/workflows/release.yml
Normal file
51
.gitea/workflows/release.yml
Normal 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
|
||||
31
README.md
31
README.md
@@ -73,6 +73,37 @@ lightops/
|
||||
|
||||
推荐生产环境使用“发布包安装”。目标服务器只需要下载发布包、解压并注册 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 上生成发布包:
|
||||
|
||||
```bash
|
||||
|
||||
120
scripts/publish-gitea-release.ps1
Normal file
120
scripts/publish-gitea-release.ps1
Normal 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
186
scripts/publish-gitea-release.sh
Executable 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
|
||||
Reference in New Issue
Block a user