about summary refs log tree commit diff
path: root/ops/sync-gcsr/main.go
diff options
context:
space:
mode:
authorVincent Ambo <Vincent Ambo>2020-01-18T15·36+0000
committerVincent Ambo <Vincent Ambo>2020-01-18T15·36+0000
commitb8355066e84075b603038f407f18166aa034db4f (patch)
tree157e20d9dd9a92fd18923ef35188dc617998b98e /ops/sync-gcsr/main.go
parent44116522dd82aefbaf7cdb9f42dbcc1e96eedc99 (diff)
feat(sync-gcsr): Trigger sourcehut builds on master branch changes r/406
Calls the sourcehut API at builds.sr.ht to trigger a build if the
master branch changes.

The build manifest is going to be stored in the depot too, coming up
next ...
Diffstat (limited to 'ops/sync-gcsr/main.go')
-rw-r--r--ops/sync-gcsr/main.go75
1 files changed, 69 insertions, 6 deletions
diff --git a/ops/sync-gcsr/main.go b/ops/sync-gcsr/main.go
index 08e2627c6602..4841b6261934 100644
--- a/ops/sync-gcsr/main.go
+++ b/ops/sync-gcsr/main.go
@@ -3,19 +3,37 @@
 //
 // sync-gcsr implements a small utility that periodically mirrors a
 // remote Google Cloud Source Repository to a local file path.
+//
+// This utility is also responsible for triggering depot builds on
+// builds.sr.ht if a change is detected on the master branch.
 package main
 
 import (
+	"encoding/json"
 	"fmt"
+	"io/ioutil"
 	"log"
+	"net/http"
 	"os"
 	"time"
+	"bytes"
 
 	git "gopkg.in/src-d/go-git.v4"
 	"gopkg.in/src-d/go-git.v4/plumbing"
-	"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
+	githttp "gopkg.in/src-d/go-git.v4/plumbing/transport/http"
 )
 
+// Path to the build manifest, added by Nix at compile time.
+var BuildManifest string
+
+// 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 EnvOr(key, def string) string {
 	v := os.Getenv(key)
 	if v == "" {
@@ -25,8 +43,49 @@ func EnvOr(key, def string) string {
 	return v
 }
 
+// Trigger a build of master on builds.sr.ht
+func triggerBuild(commit string) {
+	manifest, err := ioutil.ReadFile(BuildManifest)
+	if err != nil {
+		log.Fatalln("[ERROR] failed to read sr.ht build manifest:", err)
+	}
+
+	build := Build{
+		Manifest: string(manifest),
+		Note:     fmt.Sprintf("Build of 'master' at '%s'", commit),
+		Tags: []string{
+			"tazjins-depot", "master",
+		},
+	}
+
+	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 {
+		log.Fatalln("[ERROR] failed to create an HTTP request:", err)
+	}
+
+	req.Header.Add("Authorization", fmt.Sprintf("token %s", os.Getenv("SRHT_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.
+		log.Println("failed to send builds.sr.ht request:", err)
+		return
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != 200 {
+		respBody, err := ioutil.ReadAll(resp.Body)
+		log.Printf("received non-success response from builds.sr.ht: %s (%v)[%s]", respBody, resp.Status, err)
+	}
+}
+
 // ensure that all remote branches exist locally & are up to date.
-func updateBranches(auth *http.BasicAuth, repo *git.Repository) error {
+func updateBranches(auth *githttp.BasicAuth, repo *git.Repository) error {
 	origin, err := repo.Remote("origin")
 	if err != nil {
 		return err
@@ -61,13 +120,17 @@ func updateBranches(auth *http.BasicAuth, repo *git.Repository) error {
 			return err
 		}
 
+		if ref.Name().Short() == "master" {
+			go triggerBuild(ref.Hash().String())
+		}
+
 		log.Println("Updated branch", ref.Name().String())
 	}
 
 	return nil
 }
 
-func updateRepo(auth *http.BasicAuth, repo *git.Repository, opts *git.FetchOptions) error {
+func updateRepo(auth *githttp.BasicAuth, repo *git.Repository, opts *git.FetchOptions) error {
 	err := repo.Fetch(opts)
 
 	if err == git.NoErrAlreadyUpToDate {
@@ -81,7 +144,7 @@ func updateRepo(auth *http.BasicAuth, repo *git.Repository, opts *git.FetchOptio
 	return updateBranches(auth, repo)
 }
 
-func cloneRepo(dest, project, repo string, auth *http.BasicAuth) (*git.Repository, error) {
+func cloneRepo(dest, project, repo string, auth *githttp.BasicAuth) (*git.Repository, error) {
 	var cloneOpts = git.CloneOptions{
 		Auth: auth,
 		URL:  fmt.Sprintf("https://source.developers.google.com/p/%s/r/%s", project, repo),
@@ -105,9 +168,9 @@ func main() {
 
 	log.Printf("Syncing repository '%s/%s' to destination '%s'", project, repo, dest)
 
-	var auth *http.BasicAuth
+	var auth *githttp.BasicAuth
 	if user != "" && pass != "" {
-		auth = &http.BasicAuth{
+		auth = &githttp.BasicAuth{
 			Username: user,
 			Password: pass,
 		}