Working commit
This commit is contained in:
parent
2eec2e5c9b
commit
60f45e6245
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,3 +16,4 @@
|
||||
# vendor/
|
||||
|
||||
dist/
|
||||
notify-to-slack
|
||||
|
10
Makefile
10
Makefile
@ -19,7 +19,7 @@ all-linux: $(filter dist/$(APP_NAME)-linux-%,$(TARGETS))
|
||||
# Build notify-to-slack for the current machine
|
||||
$(APP_NAME): $(GOFILES)
|
||||
@echo Version: $(VERSION)
|
||||
go build -ldflags '-X "main.version=VERSION"' -o $(APP_NAME)
|
||||
go build -ldflags '-X "main.version=$(VERSION)"' -o $(APP_NAME)
|
||||
|
||||
.PHONY: build
|
||||
build: $(APP_NAME)
|
||||
@ -30,7 +30,7 @@ test:
|
||||
go test -coverprofile=coverage.out
|
||||
go tool cover -func=coverage.out
|
||||
@go tool cover -func=coverage.out | awk -v target=80.0% \
|
||||
'/^total:/ { print "Total coverage: " $3 " Minimum coverage: " target; if ($3+0.0 >= target+0.0) print "ok"; else { print "fail"; exit 1; } }'
|
||||
'/^total:/ { print "Total coverage: " $$3 " Minimum coverage: " target; if ($$3+0.0 >= target+0.0) print "ok"; else { print "fail"; exit 1; } }'
|
||||
|
||||
# Installs pre-commit hooks
|
||||
.PHONY: install-hooks
|
||||
@ -52,9 +52,9 @@ clean:
|
||||
$(TARGETS): $(GOFILES)
|
||||
mkdir -p ./dist
|
||||
GOOS=$(word 2, $(subst -, ,$(@))) GOARCH=$(word 3, $(subst -, ,$(@))) CGO_ENABLED=0 \
|
||||
go build -ldflags '-X "main.version=VERSION"' -a -installsuffix nocgo \
|
||||
-o @
|
||||
go build -ldflags '-X "main.version=$(VERSION)"' -a -installsuffix nocgo \
|
||||
-o $@
|
||||
|
||||
.PHONY: $(TARGET_ALIAS)
|
||||
$(TARGET_ALIAS):
|
||||
$(MAKE) $(addprefix dist/,@)
|
||||
$(MAKE) $(addprefix dist/,$@)
|
||||
|
107
figures/figures.go
Normal file
107
figures/figures.go
Normal file
@ -0,0 +1,107 @@
|
||||
package figures
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var ConfigFileNotFoundErr = errors.New("config file for provided path not found")
|
||||
|
||||
// fileExists checks if a file exists at a given path
|
||||
func fileExists(path string) bool {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type figuration struct {
|
||||
applicationName string
|
||||
}
|
||||
|
||||
// NewFiguration creates a new figuration for the provided application
|
||||
func NewFiguration(applicationName string) figuration {
|
||||
return figuration{
|
||||
applicationName: applicationName,
|
||||
}
|
||||
}
|
||||
|
||||
// GetConfigFilePath returns the path of a given file within the UserConfigDir.
|
||||
func (fig figuration) GetConfigFilePath(filename string) (string, error) {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error getting current config: %w", err)
|
||||
}
|
||||
|
||||
// Get or make config dir path
|
||||
configDir = filepath.Join(configDir, fig.applicationName)
|
||||
_ = os.MkdirAll(configDir, 0o755)
|
||||
|
||||
// Get the path to the provided file name within the config directory
|
||||
configFilePath := filepath.Join(configDir, filename)
|
||||
|
||||
return configFilePath, err
|
||||
}
|
||||
|
||||
// ReadConfig populates a provided struct with the data in config file of a given name
|
||||
func (fig figuration) ReadConfig(name string, data interface{}) error {
|
||||
configPath, err := fig.GetConfigFilePath(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !fileExists(configPath) {
|
||||
return fmt.Errorf("error reading config from file %s: %w", name, ConfigFileNotFoundErr)
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading config from file: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(content, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed parsing json from config file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteConfig writes data from the provided struct to the config file with the provided name
|
||||
func (fig figuration) WriteConfig(name string, data interface{}) error {
|
||||
configPath, err := fig.GetConfigFilePath(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
contents, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed converting config to json: %w", err)
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile(configPath, contents, 0o600); err != nil {
|
||||
return fmt.Errorf("error writing config to file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Config interface {
|
||||
ApplicationName() string
|
||||
Filename() string
|
||||
}
|
||||
|
||||
// ReadConfig populates the provided struct matching the Config interface with config values from the user's config store
|
||||
func ReadConfig(config Config) error {
|
||||
return NewFiguration(config.ApplicationName()).ReadConfig(config.Filename(), config)
|
||||
}
|
||||
|
||||
// WriteConfig writes the provided struct matching the Config interface to the users config store
|
||||
func WriteConfig(config Config) error {
|
||||
return NewFiguration(config.ApplicationName()).WriteConfig(config.Filename(), config)
|
||||
}
|
9
go.mod
9
go.mod
@ -1,6 +1,13 @@
|
||||
module /iamthefij/notify-to-slack
|
||||
module git.iamthefij.com/iamthefij/notify-to-slack
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/karrick/golf v1.4.0
|
||||
github.com/slack-go/slack v0.10.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/pkg/errors v0.8.0 // indirect
|
||||
)
|
||||
|
16
go.sum
Normal file
16
go.sum
Normal file
@ -0,0 +1,16 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
|
||||
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/karrick/golf v1.4.0 h1:9i9HnUh7uCyUFJhIqg311HBibw4f2pbGldi0ZM2FhaQ=
|
||||
github.com/karrick/golf v1.4.0/go.mod h1:qGN0IhcEL+IEgCXp00RvH32UP59vtwc8w5YcIdArNRk=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/slack-go/slack v0.10.0 h1:L16Eqg3QZzRKGXIVsFSZdJdygjOphb2FjRUwH6VrFu8=
|
||||
github.com/slack-go/slack v0.10.0/go.mod h1:wWL//kk0ho+FcQXcBTmEafUI5dz4qz5f4mMk8oIkioQ=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
185
main.go
185
main.go
@ -1,18 +1,183 @@
|
||||
package main
|
||||
|
||||
var (
|
||||
// version of notify-to-slack being run
|
||||
version = "dev"
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/karrick/golf"
|
||||
"github.com/slack-go/slack"
|
||||
|
||||
"git.iamthefij.com/iamthefij/notify-to-slack/figures"
|
||||
)
|
||||
|
||||
func main() {
|
||||
showVersion := flag.Bool("version", false, "Display the version of minitor and exit")
|
||||
flag.Parse()
|
||||
var (
|
||||
applicationName = "notify-to-slack"
|
||||
version = "dev"
|
||||
// hookURL = "https://hooks.slack.com/services/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"
|
||||
)
|
||||
|
||||
// Print version if flag is provided
|
||||
if *showVersion {
|
||||
fmt.Println("notify-to-slack version:", version)
|
||||
type Config struct {
|
||||
HookURL string
|
||||
}
|
||||
|
||||
return
|
||||
func (_ Config) ApplicationName() string {
|
||||
return applicationName
|
||||
}
|
||||
|
||||
func (_ Config) Filename() string {
|
||||
return "config"
|
||||
}
|
||||
|
||||
// ShellCommand takes a string and constructs a command that executs within `sh`
|
||||
func ShellCommand(command string) *exec.Cmd {
|
||||
shellCommand := []string{"sh", "-c", strings.TrimSpace(command)}
|
||||
|
||||
return exec.Command(shellCommand[0], shellCommand[1:]...)
|
||||
}
|
||||
|
||||
func maybeHostname() string {
|
||||
if hostname, err := os.Hostname(); err != nil {
|
||||
return "unknown hostname"
|
||||
} else {
|
||||
return hostname
|
||||
}
|
||||
}
|
||||
|
||||
func maybeUsername() string {
|
||||
if currentUser, err := user.Current(); err != nil {
|
||||
return "unknown user"
|
||||
} else {
|
||||
return currentUser.Username
|
||||
}
|
||||
}
|
||||
|
||||
func printUsage() {
|
||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "Reads in and does stuff\n")
|
||||
configPath, err := figures.NewFiguration(applicationName).GetConfigFilePath("")
|
||||
if err == nil {
|
||||
fmt.Fprintf(os.Stderr, "Config directory is %s\n", configPath)
|
||||
}
|
||||
golf.PrintDefaults()
|
||||
}
|
||||
|
||||
func printVersion() {
|
||||
// fmt.Fprintf(os.Stderr, "%s: at version %s\n", os.Args[0], version)
|
||||
fmt.Fprintf(os.Stderr, "%s: at version %s\n", os.Args[0], version)
|
||||
}
|
||||
|
||||
func main() {
|
||||
showHelp := golf.BoolP('h', "help", false, "show usage and exit")
|
||||
showVersion := golf.BoolP('V', "version", false, "show version and exit")
|
||||
lastStatus := golf.IntP('s', "status", -1, "last command status")
|
||||
command := golf.StringP('c', "command", "", "execute command in a sub shell and publish results")
|
||||
atChannel := golf.BoolP('a', "at-channel", false, "add @channel to the message")
|
||||
channel := golf.StringP('l', "channel", "", "select channel to send message to, if not the default for your integration")
|
||||
hookURL := golf.StringP('u', "hook-url", "", "set the webhook URL to use")
|
||||
golf.Parse()
|
||||
|
||||
if *showHelp {
|
||||
printUsage()
|
||||
return
|
||||
}
|
||||
if *showVersion {
|
||||
printVersion()
|
||||
return
|
||||
}
|
||||
|
||||
// Read the configuration
|
||||
config := Config{}
|
||||
err := figures.ReadConfig(&config)
|
||||
|
||||
// User set a new hook url
|
||||
if *hookURL != "" {
|
||||
config.HookURL = *hookURL
|
||||
err = figures.WriteConfig(config)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Could not write to config file: %s", err))
|
||||
}
|
||||
} else {
|
||||
if err != nil && !errors.Is(err, figures.ConfigFileNotFoundErr) {
|
||||
panic(fmt.Sprintf("Error attempting to read the config file. %s", err))
|
||||
}
|
||||
if config.HookURL == "" {
|
||||
panic("You have not set a hook url. Try running with --hook-url <your url here>")
|
||||
}
|
||||
}
|
||||
|
||||
attachments := []slack.Attachment{}
|
||||
|
||||
// Execute nested command
|
||||
if *command != "" {
|
||||
c := ShellCommand(*command)
|
||||
var footer string
|
||||
output, err := c.CombinedOutput()
|
||||
color := "good"
|
||||
if err != nil {
|
||||
color = "danger"
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
footer = fmt.Sprintf("status %d", exitError.ExitCode())
|
||||
} else {
|
||||
footer = fmt.Sprintf("unknown status %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
attachments = append(attachments, slack.Attachment{
|
||||
Color: color,
|
||||
AuthorName: maybeUsername(),
|
||||
AuthorSubname: maybeHostname(),
|
||||
Text: string(output),
|
||||
Footer: footer,
|
||||
Ts: json.Number(strconv.FormatInt(time.Now().Unix(), 10)),
|
||||
})
|
||||
}
|
||||
|
||||
// Get message text from provided arguments
|
||||
args := golf.Args()
|
||||
message := strings.Join(args, " ")
|
||||
|
||||
// Build status attachment if a status was provided
|
||||
if *lastStatus >= 0 {
|
||||
color := "good"
|
||||
if *lastStatus > 0 {
|
||||
color = "danger"
|
||||
}
|
||||
|
||||
attachments = append(attachments, slack.Attachment{
|
||||
Color: color,
|
||||
AuthorName: maybeUsername(),
|
||||
AuthorSubname: maybeHostname(),
|
||||
Text: message,
|
||||
Footer: fmt.Sprintf("status %d", *lastStatus),
|
||||
Ts: json.Number(strconv.FormatInt(time.Now().Unix(), 10)),
|
||||
})
|
||||
|
||||
// Empty out message to avoid duplicating in the message content
|
||||
message = ""
|
||||
|
||||
}
|
||||
|
||||
// Maybe prepend an @channel
|
||||
if *atChannel {
|
||||
message = strings.Join([]string{"<!channel>", message}, " ")
|
||||
}
|
||||
|
||||
msg := slack.WebhookMessage{
|
||||
Attachments: attachments,
|
||||
Username: "cli-noti",
|
||||
Text: message,
|
||||
Channel: *channel,
|
||||
}
|
||||
|
||||
err = slack.PostWebhook(config.HookURL, &msg)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user