Skip to content

Instantly share code, notes, and snippets.

@cbeuw
Last active March 13, 2019 15:20
Show Gist options
  • Save cbeuw/dd34bf577db85d857ae2f8bb960733e3 to your computer and use it in GitHub Desktop.
Save cbeuw/dd34bf577db85d857ae2f8bb960733e3 to your computer and use it in GitHub Desktop.
Download source, compile and install shadowsocks-libev and Cloak
#!/usr/bin/env bash
PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/go/bin:/usr/local/sbin:~/bin
export PATH
#
# Auto compile install Shadowsocks Server with Cloak
#
# Copyright (C) 2016-2018 Teddysun <i@teddysun.com>
#
# Forked and modified by cbeuw from https://github.com/teddysun/shadowsocks_install/blob/master/shadowsocks-all.sh
#
# System Required: CentOS 6+, Debian7+, Ubuntu12+
#
# Reference URL:
# https://github.com/shadowsocks/shadowsocks
# https://github.com/shadowsocks/shadowsocks-libev
# https://github.com/cbeuw/Cloak
red='\033[0;31m'
green='\033[0;32m'
yellow='\033[0;33m'
plain='\033[0m'
[[ $EUID -ne 0 ]] && echo -e "[${red}Error${plain}] This script must be run as root!" && exit 1
cur_dir=$( pwd )
libsodium_file="libsodium-1.0.16"
libsodium_url="https://github.com/jedisct1/libsodium/releases/download/1.0.16/libsodium-1.0.16.tar.gz"
mbedtls_file="mbedtls-2.12.0"
mbedtls_url="https://tls.mbed.org/download/mbedtls-2.12.0-gpl.tgz"
shadowsocks_libev_init="/etc/init.d/shadowsocks-libev"
shadowsocks_libev_config="/etc/shadowsocks-libev/config.json"
shadowsocks_libev_centos="https://raw.githubusercontent.com/teddysun/shadowsocks_install/master/shadowsocks-libev"
shadowsocks_libev_debian="https://raw.githubusercontent.com/teddysun/shadowsocks_install/master/shadowsocks-libev-debian"
# Stream Ciphers
common_ciphers=(
aes-256-gcm
aes-192-gcm
aes-128-gcm
aes-256-ctr
aes-192-ctr
aes-128-ctr
aes-256-cfb
aes-192-cfb
aes-128-cfb
camellia-128-cfb
camellia-192-cfb
camellia-256-cfb
xchacha20-ietf-poly1305
chacha20-ietf-poly1305
chacha20-ietf
chacha20
salsa20
rc4-md5
)
disable_selinux(){
if [ -s /etc/selinux/config ] && grep 'SELINUX=enforcing' /etc/selinux/config; then
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
setenforce 0
fi
}
check_sys(){
local checkType=$1
local value=$2
local release=''
local systemPackage=''
if [[ -f /etc/redhat-release ]]; then
release="centos"
systemPackage="yum"
elif grep -Eqi "debian" /etc/issue; then
release="debian"
systemPackage="apt"
elif grep -Eqi "ubuntu" /etc/issue; then
release="ubuntu"
systemPackage="apt"
elif grep -Eqi "centos|red hat|redhat" /etc/issue; then
release="centos"
systemPackage="yum"
elif grep -Eqi "debian|raspbian" /proc/version; then
release="debian"
systemPackage="apt"
elif grep -Eqi "ubuntu" /proc/version; then
release="ubuntu"
systemPackage="apt"
elif grep -Eqi "centos|red hat|redhat" /proc/version; then
release="centos"
systemPackage="yum"
fi
if [[ "${checkType}" == "sysRelease" ]]; then
if [ "${value}" == "${release}" ]; then
return 0
else
return 1
fi
elif [[ "${checkType}" == "packageManager" ]]; then
if [ "${value}" == "${systemPackage}" ]; then
return 0
else
return 1
fi
fi
}
version_ge(){
test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"
}
version_gt(){
test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"
}
check_kernel_version(){
local kernel_version=$(uname -r | cut -d- -f1)
if version_gt ${kernel_version} 3.7.0; then
return 0
else
return 1
fi
}
check_kernel_headers(){
if check_sys packageManager yum; then
if rpm -qa | grep -q headers-$(uname -r); then
return 0
else
return 1
fi
elif check_sys packageManager apt; then
if dpkg -s linux-headers-$(uname -r) > /dev/null 2>&1; then
return 0
else
return 1
fi
fi
return 1
}
getversion(){
if [[ -s /etc/redhat-release ]]; then
grep -oE "[0-9.]+" /etc/redhat-release
else
grep -oE "[0-9.]+" /etc/issue
fi
}
centosversion(){
if check_sys sysRelease centos; then
local code=$1
local version="$(getversion)"
local main_ver=${version%%.*}
if [ "$main_ver" == "$code" ]; then
return 0
else
return 1
fi
else
return 1
fi
}
autoconf_version(){
if [ ! "$(command -v autoconf)" ]; then
echo -e "[${green}Info${plain}] Starting install package autoconf"
if check_sys packageManager yum; then
yum install -y autoconf > /dev/null 2>&1 || echo -e "[${red}Error:${plain}] Failed to install autoconf"
elif check_sys packageManager apt; then
apt-get -y update > /dev/null 2>&1
apt-get -y install autoconf > /dev/null 2>&1 || echo -e "[${red}Error:${plain}] Failed to install autoconf"
fi
fi
local autoconf_ver=$(autoconf --version | grep autoconf | grep -oE "[0-9.]+")
if version_ge ${autoconf_ver} 2.67; then
return 0
else
return 1
fi
}
get_ip(){
[ -z ${IP} ] && IP=$( wget -qO- -t1 -T2 ipv4.icanhazip.com )
[ -z ${IP} ] && IP=$( wget -qO- -t1 -T2 ipinfo.io/ip )
[ -z ${IP} ] && IP=$( wget -qO- -t1 -T2 ip.42.pl/ip )
echo ${IP}
}
get_ipv6(){
local ipv6=$(wget -qO- -t1 -T2 ipv6.icanhazip.com)
[ -z ${ipv6} ] && return 1 || return 0
}
get_libev_ver(){
libev_ver=$(wget --no-check-certificate -qO- https://api.github.com/repos/shadowsocks/shadowsocks-libev/releases/latest | grep 'tag_name' | cut -d\" -f4)
[ -z ${libev_ver} ] && echo -e "[${red}Error${plain}] Get shadowsocks-libev latest version failed" && exit 1
}
get_opsy(){
[ -f /etc/redhat-release ] && awk '{print ($1,$3~/^[0-9]/?$3:$4)}' /etc/redhat-release && return
[ -f /etc/os-release ] && awk -F'[= "]' '/PRETTY_NAME/{print $3,$4,$5}' /etc/os-release && return
[ -f /etc/lsb-release ] && awk -F'[="]+' '/DESCRIPTION/{print $2}' /etc/lsb-release && return
}
is_64bit(){
if [ `getconf WORD_BIT` = '32' ] && [ `getconf LONG_BIT` = '64' ] ; then
return 0
else
return 1
fi
}
debianversion(){
if check_sys sysRelease debian;then
local version=$( get_opsy )
local code=${1}
local main_ver=$( echo ${version} | sed 's/[^0-9]//g')
if [ "${main_ver}" == "${code}" ];then
return 0
else
return 1
fi
else
return 1
fi
}
download(){
local filename=$(basename $1)
if [ -f ${1} ]; then
echo "${filename} [found]"
else
echo "${filename} not found, download now..."
wget --no-check-certificate -c -t3 -T60 -O ${1} ${2}
if [ $? -ne 0 ]; then
echo -e "[${red}Error${plain}] Download ${filename} failed."
exit 1
fi
fi
}
download_files(){
cd ${cur_dir}
get_libev_ver
shadowsocks_libev_file="shadowsocks-libev-$(echo ${libev_ver} | sed -e 's/^[a-zA-Z]//g')"
shadowsocks_libev_url="https://github.com/shadowsocks/shadowsocks-libev/releases/download/${libev_ver}/${shadowsocks_libev_file}.tar.gz"
download "${shadowsocks_libev_file}.tar.gz" "${shadowsocks_libev_url}"
if check_sys packageManager yum; then
download "${shadowsocks_libev_init}" "${shadowsocks_libev_centos}"
elif check_sys packageManager apt; then
download "${shadowsocks_libev_init}" "${shadowsocks_libev_debian}"
fi
}
get_char(){
SAVEDSTTY=$(stty -g)
stty -echo
stty cbreak
dd if=/dev/tty bs=1 count=1 2> /dev/null
stty -raw
stty echo
stty $SAVEDSTTY
}
error_detect_depends(){
local command=$1
local depend=`echo "${command}" | awk '{print $4}'`
echo -e "[${green}Info${plain}] Starting to install package ${depend}"
${command} > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo -e "[${red}Error${plain}] Failed to install ${red}${depend}${plain}"
exit 1
fi
}
config_firewall(){
if centosversion 6; then
/etc/init.d/iptables status > /dev/null 2>&1
if [ $? -eq 0 ]; then
iptables -L -n | grep -i ${shadowsocksport} > /dev/null 2>&1
if [ $? -ne 0 ]; then
iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport ${shadowsocksport} -j ACCEPT
iptables -I INPUT -m state --state NEW -m udp -p udp --dport ${shadowsocksport} -j ACCEPT
/etc/init.d/iptables save
/etc/init.d/iptables restart
else
echo -e "[${green}Info${plain}] port ${green}${shadowsocksport}${plain} already be enabled."
fi
else
echo -e "[${yellow}Warning${plain}] iptables looks like not running or not installed, please enable port ${shadowsocksport} manually if necessary."
fi
elif centosversion 7; then
systemctl status firewalld > /dev/null 2>&1
if [ $? -eq 0 ]; then
firewall-cmd --permanent --zone=public --add-port=${shadowsocksport}/tcp
firewall-cmd --permanent --zone=public --add-port=${shadowsocksport}/udp
firewall-cmd --reload
else
echo -e "[${yellow}Warning${plain}] firewalld looks like not running or not installed, please enable port ${shadowsocksport} manually if necessary."
fi
fi
}
generate_credentials(){
if [ "${cloak}" == "y" ] || [ "${cloak}" == "Y" ]; then
ck-server -u | read ckauid
ck-server -k | read -d, ckpub ckpv
fi
}
config_shadowsocks(){
local server_value="\"0.0.0.0\""
if get_ipv6; then
server_value="[\"[::0]\",\"0.0.0.0\"]"
fi
if [ ! -d "$(dirname ${shadowsocks_libev_config})" ]; then
mkdir -p $(dirname ${shadowsocks_libev_config})
fi
if [ "${cloak}" == "y" ] || [ "${cloak}" == "Y" ]; then
cat > ${shadowsocks_libev_config}<<-EOF
{
"server":${server_value},
"server_port":${shadowsocksport},
"password":"${shadowsockspwd}",
"timeout":300,
"user":"nobody",
"method":"${shadowsockscipher}",
"fast_open":false,
"nameserver":"8.8.8.8",
"mode":"tcp_and_udp",
"plugin":"ck-server",
"plugin_opts":"WebServerAddr=${ckwebaddr};PrivateKey=${ckpv};AdminUID=${ckauid}"
}
EOF
else
cat > ${shadowsocks_libev_config}<<-EOF
{
"server":${server_value},
"server_port":${shadowsocksport},
"password":"${shadowsockspwd}",
"timeout":300,
"user":"nobody",
"method":"${shadowsockscipher}",
"fast_open":false,
"nameserver":"8.8.8.8",
"mode":"tcp_and_udp"
}
EOF
fi
}
install_dependencies(){
if check_sys packageManager yum; then
echo -e "[${green}Info${plain}] Checking the EPEL repository..."
if [ ! -f /etc/yum.repos.d/epel.repo ]; then
yum install -y epel-release > /dev/null 2>&1
fi
[ ! -f /etc/yum.repos.d/epel.repo ] && echo -e "[${red}Error${plain}] Install EPEL repository failed, please check it." && exit 1
[ ! "$(command -v yum-config-manager)" ] && yum install -y yum-utils > /dev/null 2>&1
[ x"$(yum-config-manager epel | grep -w enabled | awk '{print $3}')" != x"True" ] && yum-config-manager --enable epel > /dev/null 2>&1
echo -e "[${green}Info${plain}] Checking the EPEL repository complete..."
yum_depends=(
unzip gzip openssl openssl-devel gcc pcre pcre-devel libtool libevent
autoconf automake make curl curl-devel zlib-devel cpio expat-devel gettext-devel
libev-devel c-ares-devel git qrencode
)
for depend in ${yum_depends[@]}; do
error_detect_depends "yum -y install ${depend}"
done
elif check_sys packageManager apt; then
apt_depends=(
gettext build-essential unzip gzip curl openssl libssl-dev
autoconf automake libtool gcc make cpio libpcre3 libpcre3-dev zlib1g-dev libev-dev libc-ares-dev git qrencode
)
apt-get -y update
for depend in ${apt_depends[@]}; do
error_detect_depends "apt-get -y install ${depend}"
done
fi
}
install_check(){
if check_sys packageManager yum || check_sys packageManager apt; then
if centosversion 5; then
return 1
fi
return 0
else
return 1
fi
}
install_select(){
if ! install_check; then
echo -e "[${red}Error${plain}] Your OS is not supported to run it!"
echo "Please change to CentOS 6+/Debian 7+/Ubuntu 12+ and try again."
exit 1
fi
clear
}
install_prepare_password(){
echo "Please enter password for shadowsocks-libev:"
read -p "(Default password: teddysun.com):" shadowsockspwd
[ -z "${shadowsockspwd}" ] && shadowsockspwd="teddysun.com"
echo
echo "password = ${shadowsockspwd}"
echo
}
install_prepare_port() {
while true
do
dport=443
echo -e "Please enter a port for shadowsocks-libev [1-65535]"
read -p "(Default port: ${dport}):" shadowsocksport
[ -z "${shadowsocksport}" ] && shadowsocksport=${dport}
expr ${shadowsocksport} + 1 &>/dev/null
if [ $? -eq 0 ]; then
if [ ${shadowsocksport} -ge 1 ] && [ ${shadowsocksport} -le 65535 ] && [ ${shadowsocksport:0:1} != 0 ]; then
echo
echo "port = ${shadowsocksport}"
echo
break
fi
fi
echo -e "[${red}Error${plain}] Please enter a correct number [1-65535]"
done
}
install_prepare_cipher(){
while true
do
echo -e "Please select stream cipher for shadowsocks-libev:"
for ((i=1;i<=${#common_ciphers[@]};i++ )); do
hint="${common_ciphers[$i-1]}"
echo -e "${green}${i}${plain}) ${hint}"
done
read -p "Which cipher you'd select(Default: ${common_ciphers[0]}):" pick
[ -z "$pick" ] && pick=1
expr ${pick} + 1 &>/dev/null
if [ $? -ne 0 ]; then
echo -e "[${red}Error${plain}] Please enter a number"
continue
fi
if [[ "$pick" -lt 1 || "$pick" -gt ${#common_ciphers[@]} ]]; then
echo -e "[${red}Error${plain}] Please enter a number between 1 and ${#common_ciphers[@]}"
continue
fi
shadowsockscipher=${common_ciphers[$pick-1]}
echo
echo "cipher = ${shadowsockscipher}"
echo
break
done
}
install_prepare_libev_cloak(){
while true
do
echo -e "Do you want install Cloak for shadowsocks-libev? [y/n]"
read -p "(default: y):" cloak
[ -z "$cloak" ] && cloak=y
case "${cloak}" in
y|Y|n|N)
echo
echo "You choose = ${cloak}"
echo
break
;;
*)
echo -e "[${red}Error${plain}] Please only enter [y/n]"
;;
esac
done
if [ "${cloak}" == "y" ] || [ "${cloak}" == "Y" ]; then
echo -e "Please enter a redirection IP for Cloak (leave blank to set it to 204.79.197.200:443 of bing.com):"
read -p "" ckwebaddr
[ -z "$ckwebaddr" ] && ckwebaddr="204.79.197.200:443"
fi
}
install_prepare(){
install_prepare_password
install_prepare_port
install_prepare_cipher
install_prepare_libev_cloak
echo
echo "Press any key to start...or Press Ctrl+C to cancel"
char=`get_char`
}
install_libsodium(){
if [ ! -f /usr/lib/libsodium.a ]; then
cd ${cur_dir}
download "${libsodium_file}.tar.gz" "${libsodium_url}"
tar zxf ${libsodium_file}.tar.gz
cd ${libsodium_file}
./configure --prefix=/usr && make && make install
if [ $? -ne 0 ]; then
echo -e "[${red}Error${plain}] ${libsodium_file} install failed."
install_cleanup
exit 1
fi
else
echo -e "[${green}Info${plain}] ${libsodium_file} already installed."
fi
}
install_mbedtls(){
if [ ! -f /usr/lib/libmbedtls.a ]; then
cd ${cur_dir}
download "${mbedtls_file}-gpl.tgz" "${mbedtls_url}"
tar xf ${mbedtls_file}-gpl.tgz
cd ${mbedtls_file}
make SHARED=1 CFLAGS=-fPIC
make DESTDIR=/usr install
if [ $? -ne 0 ]; then
echo -e "[${red}Error${plain}] ${mbedtls_file} install failed."
install_cleanup
exit 1
fi
else
echo -e "[${green}Info${plain}] ${mbedtls_file} already installed."
fi
}
install_shadowsocks_libev(){
cd ${cur_dir}
tar zxf ${shadowsocks_libev_file}.tar.gz
cd ${shadowsocks_libev_file}
./configure --disable-documentation && make && make install
if [ $? -eq 0 ]; then
chmod +x ${shadowsocks_libev_init}
local service_name=$(basename ${shadowsocks_libev_init})
if check_sys packageManager yum; then
chkconfig --add ${service_name}
chkconfig ${service_name} on
elif check_sys packageManager apt; then
update-rc.d -f ${service_name} defaults
fi
else
echo
echo -e "[${red}Error${plain}] shadowsocks-libev install failed."
install_cleanup
exit 1
fi
}
install_shadowsocks_libev_cloak(){
if [ "${cloak}" == "y" ] || [ "${cloak}" == "Y" ]; then
cd ${cur_dir}
mkdir tempgo
GOPATH=${cur_dir}/tempgo
go get github.com/cbeuw/Cloak
go get github.com/juju/ratelimit
go get github.com/boltdb/bolt
go get github.com/cbeuw/go-ecdh
[ -d ./tempgo/src/github.com/cbeuw/Cloak ] && cd tempgo/src/github.com/cbeuw/Cloak || echo -e "[${red}Error:${plain}] Failed to git clone Cloak."
make server
make install
if [ ! "$(command -v ck-server)" ]; then
echo -e "[${red}Error${plain}] Cloak for shadowsocks-libev install failed."
install_cleanup
exit 1
fi
fi
}
install_completed_libev(){
clear
ldconfig
${shadowsocks_libev_init} start
echo
echo -e "Congratulations, ${green}shadowsocks-libev${plain} server install completed!"
echo -e "Your Server IP : ${red} $(get_ip) ${plain}"
echo -e "Your Server Port : ${red} ${shadowsocksport} ${plain}"
echo -e "Your Password : ${red} ${shadowsockspwd} ${plain}"
echo -e "Your Encryption Method: ${red} ${shadowsockscipher} ${plain}"
echo -e "Your Cloak's Public Key: ${red} ${ckpub} ${plain}"
echo -e "Your Cloak's Private Key: ${red} ${ckpv} ${plain}"
echo -e "Your Cloak's AdminUID: ${red} ${ckauid} ${plain}"
}
qr_generate_libev(){
if [ "$(command -v qrencode)" ]; then
local tmp=$(echo -n "${shadowsockscipher}:${shadowsockspwd}@$(get_ip):${shadowsocksport}" | base64 -w0)
local qr_code="ss://${tmp}"
echo
echo "Your QR Code: (For Shadowsocks Windows, OSX, Android and iOS clients)"
echo -e "${green} ${qr_code} ${plain}"
echo -n "${qr_code}" | qrencode -s8 -o ${cur_dir}/shadowsocks_libev_qr.png
echo "Your QR Code has been saved as a PNG file path:"
echo -e "${green} ${cur_dir}/shadowsocks_libev_qr.png ${plain}"
fi
}
install_main(){
install_libsodium
if ! ldconfig -p | grep -wq "/usr/lib"; then
echo "/usr/lib" > /etc/ld.so.conf.d/lib.conf
fi
ldconfig
install_mbedtls
install_shadowsocks_libev
install_shadowsocks_libev_cloak
install_completed_libev
qr_generate_libev
echo
echo "Enjoy it!"
echo
}
install_cleanup(){
cd ${cur_dir}
rm -rf tempgo
rm -rf ${libsodium_file} ${libsodium_file}.tar.gz
rm -rf ${mbedtls_file} ${mbedtls_file}-gpl.tgz
rm -rf ${shadowsocks_libev_file} ${shadowsocks_libev_file}.tar.gz
}
install_shadowsocks(){
disable_selinux
install_select
install_prepare
install_dependencies
download_files
if check_sys packageManager yum; then
config_firewall
fi
install_main
generate_credentials
config_shadowsocks
install_cleanup
}
uninstall_shadowsocks_libev(){
printf "Are you sure uninstall ${red}shadowsocks-libev and Cloak${plain}? [y/n]\n"
read -p "(default: n):" answer
[ -z ${answer} ] && answer="n"
if [ "${answer}" == "y" ] || [ "${answer}" == "Y" ]; then
${shadowsocks_libev_init} status > /dev/null 2>&1
if [ $? -eq 0 ]; then
${shadowsocks_libev_init} stop
fi
local service_name=$(basename ${shadowsocks_libev_init})
if check_sys packageManager yum; then
chkconfig --del ${service_name}
elif check_sys packageManager apt; then
update-rc.d -f ${service_name} remove
fi
rm -fr $(dirname ${shadowsocks_libev_config})
rm -f /usr/local/bin/ss-local
rm -f /usr/local/bin/ss-tunnel
rm -f /usr/local/bin/ss-server
rm -f /usr/local/bin/ss-manager
rm -f /usr/local/bin/ss-redir
rm -f /usr/local/bin/ss-nat
rm -f /usr/local/bin/ck-client
rm -f /usr/local/bin/ck-server
rm -f /usr/local/lib/libshadowsocks-libev.a
rm -f /usr/local/lib/libshadowsocks-libev.la
rm -f /usr/local/include/shadowsocks.h
rm -f /usr/local/lib/pkgconfig/shadowsocks-libev.pc
rm -f /usr/local/share/man/man1/ss-local.1
rm -f /usr/local/share/man/man1/ss-tunnel.1
rm -f /usr/local/share/man/man1/ss-server.1
rm -f /usr/local/share/man/man1/ss-manager.1
rm -f /usr/local/share/man/man1/ss-redir.1
rm -f /usr/local/share/man/man1/ss-nat.1
rm -f /usr/local/share/man/man8/shadowsocks-libev.8
rm -fr /usr/local/share/doc/shadowsocks-libev
rm -f ${shadowsocks_libev_init}
echo -e "[${green}Info${plain}] shadowsocks-libev uninstall success"
else
echo
echo -e "[${green}Info${plain}] shadowsocks-libev uninstall cancelled, nothing to do..."
echo
fi
}
uninstall_shadowsocks(){
if [ -f ${shadowsocks_libev_init} ]; then
uninstall_shadowsocks_libev
else
echo -e "[${red}Error${plain}] shadowsocks-libev not installed, please check it and try again."
echo
exit 1
fi
}
# Initialization step
action=$1
[ -z $1 ] && action=install
case "${action}" in
install|uninstall)
${action}_shadowsocks
;;
*)
echo "Arguments error! [${action}]"
echo "Usage: $(basename $0) [install|uninstall]"
;;
esac
Copy link

ghost commented Dec 20, 2018

sounds good, looking forward to using this script on my own raspberry pi 3

// please update here once u fix it and ill try the script again, really excited for this

@cbeuw
Copy link
Author

cbeuw commented Dec 23, 2018

Sorry for the delay, my XPS 15 broke so I had to move to my old desktop :(

So because of how GOPATH works, it's all a bit awkward. What I did is that I made a temp GOPATH folder under the working directory of the script called tempgo and use go get to download all Cloak's dependency here, and in the end that temp folder gets deleted.

Regarding adding a ppa to apt in this script to install an up-to-date version of golang, I don't think that is a good idea because adding a ppa has global effect to the system which is not something everyone wants. Now realistically if the user wants to build both shadowsocks-libev and Cloak from source on their machine, they should be able to figure out how to install golang manually. Most users should just use the released binary instead (which I will produce once I finish most of the TODOs).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment