Skip to content

Commit dd0e1a8

Browse files
committed
docs: add API Design page (typed vs. untyped); expand Terraform docs with SQL + delivery attempt recipes; update docs nav; release: add CHANGELOG entry for 0.0.2-rc1; ci: enforce CHANGELOG contains current version
1 parent 97cd6ae commit dd0e1a8

File tree

6 files changed

+188
-1
lines changed

6 files changed

+188
-1
lines changed

.github/workflows/test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ jobs:
5353
- name: CSpell (Spellcheck)
5454
run: scripts/spellcheck.sh
5555

56+
- name: Changelog has entry for current version
57+
run: ruby scripts/check_changelog_version.rb
58+
5659
- name: Prettier (Formatting)
5760
run: scripts/prettier.sh --check
5861

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Pushed empty gem to RubyGems to secure the name
13+
14+
## [0.0.2-rc1] - 2025-09-05
15+
16+
### Added
17+
18+
- Unified GitHub Actions workflow to release RubyGem and sync/tag the Terraform provider; dry-run support for safe validation
19+
- Provider catalog export script and automated provider CI (build/vet/test)
20+
- Terraform docs page with quickstart, recipes, and provider README link
21+
- API design doc (typed vs. untyped) planned; initial philosophy captured
22+
- Coverage threshold gate (>= 80%) in CI; additional tests to lift coverage
23+
- Release helper: scripts/create_release_tag.sh (creates annotated tag from version.rb)
24+
25+
### Changed
26+
27+
- ActionMailer callbacks patched for Rails 7.0; event logging + metadata collection tested
28+
- Provider CloudWatch filter builder refactored for testability; key lookups aligned with exported catalog (event/source)
29+
30+
### Fixed
31+
32+
- Pre-commit hooks reporting; cspell dictionary updated; TypeScript import fixes in site

scripts/check_changelog_version.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env ruby
2+
# typed: strict
3+
# frozen_string_literal: true
4+
5+
require "date"
6+
7+
version = File.read("lib/log_struct/version.rb")[/VERSION\s*=\s*"([^"]+)"/, 1]
8+
abort "Could not determine version from lib/log_struct/version.rb" unless version
9+
10+
changelog = begin
11+
File.read("CHANGELOG.md")
12+
rescue
13+
""
14+
end
15+
16+
# Accept headings like: ## [0.0.2-rc1] - 2025-09-05 or ## 0.0.2-rc1
17+
heading_regex = /^##\s*\[?#{Regexp.escape(version)}\]?/m
18+
19+
if changelog.match?(heading_regex)
20+
puts "CHANGELOG contains entry for #{version}"
21+
exit 0
22+
else
23+
warn "CHANGELOG is missing an entry for version #{version}."
24+
warn "Add a section like: \n\n## [#{version}] - #{Date.today}"
25+
exit 1
26+
end

site/app/docs/api-design/page.tsx

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { CodeBlock } from '@/components/code-block';
2+
import { EditPageLink } from '@/components/edit-page-link';
3+
4+
export const metadata = {
5+
title: 'API Design (Typed vs. Untyped)',
6+
description:
7+
'How LogStruct balances an idiomatic Rails logging API with an optional typed path for teams using sorbet-runtime.',
8+
};
9+
10+
export default function ApiDesignPage() {
11+
const untypedExample = `# Untyped, idiomatic Rails logging (works out of the box)
12+
Rails.logger.info({
13+
msg: "User signed in",
14+
user_id: current_user.id,
15+
feature: "onboarding"
16+
})`;
17+
18+
const typedExample = `# Optional typed API (sorbet-runtime)
19+
log = LogStruct::Log::Request.new(
20+
message: "GET /projects",
21+
event: LogStruct::Event::Request,
22+
source: LogStruct::Source::Rails,
23+
controller: "ProjectsController",
24+
action: "index"
25+
)
26+
27+
LogStruct.info(log)`;
28+
29+
const customStructSketch = `# Sketch: defining a custom typed log struct
30+
class MyApp::Logs::Checkout < T::Struct
31+
include LogStruct::Log::Interfaces::CommonFields
32+
include LogStruct::Log::Interfaces::AdditionalDataField
33+
34+
const :event, LogStruct::Event::Log
35+
const :source, LogStruct::Source, default: T.let(LogStruct::Source::App, LogStruct::Source)
36+
const :message, String
37+
const :cart_id, String
38+
const :amount_cents, Integer
39+
end
40+
41+
# Then log it with
42+
LogStruct.info(
43+
MyApp::Logs::Checkout.new(message: "checkout_completed", cart_id: cart.id, amount_cents: 1299)
44+
)`;
45+
46+
return (
47+
<div className="space-y-8">
48+
<h1 className="text-3xl font-bold">API Design: Typed vs. Untyped</h1>
49+
<p className="text-neutral-600 dark:text-neutral-400">
50+
LogStruct is designed for an idiomatic Rails experience first, with an
51+
optional typed path for teams that use sorbet-runtime. Most Rails
52+
developers can adopt LogStruct without learning Sorbet. Teams wanting
53+
stronger guarantees can progressively introduce typed log structs.
54+
</p>
55+
56+
<h2 className="text-2xl font-semibold">Untyped, Idiomatic Rails</h2>
57+
<p>
58+
You can continue using <code>Rails.logger</code> with hashes and
59+
strings. LogStruct&apos;s formatter scrubs sensitive values and keeps
60+
output JSON-friendly.
61+
</p>
62+
<CodeBlock language="ruby">{untypedExample}</CodeBlock>
63+
64+
<h2 className="text-2xl font-semibold">Optional Typed Path</h2>
65+
<p>
66+
For teams that want stricter contracts, use LogStruct&apos;s typed
67+
structs. These are runtime-checked via sorbet-runtime and integrate with
68+
our formatter seamlessly.
69+
</p>
70+
<CodeBlock language="ruby">{typedExample}</CodeBlock>
71+
72+
<h2 className="text-2xl font-semibold">
73+
Custom Typed Structures (Sketch)
74+
</h2>
75+
<p>
76+
You can define app-specific typed logs by composing LogStruct
77+
interfaces. Keep this ergonomic and discoverable; the untyped path
78+
remains first-class.
79+
</p>
80+
<CodeBlock language="ruby">{customStructSketch}</CodeBlock>
81+
82+
<EditPageLink path="app/docs/api-design/page.tsx" />
83+
</div>
84+
);
85+
}

