loadrunner 0.0.1
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 +7 -0
- data/README.md +36 -0
- data/bin/loadrunner +9 -0
- data/lib/load_runner/_server.rb +44 -0
- data/lib/load_runner/client.rb +70 -0
- data/lib/load_runner/command_line.rb +62 -0
- data/lib/load_runner/docopt.txt +55 -0
- data/lib/load_runner/exceptions.rb +3 -0
- data/lib/load_runner/server.rb +35 -0
- data/lib/load_runner/server_base.rb +29 -0
- data/lib/load_runner/version.rb +3 -0
- data/lib/loadrunner.rb +6 -0
- metadata +195 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ed96c8d23afef79bc83474cdeb980cdea9a27177
|
4
|
+
data.tar.gz: 0bf010a529fad2cb7b1aa6b701be77e719e8c213
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0c32f1097bcee3743adc63cb1148fecf533b4e78fdd24e1a2f1ead011f7e9301431f4894bb1fa30e3db081c3badb100c252a9389961fba5f09c17406ea3f4537
|
7
|
+
data.tar.gz: 2c47338a7941d91f70b91b260fa0806f6cc97694f6520f9d1e02c68f71a9b27884befdd9c0ae9000bc3a1c18c4d30483d04c0a943c286340e68006adf8324357
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
LoadRunner - GitHub Webhook Server and Event Simulator
|
2
|
+
======================================================
|
3
|
+
|
4
|
+
[](https://rubygems.org/gems/loadrunner)
|
5
|
+
[](https://travis-ci.org/DannyBen/loadrunner)
|
6
|
+
[](https://codeclimate.com/github/DannyBen/loadrunner)
|
7
|
+
[](https://gemnasium.com/DannyBen/loadrunner)
|
8
|
+
|
9
|
+
---
|
10
|
+
|
11
|
+
LoadRunner is a dual-purpose utility for working with GitHub webhooks.
|
12
|
+
|
13
|
+
It provides these features:
|
14
|
+
|
15
|
+
- A webserver that responds to GitHub webhook events and can run any
|
16
|
+
arbitrary script writtn in any language.
|
17
|
+
- A command line utility for testing your webhook server configuration.
|
18
|
+
|
19
|
+
---
|
20
|
+
|
21
|
+
Install
|
22
|
+
--------------------------------------------------
|
23
|
+
|
24
|
+
```
|
25
|
+
$ gem install loadrunner
|
26
|
+
```
|
27
|
+
|
28
|
+
Getting Started
|
29
|
+
--------------------------------------------------
|
30
|
+
|
31
|
+
Soon
|
32
|
+
|
33
|
+
Usage
|
34
|
+
--------------------------------------------------
|
35
|
+
|
36
|
+
Soon
|
data/bin/loadrunner
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require "sinatra/reloader" if development?
|
3
|
+
require 'json'
|
4
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
5
|
+
require 'byebug'
|
6
|
+
|
7
|
+
set port: 3000
|
8
|
+
set bind: '0.0.0.0'
|
9
|
+
|
10
|
+
ENV['SECRET_TOKEN'] = '123'
|
11
|
+
|
12
|
+
post '/payload' do
|
13
|
+
request.env['HTTP_X_HUB_SIGNATURE']
|
14
|
+
|
15
|
+
request.body.rewind
|
16
|
+
payload_body = request.body.read
|
17
|
+
|
18
|
+
verify_signature payload_body
|
19
|
+
|
20
|
+
push = ActiveSupport::HashWithIndifferentAccess.new JSON.parse payload_body
|
21
|
+
|
22
|
+
branch = push[:ref] =~ /refs\/head/ ? push[:ref].sub('refs/heads/', '') : nil;
|
23
|
+
tag = push[:ref] =~ /refs\/tags/ ? push[:ref].sub('refs/tags/', '') : nil;
|
24
|
+
|
25
|
+
result = {
|
26
|
+
event: request.env['HTTP_X_GITHUB_EVENT'],
|
27
|
+
repo: push[:repository][:name],
|
28
|
+
branch: branch,
|
29
|
+
tag: tag
|
30
|
+
# payload: push
|
31
|
+
}
|
32
|
+
|
33
|
+
result.inspect
|
34
|
+
end
|
35
|
+
|
36
|
+
post '/debug' do
|
37
|
+
request.env['HTTP_X_HUB_SIGNATURE']
|
38
|
+
end
|
39
|
+
|
40
|
+
def verify_signature(payload_body)
|
41
|
+
signature = 'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), ENV['SECRET_TOKEN'], payload_body)
|
42
|
+
signature_match = Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE'])
|
43
|
+
return halt 401, "Bad Signature" unless signature_match
|
44
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
module LoadRunner
|
4
|
+
class Client
|
5
|
+
include HTTParty
|
6
|
+
attr_accessor :secret_token, :base_url, :payload
|
7
|
+
|
8
|
+
def initialize(opts={})
|
9
|
+
@secret_token = opts[:secret_token]
|
10
|
+
@base_url = opts[:base_url] || 'localhost:3000'
|
11
|
+
self.class.base_uri base_url
|
12
|
+
end
|
13
|
+
|
14
|
+
def send(event=:push, opts={})
|
15
|
+
payload = build_payload opts
|
16
|
+
send_payload event, payload
|
17
|
+
end
|
18
|
+
|
19
|
+
def send_payload(event=:push, payload)
|
20
|
+
@payload = payload.is_a?(String) ? payload : payload.to_json
|
21
|
+
self.class.post "/payload", body: @payload, headers: headers(:push)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def headers(event=:push)
|
27
|
+
{}.tap do |header|
|
28
|
+
header['X_GITHUB_EVENT'] = event.to_s if event
|
29
|
+
header['X_HUB_SIGNATURE'] = signature if secret_token
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def signature
|
34
|
+
return nil unless secret_token
|
35
|
+
signature = 'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), secret_token, payload)
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_payload(opts={})
|
39
|
+
{}.tap do |result|
|
40
|
+
result[:ref] = ref_from_opts opts
|
41
|
+
result[:repository] = repo_from_opts opts
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def ref_from_opts(opts)
|
46
|
+
if opts[:ref]
|
47
|
+
opts[:ref]
|
48
|
+
elsif opts[:branch]
|
49
|
+
"refs/heads/#{opts[:branch]}"
|
50
|
+
elsif opts[:tag]
|
51
|
+
"refs/tags/#{opts[:tag]}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def repo_from_opts(opts)
|
56
|
+
if opts[:repo] =~ /.+\/.+/
|
57
|
+
_owner, name = opts[:repo].split '/'
|
58
|
+
{
|
59
|
+
name: name,
|
60
|
+
full_name: opts[:repo]
|
61
|
+
}
|
62
|
+
else
|
63
|
+
{
|
64
|
+
name: opts[:repo]
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'docopt'
|
3
|
+
|
4
|
+
module LoadRunner
|
5
|
+
|
6
|
+
# Handles the command line interface
|
7
|
+
class CommandLine
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
attr_reader :args
|
11
|
+
|
12
|
+
# Gets an array of arguments (e.g. ARGV), executes the command if valid
|
13
|
+
# and shows usage patterns / help otherwise.
|
14
|
+
def execute(argv=[])
|
15
|
+
doc = File.read File.dirname(__FILE__) + '/docopt.txt'
|
16
|
+
begin
|
17
|
+
@args = Docopt::docopt(doc, argv: argv, version: VERSION)
|
18
|
+
handle
|
19
|
+
rescue Docopt::Exit => e
|
20
|
+
puts e.message
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Called when the arguments match one of the usage patterns. Will
|
27
|
+
# delegate action to other, more specialized methods.
|
28
|
+
def handle
|
29
|
+
return send if args['send']
|
30
|
+
return server if args['server']
|
31
|
+
end
|
32
|
+
|
33
|
+
def send
|
34
|
+
client = Client.new client_opts
|
35
|
+
response = client.send args['EVENT'], payload_opts
|
36
|
+
puts response
|
37
|
+
end
|
38
|
+
|
39
|
+
def server
|
40
|
+
Server.prepare port: args['--port'], bind: args['--bind']
|
41
|
+
Server.run!
|
42
|
+
end
|
43
|
+
|
44
|
+
def client_opts
|
45
|
+
{
|
46
|
+
base_url: args['URL'],
|
47
|
+
secret_token: '123'
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def payload_opts
|
52
|
+
result = { repo: args['REPO'] }
|
53
|
+
|
54
|
+
ref = args['REF']
|
55
|
+
ref = "refs/tags/#{$1}" if ref =~ /^tag:(.+)/
|
56
|
+
ref = "refs/heads/#{$1}" if ref =~ /^branch:(.+)/
|
57
|
+
|
58
|
+
result[:ref] = ref
|
59
|
+
result
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
LoadRunner
|
2
|
+
|
3
|
+
Usage:
|
4
|
+
loadrunner server [--port N --bind IP]
|
5
|
+
loadrunner send URL REPO EVENT REF
|
6
|
+
loadrunner (-h|--help|--version)
|
7
|
+
|
8
|
+
Commands:
|
9
|
+
server
|
10
|
+
Start the webhook server.
|
11
|
+
|
12
|
+
send
|
13
|
+
Send a simulated GitHub event to a webhook server.
|
14
|
+
|
15
|
+
Parameters:
|
16
|
+
URL
|
17
|
+
The URL of the webhook server. This server should have a /payload
|
18
|
+
endpoint that responds to POST requests.
|
19
|
+
|
20
|
+
REPO
|
21
|
+
The name of the repository. This can be either the short name
|
22
|
+
(my_repo), or the full name (my_name/my_repo).
|
23
|
+
|
24
|
+
EVENT
|
25
|
+
Any GitHub event, for example: push or ping.
|
26
|
+
|
27
|
+
REF
|
28
|
+
A branch or tag specifier. This parameter supports three formats:
|
29
|
+
* tag:tag_name
|
30
|
+
* branch:branch_name
|
31
|
+
* raw ref string (for example /refs/tags/tagname)
|
32
|
+
|
33
|
+
Options:
|
34
|
+
--port N
|
35
|
+
Set the port of the webhook server.
|
36
|
+
|
37
|
+
--bind IP
|
38
|
+
Set the listening address of the webhook server.
|
39
|
+
|
40
|
+
|
41
|
+
Environment Variables:
|
42
|
+
GITHUB_SECRET_TOKEN=y0urAP1k3y
|
43
|
+
Set Your GitHub secret token.
|
44
|
+
|
45
|
+
Examples:
|
46
|
+
# Simulate push events
|
47
|
+
loadrunner send localhost:3000 my_repo push branch:master
|
48
|
+
loadrunner send localhost:3000 my_repo push tag:staging
|
49
|
+
loadrunner send localhost:3000 my_repo push refs/tags/staging
|
50
|
+
|
51
|
+
# Start the server
|
52
|
+
loadrunner server
|
53
|
+
loadrunner server --bind 0.0.0.0 --port 3000
|
54
|
+
|
55
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module LoadRunner
|
2
|
+
|
3
|
+
# The Sinatra server
|
4
|
+
class Server < ServerBase
|
5
|
+
post '/payload' do
|
6
|
+
request.body.rewind
|
7
|
+
payload_body = request.body.read
|
8
|
+
|
9
|
+
verify_signature payload_body if secret_token
|
10
|
+
|
11
|
+
push = ActiveSupport::HashWithIndifferentAccess.new JSON.parse payload_body
|
12
|
+
|
13
|
+
branch = push[:ref] =~ /refs\/heads/ ? push[:ref].sub('refs/heads/', '') : nil
|
14
|
+
tag = push[:ref] =~ /refs\/tags/ ? push[:ref].sub('refs/tags/', '') : nil
|
15
|
+
repo = push[:repository][:name]
|
16
|
+
event = request.env['HTTP_X_GITHUB_EVENT']
|
17
|
+
|
18
|
+
result = { event: event, repo: repo, branch: branch, tag: tag }
|
19
|
+
result.inspect
|
20
|
+
|
21
|
+
file = "handlers/#{repo}/#{event}"
|
22
|
+
File.exist?(file) ? `#{file}` : result.inspect
|
23
|
+
end
|
24
|
+
|
25
|
+
def verify_signature(payload_body)
|
26
|
+
signature = 'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), secret_token, payload_body)
|
27
|
+
signature_match = Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE'])
|
28
|
+
return halt 401, "Bad Signature" unless signature_match
|
29
|
+
end
|
30
|
+
|
31
|
+
def secret_token
|
32
|
+
ENV['GITHUB_SECRET_TOKEN']
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require "sinatra/reloader"
|
3
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
4
|
+
|
5
|
+
module LoadRunner
|
6
|
+
|
7
|
+
# The base class for the sinatra server.
|
8
|
+
# Initialize what we can here, but since there are values that will
|
9
|
+
# become known only later, the #prepare method is provided.
|
10
|
+
class ServerBase < Sinatra::Application
|
11
|
+
set :root, File.expand_path('../../', __dir__)
|
12
|
+
set :server, :puma
|
13
|
+
|
14
|
+
configure :development do
|
15
|
+
register Sinatra::Reloader if defined? Sinatra::Reloader
|
16
|
+
end
|
17
|
+
|
18
|
+
# Since we cannot use any config values in the main body of the class,
|
19
|
+
# since they will be updated later, we need to set anything that relys
|
20
|
+
# on the config values just before running the server.
|
21
|
+
# The CommandLine class and the test suite should both call
|
22
|
+
# `Server.prepare` before calling Server.run!
|
23
|
+
def self.prepare(opts={})
|
24
|
+
set :bind, opts[:bind] || '0.0.0.0'
|
25
|
+
set :port, opts[:port] || '3000'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/lib/loadrunner.rb
ADDED
metadata
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: loadrunner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Danny Ben Shitrit
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-08-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.14'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sinatra
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: puma
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.9'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.9'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activesupport
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: runfile
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.10'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.10'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: runfile-tasks
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.4'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.4'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.4'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.4'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.14'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.14'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: byebug
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '9.0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '9.0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: sinatra-contrib
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '2.0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '2.0'
|
153
|
+
description: Run your GitHub webhook server and Send simulated github events
|
154
|
+
email: db@dannyben.com
|
155
|
+
executables:
|
156
|
+
- loadrunner
|
157
|
+
extensions: []
|
158
|
+
extra_rdoc_files: []
|
159
|
+
files:
|
160
|
+
- README.md
|
161
|
+
- bin/loadrunner
|
162
|
+
- lib/load_runner/_server.rb
|
163
|
+
- lib/load_runner/client.rb
|
164
|
+
- lib/load_runner/command_line.rb
|
165
|
+
- lib/load_runner/docopt.txt
|
166
|
+
- lib/load_runner/exceptions.rb
|
167
|
+
- lib/load_runner/server.rb
|
168
|
+
- lib/load_runner/server_base.rb
|
169
|
+
- lib/load_runner/version.rb
|
170
|
+
- lib/loadrunner.rb
|
171
|
+
homepage: https://github.com/DannyBen/loadrunner
|
172
|
+
licenses:
|
173
|
+
- MIT
|
174
|
+
metadata: {}
|
175
|
+
post_install_message:
|
176
|
+
rdoc_options: []
|
177
|
+
require_paths:
|
178
|
+
- lib
|
179
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
180
|
+
requirements:
|
181
|
+
- - ">="
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: 2.0.0
|
184
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - ">="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: '0'
|
189
|
+
requirements: []
|
190
|
+
rubyforge_project:
|
191
|
+
rubygems_version: 2.6.11
|
192
|
+
signing_key:
|
193
|
+
specification_version: 4
|
194
|
+
summary: GitHub Webhook Server and Simulator
|
195
|
+
test_files: []
|