diff --git a/README.md b/README.md index a0b5c967..910c1390 100644 --- a/README.md +++ b/README.md @@ -78,12 +78,14 @@ The following is a minimal setup command to install chezmoi and my dotfiles from ## โš™๏ธ Install & Setup Application Individually This repository provides for the installation and setup of each application individually. -The desired application can be installed as follows (e.g., docker installation on MacOS): +The desired application can be installed as follows (e.g., Apple Container and Socktainer installation on MacOS): ```shell -bash install/macos/common/docker.sh +bash install/macos/common/container.sh ``` +On macOS, `DOCKER_HOST` defaults to `unix://${HOME}/.socktainer/container.sock` so Docker-compatible clients can talk to Socktainer. + Each installation script can be found under the [`./install`](https://github.com/shunk031/dotfiles/tree/main/install) directory. ## ๐Ÿ› ๏ธ Update & Test ๐Ÿงช @@ -96,10 +98,10 @@ To verify that the updated scripts work correctly, run the scripts on the actual The setup scripts are stored as shellscripts in an appropriate location under the [`./install`](https://github.com/shunk031/dotfiles/tree/main/install) directory. After verifying that the shellscript works, store the [chezmoi template](https://www.chezmoi.io/user-guide/templating/)-based file, which is based on the shellscript, in an appropriate location under the [`./home/.chezmoiscripts`](https://github.com/shunk031/dotfiles/tree/main/home/.chezmoiscripts) directory. -Below is the correspondence between shellscript and template for docker installation on MacOS. +Below is the correspondence between shellscript and template for Apple Container and Socktainer installation on MacOS. -- The shellscript for docker: [`install/macos/common/docker.sh`](https://github.com/shunk031/dotfiles/blob/main/install/macos/common/docker.sh) -- The chezmoi template for docker: [`home/.chezmoiscripts/macos/run_once_10-install-docker.sh.tmpl`](https://github.com/shunk031/dotfiles/blob/main/home/.chezmoiscripts/macos/run_once_10-install-docker.sh.tmpl) +- The shellscript for Apple Container and Socktainer: [`install/macos/common/container.sh`](https://github.com/shunk031/dotfiles/blob/main/install/macos/common/container.sh) +- The chezmoi template for Apple Container and Socktainer: [`home/.chezmoiscripts/macos/run_once_12-install-container.sh.tmpl`](https://github.com/shunk031/dotfiles/blob/main/home/.chezmoiscripts/macos/run_once_12-install-container.sh.tmpl) ### ๐Ÿ’พ Test on the Local Machine diff --git a/home/.chezmoiscripts/macos/run_once_10-install-docker.sh.tmpl b/home/.chezmoiscripts/macos/run_once_10-remove-docker-desktop.sh.tmpl similarity index 100% rename from home/.chezmoiscripts/macos/run_once_10-install-docker.sh.tmpl rename to home/.chezmoiscripts/macos/run_once_10-remove-docker-desktop.sh.tmpl diff --git a/home/.chezmoiscripts/macos/run_once_12-install-container.sh.tmpl b/home/.chezmoiscripts/macos/run_once_12-install-container.sh.tmpl new file mode 100644 index 00000000..74af476b --- /dev/null +++ b/home/.chezmoiscripts/macos/run_once_12-install-container.sh.tmpl @@ -0,0 +1,3 @@ +{{ if eq .chezmoi.os "darwin" -}} +{{ include "../install/macos/common/container.sh" }} +{{ end -}} diff --git a/home/dot_config/exact_sheldon/plugin_sources/client/macos.toml b/home/dot_config/exact_sheldon/plugin_sources/client/macos.toml index 44b40c1c..6e5f827b 100644 --- a/home/dot_config/exact_sheldon/plugin_sources/client/macos.toml +++ b/home/dot_config/exact_sheldon/plugin_sources/client/macos.toml @@ -31,3 +31,13 @@ function _macos_browser() { } zsh-defer _macos_browser ''' + +[plugins.socktainer-docker-host] +inline = ''' +function _socktainer_docker_host() { + if [[ -z "$DOCKER_HOST" && "$OSTYPE" == darwin* ]]; then + export DOCKER_HOST="unix://${HOME}/.socktainer/container.sock" + fi +} +zsh-defer _socktainer_docker_host +''' diff --git a/install/macos/common/container.sh b/install/macos/common/container.sh new file mode 100644 index 00000000..3b74ba38 --- /dev/null +++ b/install/macos/common/container.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +# @file install/macos/common/container.sh +# @brief Install Apple Container and Socktainer on macOS. +# @description +# Installs or removes the Apple Container and Socktainer Homebrew formulae +# and starts their services on supported non-CI macOS hosts. + +set -Eeuo pipefail + +if [ "${DOTFILES_DEBUG:-}" ]; then + set -x +fi + +# +# @description Return the host macOS major version. +# +function macos_major_version() { + sw_vers -productVersion | cut -d "." -f 1 +} + +# +# @description Check whether Apple Container and Socktainer can run on this host. +# +function is_container_stack_supported_macos() { + local major_version + major_version="$(macos_major_version)" + + [ "$(uname -m)" = "arm64" ] && [ "${major_version}" -ge 26 ] +} + +# +# @description Remove the legacy Apple Container cask when it is installed. +# +function uninstall_legacy_container_cask() { + if brew list --cask container &> /dev/null; then + brew uninstall --cask container --force + fi +} + +# +# @description Check whether a Homebrew formula is installed. +# @arg $1 string Homebrew formula token. +# +function is_formula_installed() { + local formula_token="$1" + + brew list --formula "${formula_token}" &> /dev/null +} + +# +# @description Install Apple Container and Socktainer Homebrew formulae. +# +function install_container() { + if "${CI:-false}"; then + echo "Skipping Apple Container and Socktainer install in CI." + return + fi + + if ! is_container_stack_supported_macos; then + echo "Skipping Apple Container and Socktainer install because Apple Silicon macOS 26 or newer is required." + return + fi + + uninstall_legacy_container_cask + brew tap socktainer/tap + brew install container socktainer/tap/socktainer +} + +# +# @description Start the Apple Container and Socktainer services on supported non-CI hosts. +# +function start_container_system() { + if "${CI:-false}" || ! is_container_stack_supported_macos; then + return + fi + + brew services start container + brew services start socktainer +} + +# +# @description Remove Apple Container and Socktainer Homebrew packages. +# +function uninstall_container() { + if "${CI:-false}" || ! is_container_stack_supported_macos; then + return + fi + + local formula_token + + brew services stop socktainer || true + brew services stop container || true + + for formula_token in socktainer container; do + if is_formula_installed "${formula_token}"; then + brew uninstall "${formula_token}" --force + fi + done + + uninstall_legacy_container_cask +} + +# +# @description Run the Apple Container and Socktainer installation flow. +# +function main() { + install_container + start_container_system +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main +fi diff --git a/install/macos/common/docker.sh b/install/macos/common/docker.sh index a207965a..c85e4cf0 100644 --- a/install/macos/common/docker.sh +++ b/install/macos/common/docker.sh @@ -1,9 +1,10 @@ #!/usr/bin/env bash # @file install/macos/common/docker.sh -# @brief Install Docker Desktop on macOS. +# @brief Remove Docker Desktop from macOS. # @description -# Installs or removes the Docker Desktop cask through Homebrew. +# Stops Docker Desktop, removes the Homebrew cask when present, and clears +# stale Docker Desktop helper links. set -Eeuo pipefail @@ -11,25 +12,91 @@ if [ "${DOTFILES_DEBUG:-}" ]; then set -x fi +readonly DOCKER_DESKTOP_CASK_TOKENS=( + docker-desktop + docker +) +readonly DOCKER_DESKTOP_HELPER_LINKS=( + /usr/local/bin/docker + /usr/local/bin/docker-credential-desktop + /usr/local/bin/docker-credential-ecr-login + /usr/local/bin/docker-credential-osxkeychain + /usr/local/bin/hub-tool + /usr/local/bin/kubectl.docker + /usr/local/cli-plugins/docker-compose +) + +# +# @description Check whether a Homebrew cask is installed. +# @arg $1 string Homebrew cask token. +# +function is_cask_installed() { + local cask_token="$1" + + brew list --cask "${cask_token}" &> /dev/null +} + +# +# @description Stop Docker Desktop if the application helper is available. +# +function stop_docker_desktop() { + if [ -x "/Applications/Docker.app/Contents/MacOS/com.docker.backend" ]; then + "/Applications/Docker.app/Contents/MacOS/com.docker.backend" -quit || true + fi + + osascript -e 'quit app "Docker"' &> /dev/null || true +} + +# +# @description Remove Docker Desktop Homebrew casks installed under known tokens. +# +function uninstall_docker_desktop() { + local cask_token + + for cask_token in "${DOCKER_DESKTOP_CASK_TOKENS[@]}"; do + if is_cask_installed "${cask_token}"; then + brew uninstall --cask "${cask_token}" --force + fi + done +} + +# +# @description Remove stale Docker Desktop helper symlinks. +# +function remove_docker_desktop_helper_links() { + local helper_link + + for helper_link in "${DOCKER_DESKTOP_HELPER_LINKS[@]}"; do + if [ -L "${helper_link}" ]; then + rm -f "${helper_link}" + fi + done +} + # -# @description Install the Docker Desktop Homebrew cask. +# @description Remove the Docker Desktop application bundle when it remains. # -function install_docker() { - brew install --cask docker +function remove_docker_desktop_app_bundle() { + if [ -d "/Applications/Docker.app" ]; then + rm -rf "/Applications/Docker.app" + fi } # -# @description Remove the Docker Desktop Homebrew cask. +# @description Remove Docker Desktop from this macOS host. # -function uninstall_docker() { - brew uninstall --cask docker --force +function remove_docker_desktop() { + stop_docker_desktop + uninstall_docker_desktop + remove_docker_desktop_helper_links + remove_docker_desktop_app_bundle } # -# @description Run the Docker Desktop installation flow. +# @description Run the Docker Desktop removal flow. # function main() { - install_docker + remove_docker_desktop } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then diff --git a/tests/install/macos/common/container.bats b/tests/install/macos/common/container.bats new file mode 100644 index 00000000..46c15824 --- /dev/null +++ b/tests/install/macos/common/container.bats @@ -0,0 +1,22 @@ +#!/usr/bin/env bats + +readonly SCRIPT_PATH="./install/macos/common/container.sh" + +function setup() { + source "${SCRIPT_PATH}" +} + +function teardown() { + run uninstall_container +} + +@test "[macos] container and socktainer" { + run env DOTFILES_DEBUG=1 bash "${SCRIPT_PATH}" + + [ "${status}" -eq 0 ] + + if ! "${CI:-false}" && is_container_stack_supported_macos; then + [ -x "$(command -v container)" ] + [ -x "$(command -v socktainer)" ] + fi +} diff --git a/tests/install/macos/common/docker.bats b/tests/install/macos/common/docker.bats index 3b46567b..81abacea 100644 --- a/tests/install/macos/common/docker.bats +++ b/tests/install/macos/common/docker.bats @@ -6,13 +6,8 @@ function setup() { source "${SCRIPT_PATH}" } -function teardown() { - run uninstall_docker -} - -@test "[macos] docker" { +@test "[macos] remove docker desktop" { DOTFILES_DEBUG=1 bash "${SCRIPT_PATH}" - [ -x "$(command -v docker)" ] - # [ -x "$(command -v docker-compose)" ] + [ ! -d "/Applications/Docker.app" ] }