Package Blazor

Communauté Même si j’expose pas mal de trucs au reste du monde (articles, démo, tutos…) j’ai jamais vraiment « contribué » à la communauté via des projets open source. Jusqu’à peu.  Faut bien commencer 🙂 En vrai, je m’en faisais tout une montagne. Pour moi faire un package nuget était compliqué. J’avais tord. En fait ça n’a rien de compliqué. Ce qui est vraiment chaud, c’est de gérer la maintenance et « animer » la communauté (répondre aux questions, faire un readme, corriger etc.) Blazor Je travaille sur un projet avec la techno Blazor depuis quelques mois. Je kiffe.  C’est un projet WASM. Donc qui utilise la WebAssembly. Pour moi c’est le futur. Je pense qu’on va vite se passer de Javascript d’ici 5 ans. Personnellement j’en ai plein le cul de ce language. Par contre respect, il a contribué à rendre nos app web carrément plus sympas.  Et donc on peut pas encore s »en passer vraiment, car l’essentiel des supers libs d’aujourd’hui son écrite en JS. Même si certain éditeur commence à sortir des versions WebAssembly. Mais c’est encore trop rare.  Donc faut encore faire avec. Et Blazor permet de faire un pont entre notre code C# et Javascript.  Ok certain vont me dire, à quoi ça sert, autant tout faire sur du JS. Oui. Mais sans moi, pour les raisons du dessus. Donc moi je veux faire du C#. Alors comment faire ? Faut coder un wrapper qui va nous servir d’intermediaire.  Mon premier package nuget J’avais besoin d’un lecteur de QR code dans mon appli. J’ai cherché et j’ai rien trouvé sur le gestionnaire de package Nuget. Alors j’ai décidé de faire le mien. Et puis je me suis dit que ce serait sympas de le partager. Et ça donne ça : Repo Github : https://github.com/YannVasseur35/ReactorBlazorQRCodeScanner Package Nuget : https://www.nuget.org/packages/ReactorBlazorQRCodeScanner/ Création d’une librairie Blazor La première chose à faire est de créer un nouveau projet « Bibliothèque de classes Razor » Si on ouvre le fichier projet, voici ce que j’ai  <Project Sdk= »Microsoft.NET.Sdk.Razor »> <PropertyGroup> <TargetFramework>net6.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> </PropertyGroup> <ItemGroup> <SupportedPlatform Include= »browser » /> </ItemGroup> <ItemGroup> <PackageReference Include= »Microsoft.AspNetCore.Components.Web » Version= »6.0.11″ /> </ItemGroup> </Project> Composition d’une lib razor (blazor) Voici un exemple de projet vide : Si on analyse une lib razor, voici ce qu’on y trouve : Component1.razor C’est votre composant visuel qui sera afficher dans vos pages. Suffira d’appeler <Component1/> dans votre page razor. Il affichera un cadre par défaut. A vous de customiser votre composant pour qu’il affiche ce que vous voulez qu’il affiche.  ExampleJsInterop.cs Comme son nom l’indique, c’est un exemple de ce que vous pouvez faire à minima avec du code C# qui utilise du javascript. Dans son contructeur : private readonly Lazy<Task<IJSObjectReference>> moduleTask; public ExampleJsInterop(IJSRuntime jsRuntime) { moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>( « import », « ./_content/RazorClassLibrary1/exampleJsInterop.js »).AsTask()); } On voit que ça charge un fichier js. Notez aussi le chargement « Lazy » qui va permet de ne charger les éléments de notre lib qu’au moment où c’est nécessaire.  https://learn.microsoft.com/en-us/aspnet/core/blazor/webassembly-lazy-load-assemblies?view=aspnetcore-7.0 Donc quelque part, si vous n’utilisez pas le composant dans vos premières pages, il ne sera pas chargé (téléchargé pour le client). S’en suis dans le code une seule méthode : public async ValueTask<string> Prompt(string message) { var module = await moduleTask.Value; return await module.InvokeAsync<string>(« showPrompt », message); } Ici on nous présente un méthode très simple pour afficher un prompt JS. On appelle, via module.InvokeAsync la méthode javascript « showPrompt » et on lui passe un paramètre « message ».  exampleJsInterop.js export function showPrompt(message) { return prompt(message, ‘Type anything here’); } Le code JS est super simple. On lance un prompt tout bête avec le message qu’on a en paramètre. Voilà pour la présentation d’un composant blazor.  Intégration Maintenant que l’on a un composant blazor on va l’utiliser dans notre app web WASM.  Suffit d’ajouter une référence de projet dans notre projet client. ensuite dans une page, par exemple index.razor, on ajoute ceci :  @page « / » @using RazorClassLibrary1 <Component1 /> Visuellement ça donnera ceci : Ajoutons un peu de code à notre page pour jouer avec un bouton : @page « / » @using RazorClassLibrary1 @inject IJSRuntime _jSRuntime <Component1/> <button onclick= »@Show() »>Click me</button> @code{ private ExampleJsInterop? _exampleJsInterop; protected override void OnInitialized() { _exampleJsInterop = new ExampleJsInterop(_jSRuntime); } protected async Task Show() { await _exampleJsInterop.Prompt(« coucou »); } } On vient de lancer du js avec du C#. Scanner de QR Code. Pour revenir à mon besoin initiale, je dois pouvoir scanner un QR Code. Ya pas encore de lib nuget mais y’en a quelques une sous javascript, dont jsQR https://github.com/cozmo/jsQR La lib peut faire plein de chose, moi j’ai besoin de récuperer que la lecture du code QR. Pour cela j’ai ajouté une Action (delegate) à mon composant pour que lorsque le JS trouve un QR code et arrive à le lire, cela déclanche (un Invoke) une méthode dans mon code. Le but n’est pas de décrire comment j’ai fait, le code source est dispo ici : https://github.com/YannVasseur35/ReactorBlazorQRCodeScanner J’ai bossé comme avec le précendent projet, en « local » à savoir que mon projet « lib blazor » est ajouté à mon projet blazor client.  Pour que ce soit plus simple, pour moi et pour le reste du monde, il faudrait faire un package nuget et l’importer depuis nuget.org   Le Package Et c’est la que je me faisait tout une montagne (alors que c’est plus simple que de publier une app Android ou pire Apple). Il suffit de taper une commande : dotnet pack Et c’est tout 🙂 Bien entendu il faut aussi donner quelques informations sur le package. Ceci est fait dans le fichier projet de la bibliothèque de classe razor. Voici ce que j’ai mis pour mon package de scan de QR code. <Project Sdk= »Microsoft.NET.Sdk.Razor »> <PropertyGroup> <TargetFramework>net6.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <PackageId>ReactorBlazorQRCodeScanner</PackageId> <Version>1.0.5</Version> <Authors>Yann Vasseur</Authors> <Company>reactor.fr</Company> <PackageTags>Blazor;Reactor;QRCode;ScannerQR Code</PackageTags> </PropertyGroup> <ItemGroup> <SupportedPlatform Include= »browser » /> </ItemGroup> <ItemGroup> <PackageReference Include= »Microsoft.AspNetCore.Components.Web » Version= »6.0.4″ /> </ItemGroup> </Project> Et bim on a un fichier ReactorBlazorQRCodeScanner.1.0.5.nupkg dans votre bin/debug Push sur nuget.org Pour publier en ligne, il vous faut un compte sur nuget.org. Ensuite il va vous falloir une clé api. https://www.nuget.org/account/apikeys Quand c’est fait, il restera une étape, taper cette commande (dans le dossier ou se trouve votre package) dotnet nuget push…

