faastruby 0.4.18 → 0.5.0

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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -3
  3. data/Gemfile.lock +28 -4
  4. data/README.md +63 -5
  5. data/faastruby.gemspec +5 -1
  6. data/lib/faastruby.rb +1 -0
  7. data/lib/faastruby/api.rb +154 -6
  8. data/lib/faastruby/base.rb +3 -9
  9. data/lib/faastruby/cli.rb +39 -12
  10. data/lib/faastruby/cli/base_command.rb +66 -0
  11. data/lib/faastruby/cli/commands.rb +122 -59
  12. data/lib/faastruby/cli/commands/account/base_command.rb +10 -0
  13. data/lib/faastruby/cli/commands/account/confirm.rb +94 -0
  14. data/lib/faastruby/cli/commands/account/login.rb +86 -0
  15. data/lib/faastruby/cli/commands/account/logout.rb +59 -0
  16. data/lib/faastruby/cli/commands/account/signup.rb +76 -0
  17. data/lib/faastruby/cli/commands/{function.rb → function/base_command.rb} +2 -11
  18. data/lib/faastruby/cli/commands/function/build.rb +18 -11
  19. data/lib/faastruby/cli/commands/function/deploy_to.rb +100 -37
  20. data/lib/faastruby/cli/commands/function/new.rb +89 -36
  21. data/lib/faastruby/cli/commands/function/remove_from.rb +21 -6
  22. data/lib/faastruby/cli/commands/function/run.rb +15 -15
  23. data/lib/faastruby/cli/commands/function/test.rb +5 -4
  24. data/lib/faastruby/cli/commands/function/update_context.rb +10 -3
  25. data/lib/faastruby/cli/commands/function/upgrade.rb +62 -61
  26. data/lib/faastruby/cli/commands/help.rb +33 -20
  27. data/lib/faastruby/cli/commands/project/base_command.rb +14 -0
  28. data/lib/faastruby/cli/commands/project/deploy.rb +114 -0
  29. data/lib/faastruby/cli/commands/project/down.rb +58 -0
  30. data/lib/faastruby/cli/commands/project/new.rb +237 -0
  31. data/lib/faastruby/cli/commands/workspace/cp.rb +107 -0
  32. data/lib/faastruby/cli/commands/workspace/create.rb +35 -27
  33. data/lib/faastruby/cli/commands/workspace/destroy.rb +14 -7
  34. data/lib/faastruby/cli/commands/workspace/list.rb +15 -6
  35. data/lib/faastruby/cli/commands/workspace/migrate.rb +93 -0
  36. data/lib/faastruby/cli/commands/workspace/rm.rb +81 -0
  37. data/lib/faastruby/cli/commands/workspace/update.rb +62 -0
  38. data/lib/faastruby/cli/credentials.rb +58 -57
  39. data/lib/faastruby/cli/new_credentials.rb +63 -0
  40. data/lib/faastruby/cli/package.rb +1 -0
  41. data/lib/faastruby/cli/template.rb +7 -7
  42. data/lib/faastruby/local.rb +188 -0
  43. data/lib/faastruby/local/crystal_runtime.cr +170 -0
  44. data/lib/faastruby/local/functions.rb +7 -0
  45. data/lib/faastruby/local/functions/crystal.rb +64 -0
  46. data/lib/faastruby/local/functions/function.rb +173 -0
  47. data/lib/faastruby/local/functions/ruby.rb +28 -0
  48. data/lib/faastruby/local/listeners.rb +5 -0
  49. data/lib/faastruby/local/listeners/listener.rb +104 -0
  50. data/lib/faastruby/local/logger.rb +37 -0
  51. data/lib/faastruby/local/monkey_patch.rb +38 -0
  52. data/lib/faastruby/local/processors.rb +7 -0
  53. data/lib/faastruby/local/processors/function.rb +151 -0
  54. data/lib/faastruby/local/processors/processor.rb +116 -0
  55. data/lib/faastruby/local/processors/static_file.rb +48 -0
  56. data/lib/faastruby/local/static_files.rb +5 -0
  57. data/lib/faastruby/local/static_files/static_file.rb +59 -0
  58. data/lib/faastruby/server.rb +44 -3
  59. data/lib/faastruby/server/app.rb +107 -0
  60. data/lib/faastruby/server/concurrency_controller.rb +50 -50
  61. data/lib/faastruby/server/config.ru +2 -0
  62. data/lib/faastruby/server/event.rb +3 -0
  63. data/lib/faastruby/server/event_hub.rb +7 -6
  64. data/lib/faastruby/server/local.rb +22 -0
  65. data/lib/faastruby/server/logger.rb +50 -0
  66. data/lib/faastruby/server/project_config.rb +44 -0
  67. data/lib/faastruby/server/puma.rb +4 -0
  68. data/lib/faastruby/server/response.rb +40 -0
  69. data/lib/faastruby/server/runner.rb +116 -21
  70. data/lib/faastruby/server/runner_methods.rb +17 -16
  71. data/lib/faastruby/server/sentinel.rb +496 -0
  72. data/lib/faastruby/supported_runtimes.rb +8 -0
  73. data/lib/faastruby/user.rb +77 -0
  74. data/lib/faastruby/version.rb +1 -1
  75. data/lib/faastruby/workspace.rb +36 -3
  76. data/templates/crystal/example-blank/handler.cr +3 -0
  77. data/templates/crystal/example/spec/handler_spec.cr +11 -6
  78. data/templates/public-web/assets/images/background.png +0 -0
  79. data/templates/public-web/assets/images/ruby.png +0 -0
  80. data/templates/public-web/assets/javascripts/main.js +1 -0
  81. data/templates/public-web/assets/stylesheets/main.css +70 -0
  82. data/templates/public-web/favicon.ico +0 -0
  83. data/templates/ruby/api-404/handler.rb +6 -0
  84. data/templates/ruby/api-root/handler.rb +6 -0
  85. data/templates/ruby/example-blank/handler.rb +0 -23
  86. data/templates/ruby/web-404/404.html +36 -0
  87. data/templates/ruby/web-404/handler.rb +3 -0
  88. data/templates/ruby/web-root/handler.rb +10 -0
  89. data/templates/ruby/web-root/index.html.erb +37 -0
  90. data/templates/ruby/web-root/template.rb +13 -0
  91. metadata +102 -21
  92. data/exe/faastruby-server +0 -76
  93. data/lib/faastruby/cli/commands/credentials.rb +0 -11
  94. data/lib/faastruby/cli/commands/credentials/add.rb +0 -58
  95. data/lib/faastruby/cli/commands/credentials/list.rb +0 -58
  96. data/lib/faastruby/cli/commands/workspace.rb +0 -13
  97. data/lib/faastruby/cli/commands/workspace/deploy.rb +0 -50
  98. data/templates/crystal/example-blank/README.md +0 -22
  99. data/templates/crystal/example-blank/spec/handler_spec.cr +0 -8
  100. data/templates/crystal/example-blank/spec/spec_helper.cr +0 -4
  101. data/templates/crystal/example-blank/src/handler.cr +0 -25
  102. data/templates/ruby/example-blank/Gemfile +0 -7
  103. data/templates/ruby/example-blank/README.md +0 -22
  104. data/templates/ruby/example-blank/spec/handler_spec.rb +0 -16
  105. data/templates/ruby/example-blank/spec/spec_helper.rb +0 -3
