Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
4. **This rule must ALWAYS be followed no matter what**
5. **`additional_data` is ONLY for unknown and dynamic fields that a user or integration might send. IF YOU KNOW THE NAME OF A FIELD, IT GOES IN THE SCHEMA. NEVER use `additional_data` for known fields.**
6. **NEVER run `git tag` without `-m` flag** - it opens an interactive editor and freezes. Always use: `git tag -a v0.1.7 -m "Release v0.1.7"`
7. **Filter failures must protect the value.** If a key or path filter raises and the error mode lets logging continue, filter the value. Apply size and depth limits before traversing values that will not be logged.

## Commands

Expand Down Expand Up @@ -36,6 +37,7 @@ All commands use [Task](https://taskfile.dev) - run `task --list` to see all ava
**Rails Integration Tests**: Located in `rails_test_app/templates/test/integration/`. The script automatically copies template files to the test app when you run `scripts/rails_tests.sh`.

**FORCE_RECREATE**: Do NOT use `FORCE_RECREATE=true` unless absolutely necessary. It deletes the entire test app and recreates from scratch, which is slow. Only needed when:

- Gemfile changes require reinstalling gems
- Something is fundamentally broken with the cached app
- Rails version changes (but the script auto-detects this anyway)
Expand Down Expand Up @@ -70,6 +72,7 @@ For template file changes (tests, rake tasks, etc.), just run `scripts/rails_tes

- Generate all (structs + catalog): `task generate`
- Generate Sorbet RBI files: `task ruby:tapioca`
- Verify committed Sorbet RBI files: `task ruby:tapioca:verify` (also part of `task ci`). Keep annotation updates separate from checks; downloading them while typechecking makes results depend on upstream changes and task timing.
- Generate spellcheck dictionary: `scripts/generate_lockfile_words.sh`
- Check generated files are up to date: `task ruby:check-generated`

Expand Down Expand Up @@ -168,6 +171,7 @@ You do not need to check if these are defined with `defined?` - they are guarant

What's left: splatting an Array or Hash whose size Sorbet doesn't know, a duck-typed call after `respond_to?` on
another library's object, a `Minitest::Mock`, and a test that passes a wrong type on purpose.

- Don't call `def` inside a method definition in tests (or anywhere). Use mocks or stubs.
- For modules included in other classes, use `requires_ancestor`
- Custom type overrides belong in `sorbet/rbi/overrides/`
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Fixed

- **Filtering**: Nested URL query parameters such as `password[]` and `token[value]` are filtered, including URL-encoded names and paths in Rails' `filter_parameters`.
- **Filtering**: Basic credentials are scrubbed even when their Base64 encoding is shorter than eight characters.
- **Filtering**: Symbols beyond the nesting limit are summarized instead of being written without scrubbing.
- **Filtering**: When a key or path matcher raises and error handling lets logging continue, its value is filtered. This also applies to Rails filter blocks.
- **Errors**: A failed filter or scrubber does not keep queuing or reporting errors when it fails again while formatting its own error line or report context.
- **Formatting**: Hash keys are normalized during filtering, so arrays, hashes and filtered values are no longer copied recursively before size limits are applied. Circular references are summarized at their first recurrence.
- **Docs**: Correct the supported security release series and contributor instructions for releases, generated log types and schema fields. Describe size limits as limits per value, not a guarantee on total formatting time. Ensure the site's integration tests run when a local Watchman installation is broken.
- **Development**: `task ci` verifies committed RBI files, matching GitHub CI, instead of downloading new annotations concurrently with typechecking.
- **Generation**: A failed formatter or Sorbet JSON export stops code generation instead of leaving incomplete artifacts with a successful exit status.

## [0.2.1] - 2026-10-18

### Fixed
Expand Down
49 changes: 27 additions & 22 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ However, some apps might skip certain parts of Rails and only require what they
- `defined?(::ActionMailer)`
- `defined?(::ActiveJob)`
- `defined?(::ActiveStorage)`
- `defined?(::ActiveRecord)`

And you always need to check for any third-party gems that are not part of Rails:

Expand All @@ -109,7 +110,8 @@ And you always need to check for any third-party gems that are not part of Rails
- Ensure all tests pass before submitting a pull request
- Regular tests: `scripts/test.rb`
- Rails integration tests: `scripts/rails_tests.sh`
- You can specify Rails version for integration tests: `RAILS_VERSION=7.1.3 scripts/rails_tests.sh`
- You can specify Rails version for integration tests: `RAILS_VERSION=7.2.3 scripts/rails_tests.sh`
- Run `task ci` before submitting changes, and `cd docs && pnpm check` for changes to the docs site.

## Working with Sorbet and Tapioca

Expand Down Expand Up @@ -152,7 +154,7 @@ And you always need to check for any third-party gems that are not part of Rails
When typing modules that are included in other classes (like concerns, helpers, etc.), use the following approach:

1. Create an RBI file in `sorbet/rbi/overrides/` that mirrors the module's path
2. Use `requires_ancestor` to specify that a class includes a module
2. Use `requires_ancestor` to specify the class or module that a consumer must inherit from or include
3. For class methods added via `extend`, declare them in a separate `ClassMethods` module

Example from `sorbet/rbi/overrides/log_struct/integrations/action_mailer/error_handling.rbi`:
Expand All @@ -162,7 +164,8 @@ Example from `sorbet/rbi/overrides/log_struct/integrations/action_mailer/error_h

# This tells Sorbet that any class including ErrorHandling will have these methods
module LogStruct::Integrations::ActionMailer::ErrorHandling
requires_ancestor { ActionMailer::Base }
extend T::Helpers
requires_ancestor { ::ActionMailer::Base }

# Instance methods available to including classes
def log_and_ignore_error; end
Expand All @@ -184,7 +187,8 @@ When using `ActiveSupport::Concern` with an `included` block, you need to use `T

```ruby
module MyModule
extend ActiveSupport::Concern
extend ::ActiveSupport::Concern
extend T::Helpers

requires_ancestor { ParentClass }

Expand Down Expand Up @@ -302,31 +306,37 @@ end

#### Common Sorbet Patterns

1. **Binding `self` in class methods**:
1. **Typing an instance yielded to a block**:

```ruby
class MyClass
extend T::Sig

class << self
extend T::Sig

sig { params(value: String).void }
def configure(value)
yield(new(value))
sig { params(value: String, blk: T.proc.params(instance: MyClass).void).void }
def configure(value, &blk)
blk.call(new(value))
end
end

sig { params(config: T.untyped).void }
sig { params(config: String).void }
def initialize(config)
@config = config
@config = T.let(config, String)
end

sig { returns(String) }
attr_reader :config
end

MyClass.configure("test") do |instance|
T.bind(self, MyClass)
# Now you can call MyClass instance methods
puts instance.config
end
```

Yielding an instance does not change `self`. Use the block parameter; reserve `T.bind` for DSLs that change the block's receiver.

2. **Typing procs and blocks**:

```ruby
Expand Down Expand Up @@ -364,17 +374,12 @@ Remember: Taking shortcuts with Sorbet defeats the purpose of having static type

## Releasing

1. Update the version number in `lib/log_struct/version.rb`
2. Update the `CHANGELOG.md` file
3. Create a git tag for the version
4. Push the tag to GitHub
5. Build and push the gem to RubyGems
1. Update the version in `lib/log_struct/version.rb` and the release entry in `CHANGELOG.md`.
2. Run `task ci`, commit the changes, and merge them into `main`.
3. From a clean `main` checkout matching `origin/main`, run `task gem:release`. It creates and pushes an annotated version tag.
4. Review and approve the `release` environment in the **Release Gem + Sync Terraform Provider** GitHub Actions workflow. It waits for CI on the tagged commit and publishes through RubyGems trusted publishing.

```bash
# Update version in lib/log_struct/version.rb first!
bundle exec rake build
gem push pkg/logstruct-x.y.z.gem
```
Use the workflow's `dry_run` option to validate the release without publishing. `task gem:build` builds a local package; do not push gems from a laptop. See [AGENTS.md](AGENTS.md#automated-releases-gem--provider) for the release workflow and provider synchronization.

## Documentation

Expand Down
Loading