Add CheckRun.Names scale-up trigger configuration (#390)
This allows you to trigger autoscaling depending on check_run names(i.e. actions job names). If you are willing to differentiate scale amount only for a specific job, or want to scale only on a specific job, try this.
This commit is contained in:
		
							parent
							
								
									a6270b44d5
								
							
						
					
					
						commit
						8d3a83b07a
					
				|  | @ -72,6 +72,12 @@ type GitHubEventScaleUpTriggerSpec struct { | |||
| type CheckRunSpec struct { | ||||
| 	Types  []string `json:"types,omitempty"` | ||||
| 	Status string   `json:"status,omitempty"` | ||||
| 
 | ||||
| 	// Names is a list of GitHub Actions glob patterns.
 | ||||
| 	// Any check_run event whose name matches one of patterns in the list can trigger autoscaling.
 | ||||
| 	// Note that check_run name seem to equal to the job name you've defined in your actions workflow yaml file.
 | ||||
| 	// So it is very likely that you can utilize this to trigger depending on the job.
 | ||||
| 	Names []string `json:"names,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request
 | ||||
|  |  | |||
|  | @ -66,6 +66,11 @@ func (in *CheckRunSpec) DeepCopyInto(out *CheckRunSpec) { | |||
| 		*out = make([]string, len(*in)) | ||||
| 		copy(*out, *in) | ||||
| 	} | ||||
| 	if in.Names != nil { | ||||
| 		in, out := &in.Names, &out.Names | ||||
| 		*out = make([]string, len(*in)) | ||||
| 		copy(*out, *in) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheckRunSpec.
 | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ type: application | |||
| # This is the chart version. This version number should be incremented each time you make changes | ||||
| # to the chart and its templates, including the app version. | ||||
| # Versions are expected to follow Semantic Versioning (https://semver.org/) | ||||
| version: 0.8.0 | ||||
| version: 0.9.0 | ||||
| 
 | ||||
| home: https://github.com/summerwind/actions-runner-controller | ||||
| 
 | ||||
|  |  | |||
|  | @ -148,6 +148,17 @@ spec: | |||
|                       checkRun: | ||||
|                         description: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#check_run | ||||
|                         properties: | ||||
|                           names: | ||||
|                             description: Names is a list of GitHub Actions glob patterns. | ||||
|                               Any check_run event whose name matches one of patterns | ||||
|                               in the list can trigger autoscaling. Note that check_run | ||||
|                               name seem to equal to the job name you've defined in | ||||
|                               your actions workflow yaml file. So it is very likely | ||||
|                               that you can utilize this to trigger depending on the | ||||
|                               job. | ||||
|                             items: | ||||
|                               type: string | ||||
|                             type: array | ||||
|                           status: | ||||
|                             type: string | ||||
|                           types: | ||||
|  |  | |||
|  | @ -148,6 +148,17 @@ spec: | |||
|                       checkRun: | ||||
|                         description: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#check_run | ||||
|                         properties: | ||||
|                           names: | ||||
|                             description: Names is a list of GitHub Actions glob patterns. | ||||
|                               Any check_run event whose name matches one of patterns | ||||
|                               in the list can trigger autoscaling. Note that check_run | ||||
|                               name seem to equal to the job name you've defined in | ||||
|                               your actions workflow yaml file. So it is very likely | ||||
|                               that you can utilize this to trigger depending on the | ||||
|                               job. | ||||
|                             items: | ||||
|                               type: string | ||||
|                             type: array | ||||
|                           status: | ||||
|                             type: string | ||||
|                           types: | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ package controllers | |||
| import ( | ||||
| 	"github.com/google/go-github/v33/github" | ||||
| 	"github.com/summerwind/actions-runner-controller/api/v1alpha1" | ||||
| 	"github.com/summerwind/actions-runner-controller/pkg/actionsglob" | ||||
| ) | ||||
| 
 | ||||
| func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) MatchCheckRunEvent(event *github.CheckRunEvent) func(scaleUpTrigger v1alpha1.ScaleUpTrigger) bool { | ||||
|  | @ -27,6 +28,16 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) MatchCheckRunEvent(ev | |||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		if checkRun := event.CheckRun; checkRun != nil && len(cr.Names) > 0 { | ||||
| 			for _, pat := range cr.Names { | ||||
| 				if r := actionsglob.Match(pat, checkRun.GetName()); r { | ||||
| 					return true | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		return true | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,8 @@ | |||
| This package is an implementation of glob that is intended to simulate the behaviour of  | ||||
| https://github.com/actions/toolkit/tree/master/packages/glob in many cases. | ||||
| 
 | ||||
| This isn't a complete reimplementation of the referenced nodejs package. | ||||
| 
 | ||||
| Differences: | ||||
| 
 | ||||
| - This package doesn't implement `**` | ||||
|  | @ -0,0 +1,78 @@ | |||
| package actionsglob | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| func Match(pat string, s string) bool { | ||||
| 	if len(pat) == 0 { | ||||
| 		panic(fmt.Sprintf("unexpected length of pattern: %d", len(pat))) | ||||
| 	} | ||||
| 
 | ||||
| 	var inverse bool | ||||
| 
 | ||||
| 	if pat[0] == '!' { | ||||
| 		pat = pat[1:] | ||||
| 		inverse = true | ||||
| 	} | ||||
| 
 | ||||
| 	tokens := strings.SplitAfter(pat, "*") | ||||
| 
 | ||||
| 	var wildcardInHead bool | ||||
| 
 | ||||
| 	for i := 0; i < len(tokens); i++ { | ||||
| 		p := tokens[i] | ||||
| 
 | ||||
| 		if p == "" { | ||||
| 			s = "" | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		if p == "*" { | ||||
| 			if i == len(tokens)-1 { | ||||
| 				s = "" | ||||
| 				break | ||||
| 			} | ||||
| 
 | ||||
| 			wildcardInHead = true | ||||
| 
 | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		wildcardInTail := p[len(p)-1] == '*' | ||||
| 		if wildcardInTail { | ||||
| 			p = p[:len(p)-1] | ||||
| 		} | ||||
| 
 | ||||
| 		subs := strings.SplitN(s, p, 2) | ||||
| 
 | ||||
| 		if len(subs) == 0 { | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		if subs[0] != "" { | ||||
| 			if !wildcardInHead { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if subs[1] != "" { | ||||
| 			if !wildcardInTail { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		s = subs[1] | ||||
| 
 | ||||
| 		wildcardInHead = false | ||||
| 	} | ||||
| 
 | ||||
| 	r := s == "" | ||||
| 
 | ||||
| 	if inverse { | ||||
| 		r = !r | ||||
| 	} | ||||
| 
 | ||||
| 	return r | ||||
| } | ||||
|  | @ -0,0 +1,198 @@ | |||
| package actionsglob | ||||
| 
 | ||||
| import ( | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| func TestMatch(t *testing.T) { | ||||
| 	type testcase struct { | ||||
| 		Pattern, Target string | ||||
| 		Want            bool | ||||
| 	} | ||||
| 
 | ||||
| 	run := func(t *testing.T, tc testcase) { | ||||
| 		t.Helper() | ||||
| 
 | ||||
| 		got := Match(tc.Pattern, tc.Target) | ||||
| 
 | ||||
| 		if got != tc.Want { | ||||
| 			t.Errorf("%s against %s: want %v, got %v", tc.Pattern, tc.Target, tc.Want, got) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	t.Run("foo == foo", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "foo", | ||||
| 			Target:  "foo", | ||||
| 			Want:    true, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("!foo == foo", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "!foo", | ||||
| 			Target:  "foo", | ||||
| 			Want:    false, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("foo == foo1", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "foo", | ||||
| 			Target:  "foo1", | ||||
| 			Want:    false, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("!foo == foo1", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "!foo", | ||||
| 			Target:  "foo1", | ||||
| 			Want:    true, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("*foo == foo", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "*foo", | ||||
| 			Target:  "foo", | ||||
| 			Want:    true, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("!*foo == foo", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "!*foo", | ||||
| 			Target:  "foo", | ||||
| 			Want:    false, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("*foo == 1foo", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "*foo", | ||||
| 			Target:  "1foo", | ||||
| 			Want:    true, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("!*foo == 1foo", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "!*foo", | ||||
| 			Target:  "1foo", | ||||
| 			Want:    false, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("*foo == foo1", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "*foo", | ||||
| 			Target:  "foo1", | ||||
| 			Want:    false, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("!*foo == foo1", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "!*foo", | ||||
| 			Target:  "foo1", | ||||
| 			Want:    true, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("*foo* == foo1", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "*foo*", | ||||
| 			Target:  "foo1", | ||||
| 			Want:    true, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("!*foo* == foo1", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "!*foo*", | ||||
| 			Target:  "foo1", | ||||
| 			Want:    false, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("*foo == foobar", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "*foo", | ||||
| 			Target:  "foobar", | ||||
| 			Want:    false, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("!*foo == foobar", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "!*foo", | ||||
| 			Target:  "foobar", | ||||
| 			Want:    true, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("*foo* == foobar", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "*foo*", | ||||
| 			Target:  "foobar", | ||||
| 			Want:    true, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("!*foo* == foobar", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "!*foo*", | ||||
| 			Target:  "foobar", | ||||
| 			Want:    false, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("foo* == foo", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "foo*", | ||||
| 			Target:  "foo", | ||||
| 			Want:    true, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("!foo* == foo", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "!foo*", | ||||
| 			Target:  "foo", | ||||
| 			Want:    false, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("foo* == foobar", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "foo*", | ||||
| 			Target:  "foobar", | ||||
| 			Want:    true, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("!foo* == foobar", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "!foo*", | ||||
| 			Target:  "foobar", | ||||
| 			Want:    false, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("foo (* == foo ( 1 / 2 )", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "foo (*", | ||||
| 			Target:  "foo ( 1 / 2 )", | ||||
| 			Want:    true, | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("!foo (* == foo ( 1 / 2 )", func(t *testing.T) { | ||||
| 		run(t, testcase{ | ||||
| 			Pattern: "!foo (*", | ||||
| 			Target:  "foo ( 1 / 2 )", | ||||
| 			Want:    false, | ||||
| 		}) | ||||
| 	}) | ||||
| } | ||||
		Loading…
	
		Reference in New Issue