Generateur de rapport de release MD

Parce que la flemme

a marqué ce sujet comme résolu.

Salut les zesteurs !

Etant donne que je suis feignant mais que je veux participer a la redaction d'articles concernant le dev', j'ai bricoler un petit script python permettant de generer des bases de rapport de release.

Grosso modo, le script se connecte a l'API github en mode public (limite de 60 requêtes par heures et par IP donc) et interroge pour avoir la liste des milestones puis une liste des tickets de la milestone choisies.

Un rapport en MD est ensuite généré.

C'est une version tres cru pour le moment, mais comme ce n'est qu'un simple outil a utilisation "de temps en temps"… J'ai teste sur Python 2.7 et tout roule. (besoin de la dépendance requests installable avec pip)

Voici le script (qui peut être largement rendu plus propre, je suis toujours débutant en python XD ) !

Et nouveau, une version javascript sur jsbin : http://jsbin.com/weqakariqo/16/

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# -*- coding: utf-8 -*-
import codecs
import json
import requests
import sys

# Script inspired from https://gist.github.com/unbracketed/3380407

def get_milestones():
    jalons = []
    r = requests.get("https://api.github.com/repos/zestedesavoir/zds-site/milestones")
    jalons += parse_milestones(r)

    # more pages? examine the 'link' header returned
    if 'link' in r.headers:
        pages = dict(
            [(rel[6:-1], url[url.index('<')+1:-1]) for url, rel in
                [link.split(';') for link in
                    r.headers['link'].split(',')]])
        while 'last' in pages and 'next' in pages:
            r = requests.get(pages['next'])
            jalons += parse_milestones(r)
            if pages['next'] == pages['last']:
                break

    return jalons;

def parse_milestones(req):
    if not req.status_code == 200:
        raise Exception(req.status_code)

    milestones = []
    for milestone in req.json():
        milestones.append(milestone)

    return milestones

def get_issues(milestone_id):
    r = requests.get("https://api.github.com/repos/zestedesavoir/zds-site/issues?milestone={}&state=all".
        format(milestone_id))

    [o, cb, ce, cu] = parse_issues(r)

    openissues = [] # still open issue
    closed_bug = [] # closed bug issue
    closed_evo = [] # closed evolution issue
    closed_unk = [] # closed not bug or evo issue

    openissues += o
    closed_bug += cb
    closed_evo += ce
    closed_unk += cu

    # more pages? examine the 'link' header returned
    while 'link' in r.headers:
        pages = dict(
            [(rel[6:-1], url[url.index('<')+1:-1]) for url, rel in
                [link.split(';') for link in
                    r.headers['link'].split(',')]])
        if 'next' in pages:
            r = requests.get(pages['next'])
            [o, cb, ce, cu] = parse_issues(r)
            openissues += o  # still open issue
            closed_bug += cb # closed bug issue
            closed_evo += ce # closed evolution issue
            closed_unk += cu # closed not bug or evo issue
        else:
            break # exit the while loop

    return (openissues, closed_bug, closed_evo, closed_unk);

def parse_issues(req):
    if not req.status_code == 200:
        raise Exception(req.status_code)

    openissues = [] # still open issue
    closed_bug = [] # closed bug issue
    closed_evo = [] # closed evolution issue
    closed_unk = [] # closed not bug or evo issue

    for issue in req.json():
        # check open issues
        if issue['state'] == "open":
            openissues.append(issue)
        # check closed issue
        elif issue['state'] == "closed":
            labels = []
            for l in issue['labels']:
                labels.append(l["name"].lower())

            if "bug" in labels:
                closed_bug.append(issue)
            elif "evolution" in labels:
                closed_evo.append(issue)
            else:
                closed_unk.append(issue)

    return openissues, closed_bug, closed_evo, closed_unk


def dump_issues(milestone, openissues, closed_bug, closed_evo, closed_unk):
    # output all tables to a file
    with codecs.open("output", "w", "utf-8") as out:
        out.write(u"Rapport pour le jalon **[{}](https://github.com/zestedesavoir/zds-site/milestones/{})** *({})*\n\n"
            .format(milestone["title"], milestone["title"], milestone["description"]))
        out.write(u"{} tickets sont compris dans ce jalon ({} ouverts et {} fermés)\n\n".
            format(len(openissues)+len(closed_bug)+len(closed_evo)+len(closed_unk),
                len(openissues), len(closed_bug)+len(closed_evo)+len(closed_unk)))
        out.write(u"# Ticket toujours ouvert\n\n")
        out.write(mdarray(openissues))
        out.write(u"# Ticket fermé\n\n")
        out.write(u"## Corrections de bug\n\n")
        out.write(mdarray(closed_bug))
        out.write(u"## Evolutions\n\n")
        out.write(mdarray(closed_evo))
        out.write(u"## Non défini\n\n")
        out.write(mdarray(closed_unk))

def mdarray(tableau):
    if len(tableau) == 0:
        return u"Aucun ticket\n\n"
    ret  = "Ticket # | Titre | Label(s)\n"
    ret += "---------|-------|---------\n"
    for issue in tableau:
        labels = ""
        for label in issue["labels"]:
            labels += label["name"] + ", "
        labels = labels[:-2]
        ret += u"[#{}]({}) | {} | {}\n" \
            .format(issue["number"],
                    issue["html_url"],
                    issue["title"],
                    labels)
    ret += "\n"
    return ret

###############################################################

milestones = get_milestones()

# print the milestones
for i in range(0, len(milestones)):
    print "{}. {}".format(i+1, milestones[i]['title'])

jalon_id = 0
while not jalon_id:
    try:
        jalon_id = raw_input("Quel milestone voulez-vous generer (id) (q=quitter) ? ")
        jalon_id = int(jalon_id)
        if jalon_id > len(milestones):
            print "{} ne fait pas parti des milestones connues".format(jalon_id)
            jalon_id = 0
    except:
        if jalon_id.lower() == "q":
            sys.exit("Bye Bye !")
        print "{} n'est pas un nombre !".format(jalon_id)
        jalon_id = 0

print "Recuperation des tickets..."

[openissues, closed_bug, closed_evo, closed_unk] = get_issues(milestones[jalon_id-1]["number"])
dump_issues(milestones[jalon_id-1], openissues, closed_bug, closed_evo, closed_unk)

+4 -0

J'hésitais car ca prend un peu de place pour les grosses releases… Mais allez zou, voici la milestone 1.3 par exemple : (beuh la description s'est pas affichée :( ).

Après faites vous plaisir pour tester, les requetes sont en mode public donc pas de login/mot de passe a passer au script, il y a juste à le lancer tel quel !


Rapport pour le jalon Version 1.3 ()

43 tickets sont compris dans ce jalon (3 ouverts et 40 fermés)

Ticket toujours ouvert

Ticket # Titre
#1650 Le logo sur la page « À propos » rend très mal
#1600 Garbage collector automatique sur les dépôts Git techniques
#1529 Problèmes de fiablité de npm

Ticket fermé

Corrections de bug

Ticket # Titre
#1776 Mauvais lien de citation dans les commentaires des tutoriels
#1736 Pas de liens pour télécharger l'archive d'un tuto récent
#1694 Erreur 500 quand on télécharge un tuto pas encore créé
#1682 Erreur 500 si la pagination n'est pas un int
#1680 Les liens des miniatures ne sont pas préfixés
#1672 Les emails sont illisibles sur la page de profil
#1652 Le travis.yml est invalide
#1644 L'icône des tutoriels chevauche le texte
#1643 Profil : rendre la section "Article" de la sidebar plus "logique"
#1631 Message d'avertissement pas en UTF8 = 500
#1618 Manque de coherence timestamp alert moderation
#1608 Notification fantôme lors d'une dépublication
#1606 Décalage des pouces sur mobile
#1557 Le bouton "Afficher l'aide Markdown" se superpose au bouton "Aperçu" sur mobile
#1544 Les tutoriels deviennent inaccessibles si on les renomme

Evolutions

Ticket # Titre
#1751 Ajout de la couverture de tests pour les flux RSS
#1739 Utiliser les scripts npm pour gulp et bower
#1729 Ajout d'un filtre « Sujets sans réponse » dans les forums
#1720 Ajouter les metadatas pour les utilisateurs
#1701 Outils statistique sur les posts des membres
#1647 Cohérence lors de la màj de la version locale
#1632 MAJ des noms de domaine interdits dans les mails
#1626 Améliorer l'accessibilité des listes de topics
#1621 Internationalisation du site
#1153 Boutons et éditeur en lecture seule
#1135 Envoi de MP validation
#1133 Ajouter un message de commit
#766 Voir les diffs des différentes version d'un tuto
#523 Éviter les textes en dur dans le code

Non défini

Ticket # Titre
#1779 Corrige les tests qui ne passent pas.
#1773 Travis (notre environnement d'intégration continue) est maintenant ISO préprod et prod
#1763 Initialisation de la documentation des forums
#1758 Mise à jour des requirements python
#1756 Supprimer la possibilité de s'envoyer un MP via son propre profil
#1754 Utilise l'engine pngsmith par défaut
#1749 Augmentation de la couverture de tests pour les MP
#1735 Amélioration de la documentation sur l'installation de Gulp
#1732 Correction d'URL et uniformisation du README
#1633 Rendre les messages d'erreur et d'avertissement plus cohérents entre eux
#1546 Bug dans les liens RSS
+2 -0

Excellent !

