un-ghetto errata check
This commit is contained in:
parent
874d25b388
commit
b20c2e6480
213
errata.go
213
errata.go
@ -1,9 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -12,118 +14,149 @@ import (
|
|||||||
"golang.org/x/net/html"
|
"golang.org/x/net/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Errata stores all of the erratas grouped by date
|
// Erratum are our individual chunks of errata
|
||||||
type Errata struct {
|
type Erratum struct {
|
||||||
ID int
|
ID int
|
||||||
Type string
|
Date time.Time
|
||||||
Date time.Time
|
Desc string
|
||||||
Coverage string
|
Patch string
|
||||||
Desc string
|
Link string
|
||||||
Link string
|
Sig []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errati is a collection of Errata
|
// Fetch pulls down and parses the information for a given Erratum
|
||||||
type Errati struct {
|
func (e *Erratum) Fetch() error {
|
||||||
List []Errata
|
resp, err := http.Get(e.Link)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(resp.Body)
|
||||||
|
|
||||||
|
// First two lines are our signature
|
||||||
|
// 3rd line is date
|
||||||
|
// everything from the date down to ^Index is our description
|
||||||
|
count := 0
|
||||||
|
var descr []string
|
||||||
|
var patch []string
|
||||||
|
re := regexp.MustCompile(`^Index:`)
|
||||||
|
matched := false
|
||||||
|
for scanner.Scan() {
|
||||||
|
s := scanner.Text()
|
||||||
|
if count < 2 {
|
||||||
|
e.Sig = append(e.Sig, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
if count == 3 {
|
||||||
|
parts := strings.Split(s, ",")
|
||||||
|
if len(parts) == 3 {
|
||||||
|
d := fmt.Sprintf("%s,%s",
|
||||||
|
strings.Trim(parts[1], " "),
|
||||||
|
strings.Replace(parts[2], ":", "", -1))
|
||||||
|
e.Date, err = time.Parse("January 2, 2006", d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if count > 3 {
|
||||||
|
if re.MatchString(s) {
|
||||||
|
matched = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matched {
|
||||||
|
descr = append(descr, s)
|
||||||
|
} else {
|
||||||
|
patch = append(patch, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
count = count + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Desc = strings.Join(descr, "\n")
|
||||||
|
e.Patch = strings.Join(patch, "\n")
|
||||||
|
|
||||||
|
return nil //fmt.Errorf("crap")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errata is a collection of Errata
|
||||||
|
type Errata struct {
|
||||||
|
List []Erratum
|
||||||
Length int
|
Length int
|
||||||
}
|
}
|
||||||
|
|
||||||
// By is the type of a "less" function that defines the ordering of its Planet arguments.
|
// By is the type of a "less" function that defines the ordering of its Planet arguments.
|
||||||
type By func(p1, p2 *Errata) bool
|
type By func(p1, p2 *Erratum) bool
|
||||||
|
|
||||||
// Sort is a method on the function type, By, that sorts the argument slice according to the function.
|
// Sort is a method on the function type, By, that sorts the argument slice according to the function.
|
||||||
func (by By) Sort(erratas []Errata) {
|
func (by By) Sort(errata []Erratum) {
|
||||||
es := &errataSorter{
|
es := &errataSorter{
|
||||||
erratas: erratas,
|
errata: errata,
|
||||||
by: by,
|
by: by,
|
||||||
}
|
}
|
||||||
sort.Sort(es)
|
sort.Sort(es)
|
||||||
}
|
}
|
||||||
|
|
||||||
// errataSorter joins a By function and a slice of Errati to be sorted.
|
// errataSorter joins a By function and a slice of Errata to be sorted.
|
||||||
type errataSorter struct {
|
type errataSorter struct {
|
||||||
erratas []Errata
|
errata []Erratum
|
||||||
by func(p1, p2 *Errata) bool // Closure used in the Less method.
|
by func(p1, p2 *Erratum) bool // Closure used in the Less method.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Len is part of sort.Interface.
|
// Len is part of sort.Interface.
|
||||||
func (s *errataSorter) Len() int {
|
func (s *errataSorter) Len() int {
|
||||||
return len(s.erratas)
|
return len(s.errata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swap is part of sort.Interface.
|
// Swap is part of sort.Interface.
|
||||||
func (s *errataSorter) Swap(i, j int) {
|
func (s *errataSorter) Swap(i, j int) {
|
||||||
s.erratas[i], s.erratas[j] = s.erratas[j], s.erratas[i]
|
s.errata[i], s.errata[j] = s.errata[j], s.errata[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
|
// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
|
||||||
func (s *errataSorter) Less(i, j int) bool {
|
func (s *errataSorter) Less(i, j int) bool {
|
||||||
return s.by(&s.erratas[i], &s.erratas[j])
|
return s.by(&s.errata[i], &s.errata[j])
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseErrata does the actual parsing of html (poorly!)
|
// ParseErrata does the actual parsing of html
|
||||||
func ParseErrata(body io.Reader) (*Errati, error) {
|
func ParseErrata(body io.Reader, baseURL string) (*Errata, error) {
|
||||||
var data []Errata
|
var data []Erratum
|
||||||
var errati = &Errati{}
|
var errata = &Errata{}
|
||||||
doc, err := html.Parse(body)
|
doc, err := html.Parse(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var f func(*html.Node)
|
var f func(*html.Node)
|
||||||
f = func(node *html.Node) {
|
f = func(node *html.Node) {
|
||||||
if node.Type == html.ElementNode && node.Data == "strong" {
|
if node.Type == html.ElementNode && node.Data == "a" {
|
||||||
if node.FirstChild != nil {
|
if node.FirstChild != nil {
|
||||||
var e Errata
|
var e Erratum
|
||||||
var err error
|
var err error
|
||||||
parts := strings.Split(node.FirstChild.Data, ": ")
|
|
||||||
e.ID, err = strconv.Atoi(parts[0])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
e.Type = parts[1]
|
|
||||||
e.Date, err = time.Parse("January 02, 2006", parts[2])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: not this.
|
for _, a := range node.Attr {
|
||||||
|
if a.Key == "href" {
|
||||||
|
parts := strings.Split(a.Val, "_")
|
||||||
|
|
||||||
if node.NextSibling != nil &&
|
if len(parts) >= 2 && a.Val != "" {
|
||||||
node.NextSibling.NextSibling != nil &&
|
e.Link = fmt.Sprintf("%s%s", baseURL, a.Val)
|
||||||
node.NextSibling.NextSibling.FirstChild.Data != "" {
|
e.ID, err = strconv.Atoi(parts[0])
|
||||||
e.Coverage = node.NextSibling.NextSibling.FirstChild.Data
|
if err != nil {
|
||||||
}
|
// Not a link we care about
|
||||||
|
break
|
||||||
if node.NextSibling != nil &&
|
}
|
||||||
node.NextSibling.NextSibling != nil &&
|
data = append(data, e)
|
||||||
node.NextSibling.NextSibling.NextSibling != nil &&
|
|
||||||
node.NextSibling.NextSibling.NextSibling.NextSibling != nil &&
|
|
||||||
node.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling != nil &&
|
|
||||||
node.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.Data != "" {
|
|
||||||
e.Desc = node.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.Data
|
|
||||||
e.Desc = strings.TrimRight(e.Desc, "\n")
|
|
||||||
e.Desc = strings.TrimLeft(e.Desc, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.NextSibling != nil &&
|
|
||||||
node.NextSibling.NextSibling != nil &&
|
|
||||||
node.NextSibling.NextSibling.NextSibling != nil &&
|
|
||||||
node.NextSibling.NextSibling.NextSibling.NextSibling != nil &&
|
|
||||||
node.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling != nil &&
|
|
||||||
node.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling != nil &&
|
|
||||||
node.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling != nil &&
|
|
||||||
node.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling != nil {
|
|
||||||
n := node.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling
|
|
||||||
for _, a := range n.Attr {
|
|
||||||
if a.Key == "href" {
|
|
||||||
e.Link = a.Val
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data = append(data, e)
|
|
||||||
errati.Length = errati.Length + 1
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,18 +166,18 @@ func ParseErrata(body io.Reader) (*Errati, error) {
|
|||||||
}
|
}
|
||||||
f(doc)
|
f(doc)
|
||||||
|
|
||||||
byDate := func(p1, p2 *Errata) bool {
|
byID := func(p1, p2 *Erratum) bool {
|
||||||
return p1.Date.Unix() < p2.Date.Unix()
|
return p1.ID < p2.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
By(byDate).Sort(data)
|
By(byID).Sort(data)
|
||||||
errati.List = data
|
errata.List = data
|
||||||
|
|
||||||
return errati, nil
|
return errata, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseRemoteErrata grabs all of the OpenBSD errata from an html page
|
// ParseRemoteErrata grabs all of the OpenBSD errata from an html page
|
||||||
func ParseRemoteErrata(s string) (*Errati, error) {
|
func ParseRemoteErrata(s string) (*Errata, error) {
|
||||||
resp, err := http.Get(s)
|
resp, err := http.Get(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -152,29 +185,25 @@ func ParseRemoteErrata(s string) (*Errati, error) {
|
|||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
return ParseErrata(resp.Body)
|
return ParseErrata(resp.Body, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintErrata pretty prints an errata
|
// PrintErrata pretty prints an errata
|
||||||
func PrintErrata(e *Errata) string {
|
func PrintErrata(e *Erratum) string {
|
||||||
return fmt.Sprintf("%03d: %s: %s %s\n%s\n%s",
|
return fmt.Sprintf("New OpenBSD Errata: %03d\n%s: %s\n%s",
|
||||||
e.ID,
|
e.ID,
|
||||||
e.Type,
|
|
||||||
e.Date.String(),
|
e.Date.String(),
|
||||||
e.Coverage,
|
|
||||||
e.Desc,
|
e.Desc,
|
||||||
e.Link,
|
e.Link,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintErrataMD pretty prints an errata in markdown
|
// PrintErrataMD pretty prints an errata in markdown
|
||||||
//func PrintErrataMD(e *Errata) string {
|
func PrintErrataMD(e *Erratum) string {
|
||||||
// return fmt.Sprintf("#%03d: %s: %s _%s_\n%s\n[A source code patch exists which remedies this problem.](%s)",
|
return fmt.Sprintf("# OpenBSD Errata %03d (_%s_)\n<pre>%s</pre>\n[A source code patch exists which remedies this problem.](%s)",
|
||||||
// e.ID,
|
e.ID,
|
||||||
// e.Type,
|
e.Date.Format("January 2, 2006"),
|
||||||
// e.Date.String(),
|
e.Desc,
|
||||||
// e.Coverage,
|
e.Link,
|
||||||
// e.Desc,
|
)
|
||||||
// e.Link,
|
}
|
||||||
// )
|
|
||||||
//}
|
|
||||||
|
@ -5,12 +5,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestParseErrata(t *testing.T) {
|
func TestParseErrata(t *testing.T) {
|
||||||
got, err := ParseRemoteErrata("https://www.openbsd.org/errata66.html")
|
got, err := ParseRemoteErrata("http://ftp.openbsd.org/pub/OpenBSD/patches/6.6/common/")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
l := len(got.List)
|
l := len(got.List)
|
||||||
if l == 0 {
|
if l < 1 {
|
||||||
t.Errorf("errata count %d; want > 0", l)
|
t.Errorf("errata count %d; want > 1", l)
|
||||||
|
}
|
||||||
|
|
||||||
|
erratum := got.List[len(got.List)-1]
|
||||||
|
|
||||||
|
err = erratum.Fetch()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("can't fetch data for erratum\n%s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
main.go
16
main.go
@ -162,20 +162,26 @@ func main() {
|
|||||||
errataCount, err = strconv.Atoi(storeCount)
|
errataCount, err = strconv.Atoi(storeCount)
|
||||||
for {
|
for {
|
||||||
got, err := ParseRemoteErrata(
|
got, err := ParseRemoteErrata(
|
||||||
fmt.Sprintf("https://www.openbsd.org/errata%s.html", openbsdRelease),
|
fmt.Sprintf("http://ftp.openbsd.org/pub/OpenBSD/patches/%s/common/", openbsdRelease),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
l := got.Length
|
l := len(got.List)
|
||||||
if l > errataCount {
|
if l > errataCount {
|
||||||
alertRooms, _ := store.Get("errata_rooms")
|
alertRooms, _ := store.Get("errata_rooms")
|
||||||
c := 0
|
c := 0
|
||||||
for _, errata := range got.List {
|
for _, erratum := range got.List {
|
||||||
if c+1 > errataCount {
|
if c+1 > errataCount {
|
||||||
log.Printf("%03d: %s - %s\n", errata.ID, errata.Type, errata.Desc)
|
log.Printf("Notifying for erratum %03d\n", erratum.ID)
|
||||||
|
err = erratum.Fetch()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
for _, room := range strings.Split(alertRooms, ",") {
|
for _, room := range strings.Split(alertRooms, ",") {
|
||||||
cli.SendNotice(room, PrintErrata(&errata))
|
cli.SendNotice(room, PrintErrata(&erratum))
|
||||||
|
plugins.SendMD(cli, room, PrintErrataMD(&erratum))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c = c + 1
|
c = c + 1
|
||||||
|
Loading…
Reference in New Issue
Block a user