strategia github per mantenere privata una versione del file


11

Sono un docente che scrive problemi di codifica per gli studenti. Quello che voglio fare è fornire agli studenti il ​​codice del bollettino con i segnaposto per le funzioni che gli studenti devono completare. Darò agli studenti l'accesso a un repository github privato per clonarlo.

Tuttavia, voglio anche una versione della base di codice, completa di soluzioni di esempio. Ovviamente non voglio che gli studenti abbiano accesso alla soluzione (fino alla fine dell'incarico).

Ho pensato alle filiali, ma AFAIK, non riesco a mantenere una filiale privata.

Forse potrei trasferire il progetto in un altro repository privato, ma non sono sicuro di come mantenere i progetti in snyc (a parte il file che contiene la soluzione).

Esiste un flusso di lavoro per questa situazione?


1
Non la penso così. Ma cosa fai freddo: interfacce delcare per tutti i metodi che devono essere implementati. Nel tuo repository pubblico-studente, crea classi che implementano tali interfacce con i corpi del metodo vuoto. Mantenere le soluzioni in un repository privato separato. Ciò non risolve completamente il problema di sincronizzazione, ma lo riduce all'ambito delle attività.
Marstato,

hai cercato di utilizzare l'API github per controllare l'accesso alle filiali?

Risposte:


8

Cosa potrebbe essere abbastanza fattibile:

  • Crea 2 repository: studente e insegnante.
  • Clonali sulla tua macchina (può essere fatto con il client Github)
  • Si lavora solo in insegnante , non toccare mai studente.

Quindi la tua struttura di directory è di 2 repository git clonati:

  • / studente (con una cartella .git)
  • / insegnante (con una cartella .git)

Metti dei marcatori attorno al codice "privato" nei commenti per la tua lingua, ad esempio javascript di seguito. I marcatori indicano dove inizia e finisce il codice privato.

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

Quindi crea un semplice script sul tuo computer locale:

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);
});

Lo farà: prendi tutti i tuoi file e copia i contenuti su / student (sovrascrittura) senza le parti private contrassegnate del codice. Se lo desideri, puoi inserire righe vuote lì, ma ciò potrebbe dare un suggerimento sul tipo di soluzione che ti aspetti.

È un codice di esempio non testato, quindi probabilmente devi fare un po 'di debug.

Ora l'unica cosa che devi fare è impegnare e spingere nel repository degli studenti quando sei soddisfatto dell'output. Questo può essere fatto con un clic quando si utilizza il client GitHub (in modo da poter fare una rapida revisione visiva) o semplicemente farlo manualmente dalla riga di comando.

Il repository degli studenti è solo un repository di output, quindi rimarrà sempre aggiornato, è chiaro agli studenti cosa è cambiato osservando i commit (perché mostrano solo le modifiche) ed è semplice da gestire.

Un ulteriore passo sarebbe quello di creare un git commit-hook che esegue automaticamente il tuo script.

Modifica: vedi che hai apportato una modifica al tuo post:

Ovviamente non voglio che gli studenti abbiano accesso alla soluzione (fino alla fine dell'incarico).

Ho il sospetto che sia chiaro ma per essere completo: basta rimuovere i tag intorno all'esercizio finito per pubblicare la risposta nello stesso modo in cui faresti per i normali aggiornamenti degli esercizi.


speravo di poterlo fare con un po 'di voodoo git, tuttavia la tua soluzione è molto pratica.
Ken,

Anche @Ken ci stava pensando, ma è uno strumento un po 'sbagliato per il lavoro sbagliato. Git unisce, aggiorna ecc, ma in generale non è l'idea di selezionare il codice. È bravo a mantenere la base di codice coerente su più macchine. Ecco perché ho pensato a un'altra soluzione. Quello che mi piace anche di questo approccio è che minimizza il rischio e il lavoro, quindi è facile tenerlo al passo. E, alla fine, dovresti comunque scrivere a mano il tuo messaggio di commit al repo degli studenti per dare un buon esempio ai tuoi studenti;)
Luc Franken,

Per aiutare Git a tenere traccia delle modifiche che è possibile apportare a un ramo studente nel repository dell'insegnante, eseguire lo script durante l'unione (o l'unione rimuovendo manualmente qualsiasi cosa tra i marcatori). Quindi sincronizzare il ramo studente localmente e spingerlo nel repository studente invece dell'origine dell'insegnante. In questo modo git sarebbe in una forma migliore per tenere traccia delle modifiche e avere la cronologia correttamente inoltrata da un repository a un altro. Il meglio di entrambi i mondi. Non ci ho provato, ma non vedo perché non funzionerebbe.
Newtopian,

1
Mi piace questo tranne l'idea di rimuovere i tag di fine inizio. Meglio manipolarli aggiungendo la parola "soluzione".
candied_orange,

@CandiedOrange è anche bello, d'accordo. La soluzione consentirebbe anche una formattazione diversa e differenzia chiaramente tra i tag dimenticati e la reale decisione di pubblicare la soluzione. @ newtopian: ci stavo pensando ma non ho visto abbastanza vantaggi. Inoltre ho deciso di vedere l'output dello studente come un tipo di codice totalmente diverso. Non è la vera fonte, quindi ho deciso di non farlo. Quello che farei con le filiali nel repository degli insegnanti è ad esempio: lavorare sui compiti per il prossimo semestre. Quando sei pronto, li unisci al master e quindi esegui lo script.
Luc Franken,

6

Potresti

  • Crea un repostory GitHub pubblico se commetti il ​​codice boilerplate
  • Fork questo repository come repostory GitHub privato
  • Risolvi i compiti nel repository biforcato
  • Unire ciascuna soluzione nel repository pubblico al termine dell'assegnazione

Ecco come implementerei questo flusso di lavoro:

  • Crea un repostory pubblico assignmentsospitato su GitHub. Aggiungi il codice del boilerplate per i compiti. Ad esempio, per ogni compito si introduce una nuova sottodirectory contenente il codice di caldaia dell'assegnazione.
  • Crea un nuovo repository privatoassignments-solved su GitHub. Clonare il assignmentsrepository sul computer e inviarlo al assignments-solved repository (essenzialmente fork il proprio repository come copia privata): 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
  • Aggiungi il assignments-solvedrepository come remoto al assignmentsrepository: cd assignments # change to the assignments repo on your machine git remote add solutions https://github.com/[user]/assignments-solved
  • Implementare ogni incarico nel assignments-solvedrepository. Assicurarsi che ogni commit contenga solo le modifiche di un compito.
  • Potresti voler creare un solvedramo nel assignmentsrepository, in modo che le assegnazioni originali non vengano modificate: cd assignments # change to the assignments repo on your machine git branch -b solutions git push -u origin
  • Quando si desidera pubblicare una soluzione in assignments, recuperare il solvedtelecomando e cherry-pickgli commit contenenti le soluzioni. cd assignments # change to the assignments repo on your machine git checkout solved git fetch solutions git cherry-pick [commithash] Dove [commithash]contiene il commit della tua soluzione.

Potresti anche essere in grado di implementare il flusso di lavoro implementando ogni assegnazione in un ramo separato del assignments-solvedrepository e quindi creando una richiesta pull nel assignmentsrepository. Ma non sono sicuro che funzionerà in GitHub, in quanto il assignments-solvedrepository non è un vero fork.


Ho usato con successo un metodo simile per separare un test di programmazione dalle risposte inviate. Nel mio caso, le soluzioni inviate vengono aggiunte ai singoli rami di un clone privato e non vengono mai unite al repository pubblico. Ha il vantaggio aggiuntivo di farmi vedere quale versione del test ha risolto ogni candidato, mentre si evolve nel tempo.
axl

0

Posso solo .gitignoreproporti un'utilità destinata a -ing e crittografare i file nel tuo repository. Il flusso di lavoro è leggermente difficile da usare, ma rende disponibili le controparti crittografate dei tuoi file nella copia di lavoro insieme ad altri file non segreti, il che consente di seguirli come al solito.

#!/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

Per creare un file segreto con il a.txttipo di nome file sshare -a a.txt. L'utilità crea il file a.txte il file aggiunto a .gitignore. Quindi crea una controparte "database" crittografata a.txt.sshareaggiungendo l' .sshareestensione al nome file.

Quindi puoi riempire a.txtcon del testo. Per salvare il suo stato subito prima di git commitdigitare sshare -s a.txt, quindi l'utilità richiede la password per crittografare il nuovo stato del file a.txt. Quindi utilty usando questa password aggiunge diff criptato tra lo stato precedente e quello corrente del file a.txtalla fine del a.txt.ssharefile.

Dopo aver recuperato / pull repository con file crittografati, è necessario eseguire l' sshareutilità per ciascun file utilizzando la -lchiave ("load"). In questo caso l'utilità decodifica i *.ssharefile in file di testo non tracciati da git nella copia di lavoro.

È possibile utilizzare password diverse per ciascun file segreto.

L'utilità consente a git di tracciare le modifiche in modo efficiente (la differenza dei .ssharefile è semplicemente una riga).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.