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.shandscripts/deploy.sh: the three scripts you write. Each starts with a comment that says exactly what it must do..env.example: the two settingsdeploy.shneeds, 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
scripts/setup.shchecks thatpython3andrsyncare installed, and printsmissing: TOOLfor 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.exampleto.envwhen there's no.envyet, never replacing one, makes it private, and printsReady.scripts/test.shprintsNo title: site/NAME.htmlfor every page without a<title>, and finishes with exit code 1. When every page has one, it printsEvery page has a title.and finishes with 0.scripts/deploy.shstops with exit code 1 and a message when there's no.env. It loads.envso its values are exported, stops whenDEPLOY_TARGETorHOST_TOKENis empty, naming the empty one, and runstest.sh, stopping if it fails. Only then does it copy what's insidesite/into the folder inDEPLOY_TARGETwithrsync, so that folder matchessite/exactly, and printDeployed toand the folder. It never prints the token..gitignorekeeps.envout 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 -vthat lists every missing tool before it stops. - Never replace a real .env.
[[ ! -f .env ]]is true only when there's no.envyet (Checking Before You Act: File Tests).chmod 600comes after, every time. - Exported, not only set. Secrets and .env Files shows the three lines that load
.envso 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 ... firuns 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/andsiteeach copy, and what--deletedoes.
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.
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.In a terminal in your exercise folder (Ubuntu in WSL on Windows), send your work to your new repo. Your
.gitignorekeeps.envout 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 mainIf 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 mainPaste 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.