Azure Devops Pipeline (CI/CD) – Environnement

Problème du dernier post Je me suis bien pris la tête sur le dernier billet au sujet d’un nouveau problème qui est apparu lorsque j’ai voulu diviser mon site web en 2, une partie staging et une partie prod. Ca pourrait tout aussi bien être "preprod", "recette" etc… Avant j’avais qu’un seul site web: blazordemo.reactor.fr. Celui ci a sa config Nginx et son service systemd qui fait tourner Kernel via un proxy sur le port 7777. (voir article ) J’en ai créé un nouveau "staging.blazordemo.reactor.fr".  L’idée est de faire comme les pros, un site web de "staging" et un de "prod".  dev : locale, tambouille de developeur, avec une base de données crado, des machins bricolés, des trucs à jours ou pas… staging:  distante, même environnement que la prod (meme serveur de base de données, même OS, même TOUT, sauf les données qui sont normalement copiés et anonymisés dans le meilleurs des mondes) prod: distante, le site qu’on update le vendredi soir 🙂 Donc j’ai crée "staging.blazordemo.reactor.fr". Sauf que j’ai pas modifié mon service qui fait tourner Kestrel via proxy sur port 7777.  Et j’ai mis un temps fou à m’en apercevoir. (j’ai été jusqu’a faire un gros delete de mon repertoire www. Nginx continuait à me servir un beau site web… même après un reboot. car le service était le meme…) Donc il me faut 2 services… Un de staging, l’autre de prod. Le tout sur un port proxy différent bien évidement.  Bien entendu, ce problème n’existe pas si le staging et la prod ne sont pas sur le meme serveur, ce qui est TRES recommandé. Cependant, je trouve l’exercice intéressant car il pousse dans les retranchements et force à de bonnes pratiques. Mon ancienne technique Dans le projet Blazor Server, dans Program.cs j’ajoutais ceci  Une ligne de compilation conditionnelle qui me changait le port si on était en release (ce que je considèrais comme mon unique prod).  Je pourrais ajouter une condition STAGING avec un port 7776 et m’en servir pour la version staging. A mon sens une directive de compilation n’a rien a voir avec les environnements, je veux dire c’est liés, mais c’est pas son but de définir du paramétrage de port…  Je pense qu’on peut faire mieux, avec les fichiers de conf appsettings.js. Créeons deux autres fichiers de config, un pour la prod et l’autre pour le staging. Normalement vous avez déjà par défaut un fichier appsettings.Development.json launchSettings.json Si vous travaillez avec Visual Studio ou VS Code vous devez savoir que vous avez des options de lancement de votre app qui sont configurable dans le fichier Properties/launchSettings.json Encore un fichier de config qui sème le doute… { « iisSettings »: { « windowsAuthentication »: false, « anonymousAuthentication »: true, « iisExpress »: { « applicationUrl »: « http://localhost:54778 », « sslPort »: 44382 } }, « profiles »: { « BlazorDemo.Server »: { « commandName »: « Project », « dotnetRunMessages »: true, « launchBrowser »: true, « inspectUri »: « {wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri} », « applicationUrl »: « https://localhost:7249;http://localhost:5249 », « environmentVariables »: { « ASPNETCORE_ENVIRONMENT »: « Development » } }, « IIS Express »: { « commandName »: « IISExpress », « launchBrowser »: true, « inspectUri »: « {wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri} », « environmentVariables »: { « ASPNETCORE_ENVIRONMENT »: « Development » } } } } Comme on peut le voir dans la section "profiles", je peux lancer mon app soit : Via IIS Express  Via la commande dotnet run La première est historique, je pense qu’on en parlera plus dans qq temps. Avant .net core on était obliger de lancer un server utilisant le .net Framework sous IIS qui est le serveur de Windows. La version Express est la version de dévelopement. Pour moi c’est voué à mourrir, mais certains ont des habitudes et Microsoft les forces pas à les changers.  Depuis .net Core (et donc .net 5 et 6), on peut executer nos app sur n’importe quel OS et serveur web comme apache ou nginx sous linux. On a un serveur web intermédiaire appelé Kestrel qui fait le job. D’ailleurs c’est lui que l’on utilise en dévelopement.  En executant une commande dotnet run sur notre projet serveur, on lance un Kestrel. C’est lui que l’on veut paramétrer pour avoir une config dev, staging, prod.  C’est possible d’éditer en développement ce fichier pour changer de port. Mais c’est uniquement un fichier de conf pour une execution en dev local. C’est pas un fichier de configuration plateforme de prod.  Je place ça là car c’est un fichier qui peut vous semer le doute lors de vos devops et test local. AppSettings.{ENV}.json On va modifier le fichier appSettings.Developement.json et lui rajouter ceci  { « Plateform »: « Dev », « Kestrel »: { « EndPoints »: { « Http »: { « Url »: « http://localhost:1234 » } } }, « Logging »: { « LogLevel »: { « Default »: « Information », « Microsoft.AspNetCore »: « Warning » } } } Perso je rajoute une ligne "Environment" qui me permet de voir dans sur quel type d’environement je suis (et plus tard de l’afficher dans l’app).  Ce qui nous intéresse c’est la section Kestrel. On ajoute un EndPoints et on lui précise un port.  Faites un dotnet run sur votre projet à présent : On a un warning qui nous dit que l’adresse utilisé va écrasé celle du launchsetting.json. C’est ce qu’on veut. On voit bien la nouvelle adresse http://localhost:1234.  C’était pour la démo, en dev ça sert a rien de faire cela, autant utiliser le launchSettings.json qui est la pour ça. Donc enlever la ligne Kestrel, c’était pour la démo. Par contre on va mettre ces configs pour appsettings.staging.json  { « Plateform »: « Staging », « Kestrel »: { « EndPoints »: { « Http »: { « Url »: « http://localhost:7776 » } } }, « Logging »: { « LogLevel »: { « Default »: « Information », « Microsoft.AspNetCore »: « Warning » } } } et appsettings.prod.json { « Plateform »: « Prod », « Kestrel »: { « EndPoints »: { « Http »: { « Url »: « http://localhost:7777 » } } }, « Logging »: { « LogLevel »: { « Default »: « Information », « Microsoft.AspNetCore »: « Warning » } } } ASPNETCORE_ENVIRONMENT Encore une chose à savoir. Pour déterminer sur quel environement .net se trouve, il utilise une variable d’environement ASPNETCORE_ENVIRONMENT Elle est d’ailleurs clairement définie dans le fichier launchSettings.json à "Development". Donc en dev, le fichier de config appsettings utilisé sera appsettings.development.json Pour que ce mécanisme fonctionne en prod, soit on définie la variable ASPNETCORE_ENVIRONMENT à "Production" soit par défaut, si elle n’existe pas, c’est déjà le cas (attention moi j’ai nommé mon appsettings prod et non production). Le problème c’est que une variable d’environement est définie pour tout…

Azure Devops Pipeline (CI/CD) – Versioning

C’est quoi une version ? Je vais faire quelque chose de très basique et qui ne sera probablement pas appliqué tel quel dans un cas plus professionnel. D’ailleurs, chacun ayant ses méthodes de travail, ce post reste un example de "comment on pourrait faire" pour versionner une app.  C’est quoi une version ? La encore, je suis pas sûr que tout le monde utilise la même façon de versionner.  Par exemple les versions du navigateur Firefox utilise simplement un chiffre (https://blog.mozfr.org/pages/Calendrier-versions-Firefox). Mais si on regarde de plus prêt on a quand meme 98.0.1 et donc des points de séparations.  En fait, Pour le grand public on va utiliser "Firefox 98". Mais il faut savoir qu’il existe de nombreuses versions de la 98 ! Du moins en interne. Avant de livrer une version patché 98.0.1 qui va corrigé des bugs mineures.  D’après ce que j’ai pu voir, on utilise principalement ce style de nommage de version X.Y.Z X : Version Majeure (non rétro compatible) (nouvelles fonctionalités, gros correctif, ajout de techno, changement de look…) Y : Version Mineure (rétro compatible) (correctifs, patchs, en gros pas de grosse modif, mais qui répare) Z : Révisions.  (rétro compatible) Sert plutot au dev et product owner pour savoir de quelle version on parle en pre prod pour basculer vers la prod. Quelque part, ce dernier numéro ne devrait à mon sens pas être communiqué officiellement (un peu comme Firefox qq part) Sur la version X, on a je pense tous la même idée. On sort une version X rarement. Par contre, je suis pas certain qu’on utilise tous Y et Z de la même manière. Franchement, c’est freestyle, dans les versions mineures on peut voir de nouvelles (petites) fonctionalités. C’est vraiment selon vos préférences et ceux avec qui vous bosser.  Pour le coté retro compatible, c’est ce que j’ai lu sur le net. Intéressant mais pas du tout respecté par tous.  Autre style, voici un format intéressant ou l’on cumule en plus une date : V 2020.10.23.1629 Je trouve ce format particulièrement intéressant car on sait tout de suite de quand date la version. Mais ca fait bcp de chiffre.  Ce format est utilisé par Larsen Lance avec un scipt powershell qui "incrémente" ce format à chaque build en automatique (http://www.lancelarsen.com/blazor-tips-n-tricks-auto-increment-your-version-tag-with-powershell/) Pourquoi versionner ? La version d’une app sert tout simplement de référenciel. De quoi on parle ? De la version 1.12.665 ou ya un bug qui est corrigé dans la V1.13.005. C’est quand même plus pratique.  Le product owner sera aussi attentif à ces versions concernant l’ajout de nouvelles fonctionalités et communiquer dessus.  Ca permet de prévoir une roadmap large et dire par exemple, cette fonctionalité sera attendu pour la V2.3, pas avant.  En parlant de roadmap, je cite souvent Star Citizen qui est un jeux vidéo en béta depuis des années, et qui affiche ses avancés https://robertsspaceindustries.com/roadmap/release-view Il fixe des objectifs par trimestres sous forme de version avec une majeure et une mineure. C’est clair. Mais bon il sort quand ce jeu !!!! C’est une alpha. Et certain ajoute ce terme dans la version.  Alpha, Beta, Rc (Release Candidate), Nightly Build… On peut préciser le contexte de la version.  Versionner une app .net Core avec une Pipeline automatique Tout l’enjeu de cet article. Comment je peux modifier ma pipeline pour inclure un nouveau numéro de build par exemple à ma version et l’afficher en prod. (comme sur Firefox dans la rubrique aide ou a propos) ? Afficher la version dans l’app Pour que l’on voit directement la version dans mon app Blazor, je vais carrément l’afficher en haut a droite : Une page about avec ce numéro aurait été plus propre sur une vraie App. L’idée ici c’est de l’avoir en permanence sous les yeux.  Pour cela on va modifier MainLayout.razor comme ceci La ligne importante est celle ci : Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion; La version par défaut qui s’affiche est 1.0.0. Si vous faites (sous Visual Studio) un clic droit sur le projet client de Blazor, puis propriété et enfin dans la section Package vous verrez ce numéro. Par contre il n’est pas modifiable.  Pour le modifier, il suffit de rajouter dans le .csproj une ligne version. Easy. Bingo YAML comment gérer une version Je veux une version sous cette forme yyyy.mm.dd.xx, donc date + un numéro auto incrémenté. On commence par ajouter un compteur à une variable. Ce compteur sera automatiquement incrémenté à chaque execution de la pipeline. revision: $[counter(format(‘{0:dd}’, pipeline.startTime), 1)] Pour s’en assurer, on va afficher la valeur de notre variable : #display value – script: echo $(revision) displayName: ‘revision display’ Executez plusieurs fois votre pipeline et aller voir ce que ça donne. Par exemple chez moi : J’en suis à mon 24ième run. Il me faut aussi une date du jour. pour cela on va aussi utiliser une variable dateToday ainsi qu’une derniere variable  »versionString"  qui va concaténer ces deux dernières. (1 h pour trouver comment faire… le ‘>’ magique). Ce qui donne : dateToday: ‘Will be set dynamically’ revision: $[counter(format(‘{0:dd}’, pipeline.startTime), 1)] versionString: > $(dateToday).$(revision) Il suffit à présent de générer dateToday. On va utiliser un script PowerShell (pompé et adapté sur ce site) #Preparing Build Number – task: PowerShell@2 displayName: ‘Preparing Build Number’ inputs: targetType: ‘inline’ script: | $currentDate = $(Get-Date) $year = $currentDate.Year $month = $currentDate.Month $day = $currentDate.Day Write-Host $currentDate Write-Host $day Write-Host $env:revision Write-Host « ##vso[task.setvariable variable=dateToday]$year.$month.$day » On vérifie les données : #display value – script: echo $(revision) displayName: ‘revision display’ – script: echo $(dateToday) displayName: ‘dateToday display’ – script: echo $(versionString) displayName: ‘versionString display’ Normalement on a ce résulat : 2022.3.21.29, version numéro 29 faite le 3 mars 2022, elle est pas belle la vie ?  Publication avec la version J’ai légérement adapté mon script de publication qui était anciennement ceci : # Publish Blazor Demo Server – script: dotnet publish –configuration $(buildConfiguration) src/BlazorDemo/BlazorDemo/Server/BlazorDemo.Server.csproj displayName: ‘dotnet publish $(buildConfiguration)’ Remplacé par : # Publish Blazor Demo Server – task: DotNetCoreCLI@2 displayName: Publish inputs: command: publish publishWebProjects: false projects: ‘$(projectBlazorWasmServer)’ arguments: ‘–configuration $(buildConfiguration) –output $(build.artifactstagingdirectory) /p:Version=$(versionString)’ zipAfterPublish: false modifyOutputPath: false…

Azure Devops Pipeline (CI/CD) – Deploy

Objectif : déploiement en production Dans la précédente partie on a déployé une pipeline sur Azure DevOps. Celle ci nous permet de lancer une build dès que la branche master de notre repo Blazor Sandbox reçoit un nouveau commit. De cette manière on s’assure que le build fonctionne ou pas. On est prévenu du résultat par email.  Ce qui serait intéressant, c’est de pouvoir la déployer sur un serveur web automatiquement en cas de succès.  Ma config J’ai un serveur VPS chez iKoula pour 4€/mois, avec une debian linux.  Ca tourne plutot bien. Mais bon, j’ai pas beaucoup de traffic 🙂 Donc j’ai un site web https://dev.blazordemo.reactor.fr et https://blazordemo.reactor.fr qui tournent tous les deux sous Nginx. Derrière chez deux services kestrel asp.net core sous .net 6.0. (pour faire bien, il aurait fallut dissocier la prod de mon dev (ou recette, staging). Petit budget… Dans /var/www/blazordemo.reactor.fr et /var/www/dev.blazordemo.reactor.fr j’ai mes publications de site web asp.net core Blazor WASM (le serveur et le client WASM "all in one"). L’idée est de dire à Azure Devops Pipeline, lorsque la branche master reçoit un nouveau commit, tu publies une version du serveur et tu balances le tout via SSH (sftp). Pipeline YAML On est fixé sur les objectifs. Maintenant il faut traduire cela en YAML. Voici un bout de doc : https://docs.microsoft.com/fr-fr/azure/devops/pipelines/tasks/deploy/copy-files-over-ssh?view=azure-devops En premier lieu, je vais reprendre la précédente pipeline et modifier build par publish. La différence est notable pour Blazor Server WASM. (voir mon article) Un petit commit + push et on voit que Azure Devops lance un nouveau job. On attend qu’il termine et passe au vert. Je pense qu’on va procédé ainsi, à taton. Petit pas par petit pas.  Une modif + commit + push + retour du Job.  Point SSH/SFTP Pour accéder à mon serveur en toute sécurité, je me connecte via SSH. Je prends donc la main à distance depuis mon poste windows sur mon serveur debian linux, qq part en France. J’ai un mot de passe et un login. Et roule ma poule.  Mais on peut faire mieux. J’ai ajouté une paire de clé privée/publique et une passphrase. Ainsi, il faut avoir la clé privée + la passphrase pour accéder.  C’est plus sécurisant pour moi. Je laisse l’accès SSH classique pour des opérations plus ponctuelles. J’ai de mon côté une serie de fichier .bat qui lance une commande de publication .net core et des commandes winscp (permet de lancer des commandes SFTP depuis Windows). La connection se fait via cette clé et cette passphrase. Ainsi, je peux en un double clic mettre à jour ma prod/staging/recette/dev depuis mon poste, sans lacher mon mdp. Au lieu de le faire à la main depuis un client sftp. Ca marche ! C’est moins rapide qu’un client sftp (car les fichiers uplodé ne sont pas lancés en parrallele). J’ai deux types de scripts. La totale, qui met a jour toute la publication : et un autre "rapide" qui ne mets a jours que le minimum pour aller vite : et son fichier .bat  Donc l’idée, c’est de faire pareil sous Azure Devops. Yaml On va rajouter une nouvelle tâche dans notre pipeline YAML avec CopyFilesOverSSH 4 éléments sont à renseigner : sshEndPoint : c’est le nom de notre connexion SSH que l’on va configurer plus loin sourceFolder : le chemin relatif de votre publish. L’agent de build va s’occuper de récupérer votre publish au bon endroit. Attention au majuscule et minuscule si l’agent est sur linux !!! targetFolder: le chemin de destination de votre publication sur la machine cible.  cleanTargetFolder : facultatif, mais effacer le contenu de la destination fait pas de mal.  Service Connections Le sshEndpoint de tout à l’heure est à configurer dans les "project settings" dans la section Pieplines/Service Connections  Il reste plus qu’à créer la votre en cliquant sur le bouton new connection Sélectionnez SSH : puis remplissez selon votre config, en uploadant votre clé privée. Attention, celle ci doit être au format .pem. Et voila. C’est tout.  Relancer votre pipeline avec un petit git add commit push.  Normalement vous devriez avoir que du vert !

Azure Devops Pipeline (CI/CD) – First Build

Intro En général, quand on découvre une techno, on fonce direct en dev local et on s’amuse. C’est ce que j’ai fait aussi, mais j’ai pas tardé à mettre en prod. Car par expérience,  mieux vaut avoir les problèmes au début, tant que le projet est pas trop gros ou trop compliqué.  Donc l’idée de ce billet, c’est voir comment on pourrait monter une chaine CI/CD (continious intergration/ continuious deployement) sur un projet blazor de base.  Projet de base Blazor Avec Visual Studio 2022 Community, on créer un projet Blazor WASM, avec Hebergement Asp.net core et PWA : Pour ma part je l’ai nommé BlazorDemo.  Je me suis permis de modifier qu’une seule ligne, dans program.cs du projet server. Je spécifie juste un port pour ma prod. Voir artile https://reactor.fr/net-core-sous-linux-nginx-80-http-443-https-firewall/ Azure DevOps Pipelines Azure Devops, c’est GitHub, Maven, Jenkins et j’en passe tout ça dans une appli web.  Ca nous permet de faire du CI/CD depuis nos propres repos (qu’ils soient sur BitBucket, Azure devops, GitHub, ou autre…) L’idée est de partir sur deux branches git, une master et une develop.  La master sera considéré comme la Prod. La develop comme la branche de travail.  A chaque fois que la branche master recevra un commit, on lancera une "pipeline" qui fera plusieurs actions, comme builder, lancer des tests et deployer.   A l’avenir, la branche master ne pourra plus recevoir de commit directement, ceci sera bloqué par defaut. Le seul moyen sera de faire une Pull Request depuis la branche develop vers la branche master. Ceci afin de mettre en place de bonne pratique de développement et de gestion de code source.  Bien sûr on pourrait pousser le concept plus loin, en pratiquant du Git Flow par exemple. Mais restons simple pour bien comprendre les mécanismes d’AzureDevOps 1ere pipeline Commençons par créer notre première pipeline. Menu Pipeline Cliquez sur New Pipeline Comme je le disais tout à l’heure, notre code source peut se situer sur différent repos, choisissez le votre : On choisit ensuite son repos, dans mon cas ceci :  Ensuite il faut choisir un modèle de script YAML. Alors ici, c’est pas clair je trouve. J’ai choisis "Asp.net Core" qui n’est pas présenté comme premier choix. Ce qui pourrait m’induire en erreur. Enfin de toute manière, c’est qu’un modèle, qu’on peut modifier par la suite. Voici le résultat : En cliquant sur Save and Run, puis valider l’écran suivant par defaut, vous allez ajouter un fichier "azure-pipelines.yml" a la racine de votre repo, sur la branche master. S’en suit un run automatique, qui au bout de qq seconde va échouer : En cliquant dessus pour voir le détail : Puis sur Job et sur l’élément en rouge : On a le détail du pourquoi le job a échoué. On a le détail du pourquoi le job a échoué. Ca nous dit que l’on a pas spécifier de projet à compiler… et c’est vrai MSBUILD : error MSB1003: Specify a project or solution file. The current working directory does not contain a project or solution file. Correctif de la 1ere pipeline Vous pouvez corriger directement depuis la plateforme azure devops pour bien le faire depuis votre editeur préféré et jouer qq commande git. Pour ma part je préfère cette derniere.  Je récupère donc via un git pull ma branche master sur mon visual studio code et je modifie le fichier azure-pipelines.yml avec cette ligne script: dotnet build –configuration $(buildConfiguration) src/BlazorDemo/BlazorDemo/Server/BlazorDemo.Server.csproj Le path ici peut différer selon votre répertoire repos. (moi j’ai mis tout ca dans un folder src, qui contient un répertoire  blazordemo qui contient un répertoire "solution" blazordemo. Quand votre modification est faites, il faut alors mettre a jour le repo distant avec un commit.  Rappelons que lorsque la branche master subit un nouveau commit, ca lance automatiquement cette pipeline. Et normalement vous obtenez un build vert :  Et voila, on a fait notre première pipelines qui fait qq chose de très basique pour le moment, builder !

Blazor .net 6.0 – décollage

Découverte de Blazor J’ai été très très emballé lorsque j’en ai entendu parlé pour la premiière fois de Blazor. Bien que j’attendais à ce qu’il soit tué dans l’oeuf. Car au début, c’était un side project Microsoft sans prétention. Quelle idée, faire du web avec du C# ? et pourquoi pas VbScript tant qu’on y est… 🙂 Nan mais sans déconner, le js ça va deux minutes. Donc faire une web app en C# je signe.  J’ai donc taté du blazor en mode tuto au début. Puis j’ai oublié. Et me revoila ! Je vais présenter ici mes découvertes. En fait j’ai fait des erreurs et je voulais les écrires pour que qq évite de galérer comme moi.  Blazor .net 6.0 Bon la ça déconne plus. Blazor est officiellement inclu dans .net (depuis .net Core 3.1 il me semble) et donc on peut code pour de vrai, pour de vrai client. Et la ça change tout. Fini les tutos. Alors on  commence avec un nouveau projet sous VS2022. Et celui ci nous propose deux solutions :  Blazor WebAssembly  Balzor Server Blazor Server Ce type de projet est n’y plus n’y moins qu’un projet ASP.net Core avec du Razor en couche de présentation. Si auparavent vous avez fait du web.form pour les plus vieux, du MVC5 ou de l’Asp.net Core vous allez pas être trop perdu.  Pour les autres, on est sur une application classique de type Client (browser) Server (IIS, Apache, Nginx, Kernel…) En ajoutant une couche SignalR vous avez un site web qui déboite. Mais qui ne gère pas le offline. Blazor WebAssembly Le vrai plus est la pour moi. On peut envisager de faire une application web PWA avec .net. Et ça pour un dev Microsoft de 20 ans, c’est juste de la bombe pour moi. Et qui plus est, qui peut tourner sur un OS Linux. (voir articles sur les coûts) Donc Blazor Wasm pour les intimes et une tuerie. On va pouvoir utiliser la puissance du language C#, les outils VS et VS Code, et toutes les libs qu’on a l’habitude d’utiliser depuis des années.  Je vais parler dans le reste de l’article que de Blazor Wasm.      Comment ça marche ? L’objectif de l’article est de vous amener à bien comprendre comment Balzor fonctionne. D’un point de vue architecture applicative et d’un point de vue réseau/serveur.  Lorsque l’on créer un nouveau projet, VS2022 nous demande si on souhaite héberger le site via Asp.net Core. (kernel) Il nous demande aussi si l’on souhaite utiliser des fonctionnalités PWA. Pour ma part oui pour les deux.  Voici la différence entre un projet WASM sans serveur (à droite) et un projet avec : Le projet BlazorApp1.Client et BlazorApp2 (sans server) constituent l’application web PWA. On y trouve du code C#. Mais au final on aura que du code html/css/js pure en sortie et un dossier _framework, qui lui par contre contient qq dll indispensable au bon fonctionnement de blazor.  Voici une publication : On a un webconfig et un repertoire wwwroot qui contient ceci:     C’est la que ca devient chelou. Si on se place coté browser, et surtout en mode déconnecté, on a pas besoin d’un serveur (web, pure static de fichier), (sauf bien entendu lors du premier chargement). Ensuite on peut très bien s’en passer de ce serveur. D’ailleurs pourquoi pas héberger ce client sur un CDN (content delivery Network) Mais alors on a quoi si on se place coté serveur ? Bonne question. C’est la que l’hôte Asp.net Core intervient.  Comme on peut le voir dans le premier projet, on a un projet Server et un projet Shared. Shared est juste un projet de classe, rien de méchant on partage du code commun avec les deux autres projets.  Le projet Server par contre est le code qui va gérer la partie serveur, comme son nom l’indique. Et lui aura aussi la partie cliente. Regarder les sorties d’une publication du projet serveur :  On retrouve bien wwwroot de la partie cliente ! Ceux sont ces fichiers static que le serveur va gérer par la suite avec le browser. Donc pour résumer : On a un projet Blazor Server : projet classique client/serveur (rendu server sur chaque page) avec une liaison SignalR.  On a un projet Blazor WASM : projet PWA (client web autonome) qui peut avoir des options d’hébergement avec l’ajout d’un projet Server + Share. J’avoue qu’on s’y perds avec le terme « Server ». A mon avis les équipes de microsoft on du aussi gamberger pas mal sur le naming. Mais en fait, quand on sait, c’est clair. (complètement conne cette phrase…)   Mes erreurs Sotrie de build Lorsque l’on travaille en debug local, on ne fait pas trop attention à tout cela. Mais dès lors qu’on passe en prod c’est plus la même histoire.  La ou j’ai merdé, c’est de considérer le dossier bin/Release comme ma sortie pour la prod. Erreur ! Allez y faire un tour, on a uniquement la génération du projet courant. Ce qui est logique. Mais la petite nouveauté c’est la publication qui intègre le projet Client dans la sortie du projet Server.  Autre erreur, les ports.  En local on peut configurer les ports des projets Client et Server via le fichier properties/launchSettings.json.Et on peut executer les deux projets. Modifier les ports de ces fichiers et lancer un run.  Mais dans tous les cas ceux qui comptent pour la prod sont ceux du projet serveur. Et moi comme un con, j’avais le projet Client comme projet de démarrage. A éviter car j’ai perdu beaucoup de temps à rien comprendre.  Voila, j’y ai passé presque deux jours (config Ngix sur linux). Donc je résume un peu. Je me disais que ca valait bien un billet. ++