danger 0.10.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d02824eab3497550f11cb1f628aa5b3e94e31ad8
4
- data.tar.gz: 5f17dbe1aec05cb99751f902f19f2c886893336d
3
+ metadata.gz: db32b8346a72c88968c63bae306a254f91484df2
4
+ data.tar.gz: df97dfe2ec512b2ee4fb0fa16161b6b4cffc2307
5
5
  SHA512:
6
- metadata.gz: 2788c9f4aa4c1d30efd031a5279d0f64ac29e1f4e98706ba6172d568e5a17207065ea1df78918de37a14ffc956696d9208bcdd2128bb3ef20d10506d33e93daf
7
- data.tar.gz: 869128a91bfb572210f952ec5ce98ad7dc28f65aa04af219055a0fbde073f2c57cc40776153183125c6f38dcea574bf3ede0d43434a82e8d36c6e0294db79980
6
+ metadata.gz: 3a6bb147cd045c067b5121200b26a301f30672b96986793af3290c3fcc94549ecfcddd82210f67c990a98cb0a472da0997eecc71103b92c70c5a4cf7ab703a54
7
+ data.tar.gz: 9580fb003244cda3738463ab8f0b50f404229f1cc5376bd9b9db27ddcdf5cb5fd98d87a0ef8ebcac2d69dcef450c948ae831b0b4ce5315a5c6584f0f02f36ba1
data/README.md CHANGED
@@ -8,238 +8,84 @@ Formalize your Pull Request etiquette.
8
8
  -------
9
9
 
10
10
  <p align="center">
11
- <a href="#getting-started">Getting Started</a> &bull;
12
- <a href="#usage-on-ci">Usage</a> &bull;
13
- <a href="#dsl">DSL</a> &bull;
14
- <a href="#plugins">Plugins</a> &bull;
15
- <a href="#test-locally-with-danger-local">Local</a>
11
+ <a href="#what-is-danger">What is Danger?</a> &bull;
12
+ <a href="#im-here-to-help-out">Helping Out</a> &bull;
13
+ <a href="#tell-me-of-these-plugins">Plugin Development</a> &bull;
16
14
  </p>
17
15
 
18
16
  -------
19
17
 
20
- ## Getting Started
21
-
22
- Add this line to your application's [Gemfile](https://guides.cocoapods.org/using/a-gemfile.html):
23
-
24
- ```ruby
25
- gem 'danger'
26
- ```
27
-
28
- Next, use Bundler to install the gem.
29
-
30
- ```shell
31
- bundle install
32
- ```
33
-
34
- After the gem is installed, you can run Danger's interactive getting started guide:
35
-
36
- ```
37
- bundle exec danger init
38
- ```
39
-
40
- The guide will create a default `Dangerfile` and take you through the process of setting up an account for feedback on GitHub.
41
-
42
- ## Usage on CI
43
-
44
- ```
45
- bundle exec danger
46
- ```
47
-
48
- This will look at your `Dangerfile` and update the pull request accordingly. While you are setting up Danger, you may want to use: `--verbose` for more debug information.
49
-
50
- ## CI Support
51
-
52
- Danger currently is supported on Travis CI, Circle CI, Xcode Bots via Buildasaur, BuildKite, Jenkins, Semaphore, Drone CI and TeamCity. These work via environment variables, so it's easy to extend to include your own.
53
-
54
- ## What happens?
55
-
56
- Danger runs at the end of a CI build, she will execute a `Dangerfile`. This file is given some special variables based on the git diff and the Pull Request being running. You can use these variables in Ruby to provide messages, warnings and failures for your build. You set up Danger with a GitHub user account and she will post updates via comments on the Pull Request, and can fail your build too.
57
-
58
- ## DSL
59
-
60
- &nbsp; | &nbsp; | Danger :no_entry_sign:
61
- -------------: | ------------- | ----
62
- :sparkles: | `lines_of_code` | The total amount of lines of code in the diff
63
- :pencil2: | `modified_files` | The list of modified files
64
- :ship: | `added_files` | The list of added files
65
- :recycle: | `deleted_files` | The list of removed files
66
- :abc: | `pr_title` | The title of the PR
67
- :book: | `pr_body` | The body of the PR
68
- :busts_in_silhouette: | `pr_author` | The author who submitted the PR
69
- :bookmark: | `pr_labels` | The labels added to the PR
70
-
71
- The `Dangerfile` is a Ruby file, so really, you can do anything. However, at this stage you might need selling on the idea a bit more, so lets take some real examples:
72
-
73
- #### Dealing with WIP pull requests
74
-
75
- ```ruby
76
- # Sometimes its a README fix, or something like that - which isn't relevant for
77
- # including in a CHANGELOG for example
78
- declared_trivial = pr_title.include? "#trivial"
79
-
80
- # Just to let people know
81
- warn("PR is classed as Work in Progress", sticky: false) if pr_title.include? "[WIP]"
82
- ```
83
-
84
- #### Being cautious around specific files
85
-
86
- ``` ruby
87
- # Devs shouldn't ship changes to this file
88
- fail("Developer Specific file shouldn't be changed", sticky: false) if modified_files.include?("Artsy/View_Controllers/App_Navigation/ARTopMenuViewController+DeveloperExtras.m")
89
-
90
- # Did you make analytics changes? Well you should also include a change to our analytics spec
91
- made_analytics_changes = modified_files.include?("/Artsy/App/ARAppDelegate+Analytics.m")
92
- made_analytics_specs_changes = modified_files.include?("/Artsy_Tests/Analytics_Tests/ARAppAnalyticsSpec.m")
93
- if made_analytics_changes
94
- fail("Analytics changes should have reflected specs changes") if !made_analytics_specs_changes
95
-
96
- # And pay extra attention anyway
97
- message('Analytics dict changed, double check for ?: `@""` on new entries')
98
- message('Also, double check the [Analytics Eigen schema](https://docs.google.com/spreadsheets/u/1/d/1bLbeOgVFaWzLSjxLOBDNOKs757-zBGoLSM1lIz3OPiI/edit#gid=497747862) if the changes are non-trivial.')
99
- end
100
- ```
18
+ ## What is Danger?
101
19
 
