Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Build

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

env:
CARGO_TERM_COLOR: always

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Build
run: cargo build --verbose
23 changes: 23 additions & 0 deletions .github/workflows/build_images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Build images

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

env:
CARGO_TERM_COLOR: always

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v4
with:
just-version: '1.46.0'
- name: Build images
run: just build-images
128 changes: 128 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,131 @@ git-trainer — это образовательная платформа д
Попытки решить конкретное задание можно посмотреть в менеджере попыток.
В нём вы можете увидеть дату создания попытки и тесты, запущенные на ней, вместе с их результатами.


# Как контрибьютить?

## Задания

### Миграции

Чтобы добавить задание, придумайте для него сначала название (`name`), рабочее название (`work_name`), описание (`description`) и развёрнутое описание (`extended_description`).

- `name` будет использоваться в меню выбора задания и должен быть в человеко-читаемом виде. По названию человек должен суметь понять его суть, приветствуется оригинальность.
- `work_name` используется для названия Docker-контейнера и образов и должен быть переводом `name` на английский. Приветствуется стиль kebab-case в названии.
- `description` будет показываться в меню выбора задания и должен быть очень кратким, но ёмким описанием задания, которое может поместиться в небольшое место на экране.
- `extended_description` используется внутри контейнера, он показывается с помощью `git-trainer task` и должен быть довольно подробным описанием задания, в нём должна быть поставлена проблематика задания и вся информация для его корректного решения.

Описания заданий хранятся в базе данных, поэтому создайте в папке [migrations](migrations) миграцию с вашим заданием в виде папки, в названии которой находится дата миграции и название задания, можете посмотреть на уже существующие задания. Такое чёткое наименование не обязательно, но приветствуется.

В этой папке должны находиться два файла, up.sql и down.sql, для принятия и отзыва миграции соответственно.

up.sql должен выглядеть вот так:

```sql
INSERT INTO tasks (
id,
name,
work_name,
description,
extended_description
)
VALUES (
1,
'Привет, мир!',
'hello-world',
'В этой задаче Вам предстоит создать новый Git репозиторий и сделать в нём первый коммит.',
'Давайте начнём с чего-нибудь лёгкого.n
Создайте в папке "hello-world" новый Git репозиторий, в котором напишите код на C, выводящий на экран строчку "Hello, World!".n
После этого сделайте ровно один коммит, добавляющий этот код, с названием "Initial commit".'
);
```

ID нового задания должен быть на 1 больше самого большого из существующих, (TODO: сделать с этим что-то...) а `name`, `work_name`, `description` и `extended_description` должны быть указаны в параметрах создания новой записи в базу.

down.sql выглядит же вот так:

```sql
DELETE FROM tasks WHERE work_name = 'hello-world';
```

### Docker-образ

Для начала, вам нужен сам git-репозиторий, с которым пользователь будет работать при запуске задания. Основные требования к репозиторию такие:
- Репозиторий должен имитировать работу над каким-то осмысленным проектом, в идеале это может быть настоящий проект, в котором появилась освещаемая проблема
- Задание не должно требовать от студента владение стеком технологий, кроме того, который изучается по программе на 1 курсе программной инженерии (т.е. базовый C++). Это требование относится именно к тем технологиям, без которых задание не выполнить — то есть если в качестве задания студент будет работать с проектом, написанным на незнакомом ему языке (допустим, Haskell), но оно решается грамотной работой с Git или другими известными технологиями, то такое задание более чем приветствуется.

Этот git-репозиторий вы можете создать на каком угодно удалённом хранилище репозиториев, но будет очень хорошо, если вы сделаете его в организации [git-trainer-tasks](https://github.com/git-trainer-tasks). Для получения доступа к этой организации, напишите [вот ему](https://t.me/gohy279).

> [!NOTE]
> Кстати говоря, не обязательно иметь репозиторий для задания, например в уже существующем задании "hello-world" его не предполагается, у вас может быть так же.

Задания как таковые хранятся в папке [tasks](tasks) по своим названиям. Внутри них должна находиться папка src c Dockerfile в ней и файл justfile:
- В папке src должны находиться все пререквизиты, которые не могут и не должны находиться в репозитории-шаблоне. Например, это могут быть файлы, которые в задании должны будут находиться как незаиндексированные изменения, такие файлы никак не засунешь в репозиторий-шаблон.
- justfile — это скрипт, описывающий как задание преобразуется в Docker-образ. Он может выглядеть вот так:

```just
default:
-git clone https://github.com/git-trainer-tasks/branching.git tasks/branching/src/repo
docker build -f tasks/branching/src/Dockerfile -t git-trainer:branching .
```

- В Dockerfile должна быть описана сама сборка Docker-образа.

[Пример](tasks/branching/src/Dockerfile):

```Dockerfile
FROM git-trainer:base-task-image

ARG USERNAME=student
ARG GIT_USERNAME=student
ARG GIT_EMAIL=student@alivetech.com
ENV DESCRIPTION="В этом репозитории вы с другом пишете алгоритм сортировки подсчётом.\nВаш друг сделал отдельную ветку \"origin/counting_sort\", где имплементировал алгоритм, а вам досталась задача написать функцию вывода вектора на экран в другой ветке.\nНапишите эту функцию в ветке \"print_vector\" и объедините обе ваших ветки с главной веткой main."

COPY tasks/branching/src/repo counting-sort
RUN sudo chown -R $USERNAME:$USERNAME counting-sort/

RUN echo -n $DESCRIPTION > /etc/git-trainer/description
USER $USERNAME
RUN git config --global --add safe.directory /home/$USERNAME/counting-sort
RUN cd counting-sort && git switch main
```

> [!IMPORTANT]
> **Самое главное, что при создании образа вы должны написать копию `extended_description` в файл /etc/git-trainer/description.** Также вы должны заменить в нём все переносы строк на "\n" [#2](https://github.com/dsc-sgu/git-trainer/issues/2).

### Тесты

Тесты для задания хранятся в папке [tests](tests) по своим названиям. В них должны находиться скрипты .sh с названиями вида "test[n].sh", где "n" — номер теста.

Каждый отдельный тест должен проверять отдельную степень свободы в сданном решении. По окончании своей работы он должен выдать какой-либо текст, оповещающий либо об успешном прохождении, либо об ошибке, и exit-code: 0 для успешного прохождения и 1 для ошибки.

[Пример](tests/merge-conflict/test2.sh):

```sh
#!/bin/bash

cd "$HOME"

cd binary-addition && git status &>/dev/null

if [ "$?" -eq 0 ]; then
echo "2. Git-репозиторий существует."
exit 0
else
echo "2. Убедитесь, что в директории binary-addition существует Git-репозиторий."
exit 1
fi
```

Этот скрипт проверяет, что в папке "binary-addition" есть Git-репозиторий с помощью сравнения exit-code команды `git status` с 0.

Скрипты зависят от друг друга: если скрипт с номером n выдал ошибку или не запускался, то скрипт с номером n + 1 не будет запускаться. Так вы можете проверить в первом тесте, что существует, к примеру, Git-репозиторий, а остальные тесты писать будет намного удобнее, исходя из уверенности, что они будут проверять репозиторий тогда и только тогда, когда он действительно существует.

Заметьте, что с помощью shebang вы можете писать тесты на любом другом языке. У вас есть огромная свобода при написании тестов

## Сборка и запуск

Чтобы запустить git-trainer, установите [just](https://github.com/casey/just) и сделайте `just run`. Вы можете отдельно сделать `just build-images` для сборки только образов и `just release` для деплоя приложения.



1 change: 1 addition & 0 deletions migrations/23042026_fix_the_past/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DELETE FROM tasks WHERE work_name = 'fix-the-past';
19 changes: 19 additions & 0 deletions migrations/23042026_fix_the_past/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
INSERT INTO tasks (
id,
name,
work_name,
description,
extended_description
) VALUES (
5,
'Назад в будущее',
'fix-the-past',
'Спасите сломанный проект, найдя правильный код в истории и создав новую спасательную ветку.',
'Вы находитесь в ветке main, где последний коммит (№5) полностью ломает проект. ' ||
'В истории есть коммит №4. Если туда откатиться, то видно, что автор написал правильную функцию,' ||
' но случайно снес половину других файлов. Есть коммит №3 — стабильная версия. ' ||
'Ваша задача: Через git log найдите хэш 4-го коммита, перейдите на него и скопируйте' ||
' функцию triangleArea. Переключитесь на стабильный 3-й коммит. Создайте новую ветку, ' ||
'Вставьте скопированную строчку,' ||
' сохраните файл и сделайте git commit.'
);
1 change: 1 addition & 0 deletions migrations/23062026_where-am-i/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DELETE FROM tasks WHERE work_name = 'where-am-i';
10 changes: 10 additions & 0 deletions migrations/23062026_where-am-i/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
INSERT INTO tasks (id,
name,
work_name,
description,
extended_description)
VALUES (6,
'Где Я?',
'where-am-i',
'Программа падает из-за пропавшей константы. Научитесь заглядывать в прошлые коммиты, не ломая текущую ветку.',
'Студент дописывает программу для физических расчетов. Код не компилируется: удалено значение гравитационной постоянной. Найдите код в истории без checkout и закоммитьте исправление.');
3 changes: 3 additions & 0 deletions tasks/fix-the-past/justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
default:
-git clone https://github.com/git-trainer-tasks/fix-the-past.git tasks/fix-the-past/src/repo
docker build -f tasks/fix-the-past/src/Dockerfile -t git-trainer:fix-the-past .
17 changes: 17 additions & 0 deletions tasks/fix-the-past/src/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM git-trainer:base-task-image

ARG USERNAME=student
ARG GIT_USERNAME=student
ARG GIT_EMAIL=student@alivetech.com
ENV DESCRIPTION="Проект сломался при последнем коммите. Найдите новую функцию в истории (№4) и cout, относящийся к ней, вернитесь в стабильное состояние (№3), создайте ветку, добавьте недостающую функцию и cout, относящийся к ней (аргументы любой функции в этой задаче должны быть 4,5). Сделайте коммит и введите git-trainer submit. Сливать ветку с main пока НЕ нужно!"

RUN echo -n $DESCRIPTION > /etc/git-trainer/description
COPY ./tasks/fix-the-past/src/repo /home/${USERNAME}/fix-the-past

WORKDIR /home/${USERNAME}/fix-the-past
RUN sudo chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/fix-the-past
USER $USERNAME
RUN git config --global --add safe.directory /home/${USERNAME}/fix-the-past && git switch main



3 changes: 3 additions & 0 deletions tasks/where-am-i/justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
default:
-git clone https://github.com/git-trainer-tasks/where-am-i.git tasks/where-am-i/src/repo
docker build -f tasks/where-am-i/src/Dockerfile -t git-trainer:where-am-i .
16 changes: 16 additions & 0 deletions tasks/where-am-i/src/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM git-trainer:base-task-image

ARG USERNAME=student
ARG GIT_USERNAME=student
ARG GIT_EMAIL=student@alivetech.com
ENV DESCRIPTION="Произошла авария на расчетном сервере! Наш модуль вычисления орбитальных маневров (src/orbital_calc.cpp) перестал работать и выдает нулевую скорость. Долгие попытки разобраться показали, что во время недавнего масштабного рефакторинга кто-то случайно стер точное значение одной из фундаментальных физических констант, заменив её на ноль. Точно известно, что правильное многозначное число внедрялось в одном из прошлых коммитов, но история проекта забита десятками других правок, добавлением телеметрии и удалением старого кода.\nВаша задача: Найти в истории изменений коммит, в котором добавлялось правильное значение этой константы и восстановить значение в коде и сделать спасительный коммит.\nВАЖНО: в этом задании нельзя использовать git checkout <hash коммита>. Вам нужно просто заглянуть в прошлое - найти команду Git, которая позволяет ПОКАЗАТЬ содержимое коммита и изменения в нем прямо в терминале."
RUN echo -n $DESCRIPTION > /etc/git-trainer/description
COPY ./tasks/where-am-i/src/repo /home/${USERNAME}/where-am-i

# Есть вариант, что это лишнее
RUN sudo apt-get update && sudo apt-get install -y cmake build-essential

WORKDIR /home/${USERNAME}/where-am-i
RUN sudo chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/where-am-i
USER $USERNAME
RUN git config --global --add safe.directory /home/${USERNAME}/where-am-i && git switch main
16 changes: 16 additions & 0 deletions tests/fix-the-past/test1.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

cd "$HOME/fix-the-past" || exit 1

CURRENT_BRANCH=$(git branch --show-current)

if [ -z "$CURRENT_BRANCH" ]; then
echo "1. Вы находитесь в состоянии Detached HEAD! Создайте ветку: git checkout -b <имя_ветки>."
exit 1
elif [ "$CURRENT_BRANCH" == "main" ]; then
echo "1. Вы всё ещё в ветке main. Вам нужно найти стабильный коммит и создать ветку от него."
exit 1
else
echo "1. Отлично! Вы работаете в новой ветке: $CURRENT_BRANCH."
exit 0
fi
30 changes: 30 additions & 0 deletions tests/fix-the-past/test2.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

cd "$HOME/fix-the-past" || exit 1


TARGET_COMMIT_MSG="feat(main): implement rectangle area calculation"
TARGET_HASH=$(git log --all --grep="$TARGET_COMMIT_MSG" --format="%H" -n 1)

if [ -z "$TARGET_HASH" ]; then
echo "Системная ошибка: Целевой стабильный коммит не найден в репозитории."
echo "Пожалуйста, сбросьте задание."
exit 1
fi



BASE_HASH=$(git merge-base HEAD main)

if [ -z "$BASE_HASH" ]; then
echo "Системная ошибка: Не удалось вычислить общего предка с main."
exit 1
fi

if [ "$BASE_HASH" == "$TARGET_HASH" ]; then
echo "2. Супер! Ваша ветка отпочковалась от правильной стабильной версии."
exit 0
else
echo "2. Вы взяли за основу не тот коммит! Найдите в логах коммит с rectangleArea и создайте ветку именно от него."
exit 1
fi
Loading
Loading