стратегия github для сохранения одной версии файла в секрете

11

Я лектор, пишущий проблемы кодирования для студентов. Что я хочу сделать, так это дать учащимся шаблонный код с заполнителями для функций, которые студенты должны выполнять. Я дам студентам доступ к частному репозиторию Github, чтобы клонировать это.

Однако я также хочу версию кодовой базы, дополненную примерами решений. Очевидно, я не хочу, чтобы студенты имели доступ к решению (пока задание не закончено).

Я думал о филиалах, но AFAIK, я не могу держать один филиал частным.

Возможно, я мог бы преобразовать проект в другое частное хранилище, но не уверен, как сохранить проекты в snyc (кроме файла, в котором содержится решение).

Есть ли рабочий процесс для этой ситуации?

кругозор
источник
1
Я так не думаю. Но то, что вы делаете холодно: интерфейсы delcare для всех методов, которые должны быть реализованы. В своем репозитории для студентов создайте классы, реализующие эти интерфейсы с пустыми телами методов. Поддерживать решения в отдельном частном репо. Это не полностью решает проблему синхронизации, но сводит ее к объему задач.
marstato
вы рассматривали возможность использования API github для управления доступом к веткам?

Ответы:

8

Что может быть вполне выполнимо:

  • Создайте 2 хранилища: ученик и учитель.
  • Клонируйте их на свою машину (это можно сделать с помощью клиента Github)
  • Ты работаешь только учителем , никогда не трогай ученика.

Итак, ваша структура каталогов - это 2 клонированных репозитория git:

  • / student (с папкой .git)
  • / teacher (с папкой .git)

Вы ставите маркеры вокруг "частного" кода в комментариях к вашему языку, например, на javascript ниже. Маркеры указывают, где начинается и заканчивается закрытый код.

function sum(a, b) {
  // -----------------------START
  return a + b; // so this is what you expect from the student
  // -----------------------END
}

console.log(sum(1,1)); // I expect 2 as a result of your homework

Затем создайте простой скрипт на своей локальной машине:

files.forEach((fileContent, fileName) => {
  let newFileContent = '';
  let public = true;
  fileContent.forEach((line) => {
    switch(line) {
      case '// -----------------------START':
        public = false;
        break;
      case '// -----------------------END':
        public = true;
        break;
      default:
        if(public) {
          newFileContent = newFileContent + line + "\n";
        }
    }
  });
  writeFile('../student/' + fileName, newFileContent);
});

Он будет: принимать все ваши файлы и копировать содержимое в / student (перезаписывать) без закрытых помеченных частей кода. Если вы хотите, вы можете вставить туда пустые строки, но это может дать подсказку о том, какое решение вы ожидаете.

Это непроверенный пример кода, так что скорее всего вам придется отладить.

Теперь единственное, что вам нужно сделать, это зафиксировать и вставить репозиторий ученика, когда вы довольны результатом. Это можно сделать одним щелчком мыши при использовании клиента GitHub (так что вы можете сделать быстрый визуальный обзор) или просто сделать это вручную в командной строке.

Репозиторий для учащихся - это выходной репозиторий, поэтому он всегда будет оставаться в курсе, студентам будет ясно, что изменилось, если посмотреть на коммиты (потому что они только показывают изменения), и с ним просто работать.

Еще одним шагом будет создание git commit-hook, который автоматически запускает ваш скрипт.

Редактировать: увидеть, что вы сделали редактирование вашего сообщения:

Очевидно, я не хочу, чтобы студенты имели доступ к решению (пока задание не закончено).

Я подозреваю, что это понятно, но для завершения: просто удалите теги вокруг законченного упражнения, и публикация ответа будет точно такой же, как вы делали бы это для обычных обновлений упражнений.

