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:
2026-11-08 23:28:47 +01:00
co-authored by Claude Sonnet 5.5
parent f4a192d416
commit 3d5ec77f57
5 changed files with 56 additions and 23 deletions
+12 -6
View File
@@ -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 {