squared 0.0.4 → 0.0.6

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.
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Squared
4
+ module Workspace
5
+ module Project
6
+ class Python < Git
7
+ REF = :python
8
+ REQUIREMENTS = %w[requirements.txt pyproject.toml setup.py].freeze
9
+ OPT_USER = %w[pre dry-run].freeze
10
+ OPT_FORCE = [*OPT_USER, 'user'].freeze
11
+ OPT_UPGRADE = [*OPT_FORCE, 'eager'].freeze
12
+ OPT_GENERAL = %w{venv isolated no-cache [v]erbose}.freeze
13
+ private_constant :REF, :REQUIREMENTS, :OPT_USER, :OPT_FORCE, :OPT_UPGRADE, :OPT_GENERAL
14
+
15
+ class << self
16
+ def populate(*); end
17
+
18
+ def tasks
19
+ nil
20
+ end
21
+
22
+ def venv?
23
+ val = ENV['VIRTUAL_ENV']
24
+ !val.nil? && Dir.exist?(val)
25
+ end
26
+
27
+ def is_a?(val)
28
+ if (val = as_path(val))
29
+ REQUIREMENTS.any? { |file| val.join(file).exist? }
30
+ else
31
+ super
32
+ end
33
+ end
34
+ end
35
+
36
+ attr_reader :requirements
37
+
38
+ def initialize(name, path, workspace, *, **kwargs)
39
+ super
40
+ @reqindex = REQUIREMENTS.index { |file| base_path(file).exist? } || 0
41
+ @requirements = base_path(REQUIREMENTS[@reqindex])
42
+ initialize_build(REF, **kwargs)
43
+ end
44
+
45
+ @@tasks[REF] = {
46
+ install: %i[user upgrade force]
47
+ }.freeze
48
+
49
+ def populate(*)
50
+ super
51
+ return unless outdated?
52
+
53
+ namespace name do
54
+ @@tasks[REF].each do |action, flags|
55
+ namespace action do
56
+ flags.each do |flag|
57
+ case action
58
+ when :install
59
+ list = case flag
60
+ when :upgrade
61
+ OPT_UPGRADE
62
+ when :force
63
+ OPT_FORCE
64
+ else
65
+ OPT_USER
66
+ end
67
+ desc format_desc(action, flag, list + OPT_GENERAL)
68
+ task flag do |_, args|
69
+ depend(flag, opts: collect_args(args, :opts), override: true)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def depend(flag = nil, opts: [], override: false)
79
+ if @depend && !override
80
+ super
81
+ elsif outdated?
82
+ case (type = install_type)
83
+ when 1, 2
84
+ cmd = pip_session 'install'
85
+ case flag
86
+ when :user
87
+ cmd << '--user'
88
+ append_general opts, OPT_USER
89
+ when :upgrade
90
+ cmd << '--upgrade'
91
+ append_general opts, OPT_UPGRADE
92
+ when :force
93
+ cmd << '--force-reinstall'
94
+ append_general opts, OPT_FORCE
95
+ end
96
+ cmd << (type == 1 ? '-r requirements.txt' : '.')
97
+ run(exception: workspace.exception, sync: invoked_sync?('depend'))
98
+ when 3
99
+ run_s("#{@bin} setup.py install", sync: invoked_sync?('depend'))
100
+ end
101
+ end
102
+ end
103
+
104
+ def outdated(*); end
105
+
106
+ def install_type(*)
107
+ requirements.exist? ? @reqindex + 1 : 0
108
+ end
109
+
110
+ def depend?
111
+ outdated? || !!@depend
112
+ end
113
+
114
+ def outdated?
115
+ install_type > 0
116
+ end
117
+
118
+ private
119
+
120
+ def pip_session(*cmd)
121
+ session('pip', *cmd)
122
+ end
123
+
124
+ def append_general(opts, list = [])
125
+ list += OPT_GENERAL
126
+ opts.each do |opt|
127
+ next unless (v = opt.match(/^v+$/)) || list.include?(opt)
128
+
129
+ @session << case opt
130
+ when 'eager'
131
+ '--upgrade-strategy=eager'
132
+ when 'venv'
133
+ '--require-virtualenv'
134
+ when 'no-cache'
135
+ '--no-cache-dir'
136
+ else
137
+ (v ? "-#{v[0]}" : "--#{opt}")
138
+ end
139
+ end
140
+ @session << '--user' if env('PIP_USER')
141
+ @session << '--no-input' if env('PIP_NO_INPUT')
142
+ if (val = env('PIP_PROXY'))
143
+ @session << "--proxy=#{shell_escape(val, quote: true)}"
144
+ end
145
+ append_nocolor
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- module Repo
4
+ module Workspace
5
5
  module Project
