Unverified Commit 3ad817ee authored by Lemon-miaow's avatar Lemon-miaow
Browse files

fix(update): 重跑命令从最新 release tag 读取安装脚本

parent 26dc1722
Loading
Loading
Loading
Loading
+34 −3
Changes for cmd/felis/update.go: 34 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -50,10 +50,13 @@ type updateTarget struct {
// and even on the bootstrap path it re-images felis-api from the binary setup is already
// running (FELIS_BOOTSTRAP_BINARY), which looks like an update and changes nothing.
//
// The URL is the same one-liner both READMEs hand out. While the repo is private it
// The URL is the one-liner both READMEs hand out, read at a tag rather than main: the
// script's release channel installs the newest release's binary, and main can carry
// installer changes that binary was never tested with. installerRef picks the tag and
// renderApplyGuidance substitutes it for {ref}. While the repo is private the URL
// answers 404 (raw.githubusercontent.com hides private repos), which is why the trailer
// below points at the README's token'd form for that case.
const installerRerun = "curl -fsSL https://raw.githubusercontent.com/FelisMC/Felis/main/deploy/bootstrap.sh | sudo bash"
const installerRerun = "curl -fsSL https://raw.githubusercontent.com/FelisMC/Felis/{ref}/deploy/bootstrap.sh | sudo bash"

// updateTargets is the selector table. panel and plugins both resolve to felis-api
// because they are not separately versioned: the panel is compiled into the felis
@@ -258,7 +261,7 @@ func renderApplyGuidance(res updater.Result, selected map[string]bool, force boo
			// component, and reinstalling the current release is a valid repair action.
			fmt.Fprintf(&b, "  note: cannot tell whether %s is current — its latest version could not be discovered (see above); this reinstalls it either way\n", t.component)
		}
		fmt.Fprintf(&b, "  run: %s\n", t.command)
		fmt.Fprintf(&b, "  run: %s\n", strings.ReplaceAll(t.command, "{ref}", installerRef(byComponent)))
		offeredCommand = true
	}
	// Only explain the command when one was actually offered; a --mc-only run has
@@ -277,3 +280,31 @@ func renderApplyGuidance(res updater.Result, selected map[string]bool, force boo
	}
	return b.String()
}

// installerRef is the git ref the installer re-run reads bootstrap.sh from: the newest
// stable felis release when the feed answered, which is the release that script then
// installs; else the release this host runs; main only when neither is a release tag.
func installerRef(byComponent map[string]updates.Action) string {
	a, ok := byComponent["felis-api"]
	if !ok {
		return "main"
	}
	if a.LatestKnown && isReleaseTag(a.Latest) {
		return a.Latest.String()
	}
	if isReleaseTag(a.Current) {
		return a.Current.String()
	}
	return "main"
}

// isReleaseTag reports whether v was read from a stable vX.Y.Z tag, the only refs
// release.yml publishes a binary for.
func isReleaseTag(v updates.Version) bool {
	s := v.String()
	if !strings.HasPrefix(s, "v") || v.IsPrerelease() {
		return false
	}
	_, err := updates.Parse(s)
	return err == nil
}
+32 −0
Changes for cmd/felis/update_test.go: 32 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -167,3 +167,35 @@ func TestApplyGuidancePointsEveryComponentAtTheInstaller(t *testing.T) {
		t.Fatalf("--mc offers no command; the trailer is a non-sequitur:\n%s", mc)
	}
}

// The re-run reads bootstrap.sh at the tag whose binary it installs. main can carry
// installer changes no release was tested with.
func TestApplyGuidanceReadsTheInstallerAtTheReleaseTag(t *testing.T) {
	v := func(s string) updates.Version {
		t.Helper()
		out, err := updates.Parse(s)
		if err != nil {
			t.Fatal(err)
		}
		return out
	}
	cases := []struct {
		name string
		api  []updates.Action
		want string
	}{
		{"latest known", []updates.Action{{Component: "felis-api", Kind: updates.ActionNotify, Current: v("v1.3.0"), Latest: v("v1.4.0"), LatestKnown: true}}, "/FelisMC/Felis/v1.4.0/deploy/bootstrap.sh"},
		{"latest unknown", []updates.Action{{Component: "felis-api", Kind: updates.ActionNone, Current: v("v1.3.0")}}, "/FelisMC/Felis/v1.3.0/deploy/bootstrap.sh"},
		{"prerelease latest", []updates.Action{{Component: "felis-api", Kind: updates.ActionNone, Current: v("v1.3.0"), Latest: v("v1.4.0-rc.1"), LatestKnown: true}}, "/FelisMC/Felis/v1.3.0/deploy/bootstrap.sh"},
		{"nothing known", nil, "/FelisMC/Felis/main/deploy/bootstrap.sh"},
	}
	for _, c := range cases {
		out := renderApplyGuidance(planResult(c.api), map[string]bool{"velocity": true, "panel": true}, true)
		if !strings.Contains(out, c.want) {
			t.Errorf("%s: want %q in:\n%s", c.name, c.want, out)
		}
		if strings.Contains(out, "{ref}") {
			t.Errorf("%s: placeholder left in:\n%s", c.name, out)
		}
	}
}