# yaml-language-server: $schema=https://json.schemastore.org/github-action.json # permissions: # - contents: read # Required for reading version files --- name: Language Version Detect description: 'DEPRECATED: This action is deprecated. Inline version detection directly in your actions instead. Detects language version from project configuration files with support for PHP, Python, Go, and .NET.' author: 'Ismo Vuorinen' deprecated: true 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