@@ -20,8 +20,10 @@ import (
2020 "errors"
2121 "fmt"
2222 "net"
23+ "net/http"
2324 "os"
2425 "os/signal"
26+ "strings"
2527 "sync"
2628 "syscall"
2729 "time"
@@ -40,6 +42,7 @@ import (
4042 "github.com/uber/submitqueue/submitqueue/core/consumer"
4143 "github.com/uber/submitqueue/submitqueue/entity"
4244 "github.com/uber/submitqueue/submitqueue/extension/buildrunner"
45+ "github.com/uber/submitqueue/submitqueue/extension/buildrunner/githubactions"
4346 buildnoop "github.com/uber/submitqueue/submitqueue/extension/buildrunner/noop"
4447 "github.com/uber/submitqueue/submitqueue/extension/changeprovider"
4548 githubprovider "github.com/uber/submitqueue/submitqueue/extension/changeprovider/github"
@@ -226,8 +229,12 @@ func run() error {
226229 }
227230
228231 // Create build runner. The noop runner is the pass-through default
229- // (every build immediately succeeds) until a real backend is wired in.
230- br := buildnoop .New ()
232+ // (every build immediately succeeds); BUILD_RUNNER=githubactions enables
233+ // the GitHub Actions proof-of-architecture backend.
234+ br , err := newBuildRunnerFactory (logger )
235+ if err != nil {
236+ return fmt .Errorf ("failed to create build runner: %w" , err )
237+ }
231238
232239 // Register controllers
233240 if err := registerControllers (c , logger .Sugar (), scope , registry , mc , cp , psh , br , cnt , store ); err != nil {
@@ -456,7 +463,7 @@ type conflictFactory struct{ impl conflict.Analyzer }
456463
457464func (f conflictFactory ) For (conflict.Config ) (conflict.Analyzer , error ) { return f .impl , nil }
458465
459- func registerControllers (c consumer.Consumer , logger * zap.SugaredLogger , scope tally.Scope , registry consumer.TopicRegistry , mc mergechecker.MergeChecker , cp changeprovider.ChangeProvider , psh pusher.Pusher , br buildrunner.BuildRunner , cnt counter.Counter , store storage.Storage ) error {
466+ func registerControllers (c consumer.Consumer , logger * zap.SugaredLogger , scope tally.Scope , registry consumer.TopicRegistry , mc mergechecker.MergeChecker , cp changeprovider.ChangeProvider , psh pusher.Pusher , br buildrunner.Factory , cnt counter.Counter , store storage.Storage ) error {
460467 requestController := start .NewController (
461468 logger ,
462469 scope ,
@@ -553,7 +560,7 @@ func registerControllers(c consumer.Consumer, logger *zap.SugaredLogger, scope t
553560 logger ,
554561 scope ,
555562 store ,
556- buildRunnerFactory { impl : br } ,
563+ br ,
557564 registry ,
558565 consumer .TopicKeyBuild ,
559566 "orchestrator-build" ,
@@ -566,7 +573,7 @@ func registerControllers(c consumer.Consumer, logger *zap.SugaredLogger, scope t
566573 logger ,
567574 scope ,
568575 store ,
569- buildRunnerFactory { impl : br } ,
576+ br ,
570577 registry ,
571578 consumer .TopicKeyBuildSignal ,
572579 "orchestrator-buildsignal" ,
@@ -701,6 +708,82 @@ func newPusher(logger *zap.Logger, scope tally.Scope) (pusher.Pusher, error) {
701708 }), nil
702709}
703710
711+ // newBuildRunnerFactory creates the BuildRunner factory selected by
712+ // BUILD_RUNNER. Defaults to noop for local development. Use
713+ // BUILD_RUNNER=githubactions to dispatch a GitHub Actions workflow via
714+ // workflow_dispatch.
715+ func newBuildRunnerFactory (logger * zap.Logger ) (buildrunner.Factory , error ) {
716+ switch strings .ToLower (getEnv ("BUILD_RUNNER" , "noop" )) {
717+ case "noop" :
718+ logger .Info ("using noop build runner" )
719+ return buildRunnerFactory {impl : buildnoop .New ()}, nil
720+ case "githubactions" , "github-actions" :
721+ client , err := newGitHubHTTPClient ()
722+ if err != nil {
723+ return nil , err
724+ }
725+ f , err := githubactions .NewFactory (githubactions.FactoryParams {
726+ HTTPClient : client ,
727+ Logger : logger .Sugar (),
728+ Owner : os .Getenv ("GITHUB_ACTIONS_OWNER" ),
729+ Repo : os .Getenv ("GITHUB_ACTIONS_REPO" ),
730+ WorkflowID : os .Getenv ("GITHUB_ACTIONS_WORKFLOW" ),
731+ Ref : getEnv ("GITHUB_ACTIONS_REF" , "main" ),
732+ ExtraInputs : parseKeyValueList (
733+ os .Getenv ("GITHUB_ACTIONS_EXTRA_INPUTS" ),
734+ ),
735+ })
736+ if err != nil {
737+ return nil , err
738+ }
739+ logger .Info ("using GitHub Actions build runner" ,
740+ zap .String ("owner" , os .Getenv ("GITHUB_ACTIONS_OWNER" )),
741+ zap .String ("repo" , os .Getenv ("GITHUB_ACTIONS_REPO" )),
742+ zap .String ("workflow" , os .Getenv ("GITHUB_ACTIONS_WORKFLOW" )),
743+ zap .String ("ref" , getEnv ("GITHUB_ACTIONS_REF" , "main" )),
744+ )
745+ return f , nil
746+ default :
747+ return nil , fmt .Errorf ("unsupported BUILD_RUNNER %q (supported: noop, githubactions)" , os .Getenv ("BUILD_RUNNER" ))
748+ }
749+ }
750+
751+ func newGitHubHTTPClient () (* http.Client , error ) {
752+ client , err := httpclient .NewClient (getEnv ("GITHUB_BASE_URL" , "https://api.github.com" ))
753+ if err != nil {
754+ return nil , fmt .Errorf ("failed to build GitHub HTTP client: %w" , err )
755+ }
756+
757+ if token := os .Getenv ("GITHUB_TOKEN" ); token != "" {
758+ ts := oauth2 .StaticTokenSource (& oauth2.Token {AccessToken : token })
759+ client .Transport = & oauth2.Transport {Source : ts , Base : client .Transport }
760+ }
761+
762+ client .Timeout = parseTimeout (os .Getenv ("GITHUB_TIMEOUT" ), 30 * time .Second )
763+ return client , nil
764+ }
765+
766+ // parseKeyValueList parses comma-separated key=value entries. Invalid entries
767+ // are ignored so optional configuration cannot prevent the server from
768+ // starting.
769+ func parseKeyValueList (raw string ) map [string ]string {
770+ if raw == "" {
771+ return nil
772+ }
773+ out := make (map [string ]string )
774+ for _ , part := range strings .Split (raw , "," ) {
775+ key , value , ok := strings .Cut (strings .TrimSpace (part ), "=" )
776+ if ! ok || key == "" {
777+ continue
778+ }
779+ out [key ] = value
780+ }
781+ if len (out ) == 0 {
782+ return nil
783+ }
784+ return out
785+ }
786+
704787// noopPusher is a fallback Pusher used when PUSHER_CHECKOUT_PATH is not
705788// configured. It returns an error on every Push so the merge controller
706789// (which treats non-ErrConflict errors as transient and nacks the message)
0 commit comments