use_packs 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|