refactor: remove common-cache action

Delete common-cache action and all associated test files. All actions
now use native actions/cache@v4.3.0 instead of the wrapper.

Deleted:
- common-cache/action.yml
- common-cache/README.md
- common-cache/rules.yml
- common-cache/CustomValidator.py
- _tests/unit/common-cache/validation.spec.sh
- _tests/integration/workflows/common-cache-test.yml
- validate-inputs/tests/test_common-cache_custom.py

Action count: 28 → 27
This commit is contained in:
2025-11-20 15:10:59 +02:00
parent 1b6d7240a8
commit 33a8b50bc6
7 changed files with 0 additions and 1193 deletions

View File

@@ -1,471 +0,0 @@
---
name: Integration Test - Common Cache
on:
workflow_dispatch:
push:
paths:
- 'common-cache/**'
- '_tests/integration/workflows/common-cache-test.yml'
jobs:
test-common-cache-key-generation:
name: Test Cache Key Generation
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Test basic key generation
run: |
RUNNER_OS="Linux"
CACHE_TYPE="npm"
KEY_PREFIX=""
cache_key="$RUNNER_OS"
[ -n "$CACHE_TYPE" ] && cache_key="${cache_key}-${CACHE_TYPE}"
expected="Linux-npm"
if [[ "$cache_key" != "$expected" ]]; then
echo "❌ ERROR: Expected '$expected', got '$cache_key'"
exit 1
fi
echo "✓ Basic cache key generation works"
- name: Test key with prefix
run: |
RUNNER_OS="Linux"
CACHE_TYPE="npm"
KEY_PREFIX="node-20"
cache_key="$RUNNER_OS"
[ -n "$KEY_PREFIX" ] && cache_key="${cache_key}-${KEY_PREFIX}"
[ -n "$CACHE_TYPE" ] && cache_key="${cache_key}-${CACHE_TYPE}"
expected="Linux-node-20-npm"
if [[ "$cache_key" != "$expected" ]]; then
echo "❌ ERROR: Expected '$expected', got '$cache_key'"
exit 1
fi
echo "✓ Cache key with prefix works"
- name: Test OS-specific keys
run: |
for os in "Linux" "macOS" "Windows"; do
CACHE_TYPE="test"
cache_key="$os-$CACHE_TYPE"
if [[ ! "$cache_key" =~ ^(Linux|macOS|Windows)-test$ ]]; then
echo "❌ ERROR: Invalid key for OS $os: $cache_key"
exit 1
fi
echo "✓ OS-specific key for $os: $cache_key"
done
test-common-cache-file-hashing:
name: Test File Hashing
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Create test files
run: |
mkdir -p test-cache
cd test-cache
echo "content1" > file1.txt
echo "content2" > file2.txt
echo "content3" > file3.txt
- name: Test single file hash
run: |
cd test-cache
file_hash=$(cat file1.txt | sha256sum | cut -d' ' -f1)
if [[ ! "$file_hash" =~ ^[a-f0-9]{64}$ ]]; then
echo "❌ ERROR: Invalid hash format: $file_hash"
exit 1
fi
echo "✓ Single file hash: $file_hash"
- name: Test multiple file hash
run: |
cd test-cache
multi_hash=$(cat file1.txt file2.txt file3.txt | sha256sum | cut -d' ' -f1)
if [[ ! "$multi_hash" =~ ^[a-f0-9]{64}$ ]]; then
echo "❌ ERROR: Invalid hash format: $multi_hash"
exit 1
fi
echo "✓ Multiple file hash: $multi_hash"
- name: Test hash changes with content
run: |
cd test-cache
# Get initial hash
hash1=$(cat file1.txt | sha256sum | cut -d' ' -f1)
# Modify file
echo "modified" > file1.txt
# Get new hash
hash2=$(cat file1.txt | sha256sum | cut -d' ' -f1)
if [[ "$hash1" == "$hash2" ]]; then
echo "❌ ERROR: Hash should change when content changes"
exit 1
fi
echo "✓ Hash changes with content modification"
- name: Test comma-separated file list processing
run: |
cd test-cache
KEY_FILES="file1.txt,file2.txt,file3.txt"
IFS=',' read -ra FILES <<< "$KEY_FILES"
existing_files=()
for file in "${FILES[@]}"; do
file=$(echo "$file" | xargs)
if [ -f "$file" ]; then
existing_files+=("$file")
fi
done
if [ ${#existing_files[@]} -ne 3 ]; then
echo "❌ ERROR: Should find 3 files, found ${#existing_files[@]}"
exit 1
fi
echo "✓ Comma-separated file list processing works"
- name: Test missing file handling
run: |
cd test-cache
KEY_FILES="file1.txt,missing.txt,file2.txt"
IFS=',' read -ra FILES <<< "$KEY_FILES"
existing_files=()
for file in "${FILES[@]}"; do
file=$(echo "$file" | xargs)
if [ -f "$file" ]; then
existing_files+=("$file")
fi
done
if [ ${#existing_files[@]} -ne 2 ]; then
echo "❌ ERROR: Should find 2 files, found ${#existing_files[@]}"
exit 1
fi
echo "✓ Missing files correctly skipped"
test-common-cache-env-vars:
name: Test Environment Variables
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Test single env var inclusion
run: |
export NODE_VERSION="20.9.0"
ENV_VARS="NODE_VERSION"
IFS=',' read -ra VARS <<< "$ENV_VARS"
env_hash=""
for var in "${VARS[@]}"; do
if [ -n "${!var}" ]; then
env_hash="${env_hash}-${var}-${!var}"
fi
done
expected="-NODE_VERSION-20.9.0"
if [[ "$env_hash" != "$expected" ]]; then
echo "❌ ERROR: Expected '$expected', got '$env_hash'"
exit 1
fi
echo "✓ Single env var inclusion works"
- name: Test multiple env vars
run: |
export NODE_VERSION="20.9.0"
export PACKAGE_MANAGER="npm"
ENV_VARS="NODE_VERSION,PACKAGE_MANAGER"
IFS=',' read -ra VARS <<< "$ENV_VARS"
env_hash=""
for var in "${VARS[@]}"; do
if [ -n "${!var}" ]; then
env_hash="${env_hash}-${var}-${!var}"
fi
done
expected="-NODE_VERSION-20.9.0-PACKAGE_MANAGER-npm"
if [[ "$env_hash" != "$expected" ]]; then
echo "❌ ERROR: Expected '$expected', got '$env_hash'"
exit 1
fi
echo "✓ Multiple env vars inclusion works"
- name: Test undefined env var skipping
run: |
export NODE_VERSION="20.9.0"
ENV_VARS="NODE_VERSION,UNDEFINED_VAR"
IFS=',' read -ra VARS <<< "$ENV_VARS"
env_hash=""
for var in "${VARS[@]}"; do
if [ -n "${!var}" ]; then
env_hash="${env_hash}-${var}-${!var}"
fi
done
# Should only include NODE_VERSION
expected="-NODE_VERSION-20.9.0"
if [[ "$env_hash" != "$expected" ]]; then
echo "❌ ERROR: Expected '$expected', got '$env_hash'"
exit 1
fi
echo "✓ Undefined env vars correctly skipped"
test-common-cache-path-processing:
name: Test Path Processing
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Test single path
run: |
CACHE_PATHS="~/.npm"
IFS=',' read -ra PATHS <<< "$CACHE_PATHS"
if [ ${#PATHS[@]} -ne 1 ]; then
echo "❌ ERROR: Should have 1 path, got ${#PATHS[@]}"
exit 1
fi
echo "✓ Single path processing works"
- name: Test multiple paths
run: |
CACHE_PATHS="~/.npm,~/.yarn/cache,node_modules"
IFS=',' read -ra PATHS <<< "$CACHE_PATHS"
if [ ${#PATHS[@]} -ne 3 ]; then
echo "❌ ERROR: Should have 3 paths, got ${#PATHS[@]}"
exit 1
fi
echo "✓ Multiple paths processing works"
- name: Test path with spaces (trimming)
run: |
CACHE_PATHS=" ~/.npm , ~/.yarn/cache , node_modules "
IFS=',' read -ra PATHS <<< "$CACHE_PATHS"
trimmed_paths=()
for path in "${PATHS[@]}"; do
trimmed=$(echo "$path" | xargs)
trimmed_paths+=("$trimmed")
done
# Check first path is trimmed
if [[ "${trimmed_paths[0]}" != "~/.npm" ]]; then
echo "❌ ERROR: Path not trimmed: '${trimmed_paths[0]}'"
exit 1
fi
echo "✓ Path trimming works"
test-common-cache-complete-key-generation:
name: Test Complete Key Generation
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Create test files
run: |
mkdir -p test-complete
cd test-complete
echo "package-lock content" > package-lock.json
- name: Test complete cache key with all components
run: |
cd test-complete
RUNNER_OS="Linux"
CACHE_TYPE="npm"
KEY_PREFIX="node-20"
# Generate file hash
files_hash=$(cat package-lock.json | sha256sum | cut -d' ' -f1)
# Generate env hash
export NODE_VERSION="20.9.0"
env_hash="-NODE_VERSION-20.9.0"
# Generate final key
cache_key="$RUNNER_OS"
[ -n "$KEY_PREFIX" ] && cache_key="${cache_key}-${KEY_PREFIX}"
[ -n "$CACHE_TYPE" ] && cache_key="${cache_key}-${CACHE_TYPE}"
[ -n "$files_hash" ] && cache_key="${cache_key}-${files_hash}"
[ -n "$env_hash" ] && cache_key="${cache_key}${env_hash}"
echo "Generated cache key: $cache_key"
# Verify structure
if [[ ! "$cache_key" =~ ^Linux-node-20-npm-[a-f0-9]{64}-NODE_VERSION-20\.9\.0$ ]]; then
echo "❌ ERROR: Invalid cache key structure: $cache_key"
exit 1
fi
echo "✓ Complete cache key generation works"
test-common-cache-restore-keys:
name: Test Restore Keys
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Test single restore key
run: |
RESTORE_KEYS="Linux-npm-"
if [[ -z "$RESTORE_KEYS" ]]; then
echo "❌ ERROR: Restore keys should not be empty"
exit 1
fi
echo "✓ Single restore key: $RESTORE_KEYS"
- name: Test multiple restore keys
run: |
RESTORE_KEYS="Linux-node-20-npm-,Linux-node-npm-,Linux-npm-"
IFS=',' read -ra KEYS <<< "$RESTORE_KEYS"
if [ ${#KEYS[@]} -ne 3 ]; then
echo "❌ ERROR: Should have 3 restore keys, got ${#KEYS[@]}"
exit 1
fi
echo "✓ Multiple restore keys work"
test-common-cache-type-specific-scenarios:
name: Test Type-Specific Scenarios
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Test NPM cache key
run: |
TYPE="npm"
FILES="package-lock.json"
PATHS="~/.npm,node_modules"
echo "✓ NPM cache configuration valid"
echo " Type: $TYPE"
echo " Key files: $FILES"
echo " Paths: $PATHS"
- name: Test Composer cache key
run: |
TYPE="composer"
FILES="composer.lock"
PATHS="~/.composer/cache,vendor"
echo "✓ Composer cache configuration valid"
echo " Type: $TYPE"
echo " Key files: $FILES"
echo " Paths: $PATHS"
- name: Test Go cache key
run: |
TYPE="go"
FILES="go.sum"
PATHS="~/go/pkg/mod,~/.cache/go-build"
echo "✓ Go cache configuration valid"
echo " Type: $TYPE"
echo " Key files: $FILES"
echo " Paths: $PATHS"
- name: Test Pip cache key
run: |
TYPE="pip"
FILES="requirements.txt"
PATHS="~/.cache/pip"
echo "✓ Pip cache configuration valid"
echo " Type: $TYPE"
echo " Key files: $FILES"
echo " Paths: $PATHS"
test-common-cache-edge-cases:
name: Test Edge Cases
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Test empty prefix
run: |
KEY_PREFIX=""
cache_key="Linux"
[ -n "$KEY_PREFIX" ] && cache_key="${cache_key}-${KEY_PREFIX}"
if [[ "$cache_key" != "Linux" ]]; then
echo "❌ ERROR: Empty prefix should not modify key"
exit 1
fi
echo "✓ Empty prefix handling works"
- name: Test no key files
run: |
KEY_FILES=""
files_hash=""
if [ -n "$KEY_FILES" ]; then
echo "❌ ERROR: Should detect empty key files"
exit 1
fi
echo "✓ No key files handling works"
- name: Test no env vars
run: |
ENV_VARS=""
env_hash=""
if [ -n "$ENV_VARS" ]; then
echo "❌ ERROR: Should detect empty env vars"
exit 1
fi
echo "✓ No env vars handling works"
integration-test-summary:
name: Integration Test Summary
runs-on: ubuntu-latest
needs:
- test-common-cache-key-generation
- test-common-cache-file-hashing
- test-common-cache-env-vars
- test-common-cache-path-processing
- test-common-cache-complete-key-generation
- test-common-cache-restore-keys
- test-common-cache-type-specific-scenarios
- test-common-cache-edge-cases
steps:
- name: Summary
run: |
echo "=========================================="
echo "Common Cache Integration Tests - PASSED"
echo "=========================================="
echo ""
echo "✓ Cache key generation tests"
echo "✓ File hashing tests"
echo "✓ Environment variable tests"
echo "✓ Path processing tests"
echo "✓ Complete key generation tests"
echo "✓ Restore keys tests"
echo "✓ Type-specific scenario tests"
echo "✓ Edge case tests"
echo ""
echo "All common-cache integration tests completed successfully!"

View File

@@ -1,168 +0,0 @@
#!/usr/bin/env shellspec
# Unit tests for common-cache action validation and logic
# Framework is automatically loaded via spec_helper.sh
Describe "common-cache action"
ACTION_DIR="common-cache"
ACTION_FILE="$ACTION_DIR/action.yml"
Context "when validating cache type input"
It "accepts npm cache type"
When call validate_input_python "common-cache" "type" "npm"
The status should be success
End
It "accepts composer cache type"
When call validate_input_python "common-cache" "type" "composer"
The status should be success
End
It "accepts go cache type"
When call validate_input_python "common-cache" "type" "go"
The status should be success
End
It "accepts pip cache type"
When call validate_input_python "common-cache" "type" "pip"
The status should be success
End
It "accepts maven cache type"
When call validate_input_python "common-cache" "type" "maven"
The status should be success
End
It "accepts gradle cache type"
When call validate_input_python "common-cache" "type" "gradle"
The status should be success
End
It "rejects empty cache type"
When call validate_input_python "common-cache" "type" ""
The status should be failure
End
It "rejects invalid cache type"
Pending "TODO: Implement enum validation for cache type"
When call validate_input_python "common-cache" "type" "invalid-type"
The status should be failure
End
End
Context "when validating paths input"
It "accepts single path"
When call validate_input_python "common-cache" "paths" "node_modules"
The status should be success
End
It "accepts multiple paths"
When call validate_input_python "common-cache" "paths" "node_modules,dist,build"
The status should be success
End
It "rejects empty paths"
When call validate_input_python "common-cache" "paths" ""
The status should be failure
End
It "rejects path traversal"
When call validate_input_python "common-cache" "paths" "../../../etc/passwd"
The status should be failure
End
It "rejects command injection in paths"
When call validate_input_python "common-cache" "paths" "node_modules;rm -rf /"
The status should be failure
End
End
Context "when validating key-prefix input"
It "accepts valid key prefix"
When call validate_input_python "common-cache" "key-prefix" "v2-build"
The status should be success
End
It "rejects command injection in key-prefix"
When call validate_input_python "common-cache" "key-prefix" "v2&&malicious"
The status should be failure
End
End
Context "when validating key-files input"
It "accepts single key file"
When call validate_input_python "common-cache" "key-files" "package.json"
The status should be success
End
It "accepts multiple key files"
When call validate_input_python "common-cache" "key-files" "package.json,package-lock.json,yarn.lock"
The status should be success
End
It "rejects path traversal in key-files"
When call validate_input_python "common-cache" "key-files" "../../../sensitive.json"
The status should be failure
End
End
Context "when validating restore-keys input"
It "accepts valid restore keys format"
When call validate_input_python "common-cache" "restore-keys" "Linux-npm-,Linux-"
The status should be success
End
It "rejects malicious restore keys"
When call validate_input_python "common-cache" "restore-keys" "Linux-npm-;rm -rf /"
The status should be failure
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 "has correct action name"
name=$(get_action_name "$ACTION_FILE")
When call echo "$name"
The output should equal "Common Cache"
End
It "defines required inputs"
inputs=$(get_action_inputs "$ACTION_FILE")
When call echo "$inputs"
The output should include "type"
The output should include "paths"
End
It "defines optional inputs"
inputs=$(get_action_inputs "$ACTION_FILE")
When call echo "$inputs"
The output should include "key-prefix"
The output should include "key-files"
The output should include "restore-keys"
The output should include "env-vars"
End
It "defines expected outputs"
outputs=$(get_action_outputs "$ACTION_FILE")
When call echo "$outputs"
The output should include "cache-hit"
The output should include "cache-key"
The output should include "cache-paths"
End
End
Context "when validating security"
It "rejects injection in all input types"
When call validate_input_python "common-cache" "type" "npm;malicious"
The status should be failure
End
It "validates environment variable names safely"
When call validate_input_python "common-cache" "env-vars" "NODE_ENV,CI"
The status should be success
End
It "rejects injection in environment variables"
When call validate_input_python "common-cache" "env-vars" "NODE_ENV;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" "type" "npm" "paths" "node_modules"
The status should be success
The stderr should include "Testing action outputs for: common-cache"
The stderr should include "Output test passed for: common-cache"
End
End
End

View File

@@ -1,244 +0,0 @@
#!/usr/bin/env python3
"""Custom validator for common-cache action.
This validator handles caching-specific validation including:
- Cache types (npm, composer, go, pip, maven, gradle)
- Cache paths (comma-separated list)
- Cache keys and restore keys
- Path validation with special handling for multiple paths
"""
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
from validators.file import FileValidator
class CustomValidator(BaseValidator):
"""Custom validator for common-cache action.
Provides validation for cache configuration.
"""
def __init__(self, action_type: str = "common-cache") -> None:
"""Initialize the common-cache validator."""
super().__init__(action_type)
self.file_validator = FileValidator(action_type)
def validate_inputs(self, inputs: dict[str, str]) -> bool:
"""Validate common-cache specific inputs.
Args:
inputs: Dictionary of input names to values
Returns:
True if all validations pass, False otherwise
"""
valid = True
# Validate type (required)
if "type" in inputs:
valid &= self.validate_cache_type(inputs["type"])
else:
# Type is required
self.add_error("Cache type is required")
valid = False
# Validate paths (required)
if "paths" in inputs:
valid &= self.validate_cache_paths(inputs["paths"])
else:
# Paths is required
self.add_error("Cache paths are required")
valid = False
# Validate key-prefix (optional)
if inputs.get("key-prefix"):
valid &= self.validate_key_prefix(inputs["key-prefix"])
# Validate key-files (optional)
if inputs.get("key-files"):
valid &= self.validate_key_files(inputs["key-files"])
# Validate restore-keys (optional)
if inputs.get("restore-keys"):
valid &= self.validate_restore_keys(inputs["restore-keys"])
# Validate env-vars (optional)
if inputs.get("env-vars"):
valid &= self.validate_env_vars(inputs["env-vars"])
return valid
def get_required_inputs(self) -> list[str]:
"""Get list of required inputs for common-cache.
Returns:
List of required input names
"""
return ["type", "paths"]
def get_validation_rules(self) -> dict:
"""Get validation rules for common-cache.
Returns:
Dictionary of validation rules
"""
return {
"type": "Cache type (npm, composer, go, pip, maven, gradle)",
"paths": "Comma-separated list of paths to cache",
"key-prefix": "Optional prefix for cache key",
"key-files": "Files to include in cache key hash",
"restore-keys": "Fallback cache keys to try",
}
def validate_cache_type(self, cache_type: str) -> bool:
"""Validate cache type.
Args:
cache_type: Type of cache
Returns:
True if valid, False otherwise
"""
# Check for empty
if not cache_type or not cache_type.strip():
self.add_error("Cache type cannot be empty")
return False
# Allow GitHub Actions expressions
if self.is_github_expression(cache_type):
return True
# Note: The test says "accepts invalid cache type (no validation in action)"
# This suggests we should accept any value, not just the supported ones
# So we'll just validate for security issues, not restrict to specific types
# Check for command injection using base validator
return self.validate_security_patterns(cache_type, "cache type")
def validate_cache_paths(self, paths: str) -> bool:
"""Validate cache paths (comma-separated).
Args:
paths: Comma-separated paths
Returns:
True if valid, False otherwise
"""
# Check for empty
if not paths or not paths.strip():
self.add_error("Cache paths cannot be empty")
return False
# Allow GitHub Actions expressions
if self.is_github_expression(paths):
return True
# Split paths and validate each
path_list = [p.strip() for p in paths.split(",")]
for path in path_list:
if not path:
continue
# Use FileValidator for path validation
result = self.file_validator.validate_file_path(path, "paths")
# Propagate errors from file validator
for error in self.file_validator.errors:
if error not in self.errors:
self.add_error(error)
self.file_validator.clear_errors()
if not result:
return False
return True
def validate_key_prefix(self, key_prefix: str) -> bool:
"""Validate cache key prefix.
Args:
key_prefix: Key prefix
Returns:
True if valid, False otherwise
"""
# Allow GitHub Actions expressions
if self.is_github_expression(key_prefix):
return True
# Check for command injection using base validator
return self.validate_security_patterns(key_prefix, "key-prefix")
def validate_key_files(self, key_files: str) -> bool:
"""Validate key files (comma-separated).
Args:
key_files: Comma-separated file paths
Returns:
True if valid, False otherwise
"""
# Allow GitHub Actions expressions
if self.is_github_expression(key_files):
return True
# Split files and validate each
file_list = [f.strip() for f in key_files.split(",")]
for file_path in file_list:
if not file_path:
continue
# Use FileValidator for path validation
result = self.file_validator.validate_file_path(file_path, "key-files")
# Propagate errors from file validator
for error in self.file_validator.errors:
if error not in self.errors:
self.add_error(error)
self.file_validator.clear_errors()
if not result:
return False
return True
def validate_restore_keys(self, restore_keys: str) -> bool:
"""Validate restore keys.
Args:
restore_keys: Restore keys specification
Returns:
True if valid, False otherwise
"""
# Allow GitHub Actions expressions
if self.is_github_expression(restore_keys):
return True
# Check for command injection using base validator
return self.validate_security_patterns(restore_keys, "restore-keys")
def validate_env_vars(self, env_vars: str) -> bool:
"""Validate environment variables.
Args:
env_vars: Environment variables specification
Returns:
True if valid, False otherwise
"""
# Allow GitHub Actions expressions
if self.is_github_expression(env_vars):
return True
# Check for command injection using base validator
return self.validate_security_patterns(env_vars, "env-vars")

View File

@@ -1,72 +0,0 @@
# ivuorinen/actions/common-cache
## Common Cache
### Description
Standardized caching strategy for all actions
### Inputs
| name | description | required | default |
|----------------|------------------------------------------------------|----------|---------|
| `type` | <p>Type of cache (npm, composer, go, pip, etc.)</p> | `true` | `""` |
| `paths` | <p>Paths to cache (comma-separated)</p> | `true` | `""` |
| `key-prefix` | <p>Custom prefix for cache key</p> | `false` | `""` |
| `key-files` | <p>Files to hash for cache key (comma-separated)</p> | `false` | `""` |
| `restore-keys` | <p>Fallback keys for cache restoration</p> | `false` | `""` |
| `env-vars` | <p>Environment variables to include in cache key</p> | `false` | `""` |
### Outputs
| name | description |
|---------------|-----------------------------|
| `cache-hit` | <p>Cache hit indicator</p> |
| `cache-key` | <p>Generated cache key</p> |
| `cache-paths` | <p>Resolved cache paths</p> |
### Runs
This action is a `composite` action.
### Usage
```yaml
- uses: ivuorinen/actions/common-cache@main
with:
type:
# Type of cache (npm, composer, go, pip, etc.)
#
# Required: true
# Default: ""
paths:
# Paths to cache (comma-separated)
#
# Required: true
# Default: ""
key-prefix:
# Custom prefix for cache key
#
# Required: false
# Default: ""
key-files:
# Files to hash for cache key (comma-separated)
#
# Required: false
# Default: ""
restore-keys:
# Fallback keys for cache restoration
#
# Required: false
# Default: ""
env-vars:
# Environment variables to include in cache key
#
# Required: false
# Default: ""
```

View File

@@ -1,122 +0,0 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-action.json
# permissions:
# - contents: read # Required for reading cache contents
---
name: Common Cache
description: 'Standardized caching strategy for all actions'
author: 'Ismo Vuorinen'
branding:
icon: database
color: gray-dark
inputs:
type:
description: 'Type of cache (npm, composer, go, pip, etc.)'
required: true
paths:
description: 'Paths to cache (comma-separated)'
required: true
key-prefix:
description: 'Custom prefix for cache key'
required: false
default: ''
key-files:
description: 'Files to hash for cache key (comma-separated)'
required: false
default: ''
restore-keys:
description: 'Fallback keys for cache restoration'
required: false
default: ''
env-vars:
description: 'Environment variables to include in cache key'
required: false
default: ''
outputs:
cache-hit:
description: 'Cache hit indicator'
value: ${{ steps.cache.outputs.cache-hit }}
cache-key:
description: 'Generated cache key'
value: ${{ steps.prepare.outputs.cache-key }}
cache-paths:
description: 'Resolved cache paths'
value: ${{ steps.prepare.outputs.cache-paths }}
runs:
using: composite
steps:
- id: prepare
shell: bash
env:
RUNNER_OS: ${{ runner.os }}
CACHE_TYPE: ${{ inputs.type }}
KEY_PREFIX: ${{ inputs.key-prefix }}
KEY_FILES: ${{ inputs.key-files }}
ENV_VARS: ${{ inputs.env-vars }}
CACHE_PATHS: ${{ inputs.paths }}
run: |
set -euo pipefail
# Generate standardized cache key components
os_key="$RUNNER_OS"
type_key="$CACHE_TYPE"
prefix_key="$KEY_PREFIX"
# Process file hashes
# Note: For simple glob patterns, hashFiles() function could be used directly
# in the cache key. This manual approach is used to support comma-separated
# file lists with complex cache key construction.
files_hash=""
if [ -n "$KEY_FILES" ]; then
IFS=',' read -ra FILES <<< "$KEY_FILES"
existing_files=()
for file in "${FILES[@]}"; do
# Trim whitespace
file=$(echo "$file" | xargs)
if [ -f "$file" ]; then
existing_files+=("$file")
fi
done
# Hash all files together for better performance
if [ ${#existing_files[@]} -gt 0 ]; then
files_hash=$(cat "${existing_files[@]}" | sha256sum | cut -d' ' -f1)
fi
fi
# Process environment variables
env_hash=""
if [ -n "$ENV_VARS" ]; then
IFS=',' read -ra VARS <<< "$ENV_VARS"
for var in "${VARS[@]}"; do
if [ -n "${!var}" ]; then
env_hash="${env_hash}-${var}-${!var}"
fi
done
fi
# Generate final cache key
cache_key="${os_key}"
[ -n "$prefix_key" ] && cache_key="${cache_key}-${prefix_key}"
[ -n "$type_key" ] && cache_key="${cache_key}-${type_key}"
[ -n "$files_hash" ] && cache_key="${cache_key}-${files_hash}"
[ -n "$env_hash" ] && cache_key="${cache_key}-${env_hash}"
echo "cache-key=${cache_key}" >> $GITHUB_OUTPUT
# Process cache paths
IFS=',' read -ra PATHS <<< "$CACHE_PATHS"
cache_paths=""
for path in "${PATHS[@]}"; do
cache_paths="${cache_paths}${path}\n"
done
echo "cache-paths=${cache_paths}" >> $GITHUB_OUTPUT
- id: cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ steps.prepare.outputs.cache-paths }}
key: ${{ steps.prepare.outputs.cache-key }}
restore-keys: ${{ inputs.restore-keys }}

View File

@@ -1,42 +0,0 @@
---
# Validation rules for common-cache 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 common-cache GitHub Action.
# Rules are automatically applied by validate-inputs action when this
# action is used.
#
schema_version: '1.0'
action: common-cache
description: Standardized caching strategy for all actions
generator_version: 1.0.0
required_inputs:
- paths
- type
optional_inputs:
- env-vars
- key-files
- key-prefix
- restore-keys
conventions:
key-files: file_path
key-prefix: prefix
paths: 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: false
has_file_validation: true
has_security_validation: false

View File

@@ -1,74 +0,0 @@
"""Tests for common-cache 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 / "common-cache"
sys.path.insert(0, str(action_path))
# pylint: disable=wrong-import-position
from CustomValidator import CustomValidator
class TestCustomCommonCacheValidator:
"""Test cases for common-cache custom validator."""
def setup_method(self):
"""Set up test fixtures."""
self.validator = CustomValidator("common-cache")
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 common-cache
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 common-cache
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 common-cache
def test_validation_rules(self):
"""Test validation rules."""
rules = self.validator.get_validation_rules()
assert isinstance(rules, dict)
# TODO: Assert specific validation rules for common-cache
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