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

[Bug Report] Getting "There were stale violations found, please run packwerk update-todo" even there is no stale violations #369

Open
kalys opened this issue Sep 15, 2023 · 7 comments
Labels
bug Something isn't working

Comments

@kalys
Copy link

kalys commented Sep 15, 2023

Description
I'm getting "There were stale violations found, please run packwerk update-todo" when I run check command againts some files.
For this you have to have one violation in several files.

For example:

# This file contains a list of dependencies that are not part of the long term plan for the
# 'packs/one' package.
# We should generally work to reduce this list over time.
#
# You can regenerate this file using the following command:
#
# bin/packwerk update-todo
---
".":
  "::ApplicationController":
    violations:
    - dependency
    files:
    - packs/one/bar.rb
    - packs/one/baz.rb

To Reproduce

  1. Create a dependency violation in the first file.
  2. Create the same dependency violation in another file within the same package.
  3. Run packwerk update-todo in order to generate package_todo.yml file.
  4. Run packwerk check path/to/the/first_file.rb.

Actual Behaviour

  • Command returns 1 status.
  • Command output contains info about stale violations.
📦 Finished in X.XX seconds

No offenses detected
There were stale violations found, please run `packwerk update-todo`

Expected Behaviour

  • Command returns 0 status
  • Command output has no info about stale violations

Version Information

  • Packwerk: 3.1.0
  • Ruby: 3.2.1

Additional Context
3.0.1 has no this problem

@kalys kalys added the bug Something isn't working label Sep 15, 2023
@rafaelfranca
Copy link
Member

What happen if you run update-todo?

@kalys
Copy link
Author

kalys commented Sep 19, 2023

@rafaelfranca I added package update-todo step to STR.

@krisleech
Copy link

I know this isn't much help, but we had this problem on CI for a day or so, but it just went away. Running update-todo did not help.

@jmcfarland-figma
Copy link

jmcfarland-figma commented Oct 10, 2024

We have this problem when running packwerk check FILE_NAME to check a single file. When compiling the list of old_violations inside the PackageTodo class it looks like Packwerk is not correctly limiting the scope to the file being checked.

Then when it gets to this line:

# lib/packwerk/package_todo.rb:134
new_entries_violation_types = new_entries.dig(package, constant_name, "violations")

There are no new entries for the package, and the logic assumes this means there are stale entries. This makes sense when checking an entire codebase, but not when checking a single file or a group of files.

Running packwerk update-todo doesn't help because there are no stale violations to fix, and packwerk update-todo raises an error if you supply a single file as an argument anyway.

@jmcfarland-figma
Copy link

@rafaelfranca I have a failing test for PackageTodo.

    test "#stale_violations? returns false when violations in package_todo.yml exist but aren't relevant to passed in files" do
      package = Package.new(name: "buyers", config: { "enforce_dependencies" => true })
      violated_reference = build_reference(
        destination_package: package,
        path: "some/other/file.rb",
        constant_name: "::Buyers::Document"
      )
      package_todo = PackageTodo.new(package, "test/fixtures/package_todo.yml")
      package_todo.add_entries(violated_reference, "some_checker_type")
      refute package_todo.stale_violations?(
        Set.new(["some/other/file.rb"])
      )
    end

I believe the issue comes down to deleted_files_for - it does new_entries - old_entries to look for references that are no longer valid, but this doesn't work in the case where you pass in a single file.

For reference, we do this as a pre-commit hook, we run packwerk check $(git diff --name-only) so that developers have a fast-fail way to know if their changes will break CI.

I'm not sure what the right fix is; happy to take some direction and try to put together a patch tho.

@jmcfarland-figma
Copy link

Okay, I actually don't like the failing test I shared above. It seems like the problem sits with the deleted_files_for - it's passed a Set of files with no context on whether or not the files are all the files for the project or the files passed in via the CLI.

The object that holds that information is the FilesForProcessing object that has files and files_specified?, but by the time PackageTodo calls deleted_files_for it doesn't have the FilesForProcessing references anymore, just FilesForProcessing#files which is the Set.

I'll try to cook up a PR that passes the FilesForProcessing object all the way through.

@jmcfarland-figma
Copy link

This is the shape of the diff I came up with. I need to clean up tests before I submit a PR

