danger 0.6.5 → 0.7.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 +4 -4
- data/README.md +47 -2
- data/lib/assets/PluginTemplate.rb.template +20 -0
- data/lib/danger.rb +1 -0
- data/lib/danger/ci_source/local_git_repo.rb +8 -6
- data/lib/danger/commands/init_helpers/interviewer.rb +1 -1
- data/lib/danger/commands/local.rb +2 -1
- data/lib/danger/commands/new_plugin.rb +43 -0
- data/lib/danger/commands/runner.rb +4 -1
- data/lib/danger/comment_generators/github.md.erb +21 -4
- data/lib/danger/core_ext/string.rb +13 -0
- data/lib/danger/dangerfile.rb +30 -1
- data/lib/danger/dangerfile_dsl.rb +90 -14
- data/lib/danger/plugins/plugin.rb +26 -0
- data/lib/danger/plugins/protect_files.rb +24 -13
- data/lib/danger/request_sources/github.rb +80 -18
- data/lib/danger/scm_source/file_list.rb +12 -0
- data/lib/danger/scm_source/git_repo.rb +3 -3
- data/lib/danger/standard_error.rb +1 -1
- data/lib/danger/version.rb +2 -2
- data/lib/danger/violation.rb +10 -0
- metadata +11 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f405c58b164cfb051f33ce62eff96d6954ccedf8
|
4
|
+
data.tar.gz: e487a8349af60005ce0f63ec429cd8e73a561055
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ce00623811798d3bf0cb0e77bc788bc0c99fac1045d00a33cf3c68b297983e669de41544bae3f8310257e3a34a3e8704aedb2d9f9fc2064e4f0a3e443cdcbc5
|
7
|
+
data.tar.gz: e0cdd1d536430c45226a3638ff0a2cd659db62a19e643c931230961e00967e7705321ec561c2afd276c0e73f6570d2335f7126b82017be95f5dcd0de18d9c906
|
data/README.md
CHANGED
@@ -67,14 +67,14 @@ The `Dangerfile` is a ruby file, so really, you can do anything. However, at thi
|
|
67
67
|
declared_trivial = pr_title.include? "#trivial"
|
68
68
|
|
69
69
|
# Just to let people know
|
70
|
-
warn("PR is classed as Work in Progress") if pr_title.include? "[WIP]"
|
70
|
+
warn("PR is classed as Work in Progress", sticky: false) if pr_title.include? "[WIP]"
|
71
71
|
```
|
72
72
|
|
73
73
|
#### Being cautious around specific files
|
74
74
|
|
75
75
|
``` ruby
|
76
76
|
# Devs shouldn't ship changes to this file
|
77
|
-
fail("Developer Specific file shouldn't be changed") if modified_files.include?("Artsy/View_Controllers/App_Navigation/ARTopMenuViewController+DeveloperExtras.m")
|
77
|
+
fail("Developer Specific file shouldn't be changed", sticky: false) if modified_files.include?("Artsy/View_Controllers/App_Navigation/ARTopMenuViewController+DeveloperExtras.m")
|
78
78
|
|
79
79
|
# Did you make analytics changes? Well you should also include a change to our analytics spec
|
80
80
|
made_analytics_changes = modified_files.include?("/Artsy/App/ARAppDelegate+Analytics.m")
|
@@ -102,6 +102,40 @@ snapshots_url = build_log.match(%r{https://eigen-ci.s3.amazonaws.com/\d+/index.h
|
|
102
102
|
fail("There were [snapshot errors](#{snapshots_url})") if snapshots_url
|
103
103
|
```
|
104
104
|
|
105
|
+
#### Available commands
|
106
|
+
|
107
|
+
Command | Description
|
108
|
+
------------- | ----
|
109
|
+
`fail` | Causes the PR to fail and print out the error on the PR
|
110
|
+
`warn` | Prints out a warning to the PR, but still enables the merge button
|
111
|
+
`message` | Show neutral messages on the PR
|
112
|
+
`markdown` | Print raw markdown below the summary tables on the PR
|
113
|
+
|
114
|
+
## Plugins
|
115
|
+
|
116
|
+
Danger was built with a platform in mind: It can be used with any kind of software project and allows you to write your own action to have structured source code.
|
117
|
+
|
118
|
+
In your `Dangerfile` you can import local or remote actions using
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
import "./danger_plugins/work_in_progress_warning"
|
122
|
+
# or
|
123
|
+
import "https://raw.githubusercontent.com/danger/danger/master/danger_plugins/work_in_progress_warning.rb"
|
124
|
+
|
125
|
+
# Call those actions using
|
126
|
+
work_in_progress_warning
|
127
|
+
|
128
|
+
custom_plugin(variable: "value")
|
129
|
+
```
|
130
|
+
|
131
|
+
To create a new plugin run
|
132
|
+
|
133
|
+
```
|
134
|
+
danger new_plugin
|
135
|
+
```
|
136
|
+
|
137
|
+
This will generate a new Ruby file which you can modify to fit your needs.
|
138
|
+
|
105
139
|
## Support
|
106
140
|
|
107
141
|
Danger currently is supported on Travis CI, Circle CI, BuildKite and Jenkins. These work via environment variables, so it's easy to extend to include your own.
|
@@ -132,6 +166,15 @@ You can tell Danger to ignore a specific warning or error by commenting on the P
|
|
132
166
|
> Danger: Ignore "Developer Specific file shouldn't be changed"
|
133
167
|
```
|
134
168
|
|
169
|
+
## Sticky
|
170
|
+
|
171
|
+
Danger can keep its history if a warning/error/message is marked as *sticky*. When the violation is resolved,
|
172
|
+
Danger will update the comment to cross it out. If you don't want this behavior, just use `sticky: false`.
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
fail("PR needs labels", sticky: false) if pr_labels.empty?
|
176
|
+
```
|
177
|
+
|
135
178
|
## Useful bits of knowledge
|
136
179
|
|
137
180
|
* You can set the base branch in the command line arguments see: `bundle exec danger --help`, if you commonly merge into non-master branches.
|
@@ -141,6 +184,8 @@ Here are some real-world Dangerfiles: [artsy/eigen](https://github.com/artsy/eig
|
|
141
184
|
|
142
185
|
## License, Contributor's Guidelines and Code of Conduct
|
143
186
|
|
187
|
+
[Join our Slack Group](https://danger-slack.herokuapp.com/)
|
188
|
+
|
144
189
|
> This project is open source under the MIT license, which means you have full access to the source code and can modify it to fit your own needs.
|
145
190
|
|
146
191
|
> This project subscribes to the [Moya Contributors Guidelines](https://github.com/Moya/contributors) which TLDR: means we give out push access easily and often.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Danger
|
2
|
+
class Dangerfile
|
3
|
+
module DSL
|
4
|
+
class [[CLASS_NAME]] < Plugin
|
5
|
+
def run(parameter1: nil, parameter2: nil)
|
6
|
+
if (pr_body + pr_title).include?("WIP")
|
7
|
+
warn "Pull Request is Work in Progress"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.description
|
12
|
+
[
|
13
|
+
"Describe what this plugin does",
|
14
|
+
"and how the user can use it"
|
15
|
+
].join(" ")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/danger.rb
CHANGED
@@ -36,13 +36,15 @@ module Danger
|
|
36
36
|
|
37
37
|
# get the most recent PR merge
|
38
38
|
pr_merge = run_git "log --since='2 weeks ago' --merges --oneline | grep \"Merge pull request\" | head -n 1".strip
|
39
|
-
if pr_merge
|
40
|
-
|
41
|
-
sha = pr_merge.split(" ")[0]
|
42
|
-
parents = run_git("rev-list --parents -n 1 #{sha}").strip.split(" ")
|
43
|
-
self.base_commit = parents[0]
|
44
|
-
self.head_commit = parents[1]
|
39
|
+
if pr_merge.to_s.empty?
|
40
|
+
raise "No recent pull requests found for this repo, danger requires at least one PR for the local mode"
|
45
41
|
end
|
42
|
+
|
43
|
+
self.pull_request_id = pr_merge.match("#([0-9]+)")[1]
|
44
|
+
sha = pr_merge.split(" ")[0]
|
45
|
+
parents = run_git("rev-list --parents -n 1 #{sha}").strip.split(" ")
|
46
|
+
self.base_commit = parents[0]
|
47
|
+
self.head_commit = parents[1]
|
46
48
|
end
|
47
49
|
end
|
48
50
|
end
|
@@ -48,7 +48,8 @@ module Danger
|
|
48
48
|
dm.env.scm = GitRepo.new
|
49
49
|
|
50
50
|
dm.env.scm.diff_for_folder(".", from: dm.env.ci_source.base_commit, to: dm.env.ci_source.head_commit)
|
51
|
-
dm.parse
|
51
|
+
dm.parse(Pathname.new(@dangerfile_path))
|
52
|
+
dm.print_results
|
52
53
|
end
|
53
54
|
end
|
54
55
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Danger
|
2
|
+
class NewPlugin < Runner
|
3
|
+
self.summary = 'Generate a new danger plugin.'
|
4
|
+
self.command = 'new_plugin'
|
5
|
+
|
6
|
+
def initialize(argv)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate!
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
require 'fileutils'
|
16
|
+
|
17
|
+
puts "Must be lower case, and use a '_' between words. Do not use '.'".green
|
18
|
+
puts "examples: 'number_of_emojis', 'ensure_pr_title_contains_keyword'".green
|
19
|
+
puts "Name of your new plugin: "
|
20
|
+
name = STDIN.gets.strip
|
21
|
+
|
22
|
+
dir = Danger.gem_path
|
23
|
+
content = File.read(File.join(dir, "lib", "assets", "PluginTemplate.rb.template"))
|
24
|
+
content.gsub!("[[CLASS_NAME]]", name.danger_class)
|
25
|
+
|
26
|
+
plugins_path = "danger_plugins"
|
27
|
+
FileUtils.mkdir_p("plugins_path") unless File.directory?(plugins_path)
|
28
|
+
|
29
|
+
output_path = File.join(plugins_path, "#{name}.rb")
|
30
|
+
raise "File '#{output_path}' already exists!" if File.exist?(output_path)
|
31
|
+
File.write(output_path, content)
|
32
|
+
|
33
|
+
puts ""
|
34
|
+
puts "Successfully created new plugin at path '#{output_path}'".green
|
35
|
+
puts "Add this to your `Dangerfile` to use it:"
|
36
|
+
puts ""
|
37
|
+
puts "import \"#{output_path.gsub('.rb', '')}\"".blue
|
38
|
+
puts "#{name}(parameter1: 123, parameter2: \"Club Mate\")".blue
|
39
|
+
puts ""
|
40
|
+
puts "Enjoy ✨"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -2,6 +2,7 @@ module Danger
|
|
2
2
|
class Runner < CLAide::Command
|
3
3
|
require 'danger/commands/init'
|
4
4
|
require 'danger/commands/local'
|
5
|
+
require 'danger/commands/new_plugin'
|
5
6
|
|
6
7
|
self.summary = 'Run the Dangerfile.'
|
7
8
|
self.command = 'danger'
|
@@ -47,11 +48,13 @@ module Danger
|
|
47
48
|
post_results(dm)
|
48
49
|
|
49
50
|
dm.env.clean_up
|
51
|
+
|
52
|
+
dm.print_results
|
50
53
|
end
|
51
54
|
|
52
55
|
def post_results(dm)
|
53
56
|
gh = dm.env.request_source
|
54
|
-
gh.update_pull_request!(warnings: dm.warnings, errors: dm.errors, messages: dm.messages)
|
57
|
+
gh.update_pull_request!(warnings: dm.warnings, errors: dm.errors, messages: dm.messages, markdowns: dm.markdowns)
|
55
58
|
end
|
56
59
|
end
|
57
60
|
end
|
@@ -1,24 +1,41 @@
|
|
1
1
|
<% @tables.each do |table| %>
|
2
|
-
<% if table[:content].any? %>
|
2
|
+
<% if table[:content].any? || table[:resolved].any? %>
|
3
3
|
<table>
|
4
4
|
<thead>
|
5
5
|
<tr>
|
6
6
|
<th width="50"></th>
|
7
|
-
<th width="100%"
|
7
|
+
<th width="100%" data-kind="<%= table[:name] %>">
|
8
|
+
<% if table[:count] > 0 %>
|
9
|
+
<%= table[:count] %> <%= table[:name] %><%= "s" unless table[:count] == 1 %>
|
10
|
+
<% else %>
|
11
|
+
:white_check_mark: <%= random_compliment %>
|
12
|
+
<% end %>
|
13
|
+
</th>
|
8
14
|
</tr>
|
9
15
|
</thead>
|
10
16
|
<tbody>
|
11
|
-
<% table[:content].each do |
|
17
|
+
<% table[:content].each do |violation| -%>
|
12
18
|
<tr>
|
13
19
|
<td>:<%= table[:emoji] %>:</td>
|
14
|
-
<td><%= message %></td>
|
20
|
+
<td data-sticky="<%= violation.sticky %>"><%= violation.message %></td>
|
15
21
|
</tr>
|
16
22
|
<% end %>
|
23
|
+
<% table[:resolved].each do |message| -%>
|
24
|
+
<tr>
|
25
|
+
<td>:white_check_mark:</td>
|
26
|
+
<td data-sticky="true"><del><%= message %></del></td>
|
27
|
+
</tr>
|
28
|
+
<% end %>
|
17
29
|
</tbody>
|
18
30
|
</table>
|
19
31
|
<% end %>
|
20
32
|
<% end %>
|
21
33
|
|
34
|
+
<% @markdowns.each do |current| %>
|
35
|
+
<%= current %>
|
36
|
+
<%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
|
37
|
+
<% end %>
|
38
|
+
|
22
39
|
<p align="right" data-meta="generated_by_danger" data-base-commit="<%= @base_commit %>" data-head-commit="<%= @head_commit %>" >
|
23
40
|
Generated by :no_entry_sign: <a href="https://github.com/danger/danger/">danger</a>
|
24
41
|
</p>
|
data/lib/danger/dangerfile.rb
CHANGED
@@ -7,7 +7,7 @@ module Danger
|
|
7
7
|
class Dangerfile
|
8
8
|
include Danger::Dangerfile::DSL
|
9
9
|
|
10
|
-
attr_accessor :env, :warnings, :errors, :messages, :verbose
|
10
|
+
attr_accessor :env, :warnings, :errors, :messages, :markdowns, :verbose
|
11
11
|
|
12
12
|
# @return [Pathname] the path where the Dangerfile was loaded from. It is nil
|
13
13
|
# if the Dangerfile was generated programmatically.
|
@@ -90,5 +90,34 @@ module Danger
|
|
90
90
|
# rubocop:enable Lint/RescueException
|
91
91
|
end
|
92
92
|
end
|
93
|
+
|
94
|
+
def print_results
|
95
|
+
return if (self.errors + self.warnings + self.messages + self.markdowns).count == 0
|
96
|
+
|
97
|
+
puts ""
|
98
|
+
puts "danger results:"
|
99
|
+
[:errors, :warnings, :messages].each do |current|
|
100
|
+
params = {}
|
101
|
+
params[:rows] = self.send(current).collect { |a| [a.message] }
|
102
|
+
next unless params[:rows].count > 0
|
103
|
+
params[:title] = case current
|
104
|
+
when :errors
|
105
|
+
current.to_s.capitalize.red
|
106
|
+
when :warnings
|
107
|
+
current.to_s.capitalize.yellow
|
108
|
+
else
|
109
|
+
current.to_s.capitalize
|
110
|
+
end
|
111
|
+
|
112
|
+
puts ""
|
113
|
+
puts Terminal::Table.new(params)
|
114
|
+
puts ""
|
115
|
+
end
|
116
|
+
|
117
|
+
puts "Markdown: ".green if self.markdowns.count > 0
|
118
|
+
self.markdowns.each do |current_markdown|
|
119
|
+
puts current_markdown
|
120
|
+
end
|
121
|
+
end
|
93
122
|
end
|
94
123
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'danger/violation'
|
2
|
+
|
1
3
|
module Danger
|
2
4
|
class Dangerfile
|
3
5
|
module DSL
|
@@ -13,12 +15,47 @@ module Danger
|
|
13
15
|
self.warnings = []
|
14
16
|
self.errors = []
|
15
17
|
self.messages = []
|
16
|
-
|
18
|
+
self.markdowns = []
|
19
|
+
|
20
|
+
load_default_plugins
|
21
|
+
end
|
22
|
+
|
23
|
+
def import(path)
|
24
|
+
raise "`import` requires a string" unless path.kind_of?(String)
|
25
|
+
path += ".rb" unless path.end_with?(".rb")
|
26
|
+
|
27
|
+
if path.start_with?("http")
|
28
|
+
import_url(path)
|
29
|
+
else
|
30
|
+
import_local(path)
|
31
|
+
end
|
17
32
|
end
|
18
33
|
|
19
|
-
|
20
|
-
|
21
|
-
|
34
|
+
# Download a remote plugin and use it locally
|
35
|
+
#
|
36
|
+
# @param [String] url
|
37
|
+
# https URL to the Ruby file to use
|
38
|
+
def import_url(url)
|
39
|
+
raise "URL is not https, for security reasons `danger` only supports encrypted requests" unless url.start_with?("https://")
|
40
|
+
|
41
|
+
require 'tmpdir'
|
42
|
+
require 'faraday'
|
43
|
+
content = Faraday.get(url)
|
44
|
+
Dir.mktmpdir do |dir|
|
45
|
+
path = File.join(dir, "temporary_remote_action.rb")
|
46
|
+
File.write(path, content.body)
|
47
|
+
import_local(path)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Import one or more local plugins
|
52
|
+
#
|
53
|
+
# @param [String] path
|
54
|
+
# The path to the file to import
|
55
|
+
# Can also be a pattern (./**/*plugin.rb)
|
56
|
+
def import_local(path)
|
57
|
+
Dir[path].each do |file|
|
58
|
+
require File.expand_path(file) # without the expand_path it would fail if the path doesn't start with ./
|
22
59
|
end
|
23
60
|
end
|
24
61
|
|
@@ -30,9 +67,11 @@ module Danger
|
|
30
67
|
#
|
31
68
|
# @param [String] message
|
32
69
|
# The message to present to the user
|
33
|
-
|
70
|
+
# @param [Boolean] sticky
|
71
|
+
# Whether the message should be kept after it was fixed
|
72
|
+
def fail(message, sticky: true)
|
34
73
|
return if should_ignore_violation(message)
|
35
|
-
self.errors << message
|
74
|
+
self.errors << Violation.new(message, sticky)
|
36
75
|
puts "Raising error '#{message}'"
|
37
76
|
end
|
38
77
|
|
@@ -40,9 +79,11 @@ module Danger
|
|
40
79
|
#
|
41
80
|
# @param [String] message
|
42
81
|
# The message to present to the user
|
43
|
-
|
82
|
+
# @param [Boolean] sticky
|
83
|
+
# Whether the message should be kept after it was fixed
|
84
|
+
def warn(message, sticky: true)
|
44
85
|
return if should_ignore_violation(message)
|
45
|
-
self.warnings << message
|
86
|
+
self.warnings << Violation.new(message, sticky)
|
46
87
|
puts "Printing warning '#{message}'"
|
47
88
|
end
|
48
89
|
|
@@ -50,26 +91,61 @@ module Danger
|
|
50
91
|
#
|
51
92
|
# @param [String] message
|
52
93
|
# The message to present to the user
|
53
|
-
|
54
|
-
|
94
|
+
# @param [Boolean] sticky
|
95
|
+
# Whether the message should be kept after it was fixed
|
96
|
+
def message(message, sticky: true)
|
97
|
+
self.messages << Violation.new(message, sticky)
|
55
98
|
puts "Printing message '#{message}'"
|
56
99
|
end
|
57
100
|
|
101
|
+
# Print markdown to below the table
|
102
|
+
#
|
103
|
+
# @param [String] message
|
104
|
+
# The markdown based message to be printed below the table
|
105
|
+
def markdown(message)
|
106
|
+
self.markdowns << message
|
107
|
+
puts "Printing markdown #{message}"
|
108
|
+
end
|
109
|
+
|
58
110
|
# When an undefined method is called, we check to see if it's something
|
59
111
|
# that either the `scm` or the `request_source` can handle.
|
60
112
|
# This opens us up to letting those object extend themselves naturally.
|
61
|
-
|
113
|
+
# This will also look for plugins
|
114
|
+
def method_missing(method_sym, *arguments, &_block)
|
115
|
+
# SCM Source
|
62
116
|
if AvailableValues.scm.include?(method_sym)
|
63
|
-
# SCM Source
|
64
117
|
return env.scm.send(method_sym)
|
65
118
|
end
|
66
119
|
|
120
|
+
# Request Source
|
67
121
|
if AvailableValues.request_source.include?(method_sym)
|
68
|
-
# Request Source
|
69
122
|
return env.request_source.send(method_sym)
|
70
123
|
end
|
71
124
|
|
72
|
-
|
125
|
+
# Plugins
|
126
|
+
class_name = method_sym.to_s.danger_class
|
127
|
+
if Danger::Dangerfile::DSL.const_defined?(class_name)
|
128
|
+
plugin_ref = Danger::Dangerfile::DSL.const_get(class_name)
|
129
|
+
if plugin_ref < Plugin
|
130
|
+
plugin_ref.new(self).run(*arguments)
|
131
|
+
else
|
132
|
+
raise "'#{method_sym}' is not a valid danger plugin".red
|
133
|
+
end
|
134
|
+
else
|
135
|
+
raise "Unknown method '#{method_sym}', please check out the documentation for available plugins".red
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def load_default_plugins
|
142
|
+
Dir["./lib/danger/plugins/*.rb"].each do |file|
|
143
|
+
require File.expand_path(file)
|
144
|
+
end
|
145
|
+
|
146
|
+
Dir["./danger_plugins/*.rb"].each do |file|
|
147
|
+
require File.expand_path(file)
|
148
|
+
end
|
73
149
|
end
|
74
150
|
end
|
75
151
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Danger
|
2
|
+
class Dangerfile
|
3
|
+
module DSL
|
4
|
+
class Plugin
|
5
|
+
def initialize(dsl)
|
6
|
+
@dsl = dsl
|
7
|
+
end
|
8
|
+
|
9
|
+
# Since we have a reference to the DSL containing all the information
|
10
|
+
# We need to redirect the self calls to the DSL
|
11
|
+
def method_missing(method_sym, *arguments, &_block)
|
12
|
+
return @dsl.send(method_sym, *arguments) if @dsl.respond_to?(method_sym)
|
13
|
+
return @dsl.method_missing(method_sym, *arguments)
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
raise "run method must be implemented"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.description
|
21
|
+
"Add plugin description here"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,21 +1,32 @@
|
|
1
1
|
module Danger
|
2
2
|
class Dangerfile
|
3
3
|
module DSL
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
class ProtectFiles < Plugin
|
5
|
+
def run(path: nil, message: nil, fail_build: true)
|
6
|
+
raise "You have to provide a message" if message.to_s.length == 0
|
7
|
+
raise "You have to provide a path" if path.to_s.length == 0
|
8
|
+
|
9
|
+
broken_rule = false
|
10
|
+
|
11
|
+
Dir.glob(path) do |current|
|
12
|
+
broken_rule = true if self.env.scm.modified_files.include?(current)
|
13
|
+
end
|
12
14
|
|
13
|
-
|
15
|
+
return unless broken_rule
|
16
|
+
|
17
|
+
if fail_build
|
18
|
+
@dsl.errors << message
|
19
|
+
else
|
20
|
+
@dsl.warnings << message
|
21
|
+
end
|
22
|
+
end
|
14
23
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
24
|
+
def self.description
|
25
|
+
[
|
26
|
+
"Protect a file from being changed. This can",
|
27
|
+
"be used in combination with some kind of",
|
28
|
+
"permission check if a user is inside the org"
|
29
|
+
].join(" ")
|
19
30
|
end
|
20
31
|
end
|
21
32
|
end
|
@@ -28,7 +28,7 @@ module Danger
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def markdown_parser
|
31
|
-
@markdown_parser ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML)
|
31
|
+
@markdown_parser ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, no_intra_emphasis: true)
|
32
32
|
end
|
33
33
|
|
34
34
|
def fetch_details
|
@@ -77,16 +77,28 @@ module Danger
|
|
77
77
|
end
|
78
78
|
|
79
79
|
# Sending data to GitHub
|
80
|
-
def update_pull_request!(warnings:
|
80
|
+
def update_pull_request!(warnings: [], errors: [], messages: [], markdowns: [])
|
81
81
|
comment_result = {}
|
82
82
|
|
83
|
-
|
83
|
+
issues = client.issue_comments(ci_source.repo_slug, ci_source.pull_request_id)
|
84
|
+
editable_issues = issues.reject { |issue| issue[:body].include?("generated_by_danger") == false }
|
85
|
+
|
86
|
+
if editable_issues.empty?
|
87
|
+
previous_violations = {}
|
88
|
+
else
|
89
|
+
comment = editable_issues.first[:body]
|
90
|
+
previous_violations = parse_comment(comment)
|
91
|
+
end
|
92
|
+
|
93
|
+
if previous_violations.empty? && (warnings + errors + messages + markdowns).empty?
|
84
94
|
# Just remove the comment, if there's nothing to say.
|
85
95
|
delete_old_comments!
|
86
96
|
else
|
87
|
-
|
88
|
-
|
89
|
-
|
97
|
+
body = generate_comment(warnings: warnings,
|
98
|
+
errors: errors,
|
99
|
+
messages: messages,
|
100
|
+
markdowns: markdowns,
|
101
|
+
previous_violations: previous_violations)
|
90
102
|
|
91
103
|
if editable_issues.empty?
|
92
104
|
comment_result = client.add_comment(ci_source.repo_slug, ci_source.pull_request_id, body)
|
@@ -108,7 +120,7 @@ module Danger
|
|
108
120
|
message = generate_github_description(warnings: warnings, errors: errors)
|
109
121
|
client.create_status(ci_source.repo_slug, latest_pr_commit_ref, status, {
|
110
122
|
description: message,
|
111
|
-
context: "
|
123
|
+
context: "danger/danger",
|
112
124
|
target_url: details_url
|
113
125
|
})
|
114
126
|
rescue
|
@@ -133,11 +145,15 @@ module Danger
|
|
133
145
|
end
|
134
146
|
end
|
135
147
|
|
148
|
+
def random_compliment
|
149
|
+
compliment = ["Well done.", "Congrats.", "Woo!",
|
150
|
+
"Yay.", "Jolly good show.", "Good on 'ya.", "Nice work."]
|
151
|
+
compliment.sample
|
152
|
+
end
|
153
|
+
|
136
154
|
def generate_github_description(warnings: nil, errors: nil)
|
137
155
|
if errors.empty? && warnings.empty?
|
138
|
-
|
139
|
-
"Yay.", "Jolly good show.", "Good on 'ya.", "Nice work."]
|
140
|
-
return "All green. #{compliment.sample}"
|
156
|
+
return "All green. #{random_compliment}"
|
141
157
|
else
|
142
158
|
message = "⚠ "
|
143
159
|
message += "#{errors.count} Error#{errors.count == 1 ? '' : 's'}. " unless errors.empty?
|
@@ -147,7 +163,7 @@ module Danger
|
|
147
163
|
end
|
148
164
|
end
|
149
165
|
|
150
|
-
def generate_comment(warnings: [], errors: [], messages: [])
|
166
|
+
def generate_comment(warnings: [], errors: [], messages: [], markdowns: [], previous_violations: {})
|
151
167
|
require 'erb'
|
152
168
|
|
153
169
|
md_template = File.join(Danger.gem_path, "lib/danger/comment_generators/github.md.erb")
|
@@ -155,18 +171,64 @@ module Danger
|
|
155
171
|
# erb: http://www.rrn.dk/rubys-erb-templating-system
|
156
172
|
# for the extra args: http://stackoverflow.com/questions/4632879/erb-template-removing-the-trailing-line
|
157
173
|
@tables = [
|
158
|
-
|
159
|
-
|
160
|
-
|
174
|
+
table("Error", "no_entry_sign", errors, previous_violations),
|
175
|
+
table("Warning", "warning", warnings, previous_violations),
|
176
|
+
table("Message", "book", messages, previous_violations)
|
161
177
|
]
|
178
|
+
@markdowns = markdowns
|
179
|
+
|
162
180
|
return ERB.new(File.read(md_template), 0, "-").result(binding)
|
163
181
|
end
|
164
182
|
|
165
|
-
def
|
166
|
-
|
183
|
+
def table(name, emoji, violations, all_previous_violations)
|
184
|
+
content = violations.map { |v| process_markdown(v) }
|
185
|
+
kind = table_kind_from_title(name)
|
186
|
+
previous_violations = all_previous_violations[kind] || []
|
187
|
+
messages = content.map(&:message)
|
188
|
+
resolved_violations = previous_violations.reject { |s| messages.include? s }
|
189
|
+
count = content.count
|
190
|
+
{ name: name, emoji: emoji, content: content, resolved: resolved_violations, count: count }
|
191
|
+
end
|
192
|
+
|
193
|
+
def parse_comment(comment)
|
194
|
+
tables = parse_tables_from_comment(comment)
|
195
|
+
violations = {}
|
196
|
+
tables.each do |table|
|
197
|
+
next unless table =~ %r{<th width="100%"(.*?)</th>}im
|
198
|
+
title = Regexp.last_match(1)
|
199
|
+
kind = table_kind_from_title(title)
|
200
|
+
next unless kind
|
201
|
+
|
202
|
+
violations[kind] = violations_from_table(table)
|
203
|
+
end
|
204
|
+
|
205
|
+
violations.reject { |_, v| v.empty? }
|
206
|
+
end
|
207
|
+
|
208
|
+
def violations_from_table(table)
|
209
|
+
regex = %r{<td data-sticky="true">(?:<del>)?(.*?)(?:</del>)?\s*</td>}im
|
210
|
+
table.scan(regex).flatten.map(&:strip)
|
211
|
+
end
|
212
|
+
|
213
|
+
def table_kind_from_title(title)
|
214
|
+
if title =~ /error/i
|
215
|
+
:error
|
216
|
+
elsif title =~ /warning/i
|
217
|
+
:warning
|
218
|
+
elsif title =~ /message/i
|
219
|
+
:message
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def parse_tables_from_comment(comment)
|
224
|
+
comment.split('</table>')
|
225
|
+
end
|
226
|
+
|
227
|
+
def process_markdown(violation)
|
228
|
+
html = markdown_parser.render(violation.message)
|
167
229
|
match = html.match(%r{^<p>(.*)</p>$})
|
168
|
-
|
169
|
-
|
230
|
+
message = match.nil? ? html : match.captures.first
|
231
|
+
Violation.new(message, violation.sticky)
|
170
232
|
end
|
171
233
|
end
|
172
234
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Danger
|
2
|
+
class FileList < Array
|
3
|
+
# Information about pattern: http://ruby-doc.org/core-2.2.0/File.html#method-c-fnmatch
|
4
|
+
# e.g. "**/something.*" for any file called something with any extension
|
5
|
+
def include?(pattern)
|
6
|
+
self.each do |current|
|
7
|
+
return true if File.fnmatch(pattern, current)
|
8
|
+
end
|
9
|
+
return false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -16,15 +16,15 @@ module Danger
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def added_files
|
19
|
-
@diff.select { |diff| diff.type == "new" }.map(&:path)
|
19
|
+
Danger::FileList.new(@diff.select { |diff| diff.type == "new" }.map(&:path))
|
20
20
|
end
|
21
21
|
|
22
22
|
def deleted_files
|
23
|
-
@diff.select { |diff| diff.type == "deleted" }.map(&:path)
|
23
|
+
Danger::FileList.new(@diff.select { |diff| diff.type == "deleted" }.map(&:path))
|
24
24
|
end
|
25
25
|
|
26
26
|
def modified_files
|
27
|
-
@diff.stats[:files].keys
|
27
|
+
Danger::FileList.new(@diff.stats[:files].keys)
|
28
28
|
end
|
29
29
|
|
30
30
|
def lines_of_code
|
data/lib/danger/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: danger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Orta Therox
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-04-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: claide
|
@@ -185,14 +185,14 @@ dependencies:
|
|
185
185
|
requirements:
|
186
186
|
- - "~>"
|
187
187
|
- !ruby/object:Gem::Version
|
188
|
-
version: 0.
|
188
|
+
version: 0.38.0
|
189
189
|
type: :development
|
190
190
|
prerelease: false
|
191
191
|
version_requirements: !ruby/object:Gem::Requirement
|
192
192
|
requirements:
|
193
193
|
- - "~>"
|
194
194
|
- !ruby/object:Gem::Version
|
195
|
-
version: 0.
|
195
|
+
version: 0.38.0
|
196
196
|
description: Create a Dangerfile to introspect your pull request in CI, makes it easy
|
197
197
|
to enforce social conventions like changelogs and tests.
|
198
198
|
email:
|
@@ -207,6 +207,7 @@ files:
|
|
207
207
|
- README.md
|
208
208
|
- bin/danger
|
209
209
|
- lib/assets/DangerfileTemplate
|
210
|
+
- lib/assets/PluginTemplate.rb.template
|
210
211
|
- lib/danger.rb
|
211
212
|
- lib/danger/available_values.rb
|
212
213
|
- lib/danger/ci_source/buildkite.rb
|
@@ -219,16 +220,21 @@ files:
|
|
219
220
|
- lib/danger/commands/init.rb
|
220
221
|
- lib/danger/commands/init_helpers/interviewer.rb
|
221
222
|
- lib/danger/commands/local.rb
|
223
|
+
- lib/danger/commands/new_plugin.rb
|
222
224
|
- lib/danger/commands/runner.rb
|
223
225
|
- lib/danger/comment_generators/github.md.erb
|
226
|
+
- lib/danger/core_ext/string.rb
|
224
227
|
- lib/danger/dangerfile.rb
|
225
228
|
- lib/danger/dangerfile_dsl.rb
|
226
229
|
- lib/danger/environment_manager.rb
|
230
|
+
- lib/danger/plugins/plugin.rb
|
227
231
|
- lib/danger/plugins/protect_files.rb
|
228
232
|
- lib/danger/request_sources/github.rb
|
233
|
+
- lib/danger/scm_source/file_list.rb
|
229
234
|
- lib/danger/scm_source/git_repo.rb
|
230
235
|
- lib/danger/standard_error.rb
|
231
236
|
- lib/danger/version.rb
|
237
|
+
- lib/danger/violation.rb
|
232
238
|
homepage: http://github.com/danger/danger
|
233
239
|
licenses:
|
234
240
|
- MIT
|
@@ -249,9 +255,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
249
255
|
version: '0'
|
250
256
|
requirements: []
|
251
257
|
rubyforge_project:
|
252
|
-
rubygems_version: 2.
|
258
|
+
rubygems_version: 2.4.6
|
253
259
|
signing_key:
|
254
260
|
specification_version: 4
|
255
261
|
summary: Automate your PR etiquette.
|
256
262
|
test_files: []
|
257
|
-
has_rdoc:
|