diff --git a/.gitea/workflows/pipeline.yml b/.gitea/workflows/pipeline.yml new file mode 100644 index 0000000..f8a7825 --- /dev/null +++ b/.gitea/workflows/pipeline.yml @@ -0,0 +1,157 @@ +# GENERATED by peptide-e2e/tools/gitea-pipeline/render.py from repos.json. +# Do not edit by hand: change repos.json or the template and re-render. +name: CI / Deploy + +on: + push: + branches: [main] + pull_request: {} + workflow_dispatch: {} + +concurrency: + group: pipeline-peptide-news-plugin-${{ github.ref }} + cancel-in-progress: false + +jobs: + lint-php: + name: PHP Lint (${{ matrix.php }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: ["8.1", "8.2", "8.3"] + steps: + - uses: actions/checkout@v4 + - uses: https://git.protopeer.com/peptide-repo/peptide-e2e/.gitea/actions/ci-lint-php@main + with: + php: ${{ matrix.php }} + + phpcs: + name: PHPCS (WordPress standards) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref || github.ref_name }} + token: ${{ secrets.GITHUB_TOKEN }} + - uses: https://git.protopeer.com/peptide-repo/peptide-e2e/.gitea/actions/ci-phpcs@main + with: + autofix: 'true' + + file-size: + name: 300-line rule (includes) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: https://git.protopeer.com/peptide-repo/peptide-e2e/.gitea/actions/ci-file-size@main + with: + paths: includes + + phpunit: + name: PHPUnit - stubs (PHP ${{ matrix.php }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: ["8.1", "8.2", "8.3"] + steps: + - uses: actions/checkout@v4 + - uses: https://git.protopeer.com/peptide-repo/peptide-e2e/.gitea/actions/ci-phpunit@main + with: + php: ${{ matrix.php }} + tests: stubs + + lint-js: + name: JS Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: https://git.protopeer.com/peptide-repo/peptide-e2e/.gitea/actions/ci-lint-js@main + + security-audit: + name: Security Audit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check for common PHP security issues + shell: bash + run: | + echo ":: Checking for direct SQL queries without prepare..." + if grep -r "query\s*(\s*['\"].*\$" --include="*.php" --exclude-dir=vendor . 2>/dev/null \ + | grep -v "\$wpdb->prepare" \ + | grep -v "uninstall\.php" \ + | grep -v "class-peptide-news-activator\.php" \ + | grep -v "class-peptide-news-logger\.php" \ + | grep -v "DROP TABLE" \ + | grep -v "TRUNCATE" \ + && [ ${PIPESTATUS[0]} -eq 0 ]; then + echo "WARNING: Found potential unescaped SQL queries. Use wpdb->prepare() for all dynamic SQL." + exit 1 + fi + echo ":: Checking for eval() usage..." + if grep -r "eval\s*(" --include="*.php" --exclude-dir=vendor . 2>/dev/null; then + echo "ERROR: eval() found. This is a critical security risk."; exit 1 + fi + echo ":: Checking for base64_decode without validation..." + if grep -r "base64_decode\s*(" --include="*.php" --exclude-dir=vendor . 2>/dev/null; then + echo "WARNING: Found base64_decode(). Ensure the input is properly validated." + fi + echo ":: Checking for file_get_contents with user input..." + if grep -r "file_get_contents\s*(\s*\$" --include="*.php" --exclude-dir=vendor . 2>/dev/null; then + echo "WARNING: Found file_get_contents with variable. Ensure input is validated and restricted." + fi + echo "All security checks passed." + + deploy-staging: + name: Deploy to staging + needs: ["lint-php", "phpcs", "file-size", "phpunit", "lint-js", "security-audit"] + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + runs-on: peptide-vps + steps: + - uses: actions/checkout@v4 + - uses: https://git.protopeer.com/peptide-repo/peptide-e2e/.gitea/actions/deploy@main + with: + stage: staging + target_path: wp-content/plugins/peptide-news-plugin + ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }} + ssh_host: ${{ secrets.SSH_HOST }} + ssh_port: ${{ secrets.SSH_PORT }} + ssh_username: ${{ secrets.SSH_USERNAME }} + + smoke: + name: Smoke tests (staging) + needs: deploy-staging + runs-on: peptide-vps + timeout-minutes: 15 + steps: + - uses: https://git.protopeer.com/peptide-repo/peptide-e2e/.gitea/actions/smoke@main + with: + wp_admin_user: ${{ secrets.STAGING_WP_ADMIN_USER }} + wp_admin_password: ${{ secrets.STAGING_WP_ADMIN_PASSWORD }} + + deploy-production: + name: Deploy to production + # Smoke is a gate: production runs only if staging deploy AND smoke succeeded. + # PROD_DEPLOY_ENABLED (org variable) is the cutover switch / kill switch. + needs: [deploy-staging, smoke] + if: ${{ vars.PROD_DEPLOY_ENABLED == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main' }} + runs-on: peptide-vps + steps: + - uses: actions/checkout@v4 + - uses: https://git.protopeer.com/peptide-repo/peptide-e2e/.gitea/actions/deploy@main + with: + stage: production + target_path: wp-content/plugins/peptide-news-plugin + ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }} + ssh_host: ${{ secrets.SSH_HOST }} + ssh_port: ${{ secrets.SSH_PORT }} + ssh_username: ${{ secrets.SSH_USERNAME }} + + audit-direct-push: + name: Audit - commit came from a PR + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + runs-on: ubuntu-latest + steps: + - uses: https://git.protopeer.com/peptide-repo/peptide-e2e/.gitea/actions/audit-direct-push@main + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitea/workflows/rollback.yml b/.gitea/workflows/rollback.yml new file mode 100644 index 0000000..0e901e5 --- /dev/null +++ b/.gitea/workflows/rollback.yml @@ -0,0 +1,42 @@ +name: Rollback Deployment + +# Gitea port of .github/workflows/rollback.yml. Redeploys a chosen commit to +# production through the same deploy composite action as a normal deploy (same +# rsync excludes, inventory guard and health check) instead of building an +# orphan `deploy` branch. Respects the PROD_DEPLOY_ENABLED kill switch. +on: + workflow_dispatch: + inputs: + commit_sha: + description: 'Commit SHA to deploy (leave empty for current main)' + required: false + default: '' + +jobs: + rollback: + name: Roll production back to a commit + if: ${{ vars.PROD_DEPLOY_ENABLED == 'true' }} + runs-on: peptide-vps + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Check out target commit + shell: bash + env: + WANT: ${{ inputs.commit_sha }} + run: | + set -euo pipefail + T="${WANT:-$(git rev-parse HEAD)}" + case "$T" in *[!0-9a-fA-F]*) echo "::error::commit_sha must be hex"; exit 1 ;; esac + git cat-file -e "$T^{commit}" || { echo "::error::commit $T not found"; exit 1; } + git checkout -q --detach "$T" + echo "Rolling production back to $(git rev-parse --short HEAD): $(git log -1 --format=%s)" + - uses: https://git.protopeer.com/peptide-repo/peptide-e2e/.gitea/actions/deploy@main + with: + stage: production + target_path: wp-content/plugins/peptide-news-plugin + ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }} + ssh_host: ${{ secrets.SSH_HOST }} + ssh_port: ${{ secrets.SSH_PORT }} + ssh_username: ${{ secrets.SSH_USERNAME }}