mono_repo_deps 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.ruby-version +1 -0
  4. data/.standard.yml +3 -0
  5. data/CHANGELOG.md +5 -0
  6. data/CODE_OF_CONDUCT.md +84 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +193 -0
  9. data/Rakefile +8 -0
  10. data/example/Gemfile +1 -0
  11. data/example/Gemfile.lock +51 -0
  12. data/example/MonoRepoConfig.rb +16 -0
  13. data/example/bounded_contexts/cart/cart_app/Package.rb +11 -0
  14. data/example/bounded_contexts/cart/cart_app/bin/console +11 -0
  15. data/example/bounded_contexts/cart/cart_app/package/cart_app.rb +2 -0
  16. data/example/bounded_contexts/cart/cart_core/Package.rb +12 -0
  17. data/example/bounded_contexts/cart/cart_core/bin/console +9 -0
  18. data/example/bounded_contexts/cart/cart_core/package/cart_core.rb +2 -0
  19. data/example/bounded_contexts/cart/cart_datasets/Package.rb +10 -0
  20. data/example/bounded_contexts/cart/cart_datasets/bin/console +9 -0
  21. data/example/bounded_contexts/cart/cart_datasets/package/cart_datasets.rb +2 -0
  22. data/example/bounded_contexts/orders/orders_app/Package.rb +12 -0
  23. data/example/bounded_contexts/orders/orders_app/bin/console +9 -0
  24. data/example/bounded_contexts/orders/orders_app/package/orders_app.rb +2 -0
  25. data/example/bounded_contexts/orders/orders_core/Package.rb +13 -0
  26. data/example/bounded_contexts/orders/orders_core/bin/console +9 -0
  27. data/example/bounded_contexts/orders/orders_core/package/orders_core.rb +2 -0
  28. data/example/bounded_contexts/orders/orders_datasets/Package.rb +11 -0
  29. data/example/bounded_contexts/orders/orders_datasets/bin/console +9 -0
  30. data/example/bounded_contexts/orders/orders_datasets/package/orders_datasets.rb +2 -0
  31. data/example/bounded_contexts/orders/orders_query_api/Package.rb +13 -0
  32. data/example/bounded_contexts/orders/orders_query_api/bin/console +9 -0
  33. data/example/bounded_contexts/orders/orders_query_api/package/cart_query_api.rb +2 -0
  34. data/example/bounded_contexts/orders/paypal_client/Package.rb +9 -0
  35. data/example/bounded_contexts/orders/paypal_client/bin/console +9 -0
  36. data/example/bounded_contexts/orders/paypal_client/package/paypal_client.rb +2 -0
  37. data/example/bounded_contexts/shared/db_connection/Package.rb +10 -0
  38. data/example/bounded_contexts/shared/db_connection/bin/console +9 -0
  39. data/example/bounded_contexts/shared/db_connection/package/db_connection.rb +2 -0
  40. data/example/bounded_contexts/shared/test_utils/Package.rb +9 -0
  41. data/example/bounded_contexts/shared/test_utils/bin/console +9 -0
  42. data/example/bounded_contexts/shared/test_utils/package/test_utils.rb +2 -0
  43. data/example/bounded_contexts/storefront/storefront_app/Package.rb +11 -0
  44. data/example/bounded_contexts/storefront/storefront_app/bin/console +9 -0
  45. data/example/bounded_contexts/storefront/storefront_app/package/storefront_app.rb +2 -0
  46. data/example/bounded_contexts/storefront/storefront_core/Package.rb +11 -0
  47. data/example/bounded_contexts/storefront/storefront_core/bin/console +9 -0
  48. data/example/bounded_contexts/storefront/storefront_core/package/storefront_core.rb +2 -0
  49. data/example/bounded_contexts/storefront/storefront_datasets/Package.rb +11 -0
  50. data/example/bounded_contexts/storefront/storefront_datasets/bin/console +9 -0
  51. data/example/bounded_contexts/storefront/storefront_datasets/package/storefront_datasets.rb +2 -0
  52. data/example/configs/cart_app/default.yml +4 -0
  53. data/example/configs/cart_app/test.yml +3 -0
  54. data/example/configs/orders_app/default.yml +4 -0
  55. data/example/configs/orders_app/test.yml +2 -0
  56. data/exe/mono_repo_deps +15 -0
  57. data/exe/mrd +15 -0
  58. data/lib/mono_repo_deps/config/loader.rb +74 -0
  59. data/lib/mono_repo_deps/config/manager.rb +20 -0
  60. data/lib/mono_repo_deps/config.rb +23 -0
  61. data/lib/mono_repo_deps/loaders/base.rb +63 -0
  62. data/lib/mono_repo_deps/loaders/zeitwerk.rb +77 -0
  63. data/lib/mono_repo_deps/package/builder.rb +27 -0
  64. data/lib/mono_repo_deps/package/dependency_bypasser.rb +44 -0
  65. data/lib/mono_repo_deps/package/factory.rb +100 -0
  66. data/lib/mono_repo_deps/package/find_root.rb +33 -0
  67. data/lib/mono_repo_deps/package/indexer.rb +30 -0
  68. data/lib/mono_repo_deps/package/initializer.rb +38 -0
  69. data/lib/mono_repo_deps/package/list.rb +10 -0
  70. data/lib/mono_repo_deps/package/repo.rb +55 -0
  71. data/lib/mono_repo_deps/package.rb +48 -0
  72. data/lib/mono_repo_deps/project/builder.rb +20 -0
  73. data/lib/mono_repo_deps/project/factory.rb +115 -0
  74. data/lib/mono_repo_deps/project/find_root.rb +31 -0
  75. data/lib/mono_repo_deps/project/initializer.rb +26 -0
  76. data/lib/mono_repo_deps/project.rb +43 -0
  77. data/lib/mono_repo_deps/task/manager.rb +55 -0
  78. data/lib/mono_repo_deps/task.rb +21 -0
  79. data/lib/mono_repo_deps/version.rb +5 -0
  80. data/lib/mono_repo_deps.rb +138 -0
  81. data/sig/mono_repo_deps.rbs +4 -0
  82. metadata +314 -0
@@ -0,0 +1,38 @@
1
+ require 'benchmark'
2
+
3
+ class MonoRepoDeps::Package::Initializer
4
+ include MonoRepoDeps::Mixins
5
+
6
+ Inject = MonoRepoDeps::Deps[
7
+ "package.dependency_bypasser",
8
+ "package.repo"
9
+ ]
10
+
11
+ include Inject
12
+
13
+ sig do
14
+ params(
15
+ package_name: T.any(Symbol, String),
16
+ env: Symbol,
17
+ imported: T::Array[Symbol]
18
+ )
19
+ .void
20
+ end
21
+ def call(package_name, env:, imported: [])
22
+ package_name = package_name.to_sym
23
+ packages_import_order = []
24
+
25
+ time = Benchmark.realtime do
26
+ packages_import_order = dependency_bypasser
27
+ .call(package_name: package_name, env: env, imported: imported)
28
+ .map { repo.find(_1) }
29
+ .each { MonoRepoDeps.current_project.loader.push_dir(_1.workdir_path) }
30
+ .tap { MonoRepoDeps.current_project.loader.setup }
31
+ .each { |package| require package.entrypoint_file if File.exist?(package.entrypoint_file) }
32
+ end
33
+
34
+ puts "imported package '#{package_name}' with #{packages_import_order.size} dependencies in #{'%.2f' % time} seconds for env: #{env}"
35
+
36
+ nil
37
+ end
38
+ end
@@ -0,0 +1,10 @@
1
+ class MonoRepoDeps::Package::List
2
+ include MonoRepoDeps::Mixins
3
+
4
+ sig do
5
+ returns(T::Array[MonoRepoDeps::Package])
6
+ end
7
+ def call
8
+ MonoRepoDeps.current_project.packages
9
+ end
10
+ end
@@ -0,0 +1,55 @@
1
+ class MonoRepoDeps::Package::Repo
2
+ include MonoRepoDeps::Mixins
3
+
4
+ Inject = MonoRepoDeps::Deps[
5
+ "package.list",
6
+ ]
7
+
8
+ include Inject
9
+
10
+ sig do
11
+ returns(T::Array[MonoRepoDeps::Package])
12
+ end
13
+ def all
14
+ list.call
15
+ end
16
+
17
+ sig do
18
+ params(
19
+ name: T.nilable(T.any(String, Symbol)),
20
+ )
21
+ .returns(T::Array[MonoRepoDeps::Package])
22
+ end
23
+ def filter(name: nil)
24
+ packages = all
25
+
26
+ unless name.nil?
27
+ names = name.is_a?(Array) ? name : [name]
28
+ names = names.map(&:to_sym)
29
+
30
+ packages = packages.select { names.include?(_1.name) }
31
+ end
32
+
33
+ packages
34
+ end
35
+
36
+ sig do
37
+ params(
38
+ package_name: T.nilable(T.any(String, Symbol)),
39
+ )
40
+ .returns(T.nilable(MonoRepoDeps::Package))
41
+ end
42
+ def find(package_name)
43
+ all.detect { _1.name == package_name.to_sym }
44
+ end
45
+
46
+ sig do
47
+ params(
48
+ package_name: T.nilable(T.any(String, Symbol)),
49
+ )
50
+ .returns(MonoRepoDeps::Package)
51
+ end
52
+ def find!(package_name)
53
+ find(package_name) || (raise StandardError.new("package '#{package_name}' was not found for MonoRepoDeps project '#{MonoRepoDeps.current_project.root_path}'"))
54
+ end
55
+ end
@@ -0,0 +1,48 @@
1
+ class MonoRepoDeps::Package
2
+ include MonoRepoDeps::Mixins
3
+
4
+ attr_reader :name, :root_path, :dependencies, :configs
5
+
6
+ DEFAULT_ENV = :_default_
7
+
8
+ DependencyDto = Struct.new(:name, :only, :skip, keyword_init: true)
9
+
10
+ sig do
11
+ params(
12
+ name: Symbol,
13
+ root_path: String,
14
+ package_dirname: String,
15
+ dependencies: T::Hash[Symbol, DependencyDto]
16
+ )
17
+ .void
18
+ end
19
+ def initialize(name:, root_path:, package_dirname:, dependencies:)
20
+ @name = name
21
+ @root_path = root_path
22
+ @package_dirname = package_dirname
23
+ @dependencies = dependencies
24
+
25
+ nil
26
+ end
27
+
28
+ sig do
29
+ params(env: T.nilable(Symbol)).returns(T::Array[DependencyDto])
30
+ end
31
+ def get_dependencies(env = nil)
32
+ [DEFAULT_ENV, env].uniq.compact.inject([]) do |acc, item|
33
+ acc += @dependencies.fetch(item, [])
34
+ end
35
+ end
36
+
37
+ def get_dependency_envs
38
+ @dependencies.keys
39
+ end
40
+
41
+ def workdir_path
42
+ File.join( self.root_path, @package_dirname )
43
+ end
44
+
45
+ def entrypoint_file
46
+ File.join( self.workdir_path, "#{name}.rb" )
47
+ end
48
+ end
@@ -0,0 +1,20 @@
1
+ class MonoRepoDeps::Project::Builder
2
+ include MonoRepoDeps::Mixins
3
+
4
+ Inject = MonoRepoDeps::Deps[
5
+ "project.factory",
6
+ ]
7
+
8
+ include Inject
9
+
10
+ sig do
11
+ params(project_root: String).returns(MonoRepoDeps::Project)
12
+ end
13
+ def call(project_root)
14
+ project_file_path = "#{project_root}/#{MonoRepoDeps::PROJECT_FILENAME}"
15
+
16
+ project_init_proc = proc { instance_eval(File.read(project_file_path)) }
17
+
18
+ project = factory.call(project_root, init_proc: project_init_proc)
19
+ end
20
+ end
@@ -0,0 +1,115 @@
1
+ class MonoRepoDeps::Project::Factory
2
+ include MonoRepoDeps::Mixins
3
+
4
+ sig do
5
+ params(
6
+ root_path: String,
7
+ init_proc: T.proc.params(args: T.anything).returns(T.anything)
8
+ )
9
+ .returns(MonoRepoDeps::Project)
10
+ end
11
+ def call(root_path, init_proc:)
12
+ @tasks = []
13
+ @root_path = root_path
14
+
15
+ instance_exec(&init_proc)
16
+
17
+ project = MonoRepoDeps::Project.new(
18
+ root_path: root_path,
19
+ env: @env,
20
+ configs_dir: @configs_dir,
21
+ package_dirname: @package_dirname,
22
+ packages_lookup_subdir: @packages_lookup_subdir,
23
+ loader: @loader,
24
+ tasks: @tasks
25
+ )
26
+ end
27
+
28
+ private
29
+
30
+ sig do
31
+ params(
32
+ block: T.proc.params(args: T.anything).returns(T.anything)
33
+ )
34
+ .void
35
+ end
36
+ def setup(&block)
37
+ instance_exec(@project, &block)
38
+
39
+ nil
40
+ end
41
+
42
+ sig do
43
+ params(
44
+ value: T.any(Symbol, String, T.proc.returns(T.any(String, Symbol)))
45
+ )
46
+ .void
47
+ end
48
+ def set_env(value)
49
+ @env = (value.is_a?(Proc) ? value.call : value).to_sym
50
+
51
+ raise MonoRepoDeps::Error.new("block or value should be provided") if @env.nil?
52
+
53
+ nil
54
+ end
55
+
56
+ sig do
57
+ params(value: String).void
58
+ end
59
+ def set_configs_dir(value)
60
+ @configs_dir = value
61
+
62
+ nil
63
+ end
64
+
65
+ sig do
66
+ params(value: String).void
67
+ end
68
+ def set_package_dirname(value)
69
+ @package_dirname = value
70
+
71
+ nil
72
+ end
73
+
74
+ sig do
75
+ params(
76
+ name: Symbol,
77
+ block: T.proc.params(args: T.anything).returns(T.anything)
78
+ )
79
+ .void
80
+ end
81
+ def set_loader(name, &block)
82
+ @loader = MonoRepoDeps::Loaders::Base.registry.fetch(name).new(@root_path, &block)
83
+
84
+ nil
85
+ end
86
+
87
+ sig do
88
+ params(value: String).void
89
+ end
90
+ def set_packages_lookup_subdir(value)
91
+ @packages_lookup_subdir = value
92
+
93
+ nil
94
+ end
95
+
96
+ sig do
97
+ params(
98
+ name: Symbol,
99
+ on: Symbol,
100
+ block: T.proc.params(args: T.anything).returns(T.anything),
101
+ )
102
+ .void
103
+ end
104
+ def register_task(name, on:, &block)
105
+ @tasks.push(
106
+ MonoRepoDeps::Task.new(
107
+ name: name,
108
+ on: on,
109
+ block: block
110
+ )
111
+ )
112
+
113
+ nil
114
+ end
115
+ end
@@ -0,0 +1,31 @@
1
+ require 'zeitwerk'
2
+ require 'singleton'
3
+
4
+ class MonoRepoDeps::Project::FindRoot
5
+ include MonoRepoDeps::Mixins
6
+
7
+ SYSTEM_ROOT = '/'
8
+
9
+ sig do
10
+ params(dir: String).returns(String)
11
+ end
12
+ def call(dir)
13
+ init_dir = dir = File.expand_path(dir)
14
+
15
+ unless File.exist?(init_dir)
16
+ raise StandardError.new("path '#{init_dir}' does not exist")
17
+ end
18
+
19
+ loop do
20
+ project_file_path = File.expand_path(File.join(dir, MonoRepoDeps::PROJECT_FILENAME))
21
+
22
+ if File.exist?(project_file_path)
23
+ return dir
24
+ elsif dir == SYSTEM_ROOT
25
+ raise StandardError.new("#{MonoRepoDeps::PROJECT_FILENAME} for path '#{init_dir}' not found")
26
+ else
27
+ dir = File.expand_path("../", dir)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ class MonoRepoDeps::Project::Initializer
2
+ include MonoRepoDeps::Mixins
3
+
4
+ Inject = MonoRepoDeps::Deps[
5
+ "package.indexer",
6
+ "project.builder",
7
+ "project.find_root"
8
+ ]
9
+
10
+ include Inject
11
+
12
+ sig do
13
+ params(dir: String).returns(MonoRepoDeps::Project)
14
+ end
15
+ def call(dir)
16
+ project_root = find_root.call(dir)
17
+
18
+ project = builder.call(project_root)
19
+
20
+ project_packages = indexer.call(project.packages_lookup_subdir, project.root_path, project.package_dirname)
21
+
22
+ project.set_packages(project_packages)
23
+
24
+ project
25
+ end
26
+ end
@@ -0,0 +1,43 @@
1
+ class MonoRepoDeps::Project
2
+ include MonoRepoDeps::Mixins
3
+
4
+ attr_reader :env, :configs_dir, :package_dirname, :root_path, :packages, :loader, :tasks, :packages_lookup_subdir
5
+
6
+ DEFAULT_OPTIONS = {
7
+ configs_dir: 'configs',
8
+ package_dirname: 'package',
9
+ packages_lookup_subdir: ".",
10
+ loader: MonoRepoDeps::Loaders::Base,
11
+ tasks: []
12
+ }
13
+
14
+ sig do
15
+ params(
16
+ root_path: String,
17
+ env: Symbol,
18
+ loader: T.nilable(MonoRepoDeps::Loaders::Base),
19
+ configs_dir: T.nilable(String),
20
+ package_dirname: T.nilable(String),
21
+ packages_lookup_subdir: T.nilable(String),
22
+ tasks: T.nilable(T::Array[MonoRepoDeps::Task])
23
+ )
24
+ .void
25
+ end
26
+ def initialize(root_path:, env:, loader: nil, configs_dir: nil, package_dirname: nil, packages_lookup_subdir: nil, tasks: nil)
27
+ @root_path = root_path
28
+ @env = env
29
+ @packages = []
30
+
31
+ @loader = loader || DEFAULT_OPTIONS.fetch(:loader).new(@root_path)
32
+ @tasks = tasks || DEFAULT_OPTIONS.fetch(:tasks)
33
+ @configs_dir = configs_dir || DEFAULT_OPTIONS.fetch(:configs_dir)
34
+ @package_dirname = package_dirname || DEFAULT_OPTIONS.fetch(:package_dirname)
35
+ @packages_lookup_subdir = packages_lookup_subdir || DEFAULT_OPTIONS.fetch(:packages_lookup_subdir)
36
+
37
+ nil
38
+ end
39
+
40
+ def set_packages(packages)
41
+ @packages = packages
42
+ end
43
+ end
@@ -0,0 +1,55 @@
1
+ class MonoRepoDeps::Task::Manager
2
+ include MonoRepoDeps::Mixins
3
+
4
+ Inject = MonoRepoDeps::Deps[
5
+ packages_repo: "package.repo"
6
+ ]
7
+
8
+ include Inject
9
+
10
+ sig do
11
+ params(
12
+ method_name: Symbol,
13
+ args: T.anything,
14
+ kwargs: T.anything
15
+ )
16
+ .void
17
+ end
18
+ def method_missing(method_name, *args, **kwargs)
19
+ task = MonoRepoDeps.current_project.tasks.detect { _1.name == method_name } || (raise StandardError.new("task '#{method_name}' not found for project"))
20
+
21
+ case task.on
22
+ when :package
23
+ statuses = kwargs[:packages].inject({}) do |result, package_name|
24
+ package = packages_repo.find!(package_name.to_sym)
25
+ result[package.name] = do_in_child(task.block, package, kwargs[:args])
26
+ result
27
+ end
28
+
29
+ failed_packages = statuses.reject {|package_name, exitstatus| exitstatus.success?}
30
+
31
+ if failed_packages.any?
32
+ puts failed_packages.keys
33
+ exit 1
34
+ end
35
+ else
36
+ raise StandardError.new("unsupported task subject: #{task.on}")
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def do_in_child(block, *args)
43
+ read, write = IO.pipe
44
+
45
+ pid = fork do
46
+ read.close
47
+ block.call(*args)
48
+ end
49
+
50
+ write.close
51
+ Process.wait(pid)
52
+
53
+ exit($?.exitstatus)
54
+ end
55
+ end
@@ -0,0 +1,21 @@
1
+ class MonoRepoDeps::Task
2
+ include MonoRepoDeps::Mixins
3
+
4
+ attr_reader :name, :on, :block
5
+
6
+ sig do
7
+ params(
8
+ name: Symbol,
9
+ on: Symbol,
10
+ block: T.proc.params(args: T.anything).returns(T.anything),
11
+ )
12
+ .void
13
+ end
14
+ def initialize(name:, on:, block:)
15
+ @name = name.to_sym
16
+ @on = on
17
+ @block = block
18
+
19
+ nil
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MonoRepoDeps
4
+ VERSION = "0.1.13"
5
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mono_repo_deps/version'
4
+ require 'dry/system'
5
+ require 'benchmark'
6
+ require "zeitwerk"
7
+ require 'pry'
8
+
9
+ module MonoRepoDeps
10
+ class Error < StandardError; end
11
+
12
+ PROJECT_FILENAME = 'MonoRepoConfig.rb'
13
+ PACKAGE_FILENAME = 'Package.rb'
14
+
15
+ module Mixins
16
+ def self.included(base)
17
+ require 'sorbet-runtime'
18
+
19
+ base.extend T::Sig
20
+ end
21
+ end
22
+
23
+ class Container < Dry::System::Container
24
+ use :env, inferrer: -> { ENV.fetch("RUBY_ENV", :production).to_sym }
25
+ # use :logging
26
+
27
+ configure do |config|
28
+ config.root = __dir__
29
+
30
+ config.component_dirs.add 'mono_repo_deps' do |dir|
31
+ dir.namespaces.add nil, key: nil, const: "mono_repo_deps"
32
+ end
33
+ end
34
+ end
35
+
36
+ Deps = Container.injector
37
+
38
+ Container.finalize! unless Container.env == :test
39
+
40
+ Zeitwerk::Loader
41
+ .for_gem
42
+ .tap(&:setup)
43
+ .tap(&:eager_load)
44
+
45
+ class << self
46
+ attr_accessor :current_project
47
+
48
+ def init_package(package_name = nil, from: caller_locations.first.path, env: nil)
49
+ sync_current_project!(from) do
50
+ package_name ||= Container["package.builder"].call(from, current_project.root_path, current_project.package_dirname).name
51
+ env ||= current_project.env
52
+
53
+ Container["package.initializer"].call(package_name, env: env)
54
+ end
55
+ end
56
+
57
+ def root(from = caller_locations.first.path)
58
+ sync_current_project!(from) do
59
+ current_project.root_path
60
+ end
61
+ end
62
+
63
+ def tasks(from = caller_locations.first.path)
64
+ sync_current_project!(from) do
65
+ Container["task.manager"]
66
+ end
67
+ end
68
+
69
+ def loader(from = caller_locations.first.path)
70
+ sync_current_project!(from) do
71
+ current_project.loader
72
+ end
73
+ end
74
+
75
+ def configs(from = caller_locations.first.path)
76
+ sync_current_project!(from) do
77
+ Container["config.manager"]
78
+ end
79
+ end
80
+
81
+ def packages(from = caller_locations.first.path)
82
+ sync_current_project!(from) do
83
+ Container["package.repo"]
84
+ end
85
+ end
86
+
87
+ def check_classes(from = caller_locations.first.path)
88
+ sync_current_project!(from) do
89
+ all_packages = Container["package.repo"].all
90
+ total_count = all_packages.size
91
+ imported = []
92
+ packages_order = []
93
+
94
+ all_packages.each_with_index do |package, idx|
95
+ puts "loading package #{package.name} (#{idx+1}/#{total_count}/#{imported.size})"
96
+
97
+ Container["package.dependency_bypasser"].call(
98
+ package_name: package.name,
99
+ imported: imported,
100
+ packages_order: packages_order,
101
+ env: current_project.env
102
+ )
103
+ end
104
+
105
+ packages_order
106
+ .uniq
107
+ .map { Container["package.repo"].find(_1) }
108
+ .each { current_project.loader.push_dir(_1.workdir_path) }
109
+ .tap { current_project.loader.setup }
110
+ .each { require _1.entrypoint_file if File.exist?(_1.entrypoint_file) }
111
+ .tap { current_project.loader.check_classes }
112
+ end
113
+ end
114
+
115
+ def sync_current_project!(path, &block)
116
+ path = File.expand_path(path)
117
+ path = File.dirname(path) unless File.directory?(path)
118
+
119
+ root = Container['project.find_root'].call(path)
120
+
121
+ if @current_project && @current_project.root_path == root
122
+ return block.call
123
+ end
124
+
125
+ synchronize do
126
+ @current_project = Container["project.initializer"].call(path)
127
+
128
+ block.call
129
+ end
130
+ end
131
+
132
+ def synchronize(&block)
133
+ @mutex ||= Mutex.new
134
+
135
+ @mutex.synchronize(&block)
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,4 @@
1
+ module MonoRepoDeps
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end