Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
94171c0
feat(filtering): Open + Download Job's Trace logs
azachar Dec 18, 2017
b048c3d
doc(filtering): how to install additional packages
azachar Dec 18, 2017
cc6ae89
fix(filtering): npe in job selector
azachar Dec 18, 2017
7c86b80
feat(filtering): reload
azachar Dec 18, 2017
9797367
fix(update): update command
azachar Jan 12, 2018
d605b4c
style(job): nicer job selector
azachar Jan 12, 2018
fdae0ff
feat(pipelines): pipeline selector
azachar Jan 12, 2018
1f03e93
fix(view): double initialization
azachar Jan 12, 2018
33ccff9
fix(pipeline): clean up
azachar Jan 12, 2018
1ca7491
feat(job): show always failed jobs, then success and last unstable
azachar Jan 12, 2018
a6d3c2c
fix(pipeline): clean up
azachar Jan 12, 2018
2ab1390
feat(progress): color based on success time
azachar Jan 15, 2018
50a4c3a
feat(job): selector filtering
azachar Jan 15, 2018
a2daed5
feat(job): commit message header
azachar Jan 16, 2018
a487d1a
feat(pipeline): sort actions
azachar Jan 16, 2018
1f58ab0
style(selectors): commit title instead of message
azachar Jan 16, 2018
b07cb91
fix(pipeline): search
azachar Jan 17, 2018
5a1fe7f
fix(pipeline): sorting
azachar Jan 17, 2018
20bcd6a
feat(job): selector jobs sorting
azachar Jan 17, 2018
8a2eecb
style(pipeline): sha info
azachar Jan 17, 2018
ceca978
feat(pipeline): abs elapsed time
azachar Jan 18, 2018
0989027
fix(pipeline): skipped status
azachar Jan 18, 2018
abc769b
refactor(avatar): moved from job to pipeline
azachar Jan 18, 2018
f180333
feat(pipeline): relative time
azachar Jan 18, 2018
f36195a
fix(pipeline): always fails duplicated
azachar Jan 18, 2018
4f9826f
fix(pipeline): undefined if no jobs
azachar Jan 19, 2018
e325c67
feat(job): sort by duration
azachar Jan 21, 2018
af0db49
fix(progress): initial progress bar is calculate for jobs and pipeline
azachar Jan 21, 2018
3806b3d
feat(pipeline): sort by duration
azachar Jan 21, 2018
d84dbb1
feat(all): pipeline selector for all pipelines regardless branch
azachar Jan 21, 2018
4eca93c
fix(search): search keywords are based on template id: status: name: …
azachar Jan 24, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ Track gitlab pipelines state of your project.
![Screenshot](https://user-images.githubusercontent.com/1149069/28337289-7973ebcc-6c05-11e7-844d-c7a1e106317c.png)

# Configuration
- This fork can be installed from
```
apm install https://github.com/azachar/gitlab-integration
```
Please install additional forks to benefit from some extra functionality.

- Once installed, fill your Gitlab API token in the package's settings page
- If you don't know what they are, please refer to https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html,
Expand All @@ -21,6 +26,16 @@ Track gitlab pipelines state of your project.
- `gitlab-integration` should display pipeline statuses in the status bar if it can correctly determine and reach the gitlab server where your project is hosted like shown above.
- *In case any errors occurs, a message should be logged in Atom developer console.*

## Gitlab Build Log Filtering Support
- In order to benefit from extra log filtering to narrow down your error from your failed GitLab job's trace log, you need to install 2 additional packages to your atom. Currently, these packages are in experimental mode and still under the development (basically I forked them to gain quick functionality)
- Please install forks `language-ansi-styles` from `azachar` repo the same applies to `language-log` package.
```
apm install https://github.com/azachar/language-log
apm install https://github.com/azachar/language-ansi-styles
```

- In order to open your job's reports directly in your browser instead of downloading them, please set up your GitLab installation or modify your browser by following https://gitlab.com/gitlab-org/gitlab-ce/issues/10982#note_50291868

# Contributing
Reporting issues and pull requests are more than welcome on this project.

Expand Down
3 changes: 3 additions & 0 deletions icons/download.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions keymaps/gitlab-integration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"atom-workspace": {
"shift-ctrl-alt-g": "gitlab-integration:open-gitlab-ci-cd",
"ctrl-alt-cmd-g": "gitlab-integration:reload",
"ctrl-alt-g": "gitlab-integration:open-failed-job-selector"
}
}
172 changes: 172 additions & 0 deletions lib/all-pipeline-selector-view.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
{$$, SelectListView} = require 'atom-space-pen-views'
percentile = require 'percentile'
moment = require 'moment'

