Skip to content

Commit ff53227

Browse files
committed
Add pull request size to time-to-merge metric
1 parent 31f1194 commit ff53227

File tree

9 files changed

+311
-58
lines changed

9 files changed

+311
-58
lines changed

LICENSE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019 Mathias Schilling
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,30 @@
44

55
[![Grafana Example](./data/github-pr-metrics.png)](./data/github-pr-metrics.png)
66

7-
This exporter converts raw GitHub pull request data to time series. The exported data points represent the number of seconds elapsed between the creation of the pull request and the merging of the pull request.
7+
## Available Metrics
88

9-
The timestamp is the creation time of the PR.
9+
| Tables | Description | Graphite Path |
10+
| ------------- | :--------------------------------------------------------------------------------------------------------------------------------------------: | ---------------------------------------------------------: |
11+
| Time to merge | The exported data points represent the number of seconds elapsed between the creation of the pull request and the merging of the pull request. | `github.{OWNER}.{REPO}.pull_requests.{SIZE}.time_to_merge` |
1012

11-
Exposed metrics name: `github.{GITHUB_USER_NAME}.{GITHUB_REPO_NAME}.pull_requests.time_to_merge`
13+
**Explanation**:
14+
15+
- `OWNER`: the owner of the repo (e.g. facebook)
16+
- `REPO`: the repository name (e.g. react)
17+
- `SIZE`: size calculated based on the total lines of code changed (additions and deletions).
18+
19+
### Pull Request Sizes
20+
21+
The pull request size calculated based on the total lines of code changed (`total additions + total deletions`).
22+
23+
| Name | Description |
24+
| ---------- | ---------------------------------------- |
25+
| `size_XS` | Denotes a PR that changes 0-9 lines. |
26+
| `size_S` | Denotes a PR that changes 10-29 lines. |
27+
| `size_m` | Denotes a PR that changes 30-99 lines. |
28+
| `size_l` | Denotes a PR that changes 100-499 lines. |
29+
| `size_xl` | Denotes a PR that changes 500-999 lines. |
30+
| `size_xxl` | Denotes a PR that changes 1000+ lines. |
1231

1332
## Usage
1433

@@ -35,14 +54,15 @@ $ npm start collect
3554
$ npm start export | nc localhost 2003
3655

3756
# The generated time series will be written to `stdout`.
38-
# github.github-user-name.repository-name.pull_requests.time_to_merge 3450 1554125772
39-
# github.github-user-name.repository-name.pull_requests.time_to_merge 935617 1553187544
57+
# github.github-user-name.repository-name.pull_requests.size_m.time_to_merge 3450 1554125772
58+
# github.github-user-name.repository-name.pull_requests.size_xxl.time_to_merge 935617 1553187544
4059
# ...
4160
```
4261

4362
You can run the complete stack using [Docker Compose](https://docs.docker.com/compose/), just set your environment variables in the `.env` file in the root project according to the example `.env.example` and run `docker-compose up`.
4463

4564
For example:
65+
4666
```sh
4767
# Note that the `./data` directory is mounted to the docker container, to keep your data persistent place your sqlite database in here
4868
$ cat > .env <<EOL

data/github-pr-metrics.png

62.2 KB
Loading

grafana/dashboards/github-pull-request.json

