├── .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 | --------------------------------------------------------------------------------