class AllPipelineSelectorView extends SelectListView
initialize: (pipelines, controller, projectPath) ->
super

@pipelines = pipelines
@controller = controller
@projectPath = projectPath

@addClass('overlay from-top')
@globalCalculate pipelines
@calculate pipelines
@setItems pipelines
@panel ?= atom.workspace.addModalPanel(item: this)
@focusFilterEditor()
$$(@extraContent(@)).insertBefore(@error)
@handleEvents()
@panel.show()

getFilterKey: -> 'search'

extraContent: (thiz) ->
return ->
@div class: 'block', =>
@button outlet: 'allButton', class: 'btn btn-info', ' All', =>
@span class: 'badge badge-small', thiz.jobs?.length
@div class: 'btn-group', =>
@button outlet: 'successButton', class: 'btn btn-success', ' Success', =>
@span class: 'badge badge-small', thiz.success?.length
@button outlet: 'unstableButton', class: 'btn btn-warning', ' Unstable', =>
@span class: 'badge badge-small', thiz.unstable?.length
@button outlet: 'failedButton', class: 'btn btn-error', ' Failed', =>
@span class: 'badge badge-small', thiz.failed?.length

@div class: 'block', =>
@div class: 'btn-group', =>
@button outlet: 'sortById', class: 'btn', ' Sort by id'
@button outlet: 'sortBySha', class: 'btn', ' Sort by sha'
@button outlet: 'sortByDate', class: 'btn', ' Sort by date'
@button outlet: 'sortByDuration', class: 'btn', ' Sort by duration'
@button outlet: 'sortByBranch', class: 'btn', ' Sort by branch'

handleEvents: ->
@wireOutlets(@)

@successButton.on 'mouseover', (e) =>
@setItems @success
@calculate @items

@unstableButton.on 'mouseover', (e) =>
@setItems @unstable
@calculate @items

@failedButton.on 'mouseover', (e) =>
@setItems @failed
@calculate @items

@sortById.on 'mouseover', (e) =>
@setItems @items.sort (a, b) ->
return a.id - b.id

@sortBySha.on 'mouseover', (e) =>
@setItems @items.sort (a, b) ->
return -1 if a.sha < b.sha
return 1 if a.sha > b.sha
return 0

@sortByDate.on 'mouseover', (e) =>
@setItems @items.sort (a, b) ->
if a.created_at and b.created_at
return moment(b.created_at).diff(moment(a.created_at))
else
return 0

@sortByDuration.on 'mouseover', (e) =>
@setItems @items.sort (a, b) ->
return b.duration - a.duration

@sortByBranch.on 'mouseover', (e) =>
@setItems @items.sort (a, b) ->
return a.ref - b.ref

calculate: (items) ->
if items?.length > 0
@maxDuration = items.reduce( ((max, p) ->
Math.max(max, p.duration || 0)
), 0 )

@averageDuration = percentile(50, items, (item) -> item.duration).duration

success = items.filter( (p) -> p.status is 'success')
if success?.length > 0
@maxDurationSuccess = success?.reduce( ((max, p) ->
Math.max(max, p.durationSuccess || 0)
), 0 )
@averageDurationSuccess = percentile(50, success, (item) -> item.durationSuccess).durationSuccess

globalCalculate: (items) ->
if items?.length > 0
@success = items.filter( (p) -> p.status is 'success')
@failed = items.filter( (p) -> p.status is 'failed')
@unstable = items.filter( (p) -> p.status is 'success' and p.loadedJobs?.filter( (j) => j.status is 'failed')?.length > 0)

asUniqueNames: (jobs) =>
return jobs.map((j) => j.name).unique()

viewForItem: (pipeline) ->
pipeline.elapsed = moment(pipeline.finished_at).diff(moment(pipeline.created_at), 'seconds')

if pipeline.loadedJobs?.length > 0
{alwaysSuccess, unstable, alwaysFailed, total} = @controller.statistics(pipeline.loadedJobs)

type = @controller.toType(pipeline, @averageDuration)