@@ -0,0 +1,59 @@
1
+ module FaaStRuby
2
+ module Command
3
+ module Account
4
+ require 'faastruby/cli/commands/account/base_command'
5
+ class Logout < AccountBaseCommand
6
+ def initialize(args)
7
+ @args = args
8
+ parse_options
9
+ @credentials_file = NewCredentials::CredentialsFile.new
10
+ @credentials = @credentials_file.get
11
+ end
12
+
13
+ def run
14
+ user = User.new(@credentials)
15
+ unless user.has_credentials?
16
+ puts "Logout successful."
17
+ exit 0
18
+ end
19
+ user.logout(all: @options['all'])
20
+ FaaStRuby::CLI.error(user.errors) if !@options['force'] && user&.errors.any?
21
+ @credentials_file.clear
22
+ puts "Logout successful."
23
+ end
24
+
25
+ def self.help
26
+ "logout [ARGS]"
27
+ end
28
+
29
+ def usage
30
+ puts "Usage: faastruby #{self.class.help}"
31
+ puts %(
32
+ -a,--all # Logout from all machines
33
+ -f,--force # Logout from all machines
34
+ )
35
+ end
36
+
37
+ private
38
+
39
+ def parse_options
40
+ @options = {}
41
+ while @args.any?
42
+ option = @args.shift
43
+ case option
44
+ when '-h', '--help', 'help'
45
+ usage
46
+ exit 0
47
+ when '-f', '--force'
48
+ @options['force'] = true
49
+ when '-a', '--all'
50
+ @options['all'] = true
51
+ else
52
+ FaaStRuby::CLI.error(["Unknown argument: #{option}".red, usage], color: nil)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,76 @@
1
+ module FaaStRuby
2
+ module Command
3
+ module Account
4
+ PASSWORD_REGEX = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,20}$/
5
+ EMAIL_REGEX = /\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
6
+ require 'faastruby/cli/commands/account/base_command'
7
+ require 'io/console'
8
+ class Signup < AccountBaseCommand
9
+ def initialize(args)
10
+ @args = args
11
+ parse_options
12
+ FaaStRuby::CLI.error("You are currently logged in. Please run 'faastruby logout' to logout, then try again.") if has_user_logged_in?
13
+ end
14
+
15
+ def run
16
+ puts "\nWelcome to FaaStRuby! Please enter your email address:"
17
+ print "Email: "
18
+ email = STDIN.gets.chomp
19
+ until email_is_valid?(email) do
20
+ puts "You entered an invalid email address. Please try again:".red
21
+ print "Email: "
22
+ email = STDIN.gets.chomp
23
+ end
24
+ puts "\nNow type in a password. It must contain 8 to 20 characters and have at least one uppercase letter, one lowercase letter, one number."
25
+ print "Password: "
26
+ password = STDIN.noecho(&:gets).chomp
27
+ until password_is_valid?(password) do
28
+ puts "\nYour password must contain 8 to 20 characters and have at least one uppercase letter, one lowercase letter, one number. Please try again:".red
29
+ print "Password: "
30
+ password = STDIN.noecho(&:gets).chomp
31
+ end
32
+ spinner = spin("Creating your account...")
33
+ user = User.create(email: email, password: password)
34
+ if user.errors.any?
35
+ spinner.stop(" Failed :(")
36
+ FaaStRuby::CLI.error(user.errors)
37
+ end
38
+ spinner.stop(' Done!')
39
+ exec("faastruby confirm-account --email #{email}")
40
+ exit 0
41
+ end
42
+
43
+ def email_is_valid?(email)
44
+ email.match(EMAIL_REGEX)
45
+ end
46
+
47
+ def password_is_valid?(password)
48
+ password.match(PASSWORD_REGEX)
49
+ end
50
+
51
+ def self.help
52
+ "signup"
53
+ end
54
+
55
+ def usage
56
+ puts "Usage: faastruby #{self.class.help}"
57
+ end
58
+
59
+
60
+ def parse_options
61
+ @options = {}
62
+ while @args.any?
63
+ option = @args.shift
64
+ case option
65
+ when '-h', '--help', 'help'
66
+ usage
67
+ exit 0
68
+ else
69
+ FaaStRuby::CLI.error(["Unknown argument: #{option}".red, usage], color: nil)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -12,20 +12,11 @@ module FaaStRuby
12
12
  # deploy: false
13
13
  def load_yaml
14
14
  FaaStRuby::CLI.error("It looks like you created this function with an old version of faastruby. Please run 'faastruby upgrade'.") if File.file?('handler.rb') && !File.file?('faastruby.yml')
15
- FaaStRuby::CLI.error("Could not find file 'faastruby.yml' in the current directory") unless File.file?('faastruby.yml')
15
+ FaaStRuby::CLI.error("Could not find file 'faastruby.yml' in the current directory") if !File.file?('faastruby.yml') && @options['function_name'].nil?
16
16
  @yaml_config = YAML.load(File.read('./faastruby.yml'))
17
17
  FaaStRuby::CLI.error("Could read function name from 'faastruby.yml'. Make sure you have a key 'name: FUNCTION_NAME' in that file!") unless @yaml_config['name']
18
18
  end
19
19
  end
20
20
  end
21
21
  end
22
- end
23
-
24
- require 'faastruby/cli/commands/function/build'
25
- require 'faastruby/cli/commands/function/deploy_to'
26
- require 'faastruby/cli/commands/function/new'
27
- require 'faastruby/cli/commands/function/remove_from'
28
- require 'faastruby/cli/commands/function/test'
29
- require 'faastruby/cli/commands/function/update_context'
30
- require 'faastruby/cli/commands/function/upgrade'
31
- require 'faastruby/cli/commands/function/run'
22
+ end
@@ -1,23 +1,27 @@
1
1
  module FaaStRuby
2
2
  module Command
3
3
  module Function
4
+ require 'faastruby/cli/commands/function/base_command'
5
+ require 'faastruby/cli/package'
4
6
  class Build < FunctionBaseCommand
5
-
6
- def self.build(source, output_file, quiet = false)
7
- spinner = spin("Building package...")
7
+ def self.build(source, output_file, function_name, quiet = false)
8
+ # msg = "[#{function_name}] Building package..."
9
+ # quiet ? puts(msg) : spinner = spin(msg)
8
10
  FaaStRuby::Package.new(source, output_file).build
9
- spinner.stop('Done!')
11
+ # quiet ? puts("[#{function_name}] Package created.") : spinner.stop('Done!')
10
12
  puts "+ f #{output_file}".green unless quiet
11
13
  end
12
14
 
13
15
  def initialize(args)
14
16
  @args = args
15
17
  load_yaml
18
+ @yaml_config['before_build'] ||= []
16
19
  @function_name = @yaml_config['name']
17
20
  @abort_when_tests_fail = @yaml_config['abort_build_when_tests_fail']
18
21
  parse_options
19
22
  @options['source'] ||= '.'
20
- @options['output_file'] ||= "#{@function_name}.zip"
23
+ @package_file = Tempfile.new('package')
24
+ @options['output_file'] ||= @package_file.path
21
25
  end
22
26
 
23
27
  def ruby_runtime?
@@ -37,8 +41,10 @@ module FaaStRuby
37
41
  end
38
42
  tests_passed = run_tests
39
43
  FaaStRuby::CLI.error("Build aborted because tests failed and you have 'abort_build_when_tests_fail: true' in 'faastruby.yml'") unless tests_passed || !@abort_when_tests_fail
40
- puts "Warning: Ignoring failed tests because you have 'abort_build_when_tests_fail: false' in 'faastruby.yml'".yellow if !tests_passed && !@abort_when_tests_fail
44
+ puts "[#{@function_name}] Warning: Ignoring failed tests because you have 'abort_build_when_tests_fail: false' in 'faastruby.yml'".yellow if !tests_passed && !@abort_when_tests_fail
41
45
  build(@options['source'], @options['output_file'])
46
+ @package_file.close
47
+ @package_file.unlink
42
48
  end
43
49
 
44
50
  def self.help
@@ -52,27 +58,28 @@ module FaaStRuby
52
58
  private
53
59
 
54
60
  def build(source, output_file)
55
- spinner = spin("Running 'before_deploy' tasks...")
56
- @yaml_config['before_deploy']&.each do |command|
61
+ spinner = spin("[#{@function_name}] Running 'before_build' tasks...")
62
+ @yaml_config['before_build']&.each do |command|
57
63
  puts `#{command}`
58
64
  end
59
65
  spinner.stop(' Done!')
60
- self.class.build(source, output_file)
66
+ self.class.build(source, output_file, @function_name)
61
67
  end
62
68
 
63
69
  def shards_install
64
- puts '[build] Verifying dependencies'
65
70
  return true unless File.file?('shard.yml')
71
+ puts "[#{@function_name}] [build] Verifying dependencies"
66
72
  system('shards check') || system('shards install')
67
73
  end
68
74
 
69
75
  def bundle_install
70
- puts '[build] Verifying dependencies'
71
76
  return true unless File.file?('Gemfile')
77
+ puts "[#{@function_name}] [build] Verifying dependencies"
72
78
  system('bundle check') || system('bundle install')
73
79
  end
74
80
 
75
81
  def run_tests
82
+ require 'faastruby/cli/commands/function/test'
76
83
  FaaStRuby::Command::Function::Test.new(true).run(do_not_exit: true)
77
84
  end
78
85
 
@@ -1,16 +1,26 @@
1
1
  module FaaStRuby
2
2
  module Command
3
3
  module Function
4
+ require 'faastruby/cli/commands/function/base_command'
5
+ require 'faastruby/cli/new_credentials'
4
6
  class DeployTo < FunctionBaseCommand
5
7
  def initialize(args)
6
8
  @args = args
9
+ help
7
10
  @missing_args = []
8
11
  FaaStRuby::CLI.error(@missing_args, color: nil) if missing_args.any?
9
12
  @workspace_name = @args.shift
13
+ parse_options
10
14
  load_yaml
15
+ @yaml_config['before_build'] ||= []
11
16
  @function_name = @yaml_config['name']
12
- @abort_when_tests_fail = @yaml_config['abort_deploy_when_tests_fail']
13
- load_credentials(exit_on_error: false)
17
+ unless @yaml_config['serve_static']
18
+ @options['root_to'] = @function_name if @options['is_root']
19
+ @options['catch_all'] = @function_name if @options['is_catch_all']
20
+ # @abort_when_tests_fail = true #@yaml_config['abort_deploy_when_tests_fail']
21
+ end
22
+ load_credentials
23
+ @package_file = Tempfile.new('package')
14
24
  end
15
25
 
16
26
  def ruby_runtime?
@@ -18,69 +28,92 @@ module FaaStRuby
18
28
  end
19
29
 
20
30
  def crystal_runtime?
31
+ return false if @yaml_config['runtime'].nil?
21
32
  @yaml_config['runtime'].match(/^crystal/)
22
33
  end
23
34
 
35
+ def runtime_name
36
+ return 'Ruby' if ruby_runtime?
37
+ return 'Crystal' if crystal_runtime?
38
+ return 'Ruby'
39
+ end
40
+
24
41
  def run
25
42
  create_or_use_workspace
26
- if ruby_runtime?
27
- FaaStRuby::CLI.error('Please fix the problems above and try again') unless bundle_install
43
+ if @yaml_config['serve_static']
44
+ package_file_name = build_package
45
+ spinner = say("[#{@function_name}] Deploying static files '#{@function_name}' to workspace '#{@workspace_name}'...", quiet: @options['quiet'])
46
+ workspace = FaaStRuby::Workspace.new(name: @workspace_name).deploy(package_file_name)
47
+ else
48
+ if ruby_runtime?
49
+ FaaStRuby::CLI.error('Please fix the problems above and try again') unless bundle_install
50
+ end
51
+ if crystal_runtime?
52
+ FaaStRuby::CLI.error('Please fix the problems above and try again') unless shards_install
53
+ end
54
+ FaaStRuby::CLI.error("[#{@function_name}] Deploy aborted because 'test_command' exited non-zero.") unless run_tests
55
+ package_file_name = build_package
56
+ spinner = say("[#{@function_name}] Deploying #{runtime_name} function '#{@function_name}' to workspace '#{@workspace_name}'...", quiet: @options['quiet'])
57
+ workspace = FaaStRuby::Workspace.new(name: @workspace_name).deploy(package_file_name, root_to: @options['root_to'], catch_all: @options['catch_all'], context: @options['context'])
28
58
  end
29
- if crystal_runtime?
30
- FaaStRuby::CLI.error('Please fix the problems above and try again') unless shards_install
31
- end
32
- tests_passed = run_tests
33
- FaaStRuby::CLI.error("Deploy aborted because tests failed and you have 'abort_deploy_when_tests_fail: true' in 'faastruby.yml'") unless tests_passed || !@abort_when_tests_fail
34
- puts "Warning: Ignoring failed tests because you have 'abort_deploy_when_tests_fail: false' in 'faastruby.yml'".yellow if !tests_passed && !@abort_when_tests_fail
35
- package_file_name = build_package
36
- spinner = spin("Deploying '#{@workspace_name}/#{@function_name}'")
37
- workspace = FaaStRuby::Workspace.new(name: @workspace_name).deploy(package_file_name)
38
59
  if workspace.errors.any?
39
- spinner.stop('Failed :(')
60
+ puts ' Failed :(' unless spinner&.stop(' Failed :(')
61
+ @package_file.unlink
40
62
  FaaStRuby::CLI.error(workspace.errors)
41
63
  end
42
- spinner.stop('Done!')
43
- puts "Endpoint: #{FaaStRuby.api_host}/#{@workspace_name}/#{@function_name}"
64
+ spinner.stop(' Done!') unless @options['quiet']
65
+ @package_file.unlink
66
+ puts "* [#{@function_name}] Deploy OK".green
67
+ unless @yaml_config['serve_static']
68
+ puts "* [#{@function_name}] Workspace: #{@workspace_name}".green
69
+ puts "* [#{@function_name}] Endpoint: #{FaaStRuby.workspace_host_for(@workspace_name)}/#{@function_name unless @options['root_to']}".green
70
+ end
71
+ puts '---'
44
72
  exit 0
45
73
  end
46
74
 
47
75
  def self.help
48
- "deploy-to".light_cyan + " WORKSPACE_NAME"
76
+ "deploy-to WORKSPACE_NAME [ARGS]"
49
77
  end
50
78
 
51
79
  def usage
52
- "Usage: faastruby #{self.class.help}"
80
+ puts "\nUsage: faastruby #{self.class.help}"
81
+ puts %(
82
+ -f,--function PATH/TO/FUNCTION # Specify the directory where the function is.
83
+ --context DATA # The data to be stored as context in the cloud,
84
+ # accessible via 'event.context' from within your function.
85
+ --set-root # Set the function as the root route of the workspace/
86
+ --set-catch-all # Set the function as the catch-all route of the workspace.
87
+ --dont-create-workspace # Don't try to create the workspace if it doesn't exist.
88
+ )
53
89
  end
54
90
 
55
91
  private
56
92
 
57
- def load_credentials(exit_on_error:)
58
- @has_credentials = FaaStRuby::Credentials.load_for(@workspace_name, exit_on_error: exit_on_error)
59
- end
60
-
61
93
  def create_or_use_workspace
