Working with gitlab
This commit is contained in:
parent
615ed805fa
commit
6bef42a71e
112
ui/src/components/WorkRepository.svelte
Normal file
112
ui/src/components/WorkRepository.svelte
Normal file
@ -0,0 +1,112 @@
|
||||
<script>
|
||||
import DateFormat from '../components/DateFormat.svelte';
|
||||
import { WorkRepository, getRemoteRepositories, getRepositories } from '../lib/repositories';
|
||||
import { ToastsStore } from '../stores/toasts';
|
||||
|
||||
let className = '';
|
||||
export { className as class };
|
||||
|
||||
export let work = {};
|
||||
export let readonly = false;
|
||||
|
||||
let repo_work = null;
|
||||
let repo_used = null;
|
||||
let remote_repos = [];
|
||||
|
||||
function refresh_repo_work() {
|
||||
repo_work = getRepositories(work.id);
|
||||
repo_work.then((repos) => {
|
||||
repo_used = repos[0];
|
||||
}, () => {
|
||||
repo_used = new WorkRepository({id_work: work.id});
|
||||
remote_repos = getRemoteRepositories();
|
||||
});
|
||||
}
|
||||
refresh_repo_work();
|
||||
|
||||
let submitInProgress = false;
|
||||
function submitWorkRepository() {
|
||||
submitInProgress = true;
|
||||
repo_used.save().then(() => {
|
||||
submitInProgress = false;
|
||||
refresh_repo_work();
|
||||
}, (error) => {
|
||||
submitInProgress = false;
|
||||
ToastsStore.addErrorToast({
|
||||
msg: "Une erreur s'est produite durant l'envoi de votre clef : " + error,
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await repo_work}
|
||||
<div class="{className} text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement de vos préférences de rendu …</span>
|
||||
</div>
|
||||
{:then repos}
|
||||
{#each repos as repo (repo.id)}
|
||||
<div class="{className} card">
|
||||
<div class="card-body d-flex justify-content-between">
|
||||
<div class="d-flex flex-column justify-content-center">
|
||||
<div>
|
||||
Dépôt lié : <strong style="font-family: monospaced">{repo.uri}</strong><br>
|
||||
Dernière récupération : <strong><DateFormat date={new Date(repo.last_check)} dateStyle="medium" timeStyle="medium" /></strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-column justify-content-center">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-info float-end mb-1"
|
||||
disable={submitInProgress || readonly}
|
||||
on:click={() => repo.delete().then(() => { refresh_repo_work() })}
|
||||
>
|
||||
Mettre à jour
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-danger float-end mt-1"
|
||||
disable={submitInProgress || readonly}
|
||||
on:click={() => repo.delete().then(() => { refresh_repo_work() })}
|
||||
>
|
||||
Supprimer cette liaison
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{:catch}
|
||||
<p>
|
||||
Voici la liste des dépôts reconnus :
|
||||
</p>
|
||||
<form class="{className} form-floating" on:submit|preventDefault={submitWorkRepository}>
|
||||
{#await remote_repos}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Récupération de vos dépôts GitLab …</span>
|
||||
</div>
|
||||
{:then rrepos}
|
||||
<select class="form-select col" disabled={readonly} bind:value={repo_used.uri}>
|
||||
{#each rrepos as r (r.Id)}
|
||||
<option value={r.http_url_to_repo}>{r.path_with_namespace}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<label>Dépôt GitLab pour ce travail :</label>
|
||||
{/await}
|
||||
<button
|
||||
type="submit"
|
||||
class="mt-2 btn btn-primary"
|
||||
disable={submitInProgress || readonly}
|
||||
>
|
||||
Utiliser ce dépôt
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="mt-2 btn btn-info"
|
||||
title="Rafraîchir la liste des dépôts"
|
||||
on:click={() => remote_repos = getRemoteRepositories()}
|
||||
>
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</form>
|
||||
{/await}
|
69
ui/src/lib/repositories.js
Normal file
69
ui/src/lib/repositories.js
Normal file
@ -0,0 +1,69 @@
|
||||
export class WorkRepository {
|
||||
constructor(res) {
|
||||
if (res) {
|
||||
this.update(res);
|
||||
}
|
||||
}
|
||||
|
||||
update({ id, id_user, id_work, uri, last_check }) {
|
||||
this.id = id;
|
||||
this.id_user = id_user;
|
||||
this.id_work = id_work;
|
||||
this.uri = uri;
|
||||
this.last_check = last_check;
|
||||
}
|
||||
|
||||
async delete() {
|
||||
const res = await fetch(`api/repositories/${this.id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {'Accept': 'application/json'}
|
||||
});
|
||||
if (res.status == 200) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
async save() {
|
||||
const res = await fetch(this.id?`api/repositories/${this.id}`:'api/repositories', {
|
||||
method: this.id?'PUT':'POST',
|
||||
headers: {'Accept': 'application/json'},
|
||||
body: JSON.stringify(this),
|
||||
});
|
||||
if (res.status == 200) {
|
||||
const data = await res.json();
|
||||
this.update(data);
|
||||
return data;
|
||||
} else {
|
||||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRemoteRepositories(userid) {
|
||||
const res = await fetch(userid?`api/users/${userid}/gitlabcri/repositories`:`api/gitlabcri/repositories`, {headers: {'Accept': 'application/json'}})
|
||||
if (res.status == 200) {
|
||||
return await res.json();
|
||||
} else {
|
||||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRepositories(wid, userid) {
|
||||
const res = await fetch(userid?`api/users/${userid}/works/${wid}/repositories`:`api/works/${wid}/repositories`, {headers: {'Accept': 'application/json'}})
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).map((r) => new WorkRepository(r));
|
||||
} else {
|
||||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRepository(kid) {
|
||||
const res = await fetch(`api/repositories/${kid}`, {headers: {'Accept': 'application/json'}})
|
||||
if (res.status == 200) {
|
||||
return new Repository(await res.json());
|
||||
} else {
|
||||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
}
|
@ -101,3 +101,19 @@ ZWxxdWUgY2hvc2UK ...
|
||||
</form>
|
||||
{/if}
|
||||
{/await}
|
||||
|
||||
<h3 class="mt-4">
|
||||
Comment utiliser votre clef PGP ?
|
||||
</h3>
|
||||
<p>
|
||||
Votre clef PGP peut vous servir à sécuriser les échanges de courriers électroniques. Par exemple les courriels officiels sur les listes de diffusion sensibles sont systématiquement signés (pour attester que c'est le responsable du projet qui en est bien à l'origine) ou encore si vous ne voulez pas que des tiers exploitent vos communications et/ou données personnelles (dans ce cas, on chiffre le contenu pour qu'il ne soit lisible que par le(s) destinataire(s) attendus).
|
||||
</p>
|
||||
<p>
|
||||
Avec git, vous pouvez signer chacun de <a href="https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits/" target="_blank">vos commits</a>, ou <a href="https://dev.to/shostarsson/how-to-use-pgp-to-sign-your-commits-on-github-gitlab-bitbucket-3dae#fountainpen-sign-tags-using-your-gpg-key" target="_blank">vos tags</a>.<br>
|
||||
Si vous souhaitez contribuer au <a href="https://www.kernel.org/doc/html/latest/process/maintainer-pgp-guide.html">noyau Linux</a> (ou tout autre projet d'envergure), il est nécessaire de signer vos contributions. Cela permet d'éviter <a href="https://www.theregister.com/2021/03/29/php_repository_infected/">un certain nombre d'attaques</a>.
|
||||
</p>
|
||||
<p>
|
||||
Vous approchez à grand pas (si ce n'est pas déjà fait !) de vos premières contributions sur des projets sérieux, qu'ils appartiennent à une entreprise ou bien à la communauté. Afin de mieux vous préparer à cette transition, nous vous demandons dans le cadre de ce cours, de faire vos rendus signés avec votre clef PGP.
|
||||
</p>
|
||||
|
||||
<div class="mb-5"></div>
|
||||
|
@ -14,24 +14,37 @@
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { user } from '../../../stores/user';
|
||||
import DateFormat from '../../../components/DateFormat.svelte';
|
||||
import SubmissionStatus from '../../../components/SubmissionStatus.svelte';
|
||||
import SurveyBadge from '../../../components/SurveyBadge.svelte';
|
||||
import WorkAdmin from '../../../components/WorkAdmin.svelte';
|
||||
import WorkRepository from '../../../components/WorkRepository.svelte';
|
||||
import { getScore } from '../../../lib/users';
|
||||
|
||||
export let work = null;
|
||||
let edit = false;
|
||||
</script>
|
||||
|
||||
{#await work then w}
|
||||
{#if $user && $user.is_admin}
|
||||
<button class="btn btn-primary ms-1 float-end" on:click={() => { edit = !edit; } } title="Éditer"><i class="bi bi-pencil"></i></button>
|
||||
{/if}
|
||||
<div class="d-flex align-items-center">
|
||||
<h2>
|
||||
<a href="works/" class="text-muted" style="text-decoration: none"><</a>
|
||||
{w.title}
|
||||
</h2>
|
||||
<SurveyBadge class="ms-2" survey={w} />
|
||||
</div>
|
||||
|
||||
{#if $user && $user.is_admin}
|
||||
{#if $user && $user.is_admin && edit}
|
||||
<WorkAdmin work={w} on:saved={() => edit = false} />
|
||||
|
||||
{#if w.description}
|
||||
<hr>
|
||||
{@html w.description}
|
||||
{/if}
|
||||
|
||||
<hr>
|
||||
<h3 class="mt-3">Notes</h3>
|
||||
<div class="card mt-3 mb-5">
|
||||
@ -71,18 +84,67 @@
|
||||
</table>
|
||||
{/await}
|
||||
</div>
|
||||
{:else if new Date(w.start_availability) > new Date()}
|
||||
<div class="alert alert-warning">
|
||||
<i class="bi bi-stopwatch-fill"></i>
|
||||
<strong>Ce travail n'est pas encore ouvert.</strong> Revenez plus tard !
|
||||
</div>
|
||||
{:else}
|
||||
{#await getScore(w)}
|
||||
<div class="spinner-border spinner-border-sm" role="status"></div>
|
||||
{:then grade}
|
||||
<div class="alert alert-info">
|
||||
<strong>Note finale :</strong> <span title="Établie le {grade.date}">{grade.score}</span> {#if grade.comment}– {grade.comment}{/if}
|
||||
</div>
|
||||
{:catch error}
|
||||
<div class="alert alert-danger">
|
||||
<i class="bi text-warning bi-exclamation-triangle-fill" title={error}></i>
|
||||
<strong>{error.message}</strong>
|
||||
</div>
|
||||
{/await}
|
||||
<dl style="columns: 3">
|
||||
<dt>Date de début</dt>
|
||||
<dd><DateFormat date={new Date(w.start_availability)} dateStyle="medium" timeStyle="medium" /></dd>
|
||||
<dt>Date de fin</dt>
|
||||
<dd><DateFormat date={new Date(w.end_availability)} dateStyle="medium" timeStyle="medium" /></dd>
|
||||
</dl>
|
||||
<hr>
|
||||
|
||||
{@html w.description}
|
||||
|
||||
<hr>
|
||||
|
||||
<h3 class="mt-3">Rendu</h3>
|
||||
|
||||
{#if !w.corrected}
|
||||
<p>
|
||||
Pour rendre votre travail, vous devez préalablement créer un dépôt Git sur la <a href="https://gitlab.cri.epita.fr/" target="_blank">forge de l'école</a>.<br>Ce dépôt DOIT :
|
||||
</p>
|
||||
<ul>
|
||||
<li>être dans l'espace de nom de votre utilisateur (à la fin de la liste des <span class="fst-italic">namespaces</span>),</li>
|
||||
<li>avoir la visibilité « Privé »,</li>
|
||||
<li>avoir invité <a href="https://gitlab.cri.epita.fr/nemunaire" target="_blank" style="font-family: monospaced">nemunaire</a> avec le rôle <span class="fst-italic">Reporter</span> une fois le dépôt créé,</li>
|
||||
<li>configuré un <span class="fst-italic">webhook</span> pointant sur <code>https://lessons.nemunai.re/callback/trigger.json</code></li>
|
||||
</ul>
|
||||
|
||||
{#if w.tag}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-lightbulb-fill text-info me-1"></i>
|
||||
Vous pouvez utiliser un dépôt pour tous les travaux à effectuer, ou créer un dépôt par travail.
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<WorkRepository class="mb-3" readonly={w.corrected || new Date(w.end_availability) <= new Date()} work={w} />
|
||||
<p>
|
||||
<strong>État du rendu :</strong> <SubmissionStatus work={w} />
|
||||
</p>
|
||||
{#if w.corrected}
|
||||
{#await getScore(w)}
|
||||
<div class="spinner-border spinner-border-sm" role="status"></div>
|
||||
{:then grade}
|
||||
<div class="alert alert-info">
|
||||
<strong>Note finale :</strong> <span title="Établie le {grade.date}">{grade.score}</span> {#if grade.comment}– {grade.comment}{/if}
|
||||
</div>
|
||||
{:catch error}
|
||||
<div class="alert alert-danger">
|
||||
<i class="bi text-warning bi-exclamation-triangle-fill" title={error}></i>
|
||||
<strong>{error.message}</strong>
|
||||
</div>
|
||||
{/await}
|
||||
{:else}
|
||||
<p>
|
||||
Pour être reconnu, vous devez pousser un tag <strong><a href="keys">signé</a></strong> sur votre dépôt. {#if w.tag}Le tag attendu doit commencer par : <code>{w.tag}</code>. Par exemple <code>{w.tag}v1.0</code>, <code>{w.tag}v1.1</code>, ...{/if} Seul le dernier tag que vous envoyez avant la date du rendu sera pris en compte. Vous pouvez donc faire autant de tag que vous le souhaitez d'ici la date du rendu. Seul le dernier sera pris en compte.
|
||||
</p>
|
||||
{/if}
|
||||
{/if}
|
||||
{/await}
|
||||
<div class="mb-5"></div>
|
||||
|
Reference in New Issue
Block a user