6
6
  class Ruby < Git
7
7
  REF = :ruby
@@ -33,17 +33,21 @@ module Squared
33
33
  rake: nil
34
34
  }.freeze
35
35
 
36
- def initialize(name, path, workspace, *, **kwargs)
36
+ attr_reader :gemfile
37
+
38
+ def initialize(name, path, workspace, *, version: nil, autodetect: false, **kwargs)
37
39
  super
38
40
  initialize_build(REF, **kwargs)
39
- @version = env('BUILD', suffix: 'VERSION', strict: true) || kwargs.delete(:version)
40
- @autodetect = kwargs.key?(:autodetect) ? kwargs.delete(:autodetect) : false
41
+ @version = env('BUILD', suffix: 'VERSION', strict: true) || version
42
+ @autodetect = autodetect
43
+ index = GEMFILE.index { |file| base_path(file).exist? } || 0
44
+ @gemfile = base_path(GEMFILE[index])
41
45
  return if !@output[0].nil? || !@copy.nil? || @version || @autodetect || (file = rakefile).nil?
42
46
 
43
47
  begin
44
48
  pat = %r{\brequire\s+(["'])bundler/gem_tasks\1}
45
49
  File.foreach(file) do |line|
46
- next unless line.match?(pat)
50
+ next unless line =~ pat
47
51
 
48
52
  @output[0] = 'bundle exec rake build'
49
53
  @copy = 'bundle exec rake install'
@@ -51,7 +55,7 @@ module Squared
51
55
  break
52
56
  end
53
57
  rescue StandardError => e
54
- error e
58
+ log.error e
55
59
  end
56
60
  end
57
61
 
@@ -112,8 +116,7 @@ module Squared
112
116
  elsif outdated?
113
117
  case flag
114
118
  when :redownload, :local, :'prefer-local'
115
- cmd = bundle_session 'install'
116
- cmd << "--#{flag}"
119
+ cmd = bundle_session 'install', "--#{flag}"
117
120
  if (val = env('BUNDLE_TRUST_POLICY'))
118
121
  cmd << "--trust-policy=#{case val
119
122
  when '0'
@@ -130,14 +133,15 @@ module Squared
130
133
  val
131
134
  end}"
132
135
  end
133
- append_opts opts, OPT_INSTALL
136
+ append_bundle opts, OPT_INSTALL
134
137
  when :'with-g', :'without-g'
135
138
  guard_params('install', flag, args: group)
136
139
  cmd = gem_session 'install', '-g'
137
140
  opt = flag.to_s.sub('-g', '')
138
- group.each { |s| cmd << "--#{opt}=#{shell_escape(s)}" }
141
+ group.each { |s| cmd << "--#{opt}=#{shell_escape(s, quote: true)}" }
139
142
  else
140
143
  cmd = bundle_session 'install'
144
+ append_bundle
141
145
  end
142
146
  run(banner: banner?, exception: workspace.exception)
143
147
  end
@@ -160,7 +164,7 @@ module Squared
160
164
  a = base_path(val)
161
165
  b = dest.join(val)
162
166
  c = glob[i] || '**/*'
163
- warn "cp #{a.join(c)} #{b}"
167
+ log.warn "cp #{a.join(c)} #{b}"
164
168
  copy_d(a, b, glob: c, verbose: verbose?)
165
169
  end
166
170
  end
@@ -173,7 +177,7 @@ module Squared
173
177
  when :all, :major, :patch, :minor
174
178
  cmd << "--#{flag}"
175
179
  end
176
- append_opts opts, OPT_UPDATE
180
+ append_bundle opts, OPT_UPDATE
177
181
  run(banner: banner?, exception: workspace.exception)
178
182
  end
179
183
 
@@ -203,7 +207,7 @@ module Squared
203
207
  end
204
208
 
205
209
  def copy?
206
- return true if @copy && (@copy.is_a?(::String) || @copy.fetch(:gemdir, nil))
210
+ return true if @copy.is_a?(::String) || @copy&.fetch(:gemdir, nil)
207
211
  return gemdir? if @gemdir
208
212
 
209
213
  gempath = -> { "gems#{::File::SEPARATOR}#{project}-#{@version}" }
@@ -213,11 +217,11 @@ module Squared
213
217
  end
214
218
  return false unless @autodetect
215
219
 
216
- quit = ->(hint) { raise ArgumentError, message('failed to parse', hint: hint) }
220
+ unsafe = ->(hint) { raise_error('failed to parse', hint: hint) }
217
221
  begin
218
222
  out = `gem -C #{shell_quote(path)} list --local -d #{project}`
219
223
  data = /#{Regexp.escape(project)} \(([^)]+)\)/.match(out)
220
- quit.('version') unless data
224
+ unsafe.('version') unless data
221
225
  ver = data[1].split(/\s*,\s*/)
222
226
  if @version
223
227
  ver.unshift(@version)
@@ -227,14 +231,14 @@ module Squared
227
231
  data = /\(#{Regexp.escape(v)}(?:,[^)]+|\b)\):([^\n]+)/.match(out)
228
232
  next unless data
229
233
 
230
- warn "using version #{v} (given #{@version})" if @version && @version != v
234
+ log.warn "using version #{v} (given #{@version})" if @version && @version != v
231
235
  @version = v
232
236
  break
233
237
  end
234
- quit.('path') unless data
238
+ unsafe.('path') unless data
235
239
  @gemdir = Pathname.new(data[1].strip).join(gempath.())
236
240
  rescue StandardError => e
237
- error e
241
+ log.error e
238
242
  @version = nil
239
243
  @gemdir = nil
240
244
  @autodetect = false
@@ -244,13 +248,11 @@ module Squared
244
248
  end
245
249
 
246
250
  def depend?
247
- !!@depend || outdated?
251
+ outdated? || !!@depend
248
252
  end
249
253
 
250
254
  def outdated?
251
- return @outdated unless @outdated.nil?
252
-
253
- @outdated = GEMFILE.any? { |file| base_path(file).exist? }
255
+ gemfile.exist?
254
256
  end
255
257
 
256
258
  def dev?
@@ -259,15 +261,18 @@ module Squared
259
261
 
260
262
  private
261
263
 
262
- def append_opts(opts, list = [])
264
+ def append_bundle(opts = nil, list = nil)
263
265
  if (val = env('BUNDLE_JOBS')).to_i > 0
264
266
  @session << "-j#{val}"
265
267
  end
266
- list.each do |flag|
267
- if opts.include?(flag)
268
- @session << "--#{flag}"
269
- elsif /^g(?:roup)?=(.+)$/.match(flag)
270
- @session << "--group=#{$1}"
268
+ @session << '--norc' if env('BUNDLE_NORC')
269
+ return unless opts && list
270
+
271
+ opts.each do |opt|
272
+ if list.include?(opt)
273
+ @session << "--#{opt}"
274
+ elsif opt.match(/^g(?:roup)?=(.+)$/)
275
+ @session << "--group=#{shell_escape($1, quote: true)}"
271
276
  end
272
277
  end
273
278
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- module Repo
4
+ module Workspace
5
5
  module Project
6
6
  class << self
7
7
  attr_accessor :line_width
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Squared
4
+ module Workspace
5
+ module Repo
6
+ include Common
7
+ include Format
8
+ include System
9
+ include Task
10
+
11
+ attr_accessor :warning
12
+
13
+ def repo(url, manifest = 'latest', run: nil, dev: nil, prod: nil)
14
+ @home = if (val = env('REPO_HOME'))
15
+ home = Pathname.new(val)
16
+ if @main == home.basename.to_s
17
+ @root = home.parent
18
+ if home.exist?
19
+ @root = nil unless home.directory?
20
+ elsif !@root.exist?
21
+ @root.mkpath
22
+ elsif !install?
23
+ @root = nil unless confirm_install
24
+ end
25
+ end
26
+ raise_error('REPO_HOME', val, hint: 'invalid') unless @root
27
+
28
+ home.realdirpath
29
+ elsif (val = env('REPO_ROOT'))
30
+ @root = Pathname.new(val).realdirpath
31
+ if !@root.exist?
32
+ @root.mkpath
33
+ elsif !install?
34
+ raise_error('REPO_ROOT', val, hint: 'exist') unless confirm_install
35
+ end
36
+ @root.join(@main).realdirpath
37
+ else
38
+ install?(pwd = Pathname.pwd) ? pwd.join(@main) : pwd
39
+ end
40
+ @root = @home.parent
41
+ @manifest_url = url
42
+ @manifest = manifest
43
+ if repo?
44
+ @script[:build] = case (val = env('REPO_BUILD'))
45
+ when 'verbose'
46
+ @verbose = true
47
+ "#{run}:verbose" if run
48
+ when 'silent'
49
+ @verbose = false
50
+ @warning = false
51
+ run
52
+ else
53
+ val || run
54
+ end
55
+ @script[:dev] = bool_match(env('REPO_DEV'), dev)
56
+ @script[:prod] = bool_match(env('REPO_PROD'), prod)
57
+ @warning = case env('REPO_WARN')
58
+ when '0'
59
+ false
60
+ when '1'
61
+ true
62
+ else
63
+ !empty?(@root)
64
+ end
65
+ @extensions << :__repo__
66
+ else
67
+ @script[:build] = run
68
+ @script[:dev] = dev
69
+ @script[:prod] = prod
70
+ end
71
+ self
72
+ end
73
+
74
+ protected
75
+
76
+ def repo?
77
+ !@manifest_url.nil? && (install? || !!@override)
78
+ end
79
+
80
+ def empty?(dir)
81
+ dir.empty? || (dir.children.size == 1 && dir.join(dir.children.first).to_s == __FILE__)
82
+ end
83
+
84
+ private
85
+
86
+ def __repo__(**kwargs)
87
+ kwargs.delete(:parallel) if env('REPO_SYNC', ignore: '0')
88
+
89
+ branch = env('REPO_MANIFEST') || read_manifest
90
+ target = branch || @manifest
91
+ stage = nil
92
+ failfast = true
93
+ cmd = []
94
+ newline = ARGV.index { |val| val.start_with?('repo:') }.to_i > 0
95
+ parse_opts = lambda do |args|
96
+ collect_args(args, :opts).each do |val|
97
+ case val
98
+ when 'no-fail'
99
+ failfast = false
100
+ when 'force'
101
+ cmd << '--force-checkout'
102
+ when 'rebase'
103
+ cmd << '--rebase'
104
+ when 'detach'
105
+ cmd << '--detach'
106
+ when 'gc'
107
+ cmd << '--auto-gc'
108
+ when 'no-update'
109
+ cmd << '--no-manifest-update'
110
+ end
111
+ end
112
+ end
113
+ status = lambda do |val, alt = nil|
114
+ ver = branch || alt
115
+ ver ? message('repo', val.sub('{0}', 'opts*=force,rebase,detach,gc,no-update,no-fail'), ver) : 'inactive'
116
+ end
117
+
118
+ namespace 'repo' do |repo|
119
+ desc status.('all[{0}]')
120
+ task 'all', [:opts] do |_, args|
121
+ parse_opts.(args)
122
+ stage ||= 'all'
123
+ repo['sync'].invoke
124
+ @project.select do |_, proj|
125
+ if proj.enabled?
126
+ proj.depend
127
+ proj.build?
128
+ end
129
+ end
130
+ .each_value { |proj| proj.has?(:dev) ? proj.refresh : proj.build }
131
+ end
132
+
133
+ desc status.("init[manifest?=#{target},{0}]", target)
134
+ task 'init', [:manifest, :opts] do |_, args|
135
+ parse_opts.(args)
136
+ stage = 'init'
137
+ puts if newline
138
+ system("repo init -u #{@manifest_url} -m #{args.manifest || target}.xml", chdir: root)
139
+ repo['all'].invoke
140
+ end
141
+
142
+ desc status.('sync[{0}]')
143
+ task 'sync', [:opts] do |_, args|
144
+ raise_error('repo is not initialized', 'rake repo:init', kind: LoadError) unless branch || stage == 'init'
145
+
146
+ parse_opts.(args)
147
+ cmd << "-j#{ENV.fetch('REPO_JOBS', ::Rake::CpuCounter.count)}"
148
+ cmd << '--fail-fast' if failfast
149
+ puts if newline && stage != 'init'
150
+ begin
151
+ shell("repo sync #{cmd.join(' ')}", chdir: root, exception: failfast)
152
+ rescue StandardError => e
153
+ emphasize(e, title: "rake stash repo:#{stage || 'sync'}")
154
+ raise
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ def read_manifest
161
+ require 'rexml/document'
162
+ return unless (file = root_path('.repo/manifest.xml')).exist?
163
+
164
+ doc = REXML::Document.new(file.read)
165
+ doc.elements['manifest/include'].attributes['name']&.sub('.xml', '')
166
+ end
167
+
168
+ def confirm_install
169
+ return false unless @root.directory?
170
+
171
+ @override = confirm(
172
+ "#{log_title(:warn)} \"#{sub_style(@root, :bold)}\" is not empty. Continue with installation? [y/N] ",
173
+ default: 'N',
174
+ timeout: env('REPO_TIMEOUT', ignore: '0', default: 15)
175
+ )
176
+ end
177
+
178
+ def bool_match(val, pat)
179
+ case val
180
+ when nil, ''
181
+ pat
182
+ when '0'
183
+ false
184
+ when '1'
185
+ true
186
+ else
187
+ Regexp.new(val)
188
+ end
189
+ end
190
+
191
+ def install?(dir = @root)
192
+ return true if empty?(dir) || dir.join('.repo').directory?
193
+ return false unless dir == root && env('REPO_HOME').nil? && !env('REPO_ROOT').nil?
194
+
195
+ root.children.none? { |path| path.directory? && path.basename.to_s[0] != '.' && path.to_s != home.to_s }
196
+ end
197
+ end
198
+
199
+ Application.include Repo
200
+ end
201
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Squared
6
+ module Workspace
7
+ class Series
8
+ include ::Rake::DSL
9
+ extend Forwardable
10
+
11
+ attr_reader :workspace, :sync, :multiple, :parallel
12
+
13
+ def_delegators :@data, :[], :each, :each_key, :keys, :fetch, :to_a, :to_s, :inspect, :merge!, :key?, :has_key?
14
+
15
+ def initialize(tasks, workspace, keys: Workspace::Application::WORKSPACE_KEYS)
16
+ if tasks.is_a?(Array)
17
+ @data = {}
18
+ tasks.each { |key| __set__ key }
19
+ else
20
+ @data = tasks.dup
21
+ end
22
+ @workspace = workspace
23
+ @keys = keys
24
+ @sync = []
25
+ @multiple = []
26
+ @parallel = []
27
+ @session = {
28
+ group: {},
29
+ parent: {},
30
+ id: []
31
+ }
32
+ end
33
+
34
+ def __set__(key)
35
+ self[key.to_sym] ||= []
36
+ end
37
+
38
+ def populate(proj)
39
+ return unless @session
40
+
41
+ group, parent, id = @session.values
42
+ each do |key, items|
43
+ target = "#{proj.name}:#{key}"
44
+ if @keys.include?(key)
45
+ next unless proj.has?(key) || (target = workspace.task_base?(key, target))
46
+ else
47
+ next unless workspace.task_include?(proj, key)
48
+ end
49
+ if (g = proj.group)
50
+ id << g
51
+ (group[:"#{key}:#{g}"] ||= []).push(target)
52
+ else
53
+ items << target
54
+ end
55
+ next unless (b = find_base(proj)) && (name = b.to_sym.to_s) != g
56
+
57
+ id << name
58
+ (parent[:"#{key}:#{name}"] ||= []).push(target)
59
+ end
60
+ end
61
+
62
+ def method_missing(key, *)
63
+ self[key]
64
+ end
65
+
66
+ def finalize!(thread = [])
67
+ thread.map!(&:to_s)
68
+ group, parent, id = @session.values
69
+ merge!(parent) if id.uniq.size > 1
70
+ merge!(group)
71
+ each do |key, items|
72
+ next if items.empty?
73
+
74
+ key = key.to_s
75
+ if items.size > 1
76
+ multiple << key
77
+ if thread.include?(key) || (key.include?(':') && thread.include?(key.split(':').first.to_sym))
78
+ desc "#{key} (thread)"
79
+ multitask key => items
80
+ parallel << key
81
+
82
+ desc "#{key} (sync)"
83
+ task "#{key}:sync": items
84
+ sync << "#{key}:sync"
85
+ multiple << "#{key}:sync"
86
+ next
87
+ end
88
+ end
89
+
90
+ desc key
91
+ task key => items
92
+ end
93
+ @session = nil
94
+ end
95
+
96
+ def has?(key)
97
+ !key.nil? && key?(key = key.to_sym) && !self[key].empty?
98
+ end
99
+
100
+ def multiple?(val = nil)
101
+ already_invoked?(multiple, val)
102
+ end
103
+
104
+ def sync?(val = nil)
105
+ already_invoked?(sync, val)
106
+ end
107
+
108
+ def parallel?(val = nil)
109
+ already_invoked?(parallel, val)
110
+ end
111
+
112
+ def respond_to_missing?(key, *)
113
+ key?(key)
114
+ end
115
+
116
+ private
117
+
118
+ def already_invoked?(list, val)
119
+ return list.include?(val) && invoked?(val) if val
120
+
121
+ ::Rake::Task.tasks.any? { |item| item.already_invoked && list.include?(item.name) }
122
+ end
123
+ end
124
+ end
125
+ end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'common'
4
+
3
5
  module Squared
4
- module Repo
6
+ module Workspace
5
7
  class << self
6
8
  def resolve(*args)
7
9
  ret = args.map do |id|
@@ -35,5 +37,6 @@ module Squared
35
37
  end
36
38
  end
37
39
 
38
- require_relative 'repo/workspace'
39
- require_relative 'repo/project'
40
+ require_relative 'workspace/application'
41
+ require_relative 'workspace/series'
42
+ require_relative 'workspace/project'
data/lib/squared.rb CHANGED
@@ -1,13 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'squared/version'
4
- require_relative 'squared/common'
5
- require_relative 'squared/repo'
6
- require_relative 'squared/config'
4
+ require_relative 'squared/workspace'
7
5
 
8
- Repo::Workspace.implement(
9
- Repo::Project::Git,
10
- Repo::Project::Node,
11
- Repo::Project::Python,
12
- Repo::Project::Ruby
6
+ Workspace = Squared::Workspace
7
+ Project = Squared::Workspace::Project
8
+
9
+ Workspace::Application.implement(
10
+ Project::Git,
11
+ Project::Node,
12
+ Project::Python,
13
+ Project::Ruby
13
14
  )