Le travail dans l'équipe de gestion de la configuration est associé à la garantie de la fonctionnalité des processus de construction - assemblage des produits de l'entreprise, vérification préliminaire du code, analyse statistique, documentation et bien plus encore. De plus, nous travaillons constamment à l'optimisation de divers processus et, ce qui est merveilleux, nous sommes pratiquement libres de choisir des outils pour ce travail intéressant. Ensuite, je vais expliquer en détail comment, n'ayant qu'un niveau de connaissance différent en C # et C ++, j'ai créé un service WCF fonctionnel pour travailler avec des files d'attente de correctifs. Et pourquoi j'ai décidé que c'était très important.

Automatisation une fois ou instruction de 117 pages encore et encore
Une petite digression pour que vous compreniez pourquoi je m'inquiète tant de l'automatisation et de l'optimisation des processus.
Avant Veeam, je travaillais pour une grande entreprise internationale - j'étais le chef d'équipe de l'équipe de gestion de la configuration, j'étais impliqué dans la construction de l'application et son déploiement dans des environnements de test. Le programme a été développé avec succès, de nouvelles fonctions ont été ajoutées, une documentation a été écrite, dont j'ai également pris en charge le support. Mais j'ai toujours été surpris de voir qu'un programme aussi sérieux ne dispose pas d'un système de configuration de paramètres normal, dont il y avait des dizaines, voire des centaines.
J'ai parlé avec les développeurs de ce sujet et j'ai reçu une réponse - le client n'a pas payé pour cette fonctionnalité, n'était pas d'accord sur son coût, donc la fonctionnalité n'a pas été mise en œuvre. Mais en fait, l'AQ a souffert et nous, l'équipe SM, avons été directement touchés. La configuration du programme et sa configuration préliminaire ont été effectuées à travers de nombreux fichiers de configuration, chacun ayant des dizaines de paramètres.
Chaque nouvelle version, chaque nouvelle version apportait ses modifications à la configuration. Les anciens fichiers de configuration ne pouvaient pas être utilisés, car ils étaient souvent incompatibles avec la nouvelle version. En conséquence, chaque fois avant de déployer la build pour le test ou sur les machines de travail des testeurs, vous avez dû passer beaucoup de temps à configurer le programme, à corriger les erreurs de configuration, à consulter constamment les développeurs sur le thème "pourquoi cela ne fonctionne-t-il pas comme ça maintenant?" En général, le processus n'a pas été optimisé.
Pour aider à la configuration, nous avons eu une instruction de 117 pages en taille de police Arial 9. Nous avons dû lire très, très attentivement. Parfois, il semblait plus facile de construire un noyau Linux avec les yeux fermés sur un ordinateur éteint.
Il est devenu clair que l'optimisation ne pouvait pas être évitée ici. J'ai commencé à écrire mon configurateur pour un programme prenant en charge les profils et la possibilité de modifier les paramètres en quelques secondes, mais le projet est arrivé à sa phase finale, et je suis passé à travailler dans un autre projet. Dans ce document, nous avons analysé de nombreux journaux d'un système de facturation pour d'éventuels bogues du côté serveur. L'automatisation de nombreuses actions utilisant le langage Python m'a sauvé de la quantité monstrueuse de travail manuel. J'ai vraiment aimé ce langage de script, et avec son aide, nous avons créé un ensemble de scripts d'analyse pour toutes les occasions. Ces tâches qui ont nécessité plusieurs jours d'analyse approfondie selon le «cat logfile123 | grep something_special ”, a pris quelques minutes. Tout était super ... et ennuyeux.
Gestion de la configuration - Nouvelles aventures
Je suis venu à Veeam en tant que chef d'équipe pour une petite équipe CM. De nombreux processus ont nécessité une automatisation, une optimisation et une refonte. Mais il y avait une liberté totale dans le choix des outils! Le développeur doit utiliser un langage de programmation spécifique, de style code, un ensemble spécifique de bibliothèques. SM, d'autre part, peut ne rien utiliser du tout pour résoudre la tâche, s'il a suffisamment de temps, de courage et de patience pour cela.
Veeam, comme de nombreuses autres sociétés, a pour tâche d'assembler les mises à jour des produits. La mise à jour comprenait des centaines de fichiers, et il était nécessaire de modifier uniquement ceux qui avaient changé, en tenant compte d'un certain nombre de conditions importantes. Pour ce faire, nous avons créé un script PowerShell volumineux qui pourrait monter dans TFS, sélectionner des fichiers, les trier dans les dossiers nécessaires. La fonctionnalité du script a été complétée, elle est devenue progressivement énorme, il a fallu beaucoup de temps pour déboguer et constamment quelques béquilles pour démarrer. Il était urgent de faire quelque chose.
Que voulaient les développeurs
Voici les principales plaintes:
- Impossible de mettre en file d'attente les correctifs. Vous devez constamment vérifier la page Web pour voir quand l'assemblage du correctif privé se termine et vous pouvez commencer à créer le vôtre.
- Il n'y a pas de notifications d'erreurs - pour voir les erreurs dans l'interface graphique de l'application d'assemblage, vous devez vous rendre sur le serveur et regarder beaucoup de journaux volumineux.
- Il n'y a pas d'historique de build pour les correctifs privés.
Il était nécessaire de gérer ces tâches et d'ajouter d'agréables petites choses que les développeurs ne refuseraient pas.
Que sont les correctifs privés
Un correctif privé dans le contexte de notre développement est un certain ensemble de corrections dans le code, qui est stocké dans l'étagère de Team Foundation Server pour la branche de publication. Une petite clarification pour ceux qui ne connaissent pas trop la terminologie TFS:
- check-in - un ensemble de changements locaux dans le code source, qui est apporté au code stocké dans TFS. Cette vérification peut être vérifiée à l'aide de processus d'intégration continue / d'archivage fermé qui vous permettent d'ignorer uniquement le code correct et de rejeter toutes les vérifications qui violent la collecte du projet final.
- shelveset - un ensemble de changements locaux dans le code source qui n'est pas directement apporté au code source situé dans TFS, mais qui est accessible par son nom. Le shellset peut être déployé sur la machine locale du développeur ou du système de build pour fonctionner avec du code modifié qui n'est pas inclus dans TFS. En outre, le shellset peut être ajouté à TFS comme vérification après le déploiement, lorsque tout le travail est terminé. Par exemple, le contrôleur de porte fonctionne de cette façon. Tout d'abord, le shellset sur le générateur est vérifié. Si la vérification réussit, le shellset se transforme en vérification!
Voici ce que fait le générateur de correctifs privé:
- Obtient le nom (numéro) du shellset et le déploie sur le générateur de correctifs privé. En conséquence, nous obtenons le code source de la version du produit ainsi que les modifications / correctifs du shellset. La branche de publication reste inchangée.
- Sur un générateur de correctifs privés, un projet ou une série de projets va pour lesquels un correctif privé a été effectué.
- L'ensemble des fichiers binaires compilés est copié dans le répertoire réseau du correctif privé. Le catalogue contient le nom du shellset, qui est une séquence de nombres.
- Le code source du générateur de correctifs privés est restauré dans sa forme d'origine.
Pour la commodité des développeurs, une interface Web est utilisée où vous pouvez spécifier le produit pour lequel vous souhaitez collecter un correctif privé, spécifier le numéro de shellset, sélectionner les projets pour lesquels vous souhaitez collecter un correctif privé et ajouter l'assembly du correctif à la file d'attente. La capture d'écran ci-dessous montre la version de travail finale de l'application Web, qui affiche l'état actuel de la build, la file d'attente des correctifs privés et l'historique de leur assemblage. Dans notre exemple, seule la file d'attente pour l'assemblage des correctifs privés est prise en compte.
Quel était le mien
- Un générateur de correctifs privés qui a collecté des correctifs privés à partir du shellset TFS en lançant une application console avec les paramètres de ligne de commande donnés.
- Veeam.Builder.Agent - un service WCF écrit par Veeam qui lance l'application avec des paramètres en mode console sous l'utilisateur actuel et renvoie l'état actuel de l'application.
- Le service Web IIS est une application sur Windows Forms qui vous permet de saisir le nom du shellset, les paramètres spécifiés et de démarrer le processus de création d'un correctif privé.
- Une connaissance très peu approfondie de la programmation est le C ++, un peu de C # à l'université, et l'écriture de petites applications pour l'automatisation, ajoutant de nouvelles fonctions aux processus de construction actuels et comme passe-temps.
- Des collègues expérimentés, Google et des articles indiens MSDN sont des sources de réponses à toutes les questions.
Que ferons-nous
Dans cet article, je dirai comment j'ai implémenté la mise en file d'attente de l'assemblage des correctifs et leur lancement séquentiel sur le générateur. Voici les parties de la solution:
- QBuilder.AppQueue est mon service WCF qui fournit le travail avec la file d'attente de génération et appelle le service Veeam.Builder.Agent pour exécuter le programme de génération.
- dummybuild.exe est un programme de remplacement utilisé pour le débogage et comme aide visuelle. Nécessaire pour visualiser les paramètres transférés.
- QBuilder.AppLauncher - Service WCF qui exécute des applications dans la console de l'utilisateur actuel et fonctionne en mode interactif. Il s'agit d'un analogue considérablement simplifié du programme Veeam.Builder.Agent écrit spécifiquement pour cet article. Le service d'origine peut fonctionner comme un service Windows et exécuter des applications dans la console, ce qui nécessite un travail supplémentaire avec l'API Windows. Pour décrire toutes les astuces, un article séparé serait nécessaire. Mon exemple fonctionne comme un simple service de console interactive et utilise deux fonctions - démarrer un processus avec des paramètres et vérifier son état.
De plus, nous avons créé une nouvelle application Web pratique qui peut fonctionner avec plusieurs générateurs et conserver les journaux des événements. Afin de ne pas surcharger l'article, nous n'en parlerons pas non plus en détail. En outre, cet article ne décrit pas le travail avec TFS, avec l'historique de stockage des correctifs privés collectés et diverses classes et fonctions auxiliaires.

Création de services WCF
Il existe de nombreux articles détaillés décrivant la création de services WCF. J'ai surtout aimé le
contenu du site Microsoft . Je l'ai pris comme base de développement. Pour faciliter ma connaissance du projet, j'ai également présenté les
binaires . Commençons!
Créez le service QBuilder.AppLauncher
Ici, nous n'aurons que le disque principal du service. À ce stade, nous devons nous assurer que le service démarre et fonctionne. De plus, le code est identique pour QBuilder.AppLauncher et QBuilder.AppQueue, ce processus devra donc être répété deux fois.
- Créez une nouvelle application console appelée QBuilder.AppLauncher
- Renommez Program.cs en Service.cs
- Renommez l'espace de noms en QBuilder.AppLauncher
- Ajoutez les références suivantes au projet:
a. System.ServiceModel.dll
b. System.ServiceProcess.dll
c. System.Configuration.Install.dll
- Ajoutez les définitions suivantes à Service.cs
using System.ComponentModel; using System.ServiceModel; using System.ServiceProcess; using System.Configuration; using System.Configuration.Install;
Au cours de l'assemblage ultérieur, les définitions suivantes seront également nécessaires:
using System.Reflection; using System.Xml.Linq; using System.Xml.XPath;
- Nous définissons l'interface IAppLauncher et ajoutons des fonctions pour travailler avec la file d'attente:
- Dans la classe AppLauncherService, nous implémentons l'interface et la fonction de test TestConnection:
public class AppLauncherService : IAppLauncher { public bool TestConnection() { return true; } }
- Créez une nouvelle classe AppLauncherWindowsService qui hérite de la classe ServiceBase. Ajoutez la variable locale serviceHost - un lien vers ServiceHost. Nous définissons la méthode Main, qui appelle ServiceBase.Run (nouveau AppLauncherWindowsService ()):
public class AppLauncherWindowsService : ServiceBase { public ServiceHost serviceHost = null; public AppLauncherWindowsService() {
- Remplacez la fonction OnStart () qui crée la nouvelle instance ServiceHost:
protected override void OnStart(string[] args) { if (serviceHost != null) { serviceHost.Close(); }
- Remplacez la fonction onStop qui ferme l'instance ServiceHost:
protected override void OnStop() { if (serviceHost != null) { serviceHost.Close(); serviceHost = null; } } }
- Créez une nouvelle classe ProjectInstaller, héritée d'Installer et marquée avec RunInstallerAttribute, qui est définie sur True. Cela vous permet d'installer le service Windows à l'aide du programme installutil.exe:
[RunInstaller(true)] public class ProjectInstaller : Installer { private ServiceProcessInstaller process; private ServiceInstaller service; public ProjectInstaller() { process = new ServiceProcessInstaller(); process.Account = ServiceAccount.LocalSystem; service = new ServiceInstaller(); service.ServiceName = "QBuilder App Launcher"; Installers.Add(process); Installers.Add(service); } }
- Modifiez le contenu du fichier app.config:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services> <service name="QBuilder.AppLauncher.AppLauncherService" behaviorConfiguration="AppLauncherServiceBehavior"> <host> <baseAddresses> <add baseAddress="http://localhost:8000/QBuilderAppLauncher/service"/> </baseAddresses> </host> <endpoint address="" binding="wsHttpBinding" contract="QBuilder.AppLauncher.IAppLauncher" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> <behaviors> <serviceBehaviors> <behavior name="AppLauncherServiceBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="False"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
Vérification du bon fonctionnement du service
- Nous compilons le service.
- Installez-le avec la commande installutil.exe
1) Accédez au dossier où se trouve le fichier de service compilé
2) Exécutez la commande d'installation:
C: \ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ InstallUtil.exe - Nous allons dans le composant logiciel enfichable services.msc, vérifions la disponibilité du service QBuilder App Launcher et l'exécutons.
- Nous vérifions l'aptitude au service du service à l'aide du programme WcfTestClient.exe, qui est inclus avec VisualStudio:
1) Exécutez WcfTestClient
2) Ajoutez l'adresse du service: http: // localhost: 8000 / QBuilderAppLauncher / service
3) L'interface de service s'ouvre:

