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 +4 -4
- data/README.md +42 -196
- data/lib/danger/commands/init_helpers/interviewer.rb +1 -1
- data/lib/danger/commands/local.rb +1 -1
- data/lib/danger/commands/plugins/plugin_lint.rb +9 -1
- data/lib/danger/commands/plugins/plugin_readme.rb +2 -2
- data/lib/danger/danger_core/dangerfile.rb +7 -11
- data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +80 -2
- data/lib/danger/plugin_support/plugin.rb +2 -0
- data/lib/danger/plugin_support/plugin_file_resolver.rb +16 -16
- data/lib/danger/plugin_support/plugin_linter.rb +9 -3
- data/lib/danger/plugin_support/plugin_parser.rb +30 -14
- data/lib/danger/request_source/github.rb +5 -1
- data/lib/danger/version.rb +1 -1
- metadata +7 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db32b8346a72c88968c63bae306a254f91484df2
|
4
|
+
data.tar.gz: df97dfe2ec512b2ee4fb0fa16161b6b4cffc2307
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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="#
|
12
|
-
<a href="#
|
13
|
-
<a href="#
|
14
|
-
<a href="#plugins">Plugins</a> •
|
15
|
-
<a href="#test-locally-with-danger-local">Local</a>
|
11
|
+
<a href="#what-is-danger">What is Danger?</a> •
|
12
|
+
<a href="#im-here-to-help-out">Helping Out</a> •
|
13
|
+
<a href="#tell-me-of-these-plugins">Plugin Development</a> •
|
16
14
|
</p>
|
17
15
|
|
18
16
|
-------
|
19
17
|
|
20
|
-
##
|
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
|
-
| | 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
|
-
|
20
|
+
Danger runs after your CI, and gives teams the chance to automate common code review chores.
|
103
21
|
|
104
|
-
|
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
|
-
|
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
|
-
|
26
|
+
## For example?
|
112
27
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
143
|
-
```
|
45
|
+
## I'm here to help out!
|
144
46
|
|
145
|
-
|
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
|
-
|
56
|
+
This sets everything up and runs all of the tests.
|
156
57
|
|
157
|
-
|
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
|
-
|
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
|
-
|
62
|
+
#### Documentation
|
167
63
|
|
168
|
-
|
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
|
-
|
172
|
-
history, then append `--use-merged-pr=[id]` to the command.
|
66
|
+
#### Testing
|
173
67
|
|
174
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
77
|
+
require 'pry'
|
78
|
+
binding.pry
|
202
79
|
```
|
203
80
|
|
204
|
-
##
|
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
|
-
|
236
|
-
|
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 =
|
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::
|
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
|
-
|
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.
|
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
|
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
|
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
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
bundler.resolve_remotely!
|
30
|
+
gem_names.each do |plugin|
|
31
|
+
gemfile.write "\ngem '#{plugin}'"
|
32
|
+
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
Bundler::Installer.install(Pathname.new(dir), bundler, options)
|
34
|
+
gemfile.close
|
35
|
+
`bundle install --path vendor/gems`
|
36
36
|
|
37
|
-
#
|
38
|
-
|
39
|
-
|
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
|
-
#
|
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
|
-
|
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[:
|
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(
|
107
|
-
return {} unless
|
106
|
+
def method_params(method)
|
107
|
+
return {} unless method[:params]
|
108
108
|
|
109
|
-
params_names =
|
110
|
-
params_values =
|
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.
|
113
|
-
return {} if params_values[:types].
|
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
|
-
|
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
|
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]
|
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: 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-
|
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:
|
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:
|
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.
|
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:
|