etsy-deployinator 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+