machines 0.5.2 → 0.5.3

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.
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ == 0.5.3
2
+
3
+ * Implement `list` command to list available machines from machines.yml
4
+ * Display syntax when `build` called with no machine
5
+ * Support running multiple tasks - e.g. `machines build machine passenger passenger_nginx nginx`
6
+ * modify git_clone to pull if the repo already exists
7
+
8
+ == 0.5.2
9
+
10
+ * Bug fixes
11
+
12
+
13
+ == 0.5.1
14
+
15
+ * Added opensource flag to allow alternatives to be installed (e.g. Chrome/Chromium)
16
+ * Added config.yml option to autostart VBoxClient for WMs that don't use XDG autostart
17
+ * Only append lines in a file if they do not exist (allows repeat running of commands)
data/EXAMPLES.md CHANGED
@@ -1,3 +1,6 @@
1
+ Adding new packages
2
+ =======================================
3
+
1
4
  It's easy to add new packages if you need something new on your current install.
2
5
  I added the following to `packages/printer.rb`:
3
6
 
@@ -16,3 +19,23 @@ Then ran it locally:
16
19
 
17
20
  Now the drivers for my printer are installed.
18
21
 
22
+
23
+ Running individual tasks
24
+ =======================================
25
+
26
+ Here is an example of upgrading Ruby, passenger and Nginx. First we check what commands will be run with the `dryrun` command.
27
+
28
+ machines dryrun phil rbenv passenger passenger_nginx nginx webapps
29
+ cat log/output.log
30
+
31
+ 2% SUDO apt-get -q -y install git-core
32
+ 4% SUDO apt-get -q -y install curl
33
+ 5% RUN test -d ruby-build && (cd ruby-build && git pull) || git clone --quiet git://github.com...
34
+ 7% SUDO cd ~/ruby-build && ./install.sh
35
+ ...
36
+
37
+ We can see that git and curl are installed as part of the rbenv install (although they will be ignored as they're already installed). the latest ruby-build is then pulled as it's already on the machine and the installer run.
38
+
39
+ All looks good so we'll run the build:
40
+
41
+ machines build phil rbenv passenger passenger_nginx nginx webapps
data/TODO.md CHANGED
@@ -1,7 +1,8 @@
1
1
  TODO next
2
2
  ----------------------------------------
3
3
 
4
- Support missing environment (e.g. scm machine)
4
+ Handle unknown tasks and display a proper error message (at the moment we get an obscure undefined method on NilClass)
5
+ Support missing environment (e.g. source repo machine)
5
6
  `machines dryrun/build` with no machine name should list the machines available
6
7
  DRY up per user config by creating a "common" user config that all users pull default config from
7
8
  DRY up further by having default templates in the same way that packages default to built-in ones
@@ -9,6 +10,8 @@ DRY up further by having default templates in the same way that packages default
9
10
  Complement this by providing a command to view packages and templates
10
11
  Move dotfiles to a repo so they can be managed across projects
11
12
  Handle apt-get error 110 and retry
13
+ Move packages into a separate gem
14
+ Move template into a separate gem
12
15
 
13
16
 
14
17
  Cloud
@@ -34,7 +37,6 @@ EC2 - Look at assigning and freeing elastic IP addresses
34
37
  Package and Task Tasks
35
38
  ----------------------------------------
36
39
 
37
- Support running multiple tasks - e.g. `machines build passenger passenger_nginx nginx`
38
40
  Any methods other than `append` that cannot be repeatedly run?
39
41
  Display additional install notes for a particular package (at the end of installation) - e.g. printer setup requires Windows share to be setup
40
42
  CODE/DOC: Describe difference between package and task or merge packages with tasks if possible
@@ -3,11 +3,11 @@ module Machines
3
3
  # Loads Machinesfile, opens an SCP connection and runs all commands and file uploads
4
4
  def build options
5
5
  $conf.machine_name = options.shift
6
- $conf.task = options.shift
6
+ return say(Help.new.syntax) unless $conf.machine_name
7
7
  init
8
8
  load_machinesfile
9
9
 
10
- task $conf.task.to_sym if $conf.task
10
+ task options if options.any?
11
11
 
12
12
  ssh_options = {:paranoid => false}
13
13
  if $conf.machine.cloud
@@ -44,7 +44,7 @@ module Machines
44
44
  end
45
45
 
46
46
  # Execute a given command e.g. dryrun, build, generate, htpasswd, packages, override, tasks
47
- def execute(options)
47
+ def execute options
48
48
  help = Help.new
49
49
  action = options.shift
50
50
  if help.actions.include?(action)
@@ -90,6 +90,10 @@ module Machines
90
90
  Command.console ||= Machines::Logger.new STDOUT, :truncate => true
91
91
  end
92
92
 
93
+ def list notused
94
+ say Help.new.machine_list
95
+ end
96
+
93
97
  def load_machinesfile
94
98
  eval File.read('Machinesfile'), nil, "eval: Machinesfile"
95
99
  rescue LoadError => e
@@ -126,7 +130,10 @@ module Machines
126
130
  end
127
131
  end
128
132
 
129
- def tasks
133
+ def tasks options
134
+ $conf.machine_name = options.shift
135
+ return say(Help.new.syntax) unless $conf.machine_name
136
+
130
137
  $conf.log_only = true
131
138
  init
132
139
  load_machinesfile
data/lib/machines/core.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  module Machines
2
2
  module Core
3
3
  # If a block is given, store the task, log it and run it
4
- # If no block is given, sets commands to only those of the specified task so they can be run standalone
5
- # @param [Symbol] name Name of the task
4
+ # If no block is given, sets commands to only those of the specified tasks so they can be run standalone
5
+ # @param [Symbol, String, Array] name Name of the task or array of task names
6
6
  # @param [String] description Describe the task
7
7
  # @param [Hash] options
8
8
  # @option options [Symbol, Array] :if Dependent tasks that must already have been added for this task to be added
@@ -14,8 +14,11 @@ module Machines
14
14
  $conf.commands << LogCommand.new(name, description)
15
15
  yield
16
16
  else
17
+ tasks = [name].flatten
17
18
  $conf.commands = []
18
- $conf.tasks[name][:block].call
19
+ tasks.each do |name|
20
+ $conf.tasks[name.to_sym][:block].call
21
+ end
19
22
  end
20
23
  end
21
24
 
data/lib/machines/help.rb CHANGED
@@ -4,9 +4,9 @@ module Machines
4
4
  @actions = {
5
5
  'htpasswd' => 'Generates basic auth in webserver/conf/htpasswd',
6
6
  'new <DIR>' => 'Generates an example machines project in DIR',
7
- 'dryrun <machine>' => 'Logs commands but does not run them',
8
- 'tasks' => 'Lists the available tasks',
9
- 'build <machine> [task]' => 'Builds your chosen machine. Optionally, build just one task',
7
+ 'dryrun <machine> [tasks]' => 'Display commands that would be run. Optionally, specify tasks to run',
8
+ 'tasks <machine>' => 'Lists the available tasks for the specified machine',
9
+ 'build <machine> [tasks]' => 'Builds your chosen machine. Optionally, specify tasks to run',
10
10
  'list' => 'Lists the available machines',
11
11
  'packages' => 'Lists the available packages',
12
12
  'override <PACKAGE>' => 'Copies the default package into project/packages so it can be edited/overidden'
@@ -17,8 +17,17 @@ module Machines
17
17
  @actions.keys.map{|key| key.gsub(/ .*/, '')}
18
18
  end
19
19
 
20
+ def machine_list
21
+ $conf.machines = AppConf.new
22
+ $conf.load('machines.yml')
23
+ <<-LIST
24
+ Machines from machines.yml:
25
+ #{$conf.machines.keys.map{|machine| " #{machine}" }.join("\n")}
26
+ LIST
27
+ end
28
+
20
29
  def syntax
21
- <<HELP
30
+ <<-HELP
22
31
  machines v#{Machines::VERSION} - Ubuntu/Ruby configuration tool.
23
32
  machines COMMAND
24
33
  COMMAND can be:
@@ -73,7 +73,7 @@ module Machines
73
73
  Command.new("gem update #{options}", nil)
74
74
  end
75
75
 
76
- # Clone a project from a Git repository
76
+ # Clone (or update) a project from a Git repository
77
77
  # @param [String] url URL to clone
78
78
  # @param [Hash] options
79
79
  # @option options [Optional String] :to Folder to clone to
@@ -83,7 +83,8 @@ module Machines
83
83
  raise ArgumentError.new('git_clone Must include a url and folder') if url.nil? || url.empty?
84
84
  raise ArgumentError.new('specifying :tag also requires :to') if options[:tag] && options[:to].nil?
85
85
  branch = "--branch #{options[:branch]} " if options[:branch]
86
- command = "git clone --quiet #{branch}#{url}"
86
+ dir = options[:to] || url.gsub(/^.*\/|.git/, '')
87
+ command = "test -d #{dir} && (cd #{dir} && git pull) || git clone --quiet #{branch}#{url}"
87
88
  command << " #{options[:to]}" if options[:to]
88
89
  command = Command.new(command, check_dir(options[:to]))
89
90
  command = [command, Command.new("cd #{options[:to]} && git checkout #{options[:tag]}", "git name-rev --name-only HEAD | grep #{options[:tag]}")] if options[:tag]
@@ -1,4 +1,4 @@
1
1
  module Machines
2
- VERSION = '0.5.2'
2
+ VERSION = '0.5.3'
3
3
  end
4
4
 
@@ -2,7 +2,7 @@ task :abiword, 'Install a lightweight word processor' do
2
2
  sudo install ['abiword']
3
3
  end
4
4
 
5
- task :abiword_associations, 'Setup file associations for Abiword' do
5
+ task :abiword_assoc, 'Setup file associations for Abiword' do
6
6
  mimetypes = 'application/x-abiword;application/msword;application/rtf;'
7
7
  mimetypes.split(';').each do |mimetype|
8
8
  run append "#{mimetype}=abiword.desktop", :to => '.local/share/applications/mimeapps.list'
@@ -1,6 +1,6 @@
1
1
  username = $conf.machine.user
2
2
 
3
- task :dotfiles, "Upload files in users/#{username}/dotfiles, prepend a dot and substitute some bashrc vars" do
3
+ task :dotfiles, "Upload users/#{username}/dotfiles and set some env vars" do
4
4
  Dir["users/#{username}/dotfiles/*"].each do |source|
5
5
  run upload source, ".#{File.basename(source)}" if File.exists?(source)
6
6
  end
@@ -3,7 +3,7 @@ task :file_roller, 'Install file-roller archive manager' do
3
3
  end
4
4
 
5
5
  # Mimetypes can be discovered by looking in /usr/share/applications/*.desktop
6
- task :file_roller_associations, 'Setup file associations for file-roller' do
6
+ task :file_roller_assoc, 'Setup file associations for file-roller' do
7
7
  mimetypes = 'application/x-7z-compressed;application/x-7z-compressed-tar;application/x-ace;application/x-alz;application/x-ar;application/x-arj;application/x-bzip;application/x-bzip-compressed-tar;application/x-bzip1;application/x-bzip1-compressed-tar;application/x-cabinet;application/x-cbr;application/x-cbz;application/x-cd-image;application/x-compress;application/x-compressed-tar;application/x-cpio;application/x-deb;application/x-ear;application/x-ms-dos-executable;application/x-gtar;application/x-gzip;application/x-gzpostscript;application/x-java-archive;application/x-lha;application/x-lhz;application/x-lrzip;application/x-lrzip-compressed-tar;application/x-lzip;application/x-lzip-compressed-tar;application/x-lzma;application/x-lzma-compressed-tar;application/x-lzop;application/x-lzop-compressed-tar;application/x-rar;application/x-rar-compressed;application/x-rpm;application/x-rzip;application/x-tar;application/x-tarz;application/x-stuffit;application/x-war;application/x-xz;application/x-xz-compressed-tar;application/x-zip;application/x-zip-compressed;application/x-zoo;application/zip;'
8
8
  mimetypes.split(';').each do |mimetype|
9
9
  run append "#{mimetype}=file-roller.desktop", :to => '.local/share/applications/mimeapps.list'
@@ -2,7 +2,7 @@ task :gnumeric, 'Install gnumeric lightweight spreadsheet' do
2
2
  sudo install ['gnumeric']
3
3
  end
4
4
 
5
- task :gnumeric_associations, 'Setup file associations for Gnumeric' do
5
+ task :gnumeric_assoc, 'Setup file associations for Gnumeric' do
6
6
  mimetypes = 'application/x-gnumeric;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;text/csv;application/msexcel'
7
7
  mimetypes.split(';').each do |mimetype|
8
8
  run append "#{mimetype}=gnumeric.desktop", :to => '.local/share/applications/mimeapps.list'
@@ -1,8 +1,8 @@
1
1
  task :nginx, 'Download and configure Nginx' do
2
- sudo extract $conf.webserver.url
2
+ sudo extract "http://nginx.org/download/nginx-#{$conf.webserver.version}.tar.gz"
3
3
  modules = "#{$conf.webserver.modules} --add-module=#{$conf.passenger.nginx}"
4
4
  commands = [
5
- "cd #{$conf.webserver.src_path}",
5
+ "cd /usr/local/src/nginx-#{$conf.webserver.version}",
6
6
  "./configure #{modules}",
7
7
  "make",
8
8
  "make install"
@@ -7,7 +7,7 @@ task :logrotate_nginx, 'Logrotate nginx access and error logs and optionally gen
7
7
  else
8
8
  stats_prerotate = stats_postrotate = nil
9
9
  end
10
- settings = AppBuilder.new(
10
+ settings = Machines::AppSettings::AppBuilder.new(
11
11
  log_path: "/var/log/nginx/#{app.name}.#{type}.log",
12
12
  stats_prerotate: stats_prerotate,
13
13
  stats_postrotate: stats_postrotate
@@ -19,7 +19,7 @@ end
19
19
 
20
20
  task :logrotate_apps, 'Logrotate Rails app logs' do
21
21
  $conf.webapps.each do |app_name, app|
22
- settings = AppBuilder.new(log_path: File.join(app.path, 'shared', 'log', '*.log'))
22
+ settings = Machines::AppSettings::AppBuilder.new(log_path: File.join(app.path, 'shared', 'log', '*.log'))
23
23
  sudo create_from 'logrotate/app.erb', settings: settings, to: File.join('/etc', 'logrotate.d', "#{app.name}_app")
24
24
  end
25
25
  end
@@ -1,4 +1,4 @@
1
- task :rbenv, "Install ruby-build, rbenv, ruby #{$conf.ruby.version} and Bundler" do
1
+ task :rbenv, "Install ruby-build, rbenv, ruby #{$conf.ruby.version}-#{$conf.ruby.build} and Bundler" do
2
2
  sudo install ['git-core', 'curl']
3
3
  run git_clone 'git://github.com/sstephenson/ruby-build.git'
4
4
  sudo 'cd ~/ruby-build && ./install.sh', check_file('/usr/local/bin/ruby-build')
@@ -16,9 +16,14 @@ task :rbenv, "Install ruby-build, rbenv, ruby #{$conf.ruby.version} and Bundler"
16
16
  run append path, :to => '~/.profile'
17
17
  rbenv = '$HOME/.rbenv/bin/rbenv'
18
18
 
19
- run "#{rbenv} install #{$conf.ruby.full_version}", check_command("#{rbenv} versions", $conf.ruby.version)
19
+ full_version = "#{$conf.ruby.version}-#{$conf.ruby.build}"
20
+
21
+ $conf.ruby.gems_path = ".rbenv/versions/#{full_version}/lib/ruby/gems/1.9.1/gems"
22
+ $conf.ruby.executable = ".rbenv/versions/#{full_version}/bin/ruby"
23
+
24
+ run "#{rbenv} install #{full_version}", check_command("#{rbenv} versions", $conf.ruby.version)
20
25
  run "#{rbenv} rehash", check_command("#{path} which gem", '.rbenv/shims/gem')
21
- run "#{rbenv} global #{$conf.ruby.full_version}", check_command("#{rbenv} exec ruby -v", $conf.ruby.version)
26
+ run "#{rbenv} global #{full_version}", check_command("#{rbenv} exec ruby -v", $conf.ruby.version)
22
27
 
23
28
  run write "gem: --no-rdoc --no-ri", :to => '.gemrc', :name => '.gemrc'
24
29
  run "#{rbenv} exec gem install bundler", check_command("#{rbenv} exec gem list", 'bundler')
data/lib/packages/rvm.rb CHANGED
@@ -1,4 +1,8 @@
1
1
  task :rvm, 'Install RVM' do
2
+ full_version = "#{$conf.ruby.version}-#{$conf.ruby.build}"
3
+ $conf.ruby.gems_path = ".rvm/gems/#{full_version}/@global/gems"
4
+ $conf.ruby.executable = ".rvm/wrappers/#{full_version}@global/ruby"
5
+
2
6
  sudo install ['git-core']
3
7
  installer = "bash -s #{$conf.rvm.version} < <(wget -q https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer )"
4
8
  run installer, check_file('~/.rvm/bin/rvm')
@@ -11,7 +15,7 @@ task :rvm_prompt_off, 'turn off trust prompting for new .rvmrc files' do
11
15
  run append 'export rvm_trust_rvmrcs_flag=1', :to => '.rvmrc'
12
16
  end
13
17
 
14
- task :ruby, "Install Ruby, make #{$conf.ruby.version}@global the default and install Bundler" do
18
+ task :ruby, "Install Ruby, make #{$conf.ruby.version}-#{$conf.ruby.build}@global the default and install Bundler" do
15
19
  run "rvm install #{$conf.ruby.version}", "rvm #{$conf.ruby.version} #{echo_result}"
16
20
  run "rvm #{$conf.ruby.version}@global --default", "ruby -v | grep #{$conf.ruby.version} #{echo_result}"
17
21
  run write "gem: --no-rdoc --no-ri", :to => '.gemrc', :name => '.gemrc'
@@ -1,4 +1,4 @@
1
- task :shutdown_without_password, 'Ensure we can shutdown/reboot without needing a password for sudo' do
1
+ task :shutdown_no_password, 'Ensure we can shutdown/reboot without needing a password for sudo' do
2
2
  sudo append 'ALL ALL=NOPASSWD:/sbin/shutdown', :to => '/etc/sudoers.d/shutdown'
3
3
  sudo append 'ALL ALL=NOPASSWD:/sbin/reboot', :to => '/etc/sudoers.d/shutdown'
4
4
  sudo chmod 440, '/etc/sudoers.d/shutdown'
@@ -1,4 +1,4 @@
1
- task :unison, 'Install unison two way file sync and set it to run hourly. Config in users/user/.unison/default.prf' do
1
+ task :unison, "Install and configure Unison (users/#{$conf.machine.user}/.unison/default.prf)" do
2
2
  sudo install 'unison'
3
3
  run "echo '30 18 * * * /usr/bin/unison' | crontab", check_command('crontab -l', 'unison')
4
4
  end
@@ -1,4 +1,4 @@
1
- task :webapps, 'Sets up Web apps in config/webapps.yml using app_server.conf.erb. Copies SSL certs.' do
1
+ task :webapps, 'Sets up Web apps in config/webapps.yml using app_server.conf.erb' do
2
2
  sudo mkdir File.join($conf.webserver.path, $conf.webserver.servers_dir) if $conf.webserver.servers_dir
3
3
  $conf.webapps.each do |app_name, app|
4
4
  if $conf.environment == 'development'
@@ -54,25 +54,20 @@ monit:
54
54
  # Latest version including preferred nginx version:
55
55
  # https://github.com/FooBarWidget/passenger/blob/master/NEWS
56
56
  passenger:
57
- version: 3.0.11
58
- # Set in passenger.rb package
59
- # root: Points to passenger path in rubygems
60
- # ruby: Points to the ruby executable in rbenv, rvm, compiled or installed by your package manager
57
+ version: 3.0.17
58
+ # Set in passenger.rb package
59
+ # root: Points to passenger path in rubygems
60
+ # ruby: Points to the ruby executable in rbenv, rvm, compiled or installed by your package manager
61
61
 
62
62
  # Uncomment to use RVM
63
63
  # Ruby will be installed using RVM
64
64
  # Passenger will be installed using rvmsudo
65
65
  #rvm:
66
- # url: https://rvm.beginrescueend.com/install/rvm
67
66
  # version: 1.9.2
68
67
 
69
68
  ruby:
70
- version: 1.9.2
71
- full_version: 1.9.2-p290
72
- # gems_path: '.rvm/gems/1.9.2-p290/@global/gems'
73
- # executable: .rvm/wrappers/1.9.2-p290@global/ruby
74
- gems_path: .rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems
75
- executable: .rbenv/versions/1.9.2-p290/bin/ruby
69
+ version: 1.9.3
70
+ build: p194
76
71
 
77
72
  set_rails_env_for:
78
73
  - qa
@@ -90,7 +85,7 @@ use_opensource: true
90
85
  webserver:
91
86
  name: nginx
92
87
  modules: --with-http_ssl_module
93
- version: 1.0.11
88
+ version: 1.2.3
94
89
  url: http://nginx.org/download/nginx-1.0.11.tar.gz
95
90
  src_path: /usr/local/src/nginx-1.0.11
96
91
  path: /usr/local/nginx
@@ -7,7 +7,7 @@ describe Machines::Commandline do
7
7
 
8
8
  before(:each) do
9
9
  $conf.log_only = false
10
- File.open('config.yml', 'w') { |f| f.puts "timezone: GB" }
10
+ File.open('config.yml', 'w') {|f| f.puts "timezone: GB" }
11
11
  FileUtils.mkdir_p 'log'
12
12
  end
13
13
 
@@ -24,23 +24,29 @@ describe Machines::Commandline do
24
24
  Net::SSH.stubs(:start).returns @ssh_stub
25
25
  end
26
26
 
27
+ it 'displays syntax when no machine name specified' do
28
+ lambda { build [] }.must_output Machines::Help.new.syntax
29
+ end
30
+
27
31
  it 'sets machine_name' do
28
32
  build ['machine']
29
33
  $conf.machine_name.must_equal 'machine'
30
34
  end
31
35
 
32
36
  it 'starts an SCP session using password authentication' do
33
- Net::SSH.expects(:start).with('target', 'username', :paranoid => false, :password => 'userpass').returns @ssh_stub
34
- build []
37
+ options = {paranoid: false, password: 'userpass'}
38
+ Net::SSH.expects(:start).with('target', 'username', options).returns @ssh_stub
39
+ build ['machine']
35
40
  end
36
41
 
37
42
  it 'starts an SCP session using key based authentication' do
38
43
  $conf.machine.cloud = AppConf.new
39
44
  $conf.machine.cloud.private_key_path = 'path/to/private_key'
40
45
  $conf.machine.cloud.username = 'ubuntu'
41
- Net::SSH.expects(:start).with('target', 'ubuntu', :paranoid => false, :keys => ['path/to/private_key']).returns @ssh_stub
46
+ options = {paranoid: false, keys: ['path/to/private_key']}
47
+ Net::SSH.expects(:start).with('target', 'ubuntu', options).returns @ssh_stub
42
48
 
43
- build []
49
+ build ['machine']
44
50
  end
45
51
 
46
52
  it 'runs each command' do
@@ -49,7 +55,7 @@ describe Machines::Commandline do
49
55
 
50
56
  mock_command.expects(:run)
51
57
 
52
- build []
58
+ build ['machine']
53
59
  end
54
60
 
55
61
  it 'flushes log file after running command' do
@@ -58,17 +64,21 @@ describe Machines::Commandline do
58
64
  $conf.commands = [command_stub]
59
65
 
60
66
  Machines::Command.file.expects(:flush)
61
- build []
67
+ build ['machine']
62
68
  end
63
69
 
64
- it 'replaces commands with the single task when supplied' do
70
+ it 'only run specified tasks' do
65
71
  command_will_run = Machines::Command.new '', ''
72
+ command_also_run = Machines::Command.new '', ''
66
73
  command_wont_run = Machines::Command.new '', ''
67
- $conf.tasks = { :task => {:block => Proc.new { run command_will_run }} }
74
+ $conf.tasks = {
75
+ :task1 => {:block => Proc.new { run command_will_run }},
76
+ :task2 => {:block => Proc.new { run command_also_run }}
77
+ }
68
78
 
69
- build ['machine', 'task']
79
+ build ['machine', 'task1', 'task2']
70
80
 
71
- $conf.commands.must_equal [command_will_run]
81
+ $conf.commands.must_equal [command_will_run, command_also_run]
72
82
  end
73
83
 
74
84
  it 'logs instead of SSHing and running commands' do
@@ -76,7 +86,7 @@ describe Machines::Commandline do
76
86
  $conf.commands = [mock('Machines::Command')]
77
87
  $conf.commands.first.expects(:run)
78
88
  $conf.log_only = true
79
- build []
89
+ build ['machine']
80
90
  end
81
91
 
82
92
  describe 'interrupts' do
@@ -91,14 +101,15 @@ describe Machines::Commandline do
91
101
  it 'handles CTRL+C and calls handler' do
92
102
  expects(:prepare_to_exit)
93
103
  Kernel.expects(:trap).with('INT').yields
94
- build []
104
+ build ['machine']
95
105
  end
96
106
 
97
107
 
98
108
  it 'sets exit flag and displays message' do
99
109
  prepare_to_exit
100
110
  $exit_requested.must_equal true
101
- $console.next.must_equal colored("\nEXITING after current command completes...\n", :warning)
111
+ message = "\nEXITING after current command completes...\n"
112
+ $console.next.must_equal colored(message, :warning)
102
113
  end
103
114
 
104
115
  it 'second request to exit exits immediately' do
@@ -111,7 +122,7 @@ describe Machines::Commandline do
111
122
  $exit_requested = true
112
123
  expects(:exit)
113
124
 
114
- build []
125
+ build ['machine']
115
126
  end
116
127
  end
117
128
  end
@@ -123,6 +134,12 @@ describe Machines::Commandline do
123
134
  dryrun options
124
135
  $conf.log_only.must_equal true
125
136
  end
137
+
138
+ it 'passes tasks to build' do
139
+ options = ['machine', 'task']
140
+ expects(:build).with options
141
+ dryrun options
142
+ end
126
143
  end
127
144
 
128
145
  describe 'execute' do
@@ -210,6 +227,13 @@ describe Machines::Commandline do
210
227
  end
211
228
  end
212
229
 
230
+ describe 'list' do
231
+ it 'lists machines' do
232
+ File.open('machines.yml', 'w') {|f| f.puts({'machines' => {'machine_1' => {}, 'machine_2' => {}}}.to_yaml) }
233
+ lambda { list nil }.must_output "Machines from machines.yml:\n machine_1\n machine_2\n"
234
+ end
235
+ end
236
+
213
237
  describe 'load_machinesfile' do
214
238
  it 'raises LoadError with custom message when no Machinesfile' do
215
239
  File.expects(:read).with("Machinesfile").raises LoadError.new('Machinesfile not found')
@@ -288,11 +312,12 @@ Project packages
288
312
  :task2 => {:description => 'description 2'},
289
313
  :task3 => {:description => 'description 3'}
290
314
  }
291
- lambda { tasks }.must_output 'Tasks
315
+ lambda { tasks ['machine'] }.must_output 'Tasks
292
316
  task1 description 1
293
317
  task2 description 2
294
318
  task3 description 3
295
319
  '
320
+ $conf.machine_name.must_equal 'machine'
296
321
  end
297
322
  end
298
323
  end
@@ -37,12 +37,21 @@ describe Machines::Core do
37
37
  $conf.tasks.must_equal :name => {:description => 'description', :block => block}
38
38
  end
39
39
 
40
- it 'sets commands to only run those from the specified task' do
41
- block_ran = false
42
- block = Proc.new { block_ran = true }
43
- $conf.tasks[:name] = {:block => block}
44
- task :name, nil
45
- block_ran.must_equal true
40
+ it 'sets commands to only run those from the specified tasks' do
41
+ block_run_count = 0
42
+ block = Proc.new { block_run_count += 1 }
43
+ $conf.tasks[:task1] = {:block => block}
44
+ $conf.tasks[:task2] = {:block => block}
45
+ task [:task1, 'task2']
46
+ block_run_count.must_equal 2
47
+ end
48
+
49
+ it 'sets commands to only run those of a single task' do
50
+ block_run = false
51
+ block = Proc.new { block_run = true }
52
+ $conf.tasks[:task] = {:block => block}
53
+ task :task
54
+ block_run.must_equal true
46
55
  end
47
56
 
48
57
  describe 'when dependent task' do
@@ -7,6 +7,13 @@ describe Machines::Help do
7
7
  it { subject.actions.must_equal ['htpasswd', 'new', 'dryrun', 'tasks', 'build', 'list', 'packages', 'override'] }
8
8
  end
9
9
 
10
+ describe 'machine_list' do
11
+ it 'lists the machines from machines.yml' do
12
+ File.open('machines.yml', 'w') {|f| f.puts({'machines' => {'machine_1' => {}, 'machine_2' => {}}}.to_yaml) }
13
+ subject.machine_list.must_equal "Machines from machines.yml:\n machine_1\n machine_2\n"
14
+ end
15
+ end
16
+
10
17
  describe 'syntax' do
11
18
  it 'includes version' do
12
19
  subject.syntax.must_match /machines v0\.[0-9]+\.[0-9]+ - Ubuntu\/Ruby configuration tool\./
@@ -15,7 +22,7 @@ describe Machines::Help do
15
22
  it 'includes syntax' do
16
23
  subject.syntax.must_match /machines COMMAND/
17
24
  subject.syntax.must_match /COMMAND can be:/
18
- subject.syntax.must_match /build <machine> \[task\] Builds your chosen machine\. Optionally, build just one task/
25
+ subject.syntax.must_match /build \<machine\> \[tasks\] Builds your chosen machine/
19
26
  end
20
27
  end
21
28
  end
@@ -82,12 +82,12 @@ describe 'Installation' do
82
82
  describe 'git_clone' do
83
83
  it 'instaniates a command to clone a git repository' do
84
84
  subject = git_clone 'http://git_url.git'
85
- subject.command.must_equal 'git clone --quiet http://git_url.git'
85
+ subject.command.must_equal 'test -d git_url && (cd git_url && git pull) || git clone --quiet http://git_url.git'
86
86
  end
87
87
 
88
88
  it 'instaniates a command to clone a git repository to a specified folder' do
89
89
  subject = git_clone 'http://git_url.git', :to => 'dir'
90
- subject.command.must_equal 'git clone --quiet http://git_url.git dir'
90
+ subject.command.must_equal 'test -d dir && (cd dir && git pull) || git clone --quiet http://git_url.git dir'
91
91
  end
92
92
 
93
93
  it 'raises when no url supplied' do
@@ -98,7 +98,7 @@ describe 'Installation' do
98
98
  describe ':branch option' do
99
99
  it 'clones to a specific branch' do
100
100
  subject = git_clone 'http://git_url.git', :branch => 'other'
101
- subject.command.must_equal 'git clone --quiet --branch other http://git_url.git'
101
+ subject.command.must_equal 'test -d git_url && (cd git_url && git pull) || git clone --quiet --branch other http://git_url.git'
102
102
  end
103
103
  end
104
104
 
@@ -106,7 +106,7 @@ describe 'Installation' do
106
106
  it 'checks out a specific tag' do
107
107
  subject = git_clone 'http://git_url.git', :to => 'dir', :tag => 'v1.0'
108
108
  subject.map(&:command).must_equal [
109
- 'git clone --quiet http://git_url.git dir',
109
+ 'test -d dir && (cd dir && git pull) || git clone --quiet http://git_url.git dir',
110
110
  'cd dir && git checkout v1.0'
111
111
  ]
112
112
  end
@@ -10,7 +10,7 @@ describe 'packages/apps' do
10
10
  $conf.commands.map(&:info).must_equal [
11
11
  "TASK abiword - Install a lightweight word processor",
12
12
  "SUDO apt-get -q -y install abiword",
13
- "TASK abiword_associations - Setup file associations for Abiword",
13
+ "TASK abiword_assoc - Setup file associations for Abiword",
14
14
  "RUN grep \"application/x-abiword=abiword.desktop\" .local/share/applications/mimeapps.list || echo \"application/x-abiword=abiword.desktop\" >> .local/share/applications/mimeapps.list",
15
15
  "RUN grep \"application/msword=abiword.desktop\" .local/share/applications/mimeapps.list || echo \"application/msword=abiword.desktop\" >> .local/share/applications/mimeapps.list",
16
16
  "RUN grep \"application/rtf=abiword.desktop\" .local/share/applications/mimeapps.list || echo \"application/rtf=abiword.desktop\" >> .local/share/applications/mimeapps.list"
@@ -19,7 +19,7 @@ describe 'packages/cruisecontrol' do
19
19
  eval_package
20
20
  $conf.commands.map(&:info).join("\n").must_equal [
21
21
  "TASK cruisecontrol - Install, configure and set to start on boot",
22
- "RUN git clone --quiet https://github.com/thoughtworks/cruisecontrol.rb.git",
22
+ "RUN test -d cruisecontrol.rb && (cd cruisecontrol.rb && git pull) || git clone --quiet https://github.com/thoughtworks/cruisecontrol.rb.git",
23
23
  "RUN cd cruisecontrol.rb && bundle",
24
24
  "RUN cd cruisecontrol.rb && ruby ./cruise add Application -r github.com/project",
25
25
  "SUDO cp -rf cruisecontrol.rb/daemon/cruise /etc/init.d/cruise",
@@ -17,7 +17,7 @@ describe 'packages/dotfiles' do
17
17
  it 'adds the following commands' do
18
18
  eval_package
19
19
  $conf.commands.map(&:info).join("\n").must_equal [
20
- "TASK dotfiles - Upload files in users/username/dotfiles, prepend a dot and substitute some bashrc vars",
20
+ "TASK dotfiles - Upload users/username/dotfiles and set some env vars",
21
21
  "UPLOAD #{File.expand_path('users/username/dotfiles/bashrc')} to .bashrc",
22
22
  "RUN mkdir -p $HOME/.ssh",
23
23
  "RUN chmod 700 $HOME/.ssh",
@@ -14,7 +14,7 @@ describe 'packages/apps' do
14
14
  $conf.commands.map(&:info).must_equal [
15
15
  "TASK file_roller - Install file-roller archive manager",
16
16
  "SUDO apt-get -q -y install file-roller",
17
- "TASK file_roller_associations - Setup file associations for file-roller",
17
+ "TASK file_roller_assoc - Setup file associations for file-roller",
18
18
  append_command('application/x-7z-compressed=file-roller.desktop'),
19
19
  append_command('application/x-7z-compressed-tar=file-roller.desktop'),
20
20
  append_command('application/x-ace=file-roller.desktop'),
@@ -12,7 +12,7 @@ describe 'packages/gmate' do
12
12
  "TASK gmate - Clone and install gmate for gEdit from Github and set some preferences and plugins",
13
13
  "SUDO apt-get -q -y install python-pyinotify",
14
14
  "SUDO apt-get -q -y install ack-grep",
15
- "RUN git clone --quiet git://github.com/gmate/gmate.git apps_root/gmate",
15
+ "RUN test -d apps_root/gmate && (cd apps_root/gmate && git pull) || git clone --quiet git://github.com/gmate/gmate.git apps_root/gmate",
16
16
  "RUN cd apps_root/gmate && echo \\n | ./install.sh",
17
17
  'RUN gconftool-2 --set "/apps/gedit-2/plugins/active-plugins" --type list --list-type=string ["text_tools","smart_indent","align","rails_hotkeys","trailsave","gemini","rubyonrailsloader","gedit_openfiles","quickhighlightmode","completion","time","docinfo","filebrowser","snippets","spell","indent","tabswitch"]',
18
18
  'RUN gconftool-2 --set "/apps/gedit-2/plugins/smart_indent/haml_tab_size" --type int 2',
@@ -10,7 +10,7 @@ describe 'packages/apps' do
10
10
  $conf.commands.map(&:info).must_equal [
11
11
  "TASK gnumeric - Install gnumeric lightweight spreadsheet",
12
12
  "SUDO apt-get -q -y install gnumeric",
13
- "TASK gnumeric_associations - Setup file associations for Gnumeric",
13
+ "TASK gnumeric_assoc - Setup file associations for Gnumeric",
14
14
  "RUN grep \"application/x-gnumeric=gnumeric.desktop\" .local/share/applications/mimeapps.list || echo \"application/x-gnumeric=gnumeric.desktop\" >> .local/share/applications/mimeapps.list",
15
15
  "RUN grep \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet=gnumeric.desktop\" .local/share/applications/mimeapps.list || echo \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet=gnumeric.desktop\" >> .local/share/applications/mimeapps.list",
16
16
  "RUN grep \"text/csv=gnumeric.desktop\" .local/share/applications/mimeapps.list || echo \"text/csv=gnumeric.desktop\" >> .local/share/applications/mimeapps.list",
@@ -3,8 +3,8 @@ require 'spec_helper'
3
3
  describe 'packages/nginx' do
4
4
  before(:each) do
5
5
  load_package('nginx')
6
- $conf.from_hash(:webserver => {:name => 'nginx', :version => '1.0.2', :path => 'nginx_path', :conf_path => 'conf',
7
- :url => 'nginx_url/package', :src_path => '/usr/local/src/nginx-1.2.3', :modules => '--with-http_ssl_module'})
6
+ $conf.from_hash(webserver: {name: 'nginx', version: '1.2.3',
7
+ path: 'nginx_path', conf_path: 'conf', modules: '--with-http_ssl_module'})
8
8
  $conf.from_hash(:passenger => {:nginx => '/passenger/path/ext/nginx'})
9
9
  FileUtils.mkdir_p 'nginx'
10
10
  File.open('nginx/nginx.conf.erb', 'w') {|f| f.puts 'the template' }
@@ -16,7 +16,7 @@ describe 'packages/nginx' do
16
16
  eval_package
17
17
  $conf.commands.map(&:info).join("\n").must_equal [
18
18
  "TASK nginx - Download and configure Nginx",
19
- "SUDO cd /usr/local/src && wget nginx_url/package && tar -zxf package && rm package && cd -",
19
+ "SUDO cd /usr/local/src && wget http://nginx.org/download/nginx-1.2.3.tar.gz && tar -zxf nginx-1.2.3.tar.gz && rm nginx-1.2.3.tar.gz && cd -",
20
20
  'SUDO cd /usr/local/src/nginx-1.2.3 && ./configure --with-http_ssl_module --add-module=/passenger/path/ext/nginx && make && make install',
21
21
  "UPLOAD buffer from nginx/nginx.conf.erb to /tmp/nginx.conf",
22
22
  "SUDO cp -rf /tmp/nginx.conf nginx_path/conf/nginx.conf",
@@ -32,7 +32,7 @@ describe 'packages/nginx' do
32
32
  eval_package
33
33
  $conf.commands.map(&:info).join("\n").must_equal [
34
34
  "TASK nginx - Download and configure Nginx",
35
- "SUDO cd /usr/local/src && wget nginx_url/package && tar -zxf package && rm package && cd -",
35
+ "SUDO cd /usr/local/src && wget http://nginx.org/download/nginx-1.2.3.tar.gz && tar -zxf nginx-1.2.3.tar.gz && rm nginx-1.2.3.tar.gz && cd -",
36
36
  'SUDO cd /usr/local/src/nginx-1.2.3 && ./configure --with-http_ssl_module --add-module=/passenger/path/ext/nginx && make && make install',
37
37
  "UPLOAD buffer from nginx/nginx.conf.erb to /tmp/nginx.conf",
38
38
  "SUDO cp -rf /tmp/nginx.conf nginx_path/conf/nginx.conf",
@@ -5,18 +5,28 @@ describe 'packages/rbenv' do
5
5
  load_package('rbenv')
6
6
  $conf.ruby = AppConf.new
7
7
  $conf.ruby.version = '1.9.2'
8
- $conf.ruby.full_version = '1.9.2-p290'
8
+ $conf.ruby.build = 'p290'
9
+ end
10
+
11
+ it 'sets gems_path' do
12
+ eval_package
13
+ $conf.ruby.gems_path.must_equal '.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems'
14
+ end
15
+
16
+ it 'sets executable' do
17
+ eval_package
18
+ $conf.ruby.executable.must_equal '.rbenv/versions/1.9.2-p290/bin/ruby'
9
19
  end
10
20
 
11
21
  it 'adds the following commands' do
12
22
  eval_package
13
23
  $conf.commands.map(&:info).join("\n").must_equal [
14
- 'TASK rbenv - Install ruby-build, rbenv, ruby 1.9.2 and Bundler',
24
+ 'TASK rbenv - Install ruby-build, rbenv, ruby 1.9.2-p290 and Bundler',
15
25
  "SUDO apt-get -q -y install git-core",
16
26
  "SUDO apt-get -q -y install curl",
17
- 'RUN git clone --quiet git://github.com/sstephenson/ruby-build.git',
27
+ 'RUN test -d ruby-build && (cd ruby-build && git pull) || git clone --quiet git://github.com/sstephenson/ruby-build.git',
18
28
  'SUDO cd ~/ruby-build && ./install.sh',
19
- 'RUN git clone --quiet git://github.com/sstephenson/rbenv.git ~/.rbenv',
29
+ 'RUN test -d ~/.rbenv && (cd ~/.rbenv && git pull) || git clone --quiet git://github.com/sstephenson/rbenv.git ~/.rbenv',
20
30
  'RUN grep "PATH=.bin/safe/../../.bin:\\$HOME/.rbenv/bin:\\$HOME/.rbenv/shims:\\$PATH" ~/.profile || echo "PATH=.bin/safe/../../.bin:\\$HOME/.rbenv/bin:\\$HOME/.rbenv/shims:\\$PATH" >> ~/.profile',
21
31
 
22
32
  'RUN $HOME/.rbenv/bin/rbenv install 1.9.2-p290',
@@ -3,13 +3,23 @@ require 'spec_helper'
3
3
  describe 'packages/rvm' do
4
4
  before(:each) do
5
5
  load_package('rvm')
6
- $conf.from_hash(:ruby => {:version => '1.9'})
7
- $conf.from_hash(:rvm => {:url => 'rvm_url', :version => '1.0'})
6
+ $conf.from_hash(ruby: {version: '1.9.2', build: 'p290'})
7
+ $conf.from_hash(rvm: {version: '1.0'})
8
+ end
9
+
10
+ it 'sets gems_path' do
11
+ eval_package
12
+ $conf.ruby.gems_path.must_equal '.rvm/gems/1.9.2-p290/@global/gems'
13
+ end
14
+
15
+ it 'sets executable' do
16
+ eval_package
17
+ $conf.ruby.executable.must_equal '.rvm/wrappers/1.9.2-p290@global/ruby'
8
18
  end
9
19
 
10
20
  it 'adds the following commands' do
11
21
  eval_package
12
- $conf.commands.map(&:info).must_equal [
22
+ $conf.commands.map(&:info).join("\n").must_equal [
13
23
  "TASK rvm - Install RVM",
14
24
  "SUDO apt-get -q -y install git-core",
15
25
  'RUN bash -s 1.0 < <(wget -q https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer )',
@@ -20,12 +30,12 @@ describe 'packages/rvm' do
20
30
  "TASK rvm_prompt_off - turn off trust prompting for new .rvmrc files",
21
31
  "RUN grep \"export rvm_trust_rvmrcs_flag=1\" .rvmrc || echo \"export rvm_trust_rvmrcs_flag=1\" >> .rvmrc",
22
32
 
23
- 'TASK ruby - Install Ruby, make 1.9@global the default and install Bundler',
24
- 'RUN rvm install 1.9',
25
- 'RUN rvm 1.9@global --default',
33
+ 'TASK ruby - Install Ruby, make 1.9.2-p290@global the default and install Bundler',
34
+ 'RUN rvm install 1.9.2',
35
+ 'RUN rvm 1.9.2@global --default',
26
36
  'UPLOAD buffer from .gemrc to .gemrc',
27
37
  'RUN gem install bundler'
28
- ]
38
+ ].join("\n")
29
39
  end
30
40
  end
31
41
 
@@ -9,7 +9,7 @@ describe 'packages/sqlserver' do
9
9
  eval_package
10
10
  $conf.commands.map(&:info).must_equal [
11
11
  "TASK sqlserver - Download and build sqlserver driver",
12
- "RUN git clone --quiet git://github.com/rails-sqlserver/tiny_tds.git",
12
+ "RUN test -d tiny_tds && (cd tiny_tds && git pull) || git clone --quiet git://github.com/rails-sqlserver/tiny_tds.git",
13
13
  "RUN cd tiny_tds && rake compile && rake native gem"
14
14
  ]
15
15
  end
@@ -3,12 +3,14 @@ require 'spec_helper'
3
3
  describe 'packages/unison' do
4
4
  before(:each) do
5
5
  load_package('unison')
6
+ $conf.machine = AppConf.new
7
+ $conf.machine.user = 'username'
6
8
  end
7
9
 
8
10
  it 'adds the following commands' do
9
11
  eval_package
10
12
  $conf.commands.map(&:info).must_equal [
11
- "TASK unison - Install unison two way file sync and set it to run hourly. Config in users/user/.unison/default.prf",
13
+ "TASK unison - Install and configure Unison (users/username/.unison/default.prf)",
12
14
  "SUDO apt-get -q -y install unison",
13
15
  "RUN echo '30 18 * * * /usr/bin/unison' | crontab"
14
16
  ]
@@ -27,7 +27,7 @@ describe 'packages/webapps' do
27
27
  $conf.environment = 'production'
28
28
  eval_package
29
29
  $conf.commands.map(&:info).join("\n").must_equal [
30
- "TASK webapps - Sets up Web apps in config/webapps.yml using app_server.conf.erb. Copies SSL certs.",
30
+ "TASK webapps - Sets up Web apps in config/webapps.yml using app_server.conf.erb",
31
31
  "SUDO mkdir -p nginx_path/servers",
32
32
  "RUN mkdir -p /home/users/application/releases",
33
33
  "RUN mkdir -p /home/users/application/shared/config",
@@ -54,9 +54,9 @@ describe 'packages/webapps' do
54
54
  eval_package
55
55
  commandline = $conf.commands.map(&:info).join("\n")
56
56
  commandline.must_equal [
57
- "TASK webapps - Sets up Web apps in config/webapps.yml using app_server.conf.erb. Copies SSL certs.",
57
+ "TASK webapps - Sets up Web apps in config/webapps.yml using app_server.conf.erb",
58
58
  "SUDO mkdir -p nginx_path/servers",
59
- "RUN git clone --quiet --branch master github.com/project /home/users/application",
59
+ "RUN test -d /home/users/application && (cd /home/users/application && git pull) || git clone --quiet --branch master github.com/project /home/users/application",
60
60
  "RUN cd /home/users/application && $HOME/.rbenv/bin/rbenv exec bundle",
61
61
  "RUN cd /home/users/application && $HOME/.rbenv/bin/rbenv exec bundle --binstubs=.bin",
62
62
  "UPLOAD buffer from nginx/app_server.conf.erb to /tmp/application.conf",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: machines
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-13 00:00:00.000000000 Z
12
+ date: 2012-09-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -318,7 +318,8 @@ dependencies:
318
318
  description: Install and configure Ubuntu desktops, laptops, servers and cloud instances.
319
319
  Install software, configure settings, preferences, keys, projects, applications,
320
320
  scripts, etc...
321
- email: phil@electricvisions.com
321
+ email: !binary |-
322
+ cGhpbEBlbGVjdHJpY3Zpc2lvbnMuY29t
322
323
  executables:
323
324
  - machines
324
325
  extensions: []
@@ -326,6 +327,7 @@ extra_rdoc_files: []
326
327
  files:
327
328
  - .gitignore
328
329
  - .yardopts
330
+ - CHANGELOG.md
329
331
  - EXAMPLES.md
330
332
  - Gemfile
331
333
  - Guardfile
@@ -518,7 +520,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
518
520
  version: '0'
519
521
  segments:
520
522
  - 0
521
- hash: 1670062508932557270
523
+ hash: 4483574387537312947
522
524
  required_rubygems_version: !ruby/object:Gem::Requirement
523
525
  none: false
524
526
  requirements: