commit 682c06f23445dbddc981a3ebc207a41742f76285 Author: Brooke Kuhlmann Date: Wed Oct 5 14:51:41 2016 -0600 Added initial port of original OSX project. - This is a copy of the [OSX](https://github.com/bkuhlmann/osx) project originally released on 2012-03-31. The OSX project has been deprecated. All future development and support will take place with this project instead. - This project uses the *macOS* name in order to better match the updated branding and terminology used by Apple. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..654d0a0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,16 @@ +## Expected Behavior + + +## Actual Behavior + + +## Steps to Recreate + + +0. + +## Environment + + +## Screenshots/Screencasts + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..908fa9c --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,11 @@ +## Overview + + +## Details + + +## Notes + + +## Screenshots/Screencasts + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cc0b17b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# v1.0.0 (2016-10-05) + +- Initial version. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..722fcf3 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,61 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making +participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, +disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, +religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take +appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, +issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the +project or its community. Examples of representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed representative at an online or offline +event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at +[brooke@alchemists.io](mailto:brooke@alchemists.io). All complaints will be reviewed and investigated and will result in +a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain +confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be +posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent +repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0f99286 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# Overview + +Thanks for taking an interest in this open source project. Your support and involvement is greatly appreciated. The +following details what you need to know in order to contribute. + +# Requirements + +- Follow these [Basic Programming Styles](https://github.com/bkuhlmann/style_guides/blob/master/programming/basic.md). +- Follow these [Code Review Styles](https://github.com/bkuhlmann/style_guides/blob/master/programming/code_reviews.md). +- Follow these [Git Styles](https://github.com/bkuhlmann/style_guides/blob/master/programming/git.md). +- Follow these [Bash Styles](https://github.com/bkuhlmann/style_guides/blob/master/programming/languages/bash.md). +- Follow these [CSS Styles](https://github.com/bkuhlmann/style_guides/blob/master/programming/languages/css.md). +- Follow these [Ruby Styles](https://github.com/bkuhlmann/style_guides/blob/master/programming/languages/ruby/ruby.md). + +# Contributing Code + +0. Read the project README thoroughly before starting. +0. Fork the master branch of the repository. +0. Ensure there are no setup, usage, and/or test issues (again, follow the README). +0. Add tests for new functionality (refactoring and documentation changes can be excluded). +0. Ensure all tests pass. +0. Push your feature branch and submit a pull request. + +# Submitting Issues + +0. Submit an issue via the GitHub Issues tab (assuming one does not already exist). +0. Clearly describe the issue (including steps to reproduce). +0. Specify your enviroment setup (OS, browser, language, etc. with version info). +0. Provide a stack dump (if possible). +0. Explain any additional details that might help diagnose the problem quickly. + +# Feedback + +Expect a response within one to three business days. +Changes, alternatives, and/or improvements might be suggested upon review. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c523006 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,20 @@ +Copyright (c) 2016 [Alchemists](https://www.alchemists.io). + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5e48d93 --- /dev/null +++ b/README.md @@ -0,0 +1,150 @@ +# macOS + +[![Patreon](https://img.shields.io/badge/patreon-donate-brightgreen.svg)](https://www.patreon.com/bkuhlmann) + +Shell scripts for automated macOS machine setup. This project provides the foundational tooling for +automated macOS machine setup. To customize further see the companion +[macOS Config](https://github.com/bkuhlmann/mac_os-config) project for details. + + + +# Table of Contents + +- [Features](#features) +- [Requirements](#requirements) +- [Setup](#setup) +- [Usage](#usage) + - [Customization](#customization) +- [Versioning](#versioning) +- [Code of Conduct](#code-of-conduct) +- [Contributions](#contributions) +- [License](#license) +- [History](#history) +- [Credits](#credits) + + + +# Features + +- Provides a command line interface for installation and management of macOS software. +- Downloads and installs development tooling (required by Homebrew): + - [Xcode Command Line Tools](https://developer.apple.com/xcode) + - [Java SE Development Kit](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) +- Downloads, installs, and configures [Homebrew](http://brew.sh) command line software. +- Downloads, installs, and configures software applications generally not in the + [App Store](http://www.apple.com/macosx/whats-new/app-store.html). +- Downloads, installs, and configures software extensions. + +# Requirements + +0. [macOS](https://www.apple.com/macos) (with latest software updates applied) +0. [Xcode](https://developer.apple.com/xcode) (with accepted license agreement) + +# Setup + +Open a terminal window and execute one of the following setup sequences depending on your version +preference: + +Current Version (stable): + + git clone https://github.com/bkuhlmann/mac_os.git + cd mac_os + git checkout v1.0.0 + +Master Version (unstable): + + git clone https://github.com/bkuhlmann/mac_os.git + cd mac_os + +# Usage + +Run the following script: + + bin/run + +You will be presented with the following options: + + Boot: + B: Create boot disk. + Install: + b: Apply basic system settings. + t: Install development tools. + h: Install Homebrew software. + a: Install application software. + x: Install application software extensions. + d: Apply software defaults. + s: Setup installed software. + i: Install everything (i.e. executes all install options). + Restore: + R: Restore settings from backup. + Manage: + c: Check status of managed software. + C: Caffeinate machine. + ua: Uninstall application software. + ux: Uninstall application software extension. + ra: Reinstall application software. + rx: Reinstall application software extension. + w: Clean work (temp) directory. + q: Quit/Exit. + +Choose option `i` to run all install options or select a specific option to run a single option. +Each option is designed to be re-run if necessary. This can also be handy for performing upgrades, +re-running a missing/failed install, etc. + +The option prompt can be skipped by passing the desired option directly to the run.sh script. For +example, executing `./run.sh i` will execute the complete software install process. + +The machine should be rebooted after all install tasks have completed to ensure all settings have +been loaded. + +It is recommended that the `mac_os` project directory not be deleted and kept on the local machine +in order to manage installed software and benefit from future upgrades. + +## Customization + +Global settings can be configured via the following script: + +- `lib/settings.sh` + +All script programs can be found in the `bin` folder: + +- `bin/create_boot_disk` = Creates macOS boot disk. +- `bin/install_dev_tools` = Installs macOS development tools required by Homebrew. +- `bin/run` - The main script and interface for macOS setup. + +The `lib` folder provides foundational functions for installing, re-installing, and uninstalling +software. Everything provided via the [macOS Config](https://github.com/bkuhlmann/mac_os-config) +project is built upon the functions found in the `lib` folder. See the +[macOS Config](https://github.com/bkuhlmann/mac_os-config) project for further details. + +# Versioning + +Read [Semantic Versioning](http://semver.org) for details. Briefly, it means: + +- Patch (x.y.Z) - Incremented for small, backwards compatible bug fixes. +- Minor (x.Y.z) - Incremented for new, backwards compatible public API enhancements and/or bug fixes. +- Major (X.y.z) - Incremented for any backwards incompatible public API changes. + +# Code of Conduct + +Please note that this project is released with a [CODE OF CONDUCT](CODE_OF_CONDUCT.md). By +participating in this project you agree to abide by its terms. + +# Contributions + +Read [CONTRIBUTING](CONTRIBUTING.md) for details. + +# License + +Copyright (c) 2016 [Alchemists](https://www.alchemists.io). +Read the [LICENSE](LICENSE.md) for details. + +# History + +Read the [CHANGELOG](CHANGELOG.md) for details. +Built with [Bashsmith](https://github.com/bkuhlmann/bashsmith). + +# Credits + +Developed by [Brooke Kuhlmann](https://www.alchemists.io) at +[Alchemists](https://www.alchemists.io). diff --git a/bin/create_boot_disk b/bin/create_boot_disk new file mode 100755 index 0000000..43ed5a9 --- /dev/null +++ b/bin/create_boot_disk @@ -0,0 +1,36 @@ +#! /bin/bash + +# DESCRIPTION +# Creates macOS boot disk. + +# EXECUTION +printf "macOS Boot Disk Tips\n" +printf " - Use a USB drive (8GB or higher is best).\n" +printf " - Use Disk Utility to format the USB drive (Use \"Untitled\" for the label).\n" +printf "\nmacOS Boot Disk Usage:\n" +printf " 1. Insert the USB boot disk into the machine to be upgraded.\n" +printf " 2. Reboot the machine.\n" +printf " 3. Hold down the OPTION key before the Apple logo appears.\n" +printf " 4. Select the USB boot disk from the menu.\n" +printf " 5. Format the machine's internal drive using Disk Utility.\n" +printf " 6. Install the new operating system.\n" +printf "\nmacOS Reinstall:\n" +printf " 1. Click the Apple icon from the operating system main menu.\n" +printf " 2. Select the \"Restart...\" menu option.\n" +printf " 3. Hold down the COMMAND+R keys before the Apple logo appears.\n" +printf " 4. Wait for the macOS installer to load from the recovery partition.\n" +printf " 5. Use the dialog options to launch Disk Utility, reinstall the system, etc.\n" + +printf "\nCreating macOS boot disk...\n" + +if [[ ! -d "$MAC_OS_INSTALLER_PATH" ]]; then + printf "ERROR: macOS installer does not exist: $MAC_OS_INSTALLER_PATH. Use the App Store to download.\n" + exit 1 +fi + +if [[ ! -d "$MAC_OS_BOOT_DISK_PATH" ]]; then + printf "ERROR: Boot disk must be mounted at: $MAC_OS_BOOT_DISK_PATH.\n" + exit 1 +fi + +sudo "$MAC_OS_BOOT_DISK_CREATOR" --volume "$MAC_OS_BOOT_DISK_PATH" --applicationpath "$MAC_OS_INSTALLER_PATH" --nointeraction diff --git a/bin/install_dev_tools b/bin/install_dev_tools new file mode 100755 index 0000000..d18b004 --- /dev/null +++ b/bin/install_dev_tools @@ -0,0 +1,16 @@ +#! /bin/bash + +# DESCRIPTION +# Installs development tooling requirements. + +printf "Installing Xcode CLI tools...\n" +xcode-select --install + +read -p "Have you completed the Xcode CLI tools install (y/n)? " response +if [[ "$response" != "y" ]]; then + printf "ERROR: Xcode CLI tools must be installed before proceeding.\n" + exit 1 +fi + +printf "Installing Java...\n" +install_java "$JAVA_URL" "$JAVA_VOLUME_NAME" diff --git a/bin/run b/bin/run new file mode 100755 index 0000000..590895a --- /dev/null +++ b/bin/run @@ -0,0 +1,67 @@ +#! /bin/bash + +# DESCRIPTION +# Executes the command line interface. + +# USAGE +# ./run.sh OPTION + +# LIBRARY +source lib/installers.sh +source lib/options.sh +source lib/reinstallers.sh +source lib/restorers.sh +source lib/settings.sh +source lib/uninstallers.sh +source lib/utilities.sh +source lib/verifiers.sh + +if [[ -e "$MAC_OS_CONFIG_PATH" ]]; then + source "$MAC_OS_CONFIG_PATH/lib/settings.sh" +else + printf "ERROR: Unable to load macOS configuration: $MAC_OS_CONFIG_PATH.\n\n" + printf "Please check the following before continuing:\n" + printf " • Download the default macOS configuration here: https://github.com/bkuhlmann/mac_os-config.\n" + printf " • Customize as necessary for your setup or fork the project and make your own configuration.\n" + printf " • When finished, your folder structure should look like this:\n" + printf " • /mac_os:\n" + printf " • /mac_os-config:\n" + exit 1 +fi + +# EXECUTION +while true; do + if [[ $# == 0 ]]; then + printf "\nUsage: run OPTION\n" + printf "\nOSX Options:\n" + printf " Boot:\n" + printf " B: Create boot disk.\n" + printf " Install:\n" + printf " b: Apply basic settings.\n" + printf " t: Install development tools.\n" + printf " h: Install Homebrew software.\n" + printf " a: Install application software.\n" + printf " x: Install application software extensions.\n" + printf " d: Apply default settings.\n" + printf " s: Setup installed software.\n" + printf " i: Install everything (i.e. executes all install options).\n" + printf " Restore:\n" + printf " R: Restore settings from backup.\n" + printf " Manage:\n" + printf " c: Check status of managed software.\n" + printf " C: Caffeinate machine.\n" + printf " ua: Uninstall application software.\n" + printf " ux: Uninstall application software extension.\n" + printf " ra: Reinstall application software.\n" + printf " rx: Reinstall application software extension.\n" + printf " w: Clean work (temp) directory.\n" + printf " q: Quit/Exit.\n\n" + read -p "Enter selection: " response + printf "\n" + process_option $response + else + process_option $1 + fi + + break +done diff --git a/lib/installers.sh b/lib/installers.sh new file mode 100644 index 0000000..eb7fc76 --- /dev/null +++ b/lib/installers.sh @@ -0,0 +1,301 @@ +#! /bin/bash + +# DESCRIPTION +# Defines software installer functions. + +# Mounts a disk image. +# Parameters: +# $1 = The image path. +mount_image() { + printf "Mounting image...\n" + hdiutil attach -quiet -nobrowse -noautoopen "$1" +} +export -f mount_image + +# Unmounts a disk image. +# Parameters: +# $1 = The mount path. +unmount_image() { + printf "Unmounting image...\n" + hdiutil detach -force "$1" +} +export -f unmount_image + +# Downloads an installer to local disk. +# Parameters: +# $1 = The URL. +# $2 = The file name. +# $3 = The HTTP header. +download_installer() { + local url="$1" + local file_name="$2" + local http_header="$3" + + printf "%s\n" "Downloading $1..." + clean_work_path + mkdir $MAC_OS_WORK_PATH + curl --header "$http_header" --location --retry 3 --retry-delay 5 --fail --silent --show-error "$url" >> "$MAC_OS_WORK_PATH/$file_name" +} +export -f download_installer + +# Downloads an installer to the $HOME/Downloads folder for manual use. +# Parameters: +# $1 = The URL. +# $2 = The file name. +download_only() { + if [[ -e "$HOME/Downloads/$2" ]]; then + printf "Downloaded: $2.\n" + else + printf "Downloading $1...\n" + download_installer "$1" "$2" + mv "$MAC_OS_WORK_PATH/$2" "$HOME/Downloads" + fi +} +export -f download_only + +# Installs a single file. +# Parameters: +# $1 = The URL. +# $2 = The install path. +install_file() { + local file_url="$1" + local file_name=$(get_file_name "$1") + local install_path="$2" + + if [[ ! -e "$install_path" ]]; then + printf "Installing: $install_path...\n" + download_installer "$file_url" "$file_name" + mkdir -p $(dirname "$install_path") + mv "$MAC_OS_WORK_PATH/$file_name" "$install_path" + printf "Installed: $file_name.\n" + verify_path "$install_path" + fi +} +export -f install_file + +# Installs an application. +# Parameters: +# $1 = The application source path. +# $2 = The application name. +install_app() { + local install_root=$(get_install_root "$2") + local file_extension=$(get_file_extension "$2") + + printf "Installing: $install_root/$2...\n" + + case $file_extension in + 'app') + cp -a "$1/$2" "$install_root";; + 'prefPane') + sudo cp -pR "$1/$2" "$install_root";; + 'qlgenerator') + sudo cp -pR "$1/$2" "$install_root" && qlmanage -r;; + *) + printf "ERROR: Unknown file extension: $file_extension.\n" + esac +} +export -f install_app + +# Installs a package. +# Parameters: +# $1 = The package source path. +# $2 = The application name. +install_pkg() { + local install_root=$(get_install_root "$2") + + printf "Installing: $install_root/$2...\n" + local package=$(sudo find "$1" -maxdepth 1 -type f -name "*.pkg" -o -name "*.mpkg") + sudo installer -pkg "$package" -target / +} +export -f install_pkg + +# Installs Java. +# Parameters: +# $1 = The URL. +# $2 = The volume name. +install_java() { + local url="$1" + local volume_path="/Volumes/$2" + local app_name="java" + local install_path="/usr/bin/$app_name" + local download_file="download.dmg" + + download_installer "$url" "$download_file" "Cookie: oraclelicense=accept-securebackup-cookie" + mount_image "$MAC_OS_WORK_PATH/$download_file" + local package=$(sudo find "$volume_path" -maxdepth 1 -type f -name "*.pkg") + sudo installer -pkg "$package" -target / + unmount_image "$volume_path" + printf "Installed: $app_name.\n" +} +export -f install_java + +# Installs an application via a DMG file. +# Parameters: +# $1 = The URL. +# $2 = The mount path. +# $3 = The application name. +install_dmg_app() { + local url="$1" + local mount_point="/Volumes/$2" + local app_name="$3" + local install_path=$(get_install_path "$app_name") + local download_file="download.dmg" + + if [[ ! -e "$install_path" ]]; then + download_installer "$url" "$download_file" + mount_image "$MAC_OS_WORK_PATH/$download_file" + install_app "$mount_point" "$app_name" + unmount_image "$mount_point" + verify_application "$app_name" + fi +} +export -f install_dmg_app + +# Installs a package via a DMG file. +# Parameters: +# $1 = The URL. +# $2 = The mount path. +# $3 = The application name. +install_dmg_pkg() { + local url="$1" + local mount_point="/Volumes/$2" + local app_name="$3" + local install_path=$(get_install_path "$app_name") + local download_file="download.dmg" + + if [[ ! -e "$install_path" ]]; then + download_installer "$url" "$download_file" + mount_image "$MAC_OS_WORK_PATH/$download_file" + install_pkg "$mount_point" "$app_name" + unmount_image "$mount_point" + printf "Installed: $app_name.\n" + verify_application "$app_name" + fi +} +export -f install_dmg_pkg + +# Installs an application via a zip file. +# Parameters: +# $1 = The URL. +# $2 = The application name. +install_zip_app() { + local url="$1" + local app_name="$2" + local install_path=$(get_install_path "$app_name") + local download_file="download.zip" + + if [[ ! -e "$install_path" ]]; then + download_installer "$url" "$download_file" + + ( + printf "Preparing...\n" + cd "$MAC_OS_WORK_PATH" + unzip -q "$download_file" + ) + + install_app "$MAC_OS_WORK_PATH" "$app_name" + printf "Installed: $app_name.\n" + verify_application "$app_name" + fi +} +export -f install_zip_app + +# Installs an application via a tar file. +# Parameters: +# $1 = The URL. +# $2 = The application name. +# $3 = The decompress options. +install_tar_app() { + local url="$1" + local app_name="$2" + local options="$3" + local install_path=$(get_install_path "$app_name") + local download_file="download.tar" + + if [[ ! -e "$install_path" ]]; then + download_installer "$url" "$download_file" + + ( + printf "Preparing...\n" + cd "$MAC_OS_WORK_PATH" + tar "$options" "$download_file" + ) + + install_app "$MAC_OS_WORK_PATH" "$app_name" + printf "Installed: $app_name.\n" + verify_application "$app_name" + fi +} +export -f install_tar_app + +# Installs a package via a zip file. +# Parameters: +# $1 = The URL. +# $2 = The application name. +install_zip_pkg() { + local url="$1" + local app_name="$2" + local install_path=$(get_install_path "$app_name") + local download_file="download.zip" + + if [[ ! -e "$install_path" ]]; then + download_installer "$url" "$download_file" + + ( + printf "Preparing...\n" + cd "$MAC_OS_WORK_PATH" + unzip -q "$download_file" + ) + + install_pkg "$MAC_OS_WORK_PATH" "$app_name" + printf "Installed: $app_name.\n" + verify_application "$app_name" + fi +} +export -f install_zip_pkg + +# Installs application code from a Git repository. +# Parameters: +# $1 = Repository URL. +# $2 = Install path. +# $3 = Git clone options (if any). +install_git_app() { + local repository_url="$1" + local app_name=$(get_file_name "$2") + local install_path="$2" + local options="--quiet" + + if [[ -n "$3" ]]; then + local options="$options $3" + fi + + if [[ ! -e "$install_path" ]]; then + printf "Installing: $install_path/$app_name...\n" + git clone $options "$repository_url" "$install_path" + printf "Installed: $app_name.\n" + verify_path "$install_path" + fi +} +export -f install_git_app + +# Installs settings from a Git repository. +# Parameters: +# $1 = The repository URL. +# $2 = The repository version. +# $3 = The project directory. +# $4 = The script to run (including any arguments). +install_git_project() { + local repo_url="$1" + local repo_version="$2" + local project_dir="$3" + local script="$4" + + git clone "$repo_url" + ( + cd "$project_dir" + git checkout "$repo_version" + eval "$script" + ) + rm -rf "$project_dir" +} +export -f install_git_project diff --git a/lib/options.sh b/lib/options.sh new file mode 100644 index 0000000..1ef5f38 --- /dev/null +++ b/lib/options.sh @@ -0,0 +1,60 @@ +#! /bin/bash + +# DESCRIPTION +# Defines command line prompt options. + +# Process option selection. +# Parameters: +# $1 = The option to process. +process_option() { + case $1 in + 'B') + bin/create_boot_disk;; + 'b') + "$MAC_OS_CONFIG_PATH/bin/apply_basic_settings";; + 't') + bin/install_dev_tools;; + 'h') + "$MAC_OS_CONFIG_PATH/bin/install_homebrew";; + 'a') + "$MAC_OS_CONFIG_PATH/bin/install_applications";; + 'x') + "$MAC_OS_CONFIG_PATH/bin/install_extensions";; + 'd') + "$MAC_OS_CONFIG_PATH/bin/apply_default_settings";; + 's') + "$MAC_OS_CONFIG_PATH/bin/setup_software";; + 'i') + caffeinate_machine + "$MAC_OS_CONFIG_PATH/bin/apply_basic_settings" + bin/install_dev_tools + "$MAC_OS_CONFIG_PATH/bin/install_homebrew" + "$MAC_OS_CONFIG_PATH/bin/install_applications" + "$MAC_OS_CONFIG_PATH/bin/install_extensions" + "$MAC_OS_CONFIG_PATH/bin/apply_default_settings" + "$MAC_OS_CONFIG_PATH/bin/setup_software" + clean_work_path;; + 'R') + "$MAC_OS_CONFIG_PATH/bin/restore_backup";; + 'c') + verify_homebrews + verify_applications + verify_extensions;; + 'C') + caffeinate_machine;; + 'ua') + uninstall_application;; + 'ux') + uninstall_extension;; + 'ra') + reinstall_application;; + 'rx') + reinstall_extension;; + 'w') + clean_work_path;; + 'q');; + *) + printf "ERROR: Invalid option.\n";; + esac +} +export -f process_option diff --git a/lib/reinstallers.sh b/lib/reinstallers.sh new file mode 100644 index 0000000..b0ae9b7 --- /dev/null +++ b/lib/reinstallers.sh @@ -0,0 +1,18 @@ +#! /bin/bash + +# DESCRIPTION +# Defines reinstall functions. + +# Reinstall application. +reinstall_application() { + uninstall_application + scripts/applications.sh +} +export -f reinstall_application + +# Reinstall extension. +reinstall_extension() { + uninstall_extension + scripts/extensions.sh +} +export -f reinstall_extension diff --git a/lib/restorers.sh b/lib/restorers.sh new file mode 100644 index 0000000..4f5c19b --- /dev/null +++ b/lib/restorers.sh @@ -0,0 +1,31 @@ +#! /bin/bash + +# DESCRIPTION +# Defines software restore functions. + +# Label: Restore Preference +# Description: Restores an application preference. +# Parameters: $1 (required) - The backup volume root path, $2 (required) - The preference file. +restore_preference() { + local backup_root="$1" + local preference_file="$2" + local backup_path="$backup_root/Users/$USER/Library/Preferences/$preference_file" + local restore_root="$HOME/Library/Preferences" + + cp -p "$backup_path" "$restore_root" +} +export -f restore_preference + +# Label: Restore Application Support +# Description: Restores application support files. +# Parameters: $1 (required) - The backup volume root path, $2 required - The application name. +restore_app_support() { + local backup_root="$1" + local app_name="$2" + local backup_path="$backup_root/Users/$USER/Library/Application Support/$app_name" + local restore_path="$HOME/Library/Application Support" + + mkdir -p "$restore_path" + cp -pR "$backup_path" "$restore_path" +} +export -f restore_app_support diff --git a/lib/settings.sh b/lib/settings.sh new file mode 100644 index 0000000..3e3af0c --- /dev/null +++ b/lib/settings.sh @@ -0,0 +1,22 @@ +#! /bin/bash + +# DESCRIPTION +# Defines global settings. + +# SETTINGS +# General +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +# Globals +export MAC_OS_BOOT_DISK_CREATOR="/Applications/Install macOS Sierra.app/Contents/Resources/createinstallmedia" +export MAC_OS_BOOT_DISK_PATH="/Volumes/Untitled" +export MAC_OS_INSTALLER_PATH="/Applications/Install macOS Sierra.app" +export MAC_OS_WORK_PATH=/tmp/downloads +export MAC_OS_CONFIG_PATH="../mac_os-config" + +# Java +export JAVA_VOLUME_NAME="JDK 8 Update 101" +export JAVA_URL="http://download.oracle.com/otn-pub/java/jdk/8u101-b13/jdk-8u101-macosx-x64.dmg" diff --git a/lib/uninstallers.sh b/lib/uninstallers.sh new file mode 100644 index 0000000..384d5de --- /dev/null +++ b/lib/uninstallers.sh @@ -0,0 +1,53 @@ +#! /bin/bash + +# DESCRIPTION +# Defines uninstall functions. + +# Uninstalls selected application. +uninstall_application() { + # Only use environment keys that end with "APP_NAME". + local keys=($(set | awk -F "=" '{print $1}' | grep ".*APP_NAME")) + + printf "Select application to uninstall:\n" + for ((index = 0; index < ${#keys[*]}; index++)); do + local app_file="${!keys[$index]}" + printf " $index: ${app_file}\n" + done + printf " q: Quit/Exit\n\n" + + read -p "Enter selection: " response + printf "\n" + + local regex="^[0-9]+$" + if [[ $response =~ $regex ]]; then + local app_file="${!keys[$response]}" + local app_path=$(get_install_path "${app_file}") + sudo rm -rf "$app_path" + printf "Uninstalled: ${app_path}\n" + fi +} +export -f uninstall_application + +# Uninstalls selected extension. +uninstall_extension() { + # Only use environment keys that end with "EXTENSION_PATH". + local keys=($(set | awk -F "=" '{print $1}' | grep ".*EXTENSION_PATH")) + + printf "Select extension to uninstall:\n" + for ((index = 0; index < ${#keys[*]}; index++)); do + local extension_path="${!keys[$index]}" + printf " $index: ${extension_path}\n" + done + printf " q: Quit/Exit\n\n" + + read -p "Enter selection: " response + printf "\n" + + local regex="^[0-9]+$" + if [[ $response =~ $regex ]]; then + local extension_path="${!keys[$response]}" + rm -rf "${extension_path}" + printf "Uninstalled: ${extension_path}\n" + fi +} +export -f uninstall_extension diff --git a/lib/utilities.sh b/lib/utilities.sh new file mode 100644 index 0000000..13b7eec --- /dev/null +++ b/lib/utilities.sh @@ -0,0 +1,79 @@ +#! /bin/bash + +# DESCRIPTION +# Defines general utility functions. + +# Answers the file name. +# Parameters: +# $1 = The file path. +get_file_name() { + printf "${1##*/}" # Answers file or directory name. +} +export -f get_file_name + +# Answers the file extension. +# Parameters: +# $1 = The file name. +get_file_extension() { + local name=$(get_file_name "$1") + local extension="${1##*.}" # Excludes dot. + + if [[ "$name" == "$extension" ]]; then + printf '' + else + printf "$extension" + fi +} +export -f get_file_extension + +# Answers the root install path for file name. +# Parameters: +# $1 = The file name. +get_install_root() { + local file_name="$1" + local file_extension=$(get_file_extension "$file_name") + + # Dynamically build the install path based on file extension. + case $file_extension in + '') + printf "/usr/local/bin";; + 'app') + printf "/Applications";; + 'prefPane') + printf "/Library/PreferencePanes";; + 'qlgenerator') + printf "/Library/QuickLook";; + *) + printf "/tmp/unknown";; + esac +} +export -f get_install_root + +# Answers the full install path (including file name) for file name. +# Parameters: +# $1 = The file name. +get_install_path() { + local file_name="$1" + local install_path=$(get_install_root "$file_name") + printf "$install_path/$file_name" +} +export -f get_install_path + +# Cleans work path for temporary processing of installs. +clean_work_path() { + rm -rf "$MAC_OS_WORK_PATH" +} +export -f clean_work_path + +# Caffeinate machine. +caffeinate_machine() { + local pid=$(ps aux | grep caffeinate | grep -v grep | awk '{print $2}') + + if [[ -n "$pid" ]]; then + printf "Whoa, tweaker, machine is already caffeinated!\n" + else + caffeinate -s -u -d -i -t 3153600000 > /dev/null & + printf "Machine caffeinated.\n" + fi +} +export -f caffeinate_machine diff --git a/lib/verifiers.sh b/lib/verifiers.sh new file mode 100644 index 0000000..1e86f7a --- /dev/null +++ b/lib/verifiers.sh @@ -0,0 +1,108 @@ +#! /bin/bash + +# DESCRIPTION +# Defines verification/validation functions. + +# Verifies Homebrew software exists. +# Parameters: +# $1 = The file name. +verify_homebrew() { + local application="$1" + local applications="$2" + + if [[ "${applications[*]}" != *"$application"* ]]; then + printf " - Missing: $application\n" + fi +} +export -f verify_homebrew + +# Checks for missing Homebrew software. +verify_homebrews() { + printf "Checking Homebrew software...\n" + + local applications="$(brew list)" + + while read line; do + # Skip blank or comment lines. + if [[ "$line" == "brew install"* ]]; then + local application=$(printf "$line" | awk '{print $3}') + + # Exception: "gpg" is the binary but is listed as "gnugp". + if [[ "$application" == "gpg" ]]; then + application="gnupg" + fi + + # Exception: "hg" is the binary but is listed as "mercurial". + if [[ "$application" == "hg" ]]; then + application="mercurial" + fi + + verify_homebrew "$application" "${applications[*]}" + fi + done < "$PWD/scripts/homebrew.sh" + + printf "Homebrew check complete.\n" +} +export -f verify_homebrews + +# Verifies application exists. +# Parameters: +# $1 = The file name. +verify_application() { + local file_name="$1" + + # Display the missing install if not found. + local install_path=$(get_install_path "$file_name") + + if [[ ! -e "$install_path" ]]; then + printf " - Missing: $file_name\n" + fi +} +export -f verify_application + +# Checks for missing applications suffixed by "APP_NAME" as defined in settings.sh. +verify_applications() { + printf "\nChecking application software...\n" + + # Only use environment keys that end with "APP_NAME". + local file_names=$(set | awk -F "=" '{print $1}' | grep ".*APP_NAME") + + # For each application name, check to see if the application is installed. Otherwise, skip. + for name in $file_names; do + # Pass the key value to verfication. + verify_application "${!name}" + done + + printf "Application software check complete.\n" +} +export -f verify_applications + +# Verifies path exists. +# Parameters: +# $1 = The path. +verify_path() { + local path="$1" + + # Display the missing path if not found. + if [[ ! -e "$path" ]]; then + printf " - Missing: $path\n" + fi +} +export -f verify_path + +# Checks for missing extensions suffixed by "EXTENSION_PATH" as defined in settings.sh. +verify_extensions() { + printf "\nChecking application extensions...\n" + + # Only use environment keys that end with "EXTENSION_PATH". + local extensions=$(set | awk -F "=" '{print $1}' | grep ".*EXTENSION_PATH") + + # For each extension, check to see if the extension is installed. Otherwise, skip. + for extension in $extensions; do + # Evaluate/extract the key (extension) value and pass it on for verfication. + verify_path "${!extension}" + done + + printf "Application extension check complete.\n" +} +export -f verify_extensions