Automating NuGet Package Uploads

The Challenge

When maintaining a larger number of NuGet packages, updating to a new version can become a tedious task. You need to upload all new packages, if there are dependencies between the different packages you also need to be fast in order to avoid breaking your packages. However, this task can also be automated - here's how.

Creating an API key

First of all, you need to create an API key that allows to push new packages to the NuGet server. Once logged in, you can choose the corresponding menu from here:

On the following page you can then choose the key name, the actions available for this key and the packages this key should apply to. In this example, I've used a global pattern to allow uploading all packages starting with combit.ListLabel:

Automating NuGet Package Uploads

After clicking "Create" you'll have the only chance ever to copy/view the generated key - make sure to use this opportunity wisely. Also, note that keys do have an expiration date. After this, you need to regenerate the key.

Automating NuGet Package Uploads

Using the Key for Automated Uploads

The next step is to use this key for an automated upload, of course. We've created a rather sophisticated batch file for this purpose which I'm sharing here. All required input is queried from the user. As the owner of this batch file may upload any packages on your behalf, make sure to store it in a very secure path only. The nuget push is repeated for each applicable package at the end.

@ECHO OFF

REM ==================================================================================================================================================
REM CONFIGURATION BEGIN
REM DO NOT EDIT BEFORE THIS LINE!
REM ==================================================================================================================================================
SET MajorVersion=27

SET NuGetExePath=.\nuget.exe

SET ApiKey=<Your API key>
SET Server=https://api.nuget.org/v3/index.json

REM ==================================================================================================================================================
REM CONFIGURATION END
REM DO NOT EDIT AFTER THIS LINE!
REM ==================================================================================================================================================

ECHO INFO: The NuGet version must match the format "Major.Minor.Patch[-Suffix]" (see examples):
ECHO    27.0.0-alpha1
ECHO    27.0.0-beta1
ECHO    27.0.0-beta2
ECHO    27.1.0
ECHO    27.2.1

SET NugetVersionString=%MajorVersion%

:UserPrompt_PrereleaseVersion
ECHO.
SET "UserChoice_PrereleaseVersion="
SET /P "UserChoice_PrereleaseVersion=Enter Pre-release version (values: "alpha" or "beta" without quotes; press 'Enter' to proceed without Pre-release version): %NugetVersionString%.0.0-"

IF /I "%UserChoice_PrereleaseVersion%" == "" (
	GOTO UserPrompt_MinorVersion
) ELSE (
	SET NugetVersionString=%NugetVersionString%.0.0-%UserChoice_PrereleaseVersion%
	GOTO UserPrompt_PrereleaseVersionSuffix
)

:UserPrompt_PrereleaseVersionSuffix
ECHO.
SET "UserChoice_PrereleaseVersionSuffix="
SET /P "UserChoice_PrereleaseVersionSuffix=Enter Pre-release version suffix (values: "2" and higher without quotes; press 'Enter' to proceed with Pre-release suffix default "1"): %NugetVersionString%"

IF /I "%UserChoice_PrereleaseVersionSuffix%" == "" (
	SET NugetVersionString=%NugetVersionString%1
) ELSE (
	SET NugetVersionString=%NugetVersionString%%UserChoice_PrereleaseVersionSuffix%
)

GOTO UserPrompt_FinalWarning

:UserPrompt_MinorVersion
ECHO.
SET "UserChoice_MinorVersion="
SET /P "UserChoice_MinorVersion=Enter Minor version (values: "1" or higher without quotes; press 'Enter' to proceed with default "0"): %NugetVersionString%."

IF /I "%UserChoice_MinorVersion%" == "" (
	SET NugetVersionString=%NugetVersionString%.0
) ELSE (
	SET NugetVersionString=%NugetVersionString%.%UserChoice_MinorVersion%
)

GOTO UserPrompt_PatchVersion

:UserPrompt_PatchVersion
ECHO.
SET "UserChoice_PatchVersion="
SET /P "UserChoice_PatchVersion=Enter Patch version (values: "1" or higher without quotes; press 'Enter' to proceed with default "0"): %NugetVersionString%."

IF /I "%UserChoice_PatchVersion%" == "" (
	SET NugetVersionString=%NugetVersionString%.0
) ELSE (
	SET NugetVersionString=%NugetVersionString%.%UserChoice_PatchVersion%
)

GOTO UserPrompt_FinalWarning

:UserPrompt_FinalWarning
ECHO.
IF /I NOT "%UserChoice_PrereleaseVersion%" == "" (
	SET PackageSourcePath=\\srvchk\prod\LL%MajorVersion%\%UserChoice_PrereleaseVersion%%UserChoice_PrereleaseVersionSuffix%\NuGet
) ELSE (
	SET PackageSourcePath=\\srvchk\prod\LL%MajorVersion%\Release\%MajorVersion%.00%UserChoice_MinorVersion%\NuGet
)

SETLOCAL EnableExtensions EnableDelayedExpansion
SET "UserChoice_FinalWarning=N"
SET /P "UserChoice_FinalWarning=WARNING: This cannot be undone. Really upload NuGet packages for version "%NugetVersionString%" ("%PackageSourcePath%") [Y/N]? "
SET "UserChoice_FinalWarning=!UserChoice_FinalWarning: =!"

IF /I "!UserChoice_FinalWarning!" == "N" ENDLOCAL & GOTO :EOF
IF /I NOT "!UserChoice_FinalWarning!" == "Y" GOTO UserPrompt_FinalWarning

ENDLOCAL
GOTO Upload

:Upload
REM Push packages to server
ECHO.
ECHO Uploading packages...
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.AdhocDesign.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.AdhocDesign.Web.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.CassandraDataProvider.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.FirebirdConnectionDataProvider.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.MongoDBDataProvider.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.MySqlConnectionDataProvider.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.NpgsqlConnectionDataProvider.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.NuoDbConnectionDataProvider.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.OdbcConnectionDataProvider.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.OleDbConnectionDataProvider.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.RedisDataProvider.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.ReportServerIntegration.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.SalesforceDataProvider.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.SqlConnectionDataProvider.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.Web.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ListLabel%MajorVersion%.Wpf.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ReportServer%MajorVersion%.ClientApi.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%
%NuGetExePath% push "%PackageSourcePath%\combit.ReportServer%MajorVersion%.SharedTypes.%NugetVersionString%.nupkg" -ApiKey %ApiKey% -Source %Server%

PAUSE

The Result

Once executed, the package is uploaded and shows up with the newly created version. The result looks just like a "normal" upload, but the process is much less error prone and executed in a matter of seconds:

Automating NuGet Package Uploads

Wrapping Up

The presented batch file allows to automate the management of larger NuGet package zoos. We're using it successfully to maintain our roundabout 50 packages with 87k downloads on nuget.org. It will be a great help for others facing the same challenges.