diff --git a/cs/Cargo.lock b/cs/Cargo.lock index a4b5288..50073b0 100644 --- a/cs/Cargo.lock +++ b/cs/Cargo.lock @@ -190,9 +190,9 @@ checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "collective-score-client" -version = "0.0.2" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18bad7c08c5e9754d1b3a09aab531b2a5112ecb241c8bbf7d52c164db5cfab37" +checksum = "d7bf3513f18ebf36b12e0bf91049c2091d5d014a585c65bf54648d19223abe19" dependencies = [ "anyhow", "clap", @@ -218,6 +218,8 @@ name = "cs" version = "0.1.0" dependencies = [ "collective-score-client", + "dirs", + "regex", ] [[package]] @@ -229,6 +231,15 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-sys" version = "0.4.1" diff --git a/cs/Cargo.toml b/cs/Cargo.toml index 33afbf4..823322e 100644 --- a/cs/Cargo.toml +++ b/cs/Cargo.toml @@ -8,4 +8,6 @@ license = "AGPL-3.0" repository = "https://codeberg.org/mo8it/dev-tools" [dependencies] -collective-score-client = "0.0.2" +collective-score-client = "0.0.5" +dirs = "5.0.1" +regex = "1.9.3" diff --git a/cs/src/day1/collective_score_intro.rs b/cs/src/day1/collective_score_intro.rs index 48f8910..b7cf2d4 100644 --- a/cs/src/day1/collective_score_intro.rs +++ b/cs/src/day1/collective_score_intro.rs @@ -1,9 +1,9 @@ use collective_score_client::{ - check::{Check, IntoTask, RunnableCheck}, + check::{Check, IntoTask, Task}, validator::{stdin::Stdin, string_content::StringContent}, }; -pub fn task() -> (&'static str, Box) { +pub fn task() -> Task { Check::builder() .description("Checking stdin") .validator(Stdin { diff --git a/cs/src/day1/cowsay.rs b/cs/src/day1/cowsay.rs new file mode 100644 index 0000000..4850a7b --- /dev/null +++ b/cs/src/day1/cowsay.rs @@ -0,0 +1,60 @@ +use collective_score_client::{ + check::{Check, IntoTask, Task}, + validator::{ + command::{Command, CommandStatus, StdioVariant}, + file::FileContent, + string_content::StringContent, + }, +}; +use regex::Regex; + +pub fn task() -> Task { + let fish_history = dirs::home_dir() + .expect("Failed to get the home directory!") + .join(".local/share/fish/fish_history"); + + [ + Check::builder() + .description("Checking that `cowsay` is installed") + .validator( + CommandStatus::builder() + .command( + Command::builder() + .program("which") + .args(vec!["cowsay".into()]) + .stdout(StdioVariant::Null) + .stderr(StdioVariant::Null) + .stdin(StdioVariant::Null) + .build(), + ) + .status_code(0) + .build(), + ) + .hint("Did you install `cowsay` using the `dnf` package manager?") + .build() + .into_box(), + Check::builder() + .description("Checking that you tried to change the eyes") + .validator(FileContent { + file: fish_history.clone(), + expected: StringContent::Regex( + Regex::new(r"\n- cmd: cowsay.* -e .+\n").expect("Failed to build a regex"), + ), + }) + .hint("Did you read the help message of `cowsay` to find out how to change the eyes?") + .build() + .into_box(), + Check::builder() + .description("Checking that you tried to change the tongue") + .validator(FileContent { + file: fish_history.clone(), + expected: StringContent::Regex( + Regex::new(r"\n- cmd: cowsay.* -T .+\n").expect("Failed to build a regex"), + ), + }) + .hint("Did you read the help message of `cowsay` to find out how to change the tongue?") + .build() + .into_box(), + ] + .into_task("cowsay") +} diff --git a/cs/src/day1/initial_house.rs b/cs/src/day1/initial_house.rs new file mode 100644 index 0000000..0d9314a --- /dev/null +++ b/cs/src/day1/initial_house.rs @@ -0,0 +1,61 @@ +use collective_score_client::{ + check::{Check, IntoTask, Task}, + validator::{file::FileContent, string_content::StringContent}, +}; +use std::path::PathBuf; + +pub fn task() -> Task { + [ + Check::builder() + .description("Checking the bedroom") + .validator(FileContent { + file: PathBuf::from("bedroom/bed.txt"), + expected: StringContent::Full(""), + }) + .hint("Did you create the directory `bedroom` containing the empty file `bed.txt`?") + .build() + .into_box(), + Check::builder() + .description("Checking the bathroom") + .validator(FileContent { + file: PathBuf::from("bathroom/toilet.txt"), + expected: StringContent::Full(""), + }) + .build() + .into_box(), + Check::builder() + .description("Checking the living room") + .validator(FileContent { + file: PathBuf::from("living_room/couch.txt"), + expected: StringContent::Full(""), + }) + .build() + .into_box(), + Check::builder() + .description("Checking the kitchen") + .validator(FileContent { + file: PathBuf::from("kitchen/fridge.txt"), + expected: StringContent::Full(""), + }) + .build() + .into_box(), + Check::builder() + .description("Checking the dining room") + .validator(FileContent { + file: PathBuf::from("dining_room/chair.txt"), + expected: StringContent::Full(""), + }) + .build() + .into_box(), + Check::builder() + .description("Looking for Max in the living room") + .validator(FileContent { + file: PathBuf::from("living_room/max.txt"), + expected: StringContent::Part("beatboxing"), + }) + .hint("Did you add his hobby `beatboxing` to his file?") + .build() + .into_box(), + ] + .into_task("initial-house") +} diff --git a/cs/src/day1/lolcat.rs b/cs/src/day1/lolcat.rs new file mode 100644 index 0000000..36131e4 --- /dev/null +++ b/cs/src/day1/lolcat.rs @@ -0,0 +1,60 @@ +use collective_score_client::{ + check::{Check, IntoTask, Task}, + validator::{ + command::{Command, CommandStatus, StdioVariant}, + file::FileContent, + string_content::StringContent, + }, +}; +use regex::Regex; + +pub fn task() -> Task { + let fish_history = dirs::home_dir() + .expect("Failed to get the home directory!") + .join(".local/share/fish/fish_history"); + + [ + Check::builder() + .description("Checking that `lolcat` is installed") + .validator( + CommandStatus::builder() + .command( + Command::builder() + .program("which") + .args(vec!["lolcat".into()]) + .stdout(StdioVariant::Null) + .stderr(StdioVariant::Null) + .stdin(StdioVariant::Null) + .build(), + ) + .status_code(0) + .build(), + ) + .hint("Did you install `lolcat` using the `dnf` package manager?") + .build() + .into_box(), + Check::builder() + .description("Checking that you piped `cowsay` into `lolcat`") + .validator(FileContent { + file: fish_history.clone(), + expected: StringContent::Regex( + Regex::new(r"\n- cmd: cowsay .+ \| lolcat").expect("Failed to build a regex"), + ), + }) + .hint("Did you try the syntax with `|`?") + .build() + .into_box(), + Check::builder() + .description("Checking that you tried adding randomness to `lolcat`") + .validator(FileContent { + file: fish_history.clone(), + expected: StringContent::Regex( + Regex::new(r"lolcat.* (-r |--random)").expect("Failed to build a regex"), + ), + }) + .hint("Did you read the help message of `lolcat` to find out how to add randomness to the output colors?") + .build() + .into_box(), + ] + .into_task("lolcat") +} diff --git a/cs/src/day1/mod.rs b/cs/src/day1/mod.rs index 4420809..37c8b60 100644 --- a/cs/src/day1/mod.rs +++ b/cs/src/day1/mod.rs @@ -1,7 +1,23 @@ mod collective_score_intro; +mod cowsay; +mod initial_house; +mod lolcat; +mod passwd; +mod system_update; +mod zombie; +mod zombie_nuked; use collective_score_client::check::RunnableCheck; -pub fn tasks() -> [(&'static str, Box); 1] { - [collective_score_intro::task()] +pub fn tasks() -> [(&'static str, Box); 8] { + [ + collective_score_intro::task(), + initial_house::task(), + zombie::task(), + zombie_nuked::task(), + passwd::task(), + system_update::task(), + cowsay::task(), + lolcat::task(), + ] } diff --git a/cs/src/day1/passwd.rs b/cs/src/day1/passwd.rs new file mode 100644 index 0000000..49467cb --- /dev/null +++ b/cs/src/day1/passwd.rs @@ -0,0 +1,18 @@ +use collective_score_client::{ + check::{Check, IntoTask, Task}, + validator::{file::FileContent, string_content::StringContent}, +}; + +pub fn task() -> Task { + Check::builder() + .description("Checking that you changed your password") + .validator(FileContent { + file: dirs::home_dir() + .expect("Failed to get the home directory!") + .join(".local/share/fish/fish_history"), + expected: StringContent::Part("- cmd: passwd\n"), + }) + .hint("Did you change your password by running the command `passwd`? Are you in the fish shell?") + .build() + .into_task("passwd") +} diff --git a/cs/src/day1/system_update.rs b/cs/src/day1/system_update.rs new file mode 100644 index 0000000..f0b3e72 --- /dev/null +++ b/cs/src/day1/system_update.rs @@ -0,0 +1,18 @@ +use collective_score_client::{ + check::{Check, IntoTask, Task}, + validator::{file::FileContent, string_content::StringContent}, +}; + +pub fn task() -> Task { + Check::builder() + .description("Checking that you tried to update the system") + .validator(FileContent { + file: dirs::home_dir() + .expect("Failed to get the home directory!") + .join(".local/share/fish/fish_history"), + expected: StringContent::Part("\n- cmd: dnf upgrade"), + }) + .hint("Did you try to update the system? Are you in the fish shell?") + .build() + .into_task("system-update") +} diff --git a/cs/src/day1/zombie.rs b/cs/src/day1/zombie.rs new file mode 100644 index 0000000..36fa5df --- /dev/null +++ b/cs/src/day1/zombie.rs @@ -0,0 +1,52 @@ +use collective_score_client::{ + check::{Check, IntoTask, Task}, + validator::{ + command::{Command, CommandStatus, StdioVariant}, + file::FileContent, + string_content::StringContent, + }, +}; +use std::path::PathBuf; + +pub fn task() -> Task { + [ + Check::builder() + .description("Looking for the zombie") + .validator(FileContent { + file: PathBuf::from("living_room/zombie.txt"), + expected: StringContent::Part("Brainzzz"), + }) + .hint("Did you put a zombie in the living room having `Brainzzz` in his head?") + .build() + .into_box(), + Check::builder() + .description("Checking that Max ran away") + .validator( + CommandStatus::builder() + .command( + Command::builder() + .program("ls") + .args(vec!["living_room/max.txt".into()]) + .stdout(StdioVariant::Null) + .stderr(StdioVariant::Null) + .stdin(StdioVariant::Null) + .build(), + ) + .status_code(2) + .build(), + ) + .hint("Did you move Max away from the living room? Maybe you cloned Max and left one of his copies in the living room 😯") + .build() + .into_box(), + Check::builder() + .description("Looking for Max") + .validator(FileContent { + file: PathBuf::from("bedroom/max.txt"), + expected: StringContent::Part("beatboxing"), + }) + .hint("Did you move Max into the bedroom? Does he still have his hobby?") + .build() + .into_box(), + ] + .into_task("zombie") +} diff --git a/cs/src/day1/zombie_nuked.rs b/cs/src/day1/zombie_nuked.rs new file mode 100644 index 0000000..fed5944 --- /dev/null +++ b/cs/src/day1/zombie_nuked.rs @@ -0,0 +1,43 @@ +use collective_score_client::{ + check::{Check, IntoTask, Task}, + validator::{ + command::{Command, CommandStatus, StdioVariant}, + file::FileContent, + string_content::StringContent, + }, +}; +use std::path::PathBuf; + +pub fn task() -> Task { + [ + Check::builder() + .description("Checking that the zombie was nuked") + .validator( + CommandStatus::builder() + .command( + Command::builder() + .program("ls") + .args(vec!["living_room".into()]) + .stdout(StdioVariant::Null) + .stderr(StdioVariant::Null) + .stdin(StdioVariant::Null) + .build(), + ) + .status_code(2) + .build(), + ) + .hint("Did you destroy the living room with the Zombie in it?") + .build() + .into_box(), + Check::builder() + .description("Looking for Max") + .validator(FileContent { + file: PathBuf::from("bedroom/max.txt"), + expected: StringContent::Part("beatboxing"), + }) + .hint("Is Max in the bedroom? Does he still have his hobby?") + .build() + .into_box(), + ] + .into_task("zombie-nuked") +} diff --git a/src/day_1/tasks.md b/src/day_1/tasks.md index 28093d3..1b3dc86 100644 --- a/src/day_1/tasks.md +++ b/src/day_1/tasks.md @@ -32,7 +32,7 @@ In general, you will do a task and then run `cs task TASKCODE` to validate your `TASKCODE` is a code unique to every task/subtask. For this demo task, the task code is `collective-score-intro`. -Now, for this demo task to pass the check, run the following command: +🟢 Now, for this demo task to pass the check, run the following command: ```bash echo "OK" | cs task collective-score-intro @@ -42,39 +42,66 @@ Congratulations, you have done your first task 🎉 To see your progress at any time, run the command **`cs progress show`**. -## Task 1: Building houses +## Task 1: Building houses 🏠️ In this task, you will build a house with different rooms using (nested) directories. Start with an empty directory as the house and add directories to it as rooms. -The house should have at least 5 rooms. Give the rooms meaningful names. +Rooms (represented by directories) contain objects as text files (ending with `.txt`). +The text files for the objects should be empty for now. -Place a box as a text file in one of the rooms. Put some content in the box as text lines. +The house should have the following 5 rooms: -Place some persons as text files in different rooms. You can add some information about the persons like hobbies in their files. +- `bedroom` containing `bed.txt` +- `bathroom` containing `toilet.txt` +- `living_room` containing `couch.txt` +- `kitchen` containing `fridge.txt` +- `dining_room` containing `chair.txt` -Visualise your house structure and content using `tree` and make a screenshot. +Place Max in the living room by creating a file `Max.txt` in the living room containing his hobby `beatboxing` as text in his file. -Now, clone your whole house and then rename the persons in the new house. Make another screenshot of `tree` afterwards. +Example: -In the new house, choose a non empty room and move everything within this room into another room. Why? Because zombies are back! They did get in through _windows_... +```console +$ cat living_room/max.txt +He is watching TV. +His hobby is beatboxing. +``` -Make a screenshot of `tree`. +Run the command `tree` to see the structure of the house. -Put some zombies in this room after it is empty, make a screenshot of `tree` and then delete the room with the zombies in it. Make sure to not destroy the whole house! +🟢 Run `cs task initial-house` in the house directory. -Make a final `tree` screenshot of the rest of the house. +Zombies are back! +They did get in through _windows_… (You got it?) -Take a screenshot of the content of some files. Tipp: 😺 +Place a zombie in the living room having `Brainzzz` in his file `zombie.txt`. + +Max runs out of the living room! +Move him to the bedroom. + +🟢 Run `cs task zombie` in the house directory. + +Now, destroy the whole living room with the zombie in it. + +🟢 Run `cs task zombie-nuked` in the house directory. + +Tipps: - If you get lost, use `pwd`. - If you are looking for an option but you can not remember it, use `--help`. ## Task 2: Reset your password +⚠️ Launch the [fish shell](https://fishshell.com/) by running the command `fish` and stay in it for all following tasks. +If you close the terminal and lauch it later again, you have to run `fish` again. +We will learn more about the fish shell later. + Use the command `passwd` to reset the password of your user. It is important to have a secure password! +🟢 Run `cs task passwd`. + ## Task 3: Update your system Find out how to update your system with `dnf` and run the updates. @@ -83,6 +110,8 @@ Before you confirm, make sure that you read what packages are updated. Make a sc It is possible that you don't find any updates. In this case, you can try it again on another day! +🟢 Run `cs task system-update`. + ## Task 4: Package installation and usage Install the package `cowsay` and find out how to use it! @@ -91,10 +120,14 @@ Spoiler: You will have to deal with some cows 🐄 Find out how to give a fancy tongue and shinny eyes. -Make a screenshot of some cow wisdom 🐮 +🟢 Run `cs task cowsay`. -Now install the package `lolcat`. +Now, install the package `lolcat`. -Let's say the wisdom command you did take the last screenshot of is `cowsay (...)`. Now run `cowsay (...) | lolcat`. Describe what changed. Can you guess how this internally works? +Generate some wisdom with `cowsay (…)`. Now, run `cowsay (…) | lolcat`. +What did change? +Can you guess how this internally works? -Now read the help of `lolcat` and find out how to add some randomness. Run the command above again with the option you found and take a screenshot after you are satisfied with the randomness you get. +Now, read the help of `lolcat` and find out how to add some randomness. + +🟢 Run `cs task lolcat`.