github стратегія для збереження однієї версії файлу приватною


11

Я викладач, що пише проблеми кодування для студентів. Що я хочу зробити, це дати студентам кодовий код із заповнювачами для функцій, які учні повинні виконати. Я дам студентам доступ до приватного репортажу github, щоб клонувати це.

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

Я думав про гілки, але AFAIK, я не можу залишати одну гілку приватною.

Можливо, я міг би перенести проект в інше приватне репо, але я не впевнений, як я можу тримати проекти у snyc (крім файлу, який містить рішення).

Чи існує робочий процес для цієї ситуації?


1
Я не думаю, що так. Але те, що вам холодно: інтерфейси delcare для всіх методів, які потрібно реалізувати. У своєму репо-студенті створіть класи, реалізуючи ці інтерфейси з порожніми методами методів. Підтримуйте рішення в окремому приватному репо. Це не повністю вирішує вашу проблему синхронізації, але зменшує її до обсягу завдань.
marstato

Ви хотіли використовувати API github для контролю доступу до гілок?

Відповіді:


8

Що може бути цілком можливо:

  • Створіть 2 сховища: студент та викладач.
  • Клоніруйте їх на свою машину (це можна зробити з клієнтом Github)
  • Ви працюєте лише вчителем , ніколи не торкайтеся учня.

Отже, структура вашого каталогу - це 2 клоновані git repo:

  • / студент (із папкою .git)
  • / вчитель (із папкою .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);
});

Буде: візьміть усі ваші файли та скопіюйте вміст в / студент (перезапис) без приватних позначених частин коду. Якщо ви хочете, ви можете вставити туди порожні рядки, але це може підказати, яке рішення ви очікуєте.

Це неперевірений код прикладу, тому, ймовірно, вам доведеться виконати налагодження.

Тепер єдине, що вам потрібно зробити, це зробити фіксацію та натискання у сховищі студентів, коли ви раді раді. Це можна зробити одним кліком при використанні клієнта GitHub (так що ви можете зробити швидкий візуальний огляд) або просто зробити це вручну в командному рядку.

Студентське репо - це лише сховище вихідних даних, тому воно завжди буде в курсі сучасних даних, і студентам зрозуміло, що зміниться, переглянувши коміти (адже вони показують лише зміни), і це легко обробляти.

Ще одним кроком було б створити гак-коміт, який автоматично запускає ваш сценарій.

Редагувати: Дивіться, що ви внесли зміни до своєї публікації:

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

Я підозрюю, що це зрозуміло, але бути повним: просто видаліть теги навколо закінченої вправи, ви отримаєте відповідь так само, як і для звичайних оновлень вправ.


сподівався, що я можу це зробити за допомогою вуду, але ваше рішення дуже практичне.
Кен

@Ken теж думав про це, але це трохи неправильний інструмент для неправильної роботи. 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. Потім утиліта, що використовує цей пароль, додає зашифровані відмінності між попереднім та поточним станом файлу a.txtдо кінця a.txt.sshareфайлу.

Після отримання / витягування сховища із зашифрованими файлами слід запустити sshareутиліту для кожного файлу за допомогою -lклавіші ("load"). У цьому випадку утиліта розшифровує *.sshareфайли до текстових файлів, не відстежуваних git у робочій копії.

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

Утиліта дозволяє мерзотник для треку ефективно змінює ( дифф з .sshareфайлів просто одній лінії).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.