This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / appview / pipelines / logs.go
1.9 kB 81 lines
1package pipelines 2 3import ( 4 "errors" 5 "html/template" 6 "regexp" 7 "strings" 8 "time" 9 10 terminal "github.com/buildkite/terminal-to-html/v3" 11 "github.com/gorilla/websocket" 12 "tangled.org/core/appview/pages/markup/sanitizer" 13) 14 15// matches any ANSI escape sequence: ESC [ <params> m 16var sequenceRe = regexp.MustCompile(`\x1b\[([\d;]*)m`) 17 18// ansiState tracks the active stack across log lines 19// each non-reset SGR code is pushed onto the stack; a reset clears it. 20// 21// the stack contents are prepended to each new line so colours carry over. 22type ansiState struct { 23 stack []string 24} 25 26func NewAnsiState() *ansiState { 27 return &ansiState{ 28 stack: []string{}, 29 } 30} 31 32func (a *ansiState) Render(line string) template.HTML { 33 // prepend whatever sequences are still open from the previous line 34 prefix := strings.Join(a.stack, "") 35 // render current line with the existing prefix 36 rendered := terminal.Render([]byte(prefix + line)) 37 // sanitize 38 sanitized := sanitizer.SanitizeLogs(rendered) 39 40 // update the stack with sequences from current line 41 for _, m := range sequenceRe.FindAllStringSubmatch(line, -1) { 42 params := m[1] 43 if params == "" || params == "0" || params == "00" { 44 a.stack = a.stack[:0] 45 } else { 46 a.stack = append(a.stack, m[0]) 47 } 48 } 49 50 return template.HTML(sanitized) 51} 52 53// isExpectedClose reports whether err is a clean websocket close (or nil). 54func isExpectedClose(err error) bool { 55 if err == nil { 56 return true 57 } 58 var ce *websocket.CloseError 59 if errors.As(err, &ce) { 60 switch ce.Code { 61 case websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseAbnormalClosure: 62 return true 63 } 64 } 65 return false 66} 67 68func derefStr(s *string) string { 69 if s == nil { 70 return "" 71 } 72 return *s 73} 74 75func parseRFC3339(s string) time.Time { 76 t, err := time.Parse(time.RFC3339, s) 77 if err != nil { 78 return time.Time{} 79 } 80 return t 81}