scout-camp 0.1.13 → 0.1.14

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +52 -11
  3. data/Rakefile +5 -0
  4. data/VERSION +1 -1
  5. data/bin/scout-camp +46 -0
  6. data/doc/terraform.md +188 -0
  7. data/lib/scout/aws/s3.rb +6 -4
  8. data/lib/scout/offsite/resource.rb +110 -5
  9. data/lib/scout/offsite/step.rb +21 -14
  10. data/lib/scout/offsite/sync.rb +38 -10
  11. data/lib/scout/offsite.rb +1 -0
  12. data/lib/scout/render/engine.rb +119 -0
  13. data/lib/scout/render/helpers.rb +92 -0
  14. data/lib/scout/render/resource.rb +54 -0
  15. data/lib/scout/render.rb +3 -0
  16. data/lib/scout/sinatra/auth.rb +158 -0
  17. data/lib/scout/sinatra/base/assets.rb +245 -0
  18. data/lib/scout/sinatra/base/favicon.rb +43 -0
  19. data/lib/scout/sinatra/base/headers.rb +77 -0
  20. data/lib/scout/sinatra/base/helpers.rb +14 -0
  21. data/lib/scout/sinatra/base/parameters.rb +147 -0
  22. data/lib/scout/sinatra/base/post_processing.rb +18 -0
  23. data/lib/scout/sinatra/base/session.rb +72 -0
  24. data/lib/scout/sinatra/base.rb +253 -0
  25. data/lib/scout/sinatra/entity.rb +259 -0
  26. data/lib/scout/sinatra/finder.rb +9 -0
  27. data/lib/scout/sinatra/fragment.rb +275 -0
  28. data/lib/scout/sinatra/htmx.rb +68 -0
  29. data/lib/scout/sinatra/knowledge_base.rb +14 -0
  30. data/lib/scout/sinatra/tool.rb +11 -0
  31. data/lib/scout/sinatra/workflow.rb +129 -0
  32. data/lib/scout-camp.rb +1 -1
  33. data/scout-camp.gemspec +39 -3
  34. data/scout_commands/find +83 -0
  35. data/scout_commands/glob +90 -0
  36. data/share/aws/lambda_function.rb +53 -30
  37. data/share/terraform/aws/efs_host/data.tf +1 -2
  38. data/share/terraform/aws/efs_host/main.tf +1 -1
  39. data/share/terraform/aws/efs_host/variables.tf +5 -1
  40. data/test/scout/render/test_engine.rb +88 -0
  41. data/test/scout/render/test_resource.rb +29 -0
  42. data/test/scout/sinatra/base/test_headers.rb +125 -0
  43. data/test/scout/sinatra/base/test_parameters.rb +88 -0
  44. data/test/scout/sinatra/test_base.rb +27 -0
  45. data/test/scout/sinatra/test_entity.rb +44 -0
  46. data/test/scout/sinatra/test_render.rb +44 -0
  47. data/test/scout/sinatra/test_workflow.rb +157 -0
  48. data/test/test_helper.rb +26 -0
  49. metadata +103 -2
@@ -1,14 +1,39 @@
1
1
  require_relative 'ssh'
2
2
  class SSHLine
