Le lundi de travail a commencé par le dialogue suivant:
Leader (P): Votre équipe ne sait pas exactement qui fait quoi.
I (I): Oui, nous n'avons pas d'outil qui refléterait l'image générale du travail sur les tâches. Il y a des tableaux de kanban dans le hitlab, mais ils ne sont que dans le contexte de projets et de groupes. Un tableau kanban commun résoudrait le problème.
R: Faites ensuite une planche.
Moi: Le matin, ce sera prĂŞt.
TĂ´t
ou tard, le moment vient dans la vie d'un chef d'équipe débutant quand il se rend compte que son équipe a besoin d'une planche kanban. Il supprime l'anxiété au sujet du contrôle du processus de développement et donne confiance en l'avenir. Habituellement, ce problème est résolu par la plus faible demande au gestionnaire d'acheter un tableau magnétique, un ensemble d'autocollants multicolores et quelques marqueurs pour le tableau. Eh bien, ou en utilisant des services comme Jira. Mais notre équipe a un développeur à distance, et gitlab en circuit fermé (non disponible sur Internet pour des raisons de sécurité de l'information), j'ai donc dû choisir la plus simple des deux solutions possibles:
a) la création d'un bras mécanique et d'un contrôleur pour celui-ci, qui recolleront à distance les autocollants sur la carte, alors que pour ne pas compliquer la décision, nous devrons écrire sur les autocollants pour notre collègue inaccessible, ce qui est injuste;
b) mise en place d'un tableau de kanban logiciel qui collecterait toutes les tâches de notre hitlab.
Bien sûr, l'âme gisait contre le tube de la planche physique. J'ai même commencé à penser à utiliser DualShock 4 comme contrôleur de bras mécanique, mais j'ai moi-même désigné l'échéance le lendemain matin, j'ai donc dû m'en sortir avec une solution logicielle sans âme.