diff --git a/lib/packwerk/commands/check_command.rb b/lib/packwerk/commands/check_command.rb
index 43d9608..2c7b770 100644
--- a/lib/packwerk/commands/check_command.rb
+++ b/lib/packwerk/commands/check_command.rb
@@ -35,14 +35,14 @@ module Packwerk

         messages = [
           offenses_formatter.show_offenses(offense_collection.outstanding_offenses),
-          offenses_formatter.show_stale_violations(offense_collection, @files_for_processing.files),
+          offenses_formatter.show_stale_violations(offense_collection, @files_for_processing),
           offenses_formatter.show_strict_mode_violations(unlisted_strict_mode_violations),
         ]

         out.puts(messages.select(&:present?).join("\n") + "\n")

         offense_collection.outstanding_offenses.empty? &&
-          !offense_collection.stale_violations?(@files_for_processing.files) &&
+          !offense_collection.stale_violations?(@files_for_processing) &&
           unlisted_strict_mode_violations.empty?
       end

diff --git a/lib/packwerk/formatters/default_offenses_formatter.rb b/lib/packwerk/formatters/default_offenses_formatter.rb
index 6f12aba..9a683f1 100644
--- a/lib/packwerk/formatters/default_offenses_formatter.rb
+++ b/lib/packwerk/formatters/default_offenses_formatter.rb
@@ -20,9 +20,9 @@ module Packwerk
         EOS
       end

-      sig { override.params(offense_collection: OffenseCollection, file_set: T::Set[String]).returns(String) }
-      def show_stale_violations(offense_collection, file_set)
-        if offense_collection.stale_violations?(file_set)
+      sig { override.params(offense_collection: OffenseCollection, files_for_processing: FilesForProcessing).returns(String) }
+      def show_stale_violations(offense_collection, files_for_processing)
+        if offense_collection.stale_violations?(files_for_processing)
           "There were stale violations found, please run `packwerk update-todo`"
         else
           "No stale violations detected"
diff --git a/lib/packwerk/offense_collection.rb b/lib/packwerk/offense_collection.rb
index 1e6d638..27f4135 100644
--- a/lib/packwerk/offense_collection.rb
+++ b/lib/packwerk/offense_collection.rb
@@ -67,10 +67,10 @@ module Packwerk
       end
     end

-    sig { params(for_files: T::Set[String]).returns(T::Boolean) }
-    def stale_violations?(for_files)
+    sig { params(files_for_processing: FilesForProcessing).returns(T::Boolean) }
+    def stale_violations?(files_for_processing)
       @package_todos.values.any? do |package_todo|
-        package_todo.stale_violations?(for_files)
+        package_todo.stale_violations?(files_for_processing)
       end
     end

diff --git a/lib/packwerk/package_todo.rb b/lib/packwerk/package_todo.rb
index 4eecd24..f6eaf69 100644
--- a/lib/packwerk/package_todo.rb
+++ b/lib/packwerk/package_todo.rb
@@ -54,12 +54,16 @@ module Packwerk
       listed?(reference, violation_type: violation_type)
     end

-    sig { params(for_files: T::Set[String]).returns(T::Boolean) }
-    def stale_violations?(for_files)
+    sig { params(files_for_processing: FilesForProcessing).returns(T::Boolean) }
+    def stale_violations?(files_for_processing)
       prepare_entries_for_dump

+      for_files = files_for_processing.files
+
       old_entries.any? do |package, violations|
-        files = for_files + deleted_files_for(package)
+        # We don't try to detect deleted files when files were specified on the CLI because
+        # we don't know if the file was deleted or just not specified
+        files = files_for_processing.specified_files? ? for_files : for_files + deleted_files_for(package)
         violations_for_files = package_violations_for(violations, files: files)

jmcfarland-figma added a commit to jmcfarland-figma/packwerk that referenced this issue Oct 18, 2024
Because the PackageTodo class didn't know what files had been
supplied it would erroneously suspect files had been deleted
if you only passed a single file on the command line.

We do this in a pre-commit hook with `bundle exec packwerk check $(git diff --name-only)`
and it was causing issues; the tool would assert there were stale
todos when there are not.

This changes the method signature of OffenseFormatter.show_stale_violations to
accept a FilesForProcessing instance instead of Set[String] and
plubms the FilesForProcessing through to PackageTodo#stale_violations? so
that it can call #files_specified? on it. If there were files specified
in the command invokation it skips the #deleted_files_for call.

Happy to make any adjustments. This addresses Shopify#369
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

4 participants