Skip to content

Commit dbfb412

Browse files
committed
add repo and workflow info metrics
Signed-off-by: Markus Blaschke <[email protected]>
1 parent 3f06132 commit dbfb412

File tree

2 files changed

+114
-25
lines changed

2 files changed

+114
-25
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ or GitHub App auth with env vars `GITHUB_APP_ID` (id), `GITHUB_APP_INSTALLATION_
5353

5454
| Metric | Description |
5555
|------------------------------------------------|-----------------------------------------------|
56+
| `github_repository_info` | Repository info metric |
57+
| `github_workflow_info` | Workflow info metric |
5658
| `github_workflow_latest_run` | Latest workflow run with conclusion as label |
5759
| `github_workflow_latest_run_timestamp_seconds` | Latest workflow run with timestamp as value |
5860
| `github_workflow_consecutive_failed_runs` | Count of consecutive failed runs per workflow |

metrics_github_workflows.go

+112-25
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ import (
77
"github.com/google/go-github/v61/github"
88
"github.com/prometheus/client_golang/prometheus"
99
"github.com/webdevops/go-common/prometheus/collector"
10+
"github.com/webdevops/go-common/utils/to"
1011
)
1112

1213
type (
1314
MetricsCollectorGithubWorkflows struct {
1415
collector.Processor
1516

1617
prometheus struct {
18+
repository *prometheus.GaugeVec
19+
workflow *prometheus.GaugeVec
1720
workflowLatestRun *prometheus.GaugeVec
1821
workflowLatestRunTimestamp *prometheus.GaugeVec
1922
workflowConsecutiveFailures *prometheus.GaugeVec
@@ -24,6 +27,34 @@ type (
2427
func (m *MetricsCollectorGithubWorkflows) Setup(collector *collector.Collector) {
2528
m.Processor.Setup(collector)
2629

30+
m.prometheus.repository = prometheus.NewGaugeVec(
31+
prometheus.GaugeOpts{
32+
Name: "github_repository_info",
33+
Help: "GitHub repository info",
34+
},
35+
[]string{
36+
"org",
37+
"repo",
38+
"defaultBranch",
39+
},
40+
)
41+
m.Collector.RegisterMetricList("repository", m.prometheus.repository, true)
42+
43+
m.prometheus.workflow = prometheus.NewGaugeVec(
44+
prometheus.GaugeOpts{
45+
Name: "github_workflow_info",
46+
Help: "GitHub workflow info",
47+
},
48+
[]string{
49+
"org",
50+
"repo",
51+
"workflow",
52+
"state",
53+
"path",
54+
},
55+
)
56+
m.Collector.RegisterMetricList("workflow", m.prometheus.workflow, true)
57+
2758
m.prometheus.workflowLatestRun = prometheus.NewGaugeVec(
2859
prometheus.GaugeOpts{
2960
Name: "github_workflow_latest_run",
@@ -32,7 +63,7 @@ func (m *MetricsCollectorGithubWorkflows) Setup(collector *collector.Collector)
3263
[]string{
3364
"org",
3465
"repo",
35-
"workflowName",
66+
"workflow",
3667
"event",
3768
"branch",
3869
"conclusion",
@@ -48,7 +79,7 @@ func (m *MetricsCollectorGithubWorkflows) Setup(collector *collector.Collector)
4879
[]string{
4980
"org",
5081
"repo",
51-
"workflowName",
82+
"workflow",
5283
"event",
5384
"branch",
5485
"conclusion",
@@ -64,7 +95,7 @@ func (m *MetricsCollectorGithubWorkflows) Setup(collector *collector.Collector)
6495
[]string{
6596
"org",
6697
"repo",
67-
"workflowName",
98+
"workflow",
6899
"branch",
69100
},
70101
)
@@ -76,12 +107,12 @@ func (m *MetricsCollectorGithubWorkflows) Reset() {}
76107
func (m *MetricsCollectorGithubWorkflows) getRepoList(org string) ([]*github.Repository, error) {
77108
var repositories []*github.Repository
78109

79-
repoOpts := &github.RepositoryListByOrgOptions{
110+
opts := github.RepositoryListByOrgOptions{
80111
ListOptions: github.ListOptions{PerPage: 100, Page: 1},
81112
}
82113

83114
for {
84-
result, response, err := githubClient.Repositories.ListByOrg(m.Context(), org, repoOpts)
115+
result, response, err := githubClient.Repositories.ListByOrg(m.Context(), org, &opts)
85116
var ghRateLimitError *github.RateLimitError
86117
if ok := errors.As(err, &ghRateLimitError); ok {
87118
m.Logger().Debugf("ListByOrg ratelimited. Pausing until %s", ghRateLimitError.Rate.Reset.Time.String())
@@ -97,26 +128,54 @@ func (m *MetricsCollectorGithubWorkflows) getRepoList(org string) ([]*github.Rep
97128
if response.NextPage == 0 {
98129
break
99130
}
100-
repoOpts.Page = response.NextPage
131+
opts.Page = response.NextPage
101132
}
102133

103134
return repositories, nil
104135
}
105136

106-
func (m *MetricsCollectorGithubWorkflows) getRepoWorkflows(repo *github.Repository) ([]*github.WorkflowRun, error) {
137+
func (m *MetricsCollectorGithubWorkflows) getRepoWorkflows(org, repo string) ([]*github.Workflow, error) {
138+
var workflows []*github.Workflow
139+
140+
opts := github.ListOptions{PerPage: 100, Page: 1}
141+
142+
for {
143+
result, response, err := githubClient.Actions.ListWorkflows(m.Context(), org, repo, &opts)
144+
var ghRateLimitError *github.RateLimitError
145+
if ok := errors.As(err, &ghRateLimitError); ok {
146+
m.Logger().Debugf("ListWorkflows ratelimited. Pausing until %s", ghRateLimitError.Rate.Reset.Time.String())
147+
time.Sleep(time.Until(ghRateLimitError.Rate.Reset.Time))
148+
continue
149+
} else if err != nil {
150+
return workflows, err
151+
}
152+
153+
workflows = append(workflows, result.Workflows...)
154+
155+
// calc next page
156+
if response.NextPage == 0 {
157+
break
158+
}
159+
opts.Page = response.NextPage
160+
}
161+
162+
return workflows, nil
163+
}
164+
165+
func (m *MetricsCollectorGithubWorkflows) getRepoWorkflowRuns(repo *github.Repository) ([]*github.WorkflowRun, error) {
107166
var workflowRuns []*github.WorkflowRun
108167

109-
workflowRunOpts := &github.ListWorkflowRunsOptions{
168+
opts := github.ListWorkflowRunsOptions{
110169
Branch: repo.GetDefaultBranch(),
111170
ExcludePullRequests: true,
112171
ListOptions: github.ListOptions{PerPage: 100, Page: 1},
113172
Created: ">=" + time.Now().Add(-Opts.GitHub.Workflows.Timeframe).Format(time.RFC3339),
114173
}
115174

116175
for {
117-
m.Logger().Debugf(`fetching list of workflow runs for repo "%s" with page "%d"`, repo.GetName(), workflowRunOpts.Page)
176+
m.Logger().Debugf(`fetching list of workflow runs for repo "%s" with page "%d"`, repo.GetName(), opts.Page)
118177

119-
result, response, err := githubClient.Actions.ListRepositoryWorkflowRuns(m.Context(), Opts.GitHub.Organization, *repo.Name, workflowRunOpts)
178+
result, response, err := githubClient.Actions.ListRepositoryWorkflowRuns(m.Context(), Opts.GitHub.Organization, *repo.Name, &opts)
120179
var ghRateLimitError *github.RateLimitError
121180
if ok := errors.As(err, &ghRateLimitError); ok {
122181
m.Logger().Debugf("ListRepositoryWorkflowRuns ratelimited. Pausing until %s", ghRateLimitError.Rate.Reset.Time.String())
@@ -132,34 +191,62 @@ func (m *MetricsCollectorGithubWorkflows) getRepoWorkflows(repo *github.Reposito
132191
if response.NextPage == 0 {
133192
break
134193
}
135-
workflowRunOpts.Page = response.NextPage
194+
opts.Page = response.NextPage
136195
}
137196

138197
return workflowRuns, nil
139198
}
140199

141200
func (m *MetricsCollectorGithubWorkflows) Collect(callback chan<- func()) {
142-
repositories, err := m.getRepoList(Opts.GitHub.Organization)
201+
repositoryMetric := m.Collector.GetMetricList("repository")
202+
workflowMetric := m.Collector.GetMetricList("workflow")
203+
204+
org := Opts.GitHub.Organization
205+
206+
repositories, err := m.getRepoList(org)
143207
if err != nil {
144208
panic(err)
145209
}
146210

147211
for _, repo := range repositories {
148212
var workflowRuns []*github.WorkflowRun
149213

214+
repositoryMetric.AddInfo(prometheus.Labels{
215+
"org": org,
216+
"repo": repo.GetName(),
217+
"defaultBranch": to.String(repo.DefaultBranch),
218+
})
219+
150220
if repo.GetDefaultBranch() == "" {
151221
// repo doesn't have default branch
152222
continue
153223
}
154224

155-
workflowRuns, err := m.getRepoWorkflows(repo)
225+
workflows, err := m.getRepoWorkflows(org, repo.GetName())
156226
if err != nil {
157227
panic(err)
158228
}
159229

230+
for _, workflow := range workflows {
231+
workflowMetric.AddInfo(prometheus.Labels{
232+
"org": org,
233+
"repo": repo.GetName(),
234+
"workflow": workflow.GetName(),
235+
"state": workflow.GetState(),
236+
"path": workflow.GetPath(),
237+
})
238+
}
239+
160240
if len(workflowRuns) >= 1 {
161-
m.collectLatestRun(Opts.GitHub.Organization, repo, workflowRuns, callback)
162-
m.collectConsecutiveFailures(Opts.GitHub.Organization, repo, workflowRuns, callback)
241+
workflowRuns, err := m.getRepoWorkflowRuns(repo)
242+
if err != nil {
243+
panic(err)
244+
}
245+
246+
if len(workflowRuns) >= 1 {
247+
m.collectLatestRun(Opts.GitHub.Organization, repo, workflowRuns, callback)
248+
m.collectConsecutiveFailures(Opts.GitHub.Organization, repo, workflowRuns, callback)
249+
}
163250
}
164251
}
165252
}
@@ -201,12 +288,12 @@ func (m *MetricsCollectorGithubWorkflows) collectLatestRun(org string, repo *git
201288

202289
for _, workflowRun := range latestJobs {
203290
labels := prometheus.Labels{
204-
"org": org,
205-
"repo": repo.GetName(),
206-
"workflowName": workflowRun.GetName(),
207-
"event": workflowRun.GetEvent(),
208-
"branch": workflowRun.GetHeadBranch(),
209-
"conclusion": workflowRun.GetConclusion(),
291+
"org": org,
292+
"repo": repo.GetName(),
293+
"workflow": workflowRun.GetName(),
294+
"event": workflowRun.GetEvent(),
295+
"branch": workflowRun.GetHeadBranch(),
296+
"conclusion": workflowRun.GetConclusion(),
210297
}
211298

212299
runMetric.AddInfo(labels)
@@ -258,10 +345,10 @@ func (m *MetricsCollectorGithubWorkflows) collectConsecutiveFailures(org string,
258345
}
259346

260347
labels := prometheus.Labels{
261-
"org": org,
262-
"repo": repo.GetName(),
263-
"workflowName": workflowRun.GetName(),
264-
"branch": workflowRun.GetHeadBranch(),
348+
"org": org,
349+
"repo": repo.GetName(),
350+
"workflow": workflowRun.GetName(),
351+
"branch": workflowRun.GetHeadBranch(),
265352
}
266353
consecutiveFailuresMetric.Add(labels, float64(consecutiveFailMap[workflowId]))
267354
}

0 commit comments

Comments
 (0)