Elena Canorea
Communications Lead
Eine der Hauptstärken von Plain Concepts ist, dass wir stolz darauf sind, die Dinge richtig zu machen, indem wir die besten Praktiken befolgen. Dies zu erreichen ist nicht immer einfach und bedeutet in vielen Fällen, dass wir ständig lernen, forschen und testen müssen, um herauszufinden, welches der beste Ansatz ist, um etwas in einem breiten Spektrum von verschiedenen Bereichen zu tun.
Das mag ein wenig überwältigend klingen, aber zum Glück gibt es manchmal Werkzeuge, die sehr hilfreich sein können, und eines dieser Werkzeuge ist Sonar.
Sonar ist der Name, der für das Tool SonarQube verwendet wurde. Mit diesem Open-Source-Tool können Sie eine statische Analyse des Codes eines Projekts erstellen, um schlechte Praktiken, mögliche Fehler und Bugs zu erkennen. Diese Erkennung basiert auf einer Reihe von konfigurierbaren Regeln, die das Tool verwendet, um den gesamten Code Ihres Projekts zu überprüfen und zu analysieren und einen Abschlussbericht zu erstellen, der direkt im Internet eingesehen werden kann.
Obwohl sich Sonar in der Vergangenheit nur auf SonarQube bezog, werden heutzutage immer mehr Dinge von “on-prem” in die “Cloud” verlagert und Sonar steht dem in nichts nach. Genau wie SonarQube gibt es heute auch eine Cloud-Version namens SonarCloud, über die ich heute sprechen möchte.
Auch wenn Sonar ein Open-Source-Projekt ist, heißt das nicht, dass es kostenlos ist. Das Interessante dabei ist, dass SonarQube zwar nicht kostenlos ist, aber eine Community-Version hat und SonarCloud für Open-Source-Projekte kostenlos ist. Das bedeutet, dass es möglich ist, es auf die eine oder andere Weise zu testen, bevor man sich entscheidet, ob es für einen nützlich ist (was ich Ihnen bereits im Voraus sage).
Nachdem das gesagt ist und weil Reden einfach ist, wollen wir uns ansehen, wie wir Sonar zu unserem kontinuierlichen Integrationsprozess in Azure DevOps hinzufügen können, indem wir SonarCloud verwenden (obwohl der Prozess praktisch der gleiche ist, wenn wir SonarQube verwenden).
Das erste, was wir brauchen, ist ein SonarCloud-Konto. Um ein solches zu erhalten, gehen Sie einfach auf die Website https://sonarcloud.io und klicken Sie auf die Schaltfläche “Anmelden” in der oberen rechten Ecke:
SonarCloud ist bereits in verschiedene Quellcode-Verwaltungsplattformen wie Github oder Azure DevOps integriert, so dass wir eines dieser Konten verwenden werden, um unser SonarCloud-Konto zu erstellen. So einfach ist das!
Auf der SonarCloud-Website selbst müssen wir 2 Dinge tun:
Um ein Token für Azure DevOps zu erstellen, gehen Sie einfach nach rechts oben, wo sich das Symbol unseres Avatars befindet, und klicken Sie darauf. Daraufhin wird ein Dropdown-Menü angezeigt, in dem Sie auf “Mein Konto” klicken müssen:
Sobald wir drin sind, gehen wir auf die Registerkarte “Sicherheit” und vergeben einen Namen für das Token, damit wir es später identifizieren können, falls wir es widerrufen müssen. Sobald der Name zugewiesen wurde, klicken Sie auf die Schaltfläche “Generieren”. Dadurch wird ein Token generiert, das wir kopieren und speichern müssen, da wir es nicht mehr sehen können und wir es benötigen, wenn wir Azure DevOps konfigurieren:
Als Nächstes erstellen wir das Sonar-Projekt, in dem wir die erzeugten Metriken aufzeichnen werden. Dazu klicken wir einfach auf die Schaltfläche “+” rechts neben unserem Avatar und wählen “Neues Projekt analysieren”.
Von hier aus wird ein weiteres Fenster angezeigt, in dem wir das Projekt aus der von uns verwendeten Account-Organisation (GitHub, Bitbucket, Azure DevOps,…) auswählen können. Wenn Sie ein Repository direkt aus der Liste auswählen, kümmert sich SonarCloud um alles Notwendige, indem es die Webhooks registriert, die für die Arbeit benötigt werden.
Das ist in Ordnung, wenn wir eine schnelle und einfache Integration wünschen, aber eine Option, die für mich persönlich viel interessanter ist, ist die manuelle Erstellung des Projekts. Dies ist das Modell, dem wir in diesem Beitrag folgen werden, obwohl es für Benutzer ohne Kenntnisse nicht empfehlenswert ist, also klicken wir auf “Manuelles Erstellen eines Projekts”:
Danach, und um die Konfiguration in SonarCloud abzuschließen, müssen wir auswählen, in welcher SonarCloud-Organisation wir das Projekt erstellen wollen (oder eine neue erstellen, falls wir es in keine bestehende Organisation einordnen wollen) und einen Schlüssel für das Projekt vergeben, den wir später in Azure DevOps benötigen, sowie einen Namen für das Projekt. Wenn wir fertig sind, müssen wir nur noch auf “Einrichten” klicken, um das Projekt zu erstellen:
Wenn wir damit fertig sind, haben wir alles vorbereitet. Richten wir nun den entsprechenden Teil auf Azure DevOps ein!
Um die SonarCloud-Prozesse einfach in Azure DevOps zu integrieren, müssen wir zunächst den SonarCloud-Task in der Organisation installieren. Falls wir SonarQube verwenden, gibt es auch einen spezifischen Task von SonarQube, den wir in der Organisation installieren können und der praktisch auf die gleiche Weise konfiguriert wird.
Sobald wir den SonarCloud-Task in unserer Organisation installiert haben, gehen wir zu den Azure DevOps-Projekteigenschaften und erstellen eine Dienstverbindung vom Typ SonarCloud:
Daraufhin wird ein kleines Formular angezeigt, in das wir das Token eingeben, das wir in SonarCloud generiert haben, und der Verbindung einen Namen geben. Danach klicken Sie einfach auf “Überprüfen und speichern”, um die Verbindung zu validieren und zu speichern, damit wir sie in den Pipelines verwenden können.
Mit diesen beiden Dingen werden wir unsere Pipeline erstellen, um eine Analyse mit Sonar durchzuführen.
Wir haben bereits alles vorbereitet, um die statische Analyse mit SonarCloud automatisch in unserer Azure DevOps-Pipeline durchführen zu können, also erstellen wir eine Pipeline.
Für diesen Eintrag werden wir die Pipelines mit der grafischen Oberfläche verwenden, weil es für Leute, die es nicht gewohnt sind, die Pipelines in YAML zu verwenden, einfacher zu folgen ist, aber am Ende werde ich die komplette YAML für diejenigen lassen, die wie ich es bevorzugen, sie zu verwenden.
Klicken Sie einfach auf die Schaltfläche “Pipeline erstellen” im Menü “Pipelines”:
Dies öffnet ein Formular, in dem die Quelle des Codes für die Pipeline ausgewählt werden kann, und am unteren Rand befindet sich ein Link zur Verwendung des klassischen Editors:
Wenn wir auswählen, dass wir den klassischen Editor verwenden wollen, öffnet sich ein neues Fenster, in dem wir gefragt werden, wo sich das Repository befindet. Hier hängt es davon ab, wo jede Person das Repository hostet, da Azure Pipelines (das CI/CD-System von Azure DevOps) es zulässt, dass der Quellcode außerhalb von Azure DevOps liegt, zum Beispiel in Github, Bitbucket…
Sobald das Repository ausgewählt ist, können wir aus verschiedenen vorgefertigten Vorlagen für unterschiedliche Zwecke wählen. In diesem Beispiel werden wir ein .NET Core-Projekt analysieren, also geben wir einfach “Sonar” in die Suchmaschine ein, um zu sehen, ob es dafür eine Vorlage gibt.
Wählen Sie es einfach aus und alle notwendigen Aufgaben werden erstellt, um eine standardmäßige .NET Core-Pipeline mit der Sonar-Analyse auszuführen, die dann konfiguriert werden kann:
Was die Auswahl des Agenten betrifft, so gibt es hier kein Geheimnis. Wir wählen den Agenten, der für uns am besten geeignet ist, je nach Sprache und Plattform, für die wir die Lösung kompilieren. Dann wählen wir einfach die SonarCloud-Vorbereitungsaufgabe (die auf dem Foto oben abgebildet ist) und konfigurieren die drei erforderlichen Felder, nämlich die zuvor erstellte Dienstverbindung, die SonarCloud-Organisation, in der das Sonar-Projekt gehostet wird, und den Schlüssel, den wir bei der Erstellung des Sonar-Projekts angeben.
Sobald das alles eingerichtet ist, müssen wir die Pipeline nur noch speichern und ausführen. Wenn alles gut geht, werden wir, wenn wir zur Projektseite in SonarCloud zurückkehren, sehen, dass sie sich verändert hat und Ergebnisse über die Qualität des Codes anzeigt.
Wir haben bereits die Automatisierung der Codequalitätsanalyse vorbereitet, was an sich schon sehr nützlich ist, da wir so verschiedene Probleme mit der Codequalität erkennen können. Ohne weiter darauf einzugehen, können Sie auf dem vorigen Bild sehen, wie SonarCloud zwei Punkte entdeckt hat, an denen es etwas Seltsames im Code gibt (ich habe es absichtlich gemacht, ich schwöre es!), und so weist es auf diese Punkte hin.
Trotzdem liegt die Codeabdeckung immer noch bei 0%, obwohl mein Projekt mehr enthält. Das liegt daran, dass Sonar selbst diese Daten nicht erzeugt, obwohl es sie anzeigen und verwenden kann. Um sie zu sehen, müssen wir einige kleine Änderungen am Code und an der Pipeline vornehmen, damit der Abdeckungsbericht generiert und an SonarCloud gesendet wird.
Als erstes müssen wir das NuGet-Paket coverlet.msbuild zu den Testprojekten hinzufügen. Danach müssen wir die Testlaufaufgabe geringfügig ändern, damit der Bericht mit diesem NuGet-Paket erstellt wird. Dazu fügen wir in der “test”-Aufgabe “/p:CollectCoverage=true /p:CoverletOutputFormat=opencover –logger trx” zu den Argumenten hinzu. Damit wird das NuGet-Paket angewiesen, die Abdeckung zu sammeln und in einem Opencover-Format zu speichern:
Schließlich müssen wir bei der Vorbereitung von SonarCloud Sonar mitteilen, wie es die von uns generierte Abdeckung aufnehmen soll, indem wir dies im erweiterten Abschnitt hinzufügen:
extraProperties: |
sonar.exclusions=**/obj/**,**/*.dll
sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/**/coverage.opencover.xml
sonar.cs.vstest.reportsPaths=$(Agent.TempDirectory)/*.trx
Nachdem wir diese kleinen Änderungen vorgenommen haben, müssen wir nur noch die Pipeline laufen lassen, um einen Bericht zur Verfügung zu haben, in dem wir nun die Ausführung der Tests und deren Abdeckung sammeln.
Und wie ich bereits erwähnt habe, hier ist die YAML:
pool: vmImage: 'windows-latest' Anforderungen: java steps: - task: UseDotNet@2 Eingaben: packageType: 'sdk' Version: '3.1.301' - Aufgabe: DotNetCoreCLI@2 Anzeigename: Wiederherstellen Eingaben: command: 'wiederherstellen' Projekte: '**/*.sln' - Aufgabe: SonarSource.sonarcloud.14d9cde6-c1da-4d55-aa01-2965cd301255.SonarCloudPrepare@1 displayName: 'Analyse auf SonarCloud vorbereiten' Eingaben: SonarCloud: SonarCloud Organisation: 'jorturfer-github' ProjektSchlüssel: 'sonar-plain' extraProperties: | sonar.exclusions=**/obj/**,**/*.dll sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/**/coverage.opencover.xml sonar.cs.vstest.reportsPaths=$(Agent.TempDirectory)/*.trx - Aufgabe: DotNetCoreCLI@2 Anzeigename: Erstellen Eingaben: Projekte: '**/*.sln' Argumente: '--konfiguration Freigabe' - Aufgabe: DotNetCoreCLI@2 Anzeigename: Test Eingaben: Befehl: Test Projekte: '**/*.sln' Argumente: '--configuration Release /p:CollectCoverage=true /p:CoverletOutputFormat=opencover --logger trx' - Aufgabe: SonarSource.sonarcloud.ce096e50-6155-4de8-8800-4221aaeed4a1.SonarCloudAnalyze@1 displayName: 'Code-Analyse ausführen' - Aufgabe: SonarSource.sonarcloud.38b27399-a642-40af-bb7d-9971f69712e8.SonarCloudPublish@1 displayName: 'Publish Quality Gate Result'
Elena Canorea
Communications Lead
Cookie | Duration | Description |
---|---|---|
__cfduid | 1 year | The cookie is used by cdn services like CloudFare to identify individual clients behind a shared IP address and apply security settings on a per-client basis. It does not correspond to any user ID in the web application and does not store any personally identifiable information. |
__cfduid | 29 days 23 hours 59 minutes | The cookie is used by cdn services like CloudFare to identify individual clients behind a shared IP address and apply security settings on a per-client basis. It does not correspond to any user ID in the web application and does not store any personally identifiable information. |
__cfduid | 1 year | The cookie is used by cdn services like CloudFare to identify individual clients behind a shared IP address and apply security settings on a per-client basis. It does not correspond to any user ID in the web application and does not store any personally identifiable information. |
__cfduid | 29 days 23 hours 59 minutes | The cookie is used by cdn services like CloudFare to identify individual clients behind a shared IP address and apply security settings on a per-client basis. It does not correspond to any user ID in the web application and does not store any personally identifiable information. |
_ga | 1 year | This cookie is installed by Google Analytics. The cookie is used to calculate visitor, session, campaign data and keep track of site usage for the site's analytics report. The cookies store information anonymously and assign a randomly generated number to identify unique visitors. |
_ga | 1 year | This cookie is installed by Google Analytics. The cookie is used to calculate visitor, session, campaign data and keep track of site usage for the site's analytics report. The cookies store information anonymously and assign a randomly generated number to identify unique visitors. |
_ga | 1 year | This cookie is installed by Google Analytics. The cookie is used to calculate visitor, session, campaign data and keep track of site usage for the site's analytics report. The cookies store information anonymously and assign a randomly generated number to identify unique visitors. |
_ga | 1 year | This cookie is installed by Google Analytics. The cookie is used to calculate visitor, session, campaign data and keep track of site usage for the site's analytics report. The cookies store information anonymously and assign a randomly generated number to identify unique visitors. |
_gat_UA-326213-2 | 1 year | No description |
_gat_UA-326213-2 | 1 year | No description |
_gat_UA-326213-2 | 1 year | No description |
_gat_UA-326213-2 | 1 year | No description |
_gid | 1 year | This cookie is installed by Google Analytics. The cookie is used to store information of how visitors use a website and helps in creating an analytics report of how the wbsite is doing. The data collected including the number visitors, the source where they have come from, and the pages viisted in an anonymous form. |
_gid | 1 year | This cookie is installed by Google Analytics. The cookie is used to store information of how visitors use a website and helps in creating an analytics report of how the wbsite is doing. The data collected including the number visitors, the source where they have come from, and the pages viisted in an anonymous form. |
_gid | 1 year | This cookie is installed by Google Analytics. The cookie is used to store information of how visitors use a website and helps in creating an analytics report of how the wbsite is doing. The data collected including the number visitors, the source where they have come from, and the pages viisted in an anonymous form. |
_gid | 1 year | This cookie is installed by Google Analytics. The cookie is used to store information of how visitors use a website and helps in creating an analytics report of how the wbsite is doing. The data collected including the number visitors, the source where they have come from, and the pages viisted in an anonymous form. |
attributionCookie | session | No description |
cookielawinfo-checkbox-analytics | 1 year | Set by the GDPR Cookie Consent plugin, this cookie is used to record the user consent for the cookies in the "Analytics" category . |
cookielawinfo-checkbox-necessary | 1 year | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary". |
cookielawinfo-checkbox-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary". |
cookielawinfo-checkbox-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary". |
cookielawinfo-checkbox-necessary | 1 year | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary". |
cookielawinfo-checkbox-non-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Non Necessary". |
cookielawinfo-checkbox-non-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Non Necessary". |
cookielawinfo-checkbox-non-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Non Necessary". |
cookielawinfo-checkbox-non-necessary | 1 year | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Non Necessary". |
cookielawinfo-checkbox-performance | 1 year | Set by the GDPR Cookie Consent plugin, this cookie is used to store the user consent for cookies in the category "Performance". |
cppro-ft | 1 year | No description |
cppro-ft | 7 years 1 months 12 days 23 hours 59 minutes | No description |
cppro-ft | 7 years 1 months 12 days 23 hours 59 minutes | No description |
cppro-ft | 1 year | No description |
cppro-ft-style | 1 year | No description |
cppro-ft-style | 1 year | No description |
cppro-ft-style | session | No description |
cppro-ft-style | session | No description |
cppro-ft-style-temp | 23 hours 59 minutes | No description |
cppro-ft-style-temp | 23 hours 59 minutes | No description |
cppro-ft-style-temp | 23 hours 59 minutes | No description |
cppro-ft-style-temp | 1 year | No description |
i18n | 10 years | No description available. |
IE-jwt | 62 years 6 months 9 days 9 hours | No description |
IE-LANG_CODE | 62 years 6 months 9 days 9 hours | No description |
IE-set_country | 62 years 6 months 9 days 9 hours | No description |
JSESSIONID | session | The JSESSIONID cookie is used by New Relic to store a session identifier so that New Relic can monitor session counts for an application. |
viewed_cookie_policy | 11 months | The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data. |
viewed_cookie_policy | 1 year | The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data. |
viewed_cookie_policy | 1 year | The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data. |
viewed_cookie_policy | 11 months | The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data. |
VISITOR_INFO1_LIVE | 5 months 27 days | A cookie set by YouTube to measure bandwidth that determines whether the user gets the new or old player interface. |
wmc | 9 years 11 months 30 days 11 hours 59 minutes | No description |
Cookie | Duration | Description |
---|---|---|
__cf_bm | 30 minutes | This cookie, set by Cloudflare, is used to support Cloudflare Bot Management. |
sp_landing | 1 day | The sp_landing is set by Spotify to implement audio content from Spotify on the website and also registers information on user interaction related to the audio content. |
sp_t | 1 year | The sp_t cookie is set by Spotify to implement audio content from Spotify on the website and also registers information on user interaction related to the audio content. |
Cookie | Duration | Description |
---|---|---|
_hjAbsoluteSessionInProgress | 1 year | No description |
_hjAbsoluteSessionInProgress | 1 year | No description |
_hjAbsoluteSessionInProgress | 1 year | No description |
_hjAbsoluteSessionInProgress | 1 year | No description |
_hjFirstSeen | 29 minutes | No description |
_hjFirstSeen | 29 minutes | No description |
_hjFirstSeen | 29 minutes | No description |
_hjFirstSeen | 1 year | No description |
_hjid | 11 months 29 days 23 hours 59 minutes | This cookie is set by Hotjar. This cookie is set when the customer first lands on a page with the Hotjar script. It is used to persist the random user ID, unique to that site on the browser. This ensures that behavior in subsequent visits to the same site will be attributed to the same user ID. |
_hjid | 11 months 29 days 23 hours 59 minutes | This cookie is set by Hotjar. This cookie is set when the customer first lands on a page with the Hotjar script. It is used to persist the random user ID, unique to that site on the browser. This ensures that behavior in subsequent visits to the same site will be attributed to the same user ID. |
_hjid | 1 year | This cookie is set by Hotjar. This cookie is set when the customer first lands on a page with the Hotjar script. It is used to persist the random user ID, unique to that site on the browser. This ensures that behavior in subsequent visits to the same site will be attributed to the same user ID. |
_hjid | 1 year | This cookie is set by Hotjar. This cookie is set when the customer first lands on a page with the Hotjar script. It is used to persist the random user ID, unique to that site on the browser. This ensures that behavior in subsequent visits to the same site will be attributed to the same user ID. |
_hjIncludedInPageviewSample | 1 year | No description |
_hjIncludedInPageviewSample | 1 year | No description |
_hjIncludedInPageviewSample | 1 year | No description |
_hjIncludedInPageviewSample | 1 year | No description |
_hjSession_1776154 | session | No description |
_hjSessionUser_1776154 | session | No description |
_hjTLDTest | 1 year | No description |
_hjTLDTest | 1 year | No description |
_hjTLDTest | session | No description |
_hjTLDTest | session | No description |
_lfa_test_cookie_stored | past | No description |
Cookie | Duration | Description |
---|---|---|
loglevel | never | No description available. |
prism_90878714 | 1 month | No description |
redirectFacebook | 2 minutes | No description |
YSC | session | YSC cookie is set by Youtube and is used to track the views of embedded videos on Youtube pages. |
yt-remote-connected-devices | never | YouTube sets this cookie to store the video preferences of the user using embedded YouTube video. |
yt-remote-device-id | never | YouTube sets this cookie to store the video preferences of the user using embedded YouTube video. |
yt.innertube::nextId | never | This cookie, set by YouTube, registers a unique ID to store data on what videos from YouTube the user has seen. |
yt.innertube::requests | never | This cookie, set by YouTube, registers a unique ID to store data on what videos from YouTube the user has seen. |