Skip to content

Improve install/uninstall scripts for non-root #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[*.sh]
charset = utf-8
trim_trailing_whitespace = true
indent_size = 4
indent_style = space
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
2 changes: 1 addition & 1 deletion docs/_config.yml
Original file line number Diff line number Diff line change
@@ -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

Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ 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.

#### 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
Expand Down
204 changes: 179 additions & 25 deletions docs/install.sh
Original file line number Diff line number Diff line change
@@ -1,46 +1,200 @@
#!/usr/bin/env bash

INSTALL_DIR=~/.phpctl
if [ -z "$1" ]; then
SUDO=sudo
SYMLINK_DIR=/usr/local/bin
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=""

# --- 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 <directory> Set the installation directory (default: $HOME/.phpctl)"
echo " -s <directory> Set the symlink directory (default: /usr/local/bin)"
echo " -l <directory> 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

set_colors

# 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

ensure_dir

if [[ ! -w "${SYMLINK_DIR}" ]]; then
SUDO="sudo"
else
SUDO=""
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 [[ -n $SUDO ]]; then
echo "Running in elevated mode. This might require sudo for operations in ${SYMLINK_DIR}."
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"
spinner() {
local i=0
local sp="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
while true; do
printf "\r[%s] Cloning repository..." "${sp:i++%${#sp}:1}"
sleep 0.1
done
done
printf "\r"
}

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
}

if [ -z "$1" ]; then
echo -n "Sudo will be prompted to symlink the phpctl files."
else
echo -n "Files will be symlinked to ${SYMLINK_DIR}."
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"

trap cleanup EXIT INT TERM

spinner &
SPINNER_PID=$!
git clone --quiet "$GITHUB_REPO" "$INSTALL_DIR" &
PID=$!
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 1
fi
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 " ${RED}Sudo will be prompted to symlink the phpctl files.${NC}"
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

echo "${GREEN}Installation complete!${NC}"
58 changes: 54 additions & 4 deletions docs/uninstall.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,7 +1,57 @@
#!/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 <directory> Set the installation directory (default: $HOME/.phpctl)"
echo " -s <directory> 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
SUDO="sudo"
else
SUDO=""
fi

if [[ -n $SUDO ]]; then
echo "Running in elevated mode. This might require sudo for operations in ${SYMLINK_DIR}."
fi

LINKS=(
composer
Expand Down Expand Up @@ -31,7 +81,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."
Expand All @@ -48,4 +98,4 @@ else
echo "Skipping removal of installation directory."
fi

echo "Uninstallation complete."
echo "Uninstallation complete."
7 changes: 6 additions & 1 deletion scripts/symlink-bins.sh
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion tests/install/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -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
Expand Down