danger-migrations 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +162 -0
- data/lib/danger-migrations/migrations_alone.rb +99 -0
- data/lib/danger-migrations/private/git.rb +66 -0
- data/lib/danger-migrations/version.rb +6 -0
- data/lib/danger-migrations.rb +6 -0
- data/lib/danger_plugin.rb +4 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f2bbab97b583c8624b1bd9888ffc87b4b3b7795ead6e3ff0a6d240bc92f4072e
|
4
|
+
data.tar.gz: 54a4301bed56abcbfe9b0ddb33a9b6bc39b52ba72191a8981e5b7be93dc6e243
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 89b8cbaf9fd9519365cab32c3509bd964dbfae868aec2f6e01fff09dd478b741c39a86e9fb97f59e83741ee871f093f3c6388647b3d2b69dec9e05db641df05e
|
7
|
+
data.tar.gz: 061a43cbee49cf2dc6130e2fc2e4cbe630a4d88f817b27cc905e539e80ec3d222c56fefe153c0c6899ca6e7e7500e20d5e12a03b1ab49e942557312a90a699d4
|
data/README.md
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# danger-packwerk
|
2
|
+
|
3
|
+
`danger-packwerk` integrates [`packwerk`](https://github.com/Shopify/packwerk) with [`danger`](https://github.com/danger/danger) to provide inline comments in PRs related to boundaries in a Rails application.
|
4
|
+
|
5
|
+
## Installation and Basic Usage
|
6
|
+
Step 1: Add this line to your `Gemfile` (to whatever group your CI uses, as it is not needed in production) and `bundle install`:
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
gem 'danger-packwerk', group: :test
|
10
|
+
```
|
11
|
+
|
12
|
+
Step 2: Add these to your `Dangerfile`:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
packwerk.check
|
16
|
+
package_todo_yml_changes.check
|
17
|
+
```
|
18
|
+
|
19
|
+
That's it for basic usage!
|
20
|
+
|
21
|
+
## Advanced Usage
|
22
|
+
|
23
|
+
There are currently two danger checks that ship with `danger-packwerk`:
|
24
|
+
1) One that runs `bin/packwerk check` and leaves inline comments in source code on new violations
|
25
|
+
2) One that looks at changes to `package_todo.yml` files and leaves inline comments on added violations.
|
26
|
+
|
27
|
+
In upcoming iterations, we will include other danger checks, including:
|
28
|
+
1) A danger check that detects changes to `package.yml` files and posts user-configurable messages on the `package.yml` files that are modified.
|
29
|
+
2) A danger check that detects changes to `packwerk.yml` files and allows you to specify the action taken when that happens.
|
30
|
+
|
31
|
+
## packwerk.check
|
32
|
+

|
33
|
+

|
34
|
+
|
35
|
+
Without any configuration, `packwerk.check` should just work. By default, it will post a maximum of 15 messages in a PR and it will not fail the build.
|
36
|
+
|
37
|
+
`packwerk.check` can be configured to in the following ways:
|
38
|
+
|
39
|
+
### Change the message that displays in the markdown
|
40
|
+
To customize the message in the GitHub comment, pass in `offenses_formatter` to `packwerk.check` in your `Dangerfile`. Here's a simple example:
|
41
|
+
```ruby
|
42
|
+
class MyFormatter
|
43
|
+
extend T::Sig
|
44
|
+
include DangerPackwerk::Check::OffensesFormatter
|
45
|
+
# Packwerk::ReferenceOffense: https://github.com/Shopify/packwerk/blob/main/lib/packwerk/reference_offense.rb
|
46
|
+
sig do
|
47
|
+
override.params(
|
48
|
+
offenses: T::Array[Packwerk::ReferenceOffense],
|
49
|
+
repo_link: String,
|
50
|
+
org_name: String
|
51
|
+
repo_url_builder: T.nilable(T.proc.params(constant_path: String).returns(String))
|
52
|
+
).returns(String)
|
53
|
+
end
|
54
|
+
def format_offenses(offenses, repo_link, org_name, repo_builder_url: nil)
|
55
|
+
# your logic here
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
packwerk.check(offenses_formatter: MyFormatter.new)
|
60
|
+
```
|
61
|
+
|
62
|
+
If you'd like to keep the default messaging but add some context customized to your organization, you can pass that in as follows:
|
63
|
+
```ruby
|
64
|
+
custom_help_message = "Need help? Check out our internal docs [here](www.example.com)"
|
65
|
+
packwerk.check(offenses_formatter: DangerPackwerk::Check::DefaultFormatter.new(custom_help_message: custom_help_message))
|
66
|
+
```
|
67
|
+
|
68
|
+
### Fail the build on new violations
|
69
|
+
Simply pass in `fail_build: true` into `check`, as such:
|
70
|
+
```ruby
|
71
|
+
packwerk.check(fail_build: true)
|
72
|
+
```
|
73
|
+
|
74
|
+
If you want to change the default error message, which is `Packwerk violations were detected! Please resolve them to unblock the build.`, then you can also pass in `failure_message`.
|
75
|
+
|
76
|
+
### Change the max number of comments that will display
|
77
|
+
If you do not change this, the default max is 15. More information about why we chose this number in the source code.
|
78
|
+
```ruby
|
79
|
+
packwerk.check(max_comments: 3)
|
80
|
+
```
|
81
|
+
|
82
|
+
### Do something extra when there are packwerk failures
|
83
|
+
Maybe you want to notify slack or do something else when there are packwerk failures.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
packwerk.check(
|
87
|
+
# Offenses are a T::Array[Packwerk::ReferenceOffense] => https://github.com/Shopify/packwerk/blob/main/lib/packwerk/reference_offense.rb
|
88
|
+
on_failure: -> (offenses) do
|
89
|
+
# Notify slack or otherwise do something extra!
|
90
|
+
end
|
91
|
+
)
|
92
|
+
```
|
93
|
+
|
94
|
+
### Supporting new violation types
|
95
|
+
By default, only `dependency` and `privacy` violation types are supported. If you wish to use other violation types you'll need to provide them when calling `check`:
|
96
|
+
```ruby
|
97
|
+
packwerk.check(
|
98
|
+
violation_types: %w[dependency privacy layer]
|
99
|
+
)
|
100
|
+
```
|
101
|
+
|
102
|
+
Any violations not included in this list will be ignored.
|
103
|
+
|
104
|
+
You will also most likely want to customize the offenses formatter and provide specific feedback for the new violation types.
|
105
|
+
|
106
|
+
## package_todo_yml_changes.check
|
107
|
+

|
108
|
+
|
109
|
+
Without any configuration, `package_todo_yml_changes.check` should just work. By default, it will post a maximum of 15 messages in a PR, using default messaging defined within this gem.
|
110
|
+
|
111
|
+
`package_todo_yml_changes.check` can be configured to in the following ways:
|
112
|
+
|
113
|
+
### Change the message that displays in the markdown
|
114
|
+
To customize the message in the GitHub comment, pass in `offenses_formatter` to `package_todo_yml_changes.check` in your `Dangerfile`. Here's a simple example:
|
115
|
+
```ruby
|
116
|
+
class MyFormatter
|
117
|
+
extend T::Sig
|
118
|
+
include DangerPackwerk::Update::OffensesFormatter
|
119
|
+
# DangerPackwerk::BasicReferenceOffense
|
120
|
+
sig do
|
121
|
+
override.params(
|
122
|
+
offenses: T::Array[Packwerk::ReferenceOffense],
|
123
|
+
repo_link: String,
|
124
|
+
org_name: String
|
125
|
+
repo_url_builder: T.nilable(T.proc.params(constant_path: String).returns(String))
|
126
|
+
).returns(String)
|
127
|
+
end
|
128
|
+
def format_offenses(offenses, repo_link, org_name, repo_builder_url: nil)
|
129
|
+
# your logic here
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
package_todo_yml_changes.check(offenses_formatter: MyFormatter.new)
|
134
|
+
```
|
135
|
+
|
136
|
+
If you'd like to keep the default messaging but add some context customized to your organization, you can pass that in as follows:
|
137
|
+
```ruby
|
138
|
+
custom_help_message = "Need help? Check out our internal docs [here](www.example.com)"
|
139
|
+
package_todo_yml_changes.check(offenses_formatter: DangerPackwerk::Update::DefaultFormatter.new(custom_help_message: custom_help_message))
|
140
|
+
```
|
141
|
+
|
142
|
+
### Change the max number of comments that will display
|
143
|
+
If you do not change this, the default max is 15. More information about why we chose this number in the source code.
|
144
|
+
```ruby
|
145
|
+
package_todo_yml_changes.check(max_comments: 3)
|
146
|
+
```
|
147
|
+
|
148
|
+
### Do something extra before we leave comments
|
149
|
+
Maybe you want to notify slack or do something else before we leave comments.
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
package_todo_yml_changes.check(
|
153
|
+
# violation_diff is a DangerPackwerk::ViolationDiff and changed_package_todo_ymls is a T::Array[String]
|
154
|
+
before_comment: -> (violation_diff, changed_package_todo_ymls) do
|
155
|
+
# Notify slack or otherwise do something extra!
|
156
|
+
end
|
157
|
+
)
|
158
|
+
```
|
159
|
+
|
160
|
+
## Development
|
161
|
+
|
162
|
+
We welcome your contributions! Please create an issue or pull request and we'd be happy to take a look.
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# @team Developer Productivity Rails
|
2
|
+
# typed: strict
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require 'danger'
|
6
|
+
|
7
|
+
module Danger
|
8
|
+
class MigrationsAlone < Plugin
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
MIGRATION_DEPENDENT_PATTERNS = T.let([%r(ar_doc/), %r(db/), %r(\Agems/.*/spec/dummy/db/)].freeze, T::Array[Regexp])
|
12
|
+
ALLOWED_FOLDERS = T.let([%r(\Aspec/), %r(\Apacks/.*/spec/), %r(\Agems/.*/spec/), %r(\Asorbet/rails-rbi/), %r(\Asorbet/rbi/dsl/)].freeze, T::Array[Regexp])
|
13
|
+
|
14
|
+
sig { void }
|
15
|
+
def check
|
16
|
+
return if new_migrations.empty?
|
17
|
+
|
18
|
+
disallowed_changed_files = find_disallowed_changed_files
|
19
|
+
if disallowed_changed_files.any?
|
20
|
+
file_list = (files - disallowed_changed_files).map { |path| "<li>#{path}</li>" }.join("\n")
|
21
|
+
disallowed_files = disallowed_changed_files.map { |path| "<li>#{path}</li>" }.join("\n")
|
22
|
+
|
23
|
+
message = <<~MESSAGE
|
24
|
+
<b>Migrations must be checked in alone.</b>
|
25
|
+
To ensure application code is not dependent on database migrations taking place
|
26
|
+
during the deploys, <i>please separate the schema change along with migration files into a dedicated PR:</i>
|
27
|
+
<ul>
|
28
|
+
#{file_list}
|
29
|
+
</ul>
|
30
|
+
|
31
|
+
<b>List of disallowed files</b>
|
32
|
+
<ul>
|
33
|
+
#{disallowed_files}
|
34
|
+
</ul>
|
35
|
+
<a href='https://confluence.gustocorp.com/display/BE/Database+Schema+Migrations+in+Hawaiian+Ice'>More information</a>
|
36
|
+
MESSAGE
|
37
|
+
|
38
|
+
fail message
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
sig { returns(T::Array[String]) }
|
45
|
+
def find_disallowed_changed_files
|
46
|
+
files.reject do |file_path|
|
47
|
+
next true if migration?(file_path)
|
48
|
+
|
49
|
+
next true if %r(\Adb/.*structure.sql).match?(file_path)
|
50
|
+
|
51
|
+
next true if %r(\Adb/.*seeds.rb).match?(file_path)
|
52
|
+
|
53
|
+
next true if allow_listed_file?(file_path, MIGRATION_DEPENDENT_PATTERNS)
|
54
|
+
|
55
|
+
next true if allow_listed_file?(file_path, ALLOWED_FOLDERS)
|
56
|
+
|
57
|
+
next true if comment_and_whitespace_changes_only?(file_path)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
sig { returns(T::Array[String]) }
|
62
|
+
def new_migrations
|
63
|
+
git.added_files.select { |file_path| migration?(file_path) }
|
64
|
+
end
|
65
|
+
|
66
|
+
sig { params(file_path: String).returns(T::Boolean) }
|
67
|
+
def migration?(file_path)
|
68
|
+
%r(db/migrate/[^/]+\z).match?(file_path)
|
69
|
+
end
|
70
|
+
|
71
|
+
sig { params(file_path: String, patterns: T::Array[Regexp]).returns(T::Boolean) }
|
72
|
+
def allow_listed_file?(file_path, patterns)
|
73
|
+
patterns.any? { |pattern| pattern =~ file_path }
|
74
|
+
end
|
75
|
+
|
76
|
+
sig { params(file: String).returns(T::Boolean) }
|
77
|
+
def comment_and_whitespace_changes_only?(file)
|
78
|
+
return false unless git.modified_files.include?(file)
|
79
|
+
|
80
|
+
cleaned_diff(file).all? do |line|
|
81
|
+
# comment added || comment removed || whitespace added || whitespace removed
|
82
|
+
line.start_with?('+#', '-#') || line =~ /\A\+\s*\z/ || line =~ /\A-\s*\z/
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
sig { params(file_path: String).returns(T::Array[String]) }
|
87
|
+
def cleaned_diff(file_path)
|
88
|
+
diff = git.diff_for_file(file_path)
|
89
|
+
diff.patch.lines.select do |line|
|
90
|
+
line.start_with?('-', '+') && !line.include?(diff.path)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
sig { returns(T::Array[String]) }
|
95
|
+
def files
|
96
|
+
git.added_files + git.modified_files + git.deleted_files
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'code_ownership'
|
5
|
+
require 'packs'
|
6
|
+
|
7
|
+
# In order to support running danger-migrations from a non-root filepath, we need
|
8
|
+
# to wrap some git functions in filesystem wrappers: packwerk runs relative to
|
9
|
+
# the rails app root, whereas git returns paths on the actual filesystem.
|
10
|
+
module DangerMigrations
|
11
|
+
module Private
|
12
|
+
class GitFilesystem < T::Struct
|
13
|
+
extend T::Sig
|
14
|
+
|
15
|
+
const :git, Danger::DangerfileGitPlugin
|
16
|
+
const :root, String
|
17
|
+
|
18
|
+
sig { returns(T::Array[{ after: String, before: String }]) }
|
19
|
+
def renamed_files
|
20
|
+
@git.renamed_files.map do |f|
|
21
|
+
{
|
22
|
+
after: convert_file_from_filesystem(f[:after]),
|
23
|
+
before: convert_file_from_filesystem(f[:before]),
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
sig { returns(T::Array[String]) }
|
29
|
+
def modified_files
|
30
|
+
convert_from_filesystem(@git.modified_files.to_a)
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { returns(T::Array[String]) }
|
34
|
+
def deleted_files
|
35
|
+
convert_from_filesystem(@git.deleted_files.to_a)
|
36
|
+
end
|
37
|
+
|
38
|
+
sig { returns(T::Array[String]) }
|
39
|
+
def added_files
|
40
|
+
convert_from_filesystem(@git.added_files.to_a)
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { params(filename_on_disk: String).returns(::Git::Diff::DiffFile) }
|
44
|
+
def diff(filename_on_disk)
|
45
|
+
@git.diff[filename_on_disk]
|
46
|
+
end
|
47
|
+
|
48
|
+
sig { params(path: String).returns(String) }
|
49
|
+
def convert_to_filesystem(path)
|
50
|
+
Pathname(@root).join(path).to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
56
|
+
def convert_from_filesystem(files)
|
57
|
+
files.map { |f| convert_file_from_filesystem(f) }
|
58
|
+
end
|
59
|
+
|
60
|
+
sig { params(file: String).returns(String) }
|
61
|
+
def convert_file_from_filesystem(file)
|
62
|
+
Pathname(file).relative_path_from(@root).to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: danger-migrations
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.15.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gusto Engineers
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: danger-plugin-api
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '1.0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '1.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: sorbet-runtime
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
description: Danger plugin for migrations.
|
41
|
+
email:
|
42
|
+
- dev@gusto.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- README.md
|
48
|
+
- lib/danger-migrations.rb
|
49
|
+
- lib/danger-migrations/migrations_alone.rb
|
50
|
+
- lib/danger-migrations/private/git.rb
|
51
|
+
- lib/danger-migrations/version.rb
|
52
|
+
- lib/danger_plugin.rb
|
53
|
+
homepage: https://github.com/rubyatscale/danger-migrations
|
54
|
+
licenses:
|
55
|
+
- MIT
|
56
|
+
metadata:
|
57
|
+
homepage_uri: https://github.com/rubyatscale/danger-migrations
|
58
|
+
source_code_uri: https://github.com/rubyatscale/danger-migrations
|
59
|
+
changelog_uri: https://github.com/rubyatscale/danger-migrations/releases
|
60
|
+
allowed_push_host: https://rubygems.org
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.2'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubygems_version: 3.6.9
|
76
|
+
specification_version: 4
|
77
|
+
summary: Danger plugin for migrations.
|
78
|
+
test_files: []
|