octopusci 0.3.6 → 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/octopusci-install +131 -0
- data/bin/octopusci-post-build-request +42 -0
- data/bin/octopusci-reset-redis +42 -15
- data/bin/octopusci-reset-stage-locker +49 -0
- data/bin/octopusci-skel +61 -15
- data/bin/octopusci-tentacles +43 -29
- data/lib/octopusci/job.rb +1 -1
- data/lib/octopusci/job_store.rb +3 -0
- data/lib/octopusci/server/views/layout.erb +6 -1
- data/lib/octopusci/stage_locker.rb +8 -0
- data/lib/octopusci/version.rb +1 -1
- data/spec/lib/octopusci/stage_locker_spec.rb +29 -0
- metadata +47 -33
- data/bin/octopusci +0 -56
- data/bin/pusci-stage +0 -67
@@ -0,0 +1,131 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
|
5
|
+
APP_NAME = "octopusci-install"
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'octopusci/version'
|
9
|
+
require 'trollop'
|
10
|
+
|
11
|
+
module Octopusci
|
12
|
+
class Installer
|
13
|
+
def initialize(opts)
|
14
|
+
@opts = opts
|
15
|
+
end
|
16
|
+
|
17
|
+
def install
|
18
|
+
# Create user account for octopusci to use.
|
19
|
+
warn(account_exists?(@opts[:account]), "The \"#{@opts[:account]}\" user account already exists.") {
|
20
|
+
delete_account(@opts[:account])
|
21
|
+
}
|
22
|
+
create_account(@opts[:account], @opts[:shell], @opts[:uid], @opts[:gid])
|
23
|
+
puts "Created the \"#{@opts[:account]}\" account."
|
24
|
+
|
25
|
+
# Do the same stuff octopusci-skel currently does but also include the user and group that the octopusci-tentacles should run as
|
26
|
+
# in the config.yml so that when octopusci-tentacles launches as root at boot it can properly switch to the proper uid and guid
|
27
|
+
|
28
|
+
# Make sure that there exists a .ssh/config in the account home directory with the proper permissions and that config has the
|
29
|
+
# option that makes it so that when sshing to github it doesn't make you manually accept the server host key.
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def create_account(account_name, shell, uid, gid)
|
35
|
+
if on_mac?
|
36
|
+
# Create the user entry.
|
37
|
+
`dscl . -create /Users/#{account_name}`
|
38
|
+
|
39
|
+
# Set the users shell.
|
40
|
+
`dscl . -create /Users/#{account_name} UserShell ${shell}`
|
41
|
+
|
42
|
+
# Set the users full name.
|
43
|
+
`dscl . -create /Users/#{account_name} RealName "#{account_name}"`
|
44
|
+
|
45
|
+
# Associate the user with a unique id.
|
46
|
+
`dscl . -create /Users/#{account_name} UniqueID #{uid}`
|
47
|
+
|
48
|
+
# Associate the user with a primary gorup id.
|
49
|
+
`dscl . -create /Users/#{account_name} PrimaryGroupID #{gid}`
|
50
|
+
|
51
|
+
# Create the users home directory.
|
52
|
+
`dscl . -create /Users/#{account_name} NFSHomeDirectory /Users/#{account_name}`
|
53
|
+
|
54
|
+
# Create the home directory and set the ownership properly.
|
55
|
+
`mkdir -p /Users/#{account_name}`
|
56
|
+
`chown -R #{uid}:#{gid} /Users/#{account_name}`
|
57
|
+
|
58
|
+
# Set the users password.
|
59
|
+
puts "Please enter the password for the \"#{account_name}\" account."
|
60
|
+
`passwd ${account_name}`
|
61
|
+
else
|
62
|
+
not_implemented
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def delete_account(account_name)
|
67
|
+
if on_mac?
|
68
|
+
# Remove the directory services record
|
69
|
+
`dscl . -delete /Users/#{account_name}`
|
70
|
+
else
|
71
|
+
not_implemented
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns a boolean identifying if an account with the given name already exists or not
|
76
|
+
def account_exists?(account_name)
|
77
|
+
if on_mac?
|
78
|
+
cmd_out = `dscl . -search /Users name "#{account_name}"`
|
79
|
+
return cmd_out.empty? ? false : true
|
80
|
+
else
|
81
|
+
not_implenented
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get a boolean representing if this is running on a Mac or not.
|
86
|
+
def on_mac?
|
87
|
+
return RUBY_PLATFORM.downcase.include?("darwin") ? true : false
|
88
|
+
end
|
89
|
+
|
90
|
+
def warn(warn, msg)
|
91
|
+
if warn
|
92
|
+
if @opts[:force]
|
93
|
+
puts "Warning: (Bypassed by -f) #{msg}"
|
94
|
+
yield
|
95
|
+
else
|
96
|
+
puts "Warning: #{msg}"
|
97
|
+
exit 2
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def error(msg)
|
103
|
+
puts "Error: #{msg}"
|
104
|
+
exit 1
|
105
|
+
end
|
106
|
+
|
107
|
+
def not_implemented
|
108
|
+
error("Not currently implemented")
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
opts = Trollop::options do
|
115
|
+
version "Octopusci v#{Octopusci::Version} (c) Andrew De Ponte"
|
116
|
+
banner """Usage: #{APP_NAME} [-i|--install|-a|--account|-v|--version|-h|--help]"""
|
117
|
+
opt :install, "Actually install Octopusci.", :short => "-i", :default => false
|
118
|
+
opt :force, "Force the install to continue ignoring warnings", :short => "-f", :default => false
|
119
|
+
opt :account, "Account name to create for the Octopusci install", :short => "-a", :default => "octopusci"
|
120
|
+
opt :uid, "User id to assign to the user account that is going to be created", :short => "-u", :default => "498"
|
121
|
+
opt :gid, "Primary group id to assign to the user account that is going to be created", :short => "-g", :default => "1000"
|
122
|
+
opt :shell, "Shell that used by the account that is going to be created", :short => "-s", :default => "/bin/bash"
|
123
|
+
end
|
124
|
+
|
125
|
+
if Process.uid != 0
|
126
|
+
error("Must run as root.")
|
127
|
+
end
|
128
|
+
|
129
|
+
installer = Octopusci::Installer.new(opts)
|
130
|
+
|
131
|
+
installer.install
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'octopusci/version'
|
7
|
+
require 'trollop'
|
8
|
+
require "uri"
|
9
|
+
require "net/http"
|
10
|
+
|
11
|
+
opts = Trollop::options do
|
12
|
+
version "Octopusci v#{Octopusci::Version} (c) Andrew De Ponte"
|
13
|
+
banner """Usage: octopusci-post-build-request [-v|--version|-h|--help]"""
|
14
|
+
opt :hostname, "Hostname/IP address to send the POST request to", :short => "-n", :default => '127.0.0.1'
|
15
|
+
opt :port, "Port number to connect to", :short => "-p", :default => 80
|
16
|
+
opt :repo_name, "Repository name to send build request for", :short => "-r", :default => "octopusci"
|
17
|
+
opt :branch_name, "Branch name to send build request for", :short => "-b", :default => 'master'
|
18
|
+
opt :owner_email, "Repository owner's email address", :short => "-o", :default => 'cyphactor@gmail.com'
|
19
|
+
opt :owner_name, "Repository owner's name", :short => "-w", :default => 'cyphactor'
|
20
|
+
opt :pusher_name, "Pusher's name", :short => "-u", :default => 'cyphactor'
|
21
|
+
opt :pusher_email, "Pusher's email address", :short => "-s", :default => 'pusher@example.com'
|
22
|
+
end
|
23
|
+
|
24
|
+
http = Net::HTTP.new(opts[:hostname], opts[:port])
|
25
|
+
path = '/github-build'
|
26
|
+
|
27
|
+
# POST request -> logging in
|
28
|
+
params = { 'payload' => "{\"base_ref\":null,\"ref\":\"refs/heads/#{opts[:branch_name]}\",\"commits\":[{\"message\":\"shit son\",\"removed\":[],\"modified\":[\"test.txt\"],\"added\":[],\"author\":{\"username\":\"cyphactor\",\"email\":\"cyphactor@gmail.com\",\"name\":\"Andrew De Ponte\"},\"distinct\":true,\"timestamp\":\"2011-09-06T22:01:12-07:00\",\"id\":\"8573e06a3cb511babd9d19d33be637b661232011\",\"url\":\"https://github.com/cyphactor/octopusci/commit/8573e06a3cb511babd9d19d33be637b661232011\"},{\"message\":\"again broham\",\"removed\":[],\"modified\":[\"test.txt\"],\"added\":[],\"author\":{\"username\":\"cyphactor\",\"email\":\"cyphactor@gmail.com\",\"name\":\"Andrew De Ponte\"},\"distinct\":true,\"timestamp\":\"2011-09-06T23:01:07-07:00\",\"id\":\"771adb4b75694804f256e7e61704648a14d52afc\",\"url\":\"https://github.com/cyphactor/octopusci/commit/771adb4b75694804f256e7e61704648a14d52afc\"},{\"message\":\"again broham\",\"removed\":[],\"modified\":[\"test.txt\"],\"added\":[],\"author\":{\"username\":\"cyphactor\",\"email\":\"cyphactor@gmail.com\",\"name\":\"Andrew De Ponte\"},\"distinct\":true,\"timestamp\":\"2011-09-06T23:03:16-07:00\",\"id\":\"dadf8446b2037960cc6223d60a7d57c5d264fa48\",\"url\":\"https://github.com/cyphactor/octopusci/commit/dadf8446b2037960cc6223d60a7d57c5d264fa48\"}],\"created\":false,\"before\":\"e5a1385fd8654c46d2b52d90c1ba31c865493602\",\"repository\":{\"created_at\":\"2011/09/02 12:40:14 -0700\",\"open_issues\":0,\"forks\":1,\"description\":\"Temp repo for testing pusci as I dev it\",\"has_wiki\":true,\"fork\":false,\"watchers\":1,\"has_downloads\":true,\"homepage\":\"\",\"has_issues\":true,\"private\":false,\"size\":116,\"owner\":{\"email\":\"#{opts[:owner_email]}\",\"name\":\"#{opts[:owner_name]}\"},\"name\":\"#{opts[:repo_name]}\",\"pushed_at\":\"2011/09/06 23:03:21 -0700\",\"url\":\"https://github.com/cyphactor/octopusci\"},\"pusher\":{\"email\":\"#{opts[:pusher_email]}\",\"name\":\"#{opts[:pusher_name]}\"},\"forced\":false,\"after\":\"dd6e7913cc12937db88e469684e8698f8eee8c14\",\"deleted\":false,\"compare\":\"https://github.com/cyphactor/octopusci/compare/e5a1385...dd6e791\"}" }
|
29
|
+
|
30
|
+
data = URI.encode_www_form(params)
|
31
|
+
headers = {
|
32
|
+
'Accept' => '*/*',
|
33
|
+
'Content-Type' => 'application/x-www-form-urlencoded',
|
34
|
+
'X-Github-Event' => 'push'
|
35
|
+
}
|
36
|
+
|
37
|
+
resp, data = http.post(path, data, headers)
|
38
|
+
|
39
|
+
puts 'Code = ' + resp.code
|
40
|
+
puts 'Message = ' + resp.message
|
41
|
+
resp.each {|key, val| puts key + ' = ' + val}
|
42
|
+
puts data
|
data/bin/octopusci-reset-redis
CHANGED
@@ -3,24 +3,51 @@
|
|
3
3
|
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
4
|
|
5
5
|
require 'rubygems'
|
6
|
-
require 'octopusci'
|
6
|
+
require 'octopusci/version'
|
7
|
+
require 'octopusci/job_store'
|
8
|
+
require 'trollop'
|
9
|
+
|
10
|
+
opts = Trollop::options do
|
11
|
+
version "Octopusci v#{Octopusci::Version} (c) Andrew De Ponte"
|
12
|
+
banner """Usage: octopusci-reset-redis [-r|-v|--version|-h|--help]"""
|
13
|
+
opt :reset, "Actually reset the redis datastore", :short => "-r", :default => false
|
14
|
+
end
|
7
15
|
|
8
|
-
|
9
|
-
|
16
|
+
if opts[:reset]
|
17
|
+
num_jobs = Octopusci::JobStore.size
|
18
|
+
jobs = Octopusci::JobStore.list(0, num_jobs)
|
10
19
|
|
11
|
-
jobs.each do |j|
|
12
|
-
|
13
|
-
|
20
|
+
jobs.each do |j|
|
21
|
+
# delete the actual job record
|
22
|
+
Octopusci::JobStore.redis.del("octopusci:jobs:#{j['id']}")
|
14
23
|
|
15
|
-
|
16
|
-
|
24
|
+
# delete the repo_name, branch_name job references
|
25
|
+
Octopusci::JobStore.redis.del("octopusci:#{j['repo_name']}:#{j['branch_name']}:jobs")
|
17
26
|
|
18
|
-
|
19
|
-
|
27
|
+
# delete the github payload for the repo_name, branch_name
|
28
|
+
Octopusci::JobStore.redis.del("octopusci:github_payload:#{j['repo_name']}:#{j['branch_name']}")
|
20
29
|
|
21
|
-
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Octopusci::JobStore.redis.del('octopusci:job_count')
|
33
|
+
Octopusci::JobStore.redis.del('octopusci:jobs')
|
34
|
+
Octopusci::JobStore.redis.del('octopusci:stagelocker')
|
35
|
+
Octopusci::JobStore.redis.del('queue:octopusci:commit')
|
36
|
+
else
|
37
|
+
puts %Q{THIS IS A WARNING. THIS IS A DESTRUCTIVE COMMAND!
|
38
|
+
|
39
|
+
This command is designed to allow you to reset/clear out the values in your redis
|
40
|
+
data store associated with Octopusci. If you run this command with the proper option
|
41
|
+
it will actually get rid of all of the data stored in the redis datae store for Octopusci.
|
42
|
+
It is intended to be used very rarely and only by people that understand exactly what it
|
43
|
+
is going to do (primarily Octopusci Developers).
|
22
44
|
|
23
|
-
Octopusci
|
24
|
-
|
25
|
-
|
26
|
-
|
45
|
+
To go forward and actually reset the redis data store with respect to Octopusci run it using
|
46
|
+
the following example.
|
47
|
+
|
48
|
+
Example:
|
49
|
+
octopusci-reset-redis -r
|
50
|
+
|
51
|
+
}
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'octopusci/version'
|
7
|
+
require 'trollop'
|
8
|
+
|
9
|
+
opts = Trollop::options do
|
10
|
+
version "Octopusci v#{Octopusci::Version} (c) Andrew De Ponte"
|
11
|
+
banner """Usage: octopusci-reset-stage-locker [-r|-v|--version|-h|--help]"""
|
12
|
+
opt :reset, "Actually reset the stage locker", :short => "-r", :default => false
|
13
|
+
end
|
14
|
+
|
15
|
+
if opts[:reset]
|
16
|
+
puts "Reseting the Octopusci StageLocker, please wait..."
|
17
|
+
require 'octopusci'
|
18
|
+
Octopusci::StageLocker.load(Octopusci::Config['stages'])
|
19
|
+
puts "Succesfully Reset the Octopusci StageLocker"
|
20
|
+
else
|
21
|
+
puts %Q{THIS IS A WARNING. THIS IS A DESTRUCTIVE COMMAND!
|
22
|
+
|
23
|
+
This command is designed to allow you to reset/clear out the Octopusci StageLocker
|
24
|
+
and repopulate it with the stages defined in the config. If you run this command
|
25
|
+
with the proper option it will actually get rid of the Octopusci StageLocker and
|
26
|
+
recreate it with the stage values from the Octopusci config. It is intended to be
|
27
|
+
used very rarely.
|
28
|
+
|
29
|
+
The primary use case for it is that you have added a stage to your Octopusci setup
|
30
|
+
after already doing the initial setup. Running this command is necessary to make
|
31
|
+
Octopusci aware of the new stages you have added to the config since the initial
|
32
|
+
setup.
|
33
|
+
|
34
|
+
WARNING: You should not run this command until all job processes have completed.
|
35
|
+
Running 'octopusci-tentacles stop' only requests that the tentacles stop running
|
36
|
+
future jobs and that they kill themselves off once they are done with any current
|
37
|
+
jobs they are running. So, be sure you wait for the currently running jobs to
|
38
|
+
exit before you run this command or you could end up with multiple jobs running
|
39
|
+
on a single stage.
|
40
|
+
|
41
|
+
To go forward and actually reset the Octopusci StageLocker simply run the command
|
42
|
+
using the following example.
|
43
|
+
|
44
|
+
Example:
|
45
|
+
octopusci-reset-stage-locker -r
|
46
|
+
|
47
|
+
}
|
48
|
+
|
49
|
+
end
|
data/bin/octopusci-skel
CHANGED
@@ -2,25 +2,34 @@
|
|
2
2
|
|
3
3
|
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
4
|
|
5
|
+
APP_NAME = "octopusci-skel"
|
6
|
+
|
5
7
|
require 'rubygems'
|
8
|
+
require 'octopusci/version'
|
6
9
|
require 'fileutils'
|
10
|
+
require 'trollop'
|
7
11
|
|
8
12
|
if Process.uid != 0
|
9
13
|
puts "Must run as root"
|
10
14
|
exit 1
|
11
15
|
end
|
12
16
|
|
17
|
+
opts = Trollop::options do
|
18
|
+
version "Octopusci v#{Octopusci::Version} (c) Andrew De Ponte"
|
19
|
+
banner """Usage: #{APP_NAME} [-c|-v|--version|-h|--help]"""
|
20
|
+
opt :create, "Actually create any missing pieces of the Octopusci Skeleton.", :short => "-c", :default => false
|
21
|
+
end
|
22
|
+
|
13
23
|
JOBS_PATH = '/etc/octopusci/jobs'
|
14
24
|
CONFIG_PATH = '/etc/octopusci/config.yml'
|
25
|
+
OCTOPUSCI_BUILD_PATH = '/etc/octopusci/jobs/octopusci_rspec_build_local.rb'
|
26
|
+
WORKSPACE_BASE_PATH = '/var/octopusci'
|
15
27
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
jobs_path: \"/etc/octopusci/jobs\"
|
22
|
-
base_url: \"http://localhost:9998\"
|
23
|
-
workspace_base_path: \"/Users/octopusci/.octopusci\"
|
28
|
+
config_default_content = %Q{
|
29
|
+
general:
|
30
|
+
jobs_path: "/etc/octopusci/jobs"
|
31
|
+
workspace_base_path: "#{WORKSPACE_BASE_PATH}"
|
32
|
+
base_url: "http://localhost:9998"
|
24
33
|
|
25
34
|
http_basic:
|
26
35
|
username: admin
|
@@ -37,15 +46,52 @@ smtp:
|
|
37
46
|
raise_delivery_errors: true
|
38
47
|
|
39
48
|
projects:
|
40
|
-
- { name: octopusci, owner: cyphactor, job_klass:
|
49
|
+
- { name: octopusci, owner: cyphactor, job_klass: OctopusciRSpecBuildLocal, repo_uri: 'git@github.com:cyphactor/octopusci.git', default_email: devs@example.com }
|
41
50
|
|
42
51
|
stages:
|
43
|
-
-
|
44
|
-
|
52
|
+
- test_a
|
53
|
+
}
|
54
|
+
|
55
|
+
octopusci_build_default_content = %Q{
|
56
|
+
class OctopusciRSpecBuildLocal < Octopusci::Job
|
57
|
+
def self.run(job_rec)
|
58
|
+
context "RSpec Tests (commit)" do
|
59
|
+
run_shell_cmd!("bundle install", true)
|
60
|
+
run_shell_cmd!("STAGE=\#{job_rec['stage']} bundle exec rspec spec 2>&1")
|
61
|
+
end
|
45
62
|
end
|
46
|
-
puts "Created example #{CONFIG_PATH}, please modify appropriately"
|
47
|
-
else
|
48
|
-
puts "#{CONFIG_PATH} already exists, exiting to avoid modification."
|
49
|
-
puts "If you would like to generated the example config again please rename the existing #{CONFIG_PATH}."
|
50
63
|
end
|
64
|
+
}
|
51
65
|
|
66
|
+
if opts[:create]
|
67
|
+
FileUtils.mkdir_p(JOBS_PATH)
|
68
|
+
puts "1. Ran mkdir -p #{JOBS_PATH}"
|
69
|
+
FileUtils.mkdir_p(WORKSPACE_BASE_PATH)
|
70
|
+
puts "2. Ran mkdir -p #{WORKSPACE_BASE_PATH}"
|
71
|
+
|
72
|
+
if !File.exists?(CONFIG_PATH)
|
73
|
+
File.open(CONFIG_PATH, 'w') do |f|
|
74
|
+
f << config_default_content
|
75
|
+
end
|
76
|
+
puts "3. Created example #{CONFIG_PATH}. Please modify appropriately"
|
77
|
+
else
|
78
|
+
puts "3. #{CONFIG_PATH} already exists. It has NOT been modified."
|
79
|
+
puts " If you would like to generate the example config again please rename the existing #{CONFIG_PATH} and rerun #{APP_NAME}."
|
80
|
+
end
|
81
|
+
|
82
|
+
if !File.exists?(OCTOPUSCI_BUILD_PATH)
|
83
|
+
File.open(OCTOPUSCI_BUILD_PATH, 'w') do |f|
|
84
|
+
f << octopusci_build_default_content
|
85
|
+
end
|
86
|
+
puts "4. Created example job definition (#{OCTOPUSCI_BUILD_PATH}). Please use as a starting point for your own jobs."
|
87
|
+
else
|
88
|
+
puts "4. #{OCTOPUSCI_BUILD_PATH} already exists. It has NOT been modified."
|
89
|
+
puts " If you would like to generate the example job definition again please rename the existing #{OCTOPUSCI_BUILD_PATH} and rerun #{APP_NAME}"
|
90
|
+
end
|
91
|
+
else
|
92
|
+
puts "This is a Dry Run\n No actions have been taken this is simply a description of what would take place if you ran this command with the create option (-c).\n\n"
|
93
|
+
puts " 1. mkdir -p #{JOBS_PATH}"
|
94
|
+
puts " 2. mkdir -p #{WORKSPACE_BASE_PATH}"
|
95
|
+
puts " 3. Create example config (#{CONFIG_PATH}) if one doesn't exist"
|
96
|
+
puts " 4. Create example job definition (#{OCTOPUSCI_BUILD_PATH}) if it doesn't exist"
|
97
|
+
end
|
data/bin/octopusci-tentacles
CHANGED
@@ -1,44 +1,58 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
def display_help
|
4
|
-
puts "Usage: octopusci-tentacles <command>"
|
5
|
-
puts " Acceptable Commands:"
|
6
|
-
puts " start - Cleanup existing workers and start new worker processes in the background"
|
7
|
-
puts " stop - Signals workers to exit when current job is finished"
|
8
|
-
puts " restart - Synonym for the 'start' command, just here for convenience"
|
9
|
-
puts " help - Display this help"
|
10
|
-
puts ""
|
11
|
-
puts " Example: octopusci-tentacles start"
|
12
|
-
puts ""
|
13
|
-
end
|
14
|
-
|
15
|
-
if !["start", "stop", "restart", "help"].include?(ARGV[0])
|
16
|
-
display_help()
|
17
|
-
exit 1
|
18
|
-
end
|
19
|
-
|
20
3
|
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
21
4
|
|
22
5
|
require 'rubygems'
|
23
6
|
require 'octopusci'
|
7
|
+
require 'trollop'
|
8
|
+
|
9
|
+
SUB_COMMANDS = %w(start, restart, stop, help)
|
10
|
+
|
11
|
+
p = Trollop::Parser.new do
|
12
|
+
version "Octopusci v#{Octopusci::Version} (c) Andrew De Ponte"
|
13
|
+
banner """Usage: octopusci-tentacles [-v|--version|-h|--help] <subcommand>
|
14
|
+
Acceptable SubCommands:
|
15
|
+
start - Cleanup existing workers and start new worker processes in the background
|
16
|
+
stop - Signals workers to exit when current job is finished
|
17
|
+
restart - Synonym for the 'start' command, just here for convenience
|
18
|
+
help - Display this help
|
19
|
+
|
20
|
+
Available Options:
|
21
|
+
"""
|
22
|
+
stop_on SUB_COMMANDS
|
23
|
+
end
|
24
|
+
|
25
|
+
opts = Trollop::with_standard_exception_handling(p) do
|
26
|
+
o = p.parse ARGV
|
27
|
+
raise Trollop::HelpNeeded if ARGV.empty? # show help screen
|
28
|
+
o
|
29
|
+
end
|
30
|
+
|
31
|
+
def start_or_restart
|
32
|
+
# The StageLocker.load() method is called here not only to set things into
|
33
|
+
# valid initial state. But, also in the case that octopusci-tentacles was
|
34
|
+
# killed it nukes the potentially screwed up state the redis data is in and
|
35
|
+
# initializes it the proper state based on the config.
|
36
|
+
if Octopusci::Config.has_key?('stages') && Octopusci::StageLocker.empty?
|
37
|
+
puts "Reseting stage locker and loading stages in from config."
|
38
|
+
Octopusci::StageLocker.load(Octopusci::Config['stages'])
|
39
|
+
end
|
24
40
|
|
25
|
-
|
26
|
-
|
27
|
-
# killed it nukes the potentially screwed up state the redis data is in and
|
28
|
-
# initializes it the proper state based on the config.
|
29
|
-
if Octopusci::Config.has_key?('stages')
|
30
|
-
Octopusci::StageLocker.load(Octopusci::Config['stages'])
|
41
|
+
Octopusci::WorkerLauncher.cleanup_existing_workers()
|
42
|
+
Octopusci::WorkerLauncher.launch()
|
31
43
|
end
|
32
44
|
|
33
|
-
|
45
|
+
cmd = ARGV.shift # get the subcommand
|
46
|
+
case cmd
|
34
47
|
when "start"
|
35
|
-
|
36
|
-
Octopusci::WorkerLauncher.launch()
|
48
|
+
start_or_restart()
|
37
49
|
when "restart"
|
38
|
-
|
39
|
-
Octopusci::WorkerLauncher.launch()
|
50
|
+
start_or_restart()
|
40
51
|
when "stop"
|
41
52
|
Octopusci::WorkerLauncher.cleanup_existing_workers()
|
42
53
|
when "help"
|
43
|
-
|
54
|
+
p.educate
|
55
|
+
else
|
56
|
+
p.die "unknown subcommand #{cmd.inspect}", nil
|
44
57
|
end
|
58
|
+
|
data/lib/octopusci/job.rb
CHANGED
@@ -118,7 +118,7 @@ module Octopusci
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def self.checkout_branch(job_conf)
|
121
|
-
return run_shell_cmd("cd #{repository_path} 2>&1 && git fetch --all -p 2>&1 && git checkout #{@job['branch_name']} 2>&1 && git pull -f origin #{@job['branch_name']}:#{@job['branch_name']} 2>&1", true, false)
|
121
|
+
return run_shell_cmd("cd #{repository_path} 2>&1 && git fetch --all -p 2>&1 && git reset --hard && git checkout #{@job['branch_name']} 2>&1 && git pull -f origin #{@job['branch_name']}:#{@job['branch_name']} 2>&1", true, false)
|
122
122
|
end
|
123
123
|
|
124
124
|
def self.get_recip_email
|
data/lib/octopusci/job_store.rb
CHANGED
@@ -22,6 +22,11 @@
|
|
22
22
|
color: white;
|
23
23
|
background-color: #333;
|
24
24
|
}
|
25
|
+
|
26
|
+
.version {
|
27
|
+
font-size: 11px;
|
28
|
+
margin-right: 10px;
|
29
|
+
}
|
25
30
|
|
26
31
|
#main_content_area {
|
27
32
|
width: 960px;
|
@@ -161,7 +166,7 @@
|
|
161
166
|
<body>
|
162
167
|
<div id="header">
|
163
168
|
<div id="logo">
|
164
|
-
Octopusci
|
169
|
+
Octopusci <span class="version">v<%= Octopusci::Version %></span><% if @page_logo %>( <%= @page_logo %> )<% end %>
|
165
170
|
</div>
|
166
171
|
<div id="main_nav">
|
167
172
|
<a href="/">Dashboard</a>
|
data/lib/octopusci/version.rb
CHANGED
@@ -17,6 +17,35 @@ describe "Octopusci::StageLocker" do
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
describe "#exists?" do
|
21
|
+
it "should check if the redis key exists for the stage locker" do
|
22
|
+
@mock_redis.should_receive(:exists).with('octopusci:stagelocker')
|
23
|
+
Octopusci::StageLocker.exists?
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return fales if the stage locker key does not exists in redis" do
|
27
|
+
@mock_redis.stub(:exists).and_return(false)
|
28
|
+
Octopusci::StageLocker.exists?.should == false
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return true if the stage locker key exists in redis" do
|
32
|
+
@mock_redis.stub(:exists).and_return(true)
|
33
|
+
Octopusci::StageLocker.exists?.should == true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#empty?" do
|
38
|
+
it "should return true if exists? returns false" do
|
39
|
+
Octopusci::StageLocker.stub(:exists?).and_return(false)
|
40
|
+
Octopusci::StageLocker.empty?.should == true
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should return false if exists? returns true" do
|
44
|
+
Octopusci::StageLocker.stub(:exists?).and_return(true)
|
45
|
+
Octopusci::StageLocker.empty?.should == false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
20
49
|
describe "#clear" do
|
21
50
|
it "should clear all of the stages out of the stage locker" do
|
22
51
|
r = mock('redis')
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: octopusci
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-12-12 00:00:00.000000000Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: sinatra
|
17
|
-
requirement: &
|
17
|
+
requirement: &70349244223700 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70349244223700
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: json
|
28
|
-
requirement: &
|
28
|
+
requirement: &70349244223280 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70349244223280
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: resque
|
39
|
-
requirement: &
|
39
|
+
requirement: &70349244222860 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,10 +44,10 @@ dependencies:
|
|
44
44
|
version: '0'
|
45
45
|
type: :runtime
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *70349244222860
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: actionmailer
|
50
|
-
requirement: &
|
50
|
+
requirement: &70349244222440 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ! '>='
|
@@ -55,10 +55,10 @@ dependencies:
|
|
55
55
|
version: '0'
|
56
56
|
type: :runtime
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *70349244222440
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: multi_json
|
61
|
-
requirement: &
|
61
|
+
requirement: &70349244222020 !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
64
64
|
- - ! '>='
|
@@ -66,10 +66,10 @@ dependencies:
|
|
66
66
|
version: '0'
|
67
67
|
type: :runtime
|
68
68
|
prerelease: false
|
69
|
-
version_requirements: *
|
69
|
+
version_requirements: *70349244222020
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: time-ago-in-words
|
72
|
-
requirement: &
|
72
|
+
requirement: &70349244221600 !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
75
|
- - ! '>='
|
@@ -77,10 +77,10 @@ dependencies:
|
|
77
77
|
version: '0'
|
78
78
|
type: :runtime
|
79
79
|
prerelease: false
|
80
|
-
version_requirements: *
|
80
|
+
version_requirements: *70349244221600
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
82
|
name: ansi2html
|
83
|
-
requirement: &
|
83
|
+
requirement: &70349244221180 !ruby/object:Gem::Requirement
|
84
84
|
none: false
|
85
85
|
requirements:
|
86
86
|
- - ! '>='
|
@@ -88,10 +88,21 @@ dependencies:
|
|
88
88
|
version: '0'
|
89
89
|
type: :runtime
|
90
90
|
prerelease: false
|
91
|
-
version_requirements: *
|
91
|
+
version_requirements: *70349244221180
|
92
|
+
- !ruby/object:Gem::Dependency
|
93
|
+
name: trollop
|
94
|
+
requirement: &70349244220760 !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
type: :runtime
|
101
|
+
prerelease: false
|
102
|
+
version_requirements: *70349244220760
|
92
103
|
- !ruby/object:Gem::Dependency
|
93
104
|
name: rake
|
94
|
-
requirement: &
|
105
|
+
requirement: &70349244220340 !ruby/object:Gem::Requirement
|
95
106
|
none: false
|
96
107
|
requirements:
|
97
108
|
- - ! '>='
|
@@ -99,10 +110,10 @@ dependencies:
|
|
99
110
|
version: '0'
|
100
111
|
type: :development
|
101
112
|
prerelease: false
|
102
|
-
version_requirements: *
|
113
|
+
version_requirements: *70349244220340
|
103
114
|
- !ruby/object:Gem::Dependency
|
104
115
|
name: rspec
|
105
|
-
requirement: &
|
116
|
+
requirement: &70349244219920 !ruby/object:Gem::Requirement
|
106
117
|
none: false
|
107
118
|
requirements:
|
108
119
|
- - ! '>='
|
@@ -110,10 +121,10 @@ dependencies:
|
|
110
121
|
version: '0'
|
111
122
|
type: :development
|
112
123
|
prerelease: false
|
113
|
-
version_requirements: *
|
124
|
+
version_requirements: *70349244219920
|
114
125
|
- !ruby/object:Gem::Dependency
|
115
126
|
name: rack-test
|
116
|
-
requirement: &
|
127
|
+
requirement: &70349244219500 !ruby/object:Gem::Requirement
|
117
128
|
none: false
|
118
129
|
requirements:
|
119
130
|
- - ! '>='
|
@@ -121,10 +132,10 @@ dependencies:
|
|
121
132
|
version: '0'
|
122
133
|
type: :development
|
123
134
|
prerelease: false
|
124
|
-
version_requirements: *
|
135
|
+
version_requirements: *70349244219500
|
125
136
|
- !ruby/object:Gem::Dependency
|
126
137
|
name: guard
|
127
|
-
requirement: &
|
138
|
+
requirement: &70349244235460 !ruby/object:Gem::Requirement
|
128
139
|
none: false
|
129
140
|
requirements:
|
130
141
|
- - ! '>='
|
@@ -132,10 +143,10 @@ dependencies:
|
|
132
143
|
version: '0'
|
133
144
|
type: :development
|
134
145
|
prerelease: false
|
135
|
-
version_requirements: *
|
146
|
+
version_requirements: *70349244235460
|
136
147
|
- !ruby/object:Gem::Dependency
|
137
148
|
name: rb-fsevent
|
138
|
-
requirement: &
|
149
|
+
requirement: &70349244235040 !ruby/object:Gem::Requirement
|
139
150
|
none: false
|
140
151
|
requirements:
|
141
152
|
- - ! '>='
|
@@ -143,10 +154,10 @@ dependencies:
|
|
143
154
|
version: '0'
|
144
155
|
type: :development
|
145
156
|
prerelease: false
|
146
|
-
version_requirements: *
|
157
|
+
version_requirements: *70349244235040
|
147
158
|
- !ruby/object:Gem::Dependency
|
148
159
|
name: growl_notify
|
149
|
-
requirement: &
|
160
|
+
requirement: &70349244234620 !ruby/object:Gem::Requirement
|
150
161
|
none: false
|
151
162
|
requirements:
|
152
163
|
- - ! '>='
|
@@ -154,10 +165,10 @@ dependencies:
|
|
154
165
|
version: '0'
|
155
166
|
type: :development
|
156
167
|
prerelease: false
|
157
|
-
version_requirements: *
|
168
|
+
version_requirements: *70349244234620
|
158
169
|
- !ruby/object:Gem::Dependency
|
159
170
|
name: guard-rspec
|
160
|
-
requirement: &
|
171
|
+
requirement: &70349244234200 !ruby/object:Gem::Requirement
|
161
172
|
none: false
|
162
173
|
requirements:
|
163
174
|
- - ! '>='
|
@@ -165,8 +176,8 @@ dependencies:
|
|
165
176
|
version: '0'
|
166
177
|
type: :development
|
167
178
|
prerelease: false
|
168
|
-
version_requirements: *
|
169
|
-
description: A multi-branch
|
179
|
+
version_requirements: *70349244234200
|
180
|
+
description: A multi-branch Continuous Integration server that integrates with GitHub
|
170
181
|
email:
|
171
182
|
- cyphactor@gmail.com
|
172
183
|
- sarmiena@gmail.com
|
@@ -174,6 +185,8 @@ executables:
|
|
174
185
|
- octopusci-tentacles
|
175
186
|
- octopusci-skel
|
176
187
|
- octopusci-reset-redis
|
188
|
+
- octopusci-reset-stage-locker
|
189
|
+
- octopusci-post-build-request
|
177
190
|
extensions: []
|
178
191
|
extra_rdoc_files: []
|
179
192
|
files:
|
@@ -204,11 +217,12 @@ files:
|
|
204
217
|
- lib/octopusci/version.rb
|
205
218
|
- lib/octopusci/worker_launcher.rb
|
206
219
|
- lib/octopusci.rb
|
207
|
-
- bin/octopusci
|
220
|
+
- bin/octopusci-install
|
221
|
+
- bin/octopusci-post-build-request
|
208
222
|
- bin/octopusci-reset-redis
|
223
|
+
- bin/octopusci-reset-stage-locker
|
209
224
|
- bin/octopusci-skel
|
210
225
|
- bin/octopusci-tentacles
|
211
|
-
- bin/pusci-stage
|
212
226
|
- spec/lib/octopusci/config_spec.rb
|
213
227
|
- spec/lib/octopusci/io_spec.rb
|
214
228
|
- spec/lib/octopusci/job_spec.rb
|
data/bin/octopusci
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
-
|
5
|
-
require 'rubygems'
|
6
|
-
require 'optparse'
|
7
|
-
|
8
|
-
# This hash will hold all of the options
|
9
|
-
# parsed from the command-line by
|
10
|
-
# OptionParser.
|
11
|
-
options = {}
|
12
|
-
|
13
|
-
optparse = OptionParser.new do|opts|
|
14
|
-
# Set a banner, displayed at the top
|
15
|
-
# of the help screen.
|
16
|
-
opts.banner = "Usage: optparse1.rb [options] file1 file2 ..."
|
17
|
-
|
18
|
-
# Define the options, and what they do
|
19
|
-
options[:verbose] = false
|
20
|
-
opts.on( '-v', '--verbose', 'Output more information' ) do
|
21
|
-
options[:verbose] = true
|
22
|
-
end
|
23
|
-
|
24
|
-
options[:quick] = false
|
25
|
-
opts.on( '-q', '--quick', 'Perform the task quickly' ) do
|
26
|
-
options[:quick] = true
|
27
|
-
end
|
28
|
-
|
29
|
-
options[:logfile] = nil
|
30
|
-
opts.on( '-l', '--logfile FILE', 'Write log to FILE' ) do |file|
|
31
|
-
options[:logfile] = file
|
32
|
-
end
|
33
|
-
|
34
|
-
# This displays the help screen, all programs are
|
35
|
-
# assumed to have this option.
|
36
|
-
opts.on( '-h', '--help', 'Display this screen' ) do
|
37
|
-
puts opts
|
38
|
-
exit
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Parse the command-line. Remember there are two forms
|
43
|
-
# of the parse method. The 'parse' method simply parses
|
44
|
-
# ARGV, while the 'parse!' method parses ARGV and removes
|
45
|
-
# any options found there, as well as any parameters for
|
46
|
-
# the options. What's left is the list of files to resize.
|
47
|
-
optparse.parse!
|
48
|
-
|
49
|
-
puts "Being verbose" if options[:verbose]
|
50
|
-
puts "Being quick" if options[:quick]
|
51
|
-
puts "Logging to file #{options[:logfile]}" if options[:logfile]
|
52
|
-
|
53
|
-
ARGV.each do|f|
|
54
|
-
puts "Resizing image #{f}..."
|
55
|
-
sleep 0.5
|
56
|
-
end
|
data/bin/pusci-stage
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
-
|
5
|
-
require 'rubygems'
|
6
|
-
require 'optparse'
|
7
|
-
require 'octopusci'
|
8
|
-
|
9
|
-
# This hash will hold all of the options
|
10
|
-
# parsed from the command-line by
|
11
|
-
# OptionParser.
|
12
|
-
options = {}
|
13
|
-
|
14
|
-
optparse = OptionParser.new do|opts|
|
15
|
-
# Set a banner, displayed at the top
|
16
|
-
# of the help screen.
|
17
|
-
opts.banner = "Usage: pusci-stage [options] stage_name"
|
18
|
-
|
19
|
-
# Define the options, and what they do
|
20
|
-
options[:add] = false
|
21
|
-
opts.on('-a', '--add', 'Add a stage back into the pool') do
|
22
|
-
options[:add] = true
|
23
|
-
end
|
24
|
-
|
25
|
-
options[:rem] = false
|
26
|
-
opts.on( '-r', '--rem', 'Rem a stage from the pool' ) do
|
27
|
-
options[:rem] = true
|
28
|
-
end
|
29
|
-
|
30
|
-
options[:list] = false
|
31
|
-
opts.on( '-l', '--list', 'List all stages' ) do
|
32
|
-
options[:list] = true
|
33
|
-
end
|
34
|
-
|
35
|
-
options[:pool] = false
|
36
|
-
opts.on( '-p', '--pool', 'List all stages currently in the pool' ) do
|
37
|
-
options[:pool] = true
|
38
|
-
end
|
39
|
-
|
40
|
-
# This displays the help screen, all programs are
|
41
|
-
# assumed to have this option.
|
42
|
-
opts.on( '-h', '--help', 'Display the help screen' ) do
|
43
|
-
puts opts
|
44
|
-
exit
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Parse the command-line. Remember there are two forms
|
49
|
-
# of the parse method. The 'parse' method simply parses
|
50
|
-
# ARGV, while the 'parse!' method parses ARGV and removes
|
51
|
-
# any options found there, as well as any parameters for
|
52
|
-
# the options. What's left is the list of files to resize.
|
53
|
-
optparse.parse!
|
54
|
-
|
55
|
-
if options[:list] == true
|
56
|
-
Octopusci::StageLocker.stages.each do |s|
|
57
|
-
puts s
|
58
|
-
end
|
59
|
-
elsif options[:pool] == true
|
60
|
-
puts Octopusci::StageLocker.pool
|
61
|
-
elsif options[:add] == true
|
62
|
-
Octopusci::StageLocker.push(ARGV[0])
|
63
|
-
elsif options[:rem] == true
|
64
|
-
Octopusci::StageLocker.rem(ARGV[0])
|
65
|
-
end
|
66
|
-
|
67
|
-
exit
|