Skip to content

Commit 6eca498

Browse files
committed
Enhance get-php-versions.sh with DockerHub validation and fallback mechanism
- Added functionality to validate PHP versions against DockerHub. - Implemented a fallback to the previous patch version if the current version is unavailable. - Introduced a new option `--skip-dockerhub-validation` to bypass validation for testing. - Updated usage documentation to reflect new features and options.
1 parent 947ac7e commit 6eca498

File tree

1 file changed

+191
-2
lines changed

1 file changed

+191
-2
lines changed

scripts/get-php-versions.sh

Lines changed: 191 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,151 @@
11
#!/bin/bash
22
###################################################
3-
# Usage: get-php-versions.sh [--skip-download]
3+
# Usage: get-php-versions.sh [--skip-download] [--skip-dockerhub-validation]
44
###################################################
55
# This file takes the official latest PHP releases from php.net merges them with our
66
# "base php configuration". These files get merged into a final file called "php-versions.yml"
77
# which is used to build our GitHub Actions jobs.
88
#
9+
# 🔍 DOCKERHUB VALIDATION & FALLBACK
10+
# By default, this script validates that each PHP version from php.net is actually available
11+
# on DockerHub before including it in the final configuration. If a version is not available:
12+
# 1. The script attempts to fall back to the previous patch version (e.g., 8.3.24 -> 8.3.23)
13+
# 2. A GitHub Actions warning is displayed explaining the fallback
14+
# 3. If the fallback version is also unavailable, the script exits with an error
15+
#
16+
# This ensures that Docker builds won't fail due to non-existent base images.
17+
#
918
# 👉 REQUIRED FILES
1019
# - BASE_PHP_VERSIONS_CONFIG_FILE must be valid and set to a valid file path
1120
# (defaults to scripts/conf/php-versions-base-config.yml)
21+
#
22+
# 👉 OPTIONS
23+
# --skip-download: Skip downloading from php.net and use existing base config
24+
# --skip-dockerhub-validation: Skip DockerHub validation (useful for testing/development)
1225

1326
set -oue pipefail
1427

1528
# Uncomment below for step-by-step execution
1629
# set -x
1730
# trap read DEBUG
1831

32+
##########################
33+
# DockerHub API Functions
34+
35+
# Check if a PHP version exists on DockerHub
36+
check_dockerhub_php_version() {
37+
local version="$1"
38+
local variant="${2:-cli}"
39+
local os="${3:-}"
40+
41+
local image_tag
42+
if [ -n "$os" ] && [ "$os" != "bullseye" ] && [ "$os" != "bookworm" ]; then
43+
image_tag="${version}-${variant}-${os}"
44+
else
45+
image_tag="${version}-${variant}"
46+
fi
47+
48+
# Use Docker Hub API v2 to check if the tag exists with timeout and retry
49+
local response
50+
local max_retries=3
51+
local retry_count=0
52+
53+
while [ $retry_count -lt $max_retries ]; do
54+
response=$(curl -s --max-time 10 --connect-timeout 5 \
55+
-o /dev/null -w "%{http_code}" \
56+
"https://registry.hub.docker.com/v2/repositories/library/php/tags/${image_tag}/")
57+
58+
# Check if we got a valid HTTP response
59+
if [ "$response" = "200" ]; then
60+
return 0 # Version exists
61+
elif [ "$response" = "404" ]; then
62+
return 1 # Version definitely does not exist
63+
else
64+
# Network error or other issue, retry
65+
retry_count=$((retry_count + 1))
66+
if [ $retry_count -lt $max_retries ]; then
67+
echo_color_message yellow "⚠️ DockerHub API request failed (HTTP $response), retrying in 2 seconds..."
68+
sleep 2
69+
fi
70+
fi
71+
done
72+
73+
# If we get here, all retries failed
74+
echo_color_message red "❌ Failed to check DockerHub after $max_retries attempts for $image_tag"
75+
return 1
76+
}
77+
78+
# Get previous patch version (e.g., 8.3.24 -> 8.3.23)
79+
get_previous_patch_version() {
80+
local version="$1"
81+
local major_minor patch
82+
83+
# Split version into major.minor and patch
84+
major_minor=$(echo "$version" | cut -d'.' -f1-2)
85+
patch=$(echo "$version" | cut -d'.' -f3)
86+
87+
# Decrement patch version
88+
if [ "$patch" -gt 0 ]; then
89+
patch=$((patch - 1))
90+
echo "${major_minor}.${patch}"
91+
else
92+
# If patch is 0, we can't go lower
93+
return 1
94+
fi
95+
}
96+
97+
# Validate and potentially fallback a PHP version
98+
validate_php_version_with_fallback() {
99+
local version="$1"
100+
local original_version="$version"
101+
local fallback_attempted=false
102+
103+
echo_color_message yellow "🔍 Checking PHP version $version on DockerHub..." >&2
104+
105+
# Check if the version exists on DockerHub (using cli variant as reference)
106+
if check_dockerhub_php_version "$version" "cli"; then
107+
echo_color_message green "✅ PHP $version is available on DockerHub" >&2
108+
echo "$version" # Output to stdout for capture
109+
return 0
110+
else
111+
echo_color_message red "❌ PHP $version is not available on DockerHub" >&2
112+
113+
# Try to get previous patch version
114+
local fallback_version
115+
if fallback_version=$(get_previous_patch_version "$version"); then
116+
fallback_attempted=true
117+
echo_color_message yellow "⚠️ Attempting fallback to PHP $fallback_version..." >&2
118+
119+
if check_dockerhub_php_version "$fallback_version" "cli"; then
120+
echo_color_message yellow "::warning title=PHP Version Fallback::PHP $original_version is not available on DockerHub. Falling back to PHP $fallback_version. This may indicate that DockerHub has not yet published the latest PHP release. Consider checking DockerHub availability before updating to newer versions." >&2
121+
echo_color_message green "✅ Fallback successful: Using PHP $fallback_version" >&2
122+
echo "$fallback_version" # Output to stdout for capture
123+
return 0
124+
else
125+
echo_color_message red "❌ Fallback version PHP $fallback_version is also not available on DockerHub" >&2
126+
fi
127+
fi
128+
129+
# If we get here, both original and fallback failed
130+
if [ "$fallback_attempted" = true ]; then
131+
echo_color_message red "::error title=PHP Version Unavailable::Neither PHP $original_version nor fallback version $fallback_version are available on DockerHub. This suggests a significant lag in DockerHub publishing or a configuration issue. Please check DockerHub manually and consider using a known working version." >&2
132+
else
133+
echo_color_message red "::error title=PHP Version Unavailable::PHP $original_version is not available on DockerHub and no fallback version could be determined (patch version is 0). Please check DockerHub manually and use a known working version." >&2
134+
fi
135+
136+
return 1
137+
fi
138+
}
139+
19140
##########################
20141
# Argument Parsing
21142

22143
SKIP_DOWNLOAD="${SKIP_DOWNLOAD:-false}"
144+
SKIP_DOCKERHUB_VALIDATION="${SKIP_DOCKERHUB_VALIDATION:-false}"
23145
while [[ "$#" -gt 0 ]]; do
24146
case $1 in
25147
--skip-download) SKIP_DOWNLOAD=true ;;
148+
--skip-dockerhub-validation) SKIP_DOCKERHUB_VALIDATION=true ;;
26149
*) echo "Unknown parameter passed: $1"; exit 1 ;;
27150
esac
28151
shift
@@ -76,8 +199,74 @@ if [ "$SKIP_DOWNLOAD" = false ]; then
76199
# Fetch the JSON from the PHP website
77200
php_net_version_json=$(curl -s $PHP_VERSIONS_ACTIVE_JSON_FEED)
78201

202+
# Parse the fetched JSON data and optionally validate PHP versions on DockerHub
203+
if [ "$SKIP_DOCKERHUB_VALIDATION" = true ]; then
204+
echo_color_message yellow "⚠️ Skipping DockerHub validation as requested..."
205+
processed_json="$php_net_version_json"
206+
else
207+
echo_color_message yellow "🔍 Parsing and validating PHP versions from php.net..."
208+
209+
# First, extract versions from the JSON
210+
php_versions_raw=$(echo "$php_net_version_json" | jq -r "
211+
. as \$major |
212+
to_entries[] |
213+
.value |
214+
to_entries[] |
215+
.value.version" | grep -v "null" | sort -u)
216+
217+
# Create temporary files to store validation results
218+
validated_versions_file=$(mktemp)
219+
version_map_file=$(mktemp)
220+
221+
# Validate each version
222+
validation_failed=false
223+
while IFS= read -r version; do
224+
if [ -n "$version" ]; then
225+
echo_color_message yellow "🔍 Validating PHP $version..."
226+
# Capture validation result without color codes
227+
if validated_version=$(validate_php_version_with_fallback "$version" 2>/dev/null | tail -n1); then
228+
# Double check that we got a valid version back
229+
if [ -n "$validated_version" ] && [ "$validated_version" != "VALIDATION_FAILED" ]; then
230+
echo "$version:$validated_version" >> "$validated_versions_file"
231+
if [ "$version" != "$validated_version" ]; then
232+
# Escape special characters for sed
233+
escaped_original=$(echo "$version" | sed 's/[[\.*^$(){}?+|/]/\\&/g')
234+
escaped_validated=$(echo "$validated_version" | sed 's/[[\.*^$(){}?+|/]/\\&/g')
235+
echo "s/${escaped_original}/${escaped_validated}/g" >> "$version_map_file"
236+
fi
237+
else
238+
echo_color_message red "❌ Validation failed for PHP $version"
239+
validation_failed=true
240+
fi
241+
else
242+
echo_color_message red "❌ Validation failed for PHP $version"
243+
validation_failed=true
244+
fi
245+
fi
246+
done <<< "$php_versions_raw"
247+
248+
# Exit if any validation failed
249+
if [ "$validation_failed" = true ]; then
250+
echo_color_message red "❌ One or more PHP versions failed validation. Stopping build."
251+
rm -f "$validated_versions_file" "$version_map_file"
252+
exit 1
253+
fi
254+
255+
# Apply version substitutions if any fallbacks were used
256+
processed_json="$php_net_version_json"
257+
if [ -s "$version_map_file" ]; then
258+
echo_color_message yellow "📝 Applying version fallbacks..."
259+
while IFS= read -r substitution; do
260+
processed_json=$(echo "$processed_json" | sed "$substitution")
261+
done < "$version_map_file"
262+
fi
263+
264+
# Clean up temporary files
265+
rm -f "$validated_versions_file" "$version_map_file"
266+
fi
267+
79268
# Parse the fetched JSON data and transform it to a specific YAML structure using jq and yq.
80-
php_net_yaml_data=$(echo "$php_net_version_json" | jq -r "
269+
php_net_yaml_data=$(echo "$processed_json" | jq -r "
81270
{
82271
\"php_versions\": [
83272
. as \$major |

0 commit comments

Comments
 (0)