102
- #### Pinging people when a specific file has changed
20
+ Danger runs after your CI, and gives teams the chance to automate common code review chores.
103
21
 
104
- ```ruby
105
- markdown("@orta something changed in elan!") if modified_files.include? "/components/lib/variables/colors.json"
106
- ```
22
+ This provides another logical step in your process, through this Danger can help lint your rote tasks in daily code review.
107
23
 
108
- If you wish to ping a person on a PR you'll need to use the `markdown` method rather than the `message`, `warn` or `fail` methods.
109
- This is because the HTML generated by the latter methods doesn't trigger GitHub's notification detection.
24
+ You can use Danger to codify your teams norms. Leaving humans to think about harder problems.
110
25
 
111
- #### Exposing aspects of CI logs into the PR discussion
26
+ ## For example?
112
27
 
113
- ```ruby
114
- build_log = File.read(File.join(ENV["CIRCLE_ARTIFACTS"], "xcode_test_raw.log"))
115
- snapshots_url = build_log.match(%r{https://eigen-ci.s3.amazonaws.com/\d+/index.html})
116
- fail("There were [snapshot errors](#{snapshots_url})") if snapshots_url
117
- ```
28
+ * Enforce CHANGELOGs
29
+ * Enforce links to Trello/JIRA in PR/MR bodies
30
+ * Enforce using descriptive labels
31
+ * Look out for common anti-patterns
32
+ * Highlight interesting build artifacts
33
+ * Give specific files have extra focus
118
34
 
119
- #### Available commands
35
+ Danger simply provides the glue to let _you_ build out the rules specific to your team's culture. Offering a lot of useful metadata, and a comprehensive plugin system to share common issues.
120
36
 
121
- Command | Description
122
- ------------- | ----
123
- `fail` | Causes the PR to fail and print out the error on the PR
124
- `warn` | Prints out a warning to the PR, but still enables the merge button
125
- `message` | Show neutral messages on the PR
126
- `markdown` | Print raw markdown below the summary tables on the PR
127
-
128
- ## Plugins
129
-
130
- Danger was built with a platform in mind: she can be used with any kind of software project and allows you to write your own action to have structured source code.
37
+ ## Getting Started
131
38
 
132
- In your `Dangerfile` you can import local or remote actions using
39
+ Alright. So, actually, you may be in the wrong place. From here on in, this README is going to be for people who are interested in working on / improving on Danger.
133
40
 
134
- ```ruby
135
- import "./danger_plugins/work_in_progress_warning"
136
- # or
137
- import "https://raw.githubusercontent.com/danger/danger/master/danger_plugins/work_in_progress_warning.rb"
41
+ We keep all of the end-user documentation inside [http://danger.systems](http://danger.systems).
138
42
 
139
- # Call those actions using
140
- work_in_progress_warning
43
+ 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).
141
44
 
142
- custom_plugin(variable: "value")
143
- ```
45
+ ## I'm here to help out!
144
46
 
145
- To create a new plugin run
47
+ Brilliant. So, let's get you set up.
146
48
 
49
+ ``` sh
50
+ git clone https://github.com/danger/danger.git
51
+ cd danger
52
+ bundle install
53
+ bundle exec rake spec
147
54
  ```
