### Setup Packwerk Development Dependencies Source: https://github.com/shopify/packwerk/blob/main/README.md Run this command to install dependencies for Packwerk development. ```bash bin/setup ``` -------------------------------- ### Package Manifest Example Source: https://context7.com/shopify/packwerk/llms.txt Example package.yml file for a component. Enforces layers and dependencies. ```yaml enforce_layers: true enforce_dependencies: true dependencies: - components/application ``` -------------------------------- ### Packwerk Configuration Example Source: https://context7.com/shopify/packwerk/llms.txt Example packwerk.yml configuration file. Specifies required extensions and package dependencies. ```yaml require: - ./lib/packwerk_extensions/layer_checker.rb ``` -------------------------------- ### Start Packwerk Console Source: https://github.com/shopify/packwerk/blob/main/README.md Open an interactive prompt for experimenting with Packwerk. ```bash bin/console ``` -------------------------------- ### Install Packwerk Gem Source: https://github.com/shopify/packwerk/blob/main/README.md Install the Packwerk gem using Bundler or directly. ```bash $ bundle install ``` ```bash $ gem install packwerk ``` -------------------------------- ### CI Integration Example for Packwerk Source: https://context7.com/shopify/packwerk/llms.txt GitHub Actions workflow configuration to automatically validate and check the Packwerk package system on push and pull requests. ```yaml # .github/workflows/packwerk.yml name: Packwerk on: [push, pull_request] jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: 3.2 bundler-cache: true - name: Validate package system run: bin/packwerk validate - name: Check for violations run: bin/packwerk check ``` -------------------------------- ### Packwerk Package YAML Example Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Example of a package.yml file showing how to define package metadata, including stewards and slack channels. This metadata is not validated by Packwerk. ```yaml # components/sales/package.yml metadata: stewards: - "@Shopify/sales" slack_channels: - "#sales" ``` -------------------------------- ### Convert inflections.yml to inflections.rb Source: https://github.com/shopify/packwerk/blob/main/UPGRADING.md Migrate custom inflections from `config/inflections.yml` to `config/initializers/inflections.rb` to align with Packwerk 2.0 changes. This example shows the conversion for acronyms, singular, irregular, and uncountable words. ```yaml config/inflections.yml ```yml # List your inflections in this file instead of `inflections.rb` # See steps to set up custom inflections: # https://github.com/Shopify/packwerk/blob/main/USAGE.md#Inflections acronym: - 'HTML' - 'API' singular: - ['oxen', 'oxen'] irregular: - ['person', 'people'] uncountable: - 'fish' - 'sheep' ``` ``` ```ruby config/initializers/inflections.rb ```ruby ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.acronym('HTML') inflect.acronym('API') inflect.singular('oxen', 'oxen') inflect.irregular('person', 'people') inflect.uncountable('fish') inflect.uncountable('sheep') end ``` ``` -------------------------------- ### Check for Dependency Violations Source: https://context7.com/shopify/packwerk/llms.txt Scans the codebase for dependency violations. Supports checking specific folders, packages, and controlling parallel processing. Includes an example of violation output. ```bash # Check entire codebase bin/packwerk check # Check specific folder (includes all subfolders) bin/packwerk check components/your_package # Check specific packages (excludes nested packages) bin/packwerk check --packages=components/sales,components/billing # Enable/disable parallel processing bin/packwerk check --parallel bin/packwerk check --no-parallel # Use custom offense formatter bin/packwerk check --offenses-formatter=my_custom_formatter ``` ```text # Example output: # components/merchant/app/jobs/order_job.rb:48:6 # Dependency violation: ::Payments::Gateway belongs to 'payments', # but 'merchant' does not specify a dependency on 'payments'. # # 1 offense detected ``` -------------------------------- ### Validate Packwerk Package System Source: https://context7.com/shopify/packwerk/llms.txt Checks the package system for valid definitions, autoload paths, and an acyclic dependency graph. Provides an example of error output for circular dependencies. ```bash # Validate entire application bin/packwerk validate ``` ```text # Example error output: # Expected the package dependency graph to be acyclic, but it contains: # - components/sales -> components/inventory -> components/sales ``` -------------------------------- ### Initialize Packwerk Configuration Source: https://github.com/shopify/packwerk/blob/main/README.md Run this command to generate the configuration files for Packwerk. ```bash bin/packwerk init ``` -------------------------------- ### Display Packwerk Version and Help Source: https://context7.com/shopify/packwerk/llms.txt Commands to show the current Packwerk version and a list of available commands. ```bash # Show packwerk version bin/packwerk version # Show available commands bin/packwerk help ``` -------------------------------- ### Springify Packwerk Binstub Source: https://context7.com/shopify/packwerk/llms.txt Bash command to springify the Packwerk binstub, enabling faster startup times. ```bash # Springify the packwerk binstub bin/spring binstub packwerk # Now packwerk will use Spring for faster startup bin/packwerk check components/sales ``` -------------------------------- ### Packwerk Configuration Loading Source: https://context7.com/shopify/packwerk/llms.txt Ruby code demonstrating how to load Packwerk configuration and access its properties programmatically. ```ruby # Load packwerk require "packwerk" # Load configuration from current directory configuration = Packwerk::Configuration.from_path(Dir.pwd) # Access configuration properties puts "Root path: #{configuration.root_path}" puts "Include patterns: #{configuration.include}" puts "Exclude patterns: #{configuration.exclude}" puts "Cache enabled: #{configuration.cache_enabled?}" puts "Parallel processing: #{configuration.parallel?}" # Load all packages package_set = Packwerk::PackageSet.load_all_from( configuration.root_path, package_pathspec: configuration.package_paths ) # Iterate over packages package_set.each do |package| puts "Package: #{package.name}" puts " Dependencies: #{package.dependencies.join(', ')}" puts " Enforces dependencies: #{package.enforce_dependencies?}" puts " Config: #{package.config}" end # Find package for a file path file_path = "components/payments/app/models/payment.rb" package = package_set.package_from_path(file_path) puts "#{file_path} belongs to package: #{package.name}" # Check if a package depends on another sales_package = package_set.fetch("components/sales") payments_package = package_set.fetch("components/payments") if sales_package&.dependency?(payments_package) puts "sales depends on payments" else puts "sales does not depend on payments" end ``` -------------------------------- ### Generate Packwerk Binstub Source: https://github.com/shopify/packwerk/blob/main/README.md Run this command to generate the binstub for Packwerk. ```bash bundle binstub packwerk ``` -------------------------------- ### Run Packwerk Tests Source: https://github.com/shopify/packwerk/blob/main/README.md Execute tests for Packwerk. ```bash rake test ``` -------------------------------- ### Spring Integration for Packwerk Source: https://context7.com/shopify/packwerk/llms.txt Configuration for integrating Spring with Packwerk to speed up development feedback cycles. ```ruby # config/spring.rb require "packwerk/spring_command" ``` -------------------------------- ### Configure Spring for Packwerk Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Add this line to your application's `config/spring.rb` file to ensure Spring is aware of Packwerk commands. ```ruby require 'packwerk/spring_command' ``` -------------------------------- ### Packwerk Configuration (`packwerk.yml`) Source: https://context7.com/shopify/packwerk/llms.txt Configuration options for Packwerk, including file inclusion/exclusion patterns, package path definitions, custom associations, and parallel processing settings. ```yaml # packwerk.yml # Glob patterns for files to analyze include: - "**/*.{rb,rake,erb}" # Glob patterns for files to exclude exclude: - "{bin,node_modules,script,tmp,vendor}/**/*" # Glob patterns to find package.yml files package_paths: "**/") # Or specify multiple patterns: # package_paths: # - "components/*/" # - "engines/*/" # Custom ActiveRecord associations to track custom_associations: - "cache_belongs_to" - "has_many_cached" # Paths to exclude from association inspection associations_exclude: - "test/**/*" - "spec/**/*" # Enable parallel processing (default: true) parallel: true # Enable file parsing cache (default: false) cache: true # Cache directory (default: tmp/cache/packwerk) cache_directory: "tmp/cache/packwerk" # Custom offenses formatter offenses_formatter: my_custom_formatter # Load custom extensions require: - ./lib/packwerk_extensions/custom_checker.rb - my_packwerk_extension_gem ``` -------------------------------- ### Update Packwerk Todo List Source: https://github.com/shopify/packwerk/blob/main/RESOLVING_VIOLATIONS.md Use this command to record new dependency violations in `package_todo.yml`. This is suitable for emergency fixes, system design improvements, making implicit code explicit, or temporary states. ```bash bin/packwerk update-todo ``` -------------------------------- ### Check for Dependency Violations Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Run `bin/packwerk check` to scan the entire codebase for new or stale dependency violations. This command can also be run against specific folders or packages for shorter execution times. ```bash bin/packwerk check ``` ```bash bin/packwerk check components/your_package ``` ```bash bin/packwerk check --packages=components/your_package,components/your_other_package ``` ```bash bin/packwerk check --[no-]parallel ``` -------------------------------- ### Configure Custom Offenses Formatter via Command Line Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Alternatively, specify the custom offenses formatter using the `--offenses-formatter` flag when running Packwerk commands. ```bash bin/packwerk check --offenses-formatter=my_offenses_formatter ``` -------------------------------- ### Load Extensions with Packwerk Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Specify Ruby files to be loaded with Packwerk using a `require` directive in `packwerk.yml`. Local files can be prefixed with a dot for relative paths, or absolute paths and gem names can be used. ```yaml require: - ./path/to/file.rb - my_gem ``` -------------------------------- ### Custom Offense Formatter Configuration Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Configure a custom offense formatter by specifying the `offenses_formatter:` key in `packwerk.yml` and ensuring the custom formatter class, which includes `Packwerk::OffensesFormatter`, is required. ```ruby class MyCustomOffensesFormatter include Packwerk::OffensesFormatter # ... implementation ... end ``` -------------------------------- ### Update Todo List with Violations Source: https://context7.com/shopify/packwerk/llms.txt Records existing dependency violations in `package_todo.yml` files. Useful for managing technical debt or after emergency fixes. ```bash # Record all existing violations bin/packwerk update-todo ``` -------------------------------- ### Validate Packwerk Dependencies Source: https://github.com/shopify/packwerk/blob/main/RESOLVING_VIOLATIONS.md Run this command to check for cyclic dependencies after updating `package.yml`. It's crucial for maintaining a healthy dependency graph. ```bash bin/packwerk validate ``` -------------------------------- ### Programmatic Packwerk Check Execution Source: https://context7.com/shopify/packwerk/llms.txt Ruby code for executing Packwerk commands like 'check' and 'validate' programmatically. Captures output using StringIO. ```ruby require "packwerk" require "stringio" # Capture output output = StringIO.new error_output = StringIO.new # Create CLI instance with custom output streams cli = Packwerk::Cli.new( out: output, err_out: error_output, style: Packwerk::OutputStyles::Plain.new ) # Run check command success = cli.execute_command(["check", "components/payments"]) puts "Check #{success ? 'passed' : 'failed'}" puts output.string # Run with specific packages success = cli.execute_command([ "check", "--packages=components/sales,components/billing" ]) # Run validation success = cli.execute_command(["validate"]) puts "Validation #{success ? 'passed' : 'failed'}" ``` -------------------------------- ### Package Todo File Structure Source: https://github.com/shopify/packwerk/blob/main/USAGE.md The `package_todo.yml` file lists constant violations within a package, including the violated constant, the type of violation, and the file containing the violation. This file should be worked off over time, not added to. ```yaml components/merchant: "::Checkouts::Core::CheckoutId": violations: - dependency files: - components/merchant/app/public/merchant/generate_order.rb ``` -------------------------------- ### Implement Custom Checker in Ruby Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Create a custom checker by including `Packwerk::Checker`. Implement `violation_type`, `strict_mode_violation?`, `invalid_reference?`, and `message` methods to define custom dependency analysis logic. ```ruby # ./path/to/file.rb class MyChecker include Packwerk::Checker # implement the `Checker` interface sig { override.returns(String) } def violation_type 'my_custom_violation_type' end sig { override.params(listed_offense: ReferenceOffense).returns(T::Boolean) } def strict_mode_violation?(listed_offense) # This will allow "strict mode" to be supported in your checker referencing_package = listed_offense.reference.package referencing_package.config["enforce_custom"] == "strict" end sig { override.params(reference: Reference).returns(T::Boolean) } def invalid_reference?(reference) # your logic here end sig { override.params(reference: Reference).returns(String) } def message(reference) # your message here end end ``` -------------------------------- ### Check Specific Folders with Packwerk Source: https://github.com/shopify/packwerk/blob/main/TROUBLESHOOT.md Use this command to run Packwerk checks on specific folders or packages for faster feedback. This is useful for iterative development and reducing CI feedback cycles. ```bash bin/packwerk check components/your_package ``` -------------------------------- ### Package Definition (`package.yml`) Source: https://context7.com/shopify/packwerk/llms.txt Defines a package within the Packwerk system. Specifies whether to enforce dependencies and lists the packages it depends on. ```yaml # components/payments/package.yml # Enforce that this package declares all its dependencies enforce_dependencies: true # List of packages this package depends on dependencies: - components/core - components/logging ``` -------------------------------- ### Configure Custom Offenses Formatter in packwerk.yml Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Set the `offenses_formatter` option in your `packwerk.yml` file to the identifier of your custom formatter class. ```yaml offenses_formatter: my_offenses_formatter ``` -------------------------------- ### Custom ERB Parser for Packwerk Source: https://context7.com/shopify/packwerk/llms.txt Implement a custom ERB parser to handle non-standard ERB tags like <%graphql or <%stimulus by commenting them out before Packwerk parses the buffer. Register the custom parser by setting `Packwerk::Parsers::Factory.instance.erb_parser_class`. ```ruby class CustomErbParser < Packwerk::Parsers::Erb def parse_buffer(buffer, file_path:) preprocessed_source = buffer.source # Comment out custom tags that packwerk doesn't understand # Example: <%graphql ... %> tags from graphql-client preprocessed_source = preprocessed_source.gsub(/<%graphql/, "<%#") # Handle other custom ERB tags preprocessed_source = preprocessed_source.gsub(/<%stimulus/, "<%#") preprocessed_buffer = Parser::Source::Buffer.new(file_path) preprocessed_buffer.source = preprocessed_source super(preprocessed_buffer, file_path: file_path) end end # Register the custom parser Packwerk::Parsers::Factory.instance.erb_parser_class = CustomErbParser ``` ```yaml require: - ./lib/packwerk_extensions/custom_erb_parser.rb ``` -------------------------------- ### Custom JSON Offenses Formatter for Packwerk Source: https://context7.com/shopify/packwerk/llms.txt Create a custom offenses formatter to output violations in JSON format. This includes handling regular offenses, stale violations, and strict mode violations. Register the custom formatter in `packwerk.yml`. ```ruby class JsonOffensesFormatter include Packwerk::OffensesFormatter def identifier "json" end def show_offenses(offenses) return '{"offenses": [], "count": 0}' if offenses.empty? offense_list = offenses.compact.map do |offense| { file: offense.file, message: offense.message, location: offense.location ? { line: offense.location.line, column: offense.location.column } : nil } end JSON.pretty_generate({ offenses: offense_list, count: offenses.length }) end def show_stale_violations(offense_collection, for_files) if offense_collection.stale_violations?(for_files) '{"stale_violations": true}' else '{"stale_violations": false}' end end def show_strict_mode_violations(strict_mode_violations) return "" if strict_mode_violations.empty? violations = strict_mode_violations.map do |offense| { package: offense.reference.package.name, violation_type: offense.violation_type } end JSON.pretty_generate({ strict_mode_violations: violations }) end end ``` ```yaml require: - ./lib/packwerk_extensions/json_formatter.rb offenses_formatter: json ``` -------------------------------- ### Custom ERB Parser for Packwerk Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Define a custom ERB parser to handle specific ERB tags like <%=graphql %> by commenting them out before Packwerk parses the buffer. This is useful for integrating with libraries like graphql-client. ```ruby class CustomParser < Packwerk::Parsers::Erb def parse_buffer(buffer, file_path:) preprocessed_source = buffer.source # Comment out <%graphql ... %> tags. They won't contain any object # references anyways. preprocessed_source = preprocessed_source.gsub(/<%graphql/, "<%#") preprocessed_buffer = Parser::Source::Buffer.new(file_path) preprocessed_buffer.source = preprocessed_source super(preprocessed_buffer, file_path: file_path) end end Packwerk::Parsers::Factory.instance.erb_parser_class = CustomParser ``` -------------------------------- ### Add Packwerk Gem to Gemfile Source: https://github.com/shopify/packwerk/blob/main/README.md Add this line to your application's Gemfile to include Packwerk. ```ruby gem 'packwerk' ``` -------------------------------- ### Implement Custom Offenses Formatter in Ruby Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Define a custom offenses formatter by including `Packwerk::OffensesFormatter` and implementing the `identifier` method. This formatter can then be configured in `packwerk.yml` or via the command line. ```ruby class MyOffensesFormatter include Packwerk::OffensesFormatter # implement the `OffensesFormatter` interface def identifier 'my_offenses_formatter' end end ``` -------------------------------- ### Implement Custom Validator in Ruby Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Define a custom validator by including `Packwerk::Validator`. Implement `permitted_keys` and `call` methods to enforce custom package validations. ```ruby # ./path/to/file.rb class MyValidator include Packwerk::Validator # implement the `Validator` interface sig { override.returns(T::Array[String]) } def permitted_keys ['enforce_my_custom_checker'] end sig { override.params(package_set: PackageSet, configuration: Configuration).returns(ApplicationValidator::Result) } def call(package_set, configuration) # your logic here end end ``` -------------------------------- ### Enforcing Dependencies in Packwerk Package YAML Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Configure a package to enforce its dependencies by setting `enforce_dependencies: true` and listing the required dependencies under the `dependencies:` key in the package.yml file. This prevents the package from referencing constants not defined within itself or its declared dependencies. ```yaml # components/shop_identity/package.yml enforce_dependencies: true dependencies: - components/platform ``` -------------------------------- ### Custom Metadata Validator Source: https://context7.com/shopify/packwerk/llms.txt Ruby code for a custom Packwerk validator that checks for a 'metadata' key in package manifests. Requires 'metadata.stewards' to be a non-empty array. ```ruby # lib/packwerk_extensions/metadata_validator.rb class MetadataValidator include Packwerk::Validator def permitted_keys ["metadata"] end def call(package_set, configuration) results = package_manifests(configuration).map do |manifest_path| config = YAML.load_file(manifest_path) || {} validate_metadata(manifest_path, config, configuration) end merge_results(results) end private def validate_metadata(manifest_path, config, configuration) metadata = config["metadata"] return Validator::Result.new(ok: true) if metadata.nil? errors = [] unless metadata.is_a?(Hash) errors << "metadata must be a hash in #{relative_path(configuration, manifest_path)}" end if metadata.is_a?(Hash) unless metadata["stewards"].is_a?(Array) && metadata["stewards"].any? errors << "metadata.stewards must be a non-empty array in #{relative_path(configuration, manifest_path)}" end end if errors.empty? Validator::Result.new(ok: true) else Validator::Result.new(ok: false, error_value: errors.join("\n")) end end end ``` -------------------------------- ### Enable Strict Mode in Packwerk Source: https://github.com/shopify/packwerk/blob/main/USAGE.md To prevent new violations, change `enforce_dependencies: true` to `enforce_dependencies: strict` in your `package.yml` file. New violations will then cause an error when running `bin/packwerk check`. ```yaml enforce_dependencies: strict ``` -------------------------------- ### Custom Layer Checker for Packwerk Source: https://context7.com/shopify/packwerk/llms.txt Implement a custom checker to enforce architectural layers, preventing higher layers from depending on lower layers. Define layers in a `LAYERS` hash and configure `enforce_layers: strict` in package configurations. ```ruby class LayerChecker include Packwerk::Checker LAYERS = { "components/presentation" => 1, "components/application" => 2, "components/domain" => 3, "components/infrastructure" => 4 }.freeze def violation_type "layer" end def strict_mode_violation?(offense) offense.reference.package.config["enforce_layers"] == "strict" end def invalid_reference?(reference) return false unless reference.package.config["enforce_layers"] source_layer = layer_for(reference.package.name) target_layer = layer_for(reference.constant.package.name) return false if source_layer.nil? || target_layer.nil? # Higher layers cannot depend on lower layers source_layer < target_layer end def message(reference) source_pkg = reference.package.name target_pkg = reference.constant.package.name <<~MSG Layer violation: #{reference.constant.name} belongs to '#{target_pkg}', but '#{source_pkg}' is in a higher architectural layer and cannot depend on lower layers. MSG end private def layer_for(package_name) LAYERS.find { |prefix, _| package_name.start_with?(prefix) }&.last end end ``` -------------------------------- ### Require Custom Checker in packwerk.yml Source: https://github.com/shopify/packwerk/blob/main/USAGE.md Inform Packwerk about your custom checker by adding its file path to the `require` directive in `packwerk.yml`. ```yaml require: - ./path/to/file.rb ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.