squared 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  )