Skip to content
Snippets Groups Projects

Adds a first version of the wiki documentation

Merged orestis.malaspin requested to merge add_wiki_doc into main
1 file
+ 414
0
Compare changes
  • Side-by-side
  • Inline
+ 414
0
# How to create an assignment
We will describe how to create a very simple assignment with dojo.
Here we will build a `Hello world!` in the C programming language. We will describe how one must
modify the default template to have an exercise.
The exercise will be to fill a function to return the `Hello world!` string.
The output of the function will be printed on the standard output.
The structure will be provided to the students as well as a very simple `Makefile`
to compile and run the code.
The success or failure of this assignment will be tested by comparing the output
of our program with an the expected output (the famous `Hello world!`).
To build this exercise we need to perform several steps that will be described below in great details.
## Empty assignment creation
First create a new assignment with the command
```bash
$ dojo assignment create --name c_hello_world
```
```console
Please wait while we verify and retrieve data...
ℹ Checking Dojo session:
✔ The session is valid
✔ Teaching staff permissions
ℹ Checking Gitlab token:
✔ Read access
✔ Write access
✔ Assignment name "c_hello_world" is available
Please wait while we are creating the assignment...
✔ Assignment successfully created
ℹ Name: c_hello_world
ℹ Web URL: https://gitedu.hesge.ch/dojo/assignment/c_hello_world
ℹ HTTP Repo: https://gitedu.hesge.ch/dojo/assignment/c_hello_world.git
ℹ SSH Repo: ssh://git@ssh.hesge.ch:10572/dojo/assignment/c_hello_world.git
```
## Clone the assignment locally
The assignment is nothing more than a git repository. We just clone it and start
modifying it
```bash
$ git clone ssh://git@ssh.hesge.ch:10572/dojo/assignment/c_hello_world.git
```
```console
Cloning into 'c_hello_world'...
remote: Enumerating objects: 18, done.
remote: Counting objects: 100% (18/18), done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 18 (delta 3), reused 18 (delta 3), pack-reused 0
Receiving objects: 100% (18/18), 4.37 KiB | 4.37 MiB/s, done.
Resolving deltas: 100% (3/3), done.
```
The `c_hello_world` assignment has now the following structure
```bash
$ tree c_hello_world/
```
```console
c_hello_world/
├── docker-compose.yml
├── Dockerfile
├── dojo.assignment
└── README.md
```
## Build the development environment
In order to execute a C program we need a working compiler and `make`. In order to
produce a special result file to be parsed by the `dojo` tool to produce nicely formatted output
we will use `jq`, a command-line json creation/edition/reding tool. Our `Dockerfile`
contains
```dockerfile
FROM ubuntu:latest
RUN apt update && apt install gcc make jq -y
COPY . /
ENTRYPOINT ["./run.sh"]
```
We see that we start by installing the required packages on top of the latest Ubuntu image.
We then proceed to copy the complete assignment into the docker container, finall we run
a yet to be created `run.sh` script.
We can test our `Dockerfile` by running the command
```bash
$ docker compose run --build hello_world
```
```console
[+] Building 20.5s (8/8) FINISHED
=> [hello_world internal] load build definition from Dockerfile
=> => transferring dockerfile: 134B
=> [hello_world internal] load .dockerignore
=> => transferring context: 2B
=> [hello_world internal] load metadata for docker.io/library/ubuntu:latest
=> CACHED [hello_world 1/3] FROM docker.io/library/ubuntu:latest@sha256:ec050c32e4a6085b423d36ecd025c0d3ff00c38ab93a3d71a460ff1c44fa6d77
=> [hello_world internal] load build context
=> => transferring context: 39.87kB
=> [hello_world 2/3] RUN apt update && apt install gcc make jq -y
=> [hello_world 3/3] COPY . /
=> [hello_world] exporting to image
=> => exporting layers
=> => writing image sha256:4b561113c7123da08206a2cf2642cb4f331670fe44350646437eaa78e44aff3a
=> => naming to docker.io/library/c_hello_world-hello_world
ERRO[0021] error waiting for container:
Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "./run.sh": stat ./run.sh: no such file or directory: unknown
```
We see an error at the end of the command which is perfectly normal. We did not create the `run.sh` file et (let's leave that for later).
We then need to focus on the `docker-compose.yml` file which will take care of all the hard work
needed if complex workflows are required. Here it is as simple as possible and should contain
```yml
services:
hello_world:
container_name: hello_world
build:
context: ./
dockerfile: Dockerfile
volumes:
- hello_world_volume:/result # <hello_world_volume> must be the same as below but
# the name may be arbitrary. This volume must be
# present in the dojo_assignment.json file under the field
# "result": {
# "volume": "hello_world_volume",
# ...
# }
volumes:
hello_world_volume:
```
In this file, we see the definition of a `hello_world_volume` this is an arbitrary name and can be changed but it
must be coherent in this file and in the `dojo_assignment.json` file (more on this later configuration file in [The dojo configuration file](#the-dojo-configuration-file)).
This volume is responsible of mounting `/result/` directory which will contain all
the output generated by our assignment (again the name can be changed). In particular
in this director e will be required to create a `dojo_assignment.json` file
that contains at least if the assignment was successfully performed (more on that at the end of the [Creating the assignment files](#creating-the-assignment-files) section).
## The dojo configuration file
The `dojo_assignment.json` file contains the general configuration of the assignment.
The important configuration parameters are:
- the *immutable files* which are files that will be overwritten when the compilation pipeline is run (even if the student
modifies these files the modifications will not be taken into account),
- the *results* which is the volume corresponding to the `docker-compose.yml` file.
In its default form `dojo_assignment.json` file contains
```json
{
"dojoAssignmentVersion": 1,
"version": 1,
"immutable": [
{
"description": "Dockerfile of the unique container",
"path": "Dockerfile",
"isDirectory": false
}
],
"result": {
"container": "hello_world",
"volume": "hello_world_volume"
}
}
```
Here we see only one immutable file which is the `Dockerfile` (the `isDirectory` field is `false` but it is possible to make
complete directories immutable) and we see that:
* the value of the `container` field (`hello_world`) corresponds to
the value of the `container_name` field in the `docker-compose.yml` file,
* the value of the `volume` field (`hello_world_volume`) corresponds to the `volumes` field in the `docker-compose.yml` file.
This file will be completed in [The immutable files](#the-immutable-files) sectino with the files of our assignment that will be
created in the [next section](#creating-the-assignment-files).
## Creating the assignment files
For this assignment we will create the following files with the following content:
- `src/Makefile`
```makefile
CC:=gcc
CFLAGS:=-Wall -Wextra -Wpedantic -fsanitize=address -Werror -g
LDFLAGS:=-fsanitize=address
hello_world: hello_world.o function.o
gcc -o $@ $^ $(LDFLAGS)
hello_world.o: function.h
function.o: function.h
run: hello_world
./hello_world
clean:
rm -f *.o hello_world
```
- `src/hello_world.c`
```c
#include <stdio.h>
#include <stdlib.h>
#include "function.h"
int main()
{
printf("%s", hello_world());
return EXIT_SUCCESS;
}
```
- `src/function.h`
```c
#ifndef _FUNCTION_H_
#define _FUNCTION_H_
char *hello_world();
#endif
```
- `src/function.c`
```c
#include "function.h"
char *hello_world()
{
// TODO: Replace 0 here to return "Hello world!"
return 0;
}
```
- `src/expected_output.txt`
```
Hello world!
```
These files will be used to create an executable that will be run by the custom execution script, `run.sh`, see the `Dockerfile` in
the [Build the environment](#build-the-development-environment) section.
All these files have arbitrary names and it's completely up to the teacher to make a coherent exercise.
The only missing file is `run.sh` (do not forget to make it executable, `chmod +x run.sh`) that contains the following code
```bash
#!/bin/bash
echo "Starting tests."
GLOBAL_SUCCESS=false
make -C src clean -s
make -C src -s
if [ $? -eq 0 ]; then
make run -C src > src/output.txt -s
if [ $? -eq 0 ]; then
diff --color src/output.txt src/expected_output.txt > result/diff_output.txt
if [ $? -ne 0 ]; then
echo "Output is wrong:";
cat result/diff_output.txt
else
echo "All tests were a complete success"
GLOBAL_SUCCESS=true
fi
else
echo "Execution failed";
fi
else
echo "Compilation failed."
fi
jq --null-input --arg success $GLOBAL_SUCCESS \
'{"success": $success | test("true")}' > /result/results.json
```
Here one can see the creation of two different files that are located in the `result` directory:
- `result/results.json`
- `result/diff_output.txt`
The `results.json` is mandatory to be created and must at least contain the
`success` field must be `true` or `false` and determines whether the
assignment is a success or a failure. The other files present in the `result` folder
can be retrieved by the students.
To test if everything works according to plan, one can again use the command
```bash
$ docker compose run --build hello_world
```
```console
[+] Building 1.8s (8/8) FINISHED
=> [hello_world internal] load build definition from Dockerfile
=> => transferring dockerfile: 134B
=> [hello_world internal] load .dockerignore
=> => transferring context: 2B
=> [hello_world internal] load metadata for docker.io/library/ubuntu:latest
=> [hello_world 1/3] FROM docker.io/library/ubuntu:latest@sha256:ec050c32e4a6085b423d36ecd025c0d3ff00c38ab93
=> [hello_world internal] load build context
=> => transferring context: 3.23kB
=> CACHED [hello_world 2/3] RUN apt update && apt install gcc make jq -y
=> [hello_world 3/3] COPY . /
=> [hello_world] exporting to image
=> => exporting layers
=> => writing image sha256:5499aa3aa0b2f2021584dbf46b4b05ab83dc0a5ad4d6c81a3466c56673c3f562
=> => naming to docker.io/library/c_hello_world-hello_world
Starting tests.
Output is wrong:
1c1
< (null)
\ No newline at end of file
---
> Hello world!
\ No newline at end of file
```
where we see that the execution fails. This will be improved in the future and the actual execution as expected to be
run by the students will be added.
## The immutable files
There is only one file that should be modified by the students in this example: the `function.c` file. Therefore we can safely
add all the created files in the `src` except `src/function.c` which is precisely
the file that the student must modify, as well as the `run.sh` file. The `dojo_assignment.json` file becomes
```json
{
"dojoAssignmentVersion": 1,
"version": 1,
"immutable": [
{
"description": "Dockerfile of the unique container",
"path": "Dockerfile",
"isDirectory": false
},
{
"description": "The entry point of the Dockerfile",
"path": "run.sh",
"isDirectory": false
},
{
"description": "The makefile for compilation/execution purposes",
"path": "src/Makefile",
"isDirectory": false
},
{
"description": "Entry point of the code",
"path": "src/hello_world.c",
"isDirectory": false
},
{
"description": "The header file fot the program's assignment",
"path": "src/function.h",
"isDirectory": false
},
{
"description": "The expected output file for comparing with the actual output",
"path": "src/expected_output.txt",
"isDirectory": false
}
],
"result": {
"container": "hello_world",
"volume": "hello_world_volume"
}
}
```
## The `README.md` file
The `README.md` file is used to provide informations on the assignment and the way the teacher wants students to accomplish
the assignment and its content is completely free. It is recommended to use the `Markdown` syntax as the
file extension suggests.
In this assignment the `README.md` file reads
```markdown
# Hello world!
C'est le premier vrai exercice jamais créé sur le Dojo!
Il sert de tutoriel pour créer un exercice simple en C.
## But
Le but est de faire afficher "Hello world!" à notre programme en C.
Pour ce faire, il faut modifier le fichier `src/function.c` sous la ligne
annotée avec `TODO`. Bonne chance!
```
## Publish the work
Now that the assignment is ready, we must publish it. First add/commit/push all the
files needed for your exercise. In this case, it should be:
```console
c_hello_world/
├── docker-compose.yml
├── Dockerfile
├── dojo_assignment.json
├── README.md
├── run.sh
└── src
├── expected_output.txt
├── function.c
├── function.h
├── hello_world.c
└── Makefile
```
Then one must *publish* the assignment for the students to be able to perform to get the exercise.
```bash
$ dojo assignment publish c_hello_world
```
```console
? Are you sure you want to publish this assignment? Yes
Please wait while we verify and retrieve data...
ℹ Checking Dojo session:
✔ The session is valid
ℹ Checking assignment:
ℹ c_hello_world
✔ The assignment exists
✔ You are in the staff of this assignment
Please wait while we publish the assignment...
✔ Assignment c_hello_world successfully published
```
The assignment is now ready to be performed by students!
Loading