Skip to content

init

init #5

Workflow file for this run

name: CI – Maatify Security Guard
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test:
name: 🧪 Security Guard Tests
runs-on: ubuntu-latest
permissions:
contents: write
env:
GH_START_ISO: ${{ github.event.head_commit.timestamp }}
APP_ENV: testing
# Redis
REDIS_HOST: 127.0.0.1
REDIS_PORT: 6379
REDIS_PASS: ""
REDIS_DSN: "redis://127.0.0.1:6379"
# Mongo
MONGO_HOST: 127.0.0.1
MONGO_PORT: 27017
MONGO_DB: security_guard_test
MONGO_DSN: "mongodb://127.0.0.1:27017/security_guard_test"
# MySQL
MYSQL_HOST: 127.0.0.1
MYSQL_PORT: 3306
MYSQL_USER: root
MYSQL_PASS: root
MYSQL_DB: security_guard_test
MYSQL_DSN: "mysql:host=127.0.0.1;dbname=security_guard_test;charset=utf8mb4"
services:
redis:
image: redis:7
ports:
- "6379:6379"
mongo:
image: mongo:7
ports:
- "27017:27017"
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: security_guard_test
MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
ports:
- "3306:3306"
steps:
# ---------------------------------------------
# 1) Checkout
# ---------------------------------------------
- name: 📦 Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# ---------------------------------------------
# 2) PHP Setup
# ---------------------------------------------
- uses: shivammathur/setup-php@v2
with:
php-version: "8.4"
coverage: pcov
extensions: mbstring, intl, pdo, pdo_mysql, redis, mongodb
tools: composer
# ---------------------------------------------
# 3) Composer cache
# ---------------------------------------------
- name: 🧰 Cache Composer
uses: actions/cache@v4
with:
path: |
~/.composer/cache
~/.cache/composer
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: 🔑 Configure Composer GitHub token
run: composer config -g github-oauth.github.com "${{ github.token }}"
# ---------------------------------------------
# 4) Install dependencies
# ---------------------------------------------
- name: 📥 Install dependencies
run: composer install --no-interaction --prefer-dist --no-progress
- name: ⚙️ Rebuild Autoload
run: composer dump-autoload --optimize
# ---------------------------------------------
# 5) Wait for MySQL
# ---------------------------------------------
- name: 🕒 Wait for MySQL
run: |
for i in {1..25}; do
mysqladmin ping -h $MYSQL_HOST --silent && break
echo "⏳ Waiting for MySQL..."
sleep 2
done
- name: 🗄️ Init MySQL database
run: |
mysql -h $MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS \
-e "CREATE DATABASE IF NOT EXISTS security_guard_test;"
# ---------------------------------------------
# 6) PHPStan
# ---------------------------------------------
- name: 🧹 Run PHPStan
id: phpstan
run: |
set +e
vendor/bin/phpstan analyse src tests --level=max > phpstan.log
EXIT=${?}
ERR=$(grep -Eo "Found [0-9]+ errors" phpstan.log | grep -Eo "[0-9]+")
echo "phpstan_exit=$EXIT" >> $GITHUB_OUTPUT
echo "phpstan_errors=${ERR:-0}" >> $GITHUB_OUTPUT
- name: 📄 Show PHPStan output
if: always()
run: cat phpstan.log
# ---------------------------------------------
# 7) PHPUnit + Coverage
# ---------------------------------------------
- name: Install bc
run: sudo apt-get install -y bc
- name: 🧪 Run PHPUnit with Coverage
id: tests
run: |
touch phpunit.log
php -d pcov.enabled=1 \
-d pcov.directory=./src \
-d pcov.exclude="~(tests|vendor)~" \
vendor/bin/phpunit \
--configuration phpunit.xml.dist \
--coverage-clover coverage.xml \
--coverage-html coverage \
| tee -a phpunit.log
# ---------------------------------------------
# 8) Extract Coverage
# ---------------------------------------------
- name: Extract Coverage % (Clover)
id: coverage
run: |
if [ ! -f coverage.xml ]; then
echo "coverage=0" >> $GITHUB_OUTPUT
exit 0
fi
COV=$(php -r '
$xml=@simplexml_load_file("coverage.xml");
if(!$xml){ echo "0"; exit; }
$m = $xml->project->metrics ?? null;
if(!$m){
$statements=0; $covered=0;
foreach($xml->xpath("//metrics") ?: [] as $mx){
$statements += (int)$mx["statements"];
$covered += (int)$mx["coveredstatements"];
}
echo $statements>0 ? (int)round(($covered/$statements)*100) : 0;
exit;
}
$statements=(int)$m["statements"];
$covered=(int)$m["coveredstatements"];
echo $statements>0 ? (int)round(($covered/$statements)*100) : 0;
')
echo "Coverage: ${COV}%"
echo "coverage=${COV}" >> $GITHUB_OUTPUT
- name: Generate Coverage Badge JSON
run: |
echo "{
\"schemaVersion\": 1,
\"label\": \"coverage\",
\"message\": \"${{ steps.coverage.outputs.coverage }}%\",
\"color\": \"9C27B0\"
}" > coverage-badge.json
- name: Stash CI artifacts (before switching branches)
if: github.event_name == 'push'
run: |
cp coverage-badge.json /tmp/coverage-badge.json
cp phpunit.log /tmp/phpunit.log || true
- name: Upload Coverage Badge to badges branch
if: github.event_name == 'push'
run: |
git config user.name "github-actions"
git config user.email "actions@github.com"
git fetch origin
if git ls-remote --exit-code origin badges; then
echo "Badges branch exists. Checking out..."
git checkout badges
else
echo "Badges branch does NOT exist. Creating it..."
git checkout --orphan badges
fi
git reset --hard
git clean -fdx
cp /tmp/coverage-badge.json coverage.json
git add -f coverage.json
if git diff --cached --quiet; then
echo "No changes"
else
git commit -m "Update coverage badge to ${{ steps.coverage.outputs.coverage }}%"
fi
git push origin badges --force
# ---------------------------------------------
# 9) Summary Report
# ---------------------------------------------
- name: 🧾 Summary Report
if: always()
run: |
echo "### 🧾 Security Guard CI Summary" >> $GITHUB_STEP_SUMMARY
echo "🧹 PHPStan: ${{ steps.phpstan.outputs.phpstan_errors }} errors" >> $GITHUB_STEP_SUMMARY
echo "📊 Coverage: ${{ steps.coverage.outputs.coverage }}%" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f phpunit.log ]; then
tail -n 20 phpunit.log >> $GITHUB_STEP_SUMMARY
elif [ -f /tmp/phpunit.log ]; then
tail -n 20 /tmp/phpunit.log >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ phpunit.log not found — PHPUnit may have failed before log creation." >> $GITHUB_STEP_SUMMARY
fi
# ---------------------------------------------
# 10) Telegram Notification
# ---------------------------------------------
- name: 📲 Notify Telegram
if: always()
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_CI_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CI_CHAT_ID }}
run: |
START_TS=$(date -u -d "$GH_START_ISO" +%s)
END_TS=$(date +%s)
DURATION=$((END_TS - START_TS))
if [ "$DURATION" -lt 60 ]; then
DURATION_STR="${DURATION}s"
else
DURATION_STR="$(($DURATION / 60))m $(($DURATION % 60))s"
fi
PHPSTAN_ERR=${{ steps.phpstan.outputs.phpstan_errors }}
COV=${{ steps.coverage.outputs.coverage }}
STATUS="🟢 All tests passed"
HEADER="Security Guard CI Report"
COLOR="🟢"
if grep -q "FAILURES!" phpunit.log 2>/dev/null || [ "${{ job.status }}" != "success" ]; then
STATUS="🔴 Tests or PHPStan failed!"
HEADER="Security Guard CI Alert"
COLOR="🔴"
fi
MSG="📢 <b>${HEADER}</b>
${COLOR} ${STATUS}
🧹 <b>PHPStan:</b> ${PHPSTAN_ERR} errors
📊 <b>Coverage:</b> ${COV}%
📦 <b>Project:</b> ${GITHUB_REPOSITORY}
🌿 <b>Branch:</b> ${GITHUB_REF_NAME}
👨‍💻 <b>By:</b> ${GITHUB_ACTOR}
⏳ <b>Duration:</b> ${DURATION_STR}
🔗 <a href='${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}'>View CI Logs</a>
"
curl -s -X POST \
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg chat_id "$TELEGRAM_CHAT_ID" --arg text "$MSG" --arg pm "HTML" '{chat_id: $chat_id, text: $text, parse_mode: $pm }')"