packs 0.0.22 → 0.0.24
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +35 -8
- data/lib/packs/cli.rb +18 -3
- data/lib/packs/private/interactive_cli/use_cases/check.rb +1 -1
- data/lib/packs/private/interactive_cli/use_cases/interface.rb +2 -2
- data/lib/packs/private/interactive_cli/use_cases/update.rb +1 -1
- data/lib/packs/private/interactive_cli/use_cases/validate.rb +1 -1
- data/lib/packs/private.rb +13 -8
- data/lib/packs/user_event_logger.rb +1 -1
- data/lib/packs.rb +15 -26
- metadata +16 -3
- data/lib/packs/private/packwerk_wrapper.rb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e9fe9bf45b169182149107b9617affd7f2b678c23568d040f16a4bcb6dfd1b1f
|
4
|
+
data.tar.gz: ade3de604d5b0a7fb3b25770eb08dce6a953028f32c66008882b80c16e4a9006
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30171b07e4b4ad796dfdd0cd1ca4425bae6f974e75cf7a70e3f2c46ad296117d5002a29303830f033913d34be90e894d5dc21230cf5cec3a64c9bc8d7af5a370
|
7
|
+
data.tar.gz: 3ea267d582ea488d4f425321f2e111c0b9adeca1b092649ccb82f88314fe8572b7a4f922936822dc84a9ad8c3c39adbe57c7a60b6dd0b7bdbe76502da7a8b795
|
data/README.md
CHANGED
@@ -1,9 +1,36 @@
|
|
1
|
-
#
|
1
|
+
# packs
|
2
2
|
|
3
|
-
|
3
|
+
`packs` are a specification for an extensible packaging system to help modularize Ruby applications.
|
4
|
+
|
5
|
+
A `pack` (short for `package`) is a folder of Ruby code with a `package.yml` at the root that is intended to represent a well-modularized domain.
|
6
|
+
|
7
|
+
This gem provides a development CLI, `bin/packs`, to make using `packs` easier.
|
8
|
+
|
9
|
+
# Configuration
|
10
|
+
By default, this library will look for `packs` in the folder `packs/*/package.yml` (as well as nested packs at `packs/*/*/package.yml`). To change where `packs` are located, create a `packs.yml` file:
|
11
|
+
```yml
|
12
|
+
pack_paths:
|
13
|
+
- "{packs,utilities,deprecated}/*" # packs with multiple roots!
|
14
|
+
- "{packs,utilities,deprecated}/*/*" # nested packs!
|
15
|
+
- gems/* # gems can be packs too!
|
16
|
+
```
|
17
|
+
|
18
|
+
# Ecosystem
|
19
|
+
The rest of the [rubyatscale](https://github.com/rubyatscale) ecosystem is intended to help make using packs and improving the boundaries between them more clear.
|
20
|
+
|
21
|
+
Here are some example integrations with `packs`:
|
22
|
+
- [`packs-specification`](https://github.com/rubyatscale/packs-specification) is a low-dependency gem that allows your production environment to query simple information about packs
|
23
|
+
- [`packs-rails`](https://github.com/rubyatscale/packs-rails) can be used to integrate `packs` into your `rails` application
|
24
|
+
- [`rubocop-packs`](https://github.com/rubyatscale/rubocop-packs) contains cops to improve boundaries around `packs`
|
25
|
+
- [`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
|
26
|
+
- [`code_ownership`](https://github.com/rubyatscale/code_ownership) gives your application the capability to determine the owner of a pack
|
27
|
+
- [`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).
|
28
|
+
|
29
|
+
# How is a pack different from a gem?
|
30
|
+
A ruby [`gem`](https://guides.rubygems.org/what-is-a-gem/) is the Ruby community solution for packaging and distributing Ruby code. A gem is a great place to start new projects, and a great end state for code that's been extracted from an existing codebase. `packs` are intended to help gradually modularize an application that has some conceptual boundaries, but is not yet ready to be factored into gems.
|
4
31
|
|
5
32
|
## Usage
|
6
|
-
Make sure to run `bundle binstub
|
33
|
+
Make sure to run `bundle binstub packs` to generate `bin/packs` within your application.
|
7
34
|
|
8
35
|
## CLI Documentation
|
9
36
|
## Describe available commands or one specific command
|
@@ -90,16 +117,16 @@ Releases happen automatically through github actions once a version update is co
|
|
90
117
|
To keep things organized, here are some recommended homes:
|
91
118
|
|
92
119
|
### Issues:
|
93
|
-
https://github.com/rubyatscale/
|
120
|
+
https://github.com/rubyatscale/packs/issues
|
94
121
|
|
95
122
|
### Questions:
|
96
|
-
https://github.com/rubyatscale/
|
123
|
+
https://github.com/rubyatscale/packs/discussions/categories/q-a
|
97
124
|
|
98
125
|
### General discussions:
|
99
|
-
https://github.com/rubyatscale/
|
126
|
+
https://github.com/rubyatscale/packs/discussions/categories/general
|
100
127
|
|
101
128
|
### Ideas, new features, requests for change:
|
102
|
-
https://github.com/rubyatscale/
|
129
|
+
https://github.com/rubyatscale/packs/discussions/categories/ideas
|
103
130
|
|
104
131
|
### Showcasing your work:
|
105
|
-
https://github.com/rubyatscale/
|
132
|
+
https://github.com/rubyatscale/packs/discussions/categories/show-and-tell
|
data/lib/packs/cli.rb
CHANGED
@@ -10,6 +10,7 @@ module Packs
|
|
10
10
|
sig { params(pack_name: String).void }
|
11
11
|
def create(pack_name)
|
12
12
|
Packs.create_pack!(pack_name: pack_name)
|
13
|
+
exit_successfully
|
13
14
|
end
|
14
15
|
|
15
16
|
desc 'add_dependency packs/from_pack packs/to_pack', 'Add packs/to_pack to packs/from_pack/package.yml list of dependencies'
|
@@ -27,6 +28,7 @@ module Packs
|
|
27
28
|
pack_name: from_pack,
|
28
29
|
dependency_name: to_pack
|
29
30
|
)
|
31
|
+
exit_successfully
|
30
32
|
end
|
31
33
|
|
32
34
|
desc 'list_top_dependency_violations packs/your_pack', 'List the top dependency violations of packs/your_pack'
|
@@ -44,6 +46,7 @@ module Packs
|
|
44
46
|
pack_name: pack_name,
|
45
47
|
limit: options[:limit]
|
46
48
|
)
|
49
|
+
exit_successfully
|
47
50
|
end
|
48
51
|
|
49
52
|
desc 'list_top_privacy_violations packs/your_pack', 'List the top privacy violations of packs/your_pack'
|
@@ -61,6 +64,7 @@ module Packs
|
|
61
64
|
pack_name: pack_name,
|
62
65
|
limit: options[:limit]
|
63
66
|
)
|
67
|
+
exit_successfully
|
64
68
|
end
|
65
69
|
|
66
70
|
desc 'make_public path/to/file.rb path/to/directory', 'Make files or directories public API'
|
@@ -75,6 +79,7 @@ module Packs
|
|
75
79
|
paths_relative_to_root: paths,
|
76
80
|
per_file_processors: [Packs::RubocopPostProcessor.new, Packs::CodeOwnershipPostProcessor.new]
|
77
81
|
)
|
82
|
+
exit_successfully
|
78
83
|
end
|
79
84
|
|
80
85
|
desc 'move packs/destination_pack path/to/file.rb path/to/directory', 'Move files or directories from one pack to another'
|
@@ -91,6 +96,7 @@ module Packs
|
|
91
96
|
paths_relative_to_root: paths,
|
92
97
|
per_file_processors: [Packs::RubocopPostProcessor.new, Packs::CodeOwnershipPostProcessor.new]
|
93
98
|
)
|
99
|
+
exit_successfully
|
94
100
|
end
|
95
101
|
|
96
102
|
desc 'lint_package_todo_yml_files', 'Lint `package_todo.yml` files to check for formatting issues'
|
@@ -108,37 +114,40 @@ module Packs
|
|
108
114
|
desc 'validate', 'Run bin/packwerk validate (detects cycles)'
|
109
115
|
sig { void }
|
110
116
|
def validate
|
111
|
-
|
117
|
+
Private.exit_with(Packs.validate)
|
112
118
|
end
|
113
119
|
|
114
120
|
desc 'check [ packs/my_pack ]', 'Run bin/packwerk check'
|
115
121
|
sig { params(paths: String).void }
|
116
122
|
def check(*paths)
|
117
|
-
Packs.
|
123
|
+
Private.exit_with(Packs.check(paths))
|
118
124
|
end
|
119
125
|
|
120
126
|
desc 'update', 'Run bin/packwerk update-todo'
|
121
127
|
sig { void }
|
122
128
|
def update
|
123
|
-
|
129
|
+
Private.exit_with(Packs.update)
|
124
130
|
end
|
125
131
|
|
126
132
|
desc 'get_info [ packs/my_pack packs/my_other_pack ]', 'Get info about size and violations for packs'
|
127
133
|
sig { params(pack_names: String).void }
|
128
134
|
def get_info(*pack_names)
|
129
135
|
Private.get_info(packs: parse_pack_names(pack_names))
|
136
|
+
exit_successfully
|
130
137
|
end
|
131
138
|
|
132
139
|
desc 'visualize [ packs/my_pack packs/my_other_pack ]', 'Visualize packs'
|
133
140
|
sig { params(pack_names: String).void }
|
134
141
|
def visualize(*pack_names)
|
135
142
|
Private.visualize(packs: parse_pack_names(pack_names))
|
143
|
+
exit_successfully
|
136
144
|
end
|
137
145
|
|
138
146
|
desc 'rename', 'Rename a pack'
|
139
147
|
sig { void }
|
140
148
|
def rename
|
141
149
|
puts Private.rename_pack
|
150
|
+
exit_successfully
|
142
151
|
end
|
143
152
|
|
144
153
|
desc 'move_to_parent packs/child_pack packs/parent_pack ', 'Set packs/child_pack as a child of packs/parent_pack'
|
@@ -149,6 +158,7 @@ module Packs
|
|
149
158
|
pack_name: child_pack_name,
|
150
159
|
per_file_processors: [Packs::RubocopPostProcessor.new, Packs::CodeOwnershipPostProcessor.new]
|
151
160
|
)
|
161
|
+
exit_successfully
|
152
162
|
end
|
153
163
|
|
154
164
|
private
|
@@ -159,6 +169,11 @@ module Packs
|
|
159
169
|
def parse_pack_names(pack_names)
|
160
170
|
pack_names.empty? ? Packs.all : pack_names.map { |p| Packs.find(p.gsub(%r{/$}, '')) }.compact
|
161
171
|
end
|
172
|
+
|
173
|
+
sig { void }
|
174
|
+
def exit_successfully
|
175
|
+
Private.exit_with(true)
|
176
|
+
end
|
162
177
|
end
|
163
178
|
end
|
164
179
|
end
|
@@ -10,9 +10,9 @@ module Packs
|
|
10
10
|
|
11
11
|
interface!
|
12
12
|
|
13
|
-
sig { params(base: Class).void }
|
13
|
+
sig { params(base: T::Class[T.anything]).void }
|
14
14
|
def self.included(base)
|
15
|
-
@use_cases ||= T.let(@use_cases, T.nilable(T::Array[Class]))
|
15
|
+
@use_cases ||= T.let(@use_cases, T.nilable(T::Array[T::Class[T.anything]]))
|
16
16
|
@use_cases ||= []
|
17
17
|
@use_cases << base
|
18
18
|
end
|
data/lib/packs/private.rb
CHANGED
@@ -8,7 +8,6 @@ require 'sorbet-runtime'
|
|
8
8
|
require 'packs/private/file_move_operation'
|
9
9
|
require 'packs/private/pack_relationship_analyzer'
|
10
10
|
require 'packs/private/interactive_cli'
|
11
|
-
require 'packs/private/packwerk_wrapper'
|
12
11
|
|
13
12
|
module Packs
|
14
13
|
module Private
|
@@ -295,7 +294,7 @@ module Packs
|
|
295
294
|
config: package.config
|
296
295
|
)
|
297
296
|
ParsePackwerk.write_package_yml!(new_package)
|
298
|
-
|
297
|
+
Packs.validate
|
299
298
|
end
|
300
299
|
|
301
300
|
sig { params(file_move_operation: FileMoveOperation, per_file_processors: T::Array[Packs::PerFileProcessorInterface]).void }
|
@@ -404,7 +403,7 @@ module Packs
|
|
404
403
|
return if @loaded_client_configuration
|
405
404
|
|
406
405
|
@loaded_client_configuration = true
|
407
|
-
client_configuration = Pathname.pwd.join('config/
|
406
|
+
client_configuration = Pathname.pwd.join('config/packs.rb')
|
408
407
|
require client_configuration.to_s if client_configuration.exist?
|
409
408
|
end
|
410
409
|
|
@@ -523,13 +522,13 @@ module Packs
|
|
523
522
|
sig { void }
|
524
523
|
def self.lint_package_todo_yml_files!
|
525
524
|
contents_before = Private.get_package_todo_contents
|
526
|
-
Packs.
|
525
|
+
Packs.update
|
527
526
|
contents_after = Private.get_package_todo_contents
|
528
527
|
diff = Private.diff_package_todo_yml(contents_before, contents_after)
|
529
528
|
|
530
529
|
if diff == ''
|
531
530
|
# No diff generated by `update-todo`
|
532
|
-
|
531
|
+
exit_with true
|
533
532
|
else
|
534
533
|
output = <<~OUTPUT
|
535
534
|
All `package_todo.yml` files must be up-to-date and that no diff is generated when running `bin/packwerk update-todo`.
|
@@ -551,7 +550,7 @@ module Packs
|
|
551
550
|
|
552
551
|
puts output
|
553
552
|
Packs.config.on_package_todo_lint_failure.call(output)
|
554
|
-
|
553
|
+
exit_with false
|
555
554
|
end
|
556
555
|
end
|
557
556
|
|
@@ -608,10 +607,16 @@ module Packs
|
|
608
607
|
end
|
609
608
|
|
610
609
|
# This function exists to give us something to stub in test
|
611
|
-
sig { params(code:
|
612
|
-
def self.
|
610
|
+
sig { params(code: T::Boolean).void }
|
611
|
+
def self.exit_with(code)
|
613
612
|
exit code
|
614
613
|
end
|
614
|
+
|
615
|
+
# This function exists to give us something to stub in test
|
616
|
+
sig { params(command: String).returns(T::Boolean) }
|
617
|
+
def self.system_with(command)
|
618
|
+
T.cast(system(command), T::Boolean)
|
619
|
+
end
|
615
620
|
end
|
616
621
|
|
617
622
|
private_constant :Private
|
data/lib/packs.rb
CHANGED
@@ -36,6 +36,21 @@ module Packs
|
|
36
36
|
Private::InteractiveCli.start!
|
37
37
|
end
|
38
38
|
|
39
|
+
sig { returns(T::Boolean) }
|
40
|
+
def self.update
|
41
|
+
Private.system_with('bin/packwerk update-todo')
|
42
|
+
end
|
43
|
+
|
44
|
+
sig { returns(T::Boolean) }
|
45
|
+
def self.validate
|
46
|
+
Private.system_with('bin/packwerk validate')
|
47
|
+
end
|
48
|
+
|
49
|
+
sig { params(files: T::Array[String]).returns(T::Boolean) }
|
50
|
+
def self.check(files)
|
51
|
+
Private.system_with("bin/packwerk check #{files.join(' ')}")
|
52
|
+
end
|
53
|
+
|
39
54
|
sig do
|
40
55
|
params(
|
41
56
|
pack_name: String,
|
@@ -222,32 +237,6 @@ module Packs
|
|
222
237
|
Specification.bust_cache!
|
223
238
|
end
|
224
239
|
|
225
|
-
#
|
226
|
-
# execute_command is like `run` except it does not `exit`
|
227
|
-
#
|
228
|
-
sig { params(argv: T.untyped, formatter: T.nilable(Packwerk::OffensesFormatter)).void }
|
229
|
-
def self.execute(argv, formatter = nil)
|
230
|
-
Private::PackwerkWrapper.with_safe_exit_if_no_files_found do
|
231
|
-
Private::PackwerkWrapper.packwerk_cli(formatter).execute_command(argv)
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
sig { params(files: T::Array[String]).returns(T::Array[Packwerk::ReferenceOffense]) }
|
236
|
-
def self.get_offenses_for_files(files)
|
237
|
-
formatter = Private::PackwerkWrapper::OffensesAggregatorFormatter.new
|
238
|
-
Private::PackwerkWrapper.packwerk_cli_execute_safely(['check', *files], formatter)
|
239
|
-
formatter.aggregated_offenses.compact
|
240
|
-
end
|
241
|
-
|
242
|
-
sig { params(files: T::Array[String]).returns(T::Array[Packwerk::ReferenceOffense]) }
|
243
|
-
def self.get_offenses_for_files_by_package(files)
|
244
|
-
packages = Private::PackwerkWrapper.package_names_for_files(files)
|
245
|
-
argv = ['check', '--packages', packages.join(',')]
|
246
|
-
formatter = Private::PackwerkWrapper::OffensesAggregatorFormatter.new
|
247
|
-
Private::PackwerkWrapper.packwerk_cli_execute_safely(argv, formatter)
|
248
|
-
formatter.aggregated_offenses.compact
|
249
|
-
end
|
250
|
-
|
251
240
|
sig { void }
|
252
241
|
def self.lint_package_todo_yml_files!
|
253
242
|
Private.lint_package_todo_yml_files!
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: packs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.24
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gusto Engineers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08-
|
11
|
+
date: 2023-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: code_ownership
|
@@ -276,6 +276,20 @@ dependencies:
|
|
276
276
|
- - ">="
|
277
277
|
- !ruby/object:Gem::Version
|
278
278
|
version: '0'
|
279
|
+
- !ruby/object:Gem::Dependency
|
280
|
+
name: spoom
|
281
|
+
requirement: !ruby/object:Gem::Requirement
|
282
|
+
requirements:
|
283
|
+
- - '='
|
284
|
+
- !ruby/object:Gem::Version
|
285
|
+
version: 1.2.1
|
286
|
+
type: :development
|
287
|
+
prerelease: false
|
288
|
+
version_requirements: !ruby/object:Gem::Requirement
|
289
|
+
requirements:
|
290
|
+
- - '='
|
291
|
+
- !ruby/object:Gem::Version
|
292
|
+
version: 1.2.1
|
279
293
|
- !ruby/object:Gem::Dependency
|
280
294
|
name: tapioca
|
281
295
|
requirement: !ruby/object:Gem::Requirement
|
@@ -331,7 +345,6 @@ files:
|
|
331
345
|
- lib/packs/private/interactive_cli/use_cases/validate.rb
|
332
346
|
- lib/packs/private/interactive_cli/use_cases/visualize.rb
|
333
347
|
- lib/packs/private/pack_relationship_analyzer.rb
|
334
|
-
- lib/packs/private/packwerk_wrapper.rb
|
335
348
|
- lib/packs/private/packwerk_wrapper/offenses_aggregator_formatter.rb
|
336
349
|
- lib/packs/rubocop_post_processor.rb
|
337
350
|
- lib/packs/user_event_logger.rb
|
@@ -1,70 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
|
-
require 'packwerk'
|
4
|
-
require 'packs/private/packwerk_wrapper/offenses_aggregator_formatter'
|
5
|
-
|
6
|
-
module Packs
|
7
|
-
module Private
|
8
|
-
module PackwerkWrapper
|
9
|
-
extend T::Sig
|
10
|
-
|
11
|
-
#
|
12
|
-
# execute_command is like `run` except it does not `exit`
|
13
|
-
#
|
14
|
-
sig { params(argv: T.untyped, formatter: T.nilable(Packwerk::OffensesFormatter)).void }
|
15
|
-
def self.packwerk_cli_execute_safely(argv, formatter = nil)
|
16
|
-
with_safe_exit_if_no_files_found do
|
17
|
-
packwerk_cli(formatter).execute_command(argv)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
sig { params(block: T.proc.returns(T.untyped)).void }
|
22
|
-
def self.with_safe_exit_if_no_files_found(&block)
|
23
|
-
block.call
|
24
|
-
rescue SystemExit => e
|
25
|
-
# Packwerk should probably exit positively here rather than raising an error -- there should be no
|
26
|
-
# errors if the user has excluded all files being checked.
|
27
|
-
unless e.message == 'No files found or given. Specify files or check the include and exclude glob in the config file.'
|
28
|
-
raise
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
sig { params(formatter: T.nilable(Packwerk::OffensesFormatter)).returns(Packwerk::Cli) }
|
33
|
-
def self.packwerk_cli(formatter)
|
34
|
-
# This is mostly copied from exe/packwerk within the packwerk gem, but we use our own formatters
|
35
|
-
# Note that packwerk does not allow you to pass in your own progress formatter currently
|
36
|
-
ENV['RAILS_ENV'] = 'test'
|
37
|
-
|
38
|
-
style = Packwerk::OutputStyles::Coloured.new
|
39
|
-
Packwerk::Cli.new(style: style, offenses_formatter: formatter)
|
40
|
-
end
|
41
|
-
|
42
|
-
sig { params(files: T::Array[String]).returns(T::Array[Packwerk::ReferenceOffense]) }
|
43
|
-
def self.get_offenses_for_files(files)
|
44
|
-
formatter = OffensesAggregatorFormatter.new
|
45
|
-
packwerk_cli_execute_safely(['check', *files], formatter)
|
46
|
-
formatter.aggregated_offenses.compact
|
47
|
-
end
|
48
|
-
|
49
|
-
sig { void }
|
50
|
-
def self.validate!
|
51
|
-
formatter = OffensesAggregatorFormatter.new
|
52
|
-
packwerk_cli_execute_safely(['validate'], formatter)
|
53
|
-
end
|
54
|
-
|
55
|
-
sig { params(files: T::Array[String]).returns(T::Array[Packwerk::ReferenceOffense]) }
|
56
|
-
def self.get_offenses_for_files_by_package(files)
|
57
|
-
packages = package_names_for_files(files)
|
58
|
-
argv = ['check', '--packages', packages.join(',')]
|
59
|
-
formatter = OffensesAggregatorFormatter.new
|
60
|
-
packwerk_cli_execute_safely(argv, formatter)
|
61
|
-
formatter.aggregated_offenses.compact
|
62
|
-
end
|
63
|
-
|
64
|
-
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
65
|
-
def self.package_names_for_files(files)
|
66
|
-
files.map { |f| ParsePackwerk.package_from_path(f).name }.compact.uniq
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|