• info@noobygames.de

Category ArchiveTutorial

azure devops meme

Use child proccesses templates in azure devops

Hey, u are using azure devops for your project management and tasks and stuff? I hope u use a child process template in azure devops. This may sound familiar, if you ever worked with WordPress and wanted to customize a Theme, i bet you also created a child Theme in order to be able to update the Theme.

Using a "child process template" or inherited process, allows u to customize the Process without breaking the process for other teams.

To create a child process, simply go to your Organization Settings -> Boards, klick on the process u want to use and select "Create inherited process".

A Popup will show up and allow u to set a Name and a Description.

Now click on Create process and your are done.

Now you can customize all WorkItemTypes, set Rules etc. that only apply to the child process.

Multiple teams with only slightly different requirements

If you have multiple teams with only slightly different requirements regarding the customization of a process, setup a child process configure the overlapping requirements of the teams and create copys of the child process afterwards.

azure devops meme

Automatically create a changelog using Azure DevOps

There is a point in the live of every developer, where you have to maintain a changelog. Let's be honest, most of us start by manually creating a changelog. And then you forget to update the changelog from time to time, this is where your customer start to call your support and you start to cry. So you change your processes, add checklists, that you manually check and we all know that every time you have to manually maintain the changelog a little tear makes it all the way from your sad eye to your chin and silently drops into the darkness as you try to think of a good description for your change. BUT CRY NO MORE! You can easily automatically create a changelog using Azure DevOps

At Clarilab i have the possibility to do cool stuff. So i took some time to check out some possibilities to automate that whole tear shredding process. What follows is an explanation of our first attempt to automatically create the changelog, using build pipelines and 2 extensions.

XplatGenerateReleaseNotes

The first extension that we use is XplatGenerateReleaseNotes from Richard Fennel. It allows us to use a HandleBar template to define the content and style of the changelog.

So install the XplatGenerateReleaseNotes plugin using the link above.
Now add a Task to your build OR release pipeline and chose.
XplatGenerateReleaseNotes

You need to specify the path for the artifact. In our exmaple we chose System.DefaultWorkingDirectory so we have access to the file in the next step.

outputfile: '$(System.DefaultWorkingDirectory)\releasenotes.md'

You can either specify the template in a file in your repository, or inline. In our example we chose inline.

templateLocation: 'InLine'

Explanation of all arguments is here

