From b8355066e84075b603038f407f18166aa034db4f Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 18 Jan 2020 15:36:15 +0000 Subject: feat(sync-gcsr): Trigger sourcehut builds on master branch changes 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 ... --- ops/sync-gcsr/main.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/ops/sync-gcsr/main.go b/ops/sync-gcsr/main.go index 08e2627c66..4841b62619 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, } -- cgit 1.4.1