Exercise: A Project's Scripts

Give an animal shelter's website the scripts every project has: setup.sh checks the tools and makes a private .env, test.sh checks that every page has a title, and deploy.sh loads .env, runs the tests and copies the site with rsync; then keep .env out of Git, and let a check script tell you what's done.

Journeyman · Unix · Linux · Updated

The Hillside Animal Shelter's website has a new volunteer every few months, and each one asks the same things. What do I install? How do I see the site? How do I know I didn't break a page? How does it go live, and where's the password for that? You'll answer all of them with the scripts every project has: setup.sh checks the tools and makes a private settings file, test.sh checks the pages, and deploy.sh puts the site live, but only after the tests pass and never with a token written in a script. A check script tells you what's done.

Before you start

You need a terminal: the Terminal app on a Mac, or Ubuntu on Linux or in WSL on Windows. You also need python3 (for dev.sh's web server), rsync (for the deploy) and bats, a small program that runs checks written for bash. If bats --version, python3 --version or rsync --version doesn't print a version number yet, install what's missing:

sudo apt install bats python3 rsync      # Ubuntu, and Ubuntu in WSL
brew install bats-core python            # a Mac, with Homebrew

sudo asks for your password, the one you use to sign in; nothing shows on the screen while you type it.

Get the exercise code

Open this lesson's GitHub repository, github.com/ask-codey/exercise-bash-a-projects-scripts, also linked from the Exercise box beside the lesson. Click Code, then Download ZIP, and unzip it into your terminal-practice folder. Or clone it with Git. On Windows, put it inside Ubuntu, in your Linux home folder, not in a Windows folder under /mnt/c, so chmod works on the scripts.

The folder holds:

  • site/: the shelter's website, the pages your scripts check and deploy.
  • scripts/dev.sh: ready to use, and an example of the shape the other scripts follow.
  • scripts/setup.sh, scripts/test.sh and scripts/deploy.sh: the three scripts you write. Each starts with a comment that says exactly what it must do.
  • .env.example: the two settings deploy.sh needs, with no values. .gitignore: for you to finish.
  • check.bats: the check. You don't need to change it.

What to do

Open a terminal in the exercise folder, make the scripts runnable (Your First Script), and open the first one in nano:

chmod +x scripts/*.sh
nano scripts/setup.sh
  1. scripts/setup.sh checks that python3 and rsync are installed, and prints missing: TOOL for each one that isn't, checking both before it stops. With a tool missing, it stops with exit code 1 and makes no .env. Otherwise it copies .env.example to .env when there's no .env yet, never replacing one, makes it private, and prints Ready.
  2. scripts/test.sh prints No title: site/NAME.html for every page without a <title>, and finishes with exit code 1. When every page has one, it prints Every page has a title. and finishes with 0.
  3. scripts/deploy.sh stops with exit code 1 and a message when there's no .env. It loads .env so its values are exported, stops when DEPLOY_TARGET or HOST_TOKEN is empty, naming the empty one, and runs test.sh, stopping if it fails. Only then does it copy what's inside site/ into the folder in DEPLOY_TARGET with rsync, so that folder matches site/ exactly, and print Deployed to and the folder. It never prints the token.
  4. .gitignore keeps .env out of Git, but not .env.example.

All three scripts work from any folder: run ../scripts/test.sh from inside site/, and it checks the same pages.

Try each script as you go. To try deploy.sh, fill in .env with nano: DEPLOY_TARGET=../shelter-live, a folder beside the exercise, and a made-up HOST_TOKEN such as hx-1234-practice. Run ./scripts/deploy.sh, and the site appears in ../shelter-live. If you use Git, run git status --short too: .env shouldn't be on the list.

Check your work

From the exercise folder:

bats check.bats

At first almost every check fails. That's expected: each job you finish turns its checks into a ✓. The check copies the project into a throwaway folder and runs your scripts there, with a .env of its own and a live folder in place of a server, so nothing in your folder changes and your own .env is never read. To pretend a tool is missing, it runs setup.sh with a PATH that leaves the tool out. A failing check says what to look at, for example volunteer.html was deleted from site/, but it's still in the live folder after the next deploy.

Hints

  • Start from the top folder. The Scripts Every Project Has shows the first line every project script needs, and the tool check with command -v that lists every missing tool before it stops.
  • Never replace a real .env. [[ ! -f .env ]] is true only when there's no .env yet (Checking Before You Act: File Tests). chmod 600 comes after, every time.
  • Exported, not only set. Secrets and .env Files shows the three lines that load .env so the programs your script starts can see its values, and how to stop with a clear message when a value is empty.
  • Tests first, copy second. if ! ./scripts/test.sh; then ... fi runs the tests and lets you stop when they fail (Did It Work? Exit Codes, && and ||).
  • Mind the slash. Backups That Work: rsync shows what site/ and site each copy, and what --delete does.

When you're done

Every check passes:

check.bats
 ✓ the project's files are here, the scripts are runnable, and rsync is installed
 ✓ 1) setup.sh makes .env from .env.example, makes it private and says Ready.
 ✓ 2) setup.sh keeps a .env that's already there
 ✓ 3) setup.sh lists every missing tool, stops with exit code 1 and makes no .env
 ✓ 4) setup.sh works from any folder
 ✓ 5) test.sh says every page has a title, with exit code 0
 ✓ 6) test.sh names every page without a title, with exit code 1
 ✓ 7) test.sh works from any folder
 ✓ 8) deploy.sh copies site/ to DEPLOY_TARGET with rsync, and never prints the token
 ✓ 9) deploy.sh exports .env's values to the programs it starts
 ✓ 10) deploy.sh makes DEPLOY_TARGET match site/: a deleted page goes too
 ✓ 11) deploy.sh stops with exit code 1, and copies nothing, when there's no .env
 ✓ 12) deploy.sh stops with exit code 1, and copies nothing, when a setting is empty
 ✓ 13) deploy.sh runs test.sh first, and copies nothing when it fails
 ✓ 14) .gitignore keeps .env out of Git, but not .env.example

15 tests, 0 failures

Now picture the next volunteer. Which two commands do they type before they can see the site, and which one file do they fill in before they can put it live?

Submit your exercise

The exercise is how you finish this lesson. When your work is done, put it on GitHub in a repo of your own, then submit that repo's address here.

  1. On GitHub, click +, then New repository. Name it (for example exercise-bash-a-projects-scripts), choose Public, and click Create repository. Leave out the README: your folder already has one.

  2. In a terminal in your exercise folder (Ubuntu in WSL on Windows), send your work to your new repo. Your .gitignore keeps .env out of it. If you downloaded the ZIP:

    git init -b main
    git add .
    git commit -m "My solution"
    git remote add origin https://github.com/your-name/exercise-bash-a-projects-scripts.git
    git push -u origin main
    

    If you cloned the starter instead, point your copy at your own repo, then commit and push:

    git remote set-url origin https://github.com/your-name/exercise-bash-a-projects-scripts.git
    git add .
    git commit -m "My solution"
    git push -u origin main
    
  3. Paste your repo's address, such as https://github.com/your-name/exercise-bash-a-projects-scripts, into Submit your exercise on this page. The site checks that the repo is public and holds your commits, and then the lesson is complete.

Changed something after you submitted? Push it, and submit again: your latest submission is the one that counts.

So what

Three short scripts and one settings file turn "ask whoever set this up" into ./scripts/setup.sh, ./scripts/dev.sh, and a deploy that can't skip the tests or leak a key.

Key terms

Related