148
- danger new_plugin
149
- ```
150
-
151
- This will generate a new Ruby file which you can modify to fit your needs.
152
-
153
- ## Advanced
154
55
 
155
- You can access more detailed information by accessing the following variables
56
+ This sets everything up and runs all of the tests.
156
57
 
157
- &nbsp; | Danger :no_entry_sign:
158
- ------------- | ----
159
- `env.request_source.pr_json` | The full JSON for the pull request
160
- `env.scm.diff` | The full [Diff](https://github.com/mojombo/grit/blob/master/lib/grit/diff.rb) file for the diff.
161
- `env.ci_source` | To get information like the repo slug or pull request ID
58
+ #### Theory
162
59
 
163
- These are considered implementation details though, and may be subject to change in future releases. We're very
164
- open to turning useful bits into the official API.
60
+ Danger has a [VISION.md](https://github.com/danger/danger/blob/master/VISION.md) file, this sums up the ideas around what Danger is. It's the lower bounds of what Danger means. Orta has written on handling, and creating Danger [on the Artsy blog](http://artsy.github.io/blog/categories/danger/) too.
165
61
 
166
- ## Test locally with `danger local`
62
+ #### Documentation
167
63
 
168
- You can use `danger local` to run Danger in an environment similar to how she will be ran on CI. By default Danger will look
169
- at the most recently merged PR, then run your `Dangerfile` against that Pull Request. This is really useful when making changes.
64
+ The code you write may end up in the public part of the website, the easiest way to tell is that it is vastly overdocumented. If you are working in a space that looks over-documented, please be extra considerate to add documentation. We expect the consumers of that documentation to be non-rubyists, thus avoid specific jargon and try to provide duplicate overlapping examples.
170
65
 
171
- If you have a specific PR in mind that you'd like to work against, make sure you have it merged in your current git
172
- history, then append `--use-merged-pr=[id]` to the command.
66
+ #### Testing
173
67
 
174
- ### Adding a new CI
175
- If the CI server you're using isn't available yet, you can build it yourself:
68
+ So far, we've not really figured out the right way to make tests for our CLI commands. When we have done so, they've ended up brittle. So ideally, try to move any logic that would go into a command into separate classes, and test those. We're OK with the command not having coverage, but ideally the classes that make up what it does do.
176
69
 
177
- Take a look at some of the [already existing integrations](https://github.com/danger/danger/tree/master/lib/danger/ci_source). The class has 2 mandatory methods:
70
+ I'd strongly recommend using `bundle exec guard` to run your tests as you work. Any changes you make in the lib, or specs will have corresponding tests run instantly.
178
71
 
179
- - `self.validates?` which should detect if the CI is active (detecting via ENV variables, mostly)
180
- - `initialize` which should set 2 variables:
181
- - `self.repo_slug` the repo slug, in `org/repo` or `user/repo` format.
182
- - `self.pull_request_id` the number of the pull request that the CI is testing (often available in ENV variables)
72
+ #### Debugging
183
73
 
184
- We'd love to see pull requests for new integrations!
185
-
186
-
187
- ## Suppress Violations
188
-
189
- You can tell Danger to ignore a specific warning or error by commenting on the PR body:
190
-
191
- ```
192
- > Danger: Ignore "Developer Specific file shouldn't be changed"
193
- ```
194
-
195
- ## Sticky
196
-
197
- Danger can keep its history if a warning/error/message is marked as *sticky*. When the violation is resolved,
198
- Danger will update the comment to cross it out. If you don't want this behavior, just use `sticky: false`.
74
+ Ruby is super dynamic, one of the best ways to debug is by using [pry](http://pryrepl.org/). We include pry for developers, when you have a problem copy these two lines just before your problem and follow the instructions from "[I Want To Be A Danger Wizard](http://danger.systems/guides/troubleshooting.html#i-want-to-be-a-danger-wizard)."
199
75
 
200
76
  ```ruby