Example Configuration


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
- task: XplatGenerateReleaseNotes@3
  inputs:
    outputfile: '$(System.DefaultWorkingDirectory)\releasenotes.md'
    outputVariableName: 'kycnow-dev-release'
    templateLocation: 'InLine'
    inlinetemplate: |
      **Release Notes**

      # Notes for release  {{releaseDetails.releaseDefinition.name}}    
      **Release Number**  : {{releaseDetails.name}}

      **Release completed** : {{releaseDetails.modifiedOn}}    

      **Build Number**: {{buildDetails.id}}

      **Compared Release Number**  : {{compareReleaseDetails.name}}  

      **Build Trigger PR Number**: {{lookup buildDetails.triggerInfo 'pr.number'}}

      # Associated Pull Requests ({{pullRequests.length}})
      {{#forEach pullRequests}}
      {{#if isFirst}}### Associated Pull Requests (only shown if  PR) {{/if}}
      *  **PR {{this.id}}**  {{this.title}}
      {{/forEach}}

      # Builds with associated WI/CS/Tests ({{builds.length}})
      {{#forEach builds}}
      {{#if isFirst}}## Builds {{/if}}
      ##  Build {{this.build.buildNumber}}
      {{#forEach this.commits}}
      {{#if isFirst}}### Commits {{/if}}
      - CS {{this.id}}
      {{/forEach}}
      {{#forEach this.workitems}}
      {{#if isFirst}}### Workitems {{/if}}
      - WI {{this.id}}
      {{/forEach}}
      {{#forEach this.tests}}
      {{#if isFirst}}### Tests {{/if}}
      - Test {{this.id}}
        -  Name: {{this.testCase.name}}
        -  Outcome: {{this.outcome}}
      {{/forEach}}
      {{/forEach}}

      # Global list of WI ({{workItems.length}})
      {{#forEach this.workItems}}
      {{#if isFirst}}### WorkItems {{/if}}
      *  **{{this.id}}**  {{lookup this.fields 'System.Title'}}
        - **WIT** {{lookup this.fields 'System.WorkItemType'}}
        - **Tags** {{lookup this.fields 'System.Tags'}}
        - **Assigned** {{#with (lookup this.fields 'System.AssignedTo')}} {{displayName}} {{/with}}
        - **Description** {{{lookup this.fields 'System.Description'}}}
        - **Parents**
      {{#forEach this.relations}}
      {{#if (contains this.attributes.name 'Parent')}}
      {{#with (lookup_a_work_item ../../relatedWorkItems  this.url)}}
            - {{this.id}} - {{lookup this.fields 'System.Title'}}
      {{/with}}
      {{/if}}
      {{/forEach}}
        - **Children**
      {{#forEach this.relations}}
      {{#if (contains this.attributes.name 'Child')}}
      {{#with (lookup_a_work_item ../../relatedWorkItems  this.url)}}
            - {{this.id}} - {{lookup this.fields 'System.Title'}}
      {{/with}}
      {{/if}}
      {{/forEach}}
      {{/forEach}}

      # Global list of CS ({{commits.length}})
      {{#forEach commits}}
      {{#if isFirst}}### Associated commits{{/if}}
      * **ID{{this.id}}**
        -  **Message:** {{this.message}}
        -  **Commited by:** {{this.author.displayName}}
        -  **FileCount:** {{this.changes.length}}
      {{#forEach this.changes}}
            -  **File path (TFVC or TfsGit):** {{this.item.path}}  
      {{/forEach}}
      {{/forEach}}
    checkStage: false
    overrideStageName: 'dev'
    stopOnRedeploy: false
    sortWi: true
    dumpPayloadToConsole: false
    dumpPayloadToFile: false
    replaceFile: true
    getParentsAndChildren: true

Example Template

The example template used is simply the example template from the extension iteself. It adds linked Work items, Pull Requests and Commits.

WIKI Updater Tasks

So after the first step, we do now have a fancy markdown file. Our story could end here, as you don't have to shed single tear anymore, BUT we want ALL THE AZURE DEV OPS MAGIC! So let us automatically deploy the generated markdownfile to a github or azure dev ops wiki.

You need to install WIKI Updater Tasks, which is also from Richard Fennel, thanks dude!

This plugin simply takes the markdown file and pushes it to a git repo.
So add a Git Based Single File Updater as we only generate a single file. If you deploy to a github wiki, like in our example, you need to generate a PAT. Now you can specify a gitname and gitemail, the user and email adress does not need to exist, like in our case. You can also use Azure DevOps variables to insert the Maintainer, who triggered the release/build.

Specify the sourceFile, which is the file we created in the first step.

sourceFile: '$(System.DefaultWorkingDirectory)\releasenotes.md'

You can specify the commit message.

message: 'Update Documentation'

Specify the filename in the remote repository

filename: 'releasenotes.md'

We don't want to replace the file.

replaceFile: false

Instead we want to append to the file.

appendToFile: True

I created a secret variable in the build pipeline and named it github-passwort, that is the PAT.

password: '$(github-passwort)'

The user i created the PAT for was Nerzal.

user: 'Nerzal'

Exmaple Configuration


1
2
3
4
5
6
7
8
9
10
11
12
13
14
- task: WikiUpdaterTask@1
  inputs:
    repo: 'https://github.com/Clarilab/kycnow-changelog.git'
    filename: 'releasenotes.md'
    replaceFile: false
    appendToFile: True
    dataIsFile: true
    sourceFile: '$(System.DefaultWorkingDirectory)\releasenotes.md'
    message: 'Update Documentation'
    gitname: 'clarilab-bot'
    gitemail: 'bot@clarilab.de'
    user: 'Nerzal'
    password: '$(github-passwort)'
    localpath: '$(System.DefaultWorkingDirectory)\repo'

Wiki for both extensions

You can find the wiki for both extensions. here

Result Example

image

Where to go from here?

Now as we do automatically create and deploy a changelog on each build, we might want to adjust the handlebar template to customize our changelog.
As soon as i have a better understanding of these templates, i will write another blog post with more detail in templating.

This blog post is brought to u by my new fancy markdown plugin

gopher

Cleancode in golang #1

In diesem Beitrag geht es nicht um Prinzipien und Praktiken von Cleancode, sondern um Hilfsmittel speziell für go, die dabei helfen den idiomatischen Weg einzuhalten.

Cleancode ist auch in go eine wichtige Angelegenheit. Als einstieg in die Thematik empfehle ich, dass du dich mit den Tooling, dass go mitbringt vertraut machst. 2 Tools, die dir dabei helfen deinen Code sauber zu halten sind golint und go vet.

go lint

Der linter prüft nicht auf die Korrektheit von Code, sondern viel mehr den CodeStyle.
Den standard linter, welcher die meisten Regeln aus dem Buch "Effective Go" kennt und prüft könnt ihr mithilfe vonfolgenden Befehl installieren.

go get -u golang.org/x/lint/golint

Eine einzelne Datei kann man mit folgendem Befehl prüfen lassen:

golint main.go

Es ist auch möglich eine Liste von Dateien anzugeben:

golint file1.go file2.go

Zusätzlich kann auch ein Pfad inklusive aller Unterordner geprüft werden

golint /pfad/zum/project/...

go vet

Das vet tool prüft den Code auf Korrektheit.
Dabei untersucht vet den Go-Code und reported verdächtige Konstrukte, wie Printf calls, deren Argument nicht mit dem format string übereinstimmen. Die algorythmen, die Vet benutzt garantieren nicht, dass alle gefundenen Probleme tatsächlich welche sind, aber dafür findet es Fehler, die nicht vom Compiler gefunden werden. Es ist somit immer nützlich mal darüber zu schauen, da gerade features wie das finden von ungenutztem Code, oder auch das fehlende Prüfen auf einen error sehr nützlich sind.

Um go vet zu starten kann einfach folgender Befehl verwendet werden

go vet /pfad/zum/project/...

Dieser untersucht sämtliche packages.

Um ein spezifisches Package zu untersuchen kann der pfad zum package direkt angegeben werden

go vet /pfad/zum/package

Features von go vet

asmdecl      report mismatches between assembly files and Go declarations
assign       check for useless assignments
atomic       check for common mistakes using the sync/atomic package
bools        check for common mistakes involving boolean operators
buildtag     check that +build tags are well-formed and correctly located
cgocall      detect some violations of the cgo pointer passing rules
composites   check for unkeyed composite literals
copylocks    check for locks erroneously passed by value
httpresponse check for mistakes using HTTP responses
loopclosure  check references to loop variables from within nested functions
lostcancel   check cancel func returned by context.WithCancel is called
nilfunc      check for useless comparisons between functions and nil
printf       check consistency of Printf format strings and arguments
shift        check for shifts that equal or exceed the width of the integer
stdmethods   check signature of methods of well-known interfaces
structtag    check that struct field tags conform to reflect.StructTag.Get
tests        check for common mistaken usages of tests and examples
unmarshal    report passing non-pointer or non-interface values to unmarshal
unreachable  check for unreachable code
unsafeptr    check for invalid conversions of uintptr to unsafe.Pointer
unusedresult check for unused results of calls to some functions

golangci-lint

Wem der default linter nicht mächtig genug ist, der kann auf golangci-lint zurück greifen. Dieses mächtige Tool ist komplett konfigurierbar und bietet eine wesentlich größere Anzahl an lintern, sowie die möglichkeit einige Fehler direkt zu beheben.

Zusätzlich ist die Ausgabe der Fehler besser lesbar und enthält mehr Informationen, als beim default linter.

Beispiel Ausgabe:

pkg/onboarding/service.go:267:2: Consider preallocating `financialEligibles` (prealloc)
var financialEligibles []models.Individual
^
cmd/local/main.go:31:1: cyclomatic complexity 12 of func `main` is high (> 10) (gocyclo)
func main() {
^

golangci-lint ist als open source Projekt auf github gehostet.

Zum installieren muss lediglich folgender Befehl ausgeführt werden:

go get -u github.com/golangci/golangci-lint/cmd/golangci-lint

Golangci lint kann in alle gängigen Editoren/IDEs integriert werden.

gofmt

Wir halten nun also den default CodeStyle ein und kümmern uns um die Korrektheit. Nun wollen wir natürlich auch noch die korrekte Formatierung nutzen. Dafür gibt es das gofmt tool

Usage:

Um direkt alle Dateien zu überprüfen und fixes anzuwenden kann folgender command genutzt werden.

go fmt -n -x pfad/zum/projekt/...

Nützlich kann hierbei auch eine git pre push hook sein.

Ein Beispiel dafür findet ihr hier:

https://github.com/edsrzf/gofmt-git-hook

sonarqube_gopher

IOSP

Jeder von uns hat es schon erlebt, dass er in ein bestehendes Projekt kommt und eine neue Funktionalität implementieren will und dann einen Moloch vorfand. Einen solchen Moloch, dass wir gar nicht mehr wussten, wie man diesen Bändigen soll. Nun habe ich in solchen Fällen viele Stunden, oder gar Tage damit verbracht den Moloch zu verstehen und manchmal habe ich dem Moloch dann noch einen Arm, oder ein Bein angeheftet, was ihn nur noch größer, furchteinflößender und vor allem komplizierter gemacht hat.

Ein solcher Moloch kann in vielen Formen auftreten. Manchmal ist es eine Klasse mit tausenden LOC (Lines of Code), manchmal ist es eine Funktion mit einer unglaublich großen CC (Cyclomatic Komplexity), die durch viele IF-Else- und große Switch-Strukturen erzeugt wurde. Viele von uns haben sich bereits die Frage gestellt, wie man diese angsteinflößenden Monstrositäten bändigen kann. Ich möchte euch nun ein Mittel an die Hand geben, dass jeder Entwickler leicht verstehen und Umsetzen kann. Dies funktioniert sowohl bei alten, wie auch bei neuem Code. Das Wundermittel nennt sich Integration Operation Segregation Principle kurz IOSP.

Wie Erkenne ich einen solchen Moloch?

Glücklicherweise sind Verstöße gegen IOSP leicht an folgenden Verstößen zu erkennen:

  • Wenn man ein schlechtes Bauchgefühl hat.
  • Die Methode hat mehr als 10 - 30 Zeilen
  • Die Zyklomatische Komplexität ist sehr hoch
  • Die Methode hat viele Abhängigkeiten

Um diese Verstöße gegen IOSP und damit meist auch Verstöße gegen weitere Prinzipien. Ausfindig zu machen kann man Beispielsweise Statische Code Analyse Tools verwenden. Wie das geht zeige ich hier.

Wie funktioniert IOSP?

Wenn Code nur Bausteine aus dem eigenen Projekt, des eigenen Codes zusammensteckt, dann nennt man ihn Integration.

Wenn der Code domänen Logik und Kontrollstrukturen enthält, handelt es sich um eine Operation

In folgendem Beispiel sieht man eine Kette von Funktionsaufrufen. Möchte man nun Funktion 1 testen, so muss man mit großer Wahrscheinlichkeit auf Mocking/Faking der Abhängigkeiten zurück greifen, da hier Operation und Integration vermischt wurde.

Hier sollte man nun im ersten Schritt versuchen diese Verkettung zu vereinfachen. Eine mögliche Vereinfachung wird im nächsten Bild dargestellt.
Hier sind nun Funktion 3, 5 und 6 als Operation erkennbar. Diese können ohne weiteres einfach getestet werden. Funktion 1 Integriert hierbei Funktion 2, 4 und 6

In unserem Beispiel haben stellen wir allerdings fest, dass wir die Bausteine noch weiter voneinander entkoppeln können, indem wir die Funktion 2 bis 6 in Funktion 1 integrieren.

Im letzten Schaubild sehen wir nun, wie die "perfekte" Umsetzung von IOSP aussehen kann. Funktion 2 bis 6 sind komplett von den anderen Funktionen entkoppelt. Sie haben keine Kenntnis voneinander und stellen reine Operationen dar. Funktion 1 hingegen integriert die anderen 5.

Somit sind Funktion 2 bis 6 nun einfach über UnitTests testbar und Funktion 1 über einen Integrationstest.

Ich habe diese Entkopplung bewusst in 2 Schritten dargestellt, um klar zu machen, dass es einfacher ist sich einzelnen Teilen der Logik zu widmen, anstatt das komplette Paket in einem rutsch zu refactoren.

Welche Vorteile haben wir durch IOSP?

Setzt man IOSP Konsequent um, so ergeben sich folgende Vorteile:

  • Kurze einfach zu verstehende Methoden
  • Leicht testbarer Code
  • Evolvierbarer Code
  • Die Methodennamen werden wieder aussagekräftig

sonarqube_gopher

Statische Code Analyse mit Sonarqube

Jeder der sich um Code Qualität sorgt, hat sich schon einmal Gedanken gemacht, wie er das ganze Analysieren kann um ein Gesamtbild zu bekommen. Nun kann man den ganzen Quellcode durchschauen und sich dabei Notizen machen, oder Tickets erstellen für Stellen im Code, die nicht so geil sind, oder man benutzt ein statisches Codeanalysetool. Ich habe mich für eine statische Code Analyse mit Sonarqube entschieden. Sonarqube gibt es als OnPremise und als Cloud variante.

Aufsetzen von Sonarqube

Was braucht man dafür? Natürlich lasse ich das erst mal in einem Docker Container laufen. Dafür tippt man, vorausgesetzt Docker ist installiert, einfach die folgenden 2 Befehle ein:

1
docker pull sonarqube
1
docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 sonarqube:latest

Anschließend wartet man einen kurzen Moment, bis der Container hochgefahren ist. Das hat bei mir so 5-10 Sekunden gedauert. Jetzt könnt ihr die Sonarqube Instanz bereits unter localhost:9090 finden

Erstellen eines Projekts

Ruft im Browser den Link http://localhost:9000 auf.
Loggt euch anschließend mit folgenden Credentials ein:

Username: admin
Password: admin

login_create_project

login_create_project

Anschließend könnt ihr ein neues Projekt erstellen. Vergebt dafür einfach einen Namen, bestätigt und anschließend klickt ihr auf Generate.

Sonar Scanner

Ladet euch nun Sonar Scanner herunter. Entpackt diesen an einen beliebigen Ort und fügt den Pfad zum bin Ordner dem Umgebungspfad hinzu.

install plugin

install plugin

Analyse Starten

Startet nun die analyse im folgenden Beispiel habe ich als projectkey "gocloak" gesetzt, da ich ein Projekt mit dem key "gocloak" angelegt habe. Dies müsst ihr durch euren Key austauschen und anschließend im project-root ausführen. Den Login Wert müsst ihr durch den key austauschen, den ihr zuvor erstellt habt.

1
sonar-scanner -Dsonar.projectKey=gocloak -Dsonar.sources=. -Dsonar.host.url=http://localhost:9000 -Dsonar.login=8f032266ebed7ca4ec79e22f464b8649455bad77

Alternativ könnt ihr als Login auch folgendes verwenden:

1
sonar-scanner -Dsonar.projectKey=gocloak -Dsonar.sources=. -Dsonar.host.url=http://localhost:9000 -Dsonar.login=admin -Dsonar.password=admin

Ist der Befehl erfolgreich durchgelaufen könnt ihr euch das Ergebnis auf Sonarqube anschauen.

sonar result

sonar result

How To: ScriptableObject automatisch erstellen

Wie kann man eigentlich ein ScriptableObject automatisiert erzeugen?

Diese Fragen und weitere werde ich mit kleinen Tutorials hier im Blog beantworten. Ich habe mir für diesen Zweck ein Plugin organisiert, welches Code-Embedding vereinfacht. Nun wenden wir uns aber den ScriptableObject zu.

 

1. Erstellen eines ScriptableObjects

Zuerst benötigen wir ein ScriptableObject. Dazu habe ich beispielhaft ein sehr simples erzeugt. Unser ScriptableObject muss natürlich von ScriptableObject erben. Dazu habe ich noch 2 float Felder für den Schaden und die Geschwindigkeit eingefügt, sowie ein Feld, welches das ParticleSystem hält.

1
2
3
4
5
6
7
8
9
10
// MetaData for all Projectiles in the game
public class ProjectileMetaData : ScriptableObject {
// Damage of the Projectile
public float Damage = 1;</div>
// Speed of the Projectile
public float Speed = 10;

// ParticleSystem of the Projectile
public GameObject ParticleSystem;
}

 

2. Erstellen des EditorScripts

Das folgende Snippet sorgt dafür, dass im UnityEditor ein neuer Tab("ScriptableObjects") eingefügt wird. Dort gibt es die unterpunkte "Create" und "Projectile". Klickt man nun auf Projectile wird die Methode "CreateMyAsset" ausgeführt.

Tab

Tab

namespace Editor.ProjectileCreater {

public class MakeScriptableObject {
  [MenuItem("ScriptableObjects/Create/Projectile")]
  public static void CreateMyAsset() {
    // Erzeugt eine Instanz vom Typ "ProjectileMetaData".
    ProjectileMetaData asset = ScriptableObject.CreateInstance<ProjectileMetaData>();
    // Erzeugt ein Asset mit hilfe der gerade erzeugten Instanz.
    // Wichtig: Die gesamte Ordnerstruktur muss im Projekt existieren, sonst gibt es einen Fehler.
    AssetDatabase.CreateAsset(asset, "Assets/Scripts/Projectiles/ScriptableObjects/NewScriptableObject.asset");
    // Schreibt das Asset auf die Festplatte
    AssetDatabase.SaveAssets();
    // Setzt den Fokus
    EditorUtility.FocusProjectWindow();
    // Selektiert das gerade erzeugte Objekt
    Selection.activeObject = asset;
  }
}
}

 

3. Das Ergebnis

Das ScriptableObject wurde als Asset im gewünschten Pfad erzeugt und kann nun benutzt werden. Das ist auch schon die gesamte Magie daran. Das obige Script kann angepasst werden um beliebige ScriptableObjects zu erzeugen. Dafür muss nur der generische Typparameter gegen einen anderen getauscht werden und falls notwendig kann der Pfad angepasst werden.

ProjectView

ProjectView

%d Bloggern gefällt das:

Durch die weitere Nutzung der Seite stimmst du der Verwendung von Cookies zu. Weitere Informationen

Die Cookie-Einstellungen auf dieser Website sind auf "Cookies zulassen" eingestellt, um das beste Surferlebnis zu ermöglichen. Wenn du diese Website ohne Änderung der Cookie-Einstellungen verwendest oder auf "Akzeptieren" klickst, erklären Sie sich damit einverstanden.

Schließen