Люк Франкен
источник
Я надеялся, что смогу сделать это с помощью git voodoo, однако ваше решение очень практично.
Кен
@ Кен тоже думал об этом, но это немного не тот инструмент для неправильной работы. Git объединяет, обновляет и т. Д., Но в общем случае не стоит выбирать код. Это хорошо для поддержания вашей кодовой базы согласованной на нескольких машинах. Вот почему я нашел другое решение. Что мне также нравится в этом подходе, так это то, что он минимизирует риск и трудозатраты, поэтому с ним легко справиться. И, в конце концов, вы все равно должны написать свое сообщение о коммите в репозиторий учеников, чтобы дать хороший пример своим ученикам;)
Люк Франкен,
Чтобы помочь git отслеживать изменения, вы можете сделать ветку ученика в репозитории своего учителя, запустить сценарий при объединении (или объединении вручную, удаляя что-либо между маркерами). Затем синхронизируйте локальную ветку студента и отправьте ее в репозиторий ученика вместо источника учителя. Таким образом, git будет в лучшей форме для отслеживания изменений и правильной пересылки истории из одного репо в другой. Лучшее обоих миров. Я не пробовал, но я не понимаю, почему это не сработает.
Ньютопия
1
Мне это нравится, за исключением идеи удаления начальных и конечных тегов. Лучше их покалечить, добавив слово «решение».
candied_orange
@CandiedOrange это тоже неплохо, согласитесь. Решение также допускает некоторое другое форматирование, и оно четко различает забытые теги и реальное решение о том, что решение должно быть опубликовано. @ newtopian: я думал об этом, но я не видел достаточно преимуществ. Также я решил рассматривать результаты работы студентов как совершенно другой вид кода. Это не настоящий источник, поэтому я решил не делать этого. Например, то, что я хотел бы сделать с филиалами в репозитории для учителей: работа над заданиями на следующий семестр. Когда вы будете готовы, вы объедините их с мастером, а затем запустите скрипт.
Люк Франкен
6

Ты мог

  • Создайте общедоступный репозиторий GitHub, где вы фиксируете шаблонный код
  • Поместите этот репозиторий в качестве частного репозитория GitHub
  • Решите задания в разветвленном хранилище
  • Когда каждое задание выполнено, объедините каждое решение в публичный репозиторий.

Вот как я бы реализовал этот рабочий процесс:

  • Создайте публичный репозиторий, assignmentsразмещенный на GitHub. Добавьте стандартный шаблон для назначений. Например, для каждого назначения вы вводите новый подкаталог, содержащий шаблонный код назначения.
  • Создать новое личное хранилище assignments-solvedна GitHub. Клонируйте репозиторий assignmentsна своем компьютере и перенесите его в assignments-solvedрепозиторий (по сути, создайте собственный репозиторий как личную копию): git clone https://github.com/[user]/assignments assignments-solved cd assignments-solved git remote set-url origin https://github.com/[user]/assignments-solved git push origin master git push --all
  • Добавьте assignments-solvedрепо как удаленное к assignmentsрепо: cd assignments # change to the assignments repo on your machine git remote add solutions https://github.com/[user]/assignments-solved
  • Реализуйте каждое задание в assignments-solvedхранилище. Убедитесь, что каждый коммит содержит только изменения из одного назначения.
  • Возможно, вы захотите создать solvedветку в assignmentsрепо, чтобы исходные назначения не были изменены: cd assignments # change to the assignments repo on your machine git branch -b solutions git push -u origin
  • Если вы хотите опубликовать решение в assignments, извлеките solvedпульт и cherry-pickкоммиты, содержащие решения. cd assignments # change to the assignments repo on your machine git checkout solved git fetch solutions git cherry-pick [commithash] Где [commithash]содержится фиксация вашего решения.

Вы также можете реализовать рабочий процесс, внедрив каждое назначение в отдельную ветку assignments-solvedрепо, а затем создав пул-запрос в assignmentsрепо. Но я не уверен, что это сработает в GitHub, так как assignments-solvedрепо не является настоящим форком.

Гасте
источник
Я успешно использовал подобный метод, чтобы отделить программный тест от отправленных ответов. В моем случае представленные решения добавляются в отдельные ветви частного клона и никогда не объединяются с публичным репо. Это дает мне дополнительное преимущество, позволяя мне увидеть, какую версию теста выполнил каждый кандидат, так как он развивается с течением времени.
Axl
0

Я просто могу предложить вам утилиту, предназначенную для .gitignoreшифрования и шифрования файлов в вашем хранилище. Рабочий процесс немного сложен в использовании, но он делает зашифрованные копии ваших файлов доступными в рабочей копии вместе с другими несекретными файлами, что позволяет отслеживать их с помощью git как обычно.

#!/bin/bash

set -o errexit
set -o pipefail
set -o nounset

version=1
OPTIND=1
verbose=0
mode="add"
recurse=()
files=()

