home

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

main.go (3651B)


      1 // bus is a smal CLI tool that triggers home builds on build.sr.ht.
      2 // It is designed to be run in a cron or systemd timer.
      3 package main
      4 
      5 import (
      6 	"bytes"
      7 	"encoding/json"
      8 	"flag"
      9 	"fmt"
     10 	"io/ioutil"
     11 	"net/http"
     12 	"os"
     13 	"path/filepath"
     14 	"strings"
     15 
     16 	yaml "gopkg.in/yaml.v2"
     17 )
     18 
     19 var (
     20 	branch = flag.String("branch", "master", "branch to schedule")
     21 	secret = flag.String("secret", "/etc/secrets/srht-token", "path to find sr.ht secret")
     22 	builds = flag.String("builds", "/etc/nixos/.builds", "path to find builds manifests")
     23 )
     24 
     25 // Represents a builds.sr.ht build object as described on
     26 // https://man.sr.ht/builds.sr.ht/api.md
     27 type Build struct {
     28 	Manifest string   `json:"manifest"`
     29 	Note     string   `json:"note"`
     30 	Tags     []string `json:"tags"`
     31 }
     32 
     33 // Represents a build trigger object as described on <the docs for
     34 // this are currently down>
     35 type Trigger struct {
     36 	Action    string `json:"action"`
     37 	Condition string `json:"condition"`
     38 	To        string `json:"to"`
     39 }
     40 
     41 // Represents a build manifest for sourcehut.
     42 type Manifest struct {
     43 	Image    string                `json:"image"`
     44 	Sources  []string              `json:"sources"`
     45 	Secrets  []string              `json:"secrets"`
     46 	Tasks    [](map[string]string) `json:"tasks"`
     47 	Triggers []Trigger             `json:"triggers"`
     48 }
     49 
     50 func triggerBuild(token, name, manifest, branch string) {
     51 	build := Build{
     52 		Manifest: manifest,
     53 		Note:     fmt.Sprintf("Nightly build of '%s' on '%s'", name, branch),
     54 		Tags: []string{
     55 			// my branch names tend to contain slashes, which are not valid
     56 			// identifiers in sourcehut.
     57 			"home", "nightly", strings.ReplaceAll(branch, "/", "_"), name,
     58 		},
     59 	}
     60 
     61 	body, _ := json.Marshal(build)
     62 	reader := ioutil.NopCloser(bytes.NewReader(body))
     63 
     64 	req, err := http.NewRequest("POST", "https://builds.sr.ht/api/jobs", reader)
     65 	if err != nil {
     66 		fmt.Fprintf(os.Stderr, "failed to create an HTTP request: %s", err)
     67 		os.Exit(1)
     68 	}
     69 
     70 	req.Header.Add("Authorization", "token "+token)
     71 	req.Header.Add("Content-Type", "application/json")
     72 
     73 	resp, err := http.DefaultClient.Do(req)
     74 	if err != nil {
     75 		// This might indicate a temporary error on the sourcehut side, do
     76 		// not fail the whole program.
     77 		fmt.Fprintf(os.Stderr, "failed to send build.sr.ht request: %s", err)
     78 		return
     79 	}
     80 	defer resp.Body.Close()
     81 
     82 	if resp.StatusCode != 200 {
     83 		respBody, _ := ioutil.ReadAll(resp.Body)
     84 		fmt.Fprintf(os.Stderr, "received non-success response from builds.sr.ht: %s (%v)", respBody, resp.Status)
     85 		os.Exit(1)
     86 	} else {
     87 		fmt.Fprintf(os.Stdout, "triggered builds.sr.ht job for branch '%s'\n", branch)
     88 	}
     89 }
     90 
     91 func prepareManifest(manifest []byte) ([]byte, error) {
     92 	var m Manifest
     93 	if err := yaml.Unmarshal(manifest, &m); err != nil {
     94 		return manifest, err
     95 	}
     96 	m.Sources = append(m.Sources, "https://git.sr.ht/~vdemeester/home")
     97 	return yaml.Marshal(m)
     98 }
     99 
    100 func main() {
    101 	flag.Parse()
    102 	token, err := ioutil.ReadFile(*secret)
    103 	if err != nil {
    104 		fmt.Fprint(os.Stderr, "sourcehut token could not be read.")
    105 		os.Exit(1)
    106 	}
    107 	files, err := ioutil.ReadDir(*builds)
    108 	if err != nil {
    109 		fmt.Fprintf(os.Stderr, "cannot list builds manifest from %s: %v.\n", *builds, err)
    110 		os.Exit(1)
    111 	}
    112 	fmt.Fprintf(os.Stdout, "triggering builds for %v\n", *branch)
    113 	for _, f := range files {
    114 		path := filepath.Join(*builds, f.Name())
    115 		manifest, err := ioutil.ReadFile(path)
    116 		if err != nil {
    117 			fmt.Fprintf(os.Stderr, "cannot read manifest %s: %v.\n", path, err)
    118 			os.Exit(1)
    119 		}
    120 		manifest, err = prepareManifest(manifest)
    121 		if err != nil {
    122 			fmt.Fprintf(os.Stderr, "failed to prepare manifest %s: %v.\n", path, err)
    123 			os.Exit(1)
    124 		}
    125 		triggerBuild(string(token), f.Name(), string(manifest), *branch)
    126 	}
    127 }