Pour info, je suis en train depuis quelques semaines de développer une interface web de gestion de projets "à la ZdS" suite aux interrogations qui étaient nées sur "quelle interface il nous faut 'par-dessus' Github" (JIRA, …). Que vous n'êtes pas du tout les seuls à vous poser.

J'ai gardé les process de développement de ZdS en tête pendant que je spécifiais le projet, mais également mes projets personnels ou professionnels.

L'import d'issue Github via API (publique ou non) est évidemment au programme, et merci pour ton boulot, c'est une bonne source d'inspiration :)

Les idées de base du projet :

  • KISS (ne pas tomber dans l'usine à gaz type MS Project ou autre) : modulaire et évolutif. Mais à destinations de "petites entités" (communauté, PME, projets personnels)

  • Lien fort avec Github (import / export) utilisation intensive des APIs Github

  • Doit permettre une vue à tous les acteurs du projet (pas seulement les dev, aussi l'infra, la comm', …)

  • "Facettes" (vues adaptées) suivant l'organisation choisie. E.g. :

    • Les développeurs ont besoin de voir un historique de commits, de suivre des issues sur Github,
    • la comm' a juste besoin de savoir qu'une feature est en prod',
    • la MOA doit savoir qu'on a besoin de spécifications ou qu'elles ne sont pas claires,
    • le graphiste/ergonome/designer doit savoir de quelles ressources les développeurs ont besoin
  • Définition (facultative) de workflows de travail pour suivre facilement l'avancement des issues (EP ou BugFix) et repérer "où ça bloque".

  • Rédaction en zMarkdown

  • Possibilité d'attacher des documents, des livrables, …

  • Wiki / Documentation

  • Gestion des réunions (meetings) avec compte-rendus de meeting avec liens vers les issues discutées (typiquement les zestMeeting)

J'espère vraiment que ça pourra servir à ZdS.

+3 -0

A oui j'ai oublié de mentionner ça.

  • 100% API, pas de code dans des vues / ViewControllers. Rendu côté serveur réduit au strict minimum.

  • Tout ce qui est temps-réel (aperçu édition, notifications, messages, progressStatus pendant des synchros Github, …) passe par des websockets

  • Le plus léger possible côté serveur

Du coup je suis parti sur la pile suivante :

  • Vert.x pour l'aspect polyglote + websockets + perfs (en Groovy pour l'instant)

  • AngularJS côté client pour gérer tout le rendu

  • Foundation pour que ça ressemble à quelque chose pendant le dev

  • Hibernate parce que j'ai besoin d'un ORM

  • Une BDD relationnelle mais j'ai pas fait mon choix, j'ai pris PostgreSQL par défaut pour l'instant

Mais l'idée principale qui m'a poussé à faire ça c'est d'essayer de "plonger" dans un webframework.

Je vais avoir besoin de routes, de mapping, de marshalling, … Et je n'ai pas de "pile" à disposition (type Spring, Django, Rails, Grails …) qui généralement font ça bien.

Et j'aimerais (par aspect didactique) comprendre comment implémenter un tel framework très simple (y'a pas de trucs fantasques de JEE par exemple) proprement sur un serveur "temps-réel" réduit au strict minimum.

PS : ça va énormément limiter d'éventuels contributions, j'en suis conscient.

+0 -0

Il me semble que si le domaine d'exploitation est complètement différent tu as le droit de t'en servir. En gros il ne faut pas qu'un client puisse être induit en erreur. S'il atteri sur ton site en cherchant des boissons energisantes a priori il sait que ce n'est pas le bon projuice, donc tu n'exploites pas l'image de l'autre (mais mes cours de PI sont loin, ping Arius ?)

+1 -0

Du coup, il resterait comme NDD projuice.io ce qui est suffisant comme URL pour présenter le projet puisque l'objectif et que les gens l'installent chez eux. Je pars là-dessus et j'écris une petite présentation ce week-end :)

Merci Arius, Eskimon et surtout merci bien firm1 !

+3 -0

D'ailleurs d'où sort cette mode des NDD en .io ?

SpaceFox

D'après Wikipédia :

Les noms de domaine .io sont populaires parmi les startups[2], ainsi que dans le domaine de l'informatique, où le sigle IO est identifié comme l'abréviation de Input/Output (entrées-sorties).

Après je ne sais pas qui a utilisé ce NDD pour l'informatique en premier…

+2 -0

Idem, je n'en connais pas l'origine, mais c'est vrai que je trouve l'idée d'un NDD qui indique "ceci est un projet informatique, intéressant pour des développeurs" intéressante. Donc autant l'employer.

J'imagine que pas mal de projets ont contribué à cette hype, je pense en premier lieu à Github.

Je reviens vers vous ce week-end, j'ai un peu avancé hier dans le cambouis du webframework "à la Jersey".

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte