mirror of
https://github.com/ivuorinen/actions.git
synced 2026-02-13 10:47:19 +00:00
refactor: remove deprecated version-file-parser action
Remove version-file-parser after successful inlining into node-setup: - Delete version-file-parser action directory - Delete version-file-parser unit and integration tests - Remove version-file-parser references from spec_helper.sh - Remove version-file-parser path trigger from node-setup-test.yml - Regenerate action catalog (29 actions, down from 30) All version detection functionality now inlined into individual actions: - go-build: Go version detection - csharp-build/csharp-lint-check/csharp-publish: .NET version detection - python-lint-fix: Python version detection - php-laravel-phpunit: PHP version detection - node-setup: Node.js version detection and package manager detection Reduces external dependencies and improves initialization performance across all actions.
This commit is contained in:
18
README.md
18
README.md
@@ -22,9 +22,9 @@ Each action is fully self-contained and can be used independently in any GitHub
|
|||||||
|
|
||||||
## 📚 Action Catalog
|
## 📚 Action Catalog
|
||||||
|
|
||||||
This repository contains **30 reusable GitHub Actions** for CI/CD automation.
|
This repository contains **29 reusable GitHub Actions** for CI/CD automation.
|
||||||
|
|
||||||
### Quick Reference (30 Actions)
|
### Quick Reference (29 Actions)
|
||||||
|
|
||||||
| Icon | Action | Category | Description | Key Features |
|
| Icon | Action | Category | Description | Key Features |
|
||||||
|:----:|:-----------------------------------------------------|:-----------|:----------------------------------------------------------------|:---------------------------------------------|
|
|:----:|:-----------------------------------------------------|:-----------|:----------------------------------------------------------------|:---------------------------------------------|
|
||||||
@@ -57,7 +57,6 @@ This repository contains **30 reusable GitHub Actions** for CI/CD automation.
|
|||||||
| 🏷️ | [`sync-labels`][sync-labels] | Repository | Sync labels from a YAML file to a GitHub repository | Token auth, Outputs |
|
| 🏷️ | [`sync-labels`][sync-labels] | Repository | Sync labels from a YAML file to a GitHub repository | Token auth, Outputs |
|
||||||
| 🖥️ | [`terraform-lint-fix`][terraform-lint-fix] | Linting | Lints and fixes Terraform files with advanced validation and... | Token auth, Outputs |
|
| 🖥️ | [`terraform-lint-fix`][terraform-lint-fix] | Linting | Lints and fixes Terraform files with advanced validation and... | Token auth, Outputs |
|
||||||
| 🛡️ | [`validate-inputs`][validate-inputs] | Validation | Centralized Python-based input validation for GitHub Actions... | Token auth, Outputs |
|
| 🛡️ | [`validate-inputs`][validate-inputs] | Validation | Centralized Python-based input validation for GitHub Actions... | Token auth, Outputs |
|
||||||
| 📦 | [`version-file-parser`][version-file-parser] | Utilities | Universal parser for common version detection files (.tool-v... | Auto-detection, Outputs |
|
|
||||||
|
|
||||||
### Actions by Category
|
### Actions by Category
|
||||||
|
|
||||||
@@ -68,12 +67,11 @@ This repository contains **30 reusable GitHub Actions** for CI/CD automation.
|
|||||||
| 📝 [`language-version-detect`][language-version-detect] | Detects language version from project configuratio... | PHP, Python, Go, .NET, Node.js | Auto-detection, Token auth, Outputs |
|
| 📝 [`language-version-detect`][language-version-detect] | Detects language version from project configuratio... | PHP, Python, Go, .NET, Node.js | Auto-detection, Token auth, Outputs |
|
||||||
| 🖥️ [`node-setup`][node-setup] | Sets up Node.js environment with version detection... | Node.js, JavaScript, TypeScript | Auto-detection, Token auth, Outputs |
|
| 🖥️ [`node-setup`][node-setup] | Sets up Node.js environment with version detection... | Node.js, JavaScript, TypeScript | Auto-detection, Token auth, Outputs |
|
||||||
|
|
||||||
#### 🛠️ Utilities (2 actions)
|
#### 🛠️ Utilities (1 action)
|
||||||
|
|
||||||
| Action | Description | Languages | Features |
|
| Action | Description | Languages | Features |
|
||||||
|:------------------------------------------------|:------------------------------------------------------|:-------------------|:------------------------|
|
|:--------------------------------------------|:------------------------------------------------------|:---------------|:--------------------|
|
||||||
| 🔀 [`action-versioning`][action-versioning] | Automatically update SHA-pinned action references ... | GitHub Actions | Token auth, Outputs |
|
| 🔀 [`action-versioning`][action-versioning] | Automatically update SHA-pinned action references ... | GitHub Actions | Token auth, Outputs |
|
||||||
| 📦 [`version-file-parser`][version-file-parser] | Universal parser for common version detection file... | Multiple Languages | Auto-detection, Outputs |
|
|
||||||
|
|
||||||
#### 📝 Linting (10 actions)
|
#### 📝 Linting (10 actions)
|
||||||
|
|
||||||
@@ -164,7 +162,6 @@ This repository contains **30 reusable GitHub Actions** for CI/CD automation.
|
|||||||
| [`sync-labels`][sync-labels] | - | - | ✅ | ✅ |
|
| [`sync-labels`][sync-labels] | - | - | ✅ | ✅ |
|
||||||
| [`terraform-lint-fix`][terraform-lint-fix] | - | - | ✅ | ✅ |
|
| [`terraform-lint-fix`][terraform-lint-fix] | - | - | ✅ | ✅ |
|
||||||
| [`validate-inputs`][validate-inputs] | - | - | ✅ | ✅ |
|
| [`validate-inputs`][validate-inputs] | - | - | ✅ | ✅ |
|
||||||
| [`version-file-parser`][version-file-parser] | - | ✅ | - | ✅ |
|
|
||||||
|
|
||||||
### Language Support
|
### Language Support
|
||||||
|
|
||||||
@@ -188,7 +185,7 @@ This repository contains **30 reusable GitHub Actions** for CI/CD automation.
|
|||||||
| JavaScript | [`biome-lint`][biome-lint], [`codeql-analysis`][codeql-analysis], [`eslint-lint`][eslint-lint], [`node-setup`][node-setup], [`prettier-lint`][prettier-lint] |
|
| JavaScript | [`biome-lint`][biome-lint], [`codeql-analysis`][codeql-analysis], [`eslint-lint`][eslint-lint], [`node-setup`][node-setup], [`prettier-lint`][prettier-lint] |
|
||||||
| Laravel | [`php-laravel-phpunit`][php-laravel-phpunit] |
|
| Laravel | [`php-laravel-phpunit`][php-laravel-phpunit] |
|
||||||
| Markdown | [`prettier-lint`][prettier-lint] |
|
| Markdown | [`prettier-lint`][prettier-lint] |
|
||||||
| Multiple Languages | [`pre-commit`][pre-commit], [`version-file-parser`][version-file-parser] |
|
| Multiple Languages | [`pre-commit`][pre-commit] |
|
||||||
| Node.js | [`language-version-detect`][language-version-detect], [`node-setup`][node-setup], [`npm-publish`][npm-publish] |
|
| Node.js | [`language-version-detect`][language-version-detect], [`node-setup`][node-setup], [`npm-publish`][npm-publish] |
|
||||||
| PHP | [`language-version-detect`][language-version-detect], [`php-composer`][php-composer], [`php-laravel-phpunit`][php-laravel-phpunit], [`php-tests`][php-tests] |
|
| PHP | [`language-version-detect`][language-version-detect], [`php-composer`][php-composer], [`php-laravel-phpunit`][php-laravel-phpunit], [`php-tests`][php-tests] |
|
||||||
| PNG | [`compress-images`][compress-images] |
|
| PNG | [`compress-images`][compress-images] |
|
||||||
@@ -248,7 +245,6 @@ All actions can be used independently in your workflows:
|
|||||||
[sync-labels]: sync-labels/README.md
|
[sync-labels]: sync-labels/README.md
|
||||||
[terraform-lint-fix]: terraform-lint-fix/README.md
|
[terraform-lint-fix]: terraform-lint-fix/README.md
|
||||||
[validate-inputs]: validate-inputs/README.md
|
[validate-inputs]: validate-inputs/README.md
|
||||||
[version-file-parser]: version-file-parser/README.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'node-setup/**'
|
- 'node-setup/**'
|
||||||
- 'version-file-parser/**'
|
|
||||||
- 'common-cache/**'
|
- 'common-cache/**'
|
||||||
- 'common-retry/**'
|
|
||||||
- '_tests/integration/workflows/node-setup-test.yml'
|
- '_tests/integration/workflows/node-setup-test.yml'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
@@ -1,241 +0,0 @@
|
|||||||
---
|
|
||||||
name: Test version-file-parser Integration
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- 'version-file-parser/**'
|
|
||||||
- '_tests/integration/workflows/version-file-parser-test.yml'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test-version-file-parser:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
test-case:
|
|
||||||
- name: 'Node.js project'
|
|
||||||
language: 'node'
|
|
||||||
tool-versions-key: 'nodejs'
|
|
||||||
dockerfile-image: 'node'
|
|
||||||
expected-version: '18.0.0'
|
|
||||||
setup-files: |
|
|
||||||
echo "18.17.0" > .nvmrc
|
|
||||||
cat > package.json <<EOF
|
|
||||||
{
|
|
||||||
"name": "test-project",
|
|
||||||
"engines": { "node": ">=18.0.0" }
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
touch package-lock.json
|
|
||||||
|
|
||||||
- name: 'PHP project'
|
|
||||||
language: 'php'
|
|
||||||
tool-versions-key: 'php'
|
|
||||||
dockerfile-image: 'php'
|
|
||||||
expected-version: '8.1'
|
|
||||||
setup-files: |
|
|
||||||
cat > composer.json <<EOF
|
|
||||||
{
|
|
||||||
"require": { "php": "^8.1" }
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: 'Python project'
|
|
||||||
language: 'python'
|
|
||||||
tool-versions-key: 'python'
|
|
||||||
dockerfile-image: 'python'
|
|
||||||
expected-version: '3.9'
|
|
||||||
setup-files: |
|
|
||||||
echo "3.9.0" > .python-version
|
|
||||||
cat > pyproject.toml <<EOF
|
|
||||||
[tool.poetry.dependencies]
|
|
||||||
python = "^3.9"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: 'Go project'
|
|
||||||
language: 'go'
|
|
||||||
tool-versions-key: 'golang'
|
|
||||||
dockerfile-image: 'golang'
|
|
||||||
expected-version: '1.21'
|
|
||||||
setup-files: |
|
|
||||||
cat > go.mod <<EOF
|
|
||||||
module test-project
|
|
||||||
go 1.21
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: '.tool-versions file'
|
|
||||||
language: 'node'
|
|
||||||
tool-versions-key: 'nodejs'
|
|
||||||
dockerfile-image: 'node'
|
|
||||||
expected-version: '18.16.0'
|
|
||||||
setup-files: |
|
|
||||||
echo "nodejs 18.16.0" > .tool-versions
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Clean up test files from previous runs
|
|
||||||
run: |
|
|
||||||
rm -f .nvmrc package.json package-lock.json composer.json .python-version pyproject.toml go.mod .tool-versions
|
|
||||||
|
|
||||||
- name: Setup test files
|
|
||||||
run: ${{ matrix.test-case.setup-files }}
|
|
||||||
|
|
||||||
- name: Test version-file-parser
|
|
||||||
id: test-action
|
|
||||||
uses: ./version-file-parser
|
|
||||||
with:
|
|
||||||
language: ${{ matrix.test-case.language }}
|
|
||||||
tool-versions-key: ${{ matrix.test-case.tool-versions-key }}
|
|
||||||
dockerfile-image: ${{ matrix.test-case.dockerfile-image }}
|
|
||||||
default-version: '1.0.0'
|
|
||||||
|
|
||||||
- name: Validate outputs
|
|
||||||
run: |
|
|
||||||
echo "Test case: ${{ matrix.test-case.name }}"
|
|
||||||
echo "Expected version: ${{ matrix.test-case.expected-version }}"
|
|
||||||
echo "Detected version: ${{ steps.test-action.outputs.detected-version }}"
|
|
||||||
echo "Package manager: ${{ steps.test-action.outputs.package-manager }}"
|
|
||||||
|
|
||||||
# Validate that we got some version
|
|
||||||
if [[ -z "${{ steps.test-action.outputs.detected-version }}" ]]; then
|
|
||||||
echo "❌ ERROR: No version detected"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Validate version format (basic semver check)
|
|
||||||
if ! echo "${{ steps.test-action.outputs.detected-version }}" | grep -E '^[0-9]+\.[0-9]+(\.[0-9]+)?'; then
|
|
||||||
echo "❌ ERROR: Invalid version format: ${{ steps.test-action.outputs.detected-version }}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Validate detected version matches expected version (not the fallback)
|
|
||||||
if [[ "${{ steps.test-action.outputs.detected-version }}" != "${{ matrix.test-case.expected-version }}" ]]; then
|
|
||||||
echo "❌ ERROR: Version mismatch"
|
|
||||||
echo "Expected: ${{ matrix.test-case.expected-version }}"
|
|
||||||
echo "Got: ${{ steps.test-action.outputs.detected-version }}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "✅ Version validation passed"
|
|
||||||
|
|
||||||
# Skip external reference test in local/CI environment to avoid auth issues
|
|
||||||
- name: Test external reference (info only)
|
|
||||||
run: |
|
|
||||||
echo "External reference test would use: ivuorinen/actions/version-file-parser@main"
|
|
||||||
echo "Skipping to avoid authentication issues in local testing"
|
|
||||||
|
|
||||||
test-edge-cases:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Clean up test files from previous runs
|
|
||||||
run: |
|
|
||||||
rm -f .nvmrc package.json package-lock.json composer.json .python-version pyproject.toml go.mod .tool-versions
|
|
||||||
|
|
||||||
- name: Setup test files (package.json engines)
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -Eeuo pipefail
|
|
||||||
cat > package.json <<'EOF'
|
|
||||||
{
|
|
||||||
"name": "edge-case",
|
|
||||||
"engines": { "node": ">=18.0.0" }
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
echo "18.17.0" > .nvmrc
|
|
||||||
|
|
||||||
- name: Test version detection from existing files
|
|
||||||
id: existing-version
|
|
||||||
uses: ./version-file-parser
|
|
||||||
with:
|
|
||||||
language: 'node'
|
|
||||||
tool-versions-key: 'nodejs'
|
|
||||||
dockerfile-image: 'node'
|
|
||||||
default-version: '20.0.0'
|
|
||||||
|
|
||||||
- name: Validate existing version detection
|
|
||||||
run: |
|
|
||||||
# The action detects Node.js version from package.json engines field
|
|
||||||
# package.json >=18.0.0 is parsed as 18.0.0
|
|
||||||
# Note: .nvmrc exists but package.json takes precedence in this implementation
|
|
||||||
expected_version="18.0.0"
|
|
||||||
detected_version="${{ steps.existing-version.outputs.detected-version }}"
|
|
||||||
|
|
||||||
if [[ "$detected_version" != "$expected_version" ]]; then
|
|
||||||
echo "❌ ERROR: Version mismatch"
|
|
||||||
echo "Expected: $expected_version"
|
|
||||||
echo "Got: $detected_version"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ Existing version detection works correctly"
|
|
||||||
|
|
||||||
- name: Clean up before invalid regex test
|
|
||||||
run: |
|
|
||||||
rm -f .nvmrc package.json package-lock.json
|
|
||||||
|
|
||||||
- name: Test with invalid regex
|
|
||||||
id: invalid-regex
|
|
||||||
uses: ./version-file-parser
|
|
||||||
with:
|
|
||||||
language: 'node'
|
|
||||||
tool-versions-key: 'nodejs'
|
|
||||||
dockerfile-image: 'node'
|
|
||||||
validation-regex: 'invalid[regex'
|
|
||||||
default-version: '18.0.0'
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Validate regex error handling
|
|
||||||
run: |
|
|
||||||
echo "Testing regex error handling completed"
|
|
||||||
# Action should handle invalid regex gracefully
|
|
||||||
if [ "${{ steps.invalid-regex.outcome }}" != "failure" ]; then
|
|
||||||
echo "::error::Expected invalid-regex step to fail, but it was: ${{ steps.invalid-regex.outcome }}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ Invalid regex properly failed as expected"
|
|
||||||
|
|
||||||
test-dockerfile-parsing:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Clean up test files from previous runs
|
|
||||||
run: |
|
|
||||||
rm -f .nvmrc package.json package-lock.json composer.json .python-version pyproject.toml go.mod .tool-versions Dockerfile
|
|
||||||
|
|
||||||
- name: Create Dockerfile with Node.js
|
|
||||||
run: |
|
|
||||||
cat > Dockerfile <<EOF
|
|
||||||
FROM node:18.17.0-alpine
|
|
||||||
WORKDIR /app
|
|
||||||
COPY . .
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Test Dockerfile parsing
|
|
||||||
id: dockerfile-test
|
|
||||||
uses: ./version-file-parser
|
|
||||||
with:
|
|
||||||
language: 'node'
|
|
||||||
tool-versions-key: 'nodejs'
|
|
||||||
dockerfile-image: 'node'
|
|
||||||
|
|
||||||
- name: Validate Dockerfile parsing
|
|
||||||
run: |
|
|
||||||
expected_version="18.17.0"
|
|
||||||
detected_version="${{ steps.dockerfile-test.outputs.dockerfile-version }}"
|
|
||||||
|
|
||||||
echo "Expected version: $expected_version"
|
|
||||||
echo "Detected version: $detected_version"
|
|
||||||
|
|
||||||
if [[ "$detected_version" != "$expected_version" ]]; then
|
|
||||||
echo "❌ ERROR: Version mismatch"
|
|
||||||
echo "Expected: $expected_version"
|
|
||||||
echo "Got: $detected_version"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ Dockerfile parsing successful"
|
|
||||||
@@ -114,11 +114,6 @@ setup_default_inputs() {
|
|||||||
"validate-inputs")
|
"validate-inputs")
|
||||||
[[ "$input_name" != "action-type" && "$input_name" != "action" && "$input_name" != "rules-file" && "$input_name" != "fail-on-error" ]] && export INPUT_ACTION_TYPE="test-action"
|
[[ "$input_name" != "action-type" && "$input_name" != "action" && "$input_name" != "rules-file" && "$input_name" != "fail-on-error" ]] && export INPUT_ACTION_TYPE="test-action"
|
||||||
;;
|
;;
|
||||||
"version-file-parser")
|
|
||||||
[[ "$input_name" != "language" ]] && export INPUT_LANGUAGE="node"
|
|
||||||
[[ "$input_name" != "tool-versions-key" ]] && export INPUT_TOOL_VERSIONS_KEY="nodejs"
|
|
||||||
[[ "$input_name" != "dockerfile-image" ]] && export INPUT_DOCKERFILE_IMAGE="node"
|
|
||||||
;;
|
|
||||||
"codeql-analysis")
|
"codeql-analysis")
|
||||||
[[ "$input_name" != "language" ]] && export INPUT_LANGUAGE="javascript"
|
[[ "$input_name" != "language" ]] && export INPUT_LANGUAGE="javascript"
|
||||||
[[ "$input_name" != "token" ]] && export INPUT_TOKEN="ghp_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
[[ "$input_name" != "token" ]] && export INPUT_TOKEN="ghp_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||||
@@ -185,11 +180,6 @@ cleanup_default_inputs() {
|
|||||||
"validate-inputs")
|
"validate-inputs")
|
||||||
[[ "$input_name" != "action-type" && "$input_name" != "action" && "$input_name" != "rules-file" && "$input_name" != "fail-on-error" ]] && unset INPUT_ACTION_TYPE
|
[[ "$input_name" != "action-type" && "$input_name" != "action" && "$input_name" != "rules-file" && "$input_name" != "fail-on-error" ]] && unset INPUT_ACTION_TYPE
|
||||||
;;
|
;;
|
||||||
"version-file-parser")
|
|
||||||
[[ "$input_name" != "language" ]] && unset INPUT_LANGUAGE
|
|
||||||
[[ "$input_name" != "tool-versions-key" ]] && unset INPUT_TOOL_VERSIONS_KEY
|
|
||||||
[[ "$input_name" != "dockerfile-image" ]] && unset INPUT_DOCKERFILE_IMAGE
|
|
||||||
;;
|
|
||||||
"codeql-analysis")
|
"codeql-analysis")
|
||||||
[[ "$input_name" != "language" ]] && unset INPUT_LANGUAGE
|
[[ "$input_name" != "language" ]] && unset INPUT_LANGUAGE
|
||||||
[[ "$input_name" != "token" ]] && unset INPUT_TOKEN
|
[[ "$input_name" != "token" ]] && unset INPUT_TOKEN
|
||||||
@@ -244,10 +234,6 @@ shellspec_mock_action_run() {
|
|||||||
action_name=$(basename "$action_dir")
|
action_name=$(basename "$action_dir")
|
||||||
|
|
||||||
case "$action_name" in
|
case "$action_name" in
|
||||||
"version-file-parser")
|
|
||||||
echo "detected-version=1.0.0" >>"$GITHUB_OUTPUT"
|
|
||||||
echo "package-manager=npm" >>"$GITHUB_OUTPUT"
|
|
||||||
;;
|
|
||||||
"node-setup")
|
"node-setup")
|
||||||
echo "node-version=18.0.0" >>"$GITHUB_OUTPUT"
|
echo "node-version=18.0.0" >>"$GITHUB_OUTPUT"
|
||||||
echo "package-manager=npm" >>"$GITHUB_OUTPUT"
|
echo "package-manager=npm" >>"$GITHUB_OUTPUT"
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
#!/usr/bin/env shellspec
|
|
||||||
# Unit tests for version-file-parser action validation and logic
|
|
||||||
# Framework is automatically loaded via spec_helper.sh
|
|
||||||
|
|
||||||
Describe "version-file-parser action"
|
|
||||||
ACTION_DIR="version-file-parser"
|
|
||||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
|
||||||
|
|
||||||
Context "when validating language input"
|
|
||||||
It "accepts valid language input"
|
|
||||||
When call validate_input_python "version-file-parser" "language" "node"
|
|
||||||
The status should be success
|
|
||||||
End
|
|
||||||
It "accepts php language"
|
|
||||||
When call validate_input_python "version-file-parser" "language" "php"
|
|
||||||
The status should be success
|
|
||||||
End
|
|
||||||
It "accepts python language"
|
|
||||||
When call validate_input_python "version-file-parser" "language" "python"
|
|
||||||
The status should be success
|
|
||||||
End
|
|
||||||
It "accepts go language"
|
|
||||||
When call validate_input_python "version-file-parser" "language" "go"
|
|
||||||
The status should be success
|
|
||||||
End
|
|
||||||
It "rejects invalid language with special characters"
|
|
||||||
When call validate_input_python "version-file-parser" "language" "node; rm -rf /"
|
|
||||||
The status should be failure
|
|
||||||
End
|
|
||||||
It "rejects empty required inputs"
|
|
||||||
When call validate_input_python "version-file-parser" "language" ""
|
|
||||||
The status should be failure
|
|
||||||
End
|
|
||||||
End
|
|
||||||
|
|
||||||
Context "when validating dockerfile-image input"
|
|
||||||
It "accepts valid dockerfile image"
|
|
||||||
When call validate_input_python "version-file-parser" "dockerfile-image" "node"
|
|
||||||
The status should be success
|
|
||||||
End
|
|
||||||
It "accepts php dockerfile image"
|
|
||||||
When call validate_input_python "version-file-parser" "dockerfile-image" "php"
|
|
||||||
The status should be success
|
|
||||||
End
|
|
||||||
It "accepts python dockerfile image"
|
|
||||||
When call validate_input_python "version-file-parser" "dockerfile-image" "python"
|
|
||||||
The status should be success
|
|
||||||
End
|
|
||||||
It "rejects injection in dockerfile image"
|
|
||||||
When call validate_input_python "version-file-parser" "dockerfile-image" "node;malicious"
|
|
||||||
The status should be failure
|
|
||||||
End
|
|
||||||
End
|
|
||||||
|
|
||||||
Context "when validating optional inputs"
|
|
||||||
It "accepts valid validation regex"
|
|
||||||
When call validate_input_python "version-file-parser" "validation-regex" "^[0-9]+\.[0-9]+(\.[0-9]+)?$"
|
|
||||||
The status should be success
|
|
||||||
End
|
|
||||||
It "accepts valid default version"
|
|
||||||
When call validate_input_python "version-file-parser" "default-version" "18.0.0"
|
|
||||||
The status should be success
|
|
||||||
End
|
|
||||||
It "accepts tool versions key"
|
|
||||||
When call validate_input_python "version-file-parser" "tool-versions-key" "nodejs"
|
|
||||||
The status should be success
|
|
||||||
End
|
|
||||||
End
|
|
||||||
|
|
||||||
Context "when checking action.yml structure"
|
|
||||||
It "has valid YAML syntax"
|
|
||||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
|
||||||
The status should be success
|
|
||||||
End
|
|
||||||
|
|
||||||
It "contains required metadata"
|
|
||||||
When call get_action_name "$ACTION_FILE"
|
|
||||||
The output should equal "Version File Parser"
|
|
||||||
End
|
|
||||||
|
|
||||||
It "defines expected inputs"
|
|
||||||
When call get_action_inputs "$ACTION_FILE"
|
|
||||||
The output should include "language"
|
|
||||||
The output should include "tool-versions-key"
|
|
||||||
The output should include "dockerfile-image"
|
|
||||||
End
|
|
||||||
|
|
||||||
It "defines expected outputs"
|
|
||||||
When call get_action_outputs "$ACTION_FILE"
|
|
||||||
The output should include "detected-version"
|
|
||||||
The output should include "package-manager"
|
|
||||||
End
|
|
||||||
End
|
|
||||||
|
|
||||||
Context "when validating security"
|
|
||||||
It "rejects injection in language parameter"
|
|
||||||
When call validate_input_python "version-file-parser" "language" "node&&malicious"
|
|
||||||
The status should be failure
|
|
||||||
End
|
|
||||||
|
|
||||||
It "rejects pipe injection in tool versions key"
|
|
||||||
When call validate_input_python "version-file-parser" "tool-versions-key" "nodejs|dangerous"
|
|
||||||
The status should be failure
|
|
||||||
End
|
|
||||||
|
|
||||||
It "validates regex patterns safely"
|
|
||||||
When call validate_input_python "version-file-parser" "validation-regex" "^[0-9]+\.[0-9]+$"
|
|
||||||
The status should be success
|
|
||||||
End
|
|
||||||
|
|
||||||
It "rejects malicious regex patterns"
|
|
||||||
When call validate_input_python "version-file-parser" "validation-regex" ".*; rm -rf /"
|
|
||||||
The status should be failure
|
|
||||||
End
|
|
||||||
End
|
|
||||||
|
|
||||||
Context "when testing outputs"
|
|
||||||
It "produces all expected outputs consistently"
|
|
||||||
When call test_action_outputs "$ACTION_DIR" "language" "node" "dockerfile-image" "node"
|
|
||||||
The status should be success
|
|
||||||
The stderr should include "Testing action outputs for: version-file-parser"
|
|
||||||
The stderr should include "Output test passed for: version-file-parser"
|
|
||||||
End
|
|
||||||
End
|
|
||||||
End
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
"""Tests for version-file-parser custom validator.
|
|
||||||
|
|
||||||
Generated by generate-tests.py - Do not edit manually.
|
|
||||||
"""
|
|
||||||
# pylint: disable=invalid-name # Test file name matches action name
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Add action directory to path to import custom validator
|
|
||||||
action_path = Path(__file__).parent.parent.parent / "version-file-parser"
|
|
||||||
sys.path.insert(0, str(action_path))
|
|
||||||
|
|
||||||
# pylint: disable=wrong-import-position
|
|
||||||
from CustomValidator import CustomValidator
|
|
||||||
|
|
||||||
|
|
||||||
class TestCustomVersionFileParserValidator:
|
|
||||||
"""Test cases for version-file-parser custom validator."""
|
|
||||||
|
|
||||||
def setup_method(self):
|
|
||||||
"""Set up test fixtures."""
|
|
||||||
self.validator = CustomValidator("version-file-parser")
|
|
||||||
|
|
||||||
def teardown_method(self):
|
|
||||||
"""Clean up after tests."""
|
|
||||||
self.validator.clear_errors()
|
|
||||||
|
|
||||||
def test_validate_inputs_valid(self):
|
|
||||||
"""Test validation with valid inputs."""
|
|
||||||
# TODO: Add specific valid inputs for version-file-parser
|
|
||||||
inputs = {}
|
|
||||||
result = self.validator.validate_inputs(inputs)
|
|
||||||
# Adjust assertion based on required inputs
|
|
||||||
assert isinstance(result, bool)
|
|
||||||
|
|
||||||
def test_validate_inputs_invalid(self):
|
|
||||||
"""Test validation with invalid inputs."""
|
|
||||||
# TODO: Add specific invalid inputs for version-file-parser
|
|
||||||
inputs = {"invalid_key": "invalid_value"}
|
|
||||||
result = self.validator.validate_inputs(inputs)
|
|
||||||
# Custom validators may have specific validation rules
|
|
||||||
assert isinstance(result, bool)
|
|
||||||
|
|
||||||
def test_required_inputs(self):
|
|
||||||
"""Test required inputs detection."""
|
|
||||||
required = self.validator.get_required_inputs()
|
|
||||||
assert isinstance(required, list)
|
|
||||||
# TODO: Assert specific required inputs for version-file-parser
|
|
||||||
|
|
||||||
def test_validation_rules(self):
|
|
||||||
"""Test validation rules."""
|
|
||||||
rules = self.validator.get_validation_rules()
|
|
||||||
assert isinstance(rules, dict)
|
|
||||||
# TODO: Assert specific validation rules for version-file-parser
|
|
||||||
|
|
||||||
def test_github_expressions(self):
|
|
||||||
"""Test GitHub expression handling."""
|
|
||||||
inputs = {
|
|
||||||
"test_input": "${{ github.token }}",
|
|
||||||
}
|
|
||||||
result = self.validator.validate_inputs(inputs)
|
|
||||||
assert isinstance(result, bool)
|
|
||||||
# GitHub expressions should generally be accepted
|
|
||||||
|
|
||||||
def test_error_propagation(self):
|
|
||||||
"""Test error propagation from sub-validators."""
|
|
||||||
# Custom validators often use sub-validators
|
|
||||||
# Test that errors are properly propagated
|
|
||||||
inputs = {"test": "value"}
|
|
||||||
self.validator.validate_inputs(inputs)
|
|
||||||
# Check error handling
|
|
||||||
if self.validator.has_errors():
|
|
||||||
assert len(self.validator.errors) > 0
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Custom validator for version-file-parser action."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# Add validate-inputs directory to path to import validators
|
|
||||||
validate_inputs_path = Path(__file__).parent.parent / "validate-inputs"
|
|
||||||
sys.path.insert(0, str(validate_inputs_path))
|
|
||||||
|
|
||||||
from validators.base import BaseValidator
|
|
||||||
|
|
||||||
|
|
||||||
class CustomValidator(BaseValidator):
|
|
||||||
"""Custom validator for version-file-parser action."""
|
|
||||||
|
|
||||||
def __init__(self, action_type: str = "version-file-parser") -> None:
|
|
||||||
"""Initialize version-file-parser validator."""
|
|
||||||
super().__init__(action_type)
|
|
||||||
|
|
||||||
def validate_inputs(self, inputs: dict[str, str]) -> bool:
|
|
||||||
"""Validate version-file-parser action inputs."""
|
|
||||||
valid = True
|
|
||||||
|
|
||||||
# Validate required input: language
|
|
||||||
if "language" not in inputs or not inputs["language"]:
|
|
||||||
self.add_error("Input 'language' is required")
|
|
||||||
valid = False
|
|
||||||
elif inputs["language"]:
|
|
||||||
# Validate language is one of the supported values
|
|
||||||
valid_languages = [
|
|
||||||
"node",
|
|
||||||
"python",
|
|
||||||
"go",
|
|
||||||
"rust",
|
|
||||||
"ruby",
|
|
||||||
"php",
|
|
||||||
"java",
|
|
||||||
"dotnet",
|
|
||||||
"elixir",
|
|
||||||
]
|
|
||||||
if inputs["language"] not in valid_languages:
|
|
||||||
self.add_error(
|
|
||||||
f"Invalid language: {inputs['language']}. "
|
|
||||||
f"Must be one of: {', '.join(valid_languages)}"
|
|
||||||
)
|
|
||||||
valid = False
|
|
||||||
|
|
||||||
# Validate dockerfile-image for injection
|
|
||||||
dockerfile_key = None
|
|
||||||
if "dockerfile-image" in inputs:
|
|
||||||
dockerfile_key = "dockerfile-image"
|
|
||||||
elif "dockerfile_image" in inputs:
|
|
||||||
dockerfile_key = "dockerfile_image"
|
|
||||||
|
|
||||||
if dockerfile_key and inputs[dockerfile_key]:
|
|
||||||
value = inputs[dockerfile_key]
|
|
||||||
if ";" in value or "|" in value or "&" in value or "`" in value:
|
|
||||||
self.add_error("dockerfile-image contains potentially dangerous characters")
|
|
||||||
valid = False
|
|
||||||
|
|
||||||
# Validate tool-versions-key for injection
|
|
||||||
tool_key = None
|
|
||||||
if "tool-versions-key" in inputs:
|
|
||||||
tool_key = "tool-versions-key"
|
|
||||||
elif "tool_versions_key" in inputs:
|
|
||||||
tool_key = "tool_versions_key"
|
|
||||||
|
|
||||||
if tool_key and inputs[tool_key]:
|
|
||||||
value = inputs[tool_key]
|
|
||||||
if "|" in value or ";" in value or "&" in value or "`" in value:
|
|
||||||
self.add_error("tool-versions-key contains potentially dangerous characters")
|
|
||||||
valid = False
|
|
||||||
|
|
||||||
# Validate validation-regex for malicious patterns
|
|
||||||
regex_key = None
|
|
||||||
if "validation-regex" in inputs:
|
|
||||||
regex_key = "validation-regex"
|
|
||||||
elif "validation_regex" in inputs:
|
|
||||||
regex_key = "validation_regex"
|
|
||||||
|
|
||||||
if regex_key and inputs[regex_key]:
|
|
||||||
value = inputs[regex_key]
|
|
||||||
# Check for shell command injection in regex
|
|
||||||
if ";" in value or "|" in value or "`" in value or "rm " in value:
|
|
||||||
self.add_error("validation-regex contains potentially dangerous patterns")
|
|
||||||
valid = False
|
|
||||||
|
|
||||||
return valid
|
|
||||||
|
|
||||||
def get_required_inputs(self) -> list[str]:
|
|
||||||
"""Get list of required inputs."""
|
|
||||||
return ["language"]
|
|
||||||
|
|
||||||
def get_validation_rules(self) -> dict:
|
|
||||||
"""Get validation rules."""
|
|
||||||
return {
|
|
||||||
"language": {
|
|
||||||
"type": "string",
|
|
||||||
"required": True,
|
|
||||||
"description": "Language identifier",
|
|
||||||
},
|
|
||||||
"tool-versions-key": {
|
|
||||||
"type": "string",
|
|
||||||
"required": False,
|
|
||||||
"description": "Key in .tool-versions",
|
|
||||||
},
|
|
||||||
"dockerfile-image": {
|
|
||||||
"type": "string",
|
|
||||||
"required": False,
|
|
||||||
"description": "Dockerfile image name",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
# ivuorinen/actions/version-file-parser
|
|
||||||
|
|
||||||
## Version File Parser
|
|
||||||
|
|
||||||
### Description
|
|
||||||
|
|
||||||
Universal parser for common version detection files (.tool-versions, Dockerfile, devcontainer.json, etc.)
|
|
||||||
|
|
||||||
### Inputs
|
|
||||||
|
|
||||||
| name | description | required | default |
|
|
||||||
|---------------------|------------------------------------------------------------------------------|----------|-------------------------------|
|
|
||||||
| `language` | <p>Programming language name (node, python, php, go, dotnet)</p> | `true` | `""` |
|
|
||||||
| `tool-versions-key` | <p>Key name in .tool-versions file (nodejs, python, php, golang, dotnet)</p> | `true` | `""` |
|
|
||||||
| `dockerfile-image` | <p>Docker image name pattern (node, python, php, golang, dotnet)</p> | `true` | `""` |
|
|
||||||
| `version-file` | <p>Language-specific version file (.nvmrc, .python-version, etc.)</p> | `false` | `""` |
|
|
||||||
| `validation-regex` | <p>Version validation regex pattern</p> | `false` | `^[0-9]+\.[0-9]+(\.[0-9]+)?$` |
|
|
||||||
| `default-version` | <p>Default version to use if no version is detected</p> | `false` | `""` |
|
|
||||||
|
|
||||||
### Outputs
|
|
||||||
|
|
||||||
| name | description |
|
|
||||||
|-------------------------|-----------------------------------------------------------------------------------|
|
|
||||||
| `tool-versions-version` | <p>Version found in .tool-versions</p> |
|
|
||||||
| `dockerfile-version` | <p>Version found in Dockerfile</p> |
|
|
||||||
| `devcontainer-version` | <p>Version found in devcontainer.json</p> |
|
|
||||||
| `version-file-version` | <p>Version found in language-specific version file</p> |
|
|
||||||
| `config-file-version` | <p>Version found in language config files (package.json, composer.json, etc.)</p> |
|
|
||||||
| `detected-version` | <p>Final detected version (first found or default)</p> |
|
|
||||||
| `package-manager` | <p>Detected package manager (npm, yarn, pnpm, composer, pip, poetry, etc.)</p> |
|
|
||||||
|
|
||||||
### Runs
|
|
||||||
|
|
||||||
This action is a `composite` action.
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- uses: ivuorinen/actions/version-file-parser@main
|
|
||||||
with:
|
|
||||||
language:
|
|
||||||
# Programming language name (node, python, php, go, dotnet)
|
|
||||||
#
|
|
||||||
# Required: true
|
|
||||||
# Default: ""
|
|
||||||
|
|
||||||
tool-versions-key:
|
|
||||||
# Key name in .tool-versions file (nodejs, python, php, golang, dotnet)
|
|
||||||
#
|
|
||||||
# Required: true
|
|
||||||
# Default: ""
|
|
||||||
|
|
||||||
dockerfile-image:
|
|
||||||
# Docker image name pattern (node, python, php, golang, dotnet)
|
|
||||||
#
|
|
||||||
# Required: true
|
|
||||||
# Default: ""
|
|
||||||
|
|
||||||
version-file:
|
|
||||||
# Language-specific version file (.nvmrc, .python-version, etc.)
|
|
||||||
#
|
|
||||||
# Required: false
|
|
||||||
# Default: ""
|
|
||||||
|
|
||||||
validation-regex:
|
|
||||||
# Version validation regex pattern
|
|
||||||
#
|
|
||||||
# Required: false
|
|
||||||
# Default: ^[0-9]+\.[0-9]+(\.[0-9]+)?$
|
|
||||||
|
|
||||||
default-version:
|
|
||||||
# Default version to use if no version is detected
|
|
||||||
#
|
|
||||||
# Required: false
|
|
||||||
# Default: ""
|
|
||||||
```
|
|
||||||
@@ -1,365 +0,0 @@
|
|||||||
# yaml-language-server: $schema=https://json.schemastore.org/github-action.json
|
|
||||||
# permissions:
|
|
||||||
# - contents: read # Required for reading version files
|
|
||||||
---
|
|
||||||
name: Version File Parser
|
|
||||||
description: 'Universal parser for common version detection files (.tool-versions, Dockerfile, devcontainer.json, etc.)'
|
|
||||||
author: 'Ismo Vuorinen'
|
|
||||||
|
|
||||||
branding:
|
|
||||||
icon: search
|
|
||||||
color: gray-dark
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
language:
|
|
||||||
description: 'Programming language name (node, python, php, go, dotnet)'
|
|
||||||
required: true
|
|
||||||
tool-versions-key:
|
|
||||||
description: 'Key name in .tool-versions file (nodejs, python, php, golang, dotnet)'
|
|
||||||
required: true
|
|
||||||
dockerfile-image:
|
|
||||||
description: 'Docker image name pattern (node, python, php, golang, dotnet)'
|
|
||||||
required: true
|
|
||||||
version-file:
|
|
||||||
description: 'Language-specific version file (.nvmrc, .python-version, etc.)'
|
|
||||||
required: false
|
|
||||||
validation-regex:
|
|
||||||
description: 'Version validation regex pattern'
|
|
||||||
required: false
|
|
||||||
default: '^[0-9]+\.[0-9]+(\.[0-9]+)?$'
|
|
||||||
default-version:
|
|
||||||
description: 'Default version to use if no version is detected'
|
|
||||||
required: false
|
|
||||||
|
|
||||||
outputs:
|
|
||||||
tool-versions-version:
|
|
||||||
description: 'Version found in .tool-versions'
|
|
||||||
value: ${{ steps.parse.outputs.tool-versions-version }}
|
|
||||||
dockerfile-version:
|
|
||||||
description: 'Version found in Dockerfile'
|
|
||||||
value: ${{ steps.parse.outputs.dockerfile-version }}
|
|
||||||
devcontainer-version:
|
|
||||||
description: 'Version found in devcontainer.json'
|
|
||||||
value: ${{ steps.parse.outputs.devcontainer-version }}
|
|
||||||
version-file-version:
|
|
||||||
description: 'Version found in language-specific version file'
|
|
||||||
value: ${{ steps.parse.outputs.version-file-version }}
|
|
||||||
config-file-version:
|
|
||||||
description: 'Version found in language config files (package.json, composer.json, etc.)'
|
|
||||||
value: ${{ steps.parse.outputs.config-file-version }}
|
|
||||||
detected-version:
|
|
||||||
description: 'Final detected version (first found or default)'
|
|
||||||
value: ${{ steps.parse.outputs.detected-version }}
|
|
||||||
package-manager:
|
|
||||||
description: 'Detected package manager (npm, yarn, pnpm, composer, pip, poetry, etc.)'
|
|
||||||
value: ${{ steps.parse.outputs.package-manager }}
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: composite
|
|
||||||
steps:
|
|
||||||
- name: Parse Version Files
|
|
||||||
id: parse
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
VALIDATION_REGEX: ${{ inputs.validation-regex }}
|
|
||||||
LANGUAGE: ${{ inputs.language }}
|
|
||||||
TOOL_VERSIONS_KEY: ${{ inputs.tool-versions-key }}
|
|
||||||
DOCKERFILE_IMAGE: ${{ inputs.dockerfile-image }}
|
|
||||||
VERSION_FILE: ${{ inputs.version-file }}
|
|
||||||
DEFAULT_VERSION: ${{ inputs.default-version }}
|
|
||||||
run: |-
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Function to validate version format
|
|
||||||
validate_version() {
|
|
||||||
local version=$1
|
|
||||||
local regex="$VALIDATION_REGEX"
|
|
||||||
|
|
||||||
# Test regex validity
|
|
||||||
if ! bash -c "[[ 'test' =~ \${regex} ]]" 2>/dev/null; then
|
|
||||||
echo "::error::Invalid validation regex pattern: $regex" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Validate version using safe regex matching with quoted variable
|
|
||||||
if [[ $version =~ ^${regex}$ ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to clean version string
|
|
||||||
clean_version() {
|
|
||||||
echo "$1" | sed 's/^[vV]//' | tr -d ' \n\r'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Initialize outputs
|
|
||||||
echo "tool-versions-version=" >> $GITHUB_OUTPUT
|
|
||||||
echo "dockerfile-version=" >> $GITHUB_OUTPUT
|
|
||||||
echo "devcontainer-version=" >> $GITHUB_OUTPUT
|
|
||||||
echo "version-file-version=" >> $GITHUB_OUTPUT
|
|
||||||
echo "config-file-version=" >> $GITHUB_OUTPUT
|
|
||||||
echo "detected-version=" >> $GITHUB_OUTPUT
|
|
||||||
echo "package-manager=" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# Language detection patterns
|
|
||||||
language="$LANGUAGE"
|
|
||||||
|
|
||||||
# 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
|
|
||||||
echo "tool-versions-version=$version" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Parse Dockerfile
|
|
||||||
if [ -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
|
|
||||||
echo "dockerfile-version=$version" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Parse devcontainer.json
|
|
||||||
if [ -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
|
|
||||||
echo "devcontainer-version=$version" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "jq not available, skipping devcontainer parsing" >&2
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Parse language-specific version file
|
|
||||||
if [ -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
|
|
||||||
echo "version-file-version=$version" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Parse language-specific configuration files
|
|
||||||
config_version=""
|
|
||||||
detected_package_manager=""
|
|
||||||
|
|
||||||
case "$language" in
|
|
||||||
"node")
|
|
||||||
# Check package.json
|
|
||||||
if [ -f package.json ] && command -v jq >/dev/null 2>&1; then
|
|
||||||
version=$(jq -r '.engines.node // empty' package.json 2>/dev/null | sed -n 's/[^0-9]*\([0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?\).*/\1/p')
|
|
||||||
if [ -n "$version" ] && validate_version "$version"; then
|
|
||||||
echo "Found Node.js version in package.json: $version" >&2
|
|
||||||
config_version="$version"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Detect package manager
|
|
||||||
if [ -f bun.lockb ]; then
|
|
||||||
detected_package_manager="bun"
|
|
||||||
elif [ -f pnpm-lock.yaml ]; then
|
|
||||||
detected_package_manager="pnpm"
|
|
||||||
elif [ -f yarn.lock ]; then
|
|
||||||
detected_package_manager="yarn"
|
|
||||||
elif [ -f package-lock.json ]; then
|
|
||||||
detected_package_manager="npm"
|
|
||||||
elif [ -f package.json ] && command -v jq >/dev/null 2>&1; then
|
|
||||||
# Check packageManager field in package.json
|
|
||||||
pkg_manager=$(jq -r '.packageManager // empty' package.json 2>/dev/null | sed 's/@.*//')
|
|
||||||
if [ -n "$pkg_manager" ]; then
|
|
||||||
detected_package_manager="$pkg_manager"
|
|
||||||
else
|
|
||||||
detected_package_manager="npm"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
detected_package_manager="npm"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
"php")
|
|
||||||
# Check composer.json
|
|
||||||
if [ -f composer.json ] && command -v jq >/dev/null 2>&1; then
|
|
||||||
# Try require.php first
|
|
||||||
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
|
|
||||||
# Try platform.php
|
|
||||||
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
|
|
||||||
config_version="$version"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check phpunit.xml
|
|
||||||
if [ -z "$config_version" ]; then
|
|
||||||
phpunit_file=""
|
|
||||||
if [ -f phpunit.xml ]; then
|
|
||||||
phpunit_file="phpunit.xml"
|
|
||||||
elif [ -f phpunit.xml.dist ]; then
|
|
||||||
phpunit_file="phpunit.xml.dist"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$phpunit_file" ]; then
|
|
||||||
version=$(grep -o 'php[[:space:]]*version[[:space:]]*=[[:space:]]*"[^"]*"' "$phpunit_file" | sed -n 's/.*"\([0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?\)".*/\1/p')
|
|
||||||
if [ -n "$version" ] && validate_version "$version"; then
|
|
||||||
echo "Found PHP version in $phpunit_file: $version" >&2
|
|
||||||
config_version="$version"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Detect package manager
|
|
||||||
if [ -f composer.json ]; then
|
|
||||||
detected_package_manager="composer"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
"python")
|
|
||||||
# Check pyproject.toml
|
|
||||||
if [ -f pyproject.toml ]; then
|
|
||||||
# Try PEP 621 requires-python first (allow leading whitespace)
|
|
||||||
if command -v jq >/dev/null 2>&1 && grep -q '^\[project\]' pyproject.toml; then
|
|
||||||
# Convert TOML to JSON for PEP 621 parsing (basic approach)
|
|
||||||
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 [project] requires-python: $version" >&2
|
|
||||||
config_version="$version"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# Fallback to legacy python field if no PEP 621 version found
|
|
||||||
if [ -z "$config_version" ]; then
|
|
||||||
version=$(grep -E '^python[[:space:]]*=' pyproject.toml | sed -n 's/[^0-9]*\([0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?\).*/\1/p')
|
|
||||||
if [ -n "$version" ] && validate_version "$version"; then
|
|
||||||
echo "Found Python version in pyproject.toml: $version" >&2
|
|
||||||
config_version="$version"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check setup.py for python_requires
|
|
||||||
if [ -z "$config_version" ] && [ -f setup.py ]; then
|
|
||||||
version=$(grep -o 'python_requires[[:space:]]*=[[:space:]]*['\''"].*['\''"]' setup.py | sed -n 's/[^0-9]*\([0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?\).*/\1/p')
|
|
||||||
if [ -n "$version" ] && validate_version "$version"; then
|
|
||||||
echo "Found Python version in setup.py: $version" >&2
|
|
||||||
config_version="$version"
|
|
||||||
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"
|
|
||||||
elif [ -f requirements.txt ]; then
|
|
||||||
detected_package_manager="pip"
|
|
||||||
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
|
|
||||||
config_version="$version"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Detect package manager
|
|
||||||
if [ -f go.mod ]; then
|
|
||||||
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
|
|
||||||
config_version="$version"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check .csproj files
|
|
||||||
if [ -z "$config_version" ]; then
|
|
||||||
# Enable nullglob to handle case when no .csproj files exist
|
|
||||||
shopt -s nullglob
|
|
||||||
for csproj in *.csproj; do
|
|
||||||
if [ -f "$csproj" ]; then
|
|
||||||
# Handle both TargetFramework and TargetFrameworks, and handle -windows monikers
|
|
||||||
version=$(grep -oE '<TargetFrameworks?>net[0-9]+\.[0-9]+(-[a-z]+)?</TargetFrameworks?>' "$csproj" | sed -n 's/.*net\([0-9]\+\.[0-9]\+\).*/\1/p' | head -1)
|
|
||||||
if [ -n "$version" ] && validate_version "$version"; then
|
|
||||||
echo "Found .NET version in $csproj: $version" >&2
|
|
||||||
config_version="$version"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
# Disable nullglob after use
|
|
||||||
shopt -u nullglob
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Detect package manager
|
|
||||||
detected_package_manager="dotnet"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Set config-file-version output
|
|
||||||
if [ -n "$config_version" ]; then
|
|
||||||
echo "config-file-version=$config_version" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set package-manager output
|
|
||||||
if [ -n "$detected_package_manager" ]; then
|
|
||||||
echo "package-manager=$detected_package_manager" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Determine final detected version with priority order
|
|
||||||
# Priority order: version-file > config-file > tool-versions > dockerfile > devcontainer > default
|
|
||||||
final_version=$(grep -E "^(version-file|config-file|tool-versions|dockerfile|devcontainer)-version=" $GITHUB_OUTPUT | tac | awk -F= 'NF>1 && $2!="" {print $2; exit}')
|
|
||||||
|
|
||||||
# If no version found from any source, use default
|
|
||||||
if [ -z "$final_version" ] && [ -n "$DEFAULT_VERSION" ]; then
|
|
||||||
final_version="$DEFAULT_VERSION"
|
|
||||||
echo "Using default $LANGUAGE version: $final_version" >&2
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set final detected version
|
|
||||||
if [ -n "$final_version" ]; then
|
|
||||||
# Validate the final version against the regex
|
|
||||||
if ! validate_version "$final_version"; then
|
|
||||||
echo "::error::Detected version $final_version does not match validation regex" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "detected-version=$final_version" >> $GITHUB_OUTPUT
|
|
||||||
echo "Final detected $LANGUAGE version: $final_version" >&2
|
|
||||||
else
|
|
||||||
echo "No $LANGUAGE version detected" >&2
|
|
||||||
fi
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
---
|
|
||||||
# Validation rules for version-file-parser action
|
|
||||||
# Generated by update-validators.py v1.0.0 - DO NOT EDIT MANUALLY
|
|
||||||
# Schema version: 1.0
|
|
||||||
# Coverage: 50% (3/6 inputs)
|
|
||||||
#
|
|
||||||
# This file defines validation rules for the version-file-parser GitHub Action.
|
|
||||||
# Rules are automatically applied by validate-inputs action when this
|
|
||||||
# action is used.
|
|
||||||
#
|
|
||||||
|
|
||||||
schema_version: '1.0'
|
|
||||||
action: version-file-parser
|
|
||||||
description: Universal parser for common version detection files (.tool-versions, Dockerfile, devcontainer.json, etc.)
|
|
||||||
generator_version: 1.0.0
|
|
||||||
required_inputs:
|
|
||||||
- dockerfile-image
|
|
||||||
- language
|
|
||||||
- tool-versions-key
|
|
||||||
optional_inputs:
|
|
||||||
- default-version
|
|
||||||
- validation-regex
|
|
||||||
- version-file
|
|
||||||
conventions:
|
|
||||||
default-version: semantic_version
|
|
||||||
validation-regex: regex_pattern
|
|
||||||
version-file: file_path
|
|
||||||
overrides: {}
|
|
||||||
statistics:
|
|
||||||
total_inputs: 6
|
|
||||||
validated_inputs: 3
|
|
||||||
skipped_inputs: 0
|
|
||||||
coverage_percentage: 50
|
|
||||||
validation_coverage: 50
|
|
||||||
auto_detected: true
|
|
||||||
manual_review_required: true
|
|
||||||
quality_indicators:
|
|
||||||
has_required_inputs: true
|
|
||||||
has_token_validation: false
|
|
||||||
has_version_validation: true
|
|
||||||
has_file_validation: true
|
|
||||||
has_security_validation: false
|
|
||||||
Reference in New Issue
Block a user