studio-engine 0.139.0 → 0.140.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 +7 -0
- data/app/models/error_log.rb +5 -4
- data/db/migrate/20261006120001_ensure_error_logs_table.rb +3 -2
- data/db/migrate/20261006120002_ensure_theme_settings_table.rb +2 -2
- data/db/migrate/20261108160000_require_slugs_on_engine_tables.rb +17 -16
- data/db/migrate/20261108170000_add_users_role_known_check.rb +26 -6
- data/lib/studio/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 607b184e22c3660009dfc7e342922ca3bdae6b13a7ca7ed32dc618957dfc819a
|
|
4
|
+
data.tar.gz: 26a6741ca01d65b37d6f16a663f35c6d253f87254af05fd41836d23e2a5ce48b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1f6a8450e4862aea8e8f80c214353442eafcf0c56bbcd0d78848dd291fab92ebc95ed41b836b01ba11a6601a66a652cf06153ef97f7e7c4f31910603457572d1
|
|
7
|
+
data.tar.gz: 24cdd966d0cad48ba9efb03fe6d54c2ec801051c7ccf23f104411fe3a4cf4eac7ea7155b453fb308ba5625be4bd30d7945c83bc2d822026ae4f6b3de194b2f76
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,13 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.140.0 — 2026-11-09
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **`AddUsersRoleKnownCheck` no longer sets `users.role` NOT NULL, and writes its CHECK in the stable form.** The 0.138.0 migration's NOT NULL changed `t.string "role", default: "viewer"` in six consumers' schema dumps, which the release's schema-dump guard refuses, and an old dyno inserting a user without a role would fail during a rollback. It still fills NULL roles with `viewer` and adds CHECK `users_role_known`, now spelled `(role)::text = ANY (ARRAY['viewer'::character varying::text, ...])` on Postgres, so a dump after `db:migrate` equals the dump after `db:schema:load`. The migration was edited in place: 0.138.0 published it, but no app had installed it. Install it with `bin/rails studio_engine:install:migrations`; NOT NULL comes in a later migration.
|
|
12
|
+
- **`RequireSlugsOnEngineTables` no longer makes `slug` NOT NULL.** 0.138.0's migration set `slug` NOT NULL on `error_logs`, `theme_settings`, `studio_geo_settings` and `studio_site_identities`. Heroku runs the migration while old dynos still serve, and 0.137.0's `ErrorLog.capture!` inserts a NULL slug and sets it after, so every error write raised until the restart and again after a `heroku rollback`. The migration (edited in place; no host had installed 0.138.0) still backfills NULL and blank slugs, de-dupes, and adds the unique index, which allows many NULLs. Its `down` now changes nothing. A host adopts in one release: `bin/rails studio_engine:install:migrations && bin/rails db:migrate`, then commit `db/schema.rb`; the two-release note under 0.138.0 no longer applies. NOT NULL comes in a later engine migration, in the release after every host runs the Sluggable `ErrorLog`.
|
|
13
|
+
|
|
7
14
|
## 0.139.0 — 2026-11-08
|
|
8
15
|
|
|
9
16
|
### Added
|
data/app/models/error_log.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
class ErrorLog < ApplicationRecord
|
|
2
|
-
# `slug` is
|
|
3
|
-
# carries one, including a host's bare
|
|
4
|
-
# unique placeholder before the insert
|
|
5
|
-
# after, inside the create, once the
|
|
2
|
+
# `slug` is unique (RequireSlugsOnEngineTables) and becomes NOT NULL a release
|
|
3
|
+
# later, so every insert carries one, including a host's bare
|
|
4
|
+
# `ErrorLog.create!`. Sluggable writes a unique placeholder before the insert
|
|
5
|
+
# and settles it to `error-log-<id>` right after, inside the create, once the
|
|
6
|
+
# id exists.
|
|
6
7
|
include Sluggable
|
|
7
8
|
|
|
8
9
|
belongs_to :target, polymorphic: true, optional: true
|
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
#
|
|
16
16
|
# On every consumer that existed when this shipped, the second branch adds
|
|
17
17
|
# nothing. Studio::HostSchema checks the same columns at boot. The slug
|
|
18
|
-
# standard
|
|
19
|
-
#
|
|
18
|
+
# standard starts with the later RequireSlugsOnEngineTables (20261108160000),
|
|
19
|
+
# which backfills and uniquely indexes the column on every host; NOT NULL
|
|
20
|
+
# follows a release later, once every host runs the Sluggable ErrorLog.
|
|
20
21
|
class EnsureErrorLogsTable < ActiveRecord::Migration[7.2]
|
|
21
22
|
def up
|
|
22
23
|
unless table_exists?(:error_logs)
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
# hand-copied migration omitted it, so a theme save raises on any host built
|
|
7
7
|
# from that doc (cyvasse, mcritchie-industries and moms-app when this shipped).
|
|
8
8
|
# The slug is "theme-<app_name>", derived on save. Rows this left with a NULL
|
|
9
|
-
# slug are filled, and the column made
|
|
10
|
-
# RequireSlugsOnEngineTables (20261108160000).
|
|
9
|
+
# slug are filled, and the column made unique, by the later
|
|
10
|
+
# RequireSlugsOnEngineTables (20261108160000); NOT NULL comes a release after.
|
|
11
11
|
#
|
|
12
12
|
# A baseline, not a rebuild:
|
|
13
13
|
#
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
# The engine's own settings and log tables take the hub's slug
|
|
2
|
-
#
|
|
1
|
+
# The engine's own settings and log tables take the first half of the hub's slug
|
|
2
|
+
# standard: every slug filled and a unique index on it, on error_logs, theme_settings,
|
|
3
3
|
# studio_geo_settings and studio_site_identities (database audit 2026-11-07,
|
|
4
4
|
# finding 6). Every host inherits the tables, so one migration reaches them all.
|
|
5
5
|
#
|
|
@@ -15,13 +15,21 @@
|
|
|
15
15
|
# error_logs) may hold a slug twice; every copy after the lowest id takes
|
|
16
16
|
# `-<id>` after it. No table here has slug children, so nothing else
|
|
17
17
|
# references the old value.
|
|
18
|
-
# 3.
|
|
19
|
-
#
|
|
18
|
+
# 3. The unique index on `slug` unless one is there. A non-unique slug index
|
|
19
|
+
# is replaced by the unique one.
|
|
20
|
+
#
|
|
21
|
+
# NOT NULL is deliberately NOT here. The release sweep installs engine
|
|
22
|
+
# migrations in the same release as the gem bump, and Heroku runs them while the
|
|
23
|
+
# old dynos still serve: the shipped ErrorLog.capture! inserts a NULL slug and
|
|
24
|
+
# sets it after, so a NOT NULL would make every error write raise until the
|
|
25
|
+
# restart, and again after any `heroku rollback`. The unique index allows many
|
|
26
|
+
# NULLs, so old code keeps working. NOT NULL lands in a later engine migration,
|
|
27
|
+
# in the release after every host runs the Sluggable ErrorLog this release ships.
|
|
20
28
|
#
|
|
21
29
|
# Production on 2026-11-08 (every app with a database, read-only counts): no
|
|
22
30
|
# NULL or blank slug, no duplicate, so steps 1 and 2 change no row there. The
|
|
23
|
-
# largest table is the hub's error_logs at 606 rows, so the
|
|
24
|
-
#
|
|
31
|
+
# largest table is the hub's error_logs at 606 rows, so the index build is
|
|
32
|
+
# short. On Postgres each ALTER waits at most LOCK_TIMEOUT
|
|
25
33
|
# for its lock; a migrate that cannot get it fails whole, changes nothing, and
|
|
26
34
|
# runs again on the next release.
|
|
27
35
|
class RequireSlugsOnEngineTables < ActiveRecord::Migration[7.2]
|
|
@@ -43,21 +51,14 @@ class RequireSlugsOnEngineTables < ActiveRecord::Migration[7.2]
|
|
|
43
51
|
|
|
44
52
|
backfill(table)
|
|
45
53
|
dedupe(table)
|
|
46
|
-
change_column_null table, :slug, false
|
|
47
54
|
unique_slug_index(table)
|
|
48
55
|
end
|
|
49
56
|
end
|
|
50
57
|
|
|
51
|
-
#
|
|
52
|
-
#
|
|
58
|
+
# Nothing to undo. The unique index stays: most hosts had it before this
|
|
59
|
+
# migration, and a down cannot tell which index it added. Backfilled slugs
|
|
53
60
|
# stay too; a slug is a valid value either way.
|
|
54
|
-
def down
|
|
55
|
-
TABLES.each do |table|
|
|
56
|
-
next unless table_exists?(table) && column_exists?(table, :slug)
|
|
57
|
-
|
|
58
|
-
change_column_null table, :slug, true
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
+
def down; end
|
|
61
62
|
|
|
62
63
|
private
|
|
63
64
|
|
|
@@ -16,7 +16,14 @@
|
|
|
16
16
|
# nothing written. A stray role is a product call (Turf's "user" meant
|
|
17
17
|
# viewer); this migration does not guess it.
|
|
18
18
|
#
|
|
19
|
-
# Then it fills a NULL role with "viewer"
|
|
19
|
+
# Then it fills a NULL role with "viewer" and adds the CHECK.
|
|
20
|
+
#
|
|
21
|
+
# It sets NO NOT NULL. A dyno still on the old code during a deploy or a
|
|
22
|
+
# rollback may insert a user without a role; the column's default ("viewer")
|
|
23
|
+
# fills it, and a NULL passes a CHECK, so that write keeps working. NOT NULL is
|
|
24
|
+
# a later migration, once every host runs Studio::Account (the same reasoning as
|
|
25
|
+
# the engine-tables slug migration, engine-slug-not-null-later). Edited after
|
|
26
|
+
# 0.138.0 published it: no app had installed it yet.
|
|
20
27
|
#
|
|
21
28
|
# The list is SPELLED OUT, not read from Studio::Roles: a migration is a record
|
|
22
29
|
# that must still run years from now, and a live constant may move. Widening the
|
|
@@ -40,15 +47,13 @@ class AddUsersRoleKnownCheck < ActiveRecord::Migration[7.2]
|
|
|
40
47
|
end
|
|
41
48
|
|
|
42
49
|
execute "UPDATE users SET role = 'viewer' WHERE role IS NULL"
|
|
43
|
-
|
|
44
|
-
add_check_constraint :users, "role IN (#{allowed})", name: NAME
|
|
50
|
+
add_check_constraint :users, expression, name: NAME
|
|
45
51
|
end
|
|
46
52
|
|
|
47
53
|
# Removes the CHECK only when it is this migration's: one that names every
|
|
48
54
|
# engine role. A host's own users_role_known, which `up` left in place, is
|
|
49
|
-
# narrower (the hub's and Turf's name viewer and admin only) and stays.
|
|
50
|
-
# NULL
|
|
51
|
-
# NULL role is the one value no host wants back.
|
|
55
|
+
# narrower (the hub's and Turf's name viewer and admin only) and stays. The
|
|
56
|
+
# NULL roles `up` filled stay "viewer".
|
|
52
57
|
def down
|
|
53
58
|
return unless string_role?
|
|
54
59
|
|
|
@@ -60,6 +65,21 @@ class AddUsersRoleKnownCheck < ActiveRecord::Migration[7.2]
|
|
|
60
65
|
|
|
61
66
|
private
|
|
62
67
|
|
|
68
|
+
# On Postgres, the spelling Postgres itself settles on once a schema.rb is
|
|
69
|
+
# loaded (`'x'::character varying::text`), so a host's schema dump after this
|
|
70
|
+
# migrate equals the dump after a load, byte for byte. An `IN (...)` list
|
|
71
|
+
# dumps as `ARRAY[...]::text[]` first and changes on the next load. Anywhere
|
|
72
|
+
# else (the engine's SQLite test host), the plain IN list. The same rule as
|
|
73
|
+
# 20261108160001_add_engine_state_checks.rb.
|
|
74
|
+
def expression
|
|
75
|
+
return "role IN (#{ROLES.map { |role| connection.quote(role) }.join(', ')})" unless postgres?
|
|
76
|
+
|
|
77
|
+
array = ROLES.map { |role| "#{connection.quote(role)}::character varying::text" }.join(", ")
|
|
78
|
+
"(role)::text = ANY (ARRAY[#{array}])"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def postgres? = connection.adapter_name.downcase.include?("postgres")
|
|
82
|
+
|
|
63
83
|
def string_role?
|
|
64
84
|
return false unless table_exists?(:users)
|
|
65
85
|
|
data/lib/studio/version.rb
CHANGED