-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Support NuGet lockfiles #6031
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
Closed
Closed
Support NuGet lockfiles #6031
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
fe1d84c
NuGet lockfile implementation
anthony-c-martin 6eeadf1
Merge remote-tracking branch 'origin/main' into lockfiles
anthony-c-martin ca49f1e
Attempt to fix docker build
anthony-c-martin 259ea98
Disable dotnet globalization
anthony-c-martin 463f90f
Fix assertion message
anthony-c-martin 5a56a26
Merge remote-tracking branch 'origin/main' into lockfiles
anthony-c-martin 3e1ac1e
Test with new smoke test
anthony-c-martin 0f77df1
Merge remote-tracking branch 'origin/main' into lockfiles
anthony-c-martin 1eb8102
Allow for unchanged lock files
anthony-c-martin 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,23 @@ | ||
| FROM ghcr.io/dependabot/dependabot-updater-core | ||
|
|
||
| USER root | ||
|
|
||
| # Set up the install directory | ||
| ENV DOTNET_HOME=/opt/dotnet \ | ||
| PATH="${PATH}:/opt/dotnet" | ||
| RUN mkdir -p "$DOTNET_HOME" && chown dependabot:dependabot "$DOTNET_HOME" | ||
|
|
||
| USER dependabot | ||
|
|
||
| ENV DOTNET_NOLOGO=true \ | ||
| DOTNET_CLI_TELEMETRY_OPTOUT=true | ||
|
|
||
| # Run dotnet without globalization support | ||
| ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 | ||
|
|
||
| # Fetch and install dotnet | ||
| RUN curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel 7.0 --install-dir /opt/dotnet | ||
|
|
||
| COPY --chown=dependabot:dependabot nuget $DEPENDABOT_HOME/nuget | ||
| COPY --chown=dependabot:dependabot common $DEPENDABOT_HOME/common | ||
| COPY --chown=dependabot:dependabot updater $DEPENDABOT_HOME/dependabot-updater | ||
| COPY --chown=dependabot:dependabot updater $DEPENDABOT_HOME/dependabot-updater |
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
69 changes: 69 additions & 0 deletions
69
nuget/lib/dependabot/nuget/file_updater/lockfile_updater.rb
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,69 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require "dependabot/nuget/file_updater" | ||
|
|
||
| module Dependabot | ||
| module Nuget | ||
| class FileUpdater | ||
| class LockfileUpdater | ||
| def initialize(dependency_files:, lock_file:, credentials:) | ||
| @dependency_files = dependency_files | ||
| @lock_file = lock_file | ||
| @credentials = credentials | ||
| end | ||
|
|
||
| def updated_lockfile_content | ||
| @updated_lockfile_content ||= | ||
| begin | ||
| build_updated_lockfile | ||
| end | ||
| end | ||
|
|
||
| private | ||
|
|
||
| attr_reader :dependency_files, :lock_file, :credentials | ||
|
|
||
| def build_updated_lockfile | ||
| SharedHelpers.in_a_temporary_directory do | ||
| SharedHelpers.with_git_configured(credentials: credentials) do | ||
| dependency_files.each do |file| | ||
| path = file.name | ||
| FileUtils.mkdir_p(Pathname.new(path).dirname) | ||
| File.write(path, file.content) | ||
| end | ||
|
|
||
| Dir.chdir(lock_file_directory) do | ||
| run_dotnet_restore | ||
| end | ||
| end | ||
| end | ||
| rescue SharedHelpers::HelperSubprocessFailed => e | ||
| handle_dotnet_restore_error(e) | ||
| end | ||
|
|
||
| def run_dotnet_restore | ||
| command = [ | ||
| "dotnet", | ||
| "restore", | ||
| "--force-evaluate" | ||
| ].join(" ") | ||
| SharedHelpers.run_shell_command(command) | ||
|
|
||
| File.read(lock_file_basename) | ||
| end | ||
|
|
||
| def handle_dotnet_restore_error(error) | ||
| raise error | ||
| end | ||
|
|
||
| def lock_file_directory | ||
| Pathname.new(lock_file.name).dirname.to_s | ||
| end | ||
|
|
||
| def lock_file_basename | ||
| Pathname.new(lock_file.name).basename.to_s | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
81 changes: 81 additions & 0 deletions
81
nuget/spec/dependabot/nuget/file_updater/lockfile_updater_spec.rb
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,81 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require "json" | ||
| require "spec_helper" | ||
| require "dependabot/dependency" | ||
| require "dependabot/dependency_file" | ||
| require "dependabot/nuget/file_updater/lockfile_updater" | ||
|
|
||
| RSpec.describe Dependabot::Nuget::FileUpdater::LockfileUpdater do | ||
| let(:updater) do | ||
| described_class.new( | ||
| dependency_files: dependency_files, | ||
| lock_file: lockfile, | ||
| credentials: [{ | ||
| "type" => "git_source", | ||
| "host" => "github.com" | ||
| }] | ||
| ) | ||
| end | ||
|
|
||
| let(:dependency_files) { [csproj, lockfile] } | ||
| let(:csproj) do | ||
| Dependabot::DependencyFile.new(name: "myproj.csproj", content: csproj_body) | ||
| end | ||
| let(:lockfile) do | ||
| Dependabot::DependencyFile.new(name: "packages.lock.json", content: lockfile_body) | ||
| end | ||
| let(:csproj_body) { fixture("csproj", csproj_fixture_name) } | ||
| let(:lockfile_body) { fixture("lockfiles", lockfile_fixture_name) } | ||
| let(:csproj_fixture_name) { "lockfiles_basic" } | ||
| let(:lockfile_fixture_name) { "lockfiles_basic" } | ||
| let(:tmp_path) { Dependabot::Utils::BUMP_TMP_DIR_PATH } | ||
|
|
||
| before { FileUtils.mkdir_p(tmp_path) } | ||
|
|
||
| describe "#updated_lockfile_content" do | ||
| subject(:updated_lockfile_content) { updater.updated_lockfile_content } | ||
|
|
||
| it "doesn't store the files permanently" do | ||
| expect { updated_lockfile_content }. | ||
| to_not(change { Dir.entries(tmp_path) }) | ||
| end | ||
|
|
||
| it { expect { updated_lockfile_content }.to_not output.to_stdout } | ||
|
|
||
| context "when updating the lockfile fails" do | ||
| let(:csproj_body) { fixture("csproj", csproj_fixture_name).gsub('Version="0.11.1"', 'Version="99.99.99"') } | ||
|
|
||
| it "raises a helpful error" do | ||
| expect { updater.updated_lockfile_content }. | ||
| to raise_error do |error| | ||
| expect(error). | ||
| to be_a(Dependabot::SharedHelpers::HelperSubprocessFailed) | ||
| expect(error.message).to include( | ||
| "Failed to restore /home/dependabot/nuget/dependabot_tmp_dir/myproj.csproj" | ||
| ) | ||
| expect(error.message).to include( | ||
| "error NU1102: Unable to find package Azure.Bicep.Core with version (>= 99.99.99)" | ||
| ) | ||
| end | ||
| end | ||
| end | ||
|
|
||
| describe "the updated lockfile" do | ||
| it "updates the dependency version in the lockfile" do | ||
| prev_dependency = JSON.parse(lockfile_body)["dependencies"]["net6.0"]["Azure.Bicep.Core"] | ||
| new_dependency = JSON.parse(updated_lockfile_content)["dependencies"]["net6.0"]["Azure.Bicep.Core"] | ||
|
|
||
| expect(prev_dependency["resolved"]).to eql("0.7.4") | ||
| expect(prev_dependency["contentHash"]).to eql( | ||
| "G9FJNOcZBc74IQe7Uars6SVM8Kvum/ZJp0eyZ8Q47fiEn5+aBTFf36NkRKkknzT2bXkGubmwNa1copSiDAdzVg==" | ||
| ) | ||
|
|
||
| expect(new_dependency["resolved"]).to eql("0.11.1") | ||
| expect(new_dependency["contentHash"]).to eql( | ||
| "S6NZBEy/D9UhN45XAiL8ZnUfzMLC/jTklcyd7/xizMhQYzMutcj6D9Dzseu2Svd4lgUFSelDHR7O62bn88niVw==" | ||
| ) | ||
| end | ||
| end | ||
| 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain this check?
It seems like there can be more than one lockfile (
packages.lock.json) per project?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general there's a lockfile for every project in a solution, and transitive dependencies are represented in each lockfile.
So for example, you could have a structure like the following:
If dependabot runs for the solution file (dirs.sln), and decides to bump nugetX, you would expect to see:
Here's a concrete example of what this looks like in my project: