Last active
September 26, 2021 18:04
-
-
Save spacelatte/3b9220892d7618e2adf635991891be5f to your computer and use it in GitHub Desktop.
kanka: single-script package manager for #macos (#osx) with dependency resolution, based on #homebrew #brew.sh #api
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# -d 9 -type d | |
# -d 6 -type d | |
# -d 3 -type d | |
export APPS="${HOME}/.local/apps" | |
export PATH="${APPS}:${PATH}" | |
export JQ_URL="https://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64" | |
export JQ_PATH="${APPS}/jq-osx-amd64" | |
export BREW_URL="https://formulae.brew.sh/api/formula" | |
export BREW_CACHE="${APPS}/.cache" | |
export HOMEBREW_PREFIX="${APPS}" | |
export HOMEBREW_CELLAR="${APPS}" | |
export BREW_DIST="$(sw_vers -productVersion)" | |
export BREW_SWVER=( ${BREW_DIST//\./ } ) | |
export BREW_SWVER_MAJ=${BREW_SWVER[0]} | |
export BREW_SWVER_MIN=${BREW_SWVER[1]} | |
export BREW_SWVER_FIX=${BREW_SWVER[2]} | |
export BREW_NAMES=( | |
"cheetah" # 10.0 | |
"puma" # 10.1 | |
"jaguar" # 10.2 | |
"panther" # 10.3 | |
"tiger" # 10.4 | |
"leopard" # 10.5 | |
"snow_leopard" # 10.6 | |
"lion" # 10.7 | |
"mountain_lion" # 10.8 | |
"mavericks" # 10.9 | |
"yosemite" # 10.10 | |
"el_capitan" # 10.11 | |
"sierra" # 10.12 | |
"high_sierra" # 10.13 | |
"mojave" # 10.14 | |
"catalina" # 10.15 | |
"big_sur" # 10.16 | |
) | |
function paths_cache() { | |
echo "updating environment!" | |
find "${APPS}" -iname bash_completion -type f > "${APPS}/.completions" & | |
tee "${APPS}/.paths" >/dev/null <<-EOF | |
#!/usr/bin/env ${SHELL:-sh} | |
# --- paths source file --- | |
# echo "sourcing: \$0" | |
for arg in "\$@"; do echo "arg: \$arg"; done | |
export BASH_COMPLETION="$(find "${APPS}" -iname bash_completion -type f -print0 | sort -zbfuV | tr -s \\0 \\n)" | |
export BASH_COMPLETION_DIR="\${APPS}/etc/bash_completion.d" | |
export BASH_COMPLETION_COMPAT_DIR="\${BASH_COMPLETION_DIR}" | |
# export BASH_COMPLETION="\${X_BASH_COMPLETION}" | |
# export BASH_COMPLETION_DIR="\${X_BASH_COMPLETION_DIR}" | |
# export BASH_COMPLETION_COMPAT_DIR="\${X_BASH_COMPLETION_COMPAT_DIR}" | |
# \${MANPATH:-/usr/share/man:/usr/local/share/man} | |
export PREFIX="\${APPS}/sysroot" | |
export NPM_CONFIG_PREFIX="\${PREFIX}" | |
export KN_AP_PATH="$( find "${APPS}" -maxdepth 3 -type ld -iregex ".*/[s]\{0,1\}bin" -not -regex ".*\.app.*" -print0 | sort -zbfuV | tr -s \\0 :)" | |
export KN_FW_PATH="$( find "${APPS}" -maxdepth 9 -type ld -iregex ".*/frameworks/.*/[s]\{0,1\}bin" -not -regex ".*\.app.*" -print0 | sort -zbfuV | tr -s \\0 :)" | |
export KN_TERMINFO="$(find "${APPS}" -maxdepth 6 -type ld -iname "terminfo" -not -regex ".*\.app.*" -print0 | sort -zbfuV | tr -s \\0 :)" | |
export KN_CPATH="$( find "${APPS}" -maxdepth 4 -type ld -iname "include" -not -regex ".*\.app.*" -print0 | sort -zbfuV | tr -s \\0 :)" | |
export KN_LIBPATH="$(find "${APPS}" -maxdepth 4 -type ld -iname "lib" -not -regex ".*\.app.*" -print0 | sort -zbfuV | tr -s \\0 :)" | |
export KN_MANPATH="$(find "${APPS}" -maxdepth 4 -type ld -iname "man" -not -regex ".*\.app.*" -print0 | sort -zbfuV | tr -s \\0 :)" | |
export TERMINFO+="\${KN_TERMINFO}" | |
export MANPATH+=":\${KN_MANPATH}" | |
export PATH+=":\${KN_AP_PATH}:\${KN_FW_PATH}" | |
export CPATH+=":\${KN_CPATH}" | |
export LIBRARY_PATH+=":\${KN_LIBPATH}" | |
export C_INCLUDE_PATH+=":\${KN_CPATH}" | |
export OBJC_INCLUDE_PATH+=":\${KN_CPATH}" | |
export CPLUS_INCLUDE_PATH+=":\${KN_CPATH}" | |
export OBJCPLUS_INCLUDE_PATH+=":\${KN_CPATH}" | |
EOF | |
wait | |
source "${APPS}/.paths" | |
return | |
} | |
function brew_github_auth() { | |
local REPO="${1:? REPO is missing}" | |
curl -#Lfk \ | |
-u $(git config --get github.user):$(git config --get github.pat) \ | |
"ghcr.io/token?service=ghcr.io&scope=repository:${REPO}:pull" \ | |
| jq -r .token | |
} | |
function brew_github_helper() { | |
local TAP="${1:? TAP is missing}" | |
local URL="${2:? URL is missing}" | |
local TOKEN="$( brew_github_auth "${TAP}" )" | |
curl -#Lfk \ | |
-H "Authorization: Bearer ${TOKEN}" \ | |
"${URL}" | |
} | |
function brew_initialize() { | |
mkdir -p "${APPS}" | |
test -e "${JQ_PATH}" || { | |
echo "Downloading JQ: ${JQ_URL}" | |
/usr/bin/curl -#Lfo "${JQ_PATH}" "${JQ_URL}" | |
chmod +x "${JQ_PATH}" | |
} | |
test -e "${APPS}/.paths" && { | |
source "${APPS}/.paths" | |
} || { | |
paths_cache | |
# return | |
} | |
test -e "${BREW_CACHE}" || { | |
#touch "${BREW_CACHE}" | |
brew_update | |
} | |
test -e "${APPS}/opt" || ln -vsfF "${APPS}" "${APPS}/opt" | |
test -e "${APPS}/Cellar" || ln -vsfF "${APPS}" "${APPS}/Cellar" | |
test -e "${APPS}/sysroot" || mkdir -p "${APPS}/sysroot" | |
return | |
while read -r comp; do | |
echo "[ comp ] ${comp}" | |
source "${comp}" | |
continue | |
done < "${APPS}/.completions" | |
return | |
} | |
export SKIP_FIX=" | |
fortune | |
" | |
function brew_fix_otool() { | |
return | |
} | |
function brew_fix() { | |
#set -x | |
local term | |
#export LC_ALL=C | |
orgdir="/usr/local/Cellar" | |
fixdir="${APPS}" | |
mkdir -p "${fixdir}" | |
for term in "$@"; do | |
test -e "${APPS}/${term}" || continue | |
local deps=$(brew_depends "${term}") | |
find "${APPS}/${term}" -type f -exec file {} + \ | |
| grep -iE -e 'mach.+executable' -e 'shared.+library' -e 'mach-o.+bundle' \ | |
| cut -d: -f1 \ | |
| while read -r obj; do | |
#otool -L "${obj}" | grep HOMEBREW | cut -d\ -f1 | cut -b2- #| xargs -n1 -I% -- echo "%:$(basename %)" | |
otool -L "${obj}" \ | |
| grep -oE '\S+' \ | |
| grep HOMEBREW \ | |
| while read -r lib; do | |
echo "${obj##${APPS}/}: $lib" | |
chmod ug+w "${obj}" | |
#ls -lha "${obj}" | |
for dep in ${deps}; do | |
test -e "${APPS}/${dep}/${lib}" || continue | |
install_name_tool -add_rpath "${APPS}/${dep}/lib/." "${obj}" | |
continue | |
done | |
install_name_tool -add_rpath "/." "${obj}" | |
install_name_tool -add_rpath "/usr/." "${obj}" | |
install_name_tool -add_rpath "/usr/local/." "${obj}" | |
install_name_tool -add_rpath "${fixdir}/." "${obj}" | |
install_name_tool -add_rpath "@loader_path/." "${obj}" | |
install_name_tool -add_rpath "@executable_path/." "${obj}" | |
#install_name_tool -add_rpath "@loader_path/lib/." "${obj}" | |
#install_name_tool -add_rpath "@executable_path/lib/." "${obj}" | |
install_name_tool -change "${lib}" "${lib//@@HOMEBREW_*@@/@rpath}" "${obj}" | |
install_name_tool -id "$(basename "${lib}" )" # "${lib//@@HOMEBREW_*@@/${APPS}}" "${obj}" | |
#otool -l "${obj}" | grep -A3 RPATH | |
#otool -L "${obj}" | |
#echo "----------" | |
continue | |
local dylib="$(basename "${lib}" .dylib)" | |
install_name_tool -change "$(basename "${lib}")" \ | |
"${lib//@@HOMEBREW_*@@/@rpath}" "${obj}" | |
#"$(echo "${lib}" | sed "s:@@HOMEBREW.*@@:$APPS:g")" \ | |
#"${obj}" | |
otool -L "${obj}" | |
continue | |
done &>/dev/null & # each lib | |
wait | |
continue | |
done # each obj | |
wait | |
#find "${APPS}/${term}" -type f -exec sed -i'' "s:@@HOMEBREW_PREFIX@@:${APPS}:g" {} + | |
#find "${APPS}/${term}" -type f -exec sed -i'' "s:@@HOMEBREW_CELLAR@@:${APPS}:g" {} + | |
#continue | |
SUFFIX="old.${RANDOM}" | |
find "${APPS}/${term}" -type f -exec xattr -c {} + | |
#cp -R "${APPS}/${term}" "${fixdir}" | |
#continue | |
if echo "${SKIP_FIX}" | grep -qi "${term}"; then | |
echo "Package is blacklisted, skipping fixing..." | |
continue | |
fi | |
LC_ALL=C find "${APPS}/${term}" -type f -perm +111 \ | |
| while read file; do | |
file -b --mime-type "${file}" \ | |
| grep -qi text \ | |
|| continue | |
LC_ALL=C sed -i".${SUFFIX}" "s#${orgdir}#${fixdir}#g" "${file}" | |
LC_ALL=C sed -i".${SUFFIX}" "s#@@HOMEBREW_PREFIX@@#${fixdir}#g" "${file}" | |
LC_ALL=C sed -i".${SUFFIX}" "s#@@HOMEBREW_CELLAR@@#${fixdir}#g" "${file}" | |
rm -f "${file}.${SUFFIX}" | |
continue | |
done | |
continue | |
echo "replacing: $orgdir $fixdir" | |
LC_ALL=C find "${APPS}/${term}" -type f -perm +111 -exec \ | |
sed -i".${SUFFIX}" "s#${orgdir}#${fixdir}#g" {} + | |
#continue | |
echo "replacing: HOMEBREW_PREFIX $fixdir" | |
LC_ALL=C find "${APPS}/${term}" -type f -perm +111 -exec \ | |
sed -i".${SUFFIX}" "s#@@HOMEBREW_PREFIX@@#${fixdir}#g" {} + | |
#continue | |
echo "replacing: HOMEBREW_CELLAR $fixdir" | |
LC_ALL=C find "${APPS}/${term}" -type f -perm +111 -exec \ | |
sed -i".${SUFFIX}" "s#@@HOMEBREW_CELLAR@@#${fixdir}#g" {} + | |
#continue | |
find "${APPS}/${term}" -type f -iname "*.${SUFFIX}" -delete | |
done | |
#set +x | |
return | |
} | |
function brew_info() { | |
branch=stable | |
for term in "$@"; do | |
#jq -r ".[] | if (.name == (\"${term}\")) then del(.bottle.${branch}) else empty end" "${BREW_CACHE}" | |
"${JQ_PATH}" -r ".[] | if (.name == (\"${term}\")) then . else empty end" "${BREW_CACHE}" | |
continue | |
done | |
return | |
} | |
function brew_update() { | |
function extract_header() (cat | tr -d '\r' | grep -iE "${1}" | tr -d '[:blank:]' | cut -d: -f2-) | |
etag_local="$(cat "${BREW_CACHE}.etag" 2>/dev/null | extract_header 'etag' )" | |
etag_remote="$(/usr/bin/curl -sfI "${BREW_URL}.json" | extract_header 'etag' )" | |
length_local="$(stat -f '%z' "${BREW_CACHE}" 2>/dev/null || echo 0)" | |
length_remote="$(/usr/bin/curl -sfI "${BREW_URL}.json" | extract_header 'length' )" | |
echo "# diff (local/remote): sizes=${length_local}/${length_remote} etags=${etag_local}/${etag_remote}" | |
# test "$((length_local))" -eq "$((length_remote))" || /usr/bin/curl -#Lfo "${BREW_CACHE}" "${BREW_URL}.json" | |
test "${etag_local}" = "${etag_remote}" || \ | |
/usr/bin/curl --compressed -#Lf \ | |
-D "${BREW_CACHE}.etag" \ | |
-o "${BREW_CACHE}" \ | |
"${BREW_URL}.json" | |
return | |
} | |
function brew_search() { | |
local term | |
for term in "$@"; do | |
"${JQ_PATH}" -r ".[] | {n: .name, d: .desc} | if (.n | test(\".*${term}.*\")) then [ .n, .d ] | join(\":\") else empty end" "${BREW_CACHE}" | |
continue | |
done | column -ts ':' | |
return | |
} | |
function brew_depends() { | |
local term | |
for term in "$@"; do | |
"${JQ_PATH}" -r ".[] | select(.dependencies) | { n: .name, d: .dependencies } | if (.n == (\"${term}\")) then .d else empty end | .[]" "${BREW_CACHE}" | |
continue | |
done | |
} | |
function brew_recurse() { | |
local term | |
local deps | |
for term in "$@"; do | |
deps=( $(brew_depends "${term}") ) | |
test -n "${deps[*]}" && echo "${deps[@]}" | |
test -n "${deps[*]}" && brew_recurse "${deps[@]}" | |
continue | |
done | tr ' ' \\n | cat -s | sort -u | tr \\n ' ' | |
return | |
} | |
function brew_install() { | |
local term | |
local deps | |
local branch=stable | |
local release=${BREW_NAMES[$BREW_SWVER_MIN]} | |
#export -f brew_github_helper | |
#export -f brew_github_auth | |
for term in "$@"; do | |
deps=$(brew_recurse "${term}") | |
test -z "${skip}" && \ | |
test -n "${deps}" && { | |
local ideps=( ) | |
for dep in ${deps}; do | |
test -e "${APPS}/${dep}" || ideps+=( "${dep}" ) | |
done | |
echo "${term} dependencies: ${ideps[*]:-(none)} / ${deps}" | |
skip="$((1+${skip:-0}))" brew_install "${ideps[@]}" | |
#for dep in "${deps}"; do brew_install "${dep}"; done | |
} | |
# shellcheck disable=SC2016,SC1004 | |
echo "Installing: ${skip} ${term} " | |
fields=( | |
$("${JQ_PATH}" -r "[ .[] | select(.name == \"${term}\") | ( | |
.name, \ | |
.tap, \ | |
.bottle?.${branch}?.files?.all?.url // \ | |
.bottle?.${branch}?.files?.${release}?.url \ | |
) ] | @tsv" "${BREW_CACHE}") | |
) | |
name="${fields[0]}" | |
tap="${fields[1]}" | |
url="${fields[2]}" | |
dir="${APPS}/${name}" | |
mkdir -p "${dir}" | |
brew_github_helper "${tap}" "${url}" | /usr/bin/tar -C "${dir}" --strip=1 -ox | |
#| xargs -n3 -- bash -ec ' | |
# name="$1"; \ | |
# tap="$2"; \ | |
# url="$3"; \ | |
# dir="${APPS}/${name}"; \ | |
# mkdir -p "${dir}" && \ | |
# brew_github_helper "${tap}" "${url}" \ | |
# | /usr/bin/tar -C "${dir}" --strip=1 -ox; \ | |
#' -- && \ | |
test -e "${APPS}/${term}" && \ | |
find "${APPS}/${term}" -depth 1 -type l -print -delete && \ | |
(cd "${APPS}/${term}" && echo bin etc include lib libexec sbin share man var | xargs -n1 | xargs -I% -- ln -vsfF "./$(ls -1t | head -1)/%") && \ | |
(cd "${APPS}" && mkdir -p ./etc/bash_completion.d && cd ./etc/bash_completion.d; test -e "../../${term}" && \ | |
find "../../${term}/$(ls -1t "../../${term}" | head -1)/etc/bash_completion.d" -type f -exec ln -vsfF {} \; 2>/dev/null) && \ | |
true | |
brew_fix "${term}" | |
echo "cleaning up broken symlinks..." | |
for i in {0..3}; do | |
echo "@ depth: $i" | |
find -L "${APPS}/${term}" -depth $i -type l -exec rm -vf {} + | |
continue | |
done | |
continue | |
done | |
#brew_fix $@ | |
return | |
} | |
function brew_list() { | |
find "${APPS}" -d 1 -type d -exec basename {} \; | |
return | |
} | |
function brew_remove() { | |
for term in "$@"; do | |
find "${APPS}" -d 1 -iname "${term}" -type d -exec rm -rf {} + | |
continue | |
done | |
return | |
} | |
function brew_paths() { | |
echo "#PATH" | |
echo "$PATH" | tr : \\n | |
echo "#MANPATH" | |
echo "$MANPATH" | tr : \\n | |
echo "#LIBRARY_PATH" | |
echo "$LIBRARY_PATH" | tr : \\n | |
echo "#C_INCLUDE_PATH" | |
echo "$C_INCLUDE_PATH" | tr : \\n | |
echo "#DYLD_LIBRARY_PATH" | |
echo "$DYLD_LIBRARY_PATH" | tr : \\n | |
echo "#DYLD_FRAMEWORK_PATH" | |
echo "$DYLD_FRAMEWORK_PATH" | tr : \\n | |
echo "#DYLD_FALLBACK_LIBRARY_PATH" | |
echo "$DYLD_FALLBACK_LIBRARY_PATH" | tr : \\n | |
echo "#DYLD_FALLBACK_FRAMEWORK_PATH" | |
echo "$DYLD_FALLBACK_FRAMEWORK_PATH" | tr : \\n | |
return | |
} | |
function brew_update_auto() { | |
test -n "${KANKA_DISABLE_AUTOUPDATE}" && return | |
export -f brew_update | |
find "${BREW_CACHE}" -mmin +1440 -exec "$SHELL" -c brew_update \; | |
} | |
function kanka() { | |
local method="${1}" | |
local params=( "${@:2}" ) | |
brew_update_auto # automatic updates on-demand | |
case "$method" in | |
yukle|ekle|inst*) | |
brew_install "${params[@]}" | |
paths_cache | |
;; | |
bul|ara|hani|search|find) | |
brew_search "${params[@]}" | |
;; | |
sil|kaldir|rm|del|rem*) | |
brew_remove "${params[@]}" | |
paths_cache | |
;; | |
lazim|gerek*|deps|depend*) | |
brew_depends "${params[@]}" | |
;; | |
detay|bilgi|nasil|ne*|info|detail) | |
brew_info "${params[@]}" | |
;; | |
bak|yol*|path[s]|loc*) | |
brew_paths | |
;; | |
list*) | |
brew_list | |
;; | |
fix*) | |
brew_fix "${params[@]}" | |
;; | |
guncelle|upd*|"") | |
brew_update "${params[@]}" | |
;; | |
*) | |
local n=knk | |
echo " | |
$n Usage: | |
$n update # update the local cache | |
$n inst|install package... # install packages | |
$n del|rm package... # remove packages | |
$n deps|depend package... # show dependencies of the packages | |
" | sed -E 's:^[[:blank:]]*::g' | |
;; | |
esac | |
return | |
} | |
alias knk=kanka | |
brew_initialize |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment