danger 3.4.2 → 4.0.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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/lib/danger/ci_source/jenkins.rb +32 -1
  4. data/lib/danger/ci_source/local_git_repo.rb +79 -21
  5. data/lib/danger/ci_source/support/commits.rb +17 -0
  6. data/lib/danger/ci_source/support/find_repo_info_from_logs.rb +35 -0
  7. data/lib/danger/ci_source/support/find_repo_info_from_url.rb +27 -0
  8. data/lib/danger/ci_source/support/local_pull_request.rb +20 -0
  9. data/lib/danger/ci_source/support/no_pull_request.rb +7 -0
  10. data/lib/danger/ci_source/support/no_repo_info.rb +5 -0
  11. data/lib/danger/ci_source/support/{merged_pull_request_finder.rb → pull_request_finder.rb} +71 -44
  12. data/lib/danger/ci_source/support/remote_pull_request.rb +15 -0
  13. data/lib/danger/ci_source/support/repo_info.rb +10 -0
  14. data/lib/danger/commands/dangerfile/gem.rb +43 -0
  15. data/lib/danger/commands/dangerfile/init.rb +30 -0
  16. data/lib/danger/commands/init.rb +2 -2
  17. data/lib/danger/commands/pr.rb +119 -0
  18. data/lib/danger/commands/runner.rb +8 -1
  19. data/lib/danger/commands/systems.rb +6 -4
  20. data/lib/danger/danger_core/dangerfile.rb +11 -7
  21. data/lib/danger/danger_core/dangerfile_generator.rb +11 -0
  22. data/lib/danger/danger_core/environment_manager.rb +1 -1
  23. data/lib/danger/danger_core/executor.rb +3 -1
  24. data/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb +4 -4
  25. data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +14 -13
  26. data/lib/danger/request_sources/github.rb +17 -14
  27. data/lib/danger/request_sources/gitlab.rb +26 -12
  28. data/lib/danger/request_sources/support/get_ignored_violation.rb +17 -0
  29. data/lib/danger/scm_source/git_repo.rb +45 -23
  30. data/lib/danger/version.rb +1 -1
  31. metadata +27 -28
  32. data/lib/assets/PluginTemplate.rb.template +0 -20
  33. data/lib/danger/ci_source/support/remote_finder.rb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 44b2fc8e9ba271755b5995d03991b6039535ef80
4
- data.tar.gz: 20de81281c3a7291570bf4d0cabef7b995806cca
3
+ metadata.gz: 21f670d087989c2099520be07dd3e1204a9c2eef
4
+ data.tar.gz: 3fbfee83973b73b19496f8c8bd74dd2ecbd8e09d
5
5
  SHA512:
6
- metadata.gz: 86f4f302fdf25c27e572f08c2ec7424f52d24c87f8e125cb511e88572645e2f04a507a8aafcb82642acc73f74466695267662262cad34fc6964e9a131964dd4d
7
- data.tar.gz: 35606932eacff700e806a9cb68d629e2f0eff6d27752248f6b6c1c348772d3d7c568c3f2345c2302b112fbfb4b33b624f47ba5ec404c027262e9faac2f1a64e2
6
+ metadata.gz: 11595711e242836e3328f7f0b1f4ef3ab54d60b561c2507052ddc74b0209f323f6fe04e5c96a1525026e055745b5f3a3eb86fee550e70bcb0df7cdfafc9a902e
7
+ data.tar.gz: 0367b7abdc0e048bc0cbcb7fbb7f324520952ed13b0b4de3e4eea87cc6bafae900ff0bbf3cb97baa9ff7be89d8f0f93910cd840de9e362012c0aa4f3673a95f3
data/README.md CHANGED
@@ -45,6 +45,8 @@ We keep all of the end-user documentation at [http://danger.systems](http://dang
45
45
 
46
46
  Some quick links: [Guides Index](http://danger.systems/guides.html), [DSL Reference](http://danger.systems/reference.html), [Getting Started](http://danger.systems/guides/getting_started.html) and [What does Danger Do?](http://danger.systems/guides/what_does_danger_do.html).
47
47
 
48
+ Sidenote: There is a [pure JS version](https://github.com/danger/danger-js) in the works, it's at the point where it can fail your build but that's about it for now, would love help there if it interests you.
49
+
48
50
  ## I'm here to help out!
49
51
 
50
52
  Brilliant. So, let's get you set up.
@@ -17,6 +17,12 @@ module Danger
17
17
  #
18
18
  # With that set up, you can edit your job to add `bundle exec danger` at the build action.
19
19
  #
20
+ # ##### Pipeline
21
+ # If your're using [pipelines](https://jenkins.io/solutions/pipeline/) you should be using the [GitHub branch source plugin](https://wiki.jenkins-ci.org/display/JENKINS/GitHub+Branch+Source+Plugin)
22
+ # for easy setup and handling of PRs.
23
+ #
24
+ # After you've set up the plugin, add a `sh 'bundle exec danger'` line in your pipeline script and make sure that build PRs is enabled.
25
+ #
20
26
  # #### GitLab
21
27
  # You will want to be using the [GitLab Plugin](https://github.com/jenkinsci/gitlab-plugin)
22
28
  # in order to ensure that you have the build environment set up for MR integration.
@@ -53,7 +59,7 @@ module Danger
53
59
  end
54
60
 
55
61
  def initialize(env)
56
- self.repo_url = env.fetch("GIT_URL_1") { env["GIT_URL"] }
62
+ self.repo_url = self.class.repo_url(env)
57
63
  self.pull_request_id = self.class.pull_request_id(env)
58
64
 
59
65
  repo_matches = self.repo_url.match(%r{([\/:])([^\/]+\/[^\/.]+)(?:.git)?$})
@@ -63,9 +69,34 @@ module Danger
63
69
  def self.pull_request_id(env)
64
70
  if env["ghprbPullId"]
65
71
  env["ghprbPullId"]
72
+ elsif env["CHANGE_ID"]
73
+ env["CHANGE_ID"]
66
74
  else
67
75
  env["gitlabMergeRequestId"]
68
76
  end
69
77
  end
78
+
79
+ def self.repo_url(env)
80
+ if env["GIT_URL_1"]
81
+ env["GIT_URL_1"]
82
+ elsif env["CHANGE_URL"]
83
+ change_url = env["CHANGE_URL"]
84
+ case change_url
85
+ when %r{\/pull\/} # GitHub
86
+ matches = change_url.match(%r{(.+)\/pull\/[0-9]+})
87
+ matches[1] unless matches.nil?
88
+ when %r{\/merge_requests\/} # GitLab
89
+ matches = change_url.match(%r{(.+)\/merge_requests\/[0-9]+})
90
+ matches[1] unless matches.nil?
91
+ when %r{\/pull-requests\/} # Bitbucket
92
+ matches = change_url.match(%r{(.+)\/pull-requests\/[0-9]+})
93
+ matches[1] unless matches.nil?
94
+ else
95
+ change_url
96
+ end
97
+ else
98
+ env["GIT_URL"]
99
+ end
100
+ end
70
101
  end
71
102
  end
@@ -2,10 +2,15 @@
2
2
 
3
3
  require "git"
4
4
  require "uri"
5
- require "danger/ci_source/support/remote_finder"
6
- require "danger/ci_source/support/merged_pull_request_finder"
5
+
7
6
  require "danger/request_sources/github"
8
7
 
8
+ require "danger/ci_source/support/find_repo_info_from_url"
9
+ require "danger/ci_source/support/find_repo_info_from_logs"
10
+ require "danger/ci_source/support/no_repo_info"
11
+ require "danger/ci_source/support/pull_request_finder"
12
+ require "danger/ci_source/support/commits"
13
+
9
14
  module Danger
10
15
  # ignore
11
16
  class LocalGitRepo < CI
@@ -31,29 +36,82 @@ module Danger
31
36
  @supported_request_sources ||= [Danger::RequestSources::GitHub]
32
37
  end
33
38
 
34
- def print_repo_slug_warning
35
- puts "Danger local requires a repository hosted on GitHub.com or GitHub Enterprise.".freeze
39
+ def initialize(env = {})
40
+ @env = env
41
+
42
+ self.repo_slug = remote_info.slug
43
+ raise_error_for_missing_remote if remote_info.kind_of?(NoRepoInfo)
44
+
45
+ self.pull_request_id = found_pull_request.pull_request_id
46
+
47
+ if sha
48
+ self.base_commit = commits.base
49
+ self.head_commit = commits.head
50
+ else
51
+ self.base_commit = found_pull_request.base
52
+ self.head_commit = found_pull_request.head
53
+ end
36
54
  end
37
55
 
38
- def parents(sha)
39
- @parents ||= run_git("rev-list --parents -n 1 #{sha}").strip.split(" ".freeze)
56
+ private
57
+
58
+ attr_reader :env
59
+
60
+ def raise_error_for_missing_remote
61
+ raise missing_remote_error_message
40
62
  end
41
63
 
42
- def initialize(env = {})
43
- repo_slug = RemoteFinder.new(
44
- env["DANGER_GITHUB_HOST"] || "github.com".freeze,
45
- run_git("remote show origin -n".freeze)
46
- ).call
47
-
48
- pull_request_id, sha = MergedPullRequestFinder.new(
49
- env.fetch("LOCAL_GIT_PR_ID") { "" },
50
- run_git("log --oneline -1000000".freeze)
51
- ).call
52
-
53
- self.repo_slug = repo_slug ? repo_slug : print_repo_slug_warning
54
- self.pull_request_id = pull_request_id
55
- self.base_commit = parents(sha)[0]
56
- self.head_commit = parents(sha)[1]
64
+ def missing_remote_error_message
65
+ "danger cannot find your git remote, please set a remote. " \
66
+ "And the repository must host on GitHub.com or GitHub Enterprise."
67
+ end
68
+
69
+ def remote_info
70
+ @_remote_info ||= begin
71
+ remote_info = begin
72
+ if given_pull_request_url?
73
+ FindRepoInfoFromURL.new(env["LOCAL_GIT_PR_URL"]).call
74
+ else
75
+ FindRepoInfoFromLogs.new(
76
+ env["DANGER_GITHUB_HOST"] || "github.com".freeze,
77
+ run_git("remote show origin -n".freeze)
78
+ ).call
79
+ end
80
+ end
81
+
82
+ remote_info || NoRepoInfo.new
83
+ end
84
+ end
85
+
86
+ def found_pull_request
87
+ @_found_pull_request ||= begin
88
+ if given_pull_request_url?
89
+ PullRequestFinder.new(
90
+ remote_info.id,
91
+ remote_info.slug,
92
+ remote: true
93
+ ).call
94
+ else
95
+ PullRequestFinder.new(
96
+ env.fetch("LOCAL_GIT_PR_ID") { "".freeze },
97
+ remote_info.slug,
98
+ remote: false,
99
+ git_logs: run_git("log --oneline -1000000".freeze)
100
+ ).call
101
+ end
102
+ end
103
+ end
104
+
105
+ def given_pull_request_url?
106
+ env["LOCAL_GIT_PR_URL"] && !env["LOCAL_GIT_PR_URL"].empty?
107
+ end
108
+
109
+ def sha
110
+ @_sha ||= found_pull_request.sha
111
+ end
112
+
113
+ def commits
114
+ @_commits ||= Commits.new(run_git("rev-list --parents -n 1 #{sha}"))
57
115
  end
58
116
  end
59
117
  end
@@ -0,0 +1,17 @@
1
+ class Commits
2
+ def initialize(base_head)
3
+ @base_head = base_head.strip.split(" ".freeze)
4
+ end
5
+
6
+ def base
7
+ base_head.first
8
+ end
9
+
10
+ def head
11
+ base_head.last
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :base_head
17
+ end
@@ -0,0 +1,35 @@
1
+ require "danger/ci_source/support/repo_info"
2
+
3
+ module Danger
4
+ class FindRepoInfoFromLogs
5
+ def initialize(github_host, remote_logs)
6
+ @github_host = github_host
7
+ @remote_logs = remote_logs
8
+ end
9
+
10
+ def call
11
+ matched = remote.match(regexp)
12
+
13
+ if matched
14
+ RepoInfo.new(matched["repo_slug"], nil)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :remote_logs, :github_host
21
+
22
+ def remote
23
+ remote_logs.lines.grep(/Fetch URL/)[0].split(": ".freeze, 2)[1]
24
+ end
25
+
26
+ def regexp
27
+ %r{
28
+ #{Regexp.escape(github_host)}
29
+ (:|/|(:/))
30
+ (?<repo_slug>[^/]+/.+?)
31
+ (?:\.git)?$
32
+ }x
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ require "danger/ci_source/support/repo_info"
2
+
3
+ module Danger
4
+ class FindRepoInfoFromURL
5
+ REGEXP = %r{
6
+ (?<slug>[^/]+/[^/]+)
7
+ (/(pull|merge_requests|pull-requests)/)
8
+ (?<id>\d+)
9
+ }x
10
+
11
+ def initialize(url)
12
+ @url = url
13
+ end
14
+
15
+ def call
16
+ matched = url.match(REGEXP)
17
+
18
+ if matched
19
+ RepoInfo.new(matched[:slug], matched[:id])
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :url
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ module Danger
2
+ class LocalPullRequest
3
+ attr_reader :pull_request_id, :sha
4
+
5
+ def initialize(log_line)
6
+ @log_line = log_line
7
+
8
+ @pull_request_id = log_line.match(/#(?<id>[0-9]+)/)[:id]
9
+ @sha = log_line.split(" ".freeze).first
10
+ end
11
+
12
+ def valid?
13
+ pull_request_id && sha
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :log_line
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ module Danger
2
+ class NoPullRequest
3
+ def valid?
4
+ false
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module Danger
2
+ class NoRepoInfo
3
+ attr_reader :slug, :id
4
+ end
5
+ end
@@ -1,25 +1,82 @@
1
+ require "danger/ci_source/support/local_pull_request"
2
+ require "danger/ci_source/support/remote_pull_request"
3
+ require "danger/ci_source/support/no_pull_request"
4
+
1
5
  module Danger
2
- class MergedPullRequestFinder
3
- def initialize(specific_pull_request_id, git_logs)
6
+ class PullRequestFinder
7
+ def initialize(specific_pull_request_id, repo_slug = nil, remote: false, git_logs: "")
4
8
  @specific_pull_request_id = specific_pull_request_id
5
9
  @git_logs = git_logs
10
+ @repo_slug = repo_slug
11
+ @remote = to_boolean(remote)
6
12
  end
7
13
 
8
14
  def call
9
- check_if_any_merged_pull_request!
15
+ check_if_any_pull_request!
10
16
 
11
- [merged_pull_request_id, merged_pull_request_sha]
17
+ pull_request
12
18
  end
13
19
 
14
20
  private
15
21
 
16
- attr_reader :specific_pull_request_id, :git_logs
22
+ attr_reader :specific_pull_request_id, :git_logs, :repo_slug, :remote
23
+
24
+ def to_boolean(maybe_string)
25
+ if ["true", "1", "yes", "y", true].include?(maybe_string)
26
+ true
27
+ else
28
+ false
29
+ end
30
+ end
31
+
32
+ def check_if_any_pull_request!
33
+ unless pull_request.valid?
34
+ if !specific_pull_request_id.empty?
35
+ raise "Could not find the Pull Request (#{specific_pull_request_id}) inside the git history for this repo."
36
+ else
37
+ raise "No recent Pull Requests found for this repo, danger requires at least one Pull Request for the local mode.".freeze
38
+ end
39
+ end
40
+ end
41
+
42
+ # @return [String] Log line of most recent merged Pull Request
43
+ def pull_request
44
+ @pull_request ||= begin
45
+ return if pull_request_ref.empty?
46
+
47
+ if both_present?
48
+ LocalPullRequest.new(pick_the_most_recent_one_from_two_matches)
49
+ elsif only_merged_pull_request_present?
50
+ LocalPullRequest.new(most_recent_merged_pull_request)
51
+ elsif only_squash_and_merged_pull_request_present?
52
+ LocalPullRequest.new(most_recent_squash_and_merged_pull_request)
53
+ elsif remote && remote_pull_request
54
+ RemotePullRequest.new(
55
+ remote_pull_request.number.to_s,
56
+ remote_pull_request.head.sha,
57
+ remote_pull_request.base.sha
58
+ )
59
+ else
60
+ NoPullRequest.new
61
+ end
62
+ end
63
+ end
17
64
 
18
65
  # @return [String] "#42"
19
66
  def pull_request_ref
20
67
  !specific_pull_request_id.empty? ? "##{specific_pull_request_id}" : "#\\d+".freeze
21
68
  end
22
69
 
70
+ def remote_pull_request
71
+ @_remote_pull_request ||= begin
72
+ client.pull_request(repo_slug, specific_pull_request_id)
73
+ end
74
+ end
75
+
76
+ def both_present?
77
+ most_recent_merged_pull_request && most_recent_squash_and_merged_pull_request
78
+ end
79
+
23
80
  # @return [String] Log line of format: "Merge pull request #42"
24
81
  def most_recent_merged_pull_request
25
82
  @most_recent_merged_pull_request ||= begin
@@ -34,23 +91,17 @@ module Danger
34
91
  end
35
92
  end
36
93
 
37
- # @return [String] Log line of most recent merged Pull Request
38
- def merged_pull_request
39
- return if pull_request_ref.empty?
94
+ def pick_the_most_recent_one_from_two_matches
95
+ merged_index = git_logs.lines.index(most_recent_merged_pull_request)
96
+ squash_and_merged_index = git_logs.lines.index(most_recent_squash_and_merged_pull_request)
40
97
 
41
- if both_present?
42
- pick_the_most_recent_one_from_two_matches
43
- elsif only_merged_pull_request_present?
44
- most_recent_merged_pull_request
45
- elsif only_squash_and_merged_pull_request_present?
98
+ if merged_index > squash_and_merged_index # smaller index is more recent
46
99
  most_recent_squash_and_merged_pull_request
100
+ else
101
+ most_recent_merged_pull_request
47
102
  end
48
103
  end
49
104
 
50
- def both_present?
51
- most_recent_merged_pull_request && most_recent_squash_and_merged_pull_request
52
- end
53
-
54
105
  def only_merged_pull_request_present?
55
106
  return false if most_recent_squash_and_merged_pull_request
56
107
 
@@ -63,33 +114,9 @@ module Danger
63
114
  !most_recent_squash_and_merged_pull_request.nil? && !most_recent_squash_and_merged_pull_request.empty?
64
115
  end
65
116
 
66
- def pick_the_most_recent_one_from_two_matches
67
- merged_index = git_logs.lines.index(most_recent_merged_pull_request)
68
- squash_and_merged_index = git_logs.lines.index(most_recent_squash_and_merged_pull_request)
69
-
70
- if merged_index > squash_and_merged_index # smaller index is more recent
71
- most_recent_squash_and_merged_pull_request
72
- else
73
- most_recent_merged_pull_request
74
- end
75
- end
76
-
77
- def check_if_any_merged_pull_request!
78
- if merged_pull_request.to_s.empty?
79
- if !specific_pull_request_id.empty?
80
- raise "Could not find the Pull Request (#{specific_pull_request_id}) inside the git history for this repo."
81
- else
82
- raise "No recent Pull Requests found for this repo, danger requires at least one Pull Request for the local mode.".freeze
83
- end
84
- end
85
- end
86
-
87
- def merged_pull_request_id
88
- merged_pull_request.match(/#(?<id>[0-9]+)/)[:id]
89
- end
90
-
91
- def merged_pull_request_sha
92
- merged_pull_request.split(" ".freeze).first
117
+ def client
118
+ require "octokit"
119
+ Octokit::Client.new(access_token: ENV["DANGER_GITHUB_API_TOKEN"])
93
120
  end
94
121
  end
95
122
  end
@@ -0,0 +1,15 @@
1
+ module Danger
2
+ class RemotePullRequest
3
+ attr_reader :pull_request_id, :sha, :head, :base
4
+
5
+ def initialize(pull_request_id, head, base)
6
+ @pull_request_id = pull_request_id
7
+ @head = head
8
+ @base = base
9
+ end
10
+
11
+ def valid?
12
+ pull_request_id && head && base
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ module Danger
2
+ class RepoInfo
3
+ attr_reader :slug, :id
4
+
5
+ def initialize(slug, id)
6
+ @slug = slug
7
+ @id = id
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,43 @@
1
+ require "claide_plugin"
2
+ require "danger/commands/dangerfile/init"
3
+
4
+ module Danger
5
+ class DangerfileGem < DangerfileCommand
6
+ self.summary = "Create a gem-based Dangerfile quickly."
7
+ def self.description
8
+ <<-DESC
9
+ Creates a scaffold for the development of a new gem based Dangerfile
10
+ named `NAME` according to the best practices.
11
+ DESC
12
+ end
13
+ self.command = "gem"
14
+ self.arguments = [
15
+ CLAide::Argument.new("NAME", true)
16
+ ]
17
+
18
+ def initialize(argv)
19
+ @name = argv.shift_argument
20
+ prefix = "dangerfile" + "-"
21
+ unless @name.nil? || @name.empty? || @name.start_with?(prefix)
22
+ @name = prefix + @name.dup
23
+ end
24
+ @template_url = argv.shift_argument
25
+ super
26
+ end
27
+
28
+ def validate!
29
+ super
30
+ if @name.nil? || @name.empty?
31
+ help! "A name for the plugin is required."
32
+ end
33
+
34
+ help! "The plugin name cannot contain spaces." if @name =~ /\s/
35
+ end
36
+
37
+ def run
38
+ runner = CLAide::TemplateRunner.new(@name, "https://github.com/danger/dangerfile-gem-template")
39
+ runner.clone_template
40
+ runner.configure_template
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ require "danger/danger_core/dangerfile_generator"
2
+
3
+ # Mainly so we can have a nice strucutre for commands
4
+
5
+ module Danger
6
+ class DangerfileCommand < Runner
7
+ self.summary = "Easily create you Dangerfiles."
8
+ self.command = "dangerfile"
9
+
10
+ self.abstract_command = true
11
+ def self.options
12
+ []
13
+ end
14
+ end
15
+ end
16
+
17
+ # Just a less verbose way of doing the Dangerfile from `danger init`.
18
+
19
+ module Danger
20
+ class DangerfileInit < DangerfileCommand
21
+ self.summary = "Create an example Dangerfile."
22
+ self.command = "init"
23
+
24
+ def run
25
+ content = DangerfileGenerator.create_dangerfile(".", cork)
26
+ File.write("Dangerfile", content)
27
+ cork.puts "Created" + "./Dangerfile".green
28
+ end
29
+ end
30
+ end
@@ -1,4 +1,5 @@
1
1
  require "danger/commands/init_helpers/interviewer"
2
+ require "danger/danger_core/dangerfile_generator"
2
3
  require "danger/ci_source/local_git_repo"
3
4
  require "yaml"
4
5
 
@@ -53,8 +54,7 @@ module Danger
53
54
  end
54
55
 
55
56
  def setup_dangerfile
56
- dir = Danger.gem_path
57
- content = File.read(File.join(dir, "lib", "assets", "DangerfileTemplate"))
57
+ content = DangerfileGenerator.create_dangerfile(".", cork)
58
58
  File.write("Dangerfile", content)
59
59
 
60
60
  ui.header "Step 1: Creating a starter Dangerfile"