Skip to content
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

Publish to multiple sources/servers #627

Open
BinToss opened this issue Aug 10, 2023 · 4 comments
Open

Publish to multiple sources/servers #627

BinToss opened this issue Aug 10, 2023 · 4 comments

Comments

@BinToss
Copy link

BinToss commented Aug 10, 2023

This will require semantic-release-nuget's configuration object to be changed.

Currently, semantic-release-nuget only allows pushing to one custom server with NUGET_TOKEN. This prevents users from pushing to multiple servers e.g. NuGet.org and GitHub registries. This could be alleviated by a GitHub equivalent to publishToGitLab, but the problem still exists when an organization wants to publish their NuPkg(s) to both NuGet.org and a custom, private registry.

To allow users to push packages to multiple servers, server URLs and API tokens will need to be passed as an array of pairs i.e.

{
	"plugins": [
		...
		[
			"@droidsolutions-oss/semantic-release-nuget",
			[
				"registries": [
					[ "https://nuget.mycomapny.com/v3/index.json", "${PRIVATE_TOKEN}" ],
					[ "https://npr.customserver.com/v3/index.json", "${OTHER_TOKEN}"]
				]
			]
		],
		...
	],
}

Alternatively, an array of well-defined objects which could help with hints/suggestions in IDEs when semantic-release improves support for plugins' parameters.

"registries": [
	{
		"url": "https://nuget.mycomapny.com/v3/index.json",
		"token": "${PRIVATE_TOKEN}"
	},
	{
		"url": "https://npr.customserver.com/v3/index.json",
		"token": "${OTHER_TOKEN}"
	}
]
@Kampfmoehre
Copy link
Member

I can take a look at this, but it probably needs more refinement. The reason publishToGitLab exists is, that the handling is a bit different there. I am not sure about GitHubs registry, but I guess it is similar using some special CI variables. We either must resolve env vars in the server url, or add some type property to it

{
  "registries": [
    {
      "url": "https://nuget.mycomapny.com/v3/index.json",
      "token": "${PRIVATE_TOKEN}"
    },
    {
      "url": "https://npr.customserver.com/v3/index.json",
      "token": "${OTHER_TOKEN}"
    },
    {
      "url": "${CI_SERVER_URL}/api/v4/projects/{CI_PROJECT_ID}/packages/nuget/index.json",
      "token": "${CI_JOB_TOKEN}",
      "type": "gitlab"
    }
  ]
}

@BinToss
Copy link
Author

BinToss commented Apr 2, 2024

Currently, my release workflow is as follows:

# Test changes locally with https://github.com/nektos/act
# Design with graphs via https://marketplace.visualstudio.com/items?itemName=actionforge.actionforge
name: Release

on:
  push:
    branches:
      - main
      - develop
env:
  DOTNET_ROLL_FORWARD: "Major"
  DOTNET_CLI_TELEMETRY_OPTOUT: 1
  DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
  DOTNET_NOLOGO: 1

