Asosiy postga havola 👉: https://www.notion.so/wahids/Github-Actions-orqali-NET-CI-CD-66d97197bc924008a4dfaf6198b8671c
Bu galgi postimizda .NET dasturiga CI/CD (Continues Ingegration/Continues Delivery) qurishni ko’rib chiqamiz. Ushbu postda aynan Github Actions workflowlar orqali CI/CD yasaymiz.
Avvalo CI/CD nimalarni o’z ichiga olishini aniqlab olamiz. CI/CD tushunchasi juda keng qamrovli tushincha va dastur murakkabligiga juda ko’plab yumushlarni o’z ichiga qamrab oladi.
Qisqacha qilib aytganda esa, CI/CD - dasturdagi yangi o’zgarishlarni silliqqina bitta oqimda foydalanuvchiga yetib borishini ta’minlab beruvchi avtomatlashtirilgan yumushlar ketma-ketligi. Yangi kod yetib kelishi bilanoq minimal inson omili orqali shu kod foydalanuvchiga yetib borishini taminlaydi.
Building, Unit Testing, Integration Testing va Deployment kabilar eng ko’p ishlatiladigan CI/CD yumushlar hisoblanadi. Bugungi postda biz Github Actions orqali yangi qo’shilgan kodlarni silliqlik bilan Digital Ocean bulut servisidagi (🤡) Linux container ichiga deploy qilamiz.
Agar meni kontentlarim sizlarga yoqayotgan bolsa, meni ijtimoiy tarmoqlarimga qo’shilishni unutmang!
- Telegram kanal:
- Facebook:
- LinkedIn:
- Github:
.NET API
va xUnit
proyektlar yaratish
Bu qisimda oddiygina matematik amal bajaradigan API va shu amal to’g’ri bajarilayotganini tekshiradigan Unit Test yozib olamiz.
Qoyidagi komandalarni terib loyihani boshlaymiz.
mkdir MathApi; cd MathApi # yangi papka yaratib olish uchun
dotnet new gitignore # gitignore file ni birinchi yaratib olamiz
dotnet new sln # solution file yaratib olamiz
dotnet new webapi -o MathApi -f net6.0 # api project
dotnet new xunit -o MathApi.Tests -f net6.0 # test project
dotnet sln add MathApi/MathApi.csproj # API ni solution ga qo'shamiz
dotnet sln add MathApi.Tests/MathApi.Tests.csproj # testni ham solutionga qo'shamiz
dotnet build # hammasi ko'ngildagiday kechgani tekshiramiz
# test project ga API project ni ulash va kerakli kutubxonalarni o'rnatish
dotnet add MathApi.Tests/MathApi.Tests.csproj reference MathApi/MathApi.csproj
dotnet add MathApi.Tests/MathApi.Tests.csproj package Microsoft.Extensions.DependencyInjection -v 6.0.0
<aside> 💡 Loyihalar yaratilgandan keyin API va Test loyihalar ichiga kirib template kodlarni o’chirib tashlashni unutmang.
</aside>
MathApi
Birinchi navbatda MathApi loyihani tayyor holatga keltiramiz. Buning uchun avval template kodlarni o’chirib tashlagach bittagina MathController.cs
nomli kontroller yaratamiz. Bu controller MathService
orqali berilgan ikkita long tipidagi sonlarni yig’inidisini qaytaradi.
IMathService.cs
kontrakti.
namespace MathApi.Services;
public interface IMathService
{
Task<long> AddAsync(long a, long b);
}
MathService.cs
klasi.
namespace MathApi.Services;
public class MathService : IMathService
{
public Task<long> AddAsync(long a, long b)
{
try
{
var result = checked(a + b);
return Task.FromResult<long>(result);
}
catch(Exception)
{
throw new OverflowException($"The sum of {a} and {b} can't fit in long datatype.");
}
}
}
<aside> 💡 Etibor bering, natija checked
kalit so’zi orqali hisoblanyapti. Sababi agar ikkala sonning yig’indisi long
tipiga sig’maydigan bo’lsa shunchaki tuncated ya’ni qirqib tashlangan son joylamasdan exception
otiladi.
</aside>
MathController.cs
kontrolleri.
using MathApi.Services;
using Microsoft.AspNetCore.Mvc;
namespace MathApi.Controllers;
[ApiController]
[Route("[controller]")]
public class MathController : ControllerBase
{
[HttpGet("add")]
public async Task<IActionResult> AddAsync(
[FromQuery] long a,
[FromQuery] long b,
[FromServices] IMathService mathService)
{
var result = await mathService.AddAsync(a, b);
return Ok(new { result = result });
}
}
Program.cs
klasi.
using MathApi.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddTransient<IMathService, MathService>();
var app = builder.Build();
app.MapControllers();
app.Run();
MathApi.Tests
Endi navbat dunyodagi eng sodda unit testni yozishga. Bunda biz MathService
klasi ikkala sonni to’g’ri qo’sha olishiga ishonch hosil qilamiz.
Services/MathServiceTests.cs
klasi.
using MathApi.Services;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace MathApi.Tests.Services;
public class MathServiceTests
{
private readonly ServiceProvider services;
public MathServiceTests()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<IMathService, MathService>();
this.services = serviceCollection.BuildServiceProvider();
}
[Theory]
[InlineData(long.MaxValue, 1)]
[InlineData(long.MinValue, -1)]
public async Task ThrowsAnExceptionWhenResultDoesNotFitLong(long a, long b)
{
// givenvar mathService = services.GetRequiredService<IMathService>();
// when var task = async () => await mathService.AddAsync(a, b);
// thenawait Assert.ThrowsAsync<OverflowException>(task);
}
[Fact]
public async Task AddsTwoNumbersCorrectly()
{
// givenvar mathService = services.GetRequiredService<IMathService>();
var a = 1;
var b = 3;
// when var result = await mathService.AddAsync(a, b);
// then
Assert.Equal(3, result);
}
}
Endi bemalol solution papkaga borib dotnet test
komandasi orqali hamma testlar to’g’ri o’tishini tekshiring.
API proyekti uchun Dockerfile
fayl yozish
Buning uchun MathApi proyektini root papkasida Dockerfile
nomli fayl yaratib quyidagi kodni yozamiz.
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
WORKDIR /App
# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -c Release -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /App
COPY --from=build-env /App/out .
EXPOSE 5000
ENV ASPNETCORE_URLS=http://+:5000
ENTRYPOINT ["dotnet", "MathApi.dll"]
Barcha kodni ishga tushuruvchi docker-compose.yml
.sln
fayl mavjud papka ichida docker-compose.yml
nomli fayl yarating va quyidagi kodni kodni yozing.
version: '3'
services:
mathapi:
image: davidwahid/mathapi-test
container_name: "mathapi"
restart: always
ports:
- "80:5000"
networks:
- web
networks:
web:
external: true
Github Actions workflow
Endi kodimizni Build, Test, Dockerize va Deploy qiladigan workflow fayl yozib olamiz.
.github/workflows/cicd.yml
faylni aynan shunday papka ichida yarating va quyidagi kodlarni ichiga to’ldiring.
Workflow bir nechta ketma-ket bajariladigan job ya’ni ishlardan iborat bo’ladi.
- .NET build job
name: Build, Test and Deploy
on:push:branches:- main
jobs:build:runs-on: ubuntu-lateststeps:- name: Checkout codeuses: actions/checkout@v2- name: Setup .NET 6uses: actions/setup-dotnet@v1with:dotnet-version: '6.0.x'- name: Restore dependenciesrun: dotnet restore- name: Buildrun: dotnet build --configuration Release
Github Actions workflow biron bir trigger orqali ishga tushiriladi. Bizni misolda esa main
branchga yangi kod push qilinganda workflow ishga tushadi.
jobs:
qismida ko’rib turganingiz bitta build
nomli job yaratilgan. U job .NET 6 SDK o’rnatilgan contrainer ichida ishga tushadi. Kodni restore va build qiladi.
- .NET test job
test:needs: buildruns-on: ubuntu-lateststeps:- name: Checkout codeuses: actions/checkout@v2- name: Setup .NET 6uses: actions/setup-dotnet@v1with:dotnet-version: '6.0.x'- name: Restore dependenciesrun: dotnet restore- name: Testrun: dotnet test --configuration Release
Yuqoridagi kod .NET proyektimizni test qiladi. Hamma unit testlarni ishga tushiradi.
<aside> 💡 1. Yuqoridagi kodni .github/workflows/cicd.yml fayl davomiga yozamiz. 2. needs: build
qismiga e’tibor bering. U build job tugamasidan test job boshlanmasligini taminlaydi. Ya’ni test job build jobga bog’liq.
</aside>
- Dockerize
dockerize:needs: testruns-on: ubuntu-latestenv:DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}steps:- name: Checkout codeuses: actions/checkout@v2- name: Build Docker imagerun: docker build -t $DOCKER_USERNAME/mathapi -f MathApi/Dockerfile .- name: Push Docker imagerun: |
docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
docker push $DOCKER_USERNAME/mathapi
Ushbu kod biz yuqorida yaratgan Dockerfile orqali koddan docker image yasaydi va uni dockerhubga push qiladi.
<aside> 💡 Quyidagi action secretlarni Repo>Settings>Secrets and Variables>Actions tabga borib qo’shib kelish kerak.
</aside>
- DOCKER_USERNAME — sizning dockerhub.io saytidagi login ismingiz
- DOCKER_PASSWORD — sizning dockerhub.io saytidagi parolingiz
- Deploy to Digital Ocean
Oxirgi bosqichda Digital Ocean cloud servisida yangi Droplet (Linux machine) yaratimiz. Unga SSH connection qilamiz va Github Actions Workflowimizga kodni automatik deploy qilish qismini qo’shamiz.
A. SSH kalit yaratish
cd ~/.ssh
komandasi orqali o’z mashinangizda SSH papkasiga kiring. Agar u yo’q bo’lsa uni yarating.~/.ssh
papkasi ichida ssh-keygen
komandasi orqali yangi ssh kalit yarating. Komanda terilganda kalitni saqlash uchun fayl nomini so’raydi. Unga istagan nomni bering. Qolgan qismlariga faqatgina enter tugmasini bosing.cat {kalit_nomi}.pub
komandasi orqali public kalitni terminalga chop eting va uni ko’chirib oling.
B. Droplet yaratish
- Rasmdagiday Create>Droplet tugamsini bosib droplet yarating.
- Barcha kerakli parametrlarni o’zingizga yoqqanday to’ldiring.
- Choose Authentication Method qismiga yetganda esa SSH Key tugmasini bosib keyin New SSH Key tugmasini bosing.
- Rasmda ko’rsatilgan joyga avvalroq ko’chirib olingan SSH public kalitini joylashtiring. Unga nom bering va dropletni yakunlang.
C. Linux mashinani sozlash
O’zmashinangizdan endi SSH orqali bemalol linux mashinaga ulana olasiz. ssh root@{IP_ADDRESS}
komandasi orqali terminalda hozirgina yaratilgan linux mashinaga ulaning.