promotion 1.4.7 → 2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +5 -0
- data/README +7 -11
- data/lib/promotion/application.rb +7 -6
- data/lib/promotion/generator/crontab.rb +31 -57
- data/lib/promotion/generator/newsyslog.rb +20 -33
- data/lib/promotion/generator/profile.rb +12 -22
- data/lib/promotion/generator/rcconf.rb +14 -27
- data/lib/promotion/generator/sudoers.rb +12 -40
- data/lib/promotion/generator.rb +6 -32
- metadata +43 -75
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 108a5dc05ae4bb258ba20b5b00a77707a4031176
|
4
|
+
data.tar.gz: c5a25b54336e9b6f5247cbda54812da242c3f7a9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9497b89f2b3b4805d7db3f60fa64ee439475ad7e1f95b229876ecc94d3a120b02dda71c1a10fcfadd231b8620e5dc5af9c07634d23ec3b67dfdee68df74e6867
|
7
|
+
data.tar.gz: baa82d045a06c70f4118678baab14f6c6170f2209c76b441f6c2ae66dbaf91af90f57aebf578d12de1140b71740c162066a4120a7cc990fa7964d589301032fa
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
== Version 2.0
|
2
|
+
- Promotion no longer changes the system-wide files, such as /etc/rc.conf.local. Instead it just recommends what should be changed.
|
3
|
+
- Upgraded for Ruby 2.0, since 1.8.7 is no longer supported.
|
4
|
+
- Removed the log4r dependency in favour of the standard library Logger.
|
5
|
+
|
1
6
|
== Version 1.4.7
|
2
7
|
- Preserve empty lines in config files
|
3
8
|
|
data/README
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
= Promotion
|
2
|
-
Promotion makes it possible to repeatedly deploy an application in a fast and
|
3
|
-
reliable way.
|
2
|
+
Promotion makes it possible to repeatedly deploy an application in a fast and reliable way.
|
4
3
|
|
5
4
|
== Staging
|
6
5
|
A key concept is the *staging* area. It is a folder (+/var/staging+ by default)
|
@@ -48,11 +47,10 @@ files that need to be adjusted to enable an application to startup or run proper
|
|
48
47
|
- /etc/sudoers
|
49
48
|
- /var/cron/tabs/*
|
50
49
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
This respects the need for an administrator to add custom contents (above the line).
|
50
|
+
Deployment descriptors can include recommended settings for these sensitive system files, but it
|
51
|
+
does not change them. That would be too intrusive. Instead, Promotion reads the files to check if the
|
52
|
+
recommended entries are present, and if they are not it displays a message for the admin explaining
|
53
|
+
what needs to be done.
|
56
54
|
|
57
55
|
An important aspect is that a deployment is *idempotent* - you can do it as many
|
58
56
|
times as you like and the result will be the same. This means its safe to make a
|
@@ -67,7 +65,7 @@ to suit your system's paths.
|
|
67
65
|
|
68
66
|
== Installation
|
69
67
|
*WARNING*::
|
70
|
-
Promotion will makes changes to your operating system
|
68
|
+
Promotion will makes changes to your operating system.
|
71
69
|
|
72
70
|
DO NOT USE ON A PRODUCTION SYSTEM without rigorous testing in a virtual machine first.
|
73
71
|
|
@@ -235,9 +233,7 @@ The +User+ is the user running the process, typically an unprivileged user named
|
|
235
233
|
Leave blank to run the startup script as root (eg. as when dropping privileges).
|
236
234
|
|
237
235
|
=== +Crontab+
|
238
|
-
Cron jobs are often needed for an application to run smoothly.
|
239
|
-
you can specify each job as part of the deployment descriptor and let promotion set up the crontab
|
240
|
-
automatically.
|
236
|
+
Cron jobs are often needed for an application to run smoothly.
|
241
237
|
<Crontab>
|
242
238
|
<Schedule User="root" Hour="2" Minute="7"
|
243
239
|
Comment="Backup the entire database at 2:07am each morning">
|
@@ -3,8 +3,8 @@ require 'promotion/config'
|
|
3
3
|
require 'promotion/enforcer'
|
4
4
|
require 'promotion/generator'
|
5
5
|
require 'promotion/evolver'
|
6
|
-
require '
|
7
|
-
|
6
|
+
require 'logger'
|
7
|
+
require 'time'
|
8
8
|
|
9
9
|
module Promotion # :nodoc:
|
10
10
|
|
@@ -18,10 +18,11 @@ class Application
|
|
18
18
|
# A global +$log+ logger is also created.
|
19
19
|
def initialize(appname)
|
20
20
|
@appname = appname
|
21
|
-
$log = Logger.new(
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
$log = Logger.new(Files::Log, 0) # rotate log file at 1M limit
|
22
|
+
$log.formatter = proc { |severity, datetime, progname, message|
|
23
|
+
"#{datetime.iso8601()} #{severity} #{message}\n"
|
24
|
+
}
|
25
|
+
$log.level = Logger::INFO
|
25
26
|
end
|
26
27
|
|
27
28
|
# Promotes an application into production.
|
@@ -3,65 +3,39 @@ module Generator
|
|
3
3
|
module Crontab
|
4
4
|
|
5
5
|
# The crontab for each user must be deployed using the crontab tool
|
6
|
-
def self.
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
FileUtils.rm_f(tempFilename)
|
38
|
-
$log.info("New crontab installed for user #{user}")
|
39
|
-
}
|
40
|
-
rescue => e
|
41
|
-
$log.error("Error occurred while generating crontab\n#{e.message}" + e.backtrace.join("\n"))
|
42
|
-
exit 1
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# Generates the contents for /var/cron/tabs/{user}, containing scheduled jobs
|
47
|
-
def self.contents(user, crontablist)
|
48
|
-
contents = []
|
49
|
-
contents << "# /var/cron/tabs/#{user} - #{user}'s crontab \n#\n"
|
50
|
-
contents << "#minute hour mday month wday command\n#\n\n"
|
51
|
-
crontablist.each { |sched|
|
52
|
-
if sched.attributes["Comment"]
|
53
|
-
contents << "#______________________________\n"
|
54
|
-
contents << "# #{sched.attributes["Comment"]} \n"
|
6
|
+
def self.check(specs)
|
7
|
+
schedules = {} # keyed on user, value is array of schedule elements
|
8
|
+
specs.each { |spec|
|
9
|
+
spec.elements.each("/Specification/Crontab/Schedule") { |sched|
|
10
|
+
user = sched.attributes["User"]
|
11
|
+
schedules[user] = [] unless schedules.has_key?(user)
|
12
|
+
schedules[user] << sched
|
13
|
+
}
|
14
|
+
}
|
15
|
+
schedules.each { |user, crontablist|
|
16
|
+
userCrontab = File.expand_path(user, "/var/cron/tabs/")
|
17
|
+
contents = IO.readlines(userCrontab).collect!{ |s| s.gsub(/\s/,"") }
|
18
|
+
proposals = []
|
19
|
+
crontablist.each { |sched|
|
20
|
+
minute = sched.attributes["Minute"] || "*"
|
21
|
+
hour = sched.attributes["Hour"] || "*"
|
22
|
+
mday = sched.attributes["DayOfMonth"] || "*"
|
23
|
+
month = sched.attributes["Month"] || "*"
|
24
|
+
wday = sched.attributes["DayOfWeek"] || "*"
|
25
|
+
cmd = (sched.elements["Command"].cdatas[0]).value().strip()
|
26
|
+
needed = minute + "\t" + hour + "\t" + mday + "\t" + month + "\t" + wday + "\t" + cmd
|
27
|
+
unless contents.include?(needed.gsub(/\s/,""))
|
28
|
+
if sched.attributes["Comment"]
|
29
|
+
proposals << "# #{sched.attributes["Comment"]}"
|
30
|
+
end
|
31
|
+
proposals << needed
|
32
|
+
end
|
33
|
+
}
|
34
|
+
if proposals.size > 0
|
35
|
+
header = "# min\thour\tmday\tmonth\twday\tcommand\n"
|
36
|
+
puts("\nSuggested changes to /var/cron/tabs/#{user}:", header, proposals.join("\n"), "\n")
|
55
37
|
end
|
56
|
-
minute = sched.attributes["Minute"] || "*"
|
57
|
-
hour = sched.attributes["Hour"] || "*"
|
58
|
-
mday = sched.attributes["DayOfMonth"] || "*"
|
59
|
-
month = sched.attributes["Month"] || "*"
|
60
|
-
wday = sched.attributes["DayOfWeek"] || "*"
|
61
|
-
cmd = (sched.elements["Command"].cdatas[0]).value().strip()
|
62
|
-
contents << minute + "\t" + hour + "\t" + mday + "\t" + month + "\t" + wday + "\t" + cmd + "\n"
|
63
38
|
}
|
64
|
-
return(contents.join(""))
|
65
39
|
end
|
66
40
|
|
67
41
|
end
|
@@ -3,29 +3,12 @@ module Generator
|
|
3
3
|
module Newsyslog
|
4
4
|
|
5
5
|
# Writes the Newsyslog configuration
|
6
|
-
def self.
|
7
|
-
|
8
|
-
|
9
|
-
originalContents = Promotion::Generator::original_contents_for(sym)
|
10
|
-
newContents = originalContents + Marker + "\n" + contents(specs)
|
11
|
-
Promotion::Generator::write_file_for(sym, newContents)
|
12
|
-
rescue => e
|
13
|
-
$log.error("Error occurred while generating #{sym}\n#{e.message}" + e.backtrace.join("\n"))
|
14
|
-
exit 1
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
# Generates the contents for /etc/newsyslog.conf, which controls rolling and retention of logs
|
19
|
-
def self.contents(specs)
|
20
|
-
contents = []
|
21
|
-
contents << "# This section of the file should not be edited\n"
|
22
|
-
contents << "# It was generated by the promotion application and will be overwritten\n"
|
23
|
-
contents << "# when the next promotion occurs.\n"
|
24
|
-
contents << "# The previous section will be preserved\n\n"
|
25
|
-
contents << "# Filename Owner:Group Mode Count Size When Flags Command\n"
|
6
|
+
def self.check(specs)
|
7
|
+
contents = IO.readlines("/etc/newsyslog.conf").collect!{ |s| s.gsub(/\s/,"") }
|
8
|
+
proposals = []
|
26
9
|
specs.each { |spec|
|
27
10
|
spec.elements.each("/Specification/Newsyslog") { |nsl|
|
28
|
-
|
11
|
+
needed = nsl.text() << "\t"
|
29
12
|
owner = nsl.attributes["Owner"]
|
30
13
|
group = nsl.attributes["Group"]
|
31
14
|
if owner.nil? or group.nil?
|
@@ -33,31 +16,35 @@ module Newsyslog
|
|
33
16
|
else
|
34
17
|
og = owner + ":" + group
|
35
18
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
19
|
+
needed << og << "\t"
|
20
|
+
needed << (nsl.attributes["Mode"] || "0640") << "\t"
|
21
|
+
needed << (nsl.attributes["Count"] || "*") << "\t"
|
22
|
+
needed << (nsl.attributes["Size"] || "*") << "\t"
|
40
23
|
# treat as $-format if starts with D,W,M; treat as integer if integer
|
41
24
|
# else treat as @-format if digits with a 'T'
|
42
25
|
sched = nsl.attributes["When"] || "*"
|
43
26
|
if sched[0..0] == "D" or sched[0..0] == "W" or sched[0..0] == "M"
|
44
|
-
|
27
|
+
needed << sched << "\t"
|
45
28
|
elsif sched.include?("T")
|
46
|
-
|
29
|
+
needed << "@" << sched << "\t"
|
47
30
|
else
|
48
|
-
|
31
|
+
needed << sched << "\t"
|
49
32
|
end
|
50
33
|
# We normally do not want to add rotation messages or compress
|
51
|
-
|
52
|
-
|
34
|
+
needed << "Z" if (nsl.attributes["Zip"] || "false") == "true"
|
35
|
+
needed << "B" if (nsl.attributes["Binary"] || "true") == "true"
|
53
36
|
restart = nsl.attributes["Restart"] # service to restart
|
54
37
|
if restart
|
55
|
-
|
38
|
+
needed << "\t/etc/rc.d/#{restart} restart >/dev/null 2>&1 "
|
56
39
|
end
|
57
|
-
|
40
|
+
# see if its already present - check the pattern ignoring whitespace
|
41
|
+
proposals << needed unless contents.include?(needed.gsub(/\s/,""))
|
58
42
|
}
|
59
43
|
}
|
60
|
-
|
44
|
+
if proposals.size > 0
|
45
|
+
header = "# Filename\t\tOwner:Group\tMode\tCount\tSize\tWhen\tFlags\tCommand\n"
|
46
|
+
puts("\nSuggested changes to /etc/newsyslog.conf:", header, proposals.join("\n"), "\n")
|
47
|
+
end
|
61
48
|
end
|
62
49
|
|
63
50
|
end
|
@@ -3,39 +3,29 @@ module Generator
|
|
3
3
|
module Profile
|
4
4
|
|
5
5
|
# Writes the system profile
|
6
|
-
def self.
|
7
|
-
|
8
|
-
|
9
|
-
originalContents = Promotion::Generator::original_contents_for(sym)
|
10
|
-
newContents = originalContents + Marker + "\n" + contents(specs)
|
11
|
-
Promotion::Generator::write_file_for(sym, newContents)
|
12
|
-
rescue => e
|
13
|
-
$log.error("Error occurred while generating #{sym}\n#{e.message}" + e.backtrace.join("\n"))
|
14
|
-
exit 1
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
# Generates the contents for /etc/profile, containing environment variables and aliases
|
19
|
-
def self.contents(specs)
|
20
|
-
contents = []
|
21
|
-
contents << "# This section of the file should not be edited\n"
|
22
|
-
contents << "# It was generated by the promotion application and will be overwritten\n"
|
23
|
-
contents << "# when the next promotion occurs.\n"
|
24
|
-
contents << "# The previous section will be preserved\n"
|
6
|
+
def self.check(specs)
|
7
|
+
contents = IO.readlines("/etc/profile").collect!{ |s| s.strip() }
|
8
|
+
proposals = []
|
25
9
|
specs.each { |spec|
|
26
10
|
spec.elements.each("/Specification/Environment") { |env|
|
27
11
|
env.elements.each("Variable") { |var|
|
28
12
|
t = var.cdatas.length > 0 ? var.cdatas[0].to_s() : var.text()
|
29
|
-
|
13
|
+
needed = "export #{var.attributes["Name"]}=\"#{t}\""
|
14
|
+
unless contents.include?(needed.strip())
|
15
|
+
proposals << needed
|
16
|
+
end
|
30
17
|
}
|
31
18
|
env.elements.each("Alias") { |ali|
|
32
19
|
contents << "# #{ali.attributes["Comment"]}\n" if ali.attributes["Comment"]
|
33
20
|
t = ali.cdatas.length > 0 ? ali.cdatas[0].to_s() : ali.text()
|
34
|
-
|
21
|
+
needed = "alias #{ali.attributes["Name"]}='#{t}'"
|
22
|
+
unless contents.include?(needed.strip())
|
23
|
+
proposals << needed
|
24
|
+
end
|
35
25
|
}
|
36
26
|
}
|
37
27
|
}
|
38
|
-
|
28
|
+
puts("\nSuggested changes to /etc/profile:", proposals.join("\n"), "\n") if proposals.size > 0
|
39
29
|
end
|
40
30
|
|
41
31
|
end
|
@@ -3,27 +3,10 @@ module Generator
|
|
3
3
|
module Rcconf
|
4
4
|
|
5
5
|
# Writes the system file /etc/rc.conf.local
|
6
|
-
def self.
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
newContents = originalContents + Marker + "\n" + contents(specs)
|
11
|
-
Promotion::Generator::write_file_for(sym, newContents)
|
12
|
-
rescue => e
|
13
|
-
$log.error("Error occurred while generating #{sym}\n#{e.message}" + e.backtrace.join("\n"))
|
14
|
-
exit 1
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
# Generates the contents for /etc/rc.conf.local, containing instructions for running daemons
|
19
|
-
def self.contents(specs)
|
20
|
-
contents = []
|
21
|
-
contents << "# This section of the file should not be edited\n"
|
22
|
-
contents << "# It was generated by the promotion application and will be overwritten\n"
|
23
|
-
contents << "# when the next promotion occurs.\n"
|
24
|
-
contents << "# The previous section will be preserved\n"
|
25
|
-
contents << "# ---------------------------\n"
|
26
|
-
contents << "# generated application flags\n"
|
6
|
+
def self.check(specs)
|
7
|
+
contents = IO.readlines("/etc/rc.conf.local").collect!{ |s| s.strip() }
|
8
|
+
proposals = []
|
9
|
+
# Extract package scripts and sort them by increasing priority
|
27
10
|
daemonSpecs = specs.reject {|s| s.elements["/Specification/Daemon"].nil? }
|
28
11
|
daemonSpecs.sort!() { |a, b|
|
29
12
|
ap = a.elements["/Specification/Daemon"].attributes["Priority"].to_i || 10
|
@@ -31,18 +14,22 @@ module Rcconf
|
|
31
14
|
ap <=> bp
|
32
15
|
}
|
33
16
|
pkgScripts = []
|
34
|
-
daemonSpecs.each { |spec|
|
35
|
-
spec.each_element("Daemon") { |d|
|
17
|
+
daemonSpecs.each { |spec|
|
18
|
+
spec.each_element("Daemon") { |d| # allow for multiple daemons in a spec
|
36
19
|
# Name defaults to the name of the spec, but may be overridden by the optional Name attribute
|
37
20
|
name = d.attributes["Name"] || spec.attributes["Name"]
|
38
21
|
flags = d.attributes["Flags"]
|
39
|
-
|
22
|
+
needed = "#{name}_flags=\"#{flags}\""
|
23
|
+
proposals << needed unless contents.include?(needed.strip())
|
40
24
|
pkgScripts << name
|
41
25
|
}
|
42
26
|
}
|
43
|
-
|
44
|
-
|
45
|
-
|
27
|
+
puts("\nSuggested changes to /etc/rc.conf.local:", proposals.join("\n")) if proposals.size > 0
|
28
|
+
pkgList = pkgScripts.join(" ")
|
29
|
+
unless contents.join("\n") =~ /pkg_scripts.*#{pkgList}/
|
30
|
+
puts("pkg_scripts=\"${pkg_scripts} #{pkgList}\" ")
|
31
|
+
end
|
32
|
+
puts("") if proposals.size > 0
|
46
33
|
end
|
47
34
|
|
48
35
|
end
|
@@ -3,51 +3,23 @@ module Generator
|
|
3
3
|
module Sudoers
|
4
4
|
|
5
5
|
# Writes the sudoers file after testing it with visudo
|
6
|
-
def self.
|
7
|
-
|
8
|
-
|
9
|
-
originalContents = Promotion::Generator::original_contents_for(sym)
|
10
|
-
newContents = originalContents + Marker + "\n" + contents(specs)
|
11
|
-
tempFilename = Promotion::Generator::write_file_for(sym, newContents, true)
|
12
|
-
$log.info("Checking temporary sudoers written to #{tempFilename}.")
|
13
|
-
visudoResults = `#{Files::Visudo} -c -f #{tempFilename}`
|
14
|
-
if visudoResults =~ /parsed OK/
|
15
|
-
$log.info("visudo confirms that sudoers syntax is correct.")
|
16
|
-
else
|
17
|
-
$log.error(visudoResults)
|
18
|
-
raise
|
19
|
-
end
|
20
|
-
Promotion::Generator::write_file_for("Sudoers", newContents)
|
21
|
-
FileUtils.rm_f(tempFilename)
|
22
|
-
rescue => e
|
23
|
-
$log.error("Error occurred while generating sudoers\n#{e.message}" + e.backtrace.join("\n"))
|
24
|
-
exit 1
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# Generates the contents for /etc/sudoers, containing environment variables and aliases
|
29
|
-
def self.contents(specs)
|
30
|
-
contents = []
|
31
|
-
contents << "# This section of the file should not be edited\n"
|
32
|
-
contents << "# It was generated by the promotion application and will be overwritten\n"
|
33
|
-
contents << "# when the next promotion occurs.\n"
|
34
|
-
contents << "# The previous section will be preserved\n\n"
|
35
|
-
contents << "Defaults timestamp_timeout=55\n\n"
|
36
|
-
contents << "root ALL = (ALL) ALL \n"
|
37
|
-
contents << "# people in group wheel may run all commands \n"
|
38
|
-
contents << "%wheel ALL = (ALL) ALL \n\n"
|
39
|
-
contents << "# Generated user privilege specifications \n"
|
6
|
+
def self.check(specs)
|
7
|
+
contents = IO.readlines("/etc/sudoers").collect!{ |s| s.strip() }
|
8
|
+
proposals = []
|
40
9
|
specs.each { |spec|
|
41
10
|
spec.elements.each("/Specification/Sudoers/UserPrivilege") { |priv|
|
42
|
-
|
43
|
-
|
44
|
-
|
11
|
+
needed = "%-16s" % priv.attributes["User"]
|
12
|
+
needed << " ALL = "
|
13
|
+
needed << "(#{priv.attributes["Runas"]}) " if priv.attributes["Runas"]
|
45
14
|
pwd = (priv.attributes["Password"] || "false").downcase() == "true"
|
46
|
-
|
47
|
-
|
15
|
+
needed << (pwd ? " " : "NOPASSWD: ")
|
16
|
+
needed << "#{priv.text().strip()}"
|
17
|
+
proposals << needed unless contents.include?(needed.strip())
|
48
18
|
}
|
49
19
|
}
|
50
|
-
|
20
|
+
if proposals.size > 0
|
21
|
+
puts("\nSuggested changes to /etc/sudoers:", proposals.join("\n"), "\n") if proposals.size > 0
|
22
|
+
end
|
51
23
|
end
|
52
24
|
|
53
25
|
end
|
data/lib/promotion/generator.rb
CHANGED
@@ -8,16 +8,14 @@ require 'promotion/generator/crontab'
|
|
8
8
|
module Promotion
|
9
9
|
module Generator
|
10
10
|
|
11
|
-
Marker = "#---promotion---" # add this marker to a config file to preserve the contents before it
|
12
|
-
|
13
11
|
def self.start()
|
14
|
-
$log.info("\n#{'_'*40}\
|
12
|
+
$log.info("\n#{'_'*40}\nChecking common environment files")
|
15
13
|
specs = self.gather_specs()
|
16
|
-
Profile.
|
17
|
-
Rcconf.
|
18
|
-
Newsyslog.
|
19
|
-
Sudoers.
|
20
|
-
Crontab.
|
14
|
+
Profile.check(specs)
|
15
|
+
Rcconf.check(specs)
|
16
|
+
Newsyslog.check(specs)
|
17
|
+
Sudoers.check(specs)
|
18
|
+
Crontab.check(specs)
|
21
19
|
end
|
22
20
|
|
23
21
|
# Gathers specs from all deployment descriptors in folders with the staging area
|
@@ -40,29 +38,5 @@ module Generator
|
|
40
38
|
end
|
41
39
|
end
|
42
40
|
|
43
|
-
# Write the approved contents to the system file
|
44
|
-
# or a temporary file instead if temp is true.
|
45
|
-
def self.write_file_for(sym, newContents, temp=false)
|
46
|
-
begin
|
47
|
-
filename = Files.const_get(sym) + (temp ? ".tmp" : "")
|
48
|
-
f = File.new(filename, File::WRONLY | File::CREAT | File::TRUNC)
|
49
|
-
f.puts(newContents)
|
50
|
-
$log.info("Generated #{sym} written to #{filename}.") unless temp
|
51
|
-
ensure
|
52
|
-
f.close unless f.nil? || f.closed?
|
53
|
-
end
|
54
|
-
return(filename)
|
55
|
-
end
|
56
|
-
|
57
|
-
# Returns a string of the new content for the file up to the +marker+
|
58
|
-
def self.original_contents_for(sym)
|
59
|
-
begin
|
60
|
-
originalContents = IO.readlines(Files.const_get(sym), nil)[0].split(Marker)[0]
|
61
|
-
rescue
|
62
|
-
originalContents = ""
|
63
|
-
end
|
64
|
-
return(originalContents)
|
65
|
-
end
|
66
|
-
|
67
41
|
end
|
68
42
|
end
|
metadata
CHANGED
@@ -1,59 +1,36 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: promotion
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 4
|
9
|
-
- 7
|
10
|
-
version: 1.4.7
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '2.0'
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- Richard Kernahan
|
14
8
|
autorequire:
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
type: :runtime
|
35
|
-
version_requirements: *id001
|
36
|
-
description: "\t\tThe Promotion tool is designed to make it easy and quick to deploy an application\n\
|
37
|
-
\t\tinto production. Originally built for use with OpenBSD, it can be used on an *nix\n\
|
38
|
-
\t\tsystem by adjusting a few paths (in config.rb).\n\n\
|
39
|
-
\t\tTo deploy or install an application you just need to copy a few files into place, right?\n\
|
40
|
-
\t\tWell, the folders need to be there first of course, oh and the permissions need to be set,\n\
|
41
|
-
\t\tand I guess we need the right users set up before file ownerships can be set correctly,\n\
|
42
|
-
\t\twhich means we need groups before that ... ok, so there is more to it than copying a few files.\n\n\
|
43
|
-
\t\tThere are also system-wide settings that may need to be modified to support an application,\n\
|
44
|
-
\t\tsuch as environment variables in /etc/profile, /etc/sudoers,\n\
|
45
|
-
\t\tstartup scripts in /etc/rc.conf.local, and /var/cron/tabs/* cron jobs.\n\n\
|
46
|
-
\t\tPromotion handles all of this based on an XML deployment descriptor for each application,\n\
|
47
|
-
\t\tallowing rapid, reliable redeployment with a single line command. It also manages database\n\
|
48
|
-
\t\tschema migration.\n"
|
11
|
+
date: 2013-10-30 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: "\t\tThe Promotion tool is designed to make it easy and quick to deploy
|
14
|
+
an application\n\t\tinto production. Originally built for use with OpenBSD, it can
|
15
|
+
be used on an *nix\n\t\tsystem by adjusting a few paths (in config.rb).\n\n\t\tTo
|
16
|
+
deploy or install an application you just need to copy a few files into place, right?\n\t\tWell,
|
17
|
+
the folders need to be there first of course, oh and the permissions need to be
|
18
|
+
set,\n\t\tand I guess we need the right users set up before file ownerships can
|
19
|
+
be set correctly,\n\t\twhich means we need groups before that ... ok, so there is
|
20
|
+
more to it than copying a few files.\n\n\t\tThere are also system-wide settings
|
21
|
+
that may need to be modified to support an application,\n\t\tsuch as environment
|
22
|
+
variables in /etc/profile, /etc/sudoers,\n\t\tstartup scripts in /etc/rc.conf.local,
|
23
|
+
and /var/cron/tabs/* cron jobs.\n\t\tPromotion does not modify these sensitive files,
|
24
|
+
but it does say what's missing or needs to be changed.\n\n\t\tPromotion handles
|
25
|
+
all of this based on an XML deployment descriptor for each application,\n\t\tallowing
|
26
|
+
rapid, reliable redeployment with a single line command. It also manages database\n\t\tschema
|
27
|
+
migration.\n"
|
49
28
|
email: dev.promotion@finalstep.com.au
|
50
29
|
executables: []
|
51
|
-
|
52
30
|
extensions: []
|
53
|
-
|
54
|
-
extra_rdoc_files:
|
31
|
+
extra_rdoc_files:
|
55
32
|
- README
|
56
|
-
files:
|
33
|
+
files:
|
57
34
|
- bin/promote
|
58
35
|
- bin/evolve
|
59
36
|
- bin/devolve
|
@@ -76,41 +53,32 @@ files:
|
|
76
53
|
- README
|
77
54
|
homepage: http://rubygems.org/gems/promotion
|
78
55
|
licenses: []
|
79
|
-
|
80
|
-
post_install_message: "\n\n To install the executables (promote, evolve, devolve,
|
81
|
-
\t$ sudo ruby -rubygems -e \"require
|
82
|
-
|
56
|
+
metadata: {}
|
57
|
+
post_install_message: "\n\n To install the executables (promote, evolve, devolve,
|
58
|
+
mkdeploy)\n issue the following command:\n\t$ sudo ruby -rubygems -e \"require
|
59
|
+
'promotion/install'\"\n\n "
|
60
|
+
rdoc_options:
|
83
61
|
- --title
|
84
62
|
- Promotion
|
85
63
|
- --main
|
86
64
|
- README
|
87
|
-
require_paths:
|
65
|
+
require_paths:
|
88
66
|
- lib
|
89
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
none: false
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
hash: 3
|
104
|
-
segments:
|
105
|
-
- 0
|
106
|
-
version: "0"
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
107
77
|
requirements: []
|
108
|
-
|
109
78
|
rubyforge_project: promotion
|
110
|
-
rubygems_version:
|
79
|
+
rubygems_version: 2.0.3
|
111
80
|
signing_key:
|
112
|
-
specification_version:
|
113
|
-
summary: Promotion makes it possible to repeatedly deploy an application in a fast
|
81
|
+
specification_version: 4
|
82
|
+
summary: Promotion makes it possible to repeatedly deploy an application in a fast
|
83
|
+
and reliable way.
|
114
84
|
test_files: []
|
115
|
-
|
116
|
-
has_rdoc: "true"
|