Lines changed: 108 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
},
3636
{
3737
"datasource": "Graphite",
38-
"description": "Pull request time to merge: Days elapsed between the creation and merge of a pull request.",
38+
"description": "",
3939
"gridPos": {
4040
"h": 8,
4141
"w": 18,
@@ -91,7 +91,7 @@
9191
"targets": [
9292
{
9393
"refId": "A",
94-
"target": "averageSeries(github.*.*.pull_requests.time_to_merge)"
94+
"target": "averageSeries(github.*.*.pull_requests.*.time_to_merge)"
9595
}
9696
],
9797
"timeFrom": "now/M",
@@ -102,11 +102,11 @@
102102
{
103103
"aliasColors": {},
104104
"bars": false,
105+
"cacheTimeout": "",
105106
"dashLength": 10,
106107
"dashes": false,
107108
"datasource": "Graphite",
108109
"decimals": 2,
109-
"description": "Pull request time to merge: Days elapsed between the creation and merge of a pull request.",
110110
"fill": 1,
111111
"gridPos": {
112112
"h": 8,
@@ -115,6 +115,94 @@
115115
"y": 11
116116
},
117117
"hideTimeOverride": true,
118+
"id": 18,
119+
"legend": {
120+
"alignAsTable": true,
121+
"avg": true,
122+
"current": false,
123+
"max": false,
124+
"min": false,
125+
"rightSide": true,
126+
"show": true,
127+
"total": false,
128+
"values": true
129+
},
130+
"lines": true,
131+
"linewidth": 1,
132+
"links": [],
133+
"nullPointMode": "null",
134+
"percentage": false,
135+
"pointradius": 2,
136+
"points": false,
137+
"renderer": "flot",
138+
"seriesOverrides": [],
139+
"spaceLength": 10,
140+
"stack": false,
141+
"steppedLine": false,
142+
"targets": [
143+
{
144+
"refId": "A",
145+
"target": "aliasByMetric(smartSummarize(groupByNode(github.*.*.pull_requests.*.time_to_merge, 2, 'sum'), '1w', 'avg'))",
146+
"textEditor": false
147+
}
148+
],
149+
"thresholds": [],
150+
"timeFrom": "3M",
151+
"timeRegions": [],
152+
"timeShift": null,
153+
"title": "Trend over the last 3 month by repository",
154+
"tooltip": {
155+
"shared": true,
156+
"sort": 0,
157+
"value_type": "individual"
158+
},
159+
"type": "graph",
160+
"xaxis": {
161+
"buckets": null,
162+
"mode": "time",
163+
"name": null,
164+
"show": true,
165+
"values": []
166+
},
167+
"yaxes": [
168+
{
169+
"format": "dtdurations",
170+
"label": null,
171+
"logBase": 1,
172+
"max": null,
173+
"min": null,
174+
"show": true
175+
},
176+
{
177+
"format": "short",
178+
"label": null,
179+
"logBase": 1,
180+
"max": null,
181+
"min": null,
182+
"show": false
183+
}
184+
],
185+
"yaxis": {
186+
"align": false,
187+
"alignLevel": null
188+
}
189+
},
190+
{
191+
"aliasColors": {},
192+
"bars": false,
193+
"dashLength": 10,
194+
"dashes": false,
195+
"datasource": "Graphite",
196+
"decimals": 2,
197+
"description": "### Pull Request Sizes\n\nThe pull request size calculated based on the total lines of code changed (`total additions + total deletions`).\n\n| Name | Description |\n| ---------- | ---------------------------------------- |\n| `size_XS` | Denotes a PR that changes 0-9 lines. |\n| `size_S` | Denotes a PR that changes 10-29 lines. |\n| `size_m` | Denotes a PR that changes 30-99 lines. |\n| `size_l` | Denotes a PR that changes 100-499 lines. |\n| `size_xl` | Denotes a PR that changes 500-999 lines. |\n| `size_xxl` | Denotes a PR that changes 1000+ lines. |",
198+
"fill": 1,
199+
"gridPos": {
200+
"h": 8,
201+
"w": 18,
202+
"x": 0,
203+
"y": 19
204+
},
205+
"hideTimeOverride": true,
118206
"id": 16,
119207
"legend": {
120208
"alignAsTable": true,
@@ -126,6 +214,8 @@
126214
"min": false,
127215
"rightSide": true,
128216
"show": true,
217+
"sort": "avg",
218+
"sortDesc": true,
129219
"total": false,
130220
"values": true
131221
},
@@ -144,14 +234,14 @@
144234
"targets": [
145235
{
146236
"refId": "A",
147-
"target": "aliasByNode(summarize(github.*.*.pull_requests.time_to_merge, '1w', 'avg'), 2)"
237+
"target": "groupByNode(summarize(github.*.*.pull_requests.*.time_to_merge, '1w', 'avg'), 4, 'sum')"
148238
}
149239
],
150240
"thresholds": [],
151-
"timeFrom": "now/M",
241+
"timeFrom": "3M",
152242
"timeRegions": [],
153-
"timeShift": "1M/M",
154-
"title": "Trend over the last 4 weeks",
243+
"timeShift": null,
244+
"title": "Trend over the last 3 month by PR size",
155245
"tooltip": {
156246
"shared": true,
157247
"sort": 0,
@@ -180,7 +270,7 @@
180270
"logBase": 1,
181271
"max": null,
182272
"min": null,
183-
"show": true
273+
"show": false
184274
}
185275
],
186276
"yaxis": {
@@ -195,7 +285,7 @@
195285
"h": 8,
196286
"w": 6,
197287
"x": 0,
198-
"y": 19
288+
"y": 27
199289
},
200290
"hideTimeOverride": true,
201291
"id": 8,
@@ -204,7 +294,7 @@
204294
"maxValue": "172800",
205295
"minValue": 0,
206296
"orientation": "auto",
207-
"showThresholdLabels": true,
297+
"showThresholdLabels": false,
208298
"showThresholdMarkers": true,
209299
"thresholds": [
210300
{
@@ -236,7 +326,7 @@
236326
"targets": [
237327
{
238328
"refId": "A",
239-
"target": "averageSeries(github.*.*.pull_requests.time_to_merge)"
329+
"target": "averageSeries(github.*.*.pull_requests.*.time_to_merge)"
240330
}
241331
],
242332
"timeFrom": "now/M",
@@ -251,7 +341,7 @@
251341
"h": 8,
252342
"w": 6,
253343
"x": 6,
254-
"y": 19
344+
"y": 27
255345
},
256346
"hideTimeOverride": true,
257347
"id": 6,
@@ -292,7 +382,7 @@
292382
"targets": [
293383
{
294384
"refId": "A",
295-
"target": "averageSeries(github.*.*.pull_requests.time_to_merge)"
385+
"target": "averageSeries(github.*.*.pull_requests.*.time_to_merge)"
296386
}
297387
],
298388
"timeFrom": "now/M",
@@ -307,7 +397,7 @@
307397
"h": 8,
308398
"w": 6,
309399
"x": 12,
310-
"y": 19
400+
"y": 27
311401
},
312402
"hideTimeOverride": true,
313403
"id": 12,
@@ -316,7 +406,7 @@
316406
"maxValue": "172800",
317407
"minValue": 0,
318408
"orientation": "auto",
319-
"showThresholdLabels": true,
409+
"showThresholdLabels": false,
320410
"showThresholdMarkers": true,
321411
"thresholds": [
322412
{
@@ -348,7 +438,7 @@
348438
"targets": [
349439
{
350440
"refId": "A",
351-
"target": "averageSeries(github.*.*.pull_requests.time_to_merge)"
441+
"target": "averageSeries(github.*.*.pull_requests.*.time_to_merge)"
352442
}
353443
],
354444
"timeFrom": "now/M",
@@ -365,8 +455,8 @@
365455
"list": []
366456
},
367457
"time": {
368-
"from": "now-90d",
369-
"to": "now"
458+
"from": "now/d",
459+
"to": "now/d"
370460
},
371461
"timepicker": {
372462
"refresh_intervals": [

package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,21 @@
1515
},
1616
"scripts": {
1717
"env": "node src/print_env.js",
18-
"lint": "prettier --single-quote --trailing-comma=es5 --debug-check es5 src/**/*.js src/*.js",
19-
"lint:fix": "prettier --single-quote --trailing-comma=es5 es5 --write src/**/*.js src/*.js",
18+
"lint": "prettier --single-quote --trailing-comma=es5 --debug-check es5 src/**/*.js src/*.js README.md",
19+
"lint:fix": "prettier --single-quote --trailing-comma=es5 es5 --write src/**/*.js src/*.js README.md",
2020
"start": "node src/cli.js",
2121
"test": "jest"
2222
},
2323
"repository": {
2424
"type": "git",
2525
"url": "git+https://github.com/matchilling/github-metrics-graphite-exporter.git"
2626
},
27-
"author": "",
28-
"license": "ISC",
27+
"author": {
28+
"name": "Mathias Schilling",
29+
"email": "[email protected]",
30+
"url": "https://www.matchilling.com"
31+
},
32+
"license": "MIT",
2933
"bugs": {
3034
"url": "https://github.com/matchilling/github-metrics-graphite-exporter/issues"
3135
},

src/collector/index.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const githubRequestConfig = {
2-
MAX_ITEMS: 100,
2+
MAX_ITEMS: 50,
33
};
44

55
const collector = async (
@@ -29,14 +29,24 @@ const composeQuery = after => {
2929
edges {
3030
node { createdAt, closedAt, updatedAt, mergedAt, id, title, url, comments {
3131
totalCount
32-
} },
32+
}, additions, changedFiles, deletions },
3333
cursor
3434
}
3535
}
3636
}
3737
}`;
3838
};
3939

40+
const queryGitHub = async (githubClient, query, queryParams) => {
41+
try {
42+
return await githubClient.query(query, queryParams);
43+
} catch (error) {
44+
process.stderr.write('Error while querying GitHub:\n');
45+
process.stderr.write(JSON.stringify(error, 0, 2) + '\n');
46+
process.exit(1);
47+
}
48+
};
49+
4050
const update = async (
4151
database,
4252
githubClient,
@@ -66,7 +76,7 @@ const update = async (
6676
query = composeQuery();
6777
}
6878

69-
const response = await githubClient.query(query, queryParams);
79+
const response = await queryGitHub(githubClient, query, queryParams);
7080
const edges = response.repository.pullRequests.edges;
7181

7282
acc.numberOfPullRequestProcessed += edges.length;

0 commit comments

Comments
 (0)