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)
	}
}
Пример #2
0
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
}
Пример #3
0
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
}
Пример #4
0
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))
	}
}
Пример #5
0
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
}
Пример #6
0
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
}