danger-samsao 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.env.example +1 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +28 -0
- data/.travis.yml +12 -0
- data/CHANGELOG.md +25 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +136 -0
- data/Guardfile +21 -0
- data/LICENSE.txt +22 -0
- data/README.md +230 -0
- data/Rakefile +22 -0
- data/bitrise.wrapper.yml +51 -0
- data/bitrise.yml +70 -0
- data/danger-samsao.gemspec +33 -0
- data/lib/danger_plugin.rb +1 -0
- data/lib/danger_samsao.rb +1 -0
- data/lib/samsao/actions.rb +53 -0
- data/lib/samsao/config.rb +21 -0
- data/lib/samsao/gem_version.rb +3 -0
- data/lib/samsao/helpers.rb +79 -0
- data/lib/samsao/plugin.rb +28 -0
- data/lib/samsao/regexp.rb +29 -0
- data/spec/matchers/have_error.rb +25 -0
- data/spec/matchers/have_no_error.rb +14 -0
- data/spec/matchers/have_no_warning.rb +14 -0
- data/spec/matchers/have_warning.rb +25 -0
- data/spec/samsao_branch_helper_spec.rb +66 -0
- data/spec/samsao_branching_model_spec.rb +41 -0
- data/spec/samsao_changelog_modified_spec.rb +48 -0
- data/spec/samsao_changelog_updated_spec.rb +87 -0
- data/spec/samsao_config_spec.rb +30 -0
- data/spec/samsao_feature_single_commit_spec.rb +42 -0
- data/spec/samsao_has_app_changes_spec.rb +102 -0
- data/spec/samsao_merge_commit_detected_spec.rb +81 -0
- data/spec/samsao_regexp_spec.rb +35 -0
- data/spec/samsao_spec.rb +9 -0
- data/spec/samsao_trivial_change_spec.rb +57 -0
- data/spec/samsao_work_in_progress_pr_spec.rb +50 -0
- data/spec/spec_helper.rb +56 -0
- metadata +240 -0
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# rubocop:disable Style/HashSyntax
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
require 'rubocop/rake_task'
|
6
|
+
|
7
|
+
task default: :check
|
8
|
+
|
9
|
+
task :check => [:lint, :tests]
|
10
|
+
|
11
|
+
task :lint => [:lint_ruby, :lint_danger]
|
12
|
+
|
13
|
+
desc 'Run RuboCop on the lib/specs directory'
|
14
|
+
RuboCop::RakeTask.new(:lint_ruby)
|
15
|
+
|
16
|
+
desc 'Ensure that the plugin passes `danger plugins lint`'
|
17
|
+
task :lint_danger do
|
18
|
+
sh 'bundle exec danger plugins lint --warnings-as-errors'
|
19
|
+
end
|
20
|
+
|
21
|
+
task :test => [:tests]
|
22
|
+
RSpec::Core::RakeTask.new(:tests)
|
data/bitrise.wrapper.yml
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
---
|
2
|
+
format_version: 1.4.0
|
3
|
+
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
|
4
|
+
description: |-
|
5
|
+
This wrapper is configure in bitrise.io editor as the script to run. What it
|
6
|
+
does mainly is to simply clone the repository and forward the execution of the
|
7
|
+
workflow to the real build file (bitrise.yml) which is hosted in this repository.
|
8
|
+
|
9
|
+
To work, main workflow here must also be present in bitrise.yml, so main workflows
|
10
|
+
must be kept synchronized between the two.
|
11
|
+
|
12
|
+
Also, any change made to this file must be **manually** added to bitrise.io editor.
|
13
|
+
Simply copy/paste the content of this file into the web editor and press save.
|
14
|
+
|
15
|
+
To test locally using bitrise CLI, while at project root directory, simply do:
|
16
|
+
|
17
|
+
bitrise run <workflow> -c bitrise.wrapper.yml
|
18
|
+
|
19
|
+
See http://devcenter.bitrise.io/tips-and-tricks/use-bitrise-yml-from-repository/
|
20
|
+
for details on the technique.
|
21
|
+
|
22
|
+
trigger_map:
|
23
|
+
- push_branch: "develop"
|
24
|
+
workflow: "develop"
|
25
|
+
- pull_request_target_branch: "*"
|
26
|
+
pull_request_source_branch: "*"
|
27
|
+
workflow: "pr"
|
28
|
+
|
29
|
+
workflows:
|
30
|
+
_run_from_repo:
|
31
|
+
steps:
|
32
|
+
- activate-ssh-key@3.1.1:
|
33
|
+
title: Activate SSH credentials
|
34
|
+
run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
|
35
|
+
- git-clone@3.4.2:
|
36
|
+
title: Clone repository
|
37
|
+
- script@1.1.3:
|
38
|
+
title: Continue using repository bitrise.yml
|
39
|
+
inputs:
|
40
|
+
- content: |-
|
41
|
+
#!/bin/bash
|
42
|
+
set -ex
|
43
|
+
bitrise run "${BITRISE_TRIGGERED_WORKFLOW_ID}"
|
44
|
+
|
45
|
+
develop:
|
46
|
+
after_run:
|
47
|
+
- _run_from_repo
|
48
|
+
|
49
|
+
pr:
|
50
|
+
after_run:
|
51
|
+
- _run_from_repo
|
data/bitrise.yml
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
---
|
2
|
+
format_version: 1.4.0
|
3
|
+
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
|
4
|
+
|
5
|
+
workflows:
|
6
|
+
# Main Workflows
|
7
|
+
|
8
|
+
develop:
|
9
|
+
before_run:
|
10
|
+
- _setup
|
11
|
+
|
12
|
+
after_run:
|
13
|
+
- _teardown
|
14
|
+
|
15
|
+
pr:
|
16
|
+
before_run:
|
17
|
+
- _setup
|
18
|
+
|
19
|
+
after_run:
|
20
|
+
- _teardown
|
21
|
+
|
22
|
+
# Composite Workflows
|
23
|
+
|
24
|
+
_setup:
|
25
|
+
before_run:
|
26
|
+
- _cache_pull
|
27
|
+
- _prepare
|
28
|
+
- _lint
|
29
|
+
- _test
|
30
|
+
|
31
|
+
_teardown:
|
32
|
+
after_run:
|
33
|
+
- _cache_push
|
34
|
+
|
35
|
+
# Private workflows
|
36
|
+
|
37
|
+
_cache_push:
|
38
|
+
steps:
|
39
|
+
- cache-push@0.9.4:
|
40
|
+
title: Push cache
|
41
|
+
run_if: .IsCI
|
42
|
+
inputs:
|
43
|
+
- cache_paths: |-
|
44
|
+
$HOME/.bundle
|
45
|
+
|
46
|
+
_cache_pull:
|
47
|
+
steps:
|
48
|
+
- cache-pull@0.9.2:
|
49
|
+
title: Pull cache
|
50
|
+
|
51
|
+
_lint:
|
52
|
+
steps:
|
53
|
+
- script@1.1.3:
|
54
|
+
title: Linting
|
55
|
+
inputs:
|
56
|
+
- content: bundle exec rake lint
|
57
|
+
|
58
|
+
_prepare:
|
59
|
+
steps:
|
60
|
+
- script@1.1.3:
|
61
|
+
title: Prepare (installing dependencies)
|
62
|
+
inputs:
|
63
|
+
- content: bundle install
|
64
|
+
|
65
|
+
_test:
|
66
|
+
steps:
|
67
|
+
- script@1.1.3:
|
68
|
+
title: Tests
|
69
|
+
inputs:
|
70
|
+
- content: bundle exec rake tests
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'samsao/gem_version.rb'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'danger-samsao'
|
9
|
+
spec.version = Samsao::VERSION
|
10
|
+
spec.authors = ['Samsao Development Inc.']
|
11
|
+
spec.email = ['mvachon@samsao.co']
|
12
|
+
spec.description = 'Danger plugin for Samsao PR guidelines.'
|
13
|
+
spec.summary = 'A longer description of danger-samsao.'
|
14
|
+
spec.homepage = 'https://github.com/samsao/danger-samsao'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_runtime_dependency 'danger-plugin-api', '~> 1.0'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
25
|
+
spec.add_development_dependency 'guard', '~> 2.14'
|
26
|
+
spec.add_development_dependency 'guard-rubocop', '~> 1.2'
|
27
|
+
spec.add_development_dependency 'guard-rspec', '~> 4.7'
|
28
|
+
spec.add_development_dependency 'pry', '~> 0.11.0.pre2'
|
29
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
30
|
+
spec.add_development_dependency 'rubocop', '~> 0.41'
|
31
|
+
spec.add_development_dependency 'rspec', '~> 3.4'
|
32
|
+
spec.add_development_dependency 'yard', '~> 0.8'
|
33
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'samsao/plugin'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'samsao/gem_version'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Samsao
|
2
|
+
# Actions mixin module
|
3
|
+
module Actions
|
4
|
+
# Fails when git branching model is not respected for PR branch name.
|
5
|
+
#
|
6
|
+
# @return [void]
|
7
|
+
def fail_when_wrong_branching_model
|
8
|
+
message = 'Your branch should be prefixed with feature/, fix/, trivial/ or release/!'
|
9
|
+
|
10
|
+
fail message unless respects_branching_model
|
11
|
+
end
|
12
|
+
|
13
|
+
# Fails when a feature branch have than one commit
|
14
|
+
#
|
15
|
+
# @return [void]
|
16
|
+
def fail_when_non_single_commit_feature
|
17
|
+
commit_count = git.commits.size
|
18
|
+
message = "Your feature branch should have a single commit but found #{commit_count}, squash them together!"
|
19
|
+
|
20
|
+
fail message if feature_branch? && commit_count > 1
|
21
|
+
end
|
22
|
+
|
23
|
+
# Fails when CHANGELOG is not updated on feature or fix branches
|
24
|
+
#
|
25
|
+
# @return [void]
|
26
|
+
def fail_when_changelog_update_missing
|
27
|
+
return if trivial_change?
|
28
|
+
|
29
|
+
message = 'You did a fix or a feature without updating CHANGELOG file!'
|
30
|
+
|
31
|
+
fail message unless changelog_modified?
|
32
|
+
end
|
33
|
+
|
34
|
+
# Fails when one or more merge commit is detected.
|
35
|
+
#
|
36
|
+
# @return [void]
|
37
|
+
def fail_when_merge_commit_detected
|
38
|
+
message = 'Some merge commits were detected, you must use rebase to sync with base branch.'
|
39
|
+
merge_commit_detector = /^Merge branch '#{github.branch_for_base}'/
|
40
|
+
|
41
|
+
fail message if git.commits.any? { |commit| commit.message =~ merge_commit_detector }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Fails when CHANGELOG is not updated on feature or fix branches
|
45
|
+
#
|
46
|
+
# @return [void]
|
47
|
+
def warn_when_work_in_progess_pr
|
48
|
+
message = 'Do not merge, PR is a work in progess [WIP]!'
|
49
|
+
|
50
|
+
warn message if github.pr_title.include?('[WIP]')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Danger
|
2
|
+
# Samsao's config class
|
3
|
+
class SamsaoConfig
|
4
|
+
def initialize
|
5
|
+
@changelogs = ['CHANGELOG.md']
|
6
|
+
@sources = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def changelogs(*entries)
|
10
|
+
return @changelogs if entries.nil? || entries.empty?
|
11
|
+
|
12
|
+
@changelogs = entries
|
13
|
+
end
|
14
|
+
|
15
|
+
def sources(*entries)
|
16
|
+
return @sources if entries.nil? || entries.empty?
|
17
|
+
|
18
|
+
@sources = entries
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# rubocop:disable Style/PredicateName
|
2
|
+
|
3
|
+
module Samsao
|
4
|
+
# Helpers mixin module
|
5
|
+
module Helpers
|
6
|
+
# Check if any changelog were modified. When the helper receives nothing,
|
7
|
+
# changelogs defined by the config are used.
|
8
|
+
#
|
9
|
+
# @return [Boolean] True if any changelogs were modified in this commit
|
10
|
+
def changelog_modified?(*changelogs)
|
11
|
+
changelogs = config.changelogs if changelogs.nil? || changelogs.empty?
|
12
|
+
|
13
|
+
changelogs.any? { |changelog| git.modified_files.include?(changelog) }
|
14
|
+
end
|
15
|
+
|
16
|
+
# Return true if the current PR branch is a feature branch
|
17
|
+
#
|
18
|
+
# @return [void]
|
19
|
+
def feature_branch?
|
20
|
+
git_branch.start_with?('feature/')
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return true if the current PR branch is a feature branch
|
24
|
+
#
|
25
|
+
# @return [void]
|
26
|
+
def fix_branch?
|
27
|
+
git_branch.start_with?('fix/')
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return true if the current PR branch is a release branch
|
31
|
+
#
|
32
|
+
# @return [void]
|
33
|
+
def release_branch?
|
34
|
+
git_branch.start_with?('release/')
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return true if the current PR branch is a trivial branch
|
38
|
+
#
|
39
|
+
# @return [void]
|
40
|
+
def trivial_branch?
|
41
|
+
git_branch.start_with?('trivial/')
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return true if the current PR is a trivial change (branch is `trivial_branch?`)
|
45
|
+
# or PR title contains #trivial or #typo markers.
|
46
|
+
#
|
47
|
+
# @return [void]
|
48
|
+
def trivial_change?
|
49
|
+
trivial_branch? || ['#trivial', '#typo'].any? { |modifier| github.pr_title.include?(modifier) }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Return true if any source files are in the git modified files list.
|
53
|
+
#
|
54
|
+
# @return [void]
|
55
|
+
def has_app_changes?(*sources)
|
56
|
+
sources = config.sources if sources.nil? || sources.empty?
|
57
|
+
|
58
|
+
sources.any? do |source|
|
59
|
+
pattern = Samsao::Regexp.from_matcher(source, when_string_pattern_prefix_with: '^')
|
60
|
+
|
61
|
+
modified_file?(pattern)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def git_branch
|
68
|
+
github.branch_for_head
|
69
|
+
end
|
70
|
+
|
71
|
+
def modified_file?(matcher)
|
72
|
+
!git.modified_files.grep(matcher).empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
def respects_branching_model
|
76
|
+
feature_branch? || fix_branch? || release_branch? || trivial_branch?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'samsao/actions'
|
2
|
+
require 'samsao/config'
|
3
|
+
require 'samsao/helpers'
|
4
|
+
require 'samsao/regexp'
|
5
|
+
|
6
|
+
module Danger
|
7
|
+
# Samsao's Danger plugin.
|
8
|
+
#
|
9
|
+
# @example See README.md
|
10
|
+
#
|
11
|
+
# @see samsao/danger-samsao
|
12
|
+
# @tags internal, private, enterprise
|
13
|
+
#
|
14
|
+
class DangerSamsao < Plugin
|
15
|
+
include Samsao::Actions
|
16
|
+
include Samsao::Helpers
|
17
|
+
|
18
|
+
# Enable to configure the plugin configuration object.
|
19
|
+
#
|
20
|
+
# @return [Array<String>] (if no block given)
|
21
|
+
def config(&block)
|
22
|
+
@config = Danger::SamsaoConfig.new if @config.nil?
|
23
|
+
return @config unless block_given?
|
24
|
+
|
25
|
+
@config.instance_eval(&block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Samsao
|
2
|
+
# Regexp utility methods
|
3
|
+
module Regexp
|
4
|
+
# Turns a source entry input into a Regexp. Uses rules from [from_matcher](#from_matcher)
|
5
|
+
# function and append a `^` to final regexp when the source if a pure String.
|
6
|
+
#
|
7
|
+
# @param matcher The source entry to transform into a Regexp
|
8
|
+
#
|
9
|
+
# @return [Regexp] The source entry as a regexp
|
10
|
+
def self.from_source(source)
|
11
|
+
from_matcher(source, when_string_pattern_prefix_with: '^')
|
12
|
+
end
|
13
|
+
|
14
|
+
# Turns an input into a Regexp. If the input is a String, it turns it
|
15
|
+
# into a Regexp and escape all special characters. If the input is already
|
16
|
+
# a Regexp, it returns it without modification.
|
17
|
+
#
|
18
|
+
# @param matcher The input matcher to transform into a Regexp
|
19
|
+
# @keyword when_string_pattern_prefix_with (Default: '') The prefix that should be added to
|
20
|
+
# final regexp when the input matcher is a string type
|
21
|
+
#
|
22
|
+
# @return [Regexp] The input as a regexp
|
23
|
+
def self.from_matcher(matcher, when_string_pattern_prefix_with: '')
|
24
|
+
return matcher if matcher.is_a?(::Regexp)
|
25
|
+
|
26
|
+
/#{when_string_pattern_prefix_with}#{::Regexp.quote(matcher)}/
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rspec/expectations'
|
2
|
+
|
3
|
+
RSpec::Matchers.define :have_error do |expected|
|
4
|
+
match do |actual|
|
5
|
+
actual.status_report[:errors].any? do |error|
|
6
|
+
expected.is_a?(Regexp) ? error =~ matcher : error.start_with?(expected)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
failure_message do |actual|
|
11
|
+
message = "expected that #{Danger} would have error '#{expected}'"
|
12
|
+
|
13
|
+
errors = actual.status_report[:errors]
|
14
|
+
if errors.empty?
|
15
|
+
message += ' but there is none'
|
16
|
+
return message
|
17
|
+
end
|
18
|
+
|
19
|
+
actual.status_report[:errors].each do |error|
|
20
|
+
message += "\n * #{error}"
|
21
|
+
end
|
22
|
+
|
23
|
+
message
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rspec/expectations'
|
2
|
+
|
3
|
+
RSpec::Matchers.define :have_no_error do
|
4
|
+
match do |actual|
|
5
|
+
actual.status_report[:errors].empty?
|
6
|
+
end
|
7
|
+
|
8
|
+
failure_message do |actual|
|
9
|
+
message = "expected that #{Danger} would have no error but found '#{actual.status_report[:errors].size}'"
|
10
|
+
actual.status_report[:errors].each do |error|
|
11
|
+
message += "\n * #{error}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|