Merge pull request #62 from spotify/rugvip/builds

backend,proto: added builds service that calls out to github api
This commit is contained in:
Patrik Oldsberg
2020-02-06 13:39:17 +01:00
committed by GitHub
10 changed files with 920 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
secrets.env
+85
View File
@@ -0,0 +1,85 @@
package ghactions
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
)
type Client struct {
client *http.Client
baseURL string
accessToken string
}
func NewFromEnv() (*Client, error) {
accessToken := os.Getenv("BOSS_GH_ACCESS_TOKEN")
if accessToken == "" {
return nil, fmt.Errorf("BOSS_GH_ACCESS_TOKEN not set")
}
return New(http.DefaultClient, "https://api.github.com", accessToken), nil
}
func New(client *http.Client, baseURL, accessToken string) *Client {
return &Client{
client: client,
baseURL: baseURL,
accessToken: accessToken,
}
}
func (c *Client) createRequest(ctx context.Context, owner, repo, path string) (*http.Request, error) {
url := fmt.Sprintf("%s/repos/%s/%s/actions%s", c.baseURL, owner, repo, path)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
req.Header.Add("Authorization", "Bearer "+c.accessToken)
return req.WithContext(ctx), nil
}
func (c *Client) ListWorkflowRuns(ctx context.Context, owner, repo string) (*WorkflowRunsListResponse, error) {
req, err := c.createRequest(ctx, owner, repo, "/runs")
if err != nil {
return nil, err
}
res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var resData WorkflowRunsListResponse
if err := json.NewDecoder(res.Body).Decode(&resData); err != nil {
return nil, err
}
return &resData, nil
}
func (c *Client) GetWorkflowRun(ctx context.Context, owner, repo, runId string) (*WorkflowRunResponse, error) {
req, err := c.createRequest(ctx, owner, repo, fmt.Sprintf("/runs/%s", runId))
if err != nil {
return nil, err
}
res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var resData WorkflowRunResponse
if err := json.NewDecoder(res.Body).Decode(&resData); err != nil {
return nil, err
}
return &resData, nil
}
+53
View File
@@ -0,0 +1,53 @@
package ghactions
type User struct {
Name string `json:"name"` // "Octo Cat"
Email string `json:"email"` // "octocat@github.com"
}
type Commit struct {
ID string `json:"id"` // "acb5820ced9479c074f688cc328bf03f341a511d"
TreeID string `json:"tree_id"` // "d23f6eedb1e1b9610bbc754ddb5197bfe7271223"
Message string `json:"message"` // "Create linter.yml"
Timestamp string `json:"timestamp"` // "2020-01-22T19:33:05Z"
Author User `json:"author"`
Committer User `json:"committer"`
}
type Repository struct {
ID int64 `json:"id"` // 217723378
NodeID string `json:"node_id"` // MDEwOlJlcG9zaXRvcnkyMTc3MjMzNzg="
Name string `json:"name"` // o-repo"
FullName string `json:"full_name"` // "octo-org/octo-repo"
HTMLURL string `json:"html_url"` // "https://github.com/octo-org/octo-repo"
}
type WorkflowRunResponse struct {
ID int64 `json:"id"` // 30433642
NodeID string `json:"node_id"` // "MDEyOldvcmtmbG93IFJ1bjI2OTI4OQ=="
HeadBranch string `json:"head_branch"` // "master"
HeadSha string `json:"head_sha"` // "acb5820ced9479c074f688cc328bf03f341a511d"
RunNumber int64 `json:"run_number"` // 562
CheckSuiteID int64 `json:"check_suite_id"` // 414944374
Event string `json:"event"` // "push"
Status string `json:"status"` // "queued"
Conclusion *string `json:"conclusion"` // null
URL string `json:"url"` // "https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642"
HTMLURL string `json:"html_url"` // "https://github.com/octo-org/octo-repo/actions/runs/30433642"
CreatedAt string `json:"created_at"` // "2020-01-22T19:33:08Z"
UpdatedAt string `json:"updated_at"` // "2020-01-22T19:33:08Z"
JobsURL string `json:"jobs_url"` // "https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/jobs"
LogsURL string `json:"logs_url"` // "https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/logs"
ArtifactsURL string `json:"artifacts_url"` // "https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/artifacts"
CancelURL string `json:"cancel_url"` // "https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/cancel"
RerunURL string `json:"rerun_url"` // "https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/rerun"
WorkflowURL string `json:"workflow_url"` // "https://api.github.com/repos/octo-org/octo-repo/actions/workflows/30433642"
HeadCommit Commit `json:"head_commit"`
Repository Repository `json:"repository"`
HeadRepository Repository `json:"head_repository"`
}
type WorkflowRunsListResponse struct {
TotalCount int64 `json:"total_count"`
Runs []WorkflowRunResponse `json:"workflow_runs"`
}
+10
View File
@@ -0,0 +1,10 @@
module github.com/spotify/backstage/builds
go 1.12
replace github.com/spotify/backstage/proto => ../proto
require (
github.com/spotify/backstage/proto v0.0.0-00010101000000-000000000000
google.golang.org/grpc v1.27.1
)
+54
View File
@@ -0,0 +1,54 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+34
View File
@@ -0,0 +1,34 @@
package main
import (
"log"
"net"
"github.com/spotify/backstage/builds/ghactions"
"github.com/spotify/backstage/builds/service"
buildsv1 "github.com/spotify/backstage/proto/builds/v1"
"google.golang.org/grpc"
)
const (
port = ":50051"
)
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
ghClient, err := ghactions.NewFromEnv()
if err != nil {
log.Fatalf("Failed to create github client, %s", err)
}
buildsv1.RegisterBuildsServer(grpcServer, service.New(ghClient))
log.Println("Serving Builds Service")
grpcServer.Serve(lis)
}
+116
View File
@@ -0,0 +1,116 @@
package service
import (
"context"
"fmt"
"regexp"
"github.com/spotify/backstage/builds/ghactions"
buildsv1 "github.com/spotify/backstage/proto/builds/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type service struct {
ghClient *ghactions.Client
}
var _ buildsv1.BuildsServer = (*service)(nil)
// New creates a new identity data server
func New(ghClient *ghactions.Client) buildsv1.BuildsServer {
return &service{ghClient}
}
func (s *service) ListBuilds(ctx context.Context, req *buildsv1.ListBuildsRequest) (*buildsv1.ListBuildsReply, error) {
owner := "spotify"
repo := "backstage"
result, err := s.ghClient.ListWorkflowRuns(ctx, owner, repo)
if err != nil {
return nil, status.Errorf(codes.Internal, "Failed to fetch workflow runs for %s/%s, %s", owner, repo, err)
}
builds := make([]*buildsv1.Build, len(result.Runs))
for i, run := range result.Runs {
builds[i] = s.transformBuild(owner, repo, &run)
}
return &buildsv1.ListBuildsReply{
EntityUri: "",
Builds: builds,
}, nil
}
func (s *service) GetBuild(ctx context.Context, req *buildsv1.GetBuildRequest) (*buildsv1.GetBuildReply, error) {
uri := req.GetBuildUri()
owner, repo, runID, err := s.parseBuildURI(uri)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Invalid build URI '%s', %s", uri, err)
}
run, err := s.ghClient.GetWorkflowRun(ctx, owner, repo, runID)
if err != nil {
return nil, status.Errorf(codes.Internal, "Failed to fetch workflow run for %s/%s/%s, %s", owner, repo, runID, err)
}
return &buildsv1.GetBuildReply{
Build: s.transformBuild(owner, repo, run),
Details: &buildsv1.BuildDetails{
Author: run.HeadCommit.Author.Name,
LogUrl: run.LogsURL,
OverviewUrl: run.HTMLURL,
},
}, nil
}
func (s *service) transformBuild(owner, repo string, run *ghactions.WorkflowRunResponse) *buildsv1.Build {
stat := buildsv1.BuildStatus_NULL
switch run.Status {
case "queued":
stat = buildsv1.BuildStatus_PENDING
case "in_progress":
stat = buildsv1.BuildStatus_RUNNING
case "completed":
if run.Conclusion != nil {
switch *run.Conclusion {
case "success":
stat = buildsv1.BuildStatus_SUCCESS
case "neutral":
stat = buildsv1.BuildStatus_SUCCESS
case "failure":
stat = buildsv1.BuildStatus_FAILURE
case "cancelled":
stat = buildsv1.BuildStatus_FAILURE
case "timed_out":
stat = buildsv1.BuildStatus_FAILURE
case "action_required":
stat = buildsv1.BuildStatus_RUNNING
}
}
}
return &buildsv1.Build{
Uri: fmt.Sprintf("entity:build:%s/%s/%d", owner, repo, run.ID),
CommitId: run.HeadCommit.ID,
Message: run.HeadCommit.Message,
Status: stat,
}
}
var entityURIRegex = regexp.MustCompile("^entity:build:([^/:]+)/([^/:]+)/([^/:]+)$")
func (s *service) parseBuildURI(uri string) (owner, repo, runID string, err error) {
if uri == "" {
return "", "", "", fmt.Errorf("uri is empty")
}
match := entityURIRegex.FindStringSubmatch(uri)
if err != nil {
return "", "", "", fmt.Errorf("uri does not match")
}
return match[1], match[2], match[3], nil
}
+509
View File
@@ -0,0 +1,509 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: builds/v1/builds.proto
package buildsv1
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type BuildStatus int32
const (
BuildStatus_NULL BuildStatus = 0
BuildStatus_SUCCESS BuildStatus = 1
BuildStatus_FAILURE BuildStatus = 2
BuildStatus_PENDING BuildStatus = 3
BuildStatus_RUNNING BuildStatus = 4
)
var BuildStatus_name = map[int32]string{
0: "NULL",
1: "SUCCESS",
2: "FAILURE",
3: "PENDING",
4: "RUNNING",
}
var BuildStatus_value = map[string]int32{
"NULL": 0,
"SUCCESS": 1,
"FAILURE": 2,
"PENDING": 3,
"RUNNING": 4,
}
func (x BuildStatus) String() string {
return proto.EnumName(BuildStatus_name, int32(x))
}
func (BuildStatus) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_05a627abb7f9adb4, []int{0}
}
type ListBuildsRequest struct {
EntityUri string `protobuf:"bytes,1,opt,name=entity_uri,json=entityUri,proto3" json:"entity_uri,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListBuildsRequest) Reset() { *m = ListBuildsRequest{} }
func (m *ListBuildsRequest) String() string { return proto.CompactTextString(m) }
func (*ListBuildsRequest) ProtoMessage() {}
func (*ListBuildsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_05a627abb7f9adb4, []int{0}
}
func (m *ListBuildsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListBuildsRequest.Unmarshal(m, b)
}
func (m *ListBuildsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListBuildsRequest.Marshal(b, m, deterministic)
}
func (m *ListBuildsRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListBuildsRequest.Merge(m, src)
}
func (m *ListBuildsRequest) XXX_Size() int {
return xxx_messageInfo_ListBuildsRequest.Size(m)
}
func (m *ListBuildsRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ListBuildsRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ListBuildsRequest proto.InternalMessageInfo
func (m *ListBuildsRequest) GetEntityUri() string {
if m != nil {
return m.EntityUri
}
return ""
}
type ListBuildsReply struct {
EntityUri string `protobuf:"bytes,1,opt,name=entity_uri,json=entityUri,proto3" json:"entity_uri,omitempty"`
Builds []*Build `protobuf:"bytes,2,rep,name=builds,proto3" json:"builds,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListBuildsReply) Reset() { *m = ListBuildsReply{} }
func (m *ListBuildsReply) String() string { return proto.CompactTextString(m) }
func (*ListBuildsReply) ProtoMessage() {}
func (*ListBuildsReply) Descriptor() ([]byte, []int) {
return fileDescriptor_05a627abb7f9adb4, []int{1}
}
func (m *ListBuildsReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListBuildsReply.Unmarshal(m, b)
}
func (m *ListBuildsReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListBuildsReply.Marshal(b, m, deterministic)
}
func (m *ListBuildsReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListBuildsReply.Merge(m, src)
}
func (m *ListBuildsReply) XXX_Size() int {
return xxx_messageInfo_ListBuildsReply.Size(m)
}
func (m *ListBuildsReply) XXX_DiscardUnknown() {
xxx_messageInfo_ListBuildsReply.DiscardUnknown(m)
}
var xxx_messageInfo_ListBuildsReply proto.InternalMessageInfo
func (m *ListBuildsReply) GetEntityUri() string {
if m != nil {
return m.EntityUri
}
return ""
}
func (m *ListBuildsReply) GetBuilds() []*Build {
if m != nil {
return m.Builds
}
return nil
}
type GetBuildRequest struct {
BuildUri string `protobuf:"bytes,1,opt,name=build_uri,json=buildUri,proto3" json:"build_uri,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetBuildRequest) Reset() { *m = GetBuildRequest{} }
func (m *GetBuildRequest) String() string { return proto.CompactTextString(m) }
func (*GetBuildRequest) ProtoMessage() {}
func (*GetBuildRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_05a627abb7f9adb4, []int{2}
}
func (m *GetBuildRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetBuildRequest.Unmarshal(m, b)
}
func (m *GetBuildRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetBuildRequest.Marshal(b, m, deterministic)
}
func (m *GetBuildRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetBuildRequest.Merge(m, src)
}
func (m *GetBuildRequest) XXX_Size() int {
return xxx_messageInfo_GetBuildRequest.Size(m)
}
func (m *GetBuildRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetBuildRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GetBuildRequest proto.InternalMessageInfo
func (m *GetBuildRequest) GetBuildUri() string {
if m != nil {
return m.BuildUri
}
return ""
}
type GetBuildReply struct {
Build *Build `protobuf:"bytes,1,opt,name=build,proto3" json:"build,omitempty"`
Details *BuildDetails `protobuf:"bytes,2,opt,name=details,proto3" json:"details,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetBuildReply) Reset() { *m = GetBuildReply{} }
func (m *GetBuildReply) String() string { return proto.CompactTextString(m) }
func (*GetBuildReply) ProtoMessage() {}
func (*GetBuildReply) Descriptor() ([]byte, []int) {
return fileDescriptor_05a627abb7f9adb4, []int{3}
}
func (m *GetBuildReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetBuildReply.Unmarshal(m, b)
}
func (m *GetBuildReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetBuildReply.Marshal(b, m, deterministic)
}
func (m *GetBuildReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetBuildReply.Merge(m, src)
}
func (m *GetBuildReply) XXX_Size() int {
return xxx_messageInfo_GetBuildReply.Size(m)
}
func (m *GetBuildReply) XXX_DiscardUnknown() {
xxx_messageInfo_GetBuildReply.DiscardUnknown(m)
}
var xxx_messageInfo_GetBuildReply proto.InternalMessageInfo
func (m *GetBuildReply) GetBuild() *Build {
if m != nil {
return m.Build
}
return nil
}
func (m *GetBuildReply) GetDetails() *BuildDetails {
if m != nil {
return m.Details
}
return nil
}
type Build struct {
Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty"`
CommitId string `protobuf:"bytes,2,opt,name=commit_id,json=commitId,proto3" json:"commit_id,omitempty"`
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
Status BuildStatus `protobuf:"varint,4,opt,name=status,proto3,enum=spotify.backstage.builds.v1.BuildStatus" json:"status,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Build) Reset() { *m = Build{} }
func (m *Build) String() string { return proto.CompactTextString(m) }
func (*Build) ProtoMessage() {}
func (*Build) Descriptor() ([]byte, []int) {
return fileDescriptor_05a627abb7f9adb4, []int{4}
}
func (m *Build) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Build.Unmarshal(m, b)
}
func (m *Build) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Build.Marshal(b, m, deterministic)
}
func (m *Build) XXX_Merge(src proto.Message) {
xxx_messageInfo_Build.Merge(m, src)
}
func (m *Build) XXX_Size() int {
return xxx_messageInfo_Build.Size(m)
}
func (m *Build) XXX_DiscardUnknown() {
xxx_messageInfo_Build.DiscardUnknown(m)
}
var xxx_messageInfo_Build proto.InternalMessageInfo
func (m *Build) GetUri() string {
if m != nil {
return m.Uri
}
return ""
}
func (m *Build) GetCommitId() string {
if m != nil {
return m.CommitId
}
return ""
}
func (m *Build) GetMessage() string {
if m != nil {
return m.Message
}
return ""
}
func (m *Build) GetStatus() BuildStatus {
if m != nil {
return m.Status
}
return BuildStatus_NULL
}
type BuildDetails struct {
Author string `protobuf:"bytes,1,opt,name=author,proto3" json:"author,omitempty"`
OverviewUrl string `protobuf:"bytes,2,opt,name=overview_url,json=overviewUrl,proto3" json:"overview_url,omitempty"`
LogUrl string `protobuf:"bytes,3,opt,name=log_url,json=logUrl,proto3" json:"log_url,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BuildDetails) Reset() { *m = BuildDetails{} }
func (m *BuildDetails) String() string { return proto.CompactTextString(m) }
func (*BuildDetails) ProtoMessage() {}
func (*BuildDetails) Descriptor() ([]byte, []int) {
return fileDescriptor_05a627abb7f9adb4, []int{5}
}
func (m *BuildDetails) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BuildDetails.Unmarshal(m, b)
}
func (m *BuildDetails) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BuildDetails.Marshal(b, m, deterministic)
}
func (m *BuildDetails) XXX_Merge(src proto.Message) {
xxx_messageInfo_BuildDetails.Merge(m, src)
}
func (m *BuildDetails) XXX_Size() int {
return xxx_messageInfo_BuildDetails.Size(m)
}
func (m *BuildDetails) XXX_DiscardUnknown() {
xxx_messageInfo_BuildDetails.DiscardUnknown(m)
}
var xxx_messageInfo_BuildDetails proto.InternalMessageInfo
func (m *BuildDetails) GetAuthor() string {
if m != nil {
return m.Author
}
return ""
}
func (m *BuildDetails) GetOverviewUrl() string {
if m != nil {
return m.OverviewUrl
}
return ""
}
func (m *BuildDetails) GetLogUrl() string {
if m != nil {
return m.LogUrl
}
return ""
}
func init() {
proto.RegisterEnum("spotify.backstage.builds.v1.BuildStatus", BuildStatus_name, BuildStatus_value)
proto.RegisterType((*ListBuildsRequest)(nil), "spotify.backstage.builds.v1.ListBuildsRequest")
proto.RegisterType((*ListBuildsReply)(nil), "spotify.backstage.builds.v1.ListBuildsReply")
proto.RegisterType((*GetBuildRequest)(nil), "spotify.backstage.builds.v1.GetBuildRequest")
proto.RegisterType((*GetBuildReply)(nil), "spotify.backstage.builds.v1.GetBuildReply")
proto.RegisterType((*Build)(nil), "spotify.backstage.builds.v1.Build")
proto.RegisterType((*BuildDetails)(nil), "spotify.backstage.builds.v1.BuildDetails")
}
func init() { proto.RegisterFile("builds/v1/builds.proto", fileDescriptor_05a627abb7f9adb4) }
var fileDescriptor_05a627abb7f9adb4 = []byte{
// 447 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0x41, 0x6f, 0xd3, 0x30,
0x18, 0x25, 0x6b, 0x97, 0xb6, 0x5f, 0x07, 0x0b, 0x3e, 0x8c, 0x68, 0x13, 0x52, 0xc9, 0xa9, 0x4c,
0x28, 0x53, 0xcb, 0x05, 0x71, 0x82, 0x75, 0x65, 0xaa, 0xa8, 0x22, 0xe4, 0x2a, 0x17, 0x2e, 0x55,
0xda, 0x98, 0x62, 0x70, 0x71, 0xb0, 0x9d, 0xa0, 0xfc, 0x09, 0x0e, 0xfc, 0x3c, 0x7e, 0x0d, 0xb2,
0x9d, 0xa8, 0x11, 0x93, 0xda, 0xde, 0xfc, 0xbe, 0xf7, 0xbd, 0xbc, 0xf7, 0x49, 0x2f, 0x70, 0xb1,
0xca, 0x29, 0x4b, 0xe5, 0x4d, 0x31, 0xba, 0xb1, 0xaf, 0x30, 0x13, 0x5c, 0x71, 0x74, 0x25, 0x33,
0xae, 0xe8, 0x97, 0x32, 0x5c, 0x25, 0xeb, 0xef, 0x52, 0x25, 0x1b, 0x12, 0x56, 0x7c, 0x31, 0x0a,
0xc6, 0xf0, 0x74, 0x4e, 0xa5, 0xba, 0x35, 0x03, 0x4c, 0x7e, 0xe6, 0x44, 0x2a, 0xf4, 0x1c, 0x80,
0xfc, 0x50, 0x54, 0x95, 0xcb, 0x5c, 0x50, 0xdf, 0x19, 0x38, 0xc3, 0x1e, 0xee, 0xd9, 0x49, 0x2c,
0x68, 0xc0, 0xe0, 0xbc, 0xa9, 0xc9, 0x58, 0x79, 0x40, 0x81, 0xde, 0x82, 0x6b, 0x2d, 0xfd, 0x93,
0x41, 0x6b, 0xd8, 0x1f, 0x07, 0xe1, 0x9e, 0x4c, 0xa1, 0xf9, 0x30, 0xae, 0x14, 0x41, 0x08, 0xe7,
0xf7, 0xc4, 0x9a, 0xd5, 0xf9, 0xae, 0xa0, 0x67, 0xc8, 0x86, 0x59, 0xd7, 0x0c, 0x74, 0xba, 0xdf,
0x0e, 0x3c, 0xde, 0x09, 0x74, 0xb8, 0x37, 0x70, 0x6a, 0x58, 0xb3, 0x7a, 0x9c, 0xb9, 0x15, 0xa0,
0x09, 0x74, 0x52, 0xa2, 0x12, 0xca, 0x74, 0x70, 0xad, 0x7d, 0x79, 0x58, 0x7b, 0x67, 0x05, 0xb8,
0x56, 0x06, 0x7f, 0x1c, 0x38, 0x35, 0x0c, 0xf2, 0xa0, 0xb5, 0x4b, 0xac, 0x9f, 0xfa, 0x92, 0x35,
0xdf, 0x6e, 0xa9, 0x5a, 0xd2, 0xd4, 0x58, 0xf4, 0x70, 0xd7, 0x0e, 0x66, 0x29, 0xf2, 0xa1, 0xb3,
0x25, 0x52, 0x26, 0x1b, 0xe2, 0xb7, 0x0c, 0x55, 0x43, 0xf4, 0x0e, 0x5c, 0xa9, 0x12, 0x95, 0x4b,
0xbf, 0x3d, 0x70, 0x86, 0x4f, 0xc6, 0xc3, 0xc3, 0xb1, 0x16, 0x66, 0x1f, 0x57, 0xba, 0x60, 0x05,
0x67, 0xcd, 0xb4, 0xe8, 0x02, 0xdc, 0x24, 0x57, 0x5f, 0xb9, 0xa8, 0xd2, 0x55, 0x08, 0xbd, 0x80,
0x33, 0x5e, 0x10, 0x51, 0x50, 0xf2, 0x6b, 0x99, 0x0b, 0x56, 0x65, 0xec, 0xd7, 0xb3, 0x58, 0x30,
0xf4, 0x0c, 0x3a, 0x8c, 0x6f, 0x0c, 0x6b, 0x63, 0xba, 0x8c, 0x6f, 0x62, 0xc1, 0xae, 0x3f, 0x42,
0xbf, 0x61, 0x8d, 0xba, 0xd0, 0x8e, 0xe2, 0xf9, 0xdc, 0x7b, 0x84, 0xfa, 0xd0, 0x59, 0xc4, 0x93,
0xc9, 0x74, 0xb1, 0xf0, 0x1c, 0x0d, 0x3e, 0xbc, 0x9f, 0xcd, 0x63, 0x3c, 0xf5, 0x4e, 0x34, 0xf8,
0x34, 0x8d, 0xee, 0x66, 0xd1, 0xbd, 0xd7, 0xd2, 0x00, 0xc7, 0x51, 0xa4, 0x41, 0x7b, 0xfc, 0xd7,
0x01, 0xd7, 0x36, 0x0e, 0x7d, 0x03, 0xd8, 0xf5, 0x0f, 0x85, 0x7b, 0x6f, 0x7f, 0x50, 0xee, 0xcb,
0x57, 0x47, 0xef, 0xeb, 0xee, 0xa4, 0xd0, 0xad, 0xcb, 0x84, 0xf6, 0x2b, 0xff, 0x2b, 0xe9, 0xe5,
0xf5, 0x91, 0xdb, 0x19, 0x2b, 0x6f, 0xe1, 0xb3, 0xed, 0xaf, 0x2c, 0x46, 0x2b, 0xd7, 0xfc, 0xb5,
0xaf, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x40, 0xf3, 0x96, 0x0b, 0xcf, 0x03, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6
// BuildsClient is the client API for Builds service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type BuildsClient interface {
ListBuilds(ctx context.Context, in *ListBuildsRequest, opts ...grpc.CallOption) (*ListBuildsReply, error)
GetBuild(ctx context.Context, in *GetBuildRequest, opts ...grpc.CallOption) (*GetBuildReply, error)
}
type buildsClient struct {
cc grpc.ClientConnInterface
}
func NewBuildsClient(cc grpc.ClientConnInterface) BuildsClient {
return &buildsClient{cc}
}
func (c *buildsClient) ListBuilds(ctx context.Context, in *ListBuildsRequest, opts ...grpc.CallOption) (*ListBuildsReply, error) {
out := new(ListBuildsReply)
err := c.cc.Invoke(ctx, "/spotify.backstage.builds.v1.Builds/ListBuilds", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *buildsClient) GetBuild(ctx context.Context, in *GetBuildRequest, opts ...grpc.CallOption) (*GetBuildReply, error) {
out := new(GetBuildReply)
err := c.cc.Invoke(ctx, "/spotify.backstage.builds.v1.Builds/GetBuild", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// BuildsServer is the server API for Builds service.
type BuildsServer interface {
ListBuilds(context.Context, *ListBuildsRequest) (*ListBuildsReply, error)
GetBuild(context.Context, *GetBuildRequest) (*GetBuildReply, error)
}
// UnimplementedBuildsServer can be embedded to have forward compatible implementations.
type UnimplementedBuildsServer struct {
}
func (*UnimplementedBuildsServer) ListBuilds(ctx context.Context, req *ListBuildsRequest) (*ListBuildsReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListBuilds not implemented")
}
func (*UnimplementedBuildsServer) GetBuild(ctx context.Context, req *GetBuildRequest) (*GetBuildReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetBuild not implemented")
}
func RegisterBuildsServer(s *grpc.Server, srv BuildsServer) {
s.RegisterService(&_Builds_serviceDesc, srv)
}
func _Builds_ListBuilds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListBuildsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(BuildsServer).ListBuilds(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/spotify.backstage.builds.v1.Builds/ListBuilds",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BuildsServer).ListBuilds(ctx, req.(*ListBuildsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Builds_GetBuild_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetBuildRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(BuildsServer).GetBuild(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/spotify.backstage.builds.v1.Builds/GetBuild",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BuildsServer).GetBuild(ctx, req.(*GetBuildRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Builds_serviceDesc = grpc.ServiceDesc{
ServiceName: "spotify.backstage.builds.v1.Builds",
HandlerType: (*BuildsServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ListBuilds",
Handler: _Builds_ListBuilds_Handler,
},
{
MethodName: "GetBuild",
Handler: _Builds_GetBuild_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "builds/v1/builds.proto",
}
+9
View File
@@ -21,6 +21,15 @@ services:
volumes:
- ./backend/identity/identity-data.json:/app/identity-data.json
builds:
container_name: boss-builds
build:
context: backend
args:
service: builds
restart: unless-stopped
env_file: secrets.env
scaffolder:
container_name: boss-scaffolder
build:
+49
View File
@@ -0,0 +1,49 @@
syntax = "proto3";
package spotify.backstage.builds.v1;
option go_package = "buildsv1";
service Builds {
rpc ListBuilds(ListBuildsRequest) returns (ListBuildsReply);
rpc GetBuild(GetBuildRequest) returns (GetBuildReply);
}
message ListBuildsRequest {
string entity_uri = 1;
}
message ListBuildsReply {
string entity_uri = 1;
repeated Build builds = 2;
}
message GetBuildRequest {
string build_uri = 1;
}
message GetBuildReply {
Build build = 1;
BuildDetails details = 2;
}
message Build {
string uri = 1;
string commit_id = 2;
string message = 3;
BuildStatus status = 4;
}
message BuildDetails {
string author = 1;
string overview_url = 2;
string log_url = 3;
}
enum BuildStatus {
NULL = 0;
SUCCESS = 1;
FAILURE = 2;
PENDING = 3;
RUNNING = 4;
}