while getopts ":vaslr:" opt
do
    case "$opt" in
        \?) echo "error: invalid option: -$OPTARG" >&2 ; exit 1
            ;;
        :)  echo "error: option -$OPTARG requires an argument" >&2 ; exit 1
            ;;
        v)  let "verbose++" ; echo "verbosity increased"
            ;;
        a)  mode="add"
            ;;
        s)  mode="save"
            ;;
        l)  mode="load"
            ;;
        r)  recurse+=("$OPTARG")
            ;;
    esac
done
shift $((OPTIND-1))
if [[ "${#recurse[@]}" != 0 ]] 
then
    for pattern in "${recurse[@]}" 
    do
        while IFS= read -d $'\0' -r file
        do
            files+=("$file")
        done < <(find . -name "$pattern" -type f -print0)
    done
else
    files=("$@")
fi

[[ "${#files[@]}" != 0 ]] || { echo "list of files to process is empty" >&2 ; exit 1 ; }

if [[ $mode == "add" ]]
then
    for file in "${files[@]}"
    do
        [[ -e $file ]] && cp "$file" "${file}.bak" || touch "$file"
        sshare_file="${file}.sshare"
        [[ -e $sshare_file ]] || { echo "$version" > "$sshare_file" ; git add --intent-to-add "$sshare_file" ; echo "$file" >> .gitignore ; echo "${file}.bak" >> .gitignore ; git add .gitignore ; }
    done
    exit 0
fi
tmp_dir=`mktemp --tmpdir -d sshare.XXXX`
read -r -s -p "enter password to $mode tracked files:" sshare_password && echo ;
for file in "${files[@]}"
do
    [[ ! -e $file ]] && touch "$file" || cp "$file" "${file}.bak"
    sshare_file="${file}.sshare"
    [[ -r $sshare_file ]] || { echo "warning: can't read file '$sshare_file' (file '$file' skipped)" >&2 ; continue ; }
    file_version=$(head -1 "$sshare_file")
    [[ "$file_version" == $version ]] || { echo "warning: version '$file_version' of '$sshare_file' file differs from version '$version' of script (file '$file' skipped)" >&2 ; continue ; }
    tmp_file="$tmp_dir/$file"
    mkdir -p "$(dirname "$tmp_file")"
    > "$tmp_file"
    line_number=0
    while IFS= read -r line
    do
        let "line_number++" || :
        [[ -n $line ]] || { echo "warning: empty line encountered at #$line_number in file '$sshare_file' (ignored)" >&2 ; continue ; }
        echo "$line" | openssl enc -d -A -base64 -aes256 -k "$sshare_password" | gunzip --to-stdout --force | patch "$tmp_file" --normal --quiet
    done < <(tail --lines=+2 "$sshare_file")
    if [[ $mode == "load" ]]
    then
        cp -f "$tmp_file" . || { echo "warning: can't write to file '$file' (file '$file' skipped)" >&2 ; continue ; }
    elif [[ $mode == "save" ]]
    then
        chunk=$(diff "$tmp_file" "$file" || :)
        [[ -n $chunk ]] || { echo "nothing to comit since last edit for file '$file'" ; continue ; }
        [[ -w $sshare_file ]] || { echo "warning: can't update sshare database '$sshare_file' (file '$file' skipped)" ; continue ; }
        echo "$chunk" | gzip --stdout | openssl enc -e -A -base64 -aes256 -k "$sshare_password" >> "$sshare_file"
        echo >> "$sshare_file"
        echo "changes encrypted for file '$file'"
    fi
done

Создать секретный файл с a.txtтипом имени файла sshare -a a.txt. Утилита создания файла a.txtи файл добавлен в .gitignore. Затем он создает зашифрованный аналог «базы данных» a.txt.sshare, добавляя .sshareрасширение к имени файла.

Затем вы можете заполнить a.txtтекст. Чтобы сохранить свое состояние прямо перед git commitвводом sshare -s a.txt, утилита предложит вам ввести пароль для шифрования нового состояния файла a.txt. Затем утилита, использующая этот пароль, добавляет зашифрованный diff между предыдущим и текущим состоянием файла a.txtв конец a.txt.sshareфайла.

После извлечения / извлечения репозитория с зашифрованными файлами вы должны запустить sshareутилиту для каждого файла, используя -l(«загрузить») ключ. В этом случае утилита расшифровывает *.sshareфайлы в текстовые файлы, не отслеживаемые git в рабочей копии.

Вы можете использовать разные пароли для каждого секретного файла.

Утилита позволяет мерзавец для трека эффективно изменяет ( дифф из .sshareфайлов просто одной линии).

Томилов Анатолий
источник