site/app/docs/layout.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ export default function DocsLayout({
173173
},
174174
]}
175175
/>
176+
<DocNavItem
177+
href="/docs/api-design"
178+
title="API Design"
179+
active={pathname.startsWith('/docs/api-design')}
180+
/>
176181

177182
<DocNavItem
178183
href="/docs/comparison"

site/app/docs/terraform/page.tsx

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,42 @@ export default function TerraformDocsPage() {
3636
}
3737
}`;
3838

39+
const sqlCount = `data "logstruct_cloudwatch_filter" "sql_queries" {
40+
struct = "SQL"
41+
event = "database"
42+
}
43+
44+
resource "aws_cloudwatch_log_metric_filter" "sql_count" {
45+
name = "SQL Query Count"
46+
log_group_name = var.log_group.app
47+
pattern = data.logstruct_cloudwatch_filter.sql_queries.pattern
48+
49+
metric_transformation {
50+
name = "app_sql_query_count"
51+
namespace = var.namespace.logs
52+
value = "1"
53+
unit = "Count"
54+
}
55+
}`;
56+
57+
const mailDeliveryAttempt = `data "logstruct_cloudwatch_filter" "email_delivery_attempt" {
58+
struct = "ActionMailer"
59+
event = "delivery"
60+
}
61+
62+
resource "aws_cloudwatch_log_metric_filter" "email_delivery_attempt_count" {
63+
name = "Email Delivery Attempt Count"
64+
log_group_name = var.log_group.app
65+
pattern = data.logstruct_cloudwatch_filter.email_delivery_attempt.pattern
66+
67+
metric_transformation {
68+
name = "app_email_delivery_attempt_count"
69+
namespace = var.namespace.logs
70+
value = "1"
71+
unit = "Count"
72+
}
73+
}`;
74+
3975
return (
4076
<div className="space-y-10">
4177
<h1 className="text-3xl font-bold mb-2">Terraform Provider</h1>
@@ -154,8 +190,20 @@ resource "aws_cloudwatch_log_metric_filter" "goodjob_finish_count" {
154190
}`}
155191
</CodeBlock>
156192
</div>
193+
<div className="grid gap-6 md:grid-cols-2">
194+
<CodeBlock language="hcl" title="Count All SQL Queries">
195+
{sqlCount}
196+
</CodeBlock>
197+
<CodeBlock language="hcl" title="Count Email Delivery Attempts">
198+
{mailDeliveryAttempt}
199+
</CodeBlock>
200+
</div>
157201
<p className="text-neutral-600 dark:text-neutral-400">
158-
See the provider README for more examples and details.
202+
For delivery failures, use metric math to compare attempts vs.
203+
delivered counts. CloudWatch filter patterns do not support numeric
204+
comparisons, so slow-query thresholds are usually handled downstream
205+
(for example, count + percentiles in metrics/dashboards). See the
206+
provider README for more examples and details.
159207
</p>
160208
</section>
161209

0 commit comments

Comments
 (0)