Skip to content

Related files are matched globally by unqualified model name, so models with the same basename in different packs overwrite each other's annotations (breaks --frozen) #367

Description

@tomyyn

Steps to reproduce the problem

In a Rails app organized with packwerk packs (packs/*), create two models
that share a file basename but map to different tables:

  1. Root model app/models/address.rb:

    class Address < ApplicationRecord # table: addresses
    end
  2. Pack model packs/contacts/app/public/models/address.rb:

    module Contacts
      class Address < ApplicationRecord # table: contact_addresses
        self.table_name = "contact_addresses"
      end
    end
  3. A test file packs/contacts/test/models/address_test.rb (any content).

  4. With the configuration below, run bin/annotaterb models twice, or
    bin/annotaterb models --frozen once.

Expected behavior

Each model annotates only its own related files, and --frozen passes when
the working tree is fully up to date.

Actual behavior

Both models claim the same related file. Each run, both write their own
schema into packs/contacts/test/models/address_test.rb — the root Address
writes the addresses schema, Contacts::Address writes contact_addresses
and the last writer wins. A plain run reports the same file annotated twice:

Annotating models
Annotated (2): packs/contacts/test/models/address_test.rb, packs/contacts/test/models/address_test.rb

And --frozen always fails on whichever model "lost", even though nothing is
stale:

AnnotateRb error. packs/contacts/test/models/address_test.rb needs to be updated, but annotaterb was run with `--frozen`.

This appears to come from related files being resolved purely by name, globbed
across the whole project (unlike model files themselves, which are annotated
file → class and are unambiguous):

  • ModelAnnotator::PatternGetter#get expands every pattern for every
    root_dir entry, not just the root the current model file lives under, so a
    root-level model also generates patterns like
    packs/*/test/models/%MODEL_NAME%_test.rb.
  • ModelAnnotator::FileNameResolver.call substitutes only name placeholders.
    Both models here resolve to address (both sit directly in a model_dir, so
    even %MODEL_NAME% carries no namespace), producing identical globs.
  • ModelAnnotator::RelatedFilesListBuilder#related_files_for_pattern then
    Dir.globs those patterns with no notion of where the model came from, so
    both models match the same existing test file.

The name-based matching implicitly assumes model basenames are unique across
the project — true for flat Rails apps, but not for apps organized into packs.

Your .annotaterb.yml configuration

:exclude_tests: false
:root_dir:
  - ''
  - packs/*
:model_dir:
  - app/models
  - packs/*/app/models
  - packs/*/app/public/models
:additional_file_patterns:
  - "packs/*/test/models/%MODEL_NAME_WITHOUT_NS%_test.rb"

(Reproduces with the built-in test/fixture patterns as well — the
additional_file_patterns entry is not required for the collision.)

Environment

  • AnnotateRb version: 4.22.0
  • Ruby version: 4.0.3
  • Ruby on Rails version: 8.1.3
  • Database adapter (e.g., sqlite3, pg, mysql2): pg
  • Database adapter version: 1.6.3

Issue title: Related files are matched globally by unqualified model name, so models with the same basename in different packs
overwrite each other's annotations (breaks --frozen)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions