Github Action using ssh

From Logic Wiki
Revision as of 13:29, 5 December 2025 by AliIybar (Talk | contribs)

Jump to: navigation, search


Introducing github to server

First create a ssh key in the local machine (I named it as "github-deploy" when it asked)

ssh-keygen -t ed25519 -C "github-deploy"

-t ed25519 : encryption algorithm

-C "github-deploy" : puts a comment in public key file

Copy it to server

ssh-copy-id -i ~/.ssh/github-deploy.pub bookit

Create a secret in Github repo and store private key in it.

OR

Create the key on the server and copy private key to github secrets

Server Side

  • Add public key to authorized_keys
  • install docker
  • install docker-compose
  • create folder for deployment

Action

name: Deploy:Live:IONOS
on:
  push:
    branches:
      - main
env: 
  ENVIRONMENT: prod

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      RABBITMQ_DEFAULT_USER: ${{ steps.ssm.outputs.RABBITMQ_DEFAULT_USER }}
      RABBITMQ_DEFAULT_PASS: ${{ steps.ssm.outputs.RABBITMQ_DEFAULT_PASS }}
      MONGO_INITDB_ROOT_USERNAME: ${{ steps.ssm.outputs.MONGO_INITDB_ROOT_USERNAME }}
      MONGO_INITDB_ROOT_PASSWORD: ${{ steps.ssm.outputs.MONGO_INITDB_ROOT_PASSWORD }}
      POSTGRES_USER: ${{ steps.ssm.outputs.POSTGRES_USER }}
      POSTGRES_PASSWORD: ${{ steps.ssm.outputs.POSTGRES_PASSWORD }}
      KEYCLOAK_ADMIN: ${{ steps.ssm.outputs.KEYCLOAK_ADMIN }}
      KEYCLOAK_ADMIN_PASSWORD: ${{ steps.ssm.outputs.KEYCLOAK_ADMIN_PASSWORD }}
    steps:
      - name: Exit early if file not changed
        run: |
          FILE="docker-compose-data.yml"

          # Compare against previous commit for push
          if git diff --name-only HEAD~1 HEAD | grep -qv "^$FILE$"; then
            # Check if our file is among the changes
            if ! git diff --name-only HEAD~1 HEAD | grep -q "^$FILE$"; then
              echo "$FILE not changed — exiting workflow successfully."
              exit 0
            fi
          fi
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Load multiple AWS parameters
        id: ssm
        run: |
          PARAMS=$(aws ssm get-parameters \
            --names "/${{env.ENVIRONMENT}}/bookitfor.me.uk/rabbitMQ/username" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/rabbitMQ/password" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/mongodb/username" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/mongodb/password" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/postgresql/username" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/postgresql/password" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/keycloak/username" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/keycloak/password" \
            --output json )

          RABBITMQ_DEFAULT_USER=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/rabbitMQ/username") | .Value')
          RABBITMQ_DEFAULT_PASS=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/rabbitMQ/password") | .Value')
          MONGO_INITDB_ROOT_USERNAME=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/mongodb/username") | .Value')
          MONGO_INITDB_ROOT_PASSWORD=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/mongodb/password") | .Value')
          POSTGRES_USER=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/postgresql/username") | .Value')
          POSTGRES_PASSWORD=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/postgresql/password") | .Value')
          KEYCLOAK_ADMIN=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/keycloak/username") | .Value')
          KEYCLOAK_ADMIN_PASSWORD=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/keycloak/password") | .Value')
          echo "::group::Hidden section"
          echo "RABBITMQ_DEFAULT_USER=$RABBITMQ_DEFAULT_USER" >> $GITHUB_OUTPUT
          echo "RABBITMQ_DEFAULT_PASS=$RABBITMQ_DEFAULT_PASS" >> $GITHUB_OUTPUT
          echo "MONGO_INITDB_ROOT_USERNAME=$MONGO_INITDB_ROOT_USERNAME" >> $GITHUB_OUTPUT
          echo "MONGO_INITDB_ROOT_PASSWORD=$MONGO_INITDB_ROOT_PASSWORD" >> $GITHUB_OUTPUT
          echo "POSTGRES_USER=$POSTGRES_USER" >> $GITHUB_OUTPUT
          echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> $GITHUB_OUTPUT
          echo "KEYCLOAK_ADMIN=$KEYCLOAK_ADMIN" >> $GITHUB_OUTPUT
          echo "KEYCLOAK_ADMIN_PASSWORD=$KEYCLOAK_ADMIN_PASSWORD" >> $GITHUB_OUTPUT 
          echo "::endgroup::"  
  deploy:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Set up SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.IONOS_SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
         
      - name: Test SSH connection
        run: |
         ssh -o StrictHostKeyChecking=accept-new \
              ${{ secrets.IONOS_REMOTE_USER }}@${{ secrets.IONOS_REMOTE_HOST }} "echo Connected successfully"

      - name: Copy files to remote server
        run: |
          scp -i ~/.ssh/id_rsa -r ./docker-compose-data.yml \
             ${{ secrets.IONOS_REMOTE_USER }}@${{ secrets.IONOS_REMOTE_HOST }}:/home/logicmade/bookitfor.me.uk/            
          scp -i ~/.ssh/id_rsa -r ./sql \
             ${{ secrets.IONOS_REMOTE_USER }}@${{ secrets.IONOS_REMOTE_HOST }}:/home/logicmade/bookitfor.me.uk/sql

      - name: Persist env variables on remote host
        uses: appleboy/ssh-action@v1.2.0
        with:
          host: ${{ secrets.IONOS_REMOTE_HOST }}
          username: ${{ secrets.IONOS_REMOTE_USER }}
          key: ${{ secrets.IONOS_SSH_PRIVATE_KEY }}
          script: |
            echo "::group::Hidden section"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export RABBITMQ_DEFAULT_USER='${{ needs.build.outputs.RABBITMQ_DEFAULT_USER }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export RABBITMQ_DEFAULT_PASS='${{ needs.build.outputs.RABBITMQ_DEFAULT_PASS }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export MONGO_INITDB_ROOT_USERNAME='${{ needs.build.outputs.MONGO_INITDB_ROOT_USERNAME }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export MONGO_INITDB_ROOT_PASSWORD='${{ needs.build.outputs.MONGO_INITDB_ROOT_PASSWORD }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export POSTGRES_USER='${{ needs.build.outputs.POSTGRES_USER }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export POSTGRES_PASSWORD='${{ needs.build.outputs.POSTGRES_PASSWORD }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export KEYCLOAK_ADMIN='${{ needs.build.outputs.KEYCLOAK_ADMIN }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export KEYCLOAK_ADMIN_PASSWORD='${{ needs.build.outputs.KEYCLOAK_ADMIN_PASSWORD }}'\" >> /etc/environment"  
            echo "::endgroup::"
            echo "Environment updated."
           
      - name: Restart Docker containers on remote server
        uses: appleboy/ssh-action@v1.2.0
        with:
          host: ${{ secrets.IONOS_REMOTE_HOST }}
          username: ${{ secrets.IONOS_REMOTE_USER }}
          key: ${{ secrets.IONOS_SSH_PRIVATE_KEY }}
          script: |          
            cd /home/logicmade/bookitfor.me.uk/
            docker compose -f docker-compose-data.yml down
            docker compose -f docker-compose-data.yml up -d --build

Notes about the Yaml file

Build job must output the variables to use them in deploy job. If steps are in the same job there is no need to do that.

In the first step it checks if docker-compose-data.yml is changed. If not it exits with success

To hide echo in the logs I used

     echo "::group::Hidden section"
     ......
     echo "::endgroup::"

to use sudo with password I used this syntax

echo $Template:Secrets.IONOS SUDO PASS | sudo -S bash -c "echo \"export RABBITMQ_DEFAULT_USER='$Template:Needs.build.outputs.RABBITMQ DEFAULT USER'\" >> /etc/environment"