admin_suite 0.6.1 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 964267328a54fb0d55bfb035fab32ba694b620b0f5cbdb45194e770a3138885f
4
- data.tar.gz: eec49bd217460a272eb6ce1add0fd9ac487764a37e304bdcde105044f7eed80a
3
+ metadata.gz: 7ea45471224303ab612d3d27887cd3245fcd1f942c06c4a3108e98e5ee91ca4e
4
+ data.tar.gz: ad3e08e25c5e977ace59521565309f5702fbbfb6ff6d2a20d09630161d659c51
5
5
  SHA512:
6
- metadata.gz: 5875a1f28a12afb57b1e43c38db7f4362f6ef896f2b0de7f0af2d949945ac1c60e17f0e24a5b3f967f753fdeac8960470f2076421ef31156f3fb64b03285fbd2
7
- data.tar.gz: 76e74fce525a385fad34c84511f2affbfeca6df6ea147e81dfdaa393051c3b98915055f1b620040a140c6ce5e336823fc66928d2a0b01ca9017881914d530185
6
+ metadata.gz: b971fe2fbd3d44d7b3fc0addefd909c0cde1716442cd80c0e3627ba6bdcbcd3c4a9d42eac9e4dab767fef10c1793279639ec63c32c934682520afd8ea931998e
7
+ data.tar.gz: 7f6f6a1bdbdf194cf5eed18cbe5336c021ab4df544af1c62f0dbc4ad96e6a5c24f503126f50c1702af2bf1c8ab7c2a5ac70ba8fbf5ea6890903292b0190b622d
data/.gitignore CHANGED
@@ -10,6 +10,11 @@
10
10
  /test/dummy/storage/
11
11
  admin_suite-*.gem
12
12
  /.bundle/
13
+ /site/_site/
14
+ /site/.jekyll-cache/
15
+ /site/.sass-cache/
16
+ /site/.bundle/
17
+ /site/vendor/
13
18
 
14
19
  # Agent scratch: git worktrees created per-phase, and subagent-driven-development
15
20
  # ledgers/briefs/review diffs. Never part of the gem — `spec.files` is an
data/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ 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.7.0] - 2026-10-04
9
+
10
+ ### Added
11
+ - Public Jekyll documentation with a dark Just the Docs theme, responsive
12
+ navigation, full-text search, and versioned gem metadata.
13
+ - GitHub Pages build and deployment workflow with rendered link, asset, anchor,
14
+ and search checks. Pull requests validate without deploying.
15
+ - Deterministic, allowlisted publication export from canonical vault docs with
16
+ source revision tracking, content hashes, and export drift checks.
17
+
18
+ ## [0.6.2] - 2026-10-03
19
+
20
+ ### Fixed
21
+ - Restored database-free dummy application boot and executable action coverage.
22
+ - Updated the development dummy application to Ruby 3.4.10.
23
+
24
+ ### Changed
25
+ - Added architecture boundary checks and baseline validation to local and hosted CI.
26
+
8
27
  ## [0.6.1] - 2026-09-12
9
28
 
10
29
  ### Fixed
data/CONTRIBUTING.md CHANGED
@@ -19,6 +19,11 @@ See `../_vault/products/admin_suite/docs/development.md` for detailed informatio
19
19
 
20
20
  ## Pull Requests
21
21
 
22
+ For the public Jekyll site, edit the canonical vault sources and regenerate the
23
+ publication snapshot; do not hand-edit `site/generated/`. See
24
+ `../_vault/products/admin_suite/docs/docs-publication.md` or the
25
+ [published workflow](https://techwright-lab.github.io/admin_suite/docs-publication/).
26
+
22
27
  1. Create a feature branch from `main`
23
28
  2. Make your changes
24
29
  3. Add or update tests as needed
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gemspec
6
6
  group :development, :test do
7
7
  # Provides the asset pipeline used by the engine/dummy app.
8
8
  gem "propshaft"
9
+ gem "archspec", "1.1.0", require: false
9
10
  end
10
11
 
11
12
  group :test do
data/README.md CHANGED
@@ -16,9 +16,21 @@ across other products.
16
16
 
17
17
  ## Documentation
18
18
 
19
+ [Read the documentation](https://techwright-lab.github.io/admin_suite/) —
20
+ [Installation](https://techwright-lab.github.io/admin_suite/installation/),
21
+ [Resources](https://techwright-lab.github.io/admin_suite/resources/), and
22
+ [Admin MCP](https://techwright-lab.github.io/admin_suite/mcp/).
23
+
19
24
  Canonical AdminSuite documentation lives in the TechWright vault at
20
25
  `../_vault/products/admin_suite/docs/`. This repo intentionally has no root
21
- `docs/` tree or docs symlink.
26
+ `docs/` tree or docs symlink. The Jekyll site in `site/` publishes a generated,
27
+ allowlisted snapshot. See the [publication guide](https://techwright-lab.github.io/admin_suite/docs-publication/)
28
+ or its canonical source, `../_vault/products/admin_suite/docs/docs-publication.md`,
29
+ for export, drift checks, local preview, and GitHub Pages setup.
30
+
31
+ ## Architecture check
32
+
33
+ `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`.
22
34
 
23
35
  ## Quickstart
24
36
 
@@ -55,7 +67,7 @@ AdminSuite.configure do |config|
55
67
  end
56
68
  ```
57
69
 
58
- Read more: `../_vault/products/admin_suite/docs/configuration.md`
70
+ Read more: [Configuration](https://techwright-lab.github.io/admin_suite/configuration/).
59
71
 
60
72
  Set `config.authorize` to decide *what* an authenticated actor may do:
61
73
 
@@ -71,7 +83,7 @@ keeps authentication as the only web gate; MCP serves no tools or data. Resource
71
83
  CRUD, `toggle`, and named execute/bulk actions regardless of this hook.
72
84
 
73
85
  The MCP endpoint is `<mount>/mcp`. See
74
- `../_vault/products/admin_suite/docs/mcp.md` for setup, tool contracts and migration.
86
+ [Admin MCP](https://techwright-lab.github.io/admin_suite/mcp/) for setup, tool contracts and migration.
75
87
 
76
88
  ### Add portals (navigation metadata)
77
89
 
@@ -84,7 +96,7 @@ AdminSuite.configure do |config|
84
96
  end
85
97
  ```
86
98
 
87
- Read more: `../_vault/products/admin_suite/docs/portals.md`
99
+ Read more: [Portals & dashboards](https://techwright-lab.github.io/admin_suite/portals/).
88
100
 
89
101
  ### Add a resource
90
102
 
@@ -123,7 +135,7 @@ module Admin
123
135
  end
124
136
  ```
125
137
 
126
- Read more: `../_vault/products/admin_suite/docs/resources.md` and `../_vault/products/admin_suite/docs/fields.md`
138
+ Read more: [Resources](https://techwright-lab.github.io/admin_suite/resources/) and [Fields](https://techwright-lab.github.io/admin_suite/fields/).
127
139
 
128
140
  ### Add docs (optional)
129
141
 
@@ -133,7 +145,7 @@ Then visit:
133
145
 
134
146
  - `/internal/admin/docs`
135
147
 
136
- Read more: `../_vault/products/admin_suite/docs/docs_viewer.md`
148
+ Read more: [Docs viewer](https://techwright-lab.github.io/admin_suite/docs-viewer/).
137
149
 
138
150
  ## Contributing
139
151
 
@@ -142,4 +154,3 @@ See:
142
154
  - `CONTRIBUTING.md`
143
155
  - `../_vault/products/admin_suite/docs/development.md`
144
156
  - `../_vault/products/admin_suite/docs/releasing.md`
145
-
@@ -2,7 +2,7 @@
2
2
 
3
3
  module AdminSuite
4
4
  module Version
5
- VERSION = "0.6.1"
5
+ VERSION = "0.7.0"
6
6
  end
7
7
 
8
8
  # Backward-compatible constant.
@@ -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
- # Raise an error on page load if there are pending migrations.
35
- config.active_record.migration_error = :page_load
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
- # Highlight code that triggered database queries in logs.
38
- config.active_record.verbose_query_logs = true
39
+ # Highlight code that triggered database queries in logs.
40
+ config.active_record.verbose_query_logs = true
39
41
 
40
- # Append comments with runtime information tags to SQL queries in logs.
41
- config.active_record.query_log_tags_enabled = true
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
- # Temporarily undefine AdminSuite to test early return
100
- admin_suite_defined = defined?(AdminSuite)
101
-
102
- skip "Cannot test AdminSuite undefined condition when AdminSuite is required" if admin_suite_defined
103
-
104
- resource_class = Struct.new(:resource_name).new("test")
105
- executor = ActionExecutor.new(resource_class, :test, nil)
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
- # This should return early without error
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.1
4
+ version: 0.7.0
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-09-12 00:00:00.000000000 Z
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
@@ -359,6 +361,7 @@ licenses:
359
361
  - MIT
360
362
  metadata:
361
363
  rubygems_mfa_required: 'true'
364
+ documentation_uri: https://techwright-lab.github.io/admin_suite/
362
365
  source_code_uri: https://github.com/techwright-lab/admin_suite
363
366
  changelog_uri: https://github.com/techwright-lab/admin_suite/blob/main/CHANGELOG.md
364
367
  post_install_message: