command_tower 0.16.0 → 0.18.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/README.md +8 -8
- data/app/controllers/command_tower/application_controller.rb +1 -0
- data/app/controllers/command_tower/me/inbox_controller.rb +16 -0
- data/app/controllers/concerns/command_tower/execution/client_compatibility_boundary.rb +39 -0
- data/app/deserializers/command_tower/deserializers/messaging/inbox.rb +85 -0
- data/app/errors/command_tower/errors/client_update_required_error.rb +27 -0
- data/app/jobs/command_tower/messaging/communications/produce_recipient_job.rb +2 -0
- data/app/models/command_tower/messaging/communication.rb +31 -0
- data/app/serializers/command_tower/serializers/messaging/inbox.rb +45 -1
- data/app/services/command_tower/email_theme/resolver.rb +45 -0
- data/app/services/command_tower/messaging/accept/coordinator.rb +16 -1
- data/app/services/command_tower/messaging/accept/persister.rb +13 -0
- data/app/services/command_tower/messaging/contract/mappers/communication_mapper.rb +7 -0
- data/app/services/command_tower/messaging/contract/results/communication_result.rb +1 -0
- data/app/services/command_tower/messaging/inbox/conversation_result.rb +31 -0
- data/app/services/command_tower/messaging/inbox/entry_result.rb +36 -0
- data/app/services/command_tower/messaging/inbox/reader.rb +231 -18
- data/app/services/command_tower/messaging/inbox.rb +4 -0
- data/app/services/command_tower/messaging/notification_types/declaration.rb +14 -1
- data/app/services/command_tower/messaging/rendering/channel_renderer.rb +44 -13
- data/app/services/command_tower/messaging/rendering/inbox_document.rb +131 -0
- data/app/services/command_tower/messaging/rendering/inbox_document_renderer.rb +224 -0
- data/app/services/command_tower/messaging/rendering/inbox_presentation_resolver.rb +52 -0
- data/app/services/command_tower/messaging/rendering/inbox_presentation_snapshot.rb +64 -0
- data/app/services/command_tower/messaging/rendering/template_resolver.rb +128 -0
- data/app/services/command_tower/messaging.rb +5 -1
- data/app/services/command_tower/services/client_compatibility/evaluate.rb +219 -0
- data/app/services/command_tower/services/messaging/communications/produce.rb +4 -0
- data/app/services/command_tower/services/messaging/communications/produce_many.rb +6 -0
- data/app/services/command_tower/services/messaging/inbox.rb +55 -2
- data/app/views/command_tower/email_verification_mailer/verify_email.html.erb +18 -17
- data/app/views/command_tower/messaging/rendering/email.html.erb +6 -5
- data/app/views/command_tower/password_reset_mailer/reset_password.html.erb +24 -23
- data/app/workflows/command_tower/workflows/auth/plain_text/login_workflow.rb +3 -0
- data/app/workflows/command_tower/workflows/auth/session/show_workflow.rb +3 -0
- data/app/workflows/command_tower/workflows/client_compatibility/evaluate_workflow.rb +83 -0
- data/app/workflows/command_tower/workflows/client_compatibility/recommendation_meta.rb +25 -0
- data/app/workflows/command_tower/workflows/messaging/communications/produce_recipient_workflow.rb +5 -1
- data/app/workflows/command_tower/workflows/messaging/inbox.rb +15 -1
- data/config/routes.rb +1 -0
- data/db/migrate/20261010000001_add_inbox_presentation_snapshot_to_messaging_communications.rb +7 -0
- data/db/migrate/20261010000002_add_conversation_identity_to_messaging_communications.rb +10 -0
- data/docs/api_reference.md +13 -2
- data/docs/authorization.md +1 -1
- data/docs/bootstrap/00-ownership.md +77 -0
- data/docs/bootstrap/01-docker-make-compose.md +267 -0
- data/docs/bootstrap/02-create-the-rails-app.md +75 -0
- data/docs/bootstrap/03-pin-the-gem.md +53 -0
- data/docs/bootstrap/04-secrets-and-env.md +59 -0
- data/docs/bootstrap/05-install-migrate-doctor.md +74 -0
- data/docs/bootstrap/06-mount-and-health.md +53 -0
- data/docs/bootstrap/07-execution-bases.md +38 -0
- data/docs/bootstrap/08-initializer.md +90 -0
- data/docs/bootstrap/09-rbac.md +83 -0
- data/docs/bootstrap/10-roles-and-gates.md +62 -0
- data/docs/bootstrap/11-auth-client-path.md +57 -0
- data/docs/bootstrap/12-smoke-check.md +81 -0
- data/docs/bootstrap/13-optional.md +50 -0
- data/docs/bootstrap/14-sanity-checks.md +44 -0
- data/docs/bootstrap/README.md +90 -0
- data/docs/cookie_authentication_guide.md +11 -0
- data/docs/extending.md +5 -4
- data/docs/host_integration_guide.md +3 -246
- data/docs/initializing.md +37 -28
- data/docs/messaging_integration_guide.md +39 -1
- data/docs/principal_capabilities.md +1 -1
- data/docs/upgrades/0.10.0.md +5 -5
- data/docs/upgrades/0.11.0.md +5 -5
- data/docs/upgrades/0.17.0.md +33 -0
- data/docs/upgrades/0.18.0.md +35 -0
- data/docs/upgrades/README.md +3 -1
- data/lib/command_tower/authorization/default.yml +1 -0
- data/lib/command_tower/client_compatibility/version.rb +45 -0
- data/lib/command_tower/client_compatibility.rb +23 -0
- data/lib/command_tower/configuration/config.rb +6 -0
- data/lib/command_tower/configuration/email_theme/config.rb +69 -0
- data/lib/command_tower/configuration/registry/client_compatibility/binding_definition.rb +42 -0
- data/lib/command_tower/configuration/registry/client_compatibility/config.rb +353 -0
- data/lib/command_tower/configuration/registry/client_compatibility/contract_definition.rb +16 -0
- data/lib/command_tower/configuration/registry/client_compatibility/entity_requirement_definition.rb +67 -0
- data/lib/command_tower/configuration/registry/client_compatibility/minimum_overrides.rb +46 -0
- data/lib/command_tower/configuration/registry/client_compatibility/platform_definition.rb +66 -0
- data/lib/command_tower/configuration/registry/config.rb +14 -0
- data/lib/command_tower/configuration/registry/inbox_presentations/config.rb +105 -0
- data/lib/command_tower/configuration/registry/inbox_presentations/presentation_definition.rb +71 -0
- data/lib/command_tower/current.rb +1 -0
- data/lib/command_tower/engine.rb +4 -0
- data/lib/command_tower/inbox_presentations.rb +19 -0
- data/lib/command_tower/install/baseline.rb +2 -0
- data/lib/command_tower/version.rb +1 -1
- metadata +47 -2
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# 13 — Optional (messaging, tests, client-version compatibility)
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
After `GET /me` works, add platform extras **only if the product needs them**. A platform-proof host may skip this file and run [`14-sanity-checks.md`](14-sanity-checks.md).
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- [`12-smoke-check.md`](12-smoke-check.md) passed
|
|
10
|
+
|
|
11
|
+
Do not start here. Messaging and CVC assume install, RBAC, and gates already exist.
|
|
12
|
+
|
|
13
|
+
## Messaging
|
|
14
|
+
|
|
15
|
+
When the host **emits** notifications or uses phone/Pushover:
|
|
16
|
+
|
|
17
|
+
- Catalog, channel policy, adapters, credentials: [`../messaging_integration_guide.md`](../messaging_integration_guide.md)
|
|
18
|
+
- Admin announcements: [`../admin_workspace.md`](../admin_workspace.md)
|
|
19
|
+
|
|
20
|
+
Complete steps 00–12 first. This bootstrap tree does not duplicate the messaging guide.
|
|
21
|
+
|
|
22
|
+
## Host tests
|
|
23
|
+
|
|
24
|
+
Shared FactoryBot definitions ship with the gem. In the host test boot path:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
require "command_tower/testing"
|
|
28
|
+
CommandTower::Testing.install!
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then `FactoryBot.modify` as needed. Contract: [`../testing.md`](../testing.md).
|
|
32
|
+
|
|
33
|
+
Run specs **in Compose**:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
make rspec
|
|
37
|
+
make spec SPEC=spec/path/to/file_spec.rb
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Do not run `rspec` on the Mac.
|
|
41
|
+
|
|
42
|
+
## Client-version compatibility (CVC)
|
|
43
|
+
|
|
44
|
+
If the frontend must block incompatible native/web builds, register CVC in the host (registry / initializer). Frontend recovery UX is a client concern; the backend must serve the compatibility contract the client expects.
|
|
45
|
+
|
|
46
|
+
Follow the gem’s registry comments and [`../extending.md`](../extending.md) for `config.registry` patterns. Do not invent a parallel version table.
|
|
47
|
+
|
|
48
|
+
## Stop
|
|
49
|
+
|
|
50
|
+
Either you skipped this step, or messaging/tests/CVC point at specialty docs and run via Make. Next: [`14-sanity-checks.md`](14-sanity-checks.md).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 14 — Sanity checks
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Stop condition for **local** bootstrap. This is not a production launch checklist.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- Steps 00–12 complete
|
|
10
|
+
- Step 13 only if you opted in
|
|
11
|
+
|
|
12
|
+
## Checks
|
|
13
|
+
|
|
14
|
+
Run inside Compose (Make wrappers from [`01-docker-make-compose.md`](01-docker-make-compose.md)):
|
|
15
|
+
|
|
16
|
+
| Check | Command | Expect |
|
|
17
|
+
|-------|---------|--------|
|
|
18
|
+
| Doctor | `make doctor` | Exit 0 |
|
|
19
|
+
| Server | `make server` | API listens on the Compose port |
|
|
20
|
+
| Me smoke | [`12-smoke-check.md`](12-smoke-check.md) | `GET /me` **200** |
|
|
21
|
+
| Host specs (if you added them) | `make rspec` | Pass |
|
|
22
|
+
| RuboCop (if you added it) | `make rubocop` | Pass |
|
|
23
|
+
|
|
24
|
+
401 vs 403 vs 404: still [`12-smoke-check.md`](12-smoke-check.md).
|
|
25
|
+
|
|
26
|
+
## Not this manual
|
|
27
|
+
|
|
28
|
+
Do not treat the following as remaining bootstrap steps:
|
|
29
|
+
|
|
30
|
+
- Production deploys, systemd, reverse proxies, LXC
|
|
31
|
+
- Production secret distribution
|
|
32
|
+
- Multi-host clustering
|
|
33
|
+
- App-store / frontend release (see the Command Tower **frontend** bootstrap tree)
|
|
34
|
+
|
|
35
|
+
## After this tree
|
|
36
|
+
|
|
37
|
+
- Product routes and workflows: [`../extending.md`](../extending.md), [`../architecture.md`](../architecture.md)
|
|
38
|
+
- HTTP you did not smoke: [`../api_reference.md`](../api_reference.md)
|
|
39
|
+
- Gem upgrades: [`../upgrades/README.md`](../upgrades/README.md)
|
|
40
|
+
- Command Tower frontend on this API: [Local FE↔BE join](../../../artifacts/COMMANDTOWER_LOCAL_JOIN.md)
|
|
41
|
+
|
|
42
|
+
## Stop
|
|
43
|
+
|
|
44
|
+
Doctor is green and `GET /me` is **200**. Local Command Tower backend bootstrap is complete.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Bootstrap a new Command Tower backend host
|
|
2
|
+
|
|
3
|
+
Bring up a new Rails API host that **mounts** CommandTower, in order.
|
|
4
|
+
|
|
5
|
+
This directory is the host bring-up authority. Specialty contracts stay in sibling docs (`initializing`, `authorization`, `api_reference`, `extending`). This tree owns **order**, **which files the host authors** (including Docker and Make), and **how to invoke Rails** — always inside Compose.
|
|
6
|
+
|
|
7
|
+
Do **not** copy another product host’s tree. Do **not** copy [`../docker-compose.yaml`](../docker-compose.yaml) from this gem (that file is for **engine development**). The dummy app under [`../../rails_app/`](../../rails_app/) is an in-engine example for initializer and RBAC shape, not a product template.
|
|
8
|
+
|
|
9
|
+
## Docker-only
|
|
10
|
+
|
|
11
|
+
Every Ruby/Rails action runs inside Docker Compose.
|
|
12
|
+
|
|
13
|
+
**Host-machine tools:** Docker, the Docker Compose plugin, Make, Git. That is the entire local toolchain.
|
|
14
|
+
|
|
15
|
+
Do **not** run on the Mac (or any unsandboxed host OS):
|
|
16
|
+
|
|
17
|
+
- `gem install rails`
|
|
18
|
+
- `rails new`
|
|
19
|
+
- `bundle`
|
|
20
|
+
- `bin/rails` / `bin/rake`
|
|
21
|
+
- `rspec` / `rubocop`
|
|
22
|
+
|
|
23
|
+
Those names appear in this tree as **task names** inside the container, always invoked as `make …`.
|
|
24
|
+
|
|
25
|
+
**Chicken-and-egg:** author `Dockerfile`, `docker-compose.yml`, and `Makefile` **first** ([`01-docker-make-compose.md`](01-docker-make-compose.md)). `make build` produces a Ruby image. `make rails-new` runs `rails new` **in that image**. Only then pin the gem and install CommandTower.
|
|
26
|
+
|
|
27
|
+
## This is not deployment
|
|
28
|
+
|
|
29
|
+
This manual is **local bring-up**. It ends when the host runs in Compose and passes the checks in [`14-sanity-checks.md`](14-sanity-checks.md): `make doctor` and `GET /me` **200** for a member user.
|
|
30
|
+
|
|
31
|
+
It does **not** cover production deploys, systemd, reverse proxies, LXC, or how secrets are distributed in production. Do not add a deploy step to this tree without a separate decision.
|
|
32
|
+
|
|
33
|
+
## How to read
|
|
34
|
+
|
|
35
|
+
1. Follow **Implementation order** below. That list is the only sequence.
|
|
36
|
+
2. Open one numbered file per step. Later files assume earlier steps exist.
|
|
37
|
+
3. Each step states purpose, prerequisites, files to create, the procedure (`make` only), and the stop condition.
|
|
38
|
+
4. For field contracts, follow the linked specialty doc. Do not treat this tree as a second copy of those contracts.
|
|
39
|
+
|
|
40
|
+
A **platform-proof** host is: Docker/Make, Rails API in the container, CommandTower installed, `host_key`, host RBAC grants, feature gates, register → login → `GET /me`. Product domain and messaging adapters are optional ([`13-optional.md`](13-optional.md)).
|
|
41
|
+
|
|
42
|
+
Attaching a Command Tower **frontend** is a separate step after this tree: [Local FE↔BE join](../../../artifacts/COMMANDTOWER_LOCAL_JOIN.md).
|
|
43
|
+
|
|
44
|
+
## Implementation order
|
|
45
|
+
|
|
46
|
+
| Step | File | What you do |
|
|
47
|
+
|------|------|-------------|
|
|
48
|
+
| 00 | [`00-ownership.md`](00-ownership.md) | Engine vs host; schema; Docker-only; dummy is not a product template |
|
|
49
|
+
| 01 | [`01-docker-make-compose.md`](01-docker-make-compose.md) | Dockerfile, Compose, Makefile, `.dockerignore` |
|
|
50
|
+
| 02 | [`02-create-the-rails-app.md`](02-create-the-rails-app.md) | `make rails-new` (Rails 8 API) inside the container |
|
|
51
|
+
| 03 | [`03-pin-the-gem.md`](03-pin-the-gem.md) | `gem "command_tower"` from RubyGems (or git tag) |
|
|
52
|
+
| 04 | [`04-secrets-and-env.md`](04-secrets-and-env.md) | JWT / signup-session / password-recovery secrets in Compose |
|
|
53
|
+
| 05 | [`05-install-migrate-doctor.md`](05-install-migrate-doctor.md) | `command_tower:install`, migrate, doctor |
|
|
54
|
+
| 06 | [`06-mount-and-health.md`](06-mount-and-health.md) | Engine mount path; host healthz |
|
|
55
|
+
| 07 | [`07-execution-bases.md`](07-execution-bases.md) | Inherit CommandTower controller/job bases |
|
|
56
|
+
| 08 | [`08-initializer.md`](08-initializer.md) | `host_key`, cookies if needed, email knobs |
|
|
57
|
+
| 09 | [`09-rbac.md`](09-rbac.md) | Host `rbac_groups.yml` grants CT entity names |
|
|
58
|
+
| 10 | [`10-roles-and-gates.md`](10-roles-and-gates.md) | Default `member`; enable login / verify / reset gates |
|
|
59
|
+
| 11 | [`11-auth-client-path.md`](11-auth-client-path.md) | Bearer vs cookie+CSRF |
|
|
60
|
+
| 12 | [`12-smoke-check.md`](12-smoke-check.md) | Register → login → `GET /me` **200** |
|
|
61
|
+
| 13 | [`13-optional.md`](13-optional.md) | Optional: messaging, host tests, client-version compatibility |
|
|
62
|
+
| 14 | [`14-sanity-checks.md`](14-sanity-checks.md) | Stop: doctor + Me smoke; not deployment |
|
|
63
|
+
|
|
64
|
+
Step 13 is optional after 12.
|
|
65
|
+
|
|
66
|
+
## File index
|
|
67
|
+
|
|
68
|
+
Same files as the table. Filename numbers match the order list. If you add, remove, or reorder a step, change this README and the filenames together.
|
|
69
|
+
|
|
70
|
+
## Specialty docs this tree does not replace
|
|
71
|
+
|
|
72
|
+
| Need | Doc |
|
|
73
|
+
|------|-----|
|
|
74
|
+
| Attach a Command Tower frontend locally | [`../../../artifacts/COMMANDTOWER_LOCAL_JOIN.md`](../../../artifacts/COMMANDTOWER_LOCAL_JOIN.md) |
|
|
75
|
+
| What `command_tower:install` does, flags, schema, doctor | [`../initializing.md`](../initializing.md) |
|
|
76
|
+
| Layer map, workflow rule | [`../architecture.md`](../architecture.md) |
|
|
77
|
+
| Extension points | [`../extending.md`](../extending.md) |
|
|
78
|
+
| HTTP catalog | [`../api_reference.md`](../api_reference.md) |
|
|
79
|
+
| Route areas | [`../controllers.md`](../controllers.md) |
|
|
80
|
+
| RBAC | [`../authorization.md`](../authorization.md), [`../authentication_authorization_guide.md`](../authentication_authorization_guide.md) |
|
|
81
|
+
| Cookie / CSRF | [`../cookie_authentication_guide.md`](../cookie_authentication_guide.md) |
|
|
82
|
+
| Messaging | [`../messaging_integration_guide.md`](../messaging_integration_guide.md) |
|
|
83
|
+
| Testing factories | [`../testing.md`](../testing.md) |
|
|
84
|
+
| Admin Workspace | [`../admin_workspace.md`](../admin_workspace.md) |
|
|
85
|
+
| Principal capabilities | [`../principal_capabilities.md`](../principal_capabilities.md) |
|
|
86
|
+
| Audit registry | [`../audit.md`](../audit.md) |
|
|
87
|
+
| Eventing | [`../eventing.md`](../eventing.md) |
|
|
88
|
+
| Release upgrades | [`../upgrades/README.md`](../upgrades/README.md) |
|
|
89
|
+
|
|
90
|
+
Older entry: [`../host_integration_guide.md`](../host_integration_guide.md) is a pointer here.
|
|
@@ -37,6 +37,17 @@ end
|
|
|
37
37
|
| CSRF `same_site` / `secure` / `path` / `domain` | `nil` (inherit JWT cookie) |
|
|
38
38
|
| CSRF `ttl` | `7.days` |
|
|
39
39
|
|
|
40
|
+
### Two web apps on one hostname
|
|
41
|
+
|
|
42
|
+
The browser keys `ct_jwt` and `ct_csrf` by hostname. The port is not part of that key. Two Command Tower web apps on `localhost` overwrite each other’s session. Logout expires both cookies, so a shared CSRF name still logs the other app out on its next POST.
|
|
43
|
+
|
|
44
|
+
A second product sets both names. The frontend `session.csrfCookieName` matches `csrf.cookie_name`. Leave `header_name` as `X-CSRF-Token`. A host that runs alone keeps `ct_jwt` and `ct_csrf`.
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
config.jwt.cookie.name = "other_jwt"
|
|
48
|
+
config.jwt.cookie.csrf.cookie_name = "other_csrf"
|
|
49
|
+
```
|
|
50
|
+
|
|
40
51
|
## Token extraction order
|
|
41
52
|
|
|
42
53
|
1. `Authorization: Bearer <token>` header (wins when present)
|
data/docs/extending.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
How hosts customize and integrate with CommandTower **without forking platform internals**.
|
|
4
4
|
|
|
5
|
-
**New host?** Start with [
|
|
5
|
+
**New host?** Start with [Bootstrap a new host](bootstrap/README.md).
|
|
6
6
|
|
|
7
7
|
Layer map: [architecture.md](architecture.md). Install/configure/migrate/doctor: [initializing.md](initializing.md). Testing API: [testing.md](testing.md). HTTP catalog: [api_reference.md](api_reference.md).
|
|
8
8
|
|
|
@@ -19,6 +19,7 @@ Layer map: [architecture.md](architecture.md). Install/configure/migrate/doctor:
|
|
|
19
19
|
| Model reopen | Product associations / behavior |
|
|
20
20
|
| Initializers | Configuration, including `config.registry.audit.event`, `config.registry.admin_workspace.tool`, and `config.registry.principal_capabilities.capability` |
|
|
21
21
|
| Notification catalogs / channel policy | Host-owned messaging customization |
|
|
22
|
+
| `app/views/command_tower/messaging/rendering/**` | Host override of generic and/or per-`notification_type_key` rendered Email/SMS/Pushover/Push templates — see [messaging_integration_guide.md](messaging_integration_guide.md#rendering-template-overrides) |
|
|
22
23
|
|
|
23
24
|
## Internal platform — do not extend
|
|
24
25
|
|
|
@@ -28,7 +29,7 @@ Layer map: [architecture.md](architecture.md). Install/configure/migrate/doctor:
|
|
|
28
29
|
| ServiceBase | Shared service framework |
|
|
29
30
|
| Serializers | Platform response shaping |
|
|
30
31
|
| Deserializers | Platform request trust boundary |
|
|
31
|
-
| Messaging execution pipeline | Handoff / execution / accept internals |
|
|
32
|
+
| Messaging execution pipeline | Handoff / execution / accept internals, including `Messaging::Rendering::ChannelRenderer` / `TemplateResolver` Ruby classes — hosts customize rendering by dropping ERB views (above), never by reopening or calling these classes |
|
|
32
33
|
| RequestContext | Framework request context |
|
|
33
34
|
| JWT primitives | Token issue / validate plumbing |
|
|
34
35
|
| Internal framework plumbing | Envelope renderer, workflow base mechanics, etc. |
|
|
@@ -41,11 +42,11 @@ Full install narrative: [initializing.md](initializing.md).
|
|
|
41
42
|
|
|
42
43
|
| Surface | Notes |
|
|
43
44
|
|---------|--------|
|
|
44
|
-
| `
|
|
45
|
+
| `command_tower:install` (`make rails ARGS='command_tower:install'`) | Task name inside the container; migrations + optional configure |
|
|
45
46
|
| `SKIP_CONFIGURE=1` | Migrations only |
|
|
46
47
|
| `SKIP_MOUNT=1` | Initializer without mount |
|
|
47
48
|
| `FORCE=1` | Overwrite initializer |
|
|
48
|
-
| `rails
|
|
49
|
+
| `command_tower:configure` (`make rails ARGS='generate command_tower:configure'`) | `--skip-routes`, `--force` |
|
|
49
50
|
|
|
50
51
|
## Mounting and configuration
|
|
51
52
|
|
|
@@ -1,250 +1,7 @@
|
|
|
1
1
|
# Host Integration Guide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The new-host manual lives in [`bootstrap/README.md`](bootstrap/README.md).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
That directory is the bring-up authority: implementation order, Docker-only Make wrappers, one file per step, and an explicit note that the manual is **local bring-up** (not deployment).
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## Prerequisites
|
|
10
|
-
|
|
11
|
-
- A Rails host application (engine is mounted into the host)
|
|
12
|
-
- `gem "command_tower"` in the host Gemfile (path, git, or released gem)
|
|
13
|
-
- Host-owned database (and any host Redis/job stack your app already uses)
|
|
14
|
-
|
|
15
|
-
## Step 1 — Install
|
|
16
|
-
|
|
17
|
-
Follow [Initializing](initializing.md):
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
bundle install
|
|
21
|
-
bin/rails command_tower:install
|
|
22
|
-
bin/rails db:migrate
|
|
23
|
-
bin/rails command_tower:doctor
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
That copies CT migrations, generates `config/initializers/command_tower.rb` (unless skipped), and mounts `CommandTower::Engine` (unless skipped).
|
|
27
|
-
|
|
28
|
-
## Step 2 — Secrets and initializer
|
|
29
|
-
|
|
30
|
-
In the host initializer, set at least:
|
|
31
|
-
|
|
32
|
-
- `config.jwt.hmac_secret`
|
|
33
|
-
- `config.signup_session.jwt_secret` (or `SIGNUP_SESSION_JWT_SECRET`)
|
|
34
|
-
- `config.password_recovery_session.jwt_secret` (or `PASSWORD_RECOVERY_SESSION_JWT_SECRET`)
|
|
35
|
-
- `config.application.host_key` — server-bound host/product identity for CT-generic user-scoped state (for example experience states). Not client-supplied. Blank values cause Me experience-state routes to return **503** `experience_states_host_unconfigured`.
|
|
36
|
-
|
|
37
|
-
Re-run `bin/rails command_tower:doctor`. Details: [Initializing — Configuration](initializing.md#configuration).
|
|
38
|
-
|
|
39
|
-
Dummy-host reference: [`rails_app/config/initializers/command_tower.rb`](../rails_app/config/initializers/command_tower.rb) (sets `host_key` to `"command_tower"`).
|
|
40
|
-
|
|
41
|
-
### Email / SMTP
|
|
42
|
-
|
|
43
|
-
CommandTower owns ActionMailer delivery for engine mailers (email verification, password reset, messaging channel mail).
|
|
44
|
-
|
|
45
|
-
| Environment | Delivery |
|
|
46
|
-
|-------------|----------|
|
|
47
|
-
| `test` | `:test` (in-memory `ActionMailer::Base.deliveries`; no external SMTP) |
|
|
48
|
-
| development / production | `:smtp` unless the host explicitly sets `config.email.delivery_method` |
|
|
49
|
-
|
|
50
|
-
Non-secret knobs live on `config.email.*` (defaults: `smtp.gmail.com`, port `587`, `plain`, STARTTLS auto). Secrets come from Credential Resolution:
|
|
51
|
-
|
|
52
|
-
- `config.credentials.smtp.user_name` / `password`, or
|
|
53
|
-
- ENV `GMAIL_USER_NAME` / `GMAIL_PASSWORD`
|
|
54
|
-
|
|
55
|
-
`SmtpActionMailerBridge` merges resolved credentials into `action_mailer.smtp_settings` at the end of `CommandTower.configure`. Missing credentials fail at send when `raise_delivery_errors` is true. `From` uses the resolved SMTP username when present.
|
|
56
|
-
|
|
57
|
-
Doctor does **not** probe SMTP connectivity.
|
|
58
|
-
|
|
59
|
-
## Step 3 — Confirm mount path
|
|
60
|
-
|
|
61
|
-
Ensure routes include something like:
|
|
62
|
-
|
|
63
|
-
```ruby
|
|
64
|
-
mount CommandTower::Engine => "/" # or "/api"
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Engine paths below are **relative to that mount**. Route area index: [Controllers](controllers.md).
|
|
68
|
-
|
|
69
|
-
## Step 3b — Inherit execution-boundary bases
|
|
70
|
-
|
|
71
|
-
Greenfield hosts should inherit CommandTower bases so Execution Context is established automatically:
|
|
72
|
-
|
|
73
|
-
```ruby
|
|
74
|
-
class ApplicationController < CommandTower::ApplicationController
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
class ApplicationJob < CommandTower::ApplicationJob
|
|
78
|
-
end
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Unauthenticated host endpoints (for example health checks) still receive HTTP `execution_uuid` / `correlation_id`. Successful authentication enriches the **same** context with `user_id` / `effective_user_id`.
|
|
82
|
-
|
|
83
|
-
Workflows and services consume `CommandTower::Current` (or `execution_context`); they do not establish a new execution. For Rake/console, use `CommandTower.with_execution(source: :rake) { ... }`.
|
|
84
|
-
|
|
85
|
-
`Auth::RequestContext` is JWT request/response transport, not Execution Context. Details: [Extending — Execution Context](extending.md#execution-context).
|
|
86
|
-
|
|
87
|
-
Lifecycle and semantic events: [Eventing](eventing.md).
|
|
88
|
-
|
|
89
|
-
If superclass inheritance is technically blocked, include `CommandTower::Execution::HttpBoundary` / `JobBoundary` on the host bases. That is an escape hatch, not the preferred contract.
|
|
90
|
-
|
|
91
|
-
## Step 4 — Host RBAC (required)
|
|
92
|
-
|
|
93
|
-
AuthorizeRequest **fails closed**. CommandTower ships **CT-owned** entity definitions (Me, session, inbox, Admin Workspace capabilities, …) and the platform full-access role (`owner`) in `lib/command_tower/authorization/default.yml`. CommandTower does **not** ship an operational `admin` role.
|
|
94
|
-
|
|
95
|
-
The host YAML (default `config/rbac_groups.yml`) is a **second source**. Composition is additive. Hosts:
|
|
96
|
-
|
|
97
|
-
1. Define product roles (typically `member`).
|
|
98
|
-
2. **Grant names** of already-defined CT entities to those roles.
|
|
99
|
-
3. Optionally define **host-owned** entities for **host** controllers.
|
|
100
|
-
4. Deliberately compose operational Admin roles (least privilege or a broad host-owned `admin`).
|
|
101
|
-
|
|
102
|
-
Do **not** copy CommandTower controller/entity blocks into the host file. Do **not** redefine `owner` or CT entity names. A host **may** define an `admin` group as host policy. Conflicts fail at boot (no last-write-wins).
|
|
103
|
-
|
|
104
|
-
Dummy host grants-only example: [`rails_app/config/rbac_groups.yml`](../rails_app/config/rbac_groups.yml).
|
|
105
|
-
|
|
106
|
-
```ruby
|
|
107
|
-
CommandTower.configure do |c|
|
|
108
|
-
c.authorization.rbac_group_path = Rails.root.join("config/rbac_groups.yml")
|
|
109
|
-
c.authorization.default_membership_role = "member" # optional; nil disables
|
|
110
|
-
end
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
`default_membership_role` is validated against the **composed** graph at boot. Unknown names fail configuration finalization.
|
|
114
|
-
|
|
115
|
-
More: [Authorization](authorization.md), [Authentication & authorization guide](authentication_authorization_guide.md).
|
|
116
|
-
|
|
117
|
-
Product audit names (later) register additively:
|
|
118
|
-
|
|
119
|
-
```ruby
|
|
120
|
-
CommandTower.configure do |c|
|
|
121
|
-
c.registry.audit.event :wager_placed do |event|
|
|
122
|
-
event.allowed_changes = %i[status]
|
|
123
|
-
event.user_history = true
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Do **not** redefine CommandTower-owned audit names. Audit rows persist in CommandTower after `command_tower:install:migrations` and `db:migrate`. See [Audit](audit.md).
|
|
129
|
-
|
|
130
|
-
Admin Workspace tools register additively (navigation metadata, not a dispatcher):
|
|
131
|
-
|
|
132
|
-
```ruby
|
|
133
|
-
CommandTower.configure do |c|
|
|
134
|
-
c.registry.admin_workspace.tool :host_example do |tool|
|
|
135
|
-
tool.label = "Example"
|
|
136
|
-
tool.description = "Short launcher explanation of what this tool does."
|
|
137
|
-
tool.route = "/admin/example"
|
|
138
|
-
tool.group = :product
|
|
139
|
-
tool.sort_order = 300
|
|
140
|
-
tool.required_entity = :host_example_entity
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Do **not** redefine CommandTower-owned tool ids (`users`, `audit`, `messaging`). Hosts own copy for host tools (`description` soft ≤100 / hard ≤160). Manifest: [Admin Workspace](admin_workspace.md).
|
|
146
|
-
|
|
147
|
-
Principal capabilities register additively (frontend-projectable ids, not a dump of all entities):
|
|
148
|
-
|
|
149
|
-
```ruby
|
|
150
|
-
CommandTower.configure do |c|
|
|
151
|
-
c.registry.principal_capabilities.capability :host_example do |capability|
|
|
152
|
-
capability.required_entity = :host_example_entity
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
Do **not** redefine CommandTower-owned capability ids (`admin_workspace`, `admin_users`, `admin_impersonation`, `admin_audit_events`, `admin_messaging_announcements`). Projection: [Principal capabilities](principal_capabilities.md). Grant `admin_impersonation` only to roles that should start impersonation of visible users. Dummy `admin` does not include it.
|
|
158
|
-
|
|
159
|
-
## Step 5 — Roles on users
|
|
160
|
-
|
|
161
|
-
RBAC YAML defines what a role **may** do. Users still need that role assigned.
|
|
162
|
-
|
|
163
|
-
- Specs and the dummy pattern use role name **`member`** for Me/Auth surfaces (including entity `principal_capabilities`).
|
|
164
|
-
- When `authorization.default_membership_role` is set (for example `"member"`), register assigns that role **in the same transaction** as user create. Failure rolls back the user.
|
|
165
|
-
- When it is `nil`, register does not attach roles. Hosts may assign via product logic, ops (`command_tower:users:create`), or other host workflows.
|
|
166
|
-
- Installing CommandTower does **not** grant operational Admin access. Hosts must deliberately grant Admin entities.
|
|
167
|
-
- Admin announcements need a host role that includes `admin_messaging_announcements`.
|
|
168
|
-
- Admin Workspace manifest needs a host role that includes `admin_workspace`. Tool visibility still depends on each tool's `required_entity`.
|
|
169
|
-
- `GET /auth/principal-capabilities` needs entity `principal_capabilities` on the caller’s roles (grant on `member` like session/me). Possessed Admin projectables still depend on the Admin entity grants above.
|
|
170
|
-
- A host may define a broad `admin` role that grants many Admin entities — that is host policy, not a CommandTower default.
|
|
171
|
-
## Step 6 — Enable feature gates you need
|
|
172
|
-
|
|
173
|
-
When a gate is off, the route is **not drawn** → **404**.
|
|
174
|
-
|
|
175
|
-
| Capability | Config |
|
|
176
|
-
|------------|--------|
|
|
177
|
-
| Plain-text login | `config.login.plain_text.enable = true` |
|
|
178
|
-
| Email verification routes | `config.login.plain_text.email_verify = true` |
|
|
179
|
-
| Password reset routes | `config.login.plain_text.password_reset = true` |
|
|
180
|
-
| Email availability | `config.signup_session.email_availability = true` |
|
|
181
|
-
| Username availability | `config.username.realtime_username_check = true` |
|
|
182
|
-
|
|
183
|
-
Full gate list: [API reference — Feature gates](api_reference.md#feature-gates).
|
|
184
|
-
|
|
185
|
-
## Step 7 — Choose auth client path
|
|
186
|
-
|
|
187
|
-
- **API / mobile:** `Authorization: Bearer <jwt>` (default)
|
|
188
|
-
- **Browser / SPA cookies:** enable `config.jwt.cookie` (+ CSRF as needed) — [Cookie authentication](cookie_authentication_guide.md)
|
|
189
|
-
|
|
190
|
-
Engine HTTP success/error bodies use the application envelope `{ data, meta, errors }`. Host provisional helpers (`authenticate_user!`) can differ — [Authentication](authentication.md).
|
|
191
|
-
|
|
192
|
-
## Step 8 — Smoke check
|
|
193
|
-
|
|
194
|
-
With gates and RBAC in place (paths relative to your mount):
|
|
195
|
-
|
|
196
|
-
1. `POST /auth/register` (always drawn) — with `default_membership_role` configured, the user is created **and** assigned that role in one transaction.
|
|
197
|
-
2. `POST /auth/plain-text/login` (if enabled) — receive `data.token`.
|
|
198
|
-
3. `GET /me` with Bearer token — expect **200** and envelope `data` (account payload).
|
|
199
|
-
|
|
200
|
-
If you get **401**, authn failed. If you get **403**, fix Steps 4–5 before debugging clients.
|
|
201
|
-
|
|
202
|
-
Contracts: [API reference](api_reference.md).
|
|
203
|
-
|
|
204
|
-
## Step 9 — Messaging (when needed)
|
|
205
|
-
|
|
206
|
-
Host owns:
|
|
207
|
-
|
|
208
|
-
- Notification catalog / types
|
|
209
|
-
- `platform_enabled_channels` / channel policy
|
|
210
|
-
- Adapter credentials (email / SMS / Pushover)
|
|
211
|
-
|
|
212
|
-
Emit from a **host product workflow** via `Communications::Produce` / `ProduceMany` — not by bypassing workflows. See [Messaging](messaging_integration_guide.md) and [Extending](extending.md).
|
|
213
|
-
|
|
214
|
-
Phone/Pushover routes are always drawn; missing product readiness returns **503** capability errors.
|
|
215
|
-
|
|
216
|
-
## Step 10 — Host tests
|
|
217
|
-
|
|
218
|
-
```ruby
|
|
219
|
-
require "command_tower/testing"
|
|
220
|
-
CommandTower::Testing.install!
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
Details: [Testing](testing.md). Prefer `spec/requests/` patterns in the gem as HTTP contract proof.
|
|
224
|
-
|
|
225
|
-
## Done when
|
|
226
|
-
|
|
227
|
-
- Doctor passes for secrets/migrations
|
|
228
|
-
- Host `rbac_groups.yml` maps Me/Auth entities
|
|
229
|
-
- Users who should use Me surfaces have the host `member` (or equivalent) role
|
|
230
|
-
- Needed feature gates are enabled
|
|
231
|
-
- `GET /me` returns **200** with a Bearer token for a member user
|
|
232
|
-
|
|
233
|
-
## Common failures
|
|
234
|
-
|
|
235
|
-
| Symptom | Likely cause |
|
|
236
|
-
|---------|----------------|
|
|
237
|
-
| Doctor green, `GET /me` is **403** | Missing host RBAC YAML or user lacks `member` (Steps 4–5) |
|
|
238
|
-
| `POST /auth/plain-text/login` is **404** | `login.plain_text.enable?` off (Step 6) |
|
|
239
|
-
| Password-reset / availability **404** | Matching feature gate off |
|
|
240
|
-
| Phone / Pushover **503** | Capability / adapters not ready (Step 9) |
|
|
241
|
-
| Confused error JSON on host controllers | Provisional `authenticate_user!` vs engine envelope (Step 7) |
|
|
242
|
-
|
|
243
|
-
## Related
|
|
244
|
-
|
|
245
|
-
- [Initializing](initializing.md)
|
|
246
|
-
- [Extending](extending.md)
|
|
247
|
-
- [API reference](api_reference.md)
|
|
248
|
-
- [Authentication & authorization guide](authentication_authorization_guide.md)
|
|
249
|
-
- [Messaging](messaging_integration_guide.md)
|
|
250
|
-
- [README](../README.md)
|
|
7
|
+
Do not treat this page as a second checklist. Follow the bootstrap README.
|
data/docs/initializing.md
CHANGED
|
@@ -1,31 +1,38 @@
|
|
|
1
1
|
# Initializing CommandTower
|
|
2
2
|
|
|
3
|
-
CommandTower is a Rails engine.
|
|
3
|
+
CommandTower is a Rails engine. This page is the **install / flags / schema / doctor contract**. A new host does not start here — follow [Bootstrap a new host](bootstrap/README.md) (Docker + Make, in order). That alone is not a fully usable Me/Auth host; bootstrap continues through RBAC, feature gates, and `GET /me`.
|
|
4
|
+
|
|
5
|
+
Ruby/Rails runs **inside Compose**. Operator commands below are `make …`. `bin/rails command_tower:install` is the **task name** inside the container, not a host-machine command.
|
|
4
6
|
|
|
5
7
|
Back to [README](../README.md).
|
|
6
8
|
|
|
7
9
|
## Quick start
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
# Gemfile: gem "command_tower" (or path/git source)
|
|
11
|
-
bundle install
|
|
11
|
+
New hosts: [Bootstrap](bootstrap/README.md) (author Docker/Make first, then `make rails-new`, then pin the gem). After the gem is pinned:
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
```bash
|
|
14
|
+
make rails ARGS='command_tower:install'
|
|
15
|
+
make migrate
|
|
16
|
+
make doctor
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
`command_tower:install` does **not** run `db:migrate`. Installing and migrating stay separate on purpose.
|
|
19
20
|
|
|
21
|
+
If the host owns `/api/healthz` (or any route that must sit **before** the engine mount):
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
SKIP_MOUNT=1 make rails ARGS='command_tower:install'
|
|
25
|
+
```
|
|
26
|
+
|
|
20
27
|
### After install
|
|
21
28
|
|
|
22
|
-
Continue with [
|
|
29
|
+
Continue with [Bootstrap](bootstrap/README.md) from the RBAC / gates / smoke steps:
|
|
23
30
|
|
|
24
|
-
1. Host `rbac_groups.yml` product role that **grants** CT-owned Me/Auth entity names (required — otherwise authenticated Me/Auth calls **403**)
|
|
31
|
+
1. Host `rbac_groups.yml` product role that **grants** CT-owned Me/Auth entity names (required — otherwise authenticated Me/Auth calls **403**) — [`bootstrap/09-rbac.md`](bootstrap/09-rbac.md)
|
|
25
32
|
2. Set `authorization.default_membership_role` (for example `"member"`) or assign roles some other supported way
|
|
26
33
|
3. Enable feature gates you need (login, reset, availability, …)
|
|
27
|
-
4. Smoke-check `GET /me`
|
|
28
|
-
5. Wire messaging catalog/adapters when you emit or use phone/Pushover
|
|
34
|
+
4. Smoke-check `GET /me` — [`bootstrap/12-smoke-check.md`](bootstrap/12-smoke-check.md)
|
|
35
|
+
5. Wire messaging catalog/adapters when you emit or use phone/Pushover — [`bootstrap/13-optional.md`](bootstrap/13-optional.md)
|
|
29
36
|
|
|
30
37
|
### What `command_tower:install` does
|
|
31
38
|
|
|
@@ -41,22 +48,24 @@ Continue with [Host integration](host_integration_guide.md):
|
|
|
41
48
|
| `SKIP_MOUNT=1` | Generate initializer but do not mount routes |
|
|
42
49
|
| `FORCE=1` | Overwrite an existing `config/initializers/command_tower.rb` |
|
|
43
50
|
|
|
44
|
-
Examples:
|
|
51
|
+
Examples (Make; flags enter the container via the `rails` recipe in [`bootstrap/01-docker-make-compose.md`](bootstrap/01-docker-make-compose.md)):
|
|
45
52
|
|
|
46
53
|
```bash
|
|
47
|
-
SKIP_CONFIGURE=1
|
|
48
|
-
SKIP_MOUNT=1
|
|
49
|
-
FORCE=1
|
|
54
|
+
SKIP_CONFIGURE=1 make rails ARGS='command_tower:install'
|
|
55
|
+
SKIP_MOUNT=1 make rails ARGS='command_tower:install'
|
|
56
|
+
FORCE=1 make rails ARGS='command_tower:install'
|
|
50
57
|
```
|
|
51
58
|
|
|
59
|
+
Task names inside the container remain `command_tower:install` (and `command_tower:install:migrations`). Do not run `bin/rails` on the Mac.
|
|
60
|
+
|
|
52
61
|
## Configure generator (optional)
|
|
53
62
|
|
|
54
63
|
Prefer `command_tower:install` for greenfield hosts. The generator remains available:
|
|
55
64
|
|
|
56
65
|
```bash
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
make rails ARGS='generate command_tower:configure'
|
|
67
|
+
make rails ARGS='generate command_tower:configure --skip-routes'
|
|
68
|
+
make rails ARGS='generate command_tower:configure --force'
|
|
60
69
|
```
|
|
61
70
|
|
|
62
71
|
The generator:
|
|
@@ -75,8 +84,8 @@ CommandTower is the **sole authoring authority** for CommandTower-owned schema (
|
|
|
75
84
|
Handled by `command_tower:install` (step 1). Equivalent migration-only command:
|
|
76
85
|
|
|
77
86
|
```bash
|
|
78
|
-
|
|
79
|
-
|
|
87
|
+
make rails ARGS='command_tower:install:migrations'
|
|
88
|
+
make migrate
|
|
80
89
|
```
|
|
81
90
|
|
|
82
91
|
`command_tower:install:migrations` is the standard Rails engine task (wrapper around `railties:install:migrations`). Re-running installation is **idempotent**: already-installed CommandTower migrations are skipped.
|
|
@@ -86,9 +95,9 @@ Rails may **retimestamp** newly copied host files. That is normal. Do not hand-e
|
|
|
86
95
|
### Upgrade workflow
|
|
87
96
|
|
|
88
97
|
1. Bump/release the CommandTower gem dependency in the host.
|
|
89
|
-
2. Run `
|
|
90
|
-
3. Run `
|
|
91
|
-
4. Optionally run `
|
|
98
|
+
2. Run `SKIP_CONFIGURE=1 make rails ARGS='command_tower:install'` (or `make rails ARGS='command_tower:install:migrations'`).
|
|
99
|
+
3. Run `make migrate`.
|
|
100
|
+
4. Optionally run `make doctor`.
|
|
92
101
|
|
|
93
102
|
Do not re-run configure on hosts that already customize the initializer unless you intend to regenerate it (`FORCE=1`).
|
|
94
103
|
|
|
@@ -120,7 +129,7 @@ The generated initializer documents available options via class_composer. Defaul
|
|
|
120
129
|
## Doctor
|
|
121
130
|
|
|
122
131
|
```bash
|
|
123
|
-
|
|
132
|
+
make doctor
|
|
124
133
|
```
|
|
125
134
|
|
|
126
135
|
Checks Rails version compatibility, engine baseline migrations, host-installed migration copies, JWT / session secrets, and messaging adapter names. Failures abort with remediation text; warnings print but allow success.
|
|
@@ -151,12 +160,12 @@ Doctor does **not** probe Redis/SMTP connectivity.
|
|
|
151
160
|
If the host already has a bespoke initializer and mount:
|
|
152
161
|
|
|
153
162
|
```bash
|
|
154
|
-
SKIP_CONFIGURE=1
|
|
155
|
-
# same as:
|
|
156
|
-
|
|
163
|
+
SKIP_CONFIGURE=1 make rails ARGS='command_tower:install'
|
|
164
|
+
# same task as: make rails ARGS='command_tower:install:migrations'
|
|
165
|
+
make migrate
|
|
157
166
|
```
|
|
158
167
|
|
|
159
|
-
Do not run `rails g command_tower:configure` unless you intentionally want stock scaffolding.
|
|
168
|
+
Do not run `rails g command_tower:configure` unless you intentionally want stock scaffolding (`make rails ARGS='generate command_tower:configure'`).
|
|
160
169
|
|
|
161
170
|
## Test suite factories
|
|
162
171
|
|