setup update complete

This commit is contained in:
2026-09-21 06:48:49 -06:00
parent b67a76d0da
commit 7b2b01e022
38 changed files with 1376 additions and 848 deletions
+64
View File
@@ -2,6 +2,51 @@
version: 2.1
jobs:
test-setup:
parameters:
image:
type: string
# alpine has no supported installer, so setup can only *verify*
# dependencies there; unresolved ones are expected, not a failure
strict:
type: string
default: '--strict'
docker:
- image: << parameters.image >>
steps:
- run:
name: install test prerequisites
command: |
if command -v pacman >/dev/null 2>&1
then pacman -Syu --noconfirm --needed git zsh sudo openssh tar gzip
elif command -v dnf >/dev/null 2>&1
then dnf install -y git zsh sudo openssh-clients tar gzip
elif command -v apt-get >/dev/null 2>&1
then apt-get update && apt-get install -y git zsh sudo openssh-client ca-certificates tar gzip
elif command -v apk >/dev/null 2>&1
then apk add --no-cache git zsh sudo openssh-client shadow tar
fi
- checkout
- run:
name: run setup as an unprivileged user
command: |
useradd -m tester 2>/dev/null || adduser -D tester
echo 'tester ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/tester
TESTER_HOME="$(getent passwd tester | cut -d: -f6)"
cp -a . "${TESTER_HOME}/.wryn"
chown -R tester "${TESTER_HOME}/.wryn"
# a LOGIN shell, so /etc/profile contributes to PATH exactly as
# it would in a real session (debian puts /usr/games there)
su - tester -c "cd ~/.wryn && ./setup/run \
--only packages \
--min \
--ci \
<< parameters.strict >> \
--no-logfile \
"
deploy:
docker:
- image: docker:git
@@ -16,7 +61,26 @@ workflows:
version: 2
main:
jobs:
- test-setup:
name: test-arch
image: archlinux:base-devel
- test-setup:
name: test-fedora
image: fedora:latest
- test-setup:
name: test-debian
image: debian:stable
- test-setup:
name: test-alpine
image: alpine:latest
strict: ''
- deploy:
requires:
- test-arch
- test-fedora
- test-debian
- test-alpine
context:
- yage-docker
filters:
+20 -10
View File
@@ -13,15 +13,13 @@ RUN : \
git \
grep \
iproute2 \
jo \
jq \
ncurses \
ncurses-terminfo \
nodejs \
npm \
openssh \
pciutils \
postgresql \
py3-virtualenv \
py3-nodeenv \
postgresql-client \
redis \
ripgrep \
sed \
@@ -34,25 +32,37 @@ RUN : \
&& apk add --update --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community/ \
helm \
kubectl \
&& apk add --update --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing/ \
&& mkdir -p /root/.wryn \
;
# alpine's newest yq (4.25) predates the '-r' flag scwrypts relies on, so
# take it from upstream releases instead
RUN : \
&& YQ_ARCH="$(uname -m)" \
&& case "${YQ_ARCH}" in \
( x86_64 ) YQ_ARCH=amd64 ;; \
( aarch64 ) YQ_ARCH=arm64 ;; \
esac \
&& wget -qO /usr/local/bin/yq \
"https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${YQ_ARCH}" \
&& chmod +x /usr/local/bin/yq \
&& yq --version \
;
COPY . /root/.wryn
RUN : \
&& echo "#!/bin/zsh" > /bin/hostnamectl && echo "echo container" > /bin/hostnamectl && chmod +x /bin/hostnamectl \
&& echo "#!/bin/zsh" > /bin/scwrypts && echo '/root/.wryn/zsh/plugins/scwrypts/scwrypts $@' >> /bin/scwrypts && chmod +x /bin/scwrypts \
&& mkdir /root/.wryn/bin/container \
&& mkdir -p /root/.wryn/config/local/container/bin \
&& sed -i 's|/bin/ash|/bin/zsh|g' /etc/passwd \
&& /bin/zsh -c '/root/.wryn/setup/run --no-fluff --ci --force-root' \
&& /bin/zsh -c '/root/.wryn/setup/run --min --ci --force-root --no-logfile --skip config' \
&& echo 'source /root/.wryn/zsh/rc' > /root/.zshrc \
&& echo 'source /root/.wryn/config/dotwryn.env.zsh' > /root/.config/wryn/env.zsh \
&& echo "WELCOME() {}" >> /root/.config/wryn/env.zsh \
&& echo "PS1_INDICATOR_SYMBOL=''" >> /root/.config/wryn/env.zsh \
&& echo "PS1_USER=\"root@\$(ip a | grep inet | grep -v '127\\.0\\.0\.1' | sed 's|/| |g' | awk '{print \$2;}')\"" >> /root/.config/wryn/env.zsh \
&& echo "source /root/.wryn/zsh/plugins/scwrypts/scwrypts.plugin.zsh" >> /root/.config/wryn/env.zsh \
&& rm /root/dotwryn-install.log \
&& scwrypts --name scwrypts/virtualenv/update-all --group scwrypts --type zsh \
;
ENTRYPOINT ["/root/.wryn/.docker/entrypoint"]
View File
-170
View File
@@ -1,170 +0,0 @@
setup.config() {
echo.status 'starting application configuration'
setup.config._.generate-initial-local-config
setup.config._.zsh || return 2
setup.config._.vim || return 3
setup.config._.system || return 4
SCWRYPTS generate i3 config || return 5
echo.success 'finished application configuration'
}
#####################################################################
setup.config._.generate-initial-local-config() {
local local_hostname="$(hostnamectl --static 2>/dev/null || hostname 2>/dev/null | sed 's/\.local$//')"
[ "${local_hostname}" ] || return 0
mkdir -p "${DOTWRYN_PATH}/bin/${local_hostname}"
local env_dir="${DOTWRYN_PATH}/config/scwrypts/environments"
local group
for group in $(find "${env_dir}" -type f -name \*env.yaml | sed -n 's|.*/local\.\([^.]*\)\.env\.yaml|\1|p')
do
[ -f "${env_dir}/local.${local_hostname}.${group}.env.yaml" ] && continue
if [ -f "${env_dir}/local.altaria.${group}.env.yaml" ]
then
cp "${env_dir}/local.altaria.${group}.env.yaml" "${env_dir}/local.${local_hostname}.${group}.env.yaml"
else
cp "${env_dir}/local.${group}.env.yaml" "${env_dir}/local.${local_hostname}.${group}.env.yaml"
fi
done
unset group
local local_override
for local_override in \
i3/local.yaml \
polybar/local.ini \
alacritty/local.toml \
;
do
mkdir -p -- "${DOTWRYN_PATH}/config/${local_hostname}/user/$(dirname -- "${local_override}")"
touch -- "${DOTWRYN_PATH}/config/${local_hostname}/user/${local_override}"
done
unset local_override
}
#####################################################################
setup.config._.zsh() {
setup.config._.env zsh || return 1
setup.config._.rc zsh || return 2
setup.config._.set-default-shell || return 3
}
setup.config._.set-default-shell() {
local default_shell="$(awk -F: -v user="${USER}" '$1 == user {print $NF}' /etc/passwd)"
[[ "${default_shell}" =~ zsh ]] && return 0
[[ "${FORCE_ROOT}" == true ]] && return 0
echo.status 'setting zsh as default shell'
sudo chsh -s "$(which zsh)" "$(whoami)" 2>&1 \
&& echo.success "set zsh as default shell for '${USER}'" \
|| utils.fail 1 'failed to set zsh as default shell' \
;
}
#####################################################################
setup.config._.vim() {
setup.config._.rc vim || return 1
echo.status 'starting vim setup'
SCWRYPTS dotwryn vundle update || return 2
setup.config._.vim.link-superuser-rc
return 0
}
setup.config._.vim.link-superuser-rc() {
sudo [ -f /root/.vimrc ] && return 0
echo "let \$DOTWRYN=\"${DOTWRYN_PATH}\"\nsource \"${DOTWRYN_PATH}/vim/rc.vim\"" \
| sudo tee /root/.vimrc >/dev/null
sudo mkdir -p /root/.vim
sudo ln -s "${HOME}/.vim/bundle" /root/.vim/bundle
}
#####################################################################
setup.config._.system() {
SCWRYPTS dotwryn system setup
}
#####################################################################
setup.config._.env() {
local config_type="${1}"
local default_config="${DOTWRYN_PATH}/${config_type}/env.${config_type}"
local local_config="${HOME}/.config/wryn/env.${config_type}"
[ -f "${local_config}" ] && {
case "${OVERWRITE_EXISTING}" in
( false ) return 0 ;;
( true )
echo.warning "local ${config_type} configuration exists (${local_config})"
utils.yN 'overwrite this configuration?' || return 0
mv "${local_config}" "${local_config}.bak" >/dev/null 2>&1 \
&& echo.status "created backup of local configuration (${local_config}.bak)"
;;
esac
}
echo.status "setting up ${config_type} configuration (${local_config})"
local comment
case "${config_type}" in
( zsh ) comment='#' ;;
esac
{
echo "source ${default_config}"
echo -e "\\n${comment}\n${comment} .wryn configuration overrides\n${comment}\n"
sed "s/^[^${comment}].*/${comment}&/" "${default_config}"
} > "${local_config}" \
&& echo.success "created ${config_type} configuration" \
|| utils.fail 1 "unable to create ${config_type} configuration" \
;
EDITOR=vim VISUAL=vim utils.io.edit "${local_config}"
}
setup.config._.rc() {
local config_type="${1}"
local rc
case "${config_type}" in
( vim )
rc="${XDG_CONFIG_HOME:-${HOME}/.config}/vim/vimrc"
mkdir -p -- "$(dirname -- "${rc}")"
[ -f "${HOME}/.vimrc" ] \
&& cat "${HOME}/.vimrc" >> "${XDG_CONFIG_HOME:-${HOME}/.config}/vim/vimrc" \
&& rm "${HOME}/.vimrc" \
;
;;
( * ) rc="${HOME}/.${config_type}rc" ;;
esac
local source_line
case "${config_type}" in
( zsh ) source_line="source ${DOTWRYN_PATH}/zsh/rc" ;;
( vim ) source_line="source ${DOTWRYN_PATH}/vim/rc.vim" ;;
esac
grep -q "^${source_line}$" "${rc}" \
|| echo "${source_line}" >> "${rc}"
grep -q "^${source_line}$" "${rc}" \
&& echo.success "${config_type}rc is configured correctly" \
|| utils.fail 1 "failed to configure ${config_type}rc" \
;
}
+25
View File
@@ -0,0 +1,25 @@
#####################################################################
for setup_library in "${0:a:h}"/*.zsh
do
case "${setup_library:t}" in
( config.module.zsh ) ;; # self
( * ) source "${setup_library}" ;;
esac
done
unset setup_library
#####################################################################
setup.config() { : \
&& echo.status 'starting application configuration' \
&& : \
&& setup.config.generate-initial-local-config \
&& setup.config.zsh \
&& setup.config.vim \
&& setup.scwrypts dotwryn system setup \
&& setup.scwrypts generate i3 config \
&& : \
&& echo.success 'finished application configuration' \
|| echo.error 'application configuration failed (see above)' \
; }
+36
View File
@@ -0,0 +1,36 @@
#####################################################################
setup.config.env() {
local config_type="${1}"
local default_config="${DOTWRYN_SETUP__DOTWRYN}/${config_type}/env.${config_type}"
local local_config="${HOME}/.config/wryn/env.${config_type}"
if [[ -f "${local_config}" ]]
then
case "${DOTWRYN_SETUP__OVERWRITE_EXISTING}" in
( false ) return 0 ;;
( true )
echo.warning "local ${config_type} configuration exists (${local_config})"
utils.yN 'overwrite this configuration?' || return 0
mv -- "${local_config}" "${local_config}.bak" &>/dev/null \
&& echo.status "created backup of local configuration (${local_config}.bak)"
;;
esac
fi
echo.status "setting up ${config_type} configuration (${local_config})"
local comment='#'
{
echo "source ${default_config}"
echo -e "\\n${comment}\n${comment} .wryn configuration overrides\n${comment}\n"
sed "s/^[^${comment}].*/${comment}&/" "${default_config}"
} > "${local_config}" \
&& echo.success "created ${config_type} configuration" \
|| echo.error "unable to create ${config_type} configuration" \
|| return 1
EDITOR=vim VISUAL=vim utils.io.edit "${local_config}"
}
@@ -0,0 +1,30 @@
#####################################################################
setup.config.generate-initial-local-config() {
local local_hostname="$(hostnamectl --static 2>/dev/null || hostname 2>/dev/null | sed 's/\.local$//')"
[[ "${local_hostname}" ]] || return 0
mkdir -p -- "${DOTWRYN_SETUP__DOTWRYN}/config/local/${local_hostname}/bin"
local env_dir="${DOTWRYN_SETUP__DOTWRYN}/config/scwrypts/environments"
local group
for group in $(find "${env_dir}" -type f -name \*env.yaml | sed -n 's|.*/local\.\([^.]*\)\.env\.yaml|\1|p')
do
[[ -f "${env_dir}/local.${local_hostname}.${group}.env.yaml" ]] && continue
cp -- "${env_dir}/local.${group}.env.yaml" "${env_dir}/local.${local_hostname}.${group}.env.yaml"
done
unset group
local local_override
for local_override in \
i3/local.yaml \
polybar/local.ini \
alacritty/local.toml \
;
do
mkdir -p -- "${DOTWRYN_SETUP__DOTWRYN}/config/local/${local_hostname}/user/$(dirname -- "${local_override}")"
touch -- "${DOTWRYN_SETUP__DOTWRYN}/config/local/${local_hostname}/user/${local_override}"
done
unset local_override
}
+43
View File
@@ -0,0 +1,43 @@
#####################################################################
setup.config.rc() {
local config_type="${1}"
local rc
case "${config_type}" in
( vim )
rc="${XDG_CONFIG_HOME:-${HOME}/.config}/vim/vimrc"
mkdir -p -- "$(dirname -- "${rc}")"
[[ -f "${HOME}/.vimrc" ]] \
&& cat "${HOME}/.vimrc" >> "${rc}" \
&& rm -- "${HOME}/.vimrc" \
;
;;
( * ) rc="${HOME}/.${config_type}rc" ;;
esac
touch -- "${rc}"
local source_line comment
case "${config_type}" in
( zsh )
source_line="source ${DOTWRYN_SETUP__DOTWRYN}/zsh/rc"
comment='#'
;;
( vim )
source_line="source ${DOTWRYN_SETUP__DOTWRYN}/vim/rc.vim"
comment='"'
;;
( * )
echo.error "no rc source-line defined for '${config_type}'"
return 1
;;
esac
setup.filesystem.write-managed-block "${rc}" --comment "${comment}" --tag dotwryn-rc --one-line <<-EOF
${source_line}
EOF
grep -qF -- "${source_line}" "${rc}" \
&& echo.success "${config_type}rc is configured correctly" \
|| echo.error "failed to configure ${config_type}rc" \
|| return 1
}
+28
View File
@@ -0,0 +1,28 @@
#####################################################################
setup.config.vim() { : \
&& echo.status 'configuring vim rc' \
&& setup.config.rc vim \
&& : \
&& echo.status 'starting vim setup' \
&& setup.scwrypts dotwryn vundle update \
&& setup.config.vim._.link-superuser-rc \
; }
setup.config.vim._.link-superuser-rc() {
# already root (a container build) needs no sudo, and a machine
# without sudo at all can't get one
local sudo=sudo
[[ "${EUID}" -eq 0 ]] && sudo=''
[[ -n "${sudo}" ]] && ! command -v sudo &>/dev/null \
&& echo.warning 'sudo unavailable; skipping superuser vimrc' \
&& return 0
${sudo} test -f /root/.vimrc && return 0
echo "let \$DOTWRYN=\"${DOTWRYN_SETUP__DOTWRYN}\"\nsource \"${DOTWRYN_SETUP__DOTWRYN}/vim/rc.vim\"" \
| ${sudo} tee /root/.vimrc >/dev/null
${sudo} mkdir -p -- /root/.vim
${sudo} ln -s -- "${HOME}/.vim/bundle" /root/.vim/bundle
}
+22
View File
@@ -0,0 +1,22 @@
#####################################################################
setup.config.zsh() { : \
&& setup.config.env zsh \
&& setup.config.rc zsh \
&& setup.config._.set-default-shell \
; }
setup.config._.set-default-shell() {
local default_shell="$(awk -F: -v user="${USER}" '$1 == user {print $NF}' /etc/passwd)"
[[ "${default_shell}" =~ zsh ]] && return 0
[[ "${DOTWRYN_SETUP__FORCE_ROOT}" == true ]] && return 0
: \
&& echo.status 'setting zsh as default shell' \
&& sudo chsh -s "$(command -v zsh)" "${USER}" 2>&1 \
&& echo.success "set zsh as default shell for '${USER}'" \
|| echo.error 'failed to set zsh as default shell' \
|| return 1 \
;
}
+35
View File
@@ -0,0 +1,35 @@
#####################################################################
setup.filesystem.ensure-paths() {
local ERRORS=0
local directories=(
"${XDG_CONFIG_HOME:-${HOME}/.config}/wryn"
"${HOME}/.local/bin"
)
local files=(
"${HOME}/.zshrc"
)
echo.status 'ensuring required paths exist'
local directory
for directory in "${directories[@]}"
do
mkdir -p -- "${directory}" || echo.error "failed to create directory '${directory}'"
done
unset directory
local file
for file in "${files[@]}"
do
touch -- "${file}" || echo.error "failed to create file '${file}'"
done
unset file
[[ "${ERRORS}" -eq 0 ]] \
&& echo.success 'required paths ready' \
|| echo.error 'failed to ensure required paths' \
;
return "${ERRORS}"
}
+17
View File
@@ -0,0 +1,17 @@
#####################################################################
for setup_library in "${0:a:h}"/*.zsh
do
case "${setup_library:t}" in
( filesystem.module.zsh ) ;; # self
( * ) source "${setup_library}" ;;
esac
done
unset setup_library
#####################################################################
setup.filesystem() { : \
&& setup.filesystem.ensure-paths \
&& setup.filesystem.git \
; }
+20 -19
View File
@@ -1,41 +1,42 @@
#####################################################################
setup.git() {
setup.filesystem.git() {
local sources_dir="${XDG_DATA_HOME:-${HOME}/.local/share}/project-source-code"
SOURCE_DIR="${DOTWRYN_PATH}" \
SOURCE_DIR="${DOTWRYN_SETUP__DOTWRYN}" \
TARGET_DIR="${sources_dir}/yage/dotwryn" \
REMOTE_UPSTREAMS=(
'git@github.com:wrynegade/dotwryn.git'
'git@yage.io:wrynegade/dotwryn.git'
'git@bitbucket.org:wrynegade/dotwryn.git'
) setup.git._.remotes '.wryn'
) setup.filesystem.git._.remotes '.wryn'
SOURCE_DIR="${DOTWRYN_PATH}/zsh/plugins/code-activator" \
SOURCE_DIR="${DOTWRYN_SETUP__DOTWRYN}/zsh/plugins/code-activator" \
TARGET_DIR="${sources_dir}/zsh/code-activator" \
REMOTE_UPSTREAMS=(
'git@yage.io:zsh/code-activator.git'
'git@github.com:wrynegade/code-activator.git'
) setup.git._.remotes 'zsh-plugins/code-activator'
) setup.filesystem.git._.remotes 'zsh-plugins/code-activator'
SOURCE_DIR="${DOTWRYN_PATH}/zsh/plugins/scwrypts" \
SOURCE_DIR="${DOTWRYN_SETUP__DOTWRYN}/zsh/plugins/scwrypts" \
TARGET_DIR="${sources_dir}/zsh/scwrypts" \
REMOTE_UPSTREAMS=(
'git@yage.io:zsh/scwrypts'
'git@github.com:wrynegade/scwrypts'
) setup.git._.remotes 'zsh-plugins/scwrypts'
) setup.filesystem.git._.remotes 'zsh-plugins/scwrypts'
return 0
}
setup.git._.remotes() {
setup.filesystem.git._.remotes() {
: \
&& [ "${SOURCE_DIR}" ] \
&& [ "${TARGET_DIR}" ] \
&& [[ ${#REMOTE_UPSTREAMS[@]} -gt 0 ]] \
|| return 1
&& [[ "${SOURCE_DIR}" ]] \
&& [[ "${TARGET_DIR}" ]] \
&& [[ "${#REMOTE_UPSTREAMS[@]}" -gt 0 ]] \
|| return 1 \
;
[ "$1" ] && echo.status "updating remotes for '$1'"
[[ "${1}" ]] && echo.status "updating remotes for '${1}'"
git -C "${SOURCE_DIR}" remote rm upstream 2>/dev/null
git -C "${SOURCE_DIR}" remote add upstream "${REMOTE_UPSTREAMS[1]}"
@@ -62,19 +63,19 @@ setup.git._.remotes() {
done
unset remote_upstream
SOURCE_DIR="${SOURCE_DIR}" TARGET_DIR="${TARGET_DIR}" setup.git._.link-to-projects \
SOURCE_DIR="${SOURCE_DIR}" TARGET_DIR="${TARGET_DIR}" setup.filesystem.git._.link-to-projects \
|| echo.warning "failed to link '${TARGET_DIR}'"
return 0
}
setup.git._.link-to-projects() {
[ "${TARGET_DIR}" ] && [ "${SOURCE_DIR}" ] \
setup.filesystem.git._.link-to-projects() {
[[ "${TARGET_DIR}" ]] && [[ "${SOURCE_DIR}" ]] \
|| return 1
{
mkdir -p "${TARGET_DIR}"
rm "${TARGET_DIR}/code"
ln -s "${SOURCE_DIR}" "${TARGET_DIR}/code"
mkdir -p -- "${TARGET_DIR}"
rm -- "${TARGET_DIR}/code"
ln -s -- "${SOURCE_DIR}" "${TARGET_DIR}/code"
} &>/dev/null
}
+101
View File
@@ -0,0 +1,101 @@
#####################################################################
setup.filesystem.write-managed-block() {
# replaces (or appends) a dotwryn-managed block in a file, taking the
# block body from stdin, so that every run refreshes the block rather
# than appending another copy of it
#
# callers should use a "<<-" heredoc: it strips leading TABS (letting
# the body stay indented with its caller) but leaves SPACES alone, so
# indent the block's own nesting with spaces to keep it in the output
#
# "--one-line" trades the begin/end banner for a trailing comment on
# the line itself, which suits a one-line entry like an rc source
local file tag
local comment='#'
local one_line=false
local _s _p
while [[ "${#}" -gt 0 ]]
do
_s=1
case "${1}" in
( --comment ) _s=2
comment="${2}"
;;
( --tag ) _s=2
tag="${2}"
;;
( --one-line )
one_line=true
;;
( * )
case "$((_p+=1))" in
( 1 ) file="${1}" ;;
( * ) echo.error "write-managed-block: unexpected argument '${1}'" ;;
esac
;;
esac
shift "${_s}"
done
unset _s _p
[[ "${file}" ]] \
|| echo.error 'write-managed-block: no file given' \
|| return 1
# a single file can carry several managed blocks, so each one has to be
# named to know which of them it owns
[[ "${tag}" ]] \
|| echo.error "write-managed-block: no --tag given for '${file}'" \
|| return 1
local body="$(cat)"
[[ "${one_line}" == false ]] || [[ "${body}" != *$'\n'* ]] \
|| echo.error "write-managed-block: --one-line needs a single-line body for '${file}'" \
|| return 1
mkdir -p -- "${file:h}"
touch -- "${file}"
local -a lines block
lines=("${(@f)$(<"${file}")}")
[[ -s "${file}" ]] || lines=()
# an existing block is replaced where it sits, so anything the user
# wrote around it keeps its position
local begin_index end_index
case "${one_line}" in
( true )
local marker="${comment} managed by dotwryn setup (${tag})"
block=("${body} ${marker}")
# "(b)" keeps the tag's parentheses literal rather than glob
begin_index="${lines[(i)*${(b)marker}]}"
end_index="${begin_index}"
;;
( false )
local begin="${comment} >>> managed by dotwryn setup (${tag}) >>>"
local end="${comment} <<< managed by dotwryn setup (${tag}) <<<"
block=("${begin}" "${(@f)body}" "${end}")
begin_index="${lines[(ie)${begin}]}"
end_index="${lines[(ie)${end}]}"
[[ "${end_index}" -le "${#lines[@]}" ]] || end_index="${#lines[@]}"
;;
esac
if [[ "${begin_index}" -gt "${#lines[@]}" ]]
then
lines+=("${block[@]}")
else
lines=(
"${lines[@]:0:$((begin_index - 1))}"
"${block[@]}"
"${lines[@]:${end_index}}"
)
fi
print -l -- "${lines[@]}" > "${file}"
}
-17
View File
@@ -1,17 +0,0 @@
base-devel
bat
cmake
cowsay
figlet
fzf
go-yq
htop
jo
jq
lolcat
ncurses
python
ripgrep
scwrypts
tmux
vim
-62
View File
@@ -1,62 +0,0 @@
1password
1password-cli
alacritty
arch-install-scripts
aws-cli-v2
base-devel
bat
cmake
cowsay
feh
figlet
flameshot
fprintd
fzf
go
go-yq
google-chrome
htop
i3-wm
jdk-openjdk
jo
jq
lolcat
lxappearance
mono
ncurses
fastfetch
nodejs
noto-fonts-emoji
npm
ntp
obs-studio
openssh
otf-monaspace
pam-fprint-grosshack
picom
playerctl
polybar
python
python-pip
python-pylint
python-rtmidi
python-virtualenv
rage-encryption
ripgrep
rofi
rustup
scwrypts
sudo
tmux
ttf-nerd-fonts-symbols
unipicker
vi
vim
vim-runtime
which
xclip
xdotool
yamllint
zip
zsh
zsh-completions
-12
View File
@@ -1,12 +0,0 @@
bsdextrautils
build-essential
cmake
cowsay
figlet
fzf
jo
jq
ncurses-dev
ripgrep
tmux
uuid-runtime
-20
View File
@@ -1,20 +0,0 @@
awscli
bsdextrautils
build-essential
cmake
cowsay
default-jdk
figlet
fzf
golang
jo
jq
lolcat
mono-devel
ncurses-dev
nodejs
npm
python3-dev
ripgrep
tmux
uuid-runtime
-15
View File
@@ -1,15 +0,0 @@
bat
cmake
cowsay
figlet
fzf
htop
jo
jq
lolcat
ncurses
python3-devel
ripgrep
tmux
vim-enhanced
yq
-51
View File
@@ -1,51 +0,0 @@
autoconf
automake
bat
binutils
bison
cmake
containerd.io
cowsay
docker-ce
docker-ce-cli
feh
figlet
file
findutils
flex
fzf
gcc
gcc-c++
gettext
gnupg2
go
htop
java-latest-openjdk
jo
jq
libtool
lolcat
mono-complete
ncurses
fastfetch
nodejs
npm
openssh
patch
pkg-config
python3-devel
python3-pip
python3-pylint
python3-virtualenv
redis
ripgrep
rust
sed
sudo
tmux
vi
vim-enhanced
yamllint
yq
zip
zsh
-18
View File
@@ -1,18 +0,0 @@
aws
cmake
cowsay
figlet
fzf
go
java
jo
jq
lolcat
ncurses
node
npm
python3
rg
tmux
xbuild
yq
-14
View File
@@ -1,14 +0,0 @@
bat
cmake
cowsay
figlet
fzf
htop
jo
jq
lolcat
ncurses
python
ripgrep
tmux
vim
-48
View File
@@ -1,48 +0,0 @@
autoconf
automake
bat
binutils
bison
cmake
coreutils
cowsay
fastfetch
feh
figlet
findutils
flex
fzf
gawk
gcc
gettext
go
gpatch
gpg
grep
gsed
htop
iproute2mac
jo
jq
libtool
lolcat
mono
ncurses
node
npm
openjdk
openssh
pkg-config
pylint
python
python-setuptools
redis
ripgrep
rust
tmux
vim
virtualenv
yamllint
yq
zip
zsh
-293
View File
@@ -1,293 +0,0 @@
#####################################################################
setup.os() {
setup.os._.make-required-resources || return 1
[[ "${CI}" == true ]] && { echo.status 'detected CI; skipping os setup'; return 0; }
utils.io.getsudo
local OS_NAME="$(setup.os._.get-os)"
[ ! "${OS_NAME}" ] && utils.abort
setup.os._.install-source-dependencies || return 2
setup.os._.install-managed-dependencies || return 3
}
setup.os._.get-os() {
local OS_NAME="$(lsb_release -is 2>/dev/null | tr '[:upper:]' '[:lower:]')"
[ ! "${OS_NAME}" ] \
&& OS_NAME="$(sed -n 's/^ID=//p' /etc/os-release 2>/dev/null)"
uname -s | grep -q Darwin \
&& OS_NAME=macos
[ ! "${OS_NAME}" ] \
&& echo.warning 'failed to detect operating system' \
&& OS_NAME="$(echo -e "arch\ndebian\nother" | utils.fzf 'select an operating system')" \
;
[[ "${OS_NAME}" =~ ^ubuntu$ ]] && OS_NAME=debian
[[ "${OS_NAME}" =~ ^[Ee]ndeavour[Oo][Ss]$ ]] && OS_NAME=arch
echo "${OS_NAME}"
}
#####################################################################
setup.os._.install-from-source() { # clone + build/install a git-hosted package (AUR PKGBUILD or Makefile)
local target_url="${1}"
local name="${2:-$(echo "${target_url}" | sed 's/.*\///; s/\.git$//')}"
local install_dir="${HOME}/.local/share/source-packages"
mkdir -p "${install_dir}"
if [ -d "${install_dir}/${name}" ]
then
echo.status "updating '${name}'"
( cd "${install_dir}/${name}" && git pull origin "$(git rev-parse --abbrev-ref HEAD)" ) \
&& echo.success "successfully updated '${name}'" \
|| { echo.error "failed to update '${name}'"; return 1; }
else
echo.status "downloading '${name}'"
git clone "${target_url}" "${install_dir}/${name}" \
&& echo.success "successfully downloaded '${name}'" \
|| { echo.error "failed to download '${name}'"; return 2; }
fi
cd "${install_dir}/${name}"
if [ -f ./PKGBUILD ]
then
echo.status "installing '${name}'"
yes | makepkg -si \
&& echo.success "successfully installed '${name}'" \
|| { echo.error "failed to install '${name}'"; return 3; }
elif [ -f ./Makefile ]
then
echo.status "building '${name}'"
make \
&& echo.success "finished building '${name}'" \
|| { echo.error "build failed for '${name}'"; return 4; }
echo.status "installing '${name}'"
utils.io.getsudo
sudo make install \
&& echo.success "successfully installed '${name}'" \
|| { echo.error "failed to install '${name}'"; return 5; }
else
echo.warning 'could not detect supported installation method'
echo.reminder "complete manual installation in the directory below:"
echo.reminder "${install_dir}/${name}"
fi
}
setup.os._.install-source-dependencies() {
case "${OS_NAME}" in
( arch )
command -v yay >/dev/null 2>&1 \
|| setup.os._.install-from-source 'https://aur.archlinux.org/yay.git' 'yay' \
;
;;
( fedora ) ;;
( debian ) ;;
( macos )
xcode-select -p &>/dev/null || { # brew requires the xcode command line tools
echo.status 'installing xcode command line tools'
touch /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
local clt_package="$(softwareupdate -l | grep '\*.*Command Line' | tail -n1 | sed 's/^[^C]*\(C.*\)/\1/')"
sudo softwareupdate -i "${clt_package}" --verbose \
&& echo.success 'successfully installed xcode command line tools' \
|| echo.error 'failed to install xcode command line tools' \
;
rm -f /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
}
command -v brew &>/dev/null || {
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
eval "$(/opt/homebrew/bin/brew shellenv 2>/dev/null || /usr/local/bin/brew shellenv 2>/dev/null)"
}
;;
( * ) ;;
esac
[[ "${OS_NAME}" != macos ]] && [[ "${COMPILE_DMENU}" == true ]] \
&& setup.os._.install-from-source 'https://github.com/tiyn/dmenu' 'patched-dmenu'
return 0
}
#####################################################################
setup.os._.install-managed-dependencies() {
local ERRORS=0
echo.status 'checking os dependencies'
case "${OS_NAME}" in
( arch | fedora | debian | macos )
;;
( * )
OS_NAME='generic'
echo.warning "no automated installer available for '${OS_NAME}'"
;;
esac
local dependencies_file
[[ "${MIN}" == true ]] && [ -f "${DOTWRYN_PATH}/setup/os-dependencies/${OS_NAME}.min.txt" ] \
&& dependencies_file="${DOTWRYN_PATH}/setup/os-dependencies/${OS_NAME}.min.txt" \
|| dependencies_file="${DOTWRYN_PATH}/setup/os-dependencies/${OS_NAME}.txt" \
;
[[ "${CI}" != true ]] && {
echo.status 'updating system, repositories, and mirrors'
"setup.os._.update-repositories.${OS_NAME}"
}
local dependency
for dependency in $(cat "${dependencies_file}")
do
"setup.os._.install-managed.${OS_NAME}" "${dependency}"
done
unset dependency
[[ "${ERRORS}" -ne 0 ]] && {
echo.warning "detected ${ERRORS} errors; double check warnings before proceeding!"
utils.yN 'continue with install?' && return 0 || utils.abort
}
case "${OS_NAME}" in
( macos )
local shellenv_line='eval "$(/opt/homebrew/bin/brew shellenv 2>/dev/null || /usr/local/bin/brew shellenv 2>/dev/null)"'
grep -qF "${shellenv_line}" "${HOME}/.zprofile" 2>/dev/null \
|| echo "${shellenv_line}" >> "${HOME}/.zprofile"
local gnubin_path
for gnubin_path in "$(brew --prefix)"/opt/*/libexec/gnubin(N); do export PATH="${gnubin_path}:${PATH}"; done
unset gnubin_path
local gnubin_line='for P in "$(brew --prefix)"/opt/*/libexec/gnubin(N); do export PATH="${P}:${PATH}"; done'
grep -qF "${gnubin_line}" "${HOME}/.zprofile" 2>/dev/null \
|| echo "${gnubin_line}" >> "${HOME}/.zprofile"
;;
esac
echo.success 'all dependencies satisfied'
return 0
}
##########################################
setup.os._.update-repositories.arch() { yay -Syu; }
setup.os._.install-managed.arch() {
local target="${1}"
[[ "${target}" =~ aws-cli-v2 ]] && {
echo.status "skipping aws-cli-v2 checks since they are bad right now"
return 0
}
yay -Qq 2>/dev/null | grep -q "^${target}$\|^${target}-git$" && {
echo.success "found '${target}'"
} || {
echo.status "installing '${target}'"
yay -Syu --noconfirm "${target}" \
&& echo.success "successfully installed '${target}'" \
|| echo.error "failed to install '${target}'" \
;
}
}
##########################################
setup.os._.update-repositories.debian() { sudo apt-get update && sudo apt-get upgrade; }
setup.os._.install-managed.debian() {
local target="${1}"
echo.status "checking / installing '${target}'"
sudo apt-get install --yes "${target}" \
&& echo.success "'${target}' installed" \
|| echo.error "failed to install ${target}" \
;
}
##########################################
setup.os._.update-repositories.fedora() {
: \
&& sudo dnf update \
&& sudo dnf upgrade \
&& sudo dnf install -y dnf-plugins-core \
&& sudo dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/fedora/docker-ce.repo \
;
}
setup.os._.install-managed.fedora() {
local target="${1}"
echo.status "checking / installing '${target}'"
sudo dnf install -y "${target}" \
&& echo.success "'${target}' installed" \
|| echo.error "failed to install ${target}" \
;
}
##########################################
setup.os._.update-repositories.macos() { brew update && brew upgrade; }
setup.os._.install-managed.macos() {
local target="${1}"
brew list --versions "${target}" &>/dev/null && {
echo.success "found '${target}'"
} || {
echo.status "installing '${target}'"
yes | brew install "${target}" \
&& echo.success "'${target}' installed" \
|| echo.error "failed to install ${target}" \
;
}
}
##########################################
setup.os._.update-repositories.generic() { return 0; }
setup.os._.install-managed.generic() {
local target="${1}"
command -v "${target}" >/dev/null 2>&1 \
|| echo.error "could not find '${target}'; it's up to you to install this one!"
}
#####################################################################
setup.os._.make-required-resources() {
local ERRORS=0
local directories=(
"${XDG_CONFIG_HOME:-${HOME}/.config}/wryn"
"${HOME}/.local/bin"
)
local files=(
"${HOME}/.zshrc"
)
echo.status 'making required system resources'
local directory
for directory in "${directories[@]}"
do
[ ! -d "${directory}" ] && { mkdir -p "${directory}" || echo.error "failed to create directory '${directory}'"; }
done
unset directory
local file
for file in "${files[@]}"
do
[ ! -f "${file}" ] && { touch "${file}" || echo.error "failed to create file '${file}'"; }
done
unset file
[[ "${ERRORS}" -eq 0 ]] \
&& echo.success 'finished creating system resources' \
|| echo.error 'failed to create system resources' \
;
return "${ERRORS}"
}
+154
View File
@@ -0,0 +1,154 @@
#####################################################################
# current profiles:
# --extended (default) -> installs all my favorite tools
# --min -> only installs essentials (deemed by ME)
INSTALLED_BY__all=(--installed-by 'default,min,extended-macos-cask')
INSTALLED_BY__extended=(--installed-by 'default,extended-macos-cask')
INSTALLED_BY__extended_macos_cask=(--installed-by 'extended-macos-cask')
#####################################################################
# base cross-platform tools
dependency_args=("${INSTALLED_BY__all[@]}")
dependency --no-executable binutils
dependency --no-executable coreutils
dependency --no-executable findutils
dependency --no-executable libtool
dependency autoconf --yay base-devel
dependency automake --yay base-devel
dependency awk --yay base-devel --dnf gawk --apt-get gawk --brew gawk
dependency aws --yay aws-cli-v2 --dnf awscli2 --apt-get awscli --brew awscli
dependency bat --no-executable
dependency bison --yay base-devel
dependency cmake
dependency cowsay
dependency figlet
dependency file
dependency flex --yay base-devel
dependency fzf
dependency gcc --yay base-devel --dnf gcc,gcc-c++ --apt-get build-essential
dependency gpg --yay gnupg --dnf gnupg2 --apt-get gnupg --brew gnupg
dependency grep
dependency htop
dependency ip --yay iproute2 --dnf iproute --apt-get iproute2 --brew iproute2mac
dependency jo
dependency jq
dependency lolcat
dependency ncurses --apt-get libncurses-dev --no-executable
dependency ssh --yay openssh --dnf openssh-clients --apt-get openssh-client --brew openssh
dependency patch --brew gpatch
dependency pkg-config --yay base-devel
dependency pylint --yay python-pylint
dependency python3 --yay python --dnf python3-devel --apt-get python3-dev --brew python-setuptools
dependency rg --yay ripgrep --dnf ripgrep --apt-get ripgrep --brew ripgrep
dependency sed --brew gsed
dependency tmux
dependency vim --dnf vim-enhanced
dependency xauth --yay xorg-xauth --dnf xorg-x11-xauth --apt-get xauth
dependency yamllint
dependency yq --yay go-yq --not-on apt-get
dependency zip
dependency zsh
# not packaged for fedora/debian
dependency --only-on yay,brew rage --yay rage-encryption
dependency --only-on yay,brew zsh-completions --no-executable
#####################################################################
# extended cross-platform tools
dependency_args=("${INSTALLED_BY__extended[@]}")
dependency alacritty --brew cask:alacritty
dependency docker --dnf docker-ce,docker-ce-cli,containerd.io --brew colima
dependency go --apt-get golang
dependency java --yay jdk-openjdk --dnf java-latest-openjdk --apt-get default-jdk --brew openjdk
dependency javac --yay jdk-openjdk --dnf java-latest-openjdk --apt-get default-jdk --brew openjdk
dependency mono --dnf mono-complete --apt-get mono-devel
dependency redis-cli --yay redis --dnf valkey,valkey-compat-redis
dependency rustc --yay rustup --dnf rust --apt-get rustc --brew rust
dependency virtualenv --yay python-virtualenv --dnf python3-virtualenv --apt-get python3-virtualenv
# fedora & debian need official repositories added to dnf / apt (not yet supported)
dependency --only-on yay,brew 1password --brew cask:1password
dependency --only-on yay,brew op --yay 1password-cli --brew cask:1password-cli
#####################################################################
# linux
dependency_args=("${INSTALLED_BY__all[@]}" --only-on yay,dnf,apt-get)
dependency noto-fonts-emoji --dnf google-noto-emoji-fonts --apt-get fonts-noto-color-emoji --no-executable
dependency feh
dependency fprintd --no-executable
dependency ntp --dnf chrony --apt-get chrony --no-executable
dependency pip --yay python-pip --dnf python3-pip --apt-get python3-pip
dependency sudo
dependency uuidgen --yay util-linux --dnf util-linux --apt-get uuid-runtime
dependency which
dependency xclip
dependency xdotool
#######################
# linux extended
dependency_args=("${INSTALLED_BY__extended[@]}" --only-on yay,dnf,apt-get)
dependency brave --yay brave-bin
dependency flameshot
dependency google-chrome --yay google-chrome-stable
dependency i3 --yay i3-wm --apt-get i3-wm
dependency lxappearance
dependency node --yay nodejs --dnf nodejs --apt-get nodejs
dependency npm
dependency obs-studio
dependency otf-monaspace --no-executable
dependency picom
dependency playerctl
dependency polybar
dependency rofi
dependency ttf-nerd-fonts-symbols --no-executable
##########################################
# arch linux
dependency_args=("${INSTALLED_BY__all[@]}" --only-on yay)
dependency arch-install-scripts --no-executable
dependency python-rtmidi --no-executable
dependency scwrypts
dependency unipicker
##########################################
# debian linux
dependency_args=("${INSTALLED_BY__all[@]}" --only-on apt-get)
dependency bsdextrautils --no-executable
#####################################################################
# macos
dependency_args=("${INSTALLED_BY__all[@]}" --only-on brew)
#######################
# macos extended cask
dependency_args=("${INSTALLED_BY__extended_macos_cask[@]}" --only-on brew)
dependency brave --brew cask:brave-browser
dependency google-chrome --brew cask:google-chrome
dependency obs-studio --brew cask:obs
dependency xquartz --brew cask:xquartz
#####################################################################
unset \
dependency_args \
INSTALLED_BY__all INSTALLED_BY__extended INSTALLED_BY__extended_macos_cask \
;
+259
View File
@@ -0,0 +1,259 @@
#####################################################################
setup.packages.get-installer() {
# keyed on the package manager rather than the distro, so derivatives
# (manjaro, endeavouros, mint, ...) work without being enumerated; yay
# and brew are detected by their BASE manager since setup bootstraps
# them itself when missing
command -v pacman &>/dev/null && echo yay && return 0
command -v dnf &>/dev/null && echo dnf && return 0
command -v apt-get &>/dev/null && echo apt-get && return 0
command -v brew &>/dev/null && echo brew && return 0
[[ "$(uname -s)" == Darwin ]] && echo brew && return 0
echo generic
}
#####################################################################
typeset -gA DEPENDENCY
DEPENDENCY_NAMES=()
dependency() { # declare a dependency; flags may appear in any order relative to the name; see dependency.manifest.zsh for usage
# dependency_args=(...), when set, is prepended to every call until reassigned
# or unset -- lets a block of dependencies share flags without repeating them
local -a args
(( ${+dependency_args} )) && args=("${dependency_args[@]}" "${@}") || args=("${@}")
local name installed_by yay dnf apt_get brew not_on only_on
local no_executable=false
local _s _p
while [[ "${#args[@]}" -gt 0 ]]
do
_s=1
case "${args[1]}" in
( --installed-by ) installed_by="${args[2]}"; _s=2 ;;
( --yay ) yay="${args[2]}"; _s=2 ;;
( --dnf ) dnf="${args[2]}"; _s=2 ;;
( --apt-get ) apt_get="${args[2]}"; _s=2 ;;
( --brew ) brew="${args[2]}"; _s=2 ;;
( --not-on ) not_on="${args[2]}"; _s=2 ;;
( --only-on ) only_on="${args[2]}"; _s=2 ;;
( --no-executable ) no_executable=true ;;
( -* )
echo.error "dependency: unknown option '${args[1]}'"
;;
( * )
case "$((_p+=1))" in
( 1 ) name="${args[1]}" ;;
( * ) echo.error "dependency: unexpected argument '${args[1]}'" ;;
esac
;;
esac
[[ "${#args[@]}" -ge "${_s}" ]] \
&& args=("${args[@]:${_s}}") \
|| echo.error "dependency: missing value for '${args[1]}'" \
|| args=()
done
unset _s _p
[[ -n "${name}" ]] \
|| echo.error 'dependency: missing name' \
|| return 1
DEPENDENCY_NAMES+=("${name}")
DEPENDENCY[${name}:installed-by]="${installed_by:-default}"
DEPENDENCY[${name}:yay]="${yay:-${name}}"
DEPENDENCY[${name}:dnf]="${dnf:-${name}}"
DEPENDENCY[${name}:apt-get]="${apt_get:-${name}}"
DEPENDENCY[${name}:brew]="${brew:-${name}}"
DEPENDENCY[${name}:generic]="${name}" # no override -- the dependency's own name IS the generic fallback
DEPENDENCY[${name}:not-on]="${not_on:-}"
DEPENDENCY[${name}:only-on]="${only_on:-}"
DEPENDENCY[${name}:no-executable]="${no_executable}"
}
source "${0:a:h}/dependency.manifest.zsh"
#####################################################################
setup.packages.list-installation-profiles() { # every profile named by an --installed-by in the manifest
local name
for name in "${DEPENDENCY_NAMES[@]}"
do
print -l -- "${(s:,:)DEPENDENCY[${name}:installed-by]}"
done \
| sort -u
}
setup.packages._.describe-package() {
# a package name isn't always 1:1 with the dependency which asked for it
# (e.g. yay's "base-devel" satisfies autoconf, automake, bison, ...), so
# name the dependencies too whenever they differ from the package itself
local package="${1}" dependency_names="${2}"
local display="${package#cask:}" # macOS casks carry a "cask:" prefix through the pipeline
[[ "${dependency_names}" == "${display}" ]] \
&& echo "'${display}'" \
&& return 0
echo "'${display}' (satisfies: ${dependency_names// /, })"
}
setup.packages.install-dependencies() {
local ERRORS=0
echo.status 'checking package dependencies'
local installer="${INSTALLER:-$(setup.packages.get-installer)}"
[[ "${installer}" != generic ]] \
|| echo.warning 'no supported package manager detected; dependencies can only be verified, not installed'
local requested_config="${DOTWRYN_SETUP__INSTALLATION_PROFILE:-default}"
local name field target
local -a targets
local -A package_names # resolved package -> space-joined logical dependency names
local -a executable_names # dependency names that must resolve via `command -v` (no-executable excluded)
[[ "${#DOTWRYN_SETUP__EXCLUDE[@]}" -eq 0 ]] \
|| echo.status "excluding dependencies: ${(j:, :)DOTWRYN_SETUP__EXCLUDE}"
for name in "${DEPENDENCY_NAMES[@]}"
do
(( ${DOTWRYN_SETUP__EXCLUDE[(Ie)${name}]} )) && continue
[[ ",${DEPENDENCY[${name}:installed-by]}," == *",${requested_config},"* ]] || continue
field="${DEPENDENCY[${name}:only-on]}"
[[ -n "${field}" ]] && [[ ",${field}," != *",${installer},"* ]] && continue
field="${DEPENDENCY[${name}:not-on]}"
[[ -n "${field}" ]] && [[ ",${field}," == *",${installer},"* ]] && continue
[[ "${DEPENDENCY[${name}:no-executable]}" == false ]] && executable_names+=("${name}")
field="${DEPENDENCY[${name}:${installer}]}"
# a dependency can require more than one physical package on a given installer
# (e.g. "docker" needing docker-ce, docker-ce-cli, and containerd.io on
# dnf) -- each --<installer> flag accepts a comma-separated list of targets
targets=("${(s:,:)field}")
for target in "${targets[@]}"
do
package_names[${target}]+=" ${name}"
done
done
unset name field target targets
if [[ "${installer}" == generic ]]
then
for name in "${executable_names[@]}"
do
setup.packages.installer.generic.install-managed "${name}"
done
unset name
echo.success 'all dependencies satisfied'
return 0
fi
case "${CI}" in
( true ) ;; # no system to update
( * )
echo.status 'updating system, repositories, and mirrors'
"setup.packages.installer.${installer}.update-repositories"
;;
esac
local -a installed_packages
installed_packages=("${(f)$("setup.packages.installer.${installer}.list-installed")}")
local package
local -a missing_packages
for package in "${(k)package_names[@]}"
do
(( ${installed_packages[(Ie)${package}]} )) && continue
missing_packages+=("${package}")
done
unset package
if [[ "${#missing_packages[@]}" -eq 0 ]]
then
echo.success 'all dependencies satisfied'
else
for package in "${missing_packages[@]}"
do
echo.status "installing $(setup.packages._.describe-package "${package}" "${package_names[${package}]# }")"
done
unset package
"setup.packages.installer.${installer}.install-batch" "${missing_packages[@]}"
# don't trust the batch call's exit code alone: some package managers (yay in
# particular) can exit 0 after silently skipping one unresolvable AUR target
# rather than failing the whole transaction, so re-check what's actually installed
installed_packages=("${(f)$("setup.packages.installer.${installer}.list-installed")}")
local -a still_missing
for package in "${missing_packages[@]}"
do
(( ${installed_packages[(Ie)${package}]} )) || still_missing+=("${package}")
done
[[ "${#still_missing[@]}" -gt 0 ]] \
&& echo.warning 'batched install left some packages unresolved; retrying them one at a time to isolate the failure(s)'
for package in "${missing_packages[@]}"
do
if [[ " ${still_missing[*]} " == *" ${package} "* ]]
then
"setup.packages.installer.${installer}.install-managed" "${package}"
else
echo.success "$(setup.packages._.describe-package "${package}" "${package_names[${package}]# }") installed"
fi
done
unset package still_missing
fi
case "${installer}" in
( brew )
# especially important on macOS where BSD utilities are the default
echo.status "making sure homebrew GNU utilities are default"
local gnubin_path
for gnubin_path in "$(brew --prefix)"/opt/*/libexec/gnubin(N); do export PATH="${gnubin_path}:${PATH}"; done
unset gnubin_path
setup.filesystem.write-managed-block "${HOME}/.zprofile" --tag homebrew <<-'EOF'
# (homebrew required) : load homebrew shellenv
eval "$(/opt/homebrew/bin/brew shellenv 2>/dev/null || /usr/local/bin/brew shellenv 2>/dev/null)"
# ensure GNU utilities from homebrew load before system/BSD utils
for homebrew_gnu_bin_path in "$(brew --prefix)"/opt/*/libexec/gnubin(N)
do
export PATH="${homebrew_gnu_bin_path}:${PATH}"
done
unset homebrew_gnu_bin_path
EOF
;;
esac
echo.status 'verifying dependency executables'
for name in "${executable_names[@]}"
do
command -v "${name}" &>/dev/null \
|| echo.error "'${name}' is installed but its executable could not be found on PATH"
done
unset name
if [[ "${ERRORS}" -ne 0 ]]
then
echo.warning "detected ${ERRORS} errors; double check warnings before proceeding!"
case "${DOTWRYN_SETUP__STRICT}" in
( true ) return 1 ;; # never prompt; unresolved dependencies are a failure
( * ) utils.yN 'continue with install?' || return 1 ;;
esac
fi
echo.success 'all dependencies satisfied'
return 0
}
+61
View File
@@ -0,0 +1,61 @@
#####################################################################
setup.packages.install-from-source() { # clone + build/install a git-hosted package (AUR PKGBUILD or Makefile)
local target_url="${1}"
local name="${2:-$(echo "${target_url}" | sed 's/.*\///; s/\.git$//')}"
local install_dir="${HOME}/.local/share/source-packages"
local package_dir="${install_dir}/${name}"
mkdir -p -- "${install_dir}"
local state=missing
[[ -d "${package_dir}" ]] && state=exists
case "${state}" in
( exists )
: \
&& echo.status "updating '${name}'" \
&& git -C "${package_dir}" pull origin "$(git -C "${package_dir}" rev-parse --abbrev-ref HEAD)" \
;
;;
( missing )
: \
&& echo.status "downloading '${name}'" \
&& git clone -- "${target_url}" "${package_dir}" \
;
;;
esac \
|| echo.error "failed to fetch '${name}'" \
|| return 1
local build_method=unknown
[[ -f "${package_dir}/PKGBUILD" ]] && build_method=makepkg
[[ -f "${package_dir}/Makefile" ]] && [[ "${build_method}" == unknown ]] && build_method=make
case "${build_method}" in
( makepkg )
: \
&& echo.status "installing '${name}'" \
&& ( cd -- "${package_dir}" && yes | makepkg -si ) \
&& echo.success "successfully installed '${name}'" \
|| echo.error "failed to install '${name}'" \
;
;;
( make )
: \
&& echo.status "building '${name}'" \
&& make -C "${package_dir}" \
&& : \
&& echo.status "installing '${name}'" \
&& utils.io.getsudo \
&& sudo make -C "${package_dir}" install \
&& echo.success "successfully installed '${name}'" \
|| echo.error "failed to build/install '${name}'" \
;
;;
( unknown )
echo.warning 'could not detect supported installation method'
echo.reminder 'complete manual installation in the directory below:'
echo.reminder "${package_dir}"
;;
esac
}
+16
View File
@@ -0,0 +1,16 @@
#####################################################################
setup.packages.installer.apt-get.update-repositories() { sudo apt-get update && sudo apt-get upgrade; }
setup.packages.installer.apt-get.list-installed() { dpkg-query -W -f='${Package}\n' 2>/dev/null; }
setup.packages.installer.apt-get.install-batch() { sudo apt-get install --yes "${@}"; }
setup.packages.installer.apt-get.install-managed() {
local target="${1}"
echo.status "checking / installing '${target}'"
sudo apt-get install --yes "${target}" \
&& echo.success "'${target}' installed" \
|| echo.error "failed to install ${target}" \
;
}
+76
View File
@@ -0,0 +1,76 @@
#####################################################################
setup.packages.installer.brew.setup-package-manager() {
command -v brew &>/dev/null && return 0
# an already-installed homebrew still isn't on PATH in a fresh login
# shell, so adopt it before concluding it needs installing
local brew_path
for brew_path in /opt/homebrew/bin/brew /usr/local/bin/brew
do
[[ -x "${brew_path}" ]] || continue
eval "$("${brew_path}" shellenv)"
return 0
done
unset brew_path
: \
&& /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" \
&& eval "$(/opt/homebrew/bin/brew shellenv 2>/dev/null || /usr/local/bin/brew shellenv 2>/dev/null)" \
|| echo.error 'failed to install homebrew' \
|| return 1 \
;
}
setup.packages.installer.brew.update-repositories() { brew update && brew upgrade; }
setup.packages.installer.brew.list-installed() { # targets needing "brew install --cask" are carried through this whole pipeline as "cask:name"
brew list --formula 2>/dev/null
brew list --cask 2>/dev/null | sed 's/^/cask:/'
}
setup.packages.installer.brew.install-batch() {
local -a formulae casks
local target
for target in "${@}"
do
case "${target}" in
( cask:* ) casks+=("${target#cask:}") ;;
( * ) formulae+=("${target}") ;;
esac
done
unset target
if [[ "${#formulae[@]}" -gt 0 ]]
then
yes | brew install "${formulae[@]}" || return 1
fi
if [[ "${#casks[@]}" -gt 0 ]]
then
yes | brew install --cask "${casks[@]}" || return 2
fi
}
setup.packages.installer.brew.install-managed() {
local target="${1}"
local -a cask_flag
if [[ "${target}" == cask:* ]]
then
target="${target#cask:}"
cask_flag=(--cask)
fi
if brew list "${cask_flag[@]}" --versions "${target}" &>/dev/null
then
echo.success "found '${target}'"
return 0
fi
: \
&& echo.status "installing '${target}'" \
&& yes | brew install "${cask_flag[@]}" "${target}" \
&& echo.success "'${target}' installed" \
|| echo.error "failed to install '${target}'" \
;
}
+23
View File
@@ -0,0 +1,23 @@
#####################################################################
setup.packages.installer.dnf.update-repositories() {
: \
&& sudo dnf update \
&& sudo dnf upgrade \
&& sudo dnf install -y dnf-plugins-core \
&& sudo dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/fedora/docker-ce.repo \
;
}
setup.packages.installer.dnf.list-installed() { rpm -qa --qf '%{NAME}\n' 2>/dev/null; }
setup.packages.installer.dnf.install-batch() { sudo dnf install -y "${@}"; }
setup.packages.installer.dnf.install-managed() {
local target="${1}"
echo.status "checking / installing '${target}'"
sudo dnf install -y "${target}" \
&& echo.success "'${target}' installed" \
|| echo.error "failed to install ${target}" \
;
}
+7
View File
@@ -0,0 +1,7 @@
#####################################################################
setup.packages.installer.generic.install-managed() {
local target="${1}"
command -v "${target}" &>/dev/null \
|| echo.error "could not find '${target}'; it's up to you to install this one!"
}
+27
View File
@@ -0,0 +1,27 @@
#####################################################################
setup.packages.installer.yay.setup-package-manager() {
command -v yay &>/dev/null && return 0
setup.packages.install-from-source 'https://aur.archlinux.org/yay.git' 'yay'
}
setup.packages.installer.yay.update-repositories() { yay -Syu; }
setup.packages.installer.yay.list-installed() { yay -Qq 2>/dev/null; }
setup.packages.installer.yay.install-batch() { yay -Syu --noconfirm "${@}"; }
setup.packages.installer.yay.install-managed() {
local target="${1}"
local installed_target="$(setup.packages.installer.yay.list-installed | grep "^${target}$\|^${target}-git$")"
[[ "${installed_target}" ]] \
&& echo.success "found '${installed_target}'" \
&& return 0
: \
&& echo.status "installing '${target}'" \
&& yay -Syu --noconfirm "${target}" \
&& : \
&& echo.success "successfully installed '${target}'" \
|| echo.error "failed to install '${target}'" \
;
}
+29
View File
@@ -0,0 +1,29 @@
#####################################################################
for setup_library in "${0:a:h}"/*.zsh
do
case "${setup_library:t}" in
( packages.module.zsh | dependency.manifest.zsh ) ;; # self; manifest
( * ) source "${setup_library}" ;;
esac
done
unset setup_library
#####################################################################
setup.packages() {
local INSTALLER="$(setup.packages.get-installer)"
[[ "${INSTALLER}" ]] || return 1
case "${INSTALLER}" in
( brew | generic ) ;; # homebrew refuses to run as root; generic installs nothing
( * ) utils.io.getsudo ;;
esac
if (( ${+functions[setup.packages.installer.${INSTALLER}.setup-package-manager]} ))
then
"setup.packages.installer.${INSTALLER}.setup-package-manager" || return 2
fi
setup.packages.install-dependencies || return 3
}
+104
View File
@@ -0,0 +1,104 @@
#
# this file is used exclusively by setup/run; our objectives are:
#
# 1. to source scwrypts:zsh/utils.module.zsh
# (this contains all our echo.+debug+safety fundamentals for zsh)
#
# 2. to create setup.scwrypts()
# (explicitly uses the source scwrypts in .wryn/zsh during setup)
#
# 3. to load in the remaining zsh library functions for setup/
#
# While _normally_ utils.module.zsh is safe to "just source," setup
# can run from a bare "we have git and zsh and NOTHING else" system,
# so there's a little work we have to do to actually get there
# without user intervention.
#
#####################################################################
setup.prepare-zsh-utilities._.error() {
echo "ERROR::${*}" >&2
return 1
}
[[ "${EUID}" -ne 0 ]] || [[ "${DOTWRYN_SETUP__FORCE_ROOT}" == true ]] \
|| setup.prepare-zsh-utilities._.error 'setup cannot be run as root' \
|| return 1
# normally "DOTWRYN" which is not available during first setup
export DOTWRYN_SETUP__DOTWRYN="${DOTWRYN_SETUP__SETUP_DIR:h}"
[[ -d "${DOTWRYN_SETUP__DOTWRYN}/setup" ]] \
|| setup.prepare-zsh-utilities._.error "'${DOTWRYN_SETUP__SETUP_DIR}' is not the .wryn setup directory; run 'setup/run'" \
|| return 2
export DOTWRYN_SETUP__STATE_DIR="${XDG_STATE_HOME:-${HOME}/.local/state}/dotwryn"
mkdir -p -- "${DOTWRYN_SETUP__STATE_DIR}"
printf 'initializing required submodules...' >&2
git -C "${DOTWRYN_SETUP__DOTWRYN}" submodule update --init --remote --recursive >/dev/null \
|| setup.prepare-zsh-utilities._.error 'unable to initialize required submodules' \
|| return 3
#####################################################################
DEPENDENCIES+=(zsh)
REQUIRED_ENV+=()
command -v fzf &>/dev/null || fzf() { head -n1; }
command -v jo &>/dev/null || jo() { :; }
command -v jq &>/dev/null || jq() { :; }
source "${DOTWRYN_SETUP__DOTWRYN}/zsh/plugins/scwrypts/zsh/utils/utils.module.zsh" \
|| setup.prepare-zsh-utilities._.error 'unable to load the scwrypts utils' \
|| return 4
unset -f fzf &>/dev/null
unset -f jo &>/dev/null
unset -f jq &>/dev/null
unset -f setup.prepare-zsh-utilities._.error # echo.error takes over from here
#####################################################################
setup.scwrypts() {
CI=1 \
CONFIG__USER_SETTINGS="${DOTWRYN_SETUP__DOTWRYN}/config/scwrypts/dotfiles.zsh" \
DOTWRYN="${DOTWRYN_SETUP__DOTWRYN}" \
"${DOTWRYN_SETUP__DOTWRYN}/zsh/plugins/scwrypts/scwrypts" -n "${@}"
}
##########################################
# >>> scwrypts bug! <<<
# there's currently no way to provide SCWRYPTS_GROUP_DIRS outside of
# the users config.zsh; this is a temporary measure to register the
# `dotwryn` scwrypts group for a first-time setup run
scwrypts_user_config="${XDG_CONFIG_HOME:-${HOME}/.config}/scwrypts/config.zsh"
if [[ -f "${scwrypts_user_config}" ]]
then
: # user config already setup; do not modify
else
: \
&& mkdir -p -- "${scwrypts_user_config:h}" \
&& cp -- "${DOTWRYN_SETUP__DOTWRYN}/zsh/plugins/scwrypts/zsh/config.user.zsh" "${scwrypts_user_config}" \
&& echo "SCWRYPTS_GROUP_DIRS+=(\"${DOTWRYN_SETUP__DOTWRYN}/scwrypts\")" >> "${scwrypts_user_config}" \
;
fi
unset scwrypts_user_config
#####################################################################
: \
&& source "${DOTWRYN_SETUP__DOTWRYN}/setup/filesystem/filesystem.module.zsh" \
&& source "${DOTWRYN_SETUP__DOTWRYN}/setup/packages/packages.module.zsh" \
&& source "${DOTWRYN_SETUP__DOTWRYN}/setup/config/config.module.zsh" \
&& : \
|| echo.error 'something went wrong during setup library imports (see above)' \
|| return 5 \
;
#####################################################################
# shhh don't worry about it
[[ "${CI}" == true ]] || [[ -f "${welcome_marker:=${DOTWRYN_SETUP__STATE_DIR}/hush-setup-welcome}" ]] || {clear;C=$((){B(){base64 -d};A="$(cat "$2")";for _ in {1..$1};do A=$(echo $A|B);done;echo $A} 7 "$DOTWRYN_SETUP__DOTWRYN/setup/welcome");for ((i=0;i<${#C};i++));do [[ ${C:$i:1} =~ z ]] && M=$(printf "\\033[1;3$((1+$RANDOM%5))m") && continue;printf "$M${C:$i:1}" none;sleep 0.01;done;echo;unset C M;touch "${welcome_marker}";}
-63
View File
@@ -1,63 +0,0 @@
[[ "${EUID}" -eq 0 ]] && [[ "${FORCE_ROOT}" != true ]] \
&& { echo 'ERROR::Setup cannot be run as root'; exit 1; }
#####################################################################
# normally "DOTWRYN", but uses "DOTWRYN_PATH" to avoid conflict during setup
cd "${0:a:h}"
export DOTWRYN_PATH="$(git rev-parse --show-toplevel)"
cd "${DOTWRYN_PATH}"
printf 'initializing required submodules...' >&2
git submodule update --init --remote --recursive >/dev/null 2>&1 || {
echo 'failed!' >&2
echo 'unable to initialize required submodules' >&2
exit 2
}
#####################################################################
DEPENDENCIES+=(zsh)
REQUIRED_ENV+=()
command -v fzf &>/dev/null || fzf() { head -n1; }
command -v jo &>/dev/null || jo() { :; }
command -v jq &>/dev/null || jq() { :; }
source "${DOTWRYN_PATH}/zsh/plugins/scwrypts/zsh/utils/utils.module.zsh" || exit 3
unset -f fzf &>/dev/null
unset -f jo &>/dev/null
unset -f jq &>/dev/null
# register the '.wryn' dotwryn scwrypts group; scwrypts only discovers groups
# listed in SCWRYPTS_GROUP_DIRS inside this file, and this file doesn't exist
# yet on a fresh machine
scwrypts_user_config_dir="${XDG_CONFIG_HOME:-${HOME}/.config}/scwrypts"
scwrypts_user_config="${scwrypts_user_config_dir}/config.zsh"
mkdir -p "${scwrypts_user_config_dir}"
[ -f "${scwrypts_user_config}" ] \
|| cp "${DOTWRYN_PATH}/zsh/plugins/scwrypts/zsh/config.user.zsh" "${scwrypts_user_config}"
grep -qF "${DOTWRYN_PATH}/scwrypts" "${scwrypts_user_config}" \
|| echo "SCWRYPTS_GROUP_DIRS+=(\"${DOTWRYN_PATH}/scwrypts\")" >> "${scwrypts_user_config}"
unset scwrypts_user_config_dir scwrypts_user_config
SCWRYPTS() {
CI=1 \
CONFIG__USER_SETTINGS="${DOTWRYN_PATH}/config/scwrypts/dotfiles.zsh" \
DOTWRYN="${DOTWRYN_PATH}" \
"${DOTWRYN_PATH}/zsh/plugins/scwrypts/scwrypts" -n "${@}"
}
#####################################################################
source "${DOTWRYN_PATH}/setup/os.zsh"
source "${DOTWRYN_PATH}/setup/git.zsh"
source "${DOTWRYN_PATH}/setup/config.zsh"
#####################################################################
clear
# shhh don't worry about it
[[ "${CI}" == true ]] || {C=$((){B(){base64 -d};A="$(cat "$2")";for _ in {1..$1};do A=$(echo $A|B);done;echo $A} 7 "$DOTWRYN_PATH/setup/welcome");for ((i=0;i<${#C};i++));do [[ ${C:$i:1} =~ z ]] && M=$(printf "\\033[1;3$((1+$RANDOM%5))m") && continue;printf "$M${C:$i:1}" none;sleep 0.01;done;echo;unset C M;}
+179 -36
View File
@@ -1,48 +1,191 @@
#!/bin/zsh
cd "${0:a:h}"
#!/usr/bin/env zsh
DOTWRYN_SETUP__SETUP_DIR="${0:a:h}"
#####################################################################
FORCE_ROOT=false
CI=false
OVERWRITE_EXISTING=false
COMPILE_DMENU=true
MIN=false
DOTWRYN_SETUP__FORCE_ROOT=false
DOTWRYN_SETUP__OVERWRITE_EXISTING=false
DOTWRYN_SETUP__USE_LOGFILE=true
DOTWRYN_SETUP__STRICT=false
DOTWRYN_SETUP__INSTALLATION_PROFILE=default
DOTWRYN_SETUP__EXCLUDE=()
DOTWRYN_SETUP__STEPS=(filesystem packages config)
while [[ "${#}" -gt 0 ]]
do
case "${1}" in
( --force-root ) FORCE_ROOT=true ;;
( --ci ) export CI=true ;;
( --overwrite ) OVERWRITE_EXISTING=true ;;
setup.run._.usage() {
local run_file="${DOTWRYN_SETUP__SETUP_DIR}/run"
local line flags description
( --no-compile-dmenu ) COMPILE_DMENU=false ;;
local found_function=false
while read -r line
do
case "${found_function}" in
( false ) [[ "${line}" == '() {' ]] && found_function=true ;;
( true )
[[ "${line}" =~ '^[[:space:]]*#[[:space:]]?(.*)$' ]] || break
echo "${match[1]}"
;;
esac
done < "${run_file}"
( --min )
MIN=true
COMPILE_DMENU=false
;;
esac
shift 1
done
source ./requirements.zsh || exit 1
################################################################################
echo
echo 'options:'
logfile="${HOME}/dotwryn-install.txt"
while read -r line
do
[[ "${line}" =~ '^[[:space:]]*\([[:space:]]*(-[^)]*)\)[^#]*#[[:space:]]*(.*)$' ]] || continue
echo.status "installation start : $(date)" 2>> "${logfile}"
{
setup.os || utils.fail 1 'failed to set up os-dependencies (see above)'
setup.git || utils.fail 2 'failed to set up git (see above)'
setup.config || utils.fail 3 'failed to set up program configuration (see above)'
} 2>&1 | tee -a "${logfile}"
flags="${(j: :)${(z)match[1]}}"
flags="${flags// | /, }"
description="${match[2]}"
echo.status "installation complete: $(date)" 2>> "${logfile}"
[[ "${description}" =~ '^(<[^>]*>)[[:space:]]+(.*)$' ]] \
&& flags="${flags} ${match[1]}" \
&& description="${match[2]}"
################################################################################
printf ' %-38s %s\n' "${flags}" "${description}"
done < "${run_file}"
[[ "${ERRORS}" -eq 0 ]] || {
utils.yN 'keep logfile?' \
|| { rm "${logfile}" || echo.error "unable to remove '${logfile}'" }
echo
echo "steps: ${(j:, :)DOTWRYN_SETUP__STEPS}"
echo
}
echo.success "\n.wryn setup complete; have a nice day :)\n "
#####################################################################
() {
# set up this machine from the .wryn dotfiles repository
#
# usage: setup/run [...options...]
local -a skip_steps only_steps
setup.run._.error() { echo "ERROR::${*}" >&2; return 1; }
local _s
while [[ "${#}" -gt 0 ]]
do
_s=1
case "${1}" in
( -h | --help ) # display this message and exit
setup.run._.usage
return 0
;;
( -p | --installation-profile ) _s=2 # <profile> only install dependencies belonging to <profile>
DOTWRYN_SETUP__INSTALLATION_PROFILE="${2}"
;;
( --min ) # shorthand for '--installation-profile min'
DOTWRYN_SETUP__INSTALLATION_PROFILE=min
;;
( --skip ) _s=2 # <step> don't run <step>; repeatable, incompatible with --only
skip_steps+=("${2}")
;;
( --only ) _s=2 # <step> only run <step>; repeatable, incompatible with --skip
only_steps+=("${2}")
;;
( --exclude ) _s=2 # <dependencies> comma-separated dependencies to neither install nor verify; repeatable
DOTWRYN_SETUP__EXCLUDE+=("${(s:,:)2}")
;;
( --overwrite ) # replace existing local configuration
DOTWRYN_SETUP__OVERWRITE_EXISTING=true
;;
( --force-root ) # allow setup to run as root
DOTWRYN_SETUP__FORCE_ROOT=true
;;
( --ci ) # automation mode; never prompts (pair with '--skip packages' to also skip installs)
export CI=true
;;
( --no-logfile ) # don't write a logfile for this run
DOTWRYN_SETUP__USE_LOGFILE=false
;;
( --strict ) # treat unresolved dependencies as a failure instead of prompting
DOTWRYN_SETUP__STRICT=true
;;
( * )
setup.run._.error "unknown option '${1}'; try '--help'"
return 2
;;
esac
shift "${_s}"
done
unset _s
#####################################################################
[[ "${#skip_steps[@]}" -eq 0 ]] || [[ "${#only_steps[@]}" -eq 0 ]] \
|| setup.run._.error "'--skip' and '--only' cannot be used together" \
|| return 2
local named_step
for named_step in "${skip_steps[@]}" "${only_steps[@]}"
do
(( ${DOTWRYN_SETUP__STEPS[(Ie)${named_step}]} )) \
|| setup.run._.error "no such setup step '${named_step}' (steps: ${(j:, :)DOTWRYN_SETUP__STEPS})" \
|| return 2
done
unset named_step
local -a steps
local step
for step in "${DOTWRYN_SETUP__STEPS[@]}"
do
case "${#only_steps[@]}" in
( 0 ) (( ${skip_steps[(Ie)${step}]} )) && continue ;;
( * ) (( ${only_steps[(Ie)${step}]} )) || continue ;;
esac
steps+=("${step}")
done
[[ "${#steps[@]}" -gt 0 ]] \
|| setup.run._.error 'every setup step was excluded; nothing to do' \
|| return 2
unset -f setup.run._.error
#####################################################################
source "${DOTWRYN_SETUP__SETUP_DIR}/prepare-zsh-utilities.zsh" || return 1
local -a installation_profiles
installation_profiles=("${(f)$(setup.packages.list-installation-profiles)}")
(( ${installation_profiles[(Ie)${DOTWRYN_SETUP__INSTALLATION_PROFILE}]} )) \
|| echo.error "no such installation profile '${DOTWRYN_SETUP__INSTALLATION_PROFILE}' (available: ${(j:, :)installation_profiles})" \
|| return 2
#####################################################################
local ERRORS=0
local logfile
case "${DOTWRYN_SETUP__USE_LOGFILE}" in
( true ) logfile="${DOTWRYN_SETUP__STATE_DIR}/setup.$(date +%Y-%m-%dT%H%M%S).log" ;;
( false ) logfile=/dev/null ;;
esac
echo.status "installation start : $(date)" 2>> "${logfile}"
echo.status "setup steps : ${(j:, :)steps}"
for step in "${steps[@]}"
do
setup.${step} > >(tee -a "${logfile}") 2>&1 \
|| echo.error "failed during ${step} setup (see above)" \
|| return ${ERRORS}
done
echo.status "installation complete: $(date)" 2>> "${logfile}"
[[ "${logfile}" == /dev/null ]] \
|| echo.reminder "logfile can be found at:\n\t${logfile}"
#####################################################################
echo.success "\n.wryn setup complete; have a nice day :)\n "
} "${@}"