etsy-deployinator 1.0.1

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 (60) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +368 -0
  5. data/Rakefile +16 -0
  6. data/bin/deployinator-tailer.rb +78 -0
  7. data/deployinator.gemspec +31 -0
  8. data/lib/deployinator.rb +114 -0
  9. data/lib/deployinator/app.rb +204 -0
  10. data/lib/deployinator/base.rb +58 -0
  11. data/lib/deployinator/config.rb +7 -0
  12. data/lib/deployinator/controller.rb +147 -0
  13. data/lib/deployinator/helpers.rb +610 -0
  14. data/lib/deployinator/helpers/deploy.rb +68 -0
  15. data/lib/deployinator/helpers/dsh.rb +42 -0
  16. data/lib/deployinator/helpers/git.rb +348 -0
  17. data/lib/deployinator/helpers/plugin.rb +50 -0
  18. data/lib/deployinator/helpers/stack-tail.rb +32 -0
  19. data/lib/deployinator/helpers/version.rb +62 -0
  20. data/lib/deployinator/helpers/view.rb +67 -0
  21. data/lib/deployinator/logging.rb +16 -0
  22. data/lib/deployinator/plugin.rb +7 -0
  23. data/lib/deployinator/stack-tail.rb +34 -0
  24. data/lib/deployinator/static/css/diff_style.css +283 -0
  25. data/lib/deployinator/static/css/highlight.css +235 -0
  26. data/lib/deployinator/static/css/style.css +1223 -0
  27. data/lib/deployinator/static/js/flot/jquery.flot.min.js +1 -0
  28. data/lib/deployinator/static/js/flot/jquery.flot.selection.js +299 -0
  29. data/lib/deployinator/static/js/jquery-1.8.3.min.js +2 -0
  30. data/lib/deployinator/static/js/jquery-ui-1.8.24.min.js +5 -0
  31. data/lib/deployinator/static/js/jquery.timed_bar.js +36 -0
  32. data/lib/deployinator/tasks/initialize.rake +84 -0
  33. data/lib/deployinator/tasks/tests.rake +22 -0
  34. data/lib/deployinator/templates/deploys_status.mustache +42 -0
  35. data/lib/deployinator/templates/generic_single_push.mustache +64 -0
  36. data/lib/deployinator/templates/index.mustache +12 -0
  37. data/lib/deployinator/templates/layout.mustache +604 -0
  38. data/lib/deployinator/templates/log.mustache +24 -0
  39. data/lib/deployinator/templates/log_table.mustache +90 -0
  40. data/lib/deployinator/templates/messageboxes.mustache +29 -0
  41. data/lib/deployinator/templates/run_logs.mustache +15 -0
  42. data/lib/deployinator/templates/scroll_control.mustache +8 -0
  43. data/lib/deployinator/templates/stream.mustache +13 -0
  44. data/lib/deployinator/version.rb +3 -0
  45. data/lib/deployinator/views/deploys_status.rb +22 -0
  46. data/lib/deployinator/views/index.rb +14 -0
  47. data/lib/deployinator/views/layout.rb +48 -0
  48. data/lib/deployinator/views/log.rb +12 -0
  49. data/lib/deployinator/views/log_table.rb +35 -0
  50. data/lib/deployinator/views/run_logs.rb +32 -0
  51. data/templates/app.rb.erb +7 -0
  52. data/templates/config.ru.erb +10 -0
  53. data/templates/helper.rb.erb +15 -0
  54. data/templates/stack.rb.erb +17 -0
  55. data/templates/template.mustache +1 -0
  56. data/templates/view.rb.erb +7 -0
  57. data/test/unit/helpers_dsh_test.rb +40 -0
  58. data/test/unit/helpers_test.rb +77 -0
  59. data/test/unit/version_test.rb +104 -0
  60. metadata +245 -0
@@ -0,0 +1,50 @@
1
+ module Deployinator
2
+ module Helpers
3
+ module PluginHelpers
4
+ attr_accessor :plugins
5
+ @plugins = []
6
+
7
+ def register_plugins(stack)
8
+ @plugins = []
9
+ global_plugins = Deployinator.global_plugins
10
+ unless global_plugins.nil? then
11
+ Deployinator.global_plugins.each do |klass|
12
+ @plugins << Deployinator.const_get("#{klass}").new
13
+ end
14
+ end
15
+
16
+ unless Deployinator.stack_plugins.nil? || Deployinator.stack_plugins[stack].nil? then
17
+ Deployinator.stack_plugins[stack].each do |klass|
18
+ @plugins << Deployinator.const_get("#{klass}").new
19
+ end
20
+ end
21
+ end
22
+
23
+ def notify_plugins(event, state)
24
+ ret = nil
25
+ unless plugins.nil? then
26
+ @plugins.each do |plugin|
27
+ begin
28
+ new_ret = plugin.run(event, state)
29
+ if ret.nil? then
30
+ ret = new_ret
31
+ end
32
+ rescue => e
33
+ raise "Error running plugin #{plugin} with exception #{e.to_s}"
34
+ end
35
+ end
36
+ end
37
+ ret
38
+ end
39
+
40
+ def raise_event(event, extra_state = {})
41
+ state = extra_state
42
+ state[:username] = @username
43
+ state[:stack] = @stack
44
+ state[:stage] = @method
45
+ state[:timestamp] = Time.now.to_i
46
+ notify_plugins(event, state)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,32 @@
1
+ module Deployinator
2
+ module Helpers
3
+ # Public: helper methods to stack tailer
4
+ module StackTailHelpers
5
+ # This is used to make sure the front end javascript loaded matches the
6
+ # same backend code for the deployinator tailer. Increment this only if
7
+ # you make protocol changes for the tailer
8
+ #
9
+ # Version format: Stack Tailer 1.X - meme name
10
+ # History: 1.0 - All Your Base
11
+ # Introduced versions
12
+ STACK_TAIL_VERSION = "Stack Tailer 1.0 - All Your Base"
13
+
14
+ # So deployinator can get at this
15
+ def stack_tail_version
16
+ STACK_TAIL_VERSION
17
+ end
18
+
19
+ # This is so the tailer can access this method without having to send()
20
+ # the method into scope. If there is a better way to do this please let
21
+ # me know
22
+ def self.get_stack_tail_version
23
+ STACK_TAIL_VERSION
24
+ end
25
+
26
+ # Returns the websocket port for the stack tailer
27
+ def stack_tail_websocket_port
28
+ Deployinator.stack_tailer_port
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,62 @@
1
+ module Deployinator
2
+ module Helpers
3
+ module VersionHelpers
4
+ # Public: wrapper function to get the short SHA of a revision. The function
5
+ # checks retrieves the part of the string before the first dash. If the part
6
+ # is a valid default git short rev, i.e. alphanumeric and length 7 it is
7
+ # returned. For an invalid rev, nil is returned.
8
+ #
9
+ # ver - String representing the revision
10
+ #
11
+ # Returns the short SHA consisting of the alphanumerics until the first dash
12
+ # or nil for an invalid version string
13
+ def get_build(ver)
14
+ # return the short sha of the rev
15
+ the_sha = (ver || "")[/^([^-]+)/]
16
+ # check that we have a default git SHA
17
+ val = /^[a-zA-Z0-9]{7,}$/.match the_sha
18
+ val.nil? ? nil : the_sha
19
+ end
20
+ module_function :get_build
21
+
22
+ # Public: function to get the current software version running on a host
23
+ #
24
+ # host - String of the hostname to check
25
+ #
26
+ # Returns the full version of the current software running on the host
27
+ def get_version(host)
28
+ host_url = "http://#{host}/"
29
+ get_version_by_url("#{host_url}version.txt")
30
+ end
31
+ module_function :get_version
32
+
33
+ # Public: function to fetch a version string from a URL. The version string
34
+ # is validated to have a valid format. The function calls a lower level
35
+ # implementation method for actually getting the version.
36
+ #
37
+ # url - String representing where to get the version from
38
+ #
39
+ # Returns the version string or nil if the format is invalid
40
+ def get_version_by_url(url)
41
+ version = curl_get_url(url)
42
+ val = /^[a-zA-Z0-9]{7,}-[0-9]{8}-[0-9]{6}-UTC$/.match version
43
+ val.nil? ? nil : version.chomp
44
+ end
45
+ module_function :get_version_by_url
46
+
47
+ # Public: this helper function wraps the actual call to get the contents of a
48
+ # version file. This helps with reducing code duplication and also stubbing
49
+ # out the actual call for unit testing.
50
+ #
51
+ # url - String representing the complete URL to query
52
+ #
53
+ # Returns the contents of the URL resource
54
+ def curl_get_url(url)
55
+ with_timeout 2, "getting version via curl from #{url}" do
56
+ `curl -s #{url}`
57
+ end
58
+ end
59
+ module_function :curl_get_url
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,67 @@
1
+ module Deployinator
2
+ module Helpers
3
+ module ViewHelpers
4
+
5
+ def username
6
+ @username
7
+ end
8
+
9
+ def allowed_to_push_to_prod?
10
+ true
11
+ end
12
+
13
+ def groups
14
+ @groups.join(", ")
15
+ end
16
+
17
+ def my_url
18
+ "http://#{@host}"
19
+ end
20
+
21
+ # TODO: assimilate with plugin
22
+ def logout_url
23
+ raise_event(:logout_url)
24
+ end
25
+
26
+ def my_entries
27
+ log_entries(:stack => stack)
28
+ end
29
+
30
+ def log_lines
31
+ log_to_hash(:stack => stack)
32
+ end
33
+
34
+ def log_to_hash(opts={})
35
+ times = {}
36
+ last_time = 0
37
+ l = log_entries(opts).map do |ll|
38
+ fields = ll.split("|")
39
+ times[fields[1]] ||= []
40
+ times[fields[1]] << fields[0]
41
+
42
+ env = fields[1]
43
+
44
+ utc_time = Time.parse(fields[0] + "UTC")
45
+ {
46
+ :timestamp => fields[0],
47
+ :time => utc_time,
48
+ :time_secs => utc_time.to_i,
49
+ :env => env,
50
+ :who => fields[2],
51
+ :msg => hyperlink(fields[3]),
52
+ :old => fields[3] && fields[3][/old[\s:]*(\w+)/, 1],
53
+ :new => fields[3] && fields[3][/new[\s:]*(\w+)/, 1],
54
+ :stack => fields[4],
55
+ :run_log_url => (fields.length < 6) ? false : fields[5],
56
+ :diff_url => true,
57
+ :from_timestamp => utc_time.to_i - 1800, # + half hour before
58
+ :until_timestamp => utc_time.to_i + 1800, # + half hour after
59
+ :diff_method => 'diff'
60
+ }
61
+ end
62
+ times.each { |e,t| t.shift }
63
+ l
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,16 @@
1
+ module Deployinator
2
+ def self.setup_logging
3
+ if Deployinator.log_file?
4
+ $deployinator_log_handle = File.new(Deployinator.log_file, "a")
5
+ def $stdout.write(string)
6
+ $deployinator_log_handle.write string
7
+ super
8
+ end
9
+ $stdout.sync = true
10
+ $stderr.reopen($deployinator_log_handle)
11
+ puts "Logging #{Deployinator.log_file}"
12
+ end
13
+ end
14
+ end
15
+
16
+ Deployinator.setup_logging
@@ -0,0 +1,7 @@
1
+ module Deployinator
2
+ class Plugin
3
+ def run(event, state)
4
+ raise "Plugin: #{self.class} does not implement run method"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,34 @@
1
+ require "em-websocket"
2
+ require "eventmachine-tail"
3
+
4
+ module Tailer
5
+ # Extends FileTail to push data tailed to an EM::Channel. All open websockets
6
+ # subscribe to a channel for the request stack and this pushes the data to
7
+ # all of them at once.
8
+ class StackTail < EventMachine::FileTail
9
+ def initialize(filename, channel, startpos=-1)
10
+ super(filename, startpos)
11
+ @channel = channel
12
+ @buffer = BufferedTokenizer.new
13
+ end
14
+
15
+ # This method is called whenever FileTail receives and inotify event for
16
+ # the tailed file. It breaks up the data per line and pushes a line at a
17
+ # time. This is to prevent the last javascript line from being broken up
18
+ # over 2 pushes thus breaking the eval on the front end.
19
+ def receive_data(data)
20
+ @buffer.extract(data).each do |line|
21
+ @channel.push line
22
+ end
23
+ end
24
+ end
25
+
26
+ # Checks if stack log symlink exists and creates Tailer for it
27
+ def self.stack_tail(stack, channel, channel_count)
28
+ if Deployinator.get_stacks.include?(stack)
29
+ filename = "#{Deployinator::Helpers::RUN_LOG_PATH}current-#{stack}"
30
+ start_pos = (channel_count == 0) ? 0 : -1
31
+ File.exists?(filename) ? StackTail.new(filename, channel, start_pos) : false
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,283 @@
1
+
2
+ .ef0,.f0 { color: #000000; } .eb0,.b0 { background-color: #000000; }
3
+ .ef1,.f1 { color: #AA0000; } .eb1,.b1 { background-color: #AA0000; }
4
+ .ef2,.f2 { color: #00AA00; } .eb2,.b2 { background-color: #00AA00; }
5
+ .ef3,.f3 { color: #AA5500; } .eb3,.b3 { background-color: #AA5500; }
6
+ .ef4,.f4 { color: #0000AA; } .eb4,.b4 { background-color: #0000AA; }
7
+ .ef5,.f5 { color: #AA00AA; } .eb5,.b5 { background-color: #AA00AA; }
8
+ .ef6,.f6 { color: #00AAAA; } .eb6,.b6 { background-color: #00AAAA; }
9
+ .ef7,.f7 { color: #AAAAAA; } .eb7,.b7 { background-color: #AAAAAA; }
10
+ .ef8, .f0 > .bold,.bold > .f0 { color: #555555; font-weight: normal; }
11
+ .ef9, .f1 > .bold,.bold > .f1 { color: #FF5555; font-weight: normal; }
12
+ .ef10,.f2 > .bold,.bold > .f2 { color: #55FF55; font-weight: normal; }
13
+ .ef11,.f3 > .bold,.bold > .f3 { color: #FFFF55; font-weight: normal; }
14
+ .ef12,.f4 > .bold,.bold > .f4 { color: #5555FF; font-weight: normal; }
15
+ .ef13,.f5 > .bold,.bold > .f5 { color: #FF55FF; font-weight: normal; }
16
+ .ef14,.f6 > .bold,.bold > .f6 { color: #55FFFF; font-weight: normal; }
17
+ .ef15,.f7 > .bold,.bold > .f7 { color: #FFFFFF; font-weight: normal; }
18
+ .eb8 { background-color: #555555; }
19
+ .eb9 { background-color: #FF5555; }
20
+ .eb10 { background-color: #55FF55; }
21
+ .eb11 { background-color: #FFFF55; }
22
+ .eb12 { background-color: #5555FF; }
23
+ .eb13 { background-color: #FF55FF; }
24
+ .eb14 { background-color: #55FFFF; }
25
+ .eb15 { background-color: #FFFFFF; }
26
+ .ef16 { color: #000000; } .eb16 { background-color: #000000; }
27
+ .ef17 { color: #00005f; } .eb17 { background-color: #00005f; }
28
+ .ef18 { color: #000087; } .eb18 { background-color: #000087; }
29
+ .ef19 { color: #0000af; } .eb19 { background-color: #0000af; }
30
+ .ef20 { color: #0000d7; } .eb20 { background-color: #0000d7; }
31
+ .ef21 { color: #0000ff; } .eb21 { background-color: #0000ff; }
32
+ .ef22 { color: #005f00; } .eb22 { background-color: #005f00; }
33
+ .ef23 { color: #005f5f; } .eb23 { background-color: #005f5f; }
34
+ .ef24 { color: #005f87; } .eb24 { background-color: #005f87; }
35
+ .ef25 { color: #005faf; } .eb25 { background-color: #005faf; }
36
+ .ef26 { color: #005fd7; } .eb26 { background-color: #005fd7; }
37
+ .ef27 { color: #005fff; } .eb27 { background-color: #005fff; }
38
+ .ef28 { color: #008700; } .eb28 { background-color: #008700; }
39
+ .ef29 { color: #00875f; } .eb29 { background-color: #00875f; }
40
+ .ef30 { color: #008787; } .eb30 { background-color: #008787; }
41
+ .ef31 { color: #0087af; } .eb31 { background-color: #0087af; }
42
+ .ef32 { color: #0087d7; } .eb32 { background-color: #0087d7; }
43
+ .ef33 { color: #0087ff; } .eb33 { background-color: #0087ff; }
44
+ .ef34 { color: #00af00; } .eb34 { background-color: #00af00; }
45
+ .ef35 { color: #00af5f; } .eb35 { background-color: #00af5f; }
46
+ .ef36 { color: #00af87; } .eb36 { background-color: #00af87; }
47
+ .ef37 { color: #00afaf; } .eb37 { background-color: #00afaf; }
48
+ .ef38 { color: #00afd7; } .eb38 { background-color: #00afd7; }
49
+ .ef39 { color: #00afff; } .eb39 { background-color: #00afff; }
50
+ .ef40 { color: #00d700; } .eb40 { background-color: #00d700; }
51
+ .ef41 { color: #00d75f; } .eb41 { background-color: #00d75f; }
52
+ .ef42 { color: #00d787; } .eb42 { background-color: #00d787; }
53
+ .ef43 { color: #00d7af; } .eb43 { background-color: #00d7af; }
54
+ .ef44 { color: #00d7d7; } .eb44 { background-color: #00d7d7; }
55
+ .ef45 { color: #00d7ff; } .eb45 { background-color: #00d7ff; }
56
+ .ef46 { color: #00ff00; } .eb46 { background-color: #00ff00; }
57
+ .ef47 { color: #00ff5f; } .eb47 { background-color: #00ff5f; }
58
+ .ef48 { color: #00ff87; } .eb48 { background-color: #00ff87; }
59
+ .ef49 { color: #00ffaf; } .eb49 { background-color: #00ffaf; }
60
+ .ef50 { color: #00ffd7; } .eb50 { background-color: #00ffd7; }
61
+ .ef51 { color: #00ffff; } .eb51 { background-color: #00ffff; }
62
+ .ef52 { color: #5f0000; } .eb52 { background-color: #5f0000; }
63
+ .ef53 { color: #5f005f; } .eb53 { background-color: #5f005f; }
64
+ .ef54 { color: #5f0087; } .eb54 { background-color: #5f0087; }
65
+ .ef55 { color: #5f00af; } .eb55 { background-color: #5f00af; }
66
+ .ef56 { color: #5f00d7; } .eb56 { background-color: #5f00d7; }
67
+ .ef57 { color: #5f00ff; } .eb57 { background-color: #5f00ff; }
68
+ .ef58 { color: #5f5f00; } .eb58 { background-color: #5f5f00; }
69
+ .ef59 { color: #5f5f5f; } .eb59 { background-color: #5f5f5f; }
70
+ .ef60 { color: #5f5f87; } .eb60 { background-color: #5f5f87; }
71
+ .ef61 { color: #5f5faf; } .eb61 { background-color: #5f5faf; }
72
+ .ef62 { color: #5f5fd7; } .eb62 { background-color: #5f5fd7; }
73
+ .ef63 { color: #5f5fff; } .eb63 { background-color: #5f5fff; }
74
+ .ef64 { color: #5f8700; } .eb64 { background-color: #5f8700; }
75
+ .ef65 { color: #5f875f; } .eb65 { background-color: #5f875f; }
76
+ .ef66 { color: #5f8787; } .eb66 { background-color: #5f8787; }
77
+ .ef67 { color: #5f87af; } .eb67 { background-color: #5f87af; }
78
+ .ef68 { color: #5f87d7; } .eb68 { background-color: #5f87d7; }
79
+ .ef69 { color: #5f87ff; } .eb69 { background-color: #5f87ff; }
80
+ .ef70 { color: #5faf00; } .eb70 { background-color: #5faf00; }
81
+ .ef71 { color: #5faf5f; } .eb71 { background-color: #5faf5f; }
82
+ .ef72 { color: #5faf87; } .eb72 { background-color: #5faf87; }
83
+ .ef73 { color: #5fafaf; } .eb73 { background-color: #5fafaf; }
84
+ .ef74 { color: #5fafd7; } .eb74 { background-color: #5fafd7; }
85
+ .ef75 { color: #5fafff; } .eb75 { background-color: #5fafff; }
86
+ .ef76 { color: #5fd700; } .eb76 { background-color: #5fd700; }
87
+ .ef77 { color: #5fd75f; } .eb77 { background-color: #5fd75f; }
88
+ .ef78 { color: #5fd787; } .eb78 { background-color: #5fd787; }
89
+ .ef79 { color: #5fd7af; } .eb79 { background-color: #5fd7af; }
90
+ .ef80 { color: #5fd7d7; } .eb80 { background-color: #5fd7d7; }
91
+ .ef81 { color: #5fd7ff; } .eb81 { background-color: #5fd7ff; }
92
+ .ef82 { color: #5fff00; } .eb82 { background-color: #5fff00; }
93
+ .ef83 { color: #5fff5f; } .eb83 { background-color: #5fff5f; }
94
+ .ef84 { color: #5fff87; } .eb84 { background-color: #5fff87; }
95
+ .ef85 { color: #5fffaf; } .eb85 { background-color: #5fffaf; }
96
+ .ef86 { color: #5fffd7; } .eb86 { background-color: #5fffd7; }
97
+ .ef87 { color: #5fffff; } .eb87 { background-color: #5fffff; }
98
+ .ef88 { color: #870000; } .eb88 { background-color: #870000; }
99
+ .ef89 { color: #87005f; } .eb89 { background-color: #87005f; }
100
+ .ef90 { color: #870087; } .eb90 { background-color: #870087; }
101
+ .ef91 { color: #8700af; } .eb91 { background-color: #8700af; }
102
+ .ef92 { color: #8700d7; } .eb92 { background-color: #8700d7; }
103
+ .ef93 { color: #8700ff; } .eb93 { background-color: #8700ff; }
104
+ .ef94 { color: #875f00; } .eb94 { background-color: #875f00; }
105
+ .ef95 { color: #875f5f; } .eb95 { background-color: #875f5f; }
106
+ .ef96 { color: #875f87; } .eb96 { background-color: #875f87; }
107
+ .ef97 { color: #875faf; } .eb97 { background-color: #875faf; }
108
+ .ef98 { color: #875fd7; } .eb98 { background-color: #875fd7; }
109
+ .ef99 { color: #875fff; } .eb99 { background-color: #875fff; }
110
+ .ef100 { color: #878700; } .eb100 { background-color: #878700; }
111
+ .ef101 { color: #87875f; } .eb101 { background-color: #87875f; }
112
+ .ef102 { color: #878787; } .eb102 { background-color: #878787; }
113
+ .ef103 { color: #8787af; } .eb103 { background-color: #8787af; }
114
+ .ef104 { color: #8787d7; } .eb104 { background-color: #8787d7; }
115
+ .ef105 { color: #8787ff; } .eb105 { background-color: #8787ff; }
116
+ .ef106 { color: #87af00; } .eb106 { background-color: #87af00; }
117
+ .ef107 { color: #87af5f; } .eb107 { background-color: #87af5f; }
118
+ .ef108 { color: #87af87; } .eb108 { background-color: #87af87; }
119
+ .ef109 { color: #87afaf; } .eb109 { background-color: #87afaf; }
120
+ .ef110 { color: #87afd7; } .eb110 { background-color: #87afd7; }
121
+ .ef111 { color: #87afff; } .eb111 { background-color: #87afff; }
122
+ .ef112 { color: #87d700; } .eb112 { background-color: #87d700; }
123
+ .ef113 { color: #87d75f; } .eb113 { background-color: #87d75f; }
124
+ .ef114 { color: #87d787; } .eb114 { background-color: #87d787; }
125
+ .ef115 { color: #87d7af; } .eb115 { background-color: #87d7af; }
126
+ .ef116 { color: #87d7d7; } .eb116 { background-color: #87d7d7; }
127
+ .ef117 { color: #87d7ff; } .eb117 { background-color: #87d7ff; }
128
+ .ef118 { color: #87ff00; } .eb118 { background-color: #87ff00; }
129
+ .ef119 { color: #87ff5f; } .eb119 { background-color: #87ff5f; }
130
+ .ef120 { color: #87ff87; } .eb120 { background-color: #87ff87; }
131
+ .ef121 { color: #87ffaf; } .eb121 { background-color: #87ffaf; }
132
+ .ef122 { color: #87ffd7; } .eb122 { background-color: #87ffd7; }
133
+ .ef123 { color: #87ffff; } .eb123 { background-color: #87ffff; }
134
+ .ef124 { color: #af0000; } .eb124 { background-color: #af0000; }
135
+ .ef125 { color: #af005f; } .eb125 { background-color: #af005f; }
136
+ .ef126 { color: #af0087; } .eb126 { background-color: #af0087; }
137
+ .ef127 { color: #af00af; } .eb127 { background-color: #af00af; }
138
+ .ef128 { color: #af00d7; } .eb128 { background-color: #af00d7; }
139
+ .ef129 { color: #af00ff; } .eb129 { background-color: #af00ff; }
140
+ .ef130 { color: #af5f00; } .eb130 { background-color: #af5f00; }
141
+ .ef131 { color: #af5f5f; } .eb131 { background-color: #af5f5f; }
142
+ .ef132 { color: #af5f87; } .eb132 { background-color: #af5f87; }
143
+ .ef133 { color: #af5faf; } .eb133 { background-color: #af5faf; }
144
+ .ef134 { color: #af5fd7; } .eb134 { background-color: #af5fd7; }
145
+ .ef135 { color: #af5fff; } .eb135 { background-color: #af5fff; }
146
+ .ef136 { color: #af8700; } .eb136 { background-color: #af8700; }
147
+ .ef137 { color: #af875f; } .eb137 { background-color: #af875f; }
148
+ .ef138 { color: #af8787; } .eb138 { background-color: #af8787; }
149
+ .ef139 { color: #af87af; } .eb139 { background-color: #af87af; }
150
+ .ef140 { color: #af87d7; } .eb140 { background-color: #af87d7; }
151
+ .ef141 { color: #af87ff; } .eb141 { background-color: #af87ff; }
152
+ .ef142 { color: #afaf00; } .eb142 { background-color: #afaf00; }
153
+ .ef143 { color: #afaf5f; } .eb143 { background-color: #afaf5f; }
154
+ .ef144 { color: #afaf87; } .eb144 { background-color: #afaf87; }
155
+ .ef145 { color: #afafaf; } .eb145 { background-color: #afafaf; }
156
+ .ef146 { color: #afafd7; } .eb146 { background-color: #afafd7; }
157
+ .ef147 { color: #afafff; } .eb147 { background-color: #afafff; }
158
+ .ef148 { color: #afd700; } .eb148 { background-color: #afd700; }
159
+ .ef149 { color: #afd75f; } .eb149 { background-color: #afd75f; }
160
+ .ef150 { color: #afd787; } .eb150 { background-color: #afd787; }
161
+ .ef151 { color: #afd7af; } .eb151 { background-color: #afd7af; }
162
+ .ef152 { color: #afd7d7; } .eb152 { background-color: #afd7d7; }
163
+ .ef153 { color: #afd7ff; } .eb153 { background-color: #afd7ff; }
164
+ .ef154 { color: #afff00; } .eb154 { background-color: #afff00; }
165
+ .ef155 { color: #afff5f; } .eb155 { background-color: #afff5f; }
166
+ .ef156 { color: #afff87; } .eb156 { background-color: #afff87; }
167
+ .ef157 { color: #afffaf; } .eb157 { background-color: #afffaf; }
168
+ .ef158 { color: #afffd7; } .eb158 { background-color: #afffd7; }
169
+ .ef159 { color: #afffff; } .eb159 { background-color: #afffff; }
170
+ .ef160 { color: #d70000; } .eb160 { background-color: #d70000; }
171
+ .ef161 { color: #d7005f; } .eb161 { background-color: #d7005f; }
172
+ .ef162 { color: #d70087; } .eb162 { background-color: #d70087; }
173
+ .ef163 { color: #d700af; } .eb163 { background-color: #d700af; }
174
+ .ef164 { color: #d700d7; } .eb164 { background-color: #d700d7; }
175
+ .ef165 { color: #d700ff; } .eb165 { background-color: #d700ff; }
176
+ .ef166 { color: #d75f00; } .eb166 { background-color: #d75f00; }
177
+ .ef167 { color: #d75f5f; } .eb167 { background-color: #d75f5f; }
178
+ .ef168 { color: #d75f87; } .eb168 { background-color: #d75f87; }
179
+ .ef169 { color: #d75faf; } .eb169 { background-color: #d75faf; }
180
+ .ef170 { color: #d75fd7; } .eb170 { background-color: #d75fd7; }
181
+ .ef171 { color: #d75fff; } .eb171 { background-color: #d75fff; }
182
+ .ef172 { color: #d78700; } .eb172 { background-color: #d78700; }
183
+ .ef173 { color: #d7875f; } .eb173 { background-color: #d7875f; }
184
+ .ef174 { color: #d78787; } .eb174 { background-color: #d78787; }
185
+ .ef175 { color: #d787af; } .eb175 { background-color: #d787af; }
186
+ .ef176 { color: #d787d7; } .eb176 { background-color: #d787d7; }
187
+ .ef177 { color: #d787ff; } .eb177 { background-color: #d787ff; }
188
+ .ef178 { color: #d7af00; } .eb178 { background-color: #d7af00; }
189
+ .ef179 { color: #d7af5f; } .eb179 { background-color: #d7af5f; }
190
+ .ef180 { color: #d7af87; } .eb180 { background-color: #d7af87; }
191
+ .ef181 { color: #d7afaf; } .eb181 { background-color: #d7afaf; }
192
+ .ef182 { color: #d7afd7; } .eb182 { background-color: #d7afd7; }
193
+ .ef183 { color: #d7afff; } .eb183 { background-color: #d7afff; }
194
+ .ef184 { color: #d7d700; } .eb184 { background-color: #d7d700; }
195
+ .ef185 { color: #d7d75f; } .eb185 { background-color: #d7d75f; }
196
+ .ef186 { color: #d7d787; } .eb186 { background-color: #d7d787; }
197
+ .ef187 { color: #d7d7af; } .eb187 { background-color: #d7d7af; }
198
+ .ef188 { color: #d7d7d7; } .eb188 { background-color: #d7d7d7; }
199
+ .ef189 { color: #d7d7ff; } .eb189 { background-color: #d7d7ff; }
200
+ .ef190 { color: #d7ff00; } .eb190 { background-color: #d7ff00; }
201
+ .ef191 { color: #d7ff5f; } .eb191 { background-color: #d7ff5f; }
202
+ .ef192 { color: #d7ff87; } .eb192 { background-color: #d7ff87; }
203
+ .ef193 { color: #d7ffaf; } .eb193 { background-color: #d7ffaf; }
204
+ .ef194 { color: #d7ffd7; } .eb194 { background-color: #d7ffd7; }
205
+ .ef195 { color: #d7ffff; } .eb195 { background-color: #d7ffff; }
206
+ .ef196 { color: #ff0000; } .eb196 { background-color: #ff0000; }
207
+ .ef197 { color: #ff005f; } .eb197 { background-color: #ff005f; }
208
+ .ef198 { color: #ff0087; } .eb198 { background-color: #ff0087; }
209
+ .ef199 { color: #ff00af; } .eb199 { background-color: #ff00af; }
210
+ .ef200 { color: #ff00d7; } .eb200 { background-color: #ff00d7; }
211
+ .ef201 { color: #ff00ff; } .eb201 { background-color: #ff00ff; }
212
+ .ef202 { color: #ff5f00; } .eb202 { background-color: #ff5f00; }
213
+ .ef203 { color: #ff5f5f; } .eb203 { background-color: #ff5f5f; }
214
+ .ef204 { color: #ff5f87; } .eb204 { background-color: #ff5f87; }
215
+ .ef205 { color: #ff5faf; } .eb205 { background-color: #ff5faf; }
216
+ .ef206 { color: #ff5fd7; } .eb206 { background-color: #ff5fd7; }
217
+ .ef207 { color: #ff5fff; } .eb207 { background-color: #ff5fff; }
218
+ .ef208 { color: #ff8700; } .eb208 { background-color: #ff8700; }
219
+ .ef209 { color: #ff875f; } .eb209 { background-color: #ff875f; }
220
+ .ef210 { color: #ff8787; } .eb210 { background-color: #ff8787; }
221
+ .ef211 { color: #ff87af; } .eb211 { background-color: #ff87af; }
222
+ .ef212 { color: #ff87d7; } .eb212 { background-color: #ff87d7; }
223
+ .ef213 { color: #ff87ff; } .eb213 { background-color: #ff87ff; }
224
+ .ef214 { color: #ffaf00; } .eb214 { background-color: #ffaf00; }
225
+ .ef215 { color: #ffaf5f; } .eb215 { background-color: #ffaf5f; }
226
+ .ef216 { color: #ffaf87; } .eb216 { background-color: #ffaf87; }
227
+ .ef217 { color: #ffafaf; } .eb217 { background-color: #ffafaf; }
228
+ .ef218 { color: #ffafd7; } .eb218 { background-color: #ffafd7; }
229
+ .ef219 { color: #ffafff; } .eb219 { background-color: #ffafff; }
230
+ .ef220 { color: #ffd700; } .eb220 { background-color: #ffd700; }
231
+ .ef221 { color: #ffd75f; } .eb221 { background-color: #ffd75f; }
232
+ .ef222 { color: #ffd787; } .eb222 { background-color: #ffd787; }
233
+ .ef223 { color: #ffd7af; } .eb223 { background-color: #ffd7af; }
234
+ .ef224 { color: #ffd7d7; } .eb224 { background-color: #ffd7d7; }
235
+ .ef225 { color: #ffd7ff; } .eb225 { background-color: #ffd7ff; }
236
+ .ef226 { color: #ffff00; } .eb226 { background-color: #ffff00; }
237
+ .ef227 { color: #ffff5f; } .eb227 { background-color: #ffff5f; }
238
+ .ef228 { color: #ffff87; } .eb228 { background-color: #ffff87; }
239
+ .ef229 { color: #ffffaf; } .eb229 { background-color: #ffffaf; }
240
+ .ef230 { color: #ffffd7; } .eb230 { background-color: #ffffd7; }
241
+ .ef231 { color: #ffffff; } .eb231 { background-color: #ffffff; }
242
+ .ef232 { color: #080808; } .eb232 { background-color: #080808; }
243
+ .ef233 { color: #121212; } .eb233 { background-color: #121212; }
244
+ .ef234 { color: #1c1c1c; } .eb234 { background-color: #1c1c1c; }
245
+ .ef235 { color: #262626; } .eb235 { background-color: #262626; }
246
+ .ef236 { color: #303030; } .eb236 { background-color: #303030; }
247
+ .ef237 { color: #3a3a3a; } .eb237 { background-color: #3a3a3a; }
248
+ .ef238 { color: #444444; } .eb238 { background-color: #444444; }
249
+ .ef239 { color: #4e4e4e; } .eb239 { background-color: #4e4e4e; }
250
+ .ef240 { color: #585858; } .eb240 { background-color: #585858; }
251
+ .ef241 { color: #626262; } .eb241 { background-color: #626262; }
252
+ .ef242 { color: #6c6c6c; } .eb242 { background-color: #6c6c6c; }
253
+ .ef243 { color: #767676; } .eb243 { background-color: #767676; }
254
+ .ef244 { color: #808080; } .eb244 { background-color: #808080; }
255
+ .ef245 { color: #8a8a8a; } .eb245 { background-color: #8a8a8a; }
256
+ .ef246 { color: #949494; } .eb246 { background-color: #949494; }
257
+ .ef247 { color: #9e9e9e; } .eb247 { background-color: #9e9e9e; }
258
+ .ef248 { color: #a8a8a8; } .eb248 { background-color: #a8a8a8; }
259
+ .ef249 { color: #b2b2b2; } .eb249 { background-color: #b2b2b2; }
260
+ .ef250 { color: #bcbcbc; } .eb250 { background-color: #bcbcbc; }
261
+ .ef251 { color: #c6c6c6; } .eb251 { background-color: #c6c6c6; }
262
+ .ef252 { color: #d0d0d0; } .eb252 { background-color: #d0d0d0; }
263
+ .ef253 { color: #dadada; } .eb253 { background-color: #dadada; }
264
+ .ef254 { color: #e4e4e4; } .eb254 { background-color: #e4e4e4; }
265
+ .ef255 { color: #eeeeee; } .eb255 { background-color: #eeeeee; }
266
+
267
+ .f9 { color: #000000; }
268
+ .b9 { background-color: #FFFFFF; }
269
+ .f9 > .bold,.bold > .f9, body.f9 > pre > .bold {
270
+ /* Bold is heavy black on white, or bright white
271
+ depending on the default background */
272
+ color: #000000;
273
+ font-weight: bold;
274
+ }
275
+ .reverse {
276
+ /* CSS doesnt support swapping fg and bg colours unfortunately,
277
+ so just hardcode something that will look OK on all backgrounds. */
278
+ color: #000000; background-color: #AAAAAA;
279
+ }
280
+ .underline { text-decoration: underline; }
281
+ .line-through { text-decoration: line-through; }
282
+ .blink { text-decoration: blink; }
283
+