robot_sweatshop 0.1.2

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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rubocop.yml +12 -0
  4. data/Gemfile +2 -0
  5. data/LICENSE +21 -0
  6. data/README.md +45 -0
  7. data/Rakefile +3 -0
  8. data/bin/lib/common.rb +49 -0
  9. data/bin/lib/config.rb +20 -0
  10. data/bin/lib/inspect.rb +42 -0
  11. data/bin/lib/job.rb +13 -0
  12. data/bin/lib/start.rb +11 -0
  13. data/bin/sweatshop +73 -0
  14. data/config.rb +24 -0
  15. data/config.yaml +12 -0
  16. data/jobs/example.yaml +10 -0
  17. data/kintama/README.md +3 -0
  18. data/kintama/data/payload_data.yaml +6 -0
  19. data/kintama/end-to-end_spec.rb +30 -0
  20. data/kintama/input_http_spec.rb +45 -0
  21. data/kintama/job_assembler_spec.rb +72 -0
  22. data/kintama/job_worker_spec.rb +65 -0
  23. data/kintama/moneta-queue_spec.rb +48 -0
  24. data/kintama/payload_parser_spec.rb +71 -0
  25. data/kintama/queue_broadcaster_spec.rb +39 -0
  26. data/kintama/queue_handler_spec.rb +54 -0
  27. data/kintama/run_all.rb +3 -0
  28. data/kintama/shared/helpers.rb +55 -0
  29. data/kintama/shared/process_spawning.rb +13 -0
  30. data/lib/README.md +12 -0
  31. data/lib/input/http.rb +27 -0
  32. data/lib/job/assembler.rb +36 -0
  33. data/lib/job/worker.rb +40 -0
  34. data/lib/payload/lib/bitbucket.rb +61 -0
  35. data/lib/payload/lib/github.rb +45 -0
  36. data/lib/payload/lib/payload.rb +12 -0
  37. data/lib/payload/parser.rb +23 -0
  38. data/lib/queue-helper.rb +32 -0
  39. data/lib/queue/broadcaster.rb +18 -0
  40. data/lib/queue/handler.rb +23 -0
  41. data/lib/queue/lib/moneta-queue.rb +49 -0
  42. data/lib/queue/watcher.rb +18 -0
  43. data/robot_sweatshop.eye +58 -0
  44. data/robot_sweatshop.gemspec +26 -0
  45. data/robot_sweatshop.production.eye +8 -0
  46. data/robot_sweatshop.testing.eye +8 -0
  47. data/workspaces/.keep +0 -0
  48. metadata +233 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1eec24a1025b81640f28412b5601d5999e56040f
4
+ data.tar.gz: 9e3efeb22f6e15c8ec7dcf8c60760901eaf8be34
5
+ SHA512:
6
+ metadata.gz: 72eb1ee064569d102f7ac6f1e0a2b772dea5bd790b52f963793ca161bca22fb5a5a5edbad0082e9ffc7fe9d0a9378269ad55b943632b02d5018a889326fd9ef9
7
+ data.tar.gz: 6d0680dd1d160940db79f436240451489e843f8c0af4a127e00d38ec4413db15e2f57ba3e81e89f3fc8fc4cf2be75ef254a6412ed7065267e58253d4c1986849
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ config.user.yaml
2
+ *.gem
3
+ jobs/*.yaml
4
+ workspaces/**/*
5
+ !workspaces/.keep
6
+ Gemfile.lock
data/.rubocop.yml ADDED
@@ -0,0 +1,12 @@
1
+ AllCops:
2
+ Exclude:
3
+ - kintama/**/*
4
+
5
+ Style/Encoding:
6
+ Enabled: false
7
+
8
+ Metrics/LineLength:
9
+ Max: 80
10
+
11
+ Style/FileName:
12
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Justin Scott
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # Robot Sweatshop
2
+
3
+ [Jenkins](http://jenkins-ci.org/) is horrible to maintain and is problematic when automating installation and configuration. [Drone](https://drone.io/) assumes that you use Docker. [Travis-CI](https://travis-ci.org/recent) is difficult to self-host. All of these frameworks are highly opinionated in one way or another, forcing you to do things _their_ way.
4
+
5
+ Robot Sweatshop is a single-purpose CI server that runs collections of arbitrary scripts when it needs to, usually when new code is pushed. There's no assumptions about what you want to report, what front-end you need, or even what repositories you want to clone because you can do that better than I can. It's just you, your code, and the scripts that test and deploy it.
6
+
7
+ # Usage
8
+
9
+ ```
10
+ gem install robot_sweatshop
11
+ sweatshop start
12
+ ```
13
+
14
+ Robot Sweatshop uses Eye to handle its services and will set up and configure everything for you.
15
+
16
+ After configuring a job, POST a payload to `localhost:8080/:format/payload-for/:job`. For example, triggering a Bitbucket Git POST hook on `localhost:8080/bitbucket/payload-for/example` will parse the payload and run the 'example' job with the payload data in the environment.
17
+
18
+ You can see what jobs are available with `sweatshop job --list`.
19
+
20
+ Currently supported formats:
21
+
22
+ - bitbucket
23
+
24
+ # Configuration
25
+
26
+ The server isn't much help without a job to run. Run `sudo -E sweatshop job <name>` to create a new job or edit an existing one.
27
+
28
+ You can also use `sudo -E sweatshop config` to create and edit a user configuration at `/etc/robot_sweatshop/config.yaml`.
29
+
30
+ Not sure if your job is valid? Run `sweatshop job --inspection <name>` to see if there's something you overlooked.
31
+
32
+ # Security
33
+
34
+ _TODO: Support for running as a custom user via eye uid/gid_
35
+
36
+ # Roadmap
37
+
38
+ - Enable Github support
39
+ - CLI job running
40
+ - Common scrips such as git repo syncing
41
+ - Support for multiple workers
42
+ - Better logging for the processes
43
+ - Improved architecture:
44
+
45
+ ![Improved architecture diagram](http://40.media.tumblr.com/8a5b6ca59c0d93c4ce6fc6b733932a5f/tumblr_nko478zp9N1qh941oo1_1280.jpg)
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ task :test do
2
+ require_relative 'kintama/run_all'
3
+ end
data/bin/lib/common.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'fileutils'
2
+
3
+ def notify(type = :success, string)
4
+ color = case type
5
+ when :success
6
+ :green
7
+ when :failure
8
+ :red
9
+ when :warning
10
+ :yellow
11
+ when :info
12
+ :light_blue
13
+ else
14
+ ''
15
+ end
16
+ puts "[#{type.to_s.capitalize.colorize(color)}] #{string}"
17
+ end
18
+
19
+ def get_job_file(for_job:)
20
+ for_job = "#{__dir__}/../../jobs/#{for_job}.yaml"
21
+ if for_job.nil?
22
+ notify :failure, 'Please specify the job to create or edit. See --help for details'
23
+ nil
24
+ else
25
+ File.expand_path for_job
26
+ end
27
+ end
28
+
29
+ def edit(file)
30
+ edit = ENV['EDITOR']
31
+ if edit
32
+ notify :info, "Manually editing file '#{file}'"
33
+ system edit, file
34
+ else
35
+ notify :failure, "No editor specified in $EDITOR environment variable"
36
+ notify :info, "Displaying file contents instead"
37
+ system 'cat', file
38
+ end
39
+ end
40
+
41
+ def create_and_edit(file, with_default: '')
42
+ file = File.expand_path file
43
+ FileUtils.mkdir_p File.dirname(file)
44
+ unless File.file?(file)
45
+ File.write file, with_default
46
+ notify :success, "Created new file '#{file}'"
47
+ end
48
+ edit file
49
+ end
data/bin/lib/config.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'yaml'
2
+ require 'fileutils'
3
+ require_relative '../../config'
4
+
5
+ def default_config
6
+ File.read "#{__dir__}/../../config.yaml"
7
+ end
8
+
9
+ def user_config_file
10
+ '/etc/robot_sweatshop/config.yaml'
11
+ end
12
+
13
+ def remove_user_config
14
+ FileUtils.rm_rf user_config_file
15
+ notify :success, 'Removing current user config file'
16
+ end
17
+
18
+ def create_and_edit_user_config
19
+ create_and_edit user_config_file, with_default: default_config
20
+ end
@@ -0,0 +1,42 @@
1
+ def valid_lists?(job)
2
+ errors = false
3
+ %w(branch_whitelist commands).each do |list|
4
+ if job[list].nil? || job[list].empty?
5
+ notify :failure, "#{list} empty or not found"
6
+ errors = true
7
+ end
8
+ end
9
+ errors
10
+ end
11
+
12
+ def valid_environment?(job)
13
+ errors = false
14
+ job['environment'].each do |key, value|
15
+ unless value.is_a? String
16
+ notify :warning, "Non-string value for '#{key}'"
17
+ errors = true
18
+ end
19
+ end
20
+ errors
21
+ end
22
+
23
+ def valid_yaml?(job)
24
+ if job
25
+ true
26
+ else
27
+ notify :failure, "Invalid YAML"
28
+ false
29
+ end
30
+ end
31
+
32
+ def validate(job_file)
33
+ unless File.file?(job_file)
34
+ notify :failure, 'Job not found. Create it with \'workshop job\''
35
+ else
36
+ job = YAML.load_file job_file
37
+ return unless valid_yaml?(job)
38
+ errors = valid_lists?(job) |
39
+ valid_environment?(job)
40
+ notify :success, 'Valid job configuration' unless errors
41
+ end
42
+ end
data/bin/lib/job.rb ADDED
@@ -0,0 +1,13 @@
1
+ require_relative 'common'
2
+
3
+ def empty_job
4
+ "---\nbranch_whitelist:\n- master\n\ncommands:\n- echo 'Hello $WORLD!'\n\nenvironment:\n WORLD: Earth\n"
5
+ end
6
+
7
+ def create_and_edit_job(job_path)
8
+ create_and_edit job_path, with_default: empty_job
9
+ end
10
+
11
+ def list_jobs
12
+ puts Dir.glob("#{__dir__}/../../jobs/*").map { |file| File.basename(file, '.yaml') }
13
+ end
data/bin/lib/start.rb ADDED
@@ -0,0 +1,11 @@
1
+ def start_sweatshop(for_environment:)
2
+ eye_config = "#{__dir__}/../../robot_sweatshop.#{for_environment}.eye"
3
+ output = `eye load #{eye_config}`
4
+ if $?.exitstatus != 0
5
+ notify :failure, output
6
+ else
7
+ notify :success, "Robot Sweatshop loaded with a #{for_environment} configuration"
8
+ notify :info, `eye restart robot_sweatshop`
9
+ puts 'Check \'eye --help\' for more info on debugging'
10
+ end
11
+ end
data/bin/sweatshop ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+ require 'yaml'
3
+ require 'commander/import'
4
+ require 'colorize'
5
+ require_relative 'lib/common'
6
+ require_relative 'lib/job'
7
+ require_relative 'lib/inspect'
8
+ require_relative 'lib/start'
9
+ require_relative 'lib/config'
10
+ require_relative '../config'
11
+
12
+ program :name, 'Robot Sweatshop'
13
+ program :version, '0.1.1'
14
+ program :description, 'A lightweight, unopinionated CI server'
15
+ program :help, 'Author', 'Justin Scott <jvscott@gmail.com>'
16
+
17
+ command :job do |c|
18
+ c.syntax = 'sweatshop job <name>'
19
+ c.description = 'Creates and edits jobs.'
20
+ c.option '--list', 'List all available jobs.'
21
+ c.option '--inspection', 'Inspect the given job.'
22
+ c.action do |args, options|
23
+ options.default :list => false
24
+ options.default :inspect => false
25
+
26
+ job_file = get_job_file for_job: args.first
27
+ if options.list
28
+ list_jobs
29
+ elsif options.inspection
30
+ validate job_file unless job_file.nil?
31
+ else
32
+ create_and_edit_job job_file unless job_file.nil?
33
+ end
34
+ end
35
+ end
36
+
37
+ command :start do |c|
38
+ c.syntax = 'sweatshop start [options]'
39
+ c.description = 'Start the Sweatshop.'
40
+ c.option '--testing', 'Load the testing Eye configuration.'
41
+ c.action do |args, options|
42
+ options.default :testing => false
43
+ environment = options.testing ? 'testing' : 'production'
44
+ start_sweatshop for_environment: environment
45
+ end
46
+ end
47
+
48
+ command :stop do |c|
49
+ c.syntax = 'sweatshop stop'
50
+ c.description = 'Stop the Sweatshop.'
51
+ c.action do
52
+ notify :info, `eye stop robot_sweatshop`
53
+ end
54
+ end
55
+
56
+ command :directory do |c|
57
+ c.syntax = 'sweatshop directory'
58
+ c.description = 'Return the path that Robot Sweatshop runs from.'
59
+ c.action do
60
+ puts File.expand_path("#{__dir__}/..")
61
+ end
62
+ end
63
+
64
+ command :config do |c|
65
+ c.syntax = 'sweatshop config [options]'
66
+ c.description = 'Creates and edits the user configuration file.'
67
+ c.option '--reset', 'Reset to factory defaults before editing.'
68
+ c.action do |args, options|
69
+ options.default :reset => false
70
+ remove_user_config if options.reset
71
+ create_and_edit_user_config
72
+ end
73
+ end
data/config.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'fileutils'
2
+ require 'configatron'
3
+ require 'yaml'
4
+
5
+ configurations = [
6
+ "#{__dir__}/config.yaml",
7
+ "#{__dir__}/config.user.yaml",
8
+ "/etc/robot_sweatshop/config.yaml"
9
+ ]
10
+ configurations.each do |config_path|
11
+ if File.file? config_path
12
+ hash = YAML.load_file config_path
13
+ configatron.configure_from_hash hash
14
+ end
15
+ end
16
+
17
+ config_directories = [
18
+ configatron.common.logfile_directory,
19
+ configatron.common.pidfile_directory,
20
+ configatron.queue.moneta_directory
21
+ ]
22
+ config_directories.each do |directory|
23
+ FileUtils.mkdir_p directory unless Dir.exists? directory
24
+ end
data/config.yaml ADDED
@@ -0,0 +1,12 @@
1
+ common:
2
+ pidfile_directory: /var/run/robot_sweatshop
3
+ logfile_directory: /var/log/robot_sweatshop
4
+ user_config_directory: /etc/robot_sweatshop
5
+
6
+ input:
7
+ http:
8
+ port: 8080
9
+ bind: 0.0.0.0
10
+
11
+ queue:
12
+ moneta_directory: /var/db/robot_sweatshop
data/jobs/example.yaml ADDED
@@ -0,0 +1,10 @@
1
+ ---
2
+ branch_whitelist:
3
+ - develop
4
+
5
+ commands:
6
+ - echo $custom_env > test.txt
7
+
8
+ environment:
9
+ custom_env: success
10
+ converts_to_string: { string: only }
data/kintama/README.md ADDED
@@ -0,0 +1,3 @@
1
+ You can run individual `_spec.rb` files with Ruby or `run_all.rb` to go through each.
2
+
3
+ _Warning_: You must run http-in manually. Sinatra is a pain to start and stop programmatically. All other processes will be started by the helper.
@@ -0,0 +1,6 @@
1
+ ---
2
+ # http://www.url-encode-decode.com/
3
+ malformed: |
4
+ payload=%7B%22nothing%22%3A%22here%22%7D
5
+ bitbucket: |
6
+ payload=%7B%22repository%22%3A+%7B%22website%22%3A+%22%22%2C+%22fork%22%3A+false%2C+%22name%22%3A+%22Project+X%22%2C+%22scm%22%3A+%22git%22%2C+%22owner%22%3A+%22marcus%22%2C+%22absolute_url%22%3A+%22%2Fmarcus%2Fproject-x%2F%22%2C+%22slug%22%3A+%22project-x%22%2C+%22is_private%22%3A+true%7D%2C+%22truncated%22%3A+false%2C+%22commits%22%3A+%5B%7B%22node%22%3A+%22620ade18607a%22%2C+%22files%22%3A+%5B%7B%22type%22%3A+%22modified%22%2C+%22file%22%3A+%22README%22%7D%5D%2C+%22raw_author%22%3A+%22Marcus+Bertrand+%3Cmarcus%40somedomain.com%3E%22%2C+%22utctimestamp%22%3A+%222014-08-08+00%3A04%3A07%2B00%3A00%22%2C+%22author%22%3A+%22marcus%22%2C+%22timestamp%22%3A+%222014-08-08+02%3A04%3A07%22%2C+%22raw_node%22%3A+%22620ade18607ac42d872b568bb92acaa9a28620e9%22%2C+%22parents%22%3A+%5B%22702c70160afc%22%5D%2C+%22branch%22%3A+%22develop%22%2C+%22message%22%3A+%22Added+some+more+things+to+somefile.py%5Cn%22%2C+%22revision%22%3A+null%2C+%22size%22%3A+-1%7D%5D%2C+%22canon_url%22%3A+%22https%3A%2F%2Fbitbucket.org%22%2C+%22user%22%3A+%22marcus%22%7D
@@ -0,0 +1,30 @@
1
+ require 'kintama'
2
+ require 'ezmq'
3
+ require 'http'
4
+ require_relative 'shared/process_spawning'
5
+ require_relative 'shared/helpers'
6
+
7
+ describe 'Robot Sweatshop' do
8
+ include QueueHelper
9
+ include InHelper
10
+ include JobHelper
11
+
12
+ setup do
13
+ @client = EZMQ::Client.new port: 5556
14
+ @job_name = 'example'
15
+ @test_file = reset_test_file
16
+ clear_all_queues
17
+ end
18
+
19
+ context "POST git data to the HTTP Input" do
20
+ setup do
21
+ url = input_http_url for_job: @job_name
22
+ HTTP.post url, body: load_payload('bitbucket')
23
+ sleep $for_everything
24
+ end
25
+
26
+ should 'run jobs with the context as environment variables' do
27
+ assert_equal "success\n", File.read(@test_file)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,45 @@
1
+ require 'kintama'
2
+ require 'ezmq'
3
+ require 'json'
4
+ require 'timeout'
5
+ require 'http'
6
+ require_relative 'shared/process_spawning'
7
+ require_relative 'shared/helpers'
8
+
9
+ given 'the HTTP Input' do
10
+ include QueueHelper
11
+ include InHelper
12
+
13
+ setup do
14
+ @subscriber = EZMQ::Subscriber.new port: 5557, topic: 'busy-queues'
15
+ @client = EZMQ::Client.new port: 5556
16
+ @job_name = 'example'
17
+ @raw_payload_queue = 'raw-payload'
18
+ clear_all_queues
19
+ end
20
+
21
+ %w(Bitbucket).each do |git_host|
22
+ context "POST data from #{git_host}" do
23
+ setup do
24
+ url = input_http_url for_job: @job_name, in_format: git_host
25
+ HTTP.post url, body: load_payload(git_host)
26
+ end
27
+
28
+ should 'enqueue to \'raw-payload\'' do
29
+ Timeout.timeout($for_a_moment) do
30
+ @subscriber.listen do |message, topic|
31
+ break if message == @raw_payload_queue
32
+ end
33
+ end
34
+ end
35
+
36
+ should 'enqueue payload details' do
37
+ response = @client.request "mirror-#{@raw_payload_queue}"
38
+ data = JSON.parse response
39
+ %w(payload format job_name).each do |type|
40
+ assert_kind_of String, data[type]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end