4) Nous appelons la fonction de test TestConnection, vérifions que tout fonctionne et que la fonction renvoie une valeur:

Maintenant que nous avons un disque de travail du service, nous ajoutons les fonctions dont nous avons besoin.
Pourquoi ai-je besoin d'une fonction de test qui ne fait rien
Quand j'ai commencé à apprendre à écrire un service WCF à partir de zéro, j'ai lu un tas d'articles sur ce sujet. Sur la table, j'avais une douzaine ou deux feuilles imprimées sur lesquelles je pouvais comprendre quoi et comment. J'avoue, je n'ai pas réussi à démarrer immédiatement le service. J'ai passé beaucoup de temps et suis arrivé à la conclusion qu'il est vraiment important de faire un disque de service. Avec lui, vous serez sûr que tout fonctionne et vous pouvez commencer à mettre en œuvre les fonctions nécessaires. L'approche peut sembler inutile, mais elle vous facilitera la vie si un tas de code écrit ne fonctionne pas comme il se doit.
Ajoutez la possibilité d'exécuter à partir de la console
Retour à l'application. Au stade du débogage et dans un certain nombre d'autres cas, il est nécessaire de démarrer le service sous la forme d'une application console sans vous inscrire en tant que service. Il s'agit d'une fonctionnalité très utile qui vous permet de vous passer de l'utilisation fastidieuse des débogueurs. C'est dans ce mode que le service QBuilder.AppLauncher fonctionne. Voici comment l'implémenter:
- Ajoutez la procédure RunInteractive à la classe AppLauncherWindowsService, qui fournit le service en mode console:
static void RunInteractive(ServiceBase[] services) { Console.WriteLine("Service is running in interactive mode."); Console.WriteLine(); var start = typeof(ServiceBase).GetMethod("OnStart", BindingFlags.Instance | BindingFlags.NonPublic); foreach (var service in services) { Console.Write("Starting {0}...", service.ServiceName); start.Invoke(service, new object[] { new string[] { } }); Console.Write("Started {0}", service.ServiceName); } Console.WriteLine(); Console.WriteLine("Press any key to stop the services and end the process..."); Console.ReadKey(); Console.WriteLine(); var stop = typeof(ServiceBase).GetMethod("OnStop", BindingFlags.Instance | BindingFlags.NonPublic); foreach (var service in services) { Console.Write("Stopping {0}...", service.ServiceName); stop.Invoke(service, null); Console.WriteLine("Stopped {0}", service.ServiceName); } Console.WriteLine("All services stopped."); }
- Nous apportons des modifications à la procédure principale - ajout du traitement des paramètres de ligne de commande. Avec l'option / console et une session utilisateur active ouverte, nous lançons le programme en mode interactif. Sinon, nous le lançons en tant que service.
public static void Main(string[] args) { var services = new ServiceBase[] { new AppLauncherWindowsService() };
Ajouter des fonctions pour lancer l'application et vérifier son statut
Le service est rendu extrêmement simple, il n'y a pas de contrôles supplémentaires. Il ne peut exécuter des applications que dans la version console et au nom de l'administrateur. Il peut également les lancer en tant que service - mais vous ne les verrez pas, ils tourneront en arrière-plan et vous ne pourrez les voir que via le Gestionnaire des tâches. Tout cela peut être implémenté, mais c'est un sujet pour un article séparé. L'essentiel pour nous ici est un exemple de travail clair.
- Tout d'abord, ajoutez la variable globale appProcess, qui stocke le processus en cours d'exécution.
Ajoutez-le à la public class AppLauncherService : IAppLauncher
:
public class AppLauncherService : IAppLauncher { Process appProcess;
- Ajoutez une fonction à la même classe qui vérifie l'état du processus en cours d'exécution:
public bool IsStarted() { if (appProcess!=null) { if (appProcess.HasExited) { return false; } else { return true; } } else { return false; } }
La fonction renvoie false si le processus n'existe pas ou n'est pas déjà en cours d'exécution et true si le processus est actif.
- Ajoutez la fonction pour lancer l'application:
public bool Start(string fileName, string arguments, string workingDirectory, string domain, string userName, int timeoutInMinutes) { ProcessStartInfo processStartInfo = new ProcessStartInfo(); processStartInfo.FileName = fileName; processStartInfo.Arguments = arguments; processStartInfo.Domain = domain; processStartInfo.UserName = userName; processStartInfo.CreateNoWindow = false; processStartInfo.UseShellExecute = false; try { if (appProcess!=null) { if (!appProcess.HasExited) { Console.WriteLine("Process is still running. Waiting..."); return false; } } } catch (Exception ex) { Console.WriteLine("Error while checking process: {0}", ex); } try { appProcess = new Process(); appProcess.StartInfo = processStartInfo; appProcess.Start(); } catch (Exception ex) { Console.WriteLine("Error while starting process: {0}",ex); } return true; }
La fonction lance toute application avec des paramètres. Les paramètres Domain et Username ne sont pas utilisés dans ce contexte et peuvent être vides, car le service démarre l'application à partir de la session de console avec des droits d'administrateur.
Lancement du service QBuilder.AppLauncher
Comme décrit précédemment, ce service fonctionne de manière interactive et vous permet d'exécuter des applications dans la session actuelle de l'utilisateur, vérifie si le processus est en cours d'exécution ou déjà terminé.
- Pour fonctionner, vous avez besoin des fichiers QBuilder.AppLauncher.exe et QBuilder.AppLauncher.exe.config, qui sont dans l'archive sur le lien ci-dessus. Le code source de cette application d'auto-assemblage s'y trouve également.
- Nous démarrons le service avec des droits d'administrateur.
- La fenêtre de console du service s'ouvrira:

Toute frappe dans la console de service la ferme, soyez prudent.
- Pour les tests, exécutez wcftestclient.exe, fourni avec Visual Studio. Nous vérifions la disponibilité du service sur http: // localhost: 8000 / QBuilderAppLauncher / service ou ouvrons le lien dans Internet Explorer.
Si tout fonctionne, passez à l'étape suivante.
Création du service QBuilder.AppQueue
Et maintenant passons au service le plus important, pour lequel l'article entier a été écrit! Nous répétons la séquence d'actions dans le chapitre «Création du service QBuilder.AppLauncher» et dans le chapitre «Ajout du lancement à partir de la console», en remplaçant AppLauncher par AppQueue dans le code.
Ajouter un lien vers le service QBuilder.AppLauncher à utiliser dans le service de file d'attente
- Dans l'Explorateur de solutions pour notre projet, sélectionnez Ajouter une référence de service et spécifiez l'adresse: localhost : 8000 / QBuilderAppLauncher / service
- Sélectionnez l'espace de noms: AppLauncherService.
Nous pouvons maintenant accéder à l'interface de service à partir de notre programme.
Créer une structure pour stocker des éléments de file d'attente
Dans l'espace de noms de QBuilder.AppQueue, ajoutez la classe QBuildRecord:
Implémentation de la classe de file d'attente CXmlQueue
Nous ajoutons la classe CXmlQueue.cs à notre projet, où se trouvent un certain nombre de procédures pour travailler avec le fichier XML:
- Constructeur CXmlQueue - définit le nom initial du fichier dans lequel la file d'attente est stockée.
- SetCurrentBuild - écrit des informations sur la construction actuelle dans le fichier XML de file d'attente. Il s'agit d'un élément qui n'est pas dans la file d'attente; il stocke des informations sur le processus en cours d'exécution. Peut être vide.
- GetCurrentBuild - Obtient les paramètres du processus en cours d'exécution à partir du fichier XML de file d'attente. Peut être vide.
- ClearCurrentBuild - Cela efface l'élément currentbuild dans le fichier XML de file d'attente si le processus se termine.
- OpenXmlQueue - fonction pour ouvrir le fichier XML où la file d'attente est stockée. Si le fichier est manquant, un nouveau est créé.
- GetLastQueueBuildNumber - chaque build de la file d'attente a son propre numéro de série unique. Cette fonction renvoie sa valeur, qui est stockée dans l'attribut racine.
- IncrementLastQueueBuildNumber - augmente la valeur du numéro de build lors de la mise en file d'attente d'une nouvelle build.
- GetCurrentQueue - Renvoie une liste d'éléments QBuildRecord à partir du fichier XML de file d'attente.
Dans le code d'origine, toutes ces procédures étaient placées dans la classe principale, mais pour plus de clarté, j'ai créé une classe distincte CXmlQueue. La classe est créée dans l'espace de noms QBuilder.AppQueue, vérifiez que toutes les définitions nécessaires sont spécifiées:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; using System.Xml.XPath; using System.IO; namespace QBuilder.AppQueue { . . . }
Donc, nous mettons en œuvre. La classe CXmlQueue elle-même:
Cliquez pour agrandir le spoiler avec le code La file d'attente dans le fichier XML est la suivante:
<?xml version="1.0" encoding="utf-8"?> <BuildsQueue BuildNumber="23"> <build BuildId="14" IssueId="26086" IssueName="TestIssueName" StartDate="2018-06-13T16:49:50.515238+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="15" IssueId="59559" IssueName="TestIssueName" StartDate="2018-06-13T16:49:50.6880927+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="16" IssueId="45275" IssueName="TestIssueName" StartDate="2018-06-13T16:49:50.859937+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="17" IssueId="30990" IssueName="TestIssueName" StartDate="2018-06-13T16:49:51.0321322+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="18" IssueId="16706" IssueName="TestIssueName" StartDate="2018-06-13T16:49:51.2009904+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="19" IssueId="66540" IssueName="TestIssueName" StartDate="2018-06-13T16:49:51.3581274+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="20" IssueId="68618" IssueName="TestIssueName" StartDate="2018-06-13T16:49:51.5087854+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="21" IssueId="18453" IssueName="TestIssueName" StartDate="2018-06-13T16:49:51.6713477+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="22" IssueId="68288" IssueName="TestIssueName" StartDate="2018-06-13T16:49:51.8277942+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="23" IssueId="89884" IssueName="TestIssueName" StartDate="2018-06-13T16:49:52.0151294+02:00" Build_CSharp="true" Build_Cpp="true" /> <currentbuild BuildId="13" IssueId="4491" StartDate="13.06.2018 16:53:16" /> </BuildsQueue>
Créez un fichier BuildQueue.xml avec ce contenu et placez-le dans le répertoire contenant le fichier exécutable. Ce fichier sera utilisé dans le débogage des tests pour faire correspondre les résultats des tests.
Ajouter une classe AuxFunctions
Dans cette classe, je place des fonctions d'assistance. À l'heure actuelle, il n'y a qu'une seule fonction, FormatParameters, qui effectue le formatage des paramètres pour les transmettre à l'application console pour le lancement. Liste du fichier AuxFunctions.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace QBuilder.AppQueue { class AuxFunctions {
Ajouter de nouvelles fonctionnalités à l'interface de service
La fonction de test TestConnection peut être supprimée à ce stade. Pour implémenter le travail de la file d'attente, j'avais besoin de l'ensemble de fonctions suivant:
- PushBuild (QBuildRecord): vide. Il s'agit d'une fonction qui ajoute une nouvelle valeur au fichier XML de file d'attente avec les paramètres QBuildRecord
- TestPushBuild (): vide. Il s'agit d'une fonction de test qui ajoute des données de test à une file d'attente dans un fichier XML.
- PullBuild: QBuildRecord. Il s'agit d'une fonction qui reçoit la valeur QBuildRecord du fichier XML de file d'attente. Il peut être vide.
L'interface sera comme ceci:
public interface IAppQueue {
Nous implémentons des fonctions dans la classe AppQueueService: IAppQueue:
Cliquez pour agrandir le spoiler avec le code public class AppQueueService : IAppQueue {
Apporter des modifications à la classe AppQueueWindowsService: ServiceBase
Ajoutez de nouvelles variables au corps de classe:
Dans le constructeur AppQueueWindowsService (), ajoutez des fonctions pour lire le fichier de configuration, initialiser les services et les classes de file d'attente:
AgentTimeout - fréquence de réponse du temporisateur. Indiqué en millisecondes. Ici, nous définissons que la minuterie doit se déclencher toutes les 30 secondes. Dans l'original, ce paramètre se trouve dans le fichier de configuration. Pour l'article, j'ai décidé de le mettre en code.
Ajoutez la fonction pour vérifier le processus de génération en cours d'exécution dans la classe:
Ajoutez la procédure pour travailler avec la minuterie:
private void TimerTick(object sender, System.Timers.ElapsedEventArgs e) { try {
OnStart, :
:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using System.ServiceModel; using System.ServiceProcess; using System.Configuration; using System.Configuration.Install; using System.Reflection; using System.Xml.Linq; using System.Xml.XPath; using QBuilder.AppQueue.AppLauncherService;
App.config
:
<appSettings> <add key="QueueFileName" value="BuildQueue.xml"/> <add key="BuildToolPath" value="c:\temp\dummybuild.exe"/> <add key="BuildToolWorkDir" value="c:\temp\"/> <add key="LocalDomain" value="."/> <add key="UserName" value="username"/> <add key="ScriptPath" value="C:\Temp\BuildSample.bld"/> </appSettings>
- QBuilder.AppLauncher.zip. .
- dummybuild.exe binaries , , c:\temp. , . , BuildToolPath BuildToolWorkDir .
- \QBuilder.AppLauncher\binaries\QBuilder.AppLauncher\ QBuilder.AppLauncher.exe . .
- QBuilder.AppQueue.exe /console .
- , :

- . , 30 :

- BuildQueue.xml , currentbuild:
<?xml version="1.0" encoding="utf-8"?> <BuildsQueue BuildNumber="23"> <build BuildId="19" IssueId="66540" IssueName="TestIssueName" StartDate="2018-06-13T16:49:51.3581274+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="20" IssueId="68618" IssueName="TestIssueName" StartDate="2018-06-13T16:49:51.5087854+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="21" IssueId="18453" IssueName="TestIssueName" StartDate="2018-06-13T16:49:51.6713477+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="22" IssueId="68288" IssueName="TestIssueName" StartDate="2018-06-13T16:49:51.8277942+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="23" IssueId="89884" IssueName="TestIssueName" StartDate="2018-06-13T16:49:52.0151294+02:00" Build_CSharp="true" Build_Cpp="true" /> <currentbuild BuildId="18" IssueId="16706" StartDate="13.06.2018 23:20:06" /> </BuildsQueue>
- dummy , :
<?xml version="1.0" encoding="utf-8"?> <BuildsQueue BuildNumber="23"> <build BuildId="21" IssueId="18453" IssueName="TestIssueName" StartDate="2018-06-13T16:49:51.6713477+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="22" IssueId="68288" IssueName="TestIssueName" StartDate="2018-06-13T16:49:51.8277942+02:00" Build_CSharp="true" Build_Cpp="true" /> <build BuildId="23" IssueId="89884" IssueName="TestIssueName" StartDate="2018-06-13T16:49:52.0151294+02:00" Build_CSharp="true" Build_Cpp="true" /> <currentbuild BuildId="20" IssueId="68618" StartDate="13.06.2018 23:24:25" /> </BuildsQueue>
!
powershell- . C#. rulesets — , setup-. — , . — MD5- -, .
,, — . , , . - .
, XML, , . , . , , .
, WCF-, XML-. :
PS , . , , .