tenter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f38e303b6f15e746b96586d2a2a6e0e3ca0a2c4746cef91fec0bb455c03fd934
4
+ data.tar.gz: 38319bec27ca53fefc5fdb9f011e0607e3d47741df22f8586482615b9971d3aa
5
+ SHA512:
6
+ metadata.gz: 768b2d4c62fea0a0c2574bfb873ff8c07e7ab1a4fc535a8c32244bf8d8827e728db234aaf13fbe1febe35c6fe9e00a36a44430a19e6ec2485ad898a6927cbd31
7
+ data.tar.gz: b0281208e5189a643e7bd512f8f3a930e983c5f9f3fd106a41750fea263d54bd3c2eded9d4738802c86f9be647b9dc55364ea29cabf6749d5c109e073a7560bf
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org>
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tenter/helpers"
4
+ require "tenter/hooks"
5
+ require "tenter/utils"
6
+ require "tenter/version"
7
+
8
+ module Tenter
9
+ def self.defaults
10
+ { doc_root: "/var/www/",
11
+ config_filename: "hooks.yaml",
12
+ command_dir: "commands",
13
+ log_file: "log/commands.log" }
14
+ end
15
+
16
+ def self.reset
17
+ @settings = self.defaults
18
+ end
19
+
20
+ def self.settings=(opts = {})
21
+ @settings = self.settings.merge opts
22
+ end
23
+
24
+ def self.settings
25
+ @settings ||= self.defaults
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+
5
+ module Tenter
6
+ module Helpers
7
+ def authenticate
8
+ msg = "X-Hub-Signature header not set"
9
+ halt 400, msg unless request.env['HTTP_X_HUB_SIGNATURE']
10
+
11
+ msg = "X-Hub-Signature header did not match"
12
+ halt 403, msg unless Tenter::Utils.dir_exists? params[:site_dir]
13
+
14
+ secret = Tenter::Utils.secret params[:site_dir]
15
+
16
+ request_sig = request.env['HTTP_X_HUB_SIGNATURE']
17
+ request_body = request.body.read
18
+ computed_sig = 'sha1=' +
19
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'),
20
+ secret,
21
+ request_body)
22
+
23
+ msg = "X-Hub-Signature header did not match"
24
+ halt 403, msg unless Rack::Utils.secure_compare computed_sig, request_sig
25
+ end
26
+
27
+ def initiate
28
+ command = Tenter::Utils.command params[:command_name], params[:site_dir]
29
+
30
+ msg = "Command not found"
31
+ halt 400, msg unless File.file? command["path"]
32
+
33
+ msg = "[#{Time.now}] Initiating: #{command["path"]}\n"
34
+ Tenter::Utils.append_to_log command["log"], msg
35
+
36
+ pid = command["proc"].call
37
+ (ENV["APP_ENV"] != "test") ? Process.detach(pid) : Process.wait(pid)
38
+ end
39
+
40
+ def notify(message)
41
+ case message
42
+ when :initiated
43
+ return 200, "Command initiated"
44
+ when :missing
45
+ halt 404, "Page not found"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sinatra/base"
4
+
5
+ module Tenter
6
+ class Hooks < Sinatra::Base
7
+ helpers Tenter::Helpers
8
+
9
+ post %r{/run/(?<command_name>\w[^\\\/\s]*)/in/(?<site_dir>\w[^\\\/\s]*)/?} do
10
+ authenticate
11
+ initiate
12
+ notify :initiated
13
+ end
14
+
15
+ not_found do
16
+ notify :missing
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Tenter
6
+ module Utils
7
+ def self.config(site_dir)
8
+ @config ||= {}
9
+ @config[site_dir] ||=
10
+ YAML.load_file(File.join(Tenter.settings[:doc_root],
11
+ site_dir,
12
+ Tenter.settings[:config_filename]))
13
+ end
14
+
15
+ def self.dir_exists?(site_dir)
16
+ File.directory? File.join(Tenter.settings[:doc_root], site_dir)
17
+ end
18
+
19
+ def self.secret(site_dir)
20
+ self.config(site_dir).fetch("secret", nil)
21
+ end
22
+
23
+ def self.command(command_name, site_dir)
24
+ ts = %q{ | xargs -L 1 sh -c 'printf "[%s] %s\n" "$(date +%Y-%m-%d\ %H:%M:%S\ %z )" "$*" ' sh}
25
+ command = {}
26
+ command["pwd"] = File.join(Tenter.settings[:doc_root], site_dir)
27
+ command["path"] = File.join(command["pwd"],
28
+ Tenter.settings[:command_dir],
29
+ command_name)
30
+ command["log"] = unless Tenter.settings[:log_file].nil?
31
+ File.join(command["pwd"], Tenter.settings[:log_file])
32
+ else
33
+ "/dev/null"
34
+ end
35
+ command["proc"] = Proc.new {
36
+ spawn command["path"] + ts, { :chdir => command["pwd"],
37
+ :out => [ command["log"], "a" ] }
38
+ }
39
+
40
+ return command
41
+ end
42
+
43
+ def self.append_to_log(log, statement)
44
+ File.write(log, statement, mode: "a")
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tenter
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "./lib/tenter/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "tenter"
7
+ s.version = Tenter::VERSION
8
+ s.authors = ["Michael Camilleri"]
9
+ s.email = ["mike@inqk.net"]
10
+ s.summary = "A web app for running user-defined commands"
11
+ s.description = <<-desc.strip.gsub(/\s+/, " ")
12
+ Tenter is a Sinatra-based application that provides webhooks for use by
13
+ GitHub.
14
+ desc
15
+ s.homepage = "https://github.com/pyrmont/tenter/"
16
+ s.licenses = "Unlicense"
17
+ s.required_ruby_version = ">= 2.5"
18
+
19
+ s.files = Dir["Gemfile", "LICENSE", "tenter.gemspec", "lib/tenter.rb",
20
+ "lib/**/*.rb"]
21
+ s.require_paths = ["lib"]
22
+
23
+ s.metadata["allowed_push_host"] = "https://rubygems.org"
24
+
25
+ s.add_runtime_dependency "sinatra", "~> 2.0"
26
+
27
+ s.add_development_dependency "minitest", "~> 5.11"
28
+ s.add_development_dependency "rack-test", "~> 1.1"
29
+ s.add_development_dependency "rake", "~> 12.3"
30
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tenter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Camilleri
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-07-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.11'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rack-test
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '12.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '12.3'
69
+ description: Tenter is a Sinatra-based application that provides webhooks for use
70
+ by GitHub.
71
+ email:
72
+ - mike@inqk.net
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - Gemfile
78
+ - LICENSE
79
+ - lib/tenter.rb
80
+ - lib/tenter/helpers.rb
81
+ - lib/tenter/hooks.rb
82
+ - lib/tenter/utils.rb
83
+ - lib/tenter/version.rb
84
+ - tenter.gemspec
85
+ homepage: https://github.com/pyrmont/tenter/
86
+ licenses:
87
+ - Unlicense
88
+ metadata:
89
+ allowed_push_host: https://rubygems.org
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '2.5'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubygems_version: 3.0.3
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: A web app for running user-defined commands
109
+ test_files: []