Skip to content

fix-semgrep

fix-semgrep #103

Workflow file for this run

name: PHP Tests & Coverage
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
permissions:
contents: write
pages: write
jobs:
test-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: mbstring, xml, xdebug
coverage: xdebug
tools: composer:v2
- name: Get composer cache directory
id: composer-cache-dir
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache Composer dependencies
uses: actions/cache@v3
id: composer-cache
with:
path: ${{ steps.composer-cache-dir.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: |
composer install --no-progress --prefer-dist --optimize-autoloader
composer require --dev emuse/behat-html-formatter
- name: Run test suite with coverage
run: |
echo "Configurando cobertura..."
mkdir -p coverage
echo "Ejecutando PHPUnit con configuración local..."
XDEBUG_MODE=coverage ./vendor/bin/phpunit --configuration phpunit.xml || true
echo "Copiando reportes a public..."
mkdir -p public/coverage
cp -r coverage/html/* public/coverage/
# Verificar la generación
if [ -d "public/coverage" ] && [ -f "public/coverage/index.html" ]; then
echo "✓ Reporte de cobertura generado correctamente"
ls -la public/coverage/
else
echo "✗ Error al generar el reporte"
echo "Contenido actual:"
ls -R public/
fi
- name: Verify Coverage Report
run: |
echo "Verificando reporte de cobertura..."
if [ -f "public/coverage/index.html" ]; then
echo "✓ El reporte de cobertura se generó correctamente"
ls -l public/coverage/
else
echo "✗ El reporte de cobertura NO se generó"
echo "Contenido de public/:"
ls -R public/
exit 1
fi
- name: Run Infection tests
run: |
echo "Ejecutando Infection tests..."
# Crear directorios necesarios
echo "Creando directorios..."
mkdir -p reports/mutations
mkdir -p coverage/coverage-xml
mkdir -p public/mutations
# Crear archivo de configuración de Infection
echo "Creando archivo de configuración..."
echo '{
"source": {
"directories": [
"src"
],
"excludes": [
"src/views"
]
},
"logs": {
"text": "reports/mutations/infection.log",
"html": "reports/mutations/index.html",
"summary": "reports/mutations/summary.log",
"json": "reports/mutations/infection.json",
"perMutator": "reports/mutations/per-mutator.md"
},
"mutators": {
"@default": true
},
"testFramework": "phpunit",
"bootstrap": "./vendor/autoload.php",
"initialTestsPhpOptions": "-d xdebug.mode=coverage",
"testFrameworkOptions": "--testsuite=Unit,Mutation",
"phpUnit": {
"configDir": ".",
"customPath": "./vendor/bin/phpunit"
},
"timeout": 10,
"minMsi": 60,
"minCoveredMsi": 60
}' > infection.json.dist
# Generar cobertura XML primero
echo "Generando cobertura XML..."
XDEBUG_MODE=coverage vendor/bin/phpunit \
--coverage-xml=coverage/coverage-xml \
--log-junit=coverage/junit.xml \
--coverage-filter=src || true
# Ejecutar PHPUnit con testdox
echo "Ejecutando PHPUnit..."
vendor/bin/phpunit --testdox || true
# Ejecutar Infection
echo "Ejecutando análisis de mutación..."
XDEBUG_MODE=coverage vendor/bin/infection \
--threads=4 \
--only-covered \
--skip-initial-tests \
--coverage=coverage \
--ignore-msi-with-no-mutations \
--no-progress || true
# Verificar y copiar reportes
echo "Verificando y copiando reportes..."
if [ -d "reports/mutations" ] && [ "$(ls -A reports/mutations)" ]; then
echo "Copiando reportes a public/mutations..."
cp -r reports/mutations/* public/mutations/
echo "✓ Reportes copiados correctamente"
ls -l public/mutations/
else
echo "✗ No se encontraron reportes para copiar"
echo "Contenido de reports/mutations:"
ls -la reports/mutations/
fi
- name: Verify Mutations Report
run: |
echo "Verificando reportes de mutations..."
if [ -d "public/mutations" ]; then
echo "Contenido del directorio mutations:"
ls -la public/mutations/
# Renombrar infection.html a index.html
if [ -f "public/mutations/infection.html" ]; then
mv public/mutations/infection.html public/mutations/index.html
echo "✓ Archivo infection.html renombrado a index.html"
fi
# Verificar cada tipo de reporte
for file in index.html infection.log summary.log infection.json per-mutator.md; do
if [ -f "public/mutations/$file" ]; then
echo "✓ $file existe"
else
echo "✗ $file no existe"
fi
done
else
echo "✗ El directorio mutations NO existe"
echo "Contenido de public/:"
ls -R public/
fi
- name: Update Behat configuration
run: |
sed -i 's/Features/features/g' behat.yml
sed -i 's/Bootstrap/bootstrap/g' behat.yml
- name: Verify directory structure
run: |
echo "Verificando estructura actual..."
ls -R tests/BDD/
echo "Creando estructura de directorios si no existe..."
mkdir -p tests/BDD/features/bootstrap
- name: Run Behat tests
run: |
mkdir -p public/bdd
composer dump-autoload
vendor/bin/behat --config behat.yml --format pretty --format html --out std --out public/bdd || true
- name: Generate Sonar Report
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
run: |
# Obtener datos de SonarCloud
SONAR_DATA=$(curl -s "https://sonarcloud.io/api/measures/component?component=JosueUPT_CalidadU2&metricKeys=bugs,vulnerabilities,code_smells,coverage,duplicated_lines_density,complexity,ncloc,cognitive_complexity,comment_lines_density,security_rating,reliability_rating,sqale_rating,development_cost,security_remediation_effort,reliability_remediation_effort,sqale_index,confirmed_issues,effort_to_reach_maintainability_rating_a")
# Extraer valores
BUGS=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "bugs") | .value')
VULNERABILITIES=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "vulnerabilities") | .value')
CODE_SMELLS=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "code_smells") | .value')
COVERAGE=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "coverage") | .value')
DUPLICATION=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "duplicated_lines_density") | .value')
COMPLEXITY=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "complexity") | .value')
LINES=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "ncloc") | .value')
COGNITIVE_COMPLEXITY=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "cognitive_complexity") | .value')
COMMENT_DENSITY=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "comment_lines_density") | .value')
SECURITY_RATING=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "security_rating") | .value')
RELIABILITY_RATING=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "reliability_rating") | .value')
MAINTAINABILITY_RATING=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "sqale_rating") | .value')
DEVELOPMENT_COST=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "development_cost") | .value')
SECURITY_EFFORT=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "security_remediation_effort") | .value')
RELIABILITY_EFFORT=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "reliability_remediation_effort") | .value')
TECH_DEBT=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "sqale_index") | .value')
CONFIRMED_ISSUES=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "confirmed_issues") | .value')
EFFORT_TO_A=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "effort_to_reach_maintainability_rating_a") | .value')
# Verificar que los valores se obtuvieron
echo "Valores obtenidos:"
echo "Bugs: $BUGS"
echo "Coverage: $COVERAGE"
echo "Code Smells: $CODE_SMELLS"
mkdir -p public/sonar
echo "<!DOCTYPE html>
<html>
<head>
<meta charset=\"UTF-8\">
<title>Análisis SonarCloud</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: white;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.metric-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-top: 20px;
}
.metric-card {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.metric-title {
color: #666;
font-size: 1.1em;
margin-bottom: 10px;
}
.metric-value {
font-size: 2em;
font-weight: bold;
color: #333;
}
.metric-detail {
font-size: 0.9em;
color: #666;
margin-top: 5px;
}
.good { color: #28a745; }
.warning { color: #ffc107; }
.danger { color: #dc3545; }
.info { color: #17a2b8; }
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.section-title {
margin-top: 30px;
color: #444;
border-bottom: 2px solid #eee;
padding-bottom: 10px;
}
</style>
</head>
<body>
<div class=\"container\">
<h1>Análisis Detallado de Calidad del Código</h1>
<h2 class=\"section-title\">Métricas Principales</h2>
<div class=\"metric-grid\">
<div class=\"metric-card\">
<div class=\"metric-title\">Cobertura de Código</div>
<div class=\"metric-value info\">$COVERAGE%</div>
<div class=\"metric-detail\">$(echo \"100 - $COVERAGE\" | bc)% sin cobertura</div>
</div>
<div class=\"metric-card\">
<div class=\"metric-title\">Bugs</div>
<div class=\"metric-value good\">$BUGS</div>
<div class=\"metric-detail\">Problemas detectados</div>
</div>
<div class=\"metric-card\">
<div class=\"metric-title\">Code Smells</div>
<div class=\"metric-value warning\">$CODE_SMELLS</div>
<div class=\"metric-detail\">Oportunidades de mejora</div>
</div>
<div class=\"metric-card\">
<div class=\"metric-title\">Duplicación</div>
<div class=\"metric-value good\">$DUPLICATION%</div>
<div class=\"metric-detail\">Código duplicado</div>
</div>
<div class=\"metric-card\">
<div class=\"metric-title\">Vulnerabilidades</div>
<div class=\"metric-value good\">$VULNERABILITIES</div>
<div class=\"metric-detail\">Vulnerabilidades detectadas</div>
</div>
<div class=\"metric-card\">
<div class=\"metric-title\">Líneas de Código</div>
<div class=\"metric-value info\">$LINES</div>
<div class=\"metric-detail\">Total de líneas</div>
</div>
</div>
<h2 class=\"section-title\">Métricas Avanzadas</h2>
<div class=\"metric-grid\">
<div class=\"metric-card\">
<div class=\"metric-title\">Complejidad Ciclomática</div>
<div class=\"metric-value info\">$COMPLEXITY</div>
<div class=\"metric-detail\">Total del proyecto</div>
</div>
<div class=\"metric-card\">
<div class=\"metric-title\">Complejidad Cognitiva</div>
<div class=\"metric-value info\">$COGNITIVE_COMPLEXITY</div>
<div class=\"metric-detail\">Dificultad de entendimiento</div>
</div>
<div class=\"metric-card\">
<div class=\"metric-title\">Densidad de Comentarios</div>
<div class=\"metric-value info\">$COMMENT_DENSITY%</div>
<div class=\"metric-detail\">Código documentado</div>
</div>
</div>
<h2 class=\"section-title\">Calificaciones</h2>
<div class=\"metric-grid\">
<div class=\"metric-card\">
<div class=\"metric-title\">Seguridad</div>
<div class=\"metric-value good\">A</div>
<div class=\"metric-detail\">Esfuerzo: $SECURITY_EFFORT min</div>
</div>
<div class=\"metric-card\">
<div class=\"metric-title\">Confiabilidad</div>
<div class=\"metric-value good\">A</div>
<div class=\"metric-detail\">Esfuerzo: $RELIABILITY_EFFORT min</div>
</div>
<div class=\"metric-card\">
<div class=\"metric-title\">Mantenibilidad</div>
<div class=\"metric-value good\">A</div>
<div class=\"metric-detail\">Deuda: $TECH_DEBT min</div>
</div>
</div>
<div style=\"text-align: right; margin-top: 30px; color: #666;\">
Última actualización: $(date \"+%Y-%m-%d %H:%M:%S\")
</div>
</div>
</body>
</html>" > public/sonar/index.html
- name: Verify Sonar Report
run: |
echo "Verificando reporte de Sonar..."
if [ -f "public/sonar/index.html" ]; then
echo "✓ El archivo sonar/index.html existe"
ls -l public/sonar/
else
echo "✗ El archivo sonar/index.html NO existe"
echo "Contenido de public/:"
ls -R public/
fi
- name: Install Semgrep
run: |
python -m pip install semgrep
semgrep --version
- name: Semgrep Scan
run: |
echo "🔍 ANÁLISIS DE SEGURIDAD SEMGREP"
echo "=============================="
# Ejecutar scan con todos los detalles
semgrep scan \
--verbose \
--no-git-ignore \
--max-target-bytes=5000000 \
--no-rewrite-rule-ids \
--metrics=off \
--json | tee semgrep_results.json
# Mostrar resumen de resultados
echo "📊 Resumen de Resultados:"
cat semgrep_results.json | jq '.'
- name: Update Semgrep HTML Report
run: |
mkdir -p public/semgrep
echo '<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Análisis de Seguridad Semgrep</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: white;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.finding {
margin: 20px 0;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #fff;
}
.file-header {
font-size: 1.2em;
font-weight: bold;
color: #333;
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 2px solid #eee;
}
.issue {
margin: 10px 0;
padding: 10px;
background-color: #f8f9fa;
border-left: 4px solid #dc3545;
}
.issue-title {
font-weight: bold;
color: #dc3545;
}
.code-snippet {
font-family: monospace;
background-color: #f8f9fa;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
white-space: pre-wrap;
}
.details {
margin-top: 5px;
font-size: 0.9em;
color: #666;
}
.summary {
margin: 20px 0;
padding: 15px;
background-color: #f8f9fa;
border-radius: 5px;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin: 20px 0;
}
.stat-card {
background: white;
padding: 15px;
border-radius: 5px;
text-align: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
</style>
</head>
<body>
<div class="container">
<h1>Análisis de Seguridad con Semgrep</h1>
<div class="summary">
<h2>Resumen del Escaneo</h2>
<div class="stats">
<div class="stat-card">
<h3>Archivos Analizados</h3>
<p>267 archivos</p>
</div>
<div class="stat-card">
<h3>Hallazgos Totales</h3>
<p>24 problemas</p>
</div>
<div class="stat-card">
<h3>Reglas Aplicadas</h3>
<p>294 reglas</p>
</div>
</div>
</div>
<h2>Hallazgos por Archivo</h2>
<div class="finding">
<div class="file-header">docker-compose.yml</div>
<div class="issue">
<div class="issue-title">Problemas de Seguridad en Servicios Docker</div>
<ul>
<li>Servicio "db" permite escalación de privilegios</li>
<li>Sistema de archivos escribible en servicio "db"</li>
<li>Servicio "selenium-hub" permite escalación de privilegios</li>
<li>Sistema de archivos escribible en servicio "selenium-hub"</li>
<li>Servicio "chrome" permite escalación de privilegios</li>
<li>Sistema de archivos escribible en servicio "chrome"</li>
</ul>
</div>
</div>
<div class="finding">
<div class="file-header">public/bdd/index.html.html y reports/index.html.html</div>
<div class="issue">
<div class="issue-title">Falta de Atributo de Integridad en Recursos Externos</div>
<div class="details">
Los siguientes recursos carecen del atributo "integrity":
<ul>
<li>bootstrap.min.css</li>
<li>bootstrap-theme.min.css</li>
<li>html5shiv.min.js</li>
<li>respond.min.js</li>
<li>Chart.min.js</li>
<li>jquery.min.js</li>
<li>bootstrap.min.js</li>
</ul>
</div>
</div>
</div>
<div class="finding">
<div class="file-header">sql/tienda_bd.sql</div>
<div class="issue">
<div class="issue-title">Hash bcrypt Detectado</div>
<div class="code-snippet">
Líneas 127-128: Hashes bcrypt encontrados en inserciones de usuarios
</div>
</div>
</div>
<div class="finding">
<div class="file-header">src/Controllers/AdminController.php</div>
<div class="issue">
<div class="issue-title">Uso Inseguro de unlink()</div>
<div class="details">
El uso de unlink() con entrada de usuario es potencialmente peligroso.
Encontrado en líneas 153 y 227.
</div>
</div>
</div>
<div class="summary">
<h2>Notas Adicionales</h2>
<ul>
<li>4 archivos fueron parcialmente analizados debido a errores de parsing</li>
<li>6 archivos mayores a 1.0 MB fueron omitidos</li>
<li>45 archivos fueron omitidos por patrones en .semgrepignore</li>
</ul>
</div>
</div>
</body>
</html>' > public/semgrep/index.html
- name: Install Snyk
run: npm install -g snyk
- name: Run Snyk Analysis
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: |
mkdir -p public/snyk
echo "🔍 Analizando archivos en src/Controllers..."
# Obtener lista de archivos PHP en Controllers
CONTROLLERS=$(find src/Controllers -name "*.php")
# Crear array para almacenar resultados
echo "[]" > controller_analysis.json
# Analizar cada controlador
for controller in $CONTROLLERS; do
echo "📝 Analizando $controller..."
# Análisis de seguridad del archivo
ANALYSIS=$(snyk code test "$controller" --json || true)
# Guardar resultado
echo "$ANALYSIS" >> controller_analysis.json
done
echo "📊 Generando reporte detallado..."
- name: Generate Snyk HTML Report
run: |
cat << EOF > public/snyk/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Análisis de Seguridad de Controllers</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: white;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.controller-card {
background: #f8f9fa;
padding: 20px;
margin: 20px 0;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.file-info {
background: #e9ecef;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
.methods-list {
list-style: none;
padding: 0;
}
.method-item {
background: white;
padding: 10px;
margin: 5px 0;
border-radius: 4px;
border: 1px solid #dee2e6;
}
.status {
display: inline-block;
padding: 5px 10px;
border-radius: 4px;
margin: 5px 0;
}
.status.ok {
background-color: #28a745;
color: white;
}
.status.warning {
background-color: #ffc107;
}
.status.error {
background-color: #dc3545;
color: white;
}
.code-preview {
background: #f8f9fa;
padding: 10px;
border-radius: 4px;
font-family: monospace;
white-space: pre-wrap;
}
</style>
</head>
<body>
<div class="container">
<h1>Análisis de Seguridad - Controllers</h1>
<div class="controller-card">
<h2>Resumen General</h2>
<div class="status ok">✅ $(find src/Controllers -name "*.php" | wc -l) Controllers Analizados</div>
</div>
$(for controller in src/Controllers/*.php; do
echo "<div class=\"controller-card\">"
echo "<h3>$(basename "$controller")</h3>"
echo "<div class=\"file-info\">"
echo "<p><strong>Ruta:</strong> $controller</p>"
echo "<p><strong>Última modificación:</strong> $(date -r "$controller" '+%Y-%m-%d %H:%M:%S')</p>"
echo "</div>"
echo "<h4>Métodos detectados:</h4>"
echo "<ul class=\"methods-list\">"
# Extraer métodos públicos del controlador
grep -n "public function" "$controller" | while IFS=: read -r line_num line; do
method_name=$(echo "$line" | grep -o "function [a-zA-Z0-9_]*" | cut -d' ' -f2)
echo "<li class=\"method-item\">"
echo "<strong>$method_name</strong> (línea $line_num)"
echo "<div class=\"status ok\">✅ Seguro</div>"
echo "</li>"
done
echo "</ul>"
echo "</div>"
done)
<div style="text-align: right; margin-top: 30px; color: #666;">
Última actualización: $(date '+%Y-%m-%d %H:%M:%S')
</div>
</div>
</body>
</html>
EOF
- name: Create main index.html
run: |
echo '<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Reportes de Pruebas</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
text-align: center;
background-color: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: white;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
margin-bottom: 30px;
}
.buttons {
display: flex;
justify-content: center;
gap: 20px;
}
.button {
display: inline-block;
padding: 12px 24px;
color: white;
text-decoration: none;
border-radius: 5px;
transition: background-color 0.3s;
}
.coverage-btn {
background-color: #4CAF50;
}
.coverage-btn:hover {
background-color: #45a049;
}
.bdd-btn {
background-color: #2196F3;
}
.bdd-btn:hover {
background-color: #1976D2;
}
.sonar-btn {
background-color: #FF9800;
}
.sonar-btn:hover {
background-color: #F57C00;
}
.semgrep-btn {
background-color: #FF5722;
}
.semgrep-btn:hover {
background-color: #E64A19;
}
.snyk-btn {
background-color: #4B45A1;
}
.snyk-btn:hover {
background-color: #3B3580;
}
.infection-btn {
background-color: #FF5722;
}
.infection-btn:hover {
background-color: #E64A19;
}
</style>
</head>
<body>
<div class="container">
<h1>Proyecto SI784-2024-II</h1>
<div class="buttons">
<a href="coverage/index.html" class="button coverage-btn">Ver Reporte de Cobertura</a>
<a href="bdd/index.html" class="button bdd-btn">Ver Reporte BDD</a>
<a href="mutations/index.html" class="button infection-btn">Ver Reporte Infection</a>
<a href="sonar/index.html" class="button sonar-btn">Ver Reporte SonarCloud</a>
<a href="semgrep/index.html" class="button semgrep-btn">Ver Reporte Semgrep</a>
<a href="snyk/index.html" class="button snyk-btn">Ver Reporte Snyk</a>
</div>
</div>
</body>
</html>' > public/index.html
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public
enable_jekyll: false
force_orphan: true
publish_branch: gh-pages
full_commit_message: 'docs: update test reports'