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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"golang.org/x/mod/modfile"
"golang.org/x/tools/go/vcs"
"pault.ag/go/debian/control"
)
type dependency struct {
importPath string
packageName string
// todo version?
}
func execCheckDepends(args []string) {
cwd, err := os.Getwd()
if err != nil {
log.Fatalf("error while getting current directory: %s", err)
}
// Load the already packaged Go modules
golangBinaries, err := getGolangBinaries()
if err != nil {
log.Fatalf("error while getting packaged Go modules: %s", err)
}
// Load the dependencies defined in the Go module (go.mod)
goModDepds, err := parseGoModDependencies(cwd, golangBinaries)
if err != nil {
log.Fatalf("error while parsing go.mod: %s", err)
}
// Load the dependencies defined in the Debian packaging (d/control)
packageDeps, err := parseDebianControlDependencies(cwd)
if err != nil {
log.Fatalf("error while parsing d/control: %s", err)
}
hasChanged := false
// Check for newly introduced dependencies (defined in go.mod but not in d/control)
for _, goModDep := range goModDepds {
found := false
if goModDep.packageName == "" {
fmt.Printf("NEW dependency %s is NOT yet packaged in Debian\n", goModDep.importPath)
continue
}
for _, packageDep := range packageDeps {
if packageDep.packageName == goModDep.packageName {
found = true
break
}
}
if !found {
hasChanged = true
fmt.Printf("NEW dependency %s (%s)\n", goModDep.importPath, goModDep.packageName)
}
}
// Check for now unused dependencies (defined in d/control but not in go.mod)
for _, packageDep := range packageDeps {
found := false
for _, goModDep := range goModDepds {
if goModDep.packageName == packageDep.packageName {
found = true
break
}
}
if !found {
hasChanged = true
fmt.Printf("RM dependency %s (%s)\n", packageDep.importPath, packageDep.packageName)
}
}
if !hasChanged {
fmt.Printf("go.mod and d/control are in sync\n")
}
}
// parseGoModDependencies parse ALL dependencies listed in go.mod
// i.e. it returns the one defined in go.mod as well as the transitively ones
// TODO: this may not be the best way of doing thing since it requires the package to be converted to go module
func parseGoModDependencies(directory string, goBinaries map[string]debianPackage) ([]dependency, error) {
b, err := os.ReadFile(filepath.Join(directory, "go.mod"))
if err != nil {
return nil, err
}
modFile, err := modfile.Parse("go.mod", b, nil)
if err != nil {
return nil, err
}
var dependencies []dependency
for _, require := range modFile.Require {
if !require.Indirect {
packageName := ""
// Translate all packages to the root of their repository
rr, err := vcs.RepoRootForImportPath(require.Mod.Path, false)
if err != nil {
log.Printf("Could not determine repo path for import path %q: %v\n", require.Mod.Path, err)
continue
}
if val, exists := goBinaries[rr.Root]; exists {
packageName = val.binary
}
dependencies = append(dependencies, dependency{
importPath: rr.Root,
packageName: packageName,
})
}
}
return dependencies, nil
}
// parseDebianControlDependencies parse the Build-Depends defined in d/control
func parseDebianControlDependencies(directory string) ([]dependency, error) {
ctrl, err := control.ParseControlFile(filepath.Join(directory, "debian", "control"))
if err != nil {
return nil, err
}
var dependencies []dependency
for _, bp := range ctrl.Source.BuildDepends.GetAllPossibilities() {
packageName := strings.Trim(bp.Name, "\n")
// Ignore non -dev dependencies (i.e, debhelper-compat, git, cmake, etc...)
if !strings.HasSuffix(packageName, "-dev") {
continue
}
dependencies = append(dependencies, dependency{
importPath: "", // TODO XS-Go-Import-Path?
packageName: packageName,
})
}
return dependencies, nil
}
|