simple-httpd 0.0.4 → 0.3.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 +4 -4
- data/.gitignore +1 -1
- data/.rubocop.yml +9 -0
- data/.tm_properties +1 -0
- data/Gemfile +21 -1
- data/Makefile +9 -0
- data/README.md +87 -2
- data/Rakefile +5 -0
- data/VERSION +1 -1
- data/bin/simple-httpd +13 -0
- data/examples/README.md +41 -0
- data/examples/ex1/ex1_helpers.rb +5 -0
- data/examples/ex1/root.rb +11 -0
- data/examples/ex2/README.txt +1 -0
- data/examples/ex2/ex2_helpers.rb +5 -0
- data/examples/ex2/helpers.rb +15 -0
- data/examples/ex2/info.rb +4 -0
- data/examples/ex2/root.rb +3 -0
- data/examples/ex3/example_service.rb +13 -0
- data/examples/services/example_service.rb +25 -0
- data/examples/services/explicit_example_service.rb +18 -0
- data/examples/v2/api.js +1 -0
- data/examples/v2/jobs.rb +13 -0
- data/examples/v2/root.rb +3 -0
- data/examples/v2/v2_helpers.rb +5 -0
- data/lib/simple-service.rb +3 -0
- data/lib/simple/httpd.rb +99 -25
- data/lib/simple/httpd/base_controller.rb +2 -2
- data/lib/simple/httpd/base_controller/error_handling.rb +45 -17
- data/lib/simple/httpd/base_controller/json.rb +15 -8
- data/lib/simple/httpd/cli.rb +99 -0
- data/lib/simple/httpd/helpers.rb +54 -0
- data/lib/simple/httpd/mount_spec.rb +106 -0
- data/lib/simple/httpd/rack.rb +17 -0
- data/lib/simple/httpd/rack/dynamic_mount.rb +66 -0
- data/lib/simple/httpd/rack/merger.rb +28 -0
- data/lib/simple/httpd/rack/static_mount.rb +50 -0
- data/lib/simple/httpd/server.rb +69 -0
- data/lib/simple/httpd/service.rb +70 -0
- data/lib/simple/httpd/version.rb +1 -1
- data/lib/simple/service.rb +69 -0
- data/lib/simple/service/action.rb +78 -0
- data/lib/simple/service/context.rb +46 -0
- data/scripts/release +2 -0
- data/scripts/release.rb +91 -0
- data/simple-httpd.gemspec +9 -19
- data/spec/simple/httpd/base_controller/httpd_cors_spec.rb +15 -0
- data/spec/simple/httpd/base_controller/httpd_debug_spec.rb +11 -0
- data/spec/simple/httpd/base_controller/httpd_x_processing_copy.rb +15 -0
- data/spec/simple/httpd/base_spec.rb +16 -0
- data/spec/simple/httpd/dynamic_mounting_spec.rb +33 -0
- data/spec/simple/httpd/helpers_spec.rb +15 -0
- data/spec/simple/httpd/rspec_httpd_spec.rb +17 -0
- data/spec/simple/httpd/services/service_explicit_spec.rb +34 -0
- data/spec/simple/httpd/services/service_spec.rb +34 -0
- data/spec/simple/httpd/static_mounting_spec.rb +13 -0
- data/spec/spec_helper.rb +30 -6
- data/spec/support/004_simplecov.rb +3 -12
- metadata +61 -84
- data/lib/simple/httpd/app.rb +0 -84
- data/lib/simple/httpd/app/file_server.rb +0 -19
- data/spec/simple/httpd/version_spec.rb +0 -10
- data/tasks/release.rake +0 -104
@@ -0,0 +1,28 @@
|
|
1
|
+
# A simple file server middleware
|
2
|
+
class Simple::Httpd::Rack::Merger
|
3
|
+
Rack = Simple::Httpd::Rack
|
4
|
+
|
5
|
+
# returns an app that merges other apps
|
6
|
+
def self.build(apps)
|
7
|
+
return apps.first if apps.length == 1
|
8
|
+
|
9
|
+
new(apps)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def initialize(apps)
|
15
|
+
@apps = apps
|
16
|
+
end
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
@apps.each do |app|
|
22
|
+
status, body, headers = app.call(env)
|
23
|
+
return [status, body, headers] unless status == 404
|
24
|
+
end
|
25
|
+
|
26
|
+
Rack.error 404, "No such action"
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# A simple file server middleware
|
2
|
+
class Simple::Httpd::Rack::StaticMount
|
3
|
+
Rack = ::Simple::Httpd::Rack
|
4
|
+
|
5
|
+
EXTENSIONS = %w(.txt .md .js .css .png .jpeg .jpg)
|
6
|
+
|
7
|
+
def self.build(mount_point, path)
|
8
|
+
static_files = static_files(path)
|
9
|
+
return nil if static_files.empty?
|
10
|
+
|
11
|
+
::Simple::Httpd.logger.info do
|
12
|
+
"#{mount_point}: serving #{static_files.count} static file(s)"
|
13
|
+
end
|
14
|
+
|
15
|
+
new(path, static_files)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.static_files(path)
|
19
|
+
Dir.chdir(path) do
|
20
|
+
pattern = "**/*{" + EXTENSIONS.join(",") + "}"
|
21
|
+
Dir.glob(pattern)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :mount_point, :path
|
26
|
+
|
27
|
+
def initialize(path, static_files)
|
28
|
+
@path = path
|
29
|
+
@static_files = Set.new(static_files)
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(env)
|
33
|
+
request_path = env["PATH_INFO"]
|
34
|
+
if serve_file?(request_path)
|
35
|
+
file_path = request_path[1..-1]
|
36
|
+
env["PATH_INFO"] = file_path
|
37
|
+
file_server.call(env)
|
38
|
+
else
|
39
|
+
Rack.error 404, "No such file"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def file_server
|
44
|
+
@file_server ||= ::Rack::File.new(path)
|
45
|
+
end
|
46
|
+
|
47
|
+
def serve_file?(request_path)
|
48
|
+
@static_files.include?(request_path[1..-1])
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class Simple::Httpd
|
2
|
+
module Server
|
3
|
+
extend self
|
4
|
+
|
5
|
+
module NullLogger # :nodoc:
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def <<(msg); end
|
9
|
+
end
|
10
|
+
|
11
|
+
def listen!(app, environment: "development", host: nil, port:, logger: nil)
|
12
|
+
expect! app != nil
|
13
|
+
|
14
|
+
host ||= "127.0.0.1"
|
15
|
+
URI("http://#{host}:#{port}") # validate host and port
|
16
|
+
|
17
|
+
logger ||= ::Simple::Httpd.logger
|
18
|
+
|
19
|
+
prepare_logger!(logger)
|
20
|
+
logger.info "Starting httpd server on http://#{host}:#{port}/"
|
21
|
+
|
22
|
+
app = ::Rack::Lint.new(app) if environment != "production"
|
23
|
+
|
24
|
+
# re/AccessLog: the AccessLog setting points WEBrick's access logging to the
|
25
|
+
# NullLogger object.
|
26
|
+
#
|
27
|
+
# Instead we'll use a combination of Rack::CommonLogger (see Simple::Httpd.app),
|
28
|
+
# and sinatra's logger (see Simple::Httpd::BaseController).
|
29
|
+
::Rack::Server.start app: app,
|
30
|
+
Host: host,
|
31
|
+
Port: port,
|
32
|
+
environment: environment,
|
33
|
+
Logger: logger,
|
34
|
+
AccessLog: [[NullLogger, ""]]
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# When Webrick is being shut down via SIGTERM - which we do at least during
|
40
|
+
# rspec-httpd triggered runs - it sends a fatal message to the logger. We catch
|
41
|
+
# it - to "downgrade" it to INFO - but we still abort.
|
42
|
+
def prepare_logger!(logger)
|
43
|
+
def logger.fatal(msg, &block)
|
44
|
+
if msg.is_a?(SignalException) && msg.signo == ::Signal.list["TERM"]
|
45
|
+
if %w(test development).include?(::Simple::Httpd.env)
|
46
|
+
info "Received SIGTERM: hard killing server (due to running in #{::Simple::Httpd.env.inspect} environment)"
|
47
|
+
Simple::Httpd::Server.exit!
|
48
|
+
else
|
49
|
+
info "Received SIGTERM: shutting down server..."
|
50
|
+
exit 1
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
public
|
59
|
+
|
60
|
+
def exit!(exit_status = 1)
|
61
|
+
# Run SimpleCov if exists, and if this is the PID that started SimpleCov in the first place.
|
62
|
+
if defined?(SimpleCov) && SimpleCov.pid == Process.pid
|
63
|
+
SimpleCov.process_result(SimpleCov.result, 0)
|
64
|
+
end
|
65
|
+
|
66
|
+
Kernel.exit! exit_status
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "simple-service"
|
2
|
+
|
3
|
+
class Simple::Httpd::Service
|
4
|
+
module ControllerAdapter
|
5
|
+
def mount_service(service)
|
6
|
+
@service = service
|
7
|
+
|
8
|
+
instance_eval do
|
9
|
+
yield(service)
|
10
|
+
end
|
11
|
+
ensure
|
12
|
+
@service = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(path, opts = {}, &block)
|
16
|
+
service_route?("GET", path, opts, &block) || super
|
17
|
+
end
|
18
|
+
|
19
|
+
def post(path, opts = {}, &block)
|
20
|
+
service_route?("POST", path, opts, &block) || super
|
21
|
+
end
|
22
|
+
|
23
|
+
def put(path, opts = {}, &block)
|
24
|
+
service_route?("PUT", path, opts, &block) || super
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete(path, opts = {}, &block)
|
28
|
+
service_route?("DELETE", path, opts, &block) || super
|
29
|
+
end
|
30
|
+
|
31
|
+
def head(path, opts = {}, &block)
|
32
|
+
service_route?("HEAD", path, opts, &block) || super
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def service_route?(verb, path, opts, &block)
|
38
|
+
return false unless @service
|
39
|
+
return false if block
|
40
|
+
return false unless opts.empty?
|
41
|
+
return false unless path.is_a?(Hash) && path.size == 1
|
42
|
+
|
43
|
+
path, action_name = *path.first
|
44
|
+
|
45
|
+
# Verify existence of this action.
|
46
|
+
@service.fetch_action(action_name)
|
47
|
+
|
48
|
+
# get service reference into binding, to make it available for the route
|
49
|
+
# definition.
|
50
|
+
service = @service
|
51
|
+
|
52
|
+
# define sinatra route.
|
53
|
+
route(verb, path) do
|
54
|
+
result = service.call(action_name, parsed_body, params, context: context)
|
55
|
+
json(result)
|
56
|
+
end
|
57
|
+
|
58
|
+
true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
module Helpers
|
63
|
+
def context
|
64
|
+
@context ||= ::Simple::Service::Context.new
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
::Simple::Httpd::BaseController.extend(ControllerAdapter)
|
69
|
+
::Simple::Httpd::BaseController.helpers(Helpers)
|
70
|
+
end
|
data/lib/simple/httpd/version.rb
CHANGED
@@ -0,0 +1,69 @@
|
|
1
|
+
module Simple::Service
|
2
|
+
class ArgumentError < ::ArgumentError
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
6
|
+
require_relative "service/action"
|
7
|
+
require_relative "service/context"
|
8
|
+
|
9
|
+
module Simple::Service
|
10
|
+
def self.included(klass)
|
11
|
+
klass.extend ClassMethods
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.context
|
15
|
+
Thread.current[:"Simple::Service.context"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.with_context(ctx)
|
19
|
+
old_ctx = Thread.current[:"Simple::Service.context"]
|
20
|
+
Thread.current[:"Simple::Service.context"] = ctx
|
21
|
+
yield
|
22
|
+
ensure
|
23
|
+
Thread.current[:"Simple::Service.context"] = old_ctx
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def actions
|
28
|
+
@actions ||= Action.build_all(service_module: self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def build_service_instance
|
32
|
+
service_instance = Object.new
|
33
|
+
service_instance.extend self
|
34
|
+
service_instance
|
35
|
+
end
|
36
|
+
|
37
|
+
def fetch_action(action_name)
|
38
|
+
actions.fetch(action_name) do
|
39
|
+
informal = "service #{self} has these actions: #{actions.keys.sort.map(&:inspect).join(", ")}"
|
40
|
+
raise "No such action #{action_name.inspect}; #{informal}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def call(action_name, arguments, params, context: nil)
|
45
|
+
::Simple::Service.with_context(context) do
|
46
|
+
fetch_action(action_name).invoke(arguments, params)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Resolves a service by name. Returns nil if the name does not refer to a service,
|
52
|
+
# or the service module otherwise.
|
53
|
+
def self.resolve(str)
|
54
|
+
return unless str =~ /^[A-Z][A-Za-z0-9_]*(::[A-Z][A-Za-z0-9_]*)*$/
|
55
|
+
|
56
|
+
service = resolve_constant(str)
|
57
|
+
|
58
|
+
return unless service.is_a?(Module)
|
59
|
+
return unless service.include?(::Simple::Service)
|
60
|
+
|
61
|
+
service
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.resolve_constant(str)
|
65
|
+
const_get(str)
|
66
|
+
rescue NameError
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Simple::Service
|
2
|
+
class Action
|
3
|
+
ArgumentError = ::Simple::Service::ArgumentError
|
4
|
+
|
5
|
+
IDENTIFIER_PATTERN = "[a-z][a-z0-9_]*"
|
6
|
+
IDENTIFIER_REGEXP = Regexp.compile("\\A#{IDENTIFIER_PATTERN}\\z")
|
7
|
+
|
8
|
+
def self.build_all(service_module:)
|
9
|
+
service_module.public_instance_methods(false)
|
10
|
+
.grep(IDENTIFIER_REGEXP)
|
11
|
+
.inject({}) { |hsh, name| hsh.update name => Action.new(service_module, name) }
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :service
|
15
|
+
attr_reader :name
|
16
|
+
attr_reader :arguments
|
17
|
+
attr_reader :parameters
|
18
|
+
|
19
|
+
def initialize(service, name)
|
20
|
+
instance_method = service.instance_method(name)
|
21
|
+
|
22
|
+
@service = service
|
23
|
+
@name = name
|
24
|
+
@arguments = []
|
25
|
+
@parameters = []
|
26
|
+
|
27
|
+
instance_method.parameters.each do |kind, parameter_name|
|
28
|
+
case kind
|
29
|
+
when :req, :opt then @arguments << parameter_name
|
30
|
+
when :keyreq, :key then @parameters << parameter_name
|
31
|
+
else
|
32
|
+
raise ArgumentError, "#{full_name}: no support for #{kind.inspect} arguments, w/parameter #{parameter_name}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# build a service_instance and run the action, with arguments constructed from
|
38
|
+
# args_hsh and params_hsh
|
39
|
+
def invoke(args_hsh, params_hsh)
|
40
|
+
args_hsh ||= {}
|
41
|
+
params_hsh ||= {}
|
42
|
+
|
43
|
+
# build arguments array
|
44
|
+
args = extract_arguments(args_hsh)
|
45
|
+
args << extract_parameters(params_hsh) unless parameters.empty?
|
46
|
+
|
47
|
+
# run the action. Note: public_send is only
|
48
|
+
# an extra safeguard; since actions are already built off public methods
|
49
|
+
# there should be no way to call a private service method.
|
50
|
+
service_instance = service.build_service_instance
|
51
|
+
service_instance.public_send(@name, *args)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def extract_arguments(args_hsh)
|
57
|
+
arguments.map do |name|
|
58
|
+
args_hsh.fetch(name.to_s) { raise ArgumentError, "Missing argument in request body: #{name}" }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def extract_parameters(params_hsh)
|
63
|
+
# Note: in contrast to arguments that are being read from the body parameters that
|
64
|
+
# are not submitted are being ignored (and filled in by +nil+).
|
65
|
+
#
|
66
|
+
# Note 2: The parameter names **must** be Symbols, not Strings, otherwise
|
67
|
+
# the service_instance.send invocation later would not fill in keyword
|
68
|
+
# arguments from the parameters hash.
|
69
|
+
parameters.inject({}) do |hsh, parameter|
|
70
|
+
hsh.update parameter => params_hsh.fetch(parameter, nil)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def full_name
|
75
|
+
"#{service}##{name}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Simple::Service
|
2
|
+
class Context
|
3
|
+
def initialize
|
4
|
+
@hsh = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
IDENTIFIER_PATTERN = "[a-z][a-z0-9_]*"
|
8
|
+
IDENTIFIER_REGEXP = Regexp.compile("\\A#{IDENTIFIER_PATTERN}\\z")
|
9
|
+
ASSIGNMENT_REGEXP = Regexp.compile("\\A(#{IDENTIFIER_PATTERN})=\\z")
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
key = key.to_sym
|
13
|
+
@hsh[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
def []=(key, value)
|
17
|
+
key = key.to_sym
|
18
|
+
existing_value = @hsh[key]
|
19
|
+
|
20
|
+
unless existing_value.nil? || existing_value == value
|
21
|
+
raise "Cannot overwrite existing context setting #{key.inspect}"
|
22
|
+
end
|
23
|
+
|
24
|
+
@hsh[key] = value
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing(sym, *args, &block)
|
28
|
+
if block
|
29
|
+
super
|
30
|
+
elsif args.count == 0 && sym =~ IDENTIFIER_REGEXP
|
31
|
+
self[sym]
|
32
|
+
elsif args.count == 1 && sym =~ ASSIGNMENT_REGEXP
|
33
|
+
self[$1.to_sym] = args.first
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def respond_to_missing?(sym, include_private = false)
|
40
|
+
return true if IDENTIFIER_REGEXP.maptch?(sym)
|
41
|
+
return true if ASSIGNMENT_REGEXP.maptch?(sym)
|
42
|
+
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/scripts/release
ADDED
data/scripts/release.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# -- helpers ------------------------------------------------------------------
|
4
|
+
|
5
|
+
def sys(cmd)
|
6
|
+
STDERR.puts "> #{cmd}"
|
7
|
+
system cmd
|
8
|
+
return true if $?.success?
|
9
|
+
|
10
|
+
STDERR.puts "> #{cmd} returned with exitstatus #{$?.exitstatus}"
|
11
|
+
$?.success?
|
12
|
+
end
|
13
|
+
|
14
|
+
def sys!(cmd, error: nil)
|
15
|
+
return true if sys(cmd)
|
16
|
+
STDERR.puts error if error
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def die!(msg)
|
21
|
+
STDERR.puts msg
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
ROOT = File.expand_path("#{File.dirname(__FILE__)}/..")
|
26
|
+
|
27
|
+
GEMSPEC = Dir.glob("*.gemspec").first || die!("Missing gemspec file.")
|
28
|
+
|
29
|
+
# -- Version reading and bumping ----------------------------------------------
|
30
|
+
|
31
|
+
module Version
|
32
|
+
extend self
|
33
|
+
|
34
|
+
VERSION_FILE = "#{Dir.getwd}/VERSION"
|
35
|
+
|
36
|
+
def read_version
|
37
|
+
version = File.exist?(VERSION_FILE) ? File.read(VERSION_FILE) : "0.0.1"
|
38
|
+
version.chomp!
|
39
|
+
raise "Invalid version number in #{VERSION_FILE}" unless version =~ /^\d+\.\d+\.\d+$/
|
40
|
+
version
|
41
|
+
end
|
42
|
+
|
43
|
+
def auto_version_bump
|
44
|
+
old_version_number = read_version
|
45
|
+
old = old_version_number.split('.')
|
46
|
+
|
47
|
+
current = old[0..-2] << old[-1].next
|
48
|
+
current.join('.')
|
49
|
+
end
|
50
|
+
|
51
|
+
def bump_version
|
52
|
+
next_version = ENV["VERSION"] || auto_version_bump
|
53
|
+
File.open(VERSION_FILE, "w") { |io| io.write next_version }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# -- check, bump, release a new gem version -----------------------------------
|
58
|
+
|
59
|
+
Dir.chdir ROOT
|
60
|
+
$BASE_BRANCH = ENV['BRANCH'] || 'master'
|
61
|
+
|
62
|
+
# ENV["BUNDLE_GEMFILE"] = "#{Dir.getwd}/Gemfile"
|
63
|
+
# sys! "bundle install"
|
64
|
+
|
65
|
+
sys! "git diff --exit-code > /dev/null", error: 'There are unstaged changes in your working directory'
|
66
|
+
sys! "git diff --cached --exit-code > /dev/null", error: 'There are staged but uncommitted changes'
|
67
|
+
|
68
|
+
sys! "git checkout #{$BASE_BRANCH}"
|
69
|
+
sys! "git pull"
|
70
|
+
|
71
|
+
Version.bump_version
|
72
|
+
version = Version.read_version
|
73
|
+
|
74
|
+
sys! "git add VERSION"
|
75
|
+
sys! "git commit -m \"bump gem to v#{version}\""
|
76
|
+
sys! "git tag -a v#{version} -m \"Tag #{version}\""
|
77
|
+
|
78
|
+
sys! "gem build #{GEMSPEC}"
|
79
|
+
|
80
|
+
sys! "git push origin #{$BASE_BRANCH}"
|
81
|
+
sys! 'git push --tags --force'
|
82
|
+
sys! "gem push #{Dir.glob('*.gem').first}"
|
83
|
+
|
84
|
+
sys! "mkdir -p pkg"
|
85
|
+
sys! "mv *.gem pkg"
|
86
|
+
|
87
|
+
STDERR.puts <<-MSG
|
88
|
+
================================================================================
|
89
|
+
Thank you for releasing a new gem version. You made my day.
|
90
|
+
================================================================================
|
91
|
+
MSG
|