semrelay

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 23, 2021 License: BSD-3-Clause Imports: 5 Imported by: 0

README

Semaphore CI notification relay

This is a service to provide Linux desktop notifications for your Semaphore CI builds. Unlike Semaphore's Slack notification feature, this allows filtering to see notifications for only your own builds. It also provides DBus notifications with the Semaphore icon and a default action of opening the build results in your browser.

Example success

Example failure

It's written in Go and consists of a relay server to accept webhook notifications from Semaphore and pass them on via WebSockets, as well as a client that connects to the relay, subscribes to webhook messages, and sends DBus notifications.

It uses the Gorilla WebSocket library, and part of the server implementation is shamelessly borrowed from its example code. DBus notifications use @esiqveland's notify library.

Server

The server provides an HTTPS service to both accept Semaphore webhook notifications to the /hook endpoint and accept client WebSocket connections at the /ws endpoint. It uses CertMagic to automatically acquire a TLS certificate from Let's Encrypt. Clients and Semaphore authenticate with shared secrets; create a password for clients to use and a token for Semaphore to use. You'll also need to set up a domain name for your server.

This has very low resource requirements; a t3.nano EC2 instance works fine and costs $3/month, and can easily be configured with a domain name via Route 53.

It's configured via environment variables:

  • DOMAIN: DNS domain name to acquire a certificate for.
  • EMAIL: email address used for Let's Encrypt.
  • PASSWORD: Password used by clients.
  • TOKEN: Security token configured in Semaphore URL.
  • VERBOSE: Enable verbose logging.
  • TEST: Set to send sample messages to the specified user every 15 seconds for testing.
Running via Docker Compose

The easiest way to run the relay service is via Docker Compose, using the provided docker-compose.yml and the cswheeler/semrelay:latest Docker image built from Dockerfile.server. Copy docker-compose.yml to your server in an appropriate directory (you don't need any other files) and create a .env file in the same directory to set the above environment variables:

DOMAIN=semrelay.example.com
EMAIL=me@example.com
PASSWORD=somepassword
TOKEN=sometoken

Now run docker-compose pull semrelay && docker-compose up -d semrelay. It should acquire TLS certificates and begin listening on ports 80 and 443. It's configured to be restarted by Docker whenever it exits.

The Docker container stores its certificates in a persistent volume to avoid repeatedly generating certificates, which could run afoul of the Let's Encrypt rate limits.

Running directly

Build the server binary with CGO_ENABLED=0 go build ./cmd/semrelay and copy it to the server.

To enable it to bind to ports 80 and 443 without running as root, grant it the cap_net_bind_service capability with sudo setcap cap_net_bind_service=+ep semrelay.

Semaphore configuration

Using the token you configured the server with, set up notifications for the desired project like so:

sem create notification myproject-relay \
    --projects myproject \
    --webhook-endpoint 'https://semrelay.example.com/hook?token=<token>'

Client

The semnotify client can be installed with:

go install github.com/csw/semrelay/cmd/semnotify@latest

It reads its configuration from $XDG_CONFIG_HOME/semnotify/config (typically $HOME/.config/semnotify/config) and from the command line as well. Settings:

  • user: GitHub username to receive notifications for.
  • password: server password.
  • server: server hostname.
  • insecure: skip TLS certificate verification (for testing only)
  • promotions: whether to show notifications for promotions or only build pipelines.
  • ttl: time until notifications expire, e.g. 30s. 0 (never expire) by default.

To use it with sway or i3, you can add exec_always semnotify to your configuration.

Development

This is built with Go 1.17.

There is a simple integration test suite, runnable with ./run_integration.

Documentation

Index

Constants

View Source
const (
	RegistrationMsg = "registration"
	NotificationMsg = "notification"
	AckMsg          = "ack"
	HelloMsg        = "hello"
)

Variables

View Source
var Icon []byte
View Source
var IconImage *image.NRGBA

Functions

This section is empty.

Types

type Message added in v0.2.0

type Message struct {
	Type    string          `json:"type"`
	Id      uint64          `json:"id"`
	Payload json.RawMessage `json:"payload"`
}

func MakeAck added in v0.2.0

func MakeAck(id uint64) Message

func MakeHello added in v0.3.0

func MakeHello() Message

func MakeNotification added in v0.2.0

func MakeNotification(id uint64, payload json.RawMessage) *Message

func MakeRegistration added in v0.2.0

func MakeRegistration(user, password string) *Message

type Notification

type Notification struct {
	Version string `json:"version"`
	Project struct {
		Name string `json:"name"`
	} `json:"project"`
	Organization struct {
		Name string `json:"name"`
	}
	Repository struct {
		Url  string `json:"url"`
		Slug string `json:"slug"`
	} `json:"repository"`
	Revision struct {
		Tag    string `json:"tag"`
		Sender struct {
			Login string `json:"login"`
		} `json:"sender"`
		ReferenceType string `json:"reference_type"`
		Reference     string `json:"reference"`
		PullRequest   string `json:"pull_request"`
		CommitSHA     string `json:"commit_sha"`
		CommitMessage string `json:"commit_message"`
		Branch        struct {
			Name        string `json:"name"`
			CommitRange string `json:"commit_range"`
		} `json:"branch"`
	} `json:"revision"`
	Pipeline struct {
		Id           string `json:"id"`
		State        string `json:"state"`
		RunningAt    string `json:"running_at"`
		DoneAt       string `json:"done_at"`
		ResultReason string `json:"result_reason"`
		Result       string `json:"result"`
		YamlFileName string `json:"yaml_file_name"`
	} `json:"pipeline"`
	Workflow struct {
		Id                string `json:"id"`
		CreatedAt         string `json:"created_at"`
		InitialPipelineId string `json:"initial_pipeline_id"`
	} `json:"workflow"`
	Blocks []*struct {
		Name         string `json:"name"`
		Result       string `json:"result"`
		ResultReason string `json:"result_reason"`
		State        string `json:"state"`
		Jobs         []*struct {
			Id     string `json:"id"`
			Index  int    `json:"index"`
			Name   string `json:"name"`
			Result string `json:"result"`
			Status string `json:"status"`
		} `json:"jobs"`
	} `json:"blocks"`
}

Notification is the JSON object sent by Semaphore describing the build results. See https://docs.semaphoreci.com/essentials/webhook-notifications/. Note that not all its fields are mapped.

type Registration

type Registration struct {
	User     string `json:"user"`
	Password string `json:"password"`
}

Registration is sent by the client as JSON to authenticate and request notifications for a given user.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL