diff.go 2.29 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"sort"
)

// TODO: move this type into an internal package so that it can be shared between ci-{build,diff}
type result struct {
	Out string `json:"output"`
	Err string `json:"error"`
}

func load(path string) (map[string]result, error) {
	result := make(map[string]result)
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	if err := json.Unmarshal(b, &result); err != nil {
		return nil, err
	}
	return result, nil
}

func sorted(r map[string]result) []string {
	keys := make([]string, 0, len(r))
	for key := range r {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	return keys
}

func diff(beforePath, afterPath string) error {
	exitCode := 0

	before, err := load(beforePath)
	if err != nil {
		return err
	}
	after, err := load(afterPath)
	if err != nil {
		return err
	}

	for _, importPath := range sorted(before) {
		if _, ok := after[importPath]; !ok {
			// TODO: what bearing should this have on the exit code?
			fmt.Printf("no longer attempted to compile? %s\n", importPath)
			continue
		}
	}

	for _, importPath := range sorted(after) {
		a := after[importPath]
		b, ok := before[importPath]
		if !ok {
			if a.Err == "" {
				fmt.Printf("new package: %s\n", importPath)
				continue
			}
			fmt.Printf("new package: %s. error %s\n", importPath, a.Err)
			fmt.Println(a.Out)
			exitCode = 1
			continue
		}
		// Compiles before and after:
		if b.Err == "" && a.Err == "" {
			continue
		}
		// Did not compile before, does compile after:
		if b.Err != "" && a.Err == "" {
			fmt.Printf("now compiles: %s\n", importPath)
			continue
		}
		// Compiled before, does not compile after:
		if b.Err == "" && a.Err != "" {
			fmt.Printf("no longer compiles: %s. error %s\n", importPath, a.Err)
			fmt.Println(a.Out)
			exitCode = 1
			continue
		}
		// Does not compile before nor after:
90
		if a.Err != b.Err {
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
			// does not compile with a different error
			fmt.Printf("still does not compile (but different error): %s. error %q (before: %q)\n", importPath, a.Err, b.Err)
			fmt.Println(a.Out)
			exitCode = 1
		}
	}
	os.Exit(exitCode)
	return nil
}

func main() {
	flag.Parse()
	if flag.NArg() < 2 {
		log.Fatal("syntax: ci-build <before> <after>")
	}
	if err := diff(flag.Arg(0), flag.Arg(1)); err != nil {
		log.Fatal(err)
	}
}