mirror of
https://github.com/ivuorinen/actions.git
synced 2026-03-05 16:55:06 +00:00
feat: add GitHub Actions workflows for code quality and automation (#2)
This commit is contained in:
101
eslint-check/README.md
Normal file
101
eslint-check/README.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# ivuorinen/actions/eslint-check
|
||||
|
||||
## ESLint Check
|
||||
|
||||
### Description
|
||||
|
||||
Run ESLint check on the repository with advanced configuration and reporting
|
||||
|
||||
### Inputs
|
||||
|
||||
| name | description | required | default |
|
||||
| ------------------- | ------------------------------------------------ | -------- | ------------------- |
|
||||
| `working-directory` | <p>Directory containing files to lint</p> | `false` | `.` |
|
||||
| `eslint-version` | <p>ESLint version to use</p> | `false` | `latest` |
|
||||
| `config-file` | <p>Path to ESLint config file</p> | `false` | `.eslintrc` |
|
||||
| `ignore-file` | <p>Path to ESLint ignore file</p> | `false` | `.eslintignore` |
|
||||
| `file-extensions` | <p>File extensions to lint (comma-separated)</p> | `false` | `.js,.jsx,.ts,.tsx` |
|
||||
| `cache` | <p>Enable ESLint caching</p> | `false` | `true` |
|
||||
| `max-warnings` | <p>Maximum number of warnings allowed</p> | `false` | `0` |
|
||||
| `fail-on-error` | <p>Fail workflow if issues are found</p> | `false` | `true` |
|
||||
| `report-format` | <p>Output format (stylish, json, sarif)</p> | `false` | `sarif` |
|
||||
| `max-retries` | <p>Maximum number of retry attempts</p> | `false` | `3` |
|
||||
|
||||
### Outputs
|
||||
|
||||
| name | description |
|
||||
| --------------- | -------------------------------- |
|
||||
| `error-count` | <p>Number of errors found</p> |
|
||||
| `warning-count` | <p>Number of warnings found</p> |
|
||||
| `sarif-file` | <p>Path to SARIF report file</p> |
|
||||
| `files-checked` | <p>Number of files checked</p> |
|
||||
|
||||
### Runs
|
||||
|
||||
This action is a `composite` action.
|
||||
|
||||
### Usage
|
||||
|
||||
```yaml
|
||||
- uses: ivuorinen/actions/eslint-check@main
|
||||
with:
|
||||
working-directory:
|
||||
# Directory containing files to lint
|
||||
#
|
||||
# Required: false
|
||||
# Default: .
|
||||
|
||||
eslint-version:
|
||||
# ESLint version to use
|
||||
#
|
||||
# Required: false
|
||||
# Default: latest
|
||||
|
||||
config-file:
|
||||
# Path to ESLint config file
|
||||
#
|
||||
# Required: false
|
||||
# Default: .eslintrc
|
||||
|
||||
ignore-file:
|
||||
# Path to ESLint ignore file
|
||||
#
|
||||
# Required: false
|
||||
# Default: .eslintignore
|
||||
|
||||
file-extensions:
|
||||
# File extensions to lint (comma-separated)
|
||||
#
|
||||
# Required: false
|
||||
# Default: .js,.jsx,.ts,.tsx
|
||||
|
||||
cache:
|
||||
# Enable ESLint caching
|
||||
#
|
||||
# Required: false
|
||||
# Default: true
|
||||
|
||||
max-warnings:
|
||||
# Maximum number of warnings allowed
|
||||
#
|
||||
# Required: false
|
||||
# Default: 0
|
||||
|
||||
fail-on-error:
|
||||
# Fail workflow if issues are found
|
||||
#
|
||||
# Required: false
|
||||
# Default: true
|
||||
|
||||
report-format:
|
||||
# Output format (stylish, json, sarif)
|
||||
#
|
||||
# Required: false
|
||||
# Default: sarif
|
||||
|
||||
max-retries:
|
||||
# Maximum number of retry attempts
|
||||
#
|
||||
# Required: false
|
||||
# Default: 3
|
||||
```
|
||||
261
eslint-check/action.yml
Normal file
261
eslint-check/action.yml
Normal file
@@ -0,0 +1,261 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-action.json
|
||||
name: ESLint Check
|
||||
description: 'Run ESLint check on the repository with advanced configuration and reporting'
|
||||
author: Ismo Vuorinen
|
||||
|
||||
branding:
|
||||
icon: check-circle
|
||||
color: blue
|
||||
|
||||
inputs:
|
||||
working-directory:
|
||||
description: 'Directory containing files to lint'
|
||||
required: false
|
||||
default: '.'
|
||||
eslint-version:
|
||||
description: 'ESLint version to use'
|
||||
required: false
|
||||
default: 'latest'
|
||||
config-file:
|
||||
description: 'Path to ESLint config file'
|
||||
required: false
|
||||
default: '.eslintrc'
|
||||
ignore-file:
|
||||
description: 'Path to ESLint ignore file'
|
||||
required: false
|
||||
default: '.eslintignore'
|
||||
file-extensions:
|
||||
description: 'File extensions to lint (comma-separated)'
|
||||
required: false
|
||||
default: '.js,.jsx,.ts,.tsx'
|
||||
cache:
|
||||
description: 'Enable ESLint caching'
|
||||
required: false
|
||||
default: 'true'
|
||||
max-warnings:
|
||||
description: 'Maximum number of warnings allowed'
|
||||
required: false
|
||||
default: '0'
|
||||
fail-on-error:
|
||||
description: 'Fail workflow if issues are found'
|
||||
required: false
|
||||
default: 'true'
|
||||
report-format:
|
||||
description: 'Output format (stylish, json, sarif)'
|
||||
required: false
|
||||
default: 'sarif'
|
||||
max-retries:
|
||||
description: 'Maximum number of retry attempts'
|
||||
required: false
|
||||
default: '3'
|
||||
|
||||
outputs:
|
||||
error-count:
|
||||
description: 'Number of errors found'
|
||||
value: ${{ steps.lint.outputs.error_count }}
|
||||
warning-count:
|
||||
description: 'Number of warnings found'
|
||||
value: ${{ steps.lint.outputs.warning_count }}
|
||||
sarif-file:
|
||||
description: 'Path to SARIF report file'
|
||||
value: ${{ steps.lint.outputs.sarif_file }}
|
||||
files-checked:
|
||||
description: 'Number of files checked'
|
||||
value: ${{ steps.lint.outputs.files_checked }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Validate Inputs
|
||||
id: validate
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Validate working directory
|
||||
if [ ! -d "${{ inputs.working-directory }}" ]; then
|
||||
echo "::error::Working directory does not exist: ${{ inputs.working-directory }}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate file extensions
|
||||
if ! [[ "${{ inputs.file-extensions }}" =~ ^[.,a-zA-Z0-9]+$ ]]; then
|
||||
echo "::error::Invalid file extensions format"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate max warnings
|
||||
if ! [[ "${{ inputs.max-warnings }}" =~ ^[0-9]+$ ]]; then
|
||||
echo "::error::Invalid max warnings value"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: ivuorinen/actions/node-setup@main
|
||||
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
cd ${{ inputs.working-directory }}
|
||||
|
||||
# Install ESLint and required dependencies
|
||||
echo "Installing ESLint dependencies..."
|
||||
|
||||
# Function to install with retries
|
||||
install_with_retries() {
|
||||
local attempt=1
|
||||
local max_attempts=${{ inputs.max-retries }}
|
||||
|
||||
while [ $attempt -le $max_attempts ]; do
|
||||
echo "Installation attempt $attempt of $max_attempts"
|
||||
|
||||
if npm install \
|
||||
eslint@${{ inputs.eslint-version }} \
|
||||
@typescript-eslint/parser \
|
||||
@typescript-eslint/eslint-plugin \
|
||||
eslint-plugin-import \
|
||||
eslint-config-prettier \
|
||||
typescript; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
attempt=$((attempt + 1))
|
||||
if [ $attempt -le $max_attempts ]; then
|
||||
echo "Installation failed, waiting 10 seconds before retry..."
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
|
||||
echo "::error::Failed to install dependencies after $max_attempts attempts"
|
||||
return 1
|
||||
}
|
||||
|
||||
install_with_retries
|
||||
|
||||
- name: Prepare ESLint Configuration
|
||||
id: config
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
cd ${{ inputs.working-directory }}
|
||||
|
||||
# Create default config if none exists
|
||||
if [ ! -f "${{ inputs.config-file }}" ]; then
|
||||
echo "Creating default ESLint configuration..."
|
||||
cat > "${{ inputs.config-file }}" <<EOF
|
||||
{
|
||||
"root": true,
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings",
|
||||
"plugin:import/typescript",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2022,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["@typescript-eslint", "import"],
|
||||
"env": {
|
||||
"es2022": true,
|
||||
"node": true
|
||||
}
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Create default ignore file if none exists
|
||||
if [ ! -f "${{ inputs.ignore-file }}" ]; then
|
||||
echo "Creating default ESLint ignore file..."
|
||||
cat > "${{ inputs.ignore-file }}" <<EOF
|
||||
node_modules/
|
||||
dist/
|
||||
build/
|
||||
coverage/
|
||||
*.min.js
|
||||
EOF
|
||||
fi
|
||||
|
||||
- name: Run ESLint Check
|
||||
id: lint
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
cd ${{ inputs.working-directory }}
|
||||
|
||||
# Create reports directory
|
||||
mkdir -p reports
|
||||
|
||||
# Prepare file extensions for ESLint
|
||||
IFS=',' read -ra EXTENSIONS <<< "${{ inputs.file-extensions }}"
|
||||
ext_pattern=""
|
||||
for ext in "${EXTENSIONS[@]}"; do
|
||||
ext_pattern="$ext_pattern --ext $ext"
|
||||
done
|
||||
|
||||
# Run ESLint
|
||||
echo "Running ESLint..."
|
||||
npx eslint \
|
||||
$ext_pattern \
|
||||
--config ${{ inputs.config-file }} \
|
||||
--ignore-path ${{ inputs.ignore-file }} \
|
||||
${{ inputs.cache == 'true' && '--cache' || '' }} \
|
||||
--max-warnings ${{ inputs.max-warnings }} \
|
||||
--format=${{ inputs.report-format }} \
|
||||
--output-file=reports/eslint.${{ inputs.report-format }} \
|
||||
. || {
|
||||
error_code=$?
|
||||
|
||||
# Count errors and warnings
|
||||
if [ "${{ inputs.report-format }}" = "json" ]; then
|
||||
error_count=$(jq '[.[] | .errorCount] | add' reports/eslint.json)
|
||||
warning_count=$(jq '[.[] | .warningCount] | add' reports/eslint.json)
|
||||
else
|
||||
error_count=$(grep -c '"level": "error"' reports/eslint.sarif || echo 0)
|
||||
warning_count=$(grep -c '"level": "warning"' reports/eslint.sarif || echo 0)
|
||||
fi
|
||||
|
||||
echo "error_count=${error_count}" >> $GITHUB_OUTPUT
|
||||
echo "warning_count=${warning_count}" >> $GITHUB_OUTPUT
|
||||
|
||||
if [ "${{ inputs.fail-on-error }}" = "true" ] && [ $error_code -ne 0 ]; then
|
||||
echo "::error::ESLint found ${error_count} errors and ${warning_count} warnings"
|
||||
exit $error_code
|
||||
fi
|
||||
}
|
||||
|
||||
# Count checked files
|
||||
files_checked=$(find . -type f \( $(printf -- "-name *%s -o " "${EXTENSIONS[@]}") -false \) | wc -l)
|
||||
echo "files_checked=${files_checked}" >> $GITHUB_OUTPUT
|
||||
echo "sarif_file=reports/eslint.sarif" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload ESLint Results
|
||||
if: always() && inputs.report-format == 'sarif'
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
sarif_file: ${{ inputs.working-directory }}/reports/eslint.sarif
|
||||
category: eslint
|
||||
|
||||
- name: Cache Cleanup
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
cd ${{ inputs.working-directory }}
|
||||
|
||||
# Clean up ESLint cache if it exists
|
||||
if [ -f ".eslintcache" ]; then
|
||||
rm .eslintcache
|
||||
fi
|
||||
|
||||
# Remove temporary files
|
||||
rm -rf reports/
|
||||
Reference in New Issue
Block a user