souls 1.15.2 → 1.16.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69ade83bc2c7c2a894dc61fd17bce6aa7f3dd13828c159f576908f127eb5c0f1
4
- data.tar.gz: 4b8b1c3a17d317dc37fbce09ae7233d6d18951d2083c3bf50348be0fc48c337e
3
+ metadata.gz: 3f15f29d4efb8c41654774dac322c065ba387b326b9d8884b35dcebfe4e76f20
4
+ data.tar.gz: 4dff20a144f48f194b4d2d39cdc8d22e0e4471b6b493d30adc84029a8006fe59
5
5
  SHA512:
6
- metadata.gz: 2b492fb820040cf136f4705191576b9b05ba2037e10b5009c1d8ab3b4eaa5d48d740ed082dbc21587d2e3bba005889f0168ba5945bd28abaccce3993b19828fc
7
- data.tar.gz: fcf1ba18b3202b959b3ad3483aa0d84b91196cd0d543b94ba952e8bd7189120b1d6150abddc6c0d08653a33eb2425dc525f782acf940c33d0957f9a87a386123
6
+ metadata.gz: 48f2436a0515b1ba00183ebda98b5bd3590c0bc58a966bf2a29f31d583b31e886a022d780ffb244516bbd22f8c15f815da95e262dd2f729ee68ce6907eecc152
7
+ data.tar.gz: 4edcb626880e6e51da8333a01613f7d31e6e64ef1fcdb5a9ead2882255e426f45531f33f74656f76c95ab2007ad252bfeeabee9567c471fec02141ea82db5582
@@ -1,41 +1,57 @@
1
- require_relative "./template/functions_app"
2
- require_relative "./template/functions_gemfile"
3
- require_relative "./template/functions_env_yaml"
1
+ require_relative "./templates/functions_env_yaml"
2
+
3
+ Dir["#{Souls::SOULS_PATH}/lib/souls/cli/create/templates/*/*.rb"].map do |f|
4
+ require f
5
+ end
6
+
4
7
  module Souls
5
8
  class Create < Thor
6
9
  desc "functions", "Create SOULs functions"
7
- def functions
8
- create_app_file
9
- create_gemfile
10
- create_env_yaml
11
- end
10
+ def functions(function_name)
11
+ supported_languages = {
12
+ ruby: %w[2.6 2.7],
13
+ nodejs: %w[16 14 12 10],
14
+ python: %w[3.9 3.8 3.7],
15
+ go: ["1.16", "1.13"]
16
+ }
12
17
 
13
- private
14
-
15
- def create_app_file
16
- file_dir = "./apps/functions"
18
+ prompt = TTY::Prompt.new
19
+ runtime = prompt.select("Select Runtime?", supported_languages.keys.map(&:to_s).map(&:camelize))
20
+ runtime_downcased = runtime.downcase
21
+ version = prompt.select("Select Version?", supported_languages[runtime.downcase.to_sym].sort.reverse)
22
+ version_string = "#{runtime_downcased}#{version.gsub('.', '')}"
23
+ runtime_methods = get_runtime_create_method(runtime: runtime_downcased)
24
+ file_dir = "./apps/cf_#{version_string}_#{function_name}"
17
25
  FileUtils.mkdir_p(file_dir) unless Dir.exist?(file_dir)
18
- file_path = "#{file_dir}/app.rb"
19
- raise(StandardError, "Already Exist!") if File.exist?(file_path)
20
26
 
21
- File.write(file_path, Template.functions_app)
22
- Souls::Painter.create_file(file_path)
23
- file_path
27
+ runtime_methods.each do |method|
28
+ file_extension =
29
+ case runtime_downcased
30
+ when "nodejs"
31
+ method == "package" ? "json" : "js"
32
+ when "python"
33
+ method == "requirements" ? "txt" : "py"
34
+ when "go"
35
+ method == "go" ? "mod" : "go"
36
+ else
37
+ "rb"
38
+ end
39
+ file_path =
40
+ if method == "gemfile"
41
+ "#{file_dir}/Gemfile"
42
+ else
43
+ "#{file_dir}/#{method}.#{file_extension}"
44
+ end
45
+ file_name = file_dir.gsub("./apps/", "")
46
+ File.write(file_path, Object.const_get("Template::#{runtime}").__send__(method, file_name))
47
+ Souls::Painter.create_file(file_path)
48
+ end
49
+ create_env_yaml(file_dir: file_dir)
24
50
  end
25
51
 
26
- def create_gemfile
27
- file_dir = "./apps/functions"
28
- FileUtils.mkdir_p(file_dir) unless Dir.exist?(file_dir)
29
- file_path = "#{file_dir}/Gemfile"
30
- raise(StandardError, "Already Exist!") if File.exist?(file_path)
31
-
32
- File.write(file_path, Template.functions_gemfile)
33
- Souls::Painter.create_file(file_path)
34
- file_path
35
- end
52
+ private
36
53
 
37
- def create_env_yaml
38
- file_dir = "./apps/functions"
54
+ def create_env_yaml(file_dir:)
39
55
  FileUtils.mkdir_p(file_dir) unless Dir.exist?(file_dir)
40
56
  file_path = "#{file_dir}/.env.yaml"
41
57
  raise(StandardError, "Already Exist!") if File.exist?(file_path)
@@ -44,5 +60,11 @@ module Souls
44
60
  Souls::Painter.create_file(file_path)
45
61
  file_path
46
62
  end
63
+
64
+ def get_runtime_create_method(runtime:)
65
+ Dir["#{Souls::SOULS_PATH}/lib/souls/cli/create/templates/#{runtime}/*"].map do |n|
66
+ n.split("/").last.gsub(".rb", "")
67
+ end
68
+ end
47
69
  end
48
70
  end
@@ -0,0 +1,46 @@
1
+ module Template
2
+ module Go
3
+ def self.function(file_name)
4
+ <<~APP
5
+ // Package p contains an HTTP Cloud Function.
6
+ package p
7
+
8
+ import (
9
+ "encoding/json"
10
+ "fmt"
11
+ "html"
12
+ "io"
13
+ "log"
14
+ "net/http"
15
+ )
16
+
17
+ // HelloWorld prints the JSON encoded "message" field in the body
18
+ // of the request or "Hello, World!" if there isn't one.
19
+ func #{file_name}(w http.ResponseWriter, r *http.Request) {
20
+ var d struct {
21
+ Message string `json:"message"`
22
+ }
23
+
24
+ if err := json.NewDecoder(r.Body).Decode(&d); err != nil {
25
+ switch err {
26
+ case io.EOF:
27
+ fmt.Fprint(w, "Hello World!")
28
+ return
29
+ default:
30
+ log.Printf("json.NewDecoder: %v", err)
31
+ http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
32
+ return
33
+ }
34
+ }
35
+
36
+ if d.Message == "" {
37
+ fmt.Fprint(w, "Hello World!")
38
+ return
39
+ }
40
+ fmt.Fprint(w, html.EscapeString(d.Message))
41
+ }
42
+
43
+ APP
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,9 @@
1
+ module Template
2
+ module Go
3
+ def self.go(_args)
4
+ <<~APP
5
+ module example.com/cloudfunction
6
+ APP
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ module Template
2
+ module Nodejs
3
+ def self.index(file_name)
4
+ <<~APP
5
+ const express = require('express');
6
+ const bodyParser = require('body-parser');
7
+
8
+ const app = express();
9
+ app.use(bodyParser.urlencoded({ extended: true }));
10
+
11
+ app.get('/souls-functions-get', (req, res)=>{
12
+ res.json(req.query)
13
+ });
14
+
15
+ app.get('/souls-functions-get/:id', (req, res)=>{
16
+ res.json(req.params)
17
+ });
18
+
19
+ app.post('/souls-functions-post', (req, res)=>{
20
+ res.json(req.body)
21
+ });
22
+ exports.#{file_name} = app;
23
+ APP
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module Template
2
+ module Nodejs
3
+ def self.package(_args)
4
+ <<~PACKAGEJSON
5
+ {
6
+ "name": "souls-cf-node16",
7
+ "version": "0.0.1",
8
+ "dependencies": {
9
+ "express": "4.17.2",
10
+ "body-parser": "1.19.1"
11
+ }
12
+ }
13
+ PACKAGEJSON
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ module Template
2
+ module Python
3
+ def self.main(file_name)
4
+ <<~APP
5
+ def #{file_name}(request):
6
+ """Responds to any HTTP request.
7
+ Args:
8
+ request (flask.Request): HTTP request object.
9
+ Returns:
10
+ The response text or any set of values that can be turned into a
11
+ Response object using
12
+ `make_response <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>`.
13
+ """
14
+ request_json = request.get_json()
15
+ if request.args and 'message' in request.args:
16
+ return request.args.get('message')
17
+ elif request_json and 'message' in request_json:
18
+ return request_json['message']
19
+ else:
20
+ return f'Hello World!'
21
+ APP
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ module Template
2
+ module Python
3
+ def self.requirements(_args)
4
+ <<~PACKAGEJSON
5
+ # Function dependencies, for example:
6
+ # package>=version
7
+
8
+ PACKAGEJSON
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module Template
2
+ module Ruby
3
+ def self.gemfile(_args)
4
+ <<~GEMFILE
5
+ source "https://rubygems.org"
6
+
7
+ gem "dotenv", "2.7.6"
8
+ gem "functions_framework", "~> 0.7"
9
+ gem "sinatra", "2.1.0"
10
+
11
+ GEMFILE
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,26 @@
1
+ module Template
2
+ module Ruby
3
+ def self.index(file_name)
4
+ <<~APP
5
+ require "functions_framework"
6
+ require "sinatra/base"
7
+ require "dotenv/load"
8
+
9
+ class App < Sinatra::Base
10
+ get "/souls-functions-get/:name" do
11
+ "SOULs Functions Job Done! - \#{params['name']}"
12
+ end
13
+
14
+ post "/souls-functions-post" do
15
+ params = JSON.parse(request.body.read)
16
+ "SOULs Functions Job Done! - \#{params['name']}"
17
+ end
18
+ end
19
+
20
+ FunctionsFramework.http(\"#{file_name}\") do |request|
21
+ App.call(request.env)
22
+ end
23
+ APP
24
+ end
25
+ end
26
+ end
@@ -6,7 +6,7 @@ module Souls
6
6
  singularized_class_name = class_name.underscore.singularize
7
7
  file_path = "#{file_dir}#{singularized_class_name}_connection.rb"
8
8
  FileUtils.rm_f(file_path)
9
- puts(Paint % ["Delete file! : %{white_text}", :yellow, { white_text: [file_path.to_s, :white] }])
9
+ Souls::Painter.delete_file(file_path)
10
10
  file_path
11
11
  end
12
12
  end
@@ -7,7 +7,7 @@ module Souls
7
7
  singularized_class_name = class_name.underscore.singularize
8
8
  file_path = "#{file_dir}#{singularized_class_name}_edge.rbs"
9
9
  FileUtils.rm_f(file_path)
10
- puts(Paint % ["Delete file! : %{white_text}", :yellow, { white_text: [file_path.to_s, :white] }])
10
+ Souls::Painter.delete_file(file_path)
11
11
  file_path
12
12
  end
13
13
  end
@@ -3,15 +3,19 @@ module Souls
3
3
  desc "deploy", "Deploy Cloud Functions"
4
4
  def deploy
5
5
  require(Souls.get_mother_path.to_s + "/config/souls")
6
- project_id = Souls.configuration.project_id
7
- Dir.chdir(Souls.get_functions_path.to_s) do
8
- system(
9
- "
10
- gcloud functions deploy souls_functions --project=#{project_id} \
11
- --runtime ruby27 --trigger-http --allow-unauthenticated --env-vars-file .env.yaml
12
- "
13
- )
6
+ current_dir = FileUtils.pwd.split("/").last
7
+ unless current_dir.match?(/^cf_/)
8
+ Souls::Painter.error("You are at wrong dir!\nPlease go to `apps/functions` dir!")
9
+ return false
14
10
  end
11
+
12
+ runtime = current_dir.match(/cf_(\D+\d+)_/)[1]
13
+ system(
14
+ "
15
+ gcloud functions deploy #{current_dir} --project=#{project_id} \
16
+ --runtime #{runtime} --trigger-http --allow-unauthenticated --env-vars-file .env.yaml
17
+ "
18
+ )
15
19
  end
16
20
 
17
21
  desc "describe", "Describe SOULs Functions"
@@ -25,13 +29,28 @@ module Souls
25
29
  def url
26
30
  require(Souls.get_mother_path.to_s + "/config/souls")
27
31
  project_id = Souls.configuration.project_id
28
- system("gcloud functions describe souls_functions --project=#{project_id}| grep url")
32
+ current_dir = FileUtils.pwd.split("/").last
33
+ Souls::Painter.success(`gcloud functions describe #{current_dir} --project=#{project_id}| grep url`)
34
+ end
35
+
36
+ desc "all_url", "Get SOULs Functions All URL"
37
+ def all_url
38
+ require(Souls.get_mother_path.to_s + "/config/souls")
39
+ project_id = Souls.configuration.project_id
40
+ Dir.chdir(Souls.get_mother_path.to_s) do
41
+ souls_functions = Dir["apps/cf_*"]
42
+ cf_dir = souls_functions.map { |n| n.split("/").last }
43
+ cf_dir.each do |dir|
44
+ Souls::Painter.success(`gcloud functions describe #{dir} --project=#{project_id}| grep url`)
45
+ end
46
+ end
29
47
  end
30
48
 
31
49
  desc "dev", "Check SOULs Functions dev"
32
50
  def dev
33
51
  Dir.chdir(Souls.get_functions_path.to_s) do
34
- system("bundle exec functions-framework-ruby --target souls_functions")
52
+ current_dir = FileUtils.pwd.split("/").last
53
+ system("bundle exec functions-framework-ruby --target #{current_dir}")
35
54
  end
36
55
  end
37
56
  end
@@ -101,7 +101,8 @@ module Souls
101
101
  "roles/storage.objectAdmin",
102
102
  "roles/cloudscheduler.admin",
103
103
  "roles/appengine.appCreator",
104
- "roles/logging.admin"
104
+ "roles/logging.admin",
105
+ "roles/cloudtranslate.admin"
105
106
  ]
106
107
  roles.each do |role|
107
108
  add_service_account_role(role: role)
@@ -60,6 +60,8 @@ module Souls
60
60
  system("gcloud services enable vpcaccess.googleapis.com")
61
61
  system("gcloud services enable cloudscheduler.googleapis.com")
62
62
  system("gcloud services enable cloudresourcemanager.googleapis.com")
63
+ system("gcloud services enable translate.googleapis.com")
64
+ system("gcloud services enable firestore.googleapis.com")
63
65
  end
64
66
  end
65
67
  end
@@ -150,16 +150,14 @@ module Souls
150
150
  next
151
151
  else
152
152
  case type
153
- when "text", "date", "datetime"
153
+ when "bigint", "integer", "float", "string", "text", "date", "datetime"
154
154
  if array_true
155
- new_line.write(" \"#{name.camelize(:lower)}\" => be_all(String),\n")
155
+ new_line.write(" \"#{name.camelize(:lower)}\" => be_all(#{field}),\n")
156
156
  else
157
- new_line.write(" \"#{name.camelize(:lower)}\" => be_a(String),\n")
157
+ new_line.write(" \"#{name.camelize(:lower)}\" => be_a(#{field}),\n")
158
158
  end
159
159
  when "boolean"
160
- new_line.write(" \"#{name.singularize.camelize(:lower)}\" => be_in([true, false]),\n")
161
- when "string", "bigint", "integer", "float"
162
- new_line.write(" \"#{name.singularize.camelize(:lower)}\" => be_a(#{field}),\n")
160
+ new_line.write(" \"#{name.camelize(:lower)}\" => be_in([true, false]),\n")
163
161
  end
164
162
  end
165
163
  end
@@ -73,7 +73,7 @@ module Souls
73
73
  config.region = "asia-northeast1"
74
74
  config.endpoint = "/endpoint"
75
75
  config.strain = "api"
76
- config.fixed_gems = ["spring"]
76
+ config.fixed_gems = ["spring", "grpc"]
77
77
  config.workers = []
78
78
  end
79
79
  TEXT
@@ -121,7 +121,7 @@ module Souls
121
121
  f.write(<<~TEXT)
122
122
  source "https://rubygems.org"
123
123
 
124
- gem "activesupport", "6.1.4.1"
124
+ gem "activesupport", "7.0.0"
125
125
  gem "foreman", "0.87.2"
126
126
  gem "google-cloud-pubsub", "2.9.0"
127
127
  gem "paint", "2.2.1"
@@ -129,11 +129,11 @@ module Souls
129
129
  gem "pg", "1.2.3"
130
130
  gem "rake", "13.0.6"
131
131
  gem "rspec", "3.10.0"
132
- gem "rubocop", "1.22.3"
132
+ gem "rubocop", "1.24.1"
133
133
  gem "sinatra-activerecord", "2.0.25"
134
134
  gem "solargraph", "0.44.0"
135
135
  #{souls_gem}
136
- gem "steep", "0.46.0"
136
+ gem "steep", "0.47.0"
137
137
  gem "thor", "1.1.0"
138
138
  gem "tty-prompt", "0.23.1"
139
139
  gem "whirly", "0.3.0"
@@ -17,8 +17,8 @@ module Souls
17
17
  tmp_file = "./config/Gemfile"
18
18
  new_gems = gemfile_latest_version
19
19
  logs = []
20
- message = Paint["\nAlready Up to date!", :green]
21
- return "Already Up to date!" && puts(message) if new_gems[:gems].blank?
20
+ message = "Already Up to date!"
21
+ return "Already Up to date!" && Souls::Painter.warning(message) if new_gems[:gems].blank?
22
22
 
23
23
  @i = 0
24
24
  File.open(file_path, "r") do |f|
@@ -72,8 +72,7 @@ module Souls
72
72
  FileUtils.rm("./Gemfile.lock")
73
73
  FileUtils.mv("./config/Gemfile", "./Gemfile")
74
74
  system("bundle update")
75
- success = Paint["\n\nSuccessfully Updated These Gems!\n", :green]
76
- puts(success)
75
+ Souls::Painter.success("\n\nSuccessfully Updated These Gems!\n")
77
76
  logs.each do |line|
78
77
  puts(line)
79
78
  end
@@ -89,6 +88,7 @@ module Souls
89
88
  f.each_line do |line|
90
89
  from_dev = true if line.include?("group")
91
90
  next unless line.include?("gem ")
91
+ next if line.include?("require: ")
92
92
 
93
93
  gem = line.gsub("gem ", "").gsub("\"", "").gsub("\n", "").gsub(" ", "").split(",")
94
94
  url = URI("https://rubygems.org/api/v1/versions/#{gem[0]}/latest.json")
data/lib/souls/index.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require_relative "./version"
2
+ require_relative "./souls_path"
2
3
  require "thor"
3
4
  require "graphql"
4
5
  require "firebase_id_token"
@@ -0,0 +1,10 @@
1
+ require "fileutils"
2
+ module Souls
3
+ SOULS_PATH =
4
+ if FileUtils.pwd.split("/").last == "souls"
5
+ ".".freeze
6
+ else
7
+ "#{Gem.dir}/gems/souls-#{Souls::VERSION}".freeze
8
+ end
9
+ public_constant :SOULS_PATH
10
+ end
data/lib/souls/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Souls
2
- VERSION = "1.15.2".freeze
2
+ VERSION = "1.16.0".freeze
3
3
  public_constant :VERSION
4
4
  end
@@ -1 +1 @@
1
- 1.15.2
1
+ 1.16.0
@@ -1 +1 @@
1
- 1.15.2
1
+ 1.16.0
data/lib/souls.rb CHANGED
@@ -3,7 +3,6 @@ require_relative "souls/cli"
3
3
  require "active_support/core_ext/string/inflections"
4
4
  require "date"
5
5
  require "json"
6
- require "fileutils"
7
6
  require "net/http"
8
7
  require "paint"
9
8
  require "whirly"
@@ -13,19 +12,6 @@ require "resolv"
13
12
 
14
13
  module Souls
15
14
  extend Souls::Utils
16
- SOULS_METHODS = %w[
17
- model
18
- query
19
- mutation
20
- type
21
- resolver
22
- rspec_factory
23
- rspec_model
24
- rspec_query
25
- rspec_mutation
26
- rspec_resolver
27
- ].freeze
28
- public_constant :SOULS_METHODS
29
15
  class Error < StandardError; end
30
16
  class << self
31
17
  attr_accessor :configuration
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: souls
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.15.2
4
+ version: 1.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - POPPIN-FUMI
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2021-12-28 00:00:00.000000000 Z
13
+ date: 2022-01-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -176,9 +176,15 @@ files:
176
176
  - lib/souls/cli/console/index.rb
177
177
  - lib/souls/cli/create/functions.rb
178
178
  - lib/souls/cli/create/index.rb
179
- - lib/souls/cli/create/template/functions_app.rb
180
- - lib/souls/cli/create/template/functions_env_yaml.rb
181
- - lib/souls/cli/create/template/functions_gemfile.rb
179
+ - lib/souls/cli/create/templates/functions_env_yaml.rb
180
+ - lib/souls/cli/create/templates/go/function.rb
181
+ - lib/souls/cli/create/templates/go/go.rb
182
+ - lib/souls/cli/create/templates/nodejs/index.rb
183
+ - lib/souls/cli/create/templates/nodejs/package.rb
184
+ - lib/souls/cli/create/templates/python/main.rb
185
+ - lib/souls/cli/create/templates/python/requirements.rb
186
+ - lib/souls/cli/create/templates/ruby/gemfile.rb
187
+ - lib/souls/cli/create/templates/ruby/index.rb
182
188
  - lib/souls/cli/db/create_migration.rb
183
189
  - lib/souls/cli/db/create_migration_rbs.rb
184
190
  - lib/souls/cli/db/index.rb
@@ -269,6 +275,7 @@ files:
269
275
  - lib/souls/cli/upgrade/index.rb
270
276
  - lib/souls/cli/upgrade/submodule.rb
271
277
  - lib/souls/index.rb
278
+ - lib/souls/souls_path.rb
272
279
  - lib/souls/utils/index.rb
273
280
  - lib/souls/version.rb
274
281
  - lib/souls/versions/.souls_api_version
@@ -1,24 +0,0 @@
1
- module Template
2
- def self.functions_app
3
- <<~APP
4
- require "functions_framework"
5
- require "sinatra/base"
6
- require "dotenv/load"
7
-
8
- class App < Sinatra::Base
9
- get "/souls-functions-get/:name" do
10
- "SOULs Functions Job Done! - \#{params['name']}"
11
- end
12
-
13
- post "/souls-functions-post" do
14
- params = JSON.parse(request.body.read)
15
- "SOULs Functions Job Done! - \#{params['name']}"
16
- end
17
- end
18
-
19
- FunctionsFramework.http("souls_functions") do |request|
20
- App.call(request.env)
21
- end
22
- APP
23
- end
24
- end
@@ -1,12 +0,0 @@
1
- module Template
2
- def self.functions_gemfile
3
- <<~GEMFILE
4
- source "https://rubygems.org"
5
-
6
- gem "dotenv", "2.7.6"
7
- gem "functions_framework", "~> 0.7"
8
- gem "sinatra", "2.1.0"
9
-
10
- GEMFILE
11
- end
12
- end