netsoft-danger 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +130 -0
- data/.gitignore +5 -0
- data/Dangerfile +105 -0
- data/Gemfile +3 -0
- data/README.md +4 -0
- data/Rakefile +5 -0
- data/bin/netsoft-circle +86 -0
- data/bin/tag_check.sh +17 -0
- data/dangerfiles/issue.js +3 -0
- data/dangerfiles/pr.js +49 -0
- data/lib/netsoft-danger/cops/netsoft/auth_http_positional_arguments.rb +142 -0
- data/lib/netsoft-danger/cops.rb +1 -0
- data/lib/netsoft-danger/version.rb +3 -0
- data/lib/netsoft-danger.rb +3 -0
- data/netsoft-danger.gemspec +25 -0
- data/package.json +5 -0
- data/rubocop/config-2019-08-12.yml +46 -0
- data/settings-peril.json +10 -0
- data/yarn.lock +677 -0
- metadata +160 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ed4179318732a96ccb2143ed81c4883f5b3d09b510430dc90444e30d5884bb6d
|
4
|
+
data.tar.gz: 960344a9abbf87cf2822b69df41732f1e4f656c496e3e2b7397c82b6995bcf7d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d55935c5a579644f9fc95cb649a51ab00d200fecea37ef19cb7e30ca88cb672bb90e37bfa74f256507cb4e07fd6e3af9260851d9d95e6ca012bc108968f16ff2
|
7
|
+
data.tar.gz: 8ec910b5a74b6b419cfb1bbac8ab87d0eab232c97f3e0550257a0957e01282dff2e2f42c9cb20f9385586f7a5dffb19c88480b6656f58e12ce72eea04674e99d
|
@@ -0,0 +1,130 @@
|
|
1
|
+
version: 2
|
2
|
+
|
3
|
+
defaults: &defaults
|
4
|
+
docker: &ruby_image
|
5
|
+
- &ruby_image
|
6
|
+
image: circleci/ruby:2.4.4-stretch
|
7
|
+
environment:
|
8
|
+
RUBYOPT: '-KU -E utf-8:utf-8'
|
9
|
+
BUNDLE_PATH: vendor/bundle
|
10
|
+
BUNDLE_VERSION: 1.15.2
|
11
|
+
BUNDLE_JOBS: 4
|
12
|
+
BUNDLE_RETRY: 3
|
13
|
+
|
14
|
+
filters:
|
15
|
+
test: &filter_test
|
16
|
+
filters:
|
17
|
+
tags:
|
18
|
+
ignore: /^v.*/
|
19
|
+
beta: &filter_beta
|
20
|
+
filters:
|
21
|
+
branches:
|
22
|
+
ignore: /.*/
|
23
|
+
tags:
|
24
|
+
only: /^v[0-9]+(\.[0-9]+)+(\.[a-z].+).*/
|
25
|
+
release: &filter_release
|
26
|
+
filters:
|
27
|
+
branches:
|
28
|
+
ignore: /.*/
|
29
|
+
tags:
|
30
|
+
only: /^v[0-9]+(\.[0-9]+)+/
|
31
|
+
|
32
|
+
workflows:
|
33
|
+
version: 2
|
34
|
+
build_test:
|
35
|
+
jobs:
|
36
|
+
- "Checkout":
|
37
|
+
<<: *filter_test
|
38
|
+
context: org-global
|
39
|
+
- "Build":
|
40
|
+
<<: *filter_test
|
41
|
+
context: org-global
|
42
|
+
requires:
|
43
|
+
- "Checkout"
|
44
|
+
build_test_beta:
|
45
|
+
jobs:
|
46
|
+
- "Checkout":
|
47
|
+
<<: *filter_beta
|
48
|
+
context: org-global
|
49
|
+
- "Build":
|
50
|
+
<<: *filter_beta
|
51
|
+
context: org-global
|
52
|
+
requires:
|
53
|
+
- "Checkout"
|
54
|
+
- "Publish":
|
55
|
+
<<: *filter_beta
|
56
|
+
context: org-global
|
57
|
+
requires:
|
58
|
+
- "Build"
|
59
|
+
build_test_release:
|
60
|
+
jobs:
|
61
|
+
- "Checkout":
|
62
|
+
<<: *filter_release
|
63
|
+
context: org-global
|
64
|
+
- "Build":
|
65
|
+
<<: *filter_release
|
66
|
+
context: org-global
|
67
|
+
requires:
|
68
|
+
- "Checkout"
|
69
|
+
- "Publish":
|
70
|
+
<<: *filter_release
|
71
|
+
context: org-global
|
72
|
+
requires:
|
73
|
+
- "Build"
|
74
|
+
|
75
|
+
jobs:
|
76
|
+
"Checkout":
|
77
|
+
<<: *defaults
|
78
|
+
steps:
|
79
|
+
- attach_workspace:
|
80
|
+
at: .
|
81
|
+
- checkout
|
82
|
+
|
83
|
+
- restore_cache:
|
84
|
+
keys:
|
85
|
+
- netsoft-danger-bundle-v2-{{ checksum "Gemfile" }}-{{ checksum "netsoft-danger.gemspec" }}
|
86
|
+
- run:
|
87
|
+
name: Install bundler
|
88
|
+
command: gem install bundler --version=$BUNDLE_VERSION
|
89
|
+
- run:
|
90
|
+
name: Bundle Install
|
91
|
+
command: |-
|
92
|
+
bundle _${BUNDLE_VERSION}_ check || bundle _${BUNDLE_VERSION}_ install --retry=$BUNDLE_RETRY
|
93
|
+
- save_cache:
|
94
|
+
key: netsoft-danger-bundle-v2-{{ checksum "Gemfile" }}-{{ checksum "netsoft-danger.gemspec" }}
|
95
|
+
paths:
|
96
|
+
- vendor/bundle
|
97
|
+
- Gemfile.lock
|
98
|
+
|
99
|
+
- persist_to_workspace:
|
100
|
+
root: .
|
101
|
+
paths: .
|
102
|
+
"Build":
|
103
|
+
<<: *defaults
|
104
|
+
steps:
|
105
|
+
- attach_workspace:
|
106
|
+
at: .
|
107
|
+
- run:
|
108
|
+
name: Install bundler
|
109
|
+
command: gem install bundler --version=$BUNDLE_VERSION
|
110
|
+
- run:
|
111
|
+
name: Build gem
|
112
|
+
command: |-
|
113
|
+
gem build *.gemspec
|
114
|
+
- run:
|
115
|
+
name: Run Danger
|
116
|
+
command: |-
|
117
|
+
bundle exec danger
|
118
|
+
"Publish":
|
119
|
+
<<: *defaults
|
120
|
+
steps:
|
121
|
+
- attach_workspace:
|
122
|
+
at: .
|
123
|
+
- run:
|
124
|
+
name: Deploy to gem server
|
125
|
+
command: |-
|
126
|
+
./bin/tag_check.sh
|
127
|
+
rm -rf pkg
|
128
|
+
gem install geminabox
|
129
|
+
rake build
|
130
|
+
gem inabox -g ${HUBSTAFF_GEM_SERVER} pkg/*
|
data/.gitignore
ADDED
data/Dangerfile
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# Don't let testing shortcuts get into master by accident
|
2
|
+
if Dir.exists?('spec')
|
3
|
+
fail("fdescribe left in tests") if `grep -r -e '\\bfdescribe\\b' spec/ |grep -v 'danger ok' `.length > 1
|
4
|
+
fail("fcontext left in tests") if `grep -r -e '\\bfcontext\\b' spec/ |grep -v 'danger ok' `.length > 1
|
5
|
+
fail("fit left in tests") if `grep -r -e '\\bfit\\b' spec/ | grep -v 'danger ok' `.length > 1
|
6
|
+
fail("ap left in tests") if `grep -r -e '\\bap\\b' spec/ | grep -v 'danger ok' `.length > 1
|
7
|
+
fail("puts left in tests") if `grep -r -e '\\bputs\\b' spec/ | grep -v 'danger ok' `.length > 1
|
8
|
+
end
|
9
|
+
|
10
|
+
if File.exists?('Gemfile')
|
11
|
+
if `grep -r -e "^ *gem 'hubstaff_[a-z]\\+" Gemfile | grep -e ",.\\+[a-zA-Z]" `.length > 1
|
12
|
+
fail("[gemfile] Beta hubstaff_* gems are not allowed in master/production")
|
13
|
+
end
|
14
|
+
if `grep -r -e "^ *gem 'hubstaff_[a-z]\\+" Gemfile | grep -e ",.\\+'[~>=]\\+.\\+[a-zA-Z]" `.length > 1
|
15
|
+
fail("[gemfile] Beta hubstaff_* gems should be the exact version")
|
16
|
+
end
|
17
|
+
if `grep -r -e "^ *gem 'hubstaff_[a-z]\\+" Gemfile | grep -e ",.\\+[' ][0-9.]\\+'" | grep -v '~>' `.length > 1
|
18
|
+
fail("[gemfile] Release hubstaff_* gems should be using a ~> version")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
git.commits.each do |c|
|
23
|
+
short = " ( #{c.sha[0..7]} )"
|
24
|
+
has_migrations = c.diff_parent.any? {|f| f.path =~ /db\/migrate\// }
|
25
|
+
has_schema_changes = c.diff_parent.any? {|f| f.path =~ /db\/schema\.rb/ }
|
26
|
+
has_migration_msg = c.message =~ /^\[migration\]/
|
27
|
+
no_schema_ok = ENV['DANGER_NO_SCHEMA_OK'] || false
|
28
|
+
if has_migrations || has_schema_changes
|
29
|
+
unless has_migration_msg
|
30
|
+
fail '[migration] Schema migration commits need to be prefixed with [migration]' + short
|
31
|
+
end
|
32
|
+
if has_migrations && !has_schema_changes && !no_schema_ok
|
33
|
+
fail '[migration] Please checkin your schema.rb changes with your migration' + short
|
34
|
+
end
|
35
|
+
if !has_migrations && has_schema_changes
|
36
|
+
warn '[migration] Please checkin your migrations with your schema.rb changes' + short
|
37
|
+
end
|
38
|
+
if c.diff_parent.any? {|f| !( f.path =~ /db\/migrate\// or f.path =~ /db\/schema.rb/ ) }
|
39
|
+
fail '[migration] Migration commit contains non-migration changes' + short
|
40
|
+
end
|
41
|
+
elsif has_migration_msg
|
42
|
+
fail '[migration] Migration commit with no migrations!' + short
|
43
|
+
end
|
44
|
+
|
45
|
+
has_hubstaff_icon_changes = c.diff_parent.any? {|f| f.path =~ /hubstaff(icons|font)/ || f.path =~ /fontcustom-manifest/ }
|
46
|
+
if has_hubstaff_icon_changes
|
47
|
+
if c.diff_parent.any? {|f| !( f.path =~ /hubstaff-(icons|font)/ || f.path =~ /fontcustom-manifest/ ) }
|
48
|
+
fail '[hubstaff-icons] Put hubstaff-icon changes into their own commit' + short
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
has_gemfile_changes = c.diff_parent.any? {|f| f.path =~ /Gemfile/ || f.path =~ /gemspec/ }
|
53
|
+
has_gemfile_msg = c.message =~ /^\[gemfile\]/
|
54
|
+
if has_gemfile_changes
|
55
|
+
unless has_gemfile_msg
|
56
|
+
fail '[gemfile] Gemfile commits need to be prefixed with [gemfile] ' + short
|
57
|
+
end
|
58
|
+
if c.diff_parent.any? {|f| !( f.path =~ /Gemfile/ || f.path =~ /gemspec/ ) }
|
59
|
+
fail '[gemfile] Gemfile commit contains non-gemfile changes' + short
|
60
|
+
end
|
61
|
+
if c.diff_parent.any? {|f| f.path == 'Gemfile.lock' }
|
62
|
+
unless `grep -e '^ 1.15.2$' Gemfile.lock`.length > 1
|
63
|
+
fail("[gemfile] Gemfile not bundled with bundler 1.15.2")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
elsif has_gemfile_msg
|
67
|
+
fail '[gemfile] Gemfile commit has no gemfile changes!' + short
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
require 'open-uri'
|
72
|
+
|
73
|
+
artifact_url = "https://circleci.com/api/v1.1/project/github/#{ENV['CIRCLE_PROJECT_USERNAME']}/#{ENV["CIRCLE_PROJECT_REPONAME"]}/#{ENV['CIRCLE_BUILD_NUM']}/artifacts?circle-token=#{ENV['CIRCLE_TOKEN']}"
|
74
|
+
artifacts = JSON.load(open(artifact_url).read).map{|a| a["url"]}
|
75
|
+
|
76
|
+
jest = artifacts.find{ |artifact| artifact.end_with?('jest/index.html') }
|
77
|
+
coverage = artifacts.find{ |artifact| artifact.end_with?('coverage/index.html') }
|
78
|
+
rubocop = artifacts.find{ |artifact| artifact.end_with?('rubocop/report.html') }
|
79
|
+
eslint = artifacts.find{ |artifact| artifact.end_with?('eslint/report.html') }
|
80
|
+
rspec_files = artifacts.select{ |artifact| artifact =~ /rspec-(.+)\.html$/ }
|
81
|
+
|
82
|
+
{}.tap do |hash|
|
83
|
+
hash['Ruby coverage report'] = coverage if coverage
|
84
|
+
hash['RSpec test report'] = rspec_files unless rspec_files.empty?
|
85
|
+
hash['RuboCop inspection report'] = rubocop if rubocop
|
86
|
+
hash['ESLint inspection report'] = eslint if eslint
|
87
|
+
hash['Jest coverage report'] = jest if jest
|
88
|
+
end.each do |msg, links|
|
89
|
+
links = [*links]
|
90
|
+
if links.size == 1
|
91
|
+
message("[#{msg}](#{links[0]})")
|
92
|
+
else
|
93
|
+
r = /rspec-(.+)\.html$/
|
94
|
+
the_links = links.map do |l|
|
95
|
+
m = r.match(l)
|
96
|
+
if m
|
97
|
+
"[#{m[1]}](#{l})"
|
98
|
+
else
|
99
|
+
"[link](#{l})"
|
100
|
+
end
|
101
|
+
end.join(', ')
|
102
|
+
|
103
|
+
message("#{msg} - #{the_links}")
|
104
|
+
end
|
105
|
+
end
|
data/Gemfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
data/bin/netsoft-circle
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
class NetsoftCircle < Thor
|
7
|
+
desc 'setup', 'Setup Heroku for deployment'
|
8
|
+
def setup
|
9
|
+
|
10
|
+
File.open("#{ENV['HOME']}/.netrc", 'a+') do |file|
|
11
|
+
file << <<-EOF
|
12
|
+
|
13
|
+
machine api.heroku.com
|
14
|
+
login #{ENV['HEROKU_LOGIN']}
|
15
|
+
password #{ENV['HEROKU_API_KEY']}
|
16
|
+
machine git.heroku.com
|
17
|
+
login #{ENV['HEROKU_LOGIN']}
|
18
|
+
password #{ENV['HEROKU_API_KEY']}
|
19
|
+
EOF
|
20
|
+
end
|
21
|
+
|
22
|
+
Dir.mkdir("#{ENV['HOME']}/.ssh") unless Dir.exists?("#{ENV['HOME']}/.ssh")
|
23
|
+
File.open("#{ENV['HOME']}/.ssh/config", 'a+') do |file|
|
24
|
+
file << <<-EOF
|
25
|
+
|
26
|
+
VerifyHostKeyDNS yes
|
27
|
+
StrictHostKeyChecking no
|
28
|
+
EOF
|
29
|
+
end
|
30
|
+
|
31
|
+
system('git config --global url."https://git.heroku.com/".insteadOf heroku:')
|
32
|
+
exit(1) unless $?.success?
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'merge', 'Merges several simplecov json result files'
|
36
|
+
option :output, type: :string, aliases: '-o', desc: 'Specify an alternate output directory for the produced coverage data'
|
37
|
+
def merge(*files)
|
38
|
+
require 'simplecov'
|
39
|
+
return if files.empty?
|
40
|
+
results = []
|
41
|
+
|
42
|
+
files.each do |file|
|
43
|
+
json = JSON.parse(File.read(file))
|
44
|
+
json.each do |command_name, data|
|
45
|
+
result = SimpleCov::Result.from_hash(command_name => data)
|
46
|
+
results << result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
merged_result = SimpleCov::ResultMerger.merge_results(*results)
|
51
|
+
SimpleCov.coverage_dir options[:output] unless options[:output].nil?
|
52
|
+
SimpleCov::Formatter::HTMLFormatter.new.format(merged_result)
|
53
|
+
end
|
54
|
+
|
55
|
+
desc 'rspec', 'Run rspec'
|
56
|
+
def rspec
|
57
|
+
system <<-EOF
|
58
|
+
bundle _${BUNDLE_VERSION}_ exec rspec \
|
59
|
+
--color \
|
60
|
+
--format RspecJunitFormatter \
|
61
|
+
--out $CIRCLE_TEST_REPORTS/rspec/junit.xml \
|
62
|
+
--format html \
|
63
|
+
--out $CIRCLE_ARTIFACTS/rspec.html \
|
64
|
+
--format progress \
|
65
|
+
$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
|
66
|
+
EOF
|
67
|
+
exit(1) unless $?.success?
|
68
|
+
end
|
69
|
+
|
70
|
+
desc 'rubocop', 'Run rubocop'
|
71
|
+
def rubocop
|
72
|
+
system <<-EOF
|
73
|
+
bundle _${BUNDLE_VERSION}_ exec rubocop \
|
74
|
+
--format progress \
|
75
|
+
--format html \
|
76
|
+
--out $CIRCLE_ARTIFACTS/rubocop/report.html
|
77
|
+
EOF
|
78
|
+
exit(1) unless $?.success?
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.exit_on_failure?
|
82
|
+
true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
NetsoftCircle.start
|
data/bin/tag_check.sh
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
tag=`git describe --tags --exact-match HEAD 2> /dev/null`
|
4
|
+
|
5
|
+
if [ $? -eq 0 ]; then
|
6
|
+
version=`grep VERSION lib/netsoft-danger/version.rb | sed -e "s/.*'\([^']*\)'.*/\1/"`
|
7
|
+
|
8
|
+
if [ "v$version" = "$tag" ]; then
|
9
|
+
echo "Revision $tag Matches $version"
|
10
|
+
else
|
11
|
+
echo "Revision $tag does not match $version"
|
12
|
+
exit 2
|
13
|
+
fi
|
14
|
+
else
|
15
|
+
echo "No tag found"
|
16
|
+
exit 1
|
17
|
+
fi
|
data/dangerfiles/pr.js
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
import {danger, warn, fail, message} from "danger";
|
2
|
+
|
3
|
+
if (!danger.github.issue.labels.length) {
|
4
|
+
fail('Please add labels to this PR');
|
5
|
+
}
|
6
|
+
|
7
|
+
var labels = danger.github.issue.labels.map(l => l.name);
|
8
|
+
|
9
|
+
// Make it more obvious that a PR is a work in progress and shouldn't be merged yet
|
10
|
+
if (labels.includes('work in progress') || danger.git.commits.find(c => c && c.message.includes('WIP'))) {
|
11
|
+
fail("PR is a Work in Progress");
|
12
|
+
}
|
13
|
+
|
14
|
+
if ((danger.github.pr.body || '').length < 5) {
|
15
|
+
fail("Please provide a summary in the Pull Request description");
|
16
|
+
}
|
17
|
+
|
18
|
+
if (!labels.includes('review passed')) {
|
19
|
+
fail("Has not passed code-review");
|
20
|
+
}
|
21
|
+
|
22
|
+
if (!labels.includes('QA passed')) {
|
23
|
+
fail("Has not passed QA");
|
24
|
+
}
|
25
|
+
|
26
|
+
if (!danger.github.pr.base.ref.includes('master')) {
|
27
|
+
warn("PR base is not set to master!");
|
28
|
+
}
|
29
|
+
|
30
|
+
// Warn when there is a big PR
|
31
|
+
if (500 < (danger.github.pr.additions + danger.github.pr.deletions)) {
|
32
|
+
warn(':exclamation: Big PR');
|
33
|
+
}
|
34
|
+
|
35
|
+
if (danger.git.commits.find(c => c && c.message.match(/Merge branch/))) {
|
36
|
+
fail('Please rebase to remove merge commits in this PR');
|
37
|
+
}
|
38
|
+
|
39
|
+
if (danger.git.commits.find(c => c && c.message.match(/(fixup|squash)!/))) {
|
40
|
+
fail("Contains a fixup or squash commit");
|
41
|
+
}
|
42
|
+
|
43
|
+
if (!danger.github.pr.milestone) {
|
44
|
+
fail('Requires a milestone before merging!');
|
45
|
+
} else {
|
46
|
+
// we can do additional checks on the milestone name if we want.
|
47
|
+
// e.g. inspect milestone.title or milestone.description
|
48
|
+
}
|
49
|
+
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubocop/cop/mixin/target_rails_version'
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
module Cop
|
7
|
+
module Netsoft
|
8
|
+
# This cop is used to identify usages of http methods like `get`, `post`,
|
9
|
+
# `put`, `patch` without the usage of keyword arguments in your tests and
|
10
|
+
# change them to use keyword args. This cop only applies to Rails >= 5.
|
11
|
+
# If you are running Rails < 5 you should disable the
|
12
|
+
# Netsoft/HttpPositionalArguments cop or set your TargetRailsVersion in your
|
13
|
+
# .rubocop.yml file to 4.0, etc.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# # bad
|
17
|
+
# get :new, { user_id: 1}
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# get :new, params: { user_id: 1 }
|
21
|
+
class AuthHttpPositionalArguments < Cop
|
22
|
+
extend RuboCop::Cop::TargetRailsVersion
|
23
|
+
|
24
|
+
MSG = 'Use keyword arguments instead of ' \
|
25
|
+
'positional arguments for http call: `%<verb>s`.'.freeze
|
26
|
+
KEYWORD_ARGS = %i[
|
27
|
+
method params session body flash xhr as headers env
|
28
|
+
].freeze
|
29
|
+
HTTP_METHODS = %i[get post put patch delete head].freeze
|
30
|
+
HTTP_AUTH_METHODS = %i[get_with post_with put_with patch_with delete_with].freeze
|
31
|
+
|
32
|
+
minimum_target_rails_version 5.0
|
33
|
+
|
34
|
+
def_node_matcher :http_request?, <<-PATTERN
|
35
|
+
(send nil? {#{HTTP_METHODS.map(&:inspect).join(' ')}} !nil? $_ ...)
|
36
|
+
PATTERN
|
37
|
+
|
38
|
+
def_node_matcher :http_auth_request?, <<-PATTERN
|
39
|
+
(send nil? {#{HTTP_AUTH_METHODS.map(&:inspect).join(' ')}} !nil? !nil? $_ ...)
|
40
|
+
PATTERN
|
41
|
+
|
42
|
+
def on_send(node)
|
43
|
+
http_request?(node) do |data|
|
44
|
+
return unless needs_conversion?(data)
|
45
|
+
|
46
|
+
add_offense(node, location: :selector,
|
47
|
+
message: format(MSG, verb: node.method_name))
|
48
|
+
end
|
49
|
+
|
50
|
+
http_auth_request?(node) do |data|
|
51
|
+
return unless auth_needs_conversion?(data)
|
52
|
+
|
53
|
+
add_offense(node, location: :selector,
|
54
|
+
message: format(MSG, verb: node.method_name))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# given a pre Rails 5 method: get :new, {user_id: @user.id}, {}
|
59
|
+
#
|
60
|
+
# @return lambda of auto correct procedure
|
61
|
+
# the result should look like:
|
62
|
+
# get :new, params: { user_id: @user.id }, session: {}
|
63
|
+
# the http_method is the method used to call the controller
|
64
|
+
# the controller node can be a symbol, method, object or string
|
65
|
+
# that represents the path/action on the Rails controller
|
66
|
+
# the data is the http parameters and environment sent in
|
67
|
+
# the Rails 5 http call
|
68
|
+
def autocorrect(node)
|
69
|
+
has_auth = HTTP_AUTH_METHODS.include?(node.method_name.to_sym)
|
70
|
+
if has_auth
|
71
|
+
user, http_path, *data = *node.arguments
|
72
|
+
else
|
73
|
+
http_path, *data = *node.arguments
|
74
|
+
end
|
75
|
+
|
76
|
+
controller_action = http_path.source
|
77
|
+
params = convert_hash_data(data.first, 'params')
|
78
|
+
session = convert_hash_data(data.last, 'session') if data.size > 1
|
79
|
+
# the range of the text to replace, which is the whole line
|
80
|
+
code_to_replace = node.loc.expression
|
81
|
+
# what to replace with
|
82
|
+
format = parentheses_format(node)
|
83
|
+
new_code = format(format, name: node.method_name,
|
84
|
+
action: has_auth ? [user.source, controller_action].join(',') : controller_action,
|
85
|
+
params: params, session: session)
|
86
|
+
->(corrector) { corrector.replace(code_to_replace, new_code) }
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def needs_conversion?(data)
|
92
|
+
return true unless data.hash_type?
|
93
|
+
|
94
|
+
data.each_pair.none? do |pair|
|
95
|
+
special_keyword_arg?(pair.key) ||
|
96
|
+
format_arg?(pair.key) && data.pairs.one?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def auth_needs_conversion?(data)
|
101
|
+
return true unless data.hash_type?
|
102
|
+
|
103
|
+
data.each_pair.none? do |pair|
|
104
|
+
special_keyword_arg?(pair.key) ||
|
105
|
+
format_arg?(pair.key) && data.pairs.one?
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def special_keyword_arg?(node)
|
110
|
+
node.sym_type? && KEYWORD_ARGS.include?(node.value)
|
111
|
+
end
|
112
|
+
|
113
|
+
def format_arg?(node)
|
114
|
+
node.sym_type? && node.value == :format
|
115
|
+
end
|
116
|
+
|
117
|
+
def convert_hash_data(data, type)
|
118
|
+
return '' if data.hash_type? && data.empty?
|
119
|
+
|
120
|
+
hash_data = if data.hash_type?
|
121
|
+
format('{ %<data>s }',
|
122
|
+
data: data.pairs.map(&:source).join(', '))
|
123
|
+
else
|
124
|
+
# user supplies an object,
|
125
|
+
# no need to surround with braces
|
126
|
+
data.source
|
127
|
+
end
|
128
|
+
|
129
|
+
format(', %<type>s: %<hash_data>s', type: type, hash_data: hash_data)
|
130
|
+
end
|
131
|
+
|
132
|
+
def parentheses_format(node)
|
133
|
+
if parentheses?(node)
|
134
|
+
'%<name>s(%<action>s%<params>s%<session>s)'
|
135
|
+
else
|
136
|
+
'%<name>s %<action>s%<params>s%<session>s'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'cops/netsoft/auth_http_positional_arguments'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'netsoft-danger/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'netsoft-danger'
|
7
|
+
s.version = NetsoftDanger::VERSION
|
8
|
+
s.authors = ['urkle']
|
9
|
+
s.email = []
|
10
|
+
s.homepage = 'https://github.com/NetsoftHoldings/danger'
|
11
|
+
s.summary = 'Danger.systems conventions for Netsoft projects.'
|
12
|
+
s.description = 'Packages a Dangerfile to be used with Danger.'
|
13
|
+
s.executables << 'netsoft-circle'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.require_paths = ['lib']
|
17
|
+
|
18
|
+
s.add_development_dependency 'rake'
|
19
|
+
s.add_runtime_dependency 'danger', '~> 5.0'
|
20
|
+
s.add_runtime_dependency 'rubocop', '~> 0.74.0'
|
21
|
+
s.add_runtime_dependency 'rubocop-rails', '~> 2.2.1'
|
22
|
+
s.add_runtime_dependency 'rubocop-performance', '~> 1.4.1'
|
23
|
+
s.add_runtime_dependency 'rubocop-rspec', '~> 1.35.0'
|
24
|
+
s.add_runtime_dependency 'thor'
|
25
|
+
end
|
data/package.json
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require:
|
2
|
+
- netsoft-danger
|
3
|
+
- rubocop-rails
|
4
|
+
- rubocop-performance
|
5
|
+
- rubocop-rspec
|
6
|
+
|
7
|
+
AllCops:
|
8
|
+
TargetRubyVersion: 2.4
|
9
|
+
TargetRailsVersion: 5.2
|
10
|
+
DisplayStyleGuide: true
|
11
|
+
StyleGuideBaseURL: https://rubystyle.guide
|
12
|
+
Exclude:
|
13
|
+
- 'db/**/*'
|
14
|
+
- 'coverage/**/*'
|
15
|
+
- 'log/**/*'
|
16
|
+
- 'public/**/*'
|
17
|
+
- 'tmp/**/*'
|
18
|
+
- 'spec/dummy/**/*'
|
19
|
+
Rails:
|
20
|
+
Enabled: true
|
21
|
+
Rails/HttpPositionalArguments:
|
22
|
+
Enabled: false
|
23
|
+
Netsoft/AuthHttpPositionalArguments:
|
24
|
+
Enabled: true
|
25
|
+
Include:
|
26
|
+
- 'spec/controllers/**/*'
|
27
|
+
RSpec/NotToNot:
|
28
|
+
EnforcedStyle: to_not
|
29
|
+
RSpec/ExpectChange:
|
30
|
+
EnforcedStyle: block
|
31
|
+
Style/TrailingCommaInArrayLiteral:
|
32
|
+
EnforcedStyleForMultiline: comma
|
33
|
+
Style/TrailingCommaInHashLiteral:
|
34
|
+
EnforcedStyleForMultiline: comma
|
35
|
+
Layout/SpaceInsideBlockBraces:
|
36
|
+
EnforcedStyle: no_space
|
37
|
+
SpaceBeforeBlockParameters: false
|
38
|
+
Layout/AlignHash:
|
39
|
+
EnforcedHashRocketStyle: table
|
40
|
+
EnforcedColonStyle: table
|
41
|
+
Layout/IndentFirstHashElement:
|
42
|
+
IndentationWidth: 4
|
43
|
+
Layout/SpaceInsideHashLiteralBraces:
|
44
|
+
EnforcedStyle: no_space
|
45
|
+
Layout/IndentAssignment:
|
46
|
+
IndentationWidth: 4
|