mirror of
https://github.com/Luzifer/tmux-collector.git
synced 2024-12-22 13:51:19 +00:00
Initial version
This commit is contained in:
commit
12f462d70d
4 changed files with 272 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
tmux-collector
|
42
config.yml
Normal file
42
config.yml
Normal file
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
|
||||
# Must match background color of bar or will look weird
|
||||
base_bg_color: colour235
|
||||
|
||||
# Segments to display (first = left, last = right)
|
||||
segments:
|
||||
|
||||
# Arch Linux: Check for Updates
|
||||
- background_success: colour88
|
||||
foreground_success: colour251
|
||||
command: [bash, -c, 'echo "$(checkupdates | wc -l) Updates" | grep -v "^0 Updates" || true']
|
||||
interval: 10m
|
||||
|
||||
# Dropbox CLI: Check daemon status
|
||||
- background_success: colour19
|
||||
foreground_success: colour251
|
||||
command: [bash, -c, 'dropbox.py status | head -n1 | grep -v "Up to date" || true']
|
||||
interval: 30s
|
||||
prefix: ""
|
||||
|
||||
# System: If present display battery status
|
||||
- background_success: colour195
|
||||
foreground_success: colour237
|
||||
command: [tmux-battery]
|
||||
|
||||
# System: Display a stable subset of the uptime
|
||||
- background_success: colour34
|
||||
foreground_success: colour232
|
||||
command: [bash, -c, 'uptime | cut -f 4-5 -d " " | cut -f 1 -d ","']
|
||||
|
||||
# Time
|
||||
- background_success: colour235
|
||||
foreground_success: colour250
|
||||
command: ['date', '+%a, %H:%M:%S']
|
||||
|
||||
# Date
|
||||
- background_success: colour235
|
||||
foreground_success: colour69
|
||||
command: ['date', '+%Y-%m-%d (KW %V)']
|
||||
|
||||
...
|
94
entry.go
Normal file
94
entry.go
Normal file
|
@ -0,0 +1,94 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
BaseBgColor string `yaml:"base_bg_color" json:"base_bg_color"`
|
||||
Segments []*segment `yaml:"segments" json:"segments"`
|
||||
}
|
||||
|
||||
type segment struct {
|
||||
BackgroundError string `yaml:"background_error" json:"background_error"`
|
||||
BackgroundSuccess string `yaml:"background_success" json:"background_success"`
|
||||
ForegroundError string `yaml:"foreground_error" json:"foreground_error"`
|
||||
ForegroundSuccess string `yaml:"foreground_success" json:"foreground_success"`
|
||||
Command []string `yaml:"command" json:"command"`
|
||||
Prefix string `yaml:"prefix" json:"prefix"`
|
||||
Interval time.Duration `yaml:"interval" json:"interval"`
|
||||
|
||||
Output string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (s segment) cacheKey() string {
|
||||
return fmt.Sprintf("%x.json", sha256.Sum256([]byte(strings.Join(s.Command, " "))))
|
||||
}
|
||||
|
||||
func (s segment) storeCache() error {
|
||||
if s.Interval == 0 {
|
||||
// No interval defined = execute all the time, no caching
|
||||
return nil
|
||||
}
|
||||
|
||||
p, err := homedir.Expand(path.Join("~", ".cache", "tmux-collector", s.cacheKey()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(path.Dir(p), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fh, err := os.OpenFile(p, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
return json.NewEncoder(fh).Encode(s)
|
||||
}
|
||||
|
||||
func (s *segment) loadCache() (bool, error) {
|
||||
if s.Interval == 0 {
|
||||
// No interval defined = execute all the time, no caching
|
||||
return false, nil
|
||||
}
|
||||
|
||||
p, err := homedir.Expand(path.Join("~", ".cache", "tmux-collector", s.cacheKey()))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
stat, err := os.Stat(p)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
if time.Since(stat.ModTime()) > s.Interval {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
fh, err := os.Open(p)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
if err = json.NewDecoder(fh).Decode(s); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
135
main.go
Normal file
135
main.go
Normal file
|
@ -0,0 +1,135 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Luzifer/rconfig"
|
||||
log "github.com/sirupsen/logrus"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
cfg = struct {
|
||||
Config string `flag:"config,c" default:"config.yml" description:"Configuration file"`
|
||||
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
|
||||
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
|
||||
}{}
|
||||
|
||||
version = "dev"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := rconfig.ParseAndValidate(&cfg); err != nil {
|
||||
log.Fatalf("Unable to parse commandline options: %s", err)
|
||||
}
|
||||
|
||||
if cfg.VersionAndExit {
|
||||
fmt.Printf("tmux-collector %s\n", version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if l, err := log.ParseLevel(cfg.LogLevel); err != nil {
|
||||
log.WithError(err).Fatal("Unable to parse log level")
|
||||
} else {
|
||||
log.SetLevel(l)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
conf, err := loadConfig()
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Unable to load config")
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(len(conf.Segments))
|
||||
for _, seg := range conf.Segments {
|
||||
go executeSegment(seg, wg)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
lastBgColor := conf.BaseBgColor
|
||||
output := []string{}
|
||||
|
||||
for _, seg := range conf.Segments {
|
||||
bg := seg.BackgroundSuccess
|
||||
fg := seg.ForegroundSuccess
|
||||
|
||||
if seg.Err != nil {
|
||||
if seg.BackgroundError != "" {
|
||||
bg = seg.BackgroundError
|
||||
}
|
||||
if seg.ForegroundError != "" {
|
||||
fg = seg.ForegroundError
|
||||
}
|
||||
|
||||
if strings.TrimSpace(seg.Output) == "" {
|
||||
seg.Output = seg.Err.Error()
|
||||
}
|
||||
}
|
||||
|
||||
if strings.TrimSpace(seg.Output) == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if seg.Prefix != "" {
|
||||
seg.Output = strings.Join([]string{seg.Prefix, seg.Output}, " ")
|
||||
}
|
||||
|
||||
if lastBgColor != bg {
|
||||
output = append(output, fmt.Sprintf(" #[fg=%s,bg=%s]#[fg=%s,bg=%s] ",
|
||||
bg, lastBgColor, fg, bg,
|
||||
))
|
||||
} else {
|
||||
output = append(output, fmt.Sprintf("#[fg=%s,bg=%s] ",
|
||||
fg, bg,
|
||||
))
|
||||
}
|
||||
output = append(output, seg.Output)
|
||||
lastBgColor = bg
|
||||
}
|
||||
|
||||
fmt.Print(strings.Join(output, ""))
|
||||
}
|
||||
|
||||
func executeSegment(seg *segment, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
loaded, err := seg.loadCache()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Unable to load cache")
|
||||
}
|
||||
if loaded {
|
||||
log.WithField("command", strings.Join(seg.Command, " ")).Debug("Loaded from cache")
|
||||
return
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
cmd := exec.Command(seg.Command[0], seg.Command[1:]...)
|
||||
cmd.Stdout = buf
|
||||
|
||||
seg.Err = cmd.Run()
|
||||
seg.Output = strings.Split(buf.String(), "\n")[0]
|
||||
log.WithField("command", strings.Join(seg.Command, " ")).Debug("Freshly loaded")
|
||||
|
||||
if err = seg.storeCache(); err != nil {
|
||||
log.WithError(err).Error("Unable to store cache")
|
||||
}
|
||||
}
|
||||
|
||||
func loadConfig() (*config, error) {
|
||||
f, err := os.Open(cfg.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
out := &config{}
|
||||
return out, yaml.NewDecoder(f).Decode(out)
|
||||
}
|
Loading…
Reference in a new issue