-
Notifications
You must be signed in to change notification settings - Fork 1
/
changelog.go
116 lines (101 loc) · 2.7 KB
/
changelog.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
// Package changelog provides helpers to parse a typical
// Debian changelog file found in packages generated from source packages.
// see http://www.debian.org/doc/debian-policy/ch-source.html for details on the format
package changelog
import (
"bufio"
"bytes"
"io"
"time"
"unicode"
)
// Change describes a single changeset between 2 versions
type Change struct {
Source string
Version string
Dists []string
Urgency string
Author string
Email string
Changed time.Time
Changes []byte
}
// Changelog describes a full or partial changelog
type Changelog []Change
func (change *Change) parseVersionLine(b []byte) (ok bool) {
split := bytes.SplitN(b, []byte{';'}, 2)
f := bytes.Fields(split[0])
if len(f) < 3 || len(split) != 2 {
return false
}
u := split[1]
s, v, d := f[0], f[1], f[2:]
change.Version = string(bytes.Trim(v, "()"))
change.Source = string(s)
change.Urgency = string(bytes.TrimPrefix(bytes.TrimSpace(u), []byte("urgency=")))
change.Dists = nil
for _, dist := range d {
change.Dists = append(change.Dists, string(dist))
}
return change.Version != "" &&
change.Source != "" &&
len(change.Dists) > 0 &&
change.Urgency != ""
}
func (change *Change) parseChangedByLine(b []byte) (ok bool) {
if !bytes.HasPrefix(b, []byte(" -- ")) {
return false
}
line := bytes.TrimPrefix(b, []byte(" -- "))
start := bytes.IndexByte(line, '<')
end := bytes.IndexByte(line, '>')
if start < 0 || start >= end || end >= len(line) {
return false
}
update, err := time.Parse(time.RFC1123Z, string(bytes.TrimSpace(line[end+1:])))
if err != nil {
return false
}
change.Author = string(bytes.TrimSpace(line[:start]))
change.Email = string(line[start+1 : end-2])
change.Changed = update
return true
}
// Parse a debian changelog from r for any changes happening later than afterVersion
func Parse(r io.Reader, afterVersion string) (Changelog, error) {
scanner := bufio.NewScanner(r)
changelog := make(Changelog, 0, 5)
change := Change{}
inside := false
for scanner.Scan() {
b := bytes.TrimRightFunc(scanner.Bytes(), unicode.IsSpace)
if b2 := bytes.TrimSpace(b); len(b2) < len(b) && !inside {
b = b2
}
if len(b) == 0 {
if inside {
change.Changes = append(change.Changes, '\n')
}
continue
}
if !inside && change.parseVersionLine(b) {
if len(afterVersion) > 0 && change.Version == afterVersion {
break
}
inside = true
continue
}
if inside && change.parseChangedByLine(b) {
changelog = append(changelog, change)
change = Change{}
inside = false
continue
}
change.Changes = append(change.Changes, b...)
change.Changes = append(change.Changes, '\n')
}
if err := scanner.Err(); err != nil {
return nil, err
}
return changelog, nil
}