Files

173 lines
4.0 KiB
Go

package distros
import (
"os"
"path/filepath"
"strings"
)
const (
obsRepoDebian13 = "Debian_13"
obsRepoDebianTesting = "Debian_Testing"
obsRepoDebianUnstable = "Debian_Unstable"
)
func debianOBSRepoVersion(info *OSInfo, aptSuites []string) string {
hasSuite := make(map[string]bool, len(aptSuites))
for _, suite := range aptSuites {
hasSuite[normalizeDebianSuite(suite)] = true
}
// APT suites distinguish testing from sid. os-release cannot: both currently
// report VERSION_CODENAME=forky and PRETTY_NAME="Debian GNU/Linux forky/sid"
if hasSuite["sid"] || hasSuite["unstable"] {
return obsRepoDebianUnstable
}
if hasSuite["testing"] || hasSuite["forky"] {
return obsRepoDebianTesting
}
if hasSuite["trixie"] || hasSuite["stable"] {
return obsRepoDebian13
}
if info == nil {
return obsRepoDebian13
}
id := strings.ToLower(strings.TrimSpace(info.VersionID))
code := strings.ToLower(strings.TrimSpace(info.VersionCodename))
if code == "sid" || id == "sid" {
return obsRepoDebianUnstable
}
if id == "13" || code == "trixie" {
return obsRepoDebian13
}
if id != "" && id != "testing" {
return obsRepoDebian13
}
// Prefer Testing when os-release is the shared forky/sid string. Unstable
// OBS packages can require a newer glibc than Testing ships.
return obsRepoDebianTesting
}
func debianOBSListFileMatches(path, debianVersion string) bool {
data, err := os.ReadFile(path)
if err != nil {
return false
}
return debianOBSListPointsAt(string(data), debianVersion)
}
func debianOBSListPointsAt(contents, debianVersion string) bool {
return strings.Contains(contents, "/"+debianVersion+"/")
}
func readDebianAPTSuites() []string {
var chunks []string
if data, err := os.ReadFile("/etc/apt/sources.list"); err == nil {
chunks = append(chunks, string(data))
}
matches, _ := filepath.Glob("/etc/apt/sources.list.d/*")
for _, path := range matches {
if !strings.HasSuffix(path, ".list") && !strings.HasSuffix(path, ".sources") {
continue
}
data, err := os.ReadFile(path)
if err != nil {
continue
}
chunks = append(chunks, string(data))
}
return debianAPTSuites(strings.Join(chunks, "\n"))
}
func debianAPTSuites(sources string) []string {
seen := make(map[string]bool)
var suites []string
add := func(raw string) {
suite := normalizeDebianSuite(raw)
if suite == "" || seen[suite] {
return
}
seen[suite] = true
suites = append(suites, suite)
}
var stanza []string
flushStanza := func() {
if len(stanza) == 0 {
return
}
enabled := true
var stanzaSuites []string
for _, line := range stanza {
key, val, ok := strings.Cut(line, ":")
if !ok {
continue
}
switch strings.ToLower(strings.TrimSpace(key)) {
case "enabled":
switch strings.ToLower(strings.TrimSpace(val)) {
case "no", "false", "0":
enabled = false
}
case "suites":
stanzaSuites = strings.Fields(val)
}
}
if enabled {
for _, suite := range stanzaSuites {
add(suite)
}
}
stanza = nil
}
for _, raw := range strings.Split(sources, "\n") {
line := strings.TrimSpace(raw)
if line == "" {
flushStanza()
continue
}
if strings.HasPrefix(line, "#") {
continue
}
lower := strings.ToLower(line)
if strings.HasPrefix(lower, "deb ") || strings.HasPrefix(lower, "deb-src ") {
flushStanza()
if suite := suiteFromDebLine(line); suite != "" {
add(suite)
}
continue
}
stanza = append(stanza, line)
}
flushStanza()
return suites
}
func suiteFromDebLine(line string) string {
if i := strings.Index(line, "["); i != -1 {
if j := strings.Index(line, "]"); j > i {
line = strings.TrimSpace(line[:i] + " " + line[j+1:])
}
}
fields := strings.Fields(line)
if len(fields) < 3 {
return ""
}
return fields[2]
}
func normalizeDebianSuite(suite string) string {
suite = strings.ToLower(strings.TrimSpace(suite))
suite = strings.Trim(suite, "/")
for _, suffix := range []string{"-security", "-updates", "-backports", "-proposed-updates"} {
if strings.HasSuffix(suite, suffix) {
return strings.TrimSuffix(suite, suffix)
}
}
return suite
}