"<li class='two-lines'>
<div class='status icon icon-git-commit'></div>
<div class='primary-line icon gitlab-#{pipeline.status}'>
#{pipeline.id}
<span class='text-muted icon icon-clock'> #{moment(pipeline.created_at).format('lll')} / #{moment(pipeline.created_at).fromNow()}</span>
<span class='pull-right'>
<span class='text-subtle'>#{pipeline.commit?.short_id}</span>
<span class='text-info'>#{pipeline.ref}</span>
</span>
</div>
<div class='secondary-line no-icon'>
<div class='block'>
<span class='text-muted'>#{pipeline.commit?.title}</span>
</div>
<div class='block'>
<progress class='inline-block progress-#{type}' max='#{@maxDuration}' value='#{pipeline.duration}'></progress>
<i class='text-muted'> ♨︎ #{@controller.toHHMMSS(pipeline.duration)}</i>
<span class='text-warning'> / ABS #{@controller.toHHMMSS(pipeline.elapsed)}</span>
</div>
<span class='text-success'>#{@asUniqueNames(alwaysSuccess)}</span>
<div class='block'>
<span class='text-warning'>#{@asUniqueNames(unstable)}</span>
</div>
<div class='block'>
<span class='text-error'>#{@asUniqueNames(alwaysFailed)}</span>
</div>
<span class='badge badge-info'>#{total.length}</span>
<span class='badge badge-success'>#{alwaysSuccess.length}</span>
<span class='badge badge-warning'>#{unstable.length}</span>
<span class='badge badge-error'>#{alwaysFailed.length}</span>
<span class='pull-right'>
<img src='#{pipeline.user?.avatar_url}' class='gitlab-avatar' /> #{pipeline.user?.name}
</span>
</li>"
else
"<li class='two-lines'>
<div class='status status-added icon icon-git-commit'></div>
<div class='primary-line icon gitlab-#{pipeline.status}'>
#{pipeline.id}
<span class='pull-right'>#{pipeline.sha}</span>
<span class='text-muted'>#{pipeline.commit?.message}</span>
</div>
<div class='secondary-line no-icon'>
<span class='loading loading-spinner-tiny inline-block'></span>
</div>
</li>"

confirmed: (pipeline) =>
@cancel()
@controller.updatePipeline(pipeline, @projectPath);

cancelled: ->
@panel.hide()

module.exports = AllPipelineSelectorView
31 changes: 31 additions & 0 deletions lib/gitlab-integration.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ GitUrlParse = require 'git-url-parse'
StatusBarView = require './status-bar-view'
GitlabStatus = require './gitlab'
log = require './log'
app = require('electron').app;
moment = require 'moment'

class GitlabIntegration
config:
Expand All @@ -12,6 +14,11 @@ class GitlabIntegration
description: 'Token to access your Gitlab API'
type: 'string'
default: ''
artifactReportPath:
title: 'Artifact report path'
description: 'Usefull to open your report such as protractor-screenshoter'
type: 'string'
default: 'storage/<JOB_NAME>/index.html'
period:
title: 'Polling period (ms)'
description: 'The interval at which gitlab will be polled'
Expand Down Expand Up @@ -118,6 +125,8 @@ class GitlabIntegration
)

activate: (state) ->
if app
moment.locale(app.getLocale())
@subscriptions = new CompositeDisposable
@statusBarView = new StatusBarView
@statusBarView.init()
Expand All @@ -128,6 +137,28 @@ class GitlabIntegration
"You likely forgot to configure your gitlab token",
{dismissable: true}
)
if not atom.config.get('gitlab-integration.artifactReportPath')
atom.notifications.addInfo(
"You likely forgot to configure your gitlab artifact report path",
{dismissable: true}
)
atom.commands.add 'atom-workspace',
'gitlab-integration:open-gitlab-ci-cd': () =>
if @statusBarView?.currentProject isnt "<unknown>"
@gitlab.openGitlabCICD(@statusBarView.currentProject)

atom.commands.add 'atom-workspace',
'gitlab-integration:open-failed-job-selector': () =>
if @statusBarView?.currentProject isnt "<unknown>"
currentStages = @statusBarView?.stages[@statusBarView.currentProject]
failedStages = currentStages?.filter (stage) -> stage.status is 'failed'
if @statusBarView.currentProject and failedStages?.length > 0
@gitlab.openJobSelector(@statusBarView.currentProject, failedStages[0])

atom.commands.add 'atom-workspace',
'gitlab-integration:reload': () =>
@gitlab.update()

@handleProjects(atom.project.getDirectories())
@subscriptions.add atom.project.onDidChangePaths (paths) =>
@handleProjects(paths.map((path) => new Directory(path)))
Expand Down
Loading