The GitLab product has an open issue to support/add Cumulative Flow Diagrams to their Value Stream Analytics, however, I do not want to wait for an indeterminate timeframe. This issue and this issue have been open for over 1-2 years and are currently backlogged.
Using the GitLab resource state events API we should be able to build a tool to access the state events necessary to build a CFD from the ~workflow labels used on our Kanban board.
No installer yet.
$ pipenv run python -m gl_analytics --help
$ pipenv run python -m gl_analytics --milestone mb_v1.3 --days 30
You must setup your GitLab Access Token and make it available to your environment in TOKEN variable. An easy way to accomplish this is to create a dotenv
file named .env
.
TOKEN={your-access-token-here}
- Create installable (pyproject.toml)
- Improve access token support
- Refactor areas marked with XXX comments
- Add column to cycletime, indicating whether the WIP label existed (if not, we use opened date)
Created an access token here https://gitlab.com/-/profile/personal_access_tokens with expiry in one year, 14 Mar 2022.
I have given this read_api
permissions, presuming that is enough to query the projects & issues. Access tokens can
be passed as URL parameter (not secure), using the PRIVATE-TOKEN header, or using OAuth2 "Authorization: Bearer "
headers.
We can retrieve pages of issues filtered by (group) milestones. Using the link
response header to traverse the page.
$ curl -i --header "PRIVATE-TOKEN: <TOKEN>" https://gitlab.com/api/v4/groups/gozynta/issues?pagination=keyset&per_page=5&milestone=mb_v1.3
HTTP/2 200
date: Thu, 18 Mar 2021 14:46:11 GMT
content-type: application/json
set-cookie: __cfduid=d0d6371e9c7139653518ee29b035bf5ba1616078771; expires=Sat, 17-Apr-21 14:46:11 GMT; path=/; domain=.gitlab.com; HttpOnly; SameSite=Lax; Secure
vary: Accept-Encoding
cache-control: max-age=0, private, must-revalidate
etag: W/"458780b10fd5d4a50de8801127b66020"
link: <https://gitlab.com/api/v4/groups/gozynta/issues?id=gozynta&milestone=mb_v1.3&non_archived=true&order_by=created_at&page=2&pagination=keyset&per_page=5&sort=desc&state=all&with_labels_details=false>; rel="next", <https://gitlab.com/api/v4/groups/gozynta/issues?id=gozynta&milestone=mb_v1.3&non_archived=true&order_by=created_at&page=1&pagination=keyset&per_page=5&sort=desc&state=all&with_labels_details=false>; rel="first", <https://gitlab.com/api/v4/groups/gozynta/issues?id=gozynta&milestone=mb_v1.3&non_archived=true&order_by=created_at&page=9&pagination=keyset&per_page=5&sort=desc&state=all&with_labels_details=false>; rel="last"
...
[{"id":80750993,"iid":264,"project_id":8279995," ...
Now with the list of issue iid values we can use project label events to build our CFD dataset.
Note this example requires knowing the project id. There is probably a way to retrieve by project/group names.
$ curl --header "PRIVATE-TOKEN: <TOKEN" https://gitlab.com/api/v4/projects/8279995/issues/191/resource_label_events
[
{
"id": 86132274,
"user": {
"id": 4941123,
"name": "Nobody",
"username": "nobody_username",
"state": "active",
"avatar_url": "https://secure.gravatar.com/avatar/aa00c8fa6a966ce402cf12c46ce9831e?s=80&d=identicon",
"web_url": "https://gitlab.com/nobody_username"
},
"created_at": "2021-02-09T16:59:37.783Z",
"resource_type": "Issue",
"resource_id": 77308044,
"label": {
"id": 18205357,
"name": "workflow::Designing",
"color": "#0033CC",
"description": "Collaborative effort to understand desired outcome and evaluate possible solutions",
"description_html": "Collaborative effort to understand desired outcome and evaluate possible solutions",
"text_color": "#FFFFFF"
},
"action": "add"
},
{
"id": 86132509,
"user": {
"id": 4941123,
"name": "Nobody",
"username": "nobody_username",
"state": "active",
"avatar_url": "https://secure.gravatar.com/avatar/aa00c8fa6a966ce402cf12c46ce9831e?s=80&d=identicon",
"web_url": "https://gitlab.com/nobody_username"
},
"created_at": "2021-02-09T17:00:49.416Z",
"resource_type": "Issue",
"resource_id": 77308044,
"label": {
"id": 18205410,
"name": "workflow::In Progress",
"color": "#69D100",
"description": "Developer is implementing code & tests",
"description_html": "Developer is implementing code & tests",
"text_color": "#FFFFFF"
},
"action": "add"
},
{
"id": 86132510,
"user": {
"id": 4941123,
"name": "Nobody",
"username": "nobody_username",
"state": "active",
"avatar_url": "https://secure.gravatar.com/avatar/aa00c8fa6a966ce402cf12c46ce9831e?s=80&d=identicon",
"web_url": "https://gitlab.com/nobody_username"
},
"created_at": "2021-02-09T17:00:49.416Z",
"resource_type": "Issue",
"resource_id": 77308044,1
"label": {
"id": 18205357,
"name": "workflow::Designing",
"color": "#0033CC",
"description": "Collaborative effort to understand desired outcome and evaluate possible solutions",
"description_html": "Collaborative effort to understand desired outcome and evaluate possible solutions",
"text_color": "#FFFFFF"
},
"action": "remove"
},
...
]
First look was at resource state events but this only includes created and closed events. These are available in the issues themselves via the opened_at and closed_at fields.
{
"id": 80750993,
"iid": 264,
"project_id": 8279995,
"title": "Upgrade machine type of cloudsql instances",
"description": "..."
"state": "opened",
"created_at": "2021-03-11T19:24:21.913Z",
"updated_at": "2021-03-16T21:14:37.454Z",
"closed_at": null,
"closed_by": null,
"labels": [
"type::Chore",
"workflow::Ready"
],
So there does not seem to be a need to parse the state events.
$ curl --header "PRIVATE-TOKEN: <TOKEN>" https://gitlab.com/api/v4/projects/8279995/issues/24/resource_state_events
[
{
"id": 25457645,
"user": {
"id": 2768090,
"name": "Nobody",
"username": "nobody_username",
"state": "active",
"avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/2768090/avatar.png",
"web_url": "https://gitlab.com/nobody_username"
},
"created_at": "2021-03-09T17:59:43.041Z",
"resource_type": "Issue",
"resource_id": 30436752,
"state": "closed"
}
]