gitlab-burndown 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/bin/gitlab-burndown +31 -0
  3. data/lib/gitlab-burndown/config/configuration.rb +25 -0
  4. data/lib/gitlab-burndown/config/env_file_configuration.rb +15 -0
  5. data/lib/gitlab-burndown/csv_field/category.rb +14 -0
  6. data/lib/gitlab-burndown/csv_field/closed_at.rb +11 -0
  7. data/lib/gitlab-burndown/csv_field/closed_state.rb +9 -0
  8. data/lib/gitlab-burndown/csv_field/field.rb +11 -0
  9. data/lib/gitlab-burndown/csv_field/fields.rb +15 -0
  10. data/lib/gitlab-burndown/csv_field/hour_estimate.rb +9 -0
  11. data/lib/gitlab-burndown/csv_field/iid.rb +7 -0
  12. data/lib/gitlab-burndown/csv_field/in_sprint.rb +10 -0
  13. data/lib/gitlab-burndown/csv_field/milestone.rb +9 -0
  14. data/lib/gitlab-burndown/csv_field/title.rb +7 -0
  15. data/lib/gitlab-burndown/csv_field/web_url.rb +7 -0
  16. data/lib/gitlab-burndown/error/illegal_access.rb +2 -0
  17. data/lib/gitlab-burndown/error/no_such_variable.rb +2 -0
  18. data/lib/gitlab-burndown/error/not_implemented.rb +2 -0
  19. data/lib/gitlab-burndown/fetcher/fetcher.rb +7 -0
  20. data/lib/gitlab-burndown/fetcher/group_issues.rb +16 -0
  21. data/lib/gitlab-burndown/filter/closed_after_date.rb +16 -0
  22. data/lib/gitlab-burndown/filter/filter.rb +7 -0
  23. data/lib/gitlab-burndown/filter/filter_list.rb +15 -0
  24. data/lib/gitlab-burndown/filter/no_include_labels.rb +14 -0
  25. data/lib/gitlab-burndown/generator/burndown_csv_generator.rb +16 -0
  26. data/lib/gitlab-burndown/generator/csv_generator.rb +21 -0
  27. data/lib/gitlab-burndown/generator/generator.rb +7 -0
  28. data/lib/gitlab-burndown/generator/issue_csv_generator.rb +40 -0
  29. data/lib/gitlab-burndown/gitlab/client.rb +23 -0
  30. data/lib/gitlab-burndown/net/http/client.rb +20 -0
  31. data/lib/gitlab-burndown/net/http/paginator.rb +22 -0
  32. data/lib/gitlab-burndown/net/http/response.rb +21 -0
  33. data/lib/gitlab-burndown/writer/file.rb +14 -0
  34. data/lib/gitlab-burndown/writer/string.rb +16 -0
  35. data/lib/gitlab-burndown/writer/writer.rb +7 -0
  36. metadata +77 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4e4d84f160a4747733a96bfb9bb2a6e1f0f23f60e4846106727af58c30d3e459
4
+ data.tar.gz: 70ac3b5102113d95cf768e026edc7c90ff92df64e104f55053cac78eea8a2141
5
+ SHA512:
6
+ metadata.gz: d8bc99707477f54b09a309945ed2ee4e7acd9a642c16043d55c5838bb6fe35e314eac410072e343c2b025423767e5758fe82d780f26d1a6c8bdb2acfa3b31506
7
+ data.tar.gz: '094f88b960120723f902db6fcc1b6c4e3ef656e9f1b0312818ae34cef533e0eb005ac7e9a09cc69a2d2e9b15a08b2262e8e181dbcd89be61ba2ca2c051b29c68'
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'date'
4
+
5
+ require 'gitlab-burndown/config/env_file_configuration'
6
+ require 'gitlab-burndown/fetcher/group_issues'
7
+ require 'gitlab-burndown/filter/filter_list'
8
+ require 'gitlab-burndown/filter/closed_after_date'
9
+ require 'gitlab-burndown/filter/no_include_labels'
10
+ require 'gitlab-burndown/generator/burndown_csv_generator'
11
+ require 'gitlab-burndown/generator/issue_csv_generator'
12
+ require 'gitlab-burndown/gitlab/client'
13
+ require 'gitlab-burndown/writer/file'
14
+
15
+ if ARGV.length < 1
16
+ warn 'usage: gitlab-burndown <env_file>'
17
+ exit
18
+ end
19
+
20
+ environment_variables = %w[GL_TOKEN GL_HOST GL_API_VERSION GL_GROUP_ID INCLUDE_FROM_DATE EXCLUDE_LABELS OUTPUT_FILE]
21
+ config = EnvFileConfiguration.new(ARGV[0], environment_variables)
22
+ include_from_date = DateTime.iso8601(config.get('INCLUDE_FROM_DATE'))
23
+ labels_to_exclude = config.get('EXCLUDE_LABELS').split ','
24
+
25
+ client = GitlabHTTPClient.new(config)
26
+ issue_filter = FilterList.new(ClosedAfterDateFilter.new(include_from_date),
27
+ NoIncludeLabelsFilter.new(*labels_to_exclude))
28
+ issue_fetcher = GroupIssueFetcher.new(client, issue_filter, config.get('GL_GROUP_ID'))
29
+ csv_file_writer = FileWriter.new(config.get('OUTPUT_FILE'))
30
+ burndown_csv_generator = BurndownCSVGenerator.new(issue_fetcher, IssueCSVGenerator, csv_file_writer)
31
+ burndown_csv_generator.generate
@@ -0,0 +1,25 @@
1
+ require 'gitlab-burndown/error/not_implemented'
2
+ require 'gitlab-burndown/error/no_such_variable'
3
+
4
+ class Configuration
5
+ def initialize(required_keys)
6
+ @required_keys = required_keys
7
+ @config = Hash.new
8
+ load!
9
+ ensure_required_keys_exist
10
+ end
11
+
12
+ def get(variable_name)
13
+ @config[variable_name]
14
+ end
15
+
16
+ def load!
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def ensure_required_keys_exist
21
+ @required_keys.each do |required_key|
22
+ raise NoSuchVariableError, required_key unless @config.key? required_key
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ require 'gitlab-burndown/config/configuration'
2
+
3
+ class EnvFileConfiguration < Configuration
4
+ def initialize(filename, required_keys)
5
+ @filename = filename
6
+ super(required_keys)
7
+ end
8
+
9
+ def load!
10
+ File.foreach(@filename) do |line|
11
+ key_value_pair = line.split('=', 2)
12
+ @config[key_value_pair[0]] = key_value_pair[1].strip!
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ require 'gitlab-burndown/csv_field/field'
2
+
3
+ class IssueCategoryCSVField < CSVField
4
+ def initialize(data_container)
5
+ super(data_container)
6
+ end
7
+
8
+ def format
9
+ category_label = @data_container['labels'].find { |label| label.include? 'Category::' }
10
+ return 'Uncategorized' if category_label.nil?
11
+
12
+ category_label.gsub('Category::', '')
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ require 'date'
2
+
3
+ require 'gitlab-burndown/csv_field/field'
4
+
5
+ class IssueClosedAtCSVField < CSVField
6
+ def format
7
+ return '' if @data_container['closed_at'].nil?
8
+
9
+ Date.iso8601(@data_container['closed_at']).strftime('%d-%m-%Y')
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ require 'gitlab-burndown/csv_field/field'
2
+
3
+ class IssueClosedStateCSVField < CSVField
4
+ def format
5
+ return '' if @data_container['closed_at'].nil?
6
+
7
+ 'X'
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ require 'gitlab-burndown/error/not_implemented'
2
+
3
+ class CSVField
4
+ def initialize(data_container)
5
+ @data_container = data_container
6
+ end
7
+
8
+ def format
9
+ raise NotImplementedError
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ require 'gitlab-burndown/csv_field/field'
2
+
3
+ class CSVFields < CSVField
4
+ def initialize(data_container, *csv_field_constructors)
5
+ super(data_container)
6
+ @csv_field_constructors = csv_field_constructors
7
+ end
8
+
9
+ def format
10
+ formatted_fields = @csv_field_constructors.map do |constructor|
11
+ constructor.new(@data_container).format
12
+ end
13
+ "#{formatted_fields.join(';')}\n"
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ require 'gitlab-burndown/csv_field/field'
2
+
3
+ SECONDS_IN_HOUR = 3600
4
+
5
+ class IssueHourEstimateCSVField < CSVField
6
+ def format
7
+ @data_container['time_stats']['time_estimate'] / SECONDS_IN_HOUR
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ require 'gitlab-burndown/csv_field/field'
2
+
3
+ class IssueIIDCSVField < CSVField
4
+ def format
5
+ @data_container['iid']
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require 'gitlab-burndown/csv_field/field'
2
+
3
+ class IssueInSprintCSVField < CSVField
4
+ def format
5
+ if @data_container['labels'].include? "In Sprint"
6
+ return "X"
7
+ end
8
+ return ""
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ require 'gitlab-burndown/csv_field/field'
2
+
3
+ class IssueMilestoneCSVField < CSVField
4
+ def format
5
+ return '' if @data_container['milestone'].nil?
6
+
7
+ @data_container['milestone']['title']
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ require 'gitlab-burndown/csv_field/field'
2
+
3
+ class IssueTitleCSVField < CSVField
4
+ def format
5
+ @data_container['title']
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'gitlab-burndown/csv_field/field'
2
+
3
+ class IssueWebURLCSVField < CSVField
4
+ def format
5
+ @data_container['web_url']
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ class IllegalAccessError < StandardError
2
+ end
@@ -0,0 +1,2 @@
1
+ class NoSuchVariableError < StandardError
2
+ end
@@ -0,0 +1,2 @@
1
+ class NotImplementedError
2
+ end
@@ -0,0 +1,7 @@
1
+ require 'gitlab-burndown/error/not_implemented'
2
+
3
+ class Fetcher
4
+ def fetch
5
+ raise NotImplementedError
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ require 'gitlab-burndown/fetcher/fetcher'
2
+
3
+ class GroupIssueFetcher < Fetcher
4
+ def initialize(gitlab_client, issue_filter, group_id)
5
+ super()
6
+ @gitlab_client = gitlab_client
7
+ @issue_filter = issue_filter
8
+ @group_id = group_id
9
+ end
10
+
11
+ def fetch
12
+ @gitlab_client.get_group_issues(@group_id).select do |issue|
13
+ @issue_filter.apply issue
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ require 'date'
2
+ require 'gitlab-burndown/filter/filter'
3
+
4
+ class ClosedAfterDateFilter < Filter
5
+ def initialize(last_allowed_date)
6
+ super()
7
+ @last_allowed_date = last_allowed_date
8
+ end
9
+
10
+ def apply(target)
11
+ return true if @last_allowed_date.nil?
12
+ return true if target['closed_at'].nil?
13
+
14
+ Date.iso8601(target['closed_at']) > @last_allowed_date
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ require 'gitlab-burndown/error/not_implemented'
2
+
3
+ class Filter
4
+ def apply(target)
5
+ raise NotImplementedError
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ require 'gitlab-burndown/filter/filter'
2
+
3
+ class FilterList < Filter
4
+ def initialize(*filters)
5
+ super()
6
+ @filters = filters
7
+ end
8
+
9
+ def apply(target)
10
+ @filters.each do |filter|
11
+ return false unless filter.apply target
12
+ end
13
+ true
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ require 'gitlab-burndown/filter/filter'
2
+
3
+ class NoIncludeLabelsFilter < Filter
4
+ def initialize(*labels)
5
+ super()
6
+ @labels = labels
7
+ end
8
+
9
+ def apply(target)
10
+ should_include = target['labels'].intersection(@labels).empty?
11
+ warn "Excluding '#{target['title']}' due to label filter" unless should_include
12
+ should_include
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ require 'gitlab-burndown/generator/generator'
2
+
3
+ class BurndownCSVGenerator < Generator
4
+ def initialize(fetcher, csv_generator_constructor, writer)
5
+ super()
6
+ @fetcher = fetcher
7
+ @csv_generator_constructor = csv_generator_constructor
8
+ @writer = writer
9
+ end
10
+
11
+ def generate
12
+ issues = @fetcher.fetch
13
+ csv = @csv_generator_constructor.new(issues).generate
14
+ @writer.write csv
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ require 'gitlab-burndown/error/not_implemented'
2
+ require 'gitlab-burndown/generator/generator'
3
+ require 'gitlab-burndown/writer/string'
4
+
5
+ class CSVGenerator < Generator
6
+ def initialize(header)
7
+ super()
8
+ @header = header
9
+ @string_writer = StringWriter.new
10
+ end
11
+
12
+ def generate
13
+ @string_writer.write "#{@header}\n"
14
+ write_csv_body
15
+ @string_writer.read
16
+ end
17
+
18
+ def write_csv_body
19
+ raise NotImplementedError
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ require 'gitlab-burndown/error/not_implemented'
2
+
3
+ class Generator
4
+ def generate
5
+ raise NotImplementedError
6
+ end
7
+ end
@@ -0,0 +1,40 @@
1
+ require 'gitlab-burndown/generator/csv_generator'
2
+ require 'gitlab-burndown/csv_field/category'
3
+ require 'gitlab-burndown/csv_field/closed_at'
4
+ require 'gitlab-burndown/csv_field/closed_state'
5
+ require 'gitlab-burndown/csv_field/fields'
6
+ require 'gitlab-burndown/csv_field/hour_estimate'
7
+ require 'gitlab-burndown/csv_field/iid'
8
+ require 'gitlab-burndown/csv_field/in_sprint'
9
+ require 'gitlab-burndown/csv_field/milestone'
10
+ require 'gitlab-burndown/csv_field/title'
11
+ require 'gitlab-burndown/csv_field/web_url'
12
+
13
+ class IssueCSVGenerator < CSVGenerator
14
+ def initialize(issues)
15
+ super('IID;Title;Category;Milestone;Estimated hours;In current sprint;Is completed;Completed date;URL')
16
+ @issues = issues
17
+ end
18
+
19
+ def write_csv_body
20
+ @issues.each do |issue|
21
+ csv_line_builder = create_csv_line_builder_for_issue issue
22
+ @string_writer.write csv_line_builder.format
23
+ end
24
+ end
25
+
26
+ def create_csv_line_builder_for_issue(issue)
27
+ CSVFields.new(
28
+ issue,
29
+ IssueIIDCSVField,
30
+ IssueTitleCSVField,
31
+ IssueCategoryCSVField,
32
+ IssueMilestoneCSVField,
33
+ IssueHourEstimateCSVField,
34
+ IssueInSprintCSVField,
35
+ IssueClosedStateCSVField,
36
+ IssueClosedAtCSVField,
37
+ IssueWebURLCSVField
38
+ )
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ require 'gitlab-burndown/net/http/client'
2
+ require 'gitlab-burndown/net/http/paginator'
3
+
4
+ class GitlabHTTPClient < HTTPClient
5
+ def initialize(config)
6
+ token = config.get('GL_TOKEN')
7
+ host = config.get('GL_HOST')
8
+ api_version = config.get('GL_API_VERSION')
9
+ super(token)
10
+ @gitlab_api_host = "#{host}/api/#{api_version}"
11
+ @gitlab_group_id = config.get('GL_GROUP_ID')
12
+ end
13
+
14
+ def get_group_issues(gitlab_group_id)
15
+ paginate_group_issues(gitlab_group_id, nil)
16
+ end
17
+
18
+ def paginate_group_issues(_gitlab_group_id, _pagination)
19
+ base_url = "#{@gitlab_api_host}/groups/#{@gitlab_group_id}/issues?per_page=100&page="
20
+ paginator = HTTPPaginator.new(self, base_url, 'x-total-pages')
21
+ paginator.paginate(nil)
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'gitlab-burndown/net/http/response'
4
+
5
+ class HTTPClient
6
+ def initialize(auth_token)
7
+ @auth_token = auth_token
8
+ end
9
+
10
+ def get(url)
11
+ uri = URI(url)
12
+ request = Net::HTTP::Get.new(uri)
13
+ request['PRIVATE-TOKEN'] = @auth_token
14
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http_context|
15
+ http_context.request(request)
16
+ end
17
+
18
+ HTTPResponse.new(response)
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ Pagination = Struct.new(:page, :results)
2
+
3
+ class HTTPPaginator
4
+ def initialize(client, base_url, header_total_pages)
5
+ @client = client
6
+ @base_url = base_url
7
+ @header_total_pages = header_total_pages
8
+ end
9
+
10
+ def paginate(pagination)
11
+ pagination = Pagination.new(1, []) if pagination.nil?
12
+ url = "#{@base_url}#{pagination.page}"
13
+ warn "Reading #{url}"
14
+ response = @client.get(url)
15
+ pagination.results.concat(response.json)
16
+
17
+ return pagination.results if response.header(@header_total_pages) == pagination.page.to_s
18
+
19
+ pagination.page += 1
20
+ paginate(pagination)
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ class HTTPResponse
2
+ def initialize(builtin_response)
3
+ @builtin_response = builtin_response
4
+ end
5
+
6
+ def body
7
+ @builtin_response.body
8
+ end
9
+
10
+ def text
11
+ body
12
+ end
13
+
14
+ def json
15
+ JSON.parse(body)
16
+ end
17
+
18
+ def header(header_name)
19
+ @builtin_response[header_name]
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ require 'gitlab-burndown/writer/writer'
2
+
3
+ class FileWriter < Writer
4
+ def initialize(filepath)
5
+ super()
6
+ @filepath = filepath
7
+ end
8
+
9
+ def write(content)
10
+ File.open(@filepath, 'w') do |file|
11
+ file.write content
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ require 'gitlab-burndown/writer/writer'
2
+
3
+ class StringWriter < Writer
4
+ def initialize
5
+ super()
6
+ @string_result = ''
7
+ end
8
+
9
+ def write(content)
10
+ @string_result += content
11
+ end
12
+
13
+ def read
14
+ @string_result
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ require 'gitlab-burndown/error/not_implemented'
2
+
3
+ class Writer
4
+ def write(content)
5
+ raise NotImplementedError
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gitlab-burndown
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Andreas Kruhlmann
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-02-09 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: ''
14
+ email: andreas.kruehlmann@siemens.com
15
+ executables:
16
+ - gitlab-burndown
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - bin/gitlab-burndown
21
+ - lib/gitlab-burndown/config/configuration.rb
22
+ - lib/gitlab-burndown/config/env_file_configuration.rb
23
+ - lib/gitlab-burndown/csv_field/category.rb
24
+ - lib/gitlab-burndown/csv_field/closed_at.rb
25
+ - lib/gitlab-burndown/csv_field/closed_state.rb
26
+ - lib/gitlab-burndown/csv_field/field.rb
27
+ - lib/gitlab-burndown/csv_field/fields.rb
28
+ - lib/gitlab-burndown/csv_field/hour_estimate.rb
29
+ - lib/gitlab-burndown/csv_field/iid.rb
30
+ - lib/gitlab-burndown/csv_field/in_sprint.rb
31
+ - lib/gitlab-burndown/csv_field/milestone.rb
32
+ - lib/gitlab-burndown/csv_field/title.rb
33
+ - lib/gitlab-burndown/csv_field/web_url.rb
34
+ - lib/gitlab-burndown/error/illegal_access.rb
35
+ - lib/gitlab-burndown/error/no_such_variable.rb
36
+ - lib/gitlab-burndown/error/not_implemented.rb
37
+ - lib/gitlab-burndown/fetcher/fetcher.rb
38
+ - lib/gitlab-burndown/fetcher/group_issues.rb
39
+ - lib/gitlab-burndown/filter/closed_after_date.rb
40
+ - lib/gitlab-burndown/filter/filter.rb
41
+ - lib/gitlab-burndown/filter/filter_list.rb
42
+ - lib/gitlab-burndown/filter/no_include_labels.rb
43
+ - lib/gitlab-burndown/generator/burndown_csv_generator.rb
44
+ - lib/gitlab-burndown/generator/csv_generator.rb
45
+ - lib/gitlab-burndown/generator/generator.rb
46
+ - lib/gitlab-burndown/generator/issue_csv_generator.rb
47
+ - lib/gitlab-burndown/gitlab/client.rb
48
+ - lib/gitlab-burndown/net/http/client.rb
49
+ - lib/gitlab-burndown/net/http/paginator.rb
50
+ - lib/gitlab-burndown/net/http/response.rb
51
+ - lib/gitlab-burndown/writer/file.rb
52
+ - lib/gitlab-burndown/writer/string.rb
53
+ - lib/gitlab-burndown/writer/writer.rb
54
+ homepage: https://mo-gitlab.siemens.dk/sw-group/foundation/gitlab-burndown-csv-generator
55
+ licenses:
56
+ - Nonstandard
57
+ metadata: {}
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 3.3.15
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: GitLab burndown chart generator
77
+ test_files: []