62
- unless @has_credentials
63
- puts "Attemping to create workspace '#{@workspace_name}'"
64
- cmd = FaaStRuby::Command::Workspace::Create.new([@workspace_name])
65
- cmd.run(create_directory: false, exit_on_error: true)
66
- load_credentials(exit_on_error: true)
94
+ return true if @options['dont_create_workspace']
95
+ require 'faastruby/cli/commands/workspace/create'
96
+ # puts "[#{@function_name}] Attemping to create workspace '#{@workspace_name}'"
97
+ cmd = FaaStRuby::Command::Workspace::Create.new([@workspace_name])
98
+ result = cmd.run(create_directory: false, exit_on_error: false)
99
+ if result
67
100
  # Give a little bit of time after creating the workspace
68
101
  # for consistency. This is temporary until the API gets patched.
69
- spinner = spin("Waiting for the new workspace to be ready...")
102
+ spinner = say("[#{@function_name}] Waiting for the workspace '#{@workspace_name}' to be ready...", quiet: @options['quiet'])
70
103
  sleep 2
71
- spinner.stop("Done!")
104
+ puts ' Done!' unless spinner&.stop(' Done!')
72
105
  end
73
106
  end
74
107
 
75
108
  def shards_install
76
- puts '[build] Verifying dependencies'
77
109
  return true unless File.file?('shard.yml')
110
+ puts "[#{@function_name}] [build] Verifying dependencies"
78
111
  system('shards check') || system('shards install')
79
112
  end
80
113
 
81
114
  def bundle_install
82
- puts '[build] Verifying dependencies'
83
115
  return true unless File.file?('Gemfile')
116
+ puts "[#{@function_name}] [build] Verifying dependencies"
84
117
  system('bundle check') || system('bundle install')
85
118
  end
86
119
 
@@ -94,20 +127,50 @@ module FaaStRuby
94
127
  end
95
128
 
96
129
  def run_tests
130
+ return true unless @yaml_config['test_command']
131
+ require 'faastruby/cli/commands/function/test'
97
132
  FaaStRuby::Command::Function::Test.new(true).run(do_not_exit: true)
98
133
  end
99
134
 
100
135
  def build_package
101
136
  source = '.'
102
- output_file = "#{@function_name}.zip"
103
- spinner = spin("Running 'before_deploy' tasks...")
104
- @yaml_config['before_deploy']&.each do |command|
105
- puts `#{command}`
137
+ output_file = @package_file.path
138
+ if @yaml_config['before_build'].any?
139
+ spinner = say("[#{@function_name}] Running 'before_build' tasks...", quiet: @options['quiet'])
140
+ @yaml_config['before_build']&.each do |command|
141
+ puts `#{command}`
142
+ end
143
+ spinner&.stop(' Done!')
106
144
  end
107
- spinner.stop(' Done!')
108
- FaaStRuby::Command::Function::Build.build(source, output_file, true)
145
+ require 'faastruby/cli/commands/function/build'
146
+ FaaStRuby::Command::Function::Build.build(source, output_file, @function_name, true)
147
+ @package_file.close
109
148
  output_file
110
149
  end
150
+
151
+ def parse_options
152
+ @options = {}
153
+ while @args.any?
154
+ option = @args.shift
155
+ case option
156
+ when '-f', '--function'
157
+ Dir.chdir @args.shift
158
+ when '--context'
159
+ @options['context'] = @args.shift
160
+ when '--quiet', '-q'
161
+ @options['quiet'] = true
162
+ when '--set-root'
163
+ @options['is_root'] = true
164
+ when '--set-catch-all'
165
+ @options['is_catch_all'] = true
166
+ when '--dont-create-workspace'
167
+ @options['dont_create_workspace'] = true
168
+ else
169
+ FaaStRuby::CLI.error("Unknown argument: #{option}")
170
+ end
171
+ end
172
+ end
173
+
111
174
  end
112
175
  end
113
176
  end
@@ -1,21 +1,33 @@
1
+ require 'erb'
1
2
  module FaaStRuby
3
+ require 'faastruby/version'
2
4
  module Command
3
5
  module Function
6
+ require 'faastruby/supported_runtimes'
7
+ require 'faastruby/cli/commands/function/base_command'
8
+ require 'faastruby/cli/template'
4
9
  class New < FunctionBaseCommand
5
10
  def initialize(args)
6
11
  @args = args
12
+ help
7
13
  @missing_args = []
8
14
  FaaStRuby::CLI.error(@missing_args, color: nil) if missing_args.any?
9
15
  @function_name = @args.shift
16
+ FaaStRuby::CLI.error("The function name must have at least one character and can only contain letters, numbers, -, _, . and /. Names with just a period are not allowed. Invalid name: #{@function_name}") unless name_valid?
10
17
  parse_options
11
18
  @base_dir ||= @function_name
12
19
  @options['runtime_name'] ||= 'ruby'
13
20
  @options['runtime_version'] ||= '2.5.3'
14
- @options['template'] ||= FaaStRuby::Template.new(type: 'local', source: Template.gem_template_path_for('example', runtime: @options['runtime_name']))
21
+ if @options['blank_template']
22
+ @options['template'] = FaaStRuby::Template.new(type: 'local', source: Template.gem_template_path_for('example-blank', runtime: @options['runtime_name'] || 'ruby'))
23
+ else
24
+ @options['template'] ||= FaaStRuby::Template.new(type: 'local', source: Template.gem_template_path_for('example', runtime: @options['runtime_name']))
25
+ end
15
26
  end
16
27
 
17
- def run
18
- @options['template'].install(to: @base_dir, force: @options['force'])
28
+ def run(print_base_dir: false, blank_template: false)
29
+ @options['blank_template'] ||= blank_template
30
+ @options['template'].install(to: @base_dir, force: @options['force'], print_base_dir: print_base_dir)
19
31
  faastruby_yaml = "#{@base_dir}/faastruby.yml"
20
32
  if File.file?(faastruby_yaml)
21
33
  @yaml_content = YAML.load(File.read(faastruby_yaml))
@@ -26,32 +38,28 @@ module FaaStRuby
26
38
  else
27
39
  @yaml_content = yaml_for(@options['runtime_name'])
28
40
  end
29
- write_yaml
41
+ write_yaml(print_base_dir: print_base_dir)
30
42
  post_tasks(@options['runtime_name'])
31
43
  end
32
44
 
33
45
  def self.help
34
- "new".light_cyan + " FUNCTION_NAME [--blank] [--force] [--runtime]" +
35
- <<-EOS
36
-
37
- --blank
38
- Create a blank function
39
- --force
40
- Continue if directory already exists and overwrite files
41
- -g
42
- Initialize a Git repository.
43
- --runtime
44
- Choose the runtime. Options are: #{SUPPORTED_RUNTIMES.join(', ')}
45
- --template TYPE(local|git|github):SOURCE
46
- Use another function as template. Examples:
47
- --template local:/path/to/folder
48
- --template git:git@github.com:user/repo.git
49
- --template github:user/repo
50
- EOS
46
+ "new FUNCTION_NAME [ARGS]"
51
47
  end
52
48
 
53
49
  def usage
54
- "Usage: faastruby #{self.class.help}"
50
+ puts "\nUsage: faastruby #{self.class.help}"
51
+ puts %(
52
+ --blank # Create a blank function
53
+ --force # Continue if directory already exists and overwrite files
54
+ -g, --git # Initialize a Git repository.
55
+ --runtime # Set the language runtime.
56
+ # Options are: #{SUPPORTED_RUNTIMES.join(', ')}
57
+ --template TYPE(local|git|github):SOURCE # Initialize the function using a template
58
+ # Examples:
59
+ # --template local:/path/to/folder
60
+ # --template git:git@github.com:user/repo.git
61
+ # --template github:user/repo
62
+ )
55
63
  end
56
64
 
57
65
  private
@@ -61,7 +69,7 @@ EOS
61
69
  while @args.any?
62
70
  option = @args.shift
63
71
  case option
64
- when '-g'
72
+ when '-g', '--git'
65
73
  @options['git_init'] = true
66
74
  when '--template'
67
75
  FaaStRuby::CLI.error("Option '--template' can't be used with '--blank' or '--runtime'.".red) if @options['runtime'] || @options['blank_template']
@@ -70,7 +78,7 @@ EOS
70
78
  source = source.join(':')
71
79
  @options['template'] = FaaStRuby::Template.new(type: type, source: source)
72
80
  when '--runtime'
73
- FaaStRuby::CLI.error("Option '--template' can't be used with '--blank' or '--runtime'.".red) if @options['template']
81
+ FaaStRuby::CLI.error("Option '--runtime' can't be used with '--template' or '--blank'.".red) if @options['template']
74
82
  @options['runtime'] = @args.shift
75
83
  @options['runtime_name'], @options['runtime_version'] = @options['runtime'].split(':')
76
84
  template_name = @options['blank_template'] ? 'example-blank' : 'example'
@@ -79,9 +87,9 @@ EOS
79
87
  when '-f', '--force'
80
88
  @options['force'] = true
81
89
  when '--blank'
82
- FaaStRuby::CLI.error("Option '--template' can't be used with '--blank' or '--runtime'.".red) if @options['template']
90
+ @options['template'] = nil
91
+ FaaStRuby::CLI.error("Option '--blank' can't be used with '--blank' or '--template'.".red) if @options['template']
83
92
  @options['blank_template'] = true
