Azure DevOps path filters

Wprowadzenie

Możemy spotkać różne sposoby organizacji projektów w repozytoriach. Niektórzy preferują podejście monorepo, w którym wiele różnych projektów/aplikacji znajduje się w tym samym wspólnym repozytorium. Innym podejściem jest multirepo, gdzie dla każdego projektu/aplikacji tworzymy dedykowane repozytorium.

Jednym z problemów pierwszego podejścia jest optymalizacja budowania aplikacji w procesie CI/CD, bo nie chcemy budować wszystkich projektów w repozytorium, w momencie gdy zmienił się tylko jeden. W Azure DevOps możemy rozwiązać ten problem poprzez zastosowanie path filters w definicji pipeline.

Monorepo vs Multirepo

W tym wpisie nie będę chciał zagłębiać się w porównanie tych dwóch sposobów organizacji projektów/repozytoriów. Szczególnie że można spotkać różne warianty obu podejść (np. monorepo per zespół). Jeśli interesuje Cię ten temat, to zachęcam do przesłuchania 27. odcinka podcastu Patoarchitekci, w którym Łukasz Kałużny oraz Szymon Warda poruszają ten temat – https://patoarchitekci.io/27/.

Swoją drogą Łukasz i Szymon robią świetną robotę. Jeśli nie znasz ich podcastu, to gorąco zachęcam do słuchania. Bardzo dużo super wiedzy, podanej w krótki i skondensowany sposób.

Darmowy kurs Visual Studio

Pracując z setkami programistów, zauważyłem, że większość osób nie pracuje efektywnie w Visual Studio. W skrajnych przypadkach korzystali z kopiowania z wykorzystaniem menu Edit. Wiem, że to dziwne, ale naprawdę niektórzy tak pracują. Dlatego postanowiłem stworzyć kurs Visual Studio – aby pomóc koleżankom i kolegom w efektywniejszej pracy.

Przygotowałem 30 lekcji e-mail, w których pokażę Ci, w jaki sposób pracować efektywniej i szybciej w Visual Studio. Poznasz dodatki, bez których nie wyobrażam sobie pracy w tym IDE.

Po więcej informacji zapraszam na dedykowaną stronę kursu: Darmowy Kurs Visual Studio.

Quiz C#

Ostatnio przygotowałem również quiz C#, w którym możesz sprawdzić swoją wiedzę. Podejmiesz wyzwanie?

Buildy w Monorepo

W podejściu monorepo na ogół na głównym poziomie repozytorium dla każdej aplikacji/projektu mamy dedykowany folder, w którym następnie znajduje się cała zawartość projektu (kod, testy, dokumentacja).

Na potrzeby tego wpisu przygotowałem proste repozytorium, w którym są dwa projekty – project1 oraz project2:

Projekty w przykładowym wpisie

To co chcielibyśmy osiągnąć, to efektywne budowanie obu projektów. Czyli jeśli zmiany pojawiły się tylko w folderze project1, to chcemy uruchomić pipeline tylko dla tego projektu. Szkoda tracić czas i zasoby na budowę drugiego projektu, gdy w nim nie pojawiła się żadna zmiana. W Azure DevOps możemy coś takiego osiągnąć z wykorzystaniem path filters.

Path filters w Azure DevOps

W przykładzie każdy z projektów w swoim folderze będzie miał plik azure-pipelines.yml, w którym znajduje się definicja pipeline. Przykładowe projekty nie są mocno rozbudowane. Są to wygenerowane projekty WebApi w .NET 5, w których praktycznie nic nie zmieniałem.

Plik azure-pipelines.yml pierwszego projektu wygląda tak:

# ASP.NET Core (.NET Framework)
# Build and test ASP.NET Core projects targeting the full .NET Framework.
# Add steps that publish symbols, save build artifacts, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core
trigger:
branches:
include:
- master
paths:
include:
- project1/*
exclude:
- project1/azure-pipelines.yml
pool:
vmImage: 'windows-latest'
variables:
solution: 'project1/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: NuGetToolInstaller@1
- task: NuGetCommand@2
inputs:
restoreSolution: '$(solution)'
- task: VSBuild@1
inputs:
solution: '$(solution)'
msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(build.artifactStagingDirectory)\WebApp.zip" /p:DeployIisAppPath="Default Web Site"'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'

Z naszego punktu widzenia kluczowy jest początkowy fragment pliku (linijki 6-14). Znajduje się w nich informacja o sposobie wyzwalania danego pipeline. Tutaj jest ustawiony trigger dla branch master (czyli zmiany w tym branchu spowodują uruchomienie tego pipeline). To jest standardowy elementy tego typu plików, który zapewne widziałeś/widziałaś nie raz.

Kolejny element pliku (od linijki 10) to właśnie nasz tytułowy path filters. W nim określamy wzorzec ścieżki, który zostanie użyty do filtrowania zmiany. W tym przypadku mówimy, że interesują nasz wszystkie zmiany w folderze project1 (opcja include), poza zmianą w pliku azure-pipelines.yml (opcja exclude). Tutaj możemy podać po kilka różnych wzorców dla każdej z opcji (na przykład wykluczyć zmiany w folderze z dokumentacją).

Warto jeszcze zwrócić uwagę na jedną zmianę, jaką zrobiłem w stosunku do tego, co domyślnie generuje Azure DevOps. W linijce 20 znajduje się zmienna dla pliku solution, który później jest wykorzystywany w krokach pipeline. Domyślnie znajduje się tam wartość „**/*.sln”, która spowoduje, że build będzie wykonywany dla pierwszego pliku solution znalezionego w repozytorium. Tutaj musimy zmienić to, aby był brany pod uwagę plik solution z folderu project1.

Dla drugiego projektu przygotowałem bardzo podobny plik azure-pipelines.yml:

# ASP.NET Core (.NET Framework)
# Build and test ASP.NET Core projects targeting the full .NET Framework.
# Add steps that publish symbols, save build artifacts, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core
trigger:
branches:
include:
- master
paths:
include:
- project2/*
exclude:
- project2/azure-pipelines.yml
pool:
vmImage: 'windows-latest'
variables:
solution: 'project2/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: NuGetToolInstaller@1
- task: NuGetCommand@2
inputs:
restoreSolution: '$(solution)'
- task: VSBuild@1
inputs:
solution: '$(solution)'
msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(build.artifactStagingDirectory)\WebApp.zip" /p:DeployIisAppPath="Default Web Site"'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'

Działanie path filters

Mając już przygotowane pliki yml, dodałem w Azure DevOps dwa pipeline na podstawie tych plików. Następnie wykonałem w repozytorium dwie zmiany. Po jednej dla każdego z projektów, które następnie wrzuciłem jako oddzielne commity do branch master:

Historia zmian dwóch projektów

W efekcie wykonały się oba pipeline wyzwalane poszczególnymi zmianami dla danego projektu:

Wykonane pipeline dla poszczególnych projektów

Ładnie widać, że filtrowanie działa i zmiana w jednym projekcie powoduje wykonanie pipeline tylko dla tego projektu. Czyli mamy to, o co nam chodziło 🙂

Przykład

Na githubie znajduje się przykład do tego wpisu – https://github.com/danielplawgo/AzureDevOpsPathFilter. Znajdują się w nim oba pliki azure-pipelines.yml. Aby przetestować działanie path filters, potrzebujesz przerzucić kod do repozytorium w Azure DevOps i skonfigurować dwa pipeline dla każdego z plików azure-pipelines.yml.

Podsumowanie

Jednym z problemów podejścia monorepo jest optymalizacja budowania projektów, które znajdują się w repozytorium. Na szczęście w Azure DevOps jesteśmy w stanie ten problem bardzo łatwo obejść za pomocą path filters.

.NET 5 Web App w Azure

Szkolenie .NET 5 Web App w Azure

Zainteresował Ciebie ten temat? A może chcesz więcej? Jak tak to zapraszam na moje autorskie szkolenie o Web API działającym w Azure.

1 thought on “Azure DevOps path filters

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.