š Gitlab pipelines monitor – in Elm
Posted October 2020
ā¹ļø This is adapted from a talk I gave last year at Elm London Finally got round to bloggifying⦠You can also just check out the project on Gitlab.
Background
What?
- Itās a CI visualisation for Gitlab, now hosted at https://gitlab.com/declension
- Uses Elm 0.19, ParcelJS, Gitlab v4 API
- Designed for always-on bigscreens in the office (EDIT: remember them?)
Pretty alpha right nowEDIT: seems to be stable, some missing thingsā¦
Why?
Good reasons! Elm!
- š Exciting FP use-cases
- š¤ Also: learning experience on complex REST APIs, mutable data, and asynchronicity
- šØ A focus on smooth animations and dark UIs
- ā Need to get default and other merge request branches, too
Existing Apps
Letās face it ā this is not new territory #### gitlab-monitor
gitlab-ci-monitor
Issues
- Not registered as apps
- āNot made hereā, letās be honest.
- š± Dangerous lack of emoji on existing products
- ā This has to stop!
Challenges
The Gitlab API
- REST API with oAuth2-style authentication.
/projects
and/groups
are useful root resources- So we can list project pipelines:
HTTP GET /projects/:id/pipelines
- Butā¦
Everythingās Changing
- Projectsā metadata gets updated
- Ordering is (currently) on last update, so order changesā¦
- New branches appearā¦
- New pipelines appear in various branchesā¦
- Previously ā become āļø or āļøā¦
Data fan-out
- Each group has many projects
- ā¦each project has many pipelines
- ā¦each pipeline can have many stages
- ā¦each stage has many jobsā¦
- ā¦each job has many stepsā¦
- š±
A word about GraphQL
- GraphQL is actually a great for āunder-fetchingāā¦
- ā¦but progress was stalled indefinitely at Gitlab.
- š No, no, it isnāt any more
- But letās stick with the REST version for nowā¦
Back to authentication
- Aiming to avoid āpaste your token hereā
- Gitlab is an oAuth 2 provider
- Supports three common flowsā¦
oAuth 2 Flows
- š Web application ā server side
- š Implicit grant ā SPAs etc
- š Resource owner password credentials
ā secured host
Getting this to work
Elm setup
Simple Elm project
- š Yarn to build, Parcel.JS to do all the magic
- š SASS for styling
Current Data Model
type alias Model =
config : Flags
{ , key : Nav.Key
, token : Maybe Token
, data : GitlabData
, url : Url.Url
}
Data structure itself
type alias ProjectId =
Int
type alias GitRef =
String
type alias GitlabData =
projects : List Project
{ , pipelines : Dict ProjectId (List Pipeline)
}
Records
type alias Pipeline =
ref : String
{ , id : Int
, status : Status
, url : String
}
type alias Project =
id : Int
{ , namespace : String
, name : String
, description : Maybe String
, url : String
, lastActivity : Posix
}
type alias PipelineStore =
Dict ProjectId (Dict GitRef (List Pipeline))
Implicit Grant Flow
- One needs to register an app first:
Implicit Grant URL
- Then redirect user to a URL like
https://gitlab.example.com/oauth/authorize ?client_id=APP_ID &redirect_uri=REDIRECT_URI &state=UNIQUE_STATE_HASH &scope=REQUESTED_SCOPES
- Theyāll come back at
REDIRECT_URI
, with a token in the URL
HTTP with auth
getUrl : Token -> Url -> (Result Http.Error a -> Msg) -> Decoder a -> Cmd Msg
getUrl token url msg decoder =
Http.request
method = "GET"
{ , url = Url.toString url
, body = emptyBody
, expect = expectJson msg decoder
, headers = [ header "Authorization" ("Bearer " ++ token) ]
, timeout = Just (httpTimeout * 1000.0)
, tracker = Nothing
}
Parse that token!
This⦠worksā¦
extractToken : Url -> Maybe Token extractToken url = url.fragment |> Maybe.map (String.append "http://DUMMY?") |> Maybe.andThen Url.fromString |> Maybe.andThen (Url.Parser.parse (query toToken)) |> Maybe.withDefault Nothing
Simple Subscriptions
subscriptions : Model -> Sub Msg
subscriptions _ =
Time.every (10 * 1000) Tick
- Weāll use chained messages though, so this isnāt everythingā¦
Less Simple Messages
type Msg
= Tick Posix
| LinkClicked Browser.UrlRequest
| GotPipelinesFor ProjectId (Result Http.Error (List Pipeline))
| GotProjects (Result Http.Error (List Project))
| UrlChanged Url.Url
Lessons learned
Nested mutable data is hard
- Incomplete data makes for some challenges of its own in FP
- Caching is key (arguably the whole data structure is a cache)
- This is a case study for GraphQL in many ways
Parcel.JS
- ā¦is insanely fast š
- Elm integration ājust worksā
- Supports
.env
, SASS out of the box - I / we wonāt be using Webpack much now š (EDIT: I havenāt and thatās great)
Obvious notes to self
- Donāt leave slides to the day
beforeitself - Especially if you also custom-build the slide frameworkā¦
- Especially if thatās in Haskell š
But wait, thereās more
- Mutable nested data can be⦠hard
- Lenses arenāt really a thing in Elm:
- Which is probably good, I guess..
- (though
elm-monocle
looks interesting)