packs 0.0.5 → 0.0.22
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 +4 -4
- data/README.md +101 -12
- data/bin/packs +10 -0
- data/bin/rubocop +29 -0
- data/bin/tapioca +29 -0
- data/lib/packs/cli.rb +164 -0
- data/lib/packs/code_ownership_post_processor.rb +58 -0
- data/lib/packs/configuration.rb +61 -0
- data/lib/packs/default_user_event_logger.rb +7 -0
- data/lib/packs/logging.rb +37 -0
- data/lib/packs/per_file_processor_interface.rb +18 -0
- data/lib/packs/private/file_move_operation.rb +80 -0
- data/lib/packs/private/interactive_cli/file_selector.rb +26 -0
- data/lib/packs/private/interactive_cli/pack_selector.rb +55 -0
- data/lib/packs/private/interactive_cli/team_selector.rb +58 -0
- data/lib/packs/private/interactive_cli/use_cases/add_dependency.rb +30 -0
- data/lib/packs/private/interactive_cli/use_cases/check.rb +25 -0
- data/lib/packs/private/interactive_cli/use_cases/create.rb +27 -0
- data/lib/packs/private/interactive_cli/use_cases/get_info.rb +37 -0
- data/lib/packs/private/interactive_cli/use_cases/interface.rb +34 -0
- data/lib/packs/private/interactive_cli/use_cases/lint_package_todo_yml_files.rb +25 -0
- data/lib/packs/private/interactive_cli/use_cases/lint_package_yml_files.rb +26 -0
- data/lib/packs/private/interactive_cli/use_cases/make_public.rb +30 -0
- data/lib/packs/private/interactive_cli/use_cases/move.rb +32 -0
- data/lib/packs/private/interactive_cli/use_cases/move_to_parent.rb +31 -0
- data/lib/packs/private/interactive_cli/use_cases/query.rb +51 -0
- data/lib/packs/private/interactive_cli/use_cases/rename.rb +25 -0
- data/lib/packs/private/interactive_cli/use_cases/update.rb +25 -0
- data/lib/packs/private/interactive_cli/use_cases/validate.rb +25 -0
- data/lib/packs/private/interactive_cli/use_cases/visualize.rb +44 -0
- data/lib/packs/private/interactive_cli.rb +52 -0
- data/lib/packs/private/pack_relationship_analyzer.rb +135 -0
- data/lib/packs/private/packwerk_wrapper/offenses_aggregator_formatter.rb +44 -0
- data/lib/packs/private/packwerk_wrapper.rb +70 -0
- data/lib/packs/private.rb +606 -4
- data/lib/packs/rubocop_post_processor.rb +30 -0
- data/lib/packs/user_event_logger.rb +199 -0
- data/lib/packs.rb +233 -53
- metadata +225 -14
- data/lib/packs/pack.rb +0 -43
- data/lib/packs/private/configuration.rb +0 -36
- data/lib/packs/rspec/fixture_helper.rb +0 -33
- data/lib/packs/rspec/support.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bfd963aed9035b204092449d956a985ebdd62eec89bd32d522b2a8ca7a82ad74
|
4
|
+
data.tar.gz: 7a4e8471530a3847b42f9b1bd3f8523fa920e3b2e3f6c83036279d25ff9173b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23442ca91f171f85f9b2d523cc91bd34ace4186dbbb8b6f92f57ca896c473c3bcc0556c27a2e869d55285ff8c40e72df4a019af678c2f60a674af6230dae5d1a
|
7
|
+
data.tar.gz: 8754d2368251ab1ec8073191e38791dffd6c79f07c6a8eaaa49cd69d6dae0b99def340fde2c050140fdce8017612d2266d22b13b81ddafa8cbe6f67192cbeb7a
|
data/README.md
CHANGED
@@ -1,16 +1,105 @@
|
|
1
|
-
#
|
1
|
+
# Packs
|
2
2
|
|
3
|
-
|
3
|
+
Packs 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 [`packs-rails`](https://github.com/rubyatscale/packs-rails) to organize your packages.
|
4
4
|
|
5
|
-
|
5
|
+
## Usage
|
6
|
+
Make sure to run `bundle binstub use_packs` to generate `bin/packs` within your application.
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
- [`packwerk`](https://github.com/Shopify/packwerk) and [`packwerk-extensions`](https://github.com/rubyatscale/packwerk-extensions) help you describe and constrain your package graph in terms of dependencies between packs and pack public API
|
11
|
-
- [`code_ownership`](https://github.com/rubyatscale/code_ownership) gives your application the capability to determine the owner of a pack
|
12
|
-
- [`use_packs`](https://github.com/rubyatscale/use_packs) gives a CLI, `bin/packs`, that makes it easy to create new packs, move files between packs, and more.
|
13
|
-
- [`pack_stats`](https://github.com/rubyatscale/pack_stats) makes it easy to send metrics about pack adoption and modularization to your favorite metrics provider, such as DataDog (which has built-in support).
|
8
|
+
## CLI Documentation
|
9
|
+
## Describe available commands or one specific command
|
10
|
+
`bin/packs help [COMMAND]`
|
14
11
|
|
15
|
-
|
16
|
-
|
12
|
+
## Create pack with name packs/your_pack
|
13
|
+
`bin/packs create packs/your_pack`
|
14
|
+
|
15
|
+
## Add packs/to_pack to packs/from_pack/package.yml list of dependencies
|
16
|
+
`bin/packs add_dependency packs/from_pack packs/to_pack`
|
17
|
+
|
18
|
+
Use this to add a dependency between packs.
|
19
|
+
|
20
|
+
When you use bin/packs add_dependency packs/from_pack packs/to_pack, this command will
|
21
|
+
modify packs/from_pack/package.yml's list of dependencies and add packs/to_pack.
|
22
|
+
|
23
|
+
This command will also sort the list and make it unique.
|
24
|
+
|
25
|
+
## List the top dependency violations of packs/your_pack
|
26
|
+
`bin/packs list_top_dependency_violations packs/your_pack`
|
27
|
+
|
28
|
+
Want to see who is depending on you? Not sure how your pack's code is being used in an unstated way
|
29
|
+
|
30
|
+
You can use this command to list the top dependency violations.
|
31
|
+
|
32
|
+
If no pack name is passed in, this will list out violations across all packs.
|
33
|
+
|
34
|
+
## List the top privacy violations of packs/your_pack
|
35
|
+
`bin/packs list_top_privacy_violations packs/your_pack`
|
36
|
+
|
37
|
+
Want to create interfaces? Not sure how your pack's code is being used?
|
38
|
+
|
39
|
+
You can use this command to list the top privacy violations.
|
40
|
+
|
41
|
+
If no pack name is passed in, this will list out violations across all packs.
|
42
|
+
|
43
|
+
## Make files or directories public API
|
44
|
+
`bin/packs make_public path/to/file.rb path/to/directory`
|
45
|
+
|
46
|
+
This moves a file or directory to public API (that is -- the `app/public` folder).
|
47
|
+
|
48
|
+
Make sure there are no spaces between the comma-separated list of paths of directories.
|
49
|
+
|
50
|
+
## Move files or directories from one pack to another
|
51
|
+
`bin/packs move packs/destination_pack path/to/file.rb path/to/directory`
|
52
|
+
|
53
|
+
This is used for moving files into a pack (the pack must already exist).
|
54
|
+
Note this works for moving files to packs from the monolith or from other packs
|
55
|
+
|
56
|
+
Make sure there are no spaces between the comma-separated list of paths of directories.
|
57
|
+
|
58
|
+
## Lint `package_todo.yml` files to check for formatting issues
|
59
|
+
`bin/packs lint_package_todo_yml_files`
|
60
|
+
|
61
|
+
## Lint `package.yml` files
|
62
|
+
`bin/packs lint_package_yml_files [ packs/my_pack packs/my_other_pack ]`
|
63
|
+
|
64
|
+
## Run bin/packwerk validate (detects cycles)
|
65
|
+
`bin/packs validate`
|
66
|
+
|
67
|
+
## Run bin/packwerk check
|
68
|
+
`bin/packs check [ packs/my_pack ]`
|
69
|
+
|
70
|
+
## Run bin/packwerk update-todo
|
71
|
+
`bin/packs update`
|
72
|
+
|
73
|
+
## Get info about size and violations for packs
|
74
|
+
`bin/packs get_info [ packs/my_pack packs/my_other_pack ]`
|
75
|
+
|
76
|
+
## Visualize packs
|
77
|
+
`bin/packs visualize [ packs/my_pack packs/my_other_pack ]`
|
78
|
+
|
79
|
+
## Rename a pack
|
80
|
+
`bin/packs rename`
|
81
|
+
|
82
|
+
## Set packs/child_pack as a child of packs/parent_pack
|
83
|
+
`bin/packs move_to_parent packs/child_pack packs/parent_pack `
|
84
|
+
|
85
|
+
|
86
|
+
## Releasing
|
87
|
+
Releases happen automatically through github actions once a version update is committed to `main`.
|
88
|
+
|
89
|
+
## Discussions, Issues, Questions, and More
|
90
|
+
To keep things organized, here are some recommended homes:
|
91
|
+
|
92
|
+
### Issues:
|
93
|
+
https://github.com/rubyatscale/use_packs/issues
|
94
|
+
|
95
|
+
### Questions:
|
96
|
+
https://github.com/rubyatscale/use_packs/discussions/categories/q-a
|
97
|
+
|
98
|
+
### General discussions:
|
99
|
+
https://github.com/rubyatscale/use_packs/discussions/categories/general
|
100
|
+
|
101
|
+
### Ideas, new features, requests for change:
|
102
|
+
https://github.com/rubyatscale/use_packs/discussions/categories/ideas
|
103
|
+
|
104
|
+
### Showcasing your work:
|
105
|
+
https://github.com/rubyatscale/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')
|
data/lib/packs/cli.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
module Packs
|
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
|
+
Packs.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
|
+
Packs.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
|
+
long_desc <<~LONG_DESC
|
34
|
+
Want to see who is depending on you? Not sure how your pack's code is being used in an unstated way
|
35
|
+
|
36
|
+
You can use this command to list the top dependency violations.
|
37
|
+
|
38
|
+
If no pack name is passed in, this will list out violations across all packs.
|
39
|
+
LONG_DESC
|
40
|
+
option :limit, type: :numeric, default: 10, aliases: :l, banner: 'Specify the limit of constants to analyze'
|
41
|
+
sig { params(pack_name: String).void }
|
42
|
+
def list_top_dependency_violations(pack_name)
|
43
|
+
Packs.list_top_dependency_violations(
|
44
|
+
pack_name: pack_name,
|
45
|
+
limit: options[:limit]
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
desc 'list_top_privacy_violations packs/your_pack', 'List the top privacy violations of packs/your_pack'
|
50
|
+
long_desc <<~LONG_DESC
|
51
|
+
Want to create interfaces? Not sure how your pack's code is being used?
|
52
|
+
|
53
|
+
You can use this command to list the top privacy violations.
|
54
|
+
|
55
|
+
If no pack name is passed in, this will list out violations across all packs.
|
56
|
+
LONG_DESC
|
57
|
+
option :limit, type: :numeric, default: 10, aliases: :l, banner: 'Specify the limit of constants to analyze'
|
58
|
+
sig { params(pack_name: String).void }
|
59
|
+
def list_top_privacy_violations(pack_name)
|
60
|
+
Packs.list_top_privacy_violations(
|
61
|
+
pack_name: pack_name,
|
62
|
+
limit: options[:limit]
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
desc 'make_public path/to/file.rb path/to/directory', 'Make files or directories public API'
|
67
|
+
long_desc <<~LONG_DESC
|
68
|
+
This moves a file or directory to public API (that is -- the `app/public` folder).
|
69
|
+
|
70
|
+
Make sure there are no spaces between the comma-separated list of paths of directories.
|
71
|
+
LONG_DESC
|
72
|
+
sig { params(paths: String).void }
|
73
|
+
def make_public(*paths)
|
74
|
+
Packs.make_public!(
|
75
|
+
paths_relative_to_root: paths,
|
76
|
+
per_file_processors: [Packs::RubocopPostProcessor.new, Packs::CodeOwnershipPostProcessor.new]
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
desc 'move packs/destination_pack path/to/file.rb path/to/directory', 'Move files or directories from one pack to another'
|
81
|
+
long_desc <<~LONG_DESC
|
82
|
+
This is used for moving files into a pack (the pack must already exist).
|
83
|
+
Note this works for moving files to packs from the monolith or from other packs
|
84
|
+
|
85
|
+
Make sure there are no spaces between the comma-separated list of paths of directories.
|
86
|
+
LONG_DESC
|
87
|
+
sig { params(pack_name: String, paths: String).void }
|
88
|
+
def move(pack_name, *paths)
|
89
|
+
Packs.move_to_pack!(
|
90
|
+
pack_name: pack_name,
|
91
|
+
paths_relative_to_root: paths,
|
92
|
+
per_file_processors: [Packs::RubocopPostProcessor.new, Packs::CodeOwnershipPostProcessor.new]
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
desc 'lint_package_todo_yml_files', 'Lint `package_todo.yml` files to check for formatting issues'
|
97
|
+
sig { void }
|
98
|
+
def lint_package_todo_yml_files
|
99
|
+
Packs.lint_package_todo_yml_files!
|
100
|
+
end
|
101
|
+
|
102
|
+
desc 'lint_package_yml_files [ packs/my_pack packs/my_other_pack ]', 'Lint `package.yml` files'
|
103
|
+
sig { params(pack_names: String).void }
|
104
|
+
def lint_package_yml_files(*pack_names)
|
105
|
+
Packs.lint_package_yml_files!(parse_pack_names(pack_names))
|
106
|
+
end
|
107
|
+
|
108
|
+
desc 'validate', 'Run bin/packwerk validate (detects cycles)'
|
109
|
+
sig { void }
|
110
|
+
def validate
|
111
|
+
system('bin/packwerk validate')
|
112
|
+
end
|
113
|
+
|
114
|
+
desc 'check [ packs/my_pack ]', 'Run bin/packwerk check'
|
115
|
+
sig { params(paths: String).void }
|
116
|
+
def check(*paths)
|
117
|
+
Packs.execute(['check', *paths])
|
118
|
+
end
|
119
|
+
|
120
|
+
desc 'update', 'Run bin/packwerk update-todo'
|
121
|
+
sig { void }
|
122
|
+
def update
|
123
|
+
system('bin/packwerk update-todo')
|
124
|
+
end
|
125
|
+
|
126
|
+
desc 'get_info [ packs/my_pack packs/my_other_pack ]', 'Get info about size and violations for packs'
|
127
|
+
sig { params(pack_names: String).void }
|
128
|
+
def get_info(*pack_names)
|
129
|
+
Private.get_info(packs: parse_pack_names(pack_names))
|
130
|
+
end
|
131
|
+
|
132
|
+
desc 'visualize [ packs/my_pack packs/my_other_pack ]', 'Visualize packs'
|
133
|
+
sig { params(pack_names: String).void }
|
134
|
+
def visualize(*pack_names)
|
135
|
+
Private.visualize(packs: parse_pack_names(pack_names))
|
136
|
+
end
|
137
|
+
|
138
|
+
desc 'rename', 'Rename a pack'
|
139
|
+
sig { void }
|
140
|
+
def rename
|
141
|
+
puts Private.rename_pack
|
142
|
+
end
|
143
|
+
|
144
|
+
desc 'move_to_parent packs/child_pack packs/parent_pack ', 'Set packs/child_pack as a child of packs/parent_pack'
|
145
|
+
sig { params(child_pack_name: String, parent_pack_name: String).void }
|
146
|
+
def move_to_parent(child_pack_name, parent_pack_name)
|
147
|
+
Packs.move_to_parent!(
|
148
|
+
parent_name: parent_pack_name,
|
149
|
+
pack_name: child_pack_name,
|
150
|
+
per_file_processors: [Packs::RubocopPostProcessor.new, Packs::CodeOwnershipPostProcessor.new]
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
# This is used by thor to know that these private methods are not intended to be CLI commands
|
157
|
+
no_commands do
|
158
|
+
sig { params(pack_names: T::Array[String]).returns(T::Array[Packs::Pack]) }
|
159
|
+
def parse_pack_names(pack_names)
|
160
|
+
pack_names.empty? ? Packs.all : pack_names.map { |p| Packs.find(p.gsub(%r{/$}, '')) }.compact
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module Packs
|
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
|
+
return if !code_owners_allow_list_file.exist?
|
21
|
+
|
22
|
+
Packs.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
|
+
|
28
|
+
team = CodeOwnership.for_file(relative_path_to_origin.to_s)
|
29
|
+
|
30
|
+
if team
|
31
|
+
@teams << team.name
|
32
|
+
else
|
33
|
+
@teams << 'Unknown'
|
34
|
+
end
|
35
|
+
|
36
|
+
pack = Packs.find(file_move_operation.destination_pack.name)
|
37
|
+
if pack && !CodeOwnership.for_package(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 'packs/user_event_logger'
|
4
|
+
require 'packs/default_user_event_logger'
|
5
|
+
|
6
|
+
module Packs
|
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
|
+
OnPackageTodoLintFailure = T.type_alias do
|
17
|
+
T.proc.params(output: String).void
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { returns(OnPackageTodoLintFailure) }
|
21
|
+
attr_accessor :on_package_todo_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_package_todo_lint_failure = T.let(->(output) {}, OnPackageTodoLintFailure)
|
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 Packs
|
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 Packs
|
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 Packs
|
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,26 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module Packs
|
4
|
+
module Private
|
5
|
+
module InteractiveCli
|
6
|
+
class FileSelector
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { params(prompt: TTY::Prompt).returns(T::Array[String]) }
|
10
|
+
def self.select(prompt)
|
11
|
+
prompt.on(:keytab) do
|
12
|
+
raw_paths_relative_to_root = prompt.multiline('Please copy in a space or new line separated list of files or directories')
|
13
|
+
paths_relative_to_root = T.let([], T::Array[String])
|
14
|
+
raw_paths_relative_to_root.each do |path|
|
15
|
+
paths_relative_to_root += path.chomp.split
|
16
|
+
end
|
17
|
+
|
18
|
+
return paths_relative_to_root
|
19
|
+
end
|
20
|
+
|
21
|
+
[prompt.ask('Please input a file or directory to move (press tab to enter multiline mode)')]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|