Automated deployment using github actions ssh and shell script

This article covers a very simple method using github actions to execute a deployment shell script on your remote server over ssh.

“How to deploy your code from Github action using shell script and ssh”

We are going to be using the following github action.

https://github.com/marketplace/actions/install-ssh-key

Using this github action it allows you to set the ssh key file, and known hosts file within the github virtual machine, running the action (also know as a github ‘runner’).

Once these values are set, you can then run commands from within your github action with a ssh session to your remote machine.

These commands could be, rsync or scp. In this particular case, we run ssh to kick off a deployment script on our remote server.

The first setup is to setup your ssh keys. On your remote server, run the command ssh-keygen dont enter any passcode. This will generate some files in your ./ssh direcotry (as long as you used the defaults)

first, copy your public key to authorised_keys file

> cp id_rsa.pub authorized_keys

Then you need to add your private key into your github secrets with the variable name

> cat id_rsa

Then you can delete your private key file

> rm id_rsa

Once you have your keys all setup, you can start to build up your github action script.

This is a very basic example below.

name: CI Script
on: [push]

jobs:
  # Deploy Process
  deploy:
    runs-on: ubuntu-18.04 
    steps:   
    - name: Install SSH Key
      uses: shimataro/ssh-key-action@v2.1.0
      with:
        key: $ { { secrets.SSH_KEY } }
        known_hosts: $ { { secrets.KNOWN_HOSTS } }

    - name: Execute test Script
      run: ssh username@myserver.com bash test.sh

In the script above, you can use a dummy know hosts file for setup and demonstration purposes.

known_hosts: github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2
	A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIc
	r6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzL
	NnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6m
	UoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJ
	dsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJ
	iS5ap43JXiUFFAaQ==

This script will set the ssh_key and known_hosts values, and then run the ssh command and kick off a shell script.

If your server is behind a firewall, for example, if it is a test or stage server, then you need to allow github ip address range access your server.

The list of IPs can be found below. In this particular case, we only need the IPs listed under ‘actions’.

https://api.github.com/meta

When you make a change to main branch, ie, on push, this script will trigger.

It will run the script on your remote server, from your github action machine.

Keep this in mind when setting up your script on the remote machine.

You can see my repository with this in action here github.com/aalshukri/DevOps-Drupal-Deploy-TEST

In my example, I have added the server and username to github secrets, as to not expose them in a public script.

for example

${ { secrets.USERNAME } }
${ { secrets.SERVER } }

Would be set as

USERNAME = username
SERVER = myserver.com

so the run command in the github action script becomes

run: ssh ${ { secrets.USERNAME } }@${ { secrets.SERVER } } bash test.sh

or

run: ssh ${ { secrets.USERNAME } }@${ { secrets.SERVER } } bash /home/${ { secrets.USERNAME } }/test.sh

The entire script would be as follows below.

name: CI Script
on: [push]

jobs:
  # Deploy Process
  deploy:
    runs-on: ubuntu-18.04 
    steps:
    - name: Install SSH Key
      uses: shimataro/ssh-key-action@v2.1.0
      with:
        key: ${ { secrets.SSH_KEY } }
        known_hosts: ${ { secrets.KNOWN_HOSTS } }        
    - name: Execute test Script
      run: ssh ${ { secrets.USERNAME } }@${ { secrets.SERVER } } bash test.sh

If you would like to replicate my test.sh script, I have listed that below.

#!/bin/bash
now=$(date)
echo "Output $now" > output.txt

As you can see, this is a simple script to generate an output file along with the date.

I done some testing for the script execution time. I added a line to sleep the script for 100 seconds. I found the github action to wait until script is completed. This needs to be considered in your deployment scnario. You might want to run bash test.sh & to push the script to the background, and then exit.

The script I used to test this out was below.

#!/bin/bash
now=$(date)
echo "Output $now" > output.txt
sleep 100
echo "Done $now" >> output.txt

This will overwite output.txt on each excution, with the line output but then append the done line after the specificed wait time is compelted.

Some issues I encountered.

jailshell: test.sh: command not found

You need to either call the script with root directory, or you need to include the bash command.

ie.

ssh username@myserver.com bash test.sh

or

ssh username@myserver.com /home/username/test.sh

Warning: Permanently added the RSA host key for IP address 
to the list of known hosts.
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
	LANGUAGE = (unset),
	LC_ALL = (unset),
	LANG = "C.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
	LANGUAGE = (unset),
	LC_ALL = (unset),
	LANG = "C.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").

jailshell: test.sh: command not found

Error: Process completed with exit code 127.

As solution as above.

ssh: connect to host username@myserver.com port 22: Connection timed out

Need to add github IP addresses so github server can access your sever.

You can read more about how I have used devops in my previous projects.

If you have any questions or comments you can contact me.