mirror of
https://github.com/ivuorinen/actions.git
synced 2026-01-30 21:40:48 +00:00
Inline version-file-parser logic into language-version-detect to eliminate external dependency and make the action fully self-contained. Changes: - Replace external call to version-file-parser with inline parsing script - Use POSIX sh for maximum compatibility - Streamlined version detection logic focusing on 4 supported languages - Priority: .tool-versions > Dockerfile > devcontainer.json > version files > config files > default Benefits: - No external action dependencies - Faster execution (no action setup overhead) - Easier to maintain and test - Reduced surface area for security issues The action now handles all version detection inline while maintaining the same outputs and functionality.
389 lines
14 KiB
YAML
389 lines
14 KiB
YAML
# yaml-language-server: $schema=https://json.schemastore.org/github-action.json
|
|
# permissions:
|
|
# - contents: read # Required for reading version files
|
|
---
|
|
name: Language Version Detect
|
|
description: 'Detects language version from project configuration files with support for PHP, Python, Go, and .NET.'
|
|
author: 'Ismo Vuorinen'
|
|
|
|
branding:
|
|
icon: code
|
|
color: blue
|
|
|
|
inputs:
|
|
language:
|
|
description: 'Language to detect version for (php, python, go, dotnet)'
|
|
required: true
|
|
default-version:
|
|
description: 'Default version to use if no version is detected'
|
|
required: false
|
|
token:
|
|
description: 'GitHub token for authentication'
|
|
required: false
|
|
default: ''
|
|
|
|
outputs:
|
|
detected-version:
|
|
description: 'Detected or default language version'
|
|
value: ${{ steps.parse-version.outputs.detected-version }}
|
|
package-manager:
|
|
description: 'Detected package manager (python: pip/poetry/pipenv, php: composer)'
|
|
value: ${{ steps.parse-version.outputs.package-manager }}
|
|
|
|
runs:
|
|
using: composite
|
|
steps:
|
|
- name: Validate Inputs
|
|
id: validate
|
|
shell: sh
|
|
env:
|
|
LANGUAGE: ${{ inputs.language }}
|
|
DEFAULT_VERSION: ${{ inputs.default-version }}
|
|
run: |
|
|
set -eu
|
|
|
|
# Validate language parameter
|
|
case "$LANGUAGE" in
|
|
php|python|go|dotnet)
|
|
;;
|
|
*)
|
|
echo "::error::Invalid language: '$LANGUAGE'. Must be one of: php, python, go, dotnet"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Set default version if not provided
|
|
if [ -z "$DEFAULT_VERSION" ]; then
|
|
case "$LANGUAGE" in
|
|
php)
|
|
default="8.4"
|
|
;;
|
|
python)
|
|
default="3.12"
|
|
;;
|
|
go)
|
|
default="1.21"
|
|
;;
|
|
dotnet)
|
|
default="7.0"
|
|
;;
|
|
esac
|
|
printf 'default_version=%s\n' "$default" >> "$GITHUB_OUTPUT"
|
|
else
|
|
printf 'default_version=%s\n' "$DEFAULT_VERSION" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
# Validate version format for specified language
|
|
version="${DEFAULT_VERSION:-$default}"
|
|
|
|
case "$LANGUAGE" in
|
|
php)
|
|
# Validate PHP version format (X.Y or X.Y.Z)
|
|
case "$version" in
|
|
[0-9]*.[0-9]* | [0-9]*.[0-9]*.[0-9]*)
|
|
;;
|
|
*)
|
|
echo "::error::Invalid PHP version format: '$version'. Expected format: X.Y or X.Y.Z (e.g., 8.4, 8.3.1)"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Check for reasonable PHP version range
|
|
major_version=$(echo "$version" | cut -d'.' -f1)
|
|
if [ "$major_version" -lt 7 ] || [ "$major_version" -gt 9 ]; then
|
|
echo "::error::Invalid PHP version: '$version'. PHP major version should be between 7 and 9"
|
|
exit 1
|
|
fi
|
|
|
|
# Additional validation for PHP 8.x minor versions
|
|
if [ "$major_version" -eq 8 ]; then
|
|
minor_version=$(echo "$version" | cut -d'.' -f2)
|
|
if [ "$minor_version" -gt 4 ]; then
|
|
echo "::error::Invalid PHP 8 version: '$version'. PHP 8 minor version should be between 0 and 4"
|
|
exit 1
|
|
fi
|
|
fi
|
|
;;
|
|
|
|
python)
|
|
# Validate Python version format
|
|
case "$version" in
|
|
[0-9]*.[0-9]* | [0-9]*.[0-9]*.[0-9]*)
|
|
;;
|
|
*)
|
|
echo "::error::Invalid Python version format: '$version'. Expected format: X.Y or X.Y.Z (e.g., 3.12, 3.11.5)"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Check Python major version
|
|
major_version=$(echo "$version" | cut -d'.' -f1)
|
|
if [ "$major_version" -ne 3 ]; then
|
|
echo "::error::Invalid Python version: '$version'. Python major version should be 3"
|
|
exit 1
|
|
fi
|
|
|
|
# Check Python minor version range
|
|
minor_version=$(echo "$version" | cut -d'.' -f2)
|
|
if [ "$minor_version" -lt 8 ] || [ "$minor_version" -gt 15 ]; then
|
|
echo "::error::Invalid Python version: '$version'. Python 3 minor version should be between 8 and 15"
|
|
exit 1
|
|
fi
|
|
;;
|
|
|
|
go)
|
|
# Validate Go version format
|
|
case "$version" in
|
|
[0-9]*.[0-9]* | [0-9]*.[0-9]*.[0-9]*)
|
|
;;
|
|
*)
|
|
echo "::error::Invalid Go version format: '$version'. Expected format: X.Y or X.Y.Z (e.g., 1.21, 1.21.5)"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Check Go major version (must be 1)
|
|
major_version=$(echo "$version" | cut -d'.' -f1)
|
|
if [ "$major_version" -ne 1 ]; then
|
|
echo "::error::Invalid Go version: '$version'. Go major version should be 1"
|
|
exit 1
|
|
fi
|
|
|
|
# Check Go minor version range
|
|
minor_version=$(echo "$version" | cut -d'.' -f2)
|
|
if [ "$minor_version" -lt 16 ] || [ "$minor_version" -gt 30 ]; then
|
|
echo "::error::Invalid Go version: '$version'. Go minor version should be between 16 and 30"
|
|
exit 1
|
|
fi
|
|
;;
|
|
|
|
dotnet)
|
|
# Validate .NET version format
|
|
case "$version" in
|
|
[0-9]* | [0-9]*.[0-9]* | [0-9]*.[0-9]*.[0-9]*)
|
|
;;
|
|
*)
|
|
echo "::error::Invalid .NET version format: '$version'. Expected format: X, X.Y, or X.Y.Z (e.g., 7, 7.0, 7.0.1)"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Check .NET major version range
|
|
major_version=$(echo "$version" | cut -d'.' -f1)
|
|
if [ "$major_version" -lt 3 ] || [ "$major_version" -gt 20 ]; then
|
|
echo "::error::Invalid .NET version: '$version'. .NET major version should be between 3 and 20"
|
|
exit 1
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
echo "Input validation completed successfully for $LANGUAGE version $version"
|
|
|
|
- name: Checkout Repository
|
|
uses: actions/checkout@71cf2267d89c5cb81562390fa70a37fa40b1305e # v6-beta
|
|
with:
|
|
token: ${{ inputs.token || github.token }}
|
|
|
|
- name: Parse Language Version
|
|
id: parse-version
|
|
shell: sh
|
|
env:
|
|
LANGUAGE: ${{ inputs.language }}
|
|
DEFAULT_VERSION: ${{ steps.validate.outputs.default_version || inputs.default-version }}
|
|
run: |
|
|
set -eu
|
|
|
|
# Map language to tool-versions key and dockerfile image
|
|
case "$LANGUAGE" in
|
|
go)
|
|
TOOL_VERSIONS_KEY="golang"
|
|
DOCKERFILE_IMAGE="golang"
|
|
VERSION_FILE=".go-version"
|
|
;;
|
|
php)
|
|
TOOL_VERSIONS_KEY="php"
|
|
DOCKERFILE_IMAGE="php"
|
|
VERSION_FILE=".php-version"
|
|
;;
|
|
python)
|
|
TOOL_VERSIONS_KEY="python"
|
|
DOCKERFILE_IMAGE="python"
|
|
VERSION_FILE=".python-version"
|
|
;;
|
|
dotnet)
|
|
TOOL_VERSIONS_KEY="dotnet"
|
|
DOCKERFILE_IMAGE="dotnet"
|
|
VERSION_FILE=""
|
|
;;
|
|
esac
|
|
|
|
# Function to validate version format
|
|
validate_version() {
|
|
version=$1
|
|
# Use case pattern matching for POSIX compatibility
|
|
case "$version" in
|
|
[0-9]*.[0-9]* | [0-9]*.[0-9]*.[0-9]*)
|
|
return 0
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Function to clean version string
|
|
clean_version() {
|
|
printf '%s' "$1" | sed 's/^[vV]//' | tr -d ' \n\r'
|
|
}
|
|
|
|
# Initialize outputs
|
|
printf 'detected-version=\n' >> "$GITHUB_OUTPUT"
|
|
printf 'package-manager=\n' >> "$GITHUB_OUTPUT"
|
|
|
|
detected_version=""
|
|
detected_package_manager=""
|
|
|
|
# Parse .tool-versions file
|
|
if [ -f .tool-versions ]; then
|
|
echo "Checking .tool-versions for $TOOL_VERSIONS_KEY..." >&2
|
|
version=$(awk "/^$TOOL_VERSIONS_KEY[[:space:]]/ {gsub(/#.*/, \"\"); print \$2; exit}" .tool-versions 2>/dev/null || echo "")
|
|
if [ -n "$version" ]; then
|
|
version=$(clean_version "$version")
|
|
if validate_version "$version"; then
|
|
echo "Found $LANGUAGE version in .tool-versions: $version" >&2
|
|
detected_version="$version"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Parse Dockerfile
|
|
if [ -z "$detected_version" ] && [ -f Dockerfile ]; then
|
|
echo "Checking Dockerfile for $DOCKERFILE_IMAGE..." >&2
|
|
version=$(grep -iF "FROM" Dockerfile | grep -F "$DOCKERFILE_IMAGE:" | head -1 | \
|
|
sed -n "s/.*$DOCKERFILE_IMAGE:\([0-9]\+\(\.[0-9]\+\)*\)\(-[^:]*\)\?.*/\1/p" || echo "")
|
|
if [ -n "$version" ]; then
|
|
version=$(clean_version "$version")
|
|
if validate_version "$version"; then
|
|
echo "Found $LANGUAGE version in Dockerfile: $version" >&2
|
|
detected_version="$version"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Parse devcontainer.json
|
|
if [ -z "$detected_version" ] && [ -f .devcontainer/devcontainer.json ]; then
|
|
echo "Checking devcontainer.json for $DOCKERFILE_IMAGE..." >&2
|
|
if command -v jq >/dev/null 2>&1; then
|
|
version=$(jq -r '.image // empty' .devcontainer/devcontainer.json 2>/dev/null | sed -n "s/.*$DOCKERFILE_IMAGE:\([0-9]\+\(\.[0-9]\+\)*\)\(-[^:]*\)\?.*/\1/p" || echo "")
|
|
if [ -n "$version" ]; then
|
|
version=$(clean_version "$version")
|
|
if validate_version "$version"; then
|
|
echo "Found $LANGUAGE version in devcontainer: $version" >&2
|
|
detected_version="$version"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Parse language-specific version file
|
|
if [ -z "$detected_version" ] && [ -n "$VERSION_FILE" ] && [ -f "$VERSION_FILE" ]; then
|
|
echo "Checking $VERSION_FILE..." >&2
|
|
version=$(tr -d '\r' < "$VERSION_FILE" | head -1)
|
|
if [ -n "$version" ]; then
|
|
version=$(clean_version "$version")
|
|
if validate_version "$version"; then
|
|
echo "Found $LANGUAGE version in $VERSION_FILE: $version" >&2
|
|
detected_version="$version"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Parse language-specific configuration files
|
|
if [ -z "$detected_version" ]; then
|
|
case "$LANGUAGE" in
|
|
php)
|
|
# Check composer.json
|
|
if [ -f composer.json ] && command -v jq >/dev/null 2>&1; then
|
|
version=$(jq -r '.require.php // empty' composer.json 2>/dev/null | sed -n 's/[^0-9]*\([0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?\).*/\1/p')
|
|
if [ -z "$version" ]; then
|
|
version=$(jq -r '.config.platform.php // empty' composer.json 2>/dev/null | sed -n 's/[^0-9]*\([0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?\).*/\1/p')
|
|
fi
|
|
if [ -n "$version" ] && validate_version "$version"; then
|
|
echo "Found PHP version in composer.json: $version" >&2
|
|
detected_version="$version"
|
|
fi
|
|
fi
|
|
# Detect package manager
|
|
if [ -f composer.json ]; then
|
|
detected_package_manager="composer"
|
|
fi
|
|
;;
|
|
|
|
python)
|
|
# Check pyproject.toml
|
|
if [ -f pyproject.toml ]; then
|
|
if grep -q '^\[project\]' pyproject.toml; then
|
|
version=$(grep -A 20 '^\[project\]' pyproject.toml | grep -E '^\s*requires-python[[:space:]]*=' | sed -n 's/[^0-9]*\([0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?\).*/\1/p' | head -1)
|
|
if [ -n "$version" ] && validate_version "$version"; then
|
|
echo "Found Python version in pyproject.toml: $version" >&2
|
|
detected_version="$version"
|
|
fi
|
|
fi
|
|
fi
|
|
# Detect package manager
|
|
if [ -f pyproject.toml ] && grep -q '\[tool\.poetry\]' pyproject.toml; then
|
|
detected_package_manager="poetry"
|
|
elif [ -f Pipfile ]; then
|
|
detected_package_manager="pipenv"
|
|
else
|
|
detected_package_manager="pip"
|
|
fi
|
|
;;
|
|
|
|
go)
|
|
# Check go.mod
|
|
if [ -f go.mod ]; then
|
|
version=$(grep -E '^go[[:space:]]+[0-9]' go.mod | awk '{print $2}' | head -1 || echo "")
|
|
if [ -n "$version" ] && validate_version "$version"; then
|
|
echo "Found Go version in go.mod: $version" >&2
|
|
detected_version="$version"
|
|
fi
|
|
detected_package_manager="go"
|
|
fi
|
|
;;
|
|
|
|
dotnet)
|
|
# Check global.json
|
|
if [ -f global.json ] && command -v jq >/dev/null 2>&1; then
|
|
version=$(jq -r '.sdk.version // empty' global.json 2>/dev/null || echo "")
|
|
if [ -n "$version" ] && validate_version "$version"; then
|
|
echo "Found .NET version in global.json: $version" >&2
|
|
detected_version="$version"
|
|
fi
|
|
fi
|
|
detected_package_manager="dotnet"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
# Use default version if nothing detected
|
|
if [ -z "$detected_version" ]; then
|
|
if [ -n "$DEFAULT_VERSION" ]; then
|
|
detected_version="$DEFAULT_VERSION"
|
|
echo "Using default $LANGUAGE version: $detected_version" >&2
|
|
else
|
|
echo "No $LANGUAGE version detected and no default provided" >&2
|
|
fi
|
|
fi
|
|
|
|
# Set outputs
|
|
if [ -n "$detected_version" ]; then
|
|
printf 'detected-version=%s\n' "$detected_version" >> "$GITHUB_OUTPUT"
|
|
echo "Final detected $LANGUAGE version: $detected_version" >&2
|
|
fi
|
|
|
|
if [ -n "$detected_package_manager" ]; then
|
|
printf 'package-manager=%s\n' "$detected_package_manager" >> "$GITHUB_OUTPUT"
|
|
echo "Detected package manager: $detected_package_manager" >&2
|
|
fi
|