Skip to content

Commit

Permalink
feat: add support for apple silicon
Browse files Browse the repository at this point in the history
* update prompt-install to detect and handle prompt installation for
  apple silicon (arm64e) architecture
* install homebrew used to manage rosetta 2 installations (x86_64) in
  /usr/local
* install homebrew used to manage native installations (arm64e) in
  /opt/homebrew
* introduce `brew-intel` and `brew-arm` aliases to enable management of
  both installations regardless of the architecture of the current shell
* update `use-shell` to detect arm64e native shells
* fix an issue where the PATH variable is reordered by invalid
  path_helper invocations
* normalise and fix areas where the PATH variable is modified
* normalise eval invocations (such as completions)
* remove completion check for gulp and grunt, which are largely obsolete
* fix an issue where it was assumed that gpg was available
* add end-to-end tests for Windows 2019 under WSL 1
* add end-to-end tests for all validated linux distributions
  - create a non-root user
  - add user to sudoers
  - install gosu
  - use gosu to invoke installs and tests under non-root user
* update VERSION logic in prompt-install / update to support the `next`
  branch and any future branches
  - this should be improved in the future to correctly recommend updates
    based on semver tags (or the branch/channel used to install prompt)

Closes: automotiveMastermind#66, automotiveMastermind#69

NOTE:

Homebrew, which we rely heavily on, does not yet officially support
apple silicon. In addition, very few formulae include bottles (pre-compiled)
binaries for the SoC. As a result, most formulae must be compiled from
source. Many formulae do not yet successfully compile for apple silicon
and so we must gracefully fallback to their x86_64 counterparts.

The following methodology is used to support prompt on apple silicon
macs:

1. install homebrew instance for intel/rosetta (x86_64) under /usr/local
2. detect apple silicon (arm64e) and install homebrew instance in
  /opt/homebrew
3. attempt to install all tools included with prompt natively in
  /opt/homebrew
4. install all tools included with prompt under rosetta in /usr/local
5. ensure /opt/homebrew is prioritised in the PATH variable

The above methodology will enable native tools if and when they are
available and working with apple silicon. Tools that are not available
and/or fail will fallback to the rosetta versions. This enables a
seamless experience for apple silicon users.
  • Loading branch information
dmccaffery committed Nov 26, 2020
1 parent b420dd9 commit 939cff1
Show file tree
Hide file tree
Showing 19 changed files with 238 additions and 79 deletions.
128 changes: 100 additions & 28 deletions .github/workflows/end-to-end.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,31 +41,103 @@ jobs:
- name: run-tests
run: ./test.sh

# TODO: add this after figuring out a consistent way to create a non-root user without having to create specialised
# dockerfiles for each container type (linuxbrew refuses to install with the root user)
# docker:
# name: "end-to-end-${{ matrix.container }}"
# runs-on: ubuntu-20.04
# container: ${{ matrix.container }}
# needs: cancel
# strategy:
# matrix:
# container:
# - centos:7
# - centos:8
# - debian:9
# - debian:10
# - fedora:32
# - fedora:33
# - linuxmintd/mint18-amd64
# - linuxmintd/mint19-amd64
# - linuxmintd/mint20-amd64
# steps:
# - name: checkout
# uses: actions/checkout@v2
# - name: install-bash
# run: ./install.sh bash | tee log.txt && bash -lc 'cat $AM_PROMPT/.sha'
# - name: install-zsh
# run: ./install.sh zsh | tee log.txt && zsh -lc 'cat $AM_PROMPT/.sha'
# - name: run-tests
# run: ./test.sh
win:
name: "end-to-end-${{ matrix.os }}"
runs-on: ${{ matrix.os }}
needs: cancel
strategy:
matrix:
os:
- windows-2019
defaults:
run:
shell: wsl-bash {0}
steps:
- name: checkout
uses: actions/checkout@v2
- name: setup-wsl
uses: vampire/setup-wsl@v1
- name: install-bash
run: ./install.sh bash | tee log.txt && bash -lc 'cat $AM_PROMPT/.sha'
- name: install-zsh
run: ./install.sh zsh | tee log.txt && zsh -lc 'cat $AM_PROMPT/.sha'
- name: run-tests
run: ./test.sh

docker-apt:
name: "end-to-end-${{ matrix.container }}"
runs-on: ubuntu-20.04
container: ${{ matrix.container }}
needs: cancel
strategy:
matrix:
container:
- debian:9
- debian:10
- linuxmintd/mint18-amd64
- linuxmintd/mint19-amd64
- linuxmintd/mint20-amd64
steps:
- name: create-user
run: |-
apt-get update && apt-get install -y gosu sudo
gosu nobody true
useradd --uid 1000 --user-group --system --create-home --no-log-init --groups tty --shell /bin/bash build
echo 'build ALL=(ALL) NOPASSWD:ALL' >>/etc/sudoers
chown -R build:build /home/build
chmod u=rwx,g=rx,o= /home/build
- name: checkout
uses: actions/checkout@v2
- name: install-bash
run: |-
gosu build ./install.sh bash | tee log.txt
gosu build bash -lc 'cat $AM_PROMPT/.sha'
- name: install-zsh
run: |-
gosu build ./install.sh zsh | tee log.txt
gosu build zsh -lc 'cat $AM_PROMPT/.sha'
- name: run-tests
run: gosu build ./test.sh

docker-yum:
name: "end-to-end-${{ matrix.container }}"
runs-on: ubuntu-20.04
container: ${{ matrix.container }}
needs: cancel
strategy:
matrix:
container:
- centos:7
- centos:8
- fedora:32
- fedora:33
steps:
- name: create-user
run: |-
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4
curl -o /usr/local/bin/gosu -SL "https://github.com/tianon/gosu/releases/download/1.11/gosu-amd64"
curl -o /usr/local/bin/gosu.asc -SL "https://github.com/tianon/gosu/releases/download/1.11/gosu-amd64.asc"
gpg --verify /usr/local/bin/gosu.asc
chmod +x /usr/local/bin/gosu
gosu nobody true
yum update -y
yum install -y sudo
useradd --uid 1000 --user-group --system --create-home --no-log-init \--groups tty --shell /bin/bash build
echo 'build ALL=(ALL) NOPASSWD:ALL' >>/etc/sudoers
chown -R build:build /home/build
chmod u=rwx,g=rx,o= /home/build
- name: checkout
uses: actions/checkout@v2
- name: install-bash
run: |-
gosu build ./install.sh bash | tee log.txt
gosu build bash -lc 'cat $AM_PROMPT/.sha'
- name: install-zsh
run: |-
gosu build ./install.sh zsh | tee log.txt
gosu build zsh -lc 'cat $AM_PROMPT/.sha'
- name: run-tests
run: gosu build ./test.sh
8 changes: 7 additions & 1 deletion .releaserc.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
plugins:
- "@semantic-release/commit-analyzer"
- "@semantic-release/release-notes-generator"
- "@semantic-release/github"
- "@semantic-release/changelog"
- path: "@semantic-release/exec"
prepareCmd: echo ${nextRelease.gitHead} > VERSION
- path: "@semantic-release/github"
assets:
- CHANGELOG.md
- VERSION

branches:
- master
Expand Down
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
b420dd99ffaa647cd1d8e97ad4b1e6914b03a811
5 changes: 3 additions & 2 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
set -e

CLR_SUCCESS="\033[1;32m" # BRIGHT GREEN
CLR_WARN="\033[1;33m" # BRIGHT YELLOW
CLR_CLEAR="\033[0m" # DEFAULT COLOR
ECHO='echo'

