Skip to content

Commit fe1a3ce

Browse files
authored
Merge pull request #19 from Thavarshan/18-add-self-update-command
Merge PR #19: chore: release v1.12.1 — add self-update command and documentation
2 parents cb9cc3c + 0d1ada2 commit fe1a3ce

5 files changed

Lines changed: 223 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22

33
## [Unreleased]
44

5-
## [v1.12.0](https://github.com/Thavarshan/phpvm/compare/v1.11.0...v1.12.0) - 2026-05-20
5+
## [v1.12.1](https://github.com/Thavarshan/phpvm/compare/v1.12.0...v1.12.1) - 2026-05-24
66

77
### Added
88

9-
- **`latest-remote` keyword:** Install the newest available remote PHP version directly from package manager repos with `phpvm install latest-remote`.
10-
- **`latest-available` alias:** Alias for `latest-remote`.
9+
- **`self-update` command:** Add `phpvm self-update` to automatically update phpvm to the latest stable release and print the updated version.
1110

11+
## [v1.12.0](https://github.com/Thavarshan/phpvm/compare/v1.11.0...v1.12.0) - 2026-05-20
1212

1313
### Added
1414

15+
- **`latest-remote` keyword:** Install the newest available remote PHP version directly from package manager repos with `phpvm install latest-remote`.
16+
- **`latest-available` alias:** Alias for `latest-remote`.
17+
1518
- **`exec` command:** Run a command with a specific PHP version without globally switching. Executes in a subshell to isolate PATH changes from the current shell. Usage: `phpvm exec <version> <command> [args...]`.
1619
- **`run` command:** Sugar for `phpvm exec <version> php <script>`. Usage: `phpvm run <version> [script] [args...]`.
1720
- **`ls-remote` command:** List available PHP versions from system package manager repositories (Homebrew, apt, dnf, yum, pacman). Supports optional pattern filtering (e.g., `phpvm ls-remote 8.2`).
@@ -317,7 +320,7 @@
317320

318321
## [v1.3.0](https://github.com/Thavarshan/phpvm/compare/v1.2.0...v1.3.0) - 2025-05-11
319322

320-
## Added
323+
### Added
321324

322325
- Added `system` command to easily switch back to system PHP version
323326
- Added timestamps to all log messages for better traceability and debugging
@@ -330,7 +333,7 @@
330333
- Added ability to run self-tests with `phpvm test` command
331334
- Added debugging capability via `DEBUG=true` environment variable
332335

333-
## Changed
336+
### Changed
334337

335338
- Changed logging format to include timestamps and log levels
336339
- Changed sudo handling to use a centralized helper function
@@ -341,7 +344,7 @@
341344
- Improved bash/zsh shell compatibility with better sourcing logic
342345
- Improved code organization and reduced duplication with helper functions
343346

344-
## Fixed
347+
### Fixed
345348

346349
- Fixed shell crash issue when sourcing in zsh with p10k theme
347350
- Fixed path expansion issues in Ubuntu bashrc configurations

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
```sh
1616
$ phpvm version
17-
phpvm version 1.11.0
17+
phpvm version 1.12.1
1818

1919
PHP Version Manager for macOS and Linux
2020
Author: Jerome Thayananthajothy <tjthavarshan@gmail.com>
@@ -41,6 +41,7 @@ PHP 8.1.13
4141
- **Run commands with a specific PHP version** without globally switching (`phpvm exec`, `phpvm run`).
4242
- **List available remote PHP versions** from your system package manager (`phpvm ls-remote`).
4343
- **Install the latest available remote PHP version** with `phpvm install latest-remote`.
44+
- **Update phpvm in place** using `phpvm self-update` to pull the latest stable script version.
4445
- **Resolve version descriptors and aliases** to installed version numbers (`phpvm resolve`).
4546
- Auto-switch PHP versions based on project `.phpvmrc` (configurable depth via `PHPVM_PHPVMRC_MAX_DEPTH`).
4647
- Automatic directory-based switching via built-in cd hook (`PROMPT_COMMAND` for bash, `chpwd` for zsh).
@@ -122,6 +123,7 @@ If the installation was successful, it should output the path to `phpvm`.
122123
| `phpvm list` or `phpvm ls` | List all installed PHP versions |
123124
| `phpvm ls-remote [pattern]` | List available remote PHP versions |
124125
| `phpvm install latest-remote` | Install the latest remote PHP version available |
126+
| `phpvm self-update` | Update phpvm to the latest stable release |
125127
| `phpvm resolve <version\|alias>` | Resolve a version descriptor to an installed version |
126128
| `phpvm alias [name] [ver]` | Create, update, or list version aliases |
127129
| `phpvm unalias <name>` | Remove version alias |

phpvm.sh

Lines changed: 134 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# phpvm - A PHP Version Manager for macOS and Linux
44
# Author: Jerome Thayananthajothy (tjthavarshan@gmail.com)
5-
# Version: 1.10.0
5+
# Version: 1.12.1
66
#
77
# IMPORTANT: This script is written for bash and uses bashisms (arrays, process substitution, etc.)
88
# For sourcing into your shell, use bash only. Zsh users should run phpvm via:
@@ -11,7 +11,7 @@
1111

1212
# shellcheck disable=SC2155 # Allow declare and assign on same line for better readability
1313

14-
PHPVM_VERSION="1.12.0"
14+
PHPVM_VERSION="1.12.1"
1515

1616
# Test mode flag
1717
PHPVM_TEST_MODE="${PHPVM_TEST_MODE:-false}"
@@ -2623,6 +2623,7 @@ Usage:
26232623
phpvm alias [name] [ver] Create, update, or list version aliases
26242624
phpvm unalias <name> Remove version alias
26252625
phpvm cache <dir|clear> Manage cache directory
2626+
phpvm self-update Update phpvm to the latest stable version
26262627
phpvm help Show this help message
26272628
phpvm info Show system information for debugging
26282629
phpvm version Show version information
@@ -2678,6 +2679,129 @@ Usage: phpvm help
26782679
EOF
26792680
}
26802681

2682+
phpvm_get_self_update_target() {
2683+
local script_path
2684+
if [ -n "${PHPVM_SELF_UPDATE_DEST:-}" ]; then
2685+
script_path="$PHPVM_SELF_UPDATE_DEST"
2686+
else
2687+
script_path="${BASH_SOURCE[0]:-$0}"
2688+
fi
2689+
2690+
if [ -L "$script_path" ] && command_exists readlink; then
2691+
local link
2692+
link=$(command readlink "$script_path")
2693+
if [ "${link#/}" != "$link" ]; then
2694+
script_path="$link"
2695+
else
2696+
local dir
2697+
dir=$(cd "$(dirname "$script_path")" 2> /dev/null && pwd)
2698+
script_path="$dir/$link"
2699+
fi
2700+
fi
2701+
2702+
printf '%s
2703+
' "$script_path"
2704+
}
2705+
2706+
phpvm_download_url_to_file() {
2707+
local url="$1"
2708+
local output="$2"
2709+
2710+
if command_exists curl; then
2711+
command curl --fail --silent --location "$url" > "$output"
2712+
return "$?"
2713+
fi
2714+
2715+
if command_exists wget; then
2716+
command wget --quiet --output-document="$output" "$url"
2717+
return "$?"
2718+
fi
2719+
2720+
phpvm_err "curl or wget is required for self-update."
2721+
return "$PHPVM_EXIT_ERROR"
2722+
}
2723+
2724+
phpvm_extract_version_from_file() {
2725+
local file="$1"
2726+
local version
2727+
2728+
version=$(command grep -Eo 'PHPVM_VERSION="[0-9]+\.[0-9]+\.[0-9]+"' "$file" 2> /dev/null | command head -1 | command sed 's/PHPVM_VERSION="//;s/"$//')
2729+
if [ -z "$version" ]; then
2730+
return "$PHPVM_EXIT_ERROR"
2731+
fi
2732+
2733+
printf '%s
2734+
' "$version"
2735+
return "$PHPVM_EXIT_SUCCESS"
2736+
}
2737+
2738+
phpvm_self_update() {
2739+
local source_url="${PHPVM_SELF_UPDATE_URL:-https://raw.githubusercontent.com/Thavarshan/phpvm/main/phpvm.sh}"
2740+
local tmp_file
2741+
local remote_version
2742+
local target_script
2743+
2744+
target_script=$(phpvm_get_self_update_target) || return "$PHPVM_EXIT_ERROR"
2745+
if [ -z "$target_script" ]; then
2746+
phpvm_err "Unable to determine phpvm script path for self-update."
2747+
return "$PHPVM_EXIT_ERROR"
2748+
fi
2749+
2750+
tmp_file=$(mktemp) || {
2751+
phpvm_err "Failed to create temporary file for self-update."
2752+
return "$PHPVM_EXIT_FILE_ERROR"
2753+
}
2754+
2755+
if phpvm_is_test_mode && [ -n "${PHPVM_SELF_UPDATE_TEST_SOURCE:-}" ]; then
2756+
if [ ! -f "$PHPVM_SELF_UPDATE_TEST_SOURCE" ]; then
2757+
rm -f "$tmp_file"
2758+
phpvm_err "Self-update test source not found: $PHPVM_SELF_UPDATE_TEST_SOURCE"
2759+
return "$PHPVM_EXIT_FILE_ERROR"
2760+
fi
2761+
command cp "$PHPVM_SELF_UPDATE_TEST_SOURCE" "$tmp_file"
2762+
else
2763+
if ! phpvm_download_url_to_file "$source_url" "$tmp_file"; then
2764+
rm -f "$tmp_file"
2765+
phpvm_err "Failed to download phpvm update from $source_url"
2766+
return "$PHPVM_EXIT_ERROR"
2767+
fi
2768+
fi
2769+
2770+
remote_version=$(phpvm_extract_version_from_file "$tmp_file") || {
2771+
rm -f "$tmp_file"
2772+
phpvm_err "Failed to determine remote phpvm version."
2773+
return "$PHPVM_EXIT_ERROR"
2774+
}
2775+
2776+
if [ "$remote_version" = "$PHPVM_VERSION" ]; then
2777+
rm -f "$tmp_file"
2778+
phpvm_echo "You are already on the latest version: v$PHPVM_VERSION."
2779+
return "$PHPVM_EXIT_SUCCESS"
2780+
fi
2781+
2782+
phpvm_echo "Updating phpvm..."
2783+
2784+
if [ ! -w "$target_script" ] && command_exists sudo; then
2785+
if ! run_with_sudo cp "$tmp_file" "$target_script"; then
2786+
rm -f "$tmp_file"
2787+
phpvm_err "Failed to update phpvm."
2788+
return "$PHPVM_EXIT_ERROR"
2789+
fi
2790+
else
2791+
if ! command cp "$tmp_file" "$target_script"; then
2792+
rm -f "$tmp_file"
2793+
phpvm_err "Failed to update phpvm."
2794+
return "$PHPVM_EXIT_ERROR"
2795+
fi
2796+
fi
2797+
2798+
chmod +x "$target_script" 2> /dev/null || true
2799+
rm -f "$tmp_file"
2800+
2801+
phpvm_echo "phpvm successfully updated to the latest version: v$remote_version."
2802+
return "$PHPVM_EXIT_SUCCESS"
2803+
}
2804+
26812805
# Print system information for debugging
26822806
print_system_info() {
26832807
phpvm_echo "System Information:"
@@ -3283,6 +3407,10 @@ main() {
32833407
print_version
32843408
exit "$PHPVM_EXIT_SUCCESS"
32853409
;;
3410+
self-update)
3411+
phpvm_self_update
3412+
exit "$?"
3413+
;;
32863414
unload)
32873415
phpvm_unload
32883416
exit "$?"
@@ -3405,6 +3533,10 @@ main() {
34053533
phpvm_cache "$@"
34063534
exit "$?"
34073535
;;
3536+
self-update)
3537+
phpvm_self_update
3538+
exit "$?"
3539+
;;
34083540
*)
34093541
phpvm_err "Unknown command: $command"
34103542
print_help

release-notes-1.12.0.md

Lines changed: 0 additions & 6 deletions
This file was deleted.

tests/01_core.bats

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,83 @@ load test_helper
1818
[[ "$output" =~ "Usage:" ]]
1919
}
2020

21+
@test "phpvm self-update reports already latest version" {
22+
local remote_file="$TEST_DIR/phpvm-latest.sh"
23+
cat > "$remote_file" <<'EOF'
24+
#!/bin/bash
25+
PHPVM_VERSION="1.12.1"
26+
EOF
27+
28+
run env PHPVM_TEST_MODE=true PHPVM_SELF_UPDATE_TEST_SOURCE="$remote_file" PHPVM_SELF_UPDATE_DEST="$TEST_DIR/phpvm-self-update-target.sh" bash "$BATS_TEST_DIRNAME/../phpvm.sh" self-update
29+
[ "$status" -eq 0 ]
30+
[[ "$output" =~ "You are already on the latest version: v1.12.1." ]]
31+
}
32+
33+
@test "phpvm self-update replaces script when newer version is available" {
34+
local remote_file="$TEST_DIR/phpvm-updated.sh"
35+
local target_file="$TEST_DIR/phpvm-self-update-target.sh"
36+
cat > "$remote_file" <<'EOF'
37+
#!/bin/bash
38+
PHPVM_VERSION="1.12.2"
39+
EOF
40+
touch "$target_file"
41+
42+
run env PHPVM_TEST_MODE=true PHPVM_SELF_UPDATE_TEST_SOURCE="$remote_file" PHPVM_SELF_UPDATE_DEST="$target_file" bash "$BATS_TEST_DIRNAME/../phpvm.sh" self-update
43+
[ "$status" -eq 0 ]
44+
[[ "$output" =~ "phpvm successfully updated to the latest version: v1.12.2." ]]
45+
[ "$(grep -oE 'PHPVM_VERSION="[0-9]+\.[0-9]+\.[0-9]+"' "$target_file")" = "PHPVM_VERSION=\"1.12.2\"" ]
46+
}
47+
48+
@test "phpvm self-update downloads from remote URL and updates script" {
49+
if ! command -v python3 > /dev/null 2>&1; then
50+
skip "python3 is required for remote self-update integration test"
51+
fi
52+
53+
local server_root="$TEST_DIR/http-server"
54+
local server_log="$TEST_DIR/http-server.log"
55+
local target_file="$TEST_DIR/phpvm-self-update-http-target.sh"
56+
mkdir -p "$server_root"
57+
58+
cat > "$server_root/phpvm.sh" <<'EOF'
59+
#!/bin/bash
60+
PHPVM_VERSION="1.12.3"
61+
EOF
62+
chmod +x "$server_root/phpvm.sh"
63+
64+
python3 - "$server_root" <<'PY' > "$server_log" 2>&1 &
65+
import http.server
66+
import socketserver
67+
import os
68+
import sys
69+
70+
os.chdir(sys.argv[1])
71+
with socketserver.TCPServer(("127.0.0.1", 0), http.server.SimpleHTTPRequestHandler) as httpd:
72+
print(httpd.server_address[1], flush=True)
73+
sys.stdout.flush()
74+
httpd.serve_forever()
75+
PY
76+
local server_pid=$!
77+
sleep 1
78+
79+
local port
80+
port=$(head -n 1 "$server_log" | tr -d '[:space:]')
81+
if [ -z "$port" ]; then
82+
kill "$server_pid" > /dev/null 2>&1 || true
83+
wait "$server_pid" 2>/dev/null || true
84+
echo "Failed to start local HTTP server" >&2
85+
return 1
86+
fi
87+
88+
touch "$target_file"
89+
90+
trap 'kill "$server_pid" > /dev/null 2>&1 || true; wait "$server_pid" 2>/dev/null || true' RETURN
91+
92+
run env PHPVM_TEST_MODE=true PHPVM_SELF_UPDATE_URL="http://127.0.0.1:$port/phpvm.sh" PHPVM_SELF_UPDATE_DEST="$target_file" bash "$BATS_TEST_DIRNAME/../phpvm.sh" self-update
93+
[ "$status" -eq 0 ]
94+
[[ "$output" =~ "phpvm successfully updated to the latest version: v1.12.3." ]]
95+
[ "$(grep -oE 'PHPVM_VERSION="[0-9]+\.[0-9]+\.[0-9]+"' "$target_file")" = "PHPVM_VERSION=\"1.12.3\"" ]
96+
}
97+
2198
@test "sanitize_input rejects dangerous characters" {
2299
run sanitize_input "8.1; rm -rf /"
23100
[ "$status" -ne 0 ]

0 commit comments

Comments
 (0)