use_packs 0.0.1
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 +71 -0
- data/bin/packs +10 -0
- data/bin/rubocop +29 -0
- data/bin/tapioca +29 -0
- data/lib/use_packs/cli.rb +127 -0
- data/lib/use_packs/code_ownership_post_processor.rb +58 -0
- data/lib/use_packs/configuration.rb +61 -0
- data/lib/use_packs/default_user_event_logger.rb +7 -0
- data/lib/use_packs/logging.rb +37 -0
- data/lib/use_packs/per_file_processor_interface.rb +18 -0
- data/lib/use_packs/private/file_move_operation.rb +80 -0
- data/lib/use_packs/private/interactive_cli/pack_selector.rb +34 -0
- data/lib/use_packs/private/interactive_cli/team_selector.rb +35 -0
- data/lib/use_packs/private/interactive_cli/use_cases/add_dependency.rb +30 -0
- data/lib/use_packs/private/interactive_cli/use_cases/check.rb +25 -0
- data/lib/use_packs/private/interactive_cli/use_cases/create.rb +27 -0
- data/lib/use_packs/private/interactive_cli/use_cases/get_info.rb +74 -0
- data/lib/use_packs/private/interactive_cli/use_cases/interface.rb +34 -0
- data/lib/use_packs/private/interactive_cli/use_cases/lint_package_yml.rb +26 -0
- data/lib/use_packs/private/interactive_cli/use_cases/make_public.rb +34 -0
- data/lib/use_packs/private/interactive_cli/use_cases/move.rb +36 -0
- data/lib/use_packs/private/interactive_cli/use_cases/nest.rb +31 -0
- data/lib/use_packs/private/interactive_cli/use_cases/query.rb +51 -0
- data/lib/use_packs/private/interactive_cli/use_cases/regenerate_rubocop_todo.rb +26 -0
- data/lib/use_packs/private/interactive_cli/use_cases/rename.rb +34 -0
- data/lib/use_packs/private/interactive_cli/use_cases/update_deprecations.rb +25 -0
- data/lib/use_packs/private/interactive_cli/use_cases/validate.rb +25 -0
- data/lib/use_packs/private/interactive_cli/use_cases/visualize.rb +44 -0
- data/lib/use_packs/private/interactive_cli.rb +52 -0
- data/lib/use_packs/private/pack_relationship_analyzer.rb +135 -0
- data/lib/use_packs/private/packwerk_wrapper/offenses_aggregator_formatter.rb +34 -0
- data/lib/use_packs/private/packwerk_wrapper.rb +71 -0
- data/lib/use_packs/private.rb +453 -0
- data/lib/use_packs/rubocop_post_processor.rb +67 -0
- data/lib/use_packs/spring_command.rb +28 -0
- data/lib/use_packs/user_event_logger.rb +259 -0
- data/lib/use_packs.rb +298 -0
- metadata +351 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 88d0834366bd3d1591029828eab623a5781f0ed44a3b8c0b35ab92f131236377
|
4
|
+
data.tar.gz: 8f3dd5181a41f8bd3ff2441fc58c73bac8ec712137a5967b7447d62b9014d653
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3cb7be9b2014cc3c065d700e19472821acda6b9a41fe73cdf695e52d2d96ee1552aabaf2e123c8f4d4b21c1b469cb26073e8eaa650874d6ded5fbdb63c3a401e
|
7
|
+
data.tar.gz: 6a6a15f8f010a10988eae52796bbbf55326c7a814bc1d0ec3a550edd860cc038fb7eb917860e7629cae1d6b2462901296b5bb163857b3cf955a7c93b6d9ffe21
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# UsePacks
|
2
|
+
|
3
|
+
UsePacks is a gem that helps in creating and maintaining packs. It exists to help perform some basic operations needed for pack setup and configuration. It provides a basic ruby file packager utility for [`packwerk`](https://github.com/Shopify/packwerk/). It assumes you are using [`stimpack`](https://github.com/rubyatscale/stimpack) to organize your packages.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
### General Help
|
7
|
+
`bin/packs --help` or just `bin/packs` to enter interactive mode.
|
8
|
+
|
9
|
+
### Pack Creation
|
10
|
+
`bin/packs create packs/your_pack_name_here`
|
11
|
+
|
12
|
+
### Moving files to packs
|
13
|
+
`bin/packs move packs/your_pack_name_here path/to/file.rb path/to/directory`
|
14
|
+
This is used for moving files into a pack (the pack must already exist).
|
15
|
+
Note this works for moving files to packs from the monolith or from other packs
|
16
|
+
|
17
|
+
Make sure there are no spaces between the comma-separated list of paths of directories.
|
18
|
+
|
19
|
+
### Moving a file to public API
|
20
|
+
`bin/packs make_public path/to/file.rb,path/to/directory`
|
21
|
+
This moves a file or directory to public API (that is -- the `app/public` folder).
|
22
|
+
|
23
|
+
Make sure there are no spaces between the comma-separated list of paths of directories.
|
24
|
+
|
25
|
+
### Listing top privacy violations
|
26
|
+
`bin/packs list_top_privacy_violations packs/my_pack`
|
27
|
+
Want to create interfaces? Not sure how your pack's code is being used?
|
28
|
+
|
29
|
+
You can use this command to list the top privacy violations.
|
30
|
+
|
31
|
+
If no pack name is passed in, this will list out violations across all packs.
|
32
|
+
|
33
|
+
### Listing top dependency violations
|
34
|
+
`bin/packs list_top_dependency_violations packs/my_pack`
|
35
|
+
Want to see who is depending on you? Not sure how your pack's code is being used in an unstated way
|
36
|
+
|
37
|
+
You can use this command to list the top dependency violations.
|
38
|
+
|
39
|
+
If no pack name is passed in, this will list out violations across all packs.
|
40
|
+
|
41
|
+
### Adding a dependency
|
42
|
+
`bin/packs add_dependency packs/my_pack packs/dependency_pack_name`
|
43
|
+
|
44
|
+
This can be used to quickly modify a `package.yml` file and add a dependency. It also cleans up the list of dependencies to sort the list and remove redundant entries.
|
45
|
+
|
46
|
+
### Setting up Spring
|
47
|
+
|
48
|
+
[Spring](https://github.com/rails/spring) is a preloader for Rails. Although `use_packs` itself does not use `Rails`, this can help speed up running commands like `bin/packs` by caching the bundle.
|
49
|
+
Firstly, spring needs to know about the `bin/packs` command when spring is loading. To do that, add `require 'use_packs/spring_command'` to `config/spring.rb` in your application.
|
50
|
+
Secondly, to enable Spring, first run `bin/spring binstub packs` which will "springify" the generated binstub.
|
51
|
+
|
52
|
+
### Releasing
|
53
|
+
Releases happen automatically through github actions once a version update is committed to `main`.
|
54
|
+
|
55
|
+
## Discussions, Issues, Questions, and More
|
56
|
+
To keep things organized, here are some recommended homes:
|
57
|
+
|
58
|
+
### Issues:
|
59
|
+
https://github.com/Gusto/use_packs/issues
|
60
|
+
|
61
|
+
### Questions:
|
62
|
+
https://github.com/Gusto/use_packs/discussions/categories/q-a
|
63
|
+
|
64
|
+
### General discussions:
|
65
|
+
https://github.com/Gusto/use_packs/discussions/categories/general
|
66
|
+
|
67
|
+
### Ideas, new features, requests for change:
|
68
|
+
https://github.com/Gusto/use_packs/discussions/categories/ideas
|
69
|
+
|
70
|
+
### Showcasing your work:
|
71
|
+
https://github.com/Gusto/use_packs/discussions/categories/show-and-tell
|
data/bin/packs
ADDED
data/bin/rubocop
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rubocop' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'pathname'
|
12
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path('bundle', __dir__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'rubygems'
|
27
|
+
require 'bundler/setup'
|
28
|
+
|
29
|
+
load Gem.bin_path('rubocop', 'rubocop')
|
data/bin/tapioca
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'tapioca' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'pathname'
|
12
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path('bundle', __dir__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'rubygems'
|
27
|
+
require 'bundler/setup'
|
28
|
+
|
29
|
+
load Gem.bin_path('tapioca', 'tapioca')
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
module UsePacks
|
6
|
+
class CLI < Thor
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
desc 'create packs/your_pack', 'Create pack with name packs/your_pack'
|
10
|
+
sig { params(pack_name: String).void }
|
11
|
+
def create(pack_name)
|
12
|
+
UsePacks.create_pack!(pack_name: pack_name)
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'add_dependency packs/from_pack packs/to_pack', 'Add packs/to_pack to packs/from_pack/package.yml list of dependencies'
|
16
|
+
long_desc <<~LONG_DESC
|
17
|
+
Use this to add a dependency between packs.
|
18
|
+
|
19
|
+
When you use bin/packs add_dependency packs/from_pack packs/to_pack, this command will
|
20
|
+
modify packs/from_pack/package.yml's list of dependencies and add packs/to_pack.
|
21
|
+
|
22
|
+
This command will also sort the list and make it unique.
|
23
|
+
LONG_DESC
|
24
|
+
sig { params(from_pack: String, to_pack: String).void }
|
25
|
+
def add_dependency(from_pack, to_pack)
|
26
|
+
UsePacks.add_dependency!(
|
27
|
+
pack_name: from_pack,
|
28
|
+
dependency_name: to_pack
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'list_top_dependency_violations packs/your_pack', 'List the top dependency violations of packs/your_pack'
|
33
|
+
option :limit, type: :numeric, default: 10, aliases: :l, banner: 'Specify the limit of constants to analyze'
|
34
|
+
sig { params(pack_name: String).void }
|
35
|
+
def list_top_dependency_violations(pack_name)
|
36
|
+
UsePacks.list_top_dependency_violations(
|
37
|
+
pack_name: pack_name,
|
38
|
+
limit: options[:limit]
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'list_top_privacy_violations packs/your_pack', 'List the top privacy violations of packs/your_pack'
|
43
|
+
option :limit, type: :numeric, default: 10, aliases: :l, banner: 'Specify the limit of constants to analyze'
|
44
|
+
sig { params(pack_name: String).void }
|
45
|
+
def list_top_privacy_violations(pack_name)
|
46
|
+
UsePacks.list_top_privacy_violations(
|
47
|
+
pack_name: pack_name,
|
48
|
+
limit: options[:limit]
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
desc 'make_public path/to/file.rb path/to/directory', 'Pass in a space-separated list of file or directory paths to make public'
|
53
|
+
sig { params(paths: String).void }
|
54
|
+
def make_public(*paths)
|
55
|
+
UsePacks.make_public!(
|
56
|
+
paths_relative_to_root: paths,
|
57
|
+
per_file_processors: [UsePacks::RubocopPostProcessor.new, UsePacks::CodeOwnershipPostProcessor.new]
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
desc 'move packs/destination_pack path/to/file.rb path/to/directory', 'Pass in a destination pack and a space-separated list of file or directory paths to move to the destination pack'
|
62
|
+
sig { params(pack_name: String, paths: String).void }
|
63
|
+
def move(pack_name, *paths)
|
64
|
+
UsePacks.move_to_pack!(
|
65
|
+
pack_name: pack_name,
|
66
|
+
paths_relative_to_root: paths,
|
67
|
+
per_file_processors: [UsePacks::RubocopPostProcessor.new, UsePacks::CodeOwnershipPostProcessor.new]
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
desc 'move_to_parent packs/parent_pack packs/child_pack', 'Pass in a parent pack and another pack to be made as a child to the parent pack!'
|
72
|
+
sig { params(parent_name: String, pack_name: String).void }
|
73
|
+
def move_to_parent(parent_name, pack_name)
|
74
|
+
UsePacks.move_to_parent!(
|
75
|
+
parent_name: parent_name,
|
76
|
+
pack_name: pack_name,
|
77
|
+
per_file_processors: [UsePacks::RubocopPostProcessor.new, UsePacks::CodeOwnershipPostProcessor.new]
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
desc 'lint_deprecated_references_yml_files', 'Ensures `deprecated_references.yml` files are up to date'
|
82
|
+
sig { void }
|
83
|
+
def lint_deprecated_references_yml_files
|
84
|
+
UsePacks.lint_deprecated_references_yml_files!
|
85
|
+
end
|
86
|
+
|
87
|
+
desc 'lint_package_yml_files [ packs/my_pack packs/my_other_pack ]', 'Lint `package.yml` files'
|
88
|
+
sig { params(pack_names: String).void }
|
89
|
+
def lint_package_yml_files(*pack_names)
|
90
|
+
UsePacks.lint_package_yml_files!(parse_pack_names(pack_names))
|
91
|
+
end
|
92
|
+
|
93
|
+
desc 'validate', 'Run bin/packwerk validate (detects cycles)'
|
94
|
+
sig { void }
|
95
|
+
def validate
|
96
|
+
system('bin/packwerk validate')
|
97
|
+
end
|
98
|
+
|
99
|
+
desc 'check [ packs/my_pack ]', 'Run bin/packwerk check'
|
100
|
+
sig { params(paths: String).void }
|
101
|
+
def check(*paths)
|
102
|
+
system("bin/packwerk check #{paths.join(' ')}")
|
103
|
+
end
|
104
|
+
|
105
|
+
desc 'update [ packs/my_pack ]', 'Run bin/packwerk update-deprecations'
|
106
|
+
sig { params(paths: String).void }
|
107
|
+
def update(*paths)
|
108
|
+
system("bin/packwerk update-deprecations #{paths.join(' ')}")
|
109
|
+
end
|
110
|
+
|
111
|
+
desc 'regenerate_rubocop_todo [ packs/my_pack packs/my_other_pack ]', "Regenerate packs/*/#{RuboCop::Packs::PACK_LEVEL_RUBOCOP_TODO_YML} for one or more packs"
|
112
|
+
sig { params(pack_names: String).void }
|
113
|
+
def regenerate_rubocop_todo(*pack_names)
|
114
|
+
RuboCop::Packs.regenerate_todo(packs: parse_pack_names(pack_names))
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
# This is used by thor to know that these private methods are not intended to be CLI commands
|
120
|
+
no_commands do
|
121
|
+
sig { params(pack_names: T::Array[String]).returns(T::Array[ParsePackwerk::Package]) }
|
122
|
+
def parse_pack_names(pack_names)
|
123
|
+
pack_names.empty? ? ParsePackwerk.all : pack_names.map { |p| ParsePackwerk.find(p.gsub(%r{/$}, '')) }.compact
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module UsePacks
|
4
|
+
class CodeOwnershipPostProcessor
|
5
|
+
include PerFileProcessorInterface
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
sig { void }
|
9
|
+
def initialize
|
10
|
+
@teams = T.let([], T::Array[String])
|
11
|
+
@did_move_files = T.let(false, T::Boolean)
|
12
|
+
end
|
13
|
+
|
14
|
+
sig { override.params(file_move_operation: Private::FileMoveOperation).void }
|
15
|
+
def before_move_file!(file_move_operation)
|
16
|
+
relative_path_to_origin = file_move_operation.origin_pathname
|
17
|
+
relative_path_to_destination = file_move_operation.destination_pathname
|
18
|
+
|
19
|
+
code_owners_allow_list_file = Pathname.new('config/code_ownership.yml')
|
20
|
+
|
21
|
+
if code_owners_allow_list_file.exist?
|
22
|
+
UsePacks.replace_in_file(
|
23
|
+
file: code_owners_allow_list_file.to_s,
|
24
|
+
find: relative_path_to_origin,
|
25
|
+
replace_with: relative_path_to_destination
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
team = CodeOwnership.for_file(relative_path_to_origin.to_s)
|
30
|
+
|
31
|
+
if team
|
32
|
+
@teams << team.name
|
33
|
+
else
|
34
|
+
@teams << 'Unknown'
|
35
|
+
end
|
36
|
+
|
37
|
+
if !CodeOwnership.for_package(file_move_operation.destination_pack).nil?
|
38
|
+
CodeOwnership.remove_file_annotation!(relative_path_to_origin.to_s)
|
39
|
+
@did_move_files = true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { params(file_move_operations: T::Array[Private::FileMoveOperation]).void }
|
44
|
+
def after_move_files!(file_move_operations)
|
45
|
+
if @teams.any?
|
46
|
+
Logging.section('Code Ownership') do
|
47
|
+
Logging.print('This section contains info about the current ownership distribution of the moved files.')
|
48
|
+
@teams.group_by { |team| team }.sort_by { |_team, instances| -instances.count }.each do |team, instances|
|
49
|
+
Logging.print " #{team} - #{instances.count} files"
|
50
|
+
end
|
51
|
+
if @did_move_files
|
52
|
+
Logging.print 'Since the destination package has package-based ownership, file-annotations were removed from moved files.'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
require 'use_packs/user_event_logger'
|
4
|
+
require 'use_packs/default_user_event_logger'
|
5
|
+
|
6
|
+
module UsePacks
|
7
|
+
class Configuration
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { params(enforce_dependencies: T::Boolean).void }
|
11
|
+
attr_writer :enforce_dependencies
|
12
|
+
|
13
|
+
sig { returns(UserEventLogger) }
|
14
|
+
attr_accessor :user_event_logger
|
15
|
+
|
16
|
+
OnDeprecatedReferencesLintFailure = T.type_alias do
|
17
|
+
T.proc.params(output: String).void
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { returns(OnDeprecatedReferencesLintFailure) }
|
21
|
+
attr_accessor :on_deprecated_references_lint_failure
|
22
|
+
|
23
|
+
sig { void }
|
24
|
+
def initialize
|
25
|
+
@enforce_dependencies = T.let(default_enforce_dependencies, T::Boolean)
|
26
|
+
@user_event_logger = T.let(DefaultUserEventLogger.new, UserEventLogger)
|
27
|
+
@on_deprecated_references_lint_failure = T.let(->(output) {}, OnDeprecatedReferencesLintFailure)
|
28
|
+
end
|
29
|
+
|
30
|
+
sig { returns(T::Boolean) }
|
31
|
+
def enforce_dependencies
|
32
|
+
@enforce_dependencies
|
33
|
+
end
|
34
|
+
|
35
|
+
sig { void }
|
36
|
+
def bust_cache!
|
37
|
+
@enforce_dependencies = default_enforce_dependencies
|
38
|
+
end
|
39
|
+
|
40
|
+
sig { returns(T::Boolean) }
|
41
|
+
def default_enforce_dependencies
|
42
|
+
true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
extend T::Sig
|
48
|
+
|
49
|
+
sig { returns(Configuration) }
|
50
|
+
def config
|
51
|
+
Private.load_client_configuration
|
52
|
+
@config = T.let(@config, T.nilable(Configuration))
|
53
|
+
@config ||= Configuration.new
|
54
|
+
end
|
55
|
+
|
56
|
+
sig { params(blk: T.proc.params(arg0: Configuration).void).void }
|
57
|
+
def configure(&blk)
|
58
|
+
yield(config)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
require 'colorized_string'
|
4
|
+
|
5
|
+
module UsePacks
|
6
|
+
module Logging
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { params(title: String, block: T.proc.void).void }
|
10
|
+
def self.section(title, &block)
|
11
|
+
print_divider
|
12
|
+
out ColorizedString.new(title).green.bold
|
13
|
+
out "\n"
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { params(text: String).void }
|
18
|
+
def self.print_bold_green(text)
|
19
|
+
out ColorizedString.new(text).green.bold
|
20
|
+
end
|
21
|
+
|
22
|
+
sig { params(text: String).void }
|
23
|
+
def self.print(text)
|
24
|
+
out text
|
25
|
+
end
|
26
|
+
|
27
|
+
sig { void }
|
28
|
+
def self.print_divider
|
29
|
+
out '=' * 100
|
30
|
+
end
|
31
|
+
|
32
|
+
sig { params(str: String).void }
|
33
|
+
def self.out(str)
|
34
|
+
puts str
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module UsePacks
|
4
|
+
module PerFileProcessorInterface
|
5
|
+
extend T::Sig
|
6
|
+
extend T::Helpers
|
7
|
+
|
8
|
+
abstract!
|
9
|
+
|
10
|
+
sig { abstract.params(file_move_operation: Private::FileMoveOperation).void }
|
11
|
+
def before_move_file!(file_move_operation); end
|
12
|
+
|
13
|
+
sig { params(file_move_operations: T::Array[Private::FileMoveOperation]).void }
|
14
|
+
def after_move_files!(file_move_operations)
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module UsePacks
|
4
|
+
module Private
|
5
|
+
class FileMoveOperation < T::Struct
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
const :origin_pathname, Pathname
|
9
|
+
const :destination_pathname, Pathname
|
10
|
+
const :destination_pack, ParsePackwerk::Package
|
11
|
+
|
12
|
+
sig { returns(ParsePackwerk::Package) }
|
13
|
+
def origin_pack
|
14
|
+
ParsePackwerk.package_from_path(origin_pathname)
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { params(origin_pathname: Pathname, new_package_root: Pathname).returns(Pathname) }
|
18
|
+
def self.destination_pathname_for_package_move(origin_pathname, new_package_root)
|
19
|
+
origin_pack = ParsePackwerk.package_from_path(origin_pathname)
|
20
|
+
|
21
|
+
if origin_pack.name == ParsePackwerk::ROOT_PACKAGE_NAME
|
22
|
+
new_package_root.join(origin_pathname).cleanpath
|
23
|
+
else
|
24
|
+
Pathname.new(origin_pathname.to_s.gsub(origin_pack.name, new_package_root.to_s)).cleanpath
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
sig { params(origin_pathname: Pathname).returns(Pathname) }
|
29
|
+
def self.destination_pathname_for_new_public_api(origin_pathname)
|
30
|
+
origin_pack = ParsePackwerk.package_from_path(origin_pathname)
|
31
|
+
if origin_pack.name == ParsePackwerk::ROOT_PACKAGE_NAME
|
32
|
+
filepath_without_pack_name = origin_pathname.to_s
|
33
|
+
else
|
34
|
+
filepath_without_pack_name = origin_pathname.to_s.gsub("#{origin_pack.name}/", '')
|
35
|
+
end
|
36
|
+
|
37
|
+
# We join the pack name with the rest of the path...
|
38
|
+
path_parts = filepath_without_pack_name.split('/')
|
39
|
+
Pathname.new(origin_pack.name).join(
|
40
|
+
# ... keeping the "app" or "spec"
|
41
|
+
T.must(path_parts[0]),
|
42
|
+
# ... substituting "controllers," "services," etc. with "public"
|
43
|
+
'public',
|
44
|
+
# ... then the rest is the same
|
45
|
+
T.must(path_parts[2..]).join('/')
|
46
|
+
# and we take the cleanpath so `./app/...` becomes `app/...`
|
47
|
+
).cleanpath
|
48
|
+
end
|
49
|
+
|
50
|
+
sig { returns(FileMoveOperation) }
|
51
|
+
def spec_file_move_operation
|
52
|
+
# This could probably be implemented by some "strategy pattern" where different extension types are handled by different helpers
|
53
|
+
# Such a thing could also include, for example, when moving a controller, moving its ERB view too.
|
54
|
+
if origin_pathname.extname == '.rake'
|
55
|
+
new_origin_pathname = origin_pathname.sub('/lib/', '/spec/lib/').sub(%r{^lib/}, 'spec/lib/').sub('.rake', '_spec.rb')
|
56
|
+
new_destination_pathname = destination_pathname.sub('/lib/', '/spec/lib/').sub(%r{^lib/}, 'spec/lib/').sub('.rake', '_spec.rb')
|
57
|
+
else
|
58
|
+
new_origin_pathname = origin_pathname.sub('/app/', '/spec/').sub(%r{^app/}, 'spec/').sub('.rb', '_spec.rb')
|
59
|
+
new_destination_pathname = destination_pathname.sub('/app/', '/spec/').sub(%r{^app/}, 'spec/').sub('.rb', '_spec.rb')
|
60
|
+
end
|
61
|
+
FileMoveOperation.new(
|
62
|
+
origin_pathname: new_origin_pathname,
|
63
|
+
destination_pathname: new_destination_pathname,
|
64
|
+
destination_pack: destination_pack
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
sig { params(path: Pathname).returns(FileMoveOperation) }
|
71
|
+
def relative_to(path)
|
72
|
+
FileMoveOperation.new(
|
73
|
+
origin_pathname: origin_pathname.relative_path_from(path),
|
74
|
+
destination_pathname: destination_pathname.relative_path_from(path),
|
75
|
+
destination_pack: destination_pack
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module UsePacks
|
4
|
+
module Private
|
5
|
+
module InteractiveCli
|
6
|
+
class PackSelector
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { params(prompt: TTY::Prompt, question_text: String).returns(ParsePackwerk::Package) }
|
10
|
+
def self.single_pack_select(prompt, question_text: 'Please select a pack')
|
11
|
+
packs = ParsePackwerk.all.to_h { |t| [t.name, t] }
|
12
|
+
prompt.select(
|
13
|
+
question_text,
|
14
|
+
packs,
|
15
|
+
filter: true,
|
16
|
+
per_page: 10,
|
17
|
+
show_help: :always
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
sig { params(prompt: TTY::Prompt, question_text: String).returns(T::Array[ParsePackwerk::Package]) }
|
22
|
+
def self.single_or_all_pack_multi_select(prompt, question_text: 'Please select one or more packs')
|
23
|
+
prompt.multi_select(
|
24
|
+
question_text,
|
25
|
+
ParsePackwerk.all.to_h { |t| [t.name, t] },
|
26
|
+
filter: true,
|
27
|
+
per_page: 10,
|
28
|
+
show_help: :always
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module UsePacks
|
4
|
+
module Private
|
5
|
+
module InteractiveCli
|
6
|
+
class TeamSelector
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { params(prompt: TTY::Prompt, question_text: String).returns(CodeTeams::Team) }
|
10
|
+
def self.single_select(prompt, question_text: 'Please select a team owner')
|
11
|
+
teams = CodeTeams.all.sort_by(&:name).to_h { |t| [t.name, t] }
|
12
|
+
prompt.select(
|
13
|
+
question_text,
|
14
|
+
teams,
|
15
|
+
filter: true,
|
16
|
+
per_page: 10,
|
17
|
+
show_help: :always
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
sig { params(prompt: TTY::Prompt, question_text: String).returns(T::Array[CodeTeams::Team]) }
|
22
|
+
def self.multi_select(prompt, question_text: 'Please select team owners')
|
23
|
+
teams = CodeTeams.all.to_h { |t| [t.name, t] }
|
24
|
+
prompt.multi_select(
|
25
|
+
question_text,
|
26
|
+
teams,
|
27
|
+
filter: true,
|
28
|
+
per_page: 10,
|
29
|
+
show_help: :always
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module UsePacks
|
4
|
+
module Private
|
5
|
+
module InteractiveCli
|
6
|
+
module UseCases
|
7
|
+
class AddDependency
|
8
|
+
extend T::Sig
|
9
|
+
extend T::Helpers
|
10
|
+
include Interface
|
11
|
+
|
12
|
+
sig { override.params(prompt: TTY::Prompt).void }
|
13
|
+
def perform!(prompt)
|
14
|
+
dependent_pack = PackSelector.single_pack_select(prompt, question_text: 'Please select the pack you are adding a dependency to.')
|
15
|
+
dependency_pack = PackSelector.single_pack_select(prompt, question_text: "Please select the pack that #{dependent_pack.name} should depend on.")
|
16
|
+
UsePacks.add_dependency!(
|
17
|
+
pack_name: dependent_pack.name,
|
18
|
+
dependency_name: dependency_pack.name
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
sig { override.returns(String) }
|
23
|
+
def user_facing_name
|
24
|
+
'Add a dependency'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module UsePacks
|
4
|
+
module Private
|
5
|
+
module InteractiveCli
|
6
|
+
module UseCases
|
7
|
+
class Check
|
8
|
+
extend T::Sig
|
9
|
+
extend T::Helpers
|
10
|
+
include Interface
|
11
|
+
|
12
|
+
sig { override.returns(String) }
|
13
|
+
def user_facing_name
|
14
|
+
'Run bin/packwerk check'
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { override.params(prompt: TTY::Prompt).void }
|
18
|
+
def perform!(prompt)
|
19
|
+
system('bin/packwerk check')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|