# Exercise: A Project's Build Script

Finish the Maple Street Bikes website's build.ps1 with check, test, package and publish tasks that find their tools with Get-Command, stop when a program's exit code says it failed, and read the deploy token from the environment, and let a Pester check run each task the way a build server would.

Journeyman · PowerShell · Updated November 10, 2026

Maple Street Bikes is ready for its website to go live, and its `build.ps1` can only copy the site into `dist` so far. Before anyone publishes it, the script should check that the tools are installed, test every page, zip a release only when the tests pass, and publish without the deploy token ever being written into it. You'll write those four tasks, and a Pester check runs each one the way a build server would.

## Get the exercise code

Open this lesson's GitHub repository, [github.com/ask-codey/exercise-powershell-a-projects-build-script](https://github.com/ask-codey/exercise-powershell-a-projects-build-script), 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.

The folder holds:

- `src`: the website, three pages and a style sheet. One page has a broken link for your test task to find.
- `tools\check-pages.ps1`: a page checker, ready to use. It ends with exit code 0 when every page passes and 1 when one doesn't.
- `build.ps1`: the build script, with the `build` task written and four tasks for you to write.
- `Check.Tests.ps1`: the check. You don't need to change it.

## What to do

Open PowerShell 7 in the exercise folder and open `build.ps1` in VS Code or `edit`. Each task is a function, and each of the four you write starts as one line that stops the script:

```powershell
function Invoke-CheckTask {
    throw 'The check task is not written yet.'
}
```

Replace each `throw` with your own code. The comment above each function, and `README.md`, say what it does:

1. **`check`**: find each tool in `$tools` (`git` and `pwsh`) with `Get-Command`, print `Found git` and `Found pwsh`, and stop with an error that names a tool that's missing.
2. **`test`**: run the page checker on `src` as its own program, `pwsh -NoProfile -File $checker -Path $src`, print `All pages passed` when its exit code is 0, and stop with an error when it isn't. Then fix the broken link it finds.
3. **`package`**: test, then build, then zip what's inside `dist` into `release\bike-shop-site-1.2.0.zip`. A site that fails its test is never zipped.
4. **`publish`**: stop with an error that names `$env:BIKESHOP_DEPLOY_TOKEN` when it isn't set; otherwise package, and copy the zip into the `server` folder, which stands in for the web host. Never print the token.

Try each task as you finish it, for example `.\build.ps1 -Task check`. To try `publish`, set a practice token for this window first, such as `$env:BIKESHOP_DEPLOY_TOKEN = 'practice-token'`: any text will do.

## Check your work

From the exercise folder:

```powershell
Invoke-Pester
```

At first six of the seven tests fail. That's expected: only the `build` task works yet. Each task you finish turns its test green. The check copies your `build.ps1` into a throwaway folder beside a small site of its own and runs each task from another folder with `pwsh -File`, once with good pages and once with a broken link. It sets `PATH` and the token for those runs only, and brings a stand-in `git` of its own, so it works even without Git installed. A failing test says what to look at, for example *the checker ended with exit code 1, and the test task carried on as if nothing happened.*

If PowerShell says the check can't be loaded because running scripts is disabled or the file is not digitally signed, run `Get-ChildItem -Recurse | Unblock-File` once from the exercise folder, then `Invoke-Pester` again.

## Hints

- **One task can use another.** The functions are there to be called: `package` needs the test and the build first, as *The Scripts Every Project Has* describes.
- **A missing tool gives back nothing.** `Get-Command` with `-ErrorAction SilentlyContinue` returns nothing for a tool that isn't installed, so `if (-not (...))` asks "is it missing?"
- **A failing program doesn't stop the script by itself.** Check `$LASTEXITCODE` right after it, or set `$PSNativeCommandUseErrorActionPreference`, as in *Calling Other Programs*.
- **Check for the token first.** Before the task builds or copies anything, as in *Secrets Out of Scripts*. Single quotes in the message keep the variable's name from being replaced by its value.
- **Zip what's inside the folder.** `Compress-Archive -Path "$dist\*"` puts the pages at the top of the zip, as in *Many Files as One: `Compress-Archive` and `Expand-Archive`*. Make the `release` folder first.

## When you're done

Every test is green, and Pester's last line says:

```text
Tests Passed: 7, Failed: 0, Skipped: 0, Inconclusive: 0, NotRun: 0
```

With a practice token set, `.\build.ps1 -Task publish` runs every step in order:

```text
Checked 3 pages: no problems
All pages passed
Built 4 files into dist
Packaged C:\Users\Sam\terminal-practice\exercise-powershell-a-projects-build-script\release\bike-shop-site-1.2.0.zip
Published version 1.2.0 to C:\Users\Sam\terminal-practice\exercise-powershell-a-projects-build-script\server, with the deploy token from the environment
```

Your last line may be worded differently. Now look back at your `publish` task: if a teammate cloned the repo tomorrow, what would they need to set up, and what in your script tells them?

## 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-powershell-a-projects-build-script`), choose **Public**, and click **Create repository**. Leave out the README: your folder already has one.
2. In a terminal in your exercise folder (PowerShell works), send your work to your new repo. If you downloaded the ZIP:

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

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

   ```powershell
   git remote set-url origin https://github.com/your-name/exercise-powershell-a-projects-build-script.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-powershell-a-projects-build-script`, 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

You've written the script a real project runs a hundred times a week: it checks, tests and packages, and it publishes with a secret it never holds.

## Key terms

- [Build Script](https://www.ask-codey.com/glossary/build-script)
- [Get-Command](https://www.ask-codey.com/glossary/get-command)
- [Exit Code](https://www.ask-codey.com/glossary/exit-code-operating-system)
- [Environment Variable](https://www.ask-codey.com/glossary/environment-variable-operating-system)
- [Compress-Archive](https://www.ask-codey.com/glossary/compress-archive)
- [Pester](https://www.ask-codey.com/glossary/pester)

## Related

- [The Scripts Every Project Has](https://www.ask-codey.com/lessons/powershell-project-scripts)
- [Calling Other Programs](https://www.ask-codey.com/lessons/powershell-native-commands)
- [Secrets Out of Scripts](https://www.ask-codey.com/lessons/powershell-secrets)
- [Many Files as One: Compress-Archive and Expand-Archive](https://www.ask-codey.com/lessons/powershell-zip-files)

Source: https://www.ask-codey.com/lessons/exercise-powershell-a-projects-build-script