jobs:
  ci:
    name: CI
    uses: ./.github/workflows/ci.yml
  # based on https://github.com/semantic-release/semantic-release/blob/master/docs/recipes/ci-configurations/github-actions.md
  release:
    needs: [ci]
    runs-on: ubuntu-latest
    permissions:
      contents: write # to be able to publish a GitHub release
      issues: write # to be able to comment on released issues
      pull-requests: write # to be able to comment on released pull requests
      id-token: write # to enable use of OIDC for npm provenance
      packages: write # for pushing GitHub Nuget packages

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # necessary for semantic release

      - uses: actions/setup-node@v4
        with:
          cache: "npm"
          check-latest: true
          node-version-file: package.json
      - run: npm i -g npm@latest # required for attestation. There's a games-stopping bug in Node.js LTS's NPM.

      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: "8.x"

      - name: NPM - Clean Install
        run: npm ci

      - name: NPM - Audit Signatures
        run: npm audit signatures

      - name: Update version in README's Avalonia badge
        shell: pwsh
        run: |
          $AV = [version]::new(((dotnet msbuild .\GroupBox.Avalonia\GroupBox.Avalonia.csproj  -getItem:PackageReference | ConvertFrom-Json).Items.PackageReference | Where-Object {$_.Identity -eq 'Avalonia'}).Version).ToString();
          $pattern = "(?<=\[!\[avalonia]\(https:\/\/img\.shields\.io\/badge\/avalonia-v)\d+\.\d+\.\d+";
          (Get-Content -Path README.md -Raw) -creplace $pattern, $AV | Set-Content -NoNewLine -Path README.md

      # [release#
      # steps](https://github.com/semantic-release/semantic-release#release-steps)
      # Plugins add sub-steps e.g. @semantic-release/git's Prepare will create a
      # release commit, including configurable file assets."
      # After the new version Git tagged, @semantic-release/exec runs
      # `dotnet publish`. @semantic-release/github adds the artifacts to the
      # GitHub Release.
      - name: Semantic Release
        id: semantic_release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
        shell: pwsh
        run: |
          $output=npx semantic-release
          if ($output.EndsWith('There are no relevant changes, so no new version is released.')) {
            echo "NoNewVersion=true" >> "$env:GITHUB_OUTPUT"
          }

      # https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-nuget-registry
      # https://github.com/actions/setup-dotnet/tree/v3/#setting-up-authentication-for-nuget-feeds
      - name: .NET - Pack n' Push NuPkg
        if: ${{!steps.semantic_release.outputs.NO_NEW_VERSION}}
        env:
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
          NUGET_TOKEN: ${{secrets.NUGET_TOKEN}}
        shell: pwsh
        run: |
          dotnet pack ./GroupBox.Avalonia/GroupBox.Avalonia.csproj -o ./publish/
          dotnet nuget push ./publish/*.nupkg --source https://api.nuget.org/v3/index.json --api-key $env:NUGET_TOKEN
          dotnet nuget push ./publish/*.nupkg --source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json --api-key $env:GITHUB_TOKEN
  • packages: write permission is required for pushing package to GPR!
  • The last line's --source value is notable for the github.repository variable. This value is also available via the GITHUB_REPOSITORY_OWNER environment variable.

*Some users use a Personal Access Token instead of the workflow's auto-token for edge-cases. They may need a reminder (README?) to update its permissions if they want to push NuGet packages to GitHub.


edit: the url of a workflow's GitHub Package Repository should default to...

if (process.env["GITHUB_REPOSITORY_OWNER"]) {
	const GithubPkgUrl= `https://nuget.pkg.github.com/${process.env["GITHUB_REPOSITORY_OWNER"]}/index.json`
}

or, using the syntax in your example config...

{
	"url": "https://nuget.pkg.github.com/${GITHUB_REPOSITORY_OWNER}/index.json",
	"token": "${GITHUB_TOKEN}"
}

@BinToss
Copy link
Author

BinToss commented Apr 2, 2024

Regarding tokens, how would users go about assigning them to each registry via environment variables?
CLI may need to be adjusted, too. Perhaps --registries with a comma-separated, A-B array (url,key,url,key)?

Your example contains strings such as ${CI_JOB_TOKEN}. Do you intend this to be a string from which the environment variable is derived?
For example, given "token": "${GITHUB_TOKEN}"...

// assuming registries is mapped or forEach'ed...hmm...which registry NUGET_TOKEN apply to? If length is one, it's easy.
if (pluginConfig.registries[i].token && pluginConfig.registries[i].token.startsWith("${") {
	token = process.env[pluginConfig.registries[i].token.replace("${").replace("}")];
}

@Kampfmoehre
Copy link
Member

Regarding tokens, how would users go about assigning them to each registry via environment variables?

The idea was, to specify credentials along with the NuGet source but instead of hardcoding them (which would be bad practice) allow to use environment variables that the plugin can resolve.

Your example contains strings such as ${CI_JOB_TOKEN}. Do you intend this to be a string from which the environment variable is derived?

${CI_JOB_TOKEN} would be resolved in the verify step and replaced with the content of that environment variable. In GitLab CI this is a variable filled by GitLab with a token with enough rights to be used for that specific case.

In your case it would look more like this

  "registries": [
    {
      "name": "default",
      // url could be omitted for official NuGet server
      "token": "${NUGET_TOKEN}"
    },
    {
      "name": "github-public",
      "url": "https://nuget.pkg.github.com/${GITHUB_REPOSITORY_OWNER}/index.json",
      "token": "${GITHUB_TOKEN}",
      "type": "github"
    }
  ]

Maybe it would be better to build the url automatically instead of allowing to use env vars in it by using the type?. Type could be something like

  • default (NuGet.org)
  • gitlab_private (needs host, group, repo)
  • gitlab_public (needs group and repo names)
  • github (needs orga or user of the repo)
  • custom_nuget_server (this needs an url to be set)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants