seed: curriculum content
This commit is contained in:
30
1.solar-system/1.welcome/0.index.md
Normal file
30
1.solar-system/1.welcome/0.index.md
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
type: module
|
||||
title: "Welcome"
|
||||
description: "Your first 1–2 days. Crash course in the terminal and Git, then a paired battle."
|
||||
---
|
||||
|
||||
# Welcome
|
||||
|
||||
You've landed. The next 1–2 days are about getting you off the ground:
|
||||
the shell, Git, and how things work here. Sixteen blocks, one battle,
|
||||
then you're cleared for Python.
|
||||
|
||||
This module gives you the bare minimum tools to write code, save your
|
||||
work, and ship it. Every command you learn here will reappear, in some
|
||||
form, every day for the rest of the program.
|
||||
|
||||
## What You'll Do
|
||||
|
||||
- Walk the file system from a blank prompt
|
||||
- Make, move, and read files
|
||||
- Write your first shell script
|
||||
- Configure Git and make your first commits
|
||||
- Push your work to GitHub and open your first pull request
|
||||
- Pair with another cadet to close out the module
|
||||
|
||||
## Pace
|
||||
|
||||
Roughly 10–12 hours of focused work. Some cadets finish in a day, some
|
||||
take two. Either is fine. Do not skip blocks — the checkpoint catches
|
||||
gaps.
|
||||
84
1.solar-system/1.welcome/01.welcome/index.md
Normal file
84
1.solar-system/1.welcome/01.welcome/index.md
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
type: story
|
||||
title: "Welcome"
|
||||
xp: 25
|
||||
duration: 20
|
||||
difficulty: 1
|
||||
---
|
||||
|
||||
# Welcome
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet.
|
||||
>
|
||||
> Welcome to the program.
|
||||
>
|
||||
> You have just enrolled in twelve months of training. By the end, if
|
||||
> you finish, you will be a working developer — not a graduate of
|
||||
> something, but a person who can sit in front of a blank file and
|
||||
> produce software that does what it's supposed to do.
|
||||
>
|
||||
> We will not teach you the way you've been taught before. There will
|
||||
> be no lectures. No instructor at a board. No videos to play at 2x.
|
||||
> You will read, you will type, and you will build. Most of the
|
||||
> learning happens between your ears while your fingers move.
|
||||
>
|
||||
> Before you touch the terminal — and you will, in the very next block
|
||||
> — we want you to know where you are and what's about to happen.
|
||||
> Read this to the end.
|
||||
>
|
||||
> [TRANSMISSION CONTINUES]
|
||||
|
||||
## Where You Are
|
||||
|
||||
You have entered Stage I: **Solar System**. Your launchpad. Over the
|
||||
next four weeks, you will move from "I have never written code" to
|
||||
"I can write small programs that solve real problems."
|
||||
|
||||
Past Solar System, four more stages await:
|
||||
|
||||
- **Milky Way** — deeper Python, data structures, your first real projects.
|
||||
- **Andromeda** — web, databases, applications other people can use.
|
||||
- **Deep Space** — specialize. Backend, frontend, data, or DevOps.
|
||||
- **New Horizon** — your capstone, and the bridge to a job.
|
||||
|
||||
Don't worry about anything past Solar System right now. The path
|
||||
exists. Trust it. Walk one stage at a time.
|
||||
|
||||
## How Content Works
|
||||
|
||||
Every module in every stage is built from three kinds of blocks:
|
||||
|
||||
- **Stories** like this one. They give you context — the *why* before
|
||||
the *do*. They are short. Skim them and you'll pay for it later.
|
||||
- **Challenges** are where you build. Each one drops you a starter pack
|
||||
and a mission. You write code, submit, and an automated grader tells
|
||||
you pass or iterate.
|
||||
- **Battles** put you in a pair. One of you executes, the other
|
||||
reviews. You learn twice — once doing, once watching.
|
||||
|
||||
Every block earns XP when you complete it. XP is not a leaderboard.
|
||||
It's a measure of how much you have actually done. Skipping ahead does
|
||||
not work — checkpoints catch it, and they catch it in front of your
|
||||
peers.
|
||||
|
||||
## What This Module Holds
|
||||
|
||||
The next block is `hello-world` — your first line of code. After that,
|
||||
twelve more challenges teach you the shell and Git, the tools every
|
||||
cadet on Earth uses every day. One paired battle closes the module.
|
||||
|
||||
One to two days of work, depending on your pace. Then Python begins.
|
||||
|
||||
> **[CLOSING — Mission Control]**
|
||||
>
|
||||
> One more thing before we let you go.
|
||||
>
|
||||
> Everyone who finishes the program got stuck where you are now. Every
|
||||
> single one. The terminal you're about to open scared them too. They
|
||||
> kept going. So will you.
|
||||
>
|
||||
> Your training begins on the next block.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
35
1.solar-system/1.welcome/02.hello-world/index.md
Normal file
35
1.solar-system/1.welcome/02.hello-world/index.md
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "Hello, World"
|
||||
xp: 25
|
||||
duration: 15
|
||||
difficulty: 1
|
||||
---
|
||||
|
||||
# Hello, World
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, first signal. Write a script that creates `hello.txt`
|
||||
> containing exactly:
|
||||
>
|
||||
> ```
|
||||
> Hello, World
|
||||
> ```
|
||||
>
|
||||
> Two commands:
|
||||
>
|
||||
> - `echo` prints text
|
||||
> - `>` redirects what would print into a file
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`, write the command(s) that create `hello.txt`
|
||||
with the content `Hello, World`.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `hello.txt` exists in the working directory
|
||||
- File contents match `Hello, World` exactly
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Hello, World — your first script.
|
||||
# Write the line "Hello, World" into a file called hello.txt
|
||||
# in the current directory.
|
||||
|
||||
# Your code here.
|
||||
16
1.solar-system/1.welcome/02.hello-world/testing/test.sh
Normal file
16
1.solar-system/1.welcome/02.hello-world/testing/test.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
if [ -f hello.txt ]; then
|
||||
echo "ok 1 - hello.txt is created"
|
||||
else
|
||||
echo "not ok 1 - hello.txt is created"
|
||||
fi
|
||||
|
||||
ACTUAL=$(cat hello.txt 2>/dev/null)
|
||||
ACTUAL="${ACTUAL%$'\n'}"
|
||||
if [ "$ACTUAL" = "Hello, World" ]; then
|
||||
echo "ok 2 - hello.txt contains 'Hello, World'"
|
||||
else
|
||||
echo "not ok 2 - hello.txt contains 'Hello, World'"
|
||||
fi
|
||||
46
1.solar-system/1.welcome/03.first-repo/index.md
Normal file
46
1.solar-system/1.welcome/03.first-repo/index.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
type: battle
|
||||
title: "First Repo"
|
||||
xp: 200
|
||||
duration: 30
|
||||
difficulty: 1
|
||||
---
|
||||
|
||||
# First Repo
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadets, your first paired exercise. One of you will set up a fresh
|
||||
> Git project; the other will verify it. Then swap.
|
||||
>
|
||||
> Pair up. Decide who goes first as **defender** and who goes first
|
||||
> as **attacker**.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## The Task
|
||||
|
||||
The **defender** writes a single shell script — `setup.sh` — that,
|
||||
when run inside an empty workspace:
|
||||
|
||||
1. Initializes a fresh Git repo on branch `main`.
|
||||
2. Sets `user.name` and `user.email` on the repo.
|
||||
3. Creates a file called `hello-world.txt` containing exactly:
|
||||
```
|
||||
Hello, World
|
||||
```
|
||||
4. Stages and commits it with the message `add hello-world`.
|
||||
|
||||
## Battle Rules
|
||||
|
||||
- **Defender**: writes `setup.sh` in a clean workspace. Does not show
|
||||
the file until done.
|
||||
- **Attacker**: receives the script, runs it in a clean folder,
|
||||
inspects the resulting repo, and submits a review.
|
||||
- **Then swap**: roles flip; the new defender writes their own
|
||||
`setup.sh` (no copy-pasting).
|
||||
- Both pass the battle if both scripts pass review.
|
||||
|
||||
## Tools
|
||||
|
||||
`git init`, `git config`, `echo`, `>`, `git add`, `git commit -m`.
|
||||
38
1.solar-system/1.welcome/03.first-repo/review/general.md
Normal file
38
1.solar-system/1.welcome/03.first-repo/review/general.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# First Repo — Extended Notes
|
||||
|
||||
*Not rendered yet. For instructors and a future "learn more" surface.*
|
||||
|
||||
## Purpose
|
||||
|
||||
The first paired exercise of the program. Cadets have just finished
|
||||
the bash basics; this introduces them to the battle pattern (defender
|
||||
writes, attacker reviews, then swap) using a tiny but real Git
|
||||
workflow: init, commit, log.
|
||||
|
||||
## Skills Demonstrated
|
||||
|
||||
- Composing learned commands (`git init`, `git config`, `git add`,
|
||||
`git commit`) into a single script
|
||||
- Reading another cadet's script to verify behavior
|
||||
- Filling out the review checklist constructively
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **Skipping `git config`** — the commit either fails or uses a
|
||||
fallback identity that's not the cadet's. Reviewer should run
|
||||
`git config --local user.name` to confirm.
|
||||
- **Wrong file content** — `echo "hello world"` (lowercase) doesn't
|
||||
match `Hello, World`. Reviewer must `cat hello-world.txt` and check
|
||||
byte-for-byte.
|
||||
- **Wrong commit message** — `add hello world` (no dash) is *not*
|
||||
`add hello-world`. Reviewer should run `git log --format=%s`.
|
||||
- **Multiple commits** — sometimes a cadet stages files twice and
|
||||
ends up with two commits. The spec asks for exactly one.
|
||||
|
||||
## Discussion Prompts (post-battle)
|
||||
|
||||
After both rounds, pair discusses for 2–3 minutes:
|
||||
|
||||
1. Whose script was easier to read? Why?
|
||||
2. What did you check first when reviewing? Why?
|
||||
3. If this script ran on a server, what could go wrong?
|
||||
27
1.solar-system/1.welcome/03.first-repo/review/review.json
Normal file
27
1.solar-system/1.welcome/03.first-repo/review/review.json
Normal file
@@ -0,0 +1,27 @@
|
||||
[
|
||||
{
|
||||
"title": "Repo Setup",
|
||||
"icon": "ph:git-branch-duotone",
|
||||
"items": [
|
||||
".git/ exists after running setup.sh",
|
||||
"user.name is set on the repo",
|
||||
"user.email is set and looks like an email"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "File Content",
|
||||
"icon": "ph:file-text-duotone",
|
||||
"items": [
|
||||
"hello-world.txt exists at the repo root",
|
||||
"File contains exactly the line 'Hello, World'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Commit",
|
||||
"icon": "ph:git-commit-duotone",
|
||||
"items": [
|
||||
"Exactly one commit in git log",
|
||||
"Commit message is exactly 'add hello-world'"
|
||||
]
|
||||
}
|
||||
]
|
||||
40
1.solar-system/1.welcome/04.the-maker/index.md
Normal file
40
1.solar-system/1.welcome/04.the-maker/index.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Maker"
|
||||
xp: 50
|
||||
duration: 25
|
||||
difficulty: 2
|
||||
---
|
||||
|
||||
# The Maker
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, you've walked the field. Now build something.
|
||||
>
|
||||
> When your script runs, a stale file `cargo-old.txt` is sitting in
|
||||
> your working directory. Build a cargo bay with three rooms — `food/`,
|
||||
> `water/`, `tools/` — each holding an empty `manifest.txt`. Then
|
||||
> delete the stale file.
|
||||
>
|
||||
> Three commands:
|
||||
>
|
||||
> - `mkdir` — make a directory (use `-p` to make nested ones at once)
|
||||
> - `touch` — create an empty file
|
||||
> - `rm` — delete (be careful — there is no trash)
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`, write the commands to:
|
||||
|
||||
1. Create `cargo/food/`, `cargo/water/`, `cargo/tools/`
|
||||
2. Create an empty `manifest.txt` in each room
|
||||
3. Remove `cargo-old.txt`
|
||||
|
||||
## Objectives
|
||||
|
||||
- All three room directories exist
|
||||
- Each contains an empty `manifest.txt`
|
||||
- `cargo-old.txt` no longer exists
|
||||
13
1.solar-system/1.welcome/04.the-maker/starter/starter.sh
Normal file
13
1.solar-system/1.welcome/04.the-maker/starter/starter.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
# The Maker — build a cargo bay.
|
||||
#
|
||||
# When this script runs, a stale file `cargo-old.txt` is in your
|
||||
# working directory. Your script must:
|
||||
#
|
||||
# 1. Create directories: cargo/food, cargo/water, cargo/tools
|
||||
# 2. Create an empty file `manifest.txt` in each of those three rooms
|
||||
# 3. Remove cargo-old.txt
|
||||
#
|
||||
# Tools: mkdir (use -p for nested), touch, rm
|
||||
|
||||
# Your code here.
|
||||
17
1.solar-system/1.welcome/04.the-maker/testing/test.sh
Normal file
17
1.solar-system/1.welcome/04.the-maker/testing/test.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
echo "stale entry" > cargo-old.txt
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
N=0
|
||||
check() {
|
||||
N=$((N+1))
|
||||
if eval "$1"; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi
|
||||
}
|
||||
|
||||
check '[ -d cargo/food ]' "cargo/food exists"
|
||||
check '[ -d cargo/water ]' "cargo/water exists"
|
||||
check '[ -d cargo/tools ]' "cargo/tools exists"
|
||||
check '[ -f cargo/food/manifest.txt ] && [ ! -s cargo/food/manifest.txt ]' "cargo/food/manifest.txt exists and is empty"
|
||||
check '[ -f cargo/water/manifest.txt ] && [ ! -s cargo/water/manifest.txt ]' "cargo/water/manifest.txt exists and is empty"
|
||||
check '[ -f cargo/tools/manifest.txt ] && [ ! -s cargo/tools/manifest.txt ]' "cargo/tools/manifest.txt exists and is empty"
|
||||
check '[ ! -f cargo-old.txt ]' "cargo-old.txt removed"
|
||||
39
1.solar-system/1.welcome/05.the-mover/index.md
Normal file
39
1.solar-system/1.welcome/05.the-mover/index.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Mover"
|
||||
xp: 50
|
||||
duration: 25
|
||||
difficulty: 2
|
||||
---
|
||||
|
||||
# The Mover
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, your cargo bay is built. Now stock it.
|
||||
>
|
||||
> When your script runs, an `inbox/` of fresh supplies has arrived.
|
||||
> Each item is labeled by category — `food-`, `water-`, or `tool-`.
|
||||
> Sort them into the right rooms. Then make a backup of the food
|
||||
> manifest.
|
||||
>
|
||||
> Two commands:
|
||||
>
|
||||
> - `mv` — move (or rename)
|
||||
> - `cp` — copy
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`, write the commands to:
|
||||
|
||||
1. Move each `food-*` file into `cargo/food/`
|
||||
2. Move each `water-*` file into `cargo/water/`
|
||||
3. Move each `tool-*` file into `cargo/tools/`
|
||||
4. Copy `cargo/food/manifest.txt` to `cargo/food/manifest.backup`
|
||||
|
||||
## Objectives
|
||||
|
||||
- All `food-*`, `water-*`, `tool-*` files moved to the correct rooms
|
||||
- `cargo/food/manifest.backup` exists
|
||||
20
1.solar-system/1.welcome/05.the-mover/starter/starter.sh
Normal file
20
1.solar-system/1.welcome/05.the-mover/starter/starter.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
# The Mover — sort the supplies.
|
||||
#
|
||||
# When this script runs:
|
||||
# - cargo/food/, cargo/water/, cargo/tools/ already exist
|
||||
# - cargo/food/manifest.txt exists (empty)
|
||||
# - inbox/ contains files with prefixes:
|
||||
# food-apple.txt, food-bread.txt
|
||||
# water-bottle.txt, water-canteen.txt
|
||||
# tool-wrench.txt, tool-hammer.txt
|
||||
#
|
||||
# Your script must:
|
||||
# 1. Move all food-*.txt files into cargo/food/
|
||||
# 2. Move all water-*.txt files into cargo/water/
|
||||
# 3. Move all tool-*.txt files into cargo/tools/
|
||||
# 4. Copy cargo/food/manifest.txt to cargo/food/manifest.backup
|
||||
#
|
||||
# Tools: mv, cp
|
||||
|
||||
# Your code here.
|
||||
25
1.solar-system/1.welcome/05.the-mover/testing/test.sh
Normal file
25
1.solar-system/1.welcome/05.the-mover/testing/test.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
mkdir -p cargo/food cargo/water cargo/tools inbox
|
||||
touch cargo/food/manifest.txt cargo/water/manifest.txt cargo/tools/manifest.txt
|
||||
echo "1 apple" > inbox/food-apple.txt
|
||||
echo "1 loaf" > inbox/food-bread.txt
|
||||
echo "500ml" > inbox/water-bottle.txt
|
||||
echo "1L" > inbox/water-canteen.txt
|
||||
echo "10mm" > inbox/tool-wrench.txt
|
||||
echo "claw" > inbox/tool-hammer.txt
|
||||
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
N=0
|
||||
check() {
|
||||
N=$((N+1))
|
||||
if eval "$1"; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi
|
||||
}
|
||||
|
||||
check '[ -f cargo/food/food-apple.txt ]' "food-apple in cargo/food"
|
||||
check '[ -f cargo/food/food-bread.txt ]' "food-bread in cargo/food"
|
||||
check '[ -f cargo/water/water-bottle.txt ]' "water-bottle in cargo/water"
|
||||
check '[ -f cargo/water/water-canteen.txt ]' "water-canteen in cargo/water"
|
||||
check '[ -f cargo/tools/tool-wrench.txt ]' "tool-wrench in cargo/tools"
|
||||
check '[ -f cargo/tools/tool-hammer.txt ]' "tool-hammer in cargo/tools"
|
||||
check '[ -f cargo/food/manifest.backup ]' "cargo/food/manifest.backup exists"
|
||||
39
1.solar-system/1.welcome/06.the-reader/index.md
Normal file
39
1.solar-system/1.welcome/06.the-reader/index.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Reader"
|
||||
xp: 50
|
||||
duration: 30
|
||||
difficulty: 2
|
||||
---
|
||||
|
||||
# The Reader
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, an old mission log was recovered. We need three things from it.
|
||||
>
|
||||
> When your script runs, `mission-log.txt` is sitting in the working
|
||||
> directory. Pull these out:
|
||||
>
|
||||
> 1. The first 5 lines → save to `first-five.txt`
|
||||
> 2. The last 3 lines → save to `last-three.txt`
|
||||
> 3. The total line count → save to `line-count.txt`
|
||||
>
|
||||
> Three commands:
|
||||
>
|
||||
> - `head -n N file` — first N lines
|
||||
> - `tail -n N file` — last N lines
|
||||
> - `wc -l file` — count lines
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`, produce the three output files using `head`,
|
||||
`tail`, `wc`, and redirect.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `first-five.txt` matches the first 5 lines of the log
|
||||
- `last-three.txt` matches the last 3 lines of the log
|
||||
- `line-count.txt` contains the line count
|
||||
13
1.solar-system/1.welcome/06.the-reader/starter/starter.sh
Normal file
13
1.solar-system/1.welcome/06.the-reader/starter/starter.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
# The Reader — pull stats from a log.
|
||||
#
|
||||
# When this script runs, mission-log.txt is in your working directory.
|
||||
# Your script must produce three files:
|
||||
#
|
||||
# - first-five.txt — the first 5 lines of mission-log.txt
|
||||
# - last-three.txt — the last 3 lines of mission-log.txt
|
||||
# - line-count.txt — the line count (output of `wc -l`)
|
||||
#
|
||||
# Tools: head, tail, wc, redirect (>)
|
||||
|
||||
# Your code here.
|
||||
39
1.solar-system/1.welcome/06.the-reader/testing/test.sh
Normal file
39
1.solar-system/1.welcome/06.the-reader/testing/test.sh
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
cat > mission-log.txt <<'LOG'
|
||||
[1969-07-20 13:32] Apollo touchdown confirmed.
|
||||
[1969-07-20 13:33] Surface stable.
|
||||
[1969-07-20 13:34] Cabin sealed.
|
||||
[1969-07-20 13:35] EVA prep started.
|
||||
[1969-07-20 13:40] Hatch open check.
|
||||
[1969-07-20 13:42] Suit pressurization complete.
|
||||
[1969-07-20 13:50] Ladder deployed.
|
||||
[1969-07-20 13:55] First step recorded.
|
||||
[1969-07-20 14:00] Sample collection begun.
|
||||
[1969-07-20 14:10] All systems nominal.
|
||||
[1969-07-20 14:20] Communication test passed.
|
||||
[1969-07-20 14:30] Second sample box sealed.
|
||||
[1969-07-20 14:40] Solar panel deployed.
|
||||
[1969-07-20 14:50] Flag planted.
|
||||
[1969-07-20 15:00] Phone call inbound.
|
||||
[1969-07-20 15:10] Phone call complete.
|
||||
[1969-07-20 15:20] Sample box stowed.
|
||||
[1969-07-20 15:30] EVA wrap-up started.
|
||||
[1969-07-20 15:35] Hatch closed.
|
||||
[1969-07-20 15:40] Cabin re-pressurized.
|
||||
LOG
|
||||
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
N=0
|
||||
report() {
|
||||
N=$((N+1))
|
||||
if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi
|
||||
}
|
||||
|
||||
[ -f first-five.txt ]; report $? "first-five.txt exists"
|
||||
diff -q <(head -n 5 mission-log.txt) first-five.txt > /dev/null 2>&1; report $? "first-five.txt matches head -n 5"
|
||||
[ -f last-three.txt ]; report $? "last-three.txt exists"
|
||||
diff -q <(tail -n 3 mission-log.txt) last-three.txt > /dev/null 2>&1; report $? "last-three.txt matches tail -n 3"
|
||||
[ -f line-count.txt ]; report $? "line-count.txt exists"
|
||||
COUNT=$(awk '{print $1}' line-count.txt 2>/dev/null | tr -d ' ')
|
||||
[ "$COUNT" = "20" ]; report $? "line-count.txt contains 20"
|
||||
48
1.solar-system/1.welcome/07.the-editor/index.md
Normal file
48
1.solar-system/1.welcome/07.the-editor/index.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Editor"
|
||||
xp: 50
|
||||
duration: 25
|
||||
difficulty: 2
|
||||
---
|
||||
|
||||
# The Editor
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, single-line `echo` works for short messages. Real files
|
||||
> need multiple lines. Two options:
|
||||
>
|
||||
> - Multiple `echo` lines, each with its own redirect (use `>>` after
|
||||
> the first one to append)
|
||||
> - A *heredoc* — write a whole block in one go:
|
||||
>
|
||||
> ```bash
|
||||
> cat > journal.md <<EOF
|
||||
> line one
|
||||
> line two
|
||||
> EOF
|
||||
> ```
|
||||
>
|
||||
> Write a script that creates `journal.md` containing exactly:
|
||||
>
|
||||
> ```
|
||||
> # Cadet Log — Day 1
|
||||
>
|
||||
> Today I learned to navigate the shell.
|
||||
> Tomorrow I will write code.
|
||||
> ```
|
||||
>
|
||||
> Mind the empty line between the header and the body.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`, produce `journal.md` with the exact content
|
||||
above.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `journal.md` exists
|
||||
- Contents match the template exactly (4 lines + the blank line)
|
||||
19
1.solar-system/1.welcome/07.the-editor/starter/starter.sh
Normal file
19
1.solar-system/1.welcome/07.the-editor/starter/starter.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
# The Editor — write a multi-line journal entry.
|
||||
#
|
||||
# Your script must create a file `journal.md` containing exactly:
|
||||
#
|
||||
# # Cadet Log — Day 1
|
||||
#
|
||||
# Today I learned to navigate the shell.
|
||||
# Tomorrow I will write code.
|
||||
#
|
||||
# Note the empty line between the header and the body.
|
||||
#
|
||||
# A heredoc writes multiple lines at once:
|
||||
#
|
||||
# cat > journal.md <<EOF
|
||||
# ...lines here...
|
||||
# EOF
|
||||
|
||||
# Your code here.
|
||||
19
1.solar-system/1.welcome/07.the-editor/testing/test.sh
Normal file
19
1.solar-system/1.welcome/07.the-editor/testing/test.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
EXPECTED=$(cat <<'EXP'
|
||||
# Cadet Log — Day 1
|
||||
|
||||
Today I learned to navigate the shell.
|
||||
Tomorrow I will write code.
|
||||
EXP
|
||||
)
|
||||
|
||||
N=0
|
||||
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
|
||||
|
||||
[ -f journal.md ]; report $? "journal.md exists"
|
||||
|
||||
ACTUAL=$(cat journal.md 2>/dev/null)
|
||||
ACTUAL="${ACTUAL%$'\n'}"
|
||||
[ "$ACTUAL" = "$EXPECTED" ]; report $? "journal.md matches the template exactly"
|
||||
36
1.solar-system/1.welcome/08.the-searcher/index.md
Normal file
36
1.solar-system/1.welcome/08.the-searcher/index.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Searcher"
|
||||
xp: 75
|
||||
duration: 30
|
||||
difficulty: 3
|
||||
---
|
||||
|
||||
# The Searcher
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, an archive arrived. Somewhere in it, a single log file
|
||||
> mentions the word `BREACH`. We need it found.
|
||||
>
|
||||
> Two commands:
|
||||
>
|
||||
> - `find <path> -name "<pattern>"` — locate files by name
|
||||
> - `grep -rl "<text>" <path>` — search file contents recursively,
|
||||
> list only the matching file paths
|
||||
>
|
||||
> Your script must produce two files:
|
||||
>
|
||||
> 1. `logs.txt` — every `.log` file under `archive/`
|
||||
> 2. `breach.txt` — the path to the file containing `BREACH`
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`, write the two commands.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `logs.txt` lists every `.log` file under `archive/`
|
||||
- `breach.txt` contains the path to the file mentioning `BREACH`
|
||||
13
1.solar-system/1.welcome/08.the-searcher/starter/starter.sh
Normal file
13
1.solar-system/1.welcome/08.the-searcher/starter/starter.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
# The Searcher — find things in an archive.
|
||||
#
|
||||
# When this script runs, an `archive/` directory tree exists with
|
||||
# many files. Somewhere in it, a single .log file mentions BREACH.
|
||||
#
|
||||
# Your script must produce two files:
|
||||
# - logs.txt — every `.log` file path under archive/, one per line
|
||||
# - breach.txt — the path to the file containing the word BREACH
|
||||
#
|
||||
# Tools: find, grep -rl
|
||||
|
||||
# Your code here.
|
||||
22
1.solar-system/1.welcome/08.the-searcher/testing/test.sh
Normal file
22
1.solar-system/1.welcome/08.the-searcher/testing/test.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
mkdir -p archive/sectors/a archive/sectors/b archive/sectors/c archive/data archive/old
|
||||
echo "[03:30] Sector A nominal." > archive/sectors/a/sector-a.log
|
||||
echo "[03:32] Sector B nominal." > archive/sectors/b/sector-b.log
|
||||
printf '[03:30] Sector C nominal.\n[03:42] BREACH detected at perimeter.\n' > archive/sectors/c/sector-c.log
|
||||
echo "[00:00] Logger online." > archive/data/raw.log
|
||||
echo "Notes" > archive/data/notes.txt
|
||||
echo "[01:00] Legacy." > archive/old/old.log
|
||||
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
N=0
|
||||
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
|
||||
|
||||
[ -f logs.txt ]; report $? "logs.txt exists"
|
||||
|
||||
EXPECTED=$(find archive -name "*.log" | sort)
|
||||
ACTUAL=$(sort logs.txt 2>/dev/null)
|
||||
[ "$EXPECTED" = "$ACTUAL" ]; report $? "logs.txt lists all .log files"
|
||||
|
||||
[ -f breach.txt ]; report $? "breach.txt exists"
|
||||
grep -q "sector-c.log" breach.txt 2>/dev/null; report $? "breach.txt references sector-c.log"
|
||||
45
1.solar-system/1.welcome/09.the-scripter/index.md
Normal file
45
1.solar-system/1.welcome/09.the-scripter/index.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Scripter"
|
||||
xp: 100
|
||||
duration: 35
|
||||
difficulty: 3
|
||||
---
|
||||
|
||||
# The Scripter
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, time to combine commands. Bash lets you embed the output of
|
||||
> one command inside another using `$(...)` — *command substitution*:
|
||||
>
|
||||
> ```bash
|
||||
> echo "Today is $(date '+%Y-%m-%d')"
|
||||
> # → Today is 2026-05-04
|
||||
> ```
|
||||
>
|
||||
> When your script runs, a directory `supplies/` exists with several
|
||||
> files. Produce `report.txt` containing exactly two lines:
|
||||
>
|
||||
> ```
|
||||
> Date: <today in YYYY-MM-DD format>
|
||||
> Files in supplies: <count of entries in supplies/>
|
||||
> ```
|
||||
>
|
||||
> Two helpers:
|
||||
>
|
||||
> - `date '+%Y-%m-%d'` — today in the right format
|
||||
> - `ls supplies | wc -l | tr -d ' '` — entry count, whitespace stripped
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`, use `echo` + `$(...)` substitution to
|
||||
produce the two-line report.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `report.txt` exists
|
||||
- Line 1 matches `Date: <today>`
|
||||
- Line 2 matches `Files in supplies: <count>`
|
||||
17
1.solar-system/1.welcome/09.the-scripter/starter/starter.sh
Normal file
17
1.solar-system/1.welcome/09.the-scripter/starter/starter.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
# The Scripter — combine commands into a script.
|
||||
#
|
||||
# When this script runs, a directory `supplies/` exists with several
|
||||
# files. Your script must produce `report.txt` containing exactly two
|
||||
# lines:
|
||||
#
|
||||
# Date: <today, in YYYY-MM-DD format>
|
||||
# Files in supplies: <number of entries in supplies/>
|
||||
#
|
||||
# Use command substitution `$(...)` to embed command output inside
|
||||
# strings:
|
||||
#
|
||||
# $(date '+%Y-%m-%d')
|
||||
# $(ls supplies | wc -l | tr -d ' ')
|
||||
|
||||
# Your code here.
|
||||
21
1.solar-system/1.welcome/09.the-scripter/testing/test.sh
Normal file
21
1.solar-system/1.welcome/09.the-scripter/testing/test.sh
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
mkdir -p supplies
|
||||
touch supplies/oxygen-tank.txt \
|
||||
supplies/ration-pack.txt \
|
||||
supplies/medkit.txt \
|
||||
supplies/repair-kit.txt \
|
||||
supplies/comms-unit.txt
|
||||
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
N=0
|
||||
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
|
||||
|
||||
[ -f report.txt ]; report $? "report.txt exists"
|
||||
|
||||
TODAY=$(date '+%Y-%m-%d')
|
||||
LINE1=$(sed -n '1p' report.txt 2>/dev/null)
|
||||
LINE2=$(sed -n '2p' report.txt 2>/dev/null)
|
||||
|
||||
[ "$LINE1" = "Date: $TODAY" ]; report $? "line 1 is 'Date: $TODAY'"
|
||||
[ "$LINE2" = "Files in supplies: 5" ]; report $? "line 2 is 'Files in supplies: 5'"
|
||||
89
1.solar-system/1.welcome/10.how-to-learn/index.md
Normal file
89
1.solar-system/1.welcome/10.how-to-learn/index.md
Normal file
@@ -0,0 +1,89 @@
|
||||
---
|
||||
type: story
|
||||
title: "How to Learn Here"
|
||||
xp: 25
|
||||
duration: 25
|
||||
difficulty: 1
|
||||
---
|
||||
|
||||
# How to Learn Here
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, take a breath. You've spent the morning in the shell.
|
||||
> Before we take you to Git, we need you to know how learning actually
|
||||
> works in this program.
|
||||
>
|
||||
> Cadets who finish the program share three habits. Cadets who quit
|
||||
> share three different ones. We're going to tell you which is which.
|
||||
>
|
||||
> [TRANSMISSION CONTINUES]
|
||||
|
||||
## Rule 1 — Understand Every Line
|
||||
|
||||
You will use AI here. Claude, ChatGPT, our onboard NAV-7 — whatever
|
||||
you reach for. We don't forbid it. We *expect* it. The world you're
|
||||
training to enter assumes AI fluency.
|
||||
|
||||
But there is one rule that does not bend:
|
||||
|
||||
**You must be able to explain every line of code you submit.**
|
||||
|
||||
If AI writes a function and you can't say what each line does and why,
|
||||
you have not learned. You've outsourced thinking. The checkpoint will
|
||||
catch the gap. The first real project will catch it harder. Use AI as
|
||||
a tutor and a sparring partner — never as a ghostwriter.
|
||||
|
||||
## Rule 2 — Help the Cadet Next to You
|
||||
|
||||
This is a peer-learning environment. There are no professors here.
|
||||
There are people who landed last month, last week, and yesterday — and
|
||||
right now, you.
|
||||
|
||||
Two things follow from that:
|
||||
|
||||
- **When you understand something, teach it.** Explaining a concept to
|
||||
someone else cements it in your own head better than reading it twice.
|
||||
- **When you don't understand something, ask.** Your peers solved this
|
||||
exact problem hours, days, or weeks ago. Their explanation will
|
||||
usually beat ours.
|
||||
|
||||
The cohort is the curriculum. Treat it that way and the program runs
|
||||
on rails. Ignore it and you'll fall behind alone.
|
||||
|
||||
## Rule 3 — Struggle First, for Fifteen Minutes
|
||||
|
||||
When you get stuck — and you will, every day — the instinct is to ask
|
||||
right away. Resist it.
|
||||
|
||||
For fifteen minutes, struggle. Re-read the briefing. Read the error
|
||||
message slowly. Try one thing. Try a second thing. Notice what you
|
||||
expected versus what actually happened.
|
||||
|
||||
Most of your real learning happens in those fifteen minutes. Skip them
|
||||
and you don't just miss the answer — you miss the *thinking*. After
|
||||
fifteen, ask. Not before.
|
||||
|
||||
## Asking a Good Question
|
||||
|
||||
When you do ask — peer, AI, or staff — three things make a good
|
||||
question:
|
||||
|
||||
1. **Context.** What were you trying to do?
|
||||
2. **What you tried.** What approach did you take? What error did you
|
||||
see?
|
||||
3. **The exact error.** Copy it. Don't summarize it.
|
||||
|
||||
A good question gets answered in ten minutes. A bad question gets
|
||||
ignored for an hour, or worse, gets the wrong answer because nobody
|
||||
understood what you meant.
|
||||
|
||||
> **[CLOSING — Mission Control]**
|
||||
>
|
||||
> Two days in, you'll have habits. Make sure they're the right ones.
|
||||
>
|
||||
> Six Git challenges follow. Then a paired battle. Then Python.
|
||||
>
|
||||
> Keep going.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
38
1.solar-system/1.welcome/11.the-identity/index.md
Normal file
38
1.solar-system/1.welcome/11.the-identity/index.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Identity"
|
||||
xp: 50
|
||||
duration: 15
|
||||
difficulty: 2
|
||||
---
|
||||
|
||||
# The Identity
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, before Git records any of your work, it needs to know who's
|
||||
> making the changes. Every commit you'll ever make will carry your
|
||||
> name and email.
|
||||
>
|
||||
> When your script runs, a fresh Git repository already exists in the
|
||||
> current directory. Set the local identity:
|
||||
>
|
||||
> ```bash
|
||||
> git config user.name "Your Name"
|
||||
> git config user.email "you@example.com"
|
||||
> ```
|
||||
>
|
||||
> Without `--global`, these settings only apply to this one repo —
|
||||
> exactly what we want for a contained challenge.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`, set `user.name` and `user.email` on the
|
||||
current Git repo.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `user.name` is set on the repo (non-empty)
|
||||
- `user.email` is set on the repo and looks like an email
|
||||
12
1.solar-system/1.welcome/11.the-identity/starter/starter.sh
Normal file
12
1.solar-system/1.welcome/11.the-identity/starter/starter.sh
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
# The Identity — tell Git who you are.
|
||||
#
|
||||
# When this script runs, a fresh Git repo is already initialized in
|
||||
# the current directory. Your script must set both:
|
||||
#
|
||||
# - user.name (any non-empty string)
|
||||
# - user.email (must contain @ and a dot)
|
||||
#
|
||||
# Tools: git config <key> "<value>"
|
||||
|
||||
# Your code here.
|
||||
13
1.solar-system/1.welcome/11.the-identity/testing/test.sh
Normal file
13
1.solar-system/1.welcome/11.the-identity/testing/test.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
git init -q -b main
|
||||
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
N=0
|
||||
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
|
||||
|
||||
NAME=$(git config --local --get user.name 2>/dev/null)
|
||||
EMAIL=$(git config --local --get user.email 2>/dev/null)
|
||||
|
||||
[ -n "$NAME" ]; report $? "user.name is set on the repo"
|
||||
[[ "$EMAIL" == *@*.* ]]; report $? "user.email is set and looks like an email"
|
||||
43
1.solar-system/1.welcome/12.the-initiator/index.md
Normal file
43
1.solar-system/1.welcome/12.the-initiator/index.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Initiator"
|
||||
xp: 50
|
||||
duration: 20
|
||||
difficulty: 2
|
||||
---
|
||||
|
||||
# The Initiator
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, ground zero. Take an empty directory and turn it into a
|
||||
> Git project.
|
||||
>
|
||||
> Two commands today:
|
||||
>
|
||||
> - `git init` — turns the current directory into a Git repository
|
||||
> (creates a hidden `.git/` folder)
|
||||
> - `git status` — shows what Git sees: branch, tracked, untracked
|
||||
>
|
||||
> Your script must:
|
||||
>
|
||||
> 1. Run `git init` (use `-b main` to set the default branch)
|
||||
> 2. Capture `git status` to `status-before.txt`
|
||||
> 3. Create `intro.md` containing `Hello, Git`
|
||||
> 4. Capture `git status` to `status-after.txt`
|
||||
>
|
||||
> Notice the second status sees the new file as *untracked* — that
|
||||
> distinction is the heart of how Git works.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`, write the four steps above.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `.git/` exists
|
||||
- `intro.md` contains `Hello, Git`
|
||||
- `status-before.txt` looks like git status output
|
||||
- `status-after.txt` mentions `intro.md`
|
||||
14
1.solar-system/1.welcome/12.the-initiator/starter/starter.sh
Normal file
14
1.solar-system/1.welcome/12.the-initiator/starter/starter.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
# The Initiator — turn an empty directory into a Git repo.
|
||||
#
|
||||
# Your script must:
|
||||
# 1. Initialize a Git repo (use main as the default branch)
|
||||
# 2. Capture `git status` to status-before.txt
|
||||
# 3. Create intro.md containing exactly "Hello, Git"
|
||||
# 4. Capture `git status` to status-after.txt
|
||||
#
|
||||
# Notice how the second status sees intro.md as untracked.
|
||||
#
|
||||
# Tools: git init, git status, echo
|
||||
|
||||
# Your code here.
|
||||
13
1.solar-system/1.welcome/12.the-initiator/testing/test.sh
Normal file
13
1.solar-system/1.welcome/12.the-initiator/testing/test.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
N=0
|
||||
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
|
||||
|
||||
[ -d .git ]; report $? ".git/ exists"
|
||||
[ -f intro.md ]; report $? "intro.md exists"
|
||||
grep -q "Hello, Git" intro.md 2>/dev/null; report $? "intro.md contains 'Hello, Git'"
|
||||
[ -f status-before.txt ]; report $? "status-before.txt exists"
|
||||
grep -qi "branch" status-before.txt 2>/dev/null; report $? "status-before.txt looks like git status output"
|
||||
[ -f status-after.txt ]; report $? "status-after.txt exists"
|
||||
grep -q "intro.md" status-after.txt 2>/dev/null; report $? "status-after.txt mentions intro.md"
|
||||
41
1.solar-system/1.welcome/13.the-saver/index.md
Normal file
41
1.solar-system/1.welcome/13.the-saver/index.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Saver"
|
||||
xp: 75
|
||||
duration: 25
|
||||
difficulty: 2
|
||||
---
|
||||
|
||||
# The Saver
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, snapshots. Git's whole purpose is to capture *commits* —
|
||||
> snapshots of your work — that you can return to later.
|
||||
>
|
||||
> The flow per commit:
|
||||
>
|
||||
> 1. Create or modify a file
|
||||
> 2. `git add <file>` — stage it
|
||||
> 3. `git commit -m "<message>"` — lock it in
|
||||
>
|
||||
> When your script runs, a clean Git repo with identity already set
|
||||
> sits in the working directory. Make three commits, in this exact
|
||||
> order:
|
||||
>
|
||||
> 1. Create `a.txt` with `alpha`. Commit message: `add alpha`
|
||||
> 2. Create `b.txt` with `beta`. Commit message: `add beta`
|
||||
> 3. Create `c.txt` with `gamma`. Commit message: `add gamma`
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`, run six commands per the flow above (one
|
||||
echo + add + commit per file).
|
||||
|
||||
## Objectives
|
||||
|
||||
- `a.txt`, `b.txt`, `c.txt` exist with `alpha`, `beta`, `gamma`
|
||||
- `git log` shows exactly 3 commits
|
||||
- Commit messages: `add alpha`, `add beta`, `add gamma` in order
|
||||
14
1.solar-system/1.welcome/13.the-saver/starter/starter.sh
Normal file
14
1.solar-system/1.welcome/13.the-saver/starter/starter.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
# The Saver — make three commits.
|
||||
#
|
||||
# When this script runs, a fresh Git repo with identity already set
|
||||
# is in your working directory. Make three commits, in this exact
|
||||
# order:
|
||||
#
|
||||
# 1. Create a.txt containing "alpha". Commit message: "add alpha"
|
||||
# 2. Create b.txt containing "beta". Commit message: "add beta"
|
||||
# 3. Create c.txt containing "gamma". Commit message: "add gamma"
|
||||
#
|
||||
# Tools: echo, git add, git commit -m
|
||||
|
||||
# Your code here.
|
||||
20
1.solar-system/1.welcome/13.the-saver/testing/test.sh
Normal file
20
1.solar-system/1.welcome/13.the-saver/testing/test.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
git init -q -b main
|
||||
git config user.name "Setup"
|
||||
git config user.email "setup@learnroom.local"
|
||||
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
N=0
|
||||
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
|
||||
|
||||
{ [ -f a.txt ] && grep -q alpha a.txt 2>/dev/null; }; report $? "a.txt exists with 'alpha'"
|
||||
{ [ -f b.txt ] && grep -q beta b.txt 2>/dev/null; }; report $? "b.txt exists with 'beta'"
|
||||
{ [ -f c.txt ] && grep -q gamma c.txt 2>/dev/null; }; report $? "c.txt exists with 'gamma'"
|
||||
|
||||
COUNT=$(git log --oneline 2>/dev/null | wc -l | tr -d ' ')
|
||||
[ "$COUNT" = "3" ]; report $? "exactly 3 commits in git log"
|
||||
|
||||
MSGS=$(git log --reverse --format='%s' 2>/dev/null)
|
||||
EXPECTED=$'add alpha\nadd beta\nadd gamma'
|
||||
[ "$MSGS" = "$EXPECTED" ]; report $? "commit messages: 'add alpha', 'add beta', 'add gamma' in order"
|
||||
40
1.solar-system/1.welcome/14.the-historian/index.md
Normal file
40
1.solar-system/1.welcome/14.the-historian/index.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Historian"
|
||||
xp: 75
|
||||
duration: 25
|
||||
difficulty: 3
|
||||
---
|
||||
|
||||
# The Historian
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, the repo in your working directory contains five commits
|
||||
> from a previous mission. We need a historian's eye on it.
|
||||
>
|
||||
> Two commands today:
|
||||
>
|
||||
> - `git log --oneline` — compact one-line-per-commit history
|
||||
> - `git show <hash>` — show what a commit changed
|
||||
>
|
||||
> Your script must produce two files:
|
||||
>
|
||||
> 1. `log.txt` — full one-line log
|
||||
> 2. `breach-commit.txt` — short hash of the commit whose message
|
||||
> mentions `BREACH`
|
||||
>
|
||||
> Hint: pipe the log through `grep BREACH | awk '{print $1}'` to
|
||||
> extract the short hash from the matching line.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`, capture the full log and the breach commit's
|
||||
short hash.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `log.txt` lists all 5 commits, one per line
|
||||
- `breach-commit.txt` contains the short hash of the BREACH commit
|
||||
15
1.solar-system/1.welcome/14.the-historian/starter/starter.sh
Normal file
15
1.solar-system/1.welcome/14.the-historian/starter/starter.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# The Historian — read git history.
|
||||
#
|
||||
# When this script runs, the current Git repo already has 5 commits
|
||||
# from a prior mission. One of them mentions BREACH.
|
||||
#
|
||||
# Your script must produce two files:
|
||||
# - log.txt — `git log --oneline` output (one line per commit)
|
||||
# - breach-commit.txt — the short hash of the BREACH commit
|
||||
#
|
||||
# Hint: `git log --oneline | grep BREACH | awk '{print $1}'`
|
||||
# pipes the matching log line and prints the leftmost column (the
|
||||
# short hash).
|
||||
|
||||
# Your code here.
|
||||
24
1.solar-system/1.welcome/14.the-historian/testing/test.sh
Normal file
24
1.solar-system/1.welcome/14.the-historian/testing/test.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
git init -q -b main
|
||||
git config user.name "Setup"
|
||||
git config user.email "setup@learnroom.local"
|
||||
|
||||
echo "manifest" > manifest.txt; git add manifest.txt; git commit -q -m "add manifest"
|
||||
echo "sensor a online" > sensor-a.txt; git add sensor-a.txt; git commit -q -m "add sensor A"
|
||||
echo "breach details" > breach.txt; git add breach.txt; git commit -q -m "investigate BREACH at sector C"
|
||||
echo "sensor b online" > sensor-b.txt; git add sensor-b.txt; git commit -q -m "add sensor B"
|
||||
echo "resolution applied" > resolution.txt; git add resolution.txt; git commit -q -m "apply resolution"
|
||||
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
N=0
|
||||
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
|
||||
|
||||
[ -f log.txt ]; report $? "log.txt exists"
|
||||
LINES=$(wc -l < log.txt | tr -d ' ')
|
||||
[ "$LINES" = "5" ]; report $? "log.txt has 5 lines (one per commit)"
|
||||
|
||||
[ -f breach-commit.txt ]; report $? "breach-commit.txt exists"
|
||||
ACTUAL=$(tr -d '[:space:]' < breach-commit.txt 2>/dev/null)
|
||||
EXPECTED=$(git log --grep BREACH --format=%h)
|
||||
[ "$ACTUAL" = "$EXPECTED" ]; report $? "breach-commit.txt contains the BREACH commit's short hash"
|
||||
45
1.solar-system/1.welcome/15.the-cloner/index.md
Normal file
45
1.solar-system/1.welcome/15.the-cloner/index.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Cloner"
|
||||
xp: 75
|
||||
duration: 25
|
||||
difficulty: 3
|
||||
---
|
||||
|
||||
# The Cloner
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, real work doesn't start from scratch. You join an existing
|
||||
> project — clone it, make changes, commit.
|
||||
>
|
||||
> When your script runs, `remote.git` is a *bare repository* (think
|
||||
> of it as a project living on a server) sitting in your working
|
||||
> directory. Clone it into a folder called `project`:
|
||||
>
|
||||
> ```bash
|
||||
> git clone remote.git project
|
||||
> ```
|
||||
>
|
||||
> Then inside it, set your identity (Git won't let you commit
|
||||
> without one), create `log-entry.txt` containing exactly
|
||||
> `Cadet log entry — checked in.`, and commit with message
|
||||
> `add cadet log entry`.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`:
|
||||
|
||||
1. Clone `remote.git` into `project`
|
||||
2. `cd project`
|
||||
3. Set `user.name` and `user.email`
|
||||
4. Create `log-entry.txt` with the exact line above
|
||||
5. Add and commit with the exact message
|
||||
|
||||
## Objectives
|
||||
|
||||
- `project/.git` exists
|
||||
- `project/log-entry.txt` matches the exact content
|
||||
- `project/` has at least 2 commits in its log
|
||||
15
1.solar-system/1.welcome/15.the-cloner/starter/starter.sh
Normal file
15
1.solar-system/1.welcome/15.the-cloner/starter/starter.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# The Cloner — clone a remote and add a commit.
|
||||
#
|
||||
# When this script runs, a bare repository `remote.git` is in your
|
||||
# working directory. Your script must:
|
||||
#
|
||||
# 1. Clone remote.git into a folder called `project`
|
||||
# 2. Inside project/, set user.name and user.email
|
||||
# 3. Create log-entry.txt containing exactly:
|
||||
# Cadet log entry — checked in.
|
||||
# 4. Stage and commit it with message: "add cadet log entry"
|
||||
#
|
||||
# Tools: git clone, cd, git config, echo, git add, git commit -m
|
||||
|
||||
# Your code here.
|
||||
29
1.solar-system/1.welcome/15.the-cloner/testing/test.sh
Normal file
29
1.solar-system/1.welcome/15.the-cloner/testing/test.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
TMP=$(mktemp -d)
|
||||
(
|
||||
cd "$TMP"
|
||||
git init -q -b main
|
||||
git config user.name "Setup"
|
||||
git config user.email "setup@learnroom.local"
|
||||
echo "# Mission Project" > README.md
|
||||
echo "Initial mission setup." > mission.txt
|
||||
git add .
|
||||
git commit -q -m "initial mission setup"
|
||||
)
|
||||
git clone -q --bare "$TMP" remote.git
|
||||
rm -rf "$TMP"
|
||||
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
N=0
|
||||
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
|
||||
|
||||
[ -d project/.git ]; report $? "project/.git exists (clone happened)"
|
||||
[ -f project/log-entry.txt ]; report $? "project/log-entry.txt exists"
|
||||
ACTUAL=$(cat project/log-entry.txt 2>/dev/null)
|
||||
ACTUAL="${ACTUAL%$'\n'}"
|
||||
[ "$ACTUAL" = "Cadet log entry — checked in." ]; report $? "log-entry.txt contains the exact line"
|
||||
|
||||
COUNT=$(git -C project log --oneline 2>/dev/null | wc -l | tr -d ' ')
|
||||
[ "$COUNT" -ge 2 ] 2>/dev/null
|
||||
report $? "project has at least 2 commits"
|
||||
43
1.solar-system/1.welcome/16.the-pusher/index.md
Normal file
43
1.solar-system/1.welcome/16.the-pusher/index.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Pusher"
|
||||
xp: 100
|
||||
duration: 30
|
||||
difficulty: 3
|
||||
---
|
||||
|
||||
# The Pusher
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, your commits live only on your machine until you *push* them
|
||||
> somewhere shared. That's how teams work.
|
||||
>
|
||||
> When your script runs, you'll find:
|
||||
>
|
||||
> - `local-repo/` — a working repo with two commits already, identity set
|
||||
> - `remote.git/` — an empty bare repo (your shared destination)
|
||||
>
|
||||
> Two commands:
|
||||
>
|
||||
> - `git remote add <name> <path>` — register a remote and give it a
|
||||
> short name (by convention, `origin`)
|
||||
> - `git push -u <remote> <branch>` — send your branch's commits to that
|
||||
> remote (`-u` sets it as default for next time)
|
||||
>
|
||||
> Wire `local-repo` to `remote.git` and push.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.sh`:
|
||||
|
||||
1. `cd local-repo`
|
||||
2. Add `../remote.git` as a remote named `origin`
|
||||
3. Push `main` to `origin` with `-u`
|
||||
|
||||
## Objectives
|
||||
|
||||
- `local-repo` has a remote `origin` configured
|
||||
- `remote.git` has the same commits on `main` as `local-repo`
|
||||
15
1.solar-system/1.welcome/16.the-pusher/starter/starter.sh
Normal file
15
1.solar-system/1.welcome/16.the-pusher/starter/starter.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# The Pusher — send your commits to a remote.
|
||||
#
|
||||
# When this script runs:
|
||||
# - local-repo/ is a working repo with commits + identity already set
|
||||
# - remote.git/ is a bare empty repo waiting for your push
|
||||
#
|
||||
# Your script must:
|
||||
# 1. Inside local-repo, register remote.git as a remote named `origin`
|
||||
# pointing at ../remote.git
|
||||
# 2. Push the main branch to origin (use -u to set upstream)
|
||||
#
|
||||
# Tools: cd, git remote add, git push
|
||||
|
||||
# Your code here.
|
||||
27
1.solar-system/1.welcome/16.the-pusher/testing/test.sh
Normal file
27
1.solar-system/1.welcome/16.the-pusher/testing/test.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
mkdir -p local-repo
|
||||
(
|
||||
cd local-repo
|
||||
git init -q -b main
|
||||
git config user.name "Cadet Vega"
|
||||
git config user.email "cadet.vega@learnroom.local"
|
||||
echo "# Mission" > README.md
|
||||
git add README.md; git commit -q -m "initial commit"
|
||||
echo "Day 1 log" > log.txt
|
||||
git add log.txt; git commit -q -m "add log"
|
||||
)
|
||||
git init -q --bare remote.git
|
||||
|
||||
bash solution.sh > /dev/null 2>&1
|
||||
|
||||
N=0
|
||||
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
|
||||
|
||||
ORIGIN_URL=$(git -C local-repo remote get-url origin 2>/dev/null)
|
||||
[ -n "$ORIGIN_URL" ]; report $? "'origin' remote configured on local-repo"
|
||||
|
||||
LOCAL_COUNT=$(git -C local-repo log --oneline main 2>/dev/null | wc -l | tr -d ' ')
|
||||
REMOTE_COUNT=$(git -C remote.git log --oneline main 2>/dev/null | wc -l | tr -d ' ')
|
||||
|
||||
[ -n "$REMOTE_COUNT" ] && [ "$REMOTE_COUNT" != "0" ]; report $? "remote.git has commits on main"
|
||||
[ "$LOCAL_COUNT" = "$REMOTE_COUNT" ]; report $? "remote.git has same number of commits as local-repo"
|
||||
63
1.solar-system/1.welcome/17.team-build/index.md
Normal file
63
1.solar-system/1.welcome/17.team-build/index.md
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
type: battle
|
||||
title: "Team Build"
|
||||
xp: 200
|
||||
duration: 90
|
||||
difficulty: 3
|
||||
---
|
||||
|
||||
# Team Build
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadets, this is your final task before Python. You and a partner will
|
||||
> automate a mission setup together — one writing, the other
|
||||
> reviewing. Then swap.
|
||||
>
|
||||
> Pair up. Decide who goes first as **defender** and who goes first as
|
||||
> **attacker**.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## The Task
|
||||
|
||||
The **defender** writes a single shell script — `launch.sh` — that,
|
||||
when run inside an empty workspace, does the following in order:
|
||||
|
||||
1. Creates a folder called `mission/` and enters it.
|
||||
2. Initializes a fresh Git repo on branch `main`.
|
||||
3. Sets `user.name` and `user.email` on the repo (use your own).
|
||||
4. Creates `manifest.txt` containing exactly:
|
||||
```
|
||||
Mission Apollo
|
||||
```
|
||||
5. Creates `crew.txt` containing exactly:
|
||||
```
|
||||
Cadet A, Cadet B
|
||||
```
|
||||
6. Creates `coords.txt` containing exactly:
|
||||
```
|
||||
lat: 0.0, lon: 0.0
|
||||
```
|
||||
7. Commits each file separately, in this exact order, with messages:
|
||||
- `add manifest`
|
||||
- `add crew`
|
||||
- `add coords`
|
||||
8. Prints `MISSION READY` to stdout when done.
|
||||
|
||||
## Battle Rules
|
||||
|
||||
- **Defender**: writes `launch.sh` from scratch in a clean workspace.
|
||||
Does not show the file until done. Time limit: 30 minutes.
|
||||
- **Attacker**: receives the script, runs it in a clean folder,
|
||||
inspects the resulting `mission/`, and submits a review through the
|
||||
platform's review form.
|
||||
- **Then swap**: roles flip. The new defender writes their own
|
||||
`launch.sh` (no copy-pasting). 30 more minutes.
|
||||
- Both pass the battle if both scripts pass review.
|
||||
|
||||
## Tools You'll Use
|
||||
|
||||
Everything you've learned this module:
|
||||
`mkdir`, `cd`, `echo`, `>`, `git init`, `git config`, `git add`,
|
||||
`git commit`, and a shebang to make the script run.
|
||||
70
1.solar-system/1.welcome/17.team-build/review/general.md
Normal file
70
1.solar-system/1.welcome/17.team-build/review/general.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Team Build — Extended Notes
|
||||
|
||||
*Not rendered in the platform yet. Reference for instructors and for
|
||||
future "learn more" surfaces.*
|
||||
|
||||
## Purpose
|
||||
|
||||
The capstone of `1.welcome`. By this point a cadet has used every
|
||||
tool they need: `mkdir`, `echo`, `git init`, `git config`, `git add`,
|
||||
`git commit`, and shell scripting with `chmod +x`. This battle forces
|
||||
them to combine all of it into a single artifact — a working
|
||||
automation script — and to read someone else's script for the first
|
||||
time.
|
||||
|
||||
## Skills Demonstrated
|
||||
|
||||
- Composing learned commands into a sequential program
|
||||
- Writing a script with a correct shebang and execute bit
|
||||
- Producing exact output from automation
|
||||
- Reading another cadet's code well enough to score it
|
||||
- Giving and receiving honest feedback under a time limit
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **One commit covering all three files** — defenders forget that the
|
||||
spec says *each file commits separately*. Three commits, not one.
|
||||
- **File contents with extra whitespace** — `echo` adds a trailing
|
||||
newline by default, which is fine; trailing spaces on the line are
|
||||
not. Reviewers should `cat -A` to spot invisible characters.
|
||||
- **Missing or wrong identity** — `git commit` will succeed even
|
||||
without `user.name` set if a fallback is configured globally;
|
||||
reviewers should check `git -C mission config user.name` directly.
|
||||
- **Hardcoded absolute paths** — `cd /Users/...` breaks the moment
|
||||
the script runs on someone else's machine. Use relative paths.
|
||||
- **`MISSION READY` printed inside another message** — the spec says
|
||||
"exactly". `echo "Done. MISSION READY now."` should fail review.
|
||||
- **Forgetting `chmod +x`** — script runs fine via `bash launch.sh`
|
||||
but fails when executed directly. Reviewers should test both.
|
||||
|
||||
## Discussion Prompts (post-battle)
|
||||
|
||||
Ask the pair to discuss for 5 minutes after both rounds:
|
||||
|
||||
1. Whose script was easier to read? Why?
|
||||
2. Was anything in the spec ambiguous? How would you tighten it?
|
||||
3. What would you do differently if you wrote this from scratch
|
||||
today?
|
||||
4. Did you find a bug your partner missed?
|
||||
|
||||
## Alternative Approaches
|
||||
|
||||
A cadet who's seen heredocs might write:
|
||||
|
||||
```bash
|
||||
cat > manifest.txt <<EOF
|
||||
Mission Apollo
|
||||
EOF
|
||||
```
|
||||
|
||||
Both `echo "..." > file` and heredoc are acceptable. The spec
|
||||
doesn't require a specific style — only that the resulting file
|
||||
content match exactly.
|
||||
|
||||
## Why This Is the Last Block of Welcome
|
||||
|
||||
This is the bridge between mechanical skill (typing commands) and
|
||||
real engineering (composing them, reading others' code, judging
|
||||
quality). Cadets who pass this battle have proven they can act as
|
||||
both author and reviewer — the foundational loop of every
|
||||
collaborative codebase. Python is built on this foundation.
|
||||
38
1.solar-system/1.welcome/17.team-build/review/review.json
Normal file
38
1.solar-system/1.welcome/17.team-build/review/review.json
Normal file
@@ -0,0 +1,38 @@
|
||||
[
|
||||
{
|
||||
"title": "Script Quality",
|
||||
"icon": "ph:code-duotone",
|
||||
"items": [
|
||||
"Script starts with #!/bin/bash shebang",
|
||||
"Script is executable (chmod +x applied)",
|
||||
"Script runs without errors",
|
||||
"Prints exactly 'MISSION READY' at the end"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Repo State",
|
||||
"icon": "ph:git-branch-duotone",
|
||||
"items": [
|
||||
"mission/.git/ exists after running the script",
|
||||
"user.name is set on the mission repo",
|
||||
"user.email is set and looks like an email"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Files",
|
||||
"icon": "ph:files-duotone",
|
||||
"items": [
|
||||
"manifest.txt contains exactly 'Mission Apollo'",
|
||||
"crew.txt contains exactly 'Cadet A, Cadet B'",
|
||||
"coords.txt contains exactly 'lat: 0.0, lon: 0.0'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Commits",
|
||||
"icon": "ph:list-duotone",
|
||||
"items": [
|
||||
"Three separate commits exist (not one combined commit)",
|
||||
"Commit messages, in order: 'add manifest', 'add crew', 'add coords'"
|
||||
]
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user