ci: add Gitea CI/CD workflow for Laravel API
API CI/CD / Validate (composer + pint) (push) Failing after 29s
API CI/CD / Test (PHPUnit) (push) Failing after 9s
API CI/CD / Build frontend assets (push) Failing after 32s
API CI/CD / Security audit (push) Failing after 1s
API CI/CD / Deploy to production (push) Has been skipped
API CI/CD / Validate (composer + pint) (push) Failing after 29s
API CI/CD / Test (PHPUnit) (push) Failing after 9s
API CI/CD / Build frontend assets (push) Failing after 32s
API CI/CD / Security audit (push) Failing after 1s
API CI/CD / Deploy to production (push) Has been skipped
Pipeline includes validate (composer + pint), test (phpunit), build (frontend assets), security audit, and manual deploy stages.
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
# Gitea CI/CD for Laravel 12 API
|
||||
#
|
||||
# Triggers:
|
||||
# - Push to any branch
|
||||
# - Pull requests
|
||||
# - Tags
|
||||
#
|
||||
# Stages: validate → test → build → security
|
||||
|
||||
name: API CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master, develop, 'feature/**', 'fix/**']
|
||||
pull_request:
|
||||
branches: [main, master, develop]
|
||||
push:
|
||||
tags: ['v*']
|
||||
|
||||
env:
|
||||
APP_ENV: testing
|
||||
APP_DEBUG: false
|
||||
APP_KEY: ""
|
||||
LOG_CHANNEL: stderr
|
||||
LOG_LEVEL: debug
|
||||
DB_CONNECTION: sqlite
|
||||
DB_DATABASE: ${{ github.workspace }}/database/database.sqlite
|
||||
CACHE_DRIVER: array
|
||||
SESSION_DRIVER: array
|
||||
QUEUE_CONNECTION: sync
|
||||
MAIL_MAILER: array
|
||||
FILESYSTEM_DISK: local
|
||||
COMPOSER_CACHE_DIR: ${{ github.workspace }}/.composer-cache
|
||||
|
||||
jobs:
|
||||
# ──────────────────────────────────────────────
|
||||
# VALIDATE: composer validate + Laravel Pint
|
||||
# ──────────────────────────────────────────────
|
||||
validate:
|
||||
name: Validate (composer + pint)
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: php:8.3-cli
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
git unzip curl libzip-dev libpng-dev libonig-dev libxml2-dev libsqlite3-dev
|
||||
docker-php-ext-install mbstring zip gd pdo_sqlite
|
||||
|
||||
- name: Install Composer
|
||||
run: |
|
||||
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
|
||||
php composer-setup.php --install-dir=/usr/local/bin --filename=composer
|
||||
rm composer-setup.php
|
||||
|
||||
- name: Cache Composer dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
vendor/
|
||||
.composer-cache/
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-composer-
|
||||
|
||||
- name: Install PHP dependencies
|
||||
run: composer install --prefer-dist --no-interaction --no-progress
|
||||
|
||||
- name: Composer validate
|
||||
run: composer validate --strict
|
||||
|
||||
- name: Laravel Pint (code style check)
|
||||
run: |
|
||||
composer require --dev laravel/pint --ignore-platform-reqs || true
|
||||
vendor/bin/pint --test || echo "Pint found style issues (non-blocking)"
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# TEST: PHPUnit (Unit + Feature with SQLite)
|
||||
# ──────────────────────────────────────────────
|
||||
test:
|
||||
name: Test (PHPUnit)
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: php:8.3-cli
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
git unzip curl libzip-dev libpng-dev libonig-dev libxml2-dev libsqlite3-dev
|
||||
docker-php-ext-install mbstring zip gd pdo_sqlite
|
||||
|
||||
- name: Install Composer
|
||||
run: |
|
||||
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
|
||||
php composer-setup.php --install-dir=/usr/local/bin --filename=composer
|
||||
rm composer-setup.php
|
||||
|
||||
- name: Cache Composer dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
vendor/
|
||||
.composer-cache/
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-composer-
|
||||
|
||||
- name: Install PHP dependencies
|
||||
run: composer install --prefer-dist --no-interaction --no-progress
|
||||
|
||||
- name: Bootstrap Laravel testing environment
|
||||
run: |
|
||||
cat > .env <<'EOF'
|
||||
APP_NAME=school_api
|
||||
APP_ENV=testing
|
||||
APP_KEY=
|
||||
APP_DEBUG=false
|
||||
APP_URL=http://localhost
|
||||
LOG_CHANNEL=stderr
|
||||
LOG_LEVEL=debug
|
||||
DB_CONNECTION=sqlite
|
||||
DB_DATABASE=${{ github.workspace }}/database/database.sqlite
|
||||
CACHE_DRIVER=array
|
||||
SESSION_DRIVER=array
|
||||
QUEUE_CONNECTION=sync
|
||||
MAIL_MAILER=array
|
||||
FILESYSTEM_DISK=local
|
||||
JWT_SECRET=ci_test_jwt_secret_not_for_production
|
||||
EOF
|
||||
mkdir -p database storage/framework/cache storage/framework/sessions \
|
||||
storage/framework/views storage/logs bootstrap/cache reports
|
||||
touch database/database.sqlite
|
||||
php artisan key:generate --force
|
||||
php artisan jwt:secret --force || true
|
||||
php artisan config:clear
|
||||
php artisan view:clear
|
||||
|
||||
- name: Run database migrations
|
||||
run: php artisan migrate --force
|
||||
|
||||
- name: Run Unit tests
|
||||
run: php artisan test --testsuite=Unit --log-junit reports/phpunit-unit.xml
|
||||
|
||||
- name: Run Feature tests
|
||||
run: php artisan test --testsuite=Feature --log-junit reports/phpunit-feature.xml
|
||||
|
||||
- name: Upload test reports
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: test-reports
|
||||
path: reports/
|
||||
retention-days: 7
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# BUILD: Compile frontend assets (Vite)
|
||||
# ──────────────────────────────────────────────
|
||||
build:
|
||||
name: Build frontend assets
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:22-alpine
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache npm dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules/
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-npm-
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --cache .npm --prefer-offline
|
||||
|
||||
- name: Build assets
|
||||
run: npm run build
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frontend-assets
|
||||
path: |
|
||||
public/build/
|
||||
public/css/
|
||||
public/js/
|
||||
retention-days: 7
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# SECURITY: Dependency audits + secret scanning
|
||||
# ──────────────────────────────────────────────
|
||||
security:
|
||||
name: Security audit
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: php:8.3-cli
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends git unzip curl libzip-dev libpng-dev libonig-dev libxml2-dev libsqlite3-dev
|
||||
docker-php-ext-install mbstring zip gd pdo_sqlite
|
||||
|
||||
- name: Install Composer
|
||||
run: |
|
||||
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
|
||||
php composer-setup.php --install-dir=/usr/local/bin --filename=composer
|
||||
rm composer-setup.php
|
||||
|
||||
- name: Install PHP dependencies
|
||||
run: composer install --prefer-dist --no-interaction --no-progress
|
||||
|
||||
- name: Composer audit
|
||||
run: composer audit --locked --no-interaction --format=json || echo "Audit found advisories (non-blocking)"
|
||||
|
||||
- name: Secret detection (basic)
|
||||
run: |
|
||||
echo "Scanning for committed secrets..."
|
||||
if grep -RInE \
|
||||
"(APP_KEY=base64:[A-Za-z0-9+/=]{44}|DB_PASSWORD=.+|JWT_SECRET=.+|MAIL_PASSWORD=.+|AKIA[0-9A-Z]{16}|-----BEGIN (RSA|OPENSSH|PRIVATE) KEY-----)" \
|
||||
--exclude-dir=.git \
|
||||
--exclude-dir=vendor \
|
||||
--exclude-dir=node_modules \
|
||||
--exclude=.env.example \
|
||||
--exclude=.env.prod.example \
|
||||
. 2>&1; then
|
||||
echo "❌ Secrets detected!"
|
||||
exit 1
|
||||
else
|
||||
echo "✅ No secrets found"
|
||||
fi
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# DEPLOY (manual): to production via SSH
|
||||
# ──────────────────────────────────────────────
|
||||
deploy:
|
||||
name: Deploy to production
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
|
||||
needs: [test, build, security]
|
||||
environment:
|
||||
name: production
|
||||
url: ${{ vars.PRODUCTION_URL }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install SSH client
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends openssh-client sshpass rsync
|
||||
|
||||
- name: Deploy via rsync
|
||||
run: |
|
||||
sshpass -p "${{ secrets.SSH_PASSWORD }}" rsync -avz --delete \
|
||||
-e "ssh -p ${{ vars.SSH_PORT || 22 }} -o StrictHostKeyChecking=no" \
|
||||
--exclude='.env' \
|
||||
--exclude='.git' \
|
||||
--exclude='tests' \
|
||||
--exclude='storage/logs/*' \
|
||||
--exclude='database/sqlite' \
|
||||
--exclude='node_modules' \
|
||||
--exclude='vendor' \
|
||||
./ \
|
||||
${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ vars.SSH_TARGET_DIR }}/
|
||||
Reference in New Issue
Block a user