-
Notifications
You must be signed in to change notification settings - Fork 2
/
packages_condense.go
143 lines (133 loc) · 3.85 KB
/
packages_condense.go
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
// This file is part of *kellner*
//
// Copyright (C) 2016, Travelping GmbH <copyright@travelping.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"unicode"
)
// recursively scan through a list of directories and find
// packages. per package (and architecture) keep only the
// most recent version.
// the result/output is collected within a packageIndex
// and returned.
func condensePackages(roots []string, nworkers int) (*packageIndex, error) {
var (
scanner = packageScanner{
doMD5: false,
doSHA1: false,
}
now = time.Now()
)
for _, root := range roots {
werr := filepath.Walk(root, func(path string, fi os.FileInfo, walkerr error) error {
if fi == nil {
log.Printf("no such file or directory: %s\n", path)
return nil
}
if !fi.IsDir() {
return nil
}
log.Println("condensing packages from", path)
if err := scanner.scan(path, nworkers); err != nil {
return fmt.Errorf("error: %v\n", err)
}
return nil
})
if werr != nil {
return nil, werr
}
}
if scanner.packages == nil {
log.Printf("no packages found.\n")
return nil, fmt.Errorf("no packages found.")
}
// and now to the act of condensing...
log.Printf("condensing a set of %d packages\n", scanner.packages.Len())
condensate := &packageIndex{Entries: make(map[string]*ipkArchive)} // key is Package+Architecture
for pkgname, pkg := range scanner.packages.Entries {
// slight paranoia check (control data ./. pkgname):
epochless_version := pkg.Header["Version"]
if idx := strings.Index(pkg.Header["Version"], ":"); idx > 0 {
epochless_version = pkg.Header["Version"][idx+1:]
}
synthetic_pkgname := fmt.Sprintf("%s_%s_%s.ipk",
pkg.Header["Package"], epochless_version, pkg.Header["Architecture"])
if synthetic_pkgname != pkgname {
return nil, fmt.Errorf("package %s has mismatching control-information \"%s\"",
pkgname, synthetic_pkgname)
}
cname := pkg.Header["Architecture"] + pkg.Header["Package"] // condense-key
if prev, ok := condensate.Entries[cname]; ok {
// compare version
cmp := compareVersion(pkg.Header["Version"], prev.Header["Version"])
if cmp > 0 { // new finding wins
fmt.Printf("%s: %s replaced: %s\n", pkg.Header["Package"],
pkg.Header["Version"], prev.Header["Version"])
condensate.Add(cname, pkg)
}
} else {
// first finding
condensate.Add(cname, pkg)
}
}
log.Printf("condensed %d into %d packages. done after %s\n",
scanner.packages.Len(), condensate.Len(), time.Since(now))
return condensate, nil
}
// compare to versions,
// return 0 when equal or 1/-1 when different
func compareVersion(v1 string, v2 string) int {
v1ord := versionStringToOrdinals(v1)
v2ord := versionStringToOrdinals(v2)
// make them equal in length, append zeros to the shorter one:
lendiff := len(v1ord) - len(v2ord)
if lendiff < 0 {
v1ord = append(v1ord, make([]int, (-1*lendiff))...)
} else if lendiff > 0 {
v2ord = append(v2ord, make([]int, (lendiff))...)
}
for i := range v1ord {
if v1ord[i] < v2ord[i] {
return -1
}
if v1ord[i] > v2ord[i] {
return 1
}
}
return 0
}
// convert a string into an array of numbers parsed from
// all occurances of consecutive digits
// "foo123bar321" --> [123, 321]
// "version-1.0.7-rc3.3" --> [1,0,7,3,3]
func versionStringToOrdinals(version_string string) []int {
ord := ""
ordarray := []int{}
addComponent := func() {
if len(ord) > 0 {
i, _ := strconv.Atoi(ord) // error is unlikely with all digits
ordarray = append(ordarray, i)
ord = ""
}
}
for _, c := range version_string {
if !unicode.IsDigit(c) {
addComponent()
continue
}
ord += string(c)
}
addComponent()
return ordarray
}