danger 0.6.5 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|