Publish from CI¶
Use CI to pack packages into a folder, then push that folder with Sleet. This page shows secure patterns for Azure, Amazon S3, and generic CI systems.
Principles¶
Pack to a known output folder, then push the folder:
dotnet pack --configuration Release --output ./artifacts/packages
sleet push ./artifacts/packages --skip-existing
Sleet accepts files and directories. Directory inputs are searched recursively for *.nupkg. Sleet does not expand wildcards itself, although your shell might.
Use these defaults in CI:
- Pin Sleet to the current major version, such as
dotnet tool install -g sleet --version "7.*". - For exact pinning, commit a local tool manifest and run
dotnet tool restore, thendotnet sleet .... See Install Sleet. - Use
--skip-existingso reruns do not fail after a package was already published. - Keep secrets in the CI secret store.
- Prefer workload identity or OIDC over account keys.
- Avoid checked-in
sleet.jsonfiles that contain secrets. UseSLEET_FEED_*environment variables or$TOKEN$tokens instead. See configuration for tokens and feed settings. - Consider
--config nonewhen configuring only with environment variables. This prevents a straysleet.jsonfrom being discovered.
Concurrency¶
Sleet protects each feed with a lock. Parallel pushes wait for that lock, but it is still better to use your CI system's concurrency control so jobs do not pile up.
For GitHub Actions, use concurrency:
You can limit lock waiting with config.feedLockTimeoutMinutes in sleet.json:
This setting is not available through SLEET_FEED_* environment variables.
GitHub Actions to Azure with OIDC¶
This is the preferred Azure pattern. azure/login@v3 signs in with GitHub OIDC. Sleet uses DefaultAzureCredential because the feed has path and no connection string.
The Azure identity needs Storage Blob Data Contributor on the storage account or container. See Azure authentication.
name: publish-packages
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
concurrency:
group: sleet-feed
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-dotnet@v6
with:
dotnet-version: 8.0.x
- uses: azure/login@v3
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Pack
run: dotnet pack --configuration Release --output ./artifacts/packages
- name: Push to Sleet
env:
SLEET_FEED_TYPE: azure
SLEET_FEED_CONTAINER: feed
SLEET_FEED_PATH: https://myaccount.blob.core.windows.net/feed/
run: |
dotnet tool install -g sleet --version "7.*"
sleet push ./artifacts/packages --skip-existing --config none
GitHub Actions to Azure with a connection string¶
This is simpler to set up, but less secure than OIDC because it stores an account secret in GitHub Actions.
name: publish-packages
on:
push:
branches: [main]
permissions:
contents: read
concurrency:
group: sleet-feed
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-dotnet@v6
with:
dotnet-version: 8.0.x
- name: Pack
run: dotnet pack --configuration Release --output ./artifacts/packages
- name: Push to Sleet
env:
SLEET_FEED_TYPE: azure
SLEET_FEED_CONTAINER: feed
SLEET_FEED_CONNECTIONSTRING: ${{ secrets.SLEET_AZURE_CONNECTION_STRING }}
run: |
dotnet tool install -g sleet --version "7.*"
sleet push ./artifacts/packages --skip-existing --config none
GitHub Actions to Amazon S3 with OIDC¶
Use aws-actions/configure-aws-credentials@v6 to assume an AWS role. The action exports temporary credentials as AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN environment variables, which Sleet picks up. See AWS authentication.
name: publish-packages
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
concurrency:
group: sleet-feed
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-dotnet@v6
with:
dotnet-version: 8.0.x
- uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: us-west-2
- name: Pack
run: dotnet pack --configuration Release --output ./artifacts/packages
- name: Push to Sleet
env:
SLEET_FEED_TYPE: s3
SLEET_FEED_BUCKETNAME: my-bucket-feed
SLEET_FEED_REGION: us-west-2
SLEET_FEED_PATH: https://my-bucket-feed.s3.us-west-2.amazonaws.com/
run: |
dotnet tool install -g sleet --version "7.*"
sleet push ./artifacts/packages --skip-existing --config none
Use bucket names without dots if you plan to use virtual-hosted HTTPS URLs.
Azure Pipelines to Azure with a service connection¶
Run Sleet inside the AzureCLI@2 task so DefaultAzureCredential can use the Azure CLI login from the Azure Resource Manager service connection.
trigger:
branches:
include:
- main
pool:
vmImage: ubuntu-latest
steps:
- task: UseDotNet@2
inputs:
packageType: sdk
version: 8.0.x
- script: dotnet pack --configuration Release --output $(Build.ArtifactStagingDirectory)/packages
displayName: Pack
- task: AzureCLI@2
displayName: Push to Sleet
inputs:
azureSubscription: my-azure-service-connection
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
dotnet tool install -g sleet --version "7.*"
export PATH="$PATH:$HOME/.dotnet/tools"
export SLEET_FEED_TYPE=azure
export SLEET_FEED_CONTAINER=feed
export SLEET_FEED_PATH=https://myaccount.blob.core.windows.net/feed/
sleet push "$(Build.ArtifactStagingDirectory)/packages" --skip-existing --config none
On Linux and macOS agents, global .NET tools are installed under $HOME/.dotnet/tools. Add that folder to PATH if sleet is not found.
GitLab CI or a generic shell¶
Use your CI variable store for the values. This example uses Azure, but the same pattern works for S3.
publish-packages:
image: mcr.microsoft.com/dotnet/sdk:8.0
stage: deploy
variables:
SLEET_FEED_TYPE: azure
SLEET_FEED_CONTAINER: feed
SLEET_FEED_PATH: https://myaccount.blob.core.windows.net/feed/
script:
- dotnet pack --configuration Release --output ./artifacts/packages
- dotnet tool install -g sleet --version "7.*"
- export PATH="$PATH:$HOME/.dotnet/tools"
- sleet push ./artifacts/packages --skip-existing --config none
For Entra ID outside Azure, store the service principal variables as masked CI variables. If you use access keys, store them in masked CI variables and expose them as SLEET_FEED_CONNECTIONSTRING for Azure or SLEET_FEED_ACCESSKEYID and SLEET_FEED_SECRETACCESSKEY for S3.