faastruby 0.4.18 → 0.5.0

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