admin_suite 0.6.1 → 0.6.2
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 +9 -0
- data/Gemfile +1 -0
- data/README.md +4 -0
- data/lib/admin_suite/version.rb +1 -1
- data/test/archspec/enforcement_test.rb +115 -0
- data/test/dummy/config/environments/development.rb +9 -6
- data/test/dummy_boot_test.rb +21 -0
- data/test/lib/action_executor_test.rb +17 -9
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dfafbff75d098582ef9df840c364f47ca801dc8df3d01b7be84f009b04f73f73
|
|
4
|
+
data.tar.gz: e91b0ffd0d579b5b07e3ff4ecdb5ad3bb829b761d86946aeface7148d6646a21
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 17b32e4625cc21f3a64fbecf03593c96a731bdec745b6e0accaf1464fbe8bbbbf0728c3efc135396ec57055f888fe04ba30463ff61c85bf8741766861a5ce327
|
|
7
|
+
data.tar.gz: 379e5d5ce01d0d22d7b3b9be52f2ebbcc409ec4f04a3ea0d6403b238540f1eb4749c9b172c115b9832d3baf90ce686b46eddeaa551a670637c72ee99d12bbc41
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.6.2] - 2026-10-03
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Restored database-free dummy application boot and executable action coverage.
|
|
12
|
+
- Updated the development dummy application to Ruby 3.4.10.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Added architecture boundary checks and baseline validation to local and hosted CI.
|
|
16
|
+
|
|
8
17
|
## [0.6.1] - 2026-09-12
|
|
9
18
|
|
|
10
19
|
### Fixed
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -20,6 +20,10 @@ Canonical AdminSuite documentation lives in the TechWright vault at
|
|
|
20
20
|
`../_vault/products/admin_suite/docs/`. This repo intentionally has no root
|
|
21
21
|
`docs/` tree or docs symlink.
|
|
22
22
|
|
|
23
|
+
## Architecture check
|
|
24
|
+
|
|
25
|
+
`bin/archspec check` and `bin/archspec-baseline` enforce engine, namespace, and dummy-app boundaries in `Archspec.rb`. They do not boot Rails. Do not grow `archspec_todo.yml`.
|
|
26
|
+
|
|
23
27
|
## Quickstart
|
|
24
28
|
|
|
25
29
|
Add the gem:
|
data/lib/admin_suite/version.rb
CHANGED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "minitest/autorun" unless defined?(Rails)
|
|
4
|
+
require "yaml"
|
|
5
|
+
require "open3"
|
|
6
|
+
require "tmpdir"
|
|
7
|
+
require "fileutils"
|
|
8
|
+
|
|
9
|
+
class ArchspecEnforcementTest < Minitest::Test
|
|
10
|
+
ROOT = File.expand_path("../..", __dir__)
|
|
11
|
+
|
|
12
|
+
def test_pull_request_architecture_job_is_fail_closed_and_matches_bin_ci
|
|
13
|
+
workflow = YAML.load_file(File.join(ROOT, ".github/workflows/ci.yml"))
|
|
14
|
+
job = workflow.fetch("jobs").fetch("architecture")
|
|
15
|
+
assert_equal "ubuntu-latest", job["runs-on"]
|
|
16
|
+
assert_nil job["continue-on-error"]
|
|
17
|
+
assert_nil job["if"]
|
|
18
|
+
|
|
19
|
+
commands = job.fetch("steps").filter_map { |step| step["run"] }
|
|
20
|
+
assert_includes commands, "bin/archspec check"
|
|
21
|
+
assert_includes commands, "bin/archspec-baseline"
|
|
22
|
+
assert commands.none? { |command| command.include?("continue-on-error") || command.include?("[ -f") }
|
|
23
|
+
|
|
24
|
+
ci = File.read(File.join(ROOT, "bin/ci"))
|
|
25
|
+
assert_includes ci, 'step "Architecture: ArchSpec" bin/archspec check'
|
|
26
|
+
assert_includes ci, 'step "Architecture: baseline cap" bin/archspec-baseline'
|
|
27
|
+
refute_match(/if \[ -f bin\/archspec \]/, ci)
|
|
28
|
+
refute_match(/if \[ -f Archspec\.rb \]/, ci)
|
|
29
|
+
assert_equal "read", job.dig("permissions", "contents")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_mcp_cannot_call_rejects_a_write
|
|
33
|
+
spec = File.read(File.join(ROOT, "Archspec.rb"))
|
|
34
|
+
rule = spec[/mcp\.cannot_call\b.*?receiver: :any/m]
|
|
35
|
+
assert rule, "mcp.cannot_call must forbid writes on any receiver"
|
|
36
|
+
refute_match(/ui\.cannot_call/, rule)
|
|
37
|
+
assert_match(/queries\.cannot_use\b[^\n]*:mcp\b/, spec)
|
|
38
|
+
|
|
39
|
+
Dir.mktmpdir do |dir|
|
|
40
|
+
FileUtils.mkdir_p(File.join(dir, "lib/admin_suite/mcp"))
|
|
41
|
+
File.write(File.join(dir, "Archspec.rb"), spec)
|
|
42
|
+
File.write(File.join(dir, "archspec_todo.yml"), File.read(File.join(ROOT, "archspec_todo.yml")))
|
|
43
|
+
File.write(File.join(dir, "lib/admin_suite/mcp/probe.rb"), <<~RUBY)
|
|
44
|
+
module AdminSuite
|
|
45
|
+
module Mcp
|
|
46
|
+
class Probe
|
|
47
|
+
def call(record)
|
|
48
|
+
record.save
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
RUBY
|
|
54
|
+
|
|
55
|
+
output, status = run_archspec(dir, "check")
|
|
56
|
+
refute status.success?, output
|
|
57
|
+
assert_match(/must not call #save/, output)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_missing_and_invalid_configuration_fail
|
|
62
|
+
Dir.mktmpdir do |dir|
|
|
63
|
+
_output, status = run_archspec(dir, "check", "--config", File.join(dir, "missing.rb"))
|
|
64
|
+
refute status.success?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
Dir.mktmpdir do |dir|
|
|
68
|
+
File.write(File.join(dir, "Archspec.rb"), "this is not ruby (\n")
|
|
69
|
+
_output, status = run_archspec(dir, "check")
|
|
70
|
+
refute status.success?
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_stale_baseline_id_fails_and_current_set_passes
|
|
75
|
+
Dir.mktmpdir do |dir|
|
|
76
|
+
FileUtils.mkdir_p(File.join(dir, "app/models"))
|
|
77
|
+
File.write(File.join(dir, "app/models/widget.rb"), "class Widget\nend\n")
|
|
78
|
+
File.write(File.join(dir, "Archspec.rb"), <<~CONFIG)
|
|
79
|
+
todo "archspec_todo.yml"
|
|
80
|
+
component :models, in: "app/models/**/*.rb"
|
|
81
|
+
models.cannot_reference_constants "Nope"
|
|
82
|
+
CONFIG
|
|
83
|
+
File.write(File.join(dir, "archspec_todo.yml"), <<~YAML)
|
|
84
|
+
---
|
|
85
|
+
violations: []
|
|
86
|
+
YAML
|
|
87
|
+
|
|
88
|
+
_output, status = run_baseline(dir)
|
|
89
|
+
assert status.success?, "empty matching baseline must pass"
|
|
90
|
+
|
|
91
|
+
File.write(File.join(dir, "archspec_todo.yml"), <<~YAML)
|
|
92
|
+
---
|
|
93
|
+
violations:
|
|
94
|
+
- id: deadbeefdeadbeefdeadbeef
|
|
95
|
+
rule: constants.forbid
|
|
96
|
+
path: app/models/widget.rb
|
|
97
|
+
line: 1
|
|
98
|
+
message: gone
|
|
99
|
+
YAML
|
|
100
|
+
output, status = run_baseline(dir)
|
|
101
|
+
refute status.success?
|
|
102
|
+
assert_match(/stale deadbeefdeadbeefdeadbeef/, output)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
def run_archspec(dir, *args)
|
|
109
|
+
Open3.capture2e({ "BUNDLE_GEMFILE" => File.join(ROOT, "Gemfile") }, File.join(ROOT, "bin/archspec"), *args, chdir: dir)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def run_baseline(dir)
|
|
113
|
+
Open3.capture2e({ "BUNDLE_GEMFILE" => File.join(ROOT, "Gemfile") }, File.join(ROOT, "bin/archspec-baseline"), chdir: dir)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -31,14 +31,17 @@ Rails.application.configure do
|
|
|
31
31
|
# Print deprecation notices to the Rails logger.
|
|
32
32
|
config.active_support.deprecation = :log
|
|
33
33
|
|
|
34
|
-
#
|
|
35
|
-
config.active_record
|
|
34
|
+
# The dummy app can run without the Active Record railtie.
|
|
35
|
+
if config.respond_to?(:active_record)
|
|
36
|
+
# Raise an error on page load if there are pending migrations.
|
|
37
|
+
config.active_record.migration_error = :page_load
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
# Highlight code that triggered database queries in logs.
|
|
40
|
+
config.active_record.verbose_query_logs = true
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
# Append comments with runtime information tags to SQL queries in logs.
|
|
43
|
+
config.active_record.query_log_tags_enabled = true
|
|
44
|
+
end
|
|
42
45
|
|
|
43
46
|
# Highlight code that triggered redirect in logs.
|
|
44
47
|
config.action_dispatch.verbose_redirect_logs = true
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
require "open3"
|
|
5
|
+
require "rbconfig"
|
|
6
|
+
|
|
7
|
+
class DummyBootTest < ActiveSupport::TestCase
|
|
8
|
+
test "database-free dummy boots and eager loads in development" do
|
|
9
|
+
script = <<~RUBY
|
|
10
|
+
require_relative "test/dummy/config/environment"
|
|
11
|
+
abort "Unexpected ActiveRecord railtie" if Rails.application.config.respond_to?(:active_record)
|
|
12
|
+
Rails.application.eager_load!
|
|
13
|
+
RUBY
|
|
14
|
+
stdout, stderr, status = Open3.capture3(
|
|
15
|
+
{ "RAILS_ENV" => "development" }, RbConfig.ruby, "-e", script,
|
|
16
|
+
chdir: File.expand_path("..", __dir__)
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
assert status.success?, "Development boot failed:\n#{stdout}#{stderr}"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require "test_helper"
|
|
4
4
|
require "tmpdir"
|
|
5
|
+
require "open3"
|
|
6
|
+
require "rbconfig"
|
|
5
7
|
|
|
6
8
|
module Admin
|
|
7
9
|
module Base
|
|
@@ -96,16 +98,22 @@ module Admin
|
|
|
96
98
|
end
|
|
97
99
|
|
|
98
100
|
test "load_action_handlers_for_admin_suite returns early when AdminSuite is not defined" do
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
# Bundler evaluates the gemspec and defines AdminSuite::VERSION.
|
|
102
|
+
# Bypass its RUBYOPT preload in this dependency-free subprocess.
|
|
103
|
+
script = <<~RUBY
|
|
104
|
+
require_relative "lib/admin/base/action_executor"
|
|
105
|
+
abort "AdminSuite unexpectedly loaded" if defined?(AdminSuite)
|
|
106
|
+
executor = Admin::Base::ActionExecutor.new(nil, :test, nil)
|
|
107
|
+
result = executor.send(:load_action_handlers_for_admin_suite!)
|
|
108
|
+
abort "Expected early return" unless result.nil?
|
|
109
|
+
abort "Handlers marked loaded" if Admin::Base::ActionExecutor.handlers_loaded
|
|
110
|
+
RUBY
|
|
111
|
+
stdout, stderr, status = Open3.capture3(
|
|
112
|
+
{ "RUBYOPT" => nil }, RbConfig.ruby, "--disable-gems", "-e", script,
|
|
113
|
+
chdir: File.expand_path("../..", __dir__)
|
|
114
|
+
)
|
|
106
115
|
|
|
107
|
-
|
|
108
|
-
assert_nil executor.send(:load_action_handlers_for_admin_suite!)
|
|
116
|
+
assert status.success?, "Isolated executor failed:\n#{stdout}#{stderr}"
|
|
109
117
|
end
|
|
110
118
|
|
|
111
119
|
test "load_action_handlers_for_admin_suite handles empty action_globs gracefully" do
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: admin_suite
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- TechWright Labs
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -262,6 +262,7 @@ files:
|
|
|
262
262
|
- lib/generators/admin_suite/scaffold/scaffold_generator.rb
|
|
263
263
|
- lib/tasks/admin_suite_tailwind.rake
|
|
264
264
|
- lib/tasks/admin_suite_test.rake
|
|
265
|
+
- test/archspec/enforcement_test.rb
|
|
265
266
|
- test/controllers/resources_controller_test.rb
|
|
266
267
|
- test/dummy/Gemfile
|
|
267
268
|
- test/dummy/README.md
|
|
@@ -305,6 +306,7 @@ files:
|
|
|
305
306
|
- test/dummy/public/icon.svg
|
|
306
307
|
- test/dummy/public/robots.txt
|
|
307
308
|
- test/dummy/test/test_helper.rb
|
|
309
|
+
- test/dummy_boot_test.rb
|
|
308
310
|
- test/fixtures/docs/progress/PROGRESS_REPORT.md
|
|
309
311
|
- test/integration/association_linking_test.rb
|
|
310
312
|
- test/integration/authentication_test.rb
|