studio-engine 0.32.3 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +97 -1
- data/README.md +29 -0
- data/app/views/layouts/_navbar.html.erb +7 -1
- data/app/views/studio/banners/_app_banner.html.erb +15 -2
- data/app/views/studio/banners/_stack.html.erb +102 -0
- data/lib/studio/engine.rb +39 -0
- data/lib/studio/log_rotation.rb +49 -0
- data/lib/studio/version.rb +1 -1
- data/lib/studio.rb +13 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9ed77550c057e9ec4e232cc40ea934ef4753cc1d73d30e98b52c0a56f7ad8c8d
|
|
4
|
+
data.tar.gz: 01b04c9da431ef4d43b75cf12533c3aece29c5557157af4c0f91eadf1fe9e2bb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a1b26944f9f08b070f00aec356cc2435e3e35096d1353c970dd797b486293e50827fa6e6a11b3bc67ea306ef505f740114640595a4db10da4e9883a71561633b
|
|
7
|
+
data.tar.gz: ce46eddd0627c5eae83daa9fc2eca518d74eae7500ce0e44a22faf016c0059d68b01f1d7b874e1f38d063918c460d36bf981d33b2a7f5dc3fe07ac6e0db85811
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,102 @@
|
|
|
2
2
|
|
|
3
3
|
The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) — `MAJOR.MINOR.PATCH`. Consumer Rails apps install the released RubyGems package with `gem "studio-engine", "~> 0.6"`; bumping the gem version and updating consumer lockfiles is a release.
|
|
4
4
|
|
|
5
|
+
## 0.33.0 — 2026-08-10
|
|
6
|
+
|
|
7
|
+
Two changes ship together in this release: local log files are now capped, and
|
|
8
|
+
the pinned bars above the navbar become a composable **stack** that sits beside
|
|
9
|
+
the navbar rather than inside it.
|
|
10
|
+
|
|
11
|
+
**Local log files are now capped — 16 MB development, 8 MB test.** New
|
|
12
|
+
`studio.logger` initializer. Nothing to install, run, or remember: it rides the
|
|
13
|
+
gem, so every app, every future app, every worktree, and a freshly rebuilt Mac
|
|
14
|
+
all get it by construction.
|
|
15
|
+
|
|
16
|
+
Rails' own default is the reason this was needed. `config.load_defaults "7.1"`
|
|
17
|
+
sets `log_file_size` to **100 MB** for development *and* test, and each keeps one
|
|
18
|
+
rotated sibling — up to ~400 MB of log per checkout, times every worktree on the
|
|
19
|
+
machine. The logs were never unbounded; the ceiling was just far too high for a
|
|
20
|
+
machine that carries many desks. New ceiling: ~48 MB per checkout.
|
|
21
|
+
|
|
22
|
+
**Production is untouched.** The cap applies only where `Rails.env.local?`, so
|
|
23
|
+
every `production.rb` that hands its stream to STDOUT keeps doing exactly that,
|
|
24
|
+
and a host that names its own `config.logger` is never overridden.
|
|
25
|
+
|
|
26
|
+
**Ordering is the load-bearing part of this change.** Rails' `:initialize_logger`
|
|
27
|
+
is a *bootstrap* initializer — it runs before every railtie and engine
|
|
28
|
+
initializer and does `Rails.logger ||= config.logger || <default>`. An engine
|
|
29
|
+
that assigns `app.config.logger` from an ordinary initializer is therefore a
|
|
30
|
+
silent no-op: Rails has already built the logger. So `studio.logger` declares
|
|
31
|
+
`before: :initialize_logger` and sets `config.log_file_size`, the knob Rails
|
|
32
|
+
itself reads one moment later, leaving Rails owning the path, formatter, level,
|
|
33
|
+
and tagging. `after: :load_environment_hook` pins the other edge of the window,
|
|
34
|
+
which also keeps Railtie's implicit initializer chaining from dragging
|
|
35
|
+
`studio.assets` forward.
|
|
36
|
+
|
|
37
|
+
`test/integration/log_rotation_test.rb` asserts the **behavior**, not the
|
|
38
|
+
config: it boots real Rails apps and checks that the log file actually rotates
|
|
39
|
+
on disk once it passes the cap. It carries a mutation control — the same 17 MB
|
|
40
|
+
log with the cap switched off must *not* rotate — so the suite cannot be
|
|
41
|
+
satisfied by Rails' 100 MB default. Dropping the `before:` ordering reds it.
|
|
42
|
+
|
|
43
|
+
**New setting — `Studio.local_log_max_bytes`.** `false` opts out; an Integer
|
|
44
|
+
sets your own cap. Unlike every other `Studio.*` setting, it must be set in
|
|
45
|
+
`config/application.rb` (after `require "studio"`) or `config/environments/*.rb`
|
|
46
|
+
— `config/initializers/studio.rb` loads too late to be read.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
**The environment banner is now the navbar's SIBLING, not its child, and the
|
|
51
|
+
navbar sizes itself to whatever bars are actually there.** Nesting the banner
|
|
52
|
+
inside the navbar coupled two unrelated components: every new bar meant editing
|
|
53
|
+
the navbar, and there is already a second bar (impersonation), so this was never
|
|
54
|
+
a 0-or-1 problem.
|
|
55
|
+
|
|
56
|
+
Render the stack immediately before the navbar:
|
|
57
|
+
|
|
58
|
+
```erb
|
|
59
|
+
<%= render "studio/banners/stack" %>
|
|
60
|
+
<%= render "layouts/navbar" %>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The bars do not know the navbar exists; the navbar does not know which bars
|
|
64
|
+
rendered — only how tall they are, via `--studio-bars-h`.
|
|
65
|
+
|
|
66
|
+
**The height is MEASURED, not assumed.** A `ResizeObserver` on the stack
|
|
67
|
+
publishes its real height, so a bar that needs to be taller, a second bar, or one
|
|
68
|
+
that wraps on a narrow screen all work with **no change to any consuming app**.
|
|
69
|
+
A server-rendered estimate paints first so the common case never flashes, then the
|
|
70
|
+
observer replaces it with the truth. Verified in a real browser: growing the bar
|
|
71
|
+
32px moved the stack, the published property and the header's `top` together, on
|
|
72
|
+
the same frame.
|
|
73
|
+
|
|
74
|
+
**Backward-compatible.** An app that renders no stack gets
|
|
75
|
+
`var(--studio-bars-h, 0px)` — identical to the old `top-0`. Adoption is opt-in.
|
|
76
|
+
|
|
77
|
+
### Added
|
|
78
|
+
|
|
79
|
+
- **`studio/banners/stack`** — renders whichever bars apply (environment via
|
|
80
|
+
`Studio.show_environment_banner?`, impersonation when the host passes
|
|
81
|
+
`impersonated_user` / `admin_user` / `stop_path`) and publishes their measured
|
|
82
|
+
height. Takes `preview:` and forwards `devnet:` / `extra:` to the environment bar.
|
|
83
|
+
A preview render emits nothing at all, so a navbar-preview copy cannot duplicate
|
|
84
|
+
live chrome or publish a height.
|
|
85
|
+
- **`Studio.local_log_max_bytes`** — the opt-out (`false`) or override (an
|
|
86
|
+
Integer) for the new local log cap. Set it in `config/application.rb` or
|
|
87
|
+
`config/environments/*.rb`, never `config/initializers/studio.rb`.
|
|
88
|
+
|
|
89
|
+
### Changed
|
|
90
|
+
|
|
91
|
+
- **The navbar's sticky `top` reads `--studio-bars-h`** instead of hardcoding
|
|
92
|
+
`top-0`. It never branches on which bars rendered.
|
|
93
|
+
- **Banners are branded off the app's own theme tokens** — the environment bar
|
|
94
|
+
derives from `--color-warning` and impersonation from `--color-danger`, each
|
|
95
|
+
under a translucent gradient wash, replacing hardcoded hex stops. An app that
|
|
96
|
+
retunes its theme now gets a banner that follows it.
|
|
97
|
+
- **Local `development` and `test` logs rotate at 16 MB / 8 MB** via the new
|
|
98
|
+
`studio.logger` initializer, down from Rails' 100 MB default. Production is
|
|
99
|
+
untouched.
|
|
100
|
+
|
|
5
101
|
## 0.32.3 — 2026-08-09
|
|
6
102
|
|
|
7
103
|
**New public CSS custom property: `--nav-bottom`.** `_head.html.erb` now
|
|
@@ -48,7 +144,7 @@ rendered the desktop trigger on mobile beside the real one. Callers now own
|
|
|
48
144
|
the display class (bare renders default to `inline-flex`). Regression view
|
|
49
145
|
tests pin both.
|
|
50
146
|
|
|
51
|
-
## 0.32.1 —
|
|
147
|
+
## 0.32.1 — 2026-08-09
|
|
52
148
|
|
|
53
149
|
**Setup-guide corrections.** Docs only — no code, no behavior change.
|
|
54
150
|
|
data/README.md
CHANGED
|
@@ -54,6 +54,35 @@ Studio.configure do |config|
|
|
|
54
54
|
end
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
### Local log rotation (automatic — nothing to configure)
|
|
58
|
+
|
|
59
|
+
The engine caps the host app's **development** log at 16 MB and its **test** log
|
|
60
|
+
at 8 MB, keeping one rotated sibling each. There is nothing to install, run, or
|
|
61
|
+
remember: it rides the gem, so every checkout and every worktree is born with it.
|
|
62
|
+
|
|
63
|
+
It exists because Rails' own default is far too generous for a machine that
|
|
64
|
+
carries many worktrees. `config.load_defaults "7.1"` sets `log_file_size` to
|
|
65
|
+
**100 MB** for development *and* test, and each keeps a rotated sibling — up to
|
|
66
|
+
~400 MB of log per checkout.
|
|
67
|
+
|
|
68
|
+
**Production is untouched.** The cap applies only where `Rails.env.local?`, so
|
|
69
|
+
apps that hand their stream to STDOUT for the platform keep doing exactly that.
|
|
70
|
+
A host that names its own `config.logger` is never overridden.
|
|
71
|
+
|
|
72
|
+
To choose your own cap, or to opt out:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
# config/application.rb (after `require "studio"`) or config/environments/development.rb
|
|
76
|
+
Studio.local_log_max_bytes = 64.megabytes # your own cap
|
|
77
|
+
Studio.local_log_max_bytes = false # opt out; Rails' 100 MB default returns
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**This one setting cannot go in `config/initializers/studio.rb`.** It is read
|
|
81
|
+
during boot — Rails builds the logger in a bootstrap initializer, long before
|
|
82
|
+
`config/initializers` is loaded — so an initializer would be too late and would
|
|
83
|
+
silently do nothing. Every *other* `Studio.*` setting belongs in the initializer
|
|
84
|
+
as usual.
|
|
85
|
+
|
|
57
86
|
Transactional mail transport is shared through `Studio::MailTransport`:
|
|
58
87
|
|
|
59
88
|
```ruby
|
|
@@ -38,7 +38,13 @@
|
|
|
38
38
|
%>
|
|
39
39
|
|
|
40
40
|
<header x-data="{ scrolled: false }" <%= '@scroll.window="scrolled = scrolled ? (window.scrollY > 5) : (window.scrollY > 60)"'.html_safe unless is_preview %>
|
|
41
|
-
|
|
41
|
+
<%# top offsets by whatever studio/banners/_stack rendered above us. The
|
|
42
|
+
stack publishes --studio-bars-h on :root; with no stack the fallback
|
|
43
|
+
0px makes this identical to the old `top-0`, so an app that has not
|
|
44
|
+
adopted the stack is unaffected. The navbar never learns WHICH bars
|
|
45
|
+
rendered — only how tall they are. %>
|
|
46
|
+
style="<%= "top:var(--studio-bars-h, 0px);" unless is_preview %>"
|
|
47
|
+
class="<%= is_preview ? 'bg-page' : "#{'vt-pinned-header ' if pin_header}sticky z-50 bg-page transition-shadow duration-300" %>"
|
|
42
48
|
:class="scrolled && 'shadow-lg border-b border-subtle is-scrolled'">
|
|
43
49
|
<style>
|
|
44
50
|
.user-nav-col { width: 14rem; }
|
|
@@ -4,19 +4,32 @@
|
|
|
4
4
|
message = local_assigns.fetch(:message)
|
|
5
5
|
actions = local_assigns[:actions]
|
|
6
6
|
|
|
7
|
+
# Branded off the app's OWN theme tokens rather than hardcoded hexes, so a host
|
|
8
|
+
# that retunes --color-warning / --color-danger gets a banner that matches it.
|
|
9
|
+
# The gradient is a translucent black wash over the token — deliberately not
|
|
10
|
+
# color-mix(), which would need a modern-browser floor for pure decoration, and
|
|
11
|
+
# deliberately not two hardcoded stops, which cannot follow a themed colour.
|
|
7
12
|
tones = {
|
|
8
13
|
environment: {
|
|
9
|
-
background: "linear-gradient(90deg,
|
|
14
|
+
background: "linear-gradient(90deg, rgba(0,0,0,0.28) 0%, rgba(0,0,0,0.04) 100%), " \
|
|
15
|
+
"var(--color-warning, #FF7C47)",
|
|
10
16
|
color: "#ffffff",
|
|
11
17
|
shadow: "0 2px 8px rgba(0,0,0,0.25)"
|
|
12
18
|
},
|
|
13
19
|
impersonation: {
|
|
14
|
-
background: "linear-gradient(90deg,
|
|
20
|
+
background: "linear-gradient(90deg, rgba(0,0,0,0.30) 0%, rgba(0,0,0,0.06) 100%), " \
|
|
21
|
+
"var(--color-danger, #EF4444)",
|
|
15
22
|
color: "#ffffff",
|
|
16
23
|
shadow: "0 2px 8px rgba(0,0,0,0.25)"
|
|
17
24
|
}
|
|
18
25
|
}
|
|
19
26
|
|
|
27
|
+
# INTRINSIC height — content decides, and studio/banners/_stack MEASURES the
|
|
28
|
+
# result. An earlier cut declared a fixed height so the stack could compute its
|
|
29
|
+
# total server-side; that was rejected deliberately, because it cannot express
|
|
30
|
+
# a bar that needs to be taller, or two bars stacked, without every consuming
|
|
31
|
+
# app being reworked. The apps should respond to what the bar needs, not the
|
|
32
|
+
# other way round.
|
|
20
33
|
density_styles = {
|
|
21
34
|
compact: "font-size:12px; font-weight:700; padding:2px 12px;",
|
|
22
35
|
normal: "font-size:14px; font-weight:700; padding:8px 16px;"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
The bar stack — every pinned bar that sits ABOVE the navbar, as the navbar's
|
|
3
|
+
SIBLING rather than nested inside it.
|
|
4
|
+
|
|
5
|
+
Render it immediately before the navbar — the stack partial first, the navbar
|
|
6
|
+
partial second, as siblings. (Deliberately not written as ERB tags here: an
|
|
7
|
+
escaped tag inside an ERB comment ends the comment at its own closing marker
|
|
8
|
+
and dumps the rest of this block onto the page as text.)
|
|
9
|
+
|
|
10
|
+
Why a sibling and not nested (operator call, 2026-08-10): it composes. There is
|
|
11
|
+
already a second bar (impersonation), so this is not a 0-or-1 problem — it is
|
|
12
|
+
0, 1 or 2, and growing. Nesting each new bar inside the navbar coupled two
|
|
13
|
+
unrelated components and meant every new bar edited the navbar. Here the bars
|
|
14
|
+
do not know the navbar exists, and the navbar does not know which bars
|
|
15
|
+
rendered — only how tall they are, via --studio-bars-h.
|
|
16
|
+
|
|
17
|
+
HOW THE HEIGHT IS KNOWN. The stack MEASURES itself and publishes the result, so
|
|
18
|
+
a bar that needs to be taller — or a second bar, or a third — just works, and no
|
|
19
|
+
consuming app is reworked to allow it. The apps respond to what the bars need.
|
|
20
|
+
|
|
21
|
+
A server-rendered estimate (count × --studio-bar-unit) paints first so the
|
|
22
|
+
common case has no flash, then a ResizeObserver replaces it with the truth on
|
|
23
|
+
the same frame. An earlier cut declared a fixed bar height to avoid the
|
|
24
|
+
observer; that was rejected because it could only ever express the standard
|
|
25
|
+
case, which is the case that needed no help.
|
|
26
|
+
|
|
27
|
+
The property is published on :root from an inline <style>, deliberately. A
|
|
28
|
+
custom property set on this element would be invisible to the navbar, because
|
|
29
|
+
custom properties inherit DOWN, not ACROSS to siblings — and putting it on
|
|
30
|
+
<body> would need every host to change its layout.
|
|
31
|
+
|
|
32
|
+
Locals (all optional):
|
|
33
|
+
preview — true inside a navbar-preview render; renders nothing, so a
|
|
34
|
+
preview copy cannot duplicate live chrome or publish a height.
|
|
35
|
+
devnet — forwarded to the environment bar (chip / message segment).
|
|
36
|
+
extra — forwarded to the environment bar (message segments).
|
|
37
|
+
impersonated_user / admin_user / stop_path — render the impersonation bar
|
|
38
|
+
too; omit them and it is skipped.
|
|
39
|
+
%>
|
|
40
|
+
<%
|
|
41
|
+
preview = local_assigns.fetch(:preview, false)
|
|
42
|
+
|
|
43
|
+
impersonated_user = local_assigns[:impersonated_user]
|
|
44
|
+
admin_user = local_assigns[:admin_user]
|
|
45
|
+
stop_path = local_assigns[:stop_path]
|
|
46
|
+
show_impersonation = !preview && impersonated_user && admin_user && stop_path
|
|
47
|
+
|
|
48
|
+
show_environment = !preview && Studio.show_environment_banner?
|
|
49
|
+
|
|
50
|
+
bars = capture do
|
|
51
|
+
if show_environment
|
|
52
|
+
concat(render("studio/banners/environment",
|
|
53
|
+
devnet: local_assigns.fetch(:devnet, false),
|
|
54
|
+
extra: local_assigns.fetch(:extra, [])))
|
|
55
|
+
end
|
|
56
|
+
if show_impersonation
|
|
57
|
+
concat(render("studio/banners/impersonation",
|
|
58
|
+
impersonated_user: impersonated_user,
|
|
59
|
+
admin_user: admin_user,
|
|
60
|
+
stop_path: stop_path))
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# count { } and not count(true): show_impersonation is a truthy USER, not the
|
|
65
|
+
# literal true, and count(true) compares with ==. That published a one-bar
|
|
66
|
+
# height for a two-bar stack — the navbar would have sat under a bar.
|
|
67
|
+
bar_count = [show_environment, show_impersonation].count { |bar| bar }
|
|
68
|
+
%>
|
|
69
|
+
<% if bar_count.positive? %>
|
|
70
|
+
<%# First-paint estimate only — the observer below replaces it with the measured
|
|
71
|
+
height. Expressed against a token so an app that retunes the unit still gets
|
|
72
|
+
a sensible pre-measurement value. %>
|
|
73
|
+
<%= tag.style safe_join([":root{--studio-bars-h:calc(#{bar_count} * var(--studio-bar-unit, 47px))}"]) %>
|
|
74
|
+
<div class="studio-bar-stack sticky top-0 z-[60] w-full" data-studio-bar-stack><%= bars %></div>
|
|
75
|
+
<script>
|
|
76
|
+
// Publishes the stack's REAL height, so the navbar offsets by what the bars
|
|
77
|
+
// actually need. Idempotent and re-bound on Turbo navigation — a stale
|
|
78
|
+
// observer pointed at a detached node silently stops updating, and the
|
|
79
|
+
// navbar would then sit at yesterday's offset.
|
|
80
|
+
(function () {
|
|
81
|
+
var apply = function () {
|
|
82
|
+
var el = document.querySelector("[data-studio-bar-stack]");
|
|
83
|
+
if (!el) {
|
|
84
|
+
document.documentElement.style.removeProperty("--studio-bars-h");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (window.__studioBarObserver) window.__studioBarObserver.disconnect();
|
|
88
|
+
var publish = function () {
|
|
89
|
+
var h = Math.round(el.getBoundingClientRect().height);
|
|
90
|
+
if (h > 0) document.documentElement.style.setProperty("--studio-bars-h", h + "px");
|
|
91
|
+
};
|
|
92
|
+
publish();
|
|
93
|
+
if (window.ResizeObserver) {
|
|
94
|
+
window.__studioBarObserver = new ResizeObserver(publish);
|
|
95
|
+
window.__studioBarObserver.observe(el);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
apply();
|
|
99
|
+
document.addEventListener("turbo:load", apply);
|
|
100
|
+
})();
|
|
101
|
+
</script>
|
|
102
|
+
<% end %>
|
data/lib/studio/engine.rb
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
|
+
require_relative "log_rotation"
|
|
2
|
+
|
|
1
3
|
module Studio
|
|
2
4
|
class Engine < ::Rails::Engine
|
|
5
|
+
# Cap the local (development + test) log files, for every host app and
|
|
6
|
+
# every future one, with nobody running anything.
|
|
7
|
+
#
|
|
8
|
+
# ORDERING IS THE WHOLE TRICK — do not demote this to a bare initializer.
|
|
9
|
+
# Rails' `:initialize_logger` is a BOOTSTRAP initializer, so it runs before
|
|
10
|
+
# every railtie/engine initializer and does `Rails.logger ||= config.logger
|
|
11
|
+
# || <default>`. An engine that assigns `app.config.logger` from an ordinary
|
|
12
|
+
# initializer is a silent NO-OP: Rails.logger is already built. Verified,
|
|
13
|
+
# not assumed — a probe boot kept Rails' 100 MB cap and its own file.
|
|
14
|
+
# So this hands Rails the size BEFORE Rails builds the logger, which is
|
|
15
|
+
# exactly the knob `:initialize_logger` reads:
|
|
16
|
+
# ActiveSupport::Logger.new(config.default_log_file, 1, config.log_file_size)
|
|
17
|
+
# and Rails keeps ownership of the path, formatter, level, and tagging.
|
|
18
|
+
#
|
|
19
|
+
# `after: :load_environment_hook` pins the lower edge of that window for two
|
|
20
|
+
# reasons: config/environments/*.rb is loaded by `:load_environment_config`
|
|
21
|
+
# (declared `before: :load_environment_hook`), so the host's own choices are
|
|
22
|
+
# already visible here; and it keeps Railtie's implicit `after: <previous
|
|
23
|
+
# initializer>` chaining from dragging `studio.assets` forward with us.
|
|
24
|
+
# WHICH cap (and whether to touch anything at all) is Studio::LogRotation's
|
|
25
|
+
# decision — Rails-free, and unit-tested a branch at a time.
|
|
26
|
+
#
|
|
27
|
+
# The host's escape hatch, `Studio.local_log_max_bytes`, is read here rather
|
|
28
|
+
# than from config.x.<key>: an unset config.x key returns an empty
|
|
29
|
+
# ActiveSupport::OrderedOptions, NOT nil, so `config.x.foo || default`
|
|
30
|
+
# silently assigns the OrderedOptions and rotation then dies inside a
|
|
31
|
+
# rescued comparison. The behavioral test caught that; a config read-back
|
|
32
|
+
# would have called it green.
|
|
33
|
+
initializer "studio.logger", before: :initialize_logger, after: :load_environment_hook do |app|
|
|
34
|
+
cap = Studio::LogRotation.cap_for(
|
|
35
|
+
env: Rails.env,
|
|
36
|
+
host_logger: app.config.logger,
|
|
37
|
+
override: Studio.local_log_max_bytes
|
|
38
|
+
)
|
|
39
|
+
app.config.log_file_size = cap if cap
|
|
40
|
+
end
|
|
41
|
+
|
|
3
42
|
initializer "studio.assets" do |app|
|
|
4
43
|
app.config.assets.precompile += %w[
|
|
5
44
|
studio/sticky_table_header.css
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Studio
|
|
4
|
+
# The decision behind the engine's `studio.logger` initializer: given the
|
|
5
|
+
# environment and the host's own choices, how large may the local log file
|
|
6
|
+
# grow before it rotates — or should the engine keep its hands off entirely?
|
|
7
|
+
#
|
|
8
|
+
# Deliberately Rails-free so the whole decision table is unit-testable in
|
|
9
|
+
# milliseconds. The initializer in Studio::Engine owns the other half of the
|
|
10
|
+
# problem — running EARLY enough for the answer to matter — and that half is
|
|
11
|
+
# covered by test/integration/log_rotation_test.rb, which boots real apps and
|
|
12
|
+
# watches the file rotate on disk.
|
|
13
|
+
module LogRotation
|
|
14
|
+
# Deliberate values, not defaults. Rails' own `config.load_defaults "7.1"`
|
|
15
|
+
# sets log_file_size to 100 MB for development AND test, each keeping one
|
|
16
|
+
# rotated sibling — up to ~400 MB of log per checkout, times every worktree
|
|
17
|
+
# on the machine. 16 MB still holds a full day of local request logs; 8 MB
|
|
18
|
+
# covers a whole test run. Raising these is a decision, not a default.
|
|
19
|
+
DEVELOPMENT_MAX_BYTES = 16 * 1024 * 1024
|
|
20
|
+
TEST_MAX_BYTES = 8 * 1024 * 1024
|
|
21
|
+
|
|
22
|
+
# The environments whose logs are plain local files worth capping. Held here
|
|
23
|
+
# rather than delegating to Rails.env.local? so that a future Rails adding a
|
|
24
|
+
# third "local" environment cannot silently change where this engine writes
|
|
25
|
+
# a cap.
|
|
26
|
+
CAPPED_ENVIRONMENTS = %w[development test].freeze
|
|
27
|
+
|
|
28
|
+
# Returns the byte cap to apply, or nil for "leave the host's logging
|
|
29
|
+
# exactly as it is".
|
|
30
|
+
#
|
|
31
|
+
# env the Rails environment name
|
|
32
|
+
# host_logger the host's own config.logger, if it named one
|
|
33
|
+
# override Studio.local_log_max_bytes — false opts out, an Integer
|
|
34
|
+
# sets the host's own cap, nil means "use ours"
|
|
35
|
+
def self.cap_for(env:, host_logger: nil, override: nil)
|
|
36
|
+
# Production (and QA, which boots as production) hands its stream to the
|
|
37
|
+
# platform. Never point that at a file.
|
|
38
|
+
return nil unless CAPPED_ENVIRONMENTS.include?(env.to_s)
|
|
39
|
+
# A host that named its own logger has already decided.
|
|
40
|
+
return nil if host_logger
|
|
41
|
+
# Explicit opt-out; Rails' own default returns.
|
|
42
|
+
return nil if override == false
|
|
43
|
+
# Explicit host cap.
|
|
44
|
+
return override if override
|
|
45
|
+
|
|
46
|
+
env.to_s == "test" ? TEST_MAX_BYTES : DEVELOPMENT_MAX_BYTES
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/studio/version.rb
CHANGED
data/lib/studio.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "studio/version"
|
|
2
|
+
require "studio/log_rotation"
|
|
2
3
|
require "studio/engine"
|
|
3
4
|
require "studio/color_scale"
|
|
4
5
|
require "studio/environment_banner"
|
|
@@ -136,6 +137,18 @@ module Studio
|
|
|
136
137
|
mattr_accessor :impersonation_started_at_session_key, default: :impersonation_started_at
|
|
137
138
|
mattr_accessor :impersonation_max_minutes, default: 30
|
|
138
139
|
|
|
140
|
+
# Cap for the host app's local (development + test) log file, in bytes.
|
|
141
|
+
# nil means "use the engine's defaults" — Studio::Engine::DEVELOPMENT_LOG_MAX_BYTES
|
|
142
|
+
# and ::TEST_LOG_MAX_BYTES. An Integer sets your own cap; `false` opts out and
|
|
143
|
+
# leaves Rails' own 100 MB default alone.
|
|
144
|
+
#
|
|
145
|
+
# UNLIKE every other setting here, this one is read during BOOT — before
|
|
146
|
+
# config/initializers/studio.rb is loaded — so it must be set earlier than the
|
|
147
|
+
# usual seam: in config/application.rb (after `require "studio"`) or in
|
|
148
|
+
# config/environments/development.rb. Setting it in an initializer is too late
|
|
149
|
+
# and does nothing.
|
|
150
|
+
mattr_accessor :local_log_max_bytes, default: nil
|
|
151
|
+
|
|
139
152
|
# Default From: for engine-sent mail (magic links). Apps set this to their
|
|
140
153
|
# verified sending address in config/initializers/studio.rb.
|
|
141
154
|
mattr_accessor :mailer_from, default: nil
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: studio-engine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.33.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
@@ -266,6 +266,7 @@ files:
|
|
|
266
266
|
- app/views/studio/banners/_email_status_button.html.erb
|
|
267
267
|
- app/views/studio/banners/_environment.html.erb
|
|
268
268
|
- app/views/studio/banners/_impersonation.html.erb
|
|
269
|
+
- app/views/studio/banners/_stack.html.erb
|
|
269
270
|
- app/views/studio/board/_board.html.erb
|
|
270
271
|
- app/views/studio/board/_card_shell.html.erb
|
|
271
272
|
- app/views/studio/board/_column.html.erb
|
|
@@ -336,6 +337,7 @@ files:
|
|
|
336
337
|
- lib/studio/image_cache.rb
|
|
337
338
|
- lib/studio/link_resolution.rb
|
|
338
339
|
- lib/studio/link_token.rb
|
|
340
|
+
- lib/studio/log_rotation.rb
|
|
339
341
|
- lib/studio/mail_transport.rb
|
|
340
342
|
- lib/studio/redis.rb
|
|
341
343
|
- lib/studio/s3.rb
|