Follow the channel server's redirect and offer the stable releases
channels.nixos.org answers <branch>/git-revision with a redirect, which router-upgrade check did not follow: on a real router it saw an empty answer. The test channel is now an HTTP server that redirects the same way. The page labelled the running release "stable", but nixos-unstable already calls itself the next release (26.11 does not exist as a branch yet). It offers none, unstable and the stable releases listed in the code (26.05). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
This commit is contained in:
@@ -145,12 +145,18 @@ type upgradeData struct {
|
||||
Pending bool // a system waits for confirmation
|
||||
}
|
||||
|
||||
// stableChannels are the stable NixOS releases the page offers (the branch
|
||||
// nixos-26.05 and so on). They are listed here until the router can ask the
|
||||
// channel server which exist; the release a router runs says nothing about it:
|
||||
// nixos-unstable already calls itself the release that comes next.
|
||||
var stableChannels = []string{"26.05"}
|
||||
|
||||
// channelChoices are the branches worth offering: none (the packages that came
|
||||
// with the release), unstable and the running release; NixOS cannot go back
|
||||
// to an older one. A configured channel that is none of these stays listed.
|
||||
func channelChoices(version, current string) []channelChoice {
|
||||
// with the release), unstable and the stable releases. A configured channel
|
||||
// that is none of these stays listed.
|
||||
func channelChoices(current string) []channelChoice {
|
||||
choices := []channelChoice{{"", "None: the packages that came with this release of nixos-router"}, {"unstable", "unstable (newest, may break)"}}
|
||||
if r := releaseOf(version); r != "" {
|
||||
for _, r := range stableChannels {
|
||||
choices = append(choices, channelChoice{r, r + " (stable)"})
|
||||
}
|
||||
if current != "" && !slices.ContainsFunc(choices, func(c channelChoice) bool { return c.Value == current }) {
|
||||
@@ -166,7 +172,7 @@ func (a *app) upgradeData() upgradeData {
|
||||
if s, err := loadSettings(a.sys.settingsPath()); err == nil {
|
||||
d.Channel = s.channel()
|
||||
}
|
||||
d.Choices = channelChoices(d.Version, d.Channel)
|
||||
d.Choices = channelChoices(d.Channel)
|
||||
// What router-upgrade printed: a finished one-shot unit has no journal to find.
|
||||
if data, err := os.ReadFile(filepath.Join(a.sys.upgradeDir(), "last.log")); err == nil {
|
||||
d.Run.Log = tail(string(data), 64<<10)
|
||||
@@ -204,7 +210,7 @@ func (a *app) upgradeChannel(w http.ResponseWriter, r *http.Request, _ session)
|
||||
return
|
||||
}
|
||||
if v != "" && !channelRe.MatchString(v) {
|
||||
a.upgradeBack(w, r, "Not saved: the channel is unstable or a release like 26.11.")
|
||||
a.upgradeBack(w, r, "Not saved: the channel is unstable or a release like 26.05.")
|
||||
return
|
||||
}
|
||||
if err := a.sys.setChannel(v); err != nil {
|
||||
|
||||
@@ -24,14 +24,14 @@ func TestReleaseOfAndChannelChoices(t *testing.T) {
|
||||
}
|
||||
return strings.Join(v, ",")
|
||||
}
|
||||
if got := values(channelChoices("26.11.2026", "")); got != ",unstable,26.11" {
|
||||
if got := values(channelChoices("")); got != ",unstable,26.05" {
|
||||
t.Fatalf("choices: %q", got)
|
||||
}
|
||||
// A configured channel that is not offered (a release that is gone) is not lost.
|
||||
if got := values(channelChoices("26.11.2026", "27.05")); got != ",unstable,26.11,27.05" {
|
||||
if got := values(channelChoices("25.11")); got != ",unstable,26.05,25.11" {
|
||||
t.Fatalf("choices: %q", got)
|
||||
}
|
||||
if got := values(channelChoices("26.11.2026", "unstable")); got != ",unstable,26.11" {
|
||||
if got := values(channelChoices("unstable")); got != ",unstable,26.05" {
|
||||
t.Fatalf("choices: %q", got)
|
||||
}
|
||||
}
|
||||
@@ -119,13 +119,13 @@ func TestUpgradePage(t *testing.T) {
|
||||
if resp := post("/upgrade/channel", url.Values{}); !strings.Contains(resp.Header.Get("Location"), "no+channel") {
|
||||
t.Fatalf("%s", resp.Header.Get("Location"))
|
||||
}
|
||||
post("/upgrade/channel", url.Values{"channel": {"26.11"}})
|
||||
post("/upgrade/channel", url.Values{"channel": {"26.05"}})
|
||||
post("/upgrade/channel", url.Values{})
|
||||
s, _ := loadSettings(sys.settingsPath())
|
||||
if s.channel() != "26.11" {
|
||||
if s.channel() != "26.05" {
|
||||
t.Fatalf("channel %q", s.channel())
|
||||
}
|
||||
if !strings.Contains(page(), `value="26.11" selected`) {
|
||||
if !strings.Contains(page(), `value="26.05" selected`) {
|
||||
t.Fatal("the saved channel is not selected")
|
||||
}
|
||||
})
|
||||
@@ -159,7 +159,7 @@ func TestUpgradePage(t *testing.T) {
|
||||
t.Fatal("the unit was started without a preview")
|
||||
}
|
||||
os.MkdirAll(a.sys.upgradeDir(), 0o700)
|
||||
os.WriteFile(filepath.Join(a.sys.upgradeDir(), "preview.json"), []byte(`{"channel":"26.11","rev":"0123456789abcdef0123456789abcdef01234567","version":"26.11.20261110.0123456","time":"2026-11-10T10:00:00Z","download":"(350.5 MiB download, 1.2 GiB unpacked)","builds":3,"rebootNeeded":true,"changes":[{"name":"frr","from":"10.1","to":"10.2"},{"name":"linux","from":"6.12.5","to":"6.12.8"}]}`), 0o600)
|
||||
os.WriteFile(filepath.Join(a.sys.upgradeDir(), "preview.json"), []byte(`{"channel":"26.05","rev":"0123456789abcdef0123456789abcdef01234567","version":"26.11.20261110.0123456","time":"2026-11-10T10:00:00Z","download":"(350.5 MiB download, 1.2 GiB unpacked)","builds":3,"rebootNeeded":true,"changes":[{"name":"frr","from":"10.1","to":"10.2"},{"name":"linux","from":"6.12.5","to":"6.12.8"}]}`), 0o600)
|
||||
body := page()
|
||||
for _, want := range []string{"frr", "10.1", "10.2", "linux", "350.5 MiB", "3 derivations", "after the next reboot", "Upgrade to 26.11.20261110.0123456"} {
|
||||
if !strings.Contains(body, want) {
|
||||
@@ -185,13 +185,13 @@ func TestUpgradePage(t *testing.T) {
|
||||
t.Run("a preview of another channel is not shown", func(t *testing.T) {
|
||||
post("/upgrade/channel", url.Values{"channel": {"unstable"}})
|
||||
if strings.Contains(page(), "linux") {
|
||||
t.Fatal("the preview of 26.11 is shown for unstable")
|
||||
t.Fatal("the preview of 26.05 is shown for unstable")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("a check from before the last upgrade is not shown", func(t *testing.T) {
|
||||
post("/upgrade/channel", url.Values{"channel": {"26.11"}})
|
||||
check := `{"channel":"26.11","branch":"nixos-26.11","time":"2026-11-09T10:00:00Z","running":{"rev":"aaa","version":"%s"},"newest":{"rev":"0123456789abcdef"},"upToDate":false}`
|
||||
post("/upgrade/channel", url.Values{"channel": {"26.05"}})
|
||||
check := `{"channel":"26.05","branch":"nixos-26.05","time":"2026-11-09T10:00:00Z","running":{"rev":"aaa","version":"%s"},"newest":{"rev":"0123456789abcdef"},"upToDate":false}`
|
||||
os.WriteFile(filepath.Join(a.sys.upgradeDir(), "check.json"), []byte(strings.Replace(check, "%s", "26.11.20261001.old", 1)), 0o600)
|
||||
if strings.Contains(page(), "A newer revision is available") {
|
||||
t.Fatal("an outdated check is shown")
|
||||
|
||||
Reference in New Issue
Block a user