feat(agent): standalone Linux agent binary + agent assets in update chain
New entry cmd/xui-agent links only the agent dependency closure (internal/agent + config/logger/selfupdate/xray/util leaves), a drop-in replacement for the dual-mode binary's agent subcommand: same invocation (xui-agent agent -config ...), same agent.json and state files, same wire protocol. selfupdate gains AgentAssetName (x-ui-agent-linux-<arch>); the agent's one-click update now downloads the agent asset instead of the panel binary. Pre-split agents keep fetching the panel asset — the two forms are interchangeable at the install path.
This commit is contained in:
@@ -151,7 +151,12 @@ func (a *Agent) runUpdate(intent updateIntent) {
|
||||
fail(fmt.Errorf("locate own binary: %w", err))
|
||||
return
|
||||
}
|
||||
asset, err := selfupdate.AssetName(runtime.GOARCH)
|
||||
// Agent/panel split (phase 1): the agent downloads the standalone
|
||||
// agent asset, not the panel binary. An agent build predating the
|
||||
// split still asks for the panel asset (AssetName) — the two are
|
||||
// interchangeable at the same path, so each generation converges
|
||||
// on the form its own build requests.
|
||||
asset, err := selfupdate.AgentAssetName(runtime.GOARCH)
|
||||
if err != nil {
|
||||
fail(err)
|
||||
return
|
||||
|
||||
@@ -22,7 +22,9 @@ import (
|
||||
|
||||
func fakeReleaseServer(t *testing.T, payload []byte, goodSum bool, hits *atomic.Int32) *httptest.Server {
|
||||
t.Helper()
|
||||
asset, err := selfupdate.AssetName(runtime.GOARCH)
|
||||
// The agent requests the standalone agent asset (agent/panel
|
||||
// split); the fake release only serves that name.
|
||||
asset, err := selfupdate.AgentAssetName(runtime.GOARCH)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@ import (
|
||||
)
|
||||
|
||||
// DefaultBaseURL is the release download base of the fork's own Gitea.
|
||||
// A release tag directory hangs directly below it:
|
||||
// <base><tag>/x-ui-linux-<arch> plus a "<asset>.sha256" sidecar.
|
||||
// A release tag directory hangs directly below it, holding the panel
|
||||
// assets (<base><tag>/x-ui-linux-<arch>) and — since the agent/panel
|
||||
// split — the standalone agent assets (x-ui-agent-linux-<arch>),
|
||||
// each with a "<asset>.sha256" sidecar.
|
||||
const DefaultBaseURL = "https://git.881218.xyz/rogue/3x-ui-agent/releases/download/"
|
||||
|
||||
// LatestReleaseAPI is the Gitea API endpoint for the newest release of the
|
||||
@@ -41,7 +43,9 @@ func BaseURL() string {
|
||||
return DefaultBaseURL
|
||||
}
|
||||
|
||||
// AssetName maps a GOARCH to the release asset name published for it.
|
||||
// AssetName maps a GOARCH to the release asset name published for it:
|
||||
// the panel (dual-mode) binary. The panel's own native self-update
|
||||
// downloads this asset; pre-split agents consumed it too.
|
||||
func AssetName(goarch string) (string, error) {
|
||||
switch goarch {
|
||||
case "amd64":
|
||||
@@ -53,6 +57,24 @@ func AssetName(goarch string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// AgentAssetName maps a GOARCH to the standalone agent asset published
|
||||
// since the agent/panel split (phase 1). A split agent's one-click
|
||||
// update downloads this asset — never the panel binary — so a node
|
||||
// keeps its slim form across updates. Releases predating the split do
|
||||
// not carry these assets; an intent pointing at such a tag fails the
|
||||
// download and the agent keeps running its old binary (the designed
|
||||
// failure mode of runUpdate).
|
||||
func AgentAssetName(goarch string) (string, error) {
|
||||
switch goarch {
|
||||
case "amd64":
|
||||
return "x-ui-agent-linux-amd64", nil
|
||||
case "arm64":
|
||||
return "x-ui-agent-linux-arm64", nil
|
||||
default:
|
||||
return "", fmt.Errorf("no agent release asset for architecture %q", goarch)
|
||||
}
|
||||
}
|
||||
|
||||
// LatestReleaseURLEnv overrides the release-lookup endpoint as a whole
|
||||
// (mirrors, tests), analogous to BaseURLEnv.
|
||||
const LatestReleaseURLEnv = "XUI_RELEASE_LATEST_URL"
|
||||
|
||||
@@ -47,6 +47,18 @@ func TestAssetName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentAssetName(t *testing.T) {
|
||||
if name, err := AgentAssetName("amd64"); err != nil || name != "x-ui-agent-linux-amd64" {
|
||||
t.Fatalf("amd64: %q, %v", name, err)
|
||||
}
|
||||
if name, err := AgentAssetName("arm64"); err != nil || name != "x-ui-agent-linux-arm64" {
|
||||
t.Fatalf("arm64: %q, %v", name, err)
|
||||
}
|
||||
if _, err := AgentAssetName("riscv64"); err == nil {
|
||||
t.Fatal("unknown arch must error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwapExecutableKeepsBackup(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
exe := filepath.Join(dir, "xui-agent")
|
||||
|
||||
Reference in New Issue
Block a user