cheese 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +0 -0
- data/History.txt +0 -0
- data/LICENSE +22 -0
- data/Manifest.txt +30 -0
- data/README.txt +132 -0
- data/Rakefile +54 -0
- data/TODO.txt +11 -0
- data/bin/cheese +118 -0
- data/data/templates/footer.inc +1 -0
- data/data/templates/header.inc +46 -0
- data/data/templates/mongrel.inc +7 -0
- data/data/templates/proxy.inc +3 -0
- data/data/templates/vhost.inc +84 -0
- data/lib/cheese-setup.rb +144 -0
- data/lib/cheese/version.rb +9 -0
- data/lib/controller.rb +152 -0
- data/lib/database/mysql.rb +50 -0
- data/lib/database/postgresql.rb +29 -0
- data/lib/process.rb +17 -0
- data/lib/scm/subversion.rb +83 -0
- data/lib/verbose.rb +37 -0
- data/lib/web/domain.rb +22 -0
- data/lib/web/mongrel.rb +30 -0
- data/lib/web/nginx.rb +223 -0
- data/lib/web/proxy.rb +25 -0
- data/lib/web/virtual_host.rb +24 -0
- data/setup.rb +1360 -0
- metadata +80 -0
data/lib/cheese-setup.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# cheese-setup.rb
|
3
|
+
#
|
4
|
+
# Configure cheese-tools defaults and misc information
|
5
|
+
# This is a simple run-once script which needs LOTS of refactoring and cleanup
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'highline/import'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'yaml'
|
12
|
+
|
13
|
+
def draw_line(count=20)
|
14
|
+
@line = '-' * count
|
15
|
+
say "<%= color(@line, :horizontal_line) %>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def draw_box
|
19
|
+
draw_line 50
|
20
|
+
yield
|
21
|
+
draw_line 50
|
22
|
+
end
|
23
|
+
|
24
|
+
CHEESE_CONF = "/etc/cheese/cheese.conf"
|
25
|
+
SUPPORTED_SCMS = {
|
26
|
+
:svn => { :name => 'Subversion', :binary => 'svnserve' } }
|
27
|
+
SUPPORTED_DBS = {
|
28
|
+
:psql => { :name => 'Postgresql', :binary => 'psql' },
|
29
|
+
:mysql => { :name => 'MySQL', :binary => 'mysql' } }
|
30
|
+
SILENCER = ">/dev/null 2>/dev/null"
|
31
|
+
begin
|
32
|
+
FileUtils.mkdir '/etc/cheese' unless File.exists? '/etc/cheese'
|
33
|
+
FileUtils.touch CHEESE_CONF
|
34
|
+
|
35
|
+
preferences = {}
|
36
|
+
|
37
|
+
# Create a color scheme, naming color patterns with symbol names.
|
38
|
+
ft = HighLine::ColorScheme.new do |cs|
|
39
|
+
cs[:headline] = [ :bold, :yellow ]
|
40
|
+
cs[:horizontal_line] = [ :bold, :white ]
|
41
|
+
cs[:important] = [ :bold, :red ]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Assign that color scheme to HighLine...
|
45
|
+
HighLine.color_scheme = ft
|
46
|
+
|
47
|
+
say "<%= color('Welcome to the cheese setup utility', :headline) %>"
|
48
|
+
draw_line 37
|
49
|
+
|
50
|
+
say "Answer each of the following questions and then you'll be"
|
51
|
+
say "ready to start using cheese:"
|
52
|
+
|
53
|
+
# SCM
|
54
|
+
choose do |menu|
|
55
|
+
SUPPORTED_SCMS.each do |scm, details|
|
56
|
+
menu.choice(scm, details[:name]) do |command, extras|
|
57
|
+
preferences[:scm] = { :name => details[:name], :binary => details[:binary]}
|
58
|
+
say "What would you like the default #{details[:name]} username to be?"
|
59
|
+
preferences[:scm_user] = ask("=> ", String)
|
60
|
+
say "and what password would you like?"
|
61
|
+
preferences[:scm_pass] = ask("=> ", String){|prompt| prompt.echo = "*" }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
menu.choice(:skip, "You don't want to use one") do |command, details|
|
65
|
+
preferences[:scm] = :skip
|
66
|
+
end
|
67
|
+
menu.choice(:quit, "Exit setup") { exit }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Database
|
71
|
+
say "What database engine are you using?"
|
72
|
+
choose do |menu|
|
73
|
+
SUPPORTED_DBS.each do |db, details|
|
74
|
+
menu.choice(db, details[:name]) do |command, extras|
|
75
|
+
preferences[:database_type] = { :name => details[:name], :binary => details[:binary] }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
menu.choice(:skip, "You don't want to use one") do |command, details|
|
79
|
+
preferences[:database_type] = :skip
|
80
|
+
end
|
81
|
+
menu.choice(:quit, "Exit setup") { exit }
|
82
|
+
end
|
83
|
+
|
84
|
+
# Rails stack combination
|
85
|
+
say "What Ruby on Rails stack are you using?"
|
86
|
+
choose do |menu|
|
87
|
+
menu.choice(:nginx, "Nginx with Mongrel") do |command, details|
|
88
|
+
preferences[:stack] = :nginx
|
89
|
+
end
|
90
|
+
menu.choice(:skip, "You don't want to use one") do |command, details|
|
91
|
+
preferences[:scm] = :skip
|
92
|
+
end
|
93
|
+
menu.choice(:quit, "Exit setup") { exit }
|
94
|
+
end
|
95
|
+
|
96
|
+
draw_box do
|
97
|
+
say "<%= color('You need a normal account for safety reasons, running as', :bold) %>"
|
98
|
+
say "<%= color('root is dangerous, so we need to do that', :bold) %>"
|
99
|
+
end
|
100
|
+
|
101
|
+
user = ask("What username would you like for logging into the system?", String)
|
102
|
+
unless user.empty?
|
103
|
+
preferences[:user] = user
|
104
|
+
%x{ useradd -c "Cheesey User" -d /home/#{preferences[:user].chomp} --create-home #{preferences[:user].chomp} #{SILENCER} }
|
105
|
+
say "and what password would you like to use with that?"
|
106
|
+
%x{ passwd #{preferences[:user]} }
|
107
|
+
%x{ addgroup scm #{SILENCER} }
|
108
|
+
%x{ usermod -g scm #{preferences[:user]} }
|
109
|
+
else
|
110
|
+
say "Skipping user creation (I assume you must have done this already then)"
|
111
|
+
end
|
112
|
+
|
113
|
+
say "Saving and setting up those selected options"
|
114
|
+
|
115
|
+
scm = %x{ which #{ preferences[:scm][:binary] } }
|
116
|
+
xinetd_service = <<-EOF
|
117
|
+
service #{ preferences[:scm][:binary] }
|
118
|
+
{
|
119
|
+
socket_type = stream
|
120
|
+
protocol = tcp
|
121
|
+
user = scm
|
122
|
+
wait = no
|
123
|
+
disable = no
|
124
|
+
server = #{ scm.chomp }
|
125
|
+
server_args = -i --root=/var/src
|
126
|
+
port = 3690
|
127
|
+
}
|
128
|
+
EOF
|
129
|
+
File.open("/etc/xinetd.d/#{preferences[:scm][:binary]}", 'w+') do |file|
|
130
|
+
file.puts xinetd_service
|
131
|
+
end
|
132
|
+
%x{ /etc/init.d/xinetd restart #{SILENCER} }
|
133
|
+
|
134
|
+
File.open(CHEESE_CONF, "w+", 0640) {|f| YAML.dump(preferences, f)}
|
135
|
+
|
136
|
+
say "Done"
|
137
|
+
|
138
|
+
rescue Errno::EPERM
|
139
|
+
puts "This script must be run with root privileges"
|
140
|
+
exit
|
141
|
+
rescue Errno::EACCES
|
142
|
+
puts "This script must be run with root privileges"
|
143
|
+
exit
|
144
|
+
end
|
data/lib/controller.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
# controller.rb
|
2
|
+
#
|
3
|
+
# Validate the options given and delegate jobs to the other classes
|
4
|
+
#
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'rubygems'
|
8
|
+
rescue LoadError
|
9
|
+
# no rubygems to load, so we fail silently
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'yaml'
|
13
|
+
require 'process'
|
14
|
+
require 'verbose'
|
15
|
+
require 'database/mysql'
|
16
|
+
require 'database/postgresql'
|
17
|
+
require 'scm/subversion'
|
18
|
+
require 'web/domain'
|
19
|
+
require 'web/mongrel'
|
20
|
+
require 'web/nginx'
|
21
|
+
require 'web/proxy'
|
22
|
+
require 'web/virtual_host'
|
23
|
+
|
24
|
+
module Cheese
|
25
|
+
class Controller
|
26
|
+
|
27
|
+
CHEESE_CONF = "/etc/cheese/cheese.conf"
|
28
|
+
NGINX_CONF = '/etc/nginx/nginx.conf'
|
29
|
+
|
30
|
+
def initialize(options={})
|
31
|
+
# Check cheese --setup has been run
|
32
|
+
unless File.exists?(CHEESE_CONF)
|
33
|
+
puts "Please run cheese --setup before using any cheese actions."
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
# Need a better way of doing this, so it works in all files
|
37
|
+
Cheese::Verbose.dry_run = options.delete(:dry_run)
|
38
|
+
Cheese::Verbose.loud = options.delete(:verbose)
|
39
|
+
@preferences = YAML.load(File.open(CHEESE_CONF))
|
40
|
+
@options = options
|
41
|
+
run_actions
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Run all actions necessary
|
47
|
+
def run_actions
|
48
|
+
# List vhosts
|
49
|
+
if ( @options[:actions].include?(:web_server) || @options[:actions].include?(:list_vhosts))
|
50
|
+
@nginx = Cheese::Nginx::Config.new(NGINX_CONF)
|
51
|
+
end
|
52
|
+
|
53
|
+
if @options[:actions].include? :list_vhosts
|
54
|
+
Cheese::Verbose.log_task("listing vhosts in nginx.conf") do
|
55
|
+
begin
|
56
|
+
@nginx.domains.each_with_index {|domain, i| puts "#{i}. #{domain.vhost.domain} - #{domain.proxy.ports.size} threads" }
|
57
|
+
rescue Exception => e
|
58
|
+
puts "Error listing vhosts:"
|
59
|
+
puts e.message
|
60
|
+
puts "exiting"
|
61
|
+
exit
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Nginx
|
67
|
+
if @options[:actions].include? :web_server
|
68
|
+
begin
|
69
|
+
Cheese::Verbose.log_task("back up nginx.conf") do
|
70
|
+
FileUtils.cp(NGINX_CONF, NGINX_CONF + ".old") if File.exists?(NGINX_CONF)
|
71
|
+
end
|
72
|
+
rescue Errno::EPERM
|
73
|
+
puts "This script must be run with root privileges"
|
74
|
+
exit
|
75
|
+
rescue Errno::EACCES
|
76
|
+
puts "This script must be run with root privileges"
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
|
80
|
+
case @options[:remove]
|
81
|
+
when false
|
82
|
+
Cheese::Verbose.log_task("create nginx vhost (#{@options[:name]})") do
|
83
|
+
@added_domain = @nginx.add @options
|
84
|
+
end
|
85
|
+
when true
|
86
|
+
Cheese::Verbose.log_task("remove nginx vhost (#{@options[:name]})") do
|
87
|
+
@removed_domain = @nginx.remove @options
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
@nginx.save
|
92
|
+
@nginx.restart
|
93
|
+
end
|
94
|
+
|
95
|
+
# Subversion
|
96
|
+
if @options[:actions].include? :scm
|
97
|
+
if @options[:remove]
|
98
|
+
Cheese::Verbose.log_task("remove subversion repository (#{@options[:name]})") do
|
99
|
+
svn = Cheese::Subversion::Repository.remove @options[:name]
|
100
|
+
end
|
101
|
+
else
|
102
|
+
Cheese::Verbose.log_task("add subversion repository (#{@options[:name]})") do
|
103
|
+
svn = Cheese::Subversion::Repository.create @options[:name]
|
104
|
+
end
|
105
|
+
Cheese::Verbose.log_task("set the default permissions on the repository") do
|
106
|
+
user, pass = @preferences[:scm_user].chomp, @preferences[:scm_pass].chomp
|
107
|
+
Cheese::Subversion::Repository.set_permissions( :name => @options[:name],
|
108
|
+
:access => {:anon => :none, :auth => :write},
|
109
|
+
:users => {:user => user, :pass => pass})
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Mongrel cluster file
|
115
|
+
if @options[:actions].include? :app_server
|
116
|
+
if @options[:remove]
|
117
|
+
Cheese::Verbose.log_task("remove the mongrel_cluster file") do
|
118
|
+
Cheese::Mongrel.remove(@removed_domain)
|
119
|
+
end
|
120
|
+
else
|
121
|
+
Cheese::Verbose.log_task("create the mongrel_cluster file") do
|
122
|
+
Cheese::Mongrel.create(@added_domain.host, @added_domain.proxy.ports)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Database
|
128
|
+
if @options[:actions].include? :database
|
129
|
+
if @options[:remove]
|
130
|
+
Cheese::Verbose.log_task("drop a database") do
|
131
|
+
Cheese::Verbose.log_task(" requiring lib/#{@options[:database_type]}")
|
132
|
+
require "database/#{@options[:database_type]}"
|
133
|
+
Cheese::Verbose.log_task(" creating class #{@options[:database_type].to_s.capitalize}")
|
134
|
+
db_klass = Cheese.const_get(@options[:database_type].to_s.capitalize.intern)
|
135
|
+
Cheese::Verbose.log_task(" executing remove command on #{@options[:name]}")
|
136
|
+
db_klass.remove(@options[:name])
|
137
|
+
end
|
138
|
+
else
|
139
|
+
Cheese::Verbose.log_task("create a database") do
|
140
|
+
Cheese::Verbose.log_task(" requiring lib/#{@options[:database_type]}")
|
141
|
+
require "database/#{@options[:database_type]}"
|
142
|
+
Cheese::Verbose.log_task(" creating class #{@options[:database_type].to_s.capitalize}")
|
143
|
+
db_klass = Cheese.const_get(@options[:database_type].to_s.capitalize.intern)
|
144
|
+
Cheese::Verbose.log_task(" executing create command")
|
145
|
+
db_klass.create(@options[:name])
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# mysql.rb
|
2
|
+
#
|
3
|
+
# Create and remove mysql databases
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'highline/import'
|
7
|
+
require 'tempfile'
|
8
|
+
|
9
|
+
module Cheese
|
10
|
+
|
11
|
+
class Mysql
|
12
|
+
|
13
|
+
# create a user and new db
|
14
|
+
def self.create(name)
|
15
|
+
puts "What is the root password for MySQL?"
|
16
|
+
pass = ask("=> "){|prompt| prompt.echo = "*" }.chomp
|
17
|
+
while true
|
18
|
+
puts "What is the database password you want for #{name.gsub(".", "_")}?"
|
19
|
+
dbpass1 = ask("=> "){|prompt| prompt.echo = "*" }.chomp
|
20
|
+
puts "Confirm that"
|
21
|
+
dbpass2 = ask("=> "){|prompt| prompt.echo = "*" }.chomp
|
22
|
+
if dbpass1.chomp == dbpass2.chomp
|
23
|
+
break
|
24
|
+
else
|
25
|
+
puts "Those passwords didn't match, try again:"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
puts "adding db and user"
|
29
|
+
tmpfile = Tempfile.new("mysql-create")
|
30
|
+
tmpfile.puts "CREATE DATABASE #{name.gsub(".", "_")};"
|
31
|
+
tmpfile.puts "GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP ON *.* TO '#{name.gsub(".", "_")}'@'localhost' IDENTIFIED BY '#{dbpass1}';"
|
32
|
+
tmpfile.close
|
33
|
+
%x{ mysql -u root -p#{pass} mysql < #{tmpfile.path} }
|
34
|
+
end
|
35
|
+
|
36
|
+
# remove a user and new db
|
37
|
+
def self.remove(name)
|
38
|
+
puts "What is the root password for MySQL?"
|
39
|
+
pass = ask("=> "){|prompt| prompt.echo = "*" }
|
40
|
+
|
41
|
+
tmpfile = Tempfile.new("mysql-drop")
|
42
|
+
tmpfile.puts "DROP DATABASE #{name.gsub(".", "_")};"
|
43
|
+
tmpfile.puts "DROP USER '#{name.gsub(".", "_")}'@'localhost';"
|
44
|
+
tmpfile.close
|
45
|
+
%x{ mysql -u root -p#{pass} < #{tmpfile.path} }
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# postgresql.rb
|
2
|
+
#
|
3
|
+
# Create and remove postgresql databases
|
4
|
+
#
|
5
|
+
|
6
|
+
module Cheese
|
7
|
+
|
8
|
+
class Postgresql
|
9
|
+
SILENCER = ">/dev/null 2>/dev/null"
|
10
|
+
|
11
|
+
# create a user and new db
|
12
|
+
def self.create(name)
|
13
|
+
while
|
14
|
+
puts "We need to set a database password for #{name.gsub(".", "_")}."
|
15
|
+
end
|
16
|
+
|
17
|
+
%x{ su -c "createuser -S -D -R #{name.gsub(".", "_")} -W" postgres }
|
18
|
+
%x{ su -c "createdb #{name.gsub(".", "_")}" postgres #{SILENCER} }
|
19
|
+
end
|
20
|
+
|
21
|
+
# remove a user and new db
|
22
|
+
def self.remove(name)
|
23
|
+
%x{ su -c "dropuser #{name.gsub(".", "_")}" postgres #{SILENCER} }
|
24
|
+
%x{ su -c "dropdb #{name.gsub(".", "_")}" postgres #{SILENCER} }
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/lib/process.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# process.rb
|
2
|
+
#
|
3
|
+
# Run a process as a different user
|
4
|
+
# Thanks to the "Ruby Cookbook" for this one
|
5
|
+
|
6
|
+
module Process
|
7
|
+
def as_uid(uid=0)
|
8
|
+
old_euid, old_uid = Process.euid, Process.uid
|
9
|
+
Process.euid, Process.uid = uid, uid
|
10
|
+
begin
|
11
|
+
yield
|
12
|
+
ensure
|
13
|
+
Process.euid, Process.uid = old_euid, old_uid
|
14
|
+
end
|
15
|
+
end
|
16
|
+
module_function(:as_uid)
|
17
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# subversion.rb
|
2
|
+
#
|
3
|
+
# Acts as a subversion repository
|
4
|
+
# This could be done with the svn ruby bindings, but it's simple
|
5
|
+
# stuff that for now will be fine using system
|
6
|
+
|
7
|
+
require 'fileutils'
|
8
|
+
|
9
|
+
module Cheese
|
10
|
+
module Subversion
|
11
|
+
class Repository
|
12
|
+
DEFAULT_ACCESS = { :anon => :none, :auth => :write }
|
13
|
+
SILENCER = ">/dev/null 2>/dev/null"
|
14
|
+
def self.create(name)
|
15
|
+
# Create the repo
|
16
|
+
%x{ svnadmin create /var/src/#{name} #{SILENCER} }
|
17
|
+
%x{ chown svn:svn /var/src/#{name} #{SILENCER} }
|
18
|
+
%x{ chmod -R 774 /var/src/#{name} #{SILENCER} }
|
19
|
+
|
20
|
+
folder = self.create_tmp_structure
|
21
|
+
self.import(name, folder)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Create the default file structure
|
25
|
+
def self.create_tmp_structure
|
26
|
+
FileUtils.mkdir_p( File.join(Dir.tmpdir, "/svn_structure/branches")) unless File.exists? File.join(Dir.tmpdir, "/svn_structure/branches")
|
27
|
+
FileUtils.mkdir( File.join(Dir.tmpdir, "/svn_structure/trunk")) unless File.exists? File.join(Dir.tmpdir, "/svn_structure/trunk")
|
28
|
+
FileUtils.mkdir( File.join(Dir.tmpdir, "/svn_structure/tags")) unless File.exists? File.join(Dir.tmpdir, "/svn_structure/tags")
|
29
|
+
File.join(Dir.tmpdir, "/svn_structure")
|
30
|
+
end
|
31
|
+
|
32
|
+
# Set the permissions on a repository, defaults are anon-access: none and auth-access: write.
|
33
|
+
# These options can be changed with options
|
34
|
+
#
|
35
|
+
# ==== Options
|
36
|
+
# +options+
|
37
|
+
# +access+: A Hash containing :anon and :auth which can be set to either :none, :read or :write
|
38
|
+
# +users+: An Array or Hash containing user/pass combinations
|
39
|
+
# e.g. { :name => "Jamie", :password => "Chees3y" }
|
40
|
+
def self.set_permissions(options={})
|
41
|
+
access = options.delete(:access) || DEFAULT_ACCESS
|
42
|
+
name = options.delete(:name)
|
43
|
+
File.open("/var/src/#{name}/conf/svnserve.conf", "w+") do |file|
|
44
|
+
file.puts "# Generated on #{Time.now.to_s}"
|
45
|
+
file.puts "[general]"
|
46
|
+
file.puts "anon-access = #{access[:anon].to_s}"
|
47
|
+
file.puts "auth-access = #{access[:auth].to_s}"
|
48
|
+
file.puts "password-db = passwd"
|
49
|
+
end
|
50
|
+
File.open("/var/src/#{name}/conf/passwd", "w+") do |file|
|
51
|
+
file.puts "[users]"
|
52
|
+
if options[:users].is_a?Array
|
53
|
+
options[:users].each do |user, pass|
|
54
|
+
file.puts "#{user} = #{pass}"
|
55
|
+
end
|
56
|
+
elsif options[:users].is_a?Hash
|
57
|
+
file.puts "#{options[:users][:user]} = #{options[:users][:pass]}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.remove(name)
|
63
|
+
FileUtils.rm_rf("/var/src/#{name}")
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.import(name, files=[], extra_path="")
|
67
|
+
files.each do |file|
|
68
|
+
%x{ svn import #{file} file:///var/src/#{name}#{extra_path} -m "import of #{file}" }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.remove_file(name, files=[])
|
73
|
+
if files.is_a?Array
|
74
|
+
files.each do |file|
|
75
|
+
%x{ svn delete #{file} file:///var/src/#{name} -m "deleted #{file}" #{SILENCER} } if File.exists? file
|
76
|
+
end
|
77
|
+
else
|
78
|
+
%x{ svn delete #{files} file:///var/src/#{name} -m "deleted #{files} #{SILENCER}" } if File.exists? files
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|