Deploy to Production #9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| name: Deploy to Production | |
| on: | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| deploy: | |
| name: Deploy to Production Server | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v3 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install sshpass | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y sshpass | |
| - name: Deploy to production server | |
| env: | |
| SERVER_IP: ${{ secrets.PRODUCTION_SERVER_IP }} | |
| SERVER_USER: ${{ secrets.PRODUCTION_SERVER_USER }} | |
| SERVER_PASSWORD: ${{ secrets.PRODUCTION_SERVER_PASSWORD }} | |
| run: | | |
| # Create SSH key directory | |
| mkdir -p ~/.ssh | |
| chmod 700 ~/.ssh | |
| # Disable strict host key checking for deployment | |
| cat >> ~/.ssh/config <<EOF | |
| Host production-server | |
| HostName $SERVER_IP | |
| User $SERVER_USER | |
| StrictHostKeyChecking no | |
| UserKnownHostsFile /dev/null | |
| EOF | |
| # Deploy via SSH | |
| sshpass -p "$SERVER_PASSWORD" ssh \ | |
| -o StrictHostKeyChecking=no "$SERVER_USER@$SERVER_IP" << 'ENDSSH' | |
| set -e | |
| # Navigate to project directory | |
| cd /home/django/education-website | |
| # Pull latest changes from main branch | |
| git fetch --all --prune | |
| git reset --hard origin/main | |
| # Activate virtual environment and upgrade pip | |
| source venv/bin/activate | |
| pip install --upgrade pip wheel | |
| # Install/upgrade Poetry | |
| pip install --upgrade poetry==2.0.1 | |
| # Configure Poetry to use existing virtualenv | |
| poetry config virtualenvs.create false --local || true | |
| # Install dependencies | |
| poetry install --only main --no-interaction --no-ansi | |
| # Run Django migrations | |
| python manage.py migrate --noinput | |
| # Collect static files | |
| python manage.py collectstatic --noinput | |
| # Restart the web server (systemd service) | |
| sudo systemctl restart education-website | |
| # Restart nginx | |
| sudo systemctl restart nginx | |
| echo "Deployment completed successfully!" | |
| ENDSSH | |
| - name: Verify deployment | |
| env: | |
| SERVER_IP: ${{ secrets.PRODUCTION_SERVER_IP }} | |
| SERVER_USER: ${{ secrets.PRODUCTION_SERVER_USER }} | |
| SERVER_PASSWORD: ${{ secrets.PRODUCTION_SERVER_PASSWORD }} | |
| run: | | |
| echo "Verifying deployment..." | |
| sshpass -p "$SERVER_PASSWORD" ssh \ | |
| -o StrictHostKeyChecking=no "$SERVER_USER@$SERVER_IP" << 'ENDSSH' | |
| # Check if service is running | |
| sudo systemctl status education-website --no-pager || true | |
| # Check nginx status | |
| sudo systemctl status nginx --no-pager || true | |
| ENDSSH | |
| echo "Deployment verification completed!" | |
| - name: Get latest release tag | |
| id: get_latest_tag | |
| run: | | |
| # Get the latest release tag | |
| LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null \ | |
| || echo "v0.0") | |
| echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT | |
| echo "Latest tag: $LATEST_TAG" | |
| - name: Increment version | |
| id: increment_version | |
| run: | | |
| LATEST_TAG="${{ steps.get_latest_tag.outputs.latest_tag }}" | |
| # Remove 'v' prefix and split version | |
| VERSION=${LATEST_TAG#v} | |
| # Split into major.minor (assuming format v1.0, v1.1, etc.) | |
| MAJOR=$(echo $VERSION | cut -d. -f1) | |
| MINOR=$(echo $VERSION | cut -d. -f2) | |
| # Increment minor version | |
| MINOR=$((MINOR + 1)) | |
| NEW_VERSION="v${MAJOR}.${MINOR}" | |
| echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT | |
| echo "New version: $NEW_VERSION" | |
| - name: Generate release notes | |
| id: generate_notes | |
| run: | | |
| LATEST_TAG="${{ steps.get_latest_tag.outputs.latest_tag }}" | |
| NEW_VERSION="${{ steps.increment_version.outputs.new_version }}" | |
| DEPLOY_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC') | |
| # Get commits since last tag | |
| if git rev-parse "$LATEST_TAG" >/dev/null 2>&1; then | |
| COMMITS=$(git log ${LATEST_TAG}..HEAD \ | |
| --pretty=format:"- %s (%h)" --no-merges) | |
| else | |
| COMMITS=$(git log --pretty=format:"- %s (%h)" --no-merges) | |
| fi | |
| # Create release notes | |
| { | |
| echo "## Production Deployment - ${NEW_VERSION}" | |
| echo "" | |
| echo "**Deployment Date:** ${DEPLOY_DATE}" | |
| echo "" | |
| echo "### Changes in this release:" | |
| echo "" | |
| echo "$COMMITS" | |
| echo "" | |
| echo "---" | |
| echo "_This release was automatically created by the" | |
| echo "production deployment workflow._" | |
| } > /tmp/release_notes.md | |
| echo "Release notes generated successfully" | |
| cat /tmp/release_notes.md | |
| - name: Create GitHub Release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| NEW_VERSION="${{ steps.increment_version.outputs.new_version }}" | |
| # Configure git | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # Create and push the tag | |
| git tag "$NEW_VERSION" | |
| git push origin "$NEW_VERSION" | |
| # Create the release using GitHub CLI | |
| gh release create "$NEW_VERSION" \ | |
| --title "$NEW_VERSION" \ | |
| --notes-file /tmp/release_notes.md |