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

Workaround for lack of dotnet nuget push --dry-run will always fail for Nuget.org #408

Open
BinToss opened this issue Jun 18, 2024 · 1 comment
Assignees
Labels
bug Something isn't working

Comments

@BinToss
Copy link
Member

BinToss commented Jun 18, 2024

While investigating #407, I stumbled upon a design failure which may require breaking changes to fix.

Problem 1

Our Nuget.org token tests will always respond with Error 403 (unauthorized) when using the same "static dummy package" strategy we use for GitHub and GitLab (W.I.P.). The generated package ID of the dummy package is "DUMMY". This package ID corresponds to the hidden package https://www.nuget.org/packages/DUMMY/ which almost none of our API users can push to. Hence, HTTP Error 403.

Problem 2

Even if the token has permission to push DUMMY to Nuget.org, it may lack permission to push the real packages, causing a critical failure partway through semantic release's publish step. Git tags and more will have been created by then, so the partial release will need to be cleaned up manually.

Proposal

Part 1: Object Type

  • A source is Nuget API URL { url: URL | string }
  • A source cannot be accessed without an API token. { tokenEnvVar: string }
    • A token's value should be in memory for as little time as possible.
  • One or more package IDs must be paired with a token and source. A package can be pushed to multiple sources, but a package cannot have multiple tokens for one source. { packageIDs: string[] }

implements NugetRegistryPair?
extends NugetRegistryInformation?

class PushInfo {
  readonly url: URL | string;
  readonly tokenEnvVar: string
  get tokenValue(): undefined | string {
    // if not already in process.env, load .env file from process.cwd(), update process.env, and return the env var's value or undefined
    return getEnvVarValue(tokenEnvVar);
  }
}

Part 2: Implementation

  • Locate *.*proj using projectsToPackAndPush (array of glob strings)
  • Per-project, evaluate package IDs with dotnet msbuild ${projPath} -getProperty:PackageId to get their package IDs
  • If a given project ID does not exist at the given registry, set dummy version to 0.0.1-dummy.
    • otherwise, set dummy's version to latest package version.
  • Don't forget --skip-duplicate!
  • Create a copy of DUMMY.*.nupkg (or create a new one with a custom ID; refactor createDummyNupkg) with Version 0.0.1-DUMMY.
    • Can we de-list it to prevent clutter?
  • Optional: refactor dummy-push commands to semantic-release/exec's verifyConditionsCmd to conform to semantic-release expectations. We must check if ${packageId}@${nextVersion} exists at the source. This operation must occur after "Create Git tag", a step hidden between generateNotes and prepare. So, this must be the first operation of prepare.
  • Don't forget to allow signing dummy packages! Dummy packages may be rejected despite valid tokens if they are unsigned and pushed to a signed-only package ID!
@BinToss
Copy link
Member Author

BinToss commented Jun 30, 2024

semanticReleaseConfigDotnet.ts

Will resolve

NuGet clients and server APIs do not implement --dry-run functionality. The request for it has been open for three years with no progress. So, I have to improvise effectively-equivalent functionality out of existing NuGet features.

  • If the authorization token DOES NOT have permission to push new versions/packages for the given package ID, fail the release.
  • If the package ID already has the new version listed, fail the release.

Both are accomplished by the following:

  • grab the new version during Prepare
  • copy our pre-made, lightweight v0.0.1-DUMMY dummy package. Overwrite its PackageID with the real ID.
  • query the NuGet source (i.e. package registry/server) for the package ID and check the existing package versions for the "new version" we want to publish.
  • dotnet nuget push ./publish/${PackageId}.0.0.1-DUMMY.nupkg -source NugetSourceName --api-key private_token --skip duplicate.

--skip-duplicate tells the Source and client it's okay if the dummy package already exists.
If we do not receive an error, proceed. If the error is 403, then the token was denied access. This can happen if "the token is invlaid, unrecognized, or was denied access to the Source" (private, GitLab, GitHub), "the token has Write or Delete permission for one or more packages, but was used for an package ID it's not authorized to access" (NuGet.org, private), or "the token was created without Write or Delete permissions" (any source). If any error occurs, including 403, add the error to an array.

Repeat for every given PackageId, per each NuGet source. Each PackageId will need one auth token per Source.

After all test pushes finish and if the error array is not empty, throw new AggregateError(errors) and halt the release procedure. Otherwise, proceed with the rest of the release procedure.

I'll need:

  • new FileInfo class for MSBuildProject constructor parameter. Ensures the parameter passed to the constructor represents an existing file. Dirent is close, but requires extra steps to get. Our TypeScript/JavaScript runtime will not read the project file's contents directly—we delegate that to Dotnet CLI's dotnet msbuild -getProperty:PropertyName0 -getProperty:PropertyName1 ... to evaluate MSBuild properties.
  • NugetRegistryInfo constructor with MSBuildProject parameter
  • ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant