commit c94070723b495f341df41c7b8f7712f87e348fb9 Author: Andreas Düren Date: Fri Jan 9 08:33:22 2026 -0600 Initial setup: mac_os installer with custom config Contains both components needed for Mac setup: - mac_os/: Installer scripts (from bkuhlmann/mac_os) - mac_os-config/: Custom app configuration Configured apps: - Homebrew formulas: atuin, bash, ffmpeg, mas, mole, node, rename, ykman - Homebrew casks: chromium, deepl, element, nextcloud, nova, proton suite, signal, transmit - App Store: Affinity suite, Bitwarden, Final Cut Pro, Invoice Ninja, iWork, PastePal, xSearch Usage: cd mac_os && ./bin/run Co-Authored-By: Claude Opus 4.5 diff --git a/mac_os-config/.circleci/config.yml b/mac_os-config/.circleci/config.yml new file mode 100644 index 0000000..373760d --- /dev/null +++ b/mac_os-config/.circleci/config.yml @@ -0,0 +1,31 @@ +version: 2.1 +jobs: + build: + working_directory: ~/project + docker: + - image: bkuhlmann/alpine-ruby:latest + steps: + - checkout + + - restore_cache: + name: Gems Restore + keys: + - gem-cache-{{.Branch}}-{{checksum "Gemfile"}} + - gem-cache- + + - run: + name: Gems Install + command: | + gem update --system + bundle config set path "vendor/bundle" + bundle install + + - save_cache: + name: Gems Store + key: gem-cache-{{.Branch}}-{{checksum "Gemfile"}} + paths: + - vendor/bundle + + - run: + name: Rake + command: bundle exec rake diff --git a/mac_os-config/.config/rubocop/config.yml b/mac_os-config/.config/rubocop/config.yml new file mode 100644 index 0000000..bb2750e --- /dev/null +++ b/mac_os-config/.config/rubocop/config.yml @@ -0,0 +1,2 @@ +inherit_gem: + caliber: config/all.yml diff --git a/mac_os-config/.github/FUNDING.yml b/mac_os-config/.github/FUNDING.yml new file mode 100644 index 0000000..38be873 --- /dev/null +++ b/mac_os-config/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [bkuhlmann] diff --git a/mac_os-config/.github/ISSUE_TEMPLATE/config.yml b/mac_os-config/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..29a903d --- /dev/null +++ b/mac_os-config/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Community + url: https://alchemists.io/community + about: Please ask questions or discuss specifics here. + - name: Security + url: https://alchemists.io/policies/security + about: Please report security vulnerabilities here. diff --git a/mac_os-config/.github/ISSUE_TEMPLATE/issue.md b/mac_os-config/.github/ISSUE_TEMPLATE/issue.md new file mode 100644 index 0000000..61adc6e --- /dev/null +++ b/mac_os-config/.github/ISSUE_TEMPLATE/issue.md @@ -0,0 +1,18 @@ +--- +name: Issue +title: "Add|Update|Fix|Remove|Refactor " +about: Report an issue. Please use only one of the subject prefixes. +--- + + + +## Why + + +## How + + +## Notes + diff --git a/mac_os-config/.github/PULL_REQUEST_TEMPLATE.md b/mac_os-config/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..1b93c92 --- /dev/null +++ b/mac_os-config/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,8 @@ +## Overview + + +## Screenshots/Screencasts + + +## Details + diff --git a/mac_os-config/.gitignore b/mac_os-config/.gitignore new file mode 100644 index 0000000..b844b14 --- /dev/null +++ b/mac_os-config/.gitignore @@ -0,0 +1 @@ +Gemfile.lock diff --git a/mac_os-config/.ruby-version b/mac_os-config/.ruby-version new file mode 100644 index 0000000..fcdb2e1 --- /dev/null +++ b/mac_os-config/.ruby-version @@ -0,0 +1 @@ +4.0.0 diff --git a/mac_os-config/CITATION.cff b/mac_os-config/CITATION.cff new file mode 100644 index 0000000..499a73d --- /dev/null +++ b/mac_os-config/CITATION.cff @@ -0,0 +1,22 @@ +cff-version: 1.2.0 +message: Please use the following metadata when citing this project in your work. +title: macOS Configuration +abstract: Shell scripts for customized macOS machine setup. +version: 30.0.0 +license: Hippocratic-2.1 +date-released: 2026-01-01 +authors: + - family-names: Kuhlmann + given-names: Brooke + affiliation: Alchemists + orcid: https://orcid.org/0000-0002-5810-6268 +keywords: + - bash + - shell + - scripts + - automation + - setup + - recovery +repository-code: https://github.com/bkuhlmann/mac_os-config +repository-artifact: https://alchemists.io/projects/mac_os-config +url: https://alchemists.io/projects/mac_os-config diff --git a/mac_os-config/Gemfile b/mac_os-config/Gemfile new file mode 100644 index 0000000..528f7e5 --- /dev/null +++ b/mac_os-config/Gemfile @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +ruby file: ".ruby-version" + +source "https://rubygems.org" + +gem "caliber", "~> 0.82" +gem "debug", "~> 1.11" +gem "git-lint", "~> 10.0" +gem "rake", "~> 13.3" diff --git a/mac_os-config/LICENSE.adoc b/mac_os-config/LICENSE.adoc new file mode 100644 index 0000000..7f23bda --- /dev/null +++ b/mac_os-config/LICENSE.adoc @@ -0,0 +1,134 @@ += Hippocratic License + +Version: 2.1.0. + +Purpose. The purpose of this License is for the Licensor named above to +permit the Licensee (as defined below) broad permission, if consistent +with Human Rights Laws and Human Rights Principles (as each is defined +below), to use and work with the Software (as defined below) within the +full scope of Licensor’s copyright and patent rights, if any, in the +Software, while ensuring attribution and protecting the Licensor from +liability. + +Permission and Conditions. The Licensor grants permission by this +license ("License"), free of charge, to the extent of Licensor’s +rights under applicable copyright and patent law, to any person or +entity (the "Licensee") obtaining a copy of this software and +associated documentation files (the "Software"), to do everything with +the Software that would otherwise infringe (i) the Licensor’s copyright +in the Software or (ii) any patent claims to the Software that the +Licensor can license or becomes able to license, subject to all of the +following terms and conditions: + +* Acceptance. This License is automatically offered to every person and +entity subject to its terms and conditions. Licensee accepts this +License and agrees to its terms and conditions by taking any action with +the Software that, absent this License, would infringe any intellectual +property right held by Licensor. +* Notice. Licensee must ensure that everyone who gets a copy of any part +of this Software from Licensee, with or without changes, also receives +the License and the above copyright notice (and if included by the +Licensor, patent, trademark and attribution notice). Licensee must cause +any modified versions of the Software to carry prominent notices stating +that Licensee changed the Software. For clarity, although Licensee is +free to create modifications of the Software and distribute only the +modified portion created by Licensee with additional or different terms, +the portion of the Software not modified must be distributed pursuant to +this License. If anyone notifies Licensee in writing that Licensee has +not complied with this Notice section, Licensee can keep this License by +taking all practical steps to comply within 30 days after the notice. If +Licensee does not do so, Licensee’s License (and all rights licensed +hereunder) shall end immediately. +* Compliance with Human Rights Principles and Human Rights Laws. +[arabic] +. Human Rights Principles. +[loweralpha] +.. Licensee is advised to consult the articles of the United Nations +Universal Declaration of Human Rights and the United Nations Global +Compact that define recognized principles of international human rights +(the "Human Rights Principles"). Licensee shall use the Software in a +manner consistent with Human Rights Principles. +.. Unless the Licensor and Licensee agree otherwise, any dispute, +controversy, or claim arising out of or relating to (i) Section 1(a) +regarding Human Rights Principles, including the breach of Section 1(a), +termination of this License for breach of the Human Rights Principles, +or invalidity of Section 1(a) or (ii) a determination of whether any Law +is consistent or in conflict with Human Rights Principles pursuant to +Section 2, below, shall be settled by arbitration in accordance with the +Hague Rules on Business and Human Rights Arbitration (the "Rules"); +provided, however, that Licensee may elect not to participate in such +arbitration, in which event this License (and all rights licensed +hereunder) shall end immediately. The number of arbitrators shall be one +unless the Rules require otherwise. ++ +Unless both the Licensor and Licensee agree to the contrary: (1) All +documents and information concerning the arbitration shall be public and +may be disclosed by any party; (2) The repository referred to under +Article 43 of the Rules shall make available to the public in a timely +manner all documents concerning the arbitration which are communicated +to it, including all submissions of the parties, all evidence admitted +into the record of the proceedings, all transcripts or other recordings +of hearings and all orders, decisions and awards of the arbitral +tribunal, subject only to the arbitral tribunal’s powers to take such +measures as may be necessary to safeguard the integrity of the arbitral +process pursuant to Articles 18, 33, 41 and 42 of the Rules; and (3) +Article 26(6) of the Rules shall not apply. +. Human Rights Laws. The Software shall not be used by any person or +entity for any systems, activities, or other uses that violate any Human +Rights Laws. "Human Rights Laws" means any applicable laws, +regulations, or rules (collectively, "Laws") that protect human, +civil, labor, privacy, political, environmental, security, economic, due +process, or similar rights; provided, however, that such Laws are +consistent and not in conflict with Human Rights Principles (a dispute +over the consistency or a conflict between Laws and Human Rights +Principles shall be determined by arbitration as stated above). Where +the Human Rights Laws of more than one jurisdiction are applicable or in +conflict with respect to the use of the Software, the Human Rights Laws +that are most protective of the individuals or groups harmed shall +apply. +. Indemnity. Licensee shall hold harmless and indemnify Licensor (and +any other contributor) against all losses, damages, liabilities, +deficiencies, claims, actions, judgments, settlements, interest, awards, +penalties, fines, costs, or expenses of whatever kind, including +Licensor’s reasonable attorneys’ fees, arising out of or relating to +Licensee’s use of the Software in violation of Human Rights Laws or +Human Rights Principles. +* Failure to Comply. Any failure of Licensee to act according to the +terms and conditions of this License is both a breach of the License and +an infringement of the intellectual property rights of the Licensor +(subject to exceptions under Laws, e.g., fair use). In the event of a +breach or infringement, the terms and conditions of this License may be +enforced by Licensor under the Laws of any jurisdiction to which +Licensee is subject. Licensee also agrees that the Licensor may enforce +the terms and conditions of this License against Licensee through +specific performance (or similar remedy under Laws) to the extent +permitted by Laws. For clarity, except in the event of a breach of this +License, infringement, or as otherwise stated in this License, Licensor +may not terminate this License with Licensee. +* Enforceability and Interpretation. If any term or provision of this +License is determined to be invalid, illegal, or unenforceable by a +court of competent jurisdiction, then such invalidity, illegality, or +unenforceability shall not affect any other term or provision of this +License or invalidate or render unenforceable such term or provision in +any other jurisdiction; provided, however, subject to a court +modification pursuant to the immediately following sentence, if any term +or provision of this License pertaining to Human Rights Laws or Human +Rights Principles is deemed invalid, illegal, or unenforceable against +Licensee by a court of competent jurisdiction, all rights in the +Software granted to Licensee shall be deemed null and void as between +Licensor and Licensee. Upon a determination that any term or provision +is invalid, illegal, or unenforceable, to the extent permitted by Laws, +the court may modify this License to affect the original purpose that +the Software be used in compliance with Human Rights Principles and +Human Rights Laws as closely as possible. The language in this License +shall be interpreted as to its fair meaning and not strictly for or +against any party. +* Disclaimer. TO THE FULL EXTENT ALLOWED BY LAW, THIS SOFTWARE COMES +"AS IS," WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED, AND LICENSOR AND +ANY OTHER CONTRIBUTOR SHALL NOT BE LIABLE TO ANYONE FOR ANY DAMAGES OR +OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE +OR THIS LICENSE, UNDER ANY KIND OF LEGAL CLAIM. + +This Hippocratic License is an link:https://ethicalsource.dev[Ethical Source license] and is offered +for use by licensors and licensees at their own risk, on an "AS IS" basis, and with no warranties +express or implied, to the maximum extent permitted by Laws. diff --git a/mac_os-config/README.adoc b/mac_os-config/README.adoc new file mode 100644 index 0000000..da0cc71 --- /dev/null +++ b/mac_os-config/README.adoc @@ -0,0 +1,522 @@ +:toc: macro +:toclevels: 5 +:figure-caption!: + += macOS Configuration + +This project provides a highly opinionated default configuration built upon the +link:https://alchemists.io/projects/mac_os[macOS] project. Should the configuration provided by +this project not be to your liking, feel free to fork and customize for your specific needs. + +toc::[] + +== Features + +Due to the amount of tooling used, the following features are broken down into subsections for +easier navigation. + +=== Homebrew Formulas + +Installs the following link:https://brew.sh[formulas]: + +* link:https://www.gnu.org/software/bash[Bash] +* link:http://bash-completion.alioth.debian.org[Bash Completion] +* link:https://github.com/sharkdp/bat[Bat] +* link:https://github.com/toy/blueutil[blueutil] +* link:https://github.com/cloudflare/cloudflared[cloudflared] +* link:https://github.com/schollz/croc[croc] +* link:https://crystal-lang.org[Crystal] +* link:http://ctags.sourceforge.net[CTags] +* link:https://difftastic.wilfred.me.uk[Difftastic] +* link:https://direnv.net[direnv] +* link:https://github.com/wagoodman/dive[Dive] +* link:https://github.com/muesli/duf[duf] +* link:https://github.com/bootandy/dust[Dust] +* link:http://duti.org[duti] +* link:https://eradman.com/entrproject[Entr] +* link:https://exiftool.org/index.html[ExifTool] +* link:https://github.com/sharkdp/fd[fd] +* link:https://github.com/tako8ki/frum[Frum] +* link:https://github.com/Schniz/fnm[Fast Node Manager] +* link:https://fx.wtf[fx] +* link:https://github.com/junegunn/fzf[FZF] +* link:https://git-scm.com[Git] +* link:https://github.com/dandavison/delta[Git Delta] +* link:https://github.com/newren/git-filter-repo[Git Filter Repo] +* link:https://github.com/github/git-sizer[Git Sizer] +* link:https://www.gnupg.org[GPG] +* link:https://github.com/orf/gping[gping] +* link:http://www.graphicsmagick.org[Graphics Magick] +* link:https://www.graphviz.org[Graphviz] +* link:https://github.com/sharkdp/hexyl[hexyl] +* link:http://www.andre-simon.de/doku/highlight/en/highlight.php[Highlight] +* link:https://hisham.hm/htop[htop] +* link:https://github.com/reorx/httpstat[HTTP Stat] +* link:https://github.com/sharkdp/hyperfine[Hyperfine] +* link:https://imagemagick.org[ImageMagick] +* link:https://stedolan.github.io/jq[jq] +* link:https://lnav.org[lnav] +* link:https://github.com/CISOfy/lynis[Lynis] +* link:https://mailpit.axllent.org[Mailpit] +* link:https://github.com/mas-cli/mas[Mac App Store] +* link:https://jedisct1.github.io/minisign[Minisign] +* link:https://github.com/variadico/noti[Noti] +* link:https://github.com/hatoo/oha[Oha] +* link:https://ollama.com[Ollama] +* link:https://www.openssh.com[OpenSSH] +* link:https://openssl.org[OpenSSL] +* link:https://osv.dev[Open Source Vulnerability Scanner] +* link:https://github.com/DarthSim/overmind[Overmind] +* link:https://pandoc.org[Pandoc] +* link:https://savannah.gnu.org/projects/parallel[Parallel] +* link:https://github.com/sharkdp/pastel[Pastel] +* link:https://www.zlib.net/pigz[Pigz] +* link:https://github.com/GPGTools/pinentry[Pinentry] +* link:https://github.com/dalance/procs[Procs] +* link:https://tiswww.case.edu/php/chet/readline/rltop.html[Readline] +* link:https://github.com/BurntSushi/ripgrep[ripgrep] +* link:https://github.com/koalaman/shellcheck[ShellCheck] +* link:https://www.joedog.org/siege-home[Siege] +* link:https://www.tarsnap.com[Tarsnap] +* link:https://www.terraform.io[Terraform] +* link:https://github.com/ggreer/the_silver_searcher[The Silver Surfer] +* link:https://github.com/tmux/tmux/wiki[tmux] +* link:https://github.com/XAMPPRocky/tokei[Tokie] +* link:https://vale.sh[Vale] +* link:https://valkey.io[Valkey] +* link:https://github.com/sachaos/viddy[Viddy] +* link:https://www.vim.org[Vim] +* link:https://github.com/libvips/libvips[Vips] +* link:https://github.com/vi/websocat[Websocat] +* link:https://developers.yubico.com/yubikey-manager[YubiKey Manager CLI] +* link:https://github.com/ajeetdsouza/zoxide[Zoxide] + +=== Homebrew Casks + +Installs the following link:https://brew.sh[casks]: + +* link:https://www.alfredapp.com[Alfred] +* link:https://freemacsoft.net/appcleaner[App Cleaner] +* link:https://www.rogueamoeba.com/audiohijack[Audio Hijack] +* link:https://www.balena.io/etcher[Balena Etcher] +* link:https://bombich.com[Carbon Copy Cloner] +* link:https://getcleanshot.com[CleanShot] +* link:https://discord.com[Discord] +* link:https://software.charliemonroe.net/downie[Downie] +* link:https://www.getdoxie.com[Doxie] +* link:https://www.dropbox.com[Dropbox] +* link:https://www.mozilla.com/en-US/firefox[Firefox] +* link:https://www.rogueamoeba.com/fission[Fission] +* link:https://www.google.com/chrome[Google Chrome] +* link:https://www.noodlesoft.com[Hazel] +* link:https://iina.io[IINA] +* link:http://imageoptim.pornel.net[ImageOptim] +* link:https://bjango.com/mac/istatmenus[iStat Menus] +* link:https://www.iterm2.com[iTerm2] +* link:https://mitmproxy.org[mitmproxy] +* link:https://netnewswire.com[NewNewsWire] +* link:https://ngrok.com[Ngrok] +* link:https://numi.app[Numi] +* link:https://obsidian.md[Obsidian] +* link:https://www.openoffice.org[OpenOffice] +* link:https://orbstack.dev[OrbStack] +* link:https://kagi.com/orion[Orion] +* link:https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project[OWASP Zed Attack Proxy (ZAP)] +* link:https://cocoatech.com[Path Finder] +* link:https://www.pgadmin.org[pgAdmin] +* link:https://superhighfives.com/pika[Pika] +* link:https://getpixelsnap.com[PixelSnap] +* link:https://protonvpn.com[ProtonVPN] +* link:https://paw.cloud[Rapid API] +* link:https://manytricks.com/resolutionator[Resolutionator] +* link:https://flyingmeat.com/retrobatch[Retrobatch] +* link:https://signal.org[Signal] +* link:https://skim-app.sourceforge.io[Skim] +* link:https://www.sonos.com[Sonos] +* link:https://www.sublimetext.com[Sublime Text] +* link:https://panic.com/transmit[Transmit] +* link:https://twist.com[Twist] +* link:https://www.sparklabs.com/viscosity[Viscosity] +* link:https://vivaldi.com[Vivaldi] +* link:https://zed.dev[Zed] + +=== App Store + +Installs link:https://www.apple.com/app-store[App Store] applications as managed by the link:https://github.com/mas-cli/mas[Mac App Store] CLI which assumes you've _purchased_ the applications listed below: + +* link:https://secure.flyingmeat.com/acorn[Acorn] +* link:https://bitwarden.com[Bitwarden] +* link:https://ccmenu.org[CCMenu] +* link:https://sindresorhus.com/system-color-picker[Color Picker] +* link:https://daisydiskapp.com[DaisyDisk] +* link:https://www.apple.com/mac/garageband[GarageBand] +* link:https://handmirror.app[Hand Mirror] +* link:https://www.apple.com/imovie[iMovie] +* link:https://firecore.com/infuse[Infuse] +* link:https://manytricks.com/keycodes[Key Codes] +* link:https://manytricks.com/keymou[Keymou] +* link:https://www.apple.com/keynote[Keynote] +* link:https://apps.apple.com/us/app/amazon-kindle/id302584613[Kindle] +* link:http://limechat.net/mac[LimeChat] +* link:https://marked2app.com[Marked 2] +* link:http://getmedis.com[Medis] +* link:https://trymeeter.com[Meeter] +* link:https://www.markvapps.com/metadatics[Metadatics] +* link:https://mindnode.com[MindNode] +* link:https://manytricks.com/namemangler[Name Mangler] +* link:https://www.apple.com/numbers[Numbers] +* link:https://www.omnigroup.com/omnifocus[OmniFocus] +* link:https://www.apple.com/pages[Pages] +* link:https://krillapps.com/patterns[Patterns] +* link:https://software.charliemonroe.net/permute[Permute] +* link:https://apps.apple.com/app/apple-store/id1494948845[Paletter] +* link:https://apps.apple.com/gb/app/sequence-diagram/id1195426709[Sequence Diagram] +* link:https://shapesapp.com[Shapes] +* link:https://slack.com[Slack] +* link:https://www.adriangranados.com[WiFi Explorer] + +=== Applications + +Installs the following, basic, macOS applications which are not located in the App Store: + +* link:https://appmap.io[AppMap] +* link:https://www.docker.com[Docker] +* link:https://icemenubar.app[Ice] +* link:https://moneywell.app[MoneyWell] +* link:https://manytricks.com/moom[Moom] +* link:https://github.com/theory/pgenv[pgenv] +* link:https://tana.inc[Tana] + +=== Application Extensions + +Installs the following extensions to existing applications: + +* link:https://github.com/tpope/vim-bundler[Vim Bundler] +* link:https://github.com/tpope/vim-commentary[Vim Commentary] +* link:https://github.com/tpope/vim-fugitive[Vim Fugitive] +* link:https://github.com/airblade/vim-gitgutter[Vim Git Gutter] +* link:https://github.com/tpope/vim-pathogen[Vim Pathogen] +* link:https://github.com/tpope/vim-projectionist[Vim Projectionist] +* link:https://github.com/tpope/vim-rails[Vim Rails] +* link:https://github.com/vim-ruby/vim-ruby[Vim Ruby] +* link:https://github.com/AndrewRadev/splitjoin.vim[Vim Splitjoin] +* link:https://github.com/kana/vim-textobj-user[Vim Text Object User] +* link:https://github.com/tpope/vim-unimpaired[Vim Unimpaired] + +=== Node Packages + +Installs the following link:https://nodejs.org[Node] link:https://www.npmjs.com[packages]: + +_None are used at the moment._ + +=== Ruby Gems + +Installs the following link:https://www.ruby-lang.org[Ruby] link:https://rubygems.org[gems]: + +* link:https://github.com/amazing-print/amazing_print[Amazing Print] +* link:https://asciidoctor.org[ASCII Doctor] +* link:https://github.com/evanphx/benchmark-ips[Benchmark IPS] +* link:https://github.com/jmmastey/bundler-stats[Bundler Stats] +* link:https://github.com/mattbrictson/bundleup[BundleUp] +* link:https://alchemists.io/projects/caliber[Caliber] +* link:https://alchemists.io/projects/gemsmith[Gemsmith] +* link:https://alchemists.io/projects/git-lint[Git Lint] +* link:https://hanamirb.org[Hanami] +* link:https://alchemists.io/projects/hanamismith[Hanamismith] +* link:https://alchemists.io/projects/irb-kit[IRB Kit] +* link:https://github.com/jaredbeck/libyear-bundler[Libyear (Bundler)] +* link:https://alchemists.io/projects/pennyworth[Pennyworth] +* link:https://github.com/joonty/pessimize[Pessimize] +* link:https://alchemists.io/projects/pragmater[Pragmater] +* link:https://rubyonrails.org[Ruby on Rails] +* link:https://github.com/troessner/reek[Reek] +* link:https://github.com/AlexB52/retest[Retest] +* link:https://rspec.info[RSpec] +* link:https://alchemists.io/projects/rubysmith[Rubysmith] +* link:https://solargraph.org/guides[Solargraph] +* link:https://alchemists.io/projects/sublime_text_kit[Sublime Text Kit] +* link:https://github.com/ruby-syntax-tree/syntax_tree[Syntax Tree] +* link:https://github.com/red-data-tools/YouPlot[YouPlot] + +=== Rust Crates + +Installs the following link:https://www.rust-lang.org[Rust] link:https://crates.io[crates]: + +* link:https://atuin.sh[Atuin] +* link:https://github.com/nabijaczleweli/cargo-update[Cargo Update] +* link:https://dotenv-linter.github.io[Dotenv Linter] +* link:https://eza.rocks[Eza] +* link:https://jj-vcs.github.io[Jujutsu] +* link:https://jless.io[jless] +* link:https://github.com/chmln/sd[sd] + +== Requirements + +. Apple Silicon hardware. +. link:https://alchemists.io/projects/mac_os[macOS] +. link:https://developer.apple.com/xcode[Xcode] + +== Setup + +To install, run: + +[source,bash] +---- +git clone https://github.com/bkuhlmann/mac_os-config.git +cd mac_os-config +git checkout 30.0.0 +---- + +== Usage + +The following will walk you through the steps of installing/re-installing your machine. + +=== Pre-Install + +Ensure you have the following in place for your Silicon machine: + +. Ensure a backup of your Apple, NAS, backup image, and Dropbox credentials are available. +. Ensure a recent backup of your machine exists and works properly. +. Ensure link:https://support.apple.com/en-us/HT208198[Startup Security Utility] is disabled. +.. Turn off your machine. +.. Start your machine by pressing and holding the `POWER` button until you see startup options being + loaded. +.. Select Utilities → Startup Security Utility from the main menu. +.. Select _Reduced Security_. +.. Quit the utility and restart the machine. + +=== Install + +. Create a link:https://alchemists.io/projects/mac_os/#_boot_disk[macOS Boot Disk] and follow + instructions. +. Ensure latest software updates are applied per + link:https://alchemists.io/projects/mac_os/#_requirements[macOS Requirements]. +. Ensure Xcode is installed per link:https://alchemists.io/projects/mac_os/#_requirements[macOS + Requirements]. +. Run link:https://alchemists.io/projects/mac_os#_usage[macOS Install] and follow all prompts. + +=== Post-Install + +The following are additional steps, not easily automated, that are worth completing after the +install scripts have completed: + +* System Preferences +** Apple ID +*** Login (if not already). +*** Update avatar. +*** Configure iCloud. +*** Enable Find My Mac. +** Bluetooth +*** Reconnect keyboard, mouse, and earbuds. +** Sound +*** Disable _Play sound on startup_. +*** Disable _Play user interface sound effects_. +** Screen Time +*** Disable entire feature. +** General +*** AirDrop & Handoff +**** Disable _AirPlay Receiver_ so you can run link:https://github.com/rack/rack[Rack] applications on Port 5000. +*** AutoFill & Passwords +**** Disable _AutoFill Passwords and Passkeys_. +*** Login Items +**** Ensure only _Alfred_ is listed. +*** Language and Region +**** Set _First day of week_ to _Monday_. +**** Set _Date format_ to _YYYY-MM-DD_. +** Accessibility +*** Hearing +**** Enable _Turn off background sounds when your Mac is not in use._ +** Appearance +*** Use _Light_ theme. +** Apple Intelligence & Siri +*** Disable _Listen for_. +*** Disable _Keyboard shortcut_ (use Alfred instead). +** Privacy & Security +*** Enable _FileVault_. +** Wallpaper +*** Select custom image from _Documents_. +** Spotlight +*** Disable _Help Apple Improve Search_. link:https://obdev.at/blog/what-happens-on-your-device-stays-on-your-device-until-it-doesnt[Details]. +** Screen Saver +*** Use _Message_ with custom text. +** Battery +*** Click _Options_ and enable _Prevent automatic sleeping on power adapter when the +display is off_. +** Lock Screen +*** Set _Start Screen Saver when inactive_ for 5 minutes. +*** Set _Turn display off on battery when inactive_ for 2 minutes. +*** Set _Turn display off on power adapter when inactive_ for 10 minutes. +*** Set _Require password after screen saver begins or display is turned off_ for 5 minutes. +*** Set _Show message when locked_. Example: ` | | `. +** Touch ID & Password +*** Rename fingerprint. +*** Enable use of Touch ID for all settings. +*** Enable use of Apple Watch. +** Users & Groups +*** Update avatar image. +*** Remove unused login items. +*** Disable guest account. +** Internet Accounts +*** Add all accounts. +** Wallet & Apple Pay +*** Reenable all accounts and assign default card. +** Keyboard +*** Set _Key repeat rate_ to max level. +*** Set _Delay until repeat_ to short (max) level. +*** Keyboard Shortcuts +**** Select _Launchpad and Dock_ and uncheck _Turn Dock Hiding On/Off_. +**** Select _Mission Control_ and assign `CONTROL + OPTION + COMMAND + N` to _Show Notification + Center_. +**** Within _Mission Control_, assign `CONTROL + OPTION + COMMAND + W` to _Mission Control_. +**** Select _Screenshots_ and uncheck all boxes. +**** Select _Spotlight_ and uncheck all boxes. +** Mouse +*** Ensure tracking speed is on the 5th setting (i.e. 5th from right of _Slow_). +** Trackpad +*** Ensure tracking speed is on the 5th setting (i.e. 5th from right of _Slow_). +** Printers & Scanners +*** Add printer/scanner. +* iStat Menus +** Double click, within the Applications folder, to install as a system preference. +* Carbon Copy Cloner +** Rename old backup, create new backup, and schedule frequency. +* Notifications +** Set _Show previews when unlocked_. +** Disable _Allow notifications when the display is sleeping_. +** Disable _Allow notifications when the screen is locked_. +** Disable _Allow notifications when mirroring or sharing the display_. +** Disable _Show notifications on lock screen_ and _Play sound for notification_ for all applications. +** Enable _Allow notifications from iPhone_ but disable _Play sounds for notifications from iPhone_ within this option. + +=== Keyboard Shortcuts + +Several applications provide global hotkey support. These are the associations I use (which are also +captured in the `+restore.bom+` as well): + +* *COMMAND (x2)*: Siri (open) +* *COMMAND + SPACE*: Alfred/Spotlight (open) +* link:https://alchemists.io/articles/clean_shot/#_shortcuts[CleanShot] +* *CONTROL + OPTION + COMMAND + c*: Pika (copy color) +* *CONTROL + OPTION + COMMAND + d*: Alfred Define (use OPTION to open Dictionary) +* *CONTROL + OPTION + COMMAND + h*: Alfred Highlight Syntax +* *CONTROL + OPTION + COMMAND + k*: Keymou (cursor highlight show/hide) +* *CONTROL + OPTION + COMMAND + m*: Moom (toggle) +* *CONTROL + OPTION + SHIFT + m*: Moom (open custom actions) +* *CONTROL + OPTION + COMMAND + n*: Notification Center (show/hide) +* *CONTROL + OPTION + COMMAND + o*: Alfred Open URL in default browser +* link:https://alchemists.io/articles/pixel_snap/#_shortcuts[PixelSnap] +* *CONTROL + OPTION + COMMAND + r*: Resolutionator (selector) +* *CONTROL + OPTION + COMMAND + t*: Alfred Large Type +* *CONTROL + OPTION + COMMAND + w*: Mission Control +* *CONTROL + OPTION + COMMAND + ←*: Keymou (move cursor left) +* *CONTROL + OPTION + COMMAND + ↑*: Keymou (move cursor up) +* *CONTROL + OPTION + COMMAND + →*: Keymou (move cursor right) +* *CONTROL + OPTION + COMMAND + ↓*: Keymou (move cursor down) +* *CONTROL + OPTION + COMMAND + ENTER*: Keymou (move cursor by division) +* *CONTROL + OPTION + SPACE*: OmniFocus (quick entry) +* *OPTION + SPACE*: Alfred (open) + +💡 See additional link:https://saurabhs.org/macos-tips[tips] for details. + +=== Newsyslog + +Native to macOS, link:https://www.freebsd.org/cgi/man.cgi?newsyslog.conf(5)[newsyslog] can be used +to configure system-wide log rotation across multiple projects. It’s a good recommendation to set +this up so that disk space is carefully maintained. Here’s how to configure it for your system, +start by creating a configuration for your projects in the `+/etc/newsyslog.d+` directory. In my +case, I use the following configurations: + +* `+/etc/newsyslog.d/alchemists.conf+` ++ +.... + # logfilename [owner:group] mode count size when flags + /Users/bkuhlmann/Dropbox/Development/Work/**/log/*.log 644 2 5120 * GJN +.... +* `+/etc/newsyslog.d/homebrew.conf+` ++ +.... + # logfilename [owner:group] mode count size when flags + /usr/local/var/log/**/*.log 644 2 5120 * GJN +.... + +These configurations ensure that logs are rotated every 5MB (5120KB). In order to test that these +configurations are valid, run: + +.... +sudo newsyslog -nvv +.... + +If you don’t see any errors in the output, then your configuration settings are correct. + +The last thing to do is to add a launch configuration to ensure the log rotations happen at +regularly scheduled intervals. To do this create the following file: +`+$HOME/Library/LaunchAgents/com.apple.newsyslog.plist+`. It should have the following content: + +[source,xml] +---- + + + + + Label + com.apple.newsyslog + ProgramArguments + + /usr/sbin/newsyslog + + LowPriorityIO + + Nice + 1 + StartCalendarInterval + + Minute + 30 + + + +---- + +That’s it. System-wide log rotation is setup for your projects. + +=== Customization + +While this project’s configuration is opinionated and tailored for my setup, you can easily fork +this project and customize it for your environment. Start by editing the files found in the `+bin+` +and `+lib+` directories. Check out the +link:https://alchemists.io/projects/mac_os/#_customization[macOS Customization Documentation] +for further details. + +_TIP_: The installer determines which applications/extensions to install as defined in the +`+settings.sh+` script. Applications defined with the "`APP_NAME`" suffix and extensions defined +with the "`EXTENSION_PATH`" suffix inform the installer what to care about. Removing/commenting out +these applications/extensions within the `+settings.sh+` file will cause the installer to skip these +applications/extensions. + +== Development + +To contribute, run: + +[source,bash] +---- +git clone https://github.com/bkuhlmann/mac_os-config.git +cd mac_os-config +---- + +== link:https://alchemists.io/policies/license[License] + +== link:https://alchemists.io/policies/security[Security] + +== link:https://alchemists.io/policies/code_of_conduct[Code of Conduct] + +== link:https://alchemists.io/policies/contributions[Contributions] + +== link:https://alchemists.io/policies/developer_certificate_of_origin[Developer Certificate of Origin] + +== link:https://alchemists.io/projects/mac_os-config/versions[Versions] + +== link:https://alchemists.io/community[Community] + +== Credits + +Engineered by link:https://alchemists.io/team/brooke_kuhlmann[Brooke Kuhlmann]. diff --git a/mac_os-config/Rakefile b/mac_os-config/Rakefile new file mode 100644 index 0000000..c79775c --- /dev/null +++ b/mac_os-config/Rakefile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "git/lint/rake/register" +require "rubocop/rake_task" + +Git::Lint::Rake::Register.call +RuboCop::RakeTask.new + +desc "Run code quality checks" +task quality: %i[git_lint rubocop] + +task default: :quality diff --git a/mac_os-config/bin/install_app_store b/mac_os-config/bin/install_app_store new file mode 100755 index 0000000..0304889 --- /dev/null +++ b/mac_os-config/bin/install_app_store @@ -0,0 +1,17 @@ +#! /usr/bin/env bash + +# Installs App Store software. + +# Affinity Suite +mas install 1616831348 # Affinity Designer 2 +mas install 1616822987 # Affinity Photo 2 +mas install 1606941598 # Affinity Publisher 2 + +# Productivity +mas install 1352778147 # Bitwarden +mas install 424389933 # Final Cut Pro +mas install 1503970375 # Invoice Ninja +mas install 409203825 # Numbers +mas install 409201541 # Pages +mas install 1503446680 # PastePal +mas install 1579902068 # xSearch diff --git a/mac_os-config/bin/install_applications b/mac_os-config/bin/install_applications new file mode 100755 index 0000000..50f8bad --- /dev/null +++ b/mac_os-config/bin/install_applications @@ -0,0 +1,7 @@ +#! /usr/bin/env bash + +# Installs non-App Store, non-Homebrew applications. +# Add custom app installations here using functions from lib/installers.sh: +# install_dmg_app, install_zip_app, install_bare_pkg, etc. + +# Currently empty - all apps are installed via Homebrew or App Store. diff --git a/mac_os-config/bin/install_basics b/mac_os-config/bin/install_basics new file mode 100755 index 0000000..779ea7e --- /dev/null +++ b/mac_os-config/bin/install_basics @@ -0,0 +1,39 @@ +#! /usr/bin/env bash + +# Installs basic system settings. + +read -p "What is this machine's label (Example: \"Alchemist\")? " mac_os_label +if [[ -z "$mac_os_label" ]]; then + printf "%s\n" "ERROR: Invalid MacOS label." + exit 1 +fi + +read -p "What is this machine's name (Example: \"alchemist\")? " mac_os_name +if [[ -z "$mac_os_name" ]]; then + printf "%s\n" "ERROR: Invalid MacOS name." + exit 1 +fi + +read -p "Delete all files in $HOME/Documents (y/n)? " documents +if [[ "$documents" == "y" ]]; then + rm -rf "$HOME/Documents/*" + printf "%s\n" "Documents deleted." +fi + +read -p "Delete all files in $HOME/Downloads (y/n)? " downloads +if [[ "$downloads" == "y" ]]; then + rm -rf "$HOME/Downloads/*" + printf "%s\n" "Downloads deleted." +fi + +read -p "Change /usr/local ownership to $USER:staff (y/n)? " ownership +if [[ "$ownership" == "y" ]]; then + sudo chown -R "$USER":staff /usr/local + printf "%s\n" "Ownership changed." +fi + +printf "Setting system label and name...\n" +sudo scutil --set ComputerName "$mac_os_label" +sudo scutil --set HostName "$mac_os_name" +sudo scutil --set LocalHostName "$mac_os_name" +sudo defaults write /Library/Preferences/SystemConfiguration/com.apple.smb.server NetBIOSName -string $mac_os_name diff --git a/mac_os-config/bin/install_defaults b/mac_os-config/bin/install_defaults new file mode 100755 index 0000000..b0d1658 --- /dev/null +++ b/mac_os-config/bin/install_defaults @@ -0,0 +1,566 @@ +#! /usr/bin/env bash + +# Installs system and application default settings. + +printf "%s\n" "System - Disable boot sound effects." +sudo nvram SystemAudioVolume=" " + +printf "%s\n" "System - Reveal IP address, hostname, OS version, etc. when clicking login window clock." +sudo defaults write /Library/Preferences/com.apple.loginwindow AdminHostInfo HostName + +printf "%s\n" "System - Disable launching of previously open applications upon reboot." +defaults write -g ApplePersistence -bool no + +printf "%s\n" "System - Disable automatic termination of inactive apps." +defaults write NSGlobalDomain NSDisableAutomaticTermination -bool true + +printf "%s\n" "System - Expand save panel by default." +defaults write NSGlobalDomain NSNavPanelExpandedStateForSaveMode -bool true + +printf "%s\n" "System - Disable 'Are you sure you want to open this application?' dialog." +defaults write com.apple.LaunchServices LSQuarantine -bool false + +printf "%s\n" "System - Increase window resize speed for Cocoa applications." +defaults write NSGlobalDomain NSWindowResizeTime -float 0.001 + +printf "%s\n" "System - Disable window resume system-wide." +defaults write NSGlobalDomain NSQuitAlwaysKeepsWindows -bool false + +printf "%s\n" "System - Disable auto-correct." +defaults write NSGlobalDomain NSAutomaticSpellingCorrectionEnabled -bool false + +printf "%s\n" "System - Disable smart quotes (not useful when writing code)." +defaults write NSGlobalDomain NSAutomaticQuoteSubstitutionEnabled -bool false + +printf "%s\n" "System - Disable smart dashes (not useful when writing code)." +defaults write NSGlobalDomain NSAutomaticDashSubstitutionEnabled -bool false + +printf "%s\n" "System - Require password immediately after sleep or screen saver begins." +defaults write com.apple.screensaver askForPassword -int 1 +defaults write com.apple.screensaver askForPasswordDelay -int 0 + +printf "%s\n" "System - Avoid creating .DS_Store files on network volumes." +defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true + +printf "%s\n" "System - Automatically restart if system freezes." +systemsetup -setrestartfreeze on + +printf "%s\n" "System - Disable software updates." +sudo softwareupdate --schedule off + +printf "%s\n" "Keyboard - Automatically illuminate built-in MacBook keyboard in low light." +defaults write com.apple.BezelServices kDim -bool true + +printf "%s\n" "Keyboard - Turn off keyboard illumination when computer is not used for 5 minutes." +defaults write com.apple.BezelServices kDimTime -int 300 + +printf "%s\n" "Keyboard - Enable keyboard access for all controls." +defaults write NSGlobalDomain AppleKeyboardUIMode -int 3 + +printf "%s\n" "Keyboard - Set a fast keyboard repeat rate." +defaults write NSGlobalDomain KeyRepeat -int 0 + +printf "%s\n" "Keyboard - Disable press-and-hold for keys in favor of key repeat." +defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false + +printf "%s\n" "Trackpad - Map bottom right corner to right-click." +defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad TrackpadCornerSecondaryClick -int 2 +defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad TrackpadRightClick -bool true +defaults -currentHost write NSGlobalDomain com.apple.trackpad.trackpadCornerClickBehavior -int 1 +defaults -currentHost write NSGlobalDomain com.apple.trackpad.enableSecondaryClick -bool true + +printf "%s\n" "Trackpad - Enable tap to click for current user and the login screen." +defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true +defaults -currentHost write NSGlobalDomain com.apple.mouse.tapBehavior -int 1 +defaults write NSGlobalDomain com.apple.mouse.tapBehavior -int 1 + +printf "%s\n" "Trackpad - Use CONTROL (^) with scroll to zoom." +defaults write com.apple.universalaccess closeViewScrollWheelToggle -bool true +defaults write com.apple.universalaccess HIDScrollZoomModifierMask -int 262144 + +printf "%s\n" "Trackpad - Follow keyboard focus while zoomed in." +defaults write com.apple.universalaccess closeViewZoomFollowsFocus -bool true + +printf "%s\n" "Bluetooth - Increase sound quality for headphones/headsets." +defaults write com.apple.BluetoothAudioAgent "Apple Bitpool Min (editable)" -int 40 + +printf "%s\n" "Menu Bar - Show only Bluetooth and Airport." +for domain in $HOME/Library/Preferences/ByHost/com.apple.systemuiserver.*; do + defaults write "${domain}" dontAutoLoad -array "/System/Library/CoreServices/Menu Extras/TimeMachine.menu" +done + +defaults write com.apple.systemuiserver menuExtras -array \ + "/System/Library/CoreServices/Menu Extras/Bluetooth.menu" \ + "/System/Library/CoreServices/Menu Extras/AirPort.menu" + +printf "%s\n" "Dock - Remove all default app icons." +defaults write com.apple.dock persistent-apps -array + +printf "%s\n" "Dock - Automatically hide and show." +defaults write com.apple.dock autohide -bool true + +printf "%s\n" "Dock - Remove the auto-hiding delay." +defaults write com.apple.Dock autohide-delay -float 0 + +printf "%s\n" "Dock - Don’t show Dashboard as a Space." +defaults write com.apple.dock "dashboard-in-overlay" -bool true + +printf "%s\n" "iCloud - Save to disk by default." +defaults write NSGlobalDomain NSDocumentSaveNewDocumentsToCloud -bool false + +printf "%s\n" "Finder - Show the $HOME/Library folder." +chflags nohidden $HOME/Library + +printf "%s\n" "Finder - Show hidden files." +defaults write com.apple.finder AppleShowAllFiles -bool true + +printf "%s\n" "Finder - Show filename extensions." +defaults write NSGlobalDomain AppleShowAllExtensions -bool true + +printf "%s\n" "Finder - Disable the warning when changing a file extension." +defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false + +printf "%s\n" "Finder - Show path bar." +defaults write com.apple.finder ShowPathbar -bool true + +printf "%s\n" "Finder - Show status bar." +defaults write com.apple.finder ShowStatusBar -bool true + +printf "%s\n" "Finder - Display full POSIX path as window title." +defaults write com.apple.finder _FXShowPosixPathInTitle -bool true + +printf "%s\n" "Finder - Use list view in all Finder windows." +defaults write com.apple.finder FXPreferredViewStyle -string "Nlsv" + +printf "%s\n" "Finder - Allow quitting via COMMAND+Q -- Doing so will also hide desktop icons." +defaults write com.apple.finder QuitMenuItem -bool true + +printf "%s\n" "Finder - Disable the warning before emptying the Trash." +defaults write com.apple.finder WarnOnEmptyTrash -bool false + +printf "%s\n" "Finder - Allow text selection in Quick Look." +defaults write com.apple.finder QLEnableTextSelection -bool true + +printf "%s\n" "iOS Simulator - Symlink the iOS Simulator application." +sudo ln -sf "/Applications/Xcode.app/Contents/Applications/iPhone Simulator.app" "/Applications/iOS Simulator.app" + +printf "%s\n" "Safari - Set home page to 'about:blank' for faster loading." +defaults write com.apple.Safari HomePage -string "about:blank" + +printf "%s\n" "Safari - Hide bookmarks bar." +defaults write com.apple.Safari ShowFavoritesBar -bool false + +printf "%s\n" "Safari - Use Contains instead of Starts With in search banners." +defaults write com.apple.Safari FindOnPageMatchesWordStartsOnly -bool false + +printf "%s\n" "Safari - Enable debug menu." +defaults write com.apple.Safari IncludeInternalDebugMenu -bool true + +printf "%s\n" "Safari - Enable the Develop menu and the Web Inspector." +defaults write com.apple.Safari IncludeDevelopMenu -bool true +defaults write com.apple.Safari WebKitDeveloperExtrasEnabledPreferenceKey -bool true +defaults write com.apple.Safari com.apple.Safari.ContentPageGroupIdentifier.WebKit2DeveloperExtrasEnabled -bool true + +printf "%s\n" "Safari - Add a context menu item for showing the Web Inspector in web views." +defaults write NSGlobalDomain WebKitDeveloperExtras -bool true + +printf "%s\n" "Safari - Disable sending search queries to Apple.." +defaults write com.apple.Safari UniversalSearchEnabled -bool false + +printf "%s\n" "Chrome - Prevent native print dialog, use system dialog instead." +defaults write com.google.Chrome DisablePrintPreview -boolean true + +printf "%s\n" "Mail - Copy email addresses as 'foo@example.com' instead of 'Foo Bar '." +defaults write com.apple.mail AddressesIncludeNameOnPasteboard -bool false + +printf "%s\n" "Mail - Disable send animation." +defaults write com.apple.mail DisableSendAnimations -bool true + +printf "%s\n" "Mail - Disable reply animation." +defaults write com.apple.mail DisableReplyAnimations -bool true + +printf "%s\n" "Mail - Enable COMMAND+ENTER to send mail." +defaults write com.apple.mail NSUserKeyEquivalents -dict-add "Send" -string "@\\U21a9" + +printf "%s\n" "Address Book - Enable debug menu." +defaults write com.apple.addressbook ABShowDebugMenu -bool true + +printf "%s\n" "iCal - Enable debug menu." +defaults write com.apple.iCal IncludeDebugMenu -bool true + +printf "%s\n" "TextEdit - Use plain text mode for new documents." +defaults write com.apple.TextEdit RichText -int 0 + +printf "%s\n" "TextEdit - Open and save files as UTF-8 encoding." +defaults write com.apple.TextEdit PlainTextEncoding -int 4 +defaults write com.apple.TextEdit PlainTextEncodingForWrite -int 4 + +printf "%s\n" "Disk Utility - Enable debug menu." +defaults write com.apple.DiskUtility DUDebugMenuEnabled -bool true +defaults write com.apple.DiskUtility advanced-image-options -bool true + +printf "%s\n" "Time Machine - Prevent prompting to use new hard drives as backup volume." +defaults write com.apple.TimeMachine DoNotOfferNewDisksForBackup -bool true + +printf "%s\n" "Printer - Expand print panel by default." +defaults write NSGlobalDomain PMPrintingExpandedStateForPrint -bool true + +printf "%s\n" "Printer - Automatically quit printer app once the print jobs complete." +defaults write com.apple.print.PrintingPrefs "Quit When Finished" -bool true + +printf "%s\n" "Game Center - Disable Game Center." +defaults write com.apple.gamed Disabled -bool true + +printf "%s\n" "App Store - Enable the WebKit Developer Tools in the Mac App Store." +defaults write com.apple.appstore WebKitDeveloperExtras -bool true + +printf "%s\n" "App Store - Enable Debug Menu in the Mac App Store." +defaults write com.apple.appstore ShowDebugMenu -bool true + +printf "CCMenu - Add open source projects\n" +defaults write net.sourceforge.cruisecontrol.CCMenu ' +{ + NSNavLastRootDirectory = "~/Downloads"; + NSNavPanelExpandedSizeForSaveMode = "{800, 448}"; + "NSStatusItem Preferred Position Item-0" = 628; + "PlaySound Broken" = 0; + "PlaySound Fixed" = 0; + "PlaySound StillFailing" = 0; + "PlaySound Successful" = 0; + ProjectOrder = 1; + "SendNotification Broken" = 1; + "SendNotification Fixed" = 1; + "SendNotification StillFailing" = 1; + "SendNotification Successful" = 1; + ShowPipelineStatusInWindow = 1; + "Sound Broken" = Sosumi; + "Sound Fixed" = Sosumi; + "Sound StillFailing" = Sosumi; + "Sound Successful" = Sosumi; + pipelines = ( + { + feedName = "bkuhlmann/auther"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/auther.cc.xml"; + name = Auther; + }, + { + feedName = "bkuhlmann/bashsmith"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/bashsmith.cc.xml"; + name = Bashsmith; + }, + { + feedName = "bkuhlmann/benchmarks"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/benchmarks.cc.xml"; + name = Benchmarks; + }, + { + feedName = "bkuhlmann/caliber"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/caliber.cc.xml"; + name = Caliber; + }, + { + feedName = "bkuhlmann/cogger"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/cogger.cc.xml"; + name = Cogger; + }, + { + feedName = "bkuhlmann/containable"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/containable.cc.xml"; + name = Containable; + }, + { + feedName = "bkuhlmann/core"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/core.cc.xml"; + name = Core; + }, + { + feedName = "bkuhlmann/docker-alpine-base"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/docker-alpine-base.cc.xml"; + name = "Docker Alpine Base"; + }, + { + feedName = "bkuhlmann/docker-alpine-ruby"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/docker-alpine-ruby.cc.xml"; + name = "Docker Alpine Ruby"; + }, + { + feedName = "bkuhlmann/dotfiles"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/dotfiles.cc.xml"; + name = Dotfiles; + }, + { + feedName = "bkuhlmann/etcher"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/etcher.cc.xml"; + name = Etcher; + }, + { + feedName = "bkuhlmann/functionable"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/functionable.cc.xml"; + name = "Functionable"; + }, + { + feedName = "bkuhlmann/gemsmith"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/gemsmith.cc.xml"; + name = Gemsmith; + }, + { + feedName = "bkuhlmann/ghub"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/ghub.cc.xml"; + name = Ghub; + }, + { + feedName = "bkuhlmann/git-lint"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/git-lint.cc.xml"; + name = "Git Lint"; + }, + { + feedName = "bkuhlmann/gitt"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/gitt.cc.xml"; + name = Gitt; + }, + { + feedName = "bkuhlmann/hanamismith"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/hanamismith.cc.xml"; + name = Hanamismith; + }, + { + feedName = "bkuhlmann/htmx"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/htmx.cc.xml"; + name = HTMX; + }, + { + feedName = "bkuhlmann/htmx-remove"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/htmx-remove.cc.xml"; + name = "htmx Remove"; + }, + { + feedName = "bkuhlmann/htmx-select"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/htmx-select.cc.xml"; + name = "htmx Select"; + }, + { + feedName = "bkuhlmann/hemo"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/hemo.cc.xml"; + name = Hemo; + }, + { + feedName = "bkuhlmann/http-fake"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/http-fake.cc.xml"; + name = "HTTP Fake"; + }, + { + feedName = "bkuhlmann/infusible"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/infusible.cc.xml"; + name = Infusible; + }, + { + feedName = "bkuhlmann/initable"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/initable.cc.xml"; + name = Initable; + }, + { + feedName = "bkuhlmann/inspectable"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/inspectable.cc.xml"; + name = Inspectable; + }, + { + feedName = "bkuhlmann/irb-kit"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/irb-kit.cc.xml"; + name = "IRB Kit"; + }, + { + feedName = "bkuhlmann/kagi-api"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/kagi-api.cc.xml"; + name = "Kagi API"; + }, + { + feedName = "bkuhlmann/lode"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/lode.cc.xml"; + name = Lode; + }, + { + feedName = "bkuhlmann/mac_os"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/mac_os.cc.xml"; + name = macOS; + }, + { + feedName = "bkuhlmann/mac_os-config"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/mac_os-config.cc.xml"; + name = "macOS Configuration"; + }, + { + feedName = "bkuhlmann/marameters"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/marameters.cc.xml"; + name = Marameters; + }, + { + feedName = "bkuhlmann/milestoner"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/milestoner.cc.xml"; + name = Milestoner; + }, + { + feedName = "bkuhlmann/navigator"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/navigator.cc.xml"; + name = Navigator; + }, + { + feedName = "bkuhlmann/pennyworth"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/pennyworth.cc.xml"; + name = Pennyworth; + }, + { + feedName = "bkuhlmann/petail"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/petail.cc.xml"; + name = Petail; + }, + { + feedName = "bkuhlmann/pipeable"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/pipeable.cc.xml"; + name = Pipeable; + }, + { + feedName = "bkuhlmann/pkce"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/pkce.cc.xml"; + name = PKCE; + }, + { + feedName = "bkuhlmann/pragmater"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/pragmater.cc.xml"; + name = Pragmater; + }, + { + feedName = "bkuhlmann/prawn_plus"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/prawn_plus.cc.xml"; + name = "Prawn+"; + }, + { + feedName = "bkuhlmann/refinements"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/refinements.cc.xml"; + name = Refinements; + }, + { + feedName = "bkuhlmann/rubysmith"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/rubysmith.cc.xml"; + name = Rubysmith; + }, + { + feedName = "bkuhlmann/runcom"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/runcom.cc.xml"; + name = Runcom; + }, + { + feedName = "bkuhlmann/sod"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/sod.cc.xml"; + name = Sod; + }, + { + feedName = "bkuhlmann/spek"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/spek.cc.xml"; + name = Spek; + }, + { + feedName = "bkuhlmann/sublime_text_kit"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/sublime_text_kit.cc.xml"; + name = "Sublime Text Kit"; + }, + { + feedName = "bkuhlmann/sublime_text_setup"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/sublime_text_setup.cc.xml"; + name = "Sublime Text Setup"; + }, + { + feedName = "bkuhlmann/tana"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/tana.cc.xml"; + name = Tana; + }, + { + feedName = "bkuhlmann/tocer"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/tocer.cc.xml"; + name = Tocer; + }, + { + feedName = "bkuhlmann/tone"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/tone.cc.xml"; + name = Tone; + }, + { + feedName = "usetrmnl/trmnl-api"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/usetrmnl/trmnl-api.cc.xml"; + name = "TRMNL API"; + }, + { + feedName = "usetrmnl/byos_hanami"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/usetrmnl/byos_hanami.cc.xml"; + name = "TRMNL Terminus"; + }, + { + feedName = "bkuhlmann/versionaire"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/versionaire.cc.xml"; + name = Versionaire; + }, + { + feedName = "bkuhlmann/wholeable"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/wholeable.cc.xml"; + name = Wholeable; + }, + { + feedName = "bkuhlmann/xdg"; + feedType = cctray; + feedUrl = "https://circleci.com/gh/bkuhlmann/xdg.cc.xml"; + name = XDG; + } + ); +}' diff --git a/mac_os-config/bin/install_extensions b/mac_os-config/bin/install_extensions new file mode 100755 index 0000000..7985ef8 --- /dev/null +++ b/mac_os-config/bin/install_extensions @@ -0,0 +1,6 @@ +#! /usr/bin/env bash + +# Installs application extensions (editor plugins, etc.) +# Add extensions here using install_git_app or install_file from lib/installers.sh + +# Currently empty - add extensions as needed. diff --git a/mac_os-config/bin/install_homebrew_casks b/mac_os-config/bin/install_homebrew_casks new file mode 100755 index 0000000..2fd8648 --- /dev/null +++ b/mac_os-config/bin/install_homebrew_casks @@ -0,0 +1,21 @@ +#! /usr/bin/env bash + +# Installs Homebrew Casks (GUI applications). + +# Browsers +brew install --cask eloston-chromium + +# Communication +brew install --cask deepl +brew install --cask element +brew install --cask signal + +# Cloud & Sync +brew install --cask nextcloud +brew install --cask proton-drive +brew install --cask proton-mail +brew install --cask protonvpn + +# Development +brew install --cask nova +brew install --cask transmit diff --git a/mac_os-config/bin/install_homebrew_formulas b/mac_os-config/bin/install_homebrew_formulas new file mode 100755 index 0000000..45c6ffa --- /dev/null +++ b/mac_os-config/bin/install_homebrew_formulas @@ -0,0 +1,20 @@ +#! /usr/bin/env bash + +# Installs Homebrew formulas (CLI tools). + +# Shell & Terminal +brew install atuin +brew install bash +brew install bash-completion + +# Development +brew install node + +# Media +brew install ffmpeg + +# Utilities +brew install mas +brew install mole +brew install rename +brew install ykman diff --git a/mac_os-config/bin/install_node_packages b/mac_os-config/bin/install_node_packages new file mode 100755 index 0000000..2970b85 --- /dev/null +++ b/mac_os-config/bin/install_node_packages @@ -0,0 +1,6 @@ +#! /usr/bin/env bash + +# Installs global Node.js packages. +# Add packages here: npm install -g + +# Currently empty - add packages as needed. diff --git a/mac_os-config/bin/install_ruby_gems b/mac_os-config/bin/install_ruby_gems new file mode 100755 index 0000000..21e71e1 --- /dev/null +++ b/mac_os-config/bin/install_ruby_gems @@ -0,0 +1,6 @@ +#! /usr/bin/env bash + +# Installs Ruby gems. +# Add gems here: gem install + +# Currently empty - add gems as needed. diff --git a/mac_os-config/bin/install_rust_crates b/mac_os-config/bin/install_rust_crates new file mode 100755 index 0000000..6ea6046 --- /dev/null +++ b/mac_os-config/bin/install_rust_crates @@ -0,0 +1,6 @@ +#! /usr/bin/env bash + +# Installs Rust crates. +# Add crates here: cargo install + +# Currently empty - add crates as needed. diff --git a/mac_os-config/bin/install_shell b/mac_os-config/bin/install_shell new file mode 100755 index 0000000..752c7ba --- /dev/null +++ b/mac_os-config/bin/install_shell @@ -0,0 +1,11 @@ +#! /usr/bin/env bash + +# Installs shell. + +if [[ -z $(cat /etc/shells | grep "$(get_homebrew_bin_root)/bash") ]]; then + sudo bash -c "printf '$(get_homebrew_bin_root)/bash\n' >> /etc/shells" + chsh -s "$(get_homebrew_bin_root)/bash" + rm -f "$HOME/.bash_profile" +else + printf "%s\n" "Shell is installed." +fi diff --git a/mac_os-config/bin/rake b/mac_os-config/bin/rake new file mode 100755 index 0000000..9bef742 --- /dev/null +++ b/mac_os-config/bin/rake @@ -0,0 +1,6 @@ +#! /usr/bin/env ruby +# frozen_string_literal: true + +require "bundler/setup" + +load Gem.bin_path "rake", "rake" diff --git a/mac_os-config/bin/restore_backup b/mac_os-config/bin/restore_backup new file mode 100755 index 0000000..b07b5b9 --- /dev/null +++ b/mac_os-config/bin/restore_backup @@ -0,0 +1,48 @@ +#! /usr/bin/env bash + +# Performs restoration of backups. + +read -p "Enter the backup volume path (Example: \"/Volumes/alchemist\")? " mac_os_backup_root + +if [[ ! -e "$mac_os_backup_root" ]]; then + printf "%s\n" "ERROR: Backup volume cannot be found: $mac_os_backup_root." + exit 1 +fi + +mkdir -p log + +rsync \ + --rsync-path="sudo rsync" \ + --perms \ + --recursive \ + --compress \ + --numeric-ids \ + --links \ + --hard-links \ + --files-from="$MAC_OS_CONFIG_PATH/lib/restore.bom" \ + --log-file="log/restore.log" \ + --human-readable \ + --verbose \ + "$mac_os_backup_root/" / + +# Newsyslog +sudo cp -p "$mac_os_backup_root/etc/newsyslog.d/alchemists.conf" "/etc/newsyslog.d" +sudo cp -p "$mac_os_backup_root/etc/newsyslog.d/homebrew.conf" "/etc/newsyslog.d" + +# Dotfiles +( + cd "$HOME/Engineering/OSS/dotfiles" + bin/run l +) + +# Sublime Text +( + cd "$HOME/Engineering/OSS/sublime_text_setup" + bin/run l +) + +# Duti +duti "$HOME/.config/duti/configuration.duti" + +# Reboot +printf "%s\n" "Please reboot machine and then finish by installing all libraries." diff --git a/mac_os-config/lib/restore.bom b/mac_os-config/lib/restore.bom new file mode 100644 index 0000000..a195952 --- /dev/null +++ b/mac_os-config/lib/restore.bom @@ -0,0 +1,232 @@ +# APPLICATIONS + +# Acorn +/Users/bkuhlmann/Library/Preferences/com.flyingmeat.Acorn5.LSSharedFileList.plist + +# Alfred +/Users/bkuhlmann/Library/Preferences/com.runningwithcrayons.Alfred-Preferences.plist +/Users/bkuhlmann/Library/Preferences/com.runningwithcrayons.Alfred.plist + +# App Cleaner +/Users/bkuhlmann/Library/Preferences/com.freemacsoft.AppCleaner.plist +/Users/bkuhlmann/Library/Preferences/net.freemacsoft.AppCleaner.plist + +# Apple AirPlay +/Users/bkuhlmann/Library/Preferences/com.apple.airplay.plist + +# Apple App Store +/Users/bkuhlmann/Library/Preferences/com.apple.appstore.commerce.plist +/Users/bkuhlmann/Library/Preferences/com.apple.appstore.plist + +# Apple Clock +/Users/bkuhlmann/Library/Preferences/com.apple.menuextra.clock.plist + +# Apple Contacts +/Users/bkuhlmann/Library/Preferences/com.apple.AddressBook.plist + +# Apple Control Center +/Users/bkuhlmann/Library/Preferences/com.apple.controlcenter.plist + +# Apple Dock +/Users/bkuhlmann/Library/Preferences/com.apple.dock.plist + +# Apple FaceTime +/Users/bkuhlmann/Library/Preferences/com.apple.facetime.bag.plist +/Users/bkuhlmann/Library/Preferences/com.apple.FaceTime.plist +/Users/bkuhlmann/Library/Preferences/com.apple.imservice.ids.FaceTime.plist + +# Apple Finder +/Users/bkuhlmann/Library/Preferences/com.apple.finder.plist + +# Apple Image Capture +/Users/bkuhlmann/Library/Preferences/com.apple.ImageCaptureExtension2.plist +/Users/bkuhlmann/Library/Preferences/com.apple.Image_Capture.plist + +# Apple iTunes +/Users/bkuhlmann/Library/Preferences/com.apple.iTunes.eq.plist +/Users/bkuhlmann/Library/Preferences/com.apple.iTunes.Gracenote.plist +/Users/bkuhlmann/Library/Preferences/com.apple.iTunes.Gracenote.plist +/Users/bkuhlmann/Library/Preferences/com.apple.iTunes.plist +/Users/bkuhlmann/Library/Preferences/com.apple.iTunesHelper.plist + +# Apple Mouse and Trackpad +/Users/bkuhlmann/Library/Preferences/com.apple.driver.AppleBluetoothMultitouch.mouse.plist +/Users/bkuhlmann/Library/Preferences/com.apple.driver.AppleBluetoothMultitouch.trackpad.plist +/Users/bkuhlmann/Library/Preferences/com.apple.driver.AppleHIDMouse.plist + +# Apple Siri +/Users/bkuhlmann/Library/Preferences/com.apple.Siri.plist + +# Apple System Preferences +/Users/bkuhlmann/Library/Preferences/com.apple.systempreferences.plist + +# Audio Hijack +/Users/bkuhlmann/Library/Preferences/com.rogueamoeba.audiohijack3.plist + +# Balena Etcher +/Users/bkuhlmann/Library/Preferences/io.balena.etcher.plist + +# Carbon Copy Cloner +/Users/bkuhlmann/Library/Preferences/com.bombich.ccc.plist + +# CleanShot +/Users/bkuhlmann/Library/Preferences/pl.maketheweb.cleanshotx.plist + +# Discord +/Users/bkuhlmann/Library/Preferences/com.hnc.Discord.plist + +# Docker +/Users/bkuhlmann/Library/Preferences/com.docker.docker.plist + +# Doxie +/Users/bkuhlmann/Library/Preferences/com.getdoxie.doxie.plist + +# Firefox +/Users/bkuhlmann/Library/Preferences/org.mozilla.firefox.plist + +# Fission +/Users/bkuhlmann/Library/Preferences/com.rogueamoeba.Fission.plist + +# Google Chrome +/Users/bkuhlmann/Library/Preferences/com.google.Chrome.plist + +# Hazel +/Users/bkuhlmann/Library/Application Support/Hazel +/Users/bkuhlmann/Library/Preferences/com.noodlesoft.Hazel.plist +/Users/bkuhlmann/Library/Preferences/com.noodlesoft.HazelHelper.plist + +# Ice +/Users/bkuhlmann/Library/Preferences/com.jordanbaird.Ice.plist + +# IINA +/Users/bkuhlmann/Library/Preferences/com.colliderli.iina.plist + +# ImageOptim +/Users/bkuhlmann/Library/Preferences/net.pornel.ImageOptim.plist + +# iStat Menus +/Users/bkuhlmann/Library/Preferences/com.bjango.istatmenus.plist +/Users/bkuhlmann/Library/Preferences/com.bjango.istatmenus6.extras.plist + +# iTerm +/Users/bkuhlmann/Library/Preferences/com.googlecode.iterm2.plist + +# Kagi +/Users/bkuhlmann/Library/Preferences/com.kagi.kagimacOS.plist + +# Keymou +/Users/bkuhlmann/Library/Preferences/com.manytricks.Keymo.plist + +# LimeChat +/Users/bkuhlmann/Library/Preferences/net.limechat.LimeChat-AppStore.plist + +# Max +/Users/bkuhlmann/Library/Preferences/org.sbooth.Max.LSSharedFileList.plist +/Users/bkuhlmann/Library/Preferences/org.sbooth.Max.plist + +# MoneyWell +/Users/bkuhlmann/Library/Preferences/com.nothirst.moneywell.mac.LSSharedFileList.plist + +# Moom +/Users/bkuhlmann/Library/Preferences/com.manytricks.Moom.plist + +# NetNewsWire +/Users/bkuhlmann/Library/Preferences/com.ranchero.NetNewsWire-Evergreen.plist + +# Numi +/Users/bkuhlmann/Library/Preferences/com.dmitrynikolaev.numi.plist + +# Obsidian +/Users/bkuhlmann/Library/Preferences/md.obsidian.plist + +# Path Finder +/Users/bkuhlmann/Library/Application Support/Path Finder +/Users/bkuhlmann/Library/Preferences/com.cocoatech.PathFinder.LSSharedFileList.plist +/Users/bkuhlmann/Library/Preferences/com.cocoatech.PathFinder.plist + +# pgAdmin +/Users/bkuhlmann/Library/Preferences/org.pgadmin.pgAdmin 4.plist + +# Pika +/Users/bkuhlmann/Library/Preferences/com.superhighfives.Pika.plist + +# PixelSnap +/Users/bkuhlmann/Library/Preferences/pl.maketheweb.pixelsnap2.plist + +# Retrobatch +/Users/bkuhlmann/Library/Preferences/com.flyingmeat.Retrobatch.plist + +# Resolutionator +/Users/bkuhlmann/Library/Preferences/com.manytricks.Resolutionator.plist + +# Skim +/Users/bkuhlmann/Library/Prererences/net.sourceforge.skim-aoo.skim.bookmarks.plist +/Users/bkuhlmann/Library/Preferences/net.sourceforge.skim-app.skim.plist + +# Signal +/Users/bkuhlmann/Library/Preferences/org.whispersystems.signal-desktop.helper.plist +/Users/bkuhlmann/Library/Preferences/org.whispersystems.signal-desktop.plist + +# Sonos +/Users/bkuhlmann/Library/Preferences/com.sonos.macController.plist + +# Tana +/Users/bkuhlmann/Library/Preferences/inc.tana.desktop.plist + +# Transmit +/Users/bkuhlmann/Library/Preferences/com.panic.Transmit.plist + +# Viscosity +/Users/bkuhlmann/Library/Preferences/com.viscosityvpn.Viscosity.plist + +# Vivaldi +/Users/bkuhlmann/Library/Preferences/com.vivaldi.Vivaldi.plist + +# SYSTEM + +# newsyslog +/Users/bkuhlmann/Library/LaunchAgents/com.apple.newsyslog.plist + +# USER +/Users/bkuhlmann/.asciinema +/Users/bkuhlmann/.cache/milestoner +/Users/bkuhlmann/.cache/psql +/Users/bkuhlmann/.cache/ruby +/Users/bkuhlmann/.cache/zoxide +/Users/bkuhlmann/.config/asciinema +/Users/bkuhlmann/.config/atuin +/Users/bkuhlmann/.config/bash +/Users/bkuhlmann/.config/bundler +/Users/bkuhlmann/.config/duti +/Users/bkuhlmann/.config/gemsmith +/Users/bkuhlmann/.config/git +/Users/bkuhlmann/.config/hanamismith +/Users/bkuhlmann/.config/htop +/Users/bkuhlmann/.config/iterm2 +/Users/bkuhlmann/.config/lnav +/Users/bkuhlmann/.config/milestoner +/Users/bkuhlmann/.config/pennyworth +/Users/bkuhlmann/.config/pgcli +/Users/bkuhlmann/.config/pragmater +/Users/bkuhlmann/.config/rubysmith +/Users/bkuhlmann/.config/sublime_text_kit +/Users/bkuhlmann/.gem/credentials +/Users/bkuhlmann/.gem/gem-private_key.pem +/Users/bkuhlmann/.gem/gem-public_cert.pem +/Users/bkuhlmann/.gnupg +/Users/bkuhlmann/.local/share/atuin/history.db +/Users/bkuhlmann/.minisign +/Users/bkuhlmann/.netrc +/Users/bkuhlmann/.parallel +/Users/bkuhlmann/.pgadmin +/Users/bkuhlmann/.siege +/Users/bkuhlmann/.ssh +/Users/bkuhlmann/.vscode +/Users/bkuhlmann/Apps +/Users/bkuhlmann/Desktop +/Users/bkuhlmann/Documents +/Users/bkuhlmann/Downloads +/Users/bkuhlmann/Dropbox +/Users/bkuhlmann/Engineering +/Users/bkuhlmann/Scratch +/Users/bkuhlmann/Watch diff --git a/mac_os-config/lib/settings.sh b/mac_os-config/lib/settings.sh new file mode 100644 index 0000000..ba2a1ed --- /dev/null +++ b/mac_os-config/lib/settings.sh @@ -0,0 +1,19 @@ +#! /usr/bin/env bash + +# Defines global settings. + +# General +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +# Homebrew +export HOMEBREW_CURL_RETRIES=3 + +# Custom Applications +# Add app URLs and names here for apps not available via Homebrew or App Store. +# Example: +# export MY_APP_NAME="MyApp.app" +# export MY_APP_URL="https://example.com/download/myapp.dmg" +# export MY_APP_VOLUME_NAME="MyApp" diff --git a/mac_os/.circleci/config.yml b/mac_os/.circleci/config.yml new file mode 100644 index 0000000..373760d --- /dev/null +++ b/mac_os/.circleci/config.yml @@ -0,0 +1,31 @@ +version: 2.1 +jobs: + build: + working_directory: ~/project + docker: + - image: bkuhlmann/alpine-ruby:latest + steps: + - checkout + + - restore_cache: + name: Gems Restore + keys: + - gem-cache-{{.Branch}}-{{checksum "Gemfile"}} + - gem-cache- + + - run: + name: Gems Install + command: | + gem update --system + bundle config set path "vendor/bundle" + bundle install + + - save_cache: + name: Gems Store + key: gem-cache-{{.Branch}}-{{checksum "Gemfile"}} + paths: + - vendor/bundle + + - run: + name: Rake + command: bundle exec rake diff --git a/mac_os/.config/rubocop/config.yml b/mac_os/.config/rubocop/config.yml new file mode 100644 index 0000000..bb2750e --- /dev/null +++ b/mac_os/.config/rubocop/config.yml @@ -0,0 +1,2 @@ +inherit_gem: + caliber: config/all.yml diff --git a/mac_os/.github/FUNDING.yml b/mac_os/.github/FUNDING.yml new file mode 100644 index 0000000..38be873 --- /dev/null +++ b/mac_os/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [bkuhlmann] diff --git a/mac_os/.github/ISSUE_TEMPLATE/config.yml b/mac_os/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..29a903d --- /dev/null +++ b/mac_os/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Community + url: https://alchemists.io/community + about: Please ask questions or discuss specifics here. + - name: Security + url: https://alchemists.io/policies/security + about: Please report security vulnerabilities here. diff --git a/mac_os/.github/ISSUE_TEMPLATE/issue.md b/mac_os/.github/ISSUE_TEMPLATE/issue.md new file mode 100644 index 0000000..61adc6e --- /dev/null +++ b/mac_os/.github/ISSUE_TEMPLATE/issue.md @@ -0,0 +1,18 @@ +--- +name: Issue +title: "Add|Update|Fix|Remove|Refactor " +about: Report an issue. Please use only one of the subject prefixes. +--- + + + +## Why + + +## How + + +## Notes + diff --git a/mac_os/.github/PULL_REQUEST_TEMPLATE.md b/mac_os/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..1b93c92 --- /dev/null +++ b/mac_os/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,8 @@ +## Overview + + +## Screenshots/Screencasts + + +## Details + diff --git a/mac_os/.gitignore b/mac_os/.gitignore new file mode 100644 index 0000000..b844b14 --- /dev/null +++ b/mac_os/.gitignore @@ -0,0 +1 @@ +Gemfile.lock diff --git a/mac_os/.ruby-version b/mac_os/.ruby-version new file mode 100644 index 0000000..fcdb2e1 --- /dev/null +++ b/mac_os/.ruby-version @@ -0,0 +1 @@ +4.0.0 diff --git a/mac_os/CITATION.cff b/mac_os/CITATION.cff new file mode 100644 index 0000000..8a94597 --- /dev/null +++ b/mac_os/CITATION.cff @@ -0,0 +1,22 @@ +cff-version: 1.2.0 +message: Please use the following metadata when citing this project in your work. +title: macOS +abstract: Shell scripts for automated macOS machine setup. +version: 22.0.0 +license: Hippocratic-2.1 +date-released: 2026-01-01 +authors: + - family-names: Kuhlmann + given-names: Brooke + affiliation: Alchemists + orcid: https://orcid.org/0000-0002-5810-6268 +keywords: + - bash + - shell + - scripts + - automation + - setup + - recovery +repository-code: https://github.com/bkuhlmann/mac_os +repository-artifact: https://alchemists.io/projects/mac_os +url: https://alchemists.io/projects/mac_os diff --git a/mac_os/Gemfile b/mac_os/Gemfile new file mode 100644 index 0000000..7958d5d --- /dev/null +++ b/mac_os/Gemfile @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +ruby file: ".ruby-version" + +source "https://rubygems.org" + +gem "caliber", "~> 0.87" +gem "debug", "~> 1.11" +gem "git-lint", "~> 10.0" +gem "rake", "~> 13.3" diff --git a/mac_os/LICENSE.adoc b/mac_os/LICENSE.adoc new file mode 100644 index 0000000..7f23bda --- /dev/null +++ b/mac_os/LICENSE.adoc @@ -0,0 +1,134 @@ += Hippocratic License + +Version: 2.1.0. + +Purpose. The purpose of this License is for the Licensor named above to +permit the Licensee (as defined below) broad permission, if consistent +with Human Rights Laws and Human Rights Principles (as each is defined +below), to use and work with the Software (as defined below) within the +full scope of Licensor’s copyright and patent rights, if any, in the +Software, while ensuring attribution and protecting the Licensor from +liability. + +Permission and Conditions. The Licensor grants permission by this +license ("License"), free of charge, to the extent of Licensor’s +rights under applicable copyright and patent law, to any person or +entity (the "Licensee") obtaining a copy of this software and +associated documentation files (the "Software"), to do everything with +the Software that would otherwise infringe (i) the Licensor’s copyright +in the Software or (ii) any patent claims to the Software that the +Licensor can license or becomes able to license, subject to all of the +following terms and conditions: + +* Acceptance. This License is automatically offered to every person and +entity subject to its terms and conditions. Licensee accepts this +License and agrees to its terms and conditions by taking any action with +the Software that, absent this License, would infringe any intellectual +property right held by Licensor. +* Notice. Licensee must ensure that everyone who gets a copy of any part +of this Software from Licensee, with or without changes, also receives +the License and the above copyright notice (and if included by the +Licensor, patent, trademark and attribution notice). Licensee must cause +any modified versions of the Software to carry prominent notices stating +that Licensee changed the Software. For clarity, although Licensee is +free to create modifications of the Software and distribute only the +modified portion created by Licensee with additional or different terms, +the portion of the Software not modified must be distributed pursuant to +this License. If anyone notifies Licensee in writing that Licensee has +not complied with this Notice section, Licensee can keep this License by +taking all practical steps to comply within 30 days after the notice. If +Licensee does not do so, Licensee’s License (and all rights licensed +hereunder) shall end immediately. +* Compliance with Human Rights Principles and Human Rights Laws. +[arabic] +. Human Rights Principles. +[loweralpha] +.. Licensee is advised to consult the articles of the United Nations +Universal Declaration of Human Rights and the United Nations Global +Compact that define recognized principles of international human rights +(the "Human Rights Principles"). Licensee shall use the Software in a +manner consistent with Human Rights Principles. +.. Unless the Licensor and Licensee agree otherwise, any dispute, +controversy, or claim arising out of or relating to (i) Section 1(a) +regarding Human Rights Principles, including the breach of Section 1(a), +termination of this License for breach of the Human Rights Principles, +or invalidity of Section 1(a) or (ii) a determination of whether any Law +is consistent or in conflict with Human Rights Principles pursuant to +Section 2, below, shall be settled by arbitration in accordance with the +Hague Rules on Business and Human Rights Arbitration (the "Rules"); +provided, however, that Licensee may elect not to participate in such +arbitration, in which event this License (and all rights licensed +hereunder) shall end immediately. The number of arbitrators shall be one +unless the Rules require otherwise. ++ +Unless both the Licensor and Licensee agree to the contrary: (1) All +documents and information concerning the arbitration shall be public and +may be disclosed by any party; (2) The repository referred to under +Article 43 of the Rules shall make available to the public in a timely +manner all documents concerning the arbitration which are communicated +to it, including all submissions of the parties, all evidence admitted +into the record of the proceedings, all transcripts or other recordings +of hearings and all orders, decisions and awards of the arbitral +tribunal, subject only to the arbitral tribunal’s powers to take such +measures as may be necessary to safeguard the integrity of the arbitral +process pursuant to Articles 18, 33, 41 and 42 of the Rules; and (3) +Article 26(6) of the Rules shall not apply. +. Human Rights Laws. The Software shall not be used by any person or +entity for any systems, activities, or other uses that violate any Human +Rights Laws. "Human Rights Laws" means any applicable laws, +regulations, or rules (collectively, "Laws") that protect human, +civil, labor, privacy, political, environmental, security, economic, due +process, or similar rights; provided, however, that such Laws are +consistent and not in conflict with Human Rights Principles (a dispute +over the consistency or a conflict between Laws and Human Rights +Principles shall be determined by arbitration as stated above). Where +the Human Rights Laws of more than one jurisdiction are applicable or in +conflict with respect to the use of the Software, the Human Rights Laws +that are most protective of the individuals or groups harmed shall +apply. +. Indemnity. Licensee shall hold harmless and indemnify Licensor (and +any other contributor) against all losses, damages, liabilities, +deficiencies, claims, actions, judgments, settlements, interest, awards, +penalties, fines, costs, or expenses of whatever kind, including +Licensor’s reasonable attorneys’ fees, arising out of or relating to +Licensee’s use of the Software in violation of Human Rights Laws or +Human Rights Principles. +* Failure to Comply. Any failure of Licensee to act according to the +terms and conditions of this License is both a breach of the License and +an infringement of the intellectual property rights of the Licensor +(subject to exceptions under Laws, e.g., fair use). In the event of a +breach or infringement, the terms and conditions of this License may be +enforced by Licensor under the Laws of any jurisdiction to which +Licensee is subject. Licensee also agrees that the Licensor may enforce +the terms and conditions of this License against Licensee through +specific performance (or similar remedy under Laws) to the extent +permitted by Laws. For clarity, except in the event of a breach of this +License, infringement, or as otherwise stated in this License, Licensor +may not terminate this License with Licensee. +* Enforceability and Interpretation. If any term or provision of this +License is determined to be invalid, illegal, or unenforceable by a +court of competent jurisdiction, then such invalidity, illegality, or +unenforceability shall not affect any other term or provision of this +License or invalidate or render unenforceable such term or provision in +any other jurisdiction; provided, however, subject to a court +modification pursuant to the immediately following sentence, if any term +or provision of this License pertaining to Human Rights Laws or Human +Rights Principles is deemed invalid, illegal, or unenforceable against +Licensee by a court of competent jurisdiction, all rights in the +Software granted to Licensee shall be deemed null and void as between +Licensor and Licensee. Upon a determination that any term or provision +is invalid, illegal, or unenforceable, to the extent permitted by Laws, +the court may modify this License to affect the original purpose that +the Software be used in compliance with Human Rights Principles and +Human Rights Laws as closely as possible. The language in this License +shall be interpreted as to its fair meaning and not strictly for or +against any party. +* Disclaimer. TO THE FULL EXTENT ALLOWED BY LAW, THIS SOFTWARE COMES +"AS IS," WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED, AND LICENSOR AND +ANY OTHER CONTRIBUTOR SHALL NOT BE LIABLE TO ANYONE FOR ANY DAMAGES OR +OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE +OR THIS LICENSE, UNDER ANY KIND OF LEGAL CLAIM. + +This Hippocratic License is an link:https://ethicalsource.dev[Ethical Source license] and is offered +for use by licensors and licensees at their own risk, on an "AS IS" basis, and with no warranties +express or implied, to the maximum extent permitted by Laws. diff --git a/mac_os/README.adoc b/mac_os/README.adoc new file mode 100644 index 0000000..f5e2bbe --- /dev/null +++ b/mac_os/README.adoc @@ -0,0 +1,245 @@ +:toc: macro +:toclevels: 5 +:figure-caption!: + +:mac_os_defaults_link: link:https://macos-defaults.com[macOS Defaults] + += macOS + +Shell scripts for automated macOS machine setup. + +This project is a framework for automating the setup of a macOS machine. In order to illustrate the +potential of what this project can do, please see the companion +link:https://alchemists.io/projects/mac_os-config[macOS Config] project for details. The _macOS +Config_ project is an opinionated configuration which meets the needs of my development environment +but is also meant to serve as an example and guide for building your own personalized setup. Here is +how the two projects are meant to be used: + +* *macOS* (this project) - The foundational framework for building custom macOS machine setups. +* *link:https://alchemists.io/projects/mac_os-config[macOS Configuration]* - The layer on top of + this _macOS_ project which defines a custom machine implementation. The project is meant to be + forked for as many custom machine setups as needed. + +toc::[] + +== Features + +* Provides a command line interface, written in Bash, with no additional dependencies for + installation and management of a macOS machine. +* Supports macOS boot disk creation for fresh install of operating system. +* Installs link:https://developer.apple.com/xcode[Xcode Command Line Tools]. +* Installs link:http://brew.sh[Homebrew] formulas and casks. +* Installs link:http://www.apple.com/macosx/whats-new/app-store.html[App Store] software. +* Installs non-App Store software applications. +* Installs software application extensions. +* Installs dotfiles. +* Installs link:https://nodejs.org[Node] link:https://www.npmjs.com[packages]. +* Installs link:https://www.ruby-lang.org[Ruby] link:https://rubygems.org[gems]. +* Installs link:https://www.rust-lang.org[Rust] link:https://crates.io[crates]. +* Applies {mac_os_defaults_link}. +* Configures installed software. +* Supports restoration of machine backups. + +== Requirements + +. link:https://www.apple.com/os/macos[macOS 26.0.0 (Tahoe)] +. link:https://developer.apple.com/xcode[Xcode] + +== Setup + +To install, run: + +[source,bash] +---- +git clone https://github.com/bkuhlmann/mac_os.git +cd mac_os +git checkout 22.0.0 +---- + +== Usage + +Run the following: + +[source,bash] +---- +bin/run +---- + +You will be presented with the following options (listed in order of +use): + +.... +Boot: + B: Create boot disk. +Install: + b: Apply basic settings. + t: Install development tools. + hf: Install Homebrew Formulas. + hc: Install Homebrew Casks. + m: Install Mac App Store software. + a: Install application software. + x: Install application software extensions. + df: Install dotfiles. + np: Install Node packages. + rg: Install Ruby gems. + rc: Install Rust crates. + d: Apply default settings. + cs: Configure installed software. + i: Install everything (i.e. executes all install options in order listed). +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 a full install or select a specific option to run a single action. 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 `bin/run` script. For +example, executing `bin/run i` will execute the full 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. + +=== Boot Disk + +When attempting to create a boot disk via `bin/run B`, you’ll be presented with the following +documentation (provided here for reference): + +.... +macOS Boot Disk Setup + 1. Insert a USB drive (8GB or higher). + 2. Use Disk Utility to format as "Mac OS Extended (Journaled)". + 3. Use Disk Utility to set the schema, if available, as "GUID Partition Map". + 4. Use Disk Utility to label as "Untitled". + 5. Run this script to install the OS and create a bootable USB drive. + +macOS Boot Disk Usage: + 1. Insert the USB drive, created above, into the machine to be upgraded. + 2. Reboot the machine. + 3. Press and hold the POWER key before the Apple logo appears. + 4. Select the USB boot disk from the menu. + 5. Use Disk Utility to delete and/or erase the hard drive including associated partitions. + 6. Use Disk Utility to create a single "APFS" drive. + 7. Install the new operating system. + +macOS Boot Disk Recovery: + 1. Start/restart the machine. + 2. Press and hold the POWER key before the Apple logo appears. + 3. Wait for the macOS installer to load from the recovery partition. + 4. Use the dialog options to launch Disk Utility, reinstall the system, etc. +.... + +💡 You can ensure the right installer is downloaded and available for Boot Disk creation by running the following commands: + +[source,bash] +---- +# Show which installers are available. +softwareupdate --list-full-installers + +# Download desired version. +sudo softwareupdate --fetch-full-installer --full-installer-version 26.0 +---- + +=== Customization + +All executable scripts can be found in the `bin` folder: + +* `bin/apply_basic_settings` (optional, customizable): Applies basic and initial settings for + setting up a machine. +* `bin/apply_default_settings` (optional, customizable): Applies {mac_os_defaults_link}. +* `bin/configure_software` (optional, customizable): Configures installed software as part of the + post install process. +* `bin/create_boot_disk` (optional): Creates a macOS boot disk. +* `bin/install_app_store` (optional, customizable): Installs macOS, GUI-based, App Store + applications. +* `bin/install_applications` (optional, customizable): Installs macOS, GUI-based, non-App Store + applications. +* `bin/install_dev_tools` (required): Installs macOS development tools required by Homebrew. +* `bin/install_dotfiles` (optional, customizable): Installs personal dotfiles so the system is + tailored to your workflow. +* `bin/install_extensions` (optional, customizable): Installs macOS application extensions and + add-ons. +* `bin/install_homebrew_casks` (optional, customizable): Installs Homebrew Formulas. +* `bin/install_homebrew_formulas` (optional, customizable): Installs Homebrew Casks. +* `bin/install_node_packages` (optional, customizable): Installs Node packages. +* `bin/install_ruby_gems` (optional, customizable): Installs Ruby gems. +* `bin/install_rust_crates` (optional, customizable): Installs Rust crates. +* `bin/restore_backup` (optional, customizable): Restores system/application settings from backup + image. +* `bin/run` (required): The main script and interface for macOS setup. + +The `lib` folder provides the base framework for installing, re-installing, and uninstalling +software. Everything provided via the link:https://alchemists.io/projects/mac_os-config[macOS +Config] project is built upon the functions found in the `lib` folder. See the +link:https://alchemists.io/projects/mac_os-config[macOS Config] project for further details. + +* `lib/settings.sh`: Defines global settings for software applications, extensions, etc. + +=== Troubleshooting + +* *Pi-hole*: When using link:https://pi-hole.net[Pi-hole], you might need to temporarily disable + prior to upgrading as you might experience various errors with Apple not being able to detect an + internet connection which prevents the installer from working. +* *Recovery Mode*: When using the boot disk and the installer fails in some catastrophic manner, + reboot the machine into recovery mode -- pass:[POWER] (Silicon) or + pass:[COMMAND] + pass:[r] (Intel) buttons -- to download and install the + last operating system used. Alternatively, you can also use pass:[COMMAND] + + pass:[OPTION] + pass:[r] (Intel) to attempt to download the latest operating + system. +* *NVRAM/PRAM Reset*: When using the boot disk, you might experience a situation where you see a + black screen with a white circle and diagonal line running through it. This means macOS lost or + can't find the boot disk for some reason. To correct this, shut down and boot up the system again + while holding down pass:[OPTION] + pass:[COMMAND] + pass:[r] + + pass:[p] (Intel) keys simultaneously. You might want to wait for the system boot sound + to happen a few times before releasing the keys. This will clear the system NVRAM/PRAM. At this + point you can shut down and restart the system following the boot disk instructions (the boot disk + will be recognized now). +* *System Management Controller (SMC) Reset*: Sometimes it can help to reset the SMC to improve + system speed. To fix, follow these steps: +** Shut down your Mac. +** Hold down pass:[CONTROL] + pass:[OPTION] on the left side of the keyboard + and pass:[SHIFT] on the right side of the keyboard. +** After seven seconds, hold down the Power button as well. +** Release all keys after another seven seconds. +** Turn on your Mac. +** For more troubleshooting tips, check out The Eclectic Light Company's link:https://eclecticlight.co/mac-troubleshooting-summary/[Mac Troubleshooting Summary]. + +== Development + +To contribute, run: + +[source,bash] +---- +git clone https://github.com/bkuhlmann/mac_os.git +cd mac_os +---- + +== link:https://alchemists.io/policies/license[License] + +== link:https://alchemists.io/policies/security[Security] + +== link:https://alchemists.io/policies/code_of_conduct[Code of Conduct] + +== link:https://alchemists.io/policies/contributions[Contributions] + +== link:https://alchemists.io/policies/developer_certificate_of_origin[Developer Certificate of Origin] + +== link:https://alchemists.io/projects/mac_os/versions[Versions] + +== link:https://alchemists.io/community[Community] + +== Credits + +Engineered by link:https://alchemists.io/team/brooke_kuhlmann[Brooke Kuhlmann]. diff --git a/mac_os/Rakefile b/mac_os/Rakefile new file mode 100644 index 0000000..c79775c --- /dev/null +++ b/mac_os/Rakefile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "git/lint/rake/register" +require "rubocop/rake_task" + +Git::Lint::Rake::Register.call +RuboCop::RakeTask.new + +desc "Run code quality checks" +task quality: %i[git_lint rubocop] + +task default: :quality diff --git a/mac_os/bin/create_boot_disk b/mac_os/bin/create_boot_disk new file mode 100755 index 0000000..a05c2e3 --- /dev/null +++ b/mac_os/bin/create_boot_disk @@ -0,0 +1,37 @@ +#! /usr/bin/env bash + +# Creates macOS boot disk. + +printf "%s\n" "macOS Boot Disk Setup" +printf "%s\n" " 1. Insert a USB drive (8GB or higher)." +printf "%s\n" " 2. Use Disk Utility to format as \"Mac OS Extended (Journaled)\"." +printf "%s\n" " 3. Use Disk Utility to set the schema, if available, as \"GUID Partition Map\"." +printf "%s\n" " 4. Use Disk Utility to label as \"Untitled\"." +printf "%s\n" " 5. Run this script to install the OS and create a bootable USB drive." +printf "\n%s\n" "macOS Boot Disk Usage:" +printf "%s\n" " 1. Insert the USB drive, created above, into the machine to be upgraded." +printf "%s\n" " 2. Reboot the machine." +printf "%s\n" " 3. Press and hold the POWER key before the Apple logo appears." +printf "%s\n" " 4. Select the USB boot disk from the menu." +printf "%s\n" " 5. Use Disk Utility to delete and/or erase the hard drive including associated partitions." +printf "%s\n" " 6. Use Disk Utility to create a single \"APFS\" drive." +printf "%s\n" " 7. Install the new operating system." +printf "\n%s\n" "macOS Boot Disk Recovery:" +printf "%s\n" " 1. Start/restart the machine." +printf "%s\n" " 2. Press and hold the POWER key before the Apple logo appears." +printf "%s\n" " 3. Wait for the macOS installer to load from the recovery partition." +printf "%s\n" " 4. Use the dialog options to launch Disk Utility, reinstall the system, etc." + +printf "\n%s\n" "Creating macOS boot disk..." + +if [[ ! -e "$MAC_OS_BOOT_DISK_CREATOR" ]]; then + printf "%s\n" "ERROR: macOS installer does not exist: $MAC_OS_BOOT_DISK_CREATOR. Use System Preferences → Software Update to download." + exit 1 +fi + +if [[ ! -d "$MAC_OS_BOOT_DISK_PATH" ]]; then + printf "%s\n" "ERROR: Boot disk must be mounted at: $MAC_OS_BOOT_DISK_PATH." + exit 1 +fi + +sudo "$MAC_OS_BOOT_DISK_CREATOR" --volume "$MAC_OS_BOOT_DISK_PATH" --downloadassets --nointeraction diff --git a/mac_os/bin/install_app_store b/mac_os/bin/install_app_store new file mode 100755 index 0000000..2fe1453 --- /dev/null +++ b/mac_os/bin/install_app_store @@ -0,0 +1,17 @@ +#! /usr/bin/env bash + +# Installs App Store software. + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +SCRIPT_PATH="$MAC_OS_CONFIG_PATH/bin/install_app_store" + +if [[ -x "$SCRIPT_PATH" ]]; then + check_mas_install + "$SCRIPT_PATH" +else + printf "%s\n" "WARNING: App Store install script does not exist or is not executable." +fi diff --git a/mac_os/bin/install_applications b/mac_os/bin/install_applications new file mode 100755 index 0000000..c0276c3 --- /dev/null +++ b/mac_os/bin/install_applications @@ -0,0 +1,16 @@ +#! /usr/bin/env bash + +# Installs applications. + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +SCRIPT_PATH="$MAC_OS_CONFIG_PATH/bin/install_applications" + +if [[ -x "$SCRIPT_PATH" ]]; then + "$SCRIPT_PATH" +else + printf "%s\n" "WARNING: Applications install script does not exist or is not executable." +fi diff --git a/mac_os/bin/install_basics b/mac_os/bin/install_basics new file mode 100755 index 0000000..52bad38 --- /dev/null +++ b/mac_os/bin/install_basics @@ -0,0 +1,16 @@ +#! /usr/bin/env bash + +# Installs basic system settings. + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +SCRIPT_PATH="$MAC_OS_CONFIG_PATH/bin/install_basics" + +if [[ -x "$SCRIPT_PATH" ]]; then + "$SCRIPT_PATH" +else + printf "%s\n" "WARNING: Basic settings script does not exist or is not executable." +fi diff --git a/mac_os/bin/install_defaults b/mac_os/bin/install_defaults new file mode 100755 index 0000000..1494f8f --- /dev/null +++ b/mac_os/bin/install_defaults @@ -0,0 +1,16 @@ +#! /usr/bin/env bash + +# Installs system and application default settings. + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +SCRIPT_PATH="$MAC_OS_CONFIG_PATH/bin/install_defaults" + +if [[ -x "$SCRIPT_PATH" ]]; then + "$SCRIPT_PATH" +else + printf "%s\n" "WARNING: Default settings script does not exist or is not executable." +fi diff --git a/mac_os/bin/install_dev_tools b/mac_os/bin/install_dev_tools new file mode 100755 index 0000000..f26ebfb --- /dev/null +++ b/mac_os/bin/install_dev_tools @@ -0,0 +1,17 @@ +#! /usr/bin/env bash + +# Installs development tooling requirements. + +printf "%s\n" "Installing Xcode CLI tools..." +xcode-select --install + +printf "%s\n" "💡 ALT+TAB to view and accept Xcode license window." +read -p "Have you completed the Xcode CLI tools install (y/n)? " xcode_response +if [[ "$xcode_response" != "y" ]]; then + printf "%s\n" "ERROR: Xcode CLI tools must be installed before proceeding." + exit 1 +fi + +if [[ "$(/usr/bin/arch)" == "arm64" ]]; then + softwareupdate --install-rosetta --agree-to-license +fi diff --git a/mac_os/bin/install_extensions b/mac_os/bin/install_extensions new file mode 100755 index 0000000..4238a59 --- /dev/null +++ b/mac_os/bin/install_extensions @@ -0,0 +1,16 @@ +#! /usr/bin/env bash + +# Installs application extensions. + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +SCRIPT_PATH="$MAC_OS_CONFIG_PATH/bin/install_extensions" + +if [[ -x "$SCRIPT_PATH" ]]; then + "$SCRIPT_PATH" +else + printf "%s\n" "WARNING: Application extensions install script does not exist or is not executable." +fi diff --git a/mac_os/bin/install_homebrew_casks b/mac_os/bin/install_homebrew_casks new file mode 100755 index 0000000..34cc418 --- /dev/null +++ b/mac_os/bin/install_homebrew_casks @@ -0,0 +1,17 @@ +#! /usr/bin/env bash + +# Installs Homebrew Cask software. + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +SCRIPT_PATH="$MAC_OS_CONFIG_PATH/bin/install_homebrew_casks" + +if [[ -x "$SCRIPT_PATH" ]]; then + install_homebrew + "$SCRIPT_PATH" +else + printf "%s\n" "WARNING: Homebrew Casks install script does not exist or is not executable." +fi diff --git a/mac_os/bin/install_homebrew_formulas b/mac_os/bin/install_homebrew_formulas new file mode 100755 index 0000000..13f8fc9 --- /dev/null +++ b/mac_os/bin/install_homebrew_formulas @@ -0,0 +1,17 @@ +#! /usr/bin/env bash + +# Installs Homebrew Formula software. + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +SCRIPT_PATH="$MAC_OS_CONFIG_PATH/bin/install_homebrew_formulas" + +if [[ -x "$SCRIPT_PATH" ]]; then + install_homebrew + "$SCRIPT_PATH" +else + printf "%s\n" "WARNING: Homebrew Formulas install script does not exist or is not executable." +fi diff --git a/mac_os/bin/install_node_packages b/mac_os/bin/install_node_packages new file mode 100755 index 0000000..e1c26fd --- /dev/null +++ b/mac_os/bin/install_node_packages @@ -0,0 +1,17 @@ +#! /usr/bin/env bash + +# Installs Node packages. + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +SCRIPT_PATH="$MAC_OS_CONFIG_PATH/bin/install_node_packages" + +if [[ -x "$SCRIPT_PATH" ]]; then + install_node + "$SCRIPT_PATH" +else + printf "%s\n" "WARNING: Node packages install script does not exist or is not executable." +fi diff --git a/mac_os/bin/install_ruby_gems b/mac_os/bin/install_ruby_gems new file mode 100755 index 0000000..70a01a8 --- /dev/null +++ b/mac_os/bin/install_ruby_gems @@ -0,0 +1,17 @@ +#! /usr/bin/env bash + +# Installs Ruby gems. + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +SCRIPT_PATH="$MAC_OS_CONFIG_PATH/bin/install_ruby_gems" + +if [[ -x "$SCRIPT_PATH" ]]; then + install_ruby + "$SCRIPT_PATH" +else + printf "%s\n" "WARNING: Ruby gems install script does not exist or is not executable." +fi diff --git a/mac_os/bin/install_rust_crates b/mac_os/bin/install_rust_crates new file mode 100755 index 0000000..b153bc0 --- /dev/null +++ b/mac_os/bin/install_rust_crates @@ -0,0 +1,17 @@ +#! /usr/bin/env bash + +# Installs Rust crates. + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +SCRIPT_PATH="$MAC_OS_CONFIG_PATH/bin/install_rust_crates" + +if [[ -x "$SCRIPT_PATH" ]]; then + install_rust + "$SCRIPT_PATH" +else + printf "%s\n" "WARNING: Rust crates install script does not exist or is not executable." +fi diff --git a/mac_os/bin/install_shell b/mac_os/bin/install_shell new file mode 100755 index 0000000..f653fec --- /dev/null +++ b/mac_os/bin/install_shell @@ -0,0 +1,16 @@ +#! /usr/bin/env bash + +# Installs shell. + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +SCRIPT_PATH="$MAC_OS_CONFIG_PATH/bin/install_shell" + +if [[ -x "$SCRIPT_PATH" ]]; then + "$SCRIPT_PATH" +else + printf "%s\n" "WARNING: Shell script does not exist or is not executable." +fi diff --git a/mac_os/bin/rake b/mac_os/bin/rake new file mode 100755 index 0000000..9bef742 --- /dev/null +++ b/mac_os/bin/rake @@ -0,0 +1,6 @@ +#! /usr/bin/env ruby +# frozen_string_literal: true + +require "bundler/setup" + +load Gem.bin_path "rake", "rake" diff --git a/mac_os/bin/restore_backup b/mac_os/bin/restore_backup new file mode 100755 index 0000000..aca97e3 --- /dev/null +++ b/mac_os/bin/restore_backup @@ -0,0 +1,16 @@ +#! /usr/bin/env bash + +# Performs restoration of backup data. + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +SCRIPT_PATH="$MAC_OS_CONFIG_PATH/bin/restore_backup" + +if [[ -x "$SCRIPT_PATH" ]]; then + "$SCRIPT_PATH" +else + printf "%s\n" "WARNING: Restore backup script does not exist or is not executable." +fi diff --git a/mac_os/bin/run b/mac_os/bin/run new file mode 100755 index 0000000..7a7f463 --- /dev/null +++ b/mac_os/bin/run @@ -0,0 +1,62 @@ +#! /usr/bin/env bash + +# Executes the command line interface. + +source lib/installers.sh +source lib/options.sh +source lib/settings.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 "%s\n\n" "ERROR: Unable to load macOS configuration: $MAC_OS_CONFIG_PATH." + printf "%s\n" "Please check the following before continuing:" + printf "%s\n" " • Download the default macOS configuration here: https://github.com/bkuhlmann/mac_os-config." + printf "%s\n" " • Customize as necessary for your setup or fork the project and make your own configuration." + printf "%s\n" " • When finished, your folder structure should look like this:" + printf "%s\n" " • /mac_os:" + printf "%s\n" " • /mac_os-config:" + exit 1 +fi + +configure_environment + +while true; do + if [[ $# == 0 ]]; then + printf "\n%s\n" "Usage: run OPTION" + printf "\n%s\n" "OSX Options:" + printf "%s\n" " Boot:" + printf "%s\n" " B: Create boot disk." + printf "%s\n" " Install:" + printf "%s\n" " b: Install basics." + printf "%s\n" " t: Install development tools." + printf "%s\n" " hf: Install Homebrew Formulas." + printf "%s\n" " hc: Install Homebrew Casks." + printf "%s\n" " m: Install Mac App Store software." + printf "%s\n" " a: Install application software." + printf "%s\n" " x: Install application software extensions." + printf "%s\n" " d: Install defaults." + printf "%s\n" " s: Install shell." + printf "%s\n" " r: Restore backups." + printf "%s\n" " i: Install all (i.e. executes all of the above steps in order listed)." + printf "%s\n" " Libraries:" + printf "%s\n" " rc: Install Rust crates." + printf "%s\n" " rg: Install Ruby gems." + printf "%s\n" " np: Install Node packages." + printf "%s\n" " l: Install libraries (i.e. executes all of the above steps in order listed)." + printf "%s\n" " Manage:" + printf "%s\n" " c: Check status of managed software." + printf "%s\n" " C: Caffeinate machine." + printf "%s\n" " w: Clean work (temp) directory." + printf "%s\n\n" " q: Quit/Exit." + read -p "Enter selection: " response + printf "\n" + process_option "$response" + else + process_option "$1" + fi + + break +done diff --git a/mac_os/lib/installers.sh b/mac_os/lib/installers.sh new file mode 100644 index 0000000..61de5c1 --- /dev/null +++ b/mac_os/lib/installers.sh @@ -0,0 +1,370 @@ +#! /usr/bin/env bash + +# Defines software installer functions. + +# Label: Download File +# Description: Download remote file to local disk. +# Parameters: $1 (required): URL, $2 (required): File name, $3 (optional): HTTP header. +download_file() { + 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_file + +# Label: Install Application +# Description: Install an application. +# Parameters: $1 (required): Install path, $2 (required): Name. +install_app() { + local install_path="$1" + local name="$2" + local install_root="" + local file_extension="" + + install_root=$(get_install_root "$name") + file_extension=$(get_extension "$name") + + printf "%s\n" "Installing: $install_root/$name..." + + case $file_extension in + '') + cp -a "$install_path/$name" "$install_root";; + 'app') + cp -a "$install_path/$name" "$install_root";; + 'prefPane') + sudo cp -pR "$install_path/$name" "$install_root";; + 'qlgenerator') + sudo cp -pR "$install_path/$name" "$install_root" && qlmanage -r;; + *) + printf "%s\n" "ERROR: Unknown file extension: $file_extension." + esac +} +export -f install_app + +# Label: Install DMG Application +# Description: Install DMG application. +# Parameters: $1 (required): URL, $2 (required): Mount path, $3 (required): Application name. +install_dmg_app() { + local url="$1" + local mount_point="/Volumes/$2" + local app_name="$3" + local install_path="" + local work_file="download.dmg" + + install_path=$(get_install_path "$app_name") + + if [[ ! -e "$install_path" ]]; then + download_file "$url" "$work_file" + mount_image "$MAC_OS_WORK_PATH/$work_file" + install_app "$mount_point" "$app_name" + unmount_image "$mount_point" + verify_application "$app_name" + fi +} +export -f install_dmg_app + +# Label: Install DMG Package +# Description: Install DMG application via a package file. +# Parameters: $1 (required): URL, $2 (required): Mount path, $3 (required): Application name. +install_dmg_pkg() { + local url="$1" + local mount_point="/Volumes/$2" + local app_name="$3" + local install_path="" + local work_file="download.dmg" + + install_path=$(get_install_path "$app_name") + + if [[ ! -e "$install_path" ]]; then + download_file "$url" "$work_file" + mount_image "$MAC_OS_WORK_PATH/$work_file" + install_pkg "$mount_point" "$app_name" + unmount_image "$mount_point" + printf "%s\n" "Installed: $app_name." + verify_application "$app_name" + fi +} +export -f install_dmg_pkg + +# Label: Install File +# Description: Install a single file. +# Parameters: $1 (required): URL, $2 (required): Install path. +install_file() { + local file_url="$1" + local file_name="" + local install_path="$2" + + file_name=$(get_basename "$1") + + if [[ ! -e "$install_path" ]]; then + download_file "$file_url" "$file_name" + mkdir -p $(dirname "$install_path") + mv "$MAC_OS_WORK_PATH/$file_name" "$install_path" + printf "%s\n" "Installed: $file_name." + verify_path "$install_path" + fi +} +export -f install_file + +# Label: Install Git Application +# Description: Install application from a Git repository. +# Parameters: $1 (required): URL, $2 (required): Install path, $3 (optional): Git clone options. +install_git_app() { + local url="$1" + local install_path="$2" + local app_name="" + local options="--quiet" + + app_name="$(get_basename "$2")" + + if [[ -n "$3" ]]; then + local options="$options $3" + fi + + if [[ ! -e "$install_path" ]]; then + printf "%s\n" "Installing: $install_path..." + git clone $options "$url" "$install_path" + printf "%s\n" "Installed: $app_name." + verify_path "$install_path" + fi +} +export -f install_git_app + +# Label: Install Git Project +# Description: Install Git project. +# Parameters: $1 (required): URL, $2 (required): Version, $3 (required): Project directory, $4 (required): 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 -c advice.detachedHead=false checkout "$repo_version" + eval "$script" + ) + rm -rf "$project_dir" +} +export -f install_git_project + +# Label: Install Homebrew +# Description: Install and setup Homebrew. +install_homebrew() { + if ! command -v brew > /dev/null; then + /bin/bash -c "$(curl --location --fail --silent --show-error https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + echo "eval \"($(get_homebrew_bin_root)/brew shellenv)\"" > "$HOME/.zprofile" + eval "$($(get_homebrew_bin_root)/brew shellenv)" + fi +} +export -f install_homebrew + +# Label: Install Bare Package +# Description: Install a bare package. +# Parameters: $1 (required): URL, $2 (required): Application name. +install_bare_pkg() { + local url="$1" + local app_name="$2" + local install_path="" + local work_file="$app_name.pkg" + + install_path=$(get_install_path "$app_name") + + if [[ ! -e "$install_path" ]]; then + download_file "$url" "$work_file" + install_pkg "$MAC_OS_WORK_PATH" "$app_name" + printf "%s\n" "Installed: $app_name." + verify_application "$app_name" + fi +} +export -f install_bare_pkg + +# Label: Install Package +# Description: Install local package. +# Parameters: $1 (required): Package source path, $2 (required): Application name. +install_pkg() { + local source_path="$1" + local name="$2" + local install_root="" + local package="" + + install_root=$(get_install_root "$name") + package=$(sudo find "$source_path" -maxdepth 1 -type f -name "*.pkg" -o -name "*.mpkg") + + printf "%s\n" "Installing: $install_root/$name..." + sudo installer -pkg "$package" -target / +} +export -f install_pkg + +# Label: Install Program +# Description: Installs program without any packaging. +# Parameters: $1 (required): URL, $2 (required): Name. +install_program() { + local url="$1" + local program_name="$2" + local install_root="" + local install_path="" + + install_root=$(get_install_root "$program_name") + install_path=$(get_install_path "$program_name") + + if [[ ! -e "$install_path" ]]; then + download_file "$url" "$program_name" + mkdir -p "$install_root" + mv "$MAC_OS_WORK_PATH/$program_name" "$install_path" + chmod 755 "$install_path" + printf "%s\n" "Installed: $program_name." + verify_application "$program_name" + fi +} +export -f install_program + +# Label: Install Node +# Description: Install and setup Node for local development. +install_node() { + if [[ ! -x "$(command -v node)" ]]; then + "$(get_homebrew_bin_root)/fnm" install --latest + fi +} +export -f install_node + +# Label: Install Ruby +# Description: Install and setup Ruby for local development. +install_ruby() { + local version="" + + version="$(cat $HOME/.ruby-version | tr -d '\n')" + + if [[ ! -x "$(command -v ruby)" && -n $(ruby --version | grep --quiet "$version") ]]; then + "$(get_homebrew_bin_root)"/frum install "$version" \ + --with-openssl-dir="$(brew --prefix openssl)" \ + --enable-shared \ + --disable-silent-rules + "$(get_homebrew_bin_root)"/frum local "$version" + fi +} +export -f install_ruby + +# Label: Install Rust +# Description: Install and setup Rust for local development. +install_rust() { + if ! command -v cargo > /dev/null; then + curl --proto "=https" --tlsv1.2 --fail --silent --show-error https://sh.rustup.rs | sh + fi +} +export -f install_rust + +# Label: Install Tar Application +# Description: Install application from tar file. +# Parameters: $1 (required): URL, $2 (required): Name, $3 (required): Decompress options. +install_tar_app() { + local url="$1" + local app_name="$2" + local options="$3" + local install_path="" + local work_file="download.tar" + + install_path=$(get_install_path "$app_name") + + if [[ ! -e "$install_path" ]]; then + download_file "$url" "$work_file" + + ( + printf "Preparing...\n" + cd "$MAC_OS_WORK_PATH" + tar "$options" "$work_file" + ) + + install_app "$MAC_OS_WORK_PATH" "$app_name" + printf "%s\n" "Installed: $app_name." + verify_application "$app_name" + fi +} +export -f install_tar_app + +# Label: Install Zip Application +# Description: Install application from zip file. +# Parameters: $1 (required): URL, $2 (required): Name. +install_zip_app() { + local url="$1" + local app_name="$2" + local install_path="" + local work_file="download.zip" + + install_path=$(get_install_path "$app_name") + + if [[ ! -e "$install_path" ]]; then + download_file "$url" "$work_file" + + ( + printf "Preparing...\n" + cd "$MAC_OS_WORK_PATH" + unzip -q "$work_file" + find . -type d -name "$app_name" -print -exec cp -pR {} . > /dev/null 2>&1 \; + ) + + install_app "$MAC_OS_WORK_PATH" "$app_name" + printf "%s\n" "Installed: $app_name." + verify_application "$app_name" + fi +} +export -f install_zip_app + +# Label: Install Zip Package +# Description: Install application from a package within a zip file. +# Parameters: $1 (required): URL, $2 (required): Application name. +install_zip_pkg() { + local url="$1" + local app_name="$2" + local install_path="" + local work_file="download.zip" + + install_path=$(get_install_path "$app_name") + + if [[ ! -e "$install_path" ]]; then + download_file "$url" "$work_file" + + ( + printf "Preparing...\n" + cd "$MAC_OS_WORK_PATH" + unzip -q "$work_file" + ) + + install_pkg "$MAC_OS_WORK_PATH" "$app_name" + printf "%s\n" "Installed: $app_name." + verify_application "$app_name" + fi +} +export -f install_zip_pkg + +# Label: Mount Image +# Description: Mount disk image. +# Parameters: $1 (required): Path. +mount_image() { + printf "%s\n" "Mounting image..." + hdiutil attach -quiet -nobrowse -noautoopen "$1" +} +export -f mount_image + +# Label: Unmount Image +# Description: Unmount disk image. +# Parameters: $1 (required): Path. +unmount_image() { + printf "%s\n" "Unmounting image..." + hdiutil detach -force "$1" +} +export -f unmount_image diff --git a/mac_os/lib/options.sh b/mac_os/lib/options.sh new file mode 100644 index 0000000..1306d85 --- /dev/null +++ b/mac_os/lib/options.sh @@ -0,0 +1,74 @@ +#! /usr/bin/env bash + +# Defines command line prompt options. + + +# Label: Process Option +# Description: Run script based on selection. +# Parameters: $1 (required): The option to process. +process_option() { + case $1 in + 'B') + bin/create_boot_disk;; + 'b') + bin/install_basics;; + 't') + bin/install_dev_tools;; + 'hf') + bin/install_homebrew_formulas;; + 'hc') + bin/install_homebrew_casks;; + 'm') + bin/install_app_store;; + 'a') + bin/install_applications;; + 'x') + bin/install_extensions;; + 'd') + bin/install_defaults;; + 's') + bin/install_shell;; + 'r') + bin/restore_backup;; + 'i') + caffeinate_machine + bin/install_basics + bin/install_dev_tools + bin/install_homebrew_formulas + bin/install_homebrew_casks + bin/install_app_store + bin/install_applications + bin/install_extensions + bin/install_defaults + bin/install_shell + bin/restore_backup + clean_work_path;; + 'np') + bin/install_node_packages;; + 'rg') + bin/install_ruby_gems;; + 'rc') + bin/install_rust_crates;; + 'l') + bin/install_rust_crates + bin/install_ruby_gems + bin/install_node_packages;; + 'c') + verify_homebrew_formulas + verify_homebrew_casks + verify_app_store_applications + verify_applications + verify_extensions + verify_node_packages + verify_ruby_gems + verify_rust_crates;; + 'C') + caffeinate_machine;; + 'w') + clean_work_path;; + 'q');; + *) + printf "ERROR: Invalid option.\n";; + esac +} +export -f process_option diff --git a/mac_os/lib/settings.sh b/mac_os/lib/settings.sh new file mode 100644 index 0000000..74a6a0a --- /dev/null +++ b/mac_os/lib/settings.sh @@ -0,0 +1,11 @@ +#! /usr/bin/env bash + +set -o nounset +set -o errexit +set -o pipefail +IFS=$'\n\t' + +export MAC_OS_BOOT_DISK_CREATOR="/Applications/Install macOS Tahoe.app/Contents/Resources/createinstallmedia" +export MAC_OS_BOOT_DISK_PATH="/Volumes/Untitled" +export MAC_OS_WORK_PATH=/tmp/downloads +export MAC_OS_CONFIG_PATH="../mac_os-config" diff --git a/mac_os/lib/utilities.sh b/mac_os/lib/utilities.sh new file mode 100644 index 0000000..0128a1d --- /dev/null +++ b/mac_os/lib/utilities.sh @@ -0,0 +1,128 @@ +#! /usr/bin/env bash + +# Defines general utility functions. + +# Label: Caffeinate Machine +# Description: Keep machine running for a very long time. +caffeinate_machine() { + if [[ -n "$(pgrep -x caffeinate)" ]]; then + printf "Machine is already caffeinated!\n" + else + caffeinate -s -u -d -i -t 3153600000 > /dev/null & + printf "Machine caffeinated.\n" + fi +} +export -f caffeinate_machine + +# Label: Clean Work Path +# Description: Clean work path of artifacts. +clean_work_path() { + rm -rf "$MAC_OS_WORK_PATH" +} +export -f clean_work_path + +# Label: Get Basename +# Description: Answer file or directory basename. +# Parameters: $1 (required): Path. +get_basename() { + printf "%s" "${1##*/}" +} +export -f get_basename + +# Label: Get Extension +# Description: Answer file extension without dot prefix. +# Parameters: $1 (required): Path. +get_extension() { + local name="" + local extension="${1##*.}" + + name=$(get_basename "$1") + + if [[ "$name" == "$extension" ]]; then + printf '' + else + printf "%s" "$extension" + fi +} +export -f get_extension + +# Label: Get Homebrew Root +# Description: Answer Homebrew root path. +get_homebrew_root() { + if [[ "$(/usr/bin/arch)" == "arm64" ]]; then + printf "%s" "/opt/homebrew" + else + printf "%s" "/usr/local/Homebrew" + fi +} +export -f get_homebrew_root + +# Label: Get Homebrew Bin Root +# Description: Answer Homebrew binary root path. +get_homebrew_bin_root() { + if [[ "$(/usr/bin/arch)" == "arm64" ]]; then + printf "%s" "/opt/homebrew/bin" + else + printf "%s" "/usr/local/bin" + fi +} +export -f get_homebrew_bin_root + +# Label: Get Install Path +# Description: Answer full install path (including file name). +# Parameters: $1 (required): Path. +get_install_path() { + local file_name="$1" + local install_path="" + + install_path=$(get_install_root "$file_name") + + printf "%s" "$install_path/$file_name" +} +export -f get_install_path + +# Label: Get Install Root +# Description: Answer root install path. +# Parameters: $1 (required): Path. +get_install_root() { + local file_name="$1" + + case $(get_extension "$file_name") in + '') + printf "%s" "$HOME/.local/bin";; + 'app') + printf "/Applications";; + 'prefPane') + printf "/Library/PreferencePanes";; + 'qlgenerator') + printf "/Library/QuickLook";; + *) + printf "/tmp/unknown";; + esac +} +export -f get_install_root + +# Label: Check Mac App Store Install +# Description: Check Mac App Store (mas) CLI has been installed. +check_mas_install() { + if ! command -v mas > /dev/null; then + printf "%s\n" "ERROR: Mac App Store (mas) CLI can't be found." + printf "%s\n" " Please ensure mas (i.e. brew install mas) is installed." + exit 1 + fi +} +export -f check_mas_install + +# Label: Configure Environment +# Description: Configure shell and ensure PATH is properly configured. +configure_environment() { + if [[ ! -s "$HOME/.bash_profile" ]]; then + printf "%s\n" "if [ -f ~/.bashrc ]; then . ~/.bashrc; fi" > "$HOME/.bash_profile" + fi + + if [[ ! -s "$HOME/.bashrc" ]]; then + printf "%s\n" 'export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"' > "$HOME/.bashrc" + source "$HOME/.bashrc" + fi +} +export -f configure_environment diff --git a/mac_os/lib/verifiers.sh b/mac_os/lib/verifiers.sh new file mode 100644 index 0000000..26e6a6d --- /dev/null +++ b/mac_os/lib/verifiers.sh @@ -0,0 +1,202 @@ +#! /usr/bin/env bash + +# Defines verification/validation functions. + +# Label: Verify App Store Applications +# Description: Check for missing App Store applications. +verify_app_store_applications() { + local applications="" + + printf "\n%s\n" "Checking App Store applications..." + applications="$(mas list)" + + while read line; do + if [[ "$line" == "mas install"* ]]; then + application=$(printf "$line" | awk '{print $3}') + verify_listed_application "$application" "${applications[*]}" + fi + done < "$MAC_OS_CONFIG_PATH/bin/install_app_store" + + printf "%s\n" "App Store check complete." +} +export -f verify_app_store_applications + +# Label: Verify Application +# Description: Verify application exists. +# Parameters: $1 (required): File name. +verify_application() { + local file_name="$1" + + if [[ ! -e "$(get_install_path "$file_name")" ]]; then + printf "%s\n" " - Missing: $file_name" + fi +} +export -f verify_application + +# Label: Verify Applications +# Description: Check for missing applications suffixed by "APP_NAME" as defined in settings. +verify_applications() { + local file_names="" + + printf "\n%s\n" "Checking application software..." + + # Only use environment keys that end with "APP_NAME". + 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 + verify_application "${!name}" + done + + printf "%s\n" "Application software check complete." +} +export -f verify_applications + +# Label: Verify Extensions +# Description: Check for missing extensions suffixed by "EXTENSION_PATH" as defined in settings. +verify_extensions() { + local extensions="" + + printf "\n%s\n" "Checking application extensions..." + + # Only use environment keys that end with "EXTENSION_PATH". + 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 "%s\n" "Application extension check complete." +} +export -f verify_extensions + +# Label: Verify Homebrew Casks +# Description: Check for missing Homebrew casks. +verify_homebrew_casks() { + local applications="" + + printf "\nChecking Homebrew casks...\n" + + applications="$(brew list --casks)" + + while read line; do + if [[ "$line" == "brew cask install"* ]]; then + application=$(printf "%s" "$line" | awk '{print $4}') + verify_listed_application "$application" "${applications[*]}" + fi + done < "$MAC_OS_CONFIG_PATH/bin/install_homebrew_casks" + + printf "%s\n" "Homebrew cask check complete." +} +export -f verify_homebrew_casks + +# Label: Verify Homebrew Formulas +# Description: Check for missing Homebrew formulas. +verify_homebrew_formulas() { + local applications="" + + printf "Checking Homebrew formulas...\n" + + applications="$(brew list --formulae)" + + while read line; do + if [[ "$line" == "brew install"* ]]; then + application=$(printf "%s" "$line" | awk '{print $3}') + + # Exception: "gpg" is the binary but is listed as "gnugp". + if [[ "$application" == "gpg" ]]; then + application="gnupg" + fi + + verify_listed_application "$application" "${applications[*]}" + fi + done < "$MAC_OS_CONFIG_PATH/bin/install_homebrew_formulas" + + printf "%s\n" "Homebrew formula check complete." +} +export -f verify_homebrew_formulas + +# Label: Verify Listed Application +# Description: Verify listed application exists. +# Parameters: $1 (required): Current application, $2 (required): Application list. +verify_listed_application() { + local application="$1" + local applications="$2" + + if [[ "${applications[*]}" != *"$application"* ]]; then + printf "%s\n" " - Missing: $application" + fi +} +export -f verify_listed_application + +# Label: Verify Path +# Description: Verify path exists. +# Parameters: $1 (required): Path. +verify_path() { + local path="$1" + + if [[ ! -e "$path" ]]; then + printf "%s\n" " - Missing: $path" + fi +} +export -f verify_path + +# Label: Verify Node Packages +# Description: Check for missing Node packages. +verify_node_packages() { + printf "\n%s\n" "Checking Node packages..." + + while read line; do + if [[ "$line" == "npm "* ]]; then + package=$(printf "$line" | awk '{print $4}') + packages=($(npm list --global --depth=0 | grep "$package")) + + verify_listed_application "$package" "${packages[*]}" + fi + done < "$MAC_OS_CONFIG_PATH/bin/install_node_packages" + + printf "%s\n" "Node packages check complete." +} +export -f verify_node_packages + +# Label: Verify Ruby Gems +# Description: Check for missing Ruby gems. +verify_ruby_gems() { + local gems="" + + printf "\n%s\n" "Checking Ruby gems..." + + gems="$(gem list --no-versions)" + + while read line; do + if [[ "$line" == "gem install"* ]]; then + gem=$(printf "%s" "$line" | awk '{print $3}') + verify_listed_application "$gem" "${gems[*]}" + fi + done < "$MAC_OS_CONFIG_PATH/bin/install_ruby_gems" + + printf "%s\n" "Ruby gems check complete." +} +export -f verify_ruby_gems + +# Label: Verify Rust Crates +# Description: Check for missing Rust crates. +verify_rust_crates() { + local crates="" + + printf "\n%s\n" "Checking Rust crates..." + + crates="$(ls -A1 $HOME/.cargo/bin)" + + while read line; do + if [[ "$line" == "cargo install"* ]]; then + crate=$(printf "%s" "$line" | awk '{print $3}') + verify_listed_application "$crate" "${crates[*]}" + fi + done < "$MAC_OS_CONFIG_PATH/bin/install_rust_crates" + + printf "%s\n" "Rust crates check complete." +} +export -f verify_rust_crates