201
- fail("PR needs labels", sticky: false) if pr_labels.empty?
77
+ require 'pry'
78
+ binding.pry
202
79
  ```
203
80
 
204
- ## Org-Wide Dangerfile
205
-
206
- To have consistent rules across the repos in your organisation, you can use a shared `Dangerfile`:
207
-
208
- 1. create a repo on your GitHub organization called `danger` (or `Danger`)
209
- 1. Add a `Dangerfile` to the root of that repo
210
- 1. The new `Dangerfile` is ran after the current repo's `Dangerfile`
211
-
212
- The org `Dangerfile` will have access to all of the same plugins, and metadata. For an example, see [themoji/danger](https://github.com/Themoji/danger).
213
-
214
- ## Multiple Dangers
215
-
216
- If one Danger is not enough for you, you can run several ones on the same PR. Just use the `danger_id` param. For example:
217
-
218
- ```
219
- bundle exec danger --danger_id=unit_tests
220
- ```
221
-
222
- You can have each instance of Danger running on a different CI provider and even doing different validations. An use case would be:
223
-
224
- * `basic` runs on a Linux environment (such as Circle CI) and validates the PR itself (title, etc)
225
- * `compilation` runs on a Mac after running unit tests for your iOS app and comments about warnings, test failures, etc
226
- * `uitests` runs on a Mac after running UI Unit tests and comments about test failures
227
-
228
- ## Useful bits of knowledge
229
-
230
- * You can set the base branch in the command line arguments see: `bundle exec danger --help`, if you commonly merge into non-master branches.
231
- * Appending `--verbose` to `bundle exec danger` will expose all of the variables that Danger provides, and their values in the shell.
232
-
233
- Here are some real-world Dangerfiles: [artsy/eigen](https://github.com/artsy/eigen/blob/master/Dangerfile), [danger/danger](https://github.com/danger/danger/blob/master/Dangerfile), [artsy/elan](https://github.com/artsy/elan/blob/master/Dangerfile) and more!
81
+ ## Tell me of these Plugins
234
82
 
235
- ## Usage with GitHub Enterprise
236
- Danger allows usage with GitHub Enterprise by setting 2 environment variables:
237
- - `DANGER_GITHUB_HOST` to the host that GitHub is running on
238
- - `DANGER_GITHUB_API_HOST` to the host that the GitHub Enterprise API is reachable on.
83
+ * Make the "making a plugin" guide
84
+ * Talk through the tech specs here
239
85
 
240
86
  ## License, Contributor's Guidelines and Code of Conduct
241
87
 
242
- We try to keep as much discussion as possible in GitHub issues, but also have a slack, if you'd like an invite ping [@Orta](https://twitter.com/orta/) a DM on twitter with your email.
88
+ We try to keep as much discussion as possible in GitHub issues, but also have a pretty inactive slack, if you'd like an invite ping [@Orta](https://twitter.com/orta/) a DM on twitter with your email. It's mostly interesting if you want to stay on top of Danger without all of the emails from GitHub.
243
89
 
244
90
  > 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.
245
91
 
@@ -72,7 +72,7 @@ module Danger
72
72
 
73
73
  print_info = proc do
74
74
  possible_answers.each_with_index do |answer, i|
75
- the_answer = (i == 0) ? answer.underline : answer
75
+ the_answer = i.zero? ? answer.underline : answer
76
76
  ui.print " " + the_answer
77
77
  ui.print(" /") if i != possible_answers.length - 1
78
78
  end
@@ -61,7 +61,7 @@ module Danger
61
61
  # setup caching for Github calls to hitting the API rate limit too quickly
62
62
  cache_file = File.join(ENV["DANGER_TMPDIR"] || Dir.tmpdir, "danger_local_cache")
63
63
  cache = HTTPCache.new(cache_file, clear_cache: @clear_http_cache)
64
- Octokit.middleware = Faraday::Builder.new do |builder|
64
+ Octokit.middleware = Faraday::RackBuilder.new do |builder|
65
65
  builder.use Faraday::HttpCache, store: cache, serializer: Marshal, shared_cache: false
66
66
  builder.use Octokit::Response::RaiseError
67
67
  builder.adapter Faraday.default_adapter
@@ -10,6 +10,7 @@ module Danger
10
10
  attr_accessor :cork
11
11
 
12
12
  def initialize(argv)
13
+ @warnings_as_errors = argv.flag?("warnings-as-errors", false)
13
14
  @refs = argv.arguments! unless argv.arguments.empty?
14
15
  @cork = Cork::Board.new(silent: argv.option("silent", false),
15
16
  verbose: argv.option("verbose", false))
@@ -28,6 +29,12 @@ module Danger
28
29
  CLAide::Argument.new("Paths, Gems or Nothing", false, true)
29
30
  ]
30
31
 
32
+ def self.options
33
+ [
34
+ ["--warnings-as-errors", "Ensure strict linting."]
35
+ ].concat(super)
36
+ end
37
+
31
38
  def run
32
39
  file_resolver = PluginFileResolver.new(@refs)
33
40
  paths = file_resolver.resolve_to_paths
@@ -40,7 +47,8 @@ module Danger
40
47
  linter.lint
41
48
  linter.print_summary(cork)
42
49
 
43
- exit(1) if linter.failed?
50
+ abort("Failing due to errors\n".red) if linter.failed?
51
+ abort("Failing due to warnings as errors\n".red) if @warnings_as_errors && !linter.warnings.empty?
44
52
  end
45
53
  end
46
54
  end
@@ -8,7 +8,7 @@ module Danger
8
8
  self.summary = "Generates a README from a set of plugins"
9
9
  self.command = "readme"
10
10
 
11
- attr_accessor :cork
11
+ attr_accessor :cork, :json
12
12
 
13
13
  def initialize(argv)
14
14
  @refs = argv.arguments! unless argv.arguments.empty?
@@ -37,7 +37,7 @@ module Danger
37
37
  parser.parse
38
38
 
39
39
  self.markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, no_intra_emphasis: true)
40
- self.json = JSON.parse(parser.to_json)
40
+ self.json = JSON.parse(parser.to_json_string)
41
41
 
42
42
  template = File.join(Danger.gem_path, "lib/danger/plugin_support/templates/readme_table.html.erb")
43
43
  cork.puts ERB.new(File.read(template), 0, "-").result(binding)
@@ -8,8 +8,6 @@ require "danger/danger_core/plugins/dangerfile_import_plugin"
8
8
  require "danger/danger_core/plugins/dangerfile_git_plugin"
9
9
  require "danger/danger_core/plugins/dangerfile_github_plugin"
10
10
 
11
- require "danger/danger_core/plugins/dangerfile_github_plugin"
12
-
13
11
  module Danger
14
12
  class Dangerfile
15
13
  include Danger::Dangerfile::DSL
@@ -31,19 +29,14 @@ module Danger
31
29
  # These are the classes that are allowed to also use method_missing
32
30
  # in order to provide broader plugin support
33
31
  def self.core_plugin_classes
34
- [
35
- Danger::DangerfileMessagingPlugin,
36
- Danger::DangerfileImportPlugin,
37
- Danger::DangerfileGitHubPlugin,
38
- Danger::DangerfileGitPlugin
39
- ]
32
+ [Danger::DangerfileMessagingPlugin]
40
33
  end
41
34
 
42
35
  # Both of these methods exist on all objects
43
36
  # http://ruby-doc.org/core-2.2.3/Kernel.html#method-i-warn
44
37
  # http://ruby-doc.org/core-2.2.3/Kernel.html#method-i-fail
45
38
  # However, as we're using using them in the DSL, they won't
46
- # get method_missing called correctly.
39
+ # get method_missing called correctly without overriding them.
47
40
 
48
41
  def warn(*args, &blk)
49
42
  method_missing(:warn, *args, &blk)
@@ -54,7 +47,10 @@ module Danger
54
47
  end
55
48
 
56
49
  # When an undefined method is called, we check to see if it's something
57
- # that the DSLs have, then starts looking at plugins support.
50
+ # that the core DSLs have, then starts looking at plugins support.
51
+
52
+ # rubocop:disable Style/MethodMissing
53
+
58
54
  def method_missing(method_sym, *arguments, &_block)
59
55
  @core_plugins.each do |plugin|
60
56
  if plugin.public_methods(false).include?(method_sym)
@@ -201,7 +197,7 @@ module Danger
201
197
 
202
198
  def print_results
203
199
  status = status_report
204
- return if (status[:errors] + status[:warnings] + status[:messages] + status[:markdowns]).count == 0
200
+ return if (status[:errors] + status[:warnings] + status[:messages] + status[:markdowns]).count.zero?
205
201
 
206
202
  ui.section("Results:") do
207
203
  [:errors, :warnings, :messages].each do |key|
@@ -8,6 +8,10 @@ module Danger
8
8
  #
9
9
  # warn "PR is classed as Work in Progress" if github.pr_title.include? "[WIP]"
10
10
  #
11
+ # @example Declare a PR to be simple to avoid specific Danger rules
12
+ #
13
+ # declared_trivial = (github.pr_title + github.pr_body).include?("#trivial")
14
+ #
11
15
  # @example Ensure that labels have been used on the PR
12
16
  #
13
17
  # fail "Please add labels to this PR" if github.labels.empty?
@@ -15,13 +19,56 @@ module Danger
15
19
  # @example Check if a user is in a specific GitHub org, and message them if so
16
20
  #
17
21
  # unless github.api.organization_member?('danger', github.pr_author)
18
- # message "@#{pr_author} is not a contributor yet, would you like to join the Danger org?"
22
+ # message "@#{github.pr_author} is not a contributor yet, would you like to join the Danger org?"
19
23
  # end
20
24
  #
21
25
  # @example Ensure there is a summary for a PR
22
26
  #
23
27
  # fail "Please provide a summary in the Pull Request description" if github.pr_body.length < 5
24
28
  #
29
+ # @example Only accept PRs to the develop branch
30
+ #
31
+ # fail "Please re-submit this PR to develop, we may have already fixed your issue." if github.branch_for_base != "develop"
32
+ #
33
+ # @example Note when PRs don't reference a milestone, which goes away when it does
34
+ #
35
+ # has_milestone = github.pr_json["milestone"] != nil
36
+ # warn("This PR does not refer to an existing milestone", sticky: false) unless has_milestone
37
+ #
38
+ # @example Note when a PR cannot be manually merged, which goes away when you can
39
+ #
40
+ # can_merge = github.pr_json["mergeable"]
41
+ # warn("This PR cannot be merged yet.", sticky: false) unless can_merge
42
+ #
43
+ # @example Highlight when a celebrity makes a pull request
44
+ #
45
+ # message "Welcome, Danger." if github.pr_author == "dangermcshane"
46
+ #
47
+ # @example Ensure that all PRs have an assignee
48
+ #
49
+ # warn "This PR does not have any assignees yet." unless github.pr_json["assignee"]
50
+ #
51
+ # @example Send a message with links to a collection of specific files
52
+ #
53
+ # if git.modified_files.include? "config/*.js"
54
+ # config_files = git.modified_files.select { |path| path.include? "config/" }
55
+ # message "This PR changes #{ github.html_link(config_files) }"
56
+ # end
57
+ #
58
+ # @example Highlight with a clickable link if a Package.json is changed
59
+ #
60
+ # warn "#{github.html_link("Package.json")} was edited." if git.modified_files.include? "Package.json"
61
+ #
62
+ # @example Note an issue with a particular line on a file using the #L[num] syntax, e.g. `#L23`
63
+ #
64
+ # linter_json = `my_linter lint "file"`
65
+ # results = JSON.parse linter_json
66
+ # unless results.empty?
67
+ # file, line, warning = result.first
68
+ # warn "#{github.html_link("#{file}#L#{line}")} has linter issue: #{warning}."
69
+ # end
70
+ #
71
+ #
25
72
  # @see danger/danger
