Skip to content

Instantly share code, notes, and snippets.

@froger-me
Last active May 4, 2022 01:57
Show Gist options
  • Save froger-me/2a2a3d26a539bf6e78b6f1dae22204c4 to your computer and use it in GitHub Desktop.
Save froger-me/2a2a3d26a539bf6e78b6f1dae22204c4 to your computer and use it in GitHub Desktop.

Rsync Github Deploy

Preparation & requirements - on the destination server:

  • Create SSH keys (without passphrase):
cd ~/.ssh
ssh-keygen -t rsa -b 4096 -C "email@host.tld"
  • Add authorized keys:
cd ~/.ssh
cat id_rsa.pub >> authorized_keys
  • Ensure public key authentication is enabled (use your editor of choice, example with vim):

    • sudo vim /etc/ssh/sshd_config
    • using /, search for:
      • RSAAuthentication and make sure it is set to yes
      • PubkeyAuthentication and make sure it is set to yes
      • UsePAM and make sure it is set to yes
      • PreferredAuthentications and make sure publickey is the first in the list
    • if the file is changed, restart the sshd service (service sshd restart)
  • Ensure permissions are correctly set:

chmod 750 ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Secrets to add to Github repository:

Secret variable name Description Example
DEPLOY_SSH_PRIVATE_KEY The private key generated on the server. Typically, the content of the id_rsa file.
-----BEGIN RSA PRIVATE KEY-----
[..............................]
-----END RSA PRIVATE KEY-----
DEPLOY_SSH_HOST The host name of the destination server. sub.domain.tld, 888.888.888.888
DEPLOY_SSH_USER The user with which to identify when using rsync ; has the public key content in its ~/.ssh/authorized_keys file. www-data
DEPLOY_PATH_FILTERS The filters given to rsync to determine what to deploy.
+ included.html
+ /included/
+ /included/*
+ /included_all/***
- /**
DEPLOY_PATH The destination folder on the destination server. /var/www/public_html

Github Action:

name: rsync-deploy
on:
  push:
    branches: master
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      REMOTE_PATH: ${{ secrets.DEPLOY_PATH }}
      SSH_KEY: ${{ secrets.DEPLOY_SSH_PRIVATE_KEY }}
      SSH_HOST: ${{ secrets.DEPLOY_SSH_HOST }}
      SSH_FILE: id_rsa
      SSH_USER: ${{ secrets.DEPLOY_SSH_USER }}
      SSH_HOST: ${{ secrets.DEPLOY_SSH_HOST }}
      SSH_FILE: id_rsa
      # Sample directory tree:
      #  .
      #  ├── included
      #  │   ├── excluded
      #  │   │   └── excluded.html
      #  │   └── included.html
      #  ├── included_all
      #  │   ├── included
      #  │   │   └── included.html
      #  │   └── included.html
      #  ├── excluded.html
      #  └── included.html
      #
      # Sample RSYNC_FILTERS_CONTENT whitelist content:
      # + included.html
      # + /included/
      # + /included/*
      # + /included_all/***
      # - /**
      #
      # Above filters combined with "--prune-empty-dirs" results in the following being transferred:
      # included.html
      # included/
      # included/included.html
      # included_all/
      # included_all/included.html
      # included_all/included/
      # included_all/included/included.html
      RSYNC_FILTERS_CONTENT: ${{ secrets.DEPLOY_PATH_FILTERS }}
      RSYNC_FILTERS_FILE: rsync_filters.txt
    steps:
      # checkout the repo
      - uses: actions/checkout@v3
      - name: Configure SSH
        # create ~/.ssh/ directory if not exists
        # adjust permissions of ~/.ssh/ directory
        # create the specified private key file from secrets
        # adjust permissions of private key file
        # get the remote host in a variable
        # get the content to add to ~/.ssh/known_hosts
        # setup ~/.ssh/known_hosts
        run: |
          mkdir -p ~/.ssh/
          chmod 700 ~/.ssh
          echo "$SSH_KEY" > ~/.ssh/$SSH_FILE
          chmod 600 ~/.ssh/$SSH_FILE
          host=SSH_HOST
          hosts="$(dig +short "$host" | grep -v '\.$' | sed -z 's|\n|,|g')$host"
          ssh-keyscan -H "$SSH_HOST" > ~/.ssh/known_hosts
      - name: Setup rsync filters
        run: |
          touch $RSYNC_FILTERS_FILE
          echo "$RSYNC_FILTERS_CONTENT" > $RSYNC_FILTERS_FILE
      - name: Deploy with rsync
        # Details:
        # - checksum-based comparison, archive mode, compressed, quiet
        # - connect with ssh using specified identification key file, suppressing logs that are not errors
        # - specify that files not included in filtered source will be deleted at destination (other files are preserved)
        # - apply filters as defined in rsync_filters.txt 
        # - specify empty directories will be removed from filtered source (other empty directories are preserved)
        # - transfer the specified source path
        # - transfer to specified remote destination path
        # Remarks:
        # - remove `q` and/or add `v` or `vv` in `-cazq` to show more info about file transfers
        # - it is highly recommended to add a `--dry-run \` line before the specified source path `./ \` to test the filters ; remove afterwards
        run: |
         rsync -cazq \
          -e "ssh -i ~/.ssh/$SSH_FILE -o LogLevel=ERROR" \
          --delete \
          --filter="merge $RSYNC_FILTERS_FILE" \
          --prune-empty-dirs \
          ./ \
          $SSH_USER@$SSH_HOST:$REMOTE_PATH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment