- Description
- Why should you use this action ?
- Usage when pushing a single package and corresponding symbols
- Usage when pushing multiple packages and corresponding symbols
- Usage when pushing a single NuGet package and symbols package but you don't want to specify the filenames for the packages
- Action inputs
- Action outputs
- Example JSON output from
push-result
- Action exit status codes
- Action logs
- Dev notes
A composite GitHub action that can be used to push NuGet packages and symbols to nuget.org.
I started by pushing my NuGet package and symbols using a command such as:
dotnet nuget push ./*.nupkg --api-key <api-key> --source https://api.nuget.org/v3/index.json --skip-duplicate
The above command has a flag set to skip publishing the NuGet (.nupgk) if the version has already been publish. This allowed the workflow to run without failing even if it didn't produce a new version of the package.
However there was an issue with this approach in that even if the NuGet package already existed the --skip-duplicate
flag only makes it so that the nuget push
command doesn't fail due to the returned 409
from the server but it still tries to push the symbols package after.
The above doesn't fail but it makes nuget.org send emails to the owner of the package with the following:
Symbols package publishing failed. The associated symbols package could not be published due to the following reason(s):
The uploaded symbols package contains pdb(s) for a corresponding dll(s) not found in the NuGet package.
Once you've fixed the issue with your symbols package, you can re-upload it.
Please note: The last successfully published symbols package is still available for debugging and download.
The above error message is also displayed on the NuGet's package page though it's only visible to the owner of the package.
For more information about this see:
- dotnet nuget push with --skip-duplicate pushes .snupkg constantly and causes validation to fail.
- When nupkg exists on push --skip-duplicate, don't automatically push snupkg
- [Symbols] Support removing snupkg validation error messages
This action was created to avoid this from happening. This action pushes the NuGet package and only if it succeeds attempts to do a following push of the corresponding symbols package.
Warning
The main reason for using this action has now been fixed in the nuget tool. See the GitHub issues linked above. Even so, this action still has value:
- It provides better feedback on the result of pushing NuGet packages, especially when you push multiple packages.
- It allows you to control if the NuGet push operation should result in a failure if the package already exists or not. See the
fail-if-exists
action input parameter.
If you only want to push a single NuGet package and corresponding symbols you can do as shown in the example below. You must at least specify the nuget-package
input parameter, if you don't have a symbols package you don't need to use the symbols-package
input parameter.
- name: Publish NuGet and symbols
id: nuget-push
uses: edumserrano/nuget-push@v1
with:
api-key: '${{ secrets.NUGET_PUSH_API_KEY }}' # this example is using GitHub secrets to pass the API key
nuget-package: 'my-awesome-package.nupkg'
symbols-package: 'my-awesome-package.snupkg'
# The next step is using powershell to parse the action's output but you can use whatever you prefer.
# Note that in order to read the step outputs the action step must have an id.
- name: Log output
if: steps.nuget-push.conclusion != 'skipped' && always() # run regardless if the previous step failed or not, just as long as it wasn't skipped
shell: pwsh
run: |
$pushResult = '${{ steps.nuget-push.outputs.push-result }}' | ConvertFrom-Json
$pushResultAsJsonIndented = ConvertTo-Json $pushResult
Write-Output $pushResultAsJsonIndented # outputs the result of the nuget push operation as an indented JSON string
Write-Output '${{ steps.nuget-push.outputs.status }}' # outputs the overall status of the nuget push action
# since we only pushed one package/symbols there's no need to iterate the packages list
# there will only be one element in the array
$package = $pushResult.packages[0]
Write-Output "$($package.status)" # outputs the status of the nuget push operation
Write-Output "$($package.package)" # outputs the NuGet package name that was pushed
Write-Output "$($package.symbols)" # outputs the symbols package name that was pushed
If you want to push several NuGet packages and symbols in one go then you can just specify the directory where the packages are as shown in the example below. The action will pair NuGet packages with their symbols based on their filenames. If there are no matching symbols packages then only the NuGet packages are uploaded.
As an example consider that the my-packages-dir
contained the following files:
- my-awesome-package.nupkg
- my-awesome-package.snupkg
- my-super-package.nupkg
- my-super-package.snupkg
This action will pair my-awesome-package.nupkg
with my-awesome-package.snupkg
and my-super-package.nupkg
with my-super-package.snupkg
.
- name: Publish NuGet and symbols
id: nuget-push
uses: edumserrano/nuget-push@v1
with:
api-key: '${{ secrets.NUGET_PUSH_API_KEY }}' # this example is using GitHub secrets to pass the API key
working-directory: 'my-packages-dir'
# The next step is using powershell to parse the action's output but you can use whatever you prefer.
# Note that in order to read the step outputs the action step must have an id.
- name: Log output
if: steps.nuget-push.conclusion != 'skipped' && always() # run regardless if the previous step failed or not, just as long as it wasn't skipped
shell: pwsh
run: |
$pushResult = '${{ steps.nuget-push.outputs.push-result }}' | ConvertFrom-Json
$pushResultAsJsonIndented = ConvertTo-Json $pushResult
Write-Output $pushResultAsJsonIndented # outputs the result of the nuget push operation as an indented JSON string
Write-Output '${{ steps.nuget-push.outputs.status }}' # outputs the overall status of the nuget push action
# iterates over all list of packages and outputs the data from the nuget push operation for each
foreach($package in $pushResult.packages) {
Write-Output "$($package.status)" # outputs the status of the nuget push operation
Write-Output "$($package.package)" # outputs the NuGet package name that was pushed
Write-Output "$($package.symbols)" # outputs the symbols package name that was pushed
}
Usage when pushing a single NuGet package and symbols package but you don't want to specify the filenames for the packages
You can use the working-directory
input parameter to accomplish this. Just make sure that the given directory only contains a single NuGet package and potentially the corresponding symbols package.
As an example consider that the my-packages-dir
contained the following files:
- my-awesome-package.nupkg
- my-awesome-package.snupkg
Then you could do:
- name: Publish NuGet and symbols
id: nuget-push
uses: edumserrano/nuget-push@v1
with:
api-key: '${{ secrets.NUGET_PUSH_API_KEY }}' # this example is using GitHub secrets to pass the API key
working-directory: 'my-packages-dir'
# The next step is using powershell to parse the action's output but you can use whatever you prefer.
# Note that in order to read the step outputs the action step must have an id.
- name: Log output
if: steps.nuget-push.conclusion != 'skipped' && always() # run regardless if the previous step failed or not, just as long as it wasn't skipped
shell: pwsh
run: |
$pushResult = '${{ steps.nuget-push.outputs.push-result }}' | ConvertFrom-Json
$pushResultAsJsonIndented = ConvertTo-Json $pushResult
Write-Output $pushResultAsJsonIndented # outputs the result of the nuget push operation as an indented JSON string
Write-Output '${{ steps.nuget-push.outputs.status }}' # outputs the overall status of the nuget push action
# since we only pushed one package/symbols there's no need to iterate the packages list
# there will only be one element in the array
$package = $pushResult.packages[0]
Write-Output "$($package.status)" # outputs the status of the nuget push operation
Write-Output "$($package.package)" # outputs the NuGet package name that was pushed
Write-Output "$($package.symbols)" # outputs the symbols package name that was pushed
Name | Description | Required | Default value |
---|---|---|---|
api-key |
The API key for the NuGet server. Used when pushing the NuGet packages and symbols. | yes | - |
fail-if-exists |
Indicates whether this actions should fail if the version of the NuGet package being pushed already exists in the server. If multiple NuGet packages are being pushed it will fail if a single one already exists. | no | false |
working-directory |
The directory that will be used to push NuGet packages. It will push all NuGet packages (*.nupkg) and corresponding symbol packages (*.snupkg) present in the directory. Cannot be specified in combination with nuget-package input parameter. |
no | - |
nuget-package |
The filepath for the NuGet package to be pushed. Cannot be specified in combination with working-directory input parameter. |
no | - |
symbols-package |
The filepath for the symbols package to be pushed. To be used in combination with nuget-package input parameter or not at all. |
no | - |
Name | Description |
---|---|
status |
The overall status of pushing the NuGet packages and corresponding symbols. Possible values are ok or error . |
push-result |
The result of pushing the NuGet packages and corresponding symbols as a JSON string. |
Let's assume you used the action to upload the following packages from a some directory:
- my-awesome-package.nupkg
- my-awesome-package.snupkg
- my-super-package.nupkg
- my-super-package.snupkg
Let's also assume that the my-awesome-package
package was pushed successfully and that the my-super-package
package wasn't pushed because the same version of that package already existed. In this example the output would be:
{
"packages": [
{
"package": "my-awesome-package.nupkg",
"status": "ok",
"symbols": "my-awesome-package.snupkg"
}
{
"package": "my-super-package.nupkg",
"status": "nuget-already-exists",
"symbols": "my-super-package.snupkg"
}
],
"status": "ok"
}
- The possible values for the
status
property at the root of the JSON object are eitherok
orerror
. In fact the value of this property will always match the value of thestatus
action output. Thestatus
action output parameter was added explicitly as an output for convenience. - The array represented by the
packages
property will contain information about all the NuGet packages and corresponding symbols that were pushed. - Each object in the
packages
property array will have always three properties:package
: the NuGet package that was pushed.symbol
: the symbols package that was pushed, if any. It will be an empty string if no symbols package was pushed.status
: the result of pushing the NuGet package and corresponding symbols. The possible values for this property are:ok
: success pushing the NuGet package and symbols.nuget-already-exists
: the version of the NuGet package already exists in the NuGet server. When this happens the corresponding symbols package, if exists, will not even be attempted to be pushed.nuget-push-failed
: an error occurred when pushing the NuGet package.symbols-push-failed
: an error occurred when pushing the symbols package.
This action will set the exit status code based on its status
output parameter. Meaning that the action will complete successfully when the status
output parameter is ok
and it will fail when it's error
.
If this behavior is not desirable and you always want to check the push-result
output then you can set the continue-on-error
property to make sure the action never fails and then analyze the output from the action in a following step as shown below.
- name: Publish NuGet and symbols
id: nuget-push
uses: edumserrano/nuget-push@v1
continue-on-error: true
with:
api-key: '${{ secrets.NUGET_PUSH_API_KEY }}'
nuget-package: 'my-awesome-package.nupkg'
symbols-package: 'my-awesome-package.snupkg'
# The next step is using powershell to parse the action's output but you can use whatever you prefer.
# Note that in order to read the step outputs the action step must have an id.
- name: Do something based on the output of the nuget push action
shell: pwsh
run: |
$pushResult = '${{ steps.nuget-push.outputs.push-result }}' | ConvertFrom-Json
$pushResultAsJsonIndented = ConvertTo-Json $pushResult
Write-Output $pushResultAsJsonIndented # outputs the result of the nuget push operation as an indented JSON string
Write-Output '${{ steps.nuget-push.outputs.status }}' # outputs the overall status of the nuget push action
# do soemthing based on the output from the nuget-push step
...
The action will output detailed information regarding what packages it's going to push and what was the result of each push operation. Check the action logs for this information.
For notes aimed at developers working on this repo or just trying to understand it go here.