26
73
  # @tags core, github
27
74
 
@@ -104,7 +151,7 @@ module Danger
104
151
  pr_json[:head][:sha]
105
152
  end
106
153
 
107
- # @!group GitHub Misca
154
+ # @!group GitHub Misc
108
155
  # The hash that represents the PR's JSON. For an example of what this looks like
109
156
  # see the [Danger Fixture'd one](https://raw.githubusercontent.com/danger/danger/master/spec/fixtures/pr_response.json).
110
157
  # @return [Hash]
@@ -120,5 +167,36 @@ module Danger
120
167
  def api
121
168
  @github.client
122
169
  end
170
+
171
+ # @!group PR Content
172
+ # The unified diff produced by Github for this PR
173
+ # see [Unified diff](https://en.wikipedia.org/wiki/Diff_utility#Unified_format)
174
+ # @return [String]
175
+ def pr_diff
176
+ @github.pr_diff
177
+ end
178
+
179
+ # @!group GitHub Misc
180
+ # Returns a HTML anchor for a file, or files in the head repository. An example would be:
181
+ # `<a href='https://github.com/artsy/eigen/blob/561827e46167077b5e53515b4b7349b8ae04610b/file.txt'>file.txt</a>`
182
+ # @return [String]
183
+ def html_link(paths)
184
+ paths = [paths] unless paths.kind_of?(Array)
185
+ commit = head_commit
186
+ repo = pr_json[:head][:repo][:html_url]
187
+ paths = paths.map do |path|
188
+ path_with_slash = "/#{path}" unless path.start_with? "/"
189
+ create_link("#{repo}/blob/#{commit}#{path_with_slash}", path)
190
+ end
191
+
192
+ return paths.first if paths.count < 2
193
+ paths.first(paths.count - 1).join(", ") + " & " + paths.last
194
+ end
195
+
196
+ private
197
+
198
+ def create_link(href, text)
199
+ "<a href='#{href}'>#{text}</a>"
200
+ end
123
201
  end
124
202
  end
@@ -17,6 +17,8 @@ module Danger
17
17
 
18
18
  # Since we have a reference to the Dangerfile containing all the information
19
19
  # We need to redirect the self calls to the Dangerfile
20
+
21
+ # rubocop:disable Style/MethodMissing
20
22
  def method_missing(method_sym, *arguments, &block)
21
23
  @dangerfile.send(method_sym, *arguments, &block)
22
24
  end
@@ -1,5 +1,6 @@
1
1
  require "bundler"
2
2
  require "pathname"
3
+ require "fileutils"
3
4
 
4
5
  module Danger
5
6
  class PluginFileResolver
@@ -17,26 +18,25 @@ module Danger
17
18
  # When given a list of gems
18
19
  elsif @refs and @refs.kind_of? Array
19
20
  Bundler.with_clean_env do
20
- Dir.mktmpdir do |dir|
21
- gem_names = @refs
22
- deps = gem_names.map { |name| Bundler::Dependency.new(name, ">= 0") }
21
+ # We don't use the block syntax as we want it to persist until the OS cleans it on reboot
22
+ # or whatever, it needs to persist outside this scope.
23
+ dir = Dir.mktmpdir
23
24
 
24
- # Use Gems from rubygems.org
25
- source = Bundler::SourceList.new
26
- source.add_rubygems_remote("https://rubygems.org")
25
+ Dir.chdir(dir) do
26
+ gem_names = @refs
27
+ gemfile = File.new("Gemfile", "w")
28
+ gemfile.write "source 'https://rubygems.org'"
27
29
 
28
- # Create a definition to bundle, make sure it always updates
29
- # and uses the latest version from the server
30
- bundler = Bundler::Definition.new(nil, deps, source, true)
31
- bundler.resolve_remotely!
30
+ gem_names.each do |plugin|
31
+ gemfile.write "\ngem '#{plugin}'"
32
+ end
32
33
 
33
- # Install the gems into a tmp dir
34
- options = { path: dir }
35
- Bundler::Installer.install(Pathname.new(dir), bundler, options)
34
+ gemfile.close
35
+ `bundle install --path vendor/gems`
36
36
 
37
- # Get the name'd gems out of bundler, then pull out all their paths
38
- gems = gem_names.flat_map { |name| bundler.specs[name] }
39
- gems.flat_map { |gem| Dir.glob(File.join(gem.gem_dir, "lib/**/**/**.rb")) }
37
+ # the paths are relative to our current Chdir
38
+ relative_paths = gem_names.flat_map { |plugin| Dir.glob("vendor/gems/ruby/*/gems/#{plugin}*/lib/**/**/**/**.rb") }
39
+ relative_paths.map { |path| File.join(dir, path) }
40
40
  end
41
41
  end
42
42
  # When empty, imply you want to test the current lib folder as a plugin
@@ -65,14 +65,16 @@ module Danger
65
65
  end
66
66
 
67
67
  # A generic proc to handle the similarities between
68
- # erros and warnings.
68
+ # errors and warnings.
69
69
  do_rules = proc do |name, rules|
70
70
  unless rules.empty?
71
71
  ui.puts ""
72
72
  ui.section(name.bold) do
73
73
  rules.each do |rule|
74
74
  title = rule.title.bold + " - #{rule.object_applied_to}"
75
- ui.labeled(title, [rule.description, link(rule.ref)])
75
+ subtitles = [rule.description, link(rule.ref)]
76
+ subtitles += [rule.metadata[:files].join(":")] if rule.metadata[:files]
77
+ ui.labeled(title, subtitles)
76
78
  ui.puts ""
77
79
  end
78
80
  end
@@ -115,8 +117,12 @@ module Danger
115
117
  Rule.new(:warning, 43..45, "Params", "You should give a 'type' for the param, yes, ruby is duck-typey but it's useful for newbies to the language, use `@param [Type] name`.", proc do |json|
116
118
  json[:param_couplets] && json[:param_couplets].flat_map(&:values).include?(nil)
117
119
  end),
120
+ Rule.new(:warning, 43..45, "Unknown Param", "You should give a 'type' for the param, yes, ruby is duck-typey but it's useful for newbies to the language, use `@param [Type] name`.", proc do |json|
121
+ json[:param_couplets] && json[:param_couplets].flat_map(&:values).include?("Unknown")
122
+ end),
118
123
  Rule.new(:warning, 46, "Return Type", "If the function has no useful return value, use ` @return [void]` - this will be ignored by documentation generators.", proc do |json|
119
- json[:return] && json[:return].empty?
124
+ return_hash = json[:tags].find { |tag| tag[:name] == "return" }
125
+ !(return_hash && return_hash[:types] && !return_hash[:types].first.empty?)
120
126
  end)
121
127
  ]
122
128
  end
@@ -103,29 +103,37 @@ module Danger
103
103
  ""
104
104
  end
105
105
 
106
- def method_params(params)
107
- return {} unless params[:params]
106
+ def method_params(method)
107
+ return {} unless method[:params]
108
108
 
109
- params_names = params[:params].compact.flat_map(&:first)
110
- params_values = params[:tags].find { |t| t[:name] == "param" }
109
+ params_names = method[:params].map { |param| param.compact.join("=").strip }
110
+ params_values = method[:tags].select { |t| t[:name] == "param" }
111
111
 
112
- return {} if params_values.nil?
113
- return {} if params_values[:types].nil?
112
+ return {} if params_values.empty?
113
+ return {} if params_values.select { |p| p[:types] }.empty?
114
114
 
115
115
  return params_names.map.with_index do |name, index|
116
- { name => params_values[:types][index] }
116
+ name = name.delete ":"
117
+ if index < params_values.length
118
+ type = params_values[index][:types]
119
+ { name => type ? type.first : "Unknown" }
120
+ else
121
+ { name => "Unknown" }
122
+ end
117
123
  end
118
124
  end
119
125
 
120
- def method_parser(meth)
126
+ def method_parser(gem_path, meth)
121
127
  return nil if meth.nil?
122
128
  method = {
123
129
  name: meth.name,
124
130
  body_md: meth.docstring,
125
131
  params: meth.parameters,
132
+ files: meth.files.map { |item| [item.first.gsub(gem_path, ""), item.last] },
126
133
  tags: meth.tags.map { |t| { name: t.tag_name, types: t.types } }
127
134
  }
128
135
 
136
+
129
137
  return_v = method_return_string(method)
130
138
  params_v = method_params(method)
131
139
 
@@ -148,10 +156,10 @@ module Danger
148
156
  method
149
157
  end
150
158
 
151
- def attribute_parser(attribute)
159
+ def attribute_parser(gem_path, attribute)
152
160
  {
153
- read: method_parser(attribute[:read]),
154
- write: method_parser(attribute[:write])
161
+ read: method_parser(gem_path, attribute[:read]),
162
+ write: method_parser(gem_path, attribute[:write])
155
163
  }
156
164
  end
157
165
 
@@ -165,16 +173,24 @@ module Danger
165
173
  methods = klass.meths - klass.inherited_meths - attribute_meths
166
174
  usable_methods = methods.select { |m| m.visibility == :public }.reject { |m| m.name == :initialize || m.name == :instance_name }
167
175
 
176
+ plugin_gem = klass.file.include?("gems") ? klass.file.split("gems/").last.split("-")[0..-2].join("-") : nil
177
+ # Pull out the gem's path ( to make relative file paths )
178
+ # if no gem is found, index = 0, making gem_path = ""
179
+ index_of_gem_in_path = plugin_gem ? klass.file.split("/").index { |component| component.include? plugin_gem } : 0
180
+ gem_path = klass.file.split("/")[0..index_of_gem_in_path].join("/")
181
+
168
182
  {
169
183
  name: klass.name.to_s,
170
184
  body_md: klass.docstring,
171
185
  instance_name: real_klass.instance_name,
186
+ gem: plugin_gem,
187
+ gem_path: gem_path,
188
+ files: klass.files.map { |item| [item.first.gsub(gem_path, ""), item.last] },
172
189
  example_code: klass.tags.select { |t| t.tag_name == "example" }.map { |tag| { title: tag.name, text: tag.text } }.compact,
173
- attributes: klass.attributes[:instance].map { |pair| { pair.first => attribute_parser(pair.last) } },
174
- methods: usable_methods.map { |m| method_parser(m) },
190
+ attributes: klass.attributes[:instance].map { |pair| { pair.first => attribute_parser(gem_path, pair.last) } },
191
+ methods: usable_methods.map { |m| method_parser(gem_path, m) },
175
192
  tags: klass.tags.select { |t| t.tag_name == "tags" }.map(&:text).compact,
176
193
  see: klass.tags.select { |t| t.tag_name == "see" }.map(&:name).map(&:split).flatten.compact,
177
- file: klass.file.gsub(File.expand_path("."), "")
178
194
  }
179
195
  end
180
196
  end
@@ -36,6 +36,10 @@ module Danger
36
36
  @markdown_parser ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, no_intra_emphasis: true)
37
37
  end
38
38
 
39
+ def pr_diff
40
+ @pr_diff ||= client.pull_request(ci_source.repo_slug, ci_source.pull_request_id, accept: 'application/vnd.github.v3.diff')
41
+ end
42
+
39
43
  def setup_danger_branches
40
44
  # we can use a github specific feature here:
41
45
  base_commit = self.pr_json[:base][:sha]
@@ -107,7 +111,7 @@ module Danger
107
111
  end
108
112
 
109
113
  def submit_pull_request_status!(warnings: [], errors: [], details_url: [])
110
- status = (errors.count == 0 ? 'success' : 'failure')
114
+ status = (errors.count.zero? ? 'success' : 'failure')
111
115
  message = generate_github_description(warnings: warnings, errors: errors)
112
116
 
113
117
  latest_pr_commit_ref = self.pr_json[:head][:sha]
@@ -1,4 +1,4 @@
1
1
  module Danger
2
- VERSION = "0.10.1".freeze
2
+ VERSION = "2.0.0".freeze
3
3
  DESCRIPTION = "Automate your PR etiquette.".freeze
4
4
  end
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.10.1
4
+ version: 2.0.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-07-23 00:00:00.000000000 Z
12
+ date: 2016-07-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: claide
@@ -29,16 +29,16 @@ dependencies:
29
29
  name: claide-plugins
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - "~>"
32
+ - - ">"
33
33
  - !ruby/object:Gem::Version
34
- version: '0.9'
34
+ version: 0.9.0
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - "~>"
39
+ - - ">"
40
40
  - !ruby/object:Gem::Version
41
- version: '0.9'
41
+ version: 0.9.0
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: git
44
44
  requirement: !ruby/object:Gem::Requirement
@@ -383,9 +383,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
383
383
  version: '0'
384
384
  requirements: []
385
385
  rubyforge_project:
386
- rubygems_version: 2.4.8
386
+ rubygems_version: 2.2.2
387
387
  signing_key:
388
388
  specification_version: 4
389
389
  summary: Automate your PR etiquette.
390
390
  test_files: []
391
- has_rdoc: