Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/actions/configure-nuget/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# NuGet reader configuration

Call this action immediately before a NuGet restore or another phase that can download NuGet packages. It delegates source configuration and fresh Vault authentication to [the shared config-nuget action](https://github.com/SonarSource/ci-github-actions/tree/b4eede988476ebb13a242fbc3ae4e8e374d78ad0/config-nuget), pinned to that revision, with an explicit `private-reader` role and selected Repox URL.

Internal Windows and Linux runners use `https://repox.sonar.build/artifactory/api/nuget/v3/nuget/index.json` with the production native reader at `development/artifactory-edge-prod/token/{REPO_OWNER_NAME_DASH}-private-reader`. GitHub-hosted and macOS runners use `https://repox.jfrog.io/artifactory/api/nuget/v3/nuget/index.json` with the SaaS reader at `development/artifactory/token/{REPO_OWNER_NAME_DASH}-private-reader`.

The `use-prod-edge` input defaults to empty. The first step then trims surrounding whitespace and normalizes the case of the inherited `NUGET_USE_PROD_EDGE` environment variable. Empty, `true`, and `false` are accepted; other values fail before requesting credentials. Production Edge is selected only for internal Windows or Linux runners when the normalized value is not `false`. Explicit `true` or `false` inputs override this default selection without reading the inherited variable; other input values fail before requesting credentials.

The wrapper preserves the existing generic Artifactory environment, calls the shared action, then restores the prior values and exports the fresh reader as `NuGetPackageSourceCredentials_Repox`. It uses the `Username=...;Password=...` syntax supported by both modern NuGet and the NuGet client shipped with Visual Studio 2017. NuGet uses this source-specific credential before the credentials in `NuGet.Config`, so Maven, publication, signing, and Orchestrator credentials retain their own scope. Credentials stay out of the configuration file; source mapping, signature requirements, and trusted signers are preserved. The committed configuration retains its SaaS default for local use.

The reader's configured default lifetime is one hour. A call refreshes the credential for subsequent steps; it does not refresh a running command. Keep calls close to network activity and refresh again before a later download phase.

To roll back CI downloads to SaaS, set the repository or organization Actions variable `NUGET_USE_PROD_EDGE` to `false`, or pass `use-prod-edge: 'false'` at a call site. The CI workflows pass the variable to the action through the matching environment variable. The wrapper resets the source to SaaS before calling the shared action on this path, so rollback switches both source and reader even after an Edge call in the same job. An explicit `use-prod-edge: 'true'` selects production Edge regardless of runner defaults.

NuGet's environment credential precedence and syntax are documented in [Consuming packages from authenticated feeds](https://learn.microsoft.com/en-us/nuget/consume-packages/consuming-packages-authenticated-feeds#credentials-in-environment-variables).
85 changes: 85 additions & 0 deletions .github/actions/configure-nuget/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: Configure NuGet

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much of this is reusable by the other 10 repos using NuGet? This looks like something that should live in one of the public tooling repos and be reused, instead of copied 10 times.

description: Pair the Repox NuGet source with a fresh reader for the selected Artifactory instance.

inputs:
use-prod-edge:
description: Use production Edge on internal runners. Set false to use the SaaS source and reader.
default: ''

runs:
using: composite
steps:
- name: Select NuGet instance
id: instance
shell: bash
env:
USE_PROD_EDGE: ${{ inputs.use-prod-edge }}
RUNNER_ENVIRONMENT: ${{ runner.environment }}
run: |
set -euo pipefail
if [[ -z "$USE_PROD_EDGE" ]]; then
inherited_selection="$(printf '%s\n' "${NUGET_USE_PROD_EDGE:-}" | tr '[:upper:]' '[:lower:]' | sed -e ':all' -e '$!N' -e '$!b all' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
case "$inherited_selection" in
''|true|false) ;;
*) echo '::error::NUGET_USE_PROD_EDGE must be empty, true or false'; exit 1 ;;
esac
USE_PROD_EDGE=false
if [[ ("$RUNNER_OS" == Windows || "$RUNNER_OS" == Linux) && "$RUNNER_ENVIRONMENT" != github-hosted && "$inherited_selection" != false ]]; then
USE_PROD_EDGE=true
fi
fi
case "$USE_PROD_EDGE" in
true|false) ;;
*) echo '::error::use-prod-edge must be true or false'; exit 1 ;;
esac
Comment thread
gitar-bot[bot] marked this conversation as resolved.
Resolved
printf 'use-prod-edge=%s\n' "$USE_PROD_EDGE" >> "$GITHUB_OUTPUT"

- name: Preserve Artifactory environment
id: environment
shell: bash
run: |
set -euo pipefail
for variable in ARTIFACTORY_URL ARTIFACTORY_USERNAME ARTIFACTORY_ACCESS_TOKEN ARTIFACTORY_USER ARTIFACTORY_PASSWORD ARTIFACTORY_READER_ROLE; do
printf '%s=%s\n' "$variable" "${!variable-}" >> "$GITHUB_OUTPUT"
done

- name: Reset NuGet source for SaaS rollback
if: steps.instance.outputs.use-prod-edge == 'false'
shell: bash
run: |
dotnet nuget update source Repox \
--source https://repox.jfrog.io/artifactory/api/nuget/v3/nuget/index.json \
--configfile NuGet.Config

- name: Configure NuGet source and reader
id: nuget
uses: SonarSource/ci-github-actions/config-nuget@b4eede988476ebb13a242fbc3ae4e8e374d78ad0
with:
artifactory-reader-role: private-reader
repox-url: ${{ steps.instance.outputs.use-prod-edge == 'true' && 'https://repox.sonar.build' || 'https://repox.jfrog.io' }}

- name: Scope reader credentials to NuGet
if: always() && steps.environment.outcome == 'success'
shell: bash
env:
PREVIOUS_ARTIFACTORY_URL: ${{ steps.environment.outputs.ARTIFACTORY_URL }}
PREVIOUS_ARTIFACTORY_USERNAME: ${{ steps.environment.outputs.ARTIFACTORY_USERNAME }}
PREVIOUS_ARTIFACTORY_ACCESS_TOKEN: ${{ steps.environment.outputs.ARTIFACTORY_ACCESS_TOKEN }}
PREVIOUS_ARTIFACTORY_USER: ${{ steps.environment.outputs.ARTIFACTORY_USER }}
PREVIOUS_ARTIFACTORY_PASSWORD: ${{ steps.environment.outputs.ARTIFACTORY_PASSWORD }}
PREVIOUS_ARTIFACTORY_READER_ROLE: ${{ steps.environment.outputs.ARTIFACTORY_READER_ROLE }}
NUGET_CONFIGURATION_OUTCOME: ${{ steps.nuget.outcome }}
run: |
set -euo pipefail
for variable in ARTIFACTORY_URL ARTIFACTORY_USERNAME ARTIFACTORY_ACCESS_TOKEN ARTIFACTORY_USER ARTIFACTORY_PASSWORD ARTIFACTORY_READER_ROLE; do
previous_variable="PREVIOUS_$variable"
printf '%s=%s\n' "$variable" "${!previous_variable}" >> "$GITHUB_ENV"
done
if [[ "$NUGET_CONFIGURATION_OUTCOME" != success ]]; then
exit 0
fi
: "${ARTIFACTORY_USERNAME:?NuGet reader username is required}" "${ARTIFACTORY_ACCESS_TOKEN:?NuGet reader token is required}"
# NuGet 4.9 treats authentication-type metadata after Password as part of the password.
credentials="Username=$ARTIFACTORY_USERNAME;Password=$ARTIFACTORY_ACCESS_TOKEN"
printf '::add-mask::%s\n' "$credentials"
printf 'NuGetPackageSourceCredentials_Repox=%s\n' "$credentials" >> "$GITHUB_ENV"
3 changes: 3 additions & 0 deletions .github/actions/its-unix/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ runs:
fi
echo "ORCHESTRATOR_CONFIG_URL=$config_url" >> "$GITHUB_ENV"

- name: Configure NuGet
uses: ./.github/actions/configure-nuget

- name: Run Maven ITs
env:
MAVEN_OPTS: -Xmx3072m
Expand Down
3 changes: 3 additions & 0 deletions .github/actions/prepare/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ runs:
key: nuget-${{ steps.cache-month.outputs.CACHE_MONTH }}-${{ hashFiles('**/packages.lock.json') }}
restore-keys: nuget-${{ steps.cache-month.outputs.CACHE_MONTH }}-

- name: Configure NuGet
uses: ./.github/actions/configure-nuget

- name: Restore
shell: powershell
env:
Expand Down
6 changes: 6 additions & 0 deletions .github/actions/qa-unix/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,16 @@ runs:
key: nuget-${{ runner.os }}-${{ steps.cache-month.outputs.CACHE_MONTH }}-${{ hashFiles('**/packages.lock.json') }}
restore-keys: nuget-${{ runner.os }}-${{ steps.cache-month.outputs.CACHE_MONTH }}-

- name: Configure NuGet
uses: ./.github/actions/configure-nuget

- name: Build test pre-requisites
shell: bash
run: dotnet publish ./Tests/LogArgs/LogArgs.csproj --framework net10.0 --self-contained -r ${{ inputs.rid }}

- name: Refresh NuGet reader
uses: ./.github/actions/configure-nuget

- name: Run UTs
shell: bash
env:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ concurrency:

env:
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
NUGET_USE_PROD_EDGE: ${{ vars.NUGET_USE_PROD_EDGE }}

jobs:
version:
Expand Down Expand Up @@ -183,6 +184,9 @@ jobs:
path: ${{ env.NUGET_PACKAGES }}/dotnet-sonarscanner
key: s4net-${{ hashFiles('.config/dotnet-tools.json') }}

- name: Refresh NuGet reader
uses: ./.github/actions/configure-nuget

- name: Restore SonarScanner for .NET
run: dotnet tool restore --tool-manifest .config/dotnet-tools.json --configfile "NuGet.Config"

Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/its-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ on:
SONARCLOUD_PROJECT_TOKEN:
required: true

env:
NUGET_USE_PROD_EDGE: ${{ vars.NUGET_USE_PROD_EDGE }}

jobs:
its_windows:
name: ${{ matrix.scenario }}
Expand Down Expand Up @@ -223,6 +226,9 @@ jobs:
fi
echo "ORCHESTRATOR_CONFIG_URL=$config_url" >> "$GITHUB_ENV"

- name: Configure NuGet
uses: ./.github/actions/configure-nuget

- name: Run Windows ITs (${{ matrix.scenario }})
env:
MAVEN_OPTS: -Xmx3072m
Expand Down
Loading