84
- @options['template'] = FaaStRuby::Template.new(type: 'local', source: Template.gem_template_path_for('example-blank', runtime: @options['runtime_name'] || 'ruby'))
85
93
  else
86
94
  FaaStRuby::CLI.error(["Unknown argument: #{option}".red, usage], color: nil)
87
95
  end
@@ -96,6 +104,28 @@ EOS
96
104
  @missing_args
97
105
  end
98
106
 
107
+ def yaml_comments
108
+ [
109
+ '## You can add commands to run locally before building the deployment package.',
110
+ "## Some use cases are:",
111
+ "## * minifying Javascript/CSS",
112
+ "## * downloading a file to be included in the package.",
113
+ "# before_build:",
114
+ "# - curl https://some.url --output some.file",
115
+ "# - uglifyjs your.js -c -m -o your.min.js",
116
+ '',
117
+ '# To schedule periodic runs, follow the example below:',
118
+ '# schedule:',
119
+ '# job1:',
120
+ '# when: every 2 hours',
121
+ '# body: {"foo": "bar"}',
122
+ '# method: POST',
123
+ '# query_params: {"param": "value"}',
124
+ '# headers: {"Content-Type": "application/json"}',
125
+ '# job2: ...'
126
+ ].join("\n")
127
+ end
128
+
99
129
  def yaml_for(runtime_name)
100
130
  case runtime_name
101
131
  when 'crystal'
@@ -105,21 +135,29 @@ EOS
105
135
  else
106
136
  test_command = 'rspec'
107
137
  end
108
- {
109
- 'cli_version' => FaaStRuby::VERSION,
110
- 'name' => @function_name,
111
- 'runtime' => @options['runtime'] || 'ruby:2.5.3',
112
- 'test_command' => test_command,
113
- 'abort_build_when_tests_fail' => true,
114
- 'abort_deploy_when_tests_fail' => true
115
- }
138
+ if @options['blank_template']
139
+ {
140
+ 'cli_version' => FaaStRuby::VERSION,
141
+ 'name' => @function_name,
142
+ 'runtime' => @options['runtime'] || 'ruby:2.5.3'
143
+ }
144
+ else
145
+ {
146
+ 'cli_version' => FaaStRuby::VERSION,
147
+ 'name' => @function_name,
148
+ 'before_build' => [],
149
+ 'runtime' => @options['runtime'] || 'ruby:2.5.3',
150
+ 'test_command' => test_command
151
+ }
152
+ end
116
153
  end
117
154
 
118
- def write_yaml
119
- write_file("#{@function_name}/faastruby.yml", @yaml_content.to_yaml)
155
+ def write_yaml(print_base_dir: false)
156
+ write_file("#{@function_name}/faastruby.yml", @yaml_content.to_yaml, print_base_dir: print_base_dir, extra_content: yaml_comments)
120
157
  end
121
158
 
122
159
  def post_tasks(runtime_name)
160
+ return true if @options['blank_template']
123
161
  update_readme
124
162
  puts `git init #{@base_dir}` if @options['git_init']
125
163
  case runtime_name
@@ -141,6 +179,7 @@ EOS
141
179
  end
142
180
 
143
181
  def bundle_install
182
+ return true unless File.file?("#{@base_dir}/Gemfile")
144
183
  spinner = spin("Installing gems...")
145
184
  system("bundle install --gemfile=#{@base_dir}/Gemfile > /dev/null")
146
185
  spinner.stop('Done!')
@@ -167,10 +206,24 @@ EOS
167
206
  end
168
207
 
169
208
  def shards_install
209
+ return true unless File.file?("#{@base_dir}/shard.yml")
170
210
  spinner = spin("Installing shards...")
171
211
  system("cd #{@base_dir} && shards install > /dev/null")
172
212
  spinner.stop('Done!')
173
213
  end
214
+
215
+ def name_valid?
216
+ return false unless @function_name.match(/^#{FUNCTION_NAME_REGEX}$/)
217
+ while @function_name.match(/\.\./) || @function_name.match(/^\.\//) || @function_name.match(/(^\/|\/$)/)
218
+ @function_name.gsub!('..', '.')
219
+ @function_name.gsub!(/^\.\//, '')
220
+ @function_name.gsub!(/(^\/|\/$)/, '')
221
+ end
222
+ if @function_name == '.' || @function_name == '' || @function_name.match(/\.\./)
223
+ return false
224
+ end
225
+ return true
226
+ end
174
227
  end
175
228
  end
176
229
  end