Avant de commencer à décrire la partie technique de l'histoire, je vais vous expliquer comment nous utilisons gitlab à Onlanta. Les groupes dans la hitlab que nous avons correspondent au client interne / externe ou à un grand projet distinct, et le projet est un référentiel pour le code d'un service particulier. Par exemple, nous avons un groupe d'un panneau de contrôle de facturation dans lequel quatre projets sont des services et des applications Web, et il y a un groupe marketing dans lequel il y a un projet pour un site d'entreprise, un microservice pour l'intégration avec amoCRM, toutes sortes de pages de destination, etc. Nous avons actuellement neuf groupes actifs et il y a moins de programmeurs, donc tous les programmeurs participent à tous les groupes.
Et tout ce dont nous avions besoin était une page avec toutes les tâches du hitlab, de tous les groupes et projets, mais cette fonctionnalité dans GitLab, malheureusement, ne l'est pas. Google rapide n'a pas aidé à trouver une solution autonome, donc
Nous aurons besoin de:
- backend sur Node.js - pour collecter des tâches avec GitLab et les transférer au client;
- client sur Vue.js - pour le faire rapidement et magnifiquement;
- assaisonnez le tout avec TypeScript, bien sûr!
- PostgreSQL - où nous allons stocker des informations sur les tâches et leur position sur le tableau;
- 8 heures de temps de travail;
- bonne humeur.
La condition la plus importante est que le développement soit facile et amusant, appelons-le «programmation facile».
Le zoo
Pour des raisons évidentes, je ne peux pas utiliser les projets et les tâches de notre hitlab d'entreprise pour illustrer l'article sur Habré, j'ai donc déployé mon hitlab, avec des singes et des crocodiles. Oui, c'est un gitlab pour un zoo fictif, mais il n'y aura pas de tâches moins sérieuses que dans n'importe quel autre gitlab. Jetez un œil à la liste des projets:
karcass
Au cours des années de développement, j'ai développé ma propre vision de la façon dont l'application backend sur Node.js doit être construite, et cette approche a pris la forme d'un cadre - un ensemble de fichiers. Pour créer un nouveau projet, je viens de copier le répertoire avec le modèle. Cela ne pouvait pas durer éternellement, et j'ai créé le
package karcass npm (le nom carcasse a déjà été pris par le même passionné de vélo que moi), ce qui automatise le processus de création de la base de l'application:
Nous devons maintenant gérer la réception et le stockage des groupes, des projets, des tâches et, bien sûr, des interprètes.
TypeORM
"Je vois des entités ..."
- des discussions sur le forum des médiums
Puisque nous devons présenter les données du gitlab sous une forme qui nous convient, entre la collecte de ces données et leur présentation, nous devons en quelque sorte les stocker. Récemment, j'ai découvert l'incroyable bibliothèque pour travailler avec des bases de données,
TypeORM, incroyable pour l'écosystème JS. Il est toujours vert, mais a toutes les chances d'évincer Sequelize du trône.
En quelques dizaines de minutes, des migrations et des classes pour les
utilisateurs , les
groupes , les
projets et les
tâches apparaissent. En voici un:
import { Entity, PrimaryColumn, Column } from 'typeorm' @Entity({ name: 'group' }) export class Group { @PrimaryColumn('integer') public id!: number @Column('varchar') public name!: string @Column('varchar') public url!: string }
Et pour créer des migrations, j'ai de nouveau créé un
vélo assistant
CreateMigrationCommand . Cette commande est très simple à utiliser:
Dans la classe Issue, en plus des champs stockant des données de Gitlab, nous ajoutons des champs en raison desquels tout le monde a commencé, c'est-à -dire, indiquant la position de la tâche sur le tableau kanban:
export class Issue extends AbstractEntity { @Column('varchar', { name: 'kanban_status' }) public kanbanStatus?: 'new'|'planed'|'working'|'checking'|'done' @Column('int', { name: 'kanban_order' }) public kanbanOrder?: number }
Nous avons besoin d'informations
Quand j'ai commencé ma connaissance de GitLab, il me semblait un produit simple (lire "détesté"), mais en travaillant avec lui tous les jours, j'ai réalisé à quel point j'avais tort. Cette fois, j'ai donc été surpris - il s'avère que GitLab possède une API très riche qui couvre presque toutes les fonctionnalités disponibles via l'interface utilisateur. Mais pour résoudre notre problème, vous n'aurez besoin que de quatre méthodes, dont les noms parlent d'eux-mêmes:
/ utilisateurs ,
/ groupes ,
/ projets ,
/ projets /: id / issues . GitlabService sera responsable de l'interaction directe avec GitLab, et d'autres classes y accéderont. Par exemple, l'implémentation de la méthode updateGroups de la classe GroupService ressemble à ceci:
export class GroupService extends AbstractService { public async updateGroups() { for (const data of await this.app.gitlabService.getGroups()) {
La commande
updateProjectsCommand sera responsable de l'opération d'obtention des informations du gitlab et de la mise à jour des informations pertinentes dans notre base de données, qui peuvent être appelées à partir de la console, mais notre application la lancera elle-même avec la fréquence spécifiée dans la configuration:
export class Application { protected initCron() { if (this.config.gitlab.updateInterval) { setInterval(async () => { if (!this.updateProjectsCommand) { this.updateProjectsCommand = new UpdateProjectsCommand(this) } await this.updateProjectsCommand.execute() }, this.config.gitlab.updateInterval * 1000) } } }
Nous ne coderons pas en dur le nombre, la couleur et le nom des colonnes sur le tableau, mais les rendons personnalisables dans config.js. Nous en avons cinq, mais pour quelqu'un, peut-être seulement deux sont nécessaires et de couleur acide:
columns: [ { key: 'new', title: '', color: 'rgb(255, 255, 219)' }, { key: 'planed', title: '', color: 'rgb(236, 236, 191)' }, { key: 'working', title: ' ', color: 'rgb(253, 214, 162)' }, { key: 'checking', title: ' ', color: 'rgb(162, 226, 253)' }, { key: 'done', title: '', color: 'rgb(162, 253, 200)' }, ],
L'essentiel est qu'il y en ait au moins deux, sinon l'application ne démarre pas.
Pour interagir avec le front, nous avons besoin d'une méthode qui reçoit les tâches de la base de données et les trie en «colonnes»:
Afficher IssueService.ts export class IssueService extends AbstractService { public async getKanban() { let issues = await this.issueRepository.find({ where: { updatedTimestamp: MoreThanOrEqual(new Date().getTimestamp() - 60 * 60 * 24 * 30) }, order: { kanbanOrder: 'ASC', updatedTimestamp: 'DESC' }, }) const keys = this.app.config.columns.map(c => c.key) const result: { [key: string]: Issue[] } = {} for (let ki = keys.length - 1; ki >= 0; ki--) { const key = keys[ki] if (ki === keys.length - 1) {
Et un contrôleur qui enregistre de nouvelles positions de tâches:
Afficher IssueController.ts export default class IssueController extends AbstractController { public async kanbanUpdate(data: IQueryData) { const keys = this.app.config.columns.map(c => c.key) for (const c of data.params as { key: string, title: string, color: string, issues: number[] }[]) { if (keys.indexOf(c.key) < 0) { continue } let index = 0 for (const i of c.issues) { index++ const issue = await this.app.issueService.getIssue(i) if (!issue) { continue } issue.kanbanStatus = c.key issue.kanbanOrder = index this.app.issueService.issueRepository.save(issue) } } } }
Stosh. Nous collectons des informations à partir de GitLab, préparons des données pour l'affichage du tableau, savons même comment enregistrer les positions mises à jour des tâches sur le tableau. Il ne reste plus qu'à faire la planche elle-même. Et ici, nous sommes armés d'un outil puissant:
Vue CLI - au lieu de mille mots
TSX
Pour faciliter la refactorisation, la recherche de bogues, le peluchage et toute autre tranquillité d'esprit, nous utilisons presque des modèles tsx que Vue.js prend en charge
* . Par exemple, le composant le plus important de notre forum, le composant de la présentation de la tâche:
Afficher le code Issue.tsx import { Vue, Component, Prop, Watch } from 'vue-property-decorator' import './Issue.css' import { CreateElement, VNode } from 'vue' interface IIssue { groupId: number groupName: string groupUrl: string projectName: string projectUrl: string url: string title: string executor: { color: string, name: string } spent: number estimate: number } @Component export default class extends Vue { @Prop() public issue!: IIssue public issueValue!: IIssue public selectUser = false @Watch('issue', { immediate: true }) public onIssueChange() { this.issueValue = this.issue }
Ce composant affiche une barre des tâches. Notez que la tâche a réellement passé et prévu du temps:
Il existe également des
composants Column.tsx et
Kanban.tsx . Seuls trois composants assurent la présentation des tâches au tableau:
Vue.Draggable , dont les jambes se développent à partir de
SortableJS , est responsable du déplacement des tâches sur la planche. Il est extrêmement simple à utiliser:
import Draggable from 'vuedraggable' export default class extends Vue { public render(h: CreateElement): VNode { <Draggable class="kvcIssues" vModel={ this.issuesValue } group={ { name: 'issues', pull: true, put: true } } onEnd={ this.onDrag } onAdd={ this.onDrag } > { this.issuesValue.map(i => <Issue key={ i.id } issue={ i } />) } </Draggable> /* ... */ } }
Vous avez sûrement remarqué que chaque utilisateur a sa propre couleur, qui est utilisée pour afficher la connexion. C'est très pratique - parmi un tas de tâches, vous pouvez rapidement trouver la vôtre. Il existe deux façons d'obtenir la «multicolorité»: définir la couleur avec les mains pour chaque utilisateur ou la générer en fonction de quelque chose.
Donnez-moi votre nom d'utilisateur et je vous dirai de quelle couleur vous avez
L'obturateur de couleurs de la classe User est responsable de la génération des couleurs. J'ai spécifiquement implémenté cette fonctionnalité sur le backend, car j'avais besoin de cryptographie, et c'est un crime de faire glisser une bibliothèque entière vers l'avant pour une si petite tâche. Et c'est plus correct lorsque l'arrière, et non l'avant, est responsable des caractéristiques des entités:
export class User extends AbstractEntity { public get color() { const hash = crypto.createHash('md5').update(this.username).digest()
Pour ceux qui veulent savoir quelle couleur leur est destinée, je n'étais pas trop paresseux
pour couper l'extrait sur CodePen .
C'est tout
Le mardi de travail a commencé par le dialogue suivant:
R: Eh bien, comment la planche est-elle prĂŞte? ..
Moi: Oui, ici.
R: Oh, cool!
Le résultat de ma "journée de travail" se trouve ici:
https://github.com/onlanta/kanban . Vous pouvez non seulement le voir, le toucher, mais aussi l'utiliser (instructions ici). Et vous pouvez construire sur lui un traqueur de temps entier pour hitlab, ce que nous avons fait dans l'entreprise.
Pourquoi ai-je décidé d'écrire cette note? C'est l'un des récents moments forts de mon activité professionnelle, où la tâche était importante, intéressante et en même temps assez simple, et sa solution s'est avérée facile et élégante. Je suggère dans les commentaires de rappeler des tâches similaires de votre pratique.
Eh bien, oĂą en sommes-nous sans postes vacants.