diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e7d73f3..620ee40 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,6 +27,6 @@ jobs: version: latest args: release --clean env: + RELEASE_VERSION: ${{ github.ref_name }}" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GORELEASER_HOMEBREW_GITHUB_TOKEN: ${{ secrets.GORELEASER_HOMEBREW_GITHUB_TOKEN }} - diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 3dbc53a..edaed6c 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -13,6 +13,8 @@ builds: - arm64 goamd64: - v3 + ldflags: + - -s -w -X 'main.Version={{ .Env.RELEASE_VERSION }}' archives: - format: tar.gz @@ -42,7 +44,7 @@ brews: owner: ghokun name: homebrew-tap branch: main - token: "{{ .Env.GORELEASER_HOMEBREW_GITHUB_TOKEN }}" + token: '{{ .Env.GORELEASER_HOMEBREW_GITHUB_TOKEN }}' folder: Formula homepage: "https://github.com/ghokun/coyote" description: "Coyote is a RabbitMQ message sink." diff --git a/README.md b/README.md index b55c986..d796eb1 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,26 @@ brew install ghokun/tap/coyote ## Usage ```shell -Usage of coyote: - -bind string - Routing key to bind. (default "#") - -exchange string - Exchange name to listen messages. - -queue string - Interceptor queue name. (default "interceptor") - -url string - RabbitMQ url, must start with amqps:// or amqp://. +NAME: + coyote - Coyote is a RabbitMQ message sink. + +USAGE: + coyote [global options] command [command options] [arguments...] + +VERSION: + development + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --url value RabbitMQ url, must start with amqps:// or amqp://. + --exchange value Exchange name to listen messages. + --queue value Interceptor queue name. (default: "interceptor") + --bind value Routing key to bind. (default: "#") + --help, -h show help + --version, -v print the version # Example -coyote -url amqp://guest:guest@localhost -exchange your_exchange +coyote --url amqp://guest:guest@localhost --exchange your_exchange ``` diff --git a/go.mod b/go.mod index 4e3cd6d..f9b1b53 100644 --- a/go.mod +++ b/go.mod @@ -5,4 +5,11 @@ go 1.20 require ( github.com/google/uuid v1.3.0 github.com/rabbitmq/amqp091-go v1.7.0 + github.com/urfave/cli/v2 v2.24.4 +) + +require ( + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect ) diff --git a/go.sum b/go.sum index 15de9b8..55df517 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -8,10 +10,16 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rabbitmq/amqp091-go v1.7.0 h1:V5CF5qPem5OGSnEo8BoSbsDGwejg6VUJsKEdneaoTUo= github.com/rabbitmq/amqp091-go v1.7.0/go.mod h1:wfClAtY0C7bOHxd3GjmF26jEHn+rR/0B3+YV+Vn9/NI= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= +github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= diff --git a/main.go b/main.go index 34d4c1d..2b83e30 100644 --- a/main.go +++ b/main.go @@ -1,68 +1,86 @@ package main import ( - "flag" "fmt" "log" "os" "github.com/google/uuid" amqp "github.com/rabbitmq/amqp091-go" + cli "github.com/urfave/cli/v2" ) -func fatalOnError(err error, msg string) { - if err != nil { - log.Fatalf("💥 %s: %s", msg, err) - } -} - -func isFlagSet(name string) bool { - found := false - flag.Visit(func(f *flag.Flag) { - if f.Name == name { - found = true - } - }) - return found -} +var Version = "development" func main() { - url := flag.String("url", "", "RabbitMQ url, must start with amqps:// or amqp://.") - exchange := flag.String("exchange", "", "Exchange name to listen messages.") - queue := flag.String("queue", "interceptor", "Interceptor queue name.") - routingKey := flag.String("bind", "#", "Routing key to bind.") - flag.Parse() + app := &cli.App{ + Name: "coyote", + Usage: "Coyote is a RabbitMQ message sink.", + Version: Version, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "url", + Required: true, + Usage: "RabbitMQ url, must start with amqps:// or amqp://.", + }, + &cli.StringFlag{ + Name: "exchange", + Required: true, + Usage: "Exchange name to listen messages.", + }, + &cli.StringFlag{ + Name: "queue", + Value: "interceptor", + Usage: "Interceptor queue name.", + }, + &cli.StringFlag{ + Name: "bind", + Value: "#", + Usage: "Routing key to bind.", + }, + }, + Action: func(ctx *cli.Context) error { + conn, err := amqp.Dial(ctx.String("url")) + if err != nil { + return fmt.Errorf("failed to connect to RabbitMQ: %w", err) + } + defer conn.Close() - if !isFlagSet("url") || !isFlagSet("exchange") { - flag.Usage() - os.Exit(1) - } + ch, err := conn.Channel() + if err != nil { + return fmt.Errorf("failed to open a channel: %w", err) + } + defer ch.Close() - conn, err := amqp.Dial(*url) - fatalOnError(err, "Failed to connect to RabbitMQ") - defer conn.Close() + err = ch.ExchangeDeclarePassive(ctx.String("exchange"), "topic", false, true, false, false, nil) + if err != nil { + return fmt.Errorf("failed to connect to exchange: %w", err) + } - ch, err := conn.Channel() - fatalOnError(err, "Failed to open a channel") - defer ch.Close() + q, err := ch.QueueDeclare(fmt.Sprintf("%s.%s", ctx.String("queue"), uuid.NewString()), false, true, false, false, nil) + if err != nil { + return fmt.Errorf("failed to declare a queue: %w", err) + } + ch.QueueBind(q.Name, ctx.String("bind"), ctx.String("exchange"), false, nil) - err = ch.ExchangeDeclarePassive(*exchange, "topic", false, true, false, false, nil) - fatalOnError(err, "Failed to connect to exchange") + msgs, err := ch.Consume(q.Name, "", true, false, false, false, nil) + if err != nil { + return fmt.Errorf("failed to register a consumer: %w", err) + } - q, err := ch.QueueDeclare(fmt.Sprintf("%s.%s", *queue, uuid.NewString()), false, true, false, false, nil) - fatalOnError(err, "Failed to declare a queue") - ch.QueueBind(q.Name, *routingKey, *exchange, false, nil) + var forever chan struct{} + go func() { + for d := range msgs { + log.Printf("📧 Received a message on queue %s: %s", d.RoutingKey, d.Body) + } + }() - msgs, err := ch.Consume(q.Name, "", true, false, false, false, nil) - fatalOnError(err, "Failed to register a consumer") - - var forever chan struct{} - go func() { - for d := range msgs { - log.Printf("📧 Received a message on queue %s: %s", d.RoutingKey, d.Body) - } - }() - - log.Printf("⏳ Waiting for messages. To exit press CTRL+C") - <-forever + log.Printf("⏳ Waiting for messages. To exit press CTRL+C") + <-forever + return nil + }, + } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } }