func TestVersionRelationSatisfiedBy(t *testing.T) { for _, test := range []struct { Operator string Number string Version string Match bool }{ {"=", "1.0.0", "1.0.0", true}, {"=", "1.0.1", "1.0.0", false}, {"=", "1.0.0", "1.0.1", false}, {"<<", "2.0", "1.0", true}, {">>", "2.0", "1.0", false}, {">>", "2.0", "3.0", true}, {"<<", "2.0", "3.0", false}, {">=", "1.0~", "1.0", true}, {">=", "1.0~", "1.0.2", true}, {">=", "1.0~", "1.0.2.3", true}, {"<=", "1.0~", "1.0", false}, {"<=", "1.0~", "1.0.2", false}, {"<=", "1.0~", "1.0.2.3", false}, } { vr := dependency.VersionRelation{ Operator: test.Operator, Number: test.Number, } v, err := version.Parse(test.Version) assert(t, err == nil) assert(t, vr.SatisfiedBy(v) == test.Match) } }
func (v VersionRelation) SatisfiedBy(ver version.Version) bool { vVer, err := version.Parse(v.Number) if err != nil { return false } q := version.Compare(ver, vVer) switch v.Operator { case ">=": return q >= 0 case "<=": return q <= 0 case ">>": return q > 0 case "<<": return q < 0 case "=": return q == 0 } // XXX: WHAT THE SHIT return false }
func (repo Repo) ParseLogEntry(params []string) (*LogEntry, error) { if len(params) != 6 { return nil, fmt.Errorf("Unknown input string format") } version, err := version.Parse(params[3]) if err != nil { return nil, err } changes, err := control.ParseChangesFile(repo.Basedir + "/" + params[5]) if err != nil { return nil, err } return &LogEntry{ Action: params[0], Suite: params[1], Source: params[2], Version: version, Changes: *changes, }, nil }
func main() { flag.Parse() if flag.NArg() != 1 { log.Fatalf("Usage: %s [options] <path-to-changes-file>\n", os.Args[0]) } changesPath := flag.Arg(0) log.Printf("Loading changes file %q\n", changesPath) c, err := os.Open(changesPath) if err != nil { log.Fatal(err) } defer c.Close() changes, err := control.ParseChanges(bufio.NewReader(c), changesPath) if err != nil && err != io.EOF { log.Fatal(err) } binaries := make(map[string]bool) var debs []string for _, file := range changes.Files { if filepath.Ext(file.Filename) == ".deb" { debs = append(debs, file.Filename) } } for _, binary := range changes.Binaries { binaries[binary] = true } log.Printf(" - %d binary packages: %s\n", len(changes.Binaries), strings.Join(changes.Binaries, " ")) log.Printf(" - corresponding .debs (will be injected when building):\n") for _, deb := range debs { log.Printf(" %s\n", deb) } var sourcesPaths []string var packagesPaths []string indexTargets := exec.Command("apt-get", "indextargets", "--format", "$(FILENAME)", "Codename: sid", "ShortDesc: Sources") if lines, err := indexTargets.Output(); err == nil { for _, line := range strings.Split(string(lines), "\n") { trimmed := strings.TrimSpace(line) if trimmed != "" { sourcesPaths = append(sourcesPaths, line) } } binaryIndexTargets := exec.Command( "apt-get", "indextargets", "--format", "$(FILENAME)", "Codename: sid", "ShortDesc: Packages") lines, err = binaryIndexTargets.Output() if err != nil { log.Fatal("Could not get packages files using %+v: %v", binaryIndexTargets.Args, err) } for _, line := range strings.Split(string(lines), "\n") { trimmed := strings.TrimSpace(line) if trimmed != "" { packagesPaths = append(packagesPaths, line) } } } else { // Fallback for older versions of apt-get. See // https://bugs.debian.org/801594 for context. releaseMatches, err := filepath.Glob("/var/lib/apt/lists/*_InRelease") if err != nil { log.Fatal(err) } for _, releasepath := range releaseMatches { r, err := os.Open(releasepath) if err != nil { log.Fatal(err) } defer r.Close() release, err := control.ParseParagraph(bufio.NewReader(r)) if err != nil && err != io.EOF { log.Fatal(err) } if release.Values["Suite"] != "unstable" { continue } listsPrefix := listsPrefixRe.FindStringSubmatch(releasepath) if len(listsPrefix) != 2 { log.Fatalf("release file path %q does not match regexp %q\n", releasepath, listsPrefixRe) } sourceMatches, err := filepath.Glob(fmt.Sprintf("/var/lib/apt/lists/%s_*_Sources", listsPrefix[1])) if err != nil { log.Fatal(err) } sourcesPaths = append(sourcesPaths, sourceMatches...) packagesMatches, err := filepath.Glob(fmt.Sprintf("/var/lib/apt/lists/%s_*_Packages", listsPrefix[1])) if err != nil { log.Fatal(err) } packagesPaths = append(packagesPaths, packagesMatches...) } } if len(sourcesPaths) == 0 { log.Fatal("Could not find InRelease file for unstable. Are you missing unstable in your /etc/apt/sources.list?") } rebuild := make(map[string][]version.Version) archCmd := exec.Command( "dpkg-architecture", "--query=DEB_BUILD_ARCH") archOut, err := archCmd.Output() if err != nil { log.Fatal(err) } arch := strings.TrimSpace(string(archOut)) // TODO: Cache this output based on the .changes file. dose-ceve takes quite a while. ceve := exec.Command( "dose-ceve", "--deb-native-arch="+arch, "-T", "debsrc", "-r", strings.Join(changes.Binaries, ","), "-G", "pkg") for _, packagesPath := range packagesPaths { ceve.Args = append(ceve.Args, "deb://"+packagesPath) } for _, sourcesPath := range sourcesPaths { ceve.Args = append(ceve.Args, "debsrc://"+sourcesPath) } log.Printf("Figuring out reverse build dependencies using dose-ceve(1). This might take a while\n") if out, err := ceve.Output(); err == nil { r := bufio.NewReader(strings.NewReader(string(out))) for { paragraph, err := control.ParseParagraph(r) if paragraph == nil || err == io.EOF { break } pkg := paragraph.Values["Package"] ver, err := version.Parse(paragraph.Values["Version"]) if err != nil { log.Fatalf("Cannot parse version number %q in dose-ceve(1) output: %v", paragraph.Values["Version"], err) } rebuild[pkg] = append(rebuild[pkg], ver) } } else { log.Printf("dose-ceve(1) failed (%v), falling back to interpreting Sources directly\n", err) for _, sourcesPath := range sourcesPaths { if err := addReverseBuildDeps(sourcesPath, binaries, rebuild); err != nil { log.Fatal(err) } } } // TODO: add -recursive flag to also cover dependencies which are not DIRECT dependencies. use http://godoc.org/pault.ag/go/debian/control#OrderDSCForBuild (topsort) to build dependencies in the right order (saving CPU time). // TODO: what’s a good integration method for doing this in more setups, e.g. on a cloud provider or something? mapreri from #debian-qa says jenkins.debian.net is suitable. if strings.TrimSpace(*sbuildDist) == "" { *sbuildDist = changes.Distribution log.Printf("Setting -sbuild_dist=%s (from .changes file)\n", *sbuildDist) } buildresults := make(map[string]bool) for src, versions := range rebuild { sort.Sort(sort.Reverse(version.Slice(versions))) newest := versions[0] target := fmt.Sprintf("%s_%s", src, newest) // TODO: discard resulting package immediately? args := []string{ "--arch-all", "--dist=" + *sbuildDist, "--nolog", target, } for _, filename := range debs { args = append(args, fmt.Sprintf("--extra-package=%s", filename)) } cmd := exec.Command("sbuild", args...) if err := os.MkdirAll(*logDir, 0755); err != nil { log.Fatal(err) } log.Printf("Building %s (commandline: %v)\n", target, cmd.Args) if *dryRun { continue } buildlog, err := os.Create(filepath.Join(*logDir, target)) if err != nil { log.Fatal(err) } defer buildlog.Close() cmd.Stdout = buildlog cmd.Stderr = buildlog if err := cmd.Run(); err != nil { log.Printf("building %s failed: %v\n", target, err) buildresults[target] = false } else { buildresults[target] = true } } log.Printf("Build results:\n") // Print all successful builds first (not as interesting), then failed ones. for target, result := range buildresults { if !result { continue } log.Printf("PASSED: %s\n", target) } for target, result := range buildresults { if result { continue } log.Printf("FAILED: %s (see %s)\n", target, filepath.Join(*logDir, target)) } }
func ParseOne(reader *bufio.Reader) (*ChangelogEntry, error) { changeLog := ChangelogEntry{} var header string for { line, err := reader.ReadString('\n') if err != nil { return nil, err } if line == "\n" { continue } if !strings.HasPrefix(line, " ") { /* Great. Let's work with this. */ header = line break } else { return nil, fmt.Errorf("Unexpected line: %s", line) } } /* OK, so, we have a header. Let's run with it * hello (2.10-1) unstable; urgency=low */ arguments, options := partition(header, ";") /* Arguments: hello (2.10-1) unstable * Options: urgency=low, other=bar */ source, remainder := partition(arguments, "(") versionString, suite := partition(remainder, ")") var err error changeLog.Source = trim(source) changeLog.Version, err = version.Parse(trim(versionString)) if err != nil { return nil, err } changeLog.Target = trim(suite) changeLog.Arguments = map[string]string{} for _, entry := range strings.Split(options, ",") { key, value := partition(trim(entry), "=") changeLog.Arguments[trim(key)] = trim(value) } var signoff string /* OK, we've got the header. Let's zip down. */ for { line, err := reader.ReadString('\n') if err != nil { return nil, err } if !strings.HasPrefix(line, " ") && trim(line) != "" { return nil, fmt.Errorf("Error! Didn't get ending line!") } if strings.HasPrefix(line, " -- ") { signoff = line break } changeLog.Changelog = changeLog.Changelog + line } /* Right, so we have a signoff line */ _, signoff = partition(signoff, "--") /* Get rid of the leading " -- " */ whom, when := partition(signoff, " ") /* Split on the " " */ changeLog.ChangedBy = trim(whom) changeLog.When, err = time.Parse(whenLayout, trim(when)) if err != nil { return nil, fmt.Errorf("Failed parsing When %q: %v", when, err) } return &changeLog, nil }
func (can Candidates) ExplainSatisfies(arch dependency.Arch, possi dependency.Possibility) (bool, string, []control.BinaryIndex) { entries, ok := can[possi.Name] if !ok { // no known entries in the Index return false, fmt.Sprintf("Totally unknown package: %s", possi.Name), nil } if possi.Arch != nil { satisfied := false archEntries := []control.BinaryIndex{} for _, installable := range entries { if installable.Architecture.Is(possi.Arch) { archEntries = append(archEntries, installable) satisfied = true } } if !satisfied { return false, fmt.Sprintf( "Relation depends on multiarch arch %s-%s-%s. Not found", possi.Arch.ABI, possi.Arch.OS, possi.Arch.CPU, ), nil } entries = archEntries } if possi.Version == nil { return true, "Relation exists, no version constraint", entries } // OK, so we have to play with versions now. vr := *possi.Version relatioNumber, _ := version.Parse(vr.Number) satisfied := false seenRealtions := []string{} versionEntries := []control.BinaryIndex{} for _, installable := range entries { q := version.Compare(installable.Version, relatioNumber) seenRealtions = append(seenRealtions, installable.Version.String()) switch vr.Operator { case ">=": satisfied = q >= 0 case "<=": satisfied = q <= 0 case ">>": satisfied = q > 0 case "<<": satisfied = q < 0 case "=": satisfied = q == 0 default: return false, "Unknown operator D:", nil // XXX: WHAT THE SHIT } if satisfied { versionEntries = append(versionEntries, installable) } } if len(versionEntries) > 0 { return true, "Relation exists with a satisfied version constraint", versionEntries } return false, fmt.Sprintf( "%s is version constrainted %s %s. Valid options: %s", possi.Name, vr.Operator, vr.Number, strings.Join(seenRealtions, ", "), ), nil }