packs 0.0.6 → 0.0.22
Sign up to get free protection for your applications and to get access to all the features.
- 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 -48
- 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
|