commit fd5e7ad395d749eaf07d0b345fd563c251eefad9
parent da55f31755481c16a92f5f0ff6271c66f828aa09
Author: Vincent Demeester <vincent@sbr.pm>
Date: Sun, 10 May 2020 13:54:39 +0200
bus: add a small tool to schedule builds.sr.ht builds…
… used in sakhalin to schedule nightly builds.
Signed-off-by: Vincent Demeester <vincent@sbr.pm>
Diffstat:
4 files changed, 116 insertions(+), 5 deletions(-)
diff --git a/machines/sakhalin.nixos.nix b/machines/sakhalin.nixos.nix
@@ -104,6 +104,7 @@ with import ../assets/machines.nix; {
OnFailure = "status-email-root@%n.service";
};
};
+ environment.etc."secrets/srht-token".text = "${tokten_srht}";
# builds.sr.ht: daily builds
systemd.services.builds-srht = {
description = "Daily builds.sr.ht";
@@ -119,11 +120,7 @@ with import ../assets/machines.nix; {
OnFailure = "status-email-root@%n.service";
};
- path = with pkgs; [ httpie ];
- script = ''
- manifest=$(cat /etc/nixos/.builds/nixos.yml)
- http POST https://builds.sr.ht/api/jobs manifest="${manifest}" Authorization:"token ${token_srht}"
- '';
+ script = "${pkgs.my.bus}/bin/bus";
startAt = "daily";
};
diff --git a/pkgs/default.nix b/pkgs/default.nix
@@ -6,6 +6,7 @@ rec {
tmux-tpm = pkgs.callPackage ./tmux-tpm { };
vrsync = pkgs.callPackage ./vrsync { };
vde-thinkpad = pkgs.callPackage ./vde-thinkpad { };
+ bus = pkgs.callPackage ../tools/bus { };
# Mine
ape = pkgs.callPackage ./ape { };
diff --git a/tools/bus/default.nix b/tools/bus/default.nix
@@ -0,0 +1,19 @@
+{ stdenv, lib, go }:
+
+stdenv.mkDerivation rec {
+ name = "bus";
+ src = ./.;
+
+ phases = "buildPhase installPhase";
+ buildInputs = [ go ];
+
+ buildPhase = ''
+ HOME=$(pwd)
+ cp $src/main.go .
+ go build -o bus main.go
+ '';
+ installPhase = ''
+ mkdir $out
+ install -D bus $out/bin/bus
+ '';
+}
diff --git a/tools/bus/main.go b/tools/bus/main.go
@@ -0,0 +1,94 @@
+// bus is a smal CLI tool that triggers home builds on build.sr.ht.
+// It is designed to be run in a cron or systemd timer.
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+var (
+ branch = flag.String("branch", "master", "branch to schedule")
+ secret = flag.String("secret", "/etc/secrets/srht-token", "path to find sr.ht secret")
+ builds = flag.String("builds", "/etc/nixos/.builds", "path to find builds manifests")
+)
+
+// Represents a builds.sr.ht build object as described on
+// https://man.sr.ht/builds.sr.ht/api.md
+type Build struct {
+ Manifest string `json:"manifest"`
+ Note string `json:"note"`
+ Tags []string `json:"tags"`
+}
+
+func triggerBuild(token, name, manifest, branch string) {
+ build := Build{
+ Manifest: manifest,
+ Note: fmt.Sprintf("Nightly build of '%s' on '%s'", name, branch),
+ Tags: []string{
+ // my branch names tend to contain slashes, which are not valid
+ // identifiers in sourcehut.
+ "home", "nightly", strings.ReplaceAll(branch, "/", "_"), name,
+ },
+ }
+
+ body, _ := json.Marshal(build)
+ reader := ioutil.NopCloser(bytes.NewReader(body))
+
+ req, err := http.NewRequest("POST", "https://builds.sr.ht/api/jobs", reader)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "failed to create an HTTP request: %s", err)
+ os.Exit(1)
+ }
+
+ req.Header.Add("Authorization", "token "+token)
+ req.Header.Add("Content-Type", "application/json")
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ // This might indicate a temporary error on the sourcehut side, do
+ // not fail the whole program.
+ fmt.Fprintf(os.Stderr, "failed to send build.sr.ht request: %s", err)
+ return
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != 200 {
+ respBody, _ := ioutil.ReadAll(resp.Body)
+ fmt.Fprintf(os.Stderr, "received non-success response from builds.sr.ht: %s (%v)", respBody, resp.Status)
+ os.Exit(1)
+ } else {
+ fmt.Fprintf(os.Stdout, "triggered builds.sr.ht job for branch '%s'\n", branch)
+ }
+}
+
+func main() {
+ flag.Parse()
+ token, err := ioutil.ReadFile(*secret)
+ if err != nil {
+ fmt.Fprint(os.Stderr, "sourcehut token could not be read.")
+ os.Exit(1)
+ }
+ files, err := ioutil.ReadDir(*builds)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "cannot list builds manifest from %s.", *builds)
+ os.Exit(1)
+ }
+ fmt.Fprintf(os.Stdout, "triggering builds for %v\n", *branch)
+ for _, f := range files {
+ path := filepath.Join(*builds, f.Name())
+ manifest, err := ioutil.ReadFile(path)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "cannot read manifest %s.", path)
+ os.Exit(1)
+ }
+ triggerBuild(string(token), f.Name(), string(manifest), *branch)
+ }
+}