Repository navigation
BUILD-12160 Add config-nuget to resolve NuGet packages through Repox #354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| name: Config NuGet | ||
| description: GitHub Action to configure NuGet authentication and resolve the Repox package sources of NuGet configuration files | ||
| inputs: | ||
| nuget-config-files: | ||
| description: NuGet configuration files whose `https://repox.jfrog.io/artifactory` package sources resolve through `repox-url` | ||
| (multiline), relative to `working-directory`. | ||
| default: NuGet.Config | ||
| working-directory: | ||
| description: Relative path under github.workspace to execute the action in | ||
| default: . | ||
| artifactory-reader-role: | ||
| description: Suffix for the Artifactory reader role in Vault. Defaults to `private-reader` for private repositories, and `public-reader` | ||
| for public repositories. | ||
| default: '' | ||
| repox-url: | ||
| description: URL for Repox used to resolve dependencies. Use https://repox-internal.dev.sonar.build for the JFrog Edge. | ||
| default: https://repox.jfrog.io | ||
|
|
||
| runs: | ||
| using: composite | ||
| steps: | ||
|
hedinasr marked this conversation as resolved.
hedinasr marked this conversation as resolved.
|
||
| - name: Set Artifactory reader role | ||
| shell: bash | ||
| env: | ||
| ARTIFACTORY_READER_ROLE: ${{ inputs.artifactory-reader-role != '' && inputs.artifactory-reader-role || | ||
| (github.event.repository.visibility == 'public' && 'public-reader' || 'private-reader') }} | ||
| run: echo "ARTIFACTORY_READER_ROLE=${ARTIFACTORY_READER_ROLE}" >> "$GITHUB_ENV" | ||
|
|
||
| - uses: SonarSource/vault-action-wrapper@881045d830534a70ec3c7c275fa3714412c8ff6e # 3.6.1 | ||
| id: secrets | ||
| with: | ||
| # yamllint disable rule:line-length | ||
| url: ${{ contains(inputs.repox-url, 'jfrog.io') && 'https://vault.sonar.build' || 'https://vault.dev.sonar.build' }} | ||
| secrets: | | ||
| development/${{ contains(inputs.repox-url, 'jfrog.io') && 'artifactory' || 'artifactory-edge-dev' }}/token/{REPO_OWNER_NAME_DASH}-${{ env.ARTIFACTORY_READER_ROLE }} username | ARTIFACTORY_USERNAME; | ||
| development/${{ contains(inputs.repox-url, 'jfrog.io') && 'artifactory' || 'artifactory-edge-dev' }}/token/{REPO_OWNER_NAME_DASH}-${{ env.ARTIFACTORY_READER_ROLE }} access_token | ARTIFACTORY_ACCESS_TOKEN; | ||
| # yamllint enable rule:line-length | ||
|
|
||
| - name: Configure NuGet | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| env: | ||
| ACTION_PATH_CONFIG_NUGET: ${{ github.action_path }} | ||
| ARTIFACTORY_URL: ${{ format('{0}/artifactory', inputs.repox-url) }} | ||
| ARTIFACTORY_USERNAME: ${{ steps.secrets.outputs.vault && fromJSON(steps.secrets.outputs.vault).ARTIFACTORY_USERNAME || '' }} | ||
| ARTIFACTORY_ACCESS_TOKEN: ${{ steps.secrets.outputs.vault && fromJSON(steps.secrets.outputs.vault).ARTIFACTORY_ACCESS_TOKEN || '' }} | ||
| NUGET_CONFIG_FILES: ${{ inputs.nuget-config-files }} | ||
| run: '"$ACTION_PATH_CONFIG_NUGET/nuget_config.sh"' | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| #!/bin/bash | ||
| # Configure NuGet authentication and point the Repox package sources of NuGet configuration files at ARTIFACTORY_URL. | ||
| # | ||
| # Required environment variables (must be explicitly provided): | ||
| # - ARTIFACTORY_URL: URL to Artifactory used to resolve packages | ||
| # - ARTIFACTORY_USERNAME: Username to read Repox repositories | ||
| # - ARTIFACTORY_ACCESS_TOKEN: Access token to read Repox repositories | ||
| # - NUGET_CONFIG_FILES: Newline-separated NuGet configuration files to update | ||
| # | ||
| # GitHub Actions auto-provided: | ||
| # - GITHUB_ENV: Path to GitHub Actions environment file | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| : "${ARTIFACTORY_URL:?}" "${ARTIFACTORY_USERNAME:?}" "${ARTIFACTORY_ACCESS_TOKEN:?}" "${NUGET_CONFIG_FILES:?}" "${GITHUB_ENV:?}" | ||
|
|
||
| readonly SAAS_ARTIFACTORY_URL="https://repox.jfrog.io/artifactory" | ||
|
|
||
| export_credentials() { | ||
| { | ||
| echo "ARTIFACTORY_URL=$ARTIFACTORY_URL" | ||
| echo "ARTIFACTORY_USERNAME=$ARTIFACTORY_USERNAME" | ||
| echo "ARTIFACTORY_ACCESS_TOKEN=$ARTIFACTORY_ACCESS_TOKEN" | ||
| echo "ARTIFACTORY_USER=$ARTIFACTORY_USERNAME" | ||
| echo "ARTIFACTORY_PASSWORD=$ARTIFACTORY_ACCESS_TOKEN" | ||
| } >> "$GITHUB_ENV" | ||
| return 0 | ||
| } | ||
|
|
||
| point_nuget_config() { | ||
| local config="$1" | ||
| local saas_pattern="value=\"${SAAS_ARTIFACTORY_URL//./\\.}/" | ||
| if [[ ! -f "$config" ]]; then | ||
| echo "::error::NuGet configuration file not found: $config" >&2 | ||
| return 1 | ||
| fi | ||
| if ! grep -q "$saas_pattern" "$config"; then | ||
| if grep -qF "value=\"${ARTIFACTORY_URL}/" "$config"; then | ||
| echo "Package sources of $config already resolve through $ARTIFACTORY_URL" | ||
| return 0 | ||
| fi | ||
| echo "::error::No package source on $SAAS_ARTIFACTORY_URL in $config" >&2 | ||
| return 1 | ||
| fi | ||
| sed -i.bak "s#${saas_pattern}#value=\"${ARTIFACTORY_URL}/#g" "$config" | ||
|
hedinasr marked this conversation as resolved.
|
||
| rm -f "$config.bak" | ||
| echo "Package sources of $config now resolve through $ARTIFACTORY_URL" | ||
| return 0 | ||
| } | ||
|
|
||
| main() { | ||
| echo "::group::Configure NuGet" | ||
| export_credentials | ||
| if [[ "${ARTIFACTORY_URL%/}" == "$SAAS_ARTIFACTORY_URL" ]]; then | ||
| echo "NuGet configuration files already resolve through $SAAS_ARTIFACTORY_URL" | ||
| else | ||
| local config | ||
| while IFS= read -r config; do | ||
| config="${config%$'\r'}" | ||
| config="${config#"${config%%[![:space:]]*}"}" | ||
| config="${config%"${config##*[![:space:]]}"}" | ||
| [[ -z "$config" ]] && continue | ||
| point_nuget_config "$config" || return 1 | ||
|
gitar-bot[bot] marked this conversation as resolved.
|
||
| done <<< "$NUGET_CONFIG_FILES" | ||
| fi | ||
| echo "::endgroup::" | ||
| return 0 | ||
| } | ||
|
|
||
| if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then | ||
| main | ||
| fi | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| #!/usr/bin/env bash | ||
| eval "$(shellspec - -c) exit 1" | ||
|
|
||
| export GITHUB_ENV=/dev/null | ||
| export ARTIFACTORY_URL="https://repox-internal.dev.sonar.build/artifactory" | ||
| export ARTIFACTORY_USERNAME="reader" | ||
| export ARTIFACTORY_ACCESS_TOKEN="reader-token" | ||
| export NUGET_CONFIG_FILES="NuGet.Config" | ||
|
|
||
| Describe 'config-nuget/nuget_config.sh' | ||
| It 'does not run main when sourced' | ||
| When run source config-nuget/nuget_config.sh | ||
| The status should be success | ||
| The output should equal "" | ||
| End | ||
| End | ||
|
|
||
| Include config-nuget/nuget_config.sh | ||
|
|
||
| write_nuget_config() { | ||
| local nuget_config="$1" | ||
| cat > "$nuget_config" <<'EOF' | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <configuration> | ||
| <packageSources> | ||
| <clear /> | ||
| <add key="Repox proxy" value="https://repox.jfrog.io/artifactory/api/nuget/v3/nuget/index.json" protocolVersion="3" /> | ||
| <add key="Repox releases" value="https://repox.jfrog.io/artifactory/api/nuget/v3/sonarsource-nuget-releases/index.json" /> | ||
| </packageSources> | ||
| <trustedSigners> | ||
| <repository name="Repox" serviceIndex="https://repox.jfrog.io/artifactory/api/nuget/v3/nuget/index.json" /> | ||
| </trustedSigners> | ||
| </configuration> | ||
| EOF | ||
| return | ||
| } | ||
|
|
||
| common_setup() { | ||
| WORK_DIR=$(mktemp -d) | ||
| cd "$WORK_DIR" || exit 1 | ||
| GITHUB_ENV=$(mktemp) | ||
| export GITHUB_ENV | ||
| } | ||
|
|
||
| common_cleanup() { | ||
| cd - > /dev/null || true | ||
| rm -rf "$WORK_DIR" "$GITHUB_ENV" | ||
| return | ||
| } | ||
|
|
||
| Describe 'export_credentials()' | ||
| BeforeEach 'common_setup' | ||
| AfterEach 'common_cleanup' | ||
|
|
||
| It 'exports the reader credentials for NuGet and other tools' | ||
| When call export_credentials | ||
| The status should be success | ||
| The contents of file "$GITHUB_ENV" should include "ARTIFACTORY_URL=https://repox-internal.dev.sonar.build/artifactory" | ||
| The contents of file "$GITHUB_ENV" should include "ARTIFACTORY_USERNAME=reader" | ||
| The contents of file "$GITHUB_ENV" should include "ARTIFACTORY_ACCESS_TOKEN=reader-token" | ||
| The contents of file "$GITHUB_ENV" should include "ARTIFACTORY_USER=reader" | ||
| The contents of file "$GITHUB_ENV" should include "ARTIFACTORY_PASSWORD=reader-token" | ||
| End | ||
| End | ||
|
|
||
| Describe 'point_nuget_config()' | ||
| BeforeEach 'common_setup' | ||
| AfterEach 'common_cleanup' | ||
|
|
||
| It 'points every Repox package source at ARTIFACTORY_URL' | ||
| write_nuget_config NuGet.Config | ||
| When call point_nuget_config NuGet.Config | ||
| The status should be success | ||
| The output should equal "Package sources of NuGet.Config now resolve through https://repox-internal.dev.sonar.build/artifactory" | ||
| The contents of file NuGet.Config should include 'value="https://repox-internal.dev.sonar.build/artifactory/api/nuget/v3/nuget/index.json"' | ||
| The contents of file NuGet.Config should include 'value="https://repox-internal.dev.sonar.build/artifactory/api/nuget/v3/sonarsource-nuget-releases/index.json"' | ||
| The contents of file NuGet.Config should include 'serviceIndex="https://repox.jfrog.io/artifactory/api/nuget/v3/nuget/index.json"' | ||
| The file NuGet.Config.bak should not be exist | ||
| End | ||
|
|
||
| It 'skips a file that already resolves through ARTIFACTORY_URL' | ||
| write_nuget_config NuGet.Config | ||
| point_nuget_config NuGet.Config > /dev/null | ||
| When call point_nuget_config NuGet.Config | ||
| The status should be success | ||
| The output should equal "Package sources of NuGet.Config already resolve through https://repox-internal.dev.sonar.build/artifactory" | ||
| The contents of file NuGet.Config should include 'value="https://repox-internal.dev.sonar.build/artifactory/api/nuget/v3/nuget/index.json"' | ||
| End | ||
|
|
||
| It 'fails when the file does not exist' | ||
| When call point_nuget_config missing/NuGet.Config | ||
| The status should be failure | ||
| The stderr should equal "::error::NuGet configuration file not found: missing/NuGet.Config" | ||
| End | ||
|
|
||
| It 'fails when the file has no Repox package source' | ||
| echo '<configuration><packageSources><add key="nuget.org" value="https://api.nuget.org/v3/index.json" /></packageSources></configuration>' > NuGet.Config | ||
| When call point_nuget_config NuGet.Config | ||
| The status should be failure | ||
| The stderr should equal "::error::No package source on https://repox.jfrog.io/artifactory in NuGet.Config" | ||
| End | ||
| End | ||
|
|
||
| Describe 'main()' | ||
| BeforeEach 'common_setup' | ||
| AfterEach 'common_cleanup' | ||
|
|
||
| It 'updates every listed file and skips blank lines' | ||
| mkdir -p a b | ||
| write_nuget_config a/NuGet.Config | ||
| write_nuget_config b/nuget.config | ||
| export NUGET_CONFIG_FILES=$'a/NuGet.Config\r\n\n b/nuget.config \n' | ||
| When call main | ||
| The status should be success | ||
| The line 1 should equal "::group::Configure NuGet" | ||
| The line 2 should equal "Package sources of a/NuGet.Config now resolve through https://repox-internal.dev.sonar.build/artifactory" | ||
| The line 3 should equal "Package sources of b/nuget.config now resolve through https://repox-internal.dev.sonar.build/artifactory" | ||
| The line 4 should equal "::endgroup::" | ||
| The contents of file b/nuget.config should not include 'value="https://repox.jfrog.io/' | ||
| End | ||
|
|
||
| It 'keeps backslashes and apostrophes in listed paths' | ||
| write_nuget_config $'its\\NuGet.config' | ||
| mkdir -p "owner's" | ||
| write_nuget_config "owner's/NuGet.Config" | ||
| export NUGET_CONFIG_FILES=$'its\\NuGet.config\nowner'"'"'s/NuGet.Config' | ||
| When call main | ||
| The status should be success | ||
| The line 2 should equal $'Package sources of its\\NuGet.config now resolve through https://repox-internal.dev.sonar.build/artifactory' | ||
| The line 3 should equal "Package sources of owner's/NuGet.Config now resolve through https://repox-internal.dev.sonar.build/artifactory" | ||
| End | ||
|
|
||
| It 'leaves the files unchanged with SaaS Repox' | ||
| write_nuget_config NuGet.Config | ||
| export ARTIFACTORY_URL="https://repox.jfrog.io/artifactory" | ||
| export NUGET_CONFIG_FILES="NuGet.Config" | ||
| When call main | ||
| The status should be success | ||
| The line 2 should equal "NuGet configuration files already resolve through https://repox.jfrog.io/artifactory" | ||
| The contents of file NuGet.Config should include 'value="https://repox.jfrog.io/artifactory/api/nuget/v3/nuget/index.json"' | ||
| The contents of file "$GITHUB_ENV" should include "ARTIFACTORY_URL=https://repox.jfrog.io/artifactory" | ||
| End | ||
|
|
||
| It 'fails when a listed file is missing' | ||
| export NUGET_CONFIG_FILES="missing/NuGet.Config" | ||
| When run main | ||
| The status should be failure | ||
| The line 1 should equal "::group::Configure NuGet" | ||
| The stderr should equal "::error::NuGet configuration file not found: missing/NuGet.Config" | ||
| End | ||
| End |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.