mirror of
https://github.com/RuoJi6/ARTEX.git
synced 2026-10-27 17:03:49 +08:00
feat(update): publish fork releases for online self-update
This commit is contained in:
1 parent
e6ec569991
commit
714b00cccb
4 files changed
+173
-13
No files matched your search
@@ -1,17 +1,53 @@
|
||||
name: release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "已存在的正式版本标签,例如 v0.3.16"
|
||||
required: true
|
||||
type: string
|
||||
push:
|
||||
tags: ["v*"]
|
||||
|
||||
permissions:
|
||||
contents: write # 创建 Release 需要
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: release-${{ inputs.tag || github.ref_name }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
tag: ${{ steps.version.outputs.tag }}
|
||||
commit: ${{ steps.commit.outputs.sha }}
|
||||
steps:
|
||||
- name: Validate release version
|
||||
id: version
|
||||
env:
|
||||
RELEASE_TAG: ${{ inputs.tag || github.ref_name }}
|
||||
run: |
|
||||
if [[ ! "$RELEASE_TAG" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then
|
||||
echo "正式版本必须是 v主版本.次版本.修订号,例如 v0.3.16" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: refs/tags/${{ steps.version.outputs.tag }}
|
||||
- name: Pin release commit
|
||||
id: commit
|
||||
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# ① 前端静态导出(一次),产物给交叉编译各平台复用
|
||||
frontend:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.prepare.outputs.commit }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "22"
|
||||
@@ -28,7 +64,7 @@ jobs:
|
||||
|
||||
# ② 五平台交叉编译(内嵌前端)+ 打 zip
|
||||
binaries:
|
||||
needs: frontend
|
||||
needs: [prepare, frontend]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -40,9 +76,11 @@ jobs:
|
||||
- { goos: windows, goarch: amd64 }
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.prepare.outputs.commit }}
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.26"
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -52,7 +90,7 @@ jobs:
|
||||
env:
|
||||
ARTEX_TARGET_OS: ${{ matrix.goos }}
|
||||
ARTEX_TARGET_ARCH: ${{ matrix.goarch }}
|
||||
ARTEX_BUILD_VERSION: ${{ github.ref_name }}
|
||||
ARTEX_BUILD_VERSION: ${{ needs.prepare.outputs.tag }}
|
||||
ARTEX_SKIP_FRONTEND: "1"
|
||||
ARTEX_SKIP_NPM_CI: "1"
|
||||
# UPX 自解压 ELF 在部分 Linux 内核、虚拟化和安全策略下会崩溃。
|
||||
@@ -78,8 +116,14 @@ jobs:
|
||||
|
||||
# ③ 汇总所有 zip → 创建 GitHub Release
|
||||
release:
|
||||
needs: binaries
|
||||
needs: [prepare, binaries]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
RELEASE_TAG: ${{ needs.prepare.outputs.tag }}
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -88,21 +132,48 @@ jobs:
|
||||
path: dist
|
||||
- name: Generate checksums
|
||||
working-directory: dist
|
||||
run: sha256sum *.zip > SHA256SUMS
|
||||
run: |
|
||||
version="${RELEASE_TAG#v}"
|
||||
for target in linux-amd64 linux-arm64 darwin-amd64 darwin-arm64 windows-amd64; do
|
||||
test -s "artex-${version}-${target}.zip"
|
||||
done
|
||||
sha256sum *.zip > SHA256SUMS
|
||||
sha256sum --check SHA256SUMS
|
||||
- name: Refuse to overwrite a published release
|
||||
run: |
|
||||
if gh release view "$RELEASE_TAG" --json isDraft > /tmp/artex-release.json 2>/tmp/artex-release.err; then
|
||||
test "$(jq -r .isDraft /tmp/artex-release.json)" = true || {
|
||||
echo "该版本已发布,请使用新的版本标签" >&2
|
||||
exit 1
|
||||
}
|
||||
elif ! grep -qi 'release not found\|HTTP 404' /tmp/artex-release.err; then
|
||||
cat /tmp/artex-release.err >&2
|
||||
exit 1
|
||||
fi
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ needs.prepare.outputs.tag }}
|
||||
target_commitish: ${{ needs.prepare.outputs.commit }}
|
||||
draft: true
|
||||
fail_on_unmatched_files: true
|
||||
files: |
|
||||
dist/*.zip
|
||||
dist/SHA256SUMS
|
||||
generate_release_notes: true
|
||||
- name: Publish complete release
|
||||
run: gh release edit "$RELEASE_TAG" --draft=false --latest
|
||||
|
||||
# ④ 多架构镜像 → 推 Docker Hub(复用 binaries 编好的 Linux 二进制,
|
||||
# 镜像里只装工具 + 放二进制;arm64 仅需模拟 apt 层,构建快很多)
|
||||
docker:
|
||||
needs: binaries
|
||||
# 页面更新只需要 Release;镜像发布按需启用,不依赖原作者的账号。
|
||||
if: vars.ARTEX_DOCKER_IMAGE != ''
|
||||
needs: [prepare, binaries]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4 # 取 Dockerfile 与 skills/
|
||||
with:
|
||||
ref: ${{ needs.prepare.outputs.commit }}
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: bin-linux-amd64
|
||||
@@ -120,9 +191,9 @@ jobs:
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
with:
|
||||
images: autumn27/artex
|
||||
images: ${{ vars.ARTEX_DOCKER_IMAGE }}
|
||||
tags: |
|
||||
type=ref,event=tag
|
||||
type=raw,value=${{ needs.prepare.outputs.tag }}
|
||||
type=raw,value=latest
|
||||
- uses: docker/build-push-action@v6
|
||||
with:
|
||||
|
||||
@@ -100,7 +100,7 @@ docker compose up -d # 拉取 autumn27/artex 镜像 + postgres
|
||||
|
||||
### 方式三:下载预编译二进制(Releases)
|
||||
|
||||
到 [Releases](https://github.com/Autumn-27/ARTEX/releases) 下载对应平台的 zip,解压后得到 `artex` + `start.sh`(Windows 为 `start.bat`)+ `skills/` + `config.example.json`:
|
||||
到本仓库的 [Releases](https://github.com/RuoJi6/ARTEX/releases) 下载对应平台的 zip,解压后得到 `artex` + `start.sh`(Windows 为 `start.bat`)+ `skills/` + `config.example.json`。Linux ARM64(包括 ARM64 Kali)选择 `linux-arm64`,x86_64 选择 `linux-amd64`:
|
||||
|
||||
```bash
|
||||
cp config.example.json config.json # 填好 database 连接
|
||||
@@ -148,6 +148,8 @@ ARTEX_TARGETS=linux/amd64,windows/amd64 ./build.sh --release
|
||||
|
||||
在 **系统配置** 页(侧边栏「系统配置」→ `/system/settings`)的**版本与更新**卡片里,可以直接检查并安装新版本,无需登录服务器。
|
||||
|
||||
本仓库构建的程序固定从 `RuoJi6/ARTEX` 的最新正式 GitHub Release 检查更新,跳过草稿和预发布版本。旧版官方程序仍指向原作者仓库,因此首次切换必须手动安装本仓库构建的程序;以后发布更高的三段式版本即可通过页面升级。仅推送 main 或 Git 标签但未发布 Release,不会产生可在线安装的新版本。
|
||||
|
||||
点「更新」后:下载当前平台的发布包 → 比对 Release 的 `SHA256SUMS` → 用 `-h` 冒烟测试新二进制 → 暂存为 `artex.new` → 程序退出,由 `start.sh` / `start.bat` 重新拉起并完成换装。页面会自动等到新版本上线后刷新。
|
||||
|
||||
- **失败不会留下坏程序**:校验或冒烟不通过就丢弃暂存件、继续跑当前版本;换装后的新版若连续 3 次启动失败,会自动回滚到 `artex.old`(失败的那个留作 `artex.failed` 供排查)。
|
||||
@@ -157,6 +159,35 @@ ARTEX_TARGETS=linux/amd64,windows/amd64 ./build.sh --release
|
||||
- **Docker 下只换程序、不换镜像**:镜像里的 playwright / nmap 等工具链不会跟着升级,且 `docker compose up -d` 重建容器后会退回镜像自带的版本。要连镜像一起升级仍请用 `docker compose pull artex && docker compose up -d artex`。
|
||||
- 访问 GitHub 需要代理时,在同一页面配置**全局代理**即可,更新链路会走它。更新只从 GitHub 域名下载并强制 HTTPS。
|
||||
|
||||
### 维护者:通过 GitHub Actions 发布在线更新
|
||||
|
||||
发布工作流为 `.github/workflows/release.yml`。推送正式标签会触发构建,也可在 Actions → release → Run workflow 中输入已存在的标签重新执行失败的发布。标签必须是 `v主版本.次版本.修订号`,例如下例的 `v0.3.16`;后续版本必须高于用户正在运行的版本。
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
git tag v0.3.16
|
||||
git push origin v0.3.16
|
||||
```
|
||||
|
||||
工作流固定标签对应的提交,用 Node.js 22 导出前端,按照 `go.mod` 指定的 Go 版本编译内嵌前端的五个平台程序,并生成 ZIP 与 `SHA256SUMS`。全部发布包准备好后先上传到草稿 Release,上传成功才公开为最新正式版,避免页面检查到尚未上传完整的版本。已公开的同版本不允许覆盖,应发布新标签。
|
||||
|
||||
页面在线更新所需的发布只使用仓库自带 `GITHUB_TOKEN`,无需 Docker Hub 凭据。Docker 镜像任务默认跳过;如需单独发布自己的镜像,设置仓库 Actions 变量 `ARTEX_DOCKER_IMAGE`(自己的 Docker Hub 镜像名)及 Secrets `DOCKERHUB_USERNAME`、`DOCKERHUB_TOKEN`。现有 Compose 文件仍指向原镜像,使用自有镜像时应自行修改镜像地址。
|
||||
|
||||
首次迁移已有 Docker 部署时,将已校验的新 ARM64 ZIP 解压到宿主机,备份原程序,然后只替换已有容器中的 `/app/artex`。例如现有容器名为 `artex-artex-1`:
|
||||
|
||||
```bash
|
||||
# 在下载目录运行;先按 SHA256SUMS 核对下载的 ZIP,再解压。
|
||||
sha256sum --ignore-missing --check SHA256SUMS
|
||||
unzip artex-0.3.16-linux-arm64.zip
|
||||
sudo docker stop artex-artex-1
|
||||
sudo docker cp artex-artex-1:/app/artex ./artex-before-migration
|
||||
sudo docker cp ./artex-0.3.16-linux-arm64/artex artex-artex-1:/app/artex
|
||||
sudo docker start artex-artex-1
|
||||
curl --fail http://127.0.0.1:8787/api/health
|
||||
```
|
||||
|
||||
这会保留现有数据库、data 和 skills 挂载。登录系统配置页检查更新源与版本。容器内替换的程序仍属于容器可写层,删除或重建容器会丢失替换结果;需要重建时应使用包含本仓库程序的镜像。程序回滚不回退数据库迁移,升级前请备份数据库和 data。
|
||||
|
||||
### 方式二:一键更新脚本
|
||||
|
||||
```bash
|
||||
@@ -182,7 +213,7 @@ docker image prune -f # 清理旧镜像(可选)
|
||||
|
||||
### 方式四:预编译二进制(Releases)
|
||||
|
||||
到 [Releases](https://github.com/Autumn-27/ARTEX/releases) 下载新版本 zip,停掉旧进程后覆盖 `artex` 与 `skills/`(保留你的 `config.json` 与 `data/`),重启即可:
|
||||
到 [Releases](https://github.com/RuoJi6/ARTEX/releases) 下载新版本 zip,停掉旧进程后覆盖 `artex` 与 `skills/`(保留你的 `config.json` 与 `data/`),重启即可:
|
||||
|
||||
```bash
|
||||
cp -r <解压目录>/skills ./ && cp <解压目录>/artex ./
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
// Repo 是发布源。写死而不是做成配置项:更新源可配等于给任何能改配置的人一条
|
||||
// 远程代码执行通道,对一个渗透测试平台来说这个口子开不得。
|
||||
const Repo = "Autumn-27/artex"
|
||||
const Repo = "RuoJi6/ARTEX"
|
||||
|
||||
// latestURL 是 GitHub 的"最新正式版"接口。它会自动跳过 prerelease 和 draft。
|
||||
const latestURL = "https://api.github.com/repos/" + Repo + "/releases/latest"
|
||||
@@ -107,7 +107,7 @@ func FetchLatest(ctx context.Context, c *http.Client) (*Release, error) {
|
||||
// 未认证的 GitHub API 是每 IP 每小时 60 次,共用出口 IP 时很容易撞上。
|
||||
return nil, fmt.Errorf("GitHub 接口限流(每小时 60 次),请稍后再试")
|
||||
case resp.StatusCode == http.StatusNotFound:
|
||||
return nil, fmt.Errorf("仓库 %s 尚未发布任何正式版本", Repo)
|
||||
return nil, fmt.Errorf("仓库 %s 不可访问或尚未发布正式版本", Repo)
|
||||
case resp.StatusCode != http.StatusOK:
|
||||
return nil, fmt.Errorf("GitHub 返回 %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package selfupdate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type releaseTransport func(*http.Request) (*http.Response, error)
|
||||
|
||||
func (f releaseTransport) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
|
||||
|
||||
func TestFetchLatestUsesForkRelease(t *testing.T) {
|
||||
client := &http.Client{Transport: releaseTransport(func(r *http.Request) (*http.Response, error) {
|
||||
if got := r.URL.String(); got != "https://api.github.com/repos/RuoJi6/ARTEX/releases/latest" {
|
||||
t.Fatalf("unexpected update source: %s", got)
|
||||
}
|
||||
if r.Method != http.MethodGet || r.Header.Get("Accept") != "application/vnd.github+json" {
|
||||
t.Fatalf("invalid GitHub request: %s %v", r.Method, r.Header)
|
||||
}
|
||||
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader(`{
|
||||
"tag_name":"v0.3.16",
|
||||
"assets":[
|
||||
{"name":"artex-0.3.16-linux-arm64.zip","browser_download_url":"https://github.com/RuoJi6/ARTEX/releases/download/v0.3.16/artex-0.3.16-linux-arm64.zip"},
|
||||
{"name":"SHA256SUMS","browser_download_url":"https://github.com/RuoJi6/ARTEX/releases/download/v0.3.16/SHA256SUMS"}
|
||||
]
|
||||
}`))}, nil
|
||||
})}
|
||||
rel, err := FetchLatest(context.Background(), client)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, name := range []string{AssetName(rel.TagName, "linux", "arm64"), sumsAsset} {
|
||||
asset, ok := rel.FindAsset(name)
|
||||
if !ok {
|
||||
t.Fatalf("missing release asset %s", name)
|
||||
}
|
||||
body, err := get(context.Background(), &http.Client{Transport: releaseTransport(func(r *http.Request) (*http.Response, error) {
|
||||
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader("asset"))}, nil
|
||||
})}, asset.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("fork asset URL rejected: %v", err)
|
||||
}
|
||||
body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchLatestMissingRelease(t *testing.T) {
|
||||
client := &http.Client{Transport: releaseTransport(func(*http.Request) (*http.Response, error) {
|
||||
return &http.Response{StatusCode: http.StatusNotFound, Body: io.NopCloser(strings.NewReader("{}"))}, nil
|
||||
})}
|
||||
_, err := FetchLatest(context.Background(), client)
|
||||
if err == nil || !strings.Contains(err.Error(), "RuoJi6/ARTEX") || !strings.Contains(err.Error(), "不可访问或尚未发布") {
|
||||
t.Fatalf("unexpected missing-release error: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user