name: PR Deployment on: pull_request: types: [opened, synchronize, reopened, closed] permissions: contents: write pull-requests: write env: SERVER_IP: ${{ secrets.VPS_IP }} # Add this to your GitHub secrets jobs: check-changes: runs-on: ubuntu-latest if: github.event.action != 'closed' outputs: should_run: ${{ steps.filter.outputs.src_changed }} steps: - uses: actions/checkout@v4 - name: Check for relevant file changes id: filter uses: dorny/paths-filter@v2 with: filters: | src_changed: - '**/*.java' - '**/*.html' - '**/*.css' - '**/*.js' - '**/*.gradle' deploy-pr: needs: check-changes runs-on: ubuntu-latest if: needs.check-changes.outputs.should_run == 'true' steps: - uses: actions/checkout@v4 - name: Set up JDK and Gradle uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' gradle-version: 8.7 - name: Run Gradle Command run: ./gradlew clean build env: DOCKER_ENABLE_SECURITY: false - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Get version number id: versionNumber run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_API }} - name: Build and push PR-specific image uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile push: true tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:pr-${{ github.event.pull_request.number }} build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} platforms: linux/amd64 - name: Set up SSH run: | mkdir -p ~/.ssh/ echo "${{ secrets.VPS_SSH_KEY }}" > ../private.key sudo chmod 600 ../private.key ssh-keyscan -H ${{ secrets.VPS_HOST }} >> ~/.ssh/known_hosts - name: Deploy to VPS run: | # First create the docker-compose content locally cat > docker-compose.yml << 'EOF' version: '3.3' services: stirling-pdf: container_name: stirling-pdf-pr-${{ github.event.pull_request.number }} image: ${{ secrets.DOCKER_HUB_USERNAME }}/test:pr-${{ github.event.pull_request.number }} ports: - "${{ github.event.pull_request.number }}:8080" volumes: - /stirling/PR-${{ github.event.pull_request.number }}/data:/usr/share/tessdata:rw - /stirling/PR-${{ github.event.pull_request.number }}/config:/configs:rw - /stirling/PR-${{ github.event.pull_request.number }}/logs:/logs:rw environment: DOCKER_ENABLE_SECURITY: "false" SECURITY_ENABLELOGIN: "false" SYSTEM_DEFAULTLOCALE: en-GB UI_APPNAME: "Stirling-PDF PR-${{ github.event.pull_request.number }}" UI_HOMEDESCRIPTION: "PR-${{ github.event.pull_request.number }} for Stirling-PDF Latest" UI_APPNAMENAVBAR: "PR-${{ github.event.pull_request.number }}" SYSTEM_MAXFILESIZE: "100" METRICS_ENABLED: "false" SYSTEM_GOOGLEVISIBILITY: "false" SYSTEM_ENABLEANALYTICS: "false" restart: on-failure:5 EOF # Then copy the file and execute commands scp -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null docker-compose.yml ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }}:/tmp/docker-compose.yml ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -T ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << 'ENDSSH' # Create PR-specific directories mkdir -p /stirling/PR-${{ github.event.pull_request.number }}/{data,config,logs} # Move docker-compose file to correct location mv /tmp/docker-compose.yml /stirling/PR-${{ github.event.pull_request.number }}/docker-compose.yml # Start or restart the container cd /stirling/PR-${{ github.event.pull_request.number }} docker-compose pull docker-compose up -d ENDSSH - name: Post deployment URL to PR uses: actions/github-script@v7 with: script: | const { GITHUB_REPOSITORY } = process.env; const [repoOwner, repoName] = GITHUB_REPOSITORY.split('/'); const prNumber = context.issue.number; const deploymentUrl = `http://${{ secrets.VPS_HOST }}:${prNumber}`; const commentBody = `## ๐Ÿš€ PR Test Deployment\n\n` + `Your PR has been deployed for testing!\n\n` + `๐Ÿ”— **Test URL:** [${deploymentUrl}](${deploymentUrl})\n\n` + `This deployment will be automatically cleaned up when the PR is closed.\n`; // Find existing comment const comments = await github.rest.issues.listComments({ owner: repoOwner, repo: repoName, issue_number: prNumber }); const existingComment = comments.data.find(c => c.body.includes("## ๐Ÿš€ PR Test Deployment") && c.user.login === "github-actions[bot]" ); if (existingComment) { await github.rest.issues.updateComment({ owner: repoOwner, repo: repoName, comment_id: existingComment.id, body: commentBody }); } else { await github.rest.issues.createComment({ owner: repoOwner, repo: repoName, issue_number: prNumber, body: commentBody }); } cleanup: runs-on: ubuntu-latest if: github.event.action == 'closed' steps: - name: Set up SSH run: | mkdir -p ~/.ssh/ echo "${{ secrets.VPS_SSH_KEY }}" > ../private.key sudo chmod 600 ../private.key - name: Cleanup PR deployment run: | ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -T ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << 'ENDSSH' # Check if directory exists before attempting cleanup if [ -d "/stirling/PR-${{ github.event.pull_request.number }}" ]; then echo "Found PR directory, proceeding with cleanup..." # Stop and remove containers cd /stirling/PR-${{ github.event.pull_request.number }} docker-compose down || true # Go back to root before removal cd / # Remove PR-specific directories rm -rf /stirling/PR-${{ github.event.pull_request.number }} # Remove the Docker image docker rmi ${{ secrets.DOCKER_HUB_USERNAME }}/test:pr-${{ github.event.pull_request.number }} || true echo "Cleanup completed successfully" else echo "PR directory not found, nothing to clean up" fi ENDSSH - name: Post cleanup notice to PR uses: actions/github-script@v7 with: script: | const { GITHUB_REPOSITORY } = process.env; const [repoOwner, repoName] = GITHUB_REPOSITORY.split('/'); const prNumber = context.issue.number; const commentBody = `## ๐Ÿงน Deployment Cleanup\n\n` + `The test deployment for this PR has been cleaned up.`; await github.rest.issues.createComment({ owner: repoOwner, repo: repoName, issue_number: prNumber, body: commentBody });