├── .gitignore
├── .lvimrc
├── Gemfile
├── Gemfile.lock
├── LICENSE.md
├── Makefile
├── README.md
├── Vagrantfile
├── bin
├── .gitignore
└── roundup
├── test
└── nameservers-test.sh
├── ubuntu-extras.sh
├── ubuntu-postfix.sh
├── ubuntu-postgres.sh
├── ubuntu.sh
├── vagrant.rb
└── vendor
└── ruby
└── .gitignore
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle
2 | .vagrant
3 |
--------------------------------------------------------------------------------
/.lvimrc:
--------------------------------------------------------------------------------
1 | let s:roundup_command = 'bin/roundup'
2 |
3 | let VimuxResetSequence = "q C-c C-u"
4 |
5 | augroup runTestOnSave
6 | au!
7 | au BufWritePost *-test.sh call VimuxRunCommand(s:roundup_command . ' ' . expand('%:p:~:.'))
8 | augroup END
9 |
10 | augroup reloadLocalConfiguration
11 | au!
12 | au BufWritePost .lvimrc source %
13 | augroup END
14 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source :rubygems
2 |
3 | gem 'vagrant', '~>1.0.4'
4 | gem 'vagrant-vbguest', '~>0.3.5'
5 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: http://rubygems.org/
3 | specs:
4 | archive-tar-minitar (0.5.2)
5 | childprocess (0.3.5)
6 | ffi (~> 1.0, >= 1.0.6)
7 | erubis (2.7.0)
8 | ffi (1.1.5)
9 | i18n (0.6.1)
10 | json (1.5.4)
11 | log4r (1.1.10)
12 | net-scp (1.0.4)
13 | net-ssh (>= 1.99.1)
14 | net-ssh (2.2.2)
15 | vagrant (1.0.5)
16 | archive-tar-minitar (= 0.5.2)
17 | childprocess (~> 0.3.1)
18 | erubis (~> 2.7.0)
19 | i18n (~> 0.6.0)
20 | json (~> 1.5.1)
21 | log4r (~> 1.1.9)
22 | net-scp (~> 1.0.4)
23 | net-ssh (~> 2.2.2)
24 | vagrant-vbguest (0.3.5)
25 |
26 | PLATFORMS
27 | ruby
28 |
29 | DEPENDENCIES
30 | vagrant (~> 1.0.4)
31 | vagrant-vbguest (~> 0.3.5)
32 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License
2 | ===============
3 |
4 | > Copyright (c) 2011 Stan Angeloff
5 | >
6 | > Permission is hereby granted, free of charge, to any person obtaining a copy
7 | > of this software and associated documentation files (the "Software"), to deal
8 | > in the Software without restriction, including without limitation the rights
9 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | > copies of the Software, and to permit persons to whom the Software is
11 | > furnished to do so, subject to the following conditions:
12 | >
13 | > The above copyright notice and this permission notice shall be included in
14 | > all copies or substantial portions of the Software.
15 | >
16 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | > THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | BIN_PATH := $(CURDIR)/bin
2 | VENDOR_PATH := $(CURDIR)/vendor
3 |
4 | .PHONY: default install-ruby-dependencies vagrant-provision install clean test
5 |
6 | default:
7 | @echo "No default $(MAKE) target configured."
8 | @exit 1
9 |
10 | required-dependency = \
11 | echo -n "Checking if '$(1)' is available... " ; \
12 | $(eval COMMAND := which '$(1)') \
13 | if $(COMMAND) >/dev/null; then \
14 | $(COMMAND) ; \
15 | else \
16 | echo "fail:" ; \
17 | echo ; \
18 | echo " $(COMMAND)" ; \
19 | echo ; \
20 | echo "You must install '$(2)' before you could continue." ; \
21 | exit 1; \
22 | fi
23 |
24 | install-ruby-gem-if-missing = \
25 | echo -n "Checking if '$(1)' RubyGem is available... " ; \
26 | $(eval GEM_VERSION := ruby -rubygems -e "puts Gem::Specification::find_by_name('$(1)').version") \
27 | if $(GEM_VERSION) 1>/dev/null 2>&1; then \
28 | $(GEM_VERSION) ; \
29 | else \
30 | $(eval COMMAND_INSTALL := gem install --remote --no-ri --no-rdoc '$(1)') \
31 | echo "nope." ; \
32 | echo -n "Installing '$(1)' RubyGem... " ; \
33 | $(COMMAND_INSTALL) 1>/dev/null ; \
34 | $(GEM_VERSION) ; \
35 | fi
36 |
37 | install-ruby-dependencies: $(VENDOR_PATH)/ruby
38 | $(VENDOR_PATH)/ruby:
39 | @$(call required-dependency,ruby,Ruby)
40 | @$(call required-dependency,gem,RubyGems)
41 | @$(call install-ruby-gem-if-missing,bundler)
42 | @$(call required-dependency,bundle,Bundler)
43 | @echo -n 'Installing RubyGem dependencies... '
44 | @bundle install --path '$(VENDOR_PATH)' --binstubs '$(BIN_PATH)' 1>/dev/null
45 | @echo 'OK'
46 |
47 | vagrant-provision: install-ruby-dependencies $(CURDIR)/.vagrant
48 | $(CURDIR)/.vagrant:
49 | @'$(BIN_PATH)/vagrant' up
50 |
51 | install: vagrant-provision
52 |
53 | clean:
54 | @if [ -f '$(CURDIR)/.vagrant' ]; then \
55 | '$(BIN_PATH)/vagrant' destroy --force ; \
56 | rm '$(CURDIR)/.vagrant' ; \
57 | fi
58 | @if [ -d '$(VENDOR_PATH)/ruby' ]; then \
59 | rm -Rf '$(VENDOR_PATH)/ruby'* ; \
60 | fi
61 | @for file in bin/*; do \
62 | grep "$$( basename "$$file" )" bin/.gitignore >/dev/null || rm "$$file" ; \
63 | done
64 |
65 | test: install
66 | @for file in test/*-test.sh; do \
67 | '$(BIN_PATH)/vagrant' ssh -c '( cd /vagrant && bin/roundup '"$$file"' )' ; \
68 | done
69 |
70 |
71 | # vim: ts=2 sw=2 noet
72 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | vagrant-shell-scripts
2 | =====================
3 |
4 | A collection of scripts (Ubuntu-only at this time) to ease Vagrant box provisioning using the [shell][shell] method.
5 |
6 | [shell]: http://vagrantup.com/v1/docs/provisioners/shell.html
7 |
8 | Usage
9 | -----
10 |
11 | 1. Place on top of your `Vagrantfile` before `Vagrant.configure(..)`:
12 |
13 | ```ruby
14 | require File.join(File.dirname(__FILE__), 'path/to/vagrant-shell-scripts/vagrant')
15 | ```
16 |
17 | 2. Place at the end of your `Vagrantfile` before the last `end`:
18 |
19 | ```ruby
20 | config.vm.provision :shell do |shell|
21 | vagrant_shell_scripts_configure(
22 | shell,
23 | File.dirname(__FILE__),
24 | 'path/to/provisioning_script'
25 | )
26 | end
27 | ```
28 |
29 | 3. Place on top of your provisioning script:
30 |
31 | ```bash
32 | #!/usr/bin/env bash
33 |
34 | <%= import 'ubuntu.sh' %>
35 | ```
36 |
37 | Functions
38 | ---------
39 |
40 | ### Utility
41 |
42 | - `script-argument-create(value, expected)`
43 |
44 | Return the value of the first argument or exit with an error message if empty.
45 |
46 | Example:
47 |
48 | ```bash
49 | MYSQL_DBNAME=$( script-argument-create "$1" 'a MySQL database name as the first argument' )
50 | ```
51 |
52 | ### Nameservers
53 |
54 | - `nameservers-local-purge`
55 |
56 | Drop all local `10.0.x.x` nameservers in `resolv.conf`.
57 |
58 | - `nameservers-append(ip)`
59 |
60 | Set up an IP as a DNS name server if not already present in `resolv.conf`.
61 |
62 | Example (set up Google DNS):
63 |
64 | ```bash
65 | nameservers-local-purge
66 | nameservers-append '8.8.8.8'
67 | nameservers-append '8.8.4.4'
68 | ```
69 |
70 | ### Aptitude
71 |
72 | - `apt-mirror-pick(iso2)`
73 |
74 | Set up a specific two-letter country code as the preferred `aptitude` mirror.
75 |
76 | Example (use Ubuntu Bulgaria as a mirror):
77 |
78 | ```bash
79 | apt-mirror-pick 'bg'
80 | ```
81 |
82 | - `apt-packages-repository(repository[, repository[, ...]][, key[, server = 'keyserver.ubuntu.com']])`
83 |
84 | Add a custom repository as a software source.
85 |
86 | Each `repository` is a line as it would appear in your `/etc/apt/sources.list` file, e.g., `deb URL DISTRIBUTION CATEGORY`.
87 |
88 | `key` and `server` are the signing key and server.
89 | You need to add the key to your system so Ubuntu can verify the packages from the repository.
90 |
91 | Example (install MongoDB from official repositories):
92 |
93 | ```
94 | apt-packages-repository 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' '7F0CEB10'
95 | apt-packages-update
96 | apt-packages-install mongodb-10gen
97 | ```
98 |
99 | - `apt-packages-ppa(repository[, key[, server = 'keyserver.ubuntu.com']])`
100 |
101 | Add a Launchpad PPA as a software source.
102 |
103 | The `repository` is the Launchpad user and project name, e.g., `chris-lea/node.js`.
104 |
105 | `key` and `server` are the signing key and server.
106 | The key needs to added to your system so Ubuntu can verify the packages from the PPA.
107 |
108 | Example (install Node.js from unofficial PPA):
109 |
110 | ```bash
111 | apt-packages-ppa 'chris-lea/node.js'
112 | apt-packages-update
113 | apt-packages-install \
114 | nodejs \
115 | npm
116 | ```
117 |
118 | - `apt-packages-update`
119 |
120 | Update `aptitude` packages without any prompts.
121 |
122 | - `apt-packages-install(package[, package[, ...]])`
123 |
124 | Perform an unattended installation of package(s).
125 |
126 | Example:
127 |
128 | ```bash
129 | apt-packages-update
130 | apt-packages-install \
131 | apache2-mpm-worker \
132 | apache2-suexec-custom \
133 | mysql-server-5.1 \
134 | libapache2-mod-fastcgi \
135 | php5-cgi
136 | ```
137 |
138 | - `apt-packages-purge(package[, package[, ...]])`
139 |
140 | Perform an unattended complete removal (purge) of package(s).
141 |
142 | Example (purge `apache2` unnecessarily installed as part of the `php5` meta-package):
143 |
144 | ```bash
145 | apt-packages-update
146 | apt-packages-install php5
147 | apt-packages-purge 'apache2*'
148 | ```
149 |
150 | ### System
151 |
152 | - `system-upgrade()`
153 |
154 | Run a complete system upgrade.
155 | Be extremely careful as this operation can break packages, e.g., VirtualBox Guest Additions if a new kernel is installed.
156 |
157 | Example:
158 |
159 | ```bash
160 | apt-packages-update
161 | system-upgrade
162 | ```
163 |
164 | - `system-service(name, action)`
165 |
166 | Command a system service, e.g., `apache2`, `mysql`, etc.
167 |
168 | Example:
169 |
170 | ```bash
171 | system-service php5-fpm restart
172 | ```
173 |
174 | - `system-escape([glue = '-'])`
175 |
176 | Escape and normalize a string so it can be used safely in file names, etc.
177 | You can optionally specify `glue` to be a different character, e.g., an underscore `_` if you are using the result as part of a variable name.
178 |
179 | Example:
180 |
181 | ```bash
182 | echo "Hello World!" | system-escape # prints 'hello-world'
183 | ```
184 |
185 | ### Default Commands
186 |
187 | - `alternatives-ruby-install(version[, bin_path = '/usr/bin/'[, man_path = '/usr/share/man/man1/'[, priority = 500]]])`
188 |
189 | Update the Ruby binary link to point to a specific version.
190 |
191 | Example:
192 |
193 | ```bash
194 | apt-packages-install \
195 | ruby1.9.1 \
196 | ruby1.9.1-dev \
197 | rubygems1.9.1
198 | alternatives-ruby-install 1.9.1
199 | ```
200 |
201 | - `alternatives-ruby-gems()`
202 |
203 | Create symbolic links to RubyGems binaries.
204 |
205 | By default, RubyGems on Ubuntu does not create binaries on `$PATH`.
206 | Using this function would create a symbolic link in the directory of your `ruby` binary which is assumed to be on `$PATH`.
207 |
208 | Example (install stable versions of Sass and link it as `/usr/bin/sass`):
209 |
210 | ```bash
211 | apt-packages-install \
212 | ruby1.9.1 \
213 | ruby1.9.1-dev \
214 | rubygems1.9.1
215 | alternatives-ruby-install 1.9.1
216 | github-gems-install 'nex3/sass'
217 | alternatives-ruby-gems 'sass'
218 | ```
219 |
220 | ### Apache
221 |
222 | - `apache-modules-enable(module[, module[, ...]])`
223 |
224 | Enable a list of Apache modules. This requires a server restart.
225 |
226 | - `apache-modules-disable(module[, module[, ...]])`
227 |
228 | Disable a list of Apache modules. This requires a server restart.
229 |
230 | - `apache-sites-enable(name[, name[, ...]])`
231 |
232 | Enable a list of Apache sites. This requires a server restart.
233 |
234 | - `apache-sites-disable(name[, name[, ...]])`
235 |
236 | Disable a list of Apache sites. This requires a server restart.
237 |
238 | - `[PHP=/path/to/binary] apache-sites-create(name[, path = name[, user = name[, group = user[, verbosity = info]]]])`
239 |
240 | Create a new Apache site and set up Fast-CGI components.
241 |
242 | The function creates a new file under `sites-available/name` where `name` is the first argument to the function.
243 |
244 | To set up Fact-CGI, a new directory `.cgi-bin` is created in `path` if it doesn't already exist.
245 |
246 | The virtual host is pointed to `path` or `/name` if `path` is omitted.
247 |
248 | **SuExec is required** and a user and group options are set up so permissions can work properly.
249 |
250 | If you prefix the function with `PHP=/path/to/binary`, PHP will be enabled through a Fast-CGI script in `.cgi-bin`. You must have PHP installed.
251 |
252 | Example (create a new `vagrant` website from `/vagrant` and enable PHP):
253 |
254 | ```bash
255 | PHP=/usr/bin/php-cgi apache-sites-create 'vagrant'
256 | apache-sites-enable vagrant
257 | apache-restart
258 | ```
259 |
260 | - `apache-restart`
261 |
262 | Restart the Apache server and reload with new configuration.
263 |
264 | Example:
265 |
266 | ```bash
267 | apache-modules-enable actions rewrite fastcgi suexec
268 | apache-restart
269 | ```
270 |
271 | ### Nginx
272 |
273 | - `nginx-sites-enable(name[, name[, ...]])`
274 |
275 | Enable a list of Nginx sites. This requires a server restart.
276 |
277 | - `nginx-sites-disable(name[, name[, ...]])`
278 |
279 | Disable a list of Nginx sites. This requires a server restart.
280 |
281 | - `[PHP=any-value] nginx-sites-create(name[, path = name[, user = name[, group = user[, index = 'index.html'[, verbosity = info]]]]])`
282 |
283 | Create a new Nginx site and set up Fast-CGI components.
284 |
285 | The function creates a new file under `sites-available/name` where `name` is the first argument to the function.
286 |
287 | The virtual host is pointed to `path` or `/name` if `path` is omitted.
288 |
289 | If you prefix the function with `PHP=any-value`, PHP will be enabled through a PHP-FPM. You must have `php5-fpm` installed.
290 |
291 | The arguments `user` and `group` are used to re-configure PHP-FPM's default `www` pool to run under the given account.
292 |
293 | You can optionally specify space-separated directory index files to look for if a file name wasn't specified in the request.
294 |
295 | Example (create a new `vagrant` website from `/vagrant` and enable PHP):
296 |
297 | ```bash
298 | PHP=/usr/bin/php5-fpm nginx-sites-create 'vagrant'
299 | nginx-sites-enable vagrant
300 | nginx-restart
301 | ```
302 |
303 | ### PHP
304 |
305 | - `php-settings-update(name, value)`
306 |
307 | Update a PHP setting value.
308 | This function will look for all `php.ini` files in `/etc`.
309 | For each file, a `conf.d` directory would be created in the parent directory (if one doesn't already exist) and inside a file specifying the setting name/value will be placed.
310 |
311 | Example (create a default timezone):
312 |
313 | ```bash
314 | php-settings-update 'date.timezone' 'Europe/London'
315 | ```
316 |
317 | - `php-pecl-install(extension[, extension[, ...]])`
318 |
319 | Install (download, build, install) and enable a PECL extension.
320 |
321 | You may optionally specify the state/version using '@version'.
322 |
323 | Example (install MongoDB driver):
324 |
325 | ```bash
326 | apt-packages-install \
327 | php5-dev \
328 | php-pear
329 | php-pecl-install mongo
330 |
331 | # Install a specific version of 'proctitle'.
332 | php-pecl-install proctitle@0.1.2
333 | ```
334 |
335 | - `php-fpm-restart`
336 |
337 | Restart the PHP5-FPM server.
338 |
339 | ### MySQL
340 |
341 | - `mysql-database-create(name[, charset = 'utf8'[, collision = 'utf8_general_ci']])`
342 |
343 | Create a database if one doesn't already exist.
344 |
345 | - `mysql-database-restore(name, path)`
346 |
347 | Restore a MySQL database from an archived backup.
348 |
349 | This function scans for files in `path` (non-recursive) that match the format:
350 |
351 | ```
352 | ^[0-9]{8}-[0-9]{4}.tar.bz2$
353 | ```
354 |
355 | e.g., `20120101-1200.tar.bz2`. The matching files are sorted based on the file name and the newest one is picked.
356 |
357 | If the database `name` is empty, i.e., it doesn't contain any tables, the latest backup is piped to `mysql`.
358 |
359 | Example (where `/database-backups` is shared from `Vagrantfile`):
360 |
361 | ```bash
362 | mysql-database-restore 'project' '/database-backups'
363 | ```
364 |
365 | - `mysql-remote-access-allow`
366 |
367 | Allow remote passwordless `root` access for anywhere.
368 |
369 | This is only a good idea if the box is configured in 'Host-Only' network mode.
370 |
371 | - `mysql-restart`
372 |
373 | Restart the MySQL server and reload with new configuration.
374 |
375 | ### RubyGems
376 |
377 | - `ruby-gems-version(package)`
378 |
379 | Check the installed version of a package, if any.
380 |
381 | When a package is not installed, `0.0.0` is returned.
382 |
383 | - `ruby-gems-install(package[, arg[, ...]])`
384 |
385 | Perform an unattended installation of a package.
386 |
387 | If a package is already installed, it will not be re-installed or upgraded.
388 |
389 | Example:
390 |
391 | ```bash
392 | apt-packages-install \
393 | ruby1.9.1 \
394 | ruby1.9.1-dev \
395 | rubygems1.9.1
396 | alternatives-ruby-install 1.9.1
397 | ruby-gems-install pkg-config
398 | ruby-gems-install sass --version '3.2.1'
399 | ```
400 |
401 | ### NPM (Node Package Manager)
402 |
403 | - `npm-packages-install(package[, package[, ...]])`
404 |
405 | Perform an unattended **global** installation of package(s).
406 |
407 | Example (install UglifyJS):
408 |
409 | ```bash
410 | apt-packages-ppa 'chris-lea/node.js'
411 | apt-packages-update
412 | apt-packages-install \
413 | nodejs \
414 | npm
415 | npm-packages-install uglify-js
416 | ```
417 |
418 | ### GitHub
419 |
420 | - `github-gems-install(repository[, repository[, ...]])`
421 |
422 | Download and install RubyGems from GitHub.
423 |
424 | The format of each `repository` is `user/project[@branch]` where `branch` can be omitted and defaults to `master`.
425 |
426 | If a package is already installed, it will not be re-installed or upgraded.
427 |
428 | Example (install unstable versions of Sass and Compass):
429 |
430 | ```bash
431 | apt-packages-install \
432 | ruby1.9.1 \
433 | ruby1.9.1-dev \
434 | rubygems1.9.1
435 | alternatives-ruby-install 1.9.1
436 | github-gems-install \
437 | 'ttilley/fssm' \
438 | 'nex3/sass@master' \
439 | 'wvanbergen/chunky_png' \
440 | 'chriseppstein/compass@master'
441 | ```
442 |
443 | - `github-php-extension-install(specification[, specification[, ...]])`
444 |
445 | Download and install PHP extensions from GitHub.
446 |
447 | The `specification` is a string which contains the repository name (mandatory), version (optional) and `./configure` arguments (optional):
448 | `repository@version --option --option argument`
449 |
450 | Example (install native PHP Redis extension from GitHub):
451 |
452 | ```bash
453 | github-php-extension-install 'nicolasff/phpredis'
454 |
455 | # Use a specific commit and pass arguments to `./configure`.
456 | github-php-extension-install 'nicolasff/phpredis@5e5fa7895f --enable-redis-igbinary'
457 | ```
458 |
459 | ### Environment
460 |
461 | - `env-append(env_key, env_value[, env_comment])`
462 |
463 | Append an environment line to the global Bash/Zsh profile.
464 |
465 | `env_key` can be anything, but the following values have special meaning:
466 |
467 | * `PATH` - extend the `PATH` environment variable with a new directory,
468 | * `source` - include an external file (be careful not to `set -e`).
469 |
470 | Any other key would be `export`ed when a new session is initialized.
471 |
472 | Example (set `JAVA_HOME` for scripts that rely on it):
473 |
474 | ```bash
475 | env-append 'JAVA_HOME' "/usr/lib/jvm/java-7-openjdk-$( uname -i )"
476 |
477 | # $ tail -2 /etc/profile
478 | # # AUTO-GENERATED: JAVA_HOME.
479 | # [ -z "$JAVA_HOME" ] && export JAVA_HOME='/usr/lib/jvm/java-7-openjdk-i386'
480 | ```
481 |
482 | Extras
483 | ------
484 |
485 | The `ubuntu-extras.sh` file provides useful but rarely used commands.
486 |
487 | ### Archives
488 |
489 | - `archive-file-unpack(file[, directory])`
490 |
491 | Unpack the given archive `file` in `directory` (created if missing). The format is guessed from the file extension.
492 |
493 | Example:
494 |
495 | ```bash
496 | archive-file-unpack 'rsync-HEAD.tar.gz' '/tmp/'
497 | ```
498 |
499 | ### Packages (e.g., ZIP distributions)
500 |
501 | - `package-ignore-preserve(package_name, package_path, preserved_path)`
502 |
503 | Read a `.gitignore` file in the given `package_path` and remove matching files in `{preserved_path}/{package_name}-*`.
504 | This is useful when the contents of an unpacked archive are to be copied over a version-controlled directory and certain files should **not** be overwritten.
505 |
506 | Example:
507 |
508 | ```bash
509 | package-ignore-preserve 'hadoop' 'vendor/hadoop' '/tmp/hadoop-download'
510 | ```
511 |
512 | - `package-uri-download(uri[, target])`
513 |
514 | Download the file at `uri` to the given directory `target` or `STDOUT`.
515 |
516 | Example:
517 |
518 | ```bash
519 | package-uri-download 'http://www.samba.org/ftp/rsync/nightly/rsync-HEAD.tar.gz' '/tmp'
520 | ```
521 |
522 | - `package-uri-install(package_name, package_uri[, package_index[, package_path[, package_version]]])`
523 |
524 | Download, unpack and install the package at `package_uri` to `package_path`.
525 | This function does a lot internally and can be used to automate installations of certain ZIP distributions.
526 | The URI and path support placeholders:
527 |
528 | * `%name` will be substituted with the `package_name`,
529 | * `%path` for `package_path`,
530 | * `%version` for `package_version`.
531 |
532 | `package_index` should be a file which determines if the package has already been installed. It would usually point to a binary file inside the distribution archive. If omitted, it defaults to `{package_path}/bin/{package_name}`.
533 |
534 | If `package_path` is omitted, a variable `{PACKAGE_NAME}_PATH` is expected. E.g., if `package_name` is 'rsync' a variable named `RSYNC_PATH` must exist.
535 | If `package_version` is omitted, a variable `{PACKAGE_NAME}_VERSION` is expected.
536 |
537 | Requires `curl` to be installed.
538 |
539 | Example (installing Node.js from binary distribution):
540 |
541 | ```bash
542 | # {{{ Configuration
543 |
544 | NODE_PATH='/vagrant/vendor/node'
545 | NODE_VERSION=0.8.22
546 | NODE_PLATFORM=x86
547 |
548 | PHANTOMJS_VERSION=1.8.1
549 | PHANTOMJS_PATH='/vagrant/vendor/phantomjs'
550 | PHANTOMJS_PLATFORM=i686
551 |
552 | # }}}
553 |
554 | # Download and install Node.js from the official website.
555 | package-uri-install 'node' "http://nodejs.org/dist/v%version/node-v%version-linux-${NODE_PLATFORM}.tar.gz"
556 |
557 | # Download and install PhantomJS from the official website.
558 | package-uri-install 'phantomjs' "https://phantomjs.googlecode.com/files/phantomjs-%version-linux-${PHANTOMJS_PLATFORM}.tar.bz2"
559 |
560 | # Node.js installed as: /vagrant/vendor/node/bin/node
561 | # PhantomJS installed as: /vagrant/vendor/phantomjs/bin/phantomjs
562 | ```
563 |
564 | ### Temporary Files
565 |
566 | - `temporary-cleanup(tmp_path[, ...])`
567 |
568 | Delete the contents of each `tmp_path` and halt script with the exit code of the last executed command.
569 |
570 | Example (compile a project and clean up on failure):
571 |
572 | ```bash
573 | TMP_PATH="$( mktemp -d -t 'package-XXXXXXXX' )"
574 | git clone 'git://github.com/project/package.git' "$TMP_PATH"
575 | ( \
576 | cd "$TMP_PATH" && \
577 | ./configure && \
578 | make && make install \
579 | ) || temporary-cleanup "$TMP_PATH"
580 | ```
581 |
582 | PostgreSQL
583 | ----------
584 |
585 | The `ubuntu-postgres.sh` file provides functions for manipulating a PostgreSQL server instance.
586 |
587 | - `postgres-remote-access-allow()`
588 |
589 | Allow remote access for the private `192.168.1.1/22` network (`192.168.0.1` through `192.168.3.254`).
590 |
591 | This is only a good idea if the box is configured in 'Host-Only' network mode.
592 |
593 | - `postgres-password-reset(password)`
594 |
595 | Reset the `postgres` user server password. This also allows password-based authentication.
596 |
597 | Example:
598 |
599 | ```bash
600 | postgres-password-reset 'password'
601 | ```
602 |
603 | - `postgres-autovacuum-on()`
604 |
605 | Turn [autovacuuming](http://www.postgresql.org/docs/9.2/static/runtime-config-autovacuum.html) on.
606 |
607 | - `postgres-template-encoding(encoding = 'UTF8'[, ctype = 'en_GB.utf8'[, collate = ctype]])`
608 |
609 | Set the default database encoding and collision.
610 | Newly created databases will inherit those properties.
611 |
612 | Example (use UTF-8 for all new databases):
613 |
614 | ```bash
615 | postgres-template-encoding 'UTF8' 'en_GB.utf8'
616 | ```
617 |
618 | Postfix
619 | -------
620 |
621 | The `ubuntu-postfix.sh` file provides functions for manipulating an SMTP server instance using Postfix.
622 |
623 | - `smtp-sink-install(directory[, template = '%Y%m%d/%H%M.'[, port = 25[, user = 'vagrant'[, service = 'smtp-sink'[, backlog = 10]]]]])`
624 |
625 | Hassle-free SMTP in development. Create a new system service which logs all outgoing e-mails to disk.
626 |
627 | > `directory` specifies the process root directory.
628 | > The single-message files are created by expanding the `template` via strftime(3) and appending a pseudo-random hexadecimal number (example: "%Y%m%d%H/%M." expands into "2006081203/05.809a62e3"). If the template contains "/" characters, missing directories are created automatically.
629 |
630 | `port` specifies the port on which to listen.
631 | `user` switches the user privileges after opening the network socket. The user must have permissions to write in `directory`.
632 |
633 | `service` is an optional name of the system Upstart service. A file with this name is created under `/etc/init/`.
634 |
635 | `backlog` specifies the maximum length the queue of pending connections, as defined by the listen(2) system call.
636 |
637 | Example (log all messages to the `mail/` directory in the project root):
638 |
639 | ```bash
640 | smtp-sink-install '/vagrant/mail'
641 | ```
642 |
643 | The logged messages can be read using Thunderbird (append `.eml` to the file name).
644 |
645 | Environment
646 | -----------
647 |
648 | The following variables can be defined before including the `ubuntu.sh` script:
649 |
650 | - `SUDO[ = 'sudo']`
651 |
652 | Specify the prefix for all super-user commands. If your system is configured with no `sudo` command, use an empty value.
653 |
654 | Example (set up Google DNS on a system with no `sudo` command):
655 |
656 | ```bash
657 | SUDO=
658 |
659 | nameservers-local-purge
660 | nameservers-append '8.8.8.8'
661 | nameservers-append '8.8.4.4'
662 | ```
663 |
664 | - `COLORS[ = 'always']`
665 |
666 | Use terminal escape codes to print colourised output. If you wish to disable this, use a value other than `{always,yes,true,1}`.
667 |
668 | Example (turn off colours in output):
669 |
670 | ```bash
671 | COLORS=never
672 |
673 | apt-packages-update
674 | ```
675 |
676 | Goal
677 | ----
678 |
679 | As I explore different OSes, I hope to add support for more platforms.
680 |
681 | The functions should remain the same, so provisioning scripts are somewhat 'portable'.
682 |
683 | Full Example
684 | ------------
685 |
686 | The provisioning script below sets up Apache, SuExec, a virtual host for `/vagrant` (the default share) and remote `root` access to MySQL.
687 |
688 | ```bash
689 | #!/usr/bin/env bash
690 |
691 | # {{{ Ubuntu utilities
692 |
693 | <%= import 'vagrant-shell-scripts/ubuntu.sh' %>
694 |
695 | # }}}
696 |
697 | # Use Google Public DNS for resolving domain names.
698 | # The default is host-only DNS which may not be installed.
699 | nameservers-local-purge
700 | nameservers-append '8.8.8.8'
701 | nameservers-append '8.8.4.4'
702 |
703 | # Use a local Ubuntu mirror, results in faster downloads.
704 | apt-mirror-pick 'bg'
705 |
706 | # Update packages cache.
707 | apt-packages-update
708 |
709 | # Install VM packages.
710 | apt-packages-install \
711 | apache2-mpm-worker \
712 | apache2-suexec-custom \
713 | mysql-server-5.1 \
714 | libapache2-mod-fastcgi \
715 | php5-cgi \
716 | php5-gd \
717 | php5-mysql
718 |
719 | # Allow modules for Apache.
720 | apache-modules-enable actions rewrite fastcgi suexec
721 |
722 | # Replace the default Apache site.
723 | PHP=/usr/bin/php-cgi apache-sites-create 'vagrant'
724 | apache-sites-disable default default-ssl
725 | apache-sites-enable vagrant
726 |
727 | # Restart Apache web service.
728 | apache-restart
729 |
730 | # Allow unsecured remote access to MySQL.
731 | mysql-remote-access-allow
732 |
733 | # Restart MySQL service for changes to take effect.
734 | mysql-restart
735 | ```
736 |
737 | ### Copyright
738 |
739 | > Copyright (c) 2012 Stan Angeloff. See [LICENSE.md](https://github.com/StanAngeloff/vagrant-shell-scripts/blob/master/LICENSE.md) for details.
740 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby :
3 |
4 | Vagrant::Config.run do |config|
5 | # Every Vagrant virtual environment requires a box to build off of.
6 | config.vm.box = "ubuntu12.04-x86"
7 |
8 | # The url from where the 'config.vm.box' box will be fetched if it
9 | # doesn't already exist on the user's system.
10 | config.vm.box_url = "http://files.vagrantup.com/precise32.box"
11 | end
12 |
--------------------------------------------------------------------------------
/bin/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 | !roundup
4 |
--------------------------------------------------------------------------------
/bin/roundup:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # [r5]: roundup.5.html
3 | # [r1t]: roundup-1-test.sh.html
4 | # [r5t]: roundup-5-test.sh.html
5 | #
6 | # _(c) 2010 Blake Mizerany - MIT License_
7 | #
8 | # Spray **roundup** on your shells to eliminate weeds and bugs. If your shells
9 | # survive **roundup**'s deathly toxic properties, they are considered
10 | # roundup-ready.
11 | #
12 | # **roundup** reads shell scripts to form test plans. Each
13 | # test plan is sourced into a sandbox where each test is executed.
14 | #
15 | # See [roundup-1-test.sh.html][r1t] or [roundup-5-test.sh.html][r5t] for example
16 | # test plans.
17 | #
18 | # __Install__
19 | #
20 | # git clone http://github.com/bmizerany/roundup.git
21 | # cd roundup
22 | # make
23 | # sudo make install
24 | # # Alternatively, copy `roundup` wherever you like.
25 | #
26 | # __NOTE__: Because test plans are sourced into roundup, roundup prefixes its
27 | # variable and function names with `roundup_` to avoid name collisions. See
28 | # "Sandbox Test Runs" below for more insight.
29 |
30 | # Usage and Prerequisites
31 | # -----------------------
32 |
33 | # Exit if any following command exits with a non-zero status.
34 | set -e
35 |
36 | # The current version is set during `make version`. Do not modify this line in
37 | # anyway unless you know what you're doing.
38 | ROUNDUP_VERSION="0.0.5"
39 | export ROUNDUP_VERSION
40 |
41 | # Usage is defined in a specific comment syntax. It is `grep`ed out of this file
42 | # when needed (i.e. The Tomayko Method). See
43 | # [shocco](http://rtomayko.heroku.com/shocco) for more detail.
44 | #/ usage: roundup [--help|-h] [--version|-v] [plan ...]
45 |
46 | roundup_usage() {
47 | grep '^#/' <"$0" | cut -c4-
48 | }
49 |
50 | while test "$#" -gt 0
51 | do
52 | case "$1" in
53 | --help|-h)
54 | roundup_usage
55 | exit 0
56 | ;;
57 | --version|-v)
58 | echo "roundup version $ROUNDUP_VERSION"
59 | exit 0
60 | ;;
61 | --color)
62 | color=always
63 | shift
64 | ;;
65 | -)
66 | echo >&2 "roundup: unknown switch $1"
67 | exit 1
68 | ;;
69 | *)
70 | break
71 | ;;
72 | esac
73 | done
74 |
75 | # Consider all scripts with names matching `*-test.sh` the plans to run unless
76 | # otherwise specified as arguments.
77 | if [ "$#" -gt "0" ]
78 | then
79 | roundup_plans="$@"
80 | else
81 | roundup_plans="$(ls *-test.sh)"
82 | fi
83 |
84 | : ${color:="auto"}
85 |
86 | # Create a temporary storage place for test output to be retrieved for display
87 | # after failing tests.
88 | roundup_tmp="$PWD/.roundup.$$"
89 | mkdir -p $roundup_tmp
90 |
91 | trap "rm -rf \"$roundup_tmp\"" EXIT INT
92 |
93 | # __Tracing failures__
94 | roundup_trace() {
95 | # Delete the first two lines that represent roundups execution of the
96 | # test function. They are useless to the user.
97 | sed '1d' |
98 | # Delete the last line which is the "set +x" of the error trap
99 | sed '$d' |
100 | # Replace the rc=$? of the error trap with an verbose string appended
101 | # to the failing command trace line.
102 | sed '$s/.*rc=/exit code /' |
103 | # Trim the two left most `+` signs. They represent the depth at which
104 | # roundup executed the function. They also, are useless and confusing.
105 | sed 's/^++//' |
106 | # Indent the output by 4 spaces to align under the test name in the
107 | # summary.
108 | sed 's/^/ /' |
109 | # Highlight the last line in front of the exit code to bring notice to
110 | # where the error occurred.
111 | #
112 | # The sed magic puts every line into the hold buffer first, then
113 | # substitutes in the previous hold buffer content, prints that and starts
114 | # with the next cycle. At the end the last line (in the hold buffer)
115 | # is printed without substitution.
116 | sed -n "x;1!{ \$s/\(.*\)/$mag\1$clr/; };1!p;\$x;\$p"
117 | }
118 |
119 | # __Other helpers__
120 |
121 | # Track the test stats while outputting a real-time report. This takes input on
122 | # **stdin**. Each input line must come in the format of:
123 | #
124 | # # The plan description to be displayed
125 | # d
126 | #
127 | # # A passing test
128 | # p
129 | #
130 | # # A failed test
131 | # f
132 | roundup_summarize() {
133 | set -e
134 |
135 | # __Colors for output__
136 |
137 | # Use colors if we are writing to a tty device.
138 | if (test -t 1) || (test $color = always)
139 | then
140 | red=$(printf "\033[31m")
141 | grn=$(printf "\033[32m")
142 | mag=$(printf "\033[35m")
143 | clr=$(printf "\033[m")
144 | cols=$(tput cols)
145 | fi
146 |
147 | # Make these available to `roundup_trace`.
148 | export red grn mag clr
149 |
150 | ntests=0
151 | passed=0
152 | failed=0
153 |
154 | : ${cols:=10}
155 |
156 | while read status name
157 | do
158 | case $status in
159 | p)
160 | ntests=$(expr $ntests + 1)
161 | passed=$(expr $passed + 1)
162 | printf " %-48s " "$name:"
163 | printf "$grn[PASS]$clr\n"
164 | ;;
165 | f)
166 | ntests=$(expr $ntests + 1)
167 | failed=$(expr $failed + 1)
168 | printf " %-48s " "$name:"
169 | printf "$red[FAIL]$clr\n"
170 | roundup_trace < "$roundup_tmp/$name"
171 | ;;
172 | d)
173 | printf "%s\n" "$name"
174 | ;;
175 | esac
176 | done
177 | # __Test Summary__
178 | #
179 | # Display the summary now that all tests are finished.
180 | yes = | head -n 57 | tr -d '\n'
181 | printf "\n"
182 | printf "Tests: %3d | " $ntests
183 | printf "Passed: %3d | " $passed
184 | printf "Failed: %3d" $failed
185 | printf "\n"
186 |
187 | # Exit with an error if any tests failed
188 | test $failed -eq 0 || exit 2
189 | }
190 |
191 | # Sandbox Test Runs
192 | # -----------------
193 |
194 | # The above checks guarantee we have at least one test. We can now move through
195 | # each specified test plan, determine its test plan, and administer each test
196 | # listed in a isolated sandbox.
197 | for roundup_p in $roundup_plans
198 | do
199 | # Create a sandbox, source the test plan, run the tests, then leave
200 | # without a trace.
201 | (
202 | # Consider the description to be the `basename` of the plan minus the
203 | # tailing -test.sh.
204 | roundup_desc=$(basename "$roundup_p" -test.sh)
205 |
206 | # Define functions for
207 | # [roundup(5)][r5]
208 |
209 | # A custom description is recommended, but optional. Use `describe` to
210 | # set the description to something more meaningful.
211 | # TODO: reimplement this.
212 | describe() {
213 | roundup_desc="$*"
214 | }
215 |
216 | # Provide default `before` and `after` functions that run only `:`, a
217 | # no-op. They may or may not be redefined by the test plan.
218 | before() { :; }
219 | after() { :; }
220 |
221 | # Seek test methods and aggregate their names, forming a test plan.
222 | # This is done before populating the sandbox with tests to avoid odd
223 | # conflicts.
224 |
225 | # TODO: I want to do this with sed only. Please send a patch if you
226 | # know a cleaner way.
227 | roundup_plan=$(
228 | grep "^it_.*()" $roundup_p |
229 | sed "s/\(it_[a-zA-Z0-9_]*\).*$/\1/g"
230 | )
231 |
232 | # We have the test plan and are in our sandbox with [roundup(5)][r5]
233 | # defined. Now we source the plan to bring its tests into scope.
234 | . ./$roundup_p
235 |
236 | # Output the description signal
237 | printf "d %s" "$roundup_desc" | tr "\n" " "
238 | printf "\n"
239 |
240 | for roundup_test_name in $roundup_plan
241 | do
242 | # Any number of things are possible in `before`, `after`, and the
243 | # test. Drop into an subshell to contain operations that may throw
244 | # off roundup; such as `cd`.
245 | (
246 | # Output `before` trace to temporary file. If `before` runs cleanly,
247 | # the trace will be overwritten by the actual test case below.
248 | {
249 | # redirect tracing output of `before` into file.
250 | {
251 | set -x
252 | # If `before` wasn't redefined, then this is `:`.
253 | before
254 | } &>"$roundup_tmp/$roundup_test_name"
255 | # disable tracing again. Its trace output goes to /dev/null.
256 | set +x
257 | } &>/dev/null
258 |
259 | # exit subshell with return code of last failing command. This
260 | # is needed to see the return code 253 on failed assumptions.
261 | # But, only do this if the error handling is activated.
262 | set -E
263 | trap 'rc=$?; set +x; set -o | grep "errexit.*on" >/dev/null && exit $rc' ERR
264 |
265 | # If `before` wasn't redefined, then this is `:`.
266 | before
267 |
268 | # Momentarily turn off auto-fail to give us access to the tests
269 | # exit status in `$?` for capturing.
270 | set +e
271 | (
272 | # Set `-xe` before the test in the subshell. We want the
273 | # test to fail fast to allow for more accurate output of
274 | # where things went wrong but not in _our_ process because a
275 | # failed test should not immediately fail roundup. Each
276 | # tests trace output is saved in temporary storage.
277 | set -xe
278 | $roundup_test_name
279 | ) >"$roundup_tmp/$roundup_test_name" 2>&1
280 |
281 | # We need to capture the exit status before returning the `set
282 | # -e` mode. Returning with `set -e` before we capture the exit
283 | # status will result in `$?` being set with `set`'s status
284 | # instead.
285 | roundup_result=$?
286 |
287 | # It's safe to return to normal operation.
288 | set -e
289 |
290 | # If `after` wasn't redefined, then this runs `:`.
291 | after
292 |
293 | # This is the final step of a test. Print its pass/fail signal
294 | # and name.
295 | if [ "$roundup_result" -ne 0 ]
296 | then printf "f"
297 | else printf "p"
298 | fi
299 |
300 | printf " $roundup_test_name\n"
301 | )
302 | done
303 | )
304 | done |
305 |
306 | # All signals are piped to this for summary.
307 | roundup_summarize
308 |
--------------------------------------------------------------------------------
/test/nameservers-test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source "$( dirname "${BASH_SOURCE[0]}" )/../ubuntu.sh"
4 |
5 | describe "Nameservers"
6 |
7 | it_should_append_to_nameservers() {
8 | grep -v '8.8.8.8' '/etc/resolv.conf'
9 | nameservers-append '8.8.8.8'
10 | grep '8.8.8.8' '/etc/resolv.conf'
11 | }
12 |
13 | it_should_purge_local_nameservers() {
14 | echo "nameserver 10.0.255.1" | sudo tee -a '/etc/resolv.conf'
15 | echo "nameserver 10.0.255.2" | sudo tee -a '/etc/resolv.conf'
16 | grep '10.0.255.' '/etc/resolv.conf'
17 | nameservers-local-purge
18 | grep -v '10.0.255.' '/etc/resolv.conf'
19 | }
20 |
--------------------------------------------------------------------------------
/ubuntu-extras.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | # {{{ Archives
6 |
7 | archive-file-unpack() {
8 | log-operation "$FUNCNAME" "$@"
9 | if [ $# -lt 1 ]; then
10 | echo 'Usage: '"$0"' FILE [DIRECTORY]' >/dev/stderr
11 | return 1
12 | fi
13 | local file
14 | local directory
15 | local command
16 | local dependency
17 | file="$( readlink -f "$1" )"
18 | directory="${2:-.}"
19 | case "$file" in
20 | *.tar.gz | *.tgz ) command='tar -zxf' ;;
21 | *.tar.bz2 | *.tbz2 ) command='tar -jxf' ;;
22 | *.tar.xz ) command='tar -Jxf' ;;
23 | *.tar ) command='tar -xf' ;;
24 | *.zip ) command='unzip -q' ; dependency='unzip' ;;
25 | *.7z ) command='7za x' ;;
26 | *.gzip | *.gz ) command='gunzip -q' ;;
27 | *.bzip2 | *.bz2 ) command='bunzip2 -q' ; dependency='bzip2' ;;
28 | * )
29 | echo "$0: Unsupported file format: $file" >/dev/stderr ;
30 | return 2
31 | ;;
32 | esac
33 | if [ ! -d "$directory" ]; then
34 | mkdir -p "$directory"
35 | fi
36 | if [ -n "$dependency" ]; then
37 | dependency-install "$dependency"
38 | fi
39 | ( cd "$directory" && eval $command \"\$file\" )
40 | }
41 |
42 | # }}}
43 |
44 | # {{{ Packages (e.g., ZIP distributions)
45 |
46 | package-ignore-preserve() {
47 | local package_name="$1" package_path="$2" preserved_path="$3"
48 | if [ -f "${package_path}/.gitignore" ]; then
49 | for preserve in `cat "${package_path}/.gitignore" | grep '^!' | grep -v '/$' | sed -e 's#^!##g'`; do
50 | eval "rm -f \"${preserved_path}/${package_name}-\"*\"/\"${preserve}"
51 | done
52 | fi
53 | }
54 |
55 | package-uri-download() {
56 | local uri
57 | local target
58 | dependency-install 'curl'
59 | uri="$1"
60 | shift
61 | if [ -z "$1" ]; then
62 | target="-s"
63 | else
64 | target="-o $1"
65 | shift
66 | fi
67 | curl '-#' --insecure -L "$@" $target "$uri"
68 | }
69 |
70 | package-uri-install() {
71 | local package_name
72 | local package_uri
73 | local package_path
74 | local package_version
75 | local package_index
76 | package_name="$1"
77 | package_uri="$2"
78 | package_index="$3"
79 | package_path="$4"
80 | package_version="$5"
81 | if [ -z "$package_path" ]; then
82 | package_path="$( echo "$package_name" | tr '-' '_' | tr 'a-z' 'A-Z' )_PATH"
83 | package_path="${!package_path}"
84 | fi
85 | if [ -z "$package_version" ]; then
86 | package_version="$( echo "$package_name" | tr '-' '_' | tr 'a-z' 'A-Z' )_VERSION"
87 | package_version="${!package_version}"
88 | fi
89 | if [ -z "$package_index" ]; then
90 | package_index="${package_path}/bin/${package_name}"
91 | fi
92 | for variable_name in 'package_uri' 'package_index'; do
93 | eval "$variable_name"=\""$( \
94 | echo "${!variable_name}" | \
95 | sed -e 's#%name#'"$package_name"'#g' | \
96 | sed -e 's#%version#'"$package_version"'#g' |
97 | sed -e 's#%path#'"$package_path"'#g' \
98 | )"\"
99 | done
100 | log-operation "$FUNCNAME" \
101 | "$package_name" \
102 | "$package_uri" \
103 | "$package_index" \
104 | "$package_path" \
105 | "$package_version"
106 | if [ ! -f "$package_index" ]; then
107 | dependency-install 'rsync'
108 | TMP_PATH="$( mktemp -d -t "${package_name}-XXXXXXXX" )"
109 | TMP_FILE="$TMP_PATH/$( basename "$package_uri" )"
110 | package-uri-download "$package_uri" "$TMP_FILE" -f || return $?
111 | archive-file-unpack "$TMP_FILE" "$TMP_PATH"
112 | rm -f "$TMP_FILE"
113 | package-ignore-preserve "$package_name" "$package_path" "$TMP_PATH"
114 | rsync -rlptDP "$TMP_PATH/${package_name}-"*'/' "${package_path}/"
115 | rm -Rf "$TMP_PATH"
116 | fi
117 | }
118 |
119 | # }}}
120 |
121 | # {{{ Temporary Files
122 |
123 | temporary-cleanup() {
124 | local exit_code=$? tmp_path
125 | for tmp_path in "$@"; do
126 | if [ -d "$tmp_path" ]; then
127 | rm -Rf "$tmp_path"
128 | fi
129 | done
130 | exit $exit_code
131 | }
132 |
133 | # }}}
134 |
135 | # {{{ Dependency Management
136 |
137 | # Create associations for packages we are going to install.
138 | dependency-package-associate 'unzip' 'unzip'
139 | dependency-package-associate 'bzip2' 'bzip2'
140 | dependency-package-associate 'rsync' 'rsync'
141 |
142 | # }}}
143 |
--------------------------------------------------------------------------------
/ubuntu-postfix.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | # {{{ SMTP
6 |
7 | # Install an SMTP sink service which logs all outgoing e-mails to disk.
8 | smtp-sink-install() {
9 | log-operation "$FUNCNAME" "$@"
10 | local smtp_sink_directory="$1" \
11 | smtp_sink_template="${2:-%Y%m%d/%H%M.}" \
12 | smtp_port="${3:-25}" \
13 | smtp_user="${4:-vagrant}" \
14 | smtp_service="${5:-smtp-sink}" \
15 | smtp_hard_bounce_reply="550 5.3.0 Sink'd." \
16 | smtp_hostname='localhost.localdomain' \
17 | smtp_backlog="${6:-10}"
18 | if [ -z "$smtp_sink_directory" ]; then
19 | echo -e "${ERROR_BOLD}E: ${ERROR_NORMAL}You must specify 'smtp_sink_directory' to '${FUNCNAME}' at position '1'.${RESET}" 1>&2
20 | exit 1
21 | fi
22 | dependency-install 'postfix'
23 | # If Postfix is currently running, stop it.
24 | QUIET=1 system-service postfix status && system-service postfix stop || :
25 | # Stop Postfix from running at start up.
26 | $SUDO update-rc.d -f 'postfix' remove 1>/dev/null
27 | # Kill any running smtp-sink services, try graceful shutdown first.
28 | QUIET=1 system-service "$smtp_service" stop 1>/dev/null 2>&1 || :
29 | $SUDO killall -q -9 smtp-sink || :
30 | # Create a new service to log all e-mails to disk.
31 | cat <<-EOD | $SUDO tee "/etc/init/${smtp_service}.conf" 1>/dev/null
32 | # ${smtp_service}
33 | #
34 | # SMTP server which logs all outgoing e-mails to disk.
35 |
36 | description "${smtp_service}, logs all outgoing e-mails"
37 |
38 | start on runlevel [2345]
39 | stop on runlevel [!2345]
40 |
41 | console log
42 |
43 | respawn
44 | respawn limit 3 5
45 |
46 | pre-start exec mkdir -p "$smtp_sink_directory"
47 |
48 | exec start-stop-daemon --start --exec "$( which smtp-sink )" -- \\
49 | -u "$smtp_user" \\
50 | -R "$( echo "$smtp_sink_directory" | sed -e 's#/$##' )/" -d "$smtp_sink_template" \\
51 | -f '.' -B "$smtp_hard_bounce_reply" \\
52 | -h "$smtp_hostname" "$smtp_port" "$smtp_backlog"
53 | EOD
54 | # Start the service. It will also run on next start up
55 | system-service "$smtp_service" start
56 | }
57 |
58 | # }}}
59 |
60 | # {{{ Dependency Management
61 |
62 | # Create associations for packages we are going to install.
63 | dependency-package-associate 'postfix' 'postfix'
64 |
65 | # }}}
66 |
--------------------------------------------------------------------------------
/ubuntu-postgres.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | # {{{ PostgreSQL
6 |
7 | postgres-remote-access-allow() {
8 | log-operation "$FUNCNAME" "$@"
9 | $SUDO sed -e "s/[#]\?listen_addresses = .*/listen_addresses = '*'/g" -i '/etc/postgresql/'*'/main/postgresql.conf'
10 | $SUDO grep '192.168.1.1' '/etc/postgresql/'*'/main/pg_hba.conf' | \
11 | ( echo 'host all all 192.168.1.1/22 md5' | $SUDO tee -a '/etc/postgresql/'*'/main/pg_hba.conf' >/dev/null )
12 | }
13 |
14 | postgres-password-reset() {
15 | log-operation "$FUNCNAME"
16 | $SUDO -u 'postgres' psql -c "ALTER ROLE postgres WITH ENCRYPTED PASSWORD '$1'" 1>/dev/null
17 | }
18 |
19 | postgres-autovacuum-on() {
20 | log-operation "$FUNCNAME"
21 | $SUDO sed -e "s/[#]\?autovacuum = .*/autovacuum = on/g" -i '/etc/postgresql/'*'/main/postgresql.conf'
22 | $SUDO sed -e "s/[#]\?track_counts = .*/track_counts = on/g" -i '/etc/postgresql/'*'/main/postgresql.conf'
23 | }
24 |
25 | postgres-template-encoding() {
26 | local encoding
27 | local ctype
28 | local collate
29 | encoding="${1:-UTF8}"
30 | ctype="${2:-en_GB.$( echo "$encoding" | tr 'A-Z' 'a-z' )}"
31 | collate="${3:-$ctype}"
32 | log-operation "$FUNCNAME" "$@"
33 | SQL_TMP_FILE="$( mktemp -t 'sql-XXXXXXXX' )"
34 | cat > "$SQL_TMP_FILE" <<-EOD
35 | UPDATE pg_database SET datallowconn = TRUE WHERE datname = 'template0';
36 | \\c template0;
37 | UPDATE pg_database SET datistemplate = FALSE WHERE datname = 'template1';
38 | DROP DATABASE template1;
39 | CREATE DATABASE template1 WITH template = template0 ENCODING = '${encoding}' LC_CTYPE = '${ctype}' LC_COLLATE = '${collate}';
40 | UPDATE pg_database SET datistemplate = TRUE WHERE datname = 'template1';
41 | \\c template1;
42 | UPDATE pg_database SET datallowconn = FALSE WHERE datname = 'template0';
43 | EOD
44 | $SUDO chmod 0777 "$SQL_TMP_FILE"
45 | $SUDO -u 'postgres' psql -f "$SQL_TMP_FILE" 1>/dev/null || {
46 | EXIT_CODE=$?
47 | rm "$SQL_TMP_FILE"
48 | exit $EXIT_CODE
49 | }
50 | rm "$SQL_TMP_FILE"
51 | }
52 |
53 | # }}}
54 |
--------------------------------------------------------------------------------
/ubuntu.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | [ -n "${SUDO-x}" ] && SUDO='sudo'
6 |
7 | ERROR_BOLD="\e[1;31m"
8 | ERROR_NORMAL="\e[0;31m"
9 | DEBUG_BOLD="\e[1;35m"
10 | DEBUG_NORMAL="\e[0;35m"
11 | RESET="\e[00m"
12 |
13 | if [[ -n "$COLORS" ]] && [[ ! "$COLORS" =~ ^(always|yes|true|1)$ ]]; then
14 | unset ERROR_BOLD
15 | unset ERROR_NORMAL
16 | unset DEBUG_BOLD
17 | unset DEBUG_NORMAL
18 | unset RESET
19 | fi
20 |
21 | # {{{ Utils
22 |
23 | # Return the value of the first argument or exit with an error message if empty.
24 | script-argument-create() {
25 | [ -z "$1" ] && {
26 | echo -e "${ERROR_BOLD}E: ${ERROR_NORMAL}You must specify $2 to '${BASH_SOURCE[0]}'.${RESET}" 1>&2
27 | exit 1
28 | }
29 | echo "$1"
30 | }
31 |
32 | # Log an operation
33 | log-operation() {
34 | local function_name
35 | local function_values
36 | local arg
37 | function_name="$1"
38 | shift
39 | for arg in "$@"; do
40 | function_values="$function_values ""'$( echo "$arg" | sed -e 's#\s\+# #g' )'"
41 | done
42 | [ -z "$QUIET" ] && echo -e "${DEBUG_BOLD}$function_name${DEBUG_NORMAL}(""$( echo "$function_values" | sed -e 's#^ ##' -e "s#\s\+''\$##g" )"")...${RESET}"
43 | }
44 |
45 | # }}}
46 |
47 | # {{{ Nameservers
48 |
49 | # Drop all local 10.0.x.x nameservers in 'resolv.conf'.
50 | nameservers-local-purge() {
51 | log-operation "$FUNCNAME" "$@"
52 | $SUDO sed -e 's#nameserver\s*10\.0\..*$##g' -i '/etc/resolv.conf'
53 | }
54 |
55 | # Set up an IP as a DNS name server if not already present in 'resolv.conf'.
56 | nameservers-append() {
57 | log-operation "$FUNCNAME" "$@"
58 | grep "$1" '/etc/resolv.conf' >/dev/null || \
59 | ( echo "nameserver $1" | $SUDO tee -a '/etc/resolv.conf' >/dev/null )
60 | }
61 |
62 | # }}}
63 |
64 | # {{{ Aptitude
65 |
66 | # Set up a specific two-letter country code as the preferred `aptitude` mirror.
67 | apt-mirror-pick() {
68 | log-operation "$FUNCNAME" "$@"
69 | $SUDO sed -i \
70 | -e "s#\w\+\.archive\.ubuntu\.com#$1.archive.ubuntu.com#g" \
71 | -e "s#security\.ubuntu\.com#$1.archive.ubuntu.com#g" \
72 | '/etc/apt/sources.list'
73 | }
74 |
75 | # Add a custom repository as a software source.
76 | apt-packages-repository() {
77 | log-operation "$FUNCNAME" "$@"
78 | dependency-install 'add-apt-repository'
79 | while [[ "$1" =~ ^deb ]] || [[ "$1" =~ ^ppa ]]; do
80 | $SUDO add-apt-repository -y "$( echo "$1" | sed -e 's#^deb-src\b#deb#' )" 1>/dev/null
81 | # See https://bugs.launchpad.net/ubuntu/+source/software-properties/+bug/972617
82 | if [[ "$1" =~ ^deb ]] && [[ ! "$1" =~ ^deb-src ]]; then
83 | $SUDO add-apt-repository --remove -y "$( echo "$1" | sed -e 's#^deb\b#deb-src#' )" 1>/dev/null
84 | fi
85 | shift
86 | done
87 | if [ -n "$1" ] && ! $SUDO apt-key list | grep -q "$1"; then
88 | $SUDO apt-key adv -q --keyserver "${2:-keyserver.ubuntu.com}" --recv-keys "$1" 1>/dev/null
89 | fi
90 | }
91 |
92 | # Add a Launchpad PPA as a software source.
93 | apt-packages-ppa() {
94 | local ppa_repository="$1"
95 | shift
96 | # If the repository is already set up, don't re-add it.
97 | if ! apt-cache policy | grep "$ppa_repository" 1>/dev/null 2>&1; then
98 | apt-packages-repository "ppa:${ppa_repository}" "$@"
99 | fi
100 | }
101 |
102 | # Perform a non-interactive `apt-get` command.
103 | apt-non-interactive() {
104 | log-operation "$FUNCNAME" "$@"
105 | $SUDO \
106 | DEBIAN_FRONTEND=noninteractive \
107 | apt-get \
108 | -o Dpkg::Options::='--force-confdef' \
109 | -o Dpkg::Options::='--force-confold' \
110 | -f -y -qq \
111 | --no-install-recommends \
112 | "$@"
113 | }
114 |
115 | # Update `aptitude` packages without any prompts.
116 | apt-packages-update() {
117 | apt-non-interactive update
118 | }
119 |
120 | # Perform an unattended installation of package(s).
121 | apt-packages-install() {
122 | apt-non-interactive install "$@"
123 | }
124 |
125 | # Perform an unattended complete removal (purge) of package(s).
126 | apt-packages-purge() {
127 | local result
128 | local code
129 | result=$( apt-non-interactive -q purge "$@" 2>&1 ) || {
130 | code=$?
131 | # If no packages matched, it's OK.
132 | if [[ ! "$result" =~ "E: Couldn't find package" ]]; then
133 | echo "$result" 1>&2
134 | exit $code
135 | fi
136 | }
137 | # Take care of any leftovers.
138 | apt-non-interactive autoremove
139 | }
140 |
141 | # }}}
142 |
143 | # {{{ System
144 |
145 | # Run a complete system upgrade.
146 | system-upgrade() {
147 | apt-non-interactive upgrade
148 | }
149 |
150 | # Command a system service, e.g., apache2, mysql, etc.
151 | system-service() {
152 | log-operation "$FUNCNAME" "$@"
153 | $SUDO service "$1" "$2" 1>/dev/null
154 | }
155 |
156 | # Escape and normalize a string so it can be used safely in file names, etc.
157 | system-escape() {
158 | local glue
159 | glue=${1:--}
160 | while read arg; do
161 | echo "${arg,,}" | sed -e 's#[^[:alnum:]]\+#'"$glue"'#g' -e 's#^'"$glue"'\+\|'"$glue"'\+$##g'
162 | done
163 | }
164 |
165 | # }}}
166 |
167 | # {{{ Default Commands
168 |
169 | # Update the Ruby binary link to point to a specific version.
170 | alternatives-ruby-install() {
171 | log-operation "$FUNCNAME" "$@"
172 | local bin_path
173 | local man_path
174 | bin_path="${2:-/usr/bin/}"
175 | man_path="${3:-/usr/share/man/man1/}"
176 | $SUDO update-alternatives \
177 | --install "${bin_path}ruby" ruby "${bin_path}ruby$1" "${4:-500}" \
178 | --slave "${man_path}ruby.1.gz" ruby.1.gz "${man_path}ruby$1.1.gz" \
179 | --slave "${bin_path}ri" ri "${bin_path}ri$1" \
180 | --slave "${bin_path}irb" irb "${bin_path}irb$1" \
181 | --slave "${bin_path}rdoc" rdoc "${bin_path}rdoc$1"
182 | $SUDO update-alternatives --verbose \
183 | --set ruby "${bin_path}ruby$1"
184 | }
185 |
186 | # Create symbolic links to RubyGems binaries.
187 | alternatives-ruby-gems() {
188 | log-operation "$FUNCNAME" "$@"
189 | local ruby_binary
190 | local ruby_version
191 | local binary_name
192 | local binary_path
193 | ruby_binary=$( $SUDO update-alternatives --query 'ruby' | grep 'Value:' | cut -d' ' -f2- )
194 | ruby_version="${ruby_binary#*ruby}"
195 | if grep -v '^[0-9.]*$' <<< "$ruby_version"; then
196 | echo "E: Could not determine version of RubyGems."
197 | fi
198 | for binary_name in "$@"; do
199 | binary_path="/var/lib/gems/$ruby_version/bin/$binary_name"
200 | $SUDO update-alternatives --install "$( dirname "$ruby_binary" )/$binary_name" "$binary_name" "$binary_path" 500
201 | $SUDO update-alternatives --verbose --set "$binary_name" "$binary_path"
202 | done
203 | }
204 |
205 | # }}}
206 |
207 | # {{{ Apache
208 |
209 | # Enable a list of Apache modules. This requires a server restart.
210 | apache-modules-enable() {
211 | log-operation "$FUNCNAME" "$@"
212 | $SUDO a2enmod $* 1>/dev/null
213 | }
214 |
215 | # Disable a list of Apache modules. This requires a server restart.
216 | apache-modules-disable() {
217 | log-operation "$FUNCNAME" "$@"
218 | $SUDO a2dismod $* 1>/dev/null
219 | }
220 |
221 | # Enable a list of Apache sites. This requires a server restart.
222 | apache-sites-enable() {
223 | log-operation "$FUNCNAME" "$@"
224 | $SUDO a2ensite $* 1>/dev/null
225 | }
226 |
227 | # Disable a list of Apache sites. This requires a server restart.
228 | apache-sites-disable() {
229 | log-operation "$FUNCNAME" "$@"
230 | $SUDO a2dissite $* 1>/dev/null
231 | }
232 |
233 | # Create a new Apache site and set up Fast-CGI components.
234 | apache-sites-create() {
235 | log-operation "$FUNCNAME" "$@"
236 | local apache_site_name
237 | local apache_site_path
238 | local apache_site_user
239 | local apache_site_group
240 | local apache_site_config
241 | local apache_verbosity
242 | local cgi_action
243 | local cgi_apache_path
244 | local cgi_system_path
245 | local code_block
246 | apache_site_name="$1"
247 | apache_site_path="${2:-/$apache_site_name}"
248 | apache_site_user="${3:-$apache_site_name}"
249 | apache_site_group="${4:-$apache_site_user}"
250 | apache_verbosity="${5:-info}"
251 | apache_site_config="/etc/apache2/sites-available/$apache_site_name"
252 | cgi_apache_path="/cgi-bin/"
253 | cgi_system_path="$apache_site_path/.cgi-bin/"
254 | # Create the /.cgi-bin/ directory and set permissions for SuExec.
255 | $SUDO mkdir -p "$cgi_system_path"
256 | $SUDO chmod 0755 "$cgi_system_path"
257 | # Define a new virtual host with mod_fastcgi configured to use SuExec.
258 | code_block=$( cat <<-EOD
259 |
260 | FastCgiWrapper /usr/lib/apache2/suexec
261 | FastCgiConfig -pass-header HTTP_AUTHORIZATION -autoUpdate -killInterval 120 -idle-timeout 30
262 |
263 |
264 |
265 | DocumentRoot ${apache_site_path}
266 |
267 | LogLevel ${apache_verbosity}
268 | ErrorLog /var/log/apache2/error.${apache_site_name}.log
269 | CustomLog /var/log/apache2/access.${apache_site_name}.log combined
270 |
271 | SuexecUserGroup ${apache_site_user} ${apache_site_group}
272 | ScriptAlias ${cgi_apache_path} ${cgi_system_path}
273 |
274 | # Do not use kernel sendfile to deliver files to the client.
275 | EnableSendfile Off
276 |
277 |
278 | Options All
279 | AllowOverride All
280 |
281 | EOD
282 | )
283 | # Is PHP required?
284 | if [ ! -z "$PHP" ]; then
285 | cgi_action="php-fcgi"
286 | code_block=$( cat <<-EOD
287 | ${code_block}
288 |
289 |
290 |
291 | SetHandler fastcgi-script
292 | Options +ExecCGI +FollowSymLinks
293 | Order Allow,Deny
294 | Allow from all
295 |
296 | AddHandler ${cgi_action} .php
297 | Action ${cgi_action} ${cgi_apache_path}${cgi_action}
298 |
299 | EOD
300 | )
301 | $SUDO cat > "$cgi_system_path$cgi_action" <<-EOD
302 | #!/bin/bash
303 |
304 | export PHP_FCGI_CHILDREN=4
305 | export PHP_FCGI_MAX_REQUESTS=200
306 |
307 | export PHPRC="${cgi_system_path}php.ini"
308 |
309 | exec ${PHP}
310 | EOD
311 | $SUDO chmod 0755 "$cgi_system_path$cgi_action"
312 | fi
313 | code_block=$( cat <<-EOD
314 | ${code_block}
315 | ${EXTRA}
316 |
317 | EOD
318 | )
319 | # Write site configuration to Apache.
320 | echo "$code_block" | $SUDO tee "$apache_site_config" >/dev/null
321 | # Configure permissions for /.cgi-bin/ and SuExec.
322 | $SUDO chown -R "$apache_site_user":"$apache_site_group" "$cgi_system_path"
323 | # Update SuExec to accept the new document root for this website.
324 | grep "$apache_site_path" '/etc/apache2/suexec/www-data' >/dev/null || \
325 | ( $SUDO sed -e '1s#^#'"$apache_site_path""\n"'#' -i '/etc/apache2/suexec/www-data' >/dev/null )
326 | }
327 |
328 | # Restart the Apache server and reload with new configuration.
329 | apache-restart() {
330 | system-service apache2 restart
331 | }
332 |
333 | # }}}
334 |
335 | # {{{ Nginx
336 |
337 | # Figure out the path to a particular Nginx site.
338 | nginx-sites-path() {
339 | echo "/etc/nginx/sites-${2:-available}/$1"
340 | }
341 |
342 | # Enable a list of Nginx sites. This requires a server restart.
343 | nginx-sites-enable() {
344 | log-operation "$FUNCNAME" "$@"
345 | local name
346 | local file
347 | for name in "$@"; do
348 | file="$( nginx-sites-path "$name" 'enabled' )"
349 | if [ ! -L "$file" ]; then
350 | # '-f'orce because '! -L' above would still evaluate for broken symlinks.
351 | $SUDO ln -fs "$( nginx-sites-path "$name" 'available' )" "$file"
352 | fi
353 | done
354 | }
355 |
356 | # Disable a list of Nginx sites. This requires a server restart.
357 | nginx-sites-disable() {
358 | log-operation "$FUNCNAME" "$@"
359 | local name
360 | local file
361 | for name in "$@"; do
362 | file="$( nginx-sites-path "$name" 'enabled' )"
363 | if [ -L "$file" ]; then
364 | $SUDO unlink "$file"
365 | fi
366 | done
367 | }
368 |
369 | # Create a new Nginx site and set up Fast-CGI components.
370 | nginx-sites-create() {
371 | log-operation "$FUNCNAME" "$@"
372 | local nginx_site_name
373 | local nginx_site_path
374 | local nginx_site_index
375 | local nginx_site_user
376 | local nginx_site_group
377 | local nginx_verbosity
378 | local nginx_site_config
379 | local code_block
380 | nginx_site_name="$1"
381 | nginx_site_path="${2:-/$nginx_site_name}"
382 | nginx_site_user="${3:-$nginx_site_name}"
383 | nginx_site_group="${4:-$nginx_site_user}"
384 | nginx_site_index="${5:-index.html}"
385 | nginx_verbosity="${6:-info}"
386 | nginx_site_config="$( nginx-sites-path "$nginx_site_name" 'available' )"
387 | # Is PHP required?
388 | if [ ! -z "$PHP" ]; then
389 | if ! which php5-fpm >/dev/null; then
390 | echo 'E: You must install php5-fpm to use PHP in Nginx.' 1>&2
391 | exit 1
392 | fi
393 | nginx_site_index="index.php $nginx_site_index"
394 | fi
395 | code_block=$( cat <<-EOD
396 | server {
397 | listen 80;
398 |
399 | root ${nginx_site_path};
400 |
401 | error_log /var/log/nginx/error.${nginx_site_name}.log ${nginx_verbosity};
402 | access_log /var/log/nginx/access.${nginx_site_name}.log combined;
403 |
404 | index ${nginx_site_index};
405 |
406 | # Do not use kernel sendfile to deliver files to the client.
407 | sendfile off;
408 |
409 | # Prevent access to hidden files.
410 | location ~ /\. {
411 | access_log off;
412 | log_not_found off;
413 | deny all;
414 | }
415 | EOD
416 | )
417 | # Is PHP required?
418 | if [ ! -z "$PHP" ]; then
419 | code_block=$( cat <<-EOD
420 | ${code_block}
421 |
422 | # Pass PHP scripts to PHP-FPM.
423 | location ~ \.php\$ {
424 | include fastcgi_params;
425 | fastcgi_pass unix:/var/run/php5-fpm.${nginx_site_name}.sock;
426 | fastcgi_index index.php;
427 | fastcgi_split_path_info ^((?U).+\.php)(/?.+)\$;
428 | fastcgi_param PATH_INFO \$fastcgi_path_info;
429 | fastcgi_param PATH_TRANSLATED \$document_root\$fastcgi_path_info;
430 | fastcgi_param HTTP_AUTHORIZATION \$http_authorization;
431 | }
432 | EOD
433 | )
434 | # Run PHP-FPM as the selected user and group.
435 | $SUDO sed \
436 | -e 's#^\[www\]$#['"$nginx_site_name"']#g' \
437 | -e 's#^\(user\)\s*=\s*[A-Za-z0-9-]\+#\1 = '"$nginx_site_user"'#g' \
438 | -e 's#^\(group\)\s*=\s*[A-Za-z0-9-]\+#\1 = '"$nginx_site_group"'#g' \
439 | -e 's#^\(listen\)\s*=\s*.\+$#\1 = /var/run/php5-fpm.'"$nginx_site_name"'.sock#g' \
440 | < '/etc/php5/fpm/pool.d/www.conf' | $SUDO tee '/etc/php5/fpm/pool.d/'"$nginx_site_name"'.conf' >/dev/null
441 | fi
442 | code_block=$( cat <<-EOD
443 | ${code_block}
444 | ${EXTRA}
445 | }
446 | EOD
447 | )
448 | # Write site configuration to Nginx.
449 | echo "$code_block" | $SUDO tee "$nginx_site_config" >/dev/null
450 | }
451 |
452 | # Restart the Nginx server and reload with new configuration.
453 | nginx-restart() {
454 | system-service nginx restart
455 | }
456 |
457 | # }}}
458 |
459 | # {{{ PHP
460 |
461 | # Update a PHP setting value in all instances of 'php.ini'.
462 | php-settings-update() {
463 | log-operation "$FUNCNAME" "$@"
464 | local args
465 | local settings_name
466 | local php_ini
467 | local php_extra
468 | args=( "$@" )
469 | PREVIOUS_IFS="$IFS"
470 | IFS='='
471 | args="${args[*]}"
472 | IFS="$PREVIOUS_IFS"
473 | settings_name="$( echo "$args" | system-escape )"
474 | for php_ini in $( $SUDO find /etc -type f -iname 'php*.ini' ); do
475 | php_extra="$( dirname "$php_ini" )/conf.d"
476 | $SUDO mkdir -p "$php_extra"
477 | echo "$args" | $SUDO tee "$php_extra/0-$settings_name.ini" >/dev/null
478 | done
479 | }
480 |
481 | # Install (download, build, install) and enable a PECL extension.
482 | php-pecl-install() {
483 | log-operation "$FUNCNAME" "$@"
484 | local specification extension
485 | local mode
486 | dependency-install 'make'
487 | dependency-install 'pecl'
488 | dependency-install 'phpize'
489 | for specification in "$@"; do
490 | extension="${specification%@*}"
491 | if ! $SUDO pecl list | grep "^${extension}" >/dev/null; then
492 | $SUDO pecl install -s "${specification/@/-}" 1>/dev/null
493 | fi
494 | # Special case for Zend extensions.
495 | if [[ "$extension" =~ ^(xdebug)$ ]]; then
496 | mode="zend_extension"
497 | extension="$( php-config --extension-dir )/${extension}"
498 | else
499 | mode="extension"
500 | fi
501 | php-settings-update "$mode" "${extension}.so"
502 | done
503 | }
504 |
505 | # Restart the PHP5-FPM server.
506 | php-fpm-restart() {
507 | system-service php5-fpm restart
508 | }
509 |
510 | # }}}
511 |
512 | # {{{ MySQL
513 |
514 | # Create a database if one doesn't already exist.
515 | mysql-database-create() {
516 | log-operation "$FUNCNAME" "$@"
517 | mysql -u root -e "CREATE DATABASE IF NOT EXISTS \`$1\` CHARACTER SET ${2:-utf8} COLLATE '${3:-utf8_general_ci}'"
518 | }
519 |
520 | # Restore a MySQL database from an archived backup.
521 | mysql-database-restore() {
522 | log-operation "$FUNCNAME" "$@"
523 | local backups_database
524 | local backups_path
525 | local backups_file
526 | local tables_length
527 | backups_database="$1"
528 | backups_path="$2"
529 | if [ -d "$backups_path" ]; then
530 | tables_length=$( mysql -u root --skip-column-names -e "USE '$backups_database'; SHOW TABLES" | wc -l )
531 | if [ "$tables_length" -lt 1 ]; then
532 | backups_file=$( find "$backups_path" -maxdepth 1 -type f -regextype posix-basic -regex '^.*[0-9]\{8\}-[0-9]\{4\}.tar.bz2$' | \
533 | sort -g | \
534 | tail -1 )
535 | if [ ! -z "$backups_file" ]; then
536 | tar -xjf "$backups_file" -O | mysql -u root "$backups_database"
537 | fi
538 | fi
539 | fi
540 | }
541 |
542 | # Allow remote passwordless 'root' access for anywhere.
543 | # This is only a good idea if the box is configured in 'Host-Only' network mode.
544 | mysql-remote-access-allow() {
545 | log-operation "$FUNCNAME" "$@"
546 | mysql -u root -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '' WITH GRANT OPTION; FLUSH PRIVILEGES;"
547 | $SUDO sed -e 's#127.0.0.1#0.0.0.0#g' -i '/etc/mysql/my.cnf'
548 | }
549 |
550 | # Restart the MySQL server and reload with new configuration.
551 | mysql-restart() {
552 | system-service mysql restart
553 | }
554 |
555 | # }}}
556 |
557 | # {{{ RubyGems
558 |
559 | # Check the installed version of a package.
560 | ruby-gems-version() {
561 | $SUDO ruby -rubygems -e "puts Gem::Specification::find_by_name('$1').version" 2>/dev/null || \
562 | echo '0.0.0'
563 | }
564 |
565 | # Perform an unattended installation of a package.
566 | ruby-gems-install() {
567 | log-operation "$FUNCNAME" "$@"
568 | local gem_version
569 | gem_version="$( ruby-gems-version "$1" )"
570 | if [[ "$gem_version" = '0.0.0' ]]; then
571 | $SUDO gem install --no-ri --no-rdoc "$@"
572 | fi
573 | }
574 |
575 | # }}}
576 |
577 | # {{{ NPM (Node Package Manager)
578 |
579 | # Perform an unattended **global** installation of package(s).
580 | npm-packages-install() {
581 | log-operation "$FUNCNAME" "$@"
582 | $SUDO npm config set yes true
583 | $SUDO npm install -g $*
584 | }
585 |
586 | # }}}
587 |
588 | # {{{ GitHub
589 |
590 | # Download and install RubyGems from GitHub.
591 | github-gems-install() {
592 | log-operation "$FUNCNAME" "$@"
593 | local repository
594 | local clone_path
595 | local configuration
596 | local gem_version
597 | dependency-install 'git'
598 | which 'gem' >/dev/null || {
599 | echo 'E: Please install RubyGems to continue.' 1>&2
600 | exit 1
601 | }
602 | for repository in "$@"; do
603 | configuration=(${repository//@/"${IFS}"})
604 | gem_version="$( ruby-gems-version "${configuration[0]#*\/}" )"
605 | if [[ "$gem_version" = '0.0.0' ]]; then
606 | clone_path="$( mktemp -d -t 'github-'$( echo "${configuration[0]}" | system-escape )'-XXXXXXXX' )"
607 | git clone --progress "git://github.com/${configuration[0]}" "$clone_path"
608 | ( \
609 | cd "$clone_path" && \
610 | git checkout -q "${configuration[1]:-master}" && \
611 | gem build *.gemspec && \
612 | ruby-gems-install *.gem \
613 | )
614 | rm -Rf "$clone_path"
615 | fi
616 | done
617 | }
618 |
619 | # Install (download, build, install) and enable a PECL extension.
620 | github-php-extension-install() {
621 | log-operation "$FUNCNAME" "$@"
622 | local specification repository version arguments extension
623 | local raw_config_uri clone_path
624 | # We must have 'phpize' available when compiling extensions from source.
625 | dependency-install 'git'
626 | dependency-install 'curl'
627 | dependency-install 'phpize'
628 | for specification in "$@"; do
629 | repository="$specification"
630 | version='master'
631 | # If we have a space anywhere in the string, assume `configure` arguments.
632 | if [[ "$repository" =~ ' ' ]]; then
633 | arguments=( ${repository#* } )
634 | repository="${repository%% *}"
635 | fi
636 | # If we have a version specified, split the repository name and version.
637 | if [[ "$repository" =~ '@' ]]; then
638 | version="${repository#*@}"
639 | repository="${repository%@*}"
640 | fi
641 | # Let's figure out the extension name by looking at the autoconf file.
642 | raw_config_uri="https://raw.github.com/${repository}/${version}/config.m4"
643 | extension="$( curl --insecure --silent "$raw_config_uri" | grep -e 'PHP_NEW_EXTENSION' | cut -d'(' -f2 | cut -d',' -f1 | tr -d ' ' )"
644 | if [ -z "$extension" ]; then
645 | echo -e "${ERROR_BOLD}E: ${ERROR_NORMAL}Cannot find extension name in ${raw_config_uri}, expected 'PHP_NEW_EXTENSION'.${RESET}" 1>&2
646 | exit 1
647 | fi
648 | # We can't use PECL to determine if the extension is installed
649 | # so let's look for the .so file in the PHP extensions directory.
650 | if ! $SUDO ls -1 "$( php-config --extension-dir )" | grep "^${extension}.so$" >/dev/null; then
651 | clone_path="$( mktemp -d -t 'github-'$( echo "${repository}" | system-escape )'-XXXXXXXX' )"
652 | # Clone, configure, make, install... the usual.
653 | git clone --progress "git://github.com/${repository}" "$clone_path"
654 | ( \
655 | cd "$clone_path" && \
656 | git checkout -q "$version" && \
657 | phpize && \
658 | ./configure "${arguments[@]}" && \
659 | make && $SUDO make install \
660 | )
661 | # Clean up and explicitly load the extension.
662 | rm -Rf "$clone_path"
663 | php-settings-update 'extension' "${extension}.so"
664 | fi
665 | done
666 | }
667 |
668 | # }}}
669 |
670 | # {{{ Environment
671 |
672 | env-append() {
673 | local env_key="$1" env_lookup=1 env_value="$2" env_comment="$3" env_line
674 | log-operation "$FUNCNAME" "$@"
675 | for profile_env in '/etc/profile' '/etc/zsh/zshenv'; do
676 | if [ -f "$profile_env" ]; then
677 | if [ "$env_key" == 'PATH' ]; then
678 | env_line="export ${env_key}='${env_value}':"'"$PATH"'
679 | elif [[ "$env_key" =~ ^(source|eval)$ ]]; then
680 | env_line="${env_key} ${env_value} || :"
681 | else
682 | env_line="[ -z "'"$'"$env_key"'"'" ] && export ${env_key}='${env_value}'"
683 | fi
684 | eval "$env_line" || :
685 | if ! grep "${env_key}.*${env_value}" "$profile_env" &>/dev/null; then
686 | echo -e "\n# AUTO-GENERATED: ${env_key}$( echo ", ${env_comment}" | sed -e 's/^,\s*$//' ).\n${env_line}" | $SUDO tee -a "$profile_env" 1>/dev/null
687 | fi
688 | fi
689 | done
690 | }
691 |
692 | # }}}
693 |
694 | # {{{ Dependency Management
695 |
696 | # Associate a package name with a command, e.g., 'git' <- 'git-core'.
697 | dependency-package-associate() {
698 | local variable_name
699 | variable_name="DEPENDENCY_$(echo "$1" | system-escape '_' | tr '[a-z]' '[A-Z]' )"
700 | # If a second argument was specified...
701 | if [ -n "$2" ]; then
702 | # ...and a package name hasn't been associated with the command yet...
703 | if [ -z "${!variable_name}" ]; then
704 | # ...create a new association.
705 | eval $variable_name=\""$2"\"
706 | fi
707 | else
708 | echo "${!variable_name}"
709 | fi
710 | }
711 |
712 | # Satisfy a dependency by installing the associated package.
713 | dependency-install() {
714 | local binary_name
715 | local package_name
716 | local apt_update_required=0
717 | local apt_update_performed=0
718 | local apt_update_datetime apt_update_timestamp apt_update_ago
719 | local timestamp_now
720 | for binary_name in "$@"; do
721 | which "$binary_name" >/dev/null || {
722 | package_name="$( dependency-package-associate "$binary_name" )"
723 | # If we cannot find the package name in the cache, request an update.
724 | # This may happen if `apt-get update` was not run on the machine before.
725 | if ! $SUDO apt-cache pkgnames | grep "$package_name" 1>/dev/null 2>&1; then
726 | apt_update_required=1
727 | fi
728 | # If the last time we updated the cache was more than a day ago, request an update.
729 | # This is needed to prevent errors when trying to install an out-of-date package from the cache.
730 | if [[ $apt_update_required -lt 1 ]] && [[ $apt_update_performed -lt 1 ]]; then
731 | # Determine the last date/time any lists files were modified, newest first.
732 | apt_update_datetime="$( \
733 | ls -lt --time-style='long-iso' '/var/lib/apt/lists/' 2>/dev/null | grep -o '\([0-9]\{2,4\}[^0-9]\)\{3\}[0-9]\{2\}:[0-9]\{2\}' -m 1 || \
734 | date +'%Y-%m-%d %H:%M'
735 | )"
736 | # Convert the YYYY-MM-DD HH:MM format to a Unix timestamo.
737 | apt_update_timestamp=$( date --date="$apt_update_datetime" +'%s' 2>/dev/null || echo 0 )
738 | # Calculate the number of seconds that have passed since the newest update.
739 | timestamp_now=$( date +'%s' )
740 | apt_update_ago=$(( $timestamp_now-$apt_update_timestamp ))
741 | if [ $apt_update_ago -gt 86400 ]; then
742 | apt_update_required=1
743 | fi
744 | fi
745 | # If the cache needs updating, do so before installing the package (once per call only).
746 | if [[ $apt_update_required -gt 0 ]] && [[ $apt_update_performed -lt 1 ]]; then
747 | apt_update_required=0
748 | apt_update_performed=1
749 | apt-packages-update
750 | fi
751 | apt-packages-install "$package_name"
752 | }
753 | done
754 | }
755 |
756 | # Create associations for packages we are going to install.
757 | dependency-package-associate 'add-apt-repository' 'python-software-properties'
758 | dependency-package-associate 'curl' 'curl'
759 | dependency-package-associate 'git' 'git-core'
760 | dependency-package-associate 'make' 'build-essential'
761 | dependency-package-associate 'pecl' 'php-pear'
762 | dependency-package-associate 'phpize' 'php5-dev'
763 |
764 | # }}}
765 |
--------------------------------------------------------------------------------
/vagrant.rb:
--------------------------------------------------------------------------------
1 | require 'erb'
2 |
3 | def vagrant_shell_scripts_configure(shell, scripts_path, script_name = 'provision')
4 | shell.privileged = false
5 | shell.inline = ERB.new(<<-eos
6 | <%
7 |
8 | def import(file)
9 | ['#{ scripts_path }', '#{ File.dirname(__FILE__) }'].each do |path|
10 | if File.file?(File.join(path, file))
11 | return ERB.new(
12 | File.read(
13 | File.join(path, file)
14 | )
15 | ).result(binding)
16 | end
17 | end
18 | raise ArgumentError, 'The file "' + file + '" cannot be imported.'
19 | end
20 |
21 | %>
22 |
23 | <%= import '#{ script_name }' %>
24 | eos
25 | ).result
26 | end
27 |
--------------------------------------------------------------------------------
/vendor/ruby/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------