robopigeon 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.gitlab-ci.yml +27 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +65 -0
  6. data/.ruby-version +1 -0
  7. data/.version +1 -0
  8. data/Gemfile +4 -0
  9. data/README.md +256 -0
  10. data/Rakefile +11 -0
  11. data/bin/console +14 -0
  12. data/bin/rake +29 -0
  13. data/bin/rspec +29 -0
  14. data/bin/rubocop +29 -0
  15. data/bin/setup +8 -0
  16. data/exe/robopigeon +22 -0
  17. data/lib/robopigeon/documentarian.rb +39 -0
  18. data/lib/robopigeon/dsl/helpers.rb +17 -0
  19. data/lib/robopigeon/dsl/initial_jobs.rb +36 -0
  20. data/lib/robopigeon/dsl/job.rb +34 -0
  21. data/lib/robopigeon/dsl.rb +46 -0
  22. data/lib/robopigeon/git/helper_dsl.rb +60 -0
  23. data/lib/robopigeon/git.rb +7 -0
  24. data/lib/robopigeon/gitlab/client.rb +42 -0
  25. data/lib/robopigeon/gitlab/dsl.rb +174 -0
  26. data/lib/robopigeon/gitlab/helper_dsl.rb +44 -0
  27. data/lib/robopigeon/gitlab/jira.rb +0 -0
  28. data/lib/robopigeon/gitlab/merge_request.rb +78 -0
  29. data/lib/robopigeon/gitlab.rb +29 -0
  30. data/lib/robopigeon/jira/client.rb +17 -0
  31. data/lib/robopigeon/jira/dsl.rb +81 -0
  32. data/lib/robopigeon/jira/helper_dsl.rb +24 -0
  33. data/lib/robopigeon/jira/ticket.rb +186 -0
  34. data/lib/robopigeon/jira/ticket_dsl.rb +155 -0
  35. data/lib/robopigeon/jira.rb +37 -0
  36. data/lib/robopigeon/markdown/helper_dsl.rb +73 -0
  37. data/lib/robopigeon/markdown.rb +11 -0
  38. data/lib/robopigeon/resources/initial_robopigeon.rb +156 -0
  39. data/lib/robopigeon/slack/attachments_dsl.rb +63 -0
  40. data/lib/robopigeon/slack/client.rb +46 -0
  41. data/lib/robopigeon/slack/dsl.rb +74 -0
  42. data/lib/robopigeon/slack/helper_dsl.rb +26 -0
  43. data/lib/robopigeon/slack/message.rb +35 -0
  44. data/lib/robopigeon/slack.rb +30 -0
  45. data/lib/robopigeon/version.rb +3 -0
  46. data/lib/robopigeon.rb +13 -0
  47. data/robopigeon +0 -0
  48. data/robopigeon.gemspec +47 -0
  49. data/robopigeon.rb +156 -0
  50. metadata +264 -0
@@ -0,0 +1,186 @@
1
+ require 'json'
2
+
3
+ module RoboPigeon::Jira
4
+ class Ticket
5
+ MAX_TRANSITIONS = 10
6
+ ISSUE_PATH = '/rest/api/2/issue'.freeze
7
+ attr_accessor :ticket, :project, :issue_type, :summary, :fields,
8
+ :assignee, :reporter, :description
9
+
10
+ def initialize(ticket = nil)
11
+ self.ticket = ticket
12
+ self.fields = {}
13
+ update_from_server
14
+ self.project = ticket.split('-')[0] if ticket
15
+ end
16
+
17
+ def update_from_server
18
+ return unless ticket
19
+
20
+ get = jira_request.get("#{ISSUE_PATH}/#{ticket}/")
21
+ raise "Failed to look up issue #{ticket}" unless get.status < 400
22
+
23
+ raw = JSON.parse(get.body)
24
+ self.project = raw['fields']['project']['key']
25
+ self.issue_type = raw['fields']['issuetype']['name']
26
+ self.summary = raw['fields']['summary']
27
+ self.reporter = raw['fields']['reporter']['name'] if raw['fields']['reporter']
28
+ self.assignee = raw['fields']['assignee']['name'] if raw['fields']['assignee']
29
+ end
30
+
31
+ def jira_request
32
+ RoboPigeon::Jira::Client.jira_request
33
+ end
34
+
35
+ def create!
36
+ response = jira_request.post do |req|
37
+ req.url '/rest/api/2/issue'
38
+ req.body = {
39
+ fields: {
40
+ project: { key: project },
41
+ issuetype: { name: issue_type },
42
+ summary: summary,
43
+ description: description,
44
+ assignee: { name: reporter }
45
+ }.merge(fields)
46
+ }.to_json
47
+ end
48
+
49
+ begin
50
+ self.ticket = JSON.parse(response.body).fetch('key')
51
+ RoboPigeon::Jira::Client.last_created_ticket = ticket
52
+ rescue KeyError
53
+ raise RoboPigeon::Jira::RequiredFieldNotSet, "Response from JIRA did not contain new issue key: #{response.body}"
54
+ end
55
+ end
56
+
57
+ def assign(user_email)
58
+ user_data = jira_request.get("/rest/api/2/user/search?username=#{user_email}")
59
+ user = JSON.parse(user_data.body).first
60
+ raise "Unable to find jira user with email #{user_email}" unless user
61
+
62
+ self.assignee = user['name']
63
+ self.reporter ||= user['name']
64
+
65
+ if ticket
66
+ jira_request.post do |req|
67
+ req.url "/rest/api/3/issue/#{ticket}/assignee"
68
+ req.body = { name: assignee }.to_json
69
+ end
70
+ end
71
+ end
72
+
73
+ def set_reporter(user_email)
74
+ raise 'Cannot modify reporter' if ticket
75
+
76
+ user_data = jira_request.get("/rest/api/2/user/search?username=#{user_email}")
77
+ user = JSON.parse(user_data.body).first
78
+ raise "Unable to find jira user with email #{user_email}" unless user
79
+
80
+ self.reporter = user['name']
81
+ end
82
+
83
+ def description_from_template(_file)
84
+ self.description = 'Content from an erb template'
85
+ end
86
+
87
+ def set_field(name, value)
88
+ require_field(:issue_type)
89
+ request_path = "#{ISSUE_PATH}/createmeta?projectKeys=#{project}&expand=projects.issuetypes.fields"
90
+ response = jira_request.get(request_path)
91
+ raw = JSON.parse(response.body)
92
+ fields = raw['projects'].first['issuetypes'].select do |it|
93
+ it['name'] == issue_type
94
+ end.first['fields']
95
+ field = fields.keys.select do |key|
96
+ fields[key]['name'] == name
97
+ end.first
98
+
99
+ raise "Field #{name} was not found" unless field
100
+
101
+ self.fields[field] = if fields[field]['allowedValues'].nil?
102
+ value
103
+ else
104
+ { value: value }
105
+ end
106
+
107
+ if ticket
108
+ post = jira_request.put do |req|
109
+ req.url "#{ISSUE_PATH}/#{ticket}/"
110
+ req.body = { fields: self.fields }.to_json
111
+ end
112
+ end
113
+
114
+ raise 'Failed to update field' unless post.nil? || post.status < 400
115
+ end
116
+
117
+ def set_issuetype(name)
118
+ require_field(:project)
119
+ request_path = "#{ISSUE_PATH}/createmeta?projectKeys=#{project}&expand=projects.issuetypes.fields"
120
+ response = jira_request.get(request_path)
121
+ raw = JSON.parse(response.body)
122
+ types = raw['projects'].first['issuetypes'].map { |type| type['name'] }
123
+ raise RoboPigeon::Jira::FieldDoesNotConform, "Issue type #{name} was not found in #{types.join(', ')}" unless types.include?(name)
124
+
125
+ self.issue_type = name
126
+ end
127
+
128
+ def perform_transition(transition)
129
+ require_ticket
130
+ get_fields = jira_request.get("#{ISSUE_PATH}/#{ticket}/transitions?expand=transitions.fields")
131
+ transition_details = JSON.parse(get_fields.body)['transitions'].find do |trans|
132
+ trans['name'].casecmp(transition).zero?
133
+ end
134
+ raise "Unable to find valid transition '#{transition}'" unless transition_details
135
+
136
+ jira_request.post do |req|
137
+ req.url "#{ISSUE_PATH}/#{ticket}/transitions"
138
+ req.body = { transition: { id: transition_details['id'] } }.to_json
139
+ end
140
+ end
141
+
142
+ def current_state
143
+ require_ticket
144
+ get = jira_request.get do |req|
145
+ req.url "#{ISSUE_PATH}/#{ticket}/"
146
+ end
147
+ JSON.parse(get.body)['fields']['status']['name']
148
+ end
149
+
150
+ def wait_for_state!(state, timeout=10.minutes, time=Time.now, sleep_time=15)
151
+ require_ticket
152
+ waited = (Time.now - time).to_i
153
+ minutes = waited / 60
154
+ seconds = waited % 60
155
+ if current_state == state
156
+ puts "#{ticket} in #{state} after #{minutes} minutes #{seconds} seconds"
157
+ return
158
+ end
159
+ raise RoboPigeon::Jira::WaitTimeout, "Timed out waiting for #{ticket} to transition to #{state} after #{minutes} minutes #{seconds} seconds" if (timeout.to_i - waited) <= 0
160
+
161
+ puts "Still waiting for #{ticket} in #{state}... waited #{minutes} minutes #{seconds} seconds"
162
+ sleep sleep_time
163
+ wait_for_state!(state, timeout, time, sleep_time)
164
+ end
165
+
166
+ def add_comment(comment)
167
+ require_ticket
168
+
169
+ post = jira_request.post do |req|
170
+ req.url "#{ISSUE_PATH}/#{ticket}/comment"
171
+ req.body = { body: comment }.to_json
172
+ end
173
+ raise 'Failed to create comment' unless post.status == 201
174
+ end
175
+
176
+ private
177
+
178
+ def require_ticket
179
+ raise RoboPigeon::Jira::TicketNotFoundOrSet, 'Ticket was not set!' if ticket.nil?
180
+ end
181
+
182
+ def require_field(field)
183
+ raise RoboPigeon::Jira::RequiredFieldNotSet, "You must set a #{field}" if send(field).nil?
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,155 @@
1
+ module RoboPigeon::Dsl
2
+ class JiraTicket
3
+ include RoboPigeon::Dsl::Helpers
4
+ attr_accessor :ticket
5
+
6
+ def self.run(ticket_number = nil, &block)
7
+ ticket = RoboPigeon::Jira::Ticket.new(ticket_number)
8
+ jira = RoboPigeon::Dsl::JiraTicket.new
9
+ jira.ticket = ticket
10
+ jira.instance_eval(&block)
11
+ jira.ticket.create! unless jira.ticket.ticket
12
+ ticket
13
+ end
14
+
15
+ RoboPigeon::Documentarian.add_command(
16
+ 'assign',
17
+ block: %w[job jira ticket],
18
+ params: [
19
+ { name: 'email', type: 'String', desc: 'the email address for the person to assign the ticket to', example: 'someone@example.com' }
20
+ ],
21
+ desc: 'assign the ticket to a person by email address'
22
+ )
23
+ def assign(email)
24
+ ticket.assign(email)
25
+ end
26
+
27
+ RoboPigeon::Documentarian.add_command(
28
+ 'reporter',
29
+ block: %w[job jira ticket],
30
+ params: [
31
+ { name: 'email', type: 'String', desc: 'the email address for the person to reporter the ticket to', example: 'someone@example.com' }
32
+ ],
33
+ desc: 'reporter the ticket to a person by email address'
34
+ )
35
+ def reporter(email)
36
+ ticket.set_reporter(email)
37
+ end
38
+
39
+ RoboPigeon::Documentarian.add_command(
40
+ 'comment',
41
+ block: %w[job jira ticket],
42
+ params: [
43
+ { name: 'comment', type: 'String', desc: 'the comment to add to the ticket', example: 'A string comment' }
44
+ ],
45
+ desc: 'add a comment to the ticket'
46
+ )
47
+ def comment(comment)
48
+ ticket.add_comment(comment)
49
+ end
50
+
51
+ RoboPigeon::Documentarian.add_command(
52
+ 'project',
53
+ block: %w[job jira ticket],
54
+ params: [
55
+ { name: 'project_key', type: 'String', desc: 'the key for the project you want to create the ticket in', example: 'TIC' }
56
+ ],
57
+ desc: 'set which project you want to create the ticket in (only used when creating)'
58
+ )
59
+ def project(project_key)
60
+ ticket.project = project_key
61
+ end
62
+
63
+ RoboPigeon::Documentarian.add_command(
64
+ 'summary',
65
+ block: %w[job jira ticket],
66
+ params: [
67
+ { name: 'title', type: 'String', desc: 'title summary for your issue', example: 'A Bug has happend!' }
68
+ ],
69
+ desc: 'set the issue summary (only used in creation, use the field funtion for updates)'
70
+ )
71
+ def summary(title)
72
+ ticket.summary = title
73
+ end
74
+
75
+ RoboPigeon::Documentarian.add_command(
76
+ 'description',
77
+ block: %w[job jira ticket],
78
+ params: [
79
+ { name: 'text', type: 'String', desc: 'the main body of a new issue', example: 'Lots of content in here' }
80
+ ],
81
+ desc: 'Set the main body of the issue here, only used in creation, use the field function to update'
82
+ )
83
+ def description(text)
84
+ ticket.description = text
85
+ end
86
+
87
+ RoboPigeon::Documentarian.add_command(
88
+ 'issuetype',
89
+ block: %w[job jira ticket],
90
+ params: [
91
+ { name: 'type', type: 'String', desc: 'name of the issue type', example: 'Story' }
92
+ ],
93
+ desc: 'sets the issue type to use when creating a ticket'
94
+ )
95
+ def issuetype(type)
96
+ ticket.set_issuetype type
97
+ end
98
+
99
+ RoboPigeon::Documentarian.add_command(
100
+ 'create!',
101
+ block: %w[job jira ticket],
102
+ desc: 'creates the configured ticket and returns the id'
103
+ )
104
+ def create!
105
+ ticket.create!
106
+ ticket.ticket
107
+ end
108
+
109
+ RoboPigeon::Documentarian.add_command(
110
+ 'field',
111
+ block: %w[job jira ticket],
112
+ params: [
113
+ { name: 'field_name', type: 'String', desc: 'the name of the field you want to set', example: 'duedate' },
114
+ { name: 'field_value', type: 'String', desc: 'what to set that field to', example: '2/10/2030' }
115
+ ],
116
+ desc: 'set or update a field to a value'
117
+ )
118
+ def field(name, value)
119
+ ticket.set_field(name, value)
120
+ end
121
+
122
+ RoboPigeon::Documentarian.add_command(
123
+ 'print_id',
124
+ block: %w[job jira ticket],
125
+ desc: 'print the ticket number to standard out (only for already created tickets)'
126
+ )
127
+ def print_id
128
+ puts ticket.ticket.to_s
129
+ end
130
+
131
+ RoboPigeon::Documentarian.add_command(
132
+ 'transition',
133
+ block: %w[job jira ticket],
134
+ params: [
135
+ { name: 'transition_name', type: 'String', desc: 'the name of the transition to run', example: 'Ready' }
136
+ ],
137
+ desc: 'transition a ticket to a new state'
138
+ )
139
+ def transition(name)
140
+ ticket.perform_transition(name)
141
+ end
142
+
143
+ RoboPigeon::Documentarian.add_command(
144
+ 'wait_for_state',
145
+ block: %w[job jira ticket],
146
+ params: [
147
+ { name: 'state_name', type: 'String', desc: 'the name of the state to wait for', example: 'Approved' }
148
+ ],
149
+ desc: 'block execution waiting for a state transition'
150
+ )
151
+ def wait_for_state(transition_state)
152
+ ticket.wait_for_state!(transition_state)
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,37 @@
1
+ RoboPigeon::Documentarian.add_block('jira', helpers: true, block: [], desc: 'a configuration block for jira')
2
+ RoboPigeon::Documentarian.add_block('jira', helpers: true, block: ['job'], desc: 'configure a jira ticket manipulation, sends on end, can have multiple')
3
+
4
+ require 'robopigeon/jira/client'
5
+ require 'robopigeon/jira/helper_dsl'
6
+ require 'robopigeon/jira/dsl'
7
+ require 'robopigeon/jira/ticket'
8
+ require 'robopigeon/jira/ticket_dsl'
9
+
10
+ module RoboPigeon::Jira
11
+ class TicketNotFoundOrSet < StandardError; end
12
+ class RequiredFieldNotSet < StandardError; end
13
+ class FieldDoesNotConform < StandardError; end
14
+ class WaitTimeout < StandardError; end
15
+ end
16
+
17
+ module RoboPigeon::Dsl
18
+ module Helpers
19
+ include RoboPigeon::Dsl::Helpers::Jira
20
+ end
21
+ end
22
+
23
+ module RoboPigeon::Dsl
24
+ class Root
25
+ def jira(&block)
26
+ RoboPigeon::Dsl::JiraRoot.run(&block)
27
+ end
28
+ end
29
+ end
30
+
31
+ module RoboPigeon::Dsl
32
+ class Job
33
+ def jira(&block)
34
+ RoboPigeon::Dsl::Jira.run(&block)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,73 @@
1
+ require 'erb'
2
+ require 'markdown2confluence'
3
+ require 'kramdown/converter/slack'
4
+
5
+ module RoboPigeon::Dsl
6
+ module Helpers
7
+ module Markdown
8
+ RoboPigeon::Documentarian.add_command(
9
+ 'confluence_from_md',
10
+ params: [
11
+ { name: 'file', type: 'String', desc: 'path to a markdown erb file', example: 'config/wiki-page-template.erb.md' }
12
+ ],
13
+ block: ['helpers'],
14
+ desc: 'get jira/confluence markup from an erb markdown template'
15
+ )
16
+ def confluence_from_md(file)
17
+ from_md_template(file).to_confluence
18
+ end
19
+
20
+ RoboPigeon::Documentarian.add_command(
21
+ 'jira_from_md',
22
+ params: [
23
+ { name: 'file', type: 'String', desc: 'path to a markdown erb file', example: 'config/issue-template.erb.md' }
24
+ ],
25
+ block: ['helpers'],
26
+ desc: 'get jira/confluence markup from an erb markdown template'
27
+ )
28
+ def jira_from_md(file)
29
+ from_md_template(file).to_confluence
30
+ end
31
+
32
+ RoboPigeon::Documentarian.add_command(
33
+ 'html_from_md',
34
+ params: [
35
+ { name: 'file', type: 'String', desc: 'path to a markdown erb file', example: 'config/results-template.erb.md' }
36
+ ],
37
+ block: ['helpers'],
38
+ desc: 'get html markup from an erb markdown template'
39
+ )
40
+ def html_from_md(file)
41
+ from_md_template(file).to_html
42
+ end
43
+
44
+ RoboPigeon::Documentarian.add_command(
45
+ 'slack_from_md',
46
+ params: [
47
+ { name: 'file', type: 'String', desc: 'path to a markdown erb file', example: 'config/message-template.erb.md' }
48
+ ],
49
+ block: ['helpers'],
50
+ desc: 'get slack markup from an erb markdown template'
51
+ )
52
+ def slack_from_md(file)
53
+ from_md_template(file).to_slack
54
+ end
55
+
56
+ private
57
+
58
+ def from_md_template(file)
59
+ raise RoboPigeon::Markdown::Error, "template file #{file} not found" unless File.exist?(file)
60
+
61
+ raw = File.read(file)
62
+ markdown = ERB.new(raw).result(binding)
63
+ Kramdown::Document.new(markdown,
64
+ input: 'GFM',
65
+ syntax_highlighter: 'coderay',
66
+ syntax_highlighter_opts: {
67
+ css: 'style',
68
+ line_numbers: 'table'
69
+ })
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,11 @@
1
+ require 'robopigeon/markdown/helper_dsl'
2
+
3
+ module RoboPigeon::Markdown
4
+ class Error < StandardError; end
5
+ end
6
+
7
+ module RoboPigeon::Dsl
8
+ module Helpers
9
+ include RoboPigeon::Dsl::Helpers::Markdown
10
+ end
11
+ end
@@ -0,0 +1,156 @@
1
+ slack do
2
+ # Slack is currently disabled, make sure you have a valid bot token, then remove
3
+ # the following line
4
+ enabled false
5
+ # This is where you configure your global slack preferences, like bot name and api key
6
+ # You should always keep the api key either encrypted with something like `encryptatron`
7
+ # or in a ci environment variable (which is probably better)
8
+ api_key ENV['SLACK_API_KEY']
9
+ name 'RoboPigeon'
10
+ end
11
+
12
+ gitlab do
13
+ # GitLab is currently disabled, make sure you have a valid api token with acces to
14
+ # to your repo, then remove the following line
15
+ enabled false
16
+ # This is where you configure your global gitlab settings, like api url and api key
17
+ # always keep your api key either in an environment variable or encrypted with something
18
+ # something like `encryptatron` or rails secrets.
19
+ api_url ENV['CI_API_V4_URL']
20
+ api_key ENV['GITLAB_API_KEY'] # Always use an envrionment variable, don't check in secrets.
21
+ end
22
+
23
+ jira do
24
+ # Jira is currently disabled, make sure you have a valid api token with acces to
25
+ # to your repo, then remove the following line
26
+ enabled false
27
+ # This is where you configure your global jira settings, like api url and api token
28
+ # always keep your api key either in an environment variable or encrypted with
29
+ # something like `encryptatron` or rails secrets.
30
+ api_url ENV['JIRA_API_URL']
31
+ api_key ENV['JIRA_API_TOKEN'] # Always use an envrionment variable, don't check in secrets.
32
+ end
33
+
34
+ job 'deploy_complete', 'Notify slack and comment in gitlab that a deploy is complete' do
35
+ # This configures a slack notification, at the end of the block it sends to
36
+ # the users and channels in the body. Un-comment the channels and fill in the
37
+ # appropriate ones to have them sent there.
38
+ slack do
39
+ # channel '#your-team-channel'
40
+ # channel '#your-release-notifications-channel'
41
+ user ENV['GITLAB_USER_EMAIL']
42
+ # `git_committer_email` looks at the commit log and returns the email address
43
+ # for the last person to make a non-merge commit
44
+ user git_committer_email
45
+ # `git_merger_email` looks at the commit log and returns the email address
46
+ # for the last person to make a merge commit
47
+ user git_merger_email
48
+ # This configures the overall message to send, it comes first, before attachments.
49
+ # If you want to send multiple messages, you can use attachments with pretext in them
50
+ # It's not actually required to that you set a message in order to send attachments
51
+ message "#{slack_user_for(ENV['GITLAB_USER_EMAIL'])} deployed #{ENV['CI_PROJECT_NAME']} #{ENV['CI_COMMIT_TAG']} to #{ENV['CI_ENVIRONMENT_NAME']}"
52
+ attachment do
53
+ # You have to set a fallback text for the actions
54
+ fallback "<#{ENV['CI_PIPELINE_URL']}|Pipeline>"
55
+ # Actions show up as little buttons under the message, this one will be
56
+ # a green button that links to the pipeline.
57
+ action 'button', 'Pipeline', ENV['CI_PIPELINE_URL'], 'primary'
58
+ action 'button', 'Rollback', "#{ENV['CI_PROJECT_URL']}/environments", 'danger'
59
+ color 'good'
60
+ end
61
+ end
62
+
63
+ # This configures an action taken in gitlab. In this case, we're saying to use
64
+ # the last branch merged into this one as the branch name, and then comment on
65
+ # each merge request with that branch that someone has started deploying
66
+ gitlab do
67
+ merge_request_comment "#{ENV['GITLAB_USER_EMAIL']} deployed branch", git_branch_merged_source
68
+ end
69
+
70
+ # This configures an action taken in jira, In this case, we're looping over all
71
+ # of the tickets since the last deployment, and then adding a comment to them
72
+ # with what version it was released in.
73
+ jira do
74
+ # This helper gets all of the tickets referenced in the git log since the last
75
+ # deployment to the referenced environment, that contain the provided key
76
+ tickets_in_log_since_deployment_to('production', 'TIC').each do |ticket_id|
77
+ # Then it comments on them with the project that was deployed and the version
78
+ ticket ticket_id do
79
+ # Then it comments on them with the project that was deployed and the version
80
+ comment "#{ENV['CI_PROJECT_NAME']} had changes related to this deployed to production in version #{ENV['CI_COMMIT_TAG']}"
81
+ # Runs a transition called 'Release'
82
+ transition 'Release'
83
+ # and sets the fix version to the tag of the deployment
84
+ field 'Fix Version', ENV['CI_COMMIT_TAG']
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ # This example job creates a ticket, assignes it to whoever runs the pipeline, and
91
+ # then sends them a slack message with the id of the ticket
92
+ job 'create ticket' do
93
+ jira do
94
+ ticket do
95
+ # Set the project key
96
+ project 'PIG'
97
+ # Set the issue type
98
+ issuetype 'Bug'
99
+ # Set the summary of the ticket
100
+ summary 'Detecetd a defect! Fix it!'
101
+ # Assign the ticket to the user who is running the pipeline
102
+ assign ENV['GITLAB_USER_EMAIL']
103
+ # Set a description from a file template, that template can
104
+ # use any of the other dsl helpers like the git or deployment
105
+ # operations
106
+ description jira_from_md('path/to/file.erb.md')
107
+ # Create the ticket and store the id in a global variable
108
+ create!
109
+ # Print out the ticket number for logging/deubgging
110
+ puts 'Created ticket:'
111
+ print_id
112
+ end
113
+ end
114
+
115
+ slack do
116
+ user ENV['GITLAB_USER_EMAIL']
117
+ message "Created #{jira_last_created_ticket_slack_link}"
118
+ end
119
+ end
120
+
121
+ # Below are a number of pre-configured jobs that some people find useful!
122
+ job 'deploy_started', 'Notfiy slack and comment in gitlab that a deploy is starting' do
123
+ slack do
124
+ user ENV['GITLAB_USER_EMAIL']
125
+ user git_committer_email
126
+ user git_merger_email
127
+ message "#{slack_user_for(ENV['GITLAB_USER_EMAIL'])} deployed #{ENV['CI_PROJECT_NAME']} #{ENV['CI_COMMIT_TAG']} to #{ENV['CI_ENVIRONMENT_NAME']}"
128
+ attachment do
129
+ fallback "<#{ENV['CI_PIPELINE_URL']}|Pipeline>"
130
+ action 'button', 'Pipeline', ENV['CI_PIPELINE_URL'], 'primary'
131
+ color 'good'
132
+ end
133
+ end
134
+
135
+ gitlab do
136
+ merge_request_comment "#{ENV['GITLAB_USER_EMAIL']} has started deyploying this branch", git_branch_merged_source
137
+ end
138
+ end
139
+
140
+ job 'notify_failure', 'Notify slack and gitlab that a job has failed' do
141
+ slack do
142
+ # channel '#your-team-channel'
143
+ user ENV['GITLAB_USER_EMAIL']
144
+ user git_committer_email
145
+ attachment do
146
+ fallback "#{ENV['CI_PROJECT_NAME']} - #{ENV['CI_COMMIT_REF_NAME']} has a failure - <#{ENV['CI_PIPELINE_URL']}|Pipeline>"
147
+ title "#{ENV['CI_PROJECT_NAME']} - #{ENV['CI_COMMIT_REF_NAME']} has a failure"
148
+ action 'button', 'Pipeline', ENV['CI_PIPELINE_URL'], 'primary'
149
+ color 'danger'
150
+ end
151
+ end
152
+
153
+ gitlab do
154
+ merge_request_comment "The branch from this merge request has a failure - ENV['CI_PIPELINE_URL']", git_branch_merged_source
155
+ end
156
+ end
@@ -0,0 +1,63 @@
1
+ module RoboPigeon::Dsl
2
+ class SlackAttachment
3
+ include RoboPigeon::Dsl::Helpers
4
+ attr_accessor :attachment
5
+
6
+ def initialize
7
+ self.attachment = { actions: [] }
8
+ end
9
+
10
+ def self.run(&block)
11
+ attachment = RoboPigeon::Dsl::SlackAttachment.new
12
+ attachment.instance_eval(&block)
13
+ attachment.attachment
14
+ end
15
+
16
+ private
17
+
18
+ RoboPigeon::Documentarian.add_command('fallback', block: %w[job slack attachment], params: [{ name: 'text', type: 'String', desc: 'a string message', example: 'This message is displayed when the client does not support attachments' }], desc: 'Add a fallback message for clients that do not support actions')
19
+ def fallback(text)
20
+ attachment[:fallback] = text
21
+ end
22
+
23
+ RoboPigeon::Documentarian.add_command('title', block: %w[job slack attachment], params: [{ name: 'title', type: 'String', desc: 'the title of the attachment', example: 'Job Failed!' }], desc: 'Add a title to your attachment')
24
+ def title(title)
25
+ attachment[:title] = title
26
+ end
27
+
28
+ RoboPigeon::Documentarian.add_command('title_linke', block: %w[job slack attachment], params: [{ name: 'link', type: 'String', desc: 'a url', example: 'https://gitlab.com/robopigeon/robopigeon/pipelines' }], desc: 'Add a link to the title so that users can click on it')
29
+ def title_link(title_link)
30
+ attachment[:title_link] = title_link
31
+ end
32
+
33
+ RoboPigeon::Documentarian.add_command('pretext', block: %w[job slack attachment], params: [{ name: 'text', type: 'String', desc: 'a string message', example: 'This message shows up before rest of the attachment' }], desc: 'Add a message that shows up just before your attachment')
34
+ def pretext(text)
35
+ attachment[:pretext] = text
36
+ end
37
+
38
+ RoboPigeon::Documentarian.add_command('color', block: %w[job slack attachment], params: [{ name: 'color', type: 'String', desc: 'a color, hex or something', example: 'danger' }], desc: 'Change the color of the bar next to your attachment')
39
+ def color(color)
40
+ attachment[:color] = color
41
+ end
42
+
43
+ RoboPigeon::Documentarian.add_command(
44
+ 'action',
45
+ block: %w[job slack attachment],
46
+ params: [
47
+ { name: 'type', type: 'String', desc: 'Currently only supports `button`', example: 'button' },
48
+ { name: 'text', type: 'String', desc: 'Field label', example: 'Pipeline' },
49
+ { name: 'url', type: 'String', desc: 'Url to link to', example: 'https://gitlab.com/robopigeon/robopigeon/pipelines' },
50
+ { name: 'style', type: 'String', desc: 'button style, can be danger or primary', example: 'danger', defaut: 'nil' }
51
+ ],
52
+ desc: 'Add a Url linked button to your bot'
53
+ )
54
+ def action(type, text, url, style=nil)
55
+ attachment[:actions].push(
56
+ type: type,
57
+ text: text,
58
+ url: url,
59
+ style: style
60
+ )
61
+ end
62
+ end
63
+ end