danger 0.7.4 → 0.8.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 +28 -23
- data/lib/danger.rb +3 -4
- data/lib/danger/ci_source/circle.rb +1 -1
- data/lib/danger/{circle_api.rb → ci_source/circle_api.rb} +0 -0
- data/lib/danger/ci_source/drone.rb +18 -0
- data/lib/danger/ci_source/semaphore.rb +18 -0
- data/lib/danger/commands/local.rb +19 -10
- data/lib/danger/commands/runner.rb +30 -18
- data/lib/danger/comment_generators/github.md.erb +1 -1
- data/lib/danger/{scm_source → core_ext}/file_list.rb +1 -0
- data/lib/danger/danger_core/dangerfile.rb +217 -0
- data/lib/danger/danger_core/dangerfile_dsl.rb +29 -0
- data/lib/danger/{environment_manager.rb → danger_core/environment_manager.rb} +14 -6
- data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +69 -0
- data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +68 -0
- data/lib/danger/danger_core/plugins/dangerfile_import_plugin.rb +58 -0
- data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +87 -0
- data/lib/danger/{standard_error.rb → danger_core/standard_error.rb} +1 -1
- data/lib/danger/{violation.rb → danger_core/violation.rb} +0 -0
- data/lib/danger/plugin_support/plugin.rb +31 -0
- data/lib/danger/plugin_support/plugin_parser.rb +70 -0
- data/lib/danger/{request_sources → request_source}/github.rb +2 -33
- data/lib/danger/scm_source/git_repo.rb +1 -30
- data/lib/danger/version.rb +1 -1
- metadata +79 -17
- data/lib/danger/available_values.rb +0 -29
- data/lib/danger/dangerfile.rb +0 -123
- data/lib/danger/dangerfile_dsl.rb +0 -159
- data/lib/danger/plugin.rb +0 -25
- data/lib/danger/plugins/protect_files.rb +0 -34
@@ -0,0 +1,29 @@
|
|
1
|
+
module Danger
|
2
|
+
class Dangerfile
|
3
|
+
# Anything inside this module is considered public API, and in the future
|
4
|
+
# documentation will be generated from it via rdoc.
|
5
|
+
|
6
|
+
module DSL
|
7
|
+
# @!group Danger Zone
|
8
|
+
# Provides access to the raw Travis/Circle/Buildkite/GitHub objects, which
|
9
|
+
# you can use to pull out extra bits of information. _Warning_
|
10
|
+
# the interfaces of these objects is **not** considered a part of the Dangerfile public
|
11
|
+
# API, and is viable to change occasionally on the whims of developers.
|
12
|
+
# @return [EnvironmentManager]
|
13
|
+
|
14
|
+
attr_reader :env
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
load_default_plugins
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_default_plugins
|
23
|
+
Dir["./danger_plugins/*.rb"].each do |file|
|
24
|
+
require File.expand_path(file)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require "danger/ci_source/ci_source"
|
2
|
-
require "danger/
|
2
|
+
require "danger/request_source/github"
|
3
3
|
|
4
4
|
module Danger
|
5
5
|
class EnvironmentManager
|
@@ -15,7 +15,6 @@ module Danger
|
|
15
15
|
if self.ci_source.repo_slug and self.ci_source.pull_request_id
|
16
16
|
break
|
17
17
|
else
|
18
|
-
puts "Not a Pull Request - skipping `danger` run"
|
19
18
|
self.ci_source = nil
|
20
19
|
return nil
|
21
20
|
end
|
@@ -25,18 +24,27 @@ module Danger
|
|
25
24
|
|
26
25
|
# only GitHub for now, open for PRs adding more!
|
27
26
|
self.request_source = GitHub.new(self.ci_source, ENV)
|
27
|
+
# Also Git only for now, also open for PRs adding more!
|
28
|
+
self.scm = GitRepo.new # For now
|
29
|
+
end
|
30
|
+
|
31
|
+
def pr?
|
32
|
+
self.ci_source != nil
|
28
33
|
end
|
29
34
|
|
30
35
|
def fill_environment_vars
|
31
36
|
request_source.fetch_details
|
32
|
-
|
33
|
-
self.scm = GitRepo.new # For now
|
34
37
|
end
|
35
38
|
|
36
39
|
def ensure_danger_branches_are_setup
|
40
|
+
clean_up
|
41
|
+
|
37
42
|
# As this currently just works with GitHub, we can use a github specific feature here:
|
38
43
|
pull_id = ci_source.pull_request_id
|
39
|
-
|
44
|
+
|
45
|
+
# TODO: This isn't optimal, should be hidden behind some kind of facade, but the plugin makes that
|
46
|
+
# difficult to do without accessing the dangerfile
|
47
|
+
test_branch = request_source.pr_json[:base][:sha]
|
40
48
|
|
41
49
|
# Next, we want to ensure that we have a version of the current branch at a known location
|
42
50
|
scm.exec "branch #{danger_base_branch} #{test_branch}"
|
@@ -48,7 +56,7 @@ module Danger
|
|
48
56
|
|
49
57
|
def clean_up
|
50
58
|
[danger_base_branch, danger_base_branch].each do |branch|
|
51
|
-
scm.exec
|
59
|
+
scm.exec("branch -D #{branch}") unless scm.exec("rev-parse --quiet --verify #{branch}").empty?
|
52
60
|
end
|
53
61
|
end
|
54
62
|
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'danger/plugin_support/plugin'
|
2
|
+
require 'danger/core_ext/file_list'
|
3
|
+
|
4
|
+
module Danger
|
5
|
+
class DangerfileGitPlugin < Plugin
|
6
|
+
def initialize(dangerfile)
|
7
|
+
super(dangerfile)
|
8
|
+
raise unless dangerfile.env.scm.class == Danger::GitRepo
|
9
|
+
|
10
|
+
@git = dangerfile.env.scm
|
11
|
+
end
|
12
|
+
|
13
|
+
# @!group Git Files
|
14
|
+
# Paths for files that were added during the diff
|
15
|
+
# @return [FileList] an [Array] subclass
|
16
|
+
#
|
17
|
+
def added_files
|
18
|
+
Danger::FileList.new(@git.diff.select { |diff| diff.type == "new" }.map(&:path))
|
19
|
+
end
|
20
|
+
|
21
|
+
# @!group Git Files
|
22
|
+
# Paths for files that were removed during the diff
|
23
|
+
# @return [FileList] an [Array] subclass
|
24
|
+
#
|
25
|
+
def deleted_files
|
26
|
+
Danger::FileList.new(@git.diff.select { |diff| diff.type == "deleted" }.map(&:path))
|
27
|
+
end
|
28
|
+
|
29
|
+
# @!group Git Files
|
30
|
+
# Paths for files that changed during the diff
|
31
|
+
# @return [FileList] an [Array] subclass
|
32
|
+
#
|
33
|
+
def modified_files
|
34
|
+
Danger::FileList.new(@git.diff.stats[:files].keys)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!group Git Metadata
|
38
|
+
# The overall lines of code added/removed in the diff
|
39
|
+
# @return Int
|
40
|
+
#
|
41
|
+
def lines_of_code
|
42
|
+
@git.diff.lines
|
43
|
+
end
|
44
|
+
|
45
|
+
# @!group Git Metadata
|
46
|
+
# The overall lines of code removed in the diff
|
47
|
+
# @return Int
|
48
|
+
#
|
49
|
+
def deletions
|
50
|
+
@git.diff.deletions
|
51
|
+
end
|
52
|
+
|
53
|
+
# @!group Git Metadata
|
54
|
+
# The overall lines of code added in the diff
|
55
|
+
# @return Int
|
56
|
+
#
|
57
|
+
def insertions
|
58
|
+
@git.diff.insertions
|
59
|
+
end
|
60
|
+
|
61
|
+
# @!group Git Metadata
|
62
|
+
# The log of commits inside the diff
|
63
|
+
# @return [Git::Log] from the gem `git`
|
64
|
+
#
|
65
|
+
def commits
|
66
|
+
@git.log.to_a
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'danger/plugin_support/plugin'
|
2
|
+
|
3
|
+
module Danger
|
4
|
+
class DangerfileGitHubPlugin < Plugin
|
5
|
+
def initialize(dangerfile)
|
6
|
+
super(dangerfile)
|
7
|
+
return nil unless dangerfile.env.request_source.class == Danger::GitHub
|
8
|
+
|
9
|
+
@github = dangerfile.env.request_source
|
10
|
+
end
|
11
|
+
|
12
|
+
# @!group PR Metadata
|
13
|
+
# The title of the Pull Request
|
14
|
+
# @return String
|
15
|
+
#
|
16
|
+
def pr_title
|
17
|
+
@github.pr_json[:title].to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
# @!group PR Metadata
|
21
|
+
# The body text of the Pull Request
|
22
|
+
# @return String
|
23
|
+
#
|
24
|
+
def pr_body
|
25
|
+
@github.pr_json[:body].to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
# @!group PR Metadata
|
29
|
+
# The username of the author of the Pull Request
|
30
|
+
# @return String
|
31
|
+
#
|
32
|
+
def pr_author
|
33
|
+
@github.pr_json[:user][:login].to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
# @!group PR Metadata
|
37
|
+
# The labels assigned to the Pull Request
|
38
|
+
# @return [String]
|
39
|
+
#
|
40
|
+
def pr_labels
|
41
|
+
@github.issue_json[:labels].map { |l| l[:name] }
|
42
|
+
end
|
43
|
+
|
44
|
+
# @!group PR Commit Metadata
|
45
|
+
# The branch to which the PR is going to be merged into
|
46
|
+
# @return String
|
47
|
+
#
|
48
|
+
def branch_for_merge
|
49
|
+
@github.pr_json[:base][:ref]
|
50
|
+
end
|
51
|
+
|
52
|
+
# @!group PR Commit Metadata
|
53
|
+
# The base commit to which the PR is going to be merged as a parent
|
54
|
+
# @return String
|
55
|
+
#
|
56
|
+
def base_commit
|
57
|
+
@github.pr_json[:base][:sha]
|
58
|
+
end
|
59
|
+
|
60
|
+
# @!group PR Commit Metadata
|
61
|
+
# The head commit to which the PR is requesting to be merged from
|
62
|
+
# @return String
|
63
|
+
#
|
64
|
+
def head_commit
|
65
|
+
@github.pr_json[:head][:sha]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'danger/plugin_support/plugin'
|
2
|
+
|
3
|
+
module Danger
|
4
|
+
class DangerfileImportPlugin < Plugin
|
5
|
+
# @!group Plugins
|
6
|
+
# Download a local or remote plugin and use it locally
|
7
|
+
#
|
8
|
+
# @param [String] path
|
9
|
+
# a local path or a https URL to the Ruby file to import
|
10
|
+
# a danger plugin from.
|
11
|
+
def import(path)
|
12
|
+
raise "`import` requires a string" unless path.kind_of?(String)
|
13
|
+
path += ".rb" unless path.end_with?(".rb")
|
14
|
+
|
15
|
+
if path.start_with?("http")
|
16
|
+
import_url(path)
|
17
|
+
else
|
18
|
+
import_local(path)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# @!group Plugins
|
23
|
+
# Download a remote plugin and use it locally
|
24
|
+
#
|
25
|
+
# @param [String] url
|
26
|
+
# https URL to the Ruby file to use
|
27
|
+
def import_url(url)
|
28
|
+
raise "URL is not https, for security reasons `danger` only supports encrypted requests" unless url.start_with?("https://")
|
29
|
+
|
30
|
+
require 'tmpdir'
|
31
|
+
require 'faraday'
|
32
|
+
|
33
|
+
@http_client ||= Faraday.new do |b|
|
34
|
+
b.adapter :net_http
|
35
|
+
end
|
36
|
+
content = @http_client.get(url)
|
37
|
+
|
38
|
+
Dir.mktmpdir do |dir|
|
39
|
+
path = File.join(dir, "temporary_remote_action.rb")
|
40
|
+
File.write(path, content.body)
|
41
|
+
import_local(path)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @!group Plugins
|
46
|
+
# Import one or more local plugins
|
47
|
+
#
|
48
|
+
# @param [String] path
|
49
|
+
# The path to the file to import
|
50
|
+
# Can also be a pattern (./**/*plugin.rb)
|
51
|
+
def import_local(path)
|
52
|
+
Dir[path].each do |file|
|
53
|
+
require File.expand_path(file) # without the expand_path it would fail if the path doesn't start with ./
|
54
|
+
refresh_plugins
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'danger/danger_core/violation'
|
2
|
+
require 'danger/plugin_support/plugin'
|
3
|
+
|
4
|
+
module Danger
|
5
|
+
class DangerfileMessagingPlugin < Plugin
|
6
|
+
def initialize(dangerfile)
|
7
|
+
super(dangerfile)
|
8
|
+
|
9
|
+
@warnings = []
|
10
|
+
@errors = []
|
11
|
+
@messages = []
|
12
|
+
@markdowns = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# @!group Core
|
16
|
+
# Print markdown to below the table
|
17
|
+
#
|
18
|
+
# @param [String] message
|
19
|
+
# The markdown based message to be printed below the table
|
20
|
+
def markdown(message)
|
21
|
+
@markdowns << message
|
22
|
+
puts "Printing markdown #{message}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# @!group Core
|
26
|
+
# Print out a generate message on the PR
|
27
|
+
#
|
28
|
+
# @param [String] message The message to present to the user
|
29
|
+
# @param [Boolean] sticky
|
30
|
+
# Whether the message should be kept after it was fixed,
|
31
|
+
# defaults to `true`.
|
32
|
+
def message(message, sticky: true)
|
33
|
+
@messages << Violation.new(message, sticky)
|
34
|
+
puts "Printing message '#{message}'"
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!group Core
|
38
|
+
# Specifies a problem, but not critical
|
39
|
+
#
|
40
|
+
# @param [String] message The message to present to the user
|
41
|
+
# @param [Boolean] sticky
|
42
|
+
# Whether the message should be kept after it was fixed,
|
43
|
+
# defaults to `true`.
|
44
|
+
def warn(message, sticky: true)
|
45
|
+
return if should_ignore_violation(message)
|
46
|
+
@warnings << Violation.new(message, sticky)
|
47
|
+
puts "Printing warning '#{message}'"
|
48
|
+
end
|
49
|
+
|
50
|
+
# @!group Core
|
51
|
+
# Declares a CI blocking error
|
52
|
+
#
|
53
|
+
# @param [String] message
|
54
|
+
# The message to present to the user
|
55
|
+
# @param [Boolean] sticky
|
56
|
+
# Whether the message should be kept after it was fixed,
|
57
|
+
# defaults to `true`.
|
58
|
+
def fail(message, sticky: true)
|
59
|
+
return if should_ignore_violation(message)
|
60
|
+
@errors << Violation.new(message, sticky)
|
61
|
+
puts "Raising error '#{message}'"
|
62
|
+
end
|
63
|
+
|
64
|
+
def status_report
|
65
|
+
{
|
66
|
+
errors: @errors.map(&:message).clone.freeze,
|
67
|
+
warnings: @warnings.map(&:message).clone.freeze,
|
68
|
+
messages: @messages.map(&:message).clone.freeze,
|
69
|
+
markdowns: @markdowns.clone.freeze
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def violation_report
|
74
|
+
{
|
75
|
+
errors: @errors.clone.freeze,
|
76
|
+
warnings: @warnings.clone.freeze,
|
77
|
+
messages: @messages.clone.freeze
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def should_ignore_violation(message)
|
84
|
+
env.request_source.ignored_violations.include? message
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -2,7 +2,7 @@ require 'claide'
|
|
2
2
|
require 'claide/informative_error'
|
3
3
|
|
4
4
|
module Danger
|
5
|
-
#
|
5
|
+
# From below here - this entire file was taken verbatim for CocoaPods-Core.
|
6
6
|
|
7
7
|
#-------------------------------------------------------------------------#
|
8
8
|
|
File without changes
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Danger
|
2
|
+
class Plugin
|
3
|
+
def initialize(dangerfile)
|
4
|
+
@dangerfile = dangerfile
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.instance_name
|
8
|
+
self.to_s.gsub("Danger", "").danger_underscore.split('/').last
|
9
|
+
end
|
10
|
+
|
11
|
+
# Both of these methods exist on all objects
|
12
|
+
# http://ruby-doc.org/core-2.2.3/Kernel.html#method-i-warn
|
13
|
+
# http://ruby-doc.org/core-2.2.3/Kernel.html#method-i-fail
|
14
|
+
# However, as we're using using them in the DSL, they won't
|
15
|
+
# get method_missing called correctly.
|
16
|
+
|
17
|
+
def warn(*args, &blk)
|
18
|
+
method_missing(:warn, *args, &blk)
|
19
|
+
end
|
20
|
+
|
21
|
+
def fail(*args, &blk)
|
22
|
+
method_missing(:fail, *args, &blk)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Since we have a reference to the Dangerfile containing all the information
|
26
|
+
# We need to redirect the self calls to the Dangerfile
|
27
|
+
def method_missing(method_sym, *arguments, &_block)
|
28
|
+
@dangerfile.send(method_sym, *arguments)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'yard'
|
2
|
+
|
3
|
+
module Danger
|
4
|
+
class PluginParser
|
5
|
+
attr_accessor :registry
|
6
|
+
|
7
|
+
def initialize(path)
|
8
|
+
@path = path
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse
|
12
|
+
# could this go in a singleton-y place instead?
|
13
|
+
# like class initialize?
|
14
|
+
YARD::Tags::Library.define_tag('tags', :tags)
|
15
|
+
|
16
|
+
files = ["lib/danger/plugin_support/plugin.rb", @path]
|
17
|
+
self.registry = YARD::Registry.load(files, true)
|
18
|
+
end
|
19
|
+
|
20
|
+
def classes_in_file
|
21
|
+
self.registry.all
|
22
|
+
.select { |thing| thing.type == :class }
|
23
|
+
.select { |klass| klass.file == @path }
|
24
|
+
end
|
25
|
+
|
26
|
+
def plugins_from_classes(classes)
|
27
|
+
classes.select { |klass| klass.inheritance_tree.map(&:name).include? :Plugin }
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_dict(classes)
|
31
|
+
d_meth = lambda do |meth|
|
32
|
+
return nil if meth.nil?
|
33
|
+
{
|
34
|
+
name: meth.name,
|
35
|
+
body_md: meth.docstring,
|
36
|
+
tags: meth.tags.map do |t|
|
37
|
+
{
|
38
|
+
name: t.tag_name,
|
39
|
+
types: t.types
|
40
|
+
}
|
41
|
+
end
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
d_attr = lambda do |attribute|
|
46
|
+
{
|
47
|
+
read: d_meth.call(attribute[:read]),
|
48
|
+
write: d_meth.call(attribute[:write])
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
classes.map do |klass|
|
53
|
+
{
|
54
|
+
name: klass.name.to_s,
|
55
|
+
body_md: klass.docstring,
|
56
|
+
example_code: klass.tags.select { |t| t.tag_name == "example" }.map(&:text).compact,
|
57
|
+
attributes: klass.attributes[:instance].map do |pair|
|
58
|
+
{
|
59
|
+
pair.first => d_attr.call(pair.last)
|
60
|
+
}
|
61
|
+
end,
|
62
|
+
methods: (klass.meths - klass.inherited_meths).select { |m| m.visibility == :public }.map { |m| d_meth.call(m) },
|
63
|
+
tags: klass.tags.select { |t| t.tag_name == "tags" }.map(&:name).compact,
|
64
|
+
see: klass.tags.select { |t| t.tag_name == "see" }.map(&:name).map(&:split).flatten.compact,
|
65
|
+
file: klass.file
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|