3
- def self.locate(server, paths, map: :user)
4
- SSHLine.scout server, <<-EOF
5
- map = :#{map}
6
- paths = [#{paths.collect{|p| "'" + p + "'" } * ", " }]
3
+
4
+ def self.locate(server, paths, map: :user, source: false)
5
+ paths = paths.collect do |path|
6
+ next path unless path.located?
7
+ relocated = Resource.identify(File.expand_path(path)).find_all.
8
+ select{|file| Open.same_file(file, path) }.
9
+ first
10
+ next path unless relocated
11
+ relocated.identify
12
+ end
13
+
14
+ script = <<-EOF
15
+ map = #{map.nil? ? "nil" : ":#{map}" }
16
+ paths = #{paths}
7
17
  located = paths.collect{|p| Path.setup(p).find(map) }
8
- identified = paths.collect{|p| Resource.identify(p) }
9
- located = located.collect{|path| path << "/" if path.directory? }
18
+ EOF
19
+
20
+ script += <<-EOF if source
21
+ located = located.collect{|p| p.exists? ? p : nil }
22
+ EOF
23
+
24
+ script += <<-EOF
25
+ identified = paths.collect{|p| Resource.identify(p) if p }
26
+ located = located.each{|path| path << "/" if path && path.directory? && ! path.end_with?('/') }
10
27
  [located, identified]
11
28
  EOF
29
+
30
+ located, identified = SSHLine.scout server, script
31
+
32
+ identified.zip(paths).each do |ip,p|
33
+ ip.pkgdir = p.pkgdir if Path === p
34
+ end
35
+
36
+ [located, identified]
12
37
  end
13
38
 
14
39
  def self.rsync(source_path, target_path, directory: false, source: nil, target: nil, dry_run: false, hard_link: false)
@@ -24,11 +49,14 @@ located = located.collect{|path| path << "/" if path.directory? }
24
49
  Open.mkdir(File.dirname(target_path))
25
50
  end
26
51
 
52
+ source_path = File.expand_path(source_path) unless source
53
+ target_path = File.expand_path(target_path) unless target
54
+
27
55
  cmd = 'rsync '
28
56
  cmd << rsync_args
29
57
  cmd << '-nv ' if dry_run
30
- cmd << (source ? [source, source_path] * ":" : source_path) << " "
31
- cmd << (target ? [target, target_path] * ":" : target_path) << " "
58
+ cmd << (source ? [source, "'" + source_path + "'"] * ":" : "'" + source_path + "'") << " "
59
+ cmd << (target ? [target, "'" + target_path + "'"] * ":" : "'" + target_path + "'") << " "
32
60
 
33
61
  CMD.cmd_log(cmd, :log => Log::HIGH)
34
62
  end
@@ -38,14 +66,14 @@ located = located.collect{|path| path << "/" if path.directory? }
38
66
  target = nil if target == 'localhost'
39
67
 
40
68
  if source
41
- source_paths, identified_paths = SSHLine.locate(source, paths)
69
+ source_paths, identified_paths = SSHLine.locate(source, paths, source: true)
42
70
  else
43
71
  source_paths = paths.collect{|p| Path === p ? p.find : p }
44
72
  identified_paths = paths.collect{|p| Resource.identify(p) }
45
73
  end
46
74
 
47
75
  if target
48
- target_paths = SSHLine.locate(target, identified_paths, map: map)
76
+ target_paths, identified_paths = SSHLine.locate(target, identified_paths, map: map)
49
77
  else
50
78
  target_paths = identified_paths.collect{|p| p.find(map) }
51
79
  end
data/lib/scout/offsite.rb CHANGED
@@ -1,3 +1,4 @@
1
1
  require_relative 'offsite/ssh'
2
2
  require_relative 'offsite/step'
3
3
  require_relative 'offsite/sync'
4
+ require_relative 'offsite/resource'
@@ -0,0 +1,119 @@
1
+ require_relative 'resource'
2
+ require_relative 'helpers'
3
+
4
+ require 'tilt'
5
+
6
+ class TemplateNotFoundException < StandardError; end
7
+ class FragmentNotFound < StandardError; end
8
+
9
+ module ScoutRender
10
+ def self.render(template_file = nil, params = {}, &block)
11
+ exec_context = IndiferentHash.process_options params,
12
+ :exec_context,
13
+ exec_context: self
14
+
15
+ if block && template_file.nil?
16
+ params = params.values if Hash === params
17
+ block.call *params
18
+ else
19
+ Log.debug "Render #{template_file}"
20
+ begin
21
+ Tilt.new(template_file).render(exec_context, params, &block)
22
+ ensure
23
+ exec_context.add_checks template_file if exec_context.respond_to?(:add_checks)
24
+ end
25
+ end
26
+ end
27
+
28
+ def self.render_step(template_file = nil, options = {}, &block)
29
+ exec_context = IndiferentHash.process_options options, :exec_context
30
+
31
+ persist_options = IndiferentHash.pull_keys options, :persist
32
+
33
+ persist_options = IndiferentHash.add_defaults persist_options,
34
+ dir: ScoutRender.cache_dir,
35
+ other: options,
36
+ name: "Step"
37
+
38
+ step_name = IndiferentHash.process_options persist_options, :step
39
+
40
+ if step_name
41
+ dir = persist_options[:dir]
42
+ path = Path === dir ? dir[step_name] : File.join(dir, step_name)
43
+ persist_options[:path] = path
44
+ end
45
+
46
+ path, extension, update = IndiferentHash.process_options persist_options,
47
+ :path, :extension, :update,
48
+ path: Persist.persistence_path(persist_options[:name], persist_options),
49
+ extension: 'html',
50
+ update: update
51
+
52
+ path = path.set_extension extension if extension and ! path.end_with?(".#{extension}")
53
+
54
+ if block
55
+ step = Step.new path, options, exec_context: exec_context, &block
56
+ else
57
+ step = Step.new path, options, exec_context: exec_context do
58
+ ScoutRender.render(template_file, options.merge(exec_context: self))
59
+ end
60
+ end
61
+
62
+ step.exec_context = step unless exec_context
63
+
64
+ step.type = :text
65
+
66
+ step.extend ScoutRenderHelpers if exec_context.nil?
67
+
68
+ step.exec_context.instance_variable_set(:@step, step)
69
+
70
+ step
71
+ end
72
+
73
+ def self.render_template(template = nil, options = {}, &block)
74
+ options = IndiferentHash.add_defaults options, persist_name: template
75
+
76
+ extension, update, run, cache, check = IndiferentHash.process_options options,
77
+ :extension, :update, :run, :cache, :check,
78
+ extension: %w(slim haml erb),
79
+ run: true,
80
+ cache: true
81
+
82
+ template_file = ScoutRender.find_resource(template, extension: extension) if template
83
+
84
+ raise TemplateNotFoundException, template unless template_file.nil? || template_file.exists?
85
+
86
+ return ScoutRender.render(template_file, options, &block) if ! cache
87
+
88
+ step = ScoutRender.render_step(template_file, options, &block)
89
+
90
+ #checks = step.info[:checks] || []
91
+ #checks << check if check
92
+ #checks << [template_file]
93
+ #checks = checks.flatten.uniq
94
+ #step.set_info :checks, checks
95
+
96
+ case update
97
+ when :false, 'false', false, :wait, 'wait'
98
+ when :true, 'true', true, :reload, 'reload'
99
+ step.clean
100
+ when nil
101
+ if checks = step.info[:checks]
102
+ step.clean if (step.done? || step.error?) && step.path.outdated?(checks)
103
+ end
104
+ else
105
+ step.clean if step.error? && step.recoverable_error?
106
+ step.clean unless step.running?
107
+ end
108
+
109
+ if run
110
+ step.run unless step.done?
111
+ else
112
+ step
113
+ end
114
+ end
115
+
116
+ def self.render_partial(template, options = {})
117
+ render_template(template, options)
118
+ end
119
+ end
@@ -0,0 +1,92 @@
1
+ module ScoutRenderHelpers
2
+ def escape_html(text)
3
+ Rack::Utils.escape_html(text)
4
+ end
5
+
6
+ def hash_to_html_tag_attributes(hash)
7
+ return "" if hash.nil? or hash.empty?
8
+ hash.collect{|k,v|
9
+ case
10
+ when (k.nil? or v.nil? or (String === v and v.empty?))
11
+ nil
12
+ when Array === v
13
+ [k,"'" << v * " " << "'"] * "="
14
+ when String === v
15
+ [k,"'" << v << "'"] * "="
16
+ when Symbol === v
17
+ [k,"'" << v.to_s << "'"] * "="
18
+ when TrueClass === v
19
+ [k,"'" << v.to_s << "'"] * "="
20
+ when Numeric === v
21
+ [k,"'" << v.to_s << "'"] * "="
22
+ else
23
+ nil
24
+ end
25
+ }.compact * " "
26
+ end
27
+
28
+ def html_tag(tag, content = nil, params = {})
29
+ attr_str = hash_to_html_tag_attributes(params)
30
+ attr_str = " " << attr_str if String === attr_str and attr_str != ""
31
+ html = if content.nil?
32
+ "<#{ tag }#{attr_str}/>"
33
+ else
34
+ "<#{ tag }#{attr_str}>#{ content.to_s }</#{ tag }>"
35
+ end
36
+
37
+ html
38
+ end
39
+
40
+ def remove_GET_param(url, param)
41
+ if Array === param
42
+ param.each do |p|
43
+ url = remove_GET_param(url, p)
44
+ end
45
+ else
46
+ url = url.gsub(/(\?|&)#{param}=[^&]+/,'\1')
47
+ end
48
+
49
+ url = url.sub(/\?&/, '?')
50
+ url = url.sub(/&&*/, '&')
51
+ url = url.sub(/&$/, '')
52
+ url = url.sub(/\?$/,'')
53
+ url
54
+ end
55
+
56
+ def add_GET_param(url, param, value)
57
+ url = remove_GET_param(url, param)
58
+ if url =~ /\?.+=/
59
+ url + "&#{ param }=#{ value }"
60
+ else
61
+ url + "?#{ param }=#{ value }"
62
+ end
63
+ end
64
+
65
+ def add_GET_params(url, params)
66
+ params.each do |k,v|
67
+ url = add_GET_param(url, k, v)
68
+ end
69
+ url
70
+ end
71
+
72
+ def add_checks(checks)
73
+ return unless Step === @step
74
+ checks = [checks] unless Array === checks
75
+ current_checks = @step.info[:checks] || []
76
+
77
+ new_files = checks - current_checks
78
+ @step.set_info :checks, new_files if new_files.any?
79
+ end
80
+
81
+ def outdated?
82
+ return false unless Step === @step
83
+ return false unless @step.path.exists?
84
+ current_checks = @step.info[:checks] || []
85
+ @step.path.outdated?(current_checks)
86
+ end
87
+
88
+ def log(...)
89
+ return unless Step === @step
90
+ @step.log(...)
91
+ end
92
+ end
@@ -0,0 +1,54 @@
1
+ require 'scout'
2
+ module ScoutRender
3
+ extend Resource
4
+
5
+ self.subdir = 'share/views'
6
+
7
+ def self.find_resource(name, extension: %w(slim erb haml) )
8
+ return name.find_with_extension(extension) if Path === name && name.find_with_extension(extension).exists?
9
+ ScoutRender.root[name].find_with_extension(extension)
10
+ end
11
+
12
+ def self.exists?(name, extension: %w(slim erb haml) )
13
+ find_resource(name, extension: extension).exists?
14
+ end
15
+
16
+ def self.find_haml(name)
17
+ find_resource(name, extension: 'haml')
18
+ end
19
+
20
+ def self.find_slim(name)
21
+ find_resource(name, extension: 'slim')
22
+ end
23
+
24
+ def self.find_js(name)
25
+ find_resource(name, extension: 'js')
26
+ end
27
+
28
+ def self.find_css(name)
29
+ find_resource(name, extension: 'css')
30
+ end
31
+
32
+
33
+ def self.find_sass(name)
34
+ find_resource(name, extension: 'sass')
35
+ end
36
+
37
+ class << self
38
+ undef :method_missing
39
+
40
+ attr_accessor :app_dir, :cache_dir, :files_dir
41
+
42
+ def app_dir
43
+ @app_dir ||= Scout.var.render[self.to_s]
44
+ end
45
+
46
+ def cache_dir
47
+ @cache_dir ||= app_dir.cache
48
+ end
49
+
50
+ def files_dir
51
+ @files_dir ||= app_dir.files
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'render/resource'
2
+ require_relative 'render/helpers'
3
+ require_relative 'render/engine'
@@ -0,0 +1,158 @@
1
+ require "sinatra"
2
+ require "omniauth"
3
+ require "omniauth-google-oauth2"
4
+
5
+ module SinatraScoutAuth
6
+ def self.registered(app)
7
+
8
+ app.use OmniAuth::Builder do
9
+ client_id = Scout::Config.get :client, :google, :google_oauth, env:"GOOGLE_CLIENT_ID"
10
+ client_secret = Scout::Config.get :secret, :google, :google_oauth, env:"GOOGLE_CLIENT_SECRET"
11
+ provider :google_oauth2, client_id, client_secret,
12
+ access_type: 'offline',
13
+ prompt: 'select_account',
14
+ scope: 'email',
15
+ setup: lambda { |env|
16
+ script_name = env['HTTP_SCRIPT_NAME']
17
+ if script_name
18
+ referer = env['rack.session']['return_to']
19
+ uri = URI.parse(referer)
20
+ # Keep only scheme + host + (optional port)
21
+ base = "#{uri.scheme}://#{uri.host}"
22
+ base += ":#{uri.port}" if uri.port && ![80, 443].include?(uri.port)
23
+
24
+ # Construct full redirect URI
25
+ redirect_uri = "#{base}#{script_name}/auth/google_oauth2/callback"
26
+
27
+ strategy = env['omniauth.strategy']
28
+
29
+ strategy.options[:redirect_uri] = redirect_uri
30
+ end
31
+
32
+ puts "OmniAuth session: #{env['rack.session'].inspect}"
33
+ }
34
+ end
35
+
36
+ app.before do
37
+ load_preferences
38
+ end
39
+
40
+ app.get "/auth/login" do
41
+ session[:return_to] = to(request.referer || "/")
42
+
43
+ render_or 'auth/login', <<~HTML
44
+ <form method='get' action='/auth/google_oauth2'>
45
+ <input type="hidden" name="authenticity_token" value='#{request.env["rack.session"]["csrf"]}'>
46
+ <button type='submit'>Login with Google</button>
47
+ </form>
48
+ HTML
49
+ end
50
+
51
+ app.get "/auth/:provider/callback" do
52
+ auth = request.env["omniauth.auth"]
53
+
54
+ # Store relevant info in session
55
+ session[:user] = {
56
+ uid: auth.uid,
57
+ name: auth.info.name,
58
+ email: auth.info.email,
59
+ image: auth.info.image
60
+ }
61
+
62
+ return_to = session[:return_to]
63
+ redirect return_to || "/"
64
+ end
65
+
66
+ app.get "/auth/logout" do
67
+ return_to = session[:return_to]
68
+ session.clear
69
+ render_or 'auth/logout', nil, return_to: return_to do
70
+ redirect return_to || "/"
71
+ end
72
+ end
73
+
74
+ app.get "/auth/failure" do
75
+ render_or 'auth/fail', "Authentication failed: #{params[:message]}"
76
+ end
77
+
78
+ app.helpers do
79
+ def current_user
80
+ session[:user]
81
+ end
82
+
83
+ def authenticated?
84
+ !current_user.nil?
85
+ end
86
+
87
+ def protected!
88
+ render_or 'auth/missing', "Not authorized" unless authenticated?
89
+ end
90
+
91
+ def user_name
92
+ case current_user
93
+ when Hash
94
+ current_user[:name] || current_user['name'] || current_user[:email] || current_user['email']
95
+ when String
96
+ current_user
97
+ end
98
+ end
99
+
100
+ def user_dir
101
+ Scout.var.sinatra.users[user_name]
102
+ end
103
+
104
+ def user_file(file)
105
+ user_dir[file.to_s]
106
+ end
107
+
108
+ def user_save(file, content)
109
+ Open.sensible_write(user_file(file), content, force: true)
110
+ end
111
+
112
+ def user_load(file)
113
+ return nil unless user_file(file).exists?
114
+ user_file(file).read
115
+ end
116
+
117
+ def save_preferences
118
+ Log.debug "save #{preferences.to_json}"
119
+ user_save(:preferences, preferences.to_json)
120
+ end
121
+
122
+ def load_preferences
123
+ return unless user_file(:preferences).exists?
124
+ session['preferences'] = JSON.parse(user_load(:preferences))
125
+ session['preferences_time'] = Open.mtime(user_file(:preferences))
126
+ end
127
+
128
+ def updated_preferrences?
129
+ return true if session['preferences_time'].nil?
130
+ session['preferences_time'] < Open.mtime(user_file(:preferences))
131
+ end
132
+
133
+ def preferences_changed
134
+ @preferences_changed ||= []
135
+ end
136
+
137
+ def record_preference(key, value)
138
+ preferences_changed << key
139
+ preferences[key] = value
140
+ save_preferences if current_user
141
+ value
142
+ end
143
+
144
+ def get_preference(key)
145
+ load_preferences if current_user && updated_preferrences?
146
+ preferences[key]
147
+ end
148
+
149
+ def preference(key, default= nil, &block)
150
+ current = get_preference(key)
151
+ return current unless current.nil?
152
+ default = block.call if default.nil? and block_given?
153
+ record_preference(key, default) if default
154
+ default
155
+ end
156
+ end
157
+ end
158
+ end