name: CI/CD Pipeline # Updated: Removed systemd, using SSH-based testing for better stability on: push: branches: [ main, develop ] pull_request: branches: [ main ] jobs: lint: name: Lint Ansible Playbooks runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install dependencies run: | python -m pip install --upgrade pip pip install ansible ansible-lint yamllint - name: Lint YAML files run: | yamllint . continue-on-error: true - name: Lint Ansible playbooks run: | ansible-lint playbooks/*.yml || echo "Linting completed with warnings" continue-on-error: true - name: Validate Ansible syntax run: | for playbook in playbooks/*.yml; do ansible-playbook --syntax-check "$playbook" done test-ubuntu: name: Test on Ubuntu runs-on: ubuntu-latest needs: lint strategy: matrix: ubuntu_version: ['20.04', '22.04', '24.04'] steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install Ansible run: | python -m pip install --upgrade pip pip install ansible - name: Set up Docker uses: docker/setup-buildx-action@v3 - name: Create test inventory run: | mkdir -p test-inventory cat > test-inventory/hosts <> /etc/sudoers # Configure SSH RUN mkdir /var/run/sshd RUN echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config RUN echo 'PasswordAuthentication no' >> /etc/ssh/sshd_config RUN echo 'PubkeyAuthentication yes' >> /etc/ssh/sshd_config RUN ssh-keygen -A EXPOSE 22 # Start SSH service in foreground to keep container running CMD ["/usr/sbin/sshd", "-D"] EOF - name: Run test container run: | # Generate SSH key mkdir -p ~/.ssh ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N "" # Start container docker run -d --name test-server \ -p 2222:22 \ test-ubuntu:${{ matrix.ubuntu_version }} # Wait for SSH to be ready sleep 10 # Copy SSH key to container docker exec test-server mkdir -p /home/testuser/.ssh docker cp ~/.ssh/id_rsa.pub test-server:/home/testuser/.ssh/authorized_keys docker exec test-server chown -R testuser:testuser /home/testuser/.ssh docker exec test-server chmod 700 /home/testuser/.ssh docker exec test-server chmod 600 /home/testuser/.ssh/authorized_keys # Test if container is running if ! docker ps | grep -q test-server; then echo "Container failed to start, checking logs:" docker logs test-server exit 1 fi - name: Test base LEMP installation run: | # Test just the syntax and basic functionality without full installation ansible-playbook -i test-inventory/hosts \ playbooks/lemp-wordpress.yml \ --extra-vars "mysql_root_password=test123 wordpress_db_password=test123 wp_admin_password=test123" \ --check \ -v - name: Test WordPress installation run: | # Test just the syntax check for WordPress installation ansible-playbook -i test-inventory/hosts \ playbooks/install-wordpress-official.yml \ --extra-vars "wp_admin_password=test123" \ --check \ -v - name: Verify basic connectivity run: | # Wait for SSH to be fully ready echo "Waiting for SSH service..." sleep 5 # Test SSH connectivity with retry for i in {1..5}; do echo "SSH connection attempt $i..." if ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p 2222 testuser@localhost "echo 'SSH connection successful'"; then echo "SSH connection established!" break else echo "SSH connection failed, retrying in 3 seconds..." sleep 3 fi if [ $i -eq 5 ]; then echo "SSH connection failed after 5 attempts" echo "Container logs:" docker logs test-server exit 1 fi done # Test basic container functionality echo "Testing container basics..." docker exec test-server whoami docker exec test-server uname -a - name: Cleanup if: always() run: | docker stop test-server || true docker rm test-server || true test-centos: name: Test on CentOS Stream runs-on: ubuntu-latest needs: lint steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install Ansible run: | python -m pip install --upgrade pip pip install ansible - name: Set up Docker uses: docker/setup-buildx-action@v3 - name: Create test inventory run: | mkdir -p test-inventory cat > test-inventory/hosts <> /etc/sudoers # Configure SSH RUN mkdir /var/run/sshd RUN echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config RUN echo 'PasswordAuthentication no' >> /etc/ssh/sshd_config RUN echo 'PubkeyAuthentication yes' >> /etc/ssh/sshd_config RUN ssh-keygen -A EXPOSE 22 # Start SSH service in foreground to keep container running CMD ["/usr/sbin/sshd", "-D"] EOF - name: Run CentOS test container run: | # Generate SSH key (reuse from Ubuntu test) mkdir -p ~/.ssh if [ ! -f ~/.ssh/id_rsa ]; then ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N "" fi # Start container docker run -d --name test-centos \ -p 2223:22 \ test-centos:stream9 # Wait for SSH to be ready sleep 10 # Copy SSH key to container docker exec test-centos mkdir -p /home/testuser/.ssh docker cp ~/.ssh/id_rsa.pub test-centos:/home/testuser/.ssh/authorized_keys docker exec test-centos chown -R testuser:testuser /home/testuser/.ssh docker exec test-centos chmod 700 /home/testuser/.ssh docker exec test-centos chmod 600 /home/testuser/.ssh/authorized_keys # Test if container is running if ! docker ps | grep -q test-centos; then echo "CentOS container failed to start, checking logs:" docker logs test-centos exit 1 fi - name: Test LEMP installation on CentOS run: | # Just test syntax and basic functionality for CentOS ansible-playbook -i test-inventory/hosts \ playbooks/lemp-wordpress-multios.yml \ --extra-vars "mysql_root_password=test123 wordpress_db_password=test123 wp_admin_password=test123" \ --check \ -v continue-on-error: true - name: Cleanup CentOS if: always() run: | docker stop test-centos || true docker rm test-centos || true security-scan: name: Security Scanning runs-on: ubuntu-latest needs: lint permissions: security-events: write steps: - name: Checkout code uses: actions/checkout@v4 - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: scan-type: 'fs' scan-ref: '.' format: 'sarif' output: 'trivy-results.sarif' - name: Upload Trivy scan results to GitHub Security tab uses: github/codeql-action/upload-sarif@v3 # Updated to v3 to fix deprecated v2 warning if: always() with: sarif_file: 'trivy-results.sarif' release: name: Create Release runs-on: ubuntu-latest needs: [test-ubuntu, test-centos, security-scan] if: github.ref == 'refs/heads/main' && github.event_name == 'push' permissions: contents: write steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get latest tag id: get_tag run: | latest_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") echo "latest_tag=$latest_tag" >> $GITHUB_OUTPUT - name: Generate changelog run: | echo "# Changelog" > CHANGELOG_RELEASE.md echo "" >> CHANGELOG_RELEASE.md git log ${{ steps.get_tag.outputs.latest_tag }}..HEAD --pretty=format:"- %s (%h)" >> CHANGELOG_RELEASE.md - name: Create Release uses: softprops/action-gh-release@v2 with: tag_name: v${{ github.run_number }} name: Release v${{ github.run_number }} body_path: CHANGELOG_RELEASE.md draft: false prerelease: false