TX Text Control .NET Server

CI/CD Authentication

Topic

Build servers restore TX Text Control packages from the Text Control Private NuGet Feed with the same credentials as a developer machine. In GitHub Actions, GitLab CI/CD, Azure DevOps, Jenkins, Bitbucket Pipelines, TeamCity and Docker builds, you store these credentials as secrets and create the nuget.config file during the build.

How It Works

Do not commit your personalized nuget.config to the repository, because it contains your API token. Instead:

  1. Store the email address, the API token and the feed URL as secrets or protected variables in your CI/CD platform.
  2. Add a build step that writes nuget.config from these secrets before the packages are restored.
  3. Restore, build and test as usual.

The credentials never appear in your repository or its history, and the builds run fully automated.

Licensing on Build Servers

TX Text Control is licensed per developer, not per build server. If you use a build server, every developer who checks in code that uses TX Text Control classes needs their own license. See Development Licenses.

The Secrets

All examples on this page use the same three values:

SecretValue
TEXTCONTROL_EMAILThe email address of the assigned developer.
TEXTCONTROL_API_TOKENThe API token generated for that developer. It starts with ngt_.
TEXTCONTROL_FEED_URLThe feed URL, https://www.textcontrol.com/nuget/v3/index.json.

You find these values in the email you received when a serial number was assigned to you and in your downloaded nuget.config file. API tokens expire: tokens created by assigning a serial number are valid for 365 days, and tokens created manually for up to 730 days. When you replace a token, update the secret. See Creating an API Token.

The examples install the .NET 8 SDK. If your projects target a newer .NET version, install that SDK instead.

GitHub Actions

Add the credentials as repository secrets:

  1. Navigate to your repository on GitHub.
  2. Go to Settings > Secrets and variables > Actions.
  3. Click New repository secret and add TEXTCONTROL_EMAIL, TEXTCONTROL_API_TOKEN and TEXTCONTROL_FEED_URL.

The New secret form of GitHub Actions secrets with the name TEXTCONTROL_FEED_URL and the feed URL as its value, above the Add secret button.

In your workflow file, generate nuget.config before restoring packages:

name: Build

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: Create nuget.config
        run: |
          cat > nuget.config << 'EOF'
          <?xml version="1.0" encoding="utf-8"?>
          <configuration>
            <packageSources>
              <clear />
              <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
              <add key="TextControl" value="${{ secrets.TEXTCONTROL_FEED_URL }}" />
            </packageSources>
            <packageSourceCredentials>
              <TextControl>
                <add key="Username" value="${{ secrets.TEXTCONTROL_EMAIL }}" />
                <add key="ClearTextPassword" value="${{ secrets.TEXTCONTROL_API_TOKEN }}" />
              </TextControl>
            </packageSourceCredentials>
          </configuration>
          EOF

      - name: Restore dependencies
        run: dotnet restore

      - name: Build
        run: dotnet build --no-restore --configuration Release

      - name: Test
        run: dotnet test --no-build --configuration Release

The file is generated at run time from the secrets, so the credentials never appear in the repository history. The workflow runs for pushes and pull requests. The tutorial Building a TX Text Control Project with GitHub Actions and the Text Control NuGet Feed walks through the complete setup, starting from the official .NET workflow template.

GitLab CI/CD

Store the credentials as CI/CD variables in your project settings:

  1. Navigate to Settings > CI/CD > Variables.
  2. Add TEXTCONTROL_EMAIL, TEXTCONTROL_API_TOKEN and TEXTCONTROL_FEED_URL, and mark them as masked and protected.

Configure your .gitlab-ci.yml:

stages:
  - build
  - test

variables:
  DOTNET_VERSION: '8.0'

build:
  stage: build
  image: mcr.microsoft.com/dotnet/sdk:8.0
  before_script:
    - |
      cat > nuget.config << EOF
      <?xml version="1.0" encoding="utf-8"?>
      <configuration>
        <packageSources>
          <clear />
          <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
          <add key="TextControl" value="${TEXTCONTROL_FEED_URL}" />
        </packageSources>
        <packageSourceCredentials>
          <TextControl>
            <add key="Username" value="${TEXTCONTROL_EMAIL}" />
            <add key="ClearTextPassword" value="${TEXTCONTROL_API_TOKEN}" />
          </TextControl>
        </packageSourceCredentials>
      </configuration>
      EOF
  script:
    - dotnet restore
    - dotnet build --configuration Release --no-restore
  artifacts:
    paths:
      - '**/bin/Release/'

test:
  stage: test
  image: mcr.microsoft.com/dotnet/sdk:8.0
  needs:
    - build
  script:
    - dotnet test --configuration Release --no-build

Azure DevOps

Create a variable group that your pipeline references:

  1. In Azure DevOps, go to Pipelines > Library.
  2. Click + Variable group.
  3. Name the group, for example TextControl-NuGet.
  4. Add TEXTCONTROL_EMAIL, TEXTCONTROL_API_TOKEN and TEXTCONTROL_FEED_URL. Click the lock icon next to TEXTCONTROL_API_TOKEN to mark it as secret.
  5. Click Save.

The Library page of Azure Pipelines with the Variable groups tab open, the + Variable group button and a variable group named TextControl-NuGet.

Reference the variable group in your azure-pipelines.yml. The pipeline writes nuget.config from the variables and then restores the packages with this file:

trigger:
  - main

pool:
  vmImage: 'windows-latest'

variables:
  - group: TextControl-NuGet
  - name: buildConfiguration
    value: 'Release'

steps:
  - task: UseDotNet@2
    displayName: 'Setup .NET SDK'
    inputs:
      version: '8.0.x'
      includePreviewVersions: false

  - pwsh: |
      @"
      <?xml version="1.0" encoding="utf-8"?>
      <configuration>
        <packageSources>
          <clear />
          <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
          <add key="TextControl" value="$(TEXTCONTROL_FEED_URL)" />
        </packageSources>
        <packageSourceCredentials>
          <TextControl>
            <add key="Username" value="$(TEXTCONTROL_EMAIL)" />
            <add key="ClearTextPassword" value="$(TEXTCONTROL_API_TOKEN)" />
          </TextControl>
        </packageSourceCredentials>
      </configuration>
      "@ | Out-File -FilePath nuget.config -Encoding utf8
    displayName: 'Create nuget.config'

  - task: DotNetCoreCLI@2
    displayName: 'Restore packages'
    inputs:
      command: 'restore'
      projects: '**/*.csproj'
      feedsToUse: 'config'
      nugetConfigPath: 'nuget.config'

  - task: DotNetCoreCLI@2
    displayName: 'Build solution'
    inputs:
      command: 'build'
      projects: '**/*.csproj'
      arguments: '--configuration $(buildConfiguration) --no-restore'

  - task: DotNetCoreCLI@2
    displayName: 'Run tests'
    inputs:
      command: 'test'
      projects: '**/*Tests.csproj'
      arguments: '--configuration $(buildConfiguration) --no-build'

Make sure that the restore task uses the generated file (feedsToUse: 'config' and nugetConfigPath: 'nuget.config'). When you run the pipeline for the first time, click Permit to allow access to the variable group. In the log of the Restore packages step, both https://api.nuget.org/v3/index.json and https://www.textcontrol.com/nuget/v3/index.json are listed under the feeds used.

With the classic editor instead of YAML pipelines, add a Command line task that generates nuget.config before the NuGet restore task and references the variables from your variable group. The article Azure DevOps with TX Text Control .NET Server 34.0: Private NuGet Feed and Azure Artifacts describes the complete setup and an alternative with Azure Artifacts.

Jenkins

Store the credentials in the Jenkins Credentials Store:

  1. Navigate to Manage Jenkins > Credentials.
  2. Select the appropriate domain, or Global.
  3. Click Add Credentials and create three Secret text entries: textcontrol-email, textcontrol-api-token and textcontrol-feed-url.

Reference these credentials in your Jenkinsfile:

pipeline {
    agent any

    environment {
        DOTNET_VERSION = '8.0'
    }

    stages {
        stage('Setup') {
            steps {
                withCredentials([
                    string(credentialsId: 'textcontrol-email', variable: 'TEXTCONTROL_EMAIL'),
                    string(credentialsId: 'textcontrol-api-token', variable: 'TEXTCONTROL_API_TOKEN'),
                    string(credentialsId: 'textcontrol-feed-url', variable: 'TEXTCONTROL_FEED_URL')
                ]) {
                    writeFile file: 'nuget.config', text: """<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <add key="TextControl" value="${TEXTCONTROL_FEED_URL}" />
  </packageSources>
  <packageSourceCredentials>
    <TextControl>
      <add key="Username" value="${TEXTCONTROL_EMAIL}" />
      <add key="ClearTextPassword" value="${TEXTCONTROL_API_TOKEN}" />
    </TextControl>
  </packageSourceCredentials>
</configuration>"""
                }
            }
        }

        stage('Restore') {
            steps {
                sh 'dotnet restore'
            }
        }

        stage('Build') {
            steps {
                sh 'dotnet build --configuration Release --no-restore'
            }
        }

        stage('Test') {
            steps {
                sh 'dotnet test --configuration Release --no-build'
            }
        }
    }
}

The withCredentials block injects the secrets as environment variables for the duration of the step, and Jenkins masks them in the console output.

Bitbucket Pipelines

Store the credentials as repository variables:

  1. Navigate to Repository settings > Pipelines > Repository variables.
  2. Add TEXTCONTROL_EMAIL, TEXTCONTROL_API_TOKEN and TEXTCONTROL_FEED_URL, and mark each one as Secured.

Configure your bitbucket-pipelines.yml:

image: mcr.microsoft.com/dotnet/sdk:8.0

pipelines:
  default:
    - step:
        name: Build and Test
        script:
          - |
            cat > nuget.config << EOF
            <?xml version="1.0" encoding="utf-8"?>
            <configuration>
              <packageSources>
                <clear />
                <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
                <add key="TextControl" value="${TEXTCONTROL_FEED_URL}" />
              </packageSources>
              <packageSourceCredentials>
                <TextControl>
                  <add key="Username" value="${TEXTCONTROL_EMAIL}" />
                  <add key="ClearTextPassword" value="${TEXTCONTROL_API_TOKEN}" />
                </TextControl>
              </packageSourceCredentials>
            </configuration>
            EOF
          - dotnet restore
          - dotnet build --configuration Release --no-restore
          - dotnet test --configuration Release --no-build

Secured variables are encrypted and masked in build logs. To share the credentials across several repositories, define the variables at the workspace level instead.

TeamCity

Store the credentials as build parameters:

  1. Open your build configuration and go to Parameters.
  2. Click Add new parameter and create three parameters of the type Password: TEXTCONTROL_EMAIL, TEXTCONTROL_API_TOKEN and TEXTCONTROL_FEED_URL.

Add a Command Line build step that generates nuget.config before the packages are restored. TeamCity references parameters with the %parameter% syntax:

#!/bin/bash

cat > nuget.config << EOF
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <add key="TextControl" value="%TEXTCONTROL_FEED_URL%" />
  </packageSources>
  <packageSourceCredentials>
    <TextControl>
      <add key="Username" value="%TEXTCONTROL_EMAIL%" />
      <add key="ClearTextPassword" value="%TEXTCONTROL_API_TOKEN%" />
    </TextControl>
  </packageSourceCredentials>
</configuration>
EOF

dotnet restore
dotnet build --configuration Release --no-restore
dotnet test --configuration Release --no-build

Password parameters are stored encrypted and masked in build logs.

Docker Builds

When you build a Docker image, dotnet restore runs inside the build container, so the container needs the nuget.config file that your pipeline generates from the secrets, as shown in the sections above. Pass the file to docker buildx build as a build secret instead of copying it into the image: a COPY of nuget.config would store the API token in a layer of the image. Dockerfile shows a multi-stage Dockerfile that mounts the secret for dotnet restore, and Keep nuget.config Out of the Image the .dockerignore file that goes with it.

The article Using a Private NuGet Feed to Build and Restore .NET Apps for Docker Deployment describes a similar setup with packages hosted in GitHub Packages.

Next Steps