From 3804c84ce2cfd6ee19a849b8ac8be6b905edf580 Mon Sep 17 00:00:00 2001 From: Pedro Barbiero Date: Thu, 29 May 2025 10:30:50 -0300 Subject: [PATCH 1/5] feat: improve install and uninstall scripts so that it is easier to handle symlinks and non-root installs --- .editorconfig | 5 ++ docs/index.md | 4 +- docs/install.sh | 133 ++++++++++++++++++++++++++++++++-------- docs/uninstall.sh | 60 ++++++++++++++++-- scripts/symlink-bins.sh | 7 ++- 5 files changed, 175 insertions(+), 34 deletions(-) create mode 100644 .editorconfig mode change 100644 => 100755 docs/uninstall.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..189c19e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +[*.sh] +charset = utf-8 +trim_trailing_whitespace = true +indent_size = 4 +indent_style = space diff --git a/docs/index.md b/docs/index.md index aa0c9e8..3a4e8a9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -18,7 +18,7 @@ Take the advantage of goodies commands like `phpctl create` to start a new proje ### Installation ```shell -/bin/bash -c "$(curl -fsSL https://phpctl.dev/install.sh)" +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/opencodeco/phpctl/refs/heads/main/docs/install.sh)" ``` **That is it!** Now you have `phpctl` available in your system. @@ -26,7 +26,7 @@ Take the advantage of goodies commands like `phpctl create` to start a new proje #### Custom installation You can also pass an argument to install at a custom location (e.g. `~/bin`), but you have to make sure that folder is in your `$PATH` variable. ```shell -/bin/bash -c "$(curl -fsSL https://phpctl.dev/install.sh)" ~/bin +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/opencodeco/phpctl/refs/heads/main/docs/install.sh)" -s ~/bin ``` #### Homebrew diff --git a/docs/install.sh b/docs/install.sh index 0a536e3..505b20b 100755 --- a/docs/install.sh +++ b/docs/install.sh @@ -1,46 +1,125 @@ #!/usr/bin/env bash -INSTALL_DIR=~/.phpctl -if [ -z "$1" ]; then - SUDO=sudo - SYMLINK_DIR=/usr/local/bin -else - SUDO="" +INSTALL_DIR="${HOME}/.phpctl" +SYMLINK_DIR="/usr/local/bin" +LOCAL_SOURCES_DIR="" + +# --- Option Parsing --- +while getopts "hi:s:l:" opt; do + case $opt in + h) + echo "Install phpctl sources and creates symlinks for easy usage." + echo "" + echo "Usage: $0 [OPTIONS]" + echo "Options:" + echo " -h Display this help message and exit" + echo " -i Set the installation directory (default: $HOME/.phpctl)" + echo " -s Set the symlink directory (default: /usr/local/bin)" + echo " -l Set the local sources directory. If empty, will fetch sources from github. (default: empty string)" + exit 0 + ;; + i) + INSTALL_DIR="$OPTARG" + ;; + s) + SYMLINK_DIR="$OPTARG" + ;; + l) + LOCAL_SOURCES_DIR="$OPTARG" + ;; + \?) + echo "Error: Invalid option: -$OPTARG" >&2 + echo "Try '$0 -h' for more information." >&2 + exit 1 + ;; + :) + echo "Error: Option -$OPTARG requires an argument." >&2 + echo "Try '$0 -h' for more information." >&2 + exit 1 + ;; + esac +done + +# Shift off the options and their arguments, so that any remaining +# positional parameters (if any) are correctly handled. +shift $((OPTIND - 1)) + +# Compatibility with previous script version +if [[ $SYMLINK_DIR = "/usr/local/bin" && -n ${1} ]]; then SYMLINK_DIR=$1 fi -echo -e "\033[0;33mInstalling phpctl at \033[0m$INSTALL_DIR" -if [ -d "$INSTALL_DIR" ]; then - echo "The install directory is not empty. Attempting to remove it..." - rm -rf $INSTALL_DIR +if [ ! -w "${SYMLINK_DIR}" ]; then + ELEVATED=true +else + ELEVATED=false fi -echo -n "" -git clone --quiet https://github.com/opencodeco/phpctl.git $INSTALL_DIR & -PID=$! -while kill -0 $PID 2> /dev/null; do - for CHAR in '-' '/' '|' '\'; do - printf "\b$CHAR" - sleep 0.1 - done -done -printf "\r" +if "$ELEVATED"; then + echo "Running in elevated mode. This might require sudo for operations in ${SYMLINK_DIR}." +fi +[[ $ELEVATED = true ]] && SUDO="sudo" || SUDO="" -if [ -z "$1" ]; then - echo -n "Sudo will be prompted to symlink the phpctl files." +# Initialize colors (if available) +if tput setaf 1 &>/dev/null; then + RED=$(tput setaf 1) + GREEN=$(tput setaf 2) + YELLOW=$(tput setaf 3) + BLUE=$(tput setaf 4) + NC=$(tput sgr0) # Reset attributes else - echo -n "Files will be symlinked to ${SYMLINK_DIR}." + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[0;33m' + BLUE='\033[0;34m' + NC='\033[0m' # No Color +fi + +install_sources() { + echo -e "${YELLOW}Installing phpctl at ${NC}$INSTALL_DIR" + if [ -d "$INSTALL_DIR" ]; then + echo "The install directory is not empty. Attempting to remove it..." + rm -rf "$INSTALL_DIR" + fi + + if [[ -n "$LOCAL_SOURCES_DIR" ]]; then + echo "Using local sources from: $LOCAL_SOURCES_DIR" + cp -r "$LOCAL_SOURCES_DIR" "$INSTALL_DIR" + else + GITHUB_REPO="https://github.com/opencodeco/phpctl.git" + echo "Cloning from $GITHUB_REPO..." + echo -n "" + git clone --quiet "$GITHUB_REPO" "$INSTALL_DIR" & + PID=$! + while kill -0 $PID 2>/dev/null; do + for CHAR in '-' '/' '|' "\\"; do + printf "\b%s" "$CHAR" + sleep 0.1 + done + done + printf "\r" + echo " done." + fi + echo "${GREEN}Success: ${NC}Operation completed successfully." +} + +install_sources + +echo -n "Files will be symlinked to ${SYMLINK_DIR}." +if [[ -n $SUDO ]]; then + echo -n "Sudo will be prompted to symlink the phpctl files." fi -echo -e -n " \033[0;32mDo you want to continue? (Y/n)\033[0m " + +echo -e -n " ${GREEN}Do you want to continue? (Y/n)${NC} " read -r answer if [ "$answer" != "${answer#[Nn]}" ]; then - echo -e "\033[0;31mTo use phpctl globally, link the cloned script to your bin directory, like:\033[0m" - echo "" + echo -e "${RED}To use phpctl globally, link the cloned script to your bin directory, like:${NC}" for file in "${INSTALL_DIR}"/bin/*; do bin=$(basename "$file") echo " ${SUDO} ln -sf ${INSTALL_DIR}/bin/$bin ${SYMLINK_DIR}/$bin" done + echo -e "${YELLOW}Or add ${INSTALL_DIR}/bin to your PATH." else - $SUDO ${INSTALL_DIR}/scripts/symlink-bins.sh ${INSTALL_DIR} + $SUDO "${INSTALL_DIR}/scripts/symlink-bins.sh" "${INSTALL_DIR}" "${SYMLINK_DIR}" fi diff --git a/docs/uninstall.sh b/docs/uninstall.sh old mode 100644 new mode 100755 index ba40433..56288aa --- a/docs/uninstall.sh +++ b/docs/uninstall.sh @@ -1,7 +1,59 @@ #!/usr/bin/env bash -INSTALL_DIR=~/.phpctl -SYMLINK_DIR=/usr/local/bin +INSTALL_DIR="${HOME}/.phpctl" +SYMLINK_DIR="/usr/local/bin" + +while getopts "hi:s:" opt; do + case $opt in + h) + echo "Uninstall phpctl." + echo "" + echo "Usage: $0 [OPTIONS]" + echo "Options:" + echo " -h Display this help message and exit" + echo " -i Set the installation directory (default: $HOME/.phpctl)" + echo " -s Set the symlink directory (default: /usr/local/bin)" + exit 0 + ;; + i) + INSTALL_DIR="$OPTARG" + ;; + s) + SYMLINK_DIR="$OPTARG" + ;; + \?) # Handle invalid options + echo "Error: Invalid option: -$OPTARG" >&2 + echo "Try '$0 -h' for more information." >&2 + exit 1 + ;; + :) # Handle missing arguments for options that require them + echo "Error: Option -$OPTARG requires an argument." >&2 + echo "Try '$0 -h' for more information." >&2 + exit 1 + ;; + esac +done + +# Shift off the options and their arguments, so that any remaining +# positional parameters (if any) are correctly handled. +shift $((OPTIND - 1)) + +# Compatibility with previous script version +if [[ $SYMLINK_DIR = "/usr/local/bin" && -n ${1} ]]; then + SYMLINK_DIR=$1 +fi + +if [ ! -w "${SYMLINK_DIR}" ]; then + ELEVATED=true +else + ELEVATED=false +fi + +if "$ELEVATED"; then + echo "Running in elevated mode. This might require sudo for operations in ${SYMLINK_DIR}." +fi + +[[ $ELEVATED = true ]] && SUDO="sudo" || SUDO="" LINKS=( composer @@ -31,7 +83,7 @@ LINKS=( echo "Removing symbolic links..." for link in "${LINKS[@]}"; do if [ -L "${SYMLINK_DIR}/${link}" ]; then - sudo rm "${SYMLINK_DIR}/${link}" + $SUDO rm "${SYMLINK_DIR}/${link}" echo "Removed ${SYMLINK_DIR}/${link}" else echo "Link ${SYMLINK_DIR}/${link} does not exist, skipping." @@ -48,4 +100,4 @@ else echo "Skipping removal of installation directory." fi -echo "Uninstallation complete." \ No newline at end of file +echo "Uninstallation complete." diff --git a/scripts/symlink-bins.sh b/scripts/symlink-bins.sh index bb01640..8a4ce45 100755 --- a/scripts/symlink-bins.sh +++ b/scripts/symlink-bins.sh @@ -1,4 +1,9 @@ #!/usr/bin/env bash + +local_bin_path="${2:-/usr/local/bin}" +# Add a trailing slash if it's missing +local_bin_path="${local_bin_path%/}/" + for file in "${1:-.}"/bin/*; do - ln -sf "$(realpath "$file")" "/usr/local/bin/$(basename "$file")" + ln -sf "$(realpath "$file")" "${local_bin_path}$(basename "$file")" done From 1b255df071026a0b2bd2dfc03921c05e08680262 Mon Sep 17 00:00:00 2001 From: Pedro Barbiero Date: Thu, 29 May 2025 11:03:47 -0300 Subject: [PATCH 2/5] feat: improve install spinner and use braille patterns --- docs/install.sh | 60 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/docs/install.sh b/docs/install.sh index 505b20b..6bc5184 100755 --- a/docs/install.sh +++ b/docs/install.sh @@ -68,14 +68,39 @@ if tput setaf 1 &>/dev/null; then YELLOW=$(tput setaf 3) BLUE=$(tput setaf 4) NC=$(tput sgr0) # Reset attributes + + TPUT_AVAILABLE=true else RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color + + TPUT_AVAILABLE=false fi +spinner() { + local i=0 + local sp="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" + while true; do + printf "\r[%s] Cloning repository..." "${sp:i++%${#sp}:1}" + sleep 0.1 + done +} + +cleanup() { + if [[ -n "$SPINNER_PID" ]]; then + kill "$SPINNER_PID" 2>/dev/null + if $TPUT_AVAILABLE; then + printf "\r%*s\r" "$(tput cols)" "" + else + printf "\r\033[2K" + fi + fi + exit +} + install_sources() { echo -e "${YELLOW}Installing phpctl at ${NC}$INSTALL_DIR" if [ -d "$INSTALL_DIR" ]; then @@ -88,18 +113,31 @@ install_sources() { cp -r "$LOCAL_SOURCES_DIR" "$INSTALL_DIR" else GITHUB_REPO="https://github.com/opencodeco/phpctl.git" - echo "Cloning from $GITHUB_REPO..." - echo -n "" + + trap cleanup EXIT INT TERM + + spinner & + SPINNER_PID=$! git clone --quiet "$GITHUB_REPO" "$INSTALL_DIR" & PID=$! - while kill -0 $PID 2>/dev/null; do - for CHAR in '-' '/' '|' "\\"; do - printf "\b%s" "$CHAR" - sleep 0.1 - done - done - printf "\r" - echo " done." + wait "$PID" + _git_status=$? + + kill "$SPINNER_PID" 2>/dev/null + # clear the spinner line one last time + if $TPUT_AVAILABLE; then + printf "\r%*s\r" "$(tput cols)" "" + else + printf "\r\033[2K" + fi + + if [[ $_git_status -eq 0 ]]; then + echo "done." + else + echo "Failed to clone $GITHUB_REPO into $INSTALL_DIR." + echo "Error: git clone failed with status ${_git_status}" + exit 255 + fi fi echo "${GREEN}Success: ${NC}Operation completed successfully." } @@ -123,3 +161,5 @@ if [ "$answer" != "${answer#[Nn]}" ]; then else $SUDO "${INSTALL_DIR}/scripts/symlink-bins.sh" "${INSTALL_DIR}" "${SYMLINK_DIR}" fi + +echo "${GREEN}Installation complete!${NC}" From 42248836b556a3c9fb4c0c835650b90e60532e38 Mon Sep 17 00:00:00 2001 From: Pedro Barbiero Date: Thu, 29 May 2025 11:18:20 -0300 Subject: [PATCH 3/5] chore: simplify elevated variables --- docs/install.sh | 10 ++++------ docs/uninstall.sh | 8 +++----- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/docs/install.sh b/docs/install.sh index 6bc5184..9af4e20 100755 --- a/docs/install.sh +++ b/docs/install.sh @@ -50,17 +50,15 @@ if [[ $SYMLINK_DIR = "/usr/local/bin" && -n ${1} ]]; then fi if [ ! -w "${SYMLINK_DIR}" ]; then - ELEVATED=true + SUDO="sudo" else - ELEVATED=false + SUDO="" fi -if "$ELEVATED"; then +if [[ -n $SUDO ]]; then echo "Running in elevated mode. This might require sudo for operations in ${SYMLINK_DIR}." fi -[[ $ELEVATED = true ]] && SUDO="sudo" || SUDO="" - # Initialize colors (if available) if tput setaf 1 &>/dev/null; then RED=$(tput setaf 1) @@ -146,7 +144,7 @@ install_sources echo -n "Files will be symlinked to ${SYMLINK_DIR}." if [[ -n $SUDO ]]; then - echo -n "Sudo will be prompted to symlink the phpctl files." + echo -n " ${RED}Sudo will be prompted to symlink the phpctl files.${NC}" fi echo -e -n " ${GREEN}Do you want to continue? (Y/n)${NC} " diff --git a/docs/uninstall.sh b/docs/uninstall.sh index 56288aa..4ed1389 100755 --- a/docs/uninstall.sh +++ b/docs/uninstall.sh @@ -44,17 +44,15 @@ if [[ $SYMLINK_DIR = "/usr/local/bin" && -n ${1} ]]; then fi if [ ! -w "${SYMLINK_DIR}" ]; then - ELEVATED=true + SUDO="sudo" else - ELEVATED=false + SUDO="" fi -if "$ELEVATED"; then +if [[ -n $SUDO ]]; then echo "Running in elevated mode. This might require sudo for operations in ${SYMLINK_DIR}." fi -[[ $ELEVATED = true ]] && SUDO="sudo" || SUDO="" - LINKS=( composer composer-require-checker From 281c86473f234f17150934b3eeb2dc9ae16e350f Mon Sep 17 00:00:00 2001 From: Pedro Barbiero Date: Thu, 29 May 2025 11:36:45 -0300 Subject: [PATCH 4/5] feat: ensure symlink dir exists --- docs/install.sh | 79 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/docs/install.sh b/docs/install.sh index 9af4e20..130f94d 100755 --- a/docs/install.sh +++ b/docs/install.sh @@ -1,5 +1,57 @@ #!/usr/bin/env bash +set_colors() { + # Initialize colors (if available) + if tput setaf 1 &>/dev/null; then + RED=$(tput setaf 1) + GREEN=$(tput setaf 2) + YELLOW=$(tput setaf 3) + BLUE=$(tput setaf 4) + NC=$(tput sgr0) # Reset attributes + + TPUT_AVAILABLE=true + else + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[0;33m' + BLUE='\033[0;34m' + NC='\033[0m' # No Color + + TPUT_AVAILABLE=false + fi +} + +ensure_dir() { + if [[ -e "${SYMLINK_DIR}" ]]; then + if [[ ! -d "${SYMLINK_DIR}" ]]; then + echo "${RED}Error: ${SYMLINK_DIR} exists but is not a directory. Please remove it or choose a different path.${NC}" + exit 1 + fi + return + fi + + if [[ ! -d ${SYMLINK_DIR} ]]; then + echo "${YELLOW}$SYMLINK_DIR does not exist and will be created.${NC}" + mkdir -p "${SYMLINK_DIR}" 2>/dev/null + local success=$? + + if [[ $success != 0 ]]; then + echo -e -n "${RED}Could not create $SYMLINK_DIR. Exit status: ${success}.${NC} Try again with sudo? (Y/n) " + read -r answer + if [ "$answer" != "${answer#[Nn]}" ]; then + exit 1 + fi + + sudo mkdir -p "${SYMLINK_DIR}" 2>/dev/null + success=$? + if [[ -n $success ]]; then + echo -e -n "${RED}Could not create $SYMLINK_DIR. Exit status: ${success}.${NC}" + exit ${success} + fi + fi + fi +} + INSTALL_DIR="${HOME}/.phpctl" SYMLINK_DIR="/usr/local/bin" LOCAL_SOURCES_DIR="" @@ -40,6 +92,8 @@ while getopts "hi:s:l:" opt; do esac done +set_colors + # Shift off the options and their arguments, so that any remaining # positional parameters (if any) are correctly handled. shift $((OPTIND - 1)) @@ -49,7 +103,9 @@ if [[ $SYMLINK_DIR = "/usr/local/bin" && -n ${1} ]]; then SYMLINK_DIR=$1 fi -if [ ! -w "${SYMLINK_DIR}" ]; then +ensure_dir + +if [[ ! -w "${SYMLINK_DIR}" ]]; then SUDO="sudo" else SUDO="" @@ -59,25 +115,6 @@ if [[ -n $SUDO ]]; then echo "Running in elevated mode. This might require sudo for operations in ${SYMLINK_DIR}." fi -# Initialize colors (if available) -if tput setaf 1 &>/dev/null; then - RED=$(tput setaf 1) - GREEN=$(tput setaf 2) - YELLOW=$(tput setaf 3) - BLUE=$(tput setaf 4) - NC=$(tput sgr0) # Reset attributes - - TPUT_AVAILABLE=true -else - RED='\033[0;31m' - GREEN='\033[0;32m' - YELLOW='\033[0;33m' - BLUE='\033[0;34m' - NC='\033[0m' # No Color - - TPUT_AVAILABLE=false -fi - spinner() { local i=0 local sp="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" @@ -134,7 +171,7 @@ install_sources() { else echo "Failed to clone $GITHUB_REPO into $INSTALL_DIR." echo "Error: git clone failed with status ${_git_status}" - exit 255 + exit 1 fi fi echo "${GREEN}Success: ${NC}Operation completed successfully." From 45f2d010d91b5564b352d121a144f993fda82343 Mon Sep 17 00:00:00 2001 From: Pedro Barbiero Date: Thu, 29 May 2025 12:34:56 -0300 Subject: [PATCH 5/5] feat: remove phpctl.dev references --- README.md | 14 +++++++------- docs/_config.yml | 2 +- tests/install/docker-entrypoint.sh | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9fff05b..fb1f4aa 100644 --- a/README.md +++ b/README.md @@ -45,13 +45,13 @@ In fact, we use it ourselves to develop `phpctl` itself: [devcontainer.json](.de ## Getting started -- [Installation guide](https://phpctl.dev/#installation) -- [How to use](https://phpctl.dev/#usage) -- [Available commands](https://phpctl.dev/commands) -- [The `.phpctlrc` file](https://phpctl.dev/phpctlrc) -- [The `phpctl.ini` file](https://phpctl.dev/phpctlini) -- [Available extensions](https://phpctl.dev/extensions) -- [Why it exists?](https://phpctl.dev/why) +- [Installation guide](./docs/index.md#installation) +- [How to use](./docs/index.md#usage) +- [Available commands](./docs/commands.md) +- [The `.phpctlrc` file](./docs/phpctlrc.md) +- [The `phpctl.ini` file](./docs/phpctlini.md) +- [Available extensions](./docs/extensions.md) +- [Why it exists?](./docs/why.md) ## Contributing Click here to read the [contributing guidelines](CONTRIBUTING.md). diff --git a/docs/_config.yml b/docs/_config.yml index 1245489..245453c 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,7 +1,7 @@ title: phpctl description: Frictionless PHP Development remote_theme: just-the-docs/just-the-docs -url: https://phpctl.dev +url: https://github.com/opencodeco/phpctl repository: https://github.com/opencodeco/phpctl permalink: pretty diff --git a/tests/install/docker-entrypoint.sh b/tests/install/docker-entrypoint.sh index 87af11d..e62e540 100755 --- a/tests/install/docker-entrypoint.sh +++ b/tests/install/docker-entrypoint.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash neofetch -/bin/bash -c "$(curl -fsSL https://phpctl.dev/install.sh)" +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/opencodeco/phpctl/refs/heads/main/docs/install.sh)" echo "" notty phpctl doctor notty php --version