AMP stack script on Ubuntu

This article is about several things:

  • shell scripting with bash
  • using GitHub Actions
  • developing a re-usable script to prepare an Ubuntu server (or Docker container) to run Moodle.

Requirements

  • A single bash script
  • Use functions for better programming style
  • Support installing just a few options with switches
  • Make script idempotent - to re-run without failing

Environment

  • Use a public GitHub repository
  • Use GitHub actions to lint the script
  • Use GitHub actions to quickly test whether the code works (option for an ubuntu-20.04 agent, which corresponds to the desired server, and also because I'm using Ubuntu 20.04 on Windows Subsystem for Linux 2).

Script use cases

  • Install Apache
  • Install PHP
  • Install PHP-FPM
  • Install php-mysql module
  • Install Memcached
  • Set up virtual hosts with SSL certificates (even if self-signed)
  • Backup script for code and databases - local destination, and then synced to AWS S3

Set up GitHub repository

  • Sign in to GitHub with my personal account
  • Create a new repository - [nickromney/amp-moodle], with description "Install AMP stack on Ubuntu, with optional Moodle"
    • Added a README
    • Left default branch with the new default of main
  • Under Code, choose Clone, and SSH
  • Copy git@github.com:nickromney/amp-moodle.git
  • Open a terminal (for me, ZSH on WSL2 running Ubuntu 20.04)
  • Change the domain to personal.github.com to prompt for my personal SSH key while I clone the repository locally
  • git clone git@personal.github.com:nickromney/amp-moodle.git

Set up GitHub actions and linting

  • Check what default software is available on the Ubuntu 20.04.1 LTS virtual environment. We see that there's already some software which could speed us up:
    • curl
    • git
    • unzip
    • wget
    • AWS CLI
    • PHP 7.4.13
    • Google Chrome and ChromeDriver
    • PostgreSQL
    • MySQL 8.0.22
    • MySQL Server
  • And installed apt packages:
    • dnsutil
    • shellcheck

Set up ShellCheck

Create a GitHub workflow to lint with ShellCheck

cd amp-moodle
git checkout -b add-linting-workflow
  • In VSCode, add .github/workflows/lint.yml, and adapt the example from GitHub Action for Shellcheck, because we want to:
    • lint all branches except main (we want to protect the main branch, and ensure we only merge into main via Pull Requests)
    • specifically use the Ubuntu 20.04 image (by choosing runs-on: ubuntu-20.04 rather than runs-on: ubuntu-latest)
name: Lint

on:
  push:
    branches:
      - '**'
      - '!main'

jobs:
  shellcheck:
    name: Shellcheck
    runs-on: ubuntu-20.04
    steps:
    - uses: actions/checkout@v2
    - name: Run ShellCheck
      uses: ludeeus/action-shellcheck@master

This runs a workflow of two actions

  • checks out the code
  • runs shellcheck against matching files
git add .github/
git commit -m "Add linting workflow"
git push --set-upstream origin add-linting-workflow

Now we're in a position to merge the lint check to main, and then write a shell script.

  • Create Pull Request
  • Merge
  • Delete add-linting-workflow branch from GitHub

##

git checkout main
git fetch origin
git reset --hard origin/main

And I see

HEAD is now at 6b8e12b Merge pull request #1 from nickromney/add-linting-workflow

Run the script on merges to main

Then we need to actually run our amp.sh script (which we are yet to write) on merges to main For this, we'll need an additional workflow

git checkout main
git checkout -b add-ci-workflow

Create .github/workflows/ci.yml

name: CI

on:
  push:
    branches:
      - main

jobs:
  ci:
    name: Run amp.sh
    runs-on: ubuntu-20.04
    steps:
    - uses: actions/checkout@v2
      name: Checkout Code

    - name: Run amp.sh
      run: |
        FILE="/etc/passwd"
        [[ -f ./amp.sh ]] && ./amp.sh || echo "./amp.sh file not present"
git add .github/workflows/ci.yml
git commit -m "Add CI workflow"
git push --set-upstream origin add-ci-workflow
  • Pull Request
  • Merge

Write the amp.sh bash script

git checkout main
git checkout -b php-install
  • Create a new file amp.sh
  • We need a shebang to tell the script to use bash
  • Helper function to test if a command is available (using this StackOverflow answer with 3323 votes)
  • PHP function to install PHP if not available
  • We start with PHP because we believe it to be installed already
#!/bin/bash

# Helper functions

checkIsCommandAvailable() {
    local commandToCheck="$1"
    if command -v "${commandToCheck}" &> /dev/null
    then
        echo "${commandToCheck} command available"
    else
        # propagate error to caller
        return $?
    fi
}

phpInstall() {
    if ! checkIsCommandAvailable php
    then
        echo "PHP is not yet available. Starting installation of PHP"
        apt -qy install php
    fi
}

phpInstall
git add amp.sh
git update-index --chmod +x ./amp.sh
git commit -m "Add helper function and install PHP"
git push --set-upstream origin php-install
  • Pull Request
  • Merge