- Blog
- Ställa in ett frisläppande arbetsflöde för ett .NET-programmeringsbibliotek
Bygga en app? Bitwarden Passwordless.dev är ett utvecklarvänligt API som låter användare logga in med hjälp av lösenord med biometri. Kom igång gratis och se själv.
Att utveckla ett bibliotek involverar många rörliga bitar, som inte alla handlar om att skriva kod. I en modern mjukvaruutvecklingsmiljö måste utvecklare tänka på hur man bygger, testar och distribuerar koden och hur dessa processer kan automatiseras för att vara så effektiva som möjligt.
Tidigare i den här bloggserien diskuterades det hur man ställer in ett GitHub Actions-arbetsflöde för att köra tester för ett .NET-bibliotek, rendera resultaten på ett läsbart sätt för människor, samla in kodtäckningsmått och koppla upp allt för att triggas på varje commit i förvaret. Det täcker dock endast den "kontinuerliga integrationsdelen" av en potentiell CI/CD-pipeline.
För att helt automatisera bibliotekets utvecklingscykel måste utvecklare också ta hand om leveransflödet – de delar av pipelinen som är relaterade till paketeringen av biblioteket, distribuera det till NuGet-paketregistret, hålla reda på släppta versioner och tillhörande ändringar och andra sådana uppgifter. Bekvämt kan de utnyttja grundarbetet som anges i den här bloggen.
Låt oss i den här artikeln titta på hur man ställer in ett utgivningsarbetsflöde för ett .NET-bibliotek. Även om tillvägagångssätten som presenteras här kanske inte gäller för alla projekt, kan de fungera som en bra utgångspunkt för utvecklare som vill automatisera deras biblioteks releaseprocess.
Projektöversikt för .NET-utveckling
Innan vi dyker in i detaljerna, låt oss kort gå igenom tillståndet för förvaret som arbetas på, som använder .NET-ramverket. Som det ser ut finns det ett enda GitHub Actions-arbetsflöde som körs vid varje push till förvaret (inklusive pull-förfrågningar) och utför följande steg:
YAMLname: main
on:
push:
pull_request:
jobs:
test:
matrix:
os:
- windows-latest
- ubuntu-latest
- macos-latest
runs-on: ${{ matrix.os }}
permissions:
contents: read
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: |
8.0.x
6.0.x
- run: >
dotnet test
--configuration Release
--logger GitHubActions
--collect:"XPlat Code Coverage"
--
DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover
- uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
Som du kan se är arbetsflödet ganska enkelt. Den checkar ut arkivet, installerar .NET SDK, kör testen mot de angivna körtiderna, samlar in täckningsdata och laddar upp motsvarande rapporter till Codecov. För att säkerställa att biblioteket fungerar på alla plattformar som stöds är ovanstående testjobb konfigurerat för att köras samtidigt på Windows, Linux och macOS. Slutligen, för att återge testresultat på ett tillgängligt sätt, kan du använda paketet GitHubActionsTestLogger, som är en anpassad testlogger för dotnet-tester som hjälper till att uppnå exakt det.
För att förstå hur man utökar detta arbetsflöde till att omfatta releasedelen av pipelinen, låt oss först titta på hur releaseprocessen för ett .NET-bibliotek vanligtvis ser ut. Som en uppfräschning, här är den nuvarande förvarsstrukturen:
Bash├── examples │ └── ... ├── src │ ├── Passwordless │ │ ├── ... │ │ └── Passwordless.csproj │ └── Passwordless.AspNetCore │ ├── ... │ └── Passwordless.AspNetCore.csproj ├── tests │ ├── Passwordless.Tests │ │ ├── ... │ │ └── Passwordless.Tests.csproj │ └── Passwordless.AspNetCore.Tests │ ├── ... │ └── Passwordless.AspNetCore.Tests.csproj ├── Passwordless.sln └── Directory.Build.props
Som en del av releaseprocessen är målet att paketera biblioteksprojekten (Passwordless.csproj
och Passwordless.AspNetCore.csproj
) i sina respektive NuGet-paket. För att göra det, låt oss använda .NET CLI, som ger ett bekvämt dotnet
pack-kommando som kan användas för att skapa NuGet-paket från ett projekt. På samma sätt som dotnet-testkommandot
finns det inget behov av att specificera projektfilen explicit, och man kan helt enkelt köra dotnet
pack från roten av förvaret för att paketera alla projekt i lösningen:
Plain Text$ dotnet pack MSBuild version 17.8.3+195e7f5a3 for .NET Passwordless -> ./src/Passwordless/bin/Release/net6.0/Passwordless.dll Passwordless -> ./src/Passwordless/bin/Release/net7.0/Passwordless.dll Passwordless -> ./src/Passwordless/bin/Release/net462/Passwordless.dll Passwordless -> ./src/Passwordless/bin/Release/netstandard2.0/Passwordless.dll Passwordless.AspNetCore -> ./src/Passwordless.AspNetCore/bin/Release/net7.0/Passwordless.AspNetCore.dll Successfully created package './src/Passwordless/bin/Release/Passwordless.1.0.0.nupkg'. Successfully created package './src/Passwordless.AspNetCore/bin/Release/Passwordless.AspNetCore.1.0.0.nupkg'.
Dessutom, som du kan se från ovanstående utdata, är bara biblioteksprojekten paketerade och testprojekten ignoreras. Detta beror på att kommandot dotnet
pack endast paketerar projekt som har<IsPackable
> egenskapen satt till sant. Du kan använda den här egenskapen för att styra vilka projekt som ska paketeras och vilka som inte ska göra det utan att ändra argumenten som skickas till kommandot.
Nu, med nupkg-filer redo, kan du ladda upp dem till NuGet för att göra dem tillgängliga för konsumtion. För att göra det, använd dotnet nuget push-kommandot
, som tar en nupkg-fil
, eller ett globmönster som beskriver sådana filer, som ett argument och laddar upp det till det angivna NuGet-flödet. Till exempel, för att ladda upp paketen till den officiella NuGet-avgiften, kör följande kommando:
Plain Text$ dotnet nuget push "**/*.nupkg" --source https://api.nuget.org/v3/index.json --api-key <API_KEY>
Här används globmönstret **/*.nupkg
för att matcha alla nupkg-filer i den aktuella katalogen och dess underkataloger, och argumentet --source
används för att specificera NuGet-flödet att ladda upp paketen till. Slutligen anger argumentet --api-key
API-nyckeln för autentisering. Denna nyckel kan erhållas från din kontopanel på NuGets webbplats och används för att autentisera användaren och auktorisera uppladdningsoperationen.
Med ovanstående process i åtanke, låt oss fundera på hur man integrerar dem i det befintliga GitHub Actions-arbetsflödet.
Introduktion till .NET-plattformen
Översikt över .NET-plattformen
.NET-plattformen är ett robust ramverk för mjukvaruutveckling utvecklat av Microsoft, designat för att tillhandahålla en omfattande runtime-miljö och en rik uppsättning bibliotek och verktyg för att bygga och köra applikationer över olika operativsystem, inklusive Windows, macOS och Linux. Denna mångsidiga plattform stöder flera programmeringsspråk, såsom C#, F# och Visual Basic, och erbjuder utvecklare flexibiliteten att välja det bästa språket för sina projekt.
I kärnan av .NET-plattformen finns flera nyckelkomponenter. Common Language Runtime (CLR) är runtime-miljön som hanterar exekvering av .NET-kod, vilket säkerställer effektiv minneshantering, säkerhet och undantagshantering. Framework Class Library (FCL) är en stor samling återanvändbara bibliotek och klasser som tillhandahåller ett brett utbud av funktioner, från fil-I/O till webbutveckling, vilket gör det lättare för utvecklare att bygga funktionsrika applikationer.
.NET Core, en plattformsoberoende implementering av .NET-plattformen, tillåter utvecklare att skapa och köra .NET-applikationer på icke-Windows-plattformar, vilket ytterligare förbättrar plattformens mångsidighet. Denna plattformsoberoende förmåga är särskilt fördelaktig för utvecklare som vill bygga applikationer som kan köras sömlöst på olika operativsystem.
.NET-plattformen erbjuder många fördelar, inklusive en stor och aktiv community, ett brett utbud av verktyg och bibliotek och stöd för flera programmeringsspråk. Dess utbredda användning i företagsmiljöer gör den populär för att bygga storskaliga, högpresterande applikationer.
Grundläggande arbetsflöde för release
Till att börja med, låt oss utöka arbetsflödet med ett nytt jobb som heter pack
. Det här jobbet kommer att ansvara för att paketera biblioteksprojekten till nupkg-filer
. Att paketera biblioteksprojekten till nupkg-filer
är ett avgörande steg i utvecklingen av .NET-appar, vilket säkerställer att de är redo för driftsättning och användning. För att göra det, använd kommandot dotnet pack
, som diskuterats ovan:
YAMLname: main
on:
push:
pull_request:
jobs:
test:
# Test job remains unchanged, but is omitted for brevity
# ...
pack:
# Operating system doesn't matter here, but Ubuntu-based GitHub Actions
# runners are both the fastest and the cheapest.
runs-on: ubuntu-latest
permissions:
contents: read
steps:
# Clone the repository at current commit
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
# Install the .NET SDK
- uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: 8.0.x
# Create NuGet packages
- run: dotnet pack --configuration Release
Som det ser ut kommer ovanstående jobb nu att köras tillsammans med testjobbet vid varje push till förvaret och kommer att skapa nupkg-filer
från alla packbara projekt i lösningen. Bara genom att utföra paketeringssteget kan du verifiera att biblioteksprojekten bygger framgångsrikt och producerar giltiga paket; men det är inte mycket användbart om du inte gör något med de producerade filerna efteråt.
För att förbättra det kan du antingen utöka det här jobbet ytterligare genom att lägga till ett steg som laddar upp paketen till NuGet, eller alternativt delegera det ansvaret till ett separat jobb. Det finns fördelar och nackdelar med båda tillvägagångssätten, men för det här exemplet, låt oss gå med det senare.
För att dela nupkg-filerna
mellan packjobbet och vårt tänkbara distributionsjobb, använd funktionen GitHub Actions-artefakter. Artefakter tillåter oss att exponera specifika filer från ett arbetsflöde till omvärlden och antingen ladda ner dem via webbgränssnittet eller inifrån ett annat jobb.
För att skapa en artefakt, använd åtgärden actions/upload-artifact
, som tar en sökväg till en fil eller katalog (eller ett globmönster) som ett argument och laddar upp den som en offentlig resurs. Till exempel, för att ladda upp alla nupkg-filer
i den aktuella katalogen, använd följande kommando:
YAMLname: main
on:
push:
pull_request:
jobs:
test:
# Test job remains unchanged, but is omitted for brevity
# ...
pack:
runs-on: ubuntu-latest
permissions:
actions: write # this is required to upload artifacts
contents: read
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: 8.0.x
- run: dotnet pack --configuration Release
# Upload all nupkg files as an artifact blob
- uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: packages
path: "**/*.nupkg"
Med denna enkla förbättring kommer packjobbet nu att producera en artefakt som kallas
paket
, som kommer att innehålla alla nupkg-filer
i förvaret. Innehållet i artefakten kan inspekteras genom att ladda ner det från avsnittet "Artefakter" i arbetsflödessammanfattningen:

Nu, för att ta hand om själva distributionsprocessen, skapa ett nytt jobb, kallat deploy
, som kommer att ansvara för att ladda upp paketen till NuGet. För att göra det, använd actions/download-artifact-åtgärden
för att ladda ner paketartefakten och använd sedan dotnet nuget
push-kommandot för att ladda upp paketen till NuGet-registret:
YAML
name: main
on:
push:
pull_request:
jobs:
test:
# Test job remains unchanged, but is omitted for brevity
# ...
pack:
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: 8.0.x
- run: dotnet pack --configuration Release
- uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: packages
path: "**/*.nupkg"
deploy:
# Only run this job when a new tag is pushed to the repository
if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }}
# We only want the deploy stage to run after both the test and pack stages
# have completed successfully.
needs:
- test
- pack
runs-on: ubuntu-latest
permissions:
actions: read
steps:
# Download the packages artifact
- uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
with:
name: packages
# Install the .NET SDK
- uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: 8.0.x
# Upload the packages to NuGet
- run: >
dotnet nuget push "**/*.nupkg"
--source https://api.nuget.org/v3/index.json
--api-key ${{ secrets.NUGET_API_KEY }}
Som du kan se är distributionsjobbet konfigurerat att köras efter att både test-
och packjobben
har slutförts framgångsrikt, vilket säkerställer att vi inte av misstag publicerar en trasig paketuppdatering. Det körs också bara när en ny tagg skjuts till förvaret, vilket är ett vanligt sätt att markera nya utgåvor i git-baserade projekt.
Kommandot som används för att ladda upp paketen till NuGet är detsamma som det vi diskuterade tidigare men med tillägget av den speciella ${{ secrets.NUGET_API_KEY
}} token i stället för den faktiska API-nyckeln. Denna token är en GitHub Actions-hemlighet och används för att säkert lagra känslig information (som API-nycklar) i förvaret. Du kan skapa en ny hemlighet genom att navigera till fliken "Inställningar" i ditt arkiv och klicka på "Hemligheter" i sidofältet.

Om man ser tillbaka kan det verka som en onödig komplikation att
hålla driftsättningsjobbet åtskilt från packningsjobbet
, särskilt med tanke på att det fortfarande måste vänta på att det senare ska slutföras. Denna separation ger dock några fördelar:
Mindre, mer fokuserade jobb gör det enklare att förstå och underhålla arbetsflödet. Om du någonsin behöver ändra implementeringsprocessen kan vi enkelt göra det utan att påverka resten av arbetsflödet.
GitHub Actions låter dig köra om enskilda jobb om de misslyckas istället för hela arbetsflöden. Om driftsättningsjobbet misslyckas kan man köra det igen utan att bygga artefakterna eller köra testerna igen.
Du kan hantera behörigheterna för distributionsjobbet separat från resten av arbetsflödet. Detta innebär att om driftsättningsjobbet slutar med att kräva ett
innehåll:
skrivbehörighet – till exempel för att skapa en GitHub-release – kan du bevilja det utan att påverka resten av arbetsflödet.Att ladda upp paket som artefakter kan vara användbart även utanför att dela dem mellan jobb. Genom att exponera resultatet av packjobbet kan du enkelt inspektera de producerade förpackningarna och verifiera deras innehåll.
Sammantaget är dessa fördelar subtila, men de kan vara mer eller mindre viktiga beroende på detaljerna i ditt projekt.
Att välja ett CI/CD-verktyg
Populära CI/CD-verktyg för .NET
Verktyg för kontinuerlig integration och kontinuerlig distribution (CI/CD) är oumbärliga i modern mjukvaruutveckling. De gör det möjligt för utvecklare att automatisera bygg-, test- och distributionsprocesserna, vilket säkerställer att applikationer levereras snabbt och tillförlitligt. För .NET-utveckling utmärker sig flera CI/CD-verktyg som populära val.
Azure DevOps, en molnbaserad CI/CD-plattform utvecklad av Microsoft, är en topputmanare. Den erbjuder en omfattande uppsättning funktioner, inklusive automatiserad konstruktion och distribution, testning och projektledning, vilket gör det till ett kraftfullt verktyg för .NET-utveckling. Ett annat flitigt använt verktyg är Jenkins, en CI/CD-plattform med öppen källkod som stöder ett brett utbud av programmeringsspråk, inklusive .NET. Jenkins är känt för sin flexibilitet och omfattande plugin-ekosystem, vilket gör att utvecklare kan anpassa sina CI/CD-pipelines för att passa deras specifika behov.
TeamCity, ett kommersiellt CI/CD-verktyg utvecklat av JetBrains, är ett annat utmärkt alternativ. Det ger robust stöd för .NET-utveckling, tillsammans med ett användarvänligt gränssnitt och kraftfulla funktioner för bygghantering. AppVeyor, en molnbaserad CI/CD-plattform, är speciellt designad för .NET och andra programmeringsspråk, och erbjuder sömlös integration med GitHub och andra versionskontrollsystem.
När de väljer ett CI/CD-verktyg bör utvecklare överväga faktorer som användarvänlighet, skalbarhet och stöd för deras specifika programmeringsspråk och utvecklingsmiljö. Rätt verktyg kan avsevärt effektivisera utvecklingsprocessen, förbättra kodkvaliteten och påskynda leveransen av .NET-applikationer.
Konfigurera automatisk testning
Enhetstestning och integrationstestning
Automatiserad testning är en hörnsten i modern mjukvaruutveckling, vilket säkerställer att applikationer är tillförlitliga, stabila och uppfyller den funktionalitet som krävs. I samband med .NET-utveckling innefattar automatiserad testning vanligtvis två huvudtyper: enhetstestning och integrationstestning.
Enhetstestning fokuserar på att testa enskilda kodenheter, såsom metoder eller klasser, för att verifiera att de fungerar korrekt isolerat. I .NET kan enhetstestning utföras med ramverk som NUnit eller xUnit, som tillhandahåller en rad funktioner för att skriva och köra tester. Dessa ramverk hjälper utvecklare att fånga buggar tidigt i utvecklingsprocessen, vilket minskar risken för defekter i slutprodukten.
Integrationstestning innebär å andra sidan att testa hur olika kodenheter interagerar med varandra för att säkerställa att applikationen fungerar korrekt som helhet. Denna typ av testning är avgörande för att identifiera problem som kan uppstå från interaktionen mellan olika komponenter i applikationen.
För att konfigurera automatiserad testning i .NET kan utvecklare utnyttja verktyg och ramverk som Visual Studio, som erbjuder inbyggt stöd för både enhetstestning och integrationstestning. Dessutom kan tredjepartsbibliotek och ramverk som Moq och Autofac förbättra testinsatserna, tillhandahålla hånfulla och beroendeinjektionsfunktioner.
Genom att implementera automatiserade tester kan utvecklare säkerställa att deras .NET-applikationer är tillförlitliga, stabila och uppfyller den funktionalitet som krävs. Detta minskar risken för fel och buggar och förbättrar den övergripande kvaliteten på applikationerna, vilket gör dem mer robusta och underhållbara.
Versionering och pre-releases i .NET-utveckling
Med ovanstående arbetsflöde på plats kan du nu paketera och distribuera biblioteket till NuGet på varje ny tagg, men en viktig aspekt av releaseprocessen har inte täckts ännu: versionshantering. I ett typiskt .NET-bibliotek är versionsnumret en viktig del av information som hjälper konsumenterna att förstå betydelsen av en viss utgåva och besluta om de ska uppgradera till den.
Versionering är avgörande för .NET-programmering, vilket hjälper utvecklare att hantera och spåra ändringar i olika versioner.
Det vanligaste tillvägagångssättet för versionshantering är bara att ställa in värdet på<Version
> projektegenskap när en ny release görs. Även om det kräver manuellt ingripande är det enkelt, ger dig fullständig frihet över versionsschemat och håller förvaret fritt från ytterligare verktyg eller konfiguration.
XML<Project>
<PropertyGroup>
<!-- ... -->
<!-- Update this when making a new release -->
<Version>1.2.3</Version>
</PropertyGroup>
</Project>
Sedan skulle den typiska releaseprocessen bestå av följande steg:
Uppdatera versionsnumret i projektfilen
Begå förändringen
Tryck på commit
Skapa en ny tagg (1.2.3)
Tryck på taggen
Workflow kör och distribuerar den nya versionen till NuGet
Detta tillvägagångssätt fungerar bra för de flesta projekt, men det kan vara felbenäget. Det största problemet är att versionsnumret måste uppdateras på två ställen: projektfilen och taggen. Det är lätt att glömma att uppdatera båda eller att göra ett misstag. Dessutom, om du planerar att göra utgåvor ofta, kan ovanstående versioneringsprocessen bli något tråkig.
Ett alternativt tillvägagångssätt är att använda git-taggen som den enda sanningskällan för versionsnumret. På så sätt är versionsnumret alltid synkroniserat med taggen, och releaseprocessen blir lika enkel som att skapa en ny tagg utan att uppdatera någon kod.
I en sådan inställning är versionsnumret i projektfilen vanligtvis satt till ett platshållarvärde, till exempel 0.0.0-local
, och uppdateras aldrig. Istället härleds det faktiska versionsnumret från taggnamnet och skickas som en parameter under bygget. Så här gör du det i ett GitHub Actions-arbetsflöde:
YAMLname: main
on:
push:
pull_request:
jobs:
test:
# Test job remains unchanged, but is omitted for brevity
# ...
pack:
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: 8.0.x
# Set the package version to the tag name (on release)
# or fall back to a placeholder value (on regular commits).
- run: >
dotnet pack
--configuration Release
-p:Version=${{ github.ref_name || '0.0.0-ci' }}
- uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: packages
path: "**/*.nupkg"
deploy:
# Deploy job remains unchanged, but is omitted for brevity
# ...
I exemplet ovan använder du ${{ github.ref_name
}} token för att skicka taggnamnet som värdet för egenskapen Version till kommandot dotnet
pack. På så sätt kommer versionsnumret för de distribuerade paketen alltid att matcha deras taggnamn, och du behöver inte uppdatera projektfilen manuellt.
Med den här inställningen förkortas releaseprocessen till bara följande få steg:
Skapa en ny tagg (
1.2.3
)Tryck på taggen
Workflow kör och distribuerar den nya versionen till NuGet
Pre-releases är en annan sak som du kanske vill ta hänsyn till i din versionsstrategi. Förhandsutgåvor är versioner som inte anses vara stabila och som vanligtvis används för att distribuera tidiga versioner av ett bibliotek till en begränsad publik för testning. NuGet stöder inbyggt semantisk versionshantering, så pre-releases betecknas genom att lägga till ett bindestreck (-) och en alfanumerisk identifierare till versionsnumret, till exempel 1.2.3-alpha
.
Den befintliga installationen fungerar lika bra för manuellt skapade förhandsutgåvor. Du kan helt enkelt trycka på en tagg med en pre-release-version, och arbetsflödet kommer att distribuera motsvarande paket till NuGet.
Men du kanske också vill skapa och distribuera pre-release-paket automatiskt, till exempel vid varje commit. Eftersom det inte finns någon tagg för att härleda versionsnumret, måste du komma på ett annat sätt att generera förhandsversioner av paket. Det finns några vanliga metoder för att lösa detta:
Metod 1: Använd en platshållarversion, till exempel
0.0.0
, och lägg bara till commit-hash:0.0.0-ci-7ca0ec0
. Detta är det enklaste tillvägagångssättet, men det kan göra det svårare för konsumenten att förstå betydelsen av en viss pre-release eller var den hör hemma i förhållande till befintliga, stabila releaser. I praktiken kommer du att ha två separata versionstrådar, vilket kan vara förvirrande.Tillvägagångssätt 2: Generera automatiskt en version genom att trycka på patchnumret för den senaste stabila utgåvan (git-taggen) och lägga till commit-hash till den:
1.2.4-ci-7ca0ec0
. Detta tillvägagångssätt är mer konsekvent med versionsschemat du redan skulle använda för stabila utgåvor, men det kräver lite mer arbete att implementera. En större nackdel är dock att versionsschemat inte är semantiskt – din pre-release kan innebära brytande ändringar, men eftersom endast patchnumret någonsin stöts, skulle versionen inte återspegla det.Tillvägagångssätt 3: Dra det senaste stabila paketet från NuGet och jämför det med paketet som produceras av det aktuella arbetsflödet. Beroende på ändringarna som införts i det offentliga API:et utvärderar du sedan automatiskt om versionens huvud-, mindre eller patchnummer ska ändras. Även om detta tillvägagångssätt är det mest exakta, är det också det mest komplexa att implementera, och det kräver mycket extra verktyg och konfiguration.
I slutändan, på grund av komplexiteten, beslutades det att välja det första alternativet för lösenordslösa .NET SDK, eftersom det är mycket enkelt att implementera och förstå, och det är tillräckligt bra för våra behov. Så här kan du uppdatera arbetsflödesformuläret tidigare för att stödja automatiska förhandsutgåvor med detta tillvägagångssätt:
YAMLname: main
on:
push:
pull_request:
jobs:
test:
# Test job remains unchanged, but is omitted for brevity
# ...
pack:
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: 8.0.x
# Set the package version to the tag name (on release)
# or fall back to an auto-generated value (on regular commits).
- run: >
dotnet pack
--configuration Release
-p:Version=${{ (github.ref_type == 'tag' && github.ref_name) || format('0.0.0-ci-{0}', github.sha) }}
- uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: packages
path: "**/*.nupkg"
# Deploy on all commits this time, not just tags
deploy:
needs:
- test
- pack
runs-on: ubuntu-latest
permissions:
actions: read
steps:
- uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
with:
name: packages
- uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: 8.0.x
- run: >
dotnet nuget push "**/*.nupkg"
--source https://api.nuget.org/v3/index.json
--api-key ${{ secrets.NUGET_API_KEY }}
Observera att även om du kan publicera paket ofta till NuGet.org, är det i allmänhet avrådigt att distribuera en ny pre-release för varje commit, som visas ovan. Istället rekommenderas det att du använder ett privat flöde, som GitHub-paket eller MyGet, för att göra det. Så här kan du till exempel ändra driftsättningsjobbet
för att publicera förhandsutgåvor till GitHub-paket:
YAMLname: main
on:
push:
pull_request:
jobs:
test:
# Test job remains unchanged, but is omitted for brevity
# ...
pack:
# Pack job remains unchanged, but is omitted for brevity
# ...
deploy:
needs:
- test
- pack
runs-on: ubuntu-latest
permissions:
actions: read
steps:
- uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
with:
name: packages
- uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: 8.0.x
# Deploy to NuGet.org (only tagged releases)
- if: ${{ github.ref_type == 'tag' }}
run: >
dotnet nuget push "**/*.nupkg"
--source https://api.nuget.org/v3/index.json
--api-key ${{ secrets.NUGET_API_KEY }}
# Deploy to GitHub Packages (all commits and releases)
- run: >
dotnet nuget push "**/*.nupkg"
--source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
--api-key ${{ secrets.GITHUB_TOKEN }}
Automatiserad ändringslogg för .NET-projekt
En annan viktig aspekt av releaseprocessen är att hålla reda på de ändringar som införts i varje version av biblioteket. Detta görs vanligtvis med en ändringslogg, som är en fil som innehåller en lista över alla ändringar som gjorts i biblioteket, organiserade efter version. .NET Foundation spelar en viktig roll för att främja öppen utveckling och samarbete, vilket är viktigt för att upprätthålla en omfattande förändringslogg.
Det finns några olika strategier för att upprätthålla en förändringslogg. De flesta biblioteksprojekt väljer en manuellt underhållen ändringsloggfil (vanligtvis CHANGELOG.md
i arkivroten), som uppdateras av underhållarna när en ny version görs. Ändringsloggen kan följa ett väletablerat format, till exempel Keep a Changelog, som ger en uppsättning konventioner för hur man strukturerar filen och gör det lättare för vissa verktyg att analysera dem, såsom Dependabot.
Men att underhålla en ändringslogg manuellt kan vara lite tråkigt. Lyckligtvis kan GitHub göra saker lite enklare med hjälp av de automatiskt genererade releasenotes. Den här funktionen låter dig automatiskt generera en ändringslogg baserat på pull-förfrågningarna som sammanslagits sedan den senaste versionen och inkludera den i beskrivningen när du gör en ny release. Det kräver att alla meningsfulla ändringar tillämpas via pull-förfrågningar, även om du arbetar ensam, och att du gör en GitHub-release för varje ny version, men automationsfördelarna är väl värda det.
Med det arbetsflöde som nu är på plats behövs inga ändringar för att få ändringslogggenereringen att fungera. Allt du behöver göra är att skapa ett utkast till en ny version på GitHub, tilldela den till en tagg du tryckte på tidigare och klicka på "Generera release notes".

Du kan också välja att låta GitHub skapa taggen åt dig när du publicerar releasen, vilket kommer att utlösa implementeringsjobbet precis som om du hade pushat taggen manuellt. Med detta tillvägagångssätt kommer din releaseprocess att se ut så här:
Publicera en ny version på GitHub med automatiskt genererade releasenotes
Workflow kör och distribuerar den nya versionen till NuGet
Om du föredrar att undvika det manuella steget att skapa en ny version via GitHubs webbgränssnitt, kan du också automatisera den delen av processen. För att skapa en ny version programmatiskt kan du använda GitHub API eller, mer bekvämt, GitHub CLI. När du gör något av det har du möjlighet att inkludera automatiskt genererade release notes i beskrivningen av releasen. Till exempel nedan är hur du kan uppnå det med GitHub CLI: BashCopy $ gh release create 1.2.3 --repo my/repo --generate-notes
Bash$ gh release create 1.2.3 --repo my/repo --generate-notes
gh-verktyget
är redan förinstallerat på GitHub-hostade runners, så för att integrera detta i vårt arbetsflöde behöver vi bara uppdatera vårt distributionsjobb
med ett extra steg.
YAML
name: main
on:
push:
pull_request:
jobs:
test:
# Test job remains unchanged, but is omitted for brevity
# ...
pack:
# Pack job remains unchanged, but is omitted for brevity
# ...
deploy:
needs:
- test
- pack
runs-on: ubuntu-latest
permissions:
actions: read
contents: write # this is required to create releases
steps:
- uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
with:
name: packages
- uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: 8.0.x
- if: ${{ github.ref_type == 'tag' }}
run: >
dotnet nuget push "**/*.nupkg"
--source https://api.nuget.org/v3/index.json
--api-key ${{ secrets.NUGET_API_KEY }}
- run: >
dotnet nuget push "**/*.nupkg"
--source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
--api-key ${{ secrets.GITHUB_TOKEN }}
# Create a GitHub release with auto-generated release notes, and upload the packages as assets
- if: ${{ github.ref_type == 'tag' }}
run: >
gh release create ${{ github.ref_name }}
$(find . -type f -wholename **/*.nupkg -exec echo {} \; | tr '\n' ' ')
--repo ${{ github.event.repository.full_name }}
--title ${{ github.ref_name }}
--generate-notes
--verify-tag
Här är steget vi lagt till ansvar för att skapa en ny version på GitHub och ladda upp paketen som tillgångar. Flaggan --generate-notes
talar om för gh-verktyget
att automatiskt generera releasenotes baserat på pull-förfrågningarna som sammanslagits sedan den senaste releasen och inkludera dem i beskrivningen av releasen. --verify-tag-flaggan
talar om för verktyget att verifiera att taggen existerar innan releasen skapas som en förnuftskontroll för att säkerställa att releasen skapas för en giltig tagg.
Slutligen, med den här inställningen, är releaseprocessen optimerad till följande få steg:
Tryck på en ny tagg (
1.2.3
)Workflow körs, skapar en release och distribuerar den nya versionen till NuGet
Sammanfattning
Den här artikeln behandlade hur man ställer in ett utgivningsarbetsflöde för ett .NET-bibliotek baserat på vad som lärdes av Bitwarden-teamets erfarenhet av att bygga den lösenordslösa .NET SDK:n. Det befintliga arbetsflödet för GitHub Actions har utökats till att omfatta ett nytt jobb som paketerar biblioteksprojekten till nupkg-filer
och ett annat jobb som laddar upp paketen till NuGet. Dessutom behandlas några andra ämnen, till exempel hur man hanterar versionshantering och pre-releases och hur man automatiserar genereringen av release notes.
Kom igång med Bitwarden på .NET-plattformen Passwordless.dev
Är du en utvecklare som vill lägga till autentisering med lösenord med biometri till dina applikationer? Kom snabbt igång med ett gratis Passwordless.dev-konto, eller besök Passwordless.dev för att lära dig mer om erbjudandet.
Har du fortfarande frågor? Kolla in våra community-sidor och dela dina tankar och frågor där.