danger 0.7.4 → 0.8.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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +28 -23
  3. data/lib/danger.rb +3 -4
  4. data/lib/danger/ci_source/circle.rb +1 -1
  5. data/lib/danger/{circle_api.rb → ci_source/circle_api.rb} +0 -0
  6. data/lib/danger/ci_source/drone.rb +18 -0
  7. data/lib/danger/ci_source/semaphore.rb +18 -0
  8. data/lib/danger/commands/local.rb +19 -10
  9. data/lib/danger/commands/runner.rb +30 -18
  10. data/lib/danger/comment_generators/github.md.erb +1 -1
  11. data/lib/danger/{scm_source → core_ext}/file_list.rb +1 -0
  12. data/lib/danger/danger_core/dangerfile.rb +217 -0
  13. data/lib/danger/danger_core/dangerfile_dsl.rb +29 -0
  14. data/lib/danger/{environment_manager.rb → danger_core/environment_manager.rb} +14 -6
  15. data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +69 -0
  16. data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +68 -0
  17. data/lib/danger/danger_core/plugins/dangerfile_import_plugin.rb +58 -0
  18. data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +87 -0
  19. data/lib/danger/{standard_error.rb → danger_core/standard_error.rb} +1 -1
  20. data/lib/danger/{violation.rb → danger_core/violation.rb} +0 -0
  21. data/lib/danger/plugin_support/plugin.rb +31 -0
  22. data/lib/danger/plugin_support/plugin_parser.rb +70 -0
  23. data/lib/danger/{request_sources → request_source}/github.rb +2 -33
  24. data/lib/danger/scm_source/git_repo.rb +1 -30
  25. data/lib/danger/version.rb +1 -1
  26. metadata +79 -17
  27. data/lib/danger/available_values.rb +0 -29
  28. data/lib/danger/dangerfile.rb +0 -123
  29. data/lib/danger/dangerfile_dsl.rb +0 -159
  30. data/lib/danger/plugin.rb +0 -25
  31. 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/request_sources/github"
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
- test_branch = request_source.base_commit
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 "branch -D #{branch}"
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
- # Ripped direct from CocoaPods-Core - thanks!
5
+ # From below here - this entire file was taken verbatim for CocoaPods-Core.
6
6
 
7
7
  #-------------------------------------------------------------------------#
8
8
 
@@ -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