Expand All @@ -12,6 +13,7 @@ AM_PROMPT="$AM_HOME/prompt"
# when not outputing to a tty, add spacing instead of colors
if [ ! -t 1 ]; then
CLR_SUCCESS="\n------------------------------------------------------------------------------------------------------------------------\n"
CLR_WARN=$CLR_SUCCESS
CLR_CLEAR=$CLR_SUCCESS
ECHO='printf'
fi
Expand Down Expand Up @@ -116,8 +118,7 @@ __am_prompt_install() {
local CURL_OPT="$CURL_OPT -H 'Authorization: token $GITHUB_TOKEN'"
fi

local SHA_URI="https://api.github.com/repos/automotivemastermind/prompt/commits/master"
local PROMPT_SHA=$(curl $CURL_OPT $SHA_URI | grep sha | head -n 1 | sed 's#.*\:.*"\(.*\).*",#\1#')
local PROMPT_SHA=$(cat VERSION)
local PROMPT_SHA_PATH=$HOME/.am/prompt/.sha
local PROMPT_CHANGELOG_URI="https://github.com/automotivemastermind/prompt/blob/$PROMPT_SHA/CHANGELOG.md"

Expand Down
2 changes: 1 addition & 1 deletion src/bash/scripts/eval/set-gcloud-path
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ __am_prompt_set_gcloud_path() {
fi

# add gcloud to the path
export PATH="$HOME/.gcloud/bin":$PATH
export PATH="$HOME/.gcloud/bin:$PATH"
fi

local GCLOUD_CMD=$(command -v gcloud)
Expand Down
87 changes: 75 additions & 12 deletions src/sh/install/darwin.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env sh
#!/usr/bin/env bash

set -e

Expand All @@ -8,14 +8,39 @@ __am_prompt_ensure_rosetta() {
if [ "$(uname -m)" = "x86_64" ]; then

# install homebrew using defaults
__am_prompt_install_darwin
__am_prompt_install_intel

# move on immediately
return $?
fi

$ECHO ${CLR_WARN}
$ECHO "##############################################################################"
$ECHO
$ECHO " DETECTED APPLE SILICON "
$ECHO
$ECHO " Apple Silicon is not yet officially supported by homebrew. We will install "
$ECHO " homebrew in the following locations: "
$ECHO " - /opt/homebrew : arm64e native (DEFAULT) "
$ECHO " - /usr/local : x86_64 emulation via Rosetta 2 "
$ECHO
$ECHO " The Apple Silicon (arm64e) version will be the default as it will be placed "
$ECHO " on the PATH variable before the native version. This is compatible with more"
$ECHO " formulae at this time. To use the intel (x86_64) version, you can use the "
$ECHO " \`brew-intel\` alias. For example: "
$ECHO
$ECHO " brew-intel install git "
$ECHO
$ECHO " NOTE: Installation will take significantly longer as most formulae will be "
$ECHO " built from source. NOT ALL FORMULA ARE GAURANTEED TO WORK. INCLUDING "
$ECHO " THOSE WE ATTEMPT TO INSTALL BY DEFAULT WITHIN PROMPT! We do this to "
$ECHO " enable these formulae via install as soon as they start working. "
$ECHO
$ECHO "##############################################################################"
$ECHO ${CLR_CLEAR}

# install rosetta 2
softwareupdate --install-rosetta --agree-to-license
softwareupdate --install-rosetta --agree-to-license 2>/dev/null

# create the homebrew directory
if [ ! -d "/opt/homebrew" ]; then
Expand All @@ -27,24 +52,62 @@ __am_prompt_ensure_rosetta() {
sudo chown /opt/homebrew $(whoami):staff
fi

# install homebrew for apple silicon
__am_prompt_install_arm64

# install homebrew in rosetta
__am_prompt_install_darwin x86_64 /usr/local
__am_prompt_install_rosetta
}

__am_prompt_install_intel() {
$ECHO ${CLR_SUCCESS}
$ECHO "##############################################################################"
$ECHO "INSTALLING VIA HOMEBREW FOR INTEL MACS"
$ECHO "##############################################################################"
$ECHO ${CLR_CLEAR}

if ! type "${BREW_CMD}" 1>/dev/null 2>&1; then
$ECHO "${CLR_SUCCESS}installing homebrew...${CLR_CLEAR}"
/bin/bash -c "CI=true $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh) && /usr/local/bin/brew config"
fi

# install homebrew for apple silicon
__am_prompt_ensure_rosetta $(uname -m) /opt/homebrew
__am_prompt_install_darwin "/usr/local/bin/brew"
}

__am_prompt_install_darwin() {
local BREWS='openssl git go nvm python'
local ARCH=${1:-"x86-64"}
local HOMEBREW_PREFIX="${2:-/usr/local}"
local BREW_CMD="${HOMEBREW_PREFIX}/bin/brew"
__am_prompt_install_rosetta() {
$ECHO ${CLR_SUCCESS}
$ECHO "##############################################################################"
$ECHO "INSTALLING VIA HOMEBREW FOR ROSETTA 2 (x86_64)"
$ECHO "##############################################################################"
$ECHO ${CLR_CLEAR}

if ! type "${BREW_CMD}" 1>/dev/null 2>&1; then
$ECHO "${CLR_SUCCESS}installing homebrew...${CLR_CLEAR}"
arch -x86_64 /bin/bash -c "HOMEBREW_PREFIX=/usr/local CI=true $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh) && /usr/local/bin/brew config"
fi

__am_prompt_install_darwin "arch -x86_64 /usr/local/bin/brew"
}

__am_prompt_install_arm64() {
$ECHO ${CLR_SUCCESS}
$ECHO "##############################################################################"
$ECHO "INSTALLING VIA HOMEBREW FOR APPLE SILICON (arm64e)"
$ECHO "##############################################################################"
$ECHO ${CLR_CLEAR}

if ! type "${BREW_CMD}" 1>/dev/null 2>&1; then
$ECHO "${CLR_SUCCESS}installing homebrew...${CLR_CLEAR}"
arch -${ARCH} /bin/bash -c "HOMEBREW_PREFIX=${HOMEBREW_PREFIX} CI=true $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh) && ${BREW_CMD} config"
arch -x86_64 /bin/bash -c "HOMEBREW_PREFIX=/opt/homebrew CI=true $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh) && /opt/homebrew/bin/brew config"
fi

__am_prompt_install_darwin "arch -arm64e /opt/homebrew/bin/brew"
}

__am_prompt_install_darwin() {
local BREWS='openssl git go nvm python gpg pinentry-mac'
local BREW_CMD=${1:-"/usr/local/bin/brew"}

$ECHO "${CLR_SUCCESS}updating homebrew...${CLR_CLEAR}"
${BREW_CMD} update

Expand Down
2 changes: 1 addition & 1 deletion src/sh/install/linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

__am_prompt_install_linux() {

local BREWS='gcc git'
local BREWS='gcc git gpg'

if ! type brew 1>/dev/null 2>&1; then
$ECHO "${CLR_SUCCESS}installing homebrew...${CLR_CLEAR}"
Expand Down
9 changes: 8 additions & 1 deletion src/sh/profile
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ fi

export HISTCONTROL=ignoredubs

# detect the apple silicon brew path
if [ -f /opt/homebrew/bin/brew ]; then

# force update the path
export PATH="/opt/homebrew/bin:$PATH"
fi

if type brew 1>/dev/null 2>&1; then
export LOCAL_PREFIX=$(brew --prefix)
elif [ -d $LOCALAPPDATA/git ]; then
Expand All @@ -93,7 +100,7 @@ fi
# test for prompt bin
if [ -d "$AM_PROMPT/user/bin" ]; then
# append prompt to path
export PATH="$PATH:$AM_PROMPT/user/bin"
export PATH="$AM_PROMPT/user/bin:$PATH"
fi

# evaluate eval scripts
Expand Down
4 changes: 2 additions & 2 deletions src/sh/scripts/add-var
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ __am_prompt_add_var() {
local VARIABLE="$1"=\"$2\"
local VAR_PATH="$AM_PROMPT/user/variables.sh"

echo 'Adding varable: $VARIABLE'
echo "add-var: adding varable: $VARIABLE"
echo >> "$VAR_PATH"
echo "$VARIABLE" >> "$VAR_PATH"
. "$VAR_PATH"
source "$VAR_PATH"
}

__am_prompt_add_var $@
5 changes: 0 additions & 5 deletions src/sh/scripts/darwin/eval/pandoc-path

This file was deleted.

11 changes: 11 additions & 0 deletions src/sh/scripts/darwin/eval/set-brew-path
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#! /usr/bin/env sh

# -- alias for homebrew versions

if [ -f /usr/local/bin/brew ]; then
alias brew-intel='arch -x86_64 /usr/local/bin/brew'
fi

if [ -f /opt/homebrew/bin/brew ]; then
alias brew-arm='arch -arm64e /opt/homebrew/bin/brew'
fi
2 changes: 1 addition & 1 deletion src/sh/scripts/darwin/install-pandoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ __am_prompt_pandoc_install() {
brew install pandoc librsvg
brew tap homebrew/cask
brew cask install basictex
eval "$(/usr/libexec/path_helper)"
. <(/usr/libexec/path_helper -s)

echo
echo "You will be asked for admin credentials to install tex plugins"
Expand Down
4 changes: 2 additions & 2 deletions src/sh/scripts/eval/set-brew-path
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/usr/bin/env sh

if [ -f "$HOME/.linuxbrew/bin/brew" ]; then
eval $("$HOME/.linuxbrew/bin/brew" shellenv)
. <("$HOME/.linuxbrew/bin/brew" shellenv)
fi

if [ -f /home/linuxbrew/.linuxbrew/bin/brew ]; then
eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
. <(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
fi
Loading

0 comments on commit 939cff1

Please sign in to comment.