fastlane-plugin-rooster 0.2.0 → 1.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0884cfc7aded76d4b2f582b9a34f3f73d991b5f6499589c5bbfcb91e839ecd71'
4
- data.tar.gz: 766976696518e674f2a3e2c4e29249c8184bde75b39a2f3dfc569d51b81f3f51
3
+ metadata.gz: e2d7f6d82544dafd29f632bd51a77c3a6e6748f40e6b18329519f543f1062d88
4
+ data.tar.gz: 2c2fa54aa858fa9154b855dff45e2e0e29ed1b7073223fc0b4043ad780becc3c
5
5
  SHA512:
6
- metadata.gz: 907d02d8ccb22b27fa10f0a95b321e711b5703035ce4a1da3337c8c7fcfd6e1fff5307f900118dcd40b8f1787d49705dbcbf756f357545f40b59cb54c982a89c
7
- data.tar.gz: a2cf1b2186e50b8a3db7736f9a354d9b113062fb531659528b5c4d12761bdd0310d9780fa295bc65fd08ff889e478b910f57796b9410aa2e98067fc557a333ff
6
+ metadata.gz: db73fc6256c2573f8b335d9d2bdc609789091e5492f15e9836efcb3fdd417988f5596598005303a7f5c790e90702b6292e376874ba5fb64e5a80611383e17e6f
7
+ data.tar.gz: 9080433ec47b30345b68b34bf3a5c26f9ef5bf39ca38dacf4022c6b692ffdf249959fc4075f1ee38c9d6824e38e6508f25dd15dbff933902d6cfa720a1bb443b
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # rooster plugin
2
2
 
3
3
  [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-rooster)
4
+ [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/tbetmen/fastlane-plugin-rooster/blob/main/LICENSE)
5
+ [![Gem Version](https://badge.fury.io/rb/fastlane-plugin-rooster.svg)](https://rubygems.org/gems/fastlane-plugin-rooster)
4
6
 
5
7
  ## Getting Started
6
8
 
@@ -10,17 +12,82 @@ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To
10
12
  fastlane add_plugin rooster
11
13
  ```
12
14
 
13
- ## About rooster
15
+ ## About Rooster
14
16
 
15
- Send gitlab merge requests reminder to slack
16
-
17
- **Note to author:** Add a more detailed description about this plugin here. If your plugin contains multiple actions, make sure to mention them here.
17
+ Rooster is fastlane action to send Gitlab merge request to Slack using webhook, for now only has one action `rooster_merge_request`.
18
18
 
19
19
  ## Example
20
20
 
21
- Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
21
+ Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use `rooster_merge_request` action. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test_env`.
22
+
23
+ There are also some example files that used in `rooster_merge_request` action:
24
+ 1. Sample uses `.env` in [example `.env.example`](example/.env.example) for setting all action parameters or just private variable like Gitlab Api Token.
25
+ 2. [example `slack_message_format.json`](example/slack_message_format.json) used for slack message format to replace default value given in action.
26
+ 3. [example `slack_users.csv`](example/slack_users.csv) slack users and gitlab users mapping format.
27
+
28
+ Slack message preview has opened merge request
29
+
30
+ ![has merge request](./screenshots/sample%20merge%20request.png)
31
+
32
+ Slack message preview merge request empty
33
+
34
+ ![has merge request](./screenshots/sample%20merge%20request%20empty.png)
35
+
36
+ ## Parameters
37
+
38
+ The action parameters `gitlab_token` and others can also be omitted when their values are [set as environment variables](https://docs.fastlane.tools/advanced/#environment-variables).
22
39
 
23
- **Note to author:** Please set up a sample project to make it easy for users to explore what your plugin does. Provide everything that is necessary to try out the plugin in this project (including a sample Xcode/Android project if necessary)
40
+ Here is the list of all existing parameters for `rooster_merge_request` action:
41
+
42
+ | Key | Env Var | Default | Optional | Description |
43
+ |-----|-----------------------|-------------------------------------------------------------------------------|----------|-----------------------------------------------------------------------------------------------------------|
44
+ | `gitlab_token` | `ROOSTER_GITLAB_ACCESS_TOKEN` | - | false | API Token for Gitlab [find out here](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) |
45
+ | `gitlab_project_id` | `ROOSTER_GITLAB_PROJECT_ID` | - | false | Gitlab project id, you can find in Project Overview menu located below project name. |
46
+ | `slack_webhook_url` | `ROOSTER_SLACK_WEBHOOK_URL` | - | false | Slack webhook url, check this link for more detail [Slack Webhooks](https://api.slack.com/messaging/webhooks) |
47
+ | `gitlab_milestones_path` | `ROOSTER_GITLAB_MILESTONES_PATH` | - | true | Gitlab group or project milestones with given format either `groups/:group_id` or `projects/:project_id`. You can find `group id` similar with project id in Group Overview. [Gitlab Milestones](https://docs.gitlab.com/ee/user/project/milestones/) |
48
+ | `gitlab_merge_request_total` | `ROOSTER_GITLAB_MERGE_REQUEST_TOTAL` | 10 | true | Maximum merge request when fetching data from gitlab, uses in query param `per_page` |
49
+ | `slack_users_file` | `ROOSTER_SLACK_USERS_FILE` | - | true | Comma separate file that contains mapping user of gitlab and slack using id |
50
+ | `slack_message_format_file` | `ROOSTER_SLACK_MESSAGE_FORMAT_FILE` | Check in this [file](lib/fastlane/plugin/rooster/helper/slack_file_client.rb) | true | Slack message format in json format contains `text`, `header`, `mr_item`, `footer`, and `empty_mr_text`. |
51
+ | `gitlab_merge_request_milestone` | `ROOSTER_GITLAB_MERGE_REQUEST_MILESTONE` | '' | true | Milestone will be used in merge request parameter as query param. |
52
+
53
+ ## Replace word in `slack_message_format`
54
+
55
+ In slack message format json has word replacement, check this table to see all possibility:
56
+
57
+ | Key | Availability | Description |
58
+ |-----|--------------|-------------------------------------------------|
59
+ | `MR_TOTAL` | header | Total merge request to be shown in slack |
60
+ | `MR_MILESTONE` | header, empty_mr_text | Current active milestone |
61
+ | `MR_TITLE` | mr_item | Title of merge request |
62
+ | `MR_TIME` | mr_item | Time relative to current date like `3 days ago` |
63
+ | `MR_ASSIGNEE_SINGLE` | mr_item | Merge request assignee just one user |
64
+ | `MR_ASSIGNEES` | mr_item | All assignee users in merge request |
65
+ | `MR_REVIEWERS` | mr_item | All reviewer users in merge request |
66
+ | `MR_LABELS` | mr_item | Merge request labels |
67
+ | `MR_LINK` | mr_item | Merge request link |
68
+
69
+ ### Example usage
70
+
71
+ before
72
+ ```json
73
+ {
74
+ "text": "Hello, its beautiful day!",
75
+ "header": "Hi there, Just wanted to let you know that we have MR_TOTAL merge requests that need your attention.",
76
+ "mr_item": "<MR_LINK|MR_TITLE> MR_ASSIGNEE_SINGLE",
77
+ "footer": "Thank you",
78
+ "empty_mr_text": "Congratulation there is no merge request anymore, keep the good works"
79
+ }
80
+ ```
81
+ after
82
+ ```json
83
+ {
84
+ "text": "Hello, its beautiful day!",
85
+ "header": "Hi there, Just wanted to let you know that we have 10 merge requests that need your attention.",
86
+ "mr_item": "<https://gitlab.com/nnn/merge_requests/1|feat: Add new feature mobile prepaid> @Bruce Wayne",
87
+ "footer": "Thank you",
88
+ "empty_mr_text": "Congratulation there is no merge request anymore, keep the good works"
89
+ }
90
+ ```
24
91
 
25
92
  ## Run tests for this plugin
26
93
 
@@ -37,7 +104,7 @@ rubocop -a
37
104
 
38
105
  ## Issues and Feedback
39
106
 
40
- For any other issues and feedback about this plugin, please submit it to this repository.
107
+ For any other issues and feedback about this plugin, please submit new [Github Issue](https://github.com/tbetmen/fastlane-plugin-rooster/issues).
41
108
 
42
109
  ## Troubleshooting
43
110
 
@@ -0,0 +1,160 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/gitlab_api_client'
3
+ require_relative '../helper/gitlab_merge_request_transformer'
4
+ require_relative '../helper/slack_file_client'
5
+ require_relative '../helper/slack_message_client'
6
+
7
+ module Fastlane
8
+ module Actions
9
+ class RoosterMergeRequestAction < Action
10
+ def self.run(params)
11
+ # get params or envs
12
+ users_file = params[:slack_users_file] || ENV['ROOSTER_SLACK_USERS_FILE']
13
+ gitlab_project_id = params[:gitlab_project_id] || ENV['ROOSTER_GITLAB_PROJECT_ID']
14
+ slack_webhook_url = params[:slack_webhook_url] || ENV['ROOSTER_SLACK_WEBHOOK_URL']
15
+ slack_message_format_file = params[:slack_message_format_file] || ENV['ROOSTER_SLACK_MESSAGE_FORMAT_FILE']
16
+ gitlab_mr_total = params[:gitlab_merge_request_total] || ENV['ROOSTER_GITLAB_MERGE_REQUEST_TOTAL']
17
+ gitlab_milestones_path = params[:gitlab_milestones_path] || ENV['ROOSTER_GITLAB_MILESTONES_PATH']
18
+ gitlab_token = params[:gitlab_token] || ENV['ROOSTER_GITLAB_ACCESS_TOKEN']
19
+ milestone = params[:gitlab_merge_request_milestone] || ENV['ROOSTER_GITLAB_MERGE_REQUEST_MILESTONE']
20
+
21
+ # load slack files
22
+ slack_file_client = Helper::SlackFileClient.new
23
+ slack_message_format = slack_file_client.message_format(slack_message_format_file)
24
+ slack_users = slack_file_client.users(users_file)
25
+
26
+ # get gitlab merge request
27
+ gitlab_api = Helper::GitlabApiClient.new(gitlab_token)
28
+ if milestone.empty?
29
+ current_milestone = gitlab_api.current_milestone(gitlab_milestones_path)
30
+ else
31
+ current_milestone = milestone
32
+ end
33
+ mr_resp_json = gitlab_api.merge_requests(gitlab_project_id, gitlab_mr_total, current_milestone)
34
+
35
+ # transform merge request
36
+ gitlab_mr_transformer = Helper::GitlabMergeRequestTransformer.new(slack_users, mr_resp_json)
37
+ merge_request_formatted = gitlab_mr_transformer.transform
38
+
39
+ # post merge request into slack
40
+ slack_message_client = Helper::SlackMessageClient.new(
41
+ slack_users,
42
+ slack_message_format,
43
+ merge_request_formatted,
44
+ current_milestone
45
+ )
46
+ slack_message_client.post_message(
47
+ slack_webhook_url,
48
+ slack_message_client.payload
49
+ )
50
+ end
51
+
52
+ #####################################################
53
+ # @!group Documentation
54
+ #####################################################
55
+
56
+ def self.available_options
57
+ [
58
+ FastlaneCore::ConfigItem.new(
59
+ key: :gitlab_token,
60
+ env_name: 'ROOSTER_GITLAB_ACCESS_TOKEN',
61
+ description: 'Gitlab access token',
62
+ optional: false,
63
+ verify_block: proc do |value|
64
+ UI.user_error!('No gitlab access token given') unless value && !value.empty?
65
+ end
66
+ ),
67
+ FastlaneCore::ConfigItem.new(
68
+ key: :gitlab_project_id,
69
+ env_name: 'ROOSTER_GITLAB_PROJECT_ID',
70
+ description: 'Gitlab project id',
71
+ optional: false,
72
+ verify_block: proc do |value|
73
+ UI.user_error!('No gitlab project id given') unless value && !value.empty?
74
+ end
75
+ ),
76
+ FastlaneCore::ConfigItem.new(
77
+ key: :slack_webhook_url,
78
+ env_name: 'ROOSTER_SLACK_WEBHOOK_URL',
79
+ description: 'Slack webhook url',
80
+ optional: false,
81
+ verify_block: proc do |value|
82
+ UI.user_error!('No slack webhook url given') unless value && !value.empty?
83
+ end
84
+ ),
85
+ FastlaneCore::ConfigItem.new(
86
+ key: :gitlab_milestones_path,
87
+ env_name: 'ROOSTER_GITLAB_MILESTONES_PATH',
88
+ description: 'Gitlab group or project milestones with given format either `groups/:group_id` or `projects/:project_id`',
89
+ optional: true
90
+ ),
91
+ FastlaneCore::ConfigItem.new(
92
+ key: :gitlab_merge_request_total,
93
+ env_name: 'ROOSTER_GITLAB_MERGE_REQUEST_TOTAL',
94
+ default_value: 10,
95
+ type: Integer,
96
+ description: 'Maximum merge request when fetching data from gitlab, uses in query param `per_page`',
97
+ optional: true
98
+ ),
99
+ FastlaneCore::ConfigItem.new(
100
+ key: :slack_users_file,
101
+ env_name: 'ROOSTER_SLACK_USERS_FILE',
102
+ description: 'Comma separate file that contains mapping user of gitlab and slack using id',
103
+ optional: true
104
+ ),
105
+ FastlaneCore::ConfigItem.new(
106
+ key: :slack_message_format_file,
107
+ env_name: 'ROOSTER_SLACK_MESSAGE_FORMAT_FILE',
108
+ default_value: Helper::SlackFileClient::SLACK_MESSAGE_FORMAT_DEFAULT,
109
+ description: 'Slack message format in json format contains `text`, `header`, `mr_item`, `footer`, and `empty_mr_text`'\
110
+ '| In `header` you can use use `MR_TOTAL` and `MR_MILESTONE`'\
111
+ '| In `mr_item` you can use use `MR_TITLE`, `MR_TIME`, `MR_ASSIGNEE_SINGLE`, `MR_ASSIGNEES`, `MR_REVIEWERS` `MR_LABELS`, and `MR_LINK`'\
112
+ '| In `empty_mr_text` you can use `MR_MILESTONE`',
113
+ optional: true
114
+ ),
115
+ FastlaneCore::ConfigItem.new(
116
+ key: :gitlab_merge_request_milestone,
117
+ env_name: 'ROOSTER_GITLAB_MERGE_REQUEST_MILESTONE',
118
+ default_value: '',
119
+ description: 'Milestone will be used in merge request parameter as query param',
120
+ optional: true
121
+ )
122
+ ]
123
+ end
124
+
125
+ def self.example_code
126
+ [
127
+ 'rooster_merge_request',
128
+ 'rooster_merge_request(
129
+ gitlab_token: "xyx",
130
+ gitlab_project_id: "123456",
131
+ gitlab_milestones_path: "projects/12345678"
132
+ gitlab_merge_request_total: 20,
133
+ slack_users_file: "/User/documents/slack_users.csv",
134
+ slack_webhook_url: "https://hooks.slack.com/000/000",
135
+ slack_message_format_file: "/User/documents/slack_message_format.json",
136
+ )'
137
+ ]
138
+ end
139
+
140
+ def self.is_supported?(_)
141
+ true
142
+ end
143
+
144
+ def self.author
145
+ 'tbetmen (muhammadmmunir24@gmail.com)'
146
+ end
147
+
148
+ def self.description
149
+ 'Send gitlab merge requests reminder to slack.'
150
+ end
151
+
152
+ def self.details
153
+ [
154
+ 'This action is reminder system of gitlab merge request, ',
155
+ 'fetching merge requests using Gitlab REST API and send to Slack using webhook url.'
156
+ ].join('')
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,42 @@
1
+ require 'fastlane_core/ui/ui'
2
+ require 'json'
3
+ require 'net/http'
4
+ require 'uri'
5
+
6
+ module Fastlane
7
+ module Helper
8
+ module ApiRequest
9
+ def api_get_response_json(url, header)
10
+ uri = URI(url)
11
+
12
+ UI.message "Get Request from url: #{url}"
13
+ response = Net::HTTP.get_response(uri, header)
14
+
15
+ if response.is_a?(Net::HTTPSuccess)
16
+ UI.success "Successfully get request with code: #{response.code}"
17
+ JSON.parse(response.body)
18
+ else
19
+ UI.error "Failed to get request code: #{response.code} message: #{response.message}"
20
+ nil
21
+ end
22
+ end
23
+
24
+ def api_post(url, body, header)
25
+ uri = URI(url)
26
+ request = Net::HTTP::Post.new(uri, header)
27
+ request.body = JSON(body)
28
+
29
+ UI.message "Post Request to url: #{url}"
30
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |https|
31
+ https.request(request)
32
+ end
33
+
34
+ if response.is_a?(Net::HTTPSuccess)
35
+ UI.success "Successfully post request to with code: #{response.code}"
36
+ else
37
+ UI.error "Failed to post request code: #{response&.code} message: #{response&.message}"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,45 @@
1
+ require 'fastlane_core/ui/ui'
2
+ require 'csv'
3
+ require 'json'
4
+
5
+ module Fastlane
6
+ module Helper
7
+ module FileLoader
8
+ def valid_extension(filepath, ext)
9
+ File.file?(filepath.to_s) && [ext.to_s].include?(File.extname(filepath.to_s))
10
+ end
11
+
12
+ def load_json(filepath, default_value)
13
+ UI.message "Load json filepath #{filepath}"
14
+
15
+ if valid_extension(filepath, '.json')
16
+ UI.success 'Successfully loaded json file'
17
+ JSON.parse(File.read(filepath))
18
+ else
19
+ UI.success 'Successfully return default value because given file is missing or invalid json format'
20
+ default_value
21
+ end
22
+ end
23
+
24
+ def load_csv_to_hash(filepath, header_id, header_one, header_two, header_one_key, header_two_key)
25
+ UI.message "Load csv filepath #{filepath}"
26
+
27
+ unless valid_extension(filepath, '.csv')
28
+ UI.message 'The given file is missing or invalid csv format'
29
+ return nil
30
+ end
31
+
32
+ hash_value = {}
33
+ CSV.foreach(filepath, headers: true) do |user|
34
+ hash_value[user[header_id.to_s]] = {
35
+ header_one_key.to_s => user[header_one.to_s],
36
+ header_two_key.to_s => user[header_two.to_s]
37
+ }
38
+ end
39
+
40
+ UI.success 'Successfully loaded csv file into hash'
41
+ hash_value
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,72 @@
1
+ require 'date'
2
+ require 'fastlane_core/ui/ui'
3
+ require_relative './api_request'
4
+
5
+ module Fastlane
6
+ module Helper
7
+ class GitlabApiClient
8
+ include Helper::ApiRequest
9
+
10
+ BASE_URL = 'https://gitlab.com/api/v4/'.freeze
11
+
12
+ def initialize(private_token)
13
+ @header = { 'PRIVATE-TOKEN': private_token }
14
+ end
15
+
16
+ #####################################################
17
+ # @!group MILESTONE
18
+ #####################################################
19
+ def current_milestone(milestones_path)
20
+ UI.message 'Fetching current milestone'
21
+ milestones_url = milestones_url(milestones_path)
22
+
23
+ if milestones_url.nil? || milestones_url.empty?
24
+ UI.success 'Return empty milestone because invalid milestone url'
25
+ ''
26
+ else
27
+ milestones_json = api_get_response_json(milestones_url, @header)
28
+ value = milestone_title(milestones_json)
29
+ UI.success "Successfully fetched current milestone #{value}" unless value.empty?
30
+ value
31
+ end
32
+ end
33
+
34
+ # `path` available option ['groups/:group_id', 'projects/:project_id']
35
+ def milestones_url(path)
36
+ path.nil? || path.empty? ? '' : "#{BASE_URL}#{path}/milestones?state=active"
37
+ end
38
+
39
+ def milestone_title(milestone_resp_json)
40
+ return '' if milestone_resp_json.nil?
41
+
42
+ current_milestone = milestone_resp_json.find do |value|
43
+ due_date = DateTime.strptime(value['due_date'], '%Y-%m-%d')
44
+ DateTime.now.to_time.to_i <= due_date.to_time.to_i
45
+ end
46
+
47
+ current_milestone.nil? ? '' : current_milestone['title']
48
+ end
49
+
50
+ #####################################################
51
+ # @!group MERGE REQUEST
52
+ #####################################################
53
+ def merge_requests(project_id, per_page = 10, current_milestone = '')
54
+ UI.message 'Fetching merge requests'
55
+ url = merge_request_url(project_id, per_page, current_milestone)
56
+ resp = api_get_response_json(url, @header)
57
+
58
+ if resp.nil?
59
+ UI.user_error! 'Failed to fetch gitlab merge request'
60
+ else
61
+ UI.success "Successfully fetched #{resp.length} merge requests"
62
+ resp
63
+ end
64
+ end
65
+
66
+ def merge_request_url(project_id, per_page, current_milestone)
67
+ url = "#{BASE_URL}projects/#{project_id}/merge_requests?state=opened&order_by=created_at&sort=asc&per_page=#{per_page}"
68
+ current_milestone.nil? || current_milestone.empty? ? url : url + "&milestone=#{current_milestone}"
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,60 @@
1
+ require 'fastlane_core/ui/ui'
2
+
3
+ module Fastlane
4
+ module Helper
5
+ # Transform merge request json to new format by adding slack users id if `slack_users` parameter not nil or empty.
6
+ # And ignoring merge request when title has prefix `draft` and `wip`.
7
+ class GitlabMergeRequestTransformer
8
+ def initialize(slack_users, gitlab_mr_json)
9
+ @slack_users = slack_users
10
+ @gitlab_mr_json = gitlab_mr_json
11
+ end
12
+
13
+ def transform
14
+ UI.message 'Transform gitlab merge request into new format'
15
+
16
+ mr_filtered = remove_draft_merge_request(@gitlab_mr_json)
17
+ mr_transformed = compose_gitlab_merge_request(mr_filtered)
18
+ UI.success 'Successfully transformed into new format'
19
+
20
+ mr_transformed
21
+ end
22
+
23
+ def get_slack_user_id(gitlab_user_id, gitlab_username)
24
+ if !@slack_users.nil? && @slack_users.key?(gitlab_user_id.to_s)
25
+ "<@#{@slack_users[gitlab_user_id.to_s]['slack_id']}>"
26
+ else
27
+ "@#{gitlab_username}"
28
+ end
29
+ end
30
+
31
+ def get_slack_user_ids(gitlab_mr_json, key)
32
+ gitlab_mr_json[key.to_s]&.map do |v|
33
+ get_slack_user_id(v['id'], v['username'])
34
+ end
35
+ end
36
+
37
+ def remove_draft_merge_request(gitlab_mr_json)
38
+ gitlab_mr_json&.select do |value|
39
+ title = value['title']
40
+ !(title.downcase.start_with? 'draft') && !(title.downcase.start_with? 'wip')
41
+ end
42
+ end
43
+
44
+ def compose_gitlab_merge_request(gitlab_mr_json)
45
+ gitlab_mr_json&.map do |value|
46
+ {
47
+ title: value['title'],
48
+ created_at: value['created_at'],
49
+ link: value['web_url'],
50
+ assignee: get_slack_user_id(value.dig('assignee', 'id'), value.dig('assignee', 'username')),
51
+ assignees: get_slack_user_ids(value, 'assignees'),
52
+ reviewers: get_slack_user_ids(value, 'reviewers'),
53
+ labels: value['labels']&.map { |label| label },
54
+ milestone_title: value.dig('milestone', 'title')
55
+ }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,39 @@
1
+ require 'fastlane_core/ui/ui'
2
+ require_relative './file_loader'
3
+
4
+ module Fastlane
5
+ module Helper
6
+ class SlackFileClient
7
+ include Helper::FileLoader
8
+
9
+ SLACK_MESSAGE_FORMAT_DEFAULT = {
10
+ 'text' => 'Hello, its beautiful day!',
11
+ 'header' => 'Hi there, Just wanted to let you know that we have MR_TOTAL merge requests that need your attention.',
12
+ 'mr_item' => '<MR_LINK|MR_TITLE> MR_ASSIGNEE_SINGLE',
13
+ 'footer' => 'Thank you',
14
+ 'empty_mr_text' => 'Congratulation there is no merge request anymore, keep the good works'
15
+ }.freeze
16
+
17
+ def message_format(filepath)
18
+ UI.message 'Load slack message format'
19
+ load_json(filepath, SLACK_MESSAGE_FORMAT_DEFAULT)
20
+ end
21
+
22
+ def users(filepath)
23
+ UI.message 'Load slack users'
24
+ slack_users = load_csv_to_hash(
25
+ filepath,
26
+ 'Gitlab User ID',
27
+ 'Name',
28
+ 'Slack ID',
29
+ 'name',
30
+ 'slack_id'
31
+ )
32
+
33
+ UI.success 'Slack users nil, skipped using tag user `@user` in when posting message reminder in slack' if slack_users.nil?
34
+
35
+ slack_users
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,99 @@
1
+ require 'fastlane_core/ui/ui'
2
+ require_relative './api_request'
3
+ require_relative './time_format'
4
+
5
+ module Fastlane
6
+ module Helper
7
+ class SlackMessageClient
8
+ include Helper::ApiRequest
9
+ include Helper::TimeFormat
10
+
11
+ def initialize(slack_users, slack_message_format, merge_requests, current_milestone)
12
+ @slack_users = slack_users
13
+ @slack_message_format = slack_message_format
14
+ @merge_requests = merge_requests
15
+ @current_milestone = current_milestone
16
+ end
17
+
18
+ def post_message(slack_webhook_url, payload)
19
+ UI.message 'Posting slack message'
20
+ api_post(
21
+ slack_webhook_url,
22
+ payload,
23
+ { "Content-Type": 'application/json' }
24
+ )
25
+ end
26
+
27
+ def markdown_section(text)
28
+ {
29
+ type: 'section',
30
+ text: {
31
+ type: 'mrkdwn',
32
+ text: text
33
+ }
34
+ }
35
+ end
36
+
37
+ def format_users_string(users)
38
+ users.empty? ? '-' : users.join(' ')
39
+ end
40
+
41
+ def merge_request_numbering
42
+ @merge_requests.map.with_index do |mr, index|
43
+ time = format_relative_date_time(mr[:created_at])
44
+ assignees = format_users_string(mr[:assignees])
45
+ reviewers = format_users_string(mr[:reviewers])
46
+ text = @slack_message_format['mr_item'].to_s
47
+ .gsub('MR_TITLE', mr[:title].to_s)
48
+ .gsub('MR_TIME', time.to_s)
49
+ .gsub('MR_ASSIGNEE_SINGLE', mr[:assignee].to_s)
50
+ .gsub('MR_ASSIGNEES', assignees.to_s)
51
+ .gsub('MR_REVIEWERS', reviewers.to_s)
52
+ .gsub('MR_LABELS', mr[:labels].join(','))
53
+ .gsub('MR_LINK', mr[:link].to_s)
54
+ "#{index + 1}. #{text}"
55
+ end
56
+ end
57
+
58
+ def merge_request_section
59
+ mr_numbering = merge_request_numbering
60
+ mr_numbering = mr_numbering.join("\n") if mr_numbering.length > 1
61
+
62
+ markdown_section(mr_numbering)
63
+ end
64
+
65
+ def payload
66
+ UI.message 'Composing slack payload'
67
+ merge_request_size = @merge_requests.nil? ? 0 : @merge_requests.length
68
+
69
+ unless merge_request_size.positive?
70
+ UI.success 'Successfully composed slack payload but has empty merge request'
71
+ return {
72
+ text: @slack_message_format['text'],
73
+ blocks: [
74
+ markdown_section(
75
+ @slack_message_format['empty_mr_text'].gsub('MR_MILESTONE', @current_milestone)
76
+ )
77
+ ]
78
+ }
79
+ end
80
+
81
+ header_section = markdown_section(
82
+ @slack_message_format['header'].gsub('MR_TOTAL', merge_request_size.to_s)
83
+ .gsub('MR_MILESTONE', @current_milestone)
84
+ )
85
+ footer_section = markdown_section(@slack_message_format['footer'])
86
+
87
+ UI.success "Successfully composed slack payload with total #{merge_request_size} merge request"
88
+ {
89
+ text: @slack_message_format['text'],
90
+ blocks: [
91
+ header_section,
92
+ merge_request_section,
93
+ footer_section
94
+ ]
95
+ }
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,66 @@
1
+ require 'date'
2
+ require 'fastlane_core/ui/ui'
3
+
4
+ module Fastlane
5
+ module Helper
6
+ # source: https://stackoverflow.com/a/63106647
7
+ module TimeFormat
8
+ TIME_UNIT_TO_SECS = {
9
+ SECONDS: 1,
10
+ MINUTES: 60,
11
+ HOURS: 3600,
12
+ DAYS: 24 * 3600,
13
+ WEEKS: 7 * 24 * 3600,
14
+ MONTHS: 30 * 24 * 3600,
15
+ YEARS: 365 * 24 * 3600
16
+ }.freeze
17
+ TIME_UNIT_LABELS = {
18
+ SECONDS: 'seconds',
19
+ MINUTES: 'minutes',
20
+ HOURS: 'hours',
21
+ DAYS: 'days',
22
+ WEEKS: 'weeks',
23
+ MONTHS: 'months',
24
+ YEARS: 'years'
25
+ }.freeze
26
+
27
+ def format_relative_date_time(date_string, date_format = '%Y-%m-%dT%H:%M:%S.%L%Z')
28
+ created_at = DateTime.strptime(date_string, date_format)
29
+ now = DateTime.now
30
+ diff = now.to_time.to_i - created_at.to_time.to_i
31
+
32
+ if diff.negative?
33
+ UI.message '`date` cannot be in the future'
34
+ return 'in future'
35
+ end
36
+
37
+ time_unit = if diff < TIME_UNIT_TO_SECS[:MINUTES]
38
+ :SECONDS
39
+ elsif diff < TIME_UNIT_TO_SECS[:HOURS]
40
+ :MINUTES
41
+ elsif diff < TIME_UNIT_TO_SECS[:DAYS]
42
+ :HOURS
43
+ elsif diff < TIME_UNIT_TO_SECS[:WEEKS]
44
+ :DAYS
45
+ elsif diff < TIME_UNIT_TO_SECS[:MONTHS]
46
+ :WEEKS
47
+ elsif diff < TIME_UNIT_TO_SECS[:YEARS]
48
+ :MONTHS
49
+ else
50
+ :YEARS
51
+ end
52
+
53
+ time_relative = case time_unit
54
+ when :SECONDS, :MINUTES, :HOURS, :DAYS, :WEEKS
55
+ diff / TIME_UNIT_TO_SECS[time_unit]
56
+ when :MONTHS
57
+ 0.step.find { |n| (created_at >> n) > now } - 1
58
+ else
59
+ 0.step.find { |n| (created_at >> 12 * n) > now } - 1
60
+ end
61
+
62
+ "#{time_relative} #{TIME_UNIT_LABELS[time_unit]} ago"
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module Rooster
3
- VERSION = "0.2.0"
3
+ VERSION = '1.1.0'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-rooster
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
- - Muhammad M Munir
7
+ - Muhammad M. Munir
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-23 00:00:00.000000000 Z
11
+ date: 2023-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -159,8 +159,14 @@ files:
159
159
  - LICENSE
160
160
  - README.md
161
161
  - lib/fastlane/plugin/rooster.rb
162
- - lib/fastlane/plugin/rooster/actions/rooster_action.rb
163
- - lib/fastlane/plugin/rooster/helper/rooster_helper.rb
162
+ - lib/fastlane/plugin/rooster/actions/rooster_merge_request_action.rb
163
+ - lib/fastlane/plugin/rooster/helper/api_request.rb
164
+ - lib/fastlane/plugin/rooster/helper/file_loader.rb
165
+ - lib/fastlane/plugin/rooster/helper/gitlab_api_client.rb
166
+ - lib/fastlane/plugin/rooster/helper/gitlab_merge_request_transformer.rb
167
+ - lib/fastlane/plugin/rooster/helper/slack_file_client.rb
168
+ - lib/fastlane/plugin/rooster/helper/slack_message_client.rb
169
+ - lib/fastlane/plugin/rooster/helper/time_format.rb
164
170
  - lib/fastlane/plugin/rooster/version.rb
165
171
  homepage: https://github.com/tbetmen/fastlane-plugin-rooster
166
172
  licenses:
@@ -1,420 +0,0 @@
1
- require 'net/http'
2
- require 'uri'
3
- require 'json'
4
- require 'csv'
5
- require 'date'
6
-
7
- require 'fastlane/action'
8
- require_relative '../helper/rooster_helper'
9
-
10
- module Fastlane
11
- module Actions
12
- class RoosterAction < Action
13
- def self.run(params)
14
- members_file = params[:members_file] || ENV['GITLAB_SLACK_MEMBERS_FILE']
15
- gitlab_project_id = params[:gitlab_project_id] || ENV['GITLAB_PROJECT_ID']
16
- slack_webhook_url = params[:slack_webhook_url] || ENV['SLACK_WEBHOOK_URL']
17
- slack_message_format_file = params[:slack_message_format_file] || ENV['SLACK_MESSAGE_FORMAT_FILE']
18
- gitlab_mr_total = params[:gitlab_merge_request_total] || ENV['GITLAB_MERGE_REQUEST_TOTAL']
19
- gitlab_milestones_path = params[:gitlab_milestones_path] || ENV['GITLAB_MILESTONES_PATH']
20
-
21
- @gitlab_token = params[:gitlab_token] || ENV['GITLAB_ACCESS_TOKEN']
22
- @gitlab_api_base_url = 'https://gitlab.com/api/v4/'
23
- @slack_message_format = get_slack_message_format(slack_message_format_file)
24
- @members = get_members(members_file)
25
- @current_milestone = get_current_milestone(gitlab_milestones_path)
26
-
27
- mr_url = compose_merge_request_url(gitlab_project_id, gitlab_mr_total, @current_milestone)
28
- mr_resp_json = get_response_json(mr_url)
29
- merge_requests = compose_gitlab_merge_requests(mr_resp_json)
30
- slack_payload = compose_slack_payload(merge_requests)
31
-
32
- # UI.message slack_payload.to_json
33
- post_request(
34
- slack_webhook_url,
35
- slack_payload,
36
- { "Content-Type": "application/json" }
37
- )
38
- end
39
-
40
- #####################################################
41
- # @!group API REQUEST
42
- #####################################################
43
-
44
- def self.get_response_json(url)
45
- uri = URI(url)
46
- header = { 'PRIVATE-TOKEN' => @gitlab_token }
47
- response = Net::HTTP.get_response(uri, header)
48
-
49
- if response.is_a?(Net::HTTPSuccess)
50
- JSON.parse(response.body)
51
- else
52
- UI.error "Failed to get request from #{url} code #{response.code} message #{response.message}"
53
- end
54
- end
55
-
56
- def self.post_request(url, body, header)
57
- uri = URI(url)
58
- request = Net::HTTP::Post.new(uri, header)
59
- request.body = JSON(body)
60
-
61
- response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |https|
62
- https.request(request)
63
- end
64
-
65
- if response.is_a?(Net::HTTPSuccess)
66
- UI.success "Successfully post request to #{url}"
67
- else
68
- UI.error "Failed to post request to #{url} code #{response.code} message #{response.message}"
69
- end
70
- end
71
-
72
- #####################################################
73
- # @!group MILESTONE
74
- #####################################################
75
-
76
- def self.compose_milestones_url(milestones_path)
77
- return nil unless !milestones_path.nil?
78
-
79
- "#{@gitlab_api_base_url}#{milestones_path}/milestones?state=active"
80
- end
81
-
82
- def self.extract_current_milestone_title(milestone_resp_json)
83
- current_milestone = milestone_resp_json.find do |value|
84
- due_date = DateTime.strptime(value.dig('due_date'), '%Y-%m-%d')
85
- DateTime.now.to_time.to_i <= due_date.to_time.to_i
86
- end
87
-
88
- return "" unless !current_milestone.nil?
89
-
90
- current_milestone.dig('title')
91
- end
92
-
93
- def self.get_current_milestone(milestones_path)
94
- milestones_url = compose_milestones_url(milestones_path)
95
- return "" unless !milestones_url.nil?
96
-
97
- milestones_json = get_response_json(milestones_url)
98
- extract_current_milestone_title(milestones_json)
99
- end
100
-
101
- #####################################################
102
- # @!group SLACK MESSAGE FORMAT
103
- #####################################################
104
-
105
- def self.get_slack_message_format(slack_message_format_file)
106
- unless !slack_message_format_file.is_a?(Hash)
107
- return slack_message_format_file
108
- end
109
-
110
- JSON.parse(File.read(slack_message_format_file))
111
- end
112
-
113
- #####################################################
114
- # @!group MEMBERS
115
- #####################################################
116
-
117
- def self.get_members(members_file)
118
- return nil unless !members_file.nil? && !members_file.empty?
119
-
120
- members = {}
121
- CSV.foreach(members_file, headers: true) do |member|
122
- members[member['Gitlab User ID']] = {
123
- :name => member['Name'],
124
- :slack_id => member['Slack ID']
125
- }
126
- end
127
-
128
- members
129
- end
130
-
131
- #####################################################
132
- # @!group MERGE REQUEST
133
- #####################################################
134
-
135
- def self.compose_merge_request_url(project_id, per_page, current_milestone)
136
- "#{@gitlab_api_base_url}projects/#{project_id}/merge_requests?state=opened&order_by=created_at&sort=asc&per_page=#{per_page}&milestone=#{current_milestone}"
137
- end
138
-
139
- def self.get_slack_member_id(gitlab_member_id, gitlab_member_username)
140
- if !@members.nil? && @members.key?(gitlab_member_id.to_s)
141
- @members[gitlab_member_id.to_s][:slack_id]
142
- else
143
- "@#{gitlab_member_username}"
144
- end
145
- end
146
-
147
- def self.get_slack_member_ids(gitlab_mr_json, key)
148
- gitlab_mr_json.dig("#{key}")&.map do |v|
149
- get_slack_member_id(v.dig('id'), v.dig('username'))
150
- end
151
- end
152
-
153
- def self.compose_gitlab_merge_requests(gitlab_mr_json)
154
- gitlab_mr_json = gitlab_mr_json.select do |value|
155
- title = value.dig('title')
156
- !(title.downcase.start_with? 'draft') && !(title.downcase.start_with? 'wip')
157
- end
158
-
159
- gitlab_mr_json.map do |value|
160
- {
161
- :title => value.dig('title'),
162
- :created_at => value.dig('created_at'),
163
- :link => value.dig('web_url'),
164
- :assignee => get_slack_member_id(value.dig('assignee', 'id'), value.dig('assignee', 'username')),
165
- :assignees => get_slack_member_ids(value, 'assignees'),
166
- :reviewers => get_slack_member_ids(value, 'reviewers'),
167
- :labels => value.dig('labels')&.map { |label| label },
168
- :milestone_title => value.dig('milestone', 'title')
169
- }
170
- end
171
- end
172
-
173
- #####################################################
174
- # @!group TIME FORMAT
175
- # source: https://stackoverflow.com/a/63106647
176
- #####################################################
177
-
178
- def self.format_relative_date_time(date_string)
179
- time_unit_to_secs = {
180
- SECONDS: 1,
181
- MINUTES: 60,
182
- HOURS: 3600,
183
- DAYS: 24 * 3600,
184
- WEEKS: 7 * 24 * 3600,
185
- MONTHS: 30 * 24 * 3600,
186
- YEARS: 365 * 24 * 3600
187
- }
188
- time_unit_labels = {
189
- SECONDS: 'seconds',
190
- MINUTES: 'minutes',
191
- HOURS: 'hours',
192
- DAYS: 'days',
193
- WEEKS: 'weeks',
194
- MONTHS: 'months',
195
- YEARS: 'years'
196
- }
197
-
198
- created_at = DateTime.strptime(date_string, '%Y-%m-%dT%H:%M:%S.%L%Z')
199
- diff = DateTime.now.to_time.to_i - created_at.to_time.to_i
200
-
201
- UI.error("'date_time' cannot be in the future") if diff.negative?
202
-
203
- time_unit = if diff < time_unit_to_secs[:MINUTES]
204
- :SECONDS
205
- elsif diff < time_unit_to_secs[:HOURS]
206
- :MINUTES
207
- elsif diff < time_unit_to_secs[:DAYS]
208
- :HOURS
209
- elsif diff < time_unit_to_secs[:WEEKS]
210
- :DAYS
211
- elsif diff < time_unit_to_secs[:MONTHS]
212
- :WEEKS
213
- elsif diff < time_unit_to_secs[:YEARS]
214
- :MONTHS
215
- else
216
- :YEARS
217
- end
218
-
219
- time_relative = case time_unit
220
- when :SECONDS, :MINUTES, :HOURS, :DAYS, :WEEKS
221
- diff / time_unit_to_secs[time_unit]
222
- when :MONTHS
223
- 0.step.find { |n| (created_at >> n) > now } - 1
224
- else
225
- 0.step.find { |n| (created_at >> 12 * n) > now } - 1
226
- end
227
-
228
- "#{time_relative} #{time_unit_labels[time_unit]} ago"
229
- end
230
-
231
- #####################################################
232
- # @!group SLACK
233
- #####################################################
234
-
235
- def self.format_users_string(users)
236
- return '-' if users.empty?
237
-
238
- users.join(' ')
239
- end
240
-
241
- def self.compose_merge_request_list_message(merge_requests)
242
- merge_requests_formatted = merge_requests.map.with_index do |mr, index|
243
- time = format_relative_date_time(mr[:created_at])
244
- assignees = format_users_string(mr[:assignees])
245
- reviewers = format_users_string(mr[:reviewers])
246
- text = @slack_message_format['mr_item'].to_s
247
- .gsub('MR_TITLE', mr[:title].to_s)
248
- .gsub('MR_TIME', time.to_s)
249
- .gsub('MR_ASSIGNEE_SINGLE', mr[:assignee].to_s)
250
- .gsub('MR_ASSIGNEES', assignees.to_s)
251
- .gsub('MR_REVIEWERS', reviewers.to_s)
252
- .gsub('MR_LABELS', mr[:labels].join(','))
253
- .gsub('MR_LINK', mr[:link].to_s)
254
- "#{index+1}. #{text}"
255
- end
256
-
257
- if merge_requests_formatted.length > 1
258
- merge_requests_formatted = merge_requests_formatted.join("\n")
259
- end
260
-
261
- {
262
- "type": 'section',
263
- "text": {
264
- "type": 'mrkdwn',
265
- "text": merge_requests_formatted
266
- }
267
- }
268
- end
269
-
270
- def self.compose_slack_payload(merge_requests)
271
- unless merge_requests.length > 0
272
- return {
273
- :text => @slack_message_format['text'],
274
- :blocks => [
275
- {
276
- "type": 'section',
277
- "text": {
278
- "type": 'mrkdwn',
279
- "text": @slack_message_format['empty_mr_text'].gsub('MR_MILESTONE', @current_milestone)
280
- }
281
- }
282
- ]
283
- }
284
- end
285
-
286
- header_text = @slack_message_format['header'].gsub('MR_TOTAL', merge_requests.length.to_s)
287
- .gsub('MR_MILESTONE', @current_milestone)
288
- header_section = {
289
- :type => 'section',
290
- :text => {
291
- :type => 'mrkdwn',
292
- :text => header_text
293
- }
294
- }
295
- mr_section = compose_merge_request_list_message(merge_requests)
296
- footer_section = {
297
- :type => 'section',
298
- :text => {
299
- :type => 'mrkdwn',
300
- :text => @slack_message_format['footer']
301
- }
302
- }
303
-
304
- {
305
- :text => @slack_message_format['text'],
306
- :blocks => [header_section, mr_section, footer_section]
307
- }
308
- end
309
-
310
- #####################################################
311
- # @!group Documentation
312
- #####################################################
313
-
314
- def self.available_options # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
315
- [
316
- FastlaneCore::ConfigItem.new(
317
- key: :gitlab_token,
318
- env_name: 'GITLAB_ACCESS_TOKEN',
319
- description: 'Gitlab access token',
320
- optional: false,
321
- verify_block: proc do |value|
322
- UI.user_error!('No gitlab access token given') unless value and !value.empty?
323
- end
324
- ),
325
- FastlaneCore::ConfigItem.new(
326
- key: :gitlab_project_id,
327
- env_name: 'GITLAB_PROJECT_ID',
328
- description: 'Gitlab project id',
329
- optional: false,
330
- verify_block: proc do |value|
331
- UI.user_error!('No gitlab mr url given') unless value and !value.empty?
332
- end
333
- ),
334
- FastlaneCore::ConfigItem.new(
335
- key: :gitlab_milestones_path,
336
- env_name: 'GITLAB_MILESTONES_PATH',
337
- description: 'Gitlab group or project milestones with given format either `groups/:group_id` or `projects/:project_id`',
338
- optional: true,
339
- verify_block: proc do |value|
340
- UI.user_error!('No gitlab milestones path given') unless value and !value.empty?
341
- end
342
- ),
343
- FastlaneCore::ConfigItem.new(
344
- key: :gitlab_merge_request_total,
345
- env_name: 'GITLAB_MERGE_REQUEST_TOTAL',
346
- default_value: '10',
347
- description: 'Total merge request to be shown in reminder, default value is `10`',
348
- optional: true,
349
- verify_block: proc do |value|
350
- UI.user_error!('No gitlab merge request total given') unless value and !value.empty?
351
- end
352
- ),
353
- FastlaneCore::ConfigItem.new(
354
- key: :members_file,
355
- env_name: 'GITLAB_SLACK_MEMBERS_FILE',
356
- description: 'Gitlab members filepath in csv format',
357
- optional: true,
358
- verify_block: proc do |value|
359
- UI.user_error!('No members file given') unless value and !value.empty?
360
- end
361
- ),
362
- FastlaneCore::ConfigItem.new(
363
- key: :slack_webhook_url,
364
- env_name: 'SLACK_WEBHOOK_URL',
365
- description: 'Slack webhook url',
366
- optional: false,
367
- verify_block: proc do |value|
368
- UI.user_error!('No slack webhook url given') unless value and !value.empty?
369
- end
370
- ),
371
- FastlaneCore::ConfigItem.new(
372
- key: :slack_message_format_file,
373
- env_name: 'SLACK_MESSAGE_FORMAT_FILE',
374
- default_value: {
375
- 'text' => 'Hello, beautiful day!',
376
- 'header' =>'Hi there, Just wanted to let you know that we have MR_TOTAL merge requests that need your attention.',
377
- 'mr_item' => '<MR_LINK|MR_TITLE> MR_ASSIGNEE_SINGLE',
378
- 'footer' => 'Thank you',
379
- 'empty_mr_text' => 'Congratulation there is no merge request anymore, keep the good works'
380
- },
381
- description: "Slack message format in json format contains `text`, `header`, `mr_item`, `footer`, and `empty_mr_text`"\
382
- "| In `header` you can use use `MR_TOTAL` and `MR_MILESTONE`"\
383
- "| In `mr_item` you can use use `MR_TITLE`, `MR_TIME`, `MR_ASSIGNEE_SINGLE`, `MR_ASSIGNEES`, `MR_REVIEWERS` `MR_LABELS`, and `MR_LINK`"\
384
- "| In `empty_mr_text` you can use `MR_MILESTONE`",
385
- optional: true
386
- )
387
- ]
388
- end
389
-
390
- def self.example_code
391
- [
392
- 'gitlab_mr_reminder()',
393
- 'gitlab_mr_reminder(
394
- gitlab_members_file: "/User/documents/fe_members.csv",
395
- gitlab_token: "XXXX",
396
- gitlab_project_id: "https://gitlab.com/...",
397
- slack_webhook_url: "ttps://hooks.slack.com/...",
398
- slack_message_format_file: "/User/documents/slack_format.csv", # optional
399
- )'
400
- ]
401
- end
402
-
403
- def self.is_supported?(platform)
404
- true
405
- end
406
-
407
- def self.author
408
- 'tbetmen'
409
- end
410
-
411
- def self.description
412
- 'Send gitlab merge requests reminder to slack.'
413
- end
414
-
415
- def self.details
416
- "This action is reminder system of gitlab merge request, fetching merge requests using Gitlab REST API and send to Slack using webhook url."
417
- end
418
- end
419
- end
420
- end
@@ -1,16 +0,0 @@
1
- require 'fastlane_core/ui/ui'
2
-
3
- module Fastlane
4
- UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
-
6
- module Helper
7
- class RoosterHelper
8
- # class methods that you define here become available in your action
9
- # as `Helper::RoosterHelper.your_method`
10
- #
11
- def self.show_message
12
- UI.message("Hello from the rooster plugin helper!")
13
- end
14
- end